From bcc72e4d4f05b7c16923e84ce712f2ac1904fa24 Mon Sep 17 00:00:00 2001 From: lmadsen Date: Tue, 20 Jan 2009 21:00:59 +0000 Subject: Creating tag for the asterisk-1.4.23 release git-svn-id: http://svn.digium.com/svn/asterisk/tags/1.4.23@169569 f38db490-d61c-443f-a65b-d21fe96a405b --- .cleancount | 1 - BUGS | 22 - CHANGES | 370 - COPYING | 341 - CREDITS | 215 - LICENSE | 68 - Makefile | 772 - Makefile.moddir_rules | 112 - Makefile.rules | 129 - README | 262 - UPGRADE-1.2.txt | 210 - UPGRADE.txt | 500 - Zaptel-to-DAHDI.txt | 105 - agi/DialAnMp3.agi | 82 - agi/Makefile | 47 - agi/agi-test.agi | 79 - agi/eagi-sphinx-test.c | 227 - agi/eagi-test.c | 169 - agi/fastagi-test | 94 - agi/jukebox.agi | 488 - agi/numeralize | 44 - apps/Makefile | 42 - apps/app_adsiprog.c | 1592 - apps/app_alarmreceiver.c | 841 - apps/app_amd.c | 430 - apps/app_authenticate.c | 254 - apps/app_cdr.c | 78 - apps/app_chanisavail.c | 173 - apps/app_channelredirect.c | 140 - apps/app_chanspy.c | 878 - apps/app_controlplayback.c | 168 - apps/app_dahdibarge.c | 358 - apps/app_dahdiras.c | 288 - apps/app_dahdiscan.c | 389 - apps/app_db.c | 167 - apps/app_dial.c | 2015 -- apps/app_dictate.c | 349 - apps/app_directed_pickup.c | 181 - apps/app_directory.c | 707 - apps/app_disa.c | 399 - apps/app_dumpchan.c | 176 - apps/app_echo.c | 104 - apps/app_exec.c | 221 - apps/app_externalivr.c | 585 - apps/app_festival.c | 566 - apps/app_flash.c | 136 - apps/app_followme.c | 1138 - apps/app_forkcdr.c | 267 - apps/app_getcpeid.c | 148 - apps/app_hasnewvoicemail.c | 225 - apps/app_ices.c | 233 - apps/app_image.c | 125 - apps/app_ivrdemo.c | 132 - apps/app_lookupblacklist.c | 160 - apps/app_lookupcidname.c | 103 - apps/app_macro.c | 592 - apps/app_meetme.c | 5037 --- apps/app_milliwatt.c | 194 - apps/app_mixmonitor.c | 446 - apps/app_morsecode.c | 179 - apps/app_mp3.c | 255 - apps/app_nbscat.c | 237 - apps/app_osplookup.c | 1677 - apps/app_page.c | 217 - apps/app_parkandannounce.c | 260 - apps/app_playback.c | 494 - apps/app_privacy.c | 232 - apps/app_queue.c | 5140 --- apps/app_random.c | 108 - apps/app_read.c | 234 - apps/app_readfile.c | 120 - apps/app_realtime.c | 263 - apps/app_record.c | 386 - apps/app_rpt.c | 11930 ------- apps/app_sayunixtime.c | 126 - apps/app_senddtmf.c | 143 - apps/app_sendtext.c | 131 - apps/app_setcallerid.c | 162 - apps/app_setcdruserfield.c | 175 - apps/app_settransfercapability.c | 136 - apps/app_skel.c | 133 - apps/app_sms.c | 1538 - apps/app_softhangup.c | 121 - apps/app_speech_utils.c | 875 - apps/app_stack.c | 174 - apps/app_system.c | 162 - apps/app_talkdetect.c | 227 - apps/app_test.c | 512 - apps/app_transfer.c | 156 - apps/app_url.c | 173 - apps/app_userevent.c | 114 - apps/app_verbose.c | 168 - apps/app_voicemail.c | 9218 ------ apps/app_waitforring.c | 136 - apps/app_waitforsilence.c | 207 - apps/app_while.c | 339 - apps/app_zapateller.c | 120 - apps/enter.h | 287 - apps/leave.h | 207 - apps/rpt_flow.pdf | Bin 51935 -> 0 bytes autoconf/acx_pthread.m4 | 242 - autoconf/ast_c_compile_check.m4 | 31 - autoconf/ast_c_define_check.m4 | 33 - autoconf/ast_check_gnu_make.m4 | 20 - autoconf/ast_check_openh323.m4 | 126 - autoconf/ast_check_pwlib.m4 | 255 - autoconf/ast_ext_lib.m4 | 82 - autoconf/ast_func_fork.m4 | 59 - autoconf/ast_gcc_attribute.m4 | 16 - autoconf/ast_prog_egrep.m4 | 11 - autoconf/ast_prog_ld.m4 | 83 - autoconf/ast_prog_ld_gnu.m4 | 16 - autoconf/ast_prog_sed.m4 | 21 - bootstrap.sh | 40 - build_tools/cflags-devmode.xml | 17 - build_tools/cflags.xml | 21 - build_tools/embed_modules.xml | 26 - build_tools/get_makeopts | 3 - build_tools/get_moduleinfo | 3 - build_tools/make_build_h | 20 - build_tools/make_buildopts_h | 29 - build_tools/make_defaults_h | 26 - build_tools/make_sample_voicemail | 25 - build_tools/make_version | 83 - build_tools/make_version_h | 25 - build_tools/menuselect-deps.in | 41 - build_tools/mkpkgconfig | 50 - build_tools/prep_tarball | 9 - build_tools/strip_nonapi | 37 - cdr/Makefile | 32 - cdr/cdr_csv.c | 365 - cdr/cdr_custom.c | 174 - cdr/cdr_manager.c | 170 - cdr/cdr_odbc.c | 481 - cdr/cdr_pgsql.c | 336 - cdr/cdr_radius.c | 273 - cdr/cdr_sqlite.c | 217 - cdr/cdr_tds.c | 630 - channels/DialTone.h | 252 - channels/Makefile | 123 - channels/answer.h | 237 - channels/busy_tone.h | 55 - channels/chan_agent.c | 2870 -- channels/chan_alsa.c | 1391 - channels/chan_dahdi.c | 11992 ------- channels/chan_features.c | 580 - channels/chan_gtalk.c | 2067 -- channels/chan_h323.c | 3274 -- channels/chan_iax2.c | 11336 ------- channels/chan_local.c | 808 - channels/chan_mgcp.c | 4430 --- channels/chan_misdn.c | 5775 ---- channels/chan_nbs.c | 302 - channels/chan_oss.c | 1899 -- channels/chan_phone.c | 1433 - channels/chan_sip.c | 18897 ----------- channels/chan_skinny.c | 5016 --- channels/chan_vpb.cc | 2905 -- channels/gentone-ulaw.c | 157 - channels/gentone.c | 95 - channels/h323/ChangeLog | 43 - channels/h323/INSTALL.openh323 | 18 - channels/h323/Makefile.in | 48 - channels/h323/README | 144 - channels/h323/TODO | 9 - channels/h323/ast_h323.cxx | 2487 -- channels/h323/ast_h323.h | 166 - channels/h323/caps_h323.cxx | 239 - channels/h323/caps_h323.h | 124 - channels/h323/chan_h323.h | 254 - channels/h323/cisco-h225.asn | 74 - channels/h323/cisco-h225.cxx | 853 - channels/h323/cisco-h225.h | 299 - channels/h323/compat_h323.cxx | 138 - channels/h323/compat_h323.h | 80 - channels/h323/noexport.map | 5 - channels/iax2-parser.c | 1053 - channels/iax2-parser.h | 160 - channels/iax2-provision.c | 540 - channels/iax2-provision.h | 53 - channels/iax2.h | 237 - channels/misdn/Makefile | 17 - channels/misdn/chan_misdn_config.h | 152 - channels/misdn/ie.c | 1422 - channels/misdn/isdn_lib.c | 4661 --- channels/misdn/isdn_lib.h | 674 - channels/misdn/isdn_lib_intern.h | 142 - channels/misdn/isdn_msg_parser.c | 1348 - channels/misdn/portinfo.c | 198 - channels/misdn_config.c | 1160 - channels/ring10.h | 1752 - channels/ring_tone.h | 30 - codecs/Makefile | 61 - codecs/adpcm_slin_ex.h | 25 - codecs/codec_a_mu.c | 168 - codecs/codec_adpcm.c | 404 - codecs/codec_alaw.c | 189 - codecs/codec_dahdi.c | 480 - codecs/codec_g726.c | 964 - codecs/codec_gsm.c | 290 - codecs/codec_ilbc.c | 248 - codecs/codec_lpc10.c | 317 - codecs/codec_speex.c | 522 - codecs/codec_ulaw.c | 201 - codecs/g726_slin_ex.h | 25 - codecs/gsm/COPYRIGHT | 16 - codecs/gsm/Makefile | 543 - codecs/gsm/README | 37 - codecs/gsm/inc/config.h | 51 - codecs/gsm/inc/gsm.h | 71 - codecs/gsm/inc/private.h | 312 - codecs/gsm/inc/proto.h | 65 - codecs/gsm/inc/unproto.h | 23 - codecs/gsm/libgsm.vcproj | 253 - codecs/gsm/src/add.c | 235 - codecs/gsm/src/code.c | 97 - codecs/gsm/src/debug.c | 76 - codecs/gsm/src/decode.c | 62 - codecs/gsm/src/gsm_create.c | 45 - codecs/gsm/src/gsm_decode.c | 361 - codecs/gsm/src/gsm_destroy.c | 26 - codecs/gsm/src/gsm_encode.c | 451 - codecs/gsm/src/gsm_explode.c | 417 - codecs/gsm/src/gsm_implode.c | 515 - codecs/gsm/src/gsm_option.c | 69 - codecs/gsm/src/gsm_print.c | 167 - codecs/gsm/src/k6opt.h | 84 - codecs/gsm/src/k6opt.s | 739 - codecs/gsm/src/long_term.c | 955 - codecs/gsm/src/lpc.c | 372 - codecs/gsm/src/preprocess.c | 127 - codecs/gsm/src/rpe.c | 490 - codecs/gsm/src/short_term.c | 448 - codecs/gsm/src/table.c | 63 - codecs/gsm_slin_ex.h | 16 - codecs/ilbc/Makefile | 20 - codecs/ilbc_slin_ex.h | 17 - codecs/log2comp.h | 74 - codecs/lpc10/Makefile | 77 - codecs/lpc10/README | 89 - codecs/lpc10/analys.c | 649 - codecs/lpc10/bsynz.c | 447 - codecs/lpc10/chanwr.c | 232 - codecs/lpc10/dcbias.c | 107 - codecs/lpc10/decode.c | 625 - codecs/lpc10/deemp.c | 154 - codecs/lpc10/difmag.c | 133 - codecs/lpc10/dyptrk.c | 405 - codecs/lpc10/encode.c | 373 - codecs/lpc10/energy.c | 103 - codecs/lpc10/f2c.h | 325 - codecs/lpc10/f2clib.c | 85 - codecs/lpc10/ham84.c | 126 - codecs/lpc10/hp100.c | 169 - codecs/lpc10/invert.c | 193 - codecs/lpc10/irc2pc.c | 151 - codecs/lpc10/ivfilt.c | 136 - codecs/lpc10/liblpc10.vcproj | 305 - codecs/lpc10/lpc10.h | 256 - codecs/lpc10/lpcdec.c | 297 - codecs/lpc10/lpcenc.c | 181 - codecs/lpc10/lpcini.c | 446 - codecs/lpc10/lpfilt.c | 125 - codecs/lpc10/median.c | 89 - codecs/lpc10/mload.c | 163 - codecs/lpc10/onset.c | 324 - codecs/lpc10/pitsyn.c | 583 - codecs/lpc10/placea.c | 242 - codecs/lpc10/placev.c | 275 - codecs/lpc10/preemp.c | 144 - codecs/lpc10/prepro.c | 116 - codecs/lpc10/random.c | 125 - codecs/lpc10/rcchk.c | 119 - codecs/lpc10/synths.c | 425 - codecs/lpc10/tbdm.c | 188 - codecs/lpc10/voicin.c | 786 - codecs/lpc10/vparms.c | 255 - codecs/lpc10_slin_ex.h | 13 - codecs/slin_adpcm_ex.h | 25 - codecs/slin_g726_ex.h | 25 - codecs/slin_gsm_ex.h | 28 - codecs/slin_ilbc_ex.h | 28 - codecs/slin_lpc10_ex.h | 21 - codecs/slin_speex_ex.h | 262 - codecs/slin_ulaw_ex.h | 25 - codecs/speex_slin_ex.h | 16 - codecs/ulaw_slin_ex.h | 25 - config.guess | 1495 - config.sub | 1609 - configs/adsi.conf.sample | 8 - configs/adtranvofr.conf.sample | 39 - configs/agents.conf.sample | 107 - configs/alarmreceiver.conf.sample | 80 - configs/alsa.conf.sample | 62 - configs/amd.conf.sample | 18 - configs/asterisk.adsi | 159 - configs/cdr.conf.sample | 148 - configs/cdr_custom.conf.sample | 10 - configs/cdr_manager.conf.sample | 6 - configs/cdr_odbc.conf.sample | 12 - configs/cdr_pgsql.conf.sample | 9 - configs/cdr_tds.conf.sample | 11 - configs/chan_dahdi.conf.sample | 675 - configs/codecs.conf.sample | 65 - configs/dnsmgr.conf.sample | 5 - configs/dundi.conf.sample | 239 - configs/enum.conf.sample | 22 - configs/extconfig.conf.sample | 59 - configs/extensions.ael.sample | 448 - configs/extensions.conf.sample | 614 - configs/features.conf.sample | 100 - configs/festival.conf.sample | 35 - configs/followme.conf.sample | 86 - configs/func_odbc.conf.sample | 41 - configs/gtalk.conf.sample | 19 - configs/h323.conf.sample | 193 - configs/http.conf.sample | 40 - configs/iax.conf.sample | 416 - configs/iaxprov.conf.sample | 81 - configs/indications.conf.sample | 733 - configs/jabber.conf.sample | 18 - configs/logger.conf.sample | 69 - configs/manager.conf.sample | 56 - configs/meetme.conf.sample | 26 - configs/mgcp.conf.sample | 104 - configs/misdn.conf.sample | 438 - configs/modules.conf.sample | 35 - configs/musiconhold.conf.sample | 66 - configs/muted.conf.sample | 39 - configs/osp.conf.sample | 72 - configs/oss.conf.sample | 75 - configs/phone.conf.sample | 49 - configs/privacy.conf.sample | 3 - configs/queues.conf.sample | 311 - configs/res_odbc.conf.sample | 49 - configs/res_pgsql.conf.sample | 14 - configs/res_snmp.conf.sample | 10 - configs/rpt.conf.sample | 193 - configs/rtp.conf.sample | 22 - configs/say.conf.sample | 171 - configs/sip.conf.sample | 685 - configs/sip_notify.conf.sample | 22 - configs/skinny.conf.sample | 96 - configs/sla.conf.sample | 140 - configs/smdi.conf.sample | 75 - configs/telcordia-1.adsi | 83 - configs/udptl.conf.sample | 30 - configs/users.conf.sample | 79 - configs/voicemail.conf.sample | 248 - configs/vpb.conf.sample | 108 - configure | 33094 ------------------- configure.ac | 1678 - contrib/README.festival | 47 - contrib/asterisk-doxygen-header | 10 - contrib/asterisk-ices.xml | 93 - contrib/asterisk-ng-doxygen | 1309 - contrib/dictionary.digium | 31 - contrib/festival-1.4.1-diff | 76 - contrib/festival-1.4.2.diff | 75 - contrib/festival-1.4.3.diff | 93 - contrib/festival-1.95.diff | 107 - contrib/firmware/iax/iaxy.bin | Bin 39402 -> 0 bytes contrib/i18n.testsuite.conf | 136 - contrib/init.d/rc.debian.asterisk | 95 - contrib/init.d/rc.gentoo.asterisk | 26 - contrib/init.d/rc.mandrake.asterisk | 195 - contrib/init.d/rc.mandrake.zaptel | 108 - contrib/init.d/rc.redhat.asterisk | 144 - contrib/init.d/rc.slackware.asterisk | 51 - contrib/init.d/rc.suse.asterisk | 136 - contrib/scripts/README.messages-expire | 20 - contrib/scripts/agents.php | 73 - contrib/scripts/ast_grab_core | 70 - contrib/scripts/astgenkey | 61 - contrib/scripts/astgenkey.8 | 144 - contrib/scripts/autosupport | 255 - contrib/scripts/autosupport.8 | 41 - contrib/scripts/get_ilbc_source.sh | 33 - contrib/scripts/iax-friends.sql | 54 - contrib/scripts/loadtest.tcl | 148 - contrib/scripts/lookup.agi | 90 - contrib/scripts/managerproxy.pl | 242 - contrib/scripts/meetme.sql | 12 - contrib/scripts/messages-expire.pl | 96 - contrib/scripts/qview.pl | 100 - contrib/scripts/realtime_pgsql.sql | 142 - contrib/scripts/retrieve_extensions_from_mysql.pl | 113 - contrib/scripts/retrieve_extensions_from_sql.pl | 158 - contrib/scripts/retrieve_sip_conf_from_mysql.pl | 93 - contrib/scripts/safe_asterisk | 178 - contrib/scripts/safe_asterisk.8 | 69 - contrib/scripts/safe_asterisk_restart | 110 - contrib/scripts/sip-friends.sql | 54 - contrib/scripts/vmail.cgi | 1140 - contrib/scripts/vmdb.sql | 64 - contrib/thirdparty/spexxilbcfix_xlite.reg | Bin 452 -> 0 bytes contrib/thirdparty/spexxilbcfix_xpro.reg | Bin 450 -> 0 bytes contrib/utils/README.rawplayer | 37 - contrib/utils/rawplayer.c | 46 - contrib/utils/zones2indications.c | 153 - contrib/valgrind-RedHat-8.0.supp | 41 - doc/00README.1st | 75 - doc/CODING-GUIDELINES | 543 - doc/PEERING | 499 - doc/ael.txt | 1260 - doc/ajam.txt | 91 - doc/app-sms.txt | 470 - doc/apps.txt | 10 - doc/asterisk-conf.txt | 89 - doc/asterisk-mib.txt | 748 - doc/asterisk.8 | 195 - doc/asterisk.sgml | 364 - doc/backtrace.txt | 197 - doc/billing.txt | 105 - doc/callfiles.txt | 139 - doc/callingpres.txt | 18 - doc/cdrdriver.txt | 216 - doc/chaniax.txt | 369 - doc/channels.txt | 44 - doc/channelvariables.txt | 815 - doc/cli.txt | 33 - doc/cliprompt.txt | 29 - doc/configuration.txt | 180 - doc/cygwin.txt | 9 - doc/datastores.txt | 63 - doc/digium-mib.txt | 24 - doc/dundi.txt | 26 - doc/enum.txt | 308 - doc/extconfig.txt | 91 - doc/extensions.txt | 58 - doc/externalivr.txt | 109 - doc/freetds.txt | 18 - doc/h323.txt | 22 - doc/hardware.txt | 74 - doc/iax.txt | 67 - doc/ices.txt | 12 - doc/imapstorage.txt | 217 - doc/ip-tos.txt | 81 - doc/jabber.txt | 15 - doc/jingle.txt | 11 - doc/jitterbuffer.txt | 137 - doc/lang/hebrew.ods | Bin 23290 -> 0 bytes doc/linkedlists.txt | 98 - doc/localchannel.txt | 49 - doc/macroexclusive.txt | 78 - doc/manager.txt | 311 - doc/math.txt | 69 - doc/misdn.txt | 301 - doc/model.txt | 15 - doc/modules.txt | 26 - doc/mp3.txt | 13 - doc/musiconhold-fpm.txt | 8 - doc/mysql.txt | 15 - doc/odbcstorage.txt | 30 - doc/osp.txt | 421 - doc/privacy.txt | 361 - doc/queuelog.txt | 99 - doc/queues-with-callback-members.txt | 521 - doc/radius.txt | 203 - doc/realtime.txt | 138 - doc/rtp-packetization.txt | 73 - doc/security.txt | 80 - doc/sip-retransmit.txt | 126 - doc/sla.pdf | Bin 68499 -> 0 bytes doc/sla.tex | 378 - doc/smdi.txt | 137 - doc/sms.txt | 147 - doc/snmp.txt | 39 - doc/speechrec.txt | 295 - doc/valgrind.txt | 30 - doc/video.txt | 46 - doc/voicemail_odbc_postgresql.txt | 453 - formats/Makefile | 32 - formats/format_g723.c | 163 - formats/format_g726.c | 277 - formats/format_g729.c | 159 - formats/format_gsm.c | 183 - formats/format_h263.c | 197 - formats/format_h264.c | 186 - formats/format_ilbc.c | 157 - formats/format_jpeg.c | 127 - formats/format_ogg_vorbis.c | 572 - formats/format_pcm.c | 507 - formats/format_sln.c | 141 - formats/format_vox.c | 146 - formats/format_wav.c | 531 - formats/format_wav_gsm.c | 562 - formats/msgsm.h | 689 - funcs/Makefile | 32 - funcs/func_audiohookinherit.c | 282 - funcs/func_base64.c | 94 - funcs/func_callerid.c | 187 - funcs/func_cdr.c | 185 - funcs/func_channel.c | 198 - funcs/func_curl.c | 215 - funcs/func_cut.c | 330 - funcs/func_db.c | 231 - funcs/func_enum.c | 198 - funcs/func_env.c | 158 - funcs/func_global.c | 86 - funcs/func_groupcount.c | 243 - funcs/func_language.c | 90 - funcs/func_logic.c | 214 - funcs/func_math.c | 268 - funcs/func_md5.c | 120 - funcs/func_moh.c | 87 - funcs/func_odbc.c | 672 - funcs/func_rand.c | 104 - funcs/func_realtime.c | 175 - funcs/func_sha1.c | 83 - funcs/func_strings.c | 636 - funcs/func_timeout.c | 194 - funcs/func_uri.c | 101 - images/animlogo.gif | Bin 63968 -> 0 bytes images/asterisk-intro.jpg | Bin 6143 -> 0 bytes images/play.gif | Bin 341 -> 0 bytes include/asterisk.h | 210 - include/asterisk/abstract_jb.h | 226 - include/asterisk/acl.h | 57 - include/asterisk/adsi.h | 353 - include/asterisk/ael_structs.h | 195 - include/asterisk/aes.h | 170 - include/asterisk/agi.h | 57 - include/asterisk/alaw.h | 43 - include/asterisk/app.h | 434 - include/asterisk/ast_expr.h | 32 - include/asterisk/astdb.h | 52 - include/asterisk/astmm.h | 89 - include/asterisk/astobj.h | 824 - include/asterisk/astobj2.h | 560 - include/asterisk/astosp.h | 31 - include/asterisk/audiohook.h | 211 - include/asterisk/autoconfig.h.in | 713 - include/asterisk/callerid.h | 339 - include/asterisk/causes.h | 83 - include/asterisk/cdr.h | 342 - include/asterisk/channel.h | 1442 - include/asterisk/chanvars.h | 42 - include/asterisk/cli.h | 183 - include/asterisk/compat.h | 131 - include/asterisk/compiler.h | 62 - include/asterisk/config.h | 206 - include/asterisk/crypto.h | 112 - include/asterisk/dahdi_compat.h | 458 - include/asterisk/devicestate.h | 133 - include/asterisk/dial.h | 151 - include/asterisk/dns.h | 39 - include/asterisk/dnsmgr.h | 62 - include/asterisk/doxyref.h | 580 - include/asterisk/dsp.h | 124 - include/asterisk/dundi.h | 226 - include/asterisk/endian.h | 62 - include/asterisk/enum.h | 59 - include/asterisk/features.h | 101 - include/asterisk/file.h | 434 - include/asterisk/frame.h | 597 - include/asterisk/fskmodem.h | 72 - include/asterisk/global_datastores.h | 47 - include/asterisk/http.h | 65 - include/asterisk/image.h | 96 - include/asterisk/indications.h | 89 - include/asterisk/inline_api.h | 66 - include/asterisk/io.h | 145 - include/asterisk/jabber.h | 146 - include/asterisk/jingle.h | 45 - include/asterisk/linkedlists.h | 761 - include/asterisk/localtime.h | 30 - include/asterisk/lock.h | 1253 - include/asterisk/logger.h | 139 - include/asterisk/manager.h | 148 - include/asterisk/md5.h | 40 - include/asterisk/module.h | 292 - include/asterisk/monitor.h | 66 - include/asterisk/musiconhold.h | 59 - include/asterisk/netsock.h | 68 - include/asterisk/options.h | 138 - include/asterisk/paths.h | 43 - include/asterisk/pbx.h | 915 - include/asterisk/plc.h | 161 - include/asterisk/poll-compat.h | 111 - include/asterisk/privacy.h | 46 - include/asterisk/res_odbc.h | 103 - include/asterisk/rtp.h | 265 - include/asterisk/say.h | 162 - include/asterisk/sched.h | 198 - include/asterisk/sha1.h | 81 - include/asterisk/slinfactory.h | 57 - include/asterisk/smdi.h | 195 - include/asterisk/speech.h | 152 - include/asterisk/srv.h | 45 - include/asterisk/stringfields.h | 391 - include/asterisk/strings.h | 304 - include/asterisk/tdd.h | 82 - include/asterisk/term.h | 76 - include/asterisk/threadstorage.h | 498 - include/asterisk/time.h | 144 - include/asterisk/tonezone_compat.h | 35 - include/asterisk/transcap.h | 42 - include/asterisk/translate.h | 273 - include/asterisk/udptl.h | 120 - include/asterisk/ulaw.h | 43 - include/asterisk/unaligned.h | 102 - include/asterisk/utils.h | 578 - include/jitterbuf.h | 162 - include/solaris-compat/compat.h | 46 - include/solaris-compat/sys/cdefs.h | 10 - include/solaris-compat/sys/queue.h | 540 - install-sh | 269 - keys/freeworlddialup.pub | 6 - keys/iaxtel.pub | 6 - main/Makefile | 156 - main/abstract_jb.c | 822 - main/acl.c | 590 - main/aescrypt.c | 317 - main/aeskey.c | 469 - main/aesopt.h | 1029 - main/aestab.c | 232 - main/alaw.c | 101 - main/app.c | 1452 - main/ast_expr2.c | 2859 -- main/ast_expr2.fl | 414 - main/ast_expr2.h | 112 - main/ast_expr2.y | 1045 - main/ast_expr2f.c | 3349 -- main/asterisk.c | 3223 -- main/astmm.c | 498 - main/astobj2.c | 771 - main/audiohook.c | 721 - main/autoservice.c | 319 - main/buildinfo.c | 33 - main/callerid.c | 1117 - main/cdr.c | 1499 - main/channel.c | 4876 --- main/chanvars.c | 87 - main/cli.c | 2037 -- main/coef_in.h | 13 - main/coef_out.h | 4 - main/config.c | 1520 - main/cryptostub.c | 95 - main/db.c | 592 - main/db1-ast/Makefile | 71 - main/db1-ast/btree/bt_close.c | 182 - main/db1-ast/btree/bt_conv.c | 221 - main/db1-ast/btree/bt_debug.c | 329 - main/db1-ast/btree/bt_delete.c | 657 - main/db1-ast/btree/bt_get.c | 105 - main/db1-ast/btree/bt_open.c | 458 - main/db1-ast/btree/bt_overflow.c | 228 - main/db1-ast/btree/bt_page.c | 100 - main/db1-ast/btree/bt_put.c | 321 - main/db1-ast/btree/bt_search.c | 213 - main/db1-ast/btree/bt_seq.c | 460 - main/db1-ast/btree/bt_split.c | 829 - main/db1-ast/btree/bt_utils.c | 260 - main/db1-ast/btree/btree.h | 391 - main/db1-ast/btree/extern.h | 70 - main/db1-ast/db/db.c | 103 - main/db1-ast/hash/README | 72 - main/db1-ast/hash/extern.h | 65 - main/db1-ast/hash/hash.c | 999 - main/db1-ast/hash/hash.h | 293 - main/db1-ast/hash/hash_bigkey.c | 668 - main/db1-ast/hash/hash_buf.c | 355 - main/db1-ast/hash/hash_func.c | 225 - main/db1-ast/hash/hash_log2.c | 56 - main/db1-ast/hash/hash_page.c | 946 - main/db1-ast/hash/hsearch.c | 107 - main/db1-ast/hash/ndbm.c | 235 - main/db1-ast/hash/page.h | 92 - main/db1-ast/hash/search.h | 51 - main/db1-ast/include/circ-queue.h | 131 - main/db1-ast/include/compat.h | 49 - main/db1-ast/include/db.h | 250 - main/db1-ast/include/mpool.h | 115 - main/db1-ast/include/ndbm.h | 79 - main/db1-ast/libdb.map | 11 - main/db1-ast/mpool/README | 7 - main/db1-ast/mpool/mpool.c | 498 - main/db1-ast/recno/extern.h | 54 - main/db1-ast/recno/rec_close.c | 183 - main/db1-ast/recno/rec_delete.c | 197 - main/db1-ast/recno/rec_get.c | 311 - main/db1-ast/recno/rec_open.c | 241 - main/db1-ast/recno/rec_put.c | 280 - main/db1-ast/recno/rec_search.c | 126 - main/db1-ast/recno/rec_seq.c | 131 - main/db1-ast/recno/rec_utils.c | 122 - main/db1-ast/recno/recno.h | 39 - main/devicestate.c | 369 - main/dial.c | 894 - main/dns.c | 292 - main/dnsmgr.c | 424 - main/dsp.c | 1819 - main/ecdisa.h | 15 - main/editline/CHANGES | 42 - main/editline/INSTALL | 64 - main/editline/Makefile.in | 234 - main/editline/PLATFORMS | 13 - main/editline/README | 11 - main/editline/TEST/test.c | 268 - main/editline/chared.c | 695 - main/editline/chared.h | 159 - main/editline/common.c | 951 - main/editline/config.guess | 1449 - main/editline/config.h.in | 21 - main/editline/config.sub | 1412 - main/editline/configure | 2459 -- main/editline/configure.in | 278 - main/editline/editline.3 | 646 - main/editline/editrc.5 | 491 - main/editline/el.c | 509 - main/editline/el.h | 145 - main/editline/emacs.c | 488 - main/editline/hist.c | 197 - main/editline/hist.h | 80 - main/editline/histedit.h | 197 - main/editline/history.c | 875 - main/editline/install-sh | 250 - main/editline/key.c | 687 - main/editline/key.h | 79 - main/editline/makelist.in | 254 - main/editline/map.c | 1418 - main/editline/map.h | 79 - main/editline/np/fgetln.c | 88 - main/editline/np/strlcat.c | 75 - main/editline/np/strlcpy.c | 75 - main/editline/np/unvis.c | 322 - main/editline/np/vis.c | 348 - main/editline/np/vis.h | 96 - main/editline/parse.c | 259 - main/editline/parse.h | 52 - main/editline/prompt.c | 174 - main/editline/prompt.h | 62 - main/editline/read.c | 558 - main/editline/read.h | 55 - main/editline/readline.c | 1664 - main/editline/readline/readline.h | 118 - main/editline/refresh.c | 1104 - main/editline/refresh.h | 63 - main/editline/search.c | 649 - main/editline/search.h | 70 - main/editline/sig.c | 198 - main/editline/sig.h | 72 - main/editline/sys.h | 133 - main/editline/term.c | 1587 - main/editline/term.h | 124 - main/editline/tokenizer.c | 397 - main/editline/tokenizer.h | 54 - main/editline/tty.c | 1182 - main/editline/tty.h | 484 - main/editline/vi.c | 941 - main/enum.c | 671 - main/file.c | 1392 - main/fixedjitterbuf.c | 351 - main/fixedjitterbuf.h | 92 - main/frame.c | 1607 - main/fskmodem.c | 309 - main/global_datastores.c | 113 - main/http.c | 752 - main/image.c | 225 - main/indications.c | 597 - main/io.c | 371 - main/jitterbuf.c | 839 - main/loader.c | 1001 - main/logger.c | 939 - main/manager.c | 3198 -- main/md5.c | 267 - main/netsock.c | 217 - main/pbx.c | 6401 ---- main/plc.c | 251 - main/poll.c | 306 - main/privacy.c | 119 - main/rtp.c | 3833 --- main/say.c | 7394 ----- main/sched.c | 404 - main/sha1.c | 385 - main/slinfactory.c | 177 - main/srv.c | 246 - main/stdtime/Makefile | 29 - main/stdtime/localtime.c | 1651 - main/stdtime/private.h | 358 - main/stdtime/test.c | 21 - main/stdtime/tzfile.h | 184 - main/strcompat.c | 472 - main/tdd.c | 328 - main/term.c | 303 - main/threadstorage.c | 245 - main/translate.c | 986 - main/udptl.c | 1247 - main/ulaw.c | 106 - main/utils.c | 1414 - makeopts.in | 194 - missing | 198 - mkinstalldirs | 40 - pbx/Makefile | 61 - pbx/ael/ael-test/ael-ntest10/extensions.ael | 131 - pbx/ael/ael-test/ael-ntest12/extensions.ael | 13 - pbx/ael/ael-test/ael-ntest22/extensions.ael | 7 - pbx/ael/ael-test/ael-ntest22/qq.ael | 6 - pbx/ael/ael-test/ael-ntest22/t1/a.ael | 4 - pbx/ael/ael-test/ael-ntest22/t1/b.ael | 6 - pbx/ael/ael-test/ael-ntest22/t1/c.ael | 13 - pbx/ael/ael-test/ael-ntest22/t2/d.ael | 4 - pbx/ael/ael-test/ael-ntest22/t2/e.ael | 6 - pbx/ael/ael-test/ael-ntest22/t2/f.ael | 9 - pbx/ael/ael-test/ael-ntest22/t3/g.ael | 4 - pbx/ael/ael-test/ael-ntest22/t3/h.ael | 6 - pbx/ael/ael-test/ael-ntest22/t3/i.ael | 4 - pbx/ael/ael-test/ael-ntest22/t3/j.ael | 6 - pbx/ael/ael-test/ael-ntest23/extensions.ael | 7 - pbx/ael/ael-test/ael-ntest23/qq.ael | 6 - pbx/ael/ael-test/ael-ntest23/t1/a.ael | 4 - pbx/ael/ael-test/ael-ntest23/t1/b.ael | 6 - pbx/ael/ael-test/ael-ntest23/t1/c.ael | 13 - pbx/ael/ael-test/ael-ntest23/t2/d.ael | 4 - pbx/ael/ael-test/ael-ntest23/t2/e.ael | 6 - pbx/ael/ael-test/ael-ntest23/t2/f.ael | 9 - pbx/ael/ael-test/ael-ntest23/t3/g.ael | 4 - pbx/ael/ael-test/ael-ntest23/t3/h.ael | 6 - pbx/ael/ael-test/ael-ntest23/t3/i.ael | 4 - pbx/ael/ael-test/ael-ntest23/t3/j.ael | 6 - pbx/ael/ael-test/ael-ntest9/extensions.ael | 12 - pbx/ael/ael-test/ael-test1/extensions.ael | 163 - pbx/ael/ael-test/ael-test11/extensions.ael | 56 - pbx/ael/ael-test/ael-test14/extensions.ael | 20 - pbx/ael/ael-test/ael-test15/extensions.ael | 17 - pbx/ael/ael-test/ael-test16/extensions.ael | 4 - pbx/ael/ael-test/ael-test18/extensions.ael | 40 - pbx/ael/ael-test/ael-test19/extensions.ael | 377 - pbx/ael/ael-test/ael-test2/apptest.ael2 | 146 - pbx/ael/ael-test/ael-test2/extensions.ael | 8 - pbx/ael/ael-test/ael-test20/extensions.ael | 8 - pbx/ael/ael-test/ael-test3/extensions.ael | 3183 -- pbx/ael/ael-test/ael-test3/include1.ael2 | 3 - pbx/ael/ael-test/ael-test3/include2.ael2 | 4 - pbx/ael/ael-test/ael-test3/include3.ael2 | 2 - pbx/ael/ael-test/ael-test3/include4.ael2 | 2 - pbx/ael/ael-test/ael-test3/include5.ael2 | 1 - pbx/ael/ael-test/ael-test3/telemarket_torture.ael2 | 812 - pbx/ael/ael-test/ael-test4/apptest.ael2 | 146 - pbx/ael/ael-test/ael-test4/extensions.ael | 8 - pbx/ael/ael-test/ael-test5/extensions.ael | 833 - pbx/ael/ael-test/ael-test6/extensions.ael | 833 - pbx/ael/ael-test/ael-test7/extensions.ael | 460 - pbx/ael/ael-test/ael-test8/extensions.ael | 27 - pbx/ael/ael-test/ael-vtest13/extensions.ael | 3183 -- pbx/ael/ael-test/ael-vtest13/include1.ael2 | 3 - pbx/ael/ael-test/ael-vtest13/include2.ael2 | 4 - pbx/ael/ael-test/ael-vtest13/include3.ael2 | 2 - pbx/ael/ael-test/ael-vtest13/include4.ael2 | 2 - pbx/ael/ael-test/ael-vtest13/include5.ael2 | 1 - .../ael-test/ael-vtest13/telemarket_torture.ael2 | 812 - pbx/ael/ael-test/ael-vtest17/extensions.ael | 116 - pbx/ael/ael-test/ael-vtest21/extensions.ael | 14 - pbx/ael/ael-test/ael-vtest25/extensions.ael | 8 - pbx/ael/ael-test/ref.ael-ntest10 | 163 - pbx/ael/ael-test/ref.ael-ntest12 | 31 - pbx/ael/ael-test/ref.ael-ntest22 | 55 - pbx/ael/ael-test/ref.ael-ntest23 | 25 - pbx/ael/ael-test/ref.ael-ntest9 | 24 - pbx/ael/ael-test/ref.ael-test1 | 15 - pbx/ael/ael-test/ref.ael-test11 | 14 - pbx/ael/ael-test/ref.ael-test14 | 12 - pbx/ael/ael-test/ref.ael-test15 | 12 - pbx/ael/ael-test/ref.ael-test16 | 13 - pbx/ael/ael-test/ref.ael-test18 | 12 - pbx/ael/ael-test/ref.ael-test19 | 14 - pbx/ael/ael-test/ref.ael-test2 | 21 - pbx/ael/ael-test/ref.ael-test20 | 12 - pbx/ael/ael-test/ref.ael-test3 | 18 - pbx/ael/ael-test/ref.ael-test4 | 21 - pbx/ael/ael-test/ref.ael-test5 | 12 - pbx/ael/ael-test/ref.ael-test6 | 16 - pbx/ael/ael-test/ref.ael-test7 | 14 - pbx/ael/ael-test/ref.ael-test8 | 12 - pbx/ael/ael-test/ref.ael-vtest13 | 3027 -- pbx/ael/ael-test/ref.ael-vtest17 | 71 - pbx/ael/ael-test/ref.ael-vtest21 | 9 - pbx/ael/ael-test/ref.ael-vtest25 | 7 - pbx/ael/ael-test/runtests | 56 - pbx/ael/ael-test/setref | 7 - pbx/ael/ael.flex | 904 - pbx/ael/ael.tab.c | 3443 -- pbx/ael/ael.tab.h | 160 - pbx/ael/ael.y | 880 - pbx/ael/ael_lex.c | 3462 -- pbx/dundi-parser.c | 831 - pbx/dundi-parser.h | 88 - pbx/pbx_ael.c | 4959 --- pbx/pbx_config.c | 2567 -- pbx/pbx_dundi.c | 4682 --- pbx/pbx_gtkconsole.c | 515 - pbx/pbx_loopback.c | 191 - pbx/pbx_realtime.c | 258 - pbx/pbx_spool.c | 532 - res/Makefile | 39 - res/res_adsi.c | 1132 - res/res_agi.c | 2226 -- res/res_clioriginate.c | 175 - res/res_config_odbc.c | 564 - res/res_config_pgsql.c | 842 - res/res_convert.c | 224 - res/res_crypto.c | 631 - res/res_features.c | 2755 -- res/res_indications.c | 406 - res/res_jabber.c | 2518 -- res/res_monitor.c | 714 - res/res_musiconhold.c | 1480 - res/res_odbc.c | 741 - res/res_smdi.c | 1384 - res/res_snmp.c | 115 - res/res_speech.c | 404 - res/snmp/agent.c | 823 - res/snmp/agent.h | 40 - sample.call | 84 - sounds/Makefile | 158 - sounds/sounds.xml | 68 - static-http/ajamdemo.html | 219 - static-http/astman.css | 34 - static-http/astman.js | 262 - static-http/prototype.js | 1781 - utils/Makefile | 132 - utils/ael_main.c | 564 - utils/astman.1 | 102 - utils/astman.c | 755 - utils/check_expr.c | 355 - utils/expr2.testinput | 92 - utils/frame.c | 1092 - utils/frame.h | 300 - utils/muted.c | 709 - utils/smsq.c | 768 - utils/stereorize.c | 159 - utils/streamplayer.c | 124 - 933 files changed, 452595 deletions(-) delete mode 100644 .cleancount delete mode 100644 BUGS delete mode 100644 CHANGES delete mode 100644 COPYING delete mode 100644 CREDITS delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 Makefile.moddir_rules delete mode 100644 Makefile.rules delete mode 100644 README delete mode 100644 UPGRADE-1.2.txt delete mode 100644 UPGRADE.txt delete mode 100644 Zaptel-to-DAHDI.txt delete mode 100644 agi/DialAnMp3.agi delete mode 100644 agi/Makefile delete mode 100644 agi/agi-test.agi delete mode 100644 agi/eagi-sphinx-test.c delete mode 100644 agi/eagi-test.c delete mode 100644 agi/fastagi-test delete mode 100755 agi/jukebox.agi delete mode 100644 agi/numeralize delete mode 100644 apps/Makefile delete mode 100644 apps/app_adsiprog.c delete mode 100644 apps/app_alarmreceiver.c delete mode 100644 apps/app_amd.c delete mode 100644 apps/app_authenticate.c delete mode 100644 apps/app_cdr.c delete mode 100644 apps/app_chanisavail.c delete mode 100644 apps/app_channelredirect.c delete mode 100644 apps/app_chanspy.c delete mode 100644 apps/app_controlplayback.c delete mode 100644 apps/app_dahdibarge.c delete mode 100644 apps/app_dahdiras.c delete mode 100644 apps/app_dahdiscan.c delete mode 100644 apps/app_db.c delete mode 100644 apps/app_dial.c delete mode 100644 apps/app_dictate.c delete mode 100644 apps/app_directed_pickup.c delete mode 100644 apps/app_directory.c delete mode 100644 apps/app_disa.c delete mode 100644 apps/app_dumpchan.c delete mode 100644 apps/app_echo.c delete mode 100644 apps/app_exec.c delete mode 100644 apps/app_externalivr.c delete mode 100644 apps/app_festival.c delete mode 100644 apps/app_flash.c delete mode 100644 apps/app_followme.c delete mode 100644 apps/app_forkcdr.c delete mode 100644 apps/app_getcpeid.c delete mode 100644 apps/app_hasnewvoicemail.c delete mode 100644 apps/app_ices.c delete mode 100644 apps/app_image.c delete mode 100644 apps/app_ivrdemo.c delete mode 100644 apps/app_lookupblacklist.c delete mode 100644 apps/app_lookupcidname.c delete mode 100644 apps/app_macro.c delete mode 100644 apps/app_meetme.c delete mode 100644 apps/app_milliwatt.c delete mode 100644 apps/app_mixmonitor.c delete mode 100644 apps/app_morsecode.c delete mode 100644 apps/app_mp3.c delete mode 100644 apps/app_nbscat.c delete mode 100644 apps/app_osplookup.c delete mode 100644 apps/app_page.c delete mode 100644 apps/app_parkandannounce.c delete mode 100644 apps/app_playback.c delete mode 100644 apps/app_privacy.c delete mode 100644 apps/app_queue.c delete mode 100644 apps/app_random.c delete mode 100644 apps/app_read.c delete mode 100644 apps/app_readfile.c delete mode 100644 apps/app_realtime.c delete mode 100644 apps/app_record.c delete mode 100644 apps/app_rpt.c delete mode 100644 apps/app_sayunixtime.c delete mode 100644 apps/app_senddtmf.c delete mode 100644 apps/app_sendtext.c delete mode 100644 apps/app_setcallerid.c delete mode 100644 apps/app_setcdruserfield.c delete mode 100644 apps/app_settransfercapability.c delete mode 100644 apps/app_skel.c delete mode 100644 apps/app_sms.c delete mode 100644 apps/app_softhangup.c delete mode 100644 apps/app_speech_utils.c delete mode 100644 apps/app_stack.c delete mode 100644 apps/app_system.c delete mode 100644 apps/app_talkdetect.c delete mode 100644 apps/app_test.c delete mode 100644 apps/app_transfer.c delete mode 100644 apps/app_url.c delete mode 100644 apps/app_userevent.c delete mode 100644 apps/app_verbose.c delete mode 100644 apps/app_voicemail.c delete mode 100644 apps/app_waitforring.c delete mode 100644 apps/app_waitforsilence.c delete mode 100644 apps/app_while.c delete mode 100644 apps/app_zapateller.c delete mode 100644 apps/enter.h delete mode 100644 apps/leave.h delete mode 100644 apps/rpt_flow.pdf delete mode 100644 autoconf/acx_pthread.m4 delete mode 100644 autoconf/ast_c_compile_check.m4 delete mode 100644 autoconf/ast_c_define_check.m4 delete mode 100644 autoconf/ast_check_gnu_make.m4 delete mode 100644 autoconf/ast_check_openh323.m4 delete mode 100644 autoconf/ast_check_pwlib.m4 delete mode 100644 autoconf/ast_ext_lib.m4 delete mode 100644 autoconf/ast_func_fork.m4 delete mode 100644 autoconf/ast_gcc_attribute.m4 delete mode 100644 autoconf/ast_prog_egrep.m4 delete mode 100644 autoconf/ast_prog_ld.m4 delete mode 100644 autoconf/ast_prog_ld_gnu.m4 delete mode 100644 autoconf/ast_prog_sed.m4 delete mode 100755 bootstrap.sh delete mode 100644 build_tools/cflags-devmode.xml delete mode 100644 build_tools/cflags.xml delete mode 100644 build_tools/embed_modules.xml delete mode 100644 build_tools/get_makeopts delete mode 100644 build_tools/get_moduleinfo delete mode 100755 build_tools/make_build_h delete mode 100755 build_tools/make_buildopts_h delete mode 100755 build_tools/make_defaults_h delete mode 100755 build_tools/make_sample_voicemail delete mode 100755 build_tools/make_version delete mode 100755 build_tools/make_version_h delete mode 100644 build_tools/menuselect-deps.in delete mode 100755 build_tools/mkpkgconfig delete mode 100755 build_tools/prep_tarball delete mode 100755 build_tools/strip_nonapi delete mode 100644 cdr/Makefile delete mode 100644 cdr/cdr_csv.c delete mode 100644 cdr/cdr_custom.c delete mode 100644 cdr/cdr_manager.c delete mode 100644 cdr/cdr_odbc.c delete mode 100644 cdr/cdr_pgsql.c delete mode 100644 cdr/cdr_radius.c delete mode 100644 cdr/cdr_sqlite.c delete mode 100644 cdr/cdr_tds.c delete mode 100644 channels/DialTone.h delete mode 100644 channels/Makefile delete mode 100644 channels/answer.h delete mode 100644 channels/busy_tone.h delete mode 100644 channels/chan_agent.c delete mode 100644 channels/chan_alsa.c delete mode 100644 channels/chan_dahdi.c delete mode 100644 channels/chan_features.c delete mode 100644 channels/chan_gtalk.c delete mode 100644 channels/chan_h323.c delete mode 100644 channels/chan_iax2.c delete mode 100644 channels/chan_local.c delete mode 100644 channels/chan_mgcp.c delete mode 100644 channels/chan_misdn.c delete mode 100644 channels/chan_nbs.c delete mode 100644 channels/chan_oss.c delete mode 100644 channels/chan_phone.c delete mode 100644 channels/chan_sip.c delete mode 100644 channels/chan_skinny.c delete mode 100644 channels/chan_vpb.cc delete mode 100644 channels/gentone-ulaw.c delete mode 100644 channels/gentone.c delete mode 100644 channels/h323/ChangeLog delete mode 100644 channels/h323/INSTALL.openh323 delete mode 100644 channels/h323/Makefile.in delete mode 100644 channels/h323/README delete mode 100644 channels/h323/TODO delete mode 100644 channels/h323/ast_h323.cxx delete mode 100644 channels/h323/ast_h323.h delete mode 100644 channels/h323/caps_h323.cxx delete mode 100644 channels/h323/caps_h323.h delete mode 100644 channels/h323/chan_h323.h delete mode 100644 channels/h323/cisco-h225.asn delete mode 100644 channels/h323/cisco-h225.cxx delete mode 100644 channels/h323/cisco-h225.h delete mode 100644 channels/h323/compat_h323.cxx delete mode 100644 channels/h323/compat_h323.h delete mode 100644 channels/h323/noexport.map delete mode 100644 channels/iax2-parser.c delete mode 100644 channels/iax2-parser.h delete mode 100644 channels/iax2-provision.c delete mode 100644 channels/iax2-provision.h delete mode 100644 channels/iax2.h delete mode 100644 channels/misdn/Makefile delete mode 100644 channels/misdn/chan_misdn_config.h delete mode 100644 channels/misdn/ie.c delete mode 100644 channels/misdn/isdn_lib.c delete mode 100644 channels/misdn/isdn_lib.h delete mode 100644 channels/misdn/isdn_lib_intern.h delete mode 100644 channels/misdn/isdn_msg_parser.c delete mode 100644 channels/misdn/portinfo.c delete mode 100644 channels/misdn_config.c delete mode 100644 channels/ring10.h delete mode 100644 channels/ring_tone.h delete mode 100644 codecs/Makefile delete mode 100644 codecs/adpcm_slin_ex.h delete mode 100644 codecs/codec_a_mu.c delete mode 100644 codecs/codec_adpcm.c delete mode 100644 codecs/codec_alaw.c delete mode 100644 codecs/codec_dahdi.c delete mode 100644 codecs/codec_g726.c delete mode 100644 codecs/codec_gsm.c delete mode 100644 codecs/codec_ilbc.c delete mode 100644 codecs/codec_lpc10.c delete mode 100644 codecs/codec_speex.c delete mode 100644 codecs/codec_ulaw.c delete mode 100644 codecs/g726_slin_ex.h delete mode 100644 codecs/gsm/COPYRIGHT delete mode 100644 codecs/gsm/Makefile delete mode 100644 codecs/gsm/README delete mode 100644 codecs/gsm/inc/config.h delete mode 100644 codecs/gsm/inc/gsm.h delete mode 100644 codecs/gsm/inc/private.h delete mode 100644 codecs/gsm/inc/proto.h delete mode 100644 codecs/gsm/inc/unproto.h delete mode 100644 codecs/gsm/libgsm.vcproj delete mode 100644 codecs/gsm/src/add.c delete mode 100644 codecs/gsm/src/code.c delete mode 100644 codecs/gsm/src/debug.c delete mode 100644 codecs/gsm/src/decode.c delete mode 100644 codecs/gsm/src/gsm_create.c delete mode 100644 codecs/gsm/src/gsm_decode.c delete mode 100644 codecs/gsm/src/gsm_destroy.c delete mode 100644 codecs/gsm/src/gsm_encode.c delete mode 100644 codecs/gsm/src/gsm_explode.c delete mode 100644 codecs/gsm/src/gsm_implode.c delete mode 100644 codecs/gsm/src/gsm_option.c delete mode 100644 codecs/gsm/src/gsm_print.c delete mode 100644 codecs/gsm/src/k6opt.h delete mode 100644 codecs/gsm/src/k6opt.s delete mode 100644 codecs/gsm/src/long_term.c delete mode 100644 codecs/gsm/src/lpc.c delete mode 100644 codecs/gsm/src/preprocess.c delete mode 100644 codecs/gsm/src/rpe.c delete mode 100644 codecs/gsm/src/short_term.c delete mode 100644 codecs/gsm/src/table.c delete mode 100644 codecs/gsm_slin_ex.h delete mode 100644 codecs/ilbc/Makefile delete mode 100644 codecs/ilbc_slin_ex.h delete mode 100644 codecs/log2comp.h delete mode 100644 codecs/lpc10/Makefile delete mode 100644 codecs/lpc10/README delete mode 100644 codecs/lpc10/analys.c delete mode 100644 codecs/lpc10/bsynz.c delete mode 100644 codecs/lpc10/chanwr.c delete mode 100644 codecs/lpc10/dcbias.c delete mode 100644 codecs/lpc10/decode.c delete mode 100644 codecs/lpc10/deemp.c delete mode 100644 codecs/lpc10/difmag.c delete mode 100644 codecs/lpc10/dyptrk.c delete mode 100644 codecs/lpc10/encode.c delete mode 100644 codecs/lpc10/energy.c delete mode 100644 codecs/lpc10/f2c.h delete mode 100644 codecs/lpc10/f2clib.c delete mode 100644 codecs/lpc10/ham84.c delete mode 100644 codecs/lpc10/hp100.c delete mode 100644 codecs/lpc10/invert.c delete mode 100644 codecs/lpc10/irc2pc.c delete mode 100644 codecs/lpc10/ivfilt.c delete mode 100644 codecs/lpc10/liblpc10.vcproj delete mode 100644 codecs/lpc10/lpc10.h delete mode 100644 codecs/lpc10/lpcdec.c delete mode 100644 codecs/lpc10/lpcenc.c delete mode 100644 codecs/lpc10/lpcini.c delete mode 100644 codecs/lpc10/lpfilt.c delete mode 100644 codecs/lpc10/median.c delete mode 100644 codecs/lpc10/mload.c delete mode 100644 codecs/lpc10/onset.c delete mode 100644 codecs/lpc10/pitsyn.c delete mode 100644 codecs/lpc10/placea.c delete mode 100644 codecs/lpc10/placev.c delete mode 100644 codecs/lpc10/preemp.c delete mode 100644 codecs/lpc10/prepro.c delete mode 100644 codecs/lpc10/random.c delete mode 100644 codecs/lpc10/rcchk.c delete mode 100644 codecs/lpc10/synths.c delete mode 100644 codecs/lpc10/tbdm.c delete mode 100644 codecs/lpc10/voicin.c delete mode 100644 codecs/lpc10/vparms.c delete mode 100644 codecs/lpc10_slin_ex.h delete mode 100644 codecs/slin_adpcm_ex.h delete mode 100644 codecs/slin_g726_ex.h delete mode 100644 codecs/slin_gsm_ex.h delete mode 100644 codecs/slin_ilbc_ex.h delete mode 100644 codecs/slin_lpc10_ex.h delete mode 100644 codecs/slin_speex_ex.h delete mode 100644 codecs/slin_ulaw_ex.h delete mode 100644 codecs/speex_slin_ex.h delete mode 100644 codecs/ulaw_slin_ex.h delete mode 100755 config.guess delete mode 100755 config.sub delete mode 100644 configs/adsi.conf.sample delete mode 100644 configs/adtranvofr.conf.sample delete mode 100644 configs/agents.conf.sample delete mode 100644 configs/alarmreceiver.conf.sample delete mode 100644 configs/alsa.conf.sample delete mode 100644 configs/amd.conf.sample delete mode 100644 configs/asterisk.adsi delete mode 100644 configs/cdr.conf.sample delete mode 100644 configs/cdr_custom.conf.sample delete mode 100644 configs/cdr_manager.conf.sample delete mode 100644 configs/cdr_odbc.conf.sample delete mode 100644 configs/cdr_pgsql.conf.sample delete mode 100644 configs/cdr_tds.conf.sample delete mode 100644 configs/chan_dahdi.conf.sample delete mode 100644 configs/codecs.conf.sample delete mode 100644 configs/dnsmgr.conf.sample delete mode 100644 configs/dundi.conf.sample delete mode 100644 configs/enum.conf.sample delete mode 100644 configs/extconfig.conf.sample delete mode 100644 configs/extensions.ael.sample delete mode 100644 configs/extensions.conf.sample delete mode 100644 configs/features.conf.sample delete mode 100644 configs/festival.conf.sample delete mode 100644 configs/followme.conf.sample delete mode 100644 configs/func_odbc.conf.sample delete mode 100644 configs/gtalk.conf.sample delete mode 100644 configs/h323.conf.sample delete mode 100644 configs/http.conf.sample delete mode 100644 configs/iax.conf.sample delete mode 100644 configs/iaxprov.conf.sample delete mode 100644 configs/indications.conf.sample delete mode 100644 configs/jabber.conf.sample delete mode 100644 configs/logger.conf.sample delete mode 100644 configs/manager.conf.sample delete mode 100644 configs/meetme.conf.sample delete mode 100644 configs/mgcp.conf.sample delete mode 100644 configs/misdn.conf.sample delete mode 100644 configs/modules.conf.sample delete mode 100644 configs/musiconhold.conf.sample delete mode 100644 configs/muted.conf.sample delete mode 100644 configs/osp.conf.sample delete mode 100644 configs/oss.conf.sample delete mode 100644 configs/phone.conf.sample delete mode 100644 configs/privacy.conf.sample delete mode 100644 configs/queues.conf.sample delete mode 100644 configs/res_odbc.conf.sample delete mode 100644 configs/res_pgsql.conf.sample delete mode 100644 configs/res_snmp.conf.sample delete mode 100644 configs/rpt.conf.sample delete mode 100644 configs/rtp.conf.sample delete mode 100644 configs/say.conf.sample delete mode 100644 configs/sip.conf.sample delete mode 100644 configs/sip_notify.conf.sample delete mode 100644 configs/skinny.conf.sample delete mode 100644 configs/sla.conf.sample delete mode 100644 configs/smdi.conf.sample delete mode 100644 configs/telcordia-1.adsi delete mode 100644 configs/udptl.conf.sample delete mode 100644 configs/users.conf.sample delete mode 100644 configs/voicemail.conf.sample delete mode 100644 configs/vpb.conf.sample delete mode 100755 configure delete mode 100644 configure.ac delete mode 100644 contrib/README.festival delete mode 100644 contrib/asterisk-doxygen-header delete mode 100644 contrib/asterisk-ices.xml delete mode 100644 contrib/asterisk-ng-doxygen delete mode 100644 contrib/dictionary.digium delete mode 100644 contrib/festival-1.4.1-diff delete mode 100644 contrib/festival-1.4.2.diff delete mode 100644 contrib/festival-1.4.3.diff delete mode 100644 contrib/festival-1.95.diff delete mode 100644 contrib/firmware/iax/iaxy.bin delete mode 100644 contrib/i18n.testsuite.conf delete mode 100755 contrib/init.d/rc.debian.asterisk delete mode 100755 contrib/init.d/rc.gentoo.asterisk delete mode 100755 contrib/init.d/rc.mandrake.asterisk delete mode 100755 contrib/init.d/rc.mandrake.zaptel delete mode 100755 contrib/init.d/rc.redhat.asterisk delete mode 100755 contrib/init.d/rc.slackware.asterisk delete mode 100755 contrib/init.d/rc.suse.asterisk delete mode 100644 contrib/scripts/README.messages-expire delete mode 100644 contrib/scripts/agents.php delete mode 100644 contrib/scripts/ast_grab_core delete mode 100644 contrib/scripts/astgenkey delete mode 100644 contrib/scripts/astgenkey.8 delete mode 100644 contrib/scripts/autosupport delete mode 100644 contrib/scripts/autosupport.8 delete mode 100755 contrib/scripts/get_ilbc_source.sh delete mode 100644 contrib/scripts/iax-friends.sql delete mode 100644 contrib/scripts/loadtest.tcl delete mode 100644 contrib/scripts/lookup.agi delete mode 100644 contrib/scripts/managerproxy.pl delete mode 100644 contrib/scripts/meetme.sql delete mode 100644 contrib/scripts/messages-expire.pl delete mode 100644 contrib/scripts/qview.pl delete mode 100644 contrib/scripts/realtime_pgsql.sql delete mode 100644 contrib/scripts/retrieve_extensions_from_mysql.pl delete mode 100644 contrib/scripts/retrieve_extensions_from_sql.pl delete mode 100644 contrib/scripts/retrieve_sip_conf_from_mysql.pl delete mode 100644 contrib/scripts/safe_asterisk delete mode 100644 contrib/scripts/safe_asterisk.8 delete mode 100644 contrib/scripts/safe_asterisk_restart delete mode 100644 contrib/scripts/sip-friends.sql delete mode 100644 contrib/scripts/vmail.cgi delete mode 100644 contrib/scripts/vmdb.sql delete mode 100644 contrib/thirdparty/spexxilbcfix_xlite.reg delete mode 100644 contrib/thirdparty/spexxilbcfix_xpro.reg delete mode 100644 contrib/utils/README.rawplayer delete mode 100644 contrib/utils/rawplayer.c delete mode 100644 contrib/utils/zones2indications.c delete mode 100644 contrib/valgrind-RedHat-8.0.supp delete mode 100644 doc/00README.1st delete mode 100644 doc/CODING-GUIDELINES delete mode 100644 doc/PEERING delete mode 100644 doc/ael.txt delete mode 100644 doc/ajam.txt delete mode 100644 doc/app-sms.txt delete mode 100644 doc/apps.txt delete mode 100644 doc/asterisk-conf.txt delete mode 100644 doc/asterisk-mib.txt delete mode 100644 doc/asterisk.8 delete mode 100644 doc/asterisk.sgml delete mode 100644 doc/backtrace.txt delete mode 100644 doc/billing.txt delete mode 100644 doc/callfiles.txt delete mode 100644 doc/callingpres.txt delete mode 100644 doc/cdrdriver.txt delete mode 100644 doc/chaniax.txt delete mode 100644 doc/channels.txt delete mode 100644 doc/channelvariables.txt delete mode 100644 doc/cli.txt delete mode 100644 doc/cliprompt.txt delete mode 100644 doc/configuration.txt delete mode 100644 doc/cygwin.txt delete mode 100644 doc/datastores.txt delete mode 100644 doc/digium-mib.txt delete mode 100644 doc/dundi.txt delete mode 100644 doc/enum.txt delete mode 100644 doc/extconfig.txt delete mode 100644 doc/extensions.txt delete mode 100644 doc/externalivr.txt delete mode 100644 doc/freetds.txt delete mode 100644 doc/h323.txt delete mode 100644 doc/hardware.txt delete mode 100644 doc/iax.txt delete mode 100644 doc/ices.txt delete mode 100644 doc/imapstorage.txt delete mode 100644 doc/ip-tos.txt delete mode 100644 doc/jabber.txt delete mode 100644 doc/jingle.txt delete mode 100644 doc/jitterbuffer.txt delete mode 100644 doc/lang/hebrew.ods delete mode 100644 doc/linkedlists.txt delete mode 100644 doc/localchannel.txt delete mode 100644 doc/macroexclusive.txt delete mode 100644 doc/manager.txt delete mode 100644 doc/math.txt delete mode 100644 doc/misdn.txt delete mode 100644 doc/model.txt delete mode 100644 doc/modules.txt delete mode 100644 doc/mp3.txt delete mode 100644 doc/musiconhold-fpm.txt delete mode 100644 doc/mysql.txt delete mode 100644 doc/odbcstorage.txt delete mode 100644 doc/osp.txt delete mode 100644 doc/privacy.txt delete mode 100644 doc/queuelog.txt delete mode 100644 doc/queues-with-callback-members.txt delete mode 100644 doc/radius.txt delete mode 100644 doc/realtime.txt delete mode 100644 doc/rtp-packetization.txt delete mode 100644 doc/security.txt delete mode 100644 doc/sip-retransmit.txt delete mode 100644 doc/sla.pdf delete mode 100644 doc/sla.tex delete mode 100644 doc/smdi.txt delete mode 100644 doc/sms.txt delete mode 100644 doc/snmp.txt delete mode 100644 doc/speechrec.txt delete mode 100644 doc/valgrind.txt delete mode 100644 doc/video.txt delete mode 100644 doc/voicemail_odbc_postgresql.txt delete mode 100644 formats/Makefile delete mode 100644 formats/format_g723.c delete mode 100644 formats/format_g726.c delete mode 100644 formats/format_g729.c delete mode 100644 formats/format_gsm.c delete mode 100644 formats/format_h263.c delete mode 100644 formats/format_h264.c delete mode 100644 formats/format_ilbc.c delete mode 100644 formats/format_jpeg.c delete mode 100644 formats/format_ogg_vorbis.c delete mode 100644 formats/format_pcm.c delete mode 100644 formats/format_sln.c delete mode 100644 formats/format_vox.c delete mode 100644 formats/format_wav.c delete mode 100644 formats/format_wav_gsm.c delete mode 100644 formats/msgsm.h delete mode 100644 funcs/Makefile delete mode 100644 funcs/func_audiohookinherit.c delete mode 100644 funcs/func_base64.c delete mode 100644 funcs/func_callerid.c delete mode 100644 funcs/func_cdr.c delete mode 100644 funcs/func_channel.c delete mode 100644 funcs/func_curl.c delete mode 100644 funcs/func_cut.c delete mode 100644 funcs/func_db.c delete mode 100644 funcs/func_enum.c delete mode 100644 funcs/func_env.c delete mode 100644 funcs/func_global.c delete mode 100644 funcs/func_groupcount.c delete mode 100644 funcs/func_language.c delete mode 100644 funcs/func_logic.c delete mode 100644 funcs/func_math.c delete mode 100644 funcs/func_md5.c delete mode 100644 funcs/func_moh.c delete mode 100644 funcs/func_odbc.c delete mode 100644 funcs/func_rand.c delete mode 100644 funcs/func_realtime.c delete mode 100644 funcs/func_sha1.c delete mode 100644 funcs/func_strings.c delete mode 100644 funcs/func_timeout.c delete mode 100644 funcs/func_uri.c delete mode 100644 images/animlogo.gif delete mode 100644 images/asterisk-intro.jpg delete mode 100644 images/play.gif delete mode 100644 include/asterisk.h delete mode 100644 include/asterisk/abstract_jb.h delete mode 100644 include/asterisk/acl.h delete mode 100644 include/asterisk/adsi.h delete mode 100644 include/asterisk/ael_structs.h delete mode 100644 include/asterisk/aes.h delete mode 100644 include/asterisk/agi.h delete mode 100644 include/asterisk/alaw.h delete mode 100644 include/asterisk/app.h delete mode 100644 include/asterisk/ast_expr.h delete mode 100644 include/asterisk/astdb.h delete mode 100644 include/asterisk/astmm.h delete mode 100644 include/asterisk/astobj.h delete mode 100644 include/asterisk/astobj2.h delete mode 100644 include/asterisk/astosp.h delete mode 100644 include/asterisk/audiohook.h delete mode 100644 include/asterisk/autoconfig.h.in delete mode 100644 include/asterisk/callerid.h delete mode 100644 include/asterisk/causes.h delete mode 100644 include/asterisk/cdr.h delete mode 100644 include/asterisk/channel.h delete mode 100644 include/asterisk/chanvars.h delete mode 100644 include/asterisk/cli.h delete mode 100644 include/asterisk/compat.h delete mode 100644 include/asterisk/compiler.h delete mode 100644 include/asterisk/config.h delete mode 100644 include/asterisk/crypto.h delete mode 100644 include/asterisk/dahdi_compat.h delete mode 100644 include/asterisk/devicestate.h delete mode 100644 include/asterisk/dial.h delete mode 100644 include/asterisk/dns.h delete mode 100644 include/asterisk/dnsmgr.h delete mode 100644 include/asterisk/doxyref.h delete mode 100644 include/asterisk/dsp.h delete mode 100644 include/asterisk/dundi.h delete mode 100644 include/asterisk/endian.h delete mode 100644 include/asterisk/enum.h delete mode 100644 include/asterisk/features.h delete mode 100644 include/asterisk/file.h delete mode 100644 include/asterisk/frame.h delete mode 100644 include/asterisk/fskmodem.h delete mode 100644 include/asterisk/global_datastores.h delete mode 100644 include/asterisk/http.h delete mode 100644 include/asterisk/image.h delete mode 100644 include/asterisk/indications.h delete mode 100644 include/asterisk/inline_api.h delete mode 100644 include/asterisk/io.h delete mode 100644 include/asterisk/jabber.h delete mode 100644 include/asterisk/jingle.h delete mode 100644 include/asterisk/linkedlists.h delete mode 100644 include/asterisk/localtime.h delete mode 100644 include/asterisk/lock.h delete mode 100644 include/asterisk/logger.h delete mode 100644 include/asterisk/manager.h delete mode 100644 include/asterisk/md5.h delete mode 100644 include/asterisk/module.h delete mode 100644 include/asterisk/monitor.h delete mode 100644 include/asterisk/musiconhold.h delete mode 100644 include/asterisk/netsock.h delete mode 100644 include/asterisk/options.h delete mode 100644 include/asterisk/paths.h delete mode 100644 include/asterisk/pbx.h delete mode 100644 include/asterisk/plc.h delete mode 100644 include/asterisk/poll-compat.h delete mode 100644 include/asterisk/privacy.h delete mode 100644 include/asterisk/res_odbc.h delete mode 100644 include/asterisk/rtp.h delete mode 100644 include/asterisk/say.h delete mode 100644 include/asterisk/sched.h delete mode 100644 include/asterisk/sha1.h delete mode 100644 include/asterisk/slinfactory.h delete mode 100644 include/asterisk/smdi.h delete mode 100644 include/asterisk/speech.h delete mode 100644 include/asterisk/srv.h delete mode 100644 include/asterisk/stringfields.h delete mode 100644 include/asterisk/strings.h delete mode 100644 include/asterisk/tdd.h delete mode 100644 include/asterisk/term.h delete mode 100644 include/asterisk/threadstorage.h delete mode 100644 include/asterisk/time.h delete mode 100644 include/asterisk/tonezone_compat.h delete mode 100644 include/asterisk/transcap.h delete mode 100644 include/asterisk/translate.h delete mode 100644 include/asterisk/udptl.h delete mode 100644 include/asterisk/ulaw.h delete mode 100644 include/asterisk/unaligned.h delete mode 100644 include/asterisk/utils.h delete mode 100644 include/jitterbuf.h delete mode 100644 include/solaris-compat/compat.h delete mode 100644 include/solaris-compat/sys/cdefs.h delete mode 100644 include/solaris-compat/sys/queue.h delete mode 100755 install-sh delete mode 100644 keys/freeworlddialup.pub delete mode 100644 keys/iaxtel.pub delete mode 100644 main/Makefile delete mode 100644 main/abstract_jb.c delete mode 100644 main/acl.c delete mode 100644 main/aescrypt.c delete mode 100644 main/aeskey.c delete mode 100644 main/aesopt.h delete mode 100644 main/aestab.c delete mode 100644 main/alaw.c delete mode 100644 main/app.c delete mode 100644 main/ast_expr2.c delete mode 100644 main/ast_expr2.fl delete mode 100644 main/ast_expr2.h delete mode 100644 main/ast_expr2.y delete mode 100644 main/ast_expr2f.c delete mode 100644 main/asterisk.c delete mode 100644 main/astmm.c delete mode 100644 main/astobj2.c delete mode 100644 main/audiohook.c delete mode 100644 main/autoservice.c delete mode 100644 main/buildinfo.c delete mode 100644 main/callerid.c delete mode 100644 main/cdr.c delete mode 100644 main/channel.c delete mode 100644 main/chanvars.c delete mode 100644 main/cli.c delete mode 100644 main/coef_in.h delete mode 100644 main/coef_out.h delete mode 100644 main/config.c delete mode 100644 main/cryptostub.c delete mode 100644 main/db.c delete mode 100644 main/db1-ast/Makefile delete mode 100644 main/db1-ast/btree/bt_close.c delete mode 100644 main/db1-ast/btree/bt_conv.c delete mode 100644 main/db1-ast/btree/bt_debug.c delete mode 100644 main/db1-ast/btree/bt_delete.c delete mode 100644 main/db1-ast/btree/bt_get.c delete mode 100644 main/db1-ast/btree/bt_open.c delete mode 100644 main/db1-ast/btree/bt_overflow.c delete mode 100644 main/db1-ast/btree/bt_page.c delete mode 100644 main/db1-ast/btree/bt_put.c delete mode 100644 main/db1-ast/btree/bt_search.c delete mode 100644 main/db1-ast/btree/bt_seq.c delete mode 100644 main/db1-ast/btree/bt_split.c delete mode 100644 main/db1-ast/btree/bt_utils.c delete mode 100644 main/db1-ast/btree/btree.h delete mode 100644 main/db1-ast/btree/extern.h delete mode 100644 main/db1-ast/db/db.c delete mode 100644 main/db1-ast/hash/README delete mode 100644 main/db1-ast/hash/extern.h delete mode 100644 main/db1-ast/hash/hash.c delete mode 100644 main/db1-ast/hash/hash.h delete mode 100644 main/db1-ast/hash/hash_bigkey.c delete mode 100644 main/db1-ast/hash/hash_buf.c delete mode 100644 main/db1-ast/hash/hash_func.c delete mode 100644 main/db1-ast/hash/hash_log2.c delete mode 100644 main/db1-ast/hash/hash_page.c delete mode 100644 main/db1-ast/hash/hsearch.c delete mode 100644 main/db1-ast/hash/ndbm.c delete mode 100644 main/db1-ast/hash/page.h delete mode 100644 main/db1-ast/hash/search.h delete mode 100644 main/db1-ast/include/circ-queue.h delete mode 100644 main/db1-ast/include/compat.h delete mode 100644 main/db1-ast/include/db.h delete mode 100644 main/db1-ast/include/mpool.h delete mode 100644 main/db1-ast/include/ndbm.h delete mode 100644 main/db1-ast/libdb.map delete mode 100644 main/db1-ast/mpool/README delete mode 100644 main/db1-ast/mpool/mpool.c delete mode 100644 main/db1-ast/recno/extern.h delete mode 100644 main/db1-ast/recno/rec_close.c delete mode 100644 main/db1-ast/recno/rec_delete.c delete mode 100644 main/db1-ast/recno/rec_get.c delete mode 100644 main/db1-ast/recno/rec_open.c delete mode 100644 main/db1-ast/recno/rec_put.c delete mode 100644 main/db1-ast/recno/rec_search.c delete mode 100644 main/db1-ast/recno/rec_seq.c delete mode 100644 main/db1-ast/recno/rec_utils.c delete mode 100644 main/db1-ast/recno/recno.h delete mode 100644 main/devicestate.c delete mode 100644 main/dial.c delete mode 100644 main/dns.c delete mode 100644 main/dnsmgr.c delete mode 100644 main/dsp.c delete mode 100644 main/ecdisa.h delete mode 100644 main/editline/CHANGES delete mode 100644 main/editline/INSTALL delete mode 100644 main/editline/Makefile.in delete mode 100644 main/editline/PLATFORMS delete mode 100644 main/editline/README delete mode 100644 main/editline/TEST/test.c delete mode 100644 main/editline/chared.c delete mode 100644 main/editline/chared.h delete mode 100644 main/editline/common.c delete mode 100755 main/editline/config.guess delete mode 100644 main/editline/config.h.in delete mode 100755 main/editline/config.sub delete mode 100755 main/editline/configure delete mode 100644 main/editline/configure.in delete mode 100644 main/editline/editline.3 delete mode 100644 main/editline/editrc.5 delete mode 100644 main/editline/el.c delete mode 100644 main/editline/el.h delete mode 100644 main/editline/emacs.c delete mode 100644 main/editline/hist.c delete mode 100644 main/editline/hist.h delete mode 100644 main/editline/histedit.h delete mode 100644 main/editline/history.c delete mode 100755 main/editline/install-sh delete mode 100644 main/editline/key.c delete mode 100644 main/editline/key.h delete mode 100644 main/editline/makelist.in delete mode 100644 main/editline/map.c delete mode 100644 main/editline/map.h delete mode 100644 main/editline/np/fgetln.c delete mode 100644 main/editline/np/strlcat.c delete mode 100644 main/editline/np/strlcpy.c delete mode 100644 main/editline/np/unvis.c delete mode 100644 main/editline/np/vis.c delete mode 100644 main/editline/np/vis.h delete mode 100644 main/editline/parse.c delete mode 100644 main/editline/parse.h delete mode 100644 main/editline/prompt.c delete mode 100644 main/editline/prompt.h delete mode 100644 main/editline/read.c delete mode 100644 main/editline/read.h delete mode 100644 main/editline/readline.c delete mode 100644 main/editline/readline/readline.h delete mode 100644 main/editline/refresh.c delete mode 100644 main/editline/refresh.h delete mode 100644 main/editline/search.c delete mode 100644 main/editline/search.h delete mode 100644 main/editline/sig.c delete mode 100644 main/editline/sig.h delete mode 100644 main/editline/sys.h delete mode 100644 main/editline/term.c delete mode 100644 main/editline/term.h delete mode 100644 main/editline/tokenizer.c delete mode 100644 main/editline/tokenizer.h delete mode 100644 main/editline/tty.c delete mode 100644 main/editline/tty.h delete mode 100644 main/editline/vi.c delete mode 100644 main/enum.c delete mode 100644 main/file.c delete mode 100644 main/fixedjitterbuf.c delete mode 100644 main/fixedjitterbuf.h delete mode 100644 main/frame.c delete mode 100644 main/fskmodem.c delete mode 100644 main/global_datastores.c delete mode 100644 main/http.c delete mode 100644 main/image.c delete mode 100644 main/indications.c delete mode 100644 main/io.c delete mode 100644 main/jitterbuf.c delete mode 100644 main/loader.c delete mode 100644 main/logger.c delete mode 100644 main/manager.c delete mode 100644 main/md5.c delete mode 100644 main/netsock.c delete mode 100644 main/pbx.c delete mode 100644 main/plc.c delete mode 100644 main/poll.c delete mode 100644 main/privacy.c delete mode 100644 main/rtp.c delete mode 100644 main/say.c delete mode 100644 main/sched.c delete mode 100644 main/sha1.c delete mode 100644 main/slinfactory.c delete mode 100644 main/srv.c delete mode 100644 main/stdtime/Makefile delete mode 100644 main/stdtime/localtime.c delete mode 100644 main/stdtime/private.h delete mode 100644 main/stdtime/test.c delete mode 100644 main/stdtime/tzfile.h delete mode 100644 main/strcompat.c delete mode 100644 main/tdd.c delete mode 100644 main/term.c delete mode 100644 main/threadstorage.c delete mode 100644 main/translate.c delete mode 100644 main/udptl.c delete mode 100644 main/ulaw.c delete mode 100644 main/utils.c delete mode 100644 makeopts.in delete mode 100755 missing delete mode 100755 mkinstalldirs delete mode 100644 pbx/Makefile delete mode 100644 pbx/ael/ael-test/ael-ntest10/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-ntest12/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/qq.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t1/a.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t1/b.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t1/c.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t2/d.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t2/e.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t2/f.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t3/g.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t3/h.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t3/i.ael delete mode 100644 pbx/ael/ael-test/ael-ntest22/t3/j.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/qq.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t1/a.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t1/b.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t1/c.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t2/d.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t2/e.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t2/f.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t3/g.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t3/h.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t3/i.ael delete mode 100644 pbx/ael/ael-test/ael-ntest23/t3/j.ael delete mode 100755 pbx/ael/ael-test/ael-ntest9/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test1/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test11/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test14/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test15/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test16/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test18/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test19/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test2/apptest.ael2 delete mode 100644 pbx/ael/ael-test/ael-test2/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test20/extensions.ael delete mode 100755 pbx/ael/ael-test/ael-test3/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test3/include1.ael2 delete mode 100644 pbx/ael/ael-test/ael-test3/include2.ael2 delete mode 100644 pbx/ael/ael-test/ael-test3/include3.ael2 delete mode 100644 pbx/ael/ael-test/ael-test3/include4.ael2 delete mode 100644 pbx/ael/ael-test/ael-test3/include5.ael2 delete mode 100755 pbx/ael/ael-test/ael-test3/telemarket_torture.ael2 delete mode 100644 pbx/ael/ael-test/ael-test4/apptest.ael2 delete mode 100644 pbx/ael/ael-test/ael-test4/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test5/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test6/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test7/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-test8/extensions.ael delete mode 100755 pbx/ael/ael-test/ael-vtest13/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-vtest13/include1.ael2 delete mode 100644 pbx/ael/ael-test/ael-vtest13/include2.ael2 delete mode 100644 pbx/ael/ael-test/ael-vtest13/include3.ael2 delete mode 100644 pbx/ael/ael-test/ael-vtest13/include4.ael2 delete mode 100644 pbx/ael/ael-test/ael-vtest13/include5.ael2 delete mode 100755 pbx/ael/ael-test/ael-vtest13/telemarket_torture.ael2 delete mode 100644 pbx/ael/ael-test/ael-vtest17/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-vtest21/extensions.ael delete mode 100644 pbx/ael/ael-test/ael-vtest25/extensions.ael delete mode 100644 pbx/ael/ael-test/ref.ael-ntest10 delete mode 100644 pbx/ael/ael-test/ref.ael-ntest12 delete mode 100644 pbx/ael/ael-test/ref.ael-ntest22 delete mode 100644 pbx/ael/ael-test/ref.ael-ntest23 delete mode 100644 pbx/ael/ael-test/ref.ael-ntest9 delete mode 100644 pbx/ael/ael-test/ref.ael-test1 delete mode 100644 pbx/ael/ael-test/ref.ael-test11 delete mode 100644 pbx/ael/ael-test/ref.ael-test14 delete mode 100644 pbx/ael/ael-test/ref.ael-test15 delete mode 100644 pbx/ael/ael-test/ref.ael-test16 delete mode 100644 pbx/ael/ael-test/ref.ael-test18 delete mode 100644 pbx/ael/ael-test/ref.ael-test19 delete mode 100644 pbx/ael/ael-test/ref.ael-test2 delete mode 100644 pbx/ael/ael-test/ref.ael-test20 delete mode 100644 pbx/ael/ael-test/ref.ael-test3 delete mode 100644 pbx/ael/ael-test/ref.ael-test4 delete mode 100644 pbx/ael/ael-test/ref.ael-test5 delete mode 100644 pbx/ael/ael-test/ref.ael-test6 delete mode 100644 pbx/ael/ael-test/ref.ael-test7 delete mode 100644 pbx/ael/ael-test/ref.ael-test8 delete mode 100644 pbx/ael/ael-test/ref.ael-vtest13 delete mode 100644 pbx/ael/ael-test/ref.ael-vtest17 delete mode 100644 pbx/ael/ael-test/ref.ael-vtest21 delete mode 100644 pbx/ael/ael-test/ref.ael-vtest25 delete mode 100755 pbx/ael/ael-test/runtests delete mode 100755 pbx/ael/ael-test/setref delete mode 100644 pbx/ael/ael.flex delete mode 100644 pbx/ael/ael.tab.c delete mode 100644 pbx/ael/ael.tab.h delete mode 100644 pbx/ael/ael.y delete mode 100644 pbx/ael/ael_lex.c delete mode 100644 pbx/dundi-parser.c delete mode 100644 pbx/dundi-parser.h delete mode 100644 pbx/pbx_ael.c delete mode 100644 pbx/pbx_config.c delete mode 100644 pbx/pbx_dundi.c delete mode 100644 pbx/pbx_gtkconsole.c delete mode 100644 pbx/pbx_loopback.c delete mode 100644 pbx/pbx_realtime.c delete mode 100644 pbx/pbx_spool.c delete mode 100644 res/Makefile delete mode 100644 res/res_adsi.c delete mode 100644 res/res_agi.c delete mode 100644 res/res_clioriginate.c delete mode 100644 res/res_config_odbc.c delete mode 100644 res/res_config_pgsql.c delete mode 100644 res/res_convert.c delete mode 100644 res/res_crypto.c delete mode 100644 res/res_features.c delete mode 100644 res/res_indications.c delete mode 100644 res/res_jabber.c delete mode 100644 res/res_monitor.c delete mode 100644 res/res_musiconhold.c delete mode 100644 res/res_odbc.c delete mode 100644 res/res_smdi.c delete mode 100644 res/res_snmp.c delete mode 100644 res/res_speech.c delete mode 100644 res/snmp/agent.c delete mode 100644 res/snmp/agent.h delete mode 100644 sample.call delete mode 100644 sounds/Makefile delete mode 100644 sounds/sounds.xml delete mode 100644 static-http/ajamdemo.html delete mode 100644 static-http/astman.css delete mode 100644 static-http/astman.js delete mode 100644 static-http/prototype.js delete mode 100644 utils/Makefile delete mode 100644 utils/ael_main.c delete mode 100644 utils/astman.1 delete mode 100644 utils/astman.c delete mode 100644 utils/check_expr.c delete mode 100644 utils/expr2.testinput delete mode 100644 utils/frame.c delete mode 100644 utils/frame.h delete mode 100644 utils/muted.c delete mode 100644 utils/smsq.c delete mode 100644 utils/stereorize.c delete mode 100644 utils/streamplayer.c diff --git a/.cleancount b/.cleancount deleted file mode 100644 index bb95160cb..000000000 --- a/.cleancount +++ /dev/null @@ -1 +0,0 @@ -33 diff --git a/BUGS b/BUGS deleted file mode 100644 index 96b55e655..000000000 --- a/BUGS +++ /dev/null @@ -1,22 +0,0 @@ -Asterisk Bug Tracking Information -================================= - -To learn about and report Asterisk bugs, please visit -the official Asterisk Bug Tracker at: - - http://bugs.digium.com - -For more information on using the bug tracker, or to -learn how you can contribute by acting as a bug marshal -please see: - - http://www.asterisk.org/developers/bug-guidelines - -If you would like to submit a feature request, please -resist the temptation to post it to the bug tracker. -Feature requests should be posted to the asterisk-dev -mailing list, located at: - - http://lists.digium.com - -Thank you! diff --git a/CHANGES b/CHANGES deleted file mode 100644 index 699674279..000000000 --- a/CHANGES +++ /dev/null @@ -1,370 +0,0 @@ -Changes since Asterisk 1.2: - - * over 4,000 commits since 1.2 - * queue member naming - * CLI commands rework - o Change the way CLI commands are structured. - o Most commands are now - * chan_h323 update - * RTP packetization - * SLA (Shared Line Appearance) support - * T.38 Passthrough Support for faxing in SIP - * Generic channel jitterbuffer (spawned from RTP) - * Variable Length DTMF for better DTMF compatibility - * Improved chan_iax2 scalability by using multithreading - * AEL2 has replaced the original implementation of AEL. The "2" is removed. For more details, - read: http://www.voip-info.org/wiki/view/Asterisk+AEL2 - AEL is no longer considered experimental. - * New sounds; English, Spanish, and French prompts, as well as music on hold files, in - multiple Asterisk native formats. - * IMAP storage of voicemail - * Jabber/GoogleTalk integration - * New speech recognition API for interfacing to different Voice Recognition software packages - * much more customizable and portable build system - o also for asterisk-addons - * Radius CDR logging - * SNMP support - * SMDI (Simplified Message Desk Interface) support - * Redesign of MusicOnHold configuration settings - * Manager over HTTP - * Significant chan_skinny updates - * Significant chan_misdn updates - * Improved SIP transfers - * SIP MWI subscription support - * Much improved support for SIP video - * Control over SIP transfers and subscriptions (enable/disable per device) - * ChanSpy whisper mode (Whisper Paging) - * Configurable language support for saying dates and times - * Significant architecture improvements for memory usage and performance - * Media-only IAX2 transfers - * Updates to the Radio Repeater app code - * Deprecation of AgentCallbackLogin in favor of a dialplan-based solution - * uClibc builds supported - * Work done for freeBSD portability - * Work done for Solaris portability - * FreeTDS-based database can be used with Realtime - * New internal data structure, stringfields, is implemented in IAX and SIP, reducing memory consumption by about 50%. - * Use of thread local storage for reduced memory allocation/freeing and lower stack consumption - * Reorganized files into docs/ main/ configs/, including name changes in some cases - * Much effort was expended in arranging documentation in source files in doxygen format - * Improved IP TOS support for IAX and SIP - * Builtin mini HTTP server - * Added support for Sigma Designs cards. - * Frame header caching to reduce memory allocation/freeing - * Passthrough and record/playback support for G.722 wideband audio - * using mpg123 to play MP3 files for music-on-hold will be deprecated in 1.4 (start using the "native support") - * New Apps: - 1. AMD() ;; Answering Machine Detection - 2. ChannelRedirect() ;; asynch goto, redirect chan to context/exten/priority - 3. ContinueWhile() ;; Addition to the While() suite. Acts like "continue". - 4. ExitWhile() ;; Addition to the While() suite. Acts like "break". - 5. ExtenSpy() ;; A close cousin to ChanSpy(). - 6. FollowMe() ;; findme/followme call redirect app - 7. Log() ;; Send a message to the log, based on severity level. - 8. MacroExclusive() ;; No more than one invocation of this macro allowed at any one time. - 9. MorseCode() ;; turns strings into dits and dahs. A playground for ham radio licensees! - 10. OSPAuth() ;; OSP authentication - 11. QueueLog() ;; allows you to write your own events into the queue log - 12. SLAStation() ;; Shared Line Appearance - 13. SLATrunk() ;; Shared Line Appearance - 14. SpeechCreate() ;; Voice Recognition Engine interface... - 15. SpeechActivateGrammar() - 16. SpeechStart() - 17. SpeechBackground - 18. SpeechDeactivateGrammar() - 19. SpeechProcessingSound() - 20. SpeechDestroy() - 21. SpeechLoadGrammar() - 22. SpeechUnloadGrammar() - 23. StopMixMonitor() ;; to stop the MixMonitor App. - 24. TryExec() ;; execute dialplan app without fatal consequences - * Apps removed: - 1. CheckGroup -- do a comparison to ${GROUP()} - 2. Curl -- use the function CURL() instead - 3. Cut -- use the function CUT() instead - 4. DateTime -- use sayunixtime() app instead. - 5. DBget -- deprecated in 1.2, now removed. - 6. DBput -- deprecated in 1.2, now removed. - 7. Enumlookup -- use the function ENUMLOOKUP() instead - 8. Eval -- use the function EVAL() instead - 9. GetGroupCount -- use the function GROUP_COUNT() instead - 10. GetGroupMatchCount -- use the function GROUP_MATCH_COUNT() instead - 11. Intercom -- use the chan_oss module instead - 12. Math -- use the function MATH() instead - 13. MD5 -- use the function MD5() instead - 14. SetCIDname -- use the function CALLERID(name) instead - 15. SetCIDnum -- use the function CALLERID(number) instead - 16. SetGroup -- use Set(GROUP=group) instead - 17. SetRDNIS -- use the function CALLERID(rdnis) instead - 18. Sql_postgres -- was deprecated in 1.2, now removed - 19. Txtcidname -- use the function TXTCIDNAME instead - * New Dialplan Functions: - 1. ARRAY() - 2. BASE_64_DECODE() - 3. BASE_64_ENCODE() - 4. CHANNEL() - 5. CURL() - 6. CUT() - 7. DB_DELETE() - 8. FILTER() - 9. GLOBAL() - 10. IFTIME() - 11. KEYPADHASH() - 12. ODBC() - 13. QUOTE() - 14. RAND() - 15. REALTIME() - 16. SHA1() - 17. SORT() - 18. SPRINTF() - 19. SQL_ESC() - 20. STAT() - 21. STRPTIME() - 22. AUDIOHOOK_INHERIT() - * Apps that have changes to their interface: - 1. Authenticate() -- optional maxdigits argument added. - 2. ChanSpy() -- new options: - o w -- Enable 'whisper' mode, so the spying channel can talk to... - o W -- Enable 'private whisper' mode, so the spying channel can... - 3. DBdel() -- now marked as DEPRECATED in favor of the DB_DELETE func - 4. Dial() - o New Option: O([x]) for Zaptel operator mode - o New Option: K/k parking via dtmf tones - 5. Dictate() -- optional filename argument added. - 6. Directory() -- new option: e - In addition to the name, also read the extension number... - 7. ForkCDR() -- new options: - o 'a' -- update answer time on new cdr - o 'A' -- Lock the orig CDR answer time against changes. - o 'D' -- Copy the disposition from the orig to the new CDR. - o 'd' -- clear the dstcannel field in the new CDR. - o 'e' -- set the end time of the original CDR. - o 'R' -- do NOT reset the new CDR. - o 's' -- Add/change var in orig CDR. - o 'T' -- Force ast_cdr_end, answer to obey LOCKED flag for the orig. CDR. - -- ast_cdr_setvar will be forced also (used by the CDR() func in write mode) - 8. Meetme() -- new options: - o 'I' -- announce user join/leave without review - o 'l' -- set listen only mode (Listen only, no talking) - o 'o' -- set talker optimization - treats talkers who aren't speaking as... - o '1' -- do not play message when first person enters - 9. MeetmeAdmin() -- new options: - o 'r' -- Reset one user's volume settings - o 'R' -- Reset all users volume settings - o 's' -- Lower entire conference speaking volume - o 'S' -- Raise entire conference speaking volume - o 't' -- Lower one user's talk volume - o 'T' -- Lower all users talk volume - o 'u' -- Lower one user's listen volume - o 'U' -- Lower all users listen volume - o 'v' -- Lower entire conference listening volume - o 'V' -- Raise entire conference listening volume - 10. OSPFinish() : now also can return ERROR result. - 11. OSPLookup() : Sets more variables, also now returns ERROR result. - 12. Page() -- New option: r - record the page into a file (see 'r' for app_meetme) - 13. Pickup() -- multiple extensions, PICKUPMARK; read the description! - 14. Queue() - o New Argument: AGI - o New option: i - 15. Random() -- is now deprecated in 1.4 - 16. Read() -- replace 'skip' and 'noanswer' options with 's', 'n', add 'i' option. - 17. Record() -- New option: 'x' : ignore all terminator keys (DTMF) and keep recording until hangup - 18. UserEvent() -- slight change in behavior. Read the description. - 19. VoiceMailMain() -- new a(#) option, goes to folder # directly. - 20. WaitForSilence() -- new optional 3rd arg, time delay before returning. - * Functions that have changes to their interfaces: - 1. CDR -- new options: u and s - 2. LANGUAGE -- Deprecated. Use CHANNEL(language) instead. - 3. MUSICCLASS -- Deprecated. Use CHANNEL(musicclass) instead. - * Configuration File Changes: - 1. NEW config files: - 1. amd.conf -- Answering Machine Detection parameters - 2. followme.conf -- parameters for the findme/followme call forwarding - 3. func_odbc.conf -- define sql access functions here - 4. gtalk.conf -- how to handle gtalk protocol calls - 5. h323.conf -- h323 configuration - 6. http.conf -- config for the builtin mini-http server in asterisk - 7. jabber.conf -- jabber interface - 8. jingle.conf -- jingle protocol interface config - 10. res_snmp.conf -- to enable snmp in asterisk, and define full/sub agent status - 11. say.conf -- define per-language rules for numbers, dates, etc. - 12. skinny.conf -- for those special skinny phones you want to use... - 13. sla.conf -- Shared Line Appearance config - 14. smdi.conf -- SMDI messaging config - 15. udptl.conf -- T38's udptl transport config - 16. users.conf -- user config - 2. Changes to Existing Config files: - 1. In General: - o Jitterbuffer support added to several channels. Usually adds these variables to a config file: - 1. jbenable - 2. jbmaxsize - 3. jbresyncthreshold - 4. jbimpl - 5. jblog - o MusicOnHold upgrade introduces two new variables: - 1. mohinterpret - 2. mohsuggest - 2. agents.conf - o multiplelogin variable added - o maxlogintries variable added - o autologoffunavail variable added - o endcall variable added - o goodbye variable added - o createlink variable REMOVED - 3. alsa.conf - o mohinterpret variable added - o Jitterbuffer variables added - 4. cdr.conf - o endbeforehexten variable added - o sections for csv and radius added, with variables usegmtime, loguniqueid, - loguserfield, and radiuscfg variables. - 5. cdr_tds.conf - o table variable added - 6. extensions.ael - o Many upgrades. See the info at http://www.voip-info.org/wiki/view/Asterisk+AEL2 - 7. extensions.conf - o autofallthru now set to "yes" by default - o userscontext variable added - o added info/examples on paging and hints. - 8. features.conf - o parkedplay variable added (who to beep at) - o parkedmusicclass - o atxfernoanswertimeout variable added - o parkcall variable added (one step parking) - o improved documentation for dynamic feature declarations! - o added parkedcallltransfers option to control builtin transfers with parking - 9. iax.conf - o adsi variable added - o mohinterpret variable added - o mohsuggest variable added - o jitterbuffer updates - o iaxthreadcount variable added - o iaxmaxthreadcount variable added - o the way to specify TOS has changed. - o mailboxdetail variable has been REMOVED. - 10. indications.conf - o [bg] entry added (Bulgaria). - o [il] entry added (Israel) - o [in] entry added (India) - o [jp] entry added (Japan) - o [my] entry added (Malaysia) - o [th] entry added (Thailand) - 11. manager.conf - o webenabled variable added - o httptimeout variable added - o timestampevents variable added - 12. mgcp.conf - o Jitterbuffer support added - 13. misdn.conf - o l1watcher_timeout variable added - o pp_l2_check variable added - o echocancelwhenbridged variable added - o echotraining variable added - o max_incoming variable added - o max_outgoing variable added - 14. modules.conf - o a comment for preloading res_speech.so is added - o mention of global symbols is removed - o obsolesced entries for chan_modem_* and app_intercom have been removed - 15. musiconhold.conf - o the default is now to do native moh from /var/lib/asterisk/moh - 16. osp.conf - o authpolicy variable added - 17. oss.conf - o debug variable added - o device variable added - o mixer variable added - o boost variable added - o callerid variable added - o autohangup variable added - o queuesize variable added - o frags variable added - o JitterBuffer support - o sections to define alternate sound cards - 18. queues.conf - o autofill variable added - o monitor-type variable added - o musiconhold is now musicclass, with a difference in interpretation - o autofill variable added - o autopause variable added - o setinterfacevar variable added - o ringinuse variable added - 19. res_odbc.conf - o pooling variable added - 20. rpt.conf - o duplex variable added - o tailmessagetime variable added - o tailsquashedtime variable added - o tailmessages variable added - 21. rtp.conf - o rtcpinterval varaible added - 22. sip.conf - o allowguest variable can't be set to 'osp' - o allowoverlap variable added - o allowtransfer variable added - o limitonpeer variable added - o directrtpsetup variable added - o buggymwi variable added - o ospauth variable REMOVED - o notifyhold variable added - o autoframing variable added - o tos variable REMOVED - o tos_sip variable added - o tos_audio variable added - o tos_video variable added - o minexpiry variable added - o t1min variable added - o musicclass variable REMOVED - o mohinterpret variable added - o mohsuggest variable added - o allowsubscribe variable added - o videosupport variable added - o maxcallbitrate variable added - o g726nonstandard variable added - o dumphistory variable added - o t38pt_udptl variable added - o t38pt_rtp variable added - o t38pt_tcp variable added - o rfc2833compensate variable added - o matchexterniplocally variable added - o canreinvite variable can also now be set to 'nonat' - o rtsavesysname variable added - o JitterBuffer support added - o t38pt_usertpsource variable added - o regcontext variable can contains multiple contexts separated by an '&' - 23. skinny.conf - o port variable renamed to bindport - o JitterBuffer support added - o model variable REMOVED - o mohinterpret variable added - o mohsuggest variable added - o speeddial variable added - o addon variable added - 24. voicemail.conf - o userscontext variable added - o smdiport variable added - o attachfmt variable added - o volgain variable added - o tempgreetwarn variable added - 25. zapata.conf - o pritimer variable has improved documentation - o New signalling method: fgccama - o New signalling method: fgccamamf - o outsignalling variable added - o distinctiveringaftercid variable added - o cidsignalling now also accepts v23_jp, and smdi - o usesmdi variable added - o smdiport variable added - o mohinterpret variable added - o mohsuggest variable added - o JitterBuffer support added - * Removed Codecs/Channels: - 1. codec_g723 was removed because the actual codec implementation it was designed to use is not distributable - 2. chan_modem_* and related modules are gone because the kernel support for those interfaces is old, buggy and unsupported - * New Utils: - 1. aelparse -- compile .ael files outside of asterisk - * New manager events: - 1. OriginateResponse event comes to replace OriginateSuccess and OriginateFailure - * iLBC source code no longer included (see UPGRADE.txt for details) - * New CLI command "pri show version" that shows the current version of libpri - that the library was built against (requires a version of libpri since this API - feature was added). diff --git a/COPYING b/COPYING deleted file mode 100644 index aa2ebac66..000000000 --- a/COPYING +++ /dev/null @@ -1,341 +0,0 @@ - - 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. - - - Copyright (C) 19yy - - 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. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/CREDITS b/CREDITS deleted file mode 100644 index a51638214..000000000 --- a/CREDITS +++ /dev/null @@ -1,215 +0,0 @@ - -=== DEVELOPMENT SUPPORT === -We'd like to thank the following companies for helping fund development of -Asterisk: - -Pilosoft, Inc. - for supporting ADSI development in Asterisk - -Asterlink, Inc. - for supporting broad Asterisk development - -GFS - for supporting ALSA development - -Telesthetic - for supporting SIP development - -Christos Ricudis - for substantial code contributions - -nic.at - ENUM support in Asterisk - -Paul Bagyenda, Digital Solutions - for initial Voicetronix driver development - -=== WISHLIST CONTRIBUTERS === -Jeremy McNamara - SpeeX support -Nick Seraphin - RDNIS support -Gary - Phonejack ADSI (in progress) -Wasim - Hangup detect - -=== HARDWARE DONORS === -* Thanks to QuickNet Technologies for their donation of an Internet -PhoneJack and Linejack card to the project. (http://www.quicknet.net) - -* Thanks to VoipSupply for their donation of Sipura ATAs to the project for -T.38 testing. (http://www.voipsupply.com) - -* Thanks to Grandstream for their donation of ATAs to the project for -T.38 testing. (http://www.grandstream.com) - -=== MISCELLANEOUS PATCHES === -Jim Dixon - Zapata Telephony and app_rpt - http://www.zapatatelephony.org/app_rpt.html - -Russell Bryant - Asterisk 1.0 maintainer and misc. enhancements - russelb@clemson.edu - -Anthony Minessale II - Countless big and small fixes, and relentless forward push - ChanSpy, ForkCDR, ControlPlayback, While/EndWhile, DumpChan, Dictate, - MacroIf, ExecIf, ExecIfTime, RetryDial, MixMonitor applications; many realtime - concepts and implementation pieces, including res_config_odbc; format_slin; - cdr_custom; several features in Dial including L(), G() and enhancements to - M() and D(); several CDR enhancements including CDR variables; attended - transfer; one touch record; native MOH; manager eventmask; command line '-t' - flag to allow recording/voicemail on nfs shares; #exec command and multiline - comments in config files; setvar in iax and sip configs. - anthmct@yahoo.com http://www.asterlink.com - -James Golovich - Innumerable contributions - You can find him and asterisk-perl at http://asterisk.gnuinter.net - -Andre Bierwirth - Extension hints and status - -Oliver Daudey - ISDN4Linux fixes - -Pauline Middelink - ISDN4Linux patches and some general patches. - She can be found at http://www.polyware.nl/~middelink/En/ - -Jean-Denis Girard - Various contributions from the South Pacific Islands - jd-girard@esoft.pf http://www.esoft.pf - -William Jordan / Vonage - MySQL enhancements to Voicemail - wjordan@vonage.com - -Jac Kersing - Various fixes - -Steven Critchfield - Seek and Trunc functions for playback and recording - critch@basesys.com - -Jefferson Noxon - app_lookupcidname, app_db, and various other contributions - -Klaus-Peter Junghanns - in-band DTMF on SIP and MGCP - -Ross Finlayson - Dynamic RTP payload support - -Mahmut Fettahlioglu - Audio recording, music-on-hold changes, alaw file - format, and various fixes. Can be contacted at mahmut@oa.com.au - -James Dennis - Cisco SIP compatibility patches to work with SIP service - providers. Can be contacted at asterisk@jdennis.net - -Tilghman Lesher - ast_localtime(); ast_say_date_with_format(); - GotoIfTime, Random, SayUnixTime, HasNewVoicemail applications; - CUT, SORT, EVAL, CURL, FIELDQTY, STRFTIME, QUEUEAGENT* functions; - and other innumerable bug fixes. http://asterisk.drunkcoder.com/ - -Jayson Vantuyl - Manager protocol changes, various other bugs. - jvantuyl@computingedge.net - -Thorsten Lockert - OpenBSD, FreeBSD ports, making MacOS X port run on 10.3, - dialplan include verification, route lookup on OpenBSD, SNMP agent - support (res_snmp), various other bugs. tholo@sigmasoft.com - -Brian West - ODBC support and Bug Marshaling - -Josh Roberson - chan_zap reload support, Advanced Voicemail Features, other misc. patches, - and Bug Marshalling. - josh@asteriasgi.com, http://www.asteriasgi.com - -William Waites - syslog support, SIP NAT traversal for SIP-UA. ww@styx.org - -Rich Murphey - Porting to FreeBSD, NetBSD, OpenBSD, and Darwin. - rich@whiteoaklabs.com http://whiteoaklabs.com - -Simon Lockhart - Porting to Solaris (based on work of Logan ???) - simon@slimey.org - -Olle E. Johansson - SIP RFC compliance, documentation and testing, testing, testing - oej@edvina.net, http://edvina.net - -Steve Kann - new jitter buffer for IAX2 - stevek@stevek.com - -Constantine Filin - major contributions to the Asterisk Realtime Architecture - -Steve Murphy - privacy support, $[ ] parser upgrade, AEL2 parser upgrade - -Claude Patry - bug fixes, feature enhancements, and bug marshalling - cpatry@gmail.com - -Miroslav Nachev, miro@space-comm.com COSMOS Software Enterprises, Ltd. - - for Variable for No Answer Timeout for Attended Transfer - -Slav Klenov & Vanheuverzwijn Joachim - development of the generic jitterbuffer - Securax Ltd. info@securax.be - -Roy Sigurd Karlsbakk - providing funding for generic jitterbuffer development - roy@karlsbakk.net, Briiz Telecom AS - -Voop A/S, Nuvio Inc, Inotel S.A and Foniris Telecom A/S - funding for rewrite of SIP transfers - -Philippe Sultan - RADIUS CDR module - INRIA, http://www.inria.fr/ - -John Martin, Aupix - Improved video support in the SIP channel - -Steve Underwood - Provided T.38 pass through support. - -George Konstantoulakis - Support for Greek in voicemail added by InAccess Networks (work funded by HOL, www.hol.gr) gkon@inaccessnetworks.com - -Daniel Nylander - Support for Swedish and Norwegian languages in voicemail. http://www.danielnylander.se/ - -Stojan Sljivic - An option for maximum number of messsages per mailbox in voicemail. Also an issue with voicemail synchronization has been fixed. GDS Partners www.gdspartners.com . stojan.sljivic@gdspartners.com - -Bartosz Supczinski - Support for Polish added by DIR (www.dir.pl) Bartosz.Supczinski@dir.pl - -James Rothenberger - Support for IMAP storage integration added by OneBizTone LLC Work funded by University of Pennsylvania jar@onebiztone.com - -Paul Cadach - Bringing chan_h323 up to date, bug fixes, and more! - -=== OTHER CONTRIBUTIONS === -John Todd - Monkey sounds and associated teletorture prompt -Michael Jerris - bug marshaling -Leif Madsen, Jared Smith and Jim van Meggelen - the Asterisk book - available under a Creative Commons License at http://www.asteriskdocs.org -Brian M. Clapper - poll.c emulation - This product includes software developed by Brian M. Clapper - -=== HOLD MUSIC === -Music provided by www.freeplaymusic.com - -=== OTHER SOURCE CODE IN ASTERISK === -Asterisk uses libedit, the lightweight readline replacement from NetBSD. -The cdr_radius module uses libradiusclient-ng, which is also from NetBSD. -They are BSD-licensed and require the following statement: - - This product includes software developed by the NetBSD - Foundation, Inc. and its contributors. - -Digium did not implement the codecs in Asterisk. Here is the copyright on the -GSM source: - -Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, -Technische Universitaet Berlin - -Any use of this software is permitted provided that this notice is not -removed and that neither the authors nor the Technische Universitaet Berlin -are deemed to have made any representations as to the suitability of this -software for any purpose nor are held responsible for any defects of -this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. - -As a matter of courtesy, the authors request to be informed about uses -this software has found, about bugs in this software, and about any -improvements that may be of general interest. - -Berlin, 28.11.1994 -Jutta Degener -Carsten Bormann - -And the copyright on the ADPCM source: - -Copyright 1992 by Stichting Mathematisch Centrum, Amsterdam, The -Netherlands. - - All Rights Reserved - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the names of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 77ff5bb16..000000000 --- a/LICENSE +++ /dev/null @@ -1,68 +0,0 @@ -Asterisk is distributed under the GNU General Public License version 2 -and is also available under alternative licenses negotiated directly -with Digium, Inc. If you obtained Asterisk under the GPL, then the GPL -applies to all loadable Asterisk modules used on your system as well, -except as defined below. The GPL (version 2) is included in this -source tree in the file COPYING. - -This package also includes various components that are not part of -Asterisk itself; these components are in the 'contrib' directory -and its subdirectories. Most of these components are also -distributed under the GPL version 2 as well, except for the following: - -contrib/firmware/iax/iaxy.bin: - This file is Copyright (C) Digium, Inc. and is licensed for - use with Digium IAXy hardware devices only. It can be - distributed freely as long as the distribution is in the - original form present in this package (not reformatted or - modified). - -Digium, Inc. (formerly Linux Support Services) holds copyright -and/or sufficient licenses to all components of the Asterisk -package, and therefore can grant, at its sole discretion, the ability -for companies, individuals, or organizations to create proprietary or -Open Source (even if not GPL) modules which may be dynamically linked at -runtime with the portions of Asterisk which fall under our -copyright/license umbrella, or are distributed under more flexible -licenses than GPL. - -If you wish to use our code in other GPL programs, don't worry -- -there is no requirement that you provide the same exception in your -GPL'd products (although if you've written a module for Asterisk we -would strongly encourage you to make the same exception that we do). - -Specific permission is also granted to link Asterisk with OpenSSL, OpenH323 -and/or the UW IMAP Toolkit and distribute the resulting binary files. - -In addition, Asterisk implements two management/control protocols: the -Asterisk Manager Interface (AMI) and the Asterisk Gateway Interface -(AGI). It is our belief that applications using these protocols to -manage or control an Asterisk instance do not have to be licensed -under the GPL or a compatible license, as we believe these protocols -do not create a 'derivative work' as referred to in the GPL. However, -should any court or other judiciary body find that these protocols do -fall under the terms of the GPL, then we hereby grant you a license to -use these protocols in combination with Asterisk in external -applications licensed under any license you wish. - -The 'Asterisk' name and logos are trademarks owned by Digium, Inc., -and use of them is subject to our trademark licensing policies. If you -wish to use these trademarks for purposes other than simple -redistribution of Asterisk source code obtained from Digium, you -should contact our licensing department to determine the necessary -steps you must take. For more information on this policy, please read -http://www.digium.com/en/company/profile/trademarkpolicy.php - -If you have any questions regarding our licensing policy, please -contact us: - -+1.877.344.4861 (via telephone in the USA) -+1.256.428.6000 (via telephone outside the USA) -+1.256.864.0464 (via FAX inside or outside the USA) -IAX2/pbx.digium.com (via IAX2) -licensing@digium.com (via email) - -Digium, Inc. -445 Jan Davis Drive -Huntsville, AL 35806 -USA diff --git a/Makefile b/Makefile deleted file mode 100644 index 185e58f88..000000000 --- a/Makefile +++ /dev/null @@ -1,772 +0,0 @@ -# -# Asterisk -- A telephony toolkit for Linux. -# -# Top level Makefile -# -# Copyright (C) 1999-2006, Digium, Inc. -# -# Mark Spencer -# -# This program is free software, distributed under the terms of -# the GNU General Public License -# - -# All Makefiles use the following variables: -# -# ASTCFLAGS - compiler options -# ASTLDFLAGS - linker flags (not libraries) -# AST_LIBS - libraries to build binaries XXX -# LIBS - additional libraries, at top-level for all links, -# on a single object just for that object -# SOLINK - linker flags used only for creating shared objects (.so files), -# used for all .so links -# -# Default values fo ASTCFLAGS and ASTLDFLAGS can be specified in the -# environment when running make, as follows: -# -# $ ASTCFLAGS="-Werror" make - -export ASTTOPDIR -export ASTERISKVERSION -export ASTERISKVERSIONNUM -export INSTALL_PATH -export ASTETCDIR -export ASTVARRUNDIR -export MODULES_DIR -export ASTSPOOLDIR -export ASTVARLIBDIR -export ASTDATADIR -export ASTLOGDIR -export ASTLIBDIR -export ASTMANDIR -export ASTHEADERDIR -export ASTBINDIR -export ASTSBINDIR -export AGI_DIR -export ASTCONFPATH -export NOISY_BUILD -export MENUSELECT_CFLAGS -export CC -export CXX -export AR -export RANLIB -export HOST_CC -export STATIC_BUILD -export INSTALL -export DESTDIR -export PROC -export SOLINK -export STRIP -export DOWNLOAD -export AWK -export GREP -export ID -export OSARCH -export CURSES_DIR -export NCURSES_DIR -export TERMCAP_DIR -export TINFO_DIR -export GTK2_LIB -export GTK2_INCLUDE - -# even though we could use '-include makeopts' here, use a wildcard -# lookup anyway, so that make won't try to build makeopts if it doesn't -# exist (other rules will force it to be built if needed) -ifneq ($(wildcard makeopts),) - include makeopts -endif - -# Some build systems, such as the one in openwrt, like to pass custom target -# CFLAGS and LDFLAGS in the COPTS and LDOPTS variables. -ASTCFLAGS+=$(COPTS) -ASTLDFLAGS+=$(LDOPTS) - -#Uncomment this to see all build commands instead of 'quiet' output -#NOISY_BUILD=yes - -# Create OPTIONS variable -OPTIONS= - -empty:= -space:=$(empty) $(empty) -ASTTOPDIR:=$(subst $(space),\$(space),$(CURDIR)) - -# Overwite config files on "make samples" -OVERWRITE=y - -# Include debug and macro symbols in the executables (-g) and profiling info (-pg) -DEBUG=-g3 - -# Staging directory -# Files are copied here temporarily during the install process -# For example, make DESTDIR=/tmp/asterisk woud put things in -# /tmp/asterisk/etc/asterisk -# !!! Watch out, put no spaces or comments after the value !!! -#DESTDIR?=/tmp/asterisk - -# Define standard directories for various platforms -# These apply if they are not redefined in asterisk.conf -ifeq ($(OSARCH),SunOS) - ASTETCDIR=/var/etc/asterisk - ASTLIBDIR=/opt/asterisk/lib - ASTVARLIBDIR=/var/opt/asterisk - ASTSPOOLDIR=/var/spool/asterisk - ASTLOGDIR=/var/log/asterisk - ASTHEADERDIR=/opt/asterisk/include - ASTBINDIR=/opt/asterisk/bin - ASTSBINDIR=/opt/asterisk/sbin - ASTVARRUNDIR=/var/run/asterisk - ASTMANDIR=/opt/asterisk/man -else - ASTETCDIR=$(sysconfdir)/asterisk - ASTLIBDIR=$(libdir)/asterisk - ASTHEADERDIR=$(includedir)/asterisk - ASTBINDIR=$(bindir) - ASTSBINDIR=$(sbindir) - ASTSPOOLDIR=$(localstatedir)/spool/asterisk - ASTLOGDIR=$(localstatedir)/log/asterisk - ASTVARRUNDIR=$(localstatedir)/run - ASTMANDIR=$(mandir) -ifneq ($(findstring BSD,$(OSARCH)),) - ASTVARLIBDIR=$(prefix)/share/asterisk - ASTVARRUNDIR=$(localstatedir)/run/asterisk -else - ASTVARLIBDIR=$(localstatedir)/lib/asterisk -endif -endif -ifeq ($(ASTDATADIR),) - ASTDATADIR:=$(ASTVARLIBDIR) -endif - -# Asterisk.conf is located in ASTETCDIR or by using the -C flag -# when starting Asterisk -ASTCONFPATH=$(ASTETCDIR)/asterisk.conf -MODULES_DIR=$(ASTLIBDIR)/modules -AGI_DIR=$(ASTDATADIR)/agi-bin - -# If you use Apache, you may determine by a grep 'DocumentRoot' of your httpd.conf file -HTTP_DOCSDIR=/var/www/html -# Determine by a grep 'ScriptAlias' of your Apache httpd.conf file -HTTP_CGIDIR=/var/www/cgi-bin - -# Uncomment this to use the older DSP routines -#ASTCFLAGS+=-DOLD_DSP_ROUTINES - -# If the file .asterisk.makeopts is present in your home directory, you can -# include all of your favorite menuselect options so that every time you download -# a new version of Asterisk, you don't have to run menuselect to set them. -# The file /etc/asterisk.makeopts will also be included but can be overridden -# by the file in your home directory. - -GLOBAL_MAKEOPTS=$(wildcard /etc/asterisk.makeopts) -USER_MAKEOPTS=$(wildcard ~/.asterisk.makeopts) - -MOD_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include -OTHER_SUBDIR_CFLAGS=-I$(ASTTOPDIR)/include - -ifeq ($(OSARCH),linux-gnu) - ifeq ($(PROC),x86_64) - # You must have GCC 3.4 to use k8, otherwise use athlon - PROC=k8 - #PROC=athlon - endif - - ifeq ($(PROC),sparc64) - #The problem with sparc is the best stuff is in newer versions of gcc (post 3.0) only. - #This works for even old (2.96) versions of gcc and provides a small boost either way. - #A ultrasparc cpu is really v9 but the stock debian stable 3.0 gcc doesn't support it. - #So we go lowest common available by gcc and go a step down, still a step up from - #the default as we now have a better instruction set to work with. - Belgarath - PROC=ultrasparc - OPTIONS+=$(shell if $(CC) -mtune=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mtune=$(PROC)"; fi) - OPTIONS+=$(shell if $(CC) -mcpu=v8 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mcpu=v8"; fi) - OPTIONS+=-fomit-frame-pointer - endif - - ifeq ($(PROC),arm) - # The Cirrus logic is the only heavily shipping arm processor with a real floating point unit - ifeq ($(SUB_PROC),maverick) - OPTIONS+=-fsigned-char -mcpu=ep9312 - else - ifeq ($(SUB_PROC),xscale) - OPTIONS+=-fsigned-char -mcpu=xscale - else - OPTIONS+=-fsigned-char - endif - endif - endif -endif - -ifeq ($(findstring -save-temps,$(ASTCFLAGS)),) -ASTCFLAGS+=-pipe -endif - -ASTCFLAGS+=-Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) - -ifeq ($(AST_DEVMODE),yes) - ASTCFLAGS+=-Werror - ASTCFLAGS+=-Wunused - ASTCFLAGS+=$(AST_DECLARATION_AFTER_STATEMENT) - ASTCFLAGS+=$(AST_FORTIFY_SOURCE) -# ASTCFLAGS+=-Wundef - ASTCFLAGS+=-Wformat -Wformat-security - ASTCFLAGS+=-Wmissing-format-attribute -# ASTCFLAGS+=-Wformat=2 -endif - -ifneq ($(findstring BSD,$(OSARCH)),) - ASTCFLAGS+=-I/usr/local/include - ASTLDFLAGS+=-L/usr/local/lib -endif - -ifneq ($(PROC),ultrasparc) - ASTCFLAGS+=$(shell if $(CC) -march=$(PROC) -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=$(PROC)"; fi) -endif - -ifeq ($(PROC),ppc) - ASTCFLAGS+=-fsigned-char -endif - -ifeq ($(OSARCH),FreeBSD) - # -V is understood by BSD Make, not by GNU make. - BSDVERSION=$(shell make -V OSVERSION -f /usr/share/mk/bsd.port.subdir.mk) - ASTCFLAGS+=$(shell if test $(BSDVERSION) -lt 500016 ; then echo "-D_THREAD_SAFE"; fi) - AST_LIBS+=$(shell if test $(BSDVERSION) -lt 502102 ; then echo "-lc_r"; else echo "-pthread"; fi) -endif - -ifeq ($(OSARCH),NetBSD) - ASTCFLAGS+=-pthread -I/usr/pkg/include -endif - -ifeq ($(OSARCH),OpenBSD) - ASTCFLAGS+=-pthread -endif - -ifeq ($(OSARCH),SunOS) - ASTCFLAGS+=-Wcast-align -DSOLARIS -I../include/solaris-compat -I/opt/ssl/include -I/usr/local/ssl/include -endif - -ASTERISKVERSION:=$(shell GREP=$(GREP) AWK=$(AWK) build_tools/make_version .) - -ifneq ($(wildcard .version),) - ASTERISKVERSIONNUM:=$(shell $(AWK) -F. '{printf "%01d%02d%02d", $$1, $$2, $$3}' .version) -endif - -ifneq ($(wildcard .svn),) - ASTERISKVERSIONNUM=999999 -endif - -ASTCFLAGS+=$(MALLOC_DEBUG)$(BUSYDETECT)$(OPTIONS) - -MOD_SUBDIRS:=res channels pbx apps codecs formats cdr funcs main -OTHER_SUBDIRS:=utils agi -SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS) -SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install) -SUBDIRS_CLEAN:=$(SUBDIRS:%=%-clean) -SUBDIRS_DIST_CLEAN:=$(SUBDIRS:%=%-dist-clean) -SUBDIRS_UNINSTALL:=$(SUBDIRS:%=%-uninstall) -MOD_SUBDIRS_EMBED_LDSCRIPT:=$(MOD_SUBDIRS:%=%-embed-ldscript) -MOD_SUBDIRS_EMBED_LDFLAGS:=$(MOD_SUBDIRS:%=%-embed-ldflags) -MOD_SUBDIRS_EMBED_LIBS:=$(MOD_SUBDIRS:%=%-embed-libs) -MOD_SUBDIRS_MENUSELECT_TREE:=$(MOD_SUBDIRS:%=%-menuselect-tree) - -ifneq ($(findstring darwin,$(OSARCH)),) - ASTCFLAGS+=-D__Darwin__ - AUDIO_LIBS=-framework CoreAudio - SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace -else -# These are used for all but Darwin - SOLINK=-shared - ifneq ($(findstring BSD,$(OSARCH)),) - LDFLAGS+=-L/usr/local/lib - endif -endif - -ifeq ($(OSARCH),SunOS) - SOLINK=-shared -fpic -L/usr/local/ssl/lib -lrt -endif - -SUBMAKE=$(MAKE) --quiet --no-print-directory - -# This is used when generating the doxygen documentation -ifneq ($(DOT),:) - HAVEDOT=yes -else - HAVEDOT=no -endif - -all: _all - @echo " +--------- Asterisk Build Complete ---------+" - @echo " + Asterisk has successfully been built, and +" - @echo " + can be installed by running: +" - @echo " + +" -ifeq ($(MAKE), gmake) - @echo " + $(MAKE) install +" -else - @echo " + $(MAKE) install +" -endif - @echo " +-------------------------------------------+" - -_all: cleantest $(SUBDIRS) - -makeopts: configure - @echo "****" - @echo "**** The configure script must be executed before running '$(MAKE)'." - @echo "**** Please run \"./configure\"." - @echo "****" - @exit 1 - -menuselect.makeopts: menuselect/menuselect menuselect-tree - menuselect/menuselect --check-deps menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) - -$(MOD_SUBDIRS_EMBED_LDSCRIPT): - @echo "EMBED_LDSCRIPTS+="`$(SUBMAKE) -C $(@:-embed-ldscript=) SUBDIR=$(@:-embed-ldscript=) __embed_ldscript` >> makeopts.embed_rules - -$(MOD_SUBDIRS_EMBED_LDFLAGS): - @echo "EMBED_LDFLAGS+="`$(SUBMAKE) -C $(@:-embed-ldflags=) SUBDIR=$(@:-embed-ldflags=) __embed_ldflags` >> makeopts.embed_rules - -$(MOD_SUBDIRS_EMBED_LIBS): - @echo "EMBED_LIBS+="`$(SUBMAKE) -C $(@:-embed-libs=) SUBDIR=$(@:-embed-libs=) __embed_libs` >> makeopts.embed_rules - -$(MOD_SUBDIRS_MENUSELECT_TREE): - @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) moduleinfo - @$(SUBMAKE) -C $(@:-menuselect-tree=) SUBDIR=$(@:-menuselect-tree=) makeopts - -makeopts.embed_rules: menuselect.makeopts - @echo "Generating embedded module rules ..." - @rm -f $@ - @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LDSCRIPT) - @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LDFLAGS) - @$(MAKE) --no-print-directory $(MOD_SUBDIRS_EMBED_LIBS) - -$(SUBDIRS): include/asterisk/version.h include/asterisk/buildopts.h defaults.h makeopts.embed_rules - -# ensure that all module subdirectories are processed before 'main' during -# a parallel build, since if there are modules selected to be embedded the -# directories containing them must be completed before the main Asterisk -# binary can be built -main: $(filter-out main,$(MOD_SUBDIRS)) - -$(MOD_SUBDIRS): - @ASTCFLAGS="$(MOD_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" AST_LIBS="$(AST_LIBS)" $(MAKE) --no-builtin-rules -C $@ SUBDIR=$@ all - -$(OTHER_SUBDIRS): - @ASTCFLAGS="$(OTHER_SUBDIR_CFLAGS) $(ASTCFLAGS)" ASTLDFLAGS="$(ASTLDFLAGS)" AUDIO_LIBS="$(AUDIO_LIBS)" $(MAKE) --no-builtin-rules -C $@ SUBDIR=$@ all - -defaults.h: makeopts - @build_tools/make_defaults_h > $@.tmp - @if cmp -s $@.tmp $@ ; then : ; else \ - mv $@.tmp $@ ; \ - fi - @rm -f $@.tmp - -include/asterisk/version.h: FORCE - @build_tools/make_version_h > $@.tmp - @if cmp -s $@.tmp $@ ; then : ; else \ - mv $@.tmp $@ ; \ - fi - @rm -f $@.tmp - -include/asterisk/buildopts.h: menuselect.makeopts - @build_tools/make_buildopts_h > $@.tmp - @if cmp -s $@.tmp $@ ; then : ; else \ - mv $@.tmp $@ ; \ - fi - @rm -f $@.tmp - -$(SUBDIRS_CLEAN): - @$(MAKE) --no-print-directory -C $(@:-clean=) clean - -$(SUBDIRS_DIST_CLEAN): - @$(MAKE) --no-print-directory -C $(@:-dist-clean=) dist-clean - -clean: $(SUBDIRS_CLEAN) _clean - -_clean: - rm -f defaults.h - rm -f include/asterisk/build.h - rm -f include/asterisk/version.h - @$(MAKE) -C menuselect clean - cp -f .cleancount .lastclean - -dist-clean: distclean - -distclean: $(SUBDIRS_DIST_CLEAN) _clean - @$(MAKE) -C menuselect dist-clean - @$(MAKE) -C sounds dist-clean - rm -f menuselect.makeopts makeopts menuselect-tree menuselect.makedeps - rm -f makeopts.embed_rules - rm -f config.log config.status config.cache - rm -rf autom4te.cache - rm -f include/asterisk/autoconfig.h - rm -f include/asterisk/buildopts.h - rm -rf doc/api - rm -f build_tools/menuselect-deps - -datafiles: _all - if [ x`$(ID) -un` = xroot ]; then CFLAGS="$(ASTCFLAGS)" sh build_tools/mkpkgconfig $(DESTDIR)/usr/lib/pkgconfig; fi -# Should static HTTP be installed during make samples or even with its own target ala -# webvoicemail? There are portions here that *could* be customized but might also be -# improved a lot. I'll put it here for now. - mkdir -p $(DESTDIR)$(ASTDATADIR)/static-http - for x in static-http/*; do \ - $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/static-http ; \ - done - mkdir -p $(DESTDIR)$(ASTDATADIR)/images - for x in images/*.jpg; do \ - $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTDATADIR)/images ; \ - done - mkdir -p $(DESTDIR)$(AGI_DIR) - $(MAKE) -C sounds install - -update: - @if [ -d .svn ]; then \ - echo "Updating from Subversion..." ; \ - svn update | tee update.out; \ - rm -f .version; \ - if [ `grep -c ^C update.out` -gt 0 ]; then \ - echo ; echo "The following files have conflicts:" ; \ - grep ^C update.out | cut -b4- ; \ - fi ; \ - rm -f update.out; \ - else \ - echo "Not under version control"; \ - fi - -NEWHEADERS=$(notdir $(wildcard include/asterisk/*.h)) -OLDHEADERS=$(filter-out $(NEWHEADERS),$(notdir $(wildcard $(DESTDIR)$(ASTHEADERDIR)/*.h))) - -installdirs: - mkdir -p $(DESTDIR)$(MODULES_DIR) - mkdir -p $(DESTDIR)$(ASTSBINDIR) - mkdir -p $(DESTDIR)$(ASTETCDIR) - mkdir -p $(DESTDIR)$(ASTBINDIR) - mkdir -p $(DESTDIR)$(ASTVARRUNDIR) - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/dictate - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/system - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/tmp - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/meetme - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/monitor - -bininstall: _all installdirs $(SUBDIRS_INSTALL) - $(INSTALL) -m 755 main/asterisk $(DESTDIR)$(ASTSBINDIR)/ - $(LN) -sf asterisk $(DESTDIR)$(ASTSBINDIR)/rasterisk - $(INSTALL) -m 755 contrib/scripts/astgenkey $(DESTDIR)$(ASTSBINDIR)/ - $(INSTALL) -m 755 contrib/scripts/autosupport $(DESTDIR)$(ASTSBINDIR)/ - if [ ! -f $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ]; then \ - cat contrib/scripts/safe_asterisk | sed 's|__ASTERISK_SBIN_DIR__|$(ASTSBINDIR)|;s|__ASTERISK_VARRUN_DIR__|$(ASTVARRUNDIR)|;' > $(DESTDIR)$(ASTSBINDIR)/safe_asterisk ;\ - chmod 755 $(DESTDIR)$(ASTSBINDIR)/safe_asterisk;\ - fi - $(INSTALL) -d $(DESTDIR)$(ASTHEADERDIR) - $(INSTALL) -m 644 include/asterisk.h $(DESTDIR)$(includedir) - $(INSTALL) -m 644 include/asterisk/*.h $(DESTDIR)$(ASTHEADERDIR) - if [ -n "$(OLDHEADERS)" ]; then \ - rm -f $(addprefix $(DESTDIR)$(ASTHEADERDIR)/,$(OLDHEADERS)) ;\ - fi - mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-csv - mkdir -p $(DESTDIR)$(ASTLOGDIR)/cdr-custom - mkdir -p $(DESTDIR)$(ASTDATADIR)/keys - mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware - mkdir -p $(DESTDIR)$(ASTDATADIR)/firmware/iax - mkdir -p $(DESTDIR)$(ASTMANDIR)/man8 - $(INSTALL) -m 644 keys/iaxtel.pub $(DESTDIR)$(ASTDATADIR)/keys - $(INSTALL) -m 644 keys/freeworlddialup.pub $(DESTDIR)$(ASTDATADIR)/keys - $(INSTALL) -m 644 doc/asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8 - $(INSTALL) -m 644 contrib/scripts/astgenkey.8 $(DESTDIR)$(ASTMANDIR)/man8 - $(INSTALL) -m 644 contrib/scripts/autosupport.8 $(DESTDIR)$(ASTMANDIR)/man8 - $(INSTALL) -m 644 contrib/scripts/safe_asterisk.8 $(DESTDIR)$(ASTMANDIR)/man8 - if [ -f contrib/firmware/iax/iaxy.bin ] ; then \ - $(INSTALL) -m 644 contrib/firmware/iax/iaxy.bin $(DESTDIR)$(ASTDATADIR)/firmware/iax/iaxy.bin; \ - fi - -$(SUBDIRS_INSTALL): - @DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" $(MAKE) -C $(@:-install=) install - -NEWMODS=$(notdir $(wildcard */*.so)) -DEPMODS=chan_zap.so app_zapras.so app_zapscan.so app_zapbarge.so codec_zap.so -OLDMODS=$(filter-out $(NEWMODS) $(DEPMODS),$(notdir $(wildcard $(DESTDIR)$(MODULES_DIR)/*.so))) - -oldmodcheck: - @for f in $(DEPMODS); do rm -f $(DESTDIR)$(MODULES_DIR)/$$f; done - @if [ -n "$(OLDMODS)" ]; then \ - echo " WARNING WARNING WARNING" ;\ - echo "" ;\ - echo " Your Asterisk modules directory, located at" ;\ - echo " $(DESTDIR)$(MODULES_DIR)" ;\ - echo " contains modules that were not installed by this " ;\ - echo " version of Asterisk. Please ensure that these" ;\ - echo " modules are compatible with this version before" ;\ - echo " attempting to run Asterisk." ;\ - echo "" ;\ - for f in $(OLDMODS); do \ - echo " $$f" ;\ - done ;\ - echo "" ;\ - echo " WARNING WARNING WARNING" ;\ - fi - -badshell: -ifneq ($(findstring ~,$(DESTDIR)),) - @echo "Your shell doesn't do ~ expansion when expected (specifically, when doing \"make install DESTDIR=~/path\")." - @echo "Try replacing ~ with \$$HOME, as in \"make install DESTDIR=\$$HOME/path\"." - @exit 1 -endif - -install: badshell datafiles bininstall - @if [ -x /usr/sbin/asterisk-post-install ]; then \ - /usr/sbin/asterisk-post-install $(DESTDIR) . ; \ - fi - @echo " +---- Asterisk Installation Complete -------+" - @echo " + +" - @echo " + YOU MUST READ THE SECURITY DOCUMENT +" - @echo " + +" - @echo " + Asterisk has successfully been installed. +" - @echo " + If you would like to install the sample +" - @echo " + configuration files (overwriting any +" - @echo " + existing config files), run: +" - @echo " + +" -ifeq ($(MAKE), gmake) - @echo " + $(MAKE) samples +" -else - @echo " + $(MAKE) samples +" -endif - @echo " + +" - @echo " +----------------- or ---------------------+" - @echo " + +" - @echo " + You can go ahead and install the asterisk +" - @echo " + program documentation now or later run: +" - @echo " + +" -ifeq ($(MAKE), gmake) - @echo " + $(MAKE) progdocs +" -else - @echo " + $(MAKE) progdocs +" -endif - @echo " + +" - @echo " + **Note** This requires that you have +" - @echo " + doxygen installed on your local system +" - @echo " +-------------------------------------------+" - @$(MAKE) -s oldmodcheck - -upgrade: bininstall - -adsi: - mkdir -p $(DESTDIR)$(ASTETCDIR) - for x in configs/*.adsi; do \ - if [ ! -f $(DESTDIR)$(ASTETCDIR)/$$x ]; then \ - $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x` ; \ - fi ; \ - done - -samples: adsi - mkdir -p $(DESTDIR)$(ASTETCDIR) - for x in configs/*.sample; do \ - if [ -f $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` ]; then \ - if [ "$(OVERWRITE)" = "y" ]; then \ - if cmp -s $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` $$x ; then \ - echo "Config file $$x is unchanged"; \ - continue; \ - fi ; \ - mv -f $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample`.old ; \ - else \ - echo "Skipping config file $$x"; \ - continue; \ - fi ;\ - fi ; \ - $(INSTALL) -m 644 $$x $(DESTDIR)$(ASTETCDIR)/`$(BASENAME) $$x .sample` ;\ - done - if [ "$(OVERWRITE)" = "y" ] || [ ! -f $(DESTDIR)$(ASTCONFPATH) ]; then \ - ( \ - echo "[directories]" ; \ - echo "astetcdir => $(ASTETCDIR)" ; \ - echo "astmoddir => $(MODULES_DIR)" ; \ - echo "astvarlibdir => $(ASTVARLIBDIR)" ; \ - echo "astdatadir => $(ASTDATADIR)" ; \ - echo "astagidir => $(AGI_DIR)" ; \ - echo "astspooldir => $(ASTSPOOLDIR)" ; \ - echo "astrundir => $(ASTVARRUNDIR)" ; \ - echo "astlogdir => $(ASTLOGDIR)" ; \ - echo "" ; \ - echo "[options]" ; \ - echo "languageprefix = yes ; Use the new sound prefix path syntax" ; \ - echo ";verbose = 3" ; \ - echo ";debug = 3" ; \ - echo ";alwaysfork = yes ; same as -F at startup" ; \ - echo ";nofork = yes ; same as -f at startup" ; \ - echo ";quiet = yes ; same as -q at startup" ; \ - echo ";timestamp = yes ; same as -T at startup" ; \ - echo ";execincludes = yes ; support #exec in config files" ; \ - echo ";console = yes ; Run as console (same as -c at startup)" ; \ - echo ";highpriority = yes ; Run realtime priority (same as -p at startup)" ; \ - echo ";initcrypto = yes ; Initialize crypto keys (same as -i at startup)" ; \ - echo ";nocolor = yes ; Disable console colors" ; \ - echo ";dontwarn = yes ; Disable some warnings" ; \ - echo ";dumpcore = yes ; Dump core on crash (same as -g at startup)" ; \ - echo ";internal_timing = yes" ; \ - echo ";systemname = my_system_name ; prefix uniqueid with a system name for global uniqueness issues" ; \ - echo ";maxcalls = 10 ; Maximum amount of calls allowed" ; \ - echo ";maxload = 0.9 ; Asterisk stops accepting new calls if the load average exceed this limit" ; \ - echo ";cache_record_files = yes ; Cache recorded sound files to another directory during recording" ; \ - echo ";record_cache_dir = /tmp ; Specify cache directory (used in cnjunction with cache_record_files)" ; \ - echo ";transmit_silence_during_record = yes ; Transmit SLINEAR silence while a channel is being recorded" ; \ - echo ";transmit_silence = yes ; Transmit SLINEAR silence while a channel is being recorded or DTMF is being generated" ; \ - echo ";transcode_via_sln = yes ; Build transcode paths via SLINEAR, instead of directly" ; \ - echo ";runuser = asterisk ; The user to run as" ; \ - echo ";rungroup = asterisk ; The group to run as" ; \ - echo ";dahdichanname = yes ; Channels created by chan_dahdi will be called 'DAHDI', otherwise 'Zap'" ; \ - echo "" ; \ - echo "; Changing the following lines may compromise your security." ; \ - echo ";[files]" ; \ - echo ";astctlpermissions = 0660" ; \ - echo ";astctlowner = root" ; \ - echo ";astctlgroup = apache" ; \ - echo ";astctl = asterisk.ctl" ; \ - ) > $(DESTDIR)$(ASTCONFPATH) ; \ - else \ - echo "Skipping asterisk.conf creation"; \ - fi - mkdir -p $(DESTDIR)$(ASTSPOOLDIR)/voicemail/default/1234/INBOX - build_tools/make_sample_voicemail $(DESTDIR)/$(ASTDATADIR) $(DESTDIR)/$(ASTSPOOLDIR) - -webvmail: - @[ -d $(DESTDIR)$(HTTP_DOCSDIR)/ ] || ( printf "http docs directory not found.\nUpdate assignment of variable HTTP_DOCSDIR in Makefile!\n" && exit 1 ) - @[ -d $(DESTDIR)$(HTTP_CGIDIR) ] || ( printf "cgi-bin directory not found.\nUpdate assignment of variable HTTP_CGIDIR in Makefile!\n" && exit 1 ) - $(INSTALL) -m 4755 -o root -g root contrib/scripts/vmail.cgi $(DESTDIR)$(HTTP_CGIDIR)/vmail.cgi - mkdir -p $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk - for x in images/*.gif; do \ - $(INSTALL) -m 644 $$x $(DESTDIR)$(HTTP_DOCSDIR)/_asterisk/; \ - done - @echo " +--------- Asterisk Web Voicemail ----------+" - @echo " + +" - @echo " + Asterisk Web Voicemail is installed in +" - @echo " + your cgi-bin directory: +" - @echo " + $(DESTDIR)$(HTTP_CGIDIR)" - @echo " + IT USES A SETUID ROOT PERL SCRIPT, SO +" - @echo " + IF YOU DON'T LIKE THAT, UNINSTALL IT! +" - @echo " + +" - @echo " + Other static items have been stored in: +" - @echo " + $(DESTDIR)$(HTTP_DOCSDIR)" - @echo " + +" - @echo " + If these paths do not match your httpd +" - @echo " + installation, correct the definitions +" - @echo " + in your Makefile of HTTP_CGIDIR and +" - @echo " + HTTP_DOCSDIR +" - @echo " + +" - @echo " +-------------------------------------------+" - -progdocs: - (cat contrib/asterisk-ng-doxygen; echo "HAVE_DOT=$(HAVEDOT)"; \ - echo "PROJECT_NUMBER=$(ASTERISKVERSION)") | doxygen - - -config: - @if [ "${OSARCH}" = "linux-gnu" ]; then \ - if [ -f /etc/redhat-release -o -f /etc/fedora-release ]; then \ - $(INSTALL) -m 755 contrib/init.d/rc.redhat.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \ - if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \ - elif [ -f /etc/debian_version ]; then \ - $(INSTALL) -m 755 contrib/init.d/rc.debian.asterisk $(DESTDIR)/etc/init.d/asterisk; \ - if [ -z "$(DESTDIR)" ]; then /usr/sbin/update-rc.d asterisk start 50 2 3 4 5 . stop 91 2 3 4 5 .; fi; \ - elif [ -f /etc/gentoo-release ]; then \ - $(INSTALL) -m 755 contrib/init.d/rc.gentoo.asterisk $(DESTDIR)/etc/init.d/asterisk; \ - if [ -z "$(DESTDIR)" ]; then /sbin/rc-update add asterisk default; fi; \ - elif [ -f /etc/mandrake-release -o -f /etc/mandriva-release ]; then \ - $(INSTALL) -m 755 contrib/init.d/rc.mandrake.asterisk $(DESTDIR)/etc/rc.d/init.d/asterisk; \ - if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \ - elif [ -f /etc/SuSE-release -o -f /etc/novell-release ]; then \ - $(INSTALL) -m 755 contrib/init.d/rc.suse.asterisk $(DESTDIR)/etc/init.d/asterisk; \ - if [ -z "$(DESTDIR)" ]; then /sbin/chkconfig --add asterisk; fi; \ - elif [ -f /etc/slackware-version ]; then \ - echo "Slackware is not currently supported, although an init script does exist for it." \ - else \ - echo "We could not install init scripts for your distribution."; \ - fi \ - else \ - echo "We could not install init scripts for your operating system."; \ - fi - -sounds: - $(MAKE) -C sounds all - -# If the cleancount has been changed, force a make clean. -# .cleancount is the global clean count, and .lastclean is the -# last clean count we had - -cleantest: - @cmp -s .cleancount .lastclean || $(MAKE) clean - -$(SUBDIRS_UNINSTALL): - @$(MAKE) --no-print-directory -C $(@:-uninstall=) uninstall - -_uninstall: $(SUBDIRS_UNINSTALL) - rm -f $(DESTDIR)$(MODULES_DIR)/* - rm -f $(DESTDIR)$(ASTSBINDIR)/*asterisk* - rm -f $(DESTDIR)$(ASTSBINDIR)/astgenkey - rm -f $(DESTDIR)$(ASTSBINDIR)/autosupport - rm -rf $(DESTDIR)$(ASTHEADERDIR) - rm -rf $(DESTDIR)$(ASTDATADIR)/firmware - rm -f $(DESTDIR)$(ASTMANDIR)/man8/asterisk.8 - rm -f $(DESTDIR)$(ASTMANDIR)/man8/astgenkey.8 - rm -f $(DESTDIR)$(ASTMANDIR)/man8/autosupport.8 - rm -f $(DESTDIR)$(ASTMANDIR)/man8/safe_asterisk.8 - $(MAKE) -C sounds uninstall - -uninstall: _uninstall - @echo " +--------- Asterisk Uninstall Complete -----+" - @echo " + Asterisk binaries, sounds, man pages, +" - @echo " + headers, modules, and firmware builds, +" - @echo " + have all been uninstalled. +" - @echo " + +" - @echo " + To remove ALL traces of Asterisk, +" - @echo " + including configuration, spool +" - @echo " + directories, and logs, run the following +" - @echo " + command: +" - @echo " + +" -ifeq ($(MAKE), gmake) - @echo " + $(MAKE) uninstall-all +" -else - @echo " + $(MAKE) uninstall-all +" -endif - @echo " +-------------------------------------------+" - -uninstall-all: _uninstall - rm -rf $(DESTDIR)$(ASTLIBDIR) - rm -rf $(DESTDIR)$(ASTVARLIBDIR) - rm -rf $(DESTDIR)$(ASTDATADIR) - rm -rf $(DESTDIR)$(ASTSPOOLDIR) - rm -rf $(DESTDIR)$(ASTETCDIR) - rm -rf $(DESTDIR)$(ASTLOGDIR) - -menuconfig: menuselect - -gmenuconfig: gmenuselect - -menuselect: menuselect/menuselect menuselect-tree - -@menuselect/menuselect menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!" - -gmenuselect: menuselect/gmenuselect menuselect-tree - -@menuselect/gmenuselect menuselect.makeopts $(GLOBAL_MAKEOPTS) $(USER_MAKEOPTS) && (echo "menuselect changes saved!"; rm -f channels/h323/Makefile.ast main/asterisk) || echo "menuselect changes NOT saved!" - -menuselect/menuselect: makeopts menuselect/menuselect.c menuselect/menuselect_curses.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h makeopts - @CC="$(HOST_CC)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect CONFIGURE_SILENT="--silent" - -menuselect/gmenuselect: makeopts menuselect/menuselect.c menuselect/menuselect_gtk.c menuselect/menuselect_stub.c menuselect/menuselect.h menuselect/linkedlists.h makeopts - @CC="$(HOST_CC)" CXX="$(CXX)" LD="" AR="" RANLIB="" CFLAGS="" $(MAKE) -C menuselect _gmenuselect CONFIGURE_SILENT="--silent" - -menuselect-tree: $(foreach dir,$(filter-out main,$(MOD_SUBDIRS)),$(wildcard $(dir)/*.c) $(wildcard $(dir)/*.cc)) build_tools/cflags.xml build_tools/cflags-devmode.xml sounds/sounds.xml build_tools/embed_modules.xml configure - @echo "Generating input for menuselect ..." - @echo "" > $@ - @echo >> $@ - @echo "" >> $@ - @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} moduleinfo >> $@; done - @for dir in $(sort $(filter-out main,$(MOD_SUBDIRS))); do $(SUBMAKE) -C $${dir} SUBDIR=$${dir} makeopts >> $@; done - @cat build_tools/cflags.xml >> $@ - @if [ "${AST_DEVMODE}" = "yes" ]; then \ - cat build_tools/cflags-devmode.xml >> $@; \ - fi - @cat build_tools/embed_modules.xml >> $@ - @cat sounds/sounds.xml >> $@ - @echo "" >> $@ - -.PHONY: menuselect main sounds clean dist-clean distclean all prereqs cleantest uninstall _uninstall uninstall-all dont-optimize $(SUBDIRS_INSTALL) $(SUBDIRS_DIST_CLEAN) $(SUBDIRS_CLEAN) $(SUBDIRS_UNINSTALL) $(SUBDIRS) $(MOD_SUBDIRS_EMBED_LDSCRIPT) $(MOD_SUBDIRS_EMBED_LDFLAGS) $(MOD_SUBDIRS_EMBED_LIBS) badshell menuselect.makeopts installdirs _clean - -FORCE: diff --git a/Makefile.moddir_rules b/Makefile.moddir_rules deleted file mode 100644 index a0738fecb..000000000 --- a/Makefile.moddir_rules +++ /dev/null @@ -1,112 +0,0 @@ -# -# Asterisk -- A telephony toolkit for Linux. -# -# Makefile rules for subdirectories containing modules -# -# Copyright (C) 2006, Digium, Inc. -# -# Kevin P. Fleming -# -# This program is free software, distributed under the terms of -# the GNU General Public License -# - -ifeq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),) - ASTCFLAGS+=${GC_CFLAGS} -endif - -ifneq ($(findstring STATIC_BUILD,$(MENUSELECT_CFLAGS)),) - STATIC_BUILD=-static -endif - -include $(ASTTOPDIR)/Makefile.rules - -comma:=, - -$(addsuffix .o,$(C_MODS)): ASTCFLAGS+=-DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE)) -$(addsuffix .oo,$(CC_MODS)): ASTCFLAGS+=-DAST_MODULE=\"$*\" $(MENUSELECT_OPTS_$*:%=-D%) $(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_INCLUDE)) - -$(LOADABLE_MODS:%=%.so): ASTCFLAGS+=-fPIC -$(LOADABLE_MODS:%=%.so): LIBS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LIB)) -$(LOADABLE_MODS:%=%.so): ASTLDFLAGS+=$(foreach dep,$(MENUSELECT_DEPENDS_$*),$(value $(dep)_LDFLAGS)) - -$(addsuffix .so,$(filter $(LOADABLE_MODS),$(C_MODS))): %.so: %.o -$(addsuffix .so,$(filter $(LOADABLE_MODS),$(CC_MODS))): %.so: %.oo - -modules.link: $(addsuffix .o,$(filter $(EMBEDDED_MODS),$(C_MODS))) -modules.link: $(addsuffix .oo,$(filter $(EMBEDDED_MODS),$(CC_MODS))) - -.PHONY: clean uninstall _all moduleinfo makeopts - -ifneq ($(LOADABLE_MODS),) -_all: $(LOADABLE_MODS:%=%.so) -endif - -ifneq ($(EMBEDDED_MODS),) -_all: modules.link -__embed_ldscript: - @echo "../$(SUBDIR)/modules.link" -__embed_ldflags: - @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(C_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LDFLAGS))" - @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(CC_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LDFLAGS))" -__embed_libs: - @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(C_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LIB))" - @echo "$(foreach mod,$(filter $(EMBEDDED_MODS),$(CC_MODS)),$(foreach dep,$(MENUSELECT_DEPENDS_$(mod)),$(dep)_LIB))" -else -__embed_ldscript: -__embed_ldflags: -__embed_libs: -endif - -modules.link: - @rm -f $@ - @for file in $(patsubst %,$(SUBDIR)/%,$(filter %.o,$^)); do echo "INPUT (../$${file})" >> $@; done - @for file in $(patsubst %,$(SUBDIR)/%,$(filter-out %.o,$^)); do echo "INPUT (../$${file})" >> $@; done - -clean:: - rm -f *.so *.o *.oo *.s *.i *.ii - rm -f .*.d - rm -f modules.link - -install:: all - for x in $(LOADABLE_MODS:%=%.so); do $(INSTALL) -m 755 $$x $(DESTDIR)$(MODULES_DIR) ; done - -uninstall:: - -dist-clean:: - rm -f .*.moduleinfo .moduleinfo - rm -f .*.makeopts .makeopts - -.%.moduleinfo: %.c - @echo "" > $@ - $(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@ - echo "" >> $@ - -.%.moduleinfo: %.cc - @echo "" > $@ - $(AWK) -f $(ASTTOPDIR)/build_tools/get_moduleinfo $< >> $@ - echo "" >> $@ - -.moduleinfo:: $(addsuffix .moduleinfo,$(addprefix .,$(ALL_C_MODS) $(ALL_CC_MODS))) - @echo "" > $@ - @cat $^ >> $@ - @echo "" >> $@ - -moduleinfo: .moduleinfo - @cat $< - -.%.makeopts: %.c - @$(AWK) -f $(ASTTOPDIR)/build_tools/get_makeopts $< > $@ - -.%.makeopts: %.cc - @$(AWK) -f $(ASTTOPDIR)/build_tools/get_makeopts $< > $@ - -.makeopts:: $(addsuffix .makeopts,$(addprefix .,$(ALL_C_MODS) $(ALL_CC_MODS))) - @cat $^ > $@ - -makeopts: .makeopts - @cat $< - -ifneq ($(wildcard .*.d),) - include .*.d -endif diff --git a/Makefile.rules b/Makefile.rules deleted file mode 100644 index e4b174577..000000000 --- a/Makefile.rules +++ /dev/null @@ -1,129 +0,0 @@ -# -# Asterisk -- A telephony toolkit for Linux. -# -# Makefile rules -# -# Copyright (C) 2006-2008, Digium, Inc. -# -# Kevin P. Fleming -# -# This program is free software, distributed under the terms of -# the GNU General Public License -# - -# Each command is preceded by a short comment on what to do. -# Prefixing one or the other with @\# or @ or nothing makes the desired -# behaviour. ECHO_PREFIX prefixes the comment, CMD_PREFIX prefixes the command. - --include $(ASTTOPDIR)/makeopts - -.PHONY: dist-clean - -# If 'make' decides to create intermediate files to satisfy a build requirement -# (like producing a .i from a .c), we want to keep them, so tell make to keep -# all intermediate files -.SECONDARY: - -# extra cflags to build dependencies. Recursively expanded. -MAKE_DEPS=-MD -MT $@ -MF .$(subst /,_,$@).d -MP - -ifeq ($(NOISY_BUILD),) - ECHO_PREFIX=@ - CMD_PREFIX=@ -else - ECHO_PREFIX=@\# - CMD_PREFIX= -endif - -OPTIMIZE?=-O6 - -ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS)),) - # More GSM codec optimization - # Uncomment to enable MMXTM optimizations for x86 architecture CPU's - # which support MMX instructions. This should be newer pentiums, - # ppro's, etc, as well as the AMD K6 and K7. - #K6OPT=-DK6OPT - - ASTCFLAGS+=$(OPTIMIZE) -endif - -# shortcuts for common combinations of flags; these must be recursively expanded so that -# per-target settings will be applied -CC_CFLAGS=$(PTHREAD_CFLAGS) $(ASTCFLAGS) -CXX_CFLAGS=$(PTHREAD_CFLAGS) $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(AST_DECLARATION_AFTER_STATEMENT),$(ASTCFLAGS)) -CC_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -CXX_LDFLAGS_SO=$(PTHREAD_CFLAGS) $(ASTLDFLAGS) $(SOLINK) -CC_LIBS=$(PTHREAD_LIBS) $(LIBS) -CXX_LIBS=$(PTHREAD_LIBS) $(LIBS) - -# determine whether to double-compile so that the optimizer can report code path problems -# this is only done when developer mode and DONT_OPTIMIZE are both enabled -# in that case, we run the preprocessor to produce a .i or .ii file from the source -# code, then compile once with optimizer enabled (and the output to /dev/null), -# and if that doesn't fail then compile again with optimizer disabled -ifeq ($(findstring DONT_OPTIMIZE,$(MENUSELECT_CFLAGS))$(AST_DEVMODE),DONT_OPTIMIZEyes) -COMPILE_DOUBLE=yes -endif - -%.o: %.s - $(ECHO_PREFIX) echo " [AS] $< -> $@" -ifeq ($(COMPILE_DOUBLE),yes) - $(CMD_PREFIX) $(CC) -o /dev/null -c $< $(CC_CFLAGS) $(OPTIMIZE) -endif - $(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS) - -%.o: %.i - $(ECHO_PREFIX) echo " [CCi] $< -> $@" -ifeq ($(COMPILE_DOUBLE),yes) - $(CMD_PREFIX) $(CC) -o /dev/null -c $< $(CC_CFLAGS) $(OPTIMIZE) -endif - $(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS) - -ifneq ($(COMPILE_DOUBLE),yes) -%.o: %.c - $(ECHO_PREFIX) echo " [CC] $< -> $@" - $(CMD_PREFIX) $(CC) -o $@ -c $< $(CC_CFLAGS) $(MAKE_DEPS) -endif - -%.i: %.c - $(ECHO_PREFIX) echo " [CPP] $< -> $@" - $(CMD_PREFIX) $(CC) -o $@ -E $< $(CC_CFLAGS) $(MAKE_DEPS) - -%.oo: %.ii - $(ECHO_PREFIX) echo " [CXXi] $< -> $@" -ifeq ($(COMPILE_DOUBLE),yes) - $(CMD_PREFIX) $(CXX) -o /dev/null -c $< $(CXX_CFLAGS) $(OPTIMIZE) -endif - $(CMD_PREFIX) $(CXX) -o $@ -c $< $(CXX_CFLAGS) - -ifneq ($(COMPILE_DOUBLE),yes) -%.oo: %.cc - $(ECHO_PREFIX) echo " [CXX] $< -> $@" - $(CMD_PREFIX) $(CXX) -o $@ -c $< $(CXX_CFLAGS) $(MAKE_DEPS) -endif - -%.ii: %.cc - $(ECHO_PREFIX) echo " [CPP] $< -> $@" - $(CMD_PREFIX) $(CXX) -o $@ -E $< $(CXX_CFLAGS) $(MAKE_DEPS) - -%.c: %.y - $(ECHO_PREFIX) echo " [BISON] $< -> $@" - $(CMD_PREFIX) bison -o $@ -d --name-prefix=ast_yy $< - -%.c: %.fl - $(ECHO_PREFIX) echo " [FLEX] $< -> $@" - $(CMD_PREFIX) flex -o $@ --full $< - -%.so: %.o - $(ECHO_PREFIX) echo " [LD] $^ -> $@" - $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(CC_LDFLAGS_SO) $^ $(CC_LIBS) - -%.so: %.oo - $(ECHO_PREFIX) echo " [LDXX] $^ -> $@" - $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(CXX_LDFLAGS_SO) $^ $(CXX_LIBS) - -%: %.o - $(ECHO_PREFIX) echo " [LD] $^ -> $@" - $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(PTHREAD_CFLAGS) $(ASTLDFLAGS) $^ $(CXX_LIBS) - -dist-clean:: clean diff --git a/README b/README deleted file mode 100644 index 3530007a6..000000000 --- a/README +++ /dev/null @@ -1,262 +0,0 @@ -The Asterisk(R) Open Source PBX -by Mark Spencer -and the Asterisk.org developer community - -Copyright (C) 2001-2009 Digium, Inc. -and other copyright holders. -================================================================ - -* SECURITY - It is imperative that you read and fully understand the contents of -the security information file (doc/security.txt) before you attempt -to configure and run an Asterisk server. - -* WHAT IS ASTERISK? - Asterisk is an Open Source PBX and telephony toolkit. It is, in a -sense, middleware between Internet and telephony channels on the bottom, -and Internet and telephony applications at the top. For more information -on the project itself, please visit the Asterisk home page at: - - http://www.asterisk.org - -In addition you'll find lots of information compiled by the Asterisk -community on this Wiki: - - http://www.voip-info.org/wiki-Asterisk - -There is a book on Asterisk published by O'Reilly under the -Creative Commons License. It is available in book stores as well -as in a downloadable version on the http://www.asteriskdocs.org -web site. - -* SUPPORTED OPERATING SYSTEMS - -== Linux == - The Asterisk Open Source PBX is developed and tested primarily on the -GNU/Linux operating system, and is supported on every major GNU/Linux -distribution. - -== Others == - Asterisk has also been 'ported' and reportedly runs properly on other -operating systems as well, including Sun Solaris, Apple's Mac OS X, and -the BSD variants. - -* GETTING STARTED - - First, be sure you've got supported hardware (but note that you don't need -ANY special hardware, not even a soundcard) to install and run Asterisk. - - Supported telephony hardware includes: - - * All Wildcard (tm) products from Digium (www.digium.com) - * QuickNet Internet PhoneJack and LineJack (http://www.quicknet.net) - * any full duplex sound card supported by ALSA or OSS - * any ISDN card supported by mISDN on Linux (BRI) - * The Xorcom AstriBank channel bank - * VoiceTronix OpenLine products - -The are several drivers for ISDN BRI cards available from third party sources. -Check the voip-info.org wiki for more information on chan_capi and -zaphfc. - -* UPGRADING FROM AN EARLIER VERSION - - If you are updating from a previous version of Asterisk, make sure you -read the UPGRADE.txt file in the source directory. There are some files -and configuration options that you will have to change, even though we -made every effort possible to maintain backwards compatibility. - - In order to discover new features to use, please check the configuration -examples in the /configs directory of the source code distribution. -To discover the major new features of Asterisk 1.2, please visit -http://edvina.net/asterisk1-2/ - -* NEW INSTALLATIONS - - Ensure that your system contains a compatible compiler and development -libraries. Asterisk requires either the GNU Compiler Collection (GCC) version -3.0 or higher, or a compiler that supports the C99 specification and some of -the gcc language extensions. In addition, your system needs to have the C -library headers available, and the headers and libraries for OpenSSL, -ncurses and zlib. -On many distributions, these files are installed by packages with names like -'glibc-devel', 'ncurses-devel', 'openssl-devel' and 'zlib-devel' or similar. - - So let's proceed: - -1) Read this README file. - - There are more documents than this one in the doc/ directory. -You may also want to check the configuration files that contain -examples and reference guides. They are all in the configs/ -directory. - -2) Run "./configure" - - Execute the configure script to guess values for system-dependent -variables used during compilation. - -3) Run "make menuselect" [optional] - - This is needed if you want to select the modules that will be -compiled and to check modules dependencies. - -4) Run "make" - - Assuming the build completes successfully: - -5) Run "make install" - - Each time you update or checkout from the repository, you are strongly -encouraged to ensure all previous object files are removed to avoid internal -inconsistency in Asterisk. Normally, this is automatically done with -the presence of the file .cleancount, which increments each time a 'make clean' -is required, and the file .lastclean, which contains the last .cleancount used. - - If this is your first time working with Asterisk, you may wish to install -the sample PBX, with demonstration extensions, etc. If so, run: - -6) "make samples" - - Doing so will overwrite any existing config files you have. - - Finally, you can launch Asterisk in the foreground mode (not a daemon) -with: - -# asterisk -vvvc - - You'll see a bunch of verbose messages fly by your screen as Asterisk -initializes (that's the "very very verbose" mode). When it's ready, if -you specified the "c" then you'll get a command line console, that looks -like this: - -*CLI> - - You can type "help" at any time to get help with the system. For help -with a specific command, type "help ". To start the PBX using -your sound card, you can type "dial" to dial the PBX. Then you can use -"answer", "hangup", and "dial" to simulate the actions of a telephone. -Remember that if you don't have a full duplex sound card (and Asterisk -will tell you somewhere in its verbose messages if you do/don't) then it -won't work right (not yet). - - "man asterisk" at the Unix/Linux command prompt will give you detailed -information on how to start and stop Asterisk, as well as all the command -line options for starting Asterisk. - - Feel free to look over the configuration files in /etc/asterisk, where -you'll find a lot of information about what you can do with Asterisk. - -* ABOUT CONFIGURATION FILES - - All Asterisk configuration files share a common format. Comments are -delimited by ';' (since '#' of course, being a DTMF digit, may occur in -many places). A configuration file is divided into sections whose names -appear in []'s. Each section typically contains two types of statements, -those of the form 'variable = value', and those of the form 'object => -parameters'. Internally the use of '=' and '=>' is exactly the same, so -they're used only to help make the configuration file easier to -understand, and do not affect how it is actually parsed. - - Entries of the form 'variable=value' set the value of some parameter in -asterisk. For example, in chan_dahdi.conf, one might specify: - - switchtype=national - -in order to indicate to Asterisk that the switch they are connecting to is -of the type "national". In general, the parameter will apply to -instantiations which occur below its specification. For example, if the -configuration file read: - - switchtype = national - channel => 1-4 - channel => 10-12 - switchtype = dms100 - channel => 25-47 - -the "national" switchtype would be applied to channels one through -four and channels 10 through 12, whereas the "dms100" switchtype would -apply to channels 25 through 47. - - The "object => parameters" instantiates an object with the given -parameters. For example, the line "channel => 25-47" creates objects for -the channels 25 through 47 of the card, obtaining the settings -from the variables specified above. - -* SPECIAL NOTE ON TIME - - Those using SIP phones should be aware that Asterisk is sensitive to -large jumps in time. Manually changing the system time using date(1) -(or other similar commands) may cause SIP registrations and other -internal processes to fail. If your system cannot keep accurate time -by itself use NTP (http://www.ntp.org/) to keep the system clock -synchronized to "real time". NTP is designed to keep the system clock -synchronized by speeding up or slowing down the system clock until it -is synchronized to "real time" rather than by jumping the time and -causing discontinuities. Most Linux distributions include precompiled -versions of NTP. Beware of some time synchronization methods that get -the correct real time periodically and then manually set the system -clock. - - Apparent time changes due to daylight savings time are just that, -apparent. The use of daylight savings time in a Linux system is -purely a user interface issue and does not affect the operation of the -Linux kernel or Asterisk. The system clock on Linux kernels operates -on UTC. UTC does not use daylight savings time. - - Also note that this issue is separate from the clocking of TDM -channels, and is known to at least affect SIP registrations. - -* FILE DESCRIPTORS - - Depending on the size of your system and your configuration, -Asterisk can consume a large number of file descriptors. In UNIX, -file descriptors are used for more than just files on disk. File -descriptors are also used for handling network communication -(e.g. SIP, IAX2, or H.323 calls) and hardware access (e.g. analog and -digital trunk hardware). Asterisk accesses many on-disk files for -everything from configuration information to voicemail storage. - - Most systems limit the number of file descriptors that Asterisk can -have open at one time. This can limit the number of simultaneous -calls that your system can handle. For example, if the limit is set -at 1024 (a common default value) Asterisk can handle approxiately 150 -SIP calls simultaneously. To change the number of file descriptors -follow the instructions for your system below: - -== PAM-based Linux System == - - If your system uses PAM (Pluggable Authentication Modules) edit -/etc/security/limits.conf. Add these lines to the bottom of the file: - -root soft nofile 4096 -root hard nofile 8196 -asterisk soft nofile 4096 -asterisk hard nofile 8196 - -(adjust the numbers to taste). You may need to reboot the system for -these changes to take effect. - -== Generic UNIX System == - - If there are no instructions specifically adapted to your system -above you can try adding the command "ulimit -n 8192" to the script -that starts Asterisk. - -* MORE INFORMATION - - See the doc directory for more documentation on various features. Again, -please read all the configuration samples that include documentation on -the configuration options. - - Finally, you may wish to visit the web site and join the mailing list if -you're interested in getting more information. - - http://www.asterisk.org/support - - Welcome to the growing worldwide community of Asterisk users! - -Mark Spencer - ----- -Asterisk is a trademark belonging to Digium, inc diff --git a/UPGRADE-1.2.txt b/UPGRADE-1.2.txt deleted file mode 100644 index 7070bec11..000000000 --- a/UPGRADE-1.2.txt +++ /dev/null @@ -1,210 +0,0 @@ -========================================================= -=== Information for upgrading from Asterisk 1.0 to 1.2 -=== -=== -=== UPGRADE-1.2.txt -- Upgrade info for 1.0 to 1.2 -=== UPGRADE.txt -- Upgrade info for 1.2 to 1.4 -========================================================= - -Compiling: - -* The Asterisk 1.2 source code now uses C language features - supported only by 'modern' C compilers. Generally, this means GCC - version 3.0 or higher, although some GCC 2.96 releases will also - work. Some non-GCC compilers that support C99 and the common GCC - extensions (including anonymous structures and unions) will also - work. All releases of GCC 2.95 do _not_ have the requisite feature - support; systems using that compiler will need to be upgraded to - a more recent compiler release. - -Dialplan Expressions: - -* The dialplan expression parser (which handles $[ ... ] constructs) - has gone through a major upgrade, but has one incompatible change: - spaces are no longer required around expression operators, including - string comparisons. However, you can now use quoting to keep strings - together for comparison. For more details, please read the - doc/README.variables file, and check over your dialplan for possible - problems. - -Agents: - -* The default for ackcall has been changed to "no" instead of "yes" - because of a bug which caused the "yes" behavior to generally act like - "no". You may need to adjust the value if your agents behave - differently than you expect with respect to acknowledgement. - -* The AgentCallBackLogin application now requires a second '|' before - specifying an extension@context. This is to distinguish the options - string from the extension, so that they do not conflict. See - 'show application AgentCallbackLogin' for more details. - -Parking: - -* Parking behavior has changed slightly; when a parked call times out, - Asterisk will attempt to deliver the call back to the extension that - parked it, rather than the 's' extension. If that extension is busy - or unavailable, the parked call will be lost. - -Dialing: - -* The Caller*ID of the outbound leg is now the extension that was - called, rather than the Caller*ID of the inbound leg of the call. The - "o" flag for Dial can be used to restore the original behavior if - desired. Note that if you are looking for the originating callerid - from the manager event, there is a new manager event "Dial" which - provides the source and destination channels and callerid. - -IAX: - -* The naming convention for IAX channels has changed in two ways: - 1. The call number follows a "-" rather than a "/" character. - 2. The name of the channel has been simplified to IAX2/peer-callno, - rather than IAX2/peer@peer-callno or even IAX2/peer@peer/callno. - -SIP: - -* The global option "port" in 1.0.X that is used to set which port to - bind to has been changed to "bindport" to be more consistent with - the other channel drivers and to avoid confusion with the "port" - option for users/peers. - -* The "Registry" event now uses "Username" rather than "User" for - consistency with IAX. - -Applications: - -* With the addition of dialplan functions (which operate similarly - to variables), the SetVar application has been renamed to Set. - -* The CallerPres application has been removed. Use SetCallerPres - instead. It accepts both numeric and symbolic names. - -* The applications GetGroupCount, GetGroupMatchCount, SetGroup, and - CheckGroup have been deprecated in favor of functions. Here is a - table of their replacements: - - GetGroupCount([groupname][@category] GROUP_COUNT([groupname][@category]) Set(GROUPCOUNT=${GROUP_COUNT()}) - GroupMatchCount(groupmatch[@category]) GROUP_MATCH_COUNT(groupmatch[@category]) Set(GROUPCOUNT=${GROUP_MATCH_COUNT(SIP/.*)}) - SetGroup(groupname[@category]) GROUP([category])=groupname Set(GROUP()=test) - CheckGroup(max[@category]) N/A GotoIf($[ ${GROUP_COUNT()} > 5 ]?103) - - Note that CheckGroup does not have a direct replacement. There is - also a new function called GROUP_LIST() which will return a space - separated list of all of the groups set on a channel. The GROUP() - function can also return the name of the group set on a channel when - used in a read environment. - -* The applications DBGet and DBPut have been deprecated in favor of - functions. Here is a table of their replacements: - - DBGet(foo=family/key) Set(foo=${DB(family/key)}) - DBPut(family/key=${foo}) Set(DB(family/key)=${foo}) - -* The application SetLanguage has been deprecated in favor of the - function LANGUAGE(). - - SetLanguage(fr) Set(LANGUAGE()=fr) - - The LANGUAGE function can also return the currently set language: - - Set(MYLANG=${LANGUAGE()}) - -* The applications AbsoluteTimeout, DigitTimeout, and ResponseTimeout - have been deprecated in favor of the function TIMEOUT(timeouttype): - - AbsoluteTimeout(300) Set(TIMEOUT(absolute)=300) - DigitTimeout(15) Set(TIMEOUT(digit)=15) - ResponseTimeout(15) Set(TIMEOUT(response)=15) - - The TIMEOUT() function can also return the currently set timeouts: - - Set(DTIMEOUT=${TIMEOUT(digit)}) - -* The applications SetCIDName, SetCIDNum, and SetRDNIS have been - deprecated in favor of the CALLERID(datatype) function: - - SetCIDName(Joe Cool) Set(CALLERID(name)=Joe Cool) - SetCIDNum(2025551212) Set(CALLERID(number)=2025551212) - SetRDNIS(2024561414) Set(CALLERID(RDNIS)=2024561414) - -* The application Record now uses the period to separate the filename - from the format, rather than the colon. - -* The application VoiceMail now supports a 'temporary' greeting for each - mailbox. This greeting can be recorded by using option 4 in the - 'mailbox options' menu, and 'change your password' option has been - moved to option 5. - -* The application VoiceMailMain now only matches the 'default' context if - none is specified in the arguments. (This was the previously - documented behavior, however, we didn't follow that behavior.) The old - behavior can be restored by setting searchcontexts=yes in voicemail.conf. - -Queues: - -* A queue is now considered empty not only if there are no members but if - none of the members are available (e.g. agents not logged on). To - restore the original behavior, use "leavewhenempty=strict" or - "joinwhenempty=strict" instead of "=yes" for those options. - -* It is now possible to use multi-digit extensions in the exit context - for a queue (although you should not have overlapping extensions, - as there is no digit timeout). This means that the EXITWITHKEY event - in queue_log can now contain a key field with more than a single - character in it. - -Extensions: - -* By default, there is a new option called "autofallthrough" in - extensions.conf that is set to yes. Asterisk 1.0 (and earlier) - behavior was to wait for an extension to be dialed after there were no - more extensions to execute. "autofallthrough" changes this behavior - so that the call will immediately be terminated with BUSY, - CONGESTION, or HANGUP based on Asterisk's best guess. If you are - writing an extension for IVR, you must use the WaitExten application - if "autofallthrough" is set to yes. - -AGI: - -* AGI scripts did not always get SIGHUP at the end, previously. That - behavior has been fixed. If you do not want your script to terminate - at the end of AGI being called (e.g. on a hangup) then set SIGHUP to - be ignored within your application. - -* CallerID is reported with agi_callerid and agi_calleridname instead - of a single parameter holding both. - -Music On Hold: - -* The preferred format for musiconhold.conf has changed; please see the - sample configuration file for the new format. The existing format - is still supported but will generate warnings when the module is loaded. - -chan_modem: - -* All the chan_modem channel drivers (aopen, bestdata and i4l) are deprecated - in this release, and will be removed in the next major Asterisk release. - Please migrate to chan_misdn for ISDN interfaces; there is no upgrade - path for aopen and bestdata modem users. - -MeetMe: - -* The conference application now allows users to increase/decrease their - speaking volume and listening volume (independently of each other and - other users); the 'admin' and 'user' menus have changed, and new sound - files are included with this release. However, if a user calling in - over a Zaptel channel that does NOT have hardware DTMF detection - increases their speaking volume, it is likely they will no longer be - able to enter/exit the menu or make any further adjustments, as the - software DTMF detector will not be able to recognize the DTMF coming - from their device. - -GetVar Manager Action: - -* Previously, the behavior of the GetVar manager action reported the value - of a variable in the following manner: - > name: value - This has been changed to a manner similar to the SetVar action and is now - > Variable: name - > Value: value diff --git a/UPGRADE.txt b/UPGRADE.txt deleted file mode 100644 index 6dad890ea..000000000 --- a/UPGRADE.txt +++ /dev/null @@ -1,500 +0,0 @@ -========================================================= -=== Information for upgrading from Asterisk 1.2 to 1.4 -=== -=== -=== UPGRADE-1.2.txt -- Upgrade info for 1.0 to 1.2 -=== UPGRADE.txt -- Upgrade info for 1.2 to 1.4 -========================================================= - -Build Process (configure script): - -Asterisk now uses an autoconf-generated configuration script to learn how it -should build itself for your system. As it is a standard script, running: - -$ ./configure --help - -will show you all the options available. This script can be used to tell the -build process what libraries you have on your system (if it cannot find them -automatically), which libraries you wish to have ignored even though they may -be present, etc. - -You must run the configure script before Asterisk will build, although it will -attempt to automatically run it for you with no options specified; for most -users, that will result in a similar build to what they would have had before -the configure script was added to the build process (except for having to run -'make' again after the configure script is run). Note that the configure script -does NOT need to be re-run just to rebuild Asterisk; you only need to re-run it -when your system configuration changes or you wish to build Asterisk with -different options. - -Build Process (module selection): - -The Asterisk source tree now includes a basic module selection and build option -selection tool called 'menuselect'. Run 'make menuselect' to make your choices. -In this tool, you can disable building of modules that you don't care about, -turn on/off global options for the build and see which modules will not -(and cannot) be built because your system does not have the required external -dependencies installed. - -The resulting file from menuselect is called 'menuselect.makeopts'. Note that -the resulting menuselect.makeopts file generally contains which modules *not* -to build. The modules listed in this file indicate which modules have unmet -dependencies, a present conflict, or have been disabled by the user in the -menuselect interface. Compiler Flags can also be set in the menuselect -interface. In this case, the resulting file contains which CFLAGS are in use, -not which ones are not in use. - -If you would like to save your choices and have them applied against all -builds, the file can be copied to '~/.asterisk.makeopts' or -'/etc/asterisk.makeopts'. - -Build Process (Makefile targets): - -The 'valgrind' and 'dont-optimize' targets have been removed; their functionality -is available by enabling the DONT_OPTIMIZE setting in the 'Compiler Flags' menu -in the menuselect tool. - -It is now possible to run most make targets against a single subdirectory; from -the top level directory, for example, 'make channels' will run 'make all' in the -'channels' subdirectory. This also is true for 'clean', 'distclean' and 'depend'. - -Sound (prompt) and Music On Hold files: - -Beginning with Asterisk 1.4, the sound files and music on hold files supplied for -use with Asterisk have been replaced with new versions produced from high quality -master recordings, and are available in three languages (English, French and -Spanish) and in five formats (WAV (uncompressed), mu-Law, a-Law, GSM and G.729). -In addition, the music on hold files provided by FreePlay Music are now available -in the same five formats, but no longer available in MP3 format. - -The Asterisk 1.4 tarball packages will only include English prompts in GSM format, -(as were supplied with previous releases) and the FreePlay MOH files in WAV format. -All of the other variations can be installed by running 'make menuselect' and -selecting the packages you wish to install; when you run 'make install', those -packages will be downloaded and installed along with the standard files included -in the tarball. - -If for some reason you expect to not have Internet access at the time you will be -running 'make install', you can make your package selections using menuselect and -then run 'make sounds' to download (only) the sound packages; this will leave the -sound packages in the 'sounds' subdirectory to be used later during installation. - -WARNING: Asterisk 1.4 supports a new layout for sound files in multiple languages; -instead of the alternate-language files being stored in subdirectories underneath -the existing files (for French, that would be digits/fr, letters/fr, phonetic/fr, -etc.) the new layout creates one directory under /var/lib/asterisk/sounds for the -language itself, then places all the sound files for that language under that -directory and its subdirectories. This is the layout that will be created if you -select non-English languages to be installed via menuselect, HOWEVER Asterisk does -not default to this layout and will not find the files in the places it expects them -to be. If you wish to use this layout, make sure you put 'languageprefix=yes' in your -/etc/asterisk/asterisk.conf file, so that Asterisk will know how the files were -installed. - -PBX Core: - -* The (very old and undocumented) ability to use BYEXTENSION for dialing - instead of ${EXTEN} has been removed. - -* Builtin (res_features) transfer functionality attempts to use the context - defined in TRANSFER_CONTEXT variable of the transferer channel first. If - not set, it uses the transferee variable. If not set in any channel, it will - attempt to use the last non macro context. If not possible, it will default - to the current context. - -* The autofallthrough setting introduced in Asterisk 1.2 now defaults to 'yes'; - if your dialplan relies on the ability to 'run off the end' of an extension - and wait for a new extension without using WaitExten() to accomplish that, - you will need set autofallthrough to 'no' in your extensions.conf file. - -Command Line Interface: - -* 'show channels concise', designed to be used by applications that will parse - its output, previously used ':' characters to separate fields. However, some - of those fields can easily contain that character, making the output not - parseable. The delimiter has been changed to '!'. - -Applications: - -* In previous Asterisk releases, many applications would jump to priority n+101 - to indicate some kind of status or error condition. This functionality was - marked deprecated in Asterisk 1.2. An option to disable it was provided with - the default value set to 'on'. The default value for the global priority - jumping option is now 'off'. - -* The applications Cut, Sort, DBGet, DBPut, SetCIDNum, SetCIDName, SetRDNIS, - AbsoluteTimeout, DigitTimeout, ResponseTimeout, SetLanguage, GetGroupCount, - and GetGroupMatchCount were all deprecated in version 1.2, and therefore have - been removed in this version. You should use the equivalent dialplan - function in places where you have previously used one of these applications. - -* The application SetGlobalVar has been deprecated. You should replace uses - of this application with the following combination of Set and GLOBAL(): - Set(GLOBAL(name)=value). You may also access global variables exclusively by - using the GLOBAL() dialplan function, instead of relying on variable - interpolation falling back to globals when no channel variable is set. - -* The application SetVar has been renamed to Set. The syntax SetVar was marked - deprecated in version 1.2 and is no longer recognized in this version. The - use of Set with multiple argument pairs has also been deprecated. Please - separate each name/value pair into its own dialplan line. - -* app_read has been updated to use the newer options codes, using "skip" or - "noanswer" will not work. Use s or n. Also there is a new feature i, for - using indication tones, so typing in skip would give you unexpected results. - -* OSPAuth is added to authenticate OSP tokens in in_bound call setup messages. - -* The CONNECT event in the queue_log from app_queue now has a second field - in addition to the holdtime field. It contains the unique ID of the - queue member channel that is taking the call. This is useful when trying - to link recording filenames back to a particular call from the queue. - -* The old/current behavior of app_queue has a serial type behavior - in that the queue will make all waiting callers wait in the queue - even if there is more than one available member ready to take - calls until the head caller is connected with the member they - were trying to get to. The next waiting caller in line then - becomes the head caller, and they are then connected with the - next available member and all available members and waiting callers - waits while this happens. This cycle continues until there are - no more available members or waiting callers, whichever comes first. - The new behavior, enabled by setting autofill=yes in queues.conf - either at the [general] level to default for all queues or - to set on a per-queue level, makes sure that when the waiting - callers are connecting with available members in a parallel fashion - until there are no more available members or no more waiting callers, - whichever comes first. This is probably more along the lines of how - one would expect a queue should work and in most cases, you will want - to enable this new behavior. If you do not specify or comment out this - option, it will default to "no" to keep backward compatability with the old - behavior. - -* Queues depend on the channel driver reporting the proper state - for each member of the queue. To get proper signalling on - queue members that use the SIP channel driver, you need to - enable a call limit (could be set to a high value so it - is not put into action) and also make sure that both inbound - and outbound calls are accounted for. - - Example: - - [general] - limitonpeer = yes - - [peername] - type=friend - call-limit=10 - - -* The app_queue application now has the ability to use MixMonitor to - record conversations queue members are having with queue callers. Please - see configs/queues.conf.sample for more information on this option. - -* The app_queue application strategy called 'roundrobin' has been deprecated - for this release. Users are encouraged to use 'rrmemory' instead, since it - provides more 'true' round-robin call delivery. For the Asterisk 1.6 release, - 'rrmemory' will be renamed 'roundrobin'. - -* The app_queue application option called 'monitor-join' has been deprecated - for this release. Users are encouraged to use 'monitor-type=mixmonitor' instead, - since it provides the same functionality but is not dependent on soxmix or some - other external program in order to mix the audio. - -* app_meetme: The 'm' option (monitor) is renamed to 'l' (listen only), and - the 'm' option now provides the functionality of "initially muted". - In practice, most existing dialplans using the 'm' flag should not notice - any difference, unless the keypad menu is enabled, allowing the user - to unmute themsleves. - -* ast_play_and_record would attempt to cancel the recording if a DTMF - '0' was received. This behavior was not documented in most of the - applications that used ast_play_and_record and the return codes from - ast_play_and_record weren't checked for properly. - ast_play_and_record has been changed so that '0' no longer cancels a - recording. If you want to allow DTMF digits to cancel an - in-progress recording use ast_play_and_record_full which allows you - to specify which DTMF digits can be used to accept a recording and - which digits can be used to cancel a recording. - -* ast_app_messagecount has been renamed to ast_app_inboxcount. There is now a - new ast_app_messagecount function which takes a single context/mailbox/folder - mailbox specification and returns the message count for that folder only. - This addresses the deficiency of not being able to count the number of - messages in folders other than INBOX and Old. - -* The exit behavior of the AGI applications has changed. Previously, when - a connection to an AGI server failed, the application would cause the channel - to immediately stop dialplan execution and hangup. Now, the only time that - the AGI applications will cause the channel to stop dialplan execution is - when the channel itself requests hangup. The AGI applications now set an - AGISTATUS variable which will allow you to find out whether running the AGI - was successful or not. - - Previously, there was no way to handle the case where Asterisk was unable to - locally execute an AGI script for some reason. In this case, dialplan - execution will continue as it did before, but the AGISTATUS variable will be - set to "FAILURE". - - A locally executed AGI script can now exit with a non-zero exit code and this - failure will be detected by Asterisk. If an AGI script exits with a non-zero - exit code, the AGISTATUS variable will be set to "FAILURE" as opposed to - "SUCCESS". - -* app_voicemail: The ODBC_STORAGE capability now requires the extended table format - previously used only by EXTENDED_ODBC_STORAGE. This means that you will need to update - your table format using the schema provided in doc/odbcstorage.txt - -* app_waitforsilence: Fixes have been made to this application which changes the - default behavior with how quickly it returns. You can maintain "old-style" behavior - with the addition/use of a third "timeout" parameter. - Please consult the application documentation and make changes to your dialplan - if appropriate. - -Manager: - -* After executing the 'status' manager action, the "Status" manager events - included the header "CallerID:" which was actually only the CallerID number, - and not the full CallerID string. This header has been renamed to - "CallerIDNum". For compatibility purposes, the CallerID parameter will remain - until after the release of 1.4, when it will be removed. Please use the time - during the 1.4 release to make this transition. - -* The AgentConnect event now has an additional field called "BridgedChannel" - which contains the unique ID of the queue member channel that is taking the - call. This is useful when trying to link recording filenames back to - a particular call from the queue. - -* app_userevent has been modified to always send Event: UserEvent with the - additional header UserEvent: . Also, the Channel and UniqueID - headers are not automatically sent, unless you specify them as separate - arguments. Please see the application help for the new syntax. - -* app_meetme: Mute and Unmute events are now reported via the Manager API. - Native Manager API commands MeetMeMute and MeetMeUnmute are provided, which - are easier to use than "Action Command:". The MeetMeStopTalking event has - also been deprecated in favor of the already existing MeetmeTalking event - with a "Status" of "on" or "off" added. - -* OriginateFailure and OriginateSuccess events were replaced by event - OriginateResponse with a header named "Response" to indicate success or - failure - -Variables: - -* The builtin variables ${CALLERID}, ${CALLERIDNAME}, ${CALLERIDNUM}, - ${CALLERANI}, ${DNID}, ${RDNIS}, ${DATETIME}, ${TIMESTAMP}, ${ACCOUNTCODE}, - and ${LANGUAGE} have all been deprecated in favor of their related dialplan - functions. You are encouraged to move towards the associated dialplan - function, as these variables will be removed in a future release. - -* The CDR-CSV variables uniqueid, userfield, and basing time on GMT are now - adjustable from cdr.conf, instead of recompiling. - -* OSP applications exports several new variables, ${OSPINHANDLE}, - ${OSPOUTHANDLE}, ${OSPINTOKEN}, ${OSPOUTTOKEN}, ${OSPCALLING}, - ${OSPINTIMELIMIT}, and ${OSPOUTTIMELIMIT} - -* Builtin transfer functionality sets the variable ${TRANSFERERNAME} in the new - created channel. This variables holds the channel name of the transferer. - -* The dial plan variable PRI_CAUSE will be removed from future versions - of Asterisk. - It is replaced by adding a cause value to the hangup() application. - -Functions: - -* The function ${CHECK_MD5()} has been deprecated in favor of using an - expression: $[${MD5()} = ${saved_md5}]. - -* The 'builtin' functions that used to be combined in pbx_functions.so are - now built as separate modules. If you are not using 'autoload=yes' in your - modules.conf file then you will need to explicitly load the modules that - contain the functions you want to use. - -* The ENUMLOOKUP() function with the 'c' option (for counting the number of - records), but the lookup fails to match any records, the returned value will - now be "0" instead of blank. - -* The REALTIME() function is now available in version 1.4 and app_realtime has - been deprecated in favor of the new function. app_realtime will be removed - completely with the version 1.6 release so please take the time between - releases to make any necessary changes - -* The QUEUEAGENTCOUNT() function has been deprecated in favor of - QUEUE_MEMBER_COUNT(). - -The IAX2 channel: - -* It is possible that previous configurations depended on the order in which - peers and users were specified in iax.conf for forcing the order in which - chan_iax2 matched against them. This behavior is going away and is considered - deprecated in this version. Avoid having ambiguous peer and user entries and - to make things easy on yourself, always set the "username" option for users - so that the remote end can match on that exactly instead of trying to infer - which user you want based on host. - - If you would like to go ahead and use the new behavior which doesn't use the - order in the config file to influence matching order, then change the - MAX_PEER_BUCKETS define in chan_iax2.c to a value greater than one. An - example is provided there. By changing this, you will get *much* better - performance on systems that do a lot of peer and user lookups as they will be - stored in memory in a much more efficient manner. - -* The "mailboxdetail" option has been deprecated. Previously, if this option - was not enabled, the 2 byte MSGCOUNT information element would be set to all - 1's to indicate there there is some number of messages waiting. With this - option enabled, the number of new messages were placed in one byte and the - number of old messages are placed in the other. This is now the default - (and the only) behavior. - -The SIP channel: - -* The "incominglimit" setting is replaced by the "call-limit" setting in - sip.conf. - -* OSP support code is removed from SIP channel to OSP applications. ospauth - option in sip.conf is removed to osp.conf as authpolicy. allowguest option - in sip.conf cannot be set as osp anymore. - -* The Asterisk RTP stack has been changed in regards to RFC2833 reception - and transmission. Packets will now be sent with proper duration instead of all - at once. If you are receiving calls from a pre-1.4 Asterisk installation you - will want to turn on the rfc2833compensate option. Without this option your - DTMF reception may act poorly. - -* The $SIPUSERAGENT dialplan variable is deprecated and will be removed - in coming versions of Asterisk. Please use the dialplan function - SIPCHANINFO(useragent) instead. - -* The ALERT_INFO dialplan variable is deprecated and will be removed - in coming versions of Asterisk. Please use the dialplan application - sipaddheader() to add the "Alert-Info" header to the outbound invite. - -* The "canreinvite" option has changed. canreinvite=yes used to disable - re-invites if you had NAT=yes. In 1.4, you need to set canreinvite=nonat - to disable re-invites when NAT=yes. This is propably what you want. - The settings are now: "yes", "no", "nonat", "update". Please consult - sip.conf.sample for detailed information. - -The Zap channel: - -* Support for MFC/R2 has been removed, as it has not been functional for some - time and it has no maintainer. - -The Agent channel: - -* Callback mode (AgentCallbackLogin) is now deprecated, since the entire function - it provided can be done using dialplan logic, without requiring additional - channel and module locks (which frequently caused deadlocks). An example of - how to do this using AEL dialplan is in doc/queues-with-callback-members.txt. - -The G726-32 codec: - -* It has been determined that previous versions of Asterisk used the wrong codeword - packing order for G726-32 data. This version supports both available packing orders, - and can transcode between them. It also now selects the proper order when - negotiating with a SIP peer based on the codec name supplied in the SDP. However, - there are existing devices that improperly request one order and then use another; - Sipura and Grandstream ATAs are known to do this, and there may be others. To - be able to continue to use these devices with this version of Asterisk and the - G726-32 codec, a configuration parameter called 'g726nonstandard' has been added - to sip.conf, so that Asterisk can use the packing order expected by the device (even - though it requested a different order). In addition, the internal format number for - G726-32 has been changed, and the old number is now assigned to AAL2-G726-32. The - result of this is that this version of Asterisk will be able to interoperate over - IAX2 with older versions of Asterisk, as long as this version is told to allow - 'g726aal2' instead of 'g726' as the codec for the call. - -Installation: - -* On BSD systems, the installation directories have changed to more "FreeBSDish" - directories. On startup, Asterisk will look for the main configuration in - /usr/local/etc/asterisk/asterisk.conf - If you have an old installation, you might want to remove the binaries and - move the configuration files to the new locations. The following directories - are now default: - ASTLIBDIR /usr/local/lib/asterisk - ASTVARLIBDIR /usr/local/share/asterisk - ASTETCDIR /usr/local/etc/asterisk - ASTBINDIR /usr/local/bin/asterisk - ASTSBINDIR /usr/local/sbin/asterisk - -Music on Hold: - -* The music on hold handling has been changed in some significant ways in hopes - to make it work in a way that is much less confusing to users. Behavior will - not change if the same configuration is used from older versions of Asterisk. - However, there are some new configuration options that will make things work - in a way that makes more sense. - - Previously, many of the channel drivers had an option called "musicclass" or - something similar. This option set what music on hold class this channel - would *hear* when put on hold. Some people expected (with good reason) that - this option was to configure what music on hold class to play when putting - the bridged channel on hold. This option has now been deprecated. - - Two new music on hold related configuration options for channel drivers have - been introduced. Some channel drivers support both options, some just one, - and some support neither of them. Check the sample configuration files to see - which options apply to which channel driver. - - The "mohsuggest" option specifies which music on hold class to suggest to the - bridged channel when putting them on hold. The only way that this class can - be overridden is if the bridged channel has a specific music class set that - was done in the dialplan using Set(CHANNEL(musicclass)=something). - - The "mohinterpret" option is similar to the old "musicclass" option. It - specifies which music on hold class this channel would like to listen to when - put on hold. This music class is only effective if this channel has no music - class set on it from the dialplan and the bridged channel putting this one on - hold had no "mohsuggest" setting. - - The IAX2 and Zap channel drivers have an additional feature for the - "mohinterpret" option. If this option is set to "passthrough", then these - channel drivers will pass through the HOLD message in signalling instead of - starting music on hold on the channel. An example for how this would be - useful is in an enterprise network of Asterisk servers. When one phone on one - server puts a phone on a different server on hold, the remote server will be - responsible for playing the hold music to its local phone that was put on - hold instead of the far end server across the network playing the music. - -CDR Records: - -* The behavior of the "clid" field of the CDR has always been that it will - contain the callerid ANI if it is set, or the callerid number if ANI was not - set. When using the "callerid" option for various channel drivers, some - would set ANI and some would not. This has been cleared up so that all - channel drivers set ANI. If you would like to change the callerid number - on the channel from the dialplan and have that change also show up in the - CDR, then you *must* set CALLERID(ANI) as well as CALLERID(num). - -API: - -* There are some API functions that were not previously prefixed with the 'ast_' - prefix but now are; these include the ADSI, ODBC and AGI interfaces. If you - have a module that uses the services provided by res_adsi, res_odbc, or - res_agi, you will need to add ast_ prefixes to the functions that you call - from those modules. - -Formats: - -* format_wav: The GAIN preprocessor definition has been changed from 2 to 0 - in Asterisk 1.4. This change was made in response to user complaints of - choppiness or the clipping of loud signal peaks. The GAIN preprocessor - definition will be retained in Asterisk 1.4, but will be removed in a - future release. The use of GAIN for the increasing of voicemail message - volume should use the 'volgain' option in voicemail.conf - -iLBC Codec: - -* Previously, the Asterisk source code distribution included the iLBC - encoder/decoder source code, from Global IP Solutions - (http://www.gipscorp.com). This code is not licensed for - distribution, and thus has been removed from the Asterisk source - code distribution. If you wish to use codec_ilbc to support iLBC - channels in Asterisk, you can run the contrib/scripts/get_ilbc_source.sh - script to download the source and put it in the proper place in - the Asterisk build tree. Once that is done you can follow your normal - steps of building Asterisk. You will need to run 'menuselect' and enable - the iLBC codec in the 'Codec Translators' category. diff --git a/Zaptel-to-DAHDI.txt b/Zaptel-to-DAHDI.txt deleted file mode 100644 index a08fdea69..000000000 --- a/Zaptel-to-DAHDI.txt +++ /dev/null @@ -1,105 +0,0 @@ -========================================================= -=== Information for upgrading from Zaptel to DAHDI === -========================================================= - -As announced in early 2008, Digium is renaming the Zaptel telephony -interface project to DAHDI (Digium Asterisk Hardware Device Interface) -to accommodate the desires of the owner of the Zaptel trademark for -telephony purposes. - -This version of Asterisk can be built using either Zaptel or DAHDI, -and has many changes to make the use of DAHDI as easy as possible for -existing users with dialplans, CDR parsers, AMI applications, and -others that expect Zaptel to be in use. - -First, the modules that directly use services from Zaptel/DAHDI have been -renamed; the new names are: - - chan_zap.so -> chan_dahdi.so - app_zapbarge.so -> app_dahdibarge.so - app_zapras.so -> app_dahdiras.so - app_zapscan.so -> app_dahdiscan.so - codec_zap.so -> codec_dahdi.so - -However, in spite of the file name changes, the channels and -applications provided by these modules can still be used with 'Zap' -style names; see below for more information. - -Second, there are have been a number of efforts made to ensure that -existing systems will not have to have any major configuration changes -made solely because Asterisk was built against DAHDI instead of -Zaptel. This includes: - -chan_dahdi.so: - - This module will determine which channel name ('Zap' or 'DAHDI') - should be used for incoming and outgoing channels based on the - build-time choice of telephony drivers. However, if you wish to - continue using the 'Zap' channel name even though you built Asterisk - against the DAHDI drivers, you can add the following line to the - [options] section of your /etc/asterisk/asterisk.conf file: - - dahdichanname = no - - All CLI commands that begin with 'zap' are now available as 'dahdi' - commands as well; the 'zap' variants will report that they are - deprecated the first time you use each one in an Asterisk instance, - but they will otherwise operate just as they did in previous versions. - - All Asterisk Manager Interface (AMI) actions that begin with 'Zap' - are also available with 'DAHDI' prefixes. - - The ZapSendKeypadFacility dialplan application is now available as - DAHDISendKeypadFacility as well; the Zap variant will report a deprecation - warning but will otherwise operate as it did it in previous - versions. - - The configuration for the channel driver will be read from - /etc/asterisk/chan_dahdi.conf unless 'dahdichanname' has been set to - 'no' in asterisk.conf; if that is done, then the configuration will - be read from /etc/asterisk/zapata.conf, just as it was in previous - versions. - -app_dahdibarge.so: - - The ZapBarge application is now available as DAHDIBarge as well; the - ZapBarge variant will report a deprecation warning when used, but - will otherwise operate as it did in previous versions. Regardless of - which application name is used, the application will restrict itself - to channels of the proper type, based on the 'dahdichanname' setting - in asterisk.conf. - -app_dahdiras.so: - - The ZapRAS application is now available as DAHDIRAS as well; the - ZapRAS variant will report a deprecation warning when used, but will - otherwise operate as it did in previous versions. Regardless of - which application name is used, the application will restrict itself - to channels of the proper type, based on the 'dahdichanname' setting - in asterisk.conf. - -app_dahdiscan.so: - - The ZapScan application is now available as DAHDIScan as well; the - ZapScan variant will report a deprecation warning when used, but will - otherwise operate as it did in previous versions. Regardless of - which application name is used, the application will restrict itself - to channels of the proper type, based on the 'dahdichanname' setting - in asterisk.conf. - -app_flash.so: - - This application has not had any name changes, but will report its - usage (via 'show application flash') as being for either DAHDI or - Zaptel channels based on the 'dahdichanname' setting in - asterisk.conf. - -app_chanspy.so: - - This application will transparently create 'DAHDI' or 'Zap' channels - as needed, based on the 'dahdichanname' setting in asterisk.conf. - -app_meetme.so: - - This application will transparently create 'DAHDI' or 'Zap' channels - as needed, based on the 'dahdichanname' setting in asterisk.conf. diff --git a/agi/DialAnMp3.agi b/agi/DialAnMp3.agi deleted file mode 100644 index 59a54265e..000000000 --- a/agi/DialAnMp3.agi +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/perl -# -# Simple AGI application to play mp3's selected by a user both using -# xmms and over the phone itself. -# -$|=1; -while() { - 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 = ; -#checkresult($result); - -print STDERR "2. Getting song name...\n"; -print "GET DATA demo-enterkeywords\n"; -$result = ; -$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 = ; - if (&checkresult($result) < 0) { - exit 0; - } - print "EXEC MP3Player \"$key\"\n"; -# print "WAIT FOR DIGIT 60000\n"; - $result = ; - 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 = ; -checkresult($result); - diff --git a/agi/Makefile b/agi/Makefile deleted file mode 100644 index 7a13d2241..000000000 --- a/agi/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# Asterisk -- A telephony toolkit for Linux. -# -# Makefile for AGI-related stuff -# -# Copyright (C) 1999-2006, Digium -# -# Mark Spencer -# -# This program is free software, distributed under the terms of -# the GNU General Public License -# - -.PHONY: clean all uninstall - -AGIS=agi-test.agi eagi-test eagi-sphinx-test jukebox.agi - -ifeq ($(OSARCH),SunOS) - LIBS+=-lsocket -lnsl -endif - -include $(ASTTOPDIR)/Makefile.rules - -all: $(AGIS) - -strcompat.c: ../main/strcompat.c - @cp $< $@ - -eagi-test: eagi-test.o strcompat.o - -eagi-sphinx-test: eagi-sphinx-test.o - -install: all - mkdir -p $(DESTDIR)$(AGI_DIR) - for x in $(AGIS); do $(INSTALL) -m 755 $$x $(DESTDIR)$(AGI_DIR) ; done - -uninstall: - for x in $(AGIS); do rm -f $(DESTDIR)$(AGI_DIR)/$$x ; done - -clean: - rm -f *.so *.o look eagi-test eagi-sphinx-test - rm -f .*.d *.s *.i - rm -f strcompat.c - -ifneq ($(wildcard .*.d),) - include .*.d -endif diff --git a/agi/agi-test.agi b/agi/agi-test.agi deleted file mode 100644 index 4fc36eda8..000000000 --- a/agi/agi-test.agi +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/perl -use strict; - -$|=1; - -# Setup some variables -my %AGI; my $tests = 0; my $fail = 0; my $pass = 0; - -while() { - 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 = ; -&checkresult($result); - -print STDERR "2. Testing 'sendtext'..."; -print "SEND TEXT \"hello world\"\n"; -my $result = ; -&checkresult($result); - -print STDERR "3. Testing 'sendimage'..."; -print "SEND IMAGE asterisk-image\n"; -my $result = ; -&checkresult($result); - -print STDERR "4. Testing 'saynumber'..."; -print "SAY NUMBER 192837465 \"\"\n"; -my $result = ; -&checkresult($result); - -print STDERR "5. Testing 'waitdtmf'..."; -print "WAIT FOR DIGIT 1000\n"; -my $result = ; -&checkresult($result); - -print STDERR "6. Testing 'record'..."; -print "RECORD FILE testagi gsm 1234 3000\n"; -my $result = ; -&checkresult($result); - -print STDERR "6a. Testing 'record' playback..."; -print "STREAM FILE testagi \"\"\n"; -my $result = ; -&checkresult($result); - -print STDERR "================== Complete ======================\n"; -print STDERR "$tests tests completed, $pass passed, $fail failed\n"; -print STDERR "==================================================\n"; diff --git a/agi/eagi-sphinx-test.c b/agi/eagi-sphinx-test.c deleted file mode 100644 index 968e3cfc3..000000000 --- a/agi/eagi-sphinx-test.c +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Extended AGI test application - * - * This code is released into public domain - * without any warranty of any kind. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk.h" - -#include "asterisk/compat.h" - -#define AUDIO_FILENO (STDERR_FILENO + 1) - -#define SPHINX_HOST "192.168.1.108" -#define SPHINX_PORT 3460 - -static int sphinx_sock = -1; - -static int connect_sphinx(void) -{ - struct hostent *hp; - struct sockaddr_in sin; - int res; - hp = gethostbyname(SPHINX_HOST); - if (!hp) { - fprintf(stderr, "Unable to resolve '%s'\n", SPHINX_HOST); - return -1; - } - sphinx_sock = socket(PF_INET, SOCK_STREAM, 0); - if (sphinx_sock < 0) { - fprintf(stderr, "Unable to allocate socket: %s\n", strerror(errno)); - return -1; - } - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(SPHINX_PORT); - memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr)); - if (connect(sphinx_sock, (struct sockaddr *)&sin, sizeof(sin))) { - fprintf(stderr, "Unable to connect on socket: %s\n", strerror(errno)); - close(sphinx_sock); - sphinx_sock = -1; - return -1; - } - res = fcntl(sphinx_sock, F_GETFL); - if ((res < 0) || (fcntl(sphinx_sock, F_SETFL, res | O_NONBLOCK) < 0)) { - fprintf(stderr, "Unable to set flags on socket: %s\n", strerror(errno)); - close(sphinx_sock); - sphinx_sock = -1; - return -1; - } - return 0; -} - -static int read_environment(void) -{ - char buf[256]; - char *val; - /* Read environment */ - for(;;) { - if (!fgets(buf, sizeof(buf), stdin)) { - return -1; - } - if (feof(stdin)) - return -1; - buf[strlen(buf) - 1] = '\0'; - /* Check for end of environment */ - if (!strlen(buf)) - return 0; - val = strchr(buf, ':'); - if (!val) { - fprintf(stderr, "Invalid environment: '%s'\n", buf); - return -1; - } - *val = '\0'; - val++; - val++; - /* Skip space */ - fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); - - /* Load into normal environment */ - setenv(buf, val, 1); - - } - /* Never reached */ - return 0; -} - -static char *wait_result(void) -{ - fd_set fds; - int res; - int max; - static char astresp[256]; - static char sphinxresp[256]; - char audiobuf[4096]; - for (;;) { - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); - FD_SET(AUDIO_FILENO, &fds); - max = AUDIO_FILENO; - if (sphinx_sock > -1) { - FD_SET(sphinx_sock, &fds); - if (sphinx_sock > max) - max = sphinx_sock; - } - /* Wait for *some* sort of I/O */ - res = select(max + 1, &fds, NULL, NULL, NULL); - if (res < 0) { - fprintf(stderr, "Error in select: %s\n", strerror(errno)); - return NULL; - } - if (FD_ISSET(STDIN_FILENO, &fds)) { - if (!fgets(astresp, sizeof(astresp), stdin)) { - return NULL; - } - if (feof(stdin)) { - fprintf(stderr, "Got hungup on apparently\n"); - return NULL; - } - astresp[strlen(astresp) - 1] = '\0'; - fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); - return astresp; - } - if (FD_ISSET(AUDIO_FILENO, &fds)) { - res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf)); - if ((res > 0) && (sphinx_sock > -1)) { - if (write(sphinx_sock, audiobuf, res) < 0) { - fprintf(stderr, "write() failed: %s\n", strerror(errno)); - } - } - } - if ((sphinx_sock > -1) && FD_ISSET(sphinx_sock, &fds)) { - res = read(sphinx_sock, sphinxresp, sizeof(sphinxresp)); - if (res > 0) { - fprintf(stderr, "Oooh, Sphinx found a token: '%s'\n", sphinxresp); - return sphinxresp; - } else if (res == 0) { - fprintf(stderr, "Hrm, lost sphinx, guess we're on our own\n"); - close(sphinx_sock); - sphinx_sock = -1; - } - } - } - -} - -static char *run_command(char *command) -{ - fprintf(stdout, "%s\n", command); - return wait_result(); -} - -static int run_script(void) -{ - char *res; - res = run_command("STREAM FILE demo-enterkeywords 0123456789*#"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "1. Result is '%s'\n", res); - res = run_command("STREAM FILE demo-nomatch 0123456789*#"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "2. Result is '%s'\n", res); - res = run_command("SAY NUMBER 23452345 0123456789*#"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "3. Result is '%s'\n", res); - res = run_command("GET DATA demo-enterkeywords"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "4. Result is '%s'\n", res); - res = run_command("STREAM FILE auth-thankyou \"\""); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "5. Result is '%s'\n", res); - return 0; -} - -int main(int argc, char *argv[]) -{ - char *tmp; - int ver = 0; - int subver = 0; - /* Setup stdin/stdout for line buffering */ - setlinebuf(stdin); - setlinebuf(stdout); - if (read_environment()) { - fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); - exit(1); - } - connect_sphinx(); - tmp = getenv("agi_enhanced"); - if (tmp) { - if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) - ver = 0; - } - if (ver < 1) { - fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n"); - exit(1); - } - if (run_script()) - return -1; - exit(0); -} diff --git a/agi/eagi-test.c b/agi/eagi-test.c deleted file mode 100644 index 9bfbee79e..000000000 --- a/agi/eagi-test.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Extended AGI test application - * - * This code is released into the public domain - * with no warranty of any kind - */ - -#include -#include -#include -#include -#include -#include - -#include "asterisk.h" - -#include "asterisk/compat.h" - -#define AUDIO_FILENO (STDERR_FILENO + 1) - -static int read_environment(void) -{ - char buf[256]; - char *val; - /* Read environment */ - for(;;) { - if (!fgets(buf, sizeof(buf), stdin)) { - return -1; - } - if (feof(stdin)) - return -1; - buf[strlen(buf) - 1] = '\0'; - /* Check for end of environment */ - if (!strlen(buf)) - return 0; - val = strchr(buf, ':'); - if (!val) { - fprintf(stderr, "Invalid environment: '%s'\n", buf); - return -1; - } - *val = '\0'; - val++; - val++; - /* Skip space */ - fprintf(stderr, "Environment: '%s' is '%s'\n", buf, val); - - /* Load into normal environment */ - setenv(buf, val, 1); - - } - /* Never reached */ - return 0; -} - -static char *wait_result(void) -{ - fd_set fds; - int res; - int bytes = 0; - static char astresp[256]; - char audiobuf[4096]; - for (;;) { - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); - FD_SET(AUDIO_FILENO, &fds); - /* Wait for *some* sort of I/O */ - res = select(AUDIO_FILENO + 1, &fds, NULL, NULL, NULL); - if (res < 0) { - fprintf(stderr, "Error in select: %s\n", strerror(errno)); - return NULL; - } - if (FD_ISSET(STDIN_FILENO, &fds)) { - if (!fgets(astresp, sizeof(astresp), stdin)) { - return NULL; - } - if (feof(stdin)) { - fprintf(stderr, "Got hungup on apparently\n"); - return NULL; - } - astresp[strlen(astresp) - 1] = '\0'; - fprintf(stderr, "Ooh, got a response from Asterisk: '%s'\n", astresp); - return astresp; - } - if (FD_ISSET(AUDIO_FILENO, &fds)) { - res = read(AUDIO_FILENO, audiobuf, sizeof(audiobuf)); - if (res > 0) { - /* XXX Process the audio with sphinx here XXX */ -#if 0 - fprintf(stderr, "Got %d/%d bytes of audio\n", res, bytes); -#endif - bytes += res; - /* Prentend we detected some audio after 3 seconds */ - if (bytes > 16000 * 3) { - return "Sample Message"; - bytes = 0; - } - } - } - } - -} - -static char *run_command(char *command) -{ - fprintf(stdout, "%s\n", command); - return wait_result(); -} - -static int run_script(void) -{ - char *res; - res = run_command("STREAM FILE demo-enterkeywords 0123456789*#"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "1. Result is '%s'\n", res); - res = run_command("STREAM FILE demo-nomatch 0123456789*#"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "2. Result is '%s'\n", res); - res = run_command("SAY NUMBER 23452345 0123456789*#"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "3. Result is '%s'\n", res); - res = run_command("GET DATA demo-enterkeywords"); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "4. Result is '%s'\n", res); - res = run_command("STREAM FILE auth-thankyou \"\""); - if (!res) { - fprintf(stderr, "Failed to execute command\n"); - return -1; - } - fprintf(stderr, "5. Result is '%s'\n", res); - return 0; -} - -int main(int argc, char *argv[]) -{ - char *tmp; - int ver = 0; - int subver = 0; - /* Setup stdin/stdout for line buffering */ - setlinebuf(stdin); - setlinebuf(stdout); - if (read_environment()) { - fprintf(stderr, "Failed to read environment: %s\n", strerror(errno)); - exit(1); - } - tmp = getenv("agi_enhanced"); - if (tmp) { - if (sscanf(tmp, "%d.%d", &ver, &subver) != 2) - ver = 0; - } - if (ver < 1) { - fprintf(stderr, "No enhanced AGI services available. Use EAGI, not AGI\n"); - exit(1); - } - if (run_script()) - return -1; - exit(0); -} diff --git a/agi/fastagi-test b/agi/fastagi-test deleted file mode 100644 index d3f13cf6b..000000000 --- a/agi/fastagi-test +++ /dev/null @@ -1,94 +0,0 @@ -#!/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() { - 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 = ; - &checkresult($result); - - print STDERR "2. Testing 'sendtext'..."; - print CLIENT "SEND TEXT \"hello world\"\n"; - my $result = ; - &checkresult($result); - - print STDERR "3. Testing 'sendimage'..."; - print CLIENT "SEND IMAGE asterisk-image\n"; - my $result = ; - &checkresult($result); - - print STDERR "4. Testing 'saynumber'..."; - print CLIENT "SAY NUMBER 192837465 \"\"\n"; - my $result = ; - &checkresult($result); - - print STDERR "5. Testing 'waitdtmf'..."; - print CLIENT "WAIT FOR DIGIT 1000\n"; - my $result = ; - &checkresult($result); - - print STDERR "6. Testing 'record'..."; - print CLIENT "RECORD FILE testagi gsm 1234 3000\n"; - my $result = ; - &checkresult($result); - - print STDERR "6a. Testing 'record' playback..."; - print CLIENT "STREAM FILE testagi \"\"\n"; - my $result = ; - &checkresult($result); - close(CLIENT); - print STDERR "================== Complete ======================\n"; - print STDERR "$tests tests completed, $pass passed, $fail failed\n"; - print STDERR "==================================================\n"; -} - diff --git a/agi/jukebox.agi b/agi/jukebox.agi deleted file mode 100755 index 7bd9c10f9..000000000 --- a/agi/jukebox.agi +++ /dev/null @@ -1,488 +0,0 @@ -#!/usr/bin/perl -# -# Jukebox 0.2 -# -# A music manager for Asterisk. -# -# Copyright (C) 2005-2006, Justin Tunney -# -# Justin Tunney -# -# 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 () { - 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 = ; &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 = ; &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 = ; - $digit = &check_result($result); - if ($digit > 0) { - $digitstr .= chr($digit); - last; - } - } - for (;;) { - print "WAIT FOR DIGIT 3000\n"; - my $result = ; - $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; &check_result($result); - - `rm $link`; - - if (!$MENUAFTERSONG) { - print "EXEC Playback \"$FESTIVALCACHE/jukebox_thankyou\"\n"; - my $result = ; &check_result($result); - exit 0; - } else { - goto MYCONTINUE; - } - } - } - } - print "EXEC Playback \"$FESTIVALCACHE/jukebox_invalid\"\n"; - my $result = ; &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 = ; &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 = ; &check_result($result); - print "EXEC Playback \"$FESTIVALCACHE/jukebox_percent\"\n"; - my $result = ; &check_result($result); - $theTime = time(); - } - } -} - -# this function will fill the @masterCacheList of all the files that -# need to have text2speeced ulaw files of their names generated -sub music_dir_cache_genlist { - my $dir = shift; - if (!opendir(THEDIR, rmts($MUSIC.$dir))) { - print STDERR "Failed to open music directory: $dir\n"; - exit 1; - } - my @files = sort readdir THEDIR; - my $foundFiles = 0; - my $tmpMaxNum = 0; - foreach my $file (@files) { - chomp; - if ($file ne '.' && $file ne '..' && $file ne 'festivalcache') { # ignore special files - my $real_version = &rmts($MUSIC.$dir).'/'.$file; - my $cache_version = &rmts($FESTIVALCACHE.$dir).'/'.$file.'.ul'; - my $cache_version2 = &rmts($FESTIVALCACHE.$dir).'/'.$file; - my $cache_version_esc = &clean_file($cache_version); - my $cache_version2_esc = &clean_file($cache_version2); - - if (-d $real_version) { - if (music_dir_cache_genlist(rmts($dir).'/'.$file)) { - $tmpMaxNum++; - $maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber; - push(@masterCacheList, [1, $file, $cache_version_esc]) if ! -e $cache_version_esc; - $foundFiles = 1; - } - } elsif ($real_version =~ /\.mp3$/) { - $tmpMaxNum++; - $maxNumber = $tmpMaxNum if $tmpMaxNum > $maxNumber; - push(@masterCacheList, [2, &remove_file_extension($file), $cache_version_esc]) if ! -e $cache_version_esc; - $foundFiles = 1; - } - } - } - close(THEDIR); - return $foundFiles; -} - -sub rmts { # remove trailing slash - my $hog = shift; - $hog =~ s/\/$//; - return $hog; -} - -sub extract_file_name { - my $hog = shift; - $hog =~ /\/?([^\/]+)$/; - return $1; -} - -sub extract_file_dir { - my $hog = shift; - return $hog if ! ($hog =~ /\//); - $hog =~ /(.*)\/[^\/]*$/; - return $1; -} - -sub remove_file_extension { - my $hog = shift; - return $hog if ! ($hog =~ /\./); - $hog =~ /(.*)\.[^.]*$/; - return $1; -} - -sub clean_file { - my $hog = shift; - $hog =~ s/\\/_/g; - $hog =~ s/ /_/g; - $hog =~ s/\t/_/g; - $hog =~ s/\'/_/g; - $hog =~ s/\"/_/g; - $hog =~ s/\(/_/g; - $hog =~ s/\)/_/g; - $hog =~ s/&/_/g; - $hog =~ s/\[/_/g; - $hog =~ s/\]/_/g; - $hog =~ s/\$/_/g; - $hog =~ s/\|/_/g; - $hog =~ s/\^/_/g; - return $hog; -} - -sub remove_special_chars { - my $hog = shift; - $hog =~ s/\\//g; - $hog =~ s/ //g; - $hog =~ s/\t//g; - $hog =~ s/\'//g; - $hog =~ s/\"//g; - $hog =~ s/\(//g; - $hog =~ s/\)//g; - $hog =~ s/&//g; - $hog =~ s/\[//g; - $hog =~ s/\]//g; - $hog =~ s/\$//g; - $hog =~ s/\|//g; - $hog =~ s/\^//g; - return $hog; -} - -sub escape_file { - my $hog = shift; - $hog =~ s/\\/\\\\/g; - $hog =~ s/ /\\ /g; - $hog =~ s/\t/\\\t/g; - $hog =~ s/\'/\\\'/g; - $hog =~ s/\"/\\\"/g; - $hog =~ s/\(/\\\(/g; - $hog =~ s/\)/\\\)/g; - $hog =~ s/&/\\&/g; - $hog =~ s/\[/\\\[/g; - $hog =~ s/\]/\\\]/g; - $hog =~ s/\$/\\\$/g; - $hog =~ s/\|/\\\|/g; - $hog =~ s/\^/\\\^/g; - return $hog; -} - -sub check_result { - my ($res) = @_; - my $retval; - $tests++; - chomp $res; - if ($res =~ /^200/) { - $res =~ /result=(-?\d+)/; - if (!length($1)) { - print STDERR "FAIL ($res)\n"; - $fail++; - exit 1; - } else { - print STDERR "PASS ($1)\n"; - return $1; - } - } else { - print STDERR "FAIL (unexpected result '$res')\n"; - exit 1; - } -} diff --git a/agi/numeralize b/agi/numeralize deleted file mode 100644 index 5ca51913d..000000000 --- a/agi/numeralize +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/perl -# -# Build a database linking filenames to their numerical representations -# using a keypad for the DialAnMp3 application -# - -$mp3dir="/usr/media/mpeg3"; - -dbmopen(%DIGITS, "/var/lib/asterisk/mp3list", 0644) || die("Unable to open mp3list");; -sub process_dir { - my ($dir) = @_; - my $file; - my $digits; - my @entries; - opendir(DIR, $dir); - @entries = readdir(DIR); - closedir(DIR); - foreach $_ (@entries) { - if (!/^\./) { - $file = "$dir/$_"; - if (-d "$file") { - process_dir("$file"); - } else { - $digits = $_; - $digits =~ s/[^ \w]+//g; - $digits =~ s/\_/ /g; - $digits =~ tr/[a-z]/[A-Z]/; - $digits =~ tr/[A-C]/2/; - $digits =~ tr/[D-F]/3/; - $digits =~ tr/[G-I]/4/; - $digits =~ tr/[J-L]/5/; - $digits =~ tr/[M-O]/6/; - $digits =~ tr/[P-S]/7/; - $digits =~ tr/[T-V]/8/; - $digits =~ tr/[W-Z]/9/; - $digits =~ s/\s+/ /; - print "File: $file, digits: $digits\n"; - $DIGITS{$file} = $digits; - } - } - } -} - -process_dir($mp3dir); diff --git a/apps/Makefile b/apps/Makefile deleted file mode 100644 index c57926a6a..000000000 --- a/apps/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# -# Asterisk -- A telephony toolkit for Linux. -# -# Makefile for PBX applications -# -# Copyright (C) 1999-2006, Digium, Inc. -# -# This program is free software, distributed under the terms of -# the GNU General Public License -# - --include ../menuselect.makeopts ../menuselect.makedeps - -MENUSELECT_CATEGORY=APPS -MENUSELECT_DESCRIPTION=Applications - -ALL_C_MODS:=$(patsubst %.c,%,$(wildcard app_*.c)) -ALL_CC_MODS:=$(patsubst %.cc,%,$(wildcard app_*.cc)) - -C_MODS:=$(filter-out $(MENUSELECT_APPS),$(ALL_C_MODS)) -CC_MODS:=$(filter-out $(MENUSELECT_APPS),$(ALL_CC_MODS)) - -LOADABLE_MODS:=$(C_MODS) $(CC_MODS) - -ifneq ($(findstring apps,$(MENUSELECT_EMBED)),) - EMBEDDED_MODS:=$(LOADABLE_MODS) - LOADABLE_MODS:= -endif - -MENUSELECT_OPTS_app_directory:=$(MENUSELECT_OPTS_app_voicemail) -ifneq ($(findstring ODBC_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),) -MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_ODBC_STORAGE) -MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_ODBC_STORAGE) -endif -ifneq ($(findstring IMAP_STORAGE,$(MENUSELECT_OPTS_app_voicemail)),) -MENUSELECT_DEPENDS_app_voicemail+=$(MENUSELECT_DEPENDS_IMAP_STORAGE) -MENUSELECT_DEPENDS_app_directory+=$(MENUSELECT_DEPENDS_IMAP_STORAGE) -endif - -all: _all - -include $(ASTTOPDIR)/Makefile.moddir_rules diff --git a/apps/app_adsiprog.c b/apps/app_adsiprog.c deleted file mode 100644 index 750cc6fc7..000000000 --- a/apps/app_adsiprog.c +++ /dev/null @@ -1,1592 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Program Asterisk ADSI Scripts into phone - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - res_adsi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/adsi.h" -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/lock.h" - -static char *app = "ADSIProg"; - -static char *synopsis = "Load Asterisk ADSI Scripts into phone"; - -/* #define DUMP_MESSAGES */ - -static char *descrip = -" ADSIProg(script): This application programs an ADSI Phone with the given\n" -"script. If nothing is specified, the default script (asterisk.adsi) is used.\n"; - -struct adsi_event { - int id; - char *name; -}; - -static struct adsi_event events[] = { - { 1, "CALLERID" }, - { 2, "VMWI" }, - { 3, "NEARANSWER" }, - { 4, "FARANSWER" }, - { 5, "ENDOFRING" }, - { 6, "IDLE" }, - { 7, "OFFHOOK" }, - { 8, "CIDCW" }, - { 9, "BUSY" }, - { 10, "FARRING" }, - { 11, "DIALTONE" }, - { 12, "RECALL" }, - { 13, "MESSAGE" }, - { 14, "REORDER" }, - { 15, "DISTINCTIVERING" }, - { 16, "RING" }, - { 17, "REMINDERRING" }, - { 18, "SPECIALRING" }, - { 19, "CODEDRING" }, - { 20, "TIMER" }, - { 21, "INUSE" }, - { 22, "EVENT22" }, - { 23, "EVENT23" }, - { 24, "CPEID" }, -}; - -static struct adsi_event justify[] = { - { 0, "CENTER" }, - { 1, "RIGHT" }, - { 2, "LEFT" }, - { 3, "INDENT" }, -}; - -#define STATE_NORMAL 0 -#define STATE_INKEY 1 -#define STATE_INSUB 2 -#define STATE_INIF 3 - -#define MAX_RET_CODE 20 -#define MAX_SUB_LEN 255 -#define MAX_MAIN_LEN 1600 - -#define ARG_STRING (1 << 0) -#define ARG_NUMBER (1 << 1) - -struct adsi_soft_key { - char vname[40]; /* Which "variable" is associated with it */ - int retstrlen; /* Length of return string */ - int initlen; /* initial length */ - int id; - int defined; - char retstr[80]; /* Return string data */ -}; - -struct adsi_subscript { - char vname[40]; - int id; - int defined; - int datalen; - int inscount; - int ifinscount; - char *ifdata; - char data[2048]; -}; - -struct adsi_state { - char vname[40]; - int id; -}; - -struct adsi_flag { - char vname[40]; - int id; -}; - -struct adsi_display { - char vname[40]; - int id; - char data[70]; - int datalen; -}; - -struct adsi_script { - int state; - int numkeys; - int numsubs; - int numstates; - int numdisplays; - int numflags; - struct adsi_soft_key *key; - struct adsi_subscript *sub; - /* Pre-defined displays */ - struct adsi_display displays[63]; - /* ADSI States 1 (initial) - 254 */ - struct adsi_state states[256]; - /* Keys 2-63 */ - struct adsi_soft_key keys[62]; - /* Subscripts 0 (main) to 127 */ - struct adsi_subscript subs[128]; - /* Flags 1-7 */ - struct adsi_flag flags[7]; - - /* Stuff from adsi script */ - unsigned char sec[5]; - char desc[19]; - unsigned char fdn[5]; - int ver; -}; - - -static int process_token(void *out, char *src, int maxlen, int argtype) -{ - if ((strlen(src) > 1) && src[0] == '\"') { - /* This is a quoted string */ - if (!(argtype & ARG_STRING)) - return -1; - src++; - /* Don't take more than what's there */ - if (maxlen > strlen(src) - 1) - maxlen = strlen(src) - 1; - memcpy(out, src, maxlen); - ((char *)out)[maxlen] = '\0'; - } else if (!ast_strlen_zero(src) && (src[0] == '\\')) { - if (!(argtype & ARG_NUMBER)) - return -1; - /* Octal value */ - if (sscanf(src, "%o", (int *)out) != 1) - return -1; - if (argtype & ARG_STRING) { - /* Convert */ - *((unsigned int *)out) = htonl(*((unsigned int *)out)); - } - } else if ((strlen(src) > 2) && (src[0] == '0') && (tolower(src[1]) == 'x')) { - if (!(argtype & ARG_NUMBER)) - return -1; - /* Hex value */ - if (sscanf(src + 2, "%x", (unsigned int *)out) != 1) - return -1; - if (argtype & ARG_STRING) { - /* Convert */ - *((unsigned int *)out) = htonl(*((unsigned int *)out)); - } - } else if ((!ast_strlen_zero(src) && isdigit(src[0]))) { - if (!(argtype & ARG_NUMBER)) - return -1; - /* Hex value */ - if (sscanf(src, "%d", (int *)out) != 1) - return -1; - if (argtype & ARG_STRING) { - /* Convert */ - *((unsigned int *)out) = htonl(*((unsigned int *)out)); - } - } else - return -1; - return 0; -} - -static char *get_token(char **buf, char *script, int lineno) -{ - char *tmp = *buf; - char *keyword; - int quoted = 0; - /* Advance past any white space */ - while(*tmp && (*tmp < 33)) - tmp++; - if (!*tmp) - return NULL; - keyword = tmp; - while(*tmp && ((*tmp > 32) || quoted)) { - if (*tmp == '\"') { - quoted = !quoted; - } - tmp++; - } - if (quoted) { - ast_log(LOG_WARNING, "Mismatched quotes at line %d of %s\n", lineno, script); - return NULL; - } - *tmp = '\0'; - tmp++; - while(*tmp && (*tmp < 33)) - tmp++; - /* Note where we left off */ - *buf = tmp; - return keyword; -} - -static char *validdtmf = "123456789*0#ABCD"; - -static int send_dtmf(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char dtmfstr[80]; - char *a; - int bytes=0; - a = get_token(&args, script, lineno); - if (!a) { - ast_log(LOG_WARNING, "Expecting something to send for SENDDTMF at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(dtmfstr, a, sizeof(dtmfstr) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid token for SENDDTMF at line %d of %s\n", lineno, script); - return 0; - } - a = dtmfstr; - while(*a) { - if (strchr(validdtmf, *a)) { - *buf = *a; - buf++; - bytes++; - } else - ast_log(LOG_WARNING, "'%c' is not a valid DTMF tone at line %d of %s\n", *a, lineno, script); - a++; - } - return bytes; -} - -static int goto_line(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *page; - char *gline; - int line; - unsigned char cmd; - page = get_token(&args, script, lineno); - gline = get_token(&args, script, lineno); - if (!page || !gline) { - ast_log(LOG_WARNING, "Expecting page and line number for GOTOLINE at line %d of %s\n", lineno, script); - return 0; - } - if (!strcasecmp(page, "INFO")) { - cmd = 0; - } else if (!strcasecmp(page, "COMM")) { - cmd = 0x80; - } else { - ast_log(LOG_WARNING, "Expecting either 'INFO' or 'COMM' page, got got '%s' at line %d of %s\n", page, lineno, script); - return 0; - } - if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script); - return 0; - } - cmd |= line; - buf[0] = 0x8b; - buf[1] = cmd; - return 2; -} - -static int goto_line_rel(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *dir; - char *gline; - int line; - unsigned char cmd; - dir = get_token(&args, script, lineno); - gline = get_token(&args, script, lineno); - if (!dir || !gline) { - ast_log(LOG_WARNING, "Expecting direction and number of lines for GOTOLINEREL at line %d of %s\n", lineno, script); - return 0; - } - if (!strcasecmp(dir, "UP")) { - cmd = 0; - } else if (!strcasecmp(dir, "DOWN")) { - cmd = 0x20; - } else { - ast_log(LOG_WARNING, "Expecting either 'UP' or 'DOWN' direction, got '%s' at line %d of %s\n", dir, lineno, script); - return 0; - } - if (process_token(&line, gline, sizeof(line), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid line number '%s' at line %d of %s\n", gline, lineno, script); - return 0; - } - cmd |= line; - buf[0] = 0x8c; - buf[1] = cmd; - return 2; -} - -static int send_delay(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *gtime; - int ms; - gtime = get_token(&args, script, lineno); - if (!gtime) { - ast_log(LOG_WARNING, "Expecting number of milliseconds to wait at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(&ms, gtime, sizeof(ms), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid delay milliseconds '%s' at line %d of %s\n", gtime, lineno, script); - return 0; - } - buf[0] = 0x90; - if (id == 11) - buf[1] = ms / 100; - else - buf[1] = ms / 10; - return 2; -} - -static int set_state(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *gstate; - int state; - gstate = get_token(&args, script, lineno); - if (!gstate) { - ast_log(LOG_WARNING, "Expecting state number at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(&state, gstate, sizeof(state), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid state number '%s' at line %d of %s\n", gstate, lineno, script); - return 0; - } - buf[0] = id; - buf[1] = state; - return 2; -} - -static int cleartimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *tok; - tok = get_token(&args, script, lineno); - if (tok) - ast_log(LOG_WARNING, "Clearing timer requires no arguments ('%s') at line %d of %s\n", tok, lineno, script); - - buf[0] = id; - /* For some reason the clear code is different slightly */ - if (id == 7) - buf[1] = 0x10; - else - buf[1] = 0x00; - return 2; -} - -static struct adsi_flag *getflagbyname(struct adsi_script *state, char *name, char *script, int lineno, int create) -{ - int x; - for (x=0;xnumflags;x++) - if (!strcasecmp(state->flags[x].vname, name)) - return &state->flags[x]; - /* Return now if we're not allowed to create */ - if (!create) - return NULL; - if (state->numflags > 6) { - ast_log(LOG_WARNING, "No more flag space at line %d of %s\n", lineno, script); - return NULL; - } - ast_copy_string(state->flags[state->numflags].vname, name, sizeof(state->flags[state->numflags].vname)); - state->flags[state->numflags].id = state->numflags + 1; - state->numflags++; - return &state->flags[state->numflags-1]; -} - -static int setflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *tok; - char sname[80]; - struct adsi_flag *flag; - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Setting flag requires a flag number at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script); - return 0; - } - flag = getflagbyname(state, sname, script, lineno, 0); - if (!flag) { - ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script); - return 0; - } - buf[0] = id; - buf[1] = ((flag->id & 0x7) << 4) | 1; - return 2; -} - -static int clearflag(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *tok; - struct adsi_flag *flag; - char sname[80]; - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Clearing flag requires a flag number at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(sname, tok, sizeof(sname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid flag '%s' at line %d of %s\n", tok, lineno, script); - return 0; - } - flag = getflagbyname(state, sname, script, lineno, 0); - if (!flag) { - ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", sname, lineno, script); - return 0; - } - buf[0] = id; - buf[1] = ((flag->id & 0x7) << 4); - return 2; -} - -static int starttimer(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *tok; - int secs; - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Missing number of seconds at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(&secs, tok, sizeof(secs), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script); - return 0; - } - buf[0] = id; - buf[1] = 0x1; - buf[2] = secs; - return 3; -} - -static int geteventbyname(char *name) -{ - int x; - for (x=0;xnumkeys;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;xnumsubs;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;xnumstates;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;xnumdisplays;x++) - if (!strcasecmp(state->displays[x].vname, name)) - return &state->displays[x]; - /* Return now if we're not allowed to create */ - if (!create) - return NULL; - if (state->numdisplays > 61) { - ast_log(LOG_WARNING, "No more display space at line %d of %s\n", lineno, script); - return NULL; - } - ast_copy_string(state->displays[state->numdisplays].vname, name, sizeof(state->displays[state->numdisplays].vname)); - state->displays[state->numdisplays].id = state->numdisplays + 1; - state->numdisplays++; - return &state->displays[state->numdisplays-1]; -} - -static int showkeys(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *tok; - char newkey[80]; - int bytes; - unsigned char keyid[6]; - int x; - int flagid=0; - struct adsi_soft_key *key; - struct adsi_flag *flag; - - for (x=0;x<7;x++) { - /* Up to 6 key arguments */ - tok = get_token(&args, script, lineno); - if (!tok) - break; - if (!strcasecmp(tok, "UNLESS")) { - /* Check for trailing UNLESS flag */ - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script); - } else if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid flag name '%s' at line %d of %s\n", tok, lineno, script); - } else if (!(flag = getflagbyname(state, newkey, script, lineno, 0))) { - ast_log(LOG_WARNING, "Flag '%s' is undeclared at line %d of %s\n", newkey, lineno, script); - } else - flagid = flag->id; - if ((tok = get_token(&args, script, lineno))) - ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script); - break; - } - if (x > 5) { - ast_log(LOG_WARNING, "Only 6 keys can be defined, ignoring '%s' at line %d of %s\n", tok, lineno, script); - break; - } - if (process_token(newkey, tok, sizeof(newkey) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid token for key name: %s\n", tok); - continue; - } - - key = getkeybyname(state, newkey, script, lineno); - if (!key) - break; - keyid[x] = key->id; - } - buf[0] = id; - buf[1] = (flagid & 0x7) << 3 | (x & 0x7); - for (bytes=0;bytes", lineno, script); - return 0; - } - disp = getdisplaybyname(state, dispname, script, lineno, 0); - if (!disp) { - ast_log(LOG_WARNING, "Display '%s' is undefined at line %d of %s\n", dispname, lineno, script); - return 0; - } - - tok = get_token(&args, script, lineno); - if (!tok || strcasecmp(tok, "AT")) { - ast_log(LOG_WARNING, "Missing token 'AT' at line %d of %s\n", lineno, script); - return 0; - } - /* Get line number */ - tok = get_token(&args, script, lineno); - if (!tok || process_token(&line, tok, sizeof(line), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid line: '%s' at line %d of %s\n", tok ? tok : "", lineno, script); - return 0; - } - tok = get_token(&args, script, lineno); - if (tok && !strcasecmp(tok, "NOUPDATE")) { - cmd = 1; - tok = get_token(&args, script, lineno); - } - if (tok && !strcasecmp(tok, "UNLESS")) { - /* Check for trailing UNLESS flag */ - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Missing argument for UNLESS clause at line %d of %s\n", lineno, script); - } else if (process_token(&flag, tok, sizeof(flag), ARG_NUMBER)) { - ast_log(LOG_WARNING, "Invalid flag number '%s' at line %d of %s\n", tok, lineno, script); - } - if ((tok = get_token(&args, script, lineno))) - ast_log(LOG_WARNING, "Extra arguments after UNLESS clause: '%s' at line %d of %s\n", tok, lineno, script); - } - - buf[0] = id; - buf[1] = (cmd << 6) | (disp->id & 0x3f); - buf[2] = ((line & 0x1f) << 3) | (flag & 0x7); - return 3; -} - -static int cleardisplay(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *tok; - tok = get_token(&args, script, lineno); - if (tok) - ast_log(LOG_WARNING, "Clearing display requires no arguments ('%s') at line %d of %s\n", tok, lineno, script); - - buf[0] = id; - buf[1] = 0x00; - return 2; -} - -static int digitdirect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *tok; - tok = get_token(&args, script, lineno); - if (tok) - ast_log(LOG_WARNING, "Digitdirect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script); - - buf[0] = id; - buf[1] = 0x7; - return 2; -} - -static int clearcbone(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *tok; - tok = get_token(&args, script, lineno); - if (tok) - ast_log(LOG_WARNING, "CLEARCB1 requires no arguments ('%s') at line %d of %s\n", tok, lineno, script); - - buf[0] = id; - buf[1] = 0; - return 2; -} - -static int digitcollect(char *buf, char *name, int id, char *args, struct adsi_script *istate, char *script, int lineno) -{ - char *tok; - tok = get_token(&args, script, lineno); - if (tok) - ast_log(LOG_WARNING, "Digitcollect requires no arguments ('%s') at line %d of %s\n", tok, lineno, script); - - buf[0] = id; - buf[1] = 0xf; - return 2; -} - -static int subscript(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *tok; - char subscript[80]; - struct adsi_subscript *sub; - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid number of seconds '%s' at line %d of %s\n", tok, lineno, script); - return 0; - } - sub = getsubbyname(state, subscript, script, lineno); - if (!sub) - return 0; - buf[0] = 0x9d; - buf[1] = sub->id; - return 2; -} - -static int onevent(char *buf, char *name, int id, char *args, struct adsi_script *state, char *script, int lineno) -{ - char *tok; - char subscript[80]; - char sname[80]; - int sawin=0; - int event; - int snums[8]; - int scnt = 0; - int x; - struct adsi_subscript *sub; - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Missing event for 'ONEVENT' at line %d of %s\n", lineno, script); - return 0; - } - event = geteventbyname(tok); - if (event < 1) { - ast_log(LOG_WARNING, "'%s' is not a valid event name, at line %d of %s\n", args, lineno, script); - return 0; - } - tok = get_token(&args, script, lineno); - while ((!sawin && !strcasecmp(tok, "IN")) || - (sawin && !strcasecmp(tok, "OR"))) { - sawin = 1; - if (scnt > 7) { - ast_log(LOG_WARNING, "No more than 8 states may be specified for inclusion at line %d of %s\n", lineno, script); - return 0; - } - /* Process 'in' things */ - tok = get_token(&args, script, lineno); - if (process_token(sname, tok, sizeof(sname), ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid state name at line %d of %s\n", tok, lineno, script); - return 0; - } - if ((snums[scnt] = getstatebyname(state, sname, script, lineno, 0) < 0)) { - ast_log(LOG_WARNING, "State '%s' not declared at line %d of %s\n", sname, lineno, script); - return 0; - } - scnt++; - tok = get_token(&args, script, lineno); - if (!tok) - break; - } - if (!tok || strcasecmp(tok, "GOTO")) { - if (!tok) - tok = ""; - if (sawin) - ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'OR' at line %d of %s\n", tok, lineno, script); - else - ast_log(LOG_WARNING, "Got '%s' while looking for 'GOTO' or 'IN' at line %d of %s\n", tok, lineno, script); - } - tok = get_token(&args, script, lineno); - if (!tok) { - ast_log(LOG_WARNING, "Missing subscript to call at line %d of %s\n", lineno, script); - return 0; - } - if (process_token(subscript, tok, sizeof(subscript) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Invalid subscript '%s' at line %d of %s\n", tok, lineno, script); - return 0; - } - sub = getsubbyname(state, subscript, script, lineno); - if (!sub) - return 0; - buf[0] = 8; - buf[1] = event; - buf[2] = sub->id | 0x80; - for (x=0;x -1) && !strcasecmp(kcmds[x].name, code)) { - if (kcmds[x].add_args) { - res = kcmds[x].add_args(key->retstr + key->retstrlen, - code, kcmds[x].id, args, state, script, lineno); - if ((key->retstrlen + res - key->initlen) <= MAX_RET_CODE) - key->retstrlen += res; - else - ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script); - } else { - if ((unused = get_token(&args, script, lineno))) - ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", kcmds[x].name, lineno, script, unused); - if ((key->retstrlen + 1 - key->initlen) <= MAX_RET_CODE) { - key->retstr[key->retstrlen] = kcmds[x].id; - key->retstrlen++; - } else - ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", kcmds[x].name, key->vname, lineno, script); - } - return 0; - } - } - return -1; -} - -static int process_opcode(struct adsi_subscript *sub, char *code, char *args, struct adsi_script *state, char *script, int lineno) -{ - int x; - char *unused; - int res; - int max = sub->id ? MAX_SUB_LEN : MAX_MAIN_LEN; - for (x=0;x -1) && !strcasecmp(opcmds[x].name, code)) { - if (opcmds[x].add_args) { - res = opcmds[x].add_args(sub->data + sub->datalen, - code, opcmds[x].id, args, state, script, lineno); - if ((sub->datalen + res + 1) <= max) - sub->datalen += res; - else { - ast_log(LOG_WARNING, "No space for '%s' code in subscript '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script); - return -1; - } - } else { - if ((unused = get_token(&args, script, lineno))) - ast_log(LOG_WARNING, "'%s' takes no arguments at line %d of %s (token is '%s')\n", opcmds[x].name, lineno, script, unused); - if ((sub->datalen + 2) <= max) { - sub->data[sub->datalen] = opcmds[x].id; - sub->datalen++; - } else { - ast_log(LOG_WARNING, "No space for '%s' code in key '%s' at line %d of %s\n", opcmds[x].name, sub->vname, lineno, script); - return -1; - } - } - /* Separate commands with 0xff */ - sub->data[sub->datalen] = 0xff; - sub->datalen++; - sub->inscount++; - return 0; - } - } - return -1; -} - -static int adsi_process(struct adsi_script *state, char *buf, char *script, int lineno) -{ - char *keyword; - char *args; - char vname[256]; - char tmp[80]; - char tmp2[80]; - int lrci; - int wi; - int event; - struct adsi_display *disp; - struct adsi_subscript *newsub; - /* Find the first keyword */ - keyword = get_token(&buf, script, lineno); - if (!keyword) - return 0; - switch(state->state) { - case STATE_NORMAL: - if (!strcasecmp(keyword, "DESCRIPTION")) { - args = get_token(&buf, script, lineno); - if (args) { - if (process_token(state->desc, args, sizeof(state->desc) - 1, ARG_STRING)) - ast_log(LOG_WARNING, "'%s' is not a valid token for DESCRIPTION at line %d of %s\n", args, lineno, script); - } else - ast_log(LOG_WARNING, "Missing argument for DESCRIPTION at line %d of %s\n", lineno, script); - } else if (!strcasecmp(keyword, "VERSION")) { - args = get_token(&buf, script, lineno); - if (args) { - if (process_token(&state->ver, args, sizeof(state->ver) - 1, ARG_NUMBER)) - ast_log(LOG_WARNING, "'%s' is not a valid token for VERSION at line %d of %s\n", args, lineno, script); - } else - ast_log(LOG_WARNING, "Missing argument for VERSION at line %d of %s\n", lineno, script); - } else if (!strcasecmp(keyword, "SECURITY")) { - args = get_token(&buf, script, lineno); - if (args) { - if (process_token(state->sec, args, sizeof(state->sec) - 1, ARG_STRING | ARG_NUMBER)) - ast_log(LOG_WARNING, "'%s' is not a valid token for SECURITY at line %d of %s\n", args, lineno, script); - } else - ast_log(LOG_WARNING, "Missing argument for SECURITY at line %d of %s\n", lineno, script); - } else if (!strcasecmp(keyword, "FDN")) { - args = get_token(&buf, script, lineno); - if (args) { - if (process_token(state->fdn, args, sizeof(state->fdn) - 1, ARG_STRING | ARG_NUMBER)) - ast_log(LOG_WARNING, "'%s' is not a valid token for FDN at line %d of %s\n", args, lineno, script); - } else - ast_log(LOG_WARNING, "Missing argument for FDN at line %d of %s\n", lineno, script); - } else if (!strcasecmp(keyword, "KEY")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "KEY definition missing name at line %d of %s\n", lineno, script); - break; - } - if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script); - break; - } - state->key = getkeybyname(state, vname, script, lineno); - if (!state->key) { - ast_log(LOG_WARNING, "Out of key space at line %d of %s\n", lineno, script); - break; - } - if (state->key->defined) { - ast_log(LOG_WARNING, "Cannot redefine key '%s' at line %d of %s\n", vname, lineno, script); - break; - } - args = get_token(&buf, script, lineno); - if (!args || strcasecmp(args, "IS")) { - ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "", lineno, script); - break; - } - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "KEY definition missing short name at line %d of %s\n", lineno, script); - break; - } - if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY short name at line %d of %s\n", args, lineno, script); - break; - } - args = get_token(&buf, script, lineno); - if (args) { - if (strcasecmp(args, "OR")) { - ast_log(LOG_WARNING, "Expecting 'OR' but got '%s' instead at line %d of %s\n", args, lineno, script); - break; - } - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "KEY definition missing optional long name at line %d of %s\n", lineno, script); - break; - } - if (process_token(tmp2, args, sizeof(tmp2) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY long name at line %d of %s\n", args, lineno, script); - break; - } - } else { - ast_copy_string(tmp2, tmp, sizeof(tmp2)); - } - if (strlen(tmp2) > 18) { - ast_log(LOG_WARNING, "Truncating full name to 18 characters at line %d of %s\n", lineno, script); - tmp2[18] = '\0'; - } - if (strlen(tmp) > 7) { - ast_log(LOG_WARNING, "Truncating short name to 7 bytes at line %d of %s\n", lineno, script); - tmp[7] = '\0'; - } - /* Setup initial stuff */ - state->key->retstr[0] = 128; - /* 1 has the length */ - state->key->retstr[2] = state->key->id; - /* Put the Full name in */ - memcpy(state->key->retstr + 3, tmp2, strlen(tmp2)); - /* Update length */ - state->key->retstrlen = strlen(tmp2) + 3; - /* Put trailing 0xff */ - state->key->retstr[state->key->retstrlen++] = 0xff; - /* Put the short name */ - memcpy(state->key->retstr + state->key->retstrlen, tmp, strlen(tmp)); - /* Update length */ - state->key->retstrlen += strlen(tmp); - /* Put trailing 0xff */ - state->key->retstr[state->key->retstrlen++] = 0xff; - /* Record initial length */ - state->key->initlen = state->key->retstrlen; - state->state = STATE_INKEY; - } else if (!strcasecmp(keyword, "SUB")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script); - break; - } - if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script); - break; - } - state->sub = getsubbyname(state, vname, script, lineno); - if (!state->sub) { - ast_log(LOG_WARNING, "Out of subroutine space at line %d of %s\n", lineno, script); - break; - } - if (state->sub->defined) { - ast_log(LOG_WARNING, "Cannot redefine subroutine '%s' at line %d of %s\n", vname, lineno, script); - break; - } - /* Setup sub */ - state->sub->data[0] = 130; - /* 1 is the length */ - state->sub->data[2] = 0x0; /* Clear extensibility bit */ - state->sub->datalen = 3; - if (state->sub->id) { - /* If this isn't the main subroutine, make a subroutine label for it */ - state->sub->data[3] = 9; - state->sub->data[4] = state->sub->id; - /* 5 is length */ - state->sub->data[6] = 0xff; - state->sub->datalen = 7; - } - args = get_token(&buf, script, lineno); - if (!args || strcasecmp(args, "IS")) { - ast_log(LOG_WARNING, "Expecting 'IS', but got '%s' at line %d of %s\n", args ? args : "", lineno, script); - break; - } - state->state = STATE_INSUB; - } else if (!strcasecmp(keyword, "STATE")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "STATE definition missing name at line %d of %s\n", lineno, script); - break; - } - if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a STATE name at line %d of %s\n", args, lineno, script); - break; - } - if (getstatebyname(state, vname, script, lineno, 0)) { - ast_log(LOG_WARNING, "State '%s' is already defined at line %d of %s\n", vname, lineno, script); - break; - } - getstatebyname(state, vname, script, lineno, 1); - } else if (!strcasecmp(keyword, "FLAG")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "FLAG definition missing name at line %d of %s\n", lineno, script); - break; - } - if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a FLAG name at line %d of %s\n", args, lineno, script); - break; - } - if (getflagbyname(state, vname, script, lineno, 0)) { - ast_log(LOG_WARNING, "Flag '%s' is already defined\n", vname); - break; - } - getflagbyname(state, vname, script, lineno, 1); - } else if (!strcasecmp(keyword, "DISPLAY")) { - lrci = 0; - wi = 0; - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "SUB definition missing name at line %d of %s\n", lineno, script); - break; - } - if (process_token(vname, args, sizeof(vname) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid token for a KEY name at line %d of %s\n", args, lineno, script); - break; - } - if (getdisplaybyname(state, vname, script, lineno, 0)) { - ast_log(LOG_WARNING, "State '%s' is already defined\n", vname); - break; - } - disp = getdisplaybyname(state, vname, script, lineno, 1); - if (!disp) - break; - args = get_token(&buf, script, lineno); - if (!args || strcasecmp(args, "IS")) { - ast_log(LOG_WARNING, "Missing 'IS' at line %d of %s\n", lineno, script); - break; - } - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "Missing Column 1 text at line %d of %s\n", lineno, script); - break; - } - if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "Token '%s' is not valid column 1 text at line %d of %s\n", args, lineno, script); - break; - } - if (strlen(tmp) > 20) { - ast_log(LOG_WARNING, "Truncating column one to 20 characters at line %d of %s\n", lineno, script); - tmp[20] = '\0'; - } - memcpy(disp->data + 5, tmp, strlen(tmp)); - disp->datalen = strlen(tmp) + 5; - disp->data[disp->datalen++] = 0xff; - - args = get_token(&buf, script, lineno); - if (args && !process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) { - /* Got a column two */ - if (strlen(tmp) > 20) { - ast_log(LOG_WARNING, "Truncating column two to 20 characters at line %d of %s\n", lineno, script); - tmp[20] = '\0'; - } - memcpy(disp->data + disp->datalen, tmp, strlen(tmp)); - disp->datalen += strlen(tmp); - args = get_token(&buf, script, lineno); - } - while(args) { - if (!strcasecmp(args, "JUSTIFY")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "Qualifier 'JUSTIFY' requires an argument at line %d of %s\n", lineno, script); - break; - } - lrci = getjustifybyname(args); - if (lrci < 0) { - ast_log(LOG_WARNING, "'%s' is not a valid justification at line %d of %s\n", args, lineno, script); - break; - } - } else if (!strcasecmp(args, "WRAP")) { - wi = 0x80; - } else { - ast_log(LOG_WARNING, "'%s' is not a known qualifier at line %d of %s\n", args, lineno, script); - break; - } - args = get_token(&buf, script, lineno); - } - if (args) { - /* Something bad happened */ - break; - } - disp->data[0] = 129; - disp->data[1] = disp->datalen - 2; - disp->data[2] = ((lrci & 0x3) << 6) | disp->id; - disp->data[3] = wi; - disp->data[4] = 0xff; - } else { - ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in PROGRAM\n", keyword); - } - break; - case STATE_INKEY: - if (process_returncode(state->key, keyword, buf, state, script, lineno)) { - if (!strcasecmp(keyword, "ENDKEY")) { - /* Return to normal operation and increment current key */ - state->state = STATE_NORMAL; - state->key->defined = 1; - state->key->retstr[1] = state->key->retstrlen - 2; - state->key = NULL; - } else { - ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SOFTKEY definition at line %d of %s\n", keyword, lineno, script); - } - } - break; - case STATE_INIF: - if (process_opcode(state->sub, keyword, buf, state, script, lineno)) { - if (!strcasecmp(keyword, "ENDIF")) { - /* Return to normal SUB operation and increment current key */ - state->state = STATE_INSUB; - state->sub->defined = 1; - /* Store the proper number of instructions */ - state->sub->ifdata[2] = state->sub->ifinscount; - } else if (!strcasecmp(keyword, "GOTO")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "GOTO clause missing Subscript name at line %d of %s\n", lineno, script); - break; - } - if (process_token(tmp, args, sizeof(tmp) - 1, ARG_STRING)) { - ast_log(LOG_WARNING, "'%s' is not a valid subscript name token at line %d of %s\n", args, lineno, script); - break; - } - newsub = getsubbyname(state, tmp, script, lineno); - if (!newsub) - break; - /* Somehow you use GOTO to go to another place */ - state->sub->data[state->sub->datalen++] = 0x8; - state->sub->data[state->sub->datalen++] = state->sub->ifdata[1]; - state->sub->data[state->sub->datalen++] = newsub->id; - /* Terminate */ - state->sub->data[state->sub->datalen++] = 0xff; - /* Increment counters */ - state->sub->inscount++; - state->sub->ifinscount++; - } else { - ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in IF clause at line %d of %s\n", keyword, lineno, script); - } - } else - state->sub->ifinscount++; - break; - case STATE_INSUB: - if (process_opcode(state->sub, keyword, buf, state, script, lineno)) { - if (!strcasecmp(keyword, "ENDSUB")) { - /* Return to normal operation and increment current key */ - state->state = STATE_NORMAL; - state->sub->defined = 1; - /* Store the proper length */ - state->sub->data[1] = state->sub->datalen - 2; - if (state->sub->id) { - /* if this isn't main, store number of instructions, too */ - state->sub->data[5] = state->sub->inscount; - } - state->sub = NULL; - } else if (!strcasecmp(keyword, "IFEVENT")) { - args = get_token(&buf, script, lineno); - if (!args) { - ast_log(LOG_WARNING, "IFEVENT clause missing Event name at line %d of %s\n", lineno, script); - break; - } - event = geteventbyname(args); - if (event < 1) { - ast_log(LOG_WARNING, "'%s' is not a valid event\n", args); - break; - } - args = get_token(&buf, script, lineno); - if (!args || strcasecmp(args, "THEN")) { - ast_log(LOG_WARNING, "IFEVENT clause missing 'THEN' at line %d of %s\n", lineno, script); - break; - } - state->sub->ifinscount = 0; - state->sub->ifdata = state->sub->data + - state->sub->datalen; - /* Reserve header and insert op codes */ - state->sub->ifdata[0] = 0x1; - state->sub->ifdata[1] = event; - /* 2 is for the number of instructions */ - state->sub->ifdata[3] = 0xff; - state->sub->datalen += 4; - /* Update Subscript instruction count */ - state->sub->inscount++; - state->state = STATE_INIF; - } else { - ast_log(LOG_WARNING, "Invalid or Unknown keyword '%s' in SUB definition at line %d of %s\n", keyword, lineno, script); - } - } - break; - default: - ast_log(LOG_WARNING, "Can't process keyword '%s' in weird state %d\n", keyword, state->state); - } - return 0; -} - -static struct adsi_script *compile_script(char *script) -{ - FILE *f; - char fn[256]; - char buf[256]; - char *c; - int lineno=0; - int x, err; - struct adsi_script *scr; - if (script[0] == '/') - ast_copy_string(fn, script, sizeof(fn)); - else - snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, script); - f = fopen(fn, "r"); - if (!f) { - ast_log(LOG_WARNING, "Can't open file '%s'\n", fn); - return NULL; - } - if (!(scr = ast_calloc(1, sizeof(*scr)))) { - fclose(f); - return NULL; - } - /* Create "main" as first subroutine */ - getsubbyname(scr, "main", NULL, 0); - while(!feof(f)) { - if (!fgets(buf, sizeof(buf), f)) { - continue; - } - if (!feof(f)) { - lineno++; - /* Trim off trailing return */ - buf[strlen(buf) - 1] = '\0'; - c = strchr(buf, ';'); - /* Strip comments */ - if (c) - *c = '\0'; - if (!ast_strlen_zero(buf)) - adsi_process(scr, buf, script, lineno); - } - } - fclose(f); - /* Make sure we're in the main routine again */ - switch(scr->state) { - case STATE_NORMAL: - break; - case STATE_INSUB: - ast_log(LOG_WARNING, "Missing ENDSUB at end of file %s\n", script); - free(scr); - return NULL; - case STATE_INKEY: - ast_log(LOG_WARNING, "Missing ENDKEY at end of file %s\n", script); - free(scr); - return NULL; - } - err = 0; - - /* Resolve all keys and record their lengths */ - for (x=0;xnumkeys;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;xnumsubs;x++) { - if (!scr->subs[x].defined) { - ast_log(LOG_WARNING, "Subscript '%s' referenced but never defined in file %s\n", scr->subs[x].vname, fn); - err++; - } - if (x == (scr->numsubs - 1)) { - /* Clear out extension bit on last message */ - scr->subs[x].data[2] = 0x80; - } - } - - if (err) { - free(scr); - return NULL; - } - return scr; -} - -#ifdef DUMP_MESSAGES -static void dump_message(char *type, char *vname, unsigned char *buf, int buflen) -{ - int x; - printf("%s %s: [ ", type, vname); - for (x=0;xdesc, scr->fdn, scr->sec, scr->ver)) { - /* User rejected us for some reason */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "User rejected download attempt\n"); - ast_log(LOG_NOTICE, "User rejected download on channel %s\n", chan->name); - free(scr); - return -1; - } - - bytes = 0; - /* Start with key definitions */ - for (x=0;xnumkeys;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;xnumdisplays;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;xnumsubs;x++) { - if (bytes + scr->subs[x].datalen > 253) { - /* Send what we've collected so far */ - if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) { - ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x); - return -1; - } - bytes =0; - } - memcpy(buf + bytes, scr->subs[x].data, scr->subs[x].datalen); - bytes += scr->subs[x].datalen; -#ifdef DUMP_MESSAGES - dump_message("Sub", scr->subs[x].vname, scr->subs[x].data, scr->subs[x].datalen); -#endif - } - if (bytes) { - if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) { - ast_log(LOG_WARNING, "Unable to send chunk ending at %d\n", x); - return -1; - } - } - - - bytes = 0; - bytes += ast_adsi_display(buf, ADSI_INFO_PAGE, 1, ADSI_JUST_LEFT, 0, "Download complete.", ""); - bytes += ast_adsi_set_line(buf, ADSI_INFO_PAGE, 1); - if (ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY) < 0) - return -1; - if (ast_adsi_end_download(chan)) { - /* Download failed for some reason */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Download attempt failed\n"); - ast_log(LOG_NOTICE, "Download failed on %s\n", chan->name); - free(scr); - return -1; - } - free(scr); - ast_adsi_unload_session(chan); - return 0; -} - -static int adsi_exec(struct ast_channel *chan, void *data) -{ - int res=0; - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - if (ast_strlen_zero(data)) - data = "asterisk.adsi"; - - if (!ast_adsi_available(chan)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "ADSI Unavailable on CPE. Not bothering to try.\n"); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "ADSI Available on CPE. Attempting Upload.\n"); - res = adsi_prog(chan, data); - } - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - ast_module_user_hangup_all(); - - res = ast_unregister_application(app); - - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, adsi_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Asterisk ADSI Programming Application"); diff --git a/apps/app_alarmreceiver.c b/apps/app_alarmreceiver.c deleted file mode 100644 index 8afce25d5..000000000 --- a/apps/app_alarmreceiver.c +++ /dev/null @@ -1,841 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2004 - 2005 Steve Rodgers - * - * Steve Rodgers - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed 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 - * - * *** 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 -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/ulaw.h" -#include "asterisk/options.h" -#include "asterisk/app.h" -#include "asterisk/dsp.h" -#include "asterisk/config.h" -#include "asterisk/localtime.h" -#include "asterisk/callerid.h" -#include "asterisk/astdb.h" -#include "asterisk/utils.h" - -#define ALMRCV_CONFIG "alarmreceiver.conf" -#define ADEMCO_CONTACT_ID "ADEMCO_CONTACT_ID" - -struct event_node{ - char data[17]; - struct event_node *next; -}; - -typedef struct event_node event_node_t; - -static char *app = "AlarmReceiver"; - -static char *synopsis = "Provide support for receiving alarm reports from a burglar or fire alarm panel"; -static char *descrip = -" AlarmReceiver(): Only 1 signalling format is supported at this time: Ademco\n" -"Contact ID. This application should be called whenever there is an alarm\n" -"panel calling in to dump its events. The application will handshake with the\n" -"alarm panel, and receive events, validate them, handshake them, and store them\n" -"until the panel hangs up. Once the panel hangs up, the application will run the\n" -"system command specified by the eventcmd setting in alarmreceiver.conf and pipe\n" -"the events to the standard input of the application. The configuration file also\n" -"contains settings for DTMF timing, and for the loudness of the acknowledgement\n" -"tones.\n"; - -/* Config Variables */ - -static int fdtimeout = 2000; -static int sdtimeout = 200; -static int toneloudness = 4096; -static int log_individual_events = 0; -static char event_spool_dir[128] = {'\0'}; -static char event_app[128] = {'\0'}; -static char db_family[128] = {'\0'}; -static char time_stamp_format[128] = {"%a %b %d, %Y @ %H:%M:%S %Z"}; - -/* Misc variables */ - -static char event_file[14] = "/event-XXXXXX"; - -/* -* Attempt to access a database variable and increment it, -* provided that the user defined db-family in alarmreceiver.conf -* The alarmreceiver app will write statistics to a few variables -* in this family if it is defined. If the new key doesn't exist in the -* family, then create it and set its value to 1. -*/ - -static void database_increment( char *key ) -{ - int res = 0; - unsigned v; - char value[16]; - - - if (ast_strlen_zero(db_family)) - return; /* If not defined, don't do anything */ - - res = ast_db_get(db_family, key, value, sizeof(value) - 1); - - if(res){ - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Creating database entry %s and setting to 1\n", key); - /* Guess we have to create it */ - res = ast_db_put(db_family, key, "1"); - return; - } - - sscanf(value, "%u", &v); - v++; - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: New value for %s: %u\n", key, v); - - snprintf(value, sizeof(value), "%u", v); - - res = ast_db_put(db_family, key, value); - - if((res)&&(option_verbose >= 4)) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: database_increment write error\n"); - - return; -} - - -/* -* Build a MuLaw data block for a single frequency tone -*/ - -static void make_tone_burst(unsigned char *data, float freq, float loudness, int len, int *x) -{ - int i; - float val; - - for(i = 0; i < len; i++){ - val = loudness * sin((freq * 2.0 * M_PI * (*x)++)/8000.0); - data[i] = AST_LIN2MU((int)val); - } - - /* wrap back around from 8000 */ - - if (*x >= 8000) *x = 0; - return; -} - -/* -* Send a single tone burst for a specifed duration and frequency. -* Returns 0 if successful -*/ - -static int send_tone_burst(struct ast_channel *chan, float freq, int duration, int tldn) -{ - int res = 0; - int i = 0; - int x = 0; - struct ast_frame *f, wf; - - struct { - unsigned char offset[AST_FRIENDLY_OFFSET]; - unsigned char buf[640]; - } tone_block; - - for(;;) - { - - if (ast_waitfor(chan, -1) < 0){ - res = -1; - break; - } - - f = ast_read(chan); - if (!f){ - res = -1; - break; - } - - if (f->frametype == AST_FRAME_VOICE) { - wf.frametype = AST_FRAME_VOICE; - wf.subclass = AST_FORMAT_ULAW; - wf.offset = AST_FRIENDLY_OFFSET; - wf.mallocd = 0; - wf.data = tone_block.buf; - wf.datalen = f->datalen; - wf.samples = wf.datalen; - - make_tone_burst(tone_block.buf, freq, (float) tldn, wf.datalen, &x); - - i += wf.datalen / 8; - if (i > duration) { - ast_frfree(f); - break; - } - if (ast_write(chan, &wf)){ - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Failed to write frame on %s\n", chan->name); - ast_log(LOG_WARNING, "AlarmReceiver Failed to write frame on %s\n",chan->name); - res = -1; - ast_frfree(f); - break; - } - } - - ast_frfree(f); - } - return res; -} - -/* -* Receive a string of DTMF digits where the length of the digit string is known in advance. Do not give preferential -* treatment to any digit value, and allow separate time out values to be specified for the first digit and all subsequent -* digits. -* -* Returns 0 if all digits successfully received. -* Returns 1 if a digit time out occurred -* Returns -1 if the caller hung up or there was a channel error. -* -*/ - -static int receive_dtmf_digits(struct ast_channel *chan, char *digit_string, int length, int fdto, int sdto) -{ - int res = 0; - int i = 0; - int r; - struct ast_frame *f; - struct timeval lastdigittime; - - lastdigittime = ast_tvnow(); - for(;;){ - /* if outa time, leave */ - if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > - ((i > 0) ? sdto : fdto)){ - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: DTMF Digit Timeout on %s\n", chan->name); - - ast_log(LOG_DEBUG,"AlarmReceiver: DTMF timeout on chan %s\n",chan->name); - - res = 1; - break; - } - - if ((r = ast_waitfor(chan, -1) < 0)) { - ast_log(LOG_DEBUG, "Waitfor returned %d\n", r); - continue; - } - - f = ast_read(chan); - - if (f == NULL){ - res = -1; - break; - } - - /* If they hung up, leave */ - if ((f->frametype == AST_FRAME_CONTROL) && - (f->subclass == AST_CONTROL_HANGUP)){ - ast_frfree(f); - res = -1; - break; - } - - /* if not DTMF, just do it again */ - if (f->frametype != AST_FRAME_DTMF){ - ast_frfree(f); - continue; - } - - digit_string[i++] = f->subclass; /* save digit */ - - ast_frfree(f); - - /* If we have all the digits we expect, leave */ - if(i >= length) - break; - - lastdigittime = ast_tvnow(); - } - - digit_string[i] = '\0'; /* Nul terminate the end of the digit string */ - return res; - -} - -/* -* Write the metadata to the log file -*/ - -static int write_metadata( FILE *logfile, char *signalling_type, struct ast_channel *chan) -{ - int res = 0; - time_t t; - struct tm now; - char *cl,*cn; - char workstring[80]; - char timestamp[80]; - - /* Extract the caller ID location */ - if (chan->cid.cid_num) - ast_copy_string(workstring, chan->cid.cid_num, sizeof(workstring)); - workstring[sizeof(workstring) - 1] = '\0'; - - ast_callerid_parse(workstring, &cn, &cl); - if (cl) - ast_shrink_phone_number(cl); - - - /* Get the current time */ - - time(&t); - ast_localtime(&t, &now, NULL); - - /* Format the time */ - - strftime(timestamp, sizeof(timestamp), time_stamp_format, &now); - - - res = fprintf(logfile, "\n\n[metadata]\n\n"); - - if(res >= 0) - res = fprintf(logfile, "PROTOCOL=%s\n", signalling_type); - - if(res >= 0) - res = fprintf(logfile, "CALLINGFROM=%s\n", (!cl) ? "" : cl); - - if(res >- 0) - res = fprintf(logfile, "CALLERNAME=%s\n", (!cn) ? "" : cn); - - if(res >= 0) - res = fprintf(logfile, "TIMESTAMP=%s\n\n", timestamp); - - if(res >= 0) - res = fprintf(logfile, "[events]\n\n"); - - if(res < 0){ - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't write metadata\n"); - - ast_log(LOG_DEBUG,"AlarmReceiver: can't write metadata\n"); - } - else - res = 0; - - return res; -} - -/* -* Write a single event to the log file -*/ - -static int write_event( FILE *logfile, event_node_t *event) -{ - int res = 0; - - if( fprintf(logfile, "%s\n", event->data) < 0) - res = -1; - - return res; -} - - -/* -* If we are configured to log events, do so here. -* -*/ - -static int log_events(struct ast_channel *chan, char *signalling_type, event_node_t *event) -{ - - int res = 0; - char workstring[sizeof(event_spool_dir)+sizeof(event_file)] = ""; - int fd; - FILE *logfile; - event_node_t *elp = event; - - if (!ast_strlen_zero(event_spool_dir)) { - - /* Make a template */ - - ast_copy_string(workstring, event_spool_dir, sizeof(workstring)); - strncat(workstring, event_file, sizeof(workstring) - strlen(workstring) - 1); - - /* Make the temporary file */ - - fd = mkstemp(workstring); - - if(fd == -1){ - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: can't make temporary file\n"); - ast_log(LOG_DEBUG,"AlarmReceiver: can't make temporary file\n"); - res = -1; - } - - if(!res){ - logfile = fdopen(fd, "w"); - if(logfile){ - /* Write the file */ - res = write_metadata(logfile, signalling_type, chan); - if(!res) - while((!res) && (elp != NULL)){ - res = write_event(logfile, elp); - elp = elp->next; - } - if(!res){ - if(fflush(logfile) == EOF) - res = -1; - if(!res){ - if(fclose(logfile) == EOF) - res = -1; - } - } - } - else - res = -1; - } - } - - return res; -} - -/* -* This function implements the logic to receive the Ademco contact ID format. -* -* The function will return 0 when the caller hangs up, else a -1 if there was a problem. -*/ - -static int receive_ademco_contact_id( struct ast_channel *chan, void *data, int fdto, int sdto, int tldn, event_node_t **ehead) -{ - int i,j; - int res = 0; - int checksum; - char event[17]; - event_node_t *enew, *elp; - int got_some_digits = 0; - int events_received = 0; - int ack_retries = 0; - - static char digit_map[15] = "0123456789*#ABC"; - static unsigned char digit_weights[15] = {10,1,2,3,4,5,6,7,8,9,11,12,13,14,15}; - - database_increment("calls-received"); - - /* Wait for first event */ - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for first event from panel\n"); - - while(res >= 0){ - - if(got_some_digits == 0){ - - /* Send ACK tone sequence */ - - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 1400Hz 100ms burst (ACK)\n"); - - - res = send_tone_burst(chan, 1400.0, 100, tldn); - - if(!res) - res = ast_safe_sleep(chan, 100); - - if(!res){ - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Sending 2300Hz 100ms burst (ACK)\n"); - - res = send_tone_burst(chan, 2300.0, 100, tldn); - } - - } - - if( res >= 0) - res = receive_dtmf_digits(chan, event, sizeof(event) - 1, fdto, sdto); - - if (res < 0){ - - if(events_received == 0) - /* Hangup with no events received should be logged in the DB */ - database_increment("no-events-received"); - else{ - if(ack_retries){ - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: ACK retries during this call: %d\n", ack_retries); - - database_increment("ack-retries"); - } - } - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: App exiting...\n"); - res = -1; - break; - } - - if(res != 0){ - /* Didn't get all of the digits */ - if(option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Incomplete string: %s, trying again...\n", event); - - if(!got_some_digits){ - got_some_digits = (!ast_strlen_zero(event)) ? 1 : 0; - ack_retries++; - } - continue; - } - - got_some_digits = 1; - - if(option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Received Event %s\n", event); - ast_log(LOG_DEBUG, "AlarmReceiver: Received event: %s\n", event); - - /* Calculate checksum */ - - for(j = 0, checksum = 0; j < 16; j++){ - for(i = 0 ; i < sizeof(digit_map) ; i++){ - if(digit_map[i] == event[j]) - break; - } - - if(i == 16) - break; - - checksum += digit_weights[i]; - } - - if(i == 16){ - if(option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Bad DTMF character %c, trying again\n", event[j]); - continue; /* Bad character */ - } - - /* Checksum is mod(15) of the total */ - - checksum = checksum % 15; - - if (checksum) { - database_increment("checksum-errors"); - if (option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Nonzero checksum\n"); - ast_log(LOG_DEBUG, "AlarmReceiver: Nonzero checksum\n"); - continue; - } - - /* Check the message type for correctness */ - - if(strncmp(event + 4, "18", 2)){ - if(strncmp(event + 4, "98", 2)){ - database_increment("format-errors"); - if(option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "AlarmReceiver: Wrong message type\n"); - ast_log(LOG_DEBUG, "AlarmReceiver: Wrong message type\n"); - continue; - } - } - - events_received++; - - /* Queue the Event */ - if (!(enew = ast_calloc(1, sizeof(*enew)))) { - res = -1; - break; - } - - enew->next = NULL; - ast_copy_string(enew->data, event, sizeof(enew->data)); - - /* - * Insert event onto end of list - */ - - if(*ehead == NULL){ - *ehead = enew; - } - else{ - for(elp = *ehead; elp->next != NULL; elp = elp->next) - ; - - elp->next = enew; - } - - if(res > 0) - res = 0; - - /* Let the user have the option of logging the single event before sending the kissoff tone */ - - if((res == 0) && (log_individual_events)) - res = log_events(chan, ADEMCO_CONTACT_ID, enew); - - /* Wait 200 msec before sending kissoff */ - - if(res == 0) - res = ast_safe_sleep(chan, 200); - - /* Send the kissoff tone */ - - if(res == 0) - res = send_tone_burst(chan, 1400.0, 900, tldn); - } - - - return res; -} - - -/* -* This is the main function called by Asterisk Core whenever the App is invoked in the extension logic. -* This function will always return 0. -*/ - -static int alarmreceiver_exec(struct ast_channel *chan, void *data) -{ - int res = 0; - struct ast_module_user *u; - event_node_t *elp, *efree; - char signalling_type[64] = ""; - - event_node_t *event_head = NULL; - - u = ast_module_user_add(chan); - - /* Set write and read formats to ULAW */ - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Setting read and write formats to ULAW\n"); - - if (ast_set_write_format(chan,AST_FORMAT_ULAW)){ - ast_log(LOG_WARNING, "AlarmReceiver: Unable to set write format to Mu-law on %s\n",chan->name); - ast_module_user_remove(u); - return -1; - } - - if (ast_set_read_format(chan,AST_FORMAT_ULAW)){ - ast_log(LOG_WARNING, "AlarmReceiver: Unable to set read format to Mu-law on %s\n",chan->name); - ast_module_user_remove(u); - return -1; - } - - /* Set default values for this invokation of the application */ - - ast_copy_string(signalling_type, ADEMCO_CONTACT_ID, sizeof(signalling_type)); - - - /* Answer the channel if it is not already */ - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Answering channel\n"); - - if (chan->_state != AST_STATE_UP) { - - res = ast_answer(chan); - - if (res) { - ast_module_user_remove(u); - return -1; - } - } - - /* Wait for the connection to settle post-answer */ - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: Waiting for connection to stabilize\n"); - - res = ast_safe_sleep(chan, 1250); - - /* Attempt to receive the events */ - - if(!res){ - - /* Determine the protocol to receive in advance */ - /* Note: Ademco contact is the only one supported at this time */ - /* Others may be added later */ - - if(!strcmp(signalling_type, ADEMCO_CONTACT_ID)) - receive_ademco_contact_id(chan, data, fdtimeout, sdtimeout, toneloudness, &event_head); - else - res = -1; - } - - - - /* Events queued by receiver, write them all out here if so configured */ - - if((!res) && (log_individual_events == 0)){ - res = log_events(chan, signalling_type, event_head); - - } - - /* - * Do we exec a command line at the end? - */ - - if((!res) && (!ast_strlen_zero(event_app)) && (event_head)){ - ast_log(LOG_DEBUG,"Alarmreceiver: executing: %s\n", event_app); - ast_safe_system(event_app); - } - - /* - * Free up the data allocated in our linked list - */ - - for(elp = event_head; (elp != NULL);){ - efree = elp; - elp = elp->next; - free(efree); - } - - - ast_module_user_remove(u); - - return 0; -} - -/* -* Load the configuration from the configuration file -*/ - -static int load_config(void) -{ - struct ast_config *cfg; - const char *p; - - /* Read in the config file */ - - cfg = ast_config_load(ALMRCV_CONFIG); - - if(!cfg){ - - if(option_verbose >= 4) - ast_verbose(VERBOSE_PREFIX_4 "AlarmReceiver: No config file\n"); - return 0; - } - else{ - - - p = ast_variable_retrieve(cfg, "general", "eventcmd"); - - if(p){ - ast_copy_string(event_app, p, sizeof(event_app)); - event_app[sizeof(event_app) - 1] = '\0'; - } - - p = ast_variable_retrieve(cfg, "general", "loudness"); - if(p){ - toneloudness = atoi(p); - if(toneloudness < 100) - toneloudness = 100; - if(toneloudness > 8192) - toneloudness = 8192; - } - p = ast_variable_retrieve(cfg, "general", "fdtimeout"); - if(p){ - fdtimeout = atoi(p); - if(fdtimeout < 1000) - fdtimeout = 1000; - if(fdtimeout > 10000) - fdtimeout = 10000; - } - - p = ast_variable_retrieve(cfg, "general", "sdtimeout"); - if(p){ - sdtimeout = atoi(p); - if(sdtimeout < 110) - sdtimeout = 110; - if(sdtimeout > 4000) - sdtimeout = 4000; - - } - - p = ast_variable_retrieve(cfg, "general", "logindividualevents"); - if(p){ - log_individual_events = ast_true(p); - - } - - p = ast_variable_retrieve(cfg, "general", "eventspooldir"); - - if(p){ - ast_copy_string(event_spool_dir, p, sizeof(event_spool_dir)); - event_spool_dir[sizeof(event_spool_dir) - 1] = '\0'; - } - - p = ast_variable_retrieve(cfg, "general", "timestampformat"); - - if(p){ - ast_copy_string(time_stamp_format, p, sizeof(time_stamp_format)); - time_stamp_format[sizeof(time_stamp_format) - 1] = '\0'; - } - - p = ast_variable_retrieve(cfg, "general", "db-family"); - - if(p){ - ast_copy_string(db_family, p, sizeof(db_family)); - db_family[sizeof(db_family) - 1] = '\0'; - } - ast_config_destroy(cfg); - } - return 1; - -} - -/* -* These functions are required to implement an Asterisk App. -*/ - - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - if(load_config()) - return ast_register_application(app, alarmreceiver_exec, synopsis, descrip); - else - return AST_MODULE_LOAD_DECLINE; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Alarm Receiver for Asterisk"); diff --git a/apps/app_amd.c b/apps/app_amd.c deleted file mode 100644 index 52f474048..000000000 --- a/apps/app_amd.c +++ /dev/null @@ -1,430 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2003 - 2006, Aheeva Technology. - * - * Claude Klimos (claude.klimos@aheeva.com) - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - * - * A license has been granted to Digium (via disclaimer) for the use of - * this code. - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include - -#include "asterisk/module.h" -#include "asterisk/lock.h" -#include "asterisk/options.h" -#include "asterisk/channel.h" -#include "asterisk/dsp.h" -#include "asterisk/pbx.h" -#include "asterisk/config.h" -#include "asterisk/app.h" - - -static char *app = "AMD"; -static char *synopsis = "Attempts to detect answering machines"; -static char *descrip = -" AMD([initialSilence][|greeting][|afterGreetingSilence][|totalAnalysisTime]\n" -" [|minimumWordLength][|betweenWordsSilence][|maximumNumberOfWords]\n" -" [|silenceThreshold])\n" -" This application attempts to detect answering machines at the beginning\n" -" of outbound calls. Simply call this application after the call\n" -" has been answered (outbound only, of course).\n" -" When loaded, AMD reads amd.conf and uses the parameters specified as\n" -" default values. Those default values get overwritten when calling AMD\n" -" with parameters.\n" -"- 'initialSilence' is the maximum silence duration before the greeting. If\n" -" exceeded then MACHINE.\n" -"- 'greeting' is the maximum length of a greeting. If exceeded then MACHINE.\n" -"- 'afterGreetingSilence' is the silence after detecting a greeting.\n" -" If exceeded then HUMAN.\n" -"- 'totalAnalysisTime' is the maximum time allowed for the algorithm to decide\n" -" on a HUMAN or MACHINE.\n" -"- 'minimumWordLength'is the minimum duration of Voice to considered as a word.\n" -"- 'betweenWordsSilence' is the minimum duration of silence after a word to \n" -" consider the audio that follows as a new word.\n" -"- 'maximumNumberOfWords'is the maximum number of words in the greeting. \n" -" If exceeded then MACHINE.\n" -"- 'silenceThreshold' is the silence threshold.\n" -"This application sets the following channel variable upon completion:\n" -" AMDSTATUS - This is the status of the answering machine detection.\n" -" Possible values are:\n" -" MACHINE | HUMAN | NOTSURE | HANGUP\n" -" AMDCAUSE - Indicates the cause that led to the conclusion.\n" -" Possible values are:\n" -" TOOLONG-<%d total_time>\n" -" INITIALSILENCE-<%d silenceDuration>-<%d initialSilence>\n" -" HUMAN-<%d silenceDuration>-<%d afterGreetingSilence>\n" -" MAXWORDS-<%d wordsCount>-<%d maximumNumberOfWords>\n" -" LONGGREETING-<%d voiceDuration>-<%d greeting>\n"; - -#define STATE_IN_WORD 1 -#define STATE_IN_SILENCE 2 - -/* Some default values for the algorithm parameters. These defaults will be overwritten from amd.conf */ -static int dfltInitialSilence = 2500; -static int dfltGreeting = 1500; -static int dfltAfterGreetingSilence = 800; -static int dfltTotalAnalysisTime = 5000; -static int dfltMinimumWordLength = 100; -static int dfltBetweenWordsSilence = 50; -static int dfltMaximumNumberOfWords = 3; -static int dfltSilenceThreshold = 256; - -/* Set to the lowest ms value provided in amd.conf or application parameters */ -static int dfltMaxWaitTimeForFrame = 50; - -static void isAnsweringMachine(struct ast_channel *chan, void *data) -{ - int res = 0; - struct ast_frame *f = NULL; - struct ast_dsp *silenceDetector = NULL; - int dspsilence = 0, readFormat, framelength = 0; - int inInitialSilence = 1; - int inGreeting = 0; - int voiceDuration = 0; - int silenceDuration = 0; - int iTotalTime = 0; - int iWordsCount = 0; - int currentState = STATE_IN_WORD; - int previousState = STATE_IN_SILENCE; - int consecutiveVoiceDuration = 0; - char amdCause[256] = "", amdStatus[256] = ""; - char *parse = ast_strdupa(data); - - /* Lets set the initial values of the variables that will control the algorithm. - The initial values are the default ones. If they are passed as arguments - when invoking the application, then the default values will be overwritten - by the ones passed as parameters. */ - int initialSilence = dfltInitialSilence; - int greeting = dfltGreeting; - int afterGreetingSilence = dfltAfterGreetingSilence; - int totalAnalysisTime = dfltTotalAnalysisTime; - int minimumWordLength = dfltMinimumWordLength; - int betweenWordsSilence = dfltBetweenWordsSilence; - int maximumNumberOfWords = dfltMaximumNumberOfWords; - int silenceThreshold = dfltSilenceThreshold; - int maxWaitTimeForFrame = dfltMaxWaitTimeForFrame; - - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(argInitialSilence); - AST_APP_ARG(argGreeting); - AST_APP_ARG(argAfterGreetingSilence); - AST_APP_ARG(argTotalAnalysisTime); - AST_APP_ARG(argMinimumWordLength); - AST_APP_ARG(argBetweenWordsSilence); - AST_APP_ARG(argMaximumNumberOfWords); - AST_APP_ARG(argSilenceThreshold); - ); - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: %s %s %s (Fmt: %d)\n", chan->name ,chan->cid.cid_ani, chan->cid.cid_rdnis, chan->readformat); - - /* Lets parse the arguments. */ - if (!ast_strlen_zero(parse)) { - /* Some arguments have been passed. Lets parse them and overwrite the defaults. */ - AST_STANDARD_APP_ARGS(args, parse); - if (!ast_strlen_zero(args.argInitialSilence)) - initialSilence = atoi(args.argInitialSilence); - if (!ast_strlen_zero(args.argGreeting)) - greeting = atoi(args.argGreeting); - if (!ast_strlen_zero(args.argAfterGreetingSilence)) - afterGreetingSilence = atoi(args.argAfterGreetingSilence); - if (!ast_strlen_zero(args.argTotalAnalysisTime)) - totalAnalysisTime = atoi(args.argTotalAnalysisTime); - if (!ast_strlen_zero(args.argMinimumWordLength)) - minimumWordLength = atoi(args.argMinimumWordLength); - if (!ast_strlen_zero(args.argBetweenWordsSilence)) - betweenWordsSilence = atoi(args.argBetweenWordsSilence); - if (!ast_strlen_zero(args.argMaximumNumberOfWords)) - maximumNumberOfWords = atoi(args.argMaximumNumberOfWords); - if (!ast_strlen_zero(args.argSilenceThreshold)) - silenceThreshold = atoi(args.argSilenceThreshold); - } else if (option_debug) - ast_log(LOG_DEBUG, "AMD using the default parameters.\n"); - - /* Find lowest ms value, that will be max wait time for a frame */ - if (maxWaitTimeForFrame > initialSilence) - maxWaitTimeForFrame = initialSilence; - if (maxWaitTimeForFrame > greeting) - maxWaitTimeForFrame = greeting; - if (maxWaitTimeForFrame > afterGreetingSilence) - maxWaitTimeForFrame = afterGreetingSilence; - if (maxWaitTimeForFrame > totalAnalysisTime) - maxWaitTimeForFrame = totalAnalysisTime; - if (maxWaitTimeForFrame > minimumWordLength) - maxWaitTimeForFrame = minimumWordLength; - if (maxWaitTimeForFrame > betweenWordsSilence) - maxWaitTimeForFrame = betweenWordsSilence; - - /* Now we're ready to roll! */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " - "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n", - initialSilence, greeting, afterGreetingSilence, totalAnalysisTime, - minimumWordLength, betweenWordsSilence, maximumNumberOfWords, silenceThreshold ); - - /* Set read format to signed linear so we get signed linear frames in */ - readFormat = chan->readformat; - if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0 ) { - ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to set to linear mode, giving up\n", chan->name ); - pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); - pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); - return; - } - - /* Create a new DSP that will detect the silence */ - if (!(silenceDetector = ast_dsp_new())) { - ast_log(LOG_WARNING, "AMD: Channel [%s]. Unable to create silence detector :(\n", chan->name ); - pbx_builtin_setvar_helper(chan , "AMDSTATUS", ""); - pbx_builtin_setvar_helper(chan , "AMDCAUSE", ""); - return; - } - - /* Set silence threshold to specified value */ - ast_dsp_set_threshold(silenceDetector, silenceThreshold); - - /* Now we go into a loop waiting for frames from the channel */ - while ((res = ast_waitfor(chan, 2 * maxWaitTimeForFrame)) > -1) { - - /* If we fail to read in a frame, that means they hung up */ - if (!(f = ast_read(chan))) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: HANGUP\n"); - if (option_debug) - ast_log(LOG_DEBUG, "Got hangup\n"); - strcpy(amdStatus, "HANGUP"); - break; - } - - if (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_NULL || f->frametype == AST_FRAME_CNG) { - /* If the total time exceeds the analysis time then give up as we are not too sure */ - if (f->frametype == AST_FRAME_VOICE) - framelength = (ast_codec_get_samples(f) / DEFAULT_SAMPLES_PER_MS); - else - framelength += 2 * maxWaitTimeForFrame; - - iTotalTime += framelength; - if (iTotalTime >= totalAnalysisTime) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name ); - ast_frfree(f); - strcpy(amdStatus , "NOTSURE"); - sprintf(amdCause , "TOOLONG-%d", iTotalTime); - break; - } - - /* Feed the frame of audio into the silence detector and see if we get a result */ - if (f->frametype != AST_FRAME_VOICE) - dspsilence += 2 * maxWaitTimeForFrame; - else { - dspsilence = 0; - ast_dsp_silence(silenceDetector, f, &dspsilence); - } - - if (dspsilence > 0) { - silenceDuration = dspsilence; - - if (silenceDuration >= betweenWordsSilence) { - if (currentState != STATE_IN_SILENCE ) { - previousState = currentState; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: Changed state to STATE_IN_SILENCE\n"); - } - currentState = STATE_IN_SILENCE; - consecutiveVoiceDuration = 0; - } - - if (inInitialSilence == 1 && silenceDuration >= initialSilence) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: silenceDuration:%d initialSilence:%d\n", - silenceDuration, initialSilence); - ast_frfree(f); - strcpy(amdStatus , "MACHINE"); - sprintf(amdCause , "INITIALSILENCE-%d-%d", silenceDuration, initialSilence); - res = 1; - break; - } - - if (silenceDuration >= afterGreetingSilence && inGreeting == 1) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: HUMAN: silenceDuration:%d afterGreetingSilence:%d\n", - silenceDuration, afterGreetingSilence); - ast_frfree(f); - strcpy(amdStatus , "HUMAN"); - sprintf(amdCause , "HUMAN-%d-%d", silenceDuration, afterGreetingSilence); - res = 1; - break; - } - - } else { - consecutiveVoiceDuration += framelength; - voiceDuration += framelength; - - /* If I have enough consecutive voice to say that I am in a Word, I can only increment the - number of words if my previous state was Silence, which means that I moved into a word. */ - if (consecutiveVoiceDuration >= minimumWordLength && currentState == STATE_IN_SILENCE) { - iWordsCount++; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: Word detected. iWordsCount:%d\n", iWordsCount); - previousState = currentState; - currentState = STATE_IN_WORD; - } - - if (iWordsCount >= maximumNumberOfWords) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: iWordsCount:%d\n", iWordsCount); - ast_frfree(f); - strcpy(amdStatus , "MACHINE"); - sprintf(amdCause , "MAXWORDS-%d-%d", iWordsCount, maximumNumberOfWords); - res = 1; - break; - } - - if (inGreeting == 1 && voiceDuration >= greeting) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: ANSWERING MACHINE: voiceDuration:%d greeting:%d\n", voiceDuration, greeting); - ast_frfree(f); - strcpy(amdStatus , "MACHINE"); - sprintf(amdCause , "LONGGREETING-%d-%d", voiceDuration, greeting); - res = 1; - break; - } - - if (voiceDuration >= minimumWordLength ) { - silenceDuration = 0; - inInitialSilence = 0; - inGreeting = 1; - } - - } - } - ast_frfree(f); - } - - if (!res) { - /* It took too long to get a frame back. Giving up. */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD: Channel [%s]. Too long...\n", chan->name); - strcpy(amdStatus , "NOTSURE"); - sprintf(amdCause , "TOOLONG-%d", iTotalTime); - } - - /* Set the status and cause on the channel */ - pbx_builtin_setvar_helper(chan , "AMDSTATUS" , amdStatus); - pbx_builtin_setvar_helper(chan , "AMDCAUSE" , amdCause); - - /* Restore channel read format */ - if (readFormat && ast_set_read_format(chan, readFormat)) - ast_log(LOG_WARNING, "AMD: Unable to restore read format on '%s'\n", chan->name); - - /* Free the DSP used to detect silence */ - ast_dsp_free(silenceDetector); - - return; -} - - -static int amd_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u = NULL; - - u = ast_module_user_add(chan); - isAnsweringMachine(chan, data); - ast_module_user_remove(u); - - return 0; -} - -static void load_config(void) -{ - struct ast_config *cfg = NULL; - char *cat = NULL; - struct ast_variable *var = NULL; - - if (!(cfg = ast_config_load("amd.conf"))) { - ast_log(LOG_ERROR, "Configuration file amd.conf missing.\n"); - return; - } - - cat = ast_category_browse(cfg, NULL); - - while (cat) { - if (!strcasecmp(cat, "general") ) { - var = ast_variable_browse(cfg, cat); - while (var) { - if (!strcasecmp(var->name, "initial_silence")) { - dfltInitialSilence = atoi(var->value); - } else if (!strcasecmp(var->name, "greeting")) { - dfltGreeting = atoi(var->value); - } else if (!strcasecmp(var->name, "after_greeting_silence")) { - dfltAfterGreetingSilence = atoi(var->value); - } else if (!strcasecmp(var->name, "silence_threshold")) { - dfltSilenceThreshold = atoi(var->value); - } else if (!strcasecmp(var->name, "total_analysis_time")) { - dfltTotalAnalysisTime = atoi(var->value); - } else if (!strcasecmp(var->name, "min_word_length")) { - dfltMinimumWordLength = atoi(var->value); - } else if (!strcasecmp(var->name, "between_words_silence")) { - dfltBetweenWordsSilence = atoi(var->value); - } else if (!strcasecmp(var->name, "maximum_number_of_words")) { - dfltMaximumNumberOfWords = atoi(var->value); - } else { - ast_log(LOG_WARNING, "%s: Cat:%s. Unknown keyword %s at line %d of amd.conf\n", - app, cat, var->name, var->lineno); - } - var = var->next; - } - } - cat = ast_category_browse(cfg, cat); - } - - ast_config_destroy(cfg); - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "AMD defaults: initialSilence [%d] greeting [%d] afterGreetingSilence [%d] " - "totalAnalysisTime [%d] minimumWordLength [%d] betweenWordsSilence [%d] maximumNumberOfWords [%d] silenceThreshold [%d] \n", - dfltInitialSilence, dfltGreeting, dfltAfterGreetingSilence, dfltTotalAnalysisTime, - dfltMinimumWordLength, dfltBetweenWordsSilence, dfltMaximumNumberOfWords, dfltSilenceThreshold ); - - return; -} - -static int unload_module(void) -{ - ast_module_user_hangup_all(); - return ast_unregister_application(app); -} - -static int load_module(void) -{ - load_config(); - return ast_register_application(app, amd_exec, synopsis, descrip); -} - -static int reload(void) -{ - load_config(); - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Answering Machine Detection Application", - .load = load_module, - .unload = unload_module, - .reload = reload, - ); diff --git a/apps/app_authenticate.c b/apps/app_authenticate.c deleted file mode 100644 index e182deee2..000000000 --- a/apps/app_authenticate.c +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Execute arbitrary authenticate commands - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/app.h" -#include "asterisk/astdb.h" -#include "asterisk/utils.h" -#include "asterisk/options.h" - -enum { - OPT_ACCOUNT = (1 << 0), - OPT_DATABASE = (1 << 1), - OPT_JUMP = (1 << 2), - OPT_MULTIPLE = (1 << 3), - OPT_REMOVE = (1 << 4), -} auth_option_flags; - -AST_APP_OPTIONS(auth_app_options, { - AST_APP_OPTION('a', OPT_ACCOUNT), - AST_APP_OPTION('d', OPT_DATABASE), - AST_APP_OPTION('j', OPT_JUMP), - AST_APP_OPTION('m', OPT_MULTIPLE), - AST_APP_OPTION('r', OPT_REMOVE), -}); - - -static char *app = "Authenticate"; - -static char *synopsis = "Authenticate a user"; - -static char *descrip = -" Authenticate(password[|options[|maxdigits]]): This application asks the caller\n" -"to enter a given password in order to continue dialplan execution. If the password\n" -"begins with the '/' character, it is interpreted as a file which contains a list of\n" -"valid passwords, listed 1 password per line in the file.\n" -" When using a database key, the value associated with the key can be anything.\n" -"Users have three attempts to authenticate before the channel is hung up. If the\n" -"passsword is invalid, the 'j' option is specified, and priority n+101 exists,\n" -"dialplan execution will continnue at this location.\n" -" Options:\n" -" a - Set the channels' account code to the password that is entered\n" -" d - Interpret the given path as database key, not a literal file\n" -" j - Support jumping to n+101 if authentication fails\n" -" m - Interpret the given path as a file which contains a list of account\n" -" codes and password hashes delimited with ':', listed one per line in\n" -" the file. When one of the passwords is matched, the channel will have\n" -" its account code set to the corresponding account code in the file.\n" -" r - Remove the database key upon successful entry (valid with 'd' only)\n" -" maxdigits - maximum acceptable number of digits. Stops reading after\n" -" maxdigits have been entered (without requiring the user to\n" -" press the '#' key).\n" -" Defaults to 0 - no limit - wait for the user press the '#' key.\n" -; - -static int auth_exec(struct ast_channel *chan, void *data) -{ - int res=0; - int retries; - struct ast_module_user *u; - char passwd[256]; - char *prompt; - int maxdigits; - char *argcopy =NULL; - struct ast_flags flags = {0}; - - AST_DECLARE_APP_ARGS(arglist, - AST_APP_ARG(password); - AST_APP_ARG(options); - AST_APP_ARG(maxdigits); - ); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Authenticate requires an argument(password)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - if (chan->_state != AST_STATE_UP) { - res = ast_answer(chan); - if (res) { - ast_module_user_remove(u); - return -1; - } - } - - argcopy = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(arglist,argcopy); - - if (!ast_strlen_zero(arglist.options)) { - ast_app_parse_options(auth_app_options, &flags, NULL, arglist.options); - } - - if (!ast_strlen_zero(arglist.maxdigits)) { - maxdigits = atoi(arglist.maxdigits); - if ((maxdigits<1) || (maxdigits>sizeof(passwd)-2)) - maxdigits = sizeof(passwd) - 2; - } else { - maxdigits = sizeof(passwd) - 2; - } - - /* Start asking for password */ - prompt = "agent-pass"; - for (retries = 0; retries < 3; retries++) { - res = ast_app_getdata(chan, prompt, passwd, maxdigits, 0); - if (res < 0) - break; - res = 0; - if (arglist.password[0] == '/') { - if (ast_test_flag(&flags,OPT_DATABASE)) { - char tmp[256]; - /* Compare against a database key */ - if (!ast_db_get(arglist.password + 1, passwd, tmp, sizeof(tmp))) { - /* It's a good password */ - if (ast_test_flag(&flags,OPT_REMOVE)) { - ast_db_del(arglist.password + 1, passwd); - } - break; - } - } else { - /* Compare against a file */ - FILE *f; - f = fopen(arglist.password, "r"); - if (f) { - char buf[256] = ""; - char md5passwd[33] = ""; - char *md5secret = NULL; - - while (!feof(f)) { - if (!fgets(buf, sizeof(buf), f)) { - continue; - } - if (!ast_strlen_zero(buf)) { - size_t len = strlen(buf); - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - if (ast_test_flag(&flags,OPT_MULTIPLE)) { - md5secret = strchr(buf, ':'); - if (md5secret == NULL) - continue; - *md5secret = '\0'; - md5secret++; - ast_md5_hash(md5passwd, passwd); - if (!strcmp(md5passwd, md5secret)) { - if (ast_test_flag(&flags,OPT_ACCOUNT)) - ast_cdr_setaccount(chan, buf); - break; - } - } else { - if (!strcmp(passwd, buf)) { - if (ast_test_flag(&flags,OPT_ACCOUNT)) - ast_cdr_setaccount(chan, buf); - break; - } - } - } - } - fclose(f); - if (!ast_strlen_zero(buf)) { - if (ast_test_flag(&flags,OPT_MULTIPLE)) { - if (md5secret && !strcmp(md5passwd, md5secret)) - break; - } else { - if (!strcmp(passwd, buf)) - break; - } - } - } else - ast_log(LOG_WARNING, "Unable to open file '%s' for authentication: %s\n", arglist.password, strerror(errno)); - } - } else { - /* Compare against a fixed password */ - if (!strcmp(passwd, arglist.password)) - break; - } - prompt="auth-incorrect"; - } - if ((retries < 3) && !res) { - if (ast_test_flag(&flags,OPT_ACCOUNT) && !ast_test_flag(&flags,OPT_MULTIPLE)) - ast_cdr_setaccount(chan, passwd); - res = ast_streamfile(chan, "auth-thankyou", chan->language); - if (!res) - res = ast_waitstream(chan, ""); - } else { - if (ast_test_flag(&flags,OPT_JUMP) && ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101) == 0) { - res = 0; - } else { - if (!ast_streamfile(chan, "vm-goodbye", chan->language)) - res = ast_waitstream(chan, ""); - res = -1; - } - } - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - ast_module_user_hangup_all(); - - res = ast_unregister_application(app); - - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, auth_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Authentication Application"); diff --git a/apps/app_cdr.c b/apps/app_cdr.c deleted file mode 100644 index a70d9d2f5..000000000 --- a/apps/app_cdr.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Martin Pycko - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief Applications connected with CDR engine - * - * Martin Pycko - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include - -#include "asterisk/channel.h" -#include "asterisk/module.h" -#include "asterisk/pbx.h" - -static char *nocdr_descrip = -" NoCDR(): This application will tell Asterisk not to maintain a CDR for the\n" -"current call.\n"; - -static char *nocdr_app = "NoCDR"; -static char *nocdr_synopsis = "Tell Asterisk to not maintain a CDR for the current call"; - - -static int nocdr_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - if (chan->cdr) { - ast_set_flag(chan->cdr, AST_CDR_FLAG_POST_DISABLED); - } - - ast_module_user_remove(u); - - return 0; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(nocdr_app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(nocdr_app, nocdr_exec, nocdr_synopsis, nocdr_descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Tell Asterisk to not maintain a CDR for the current call"); diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c deleted file mode 100644 index c6931d8db..000000000 --- a/apps/app_chanisavail.c +++ /dev/null @@ -1,173 +0,0 @@ -/* -* Asterisk -- An open source telephony toolkit. -* -* Copyright (C) 1999 - 2005, Digium, Inc. -* -* Mark Spencer -* James Golovich -* -* See http://www.asterisk.org for more information about -* the Asterisk project. Please do not directly contact -* any of the maintainers of this project for assistance; -* the project provides a web site, mailing lists and IRC -* channels for your use. -* -* This program is free software, distributed 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 - * \author James Golovich - - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/app.h" -#include "asterisk/devicestate.h" -#include "asterisk/options.h" - -static char *app = "ChanIsAvail"; - -static char *synopsis = "Check channel availability"; - -static char *descrip = -" ChanIsAvail(Technology/resource[&Technology2/resource2...][|options]): \n" -"This application will check to see if any of the specified channels are\n" -"available. The following variables will be set by this application:\n" -" ${AVAILCHAN} - the name of the available channel, if one exists\n" -" ${AVAILORIGCHAN} - the canonical channel name that was used to create the channel\n" -" ${AVAILSTATUS} - the status code for the available channel\n" -" Options:\n" -" s - Consider the channel unavailable if the channel is in use at all\n" -" j - Support jumping to priority n+101 if no channel is available\n"; - - -static int chanavail_exec(struct ast_channel *chan, void *data) -{ - int res=-1, inuse=-1, option_state=0, priority_jump=0; - int status; - struct ast_module_user *u; - char *info, tmp[512], trychan[512], *peers, *tech, *number, *rest, *cur; - struct ast_channel *tempchan; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(reqchans); - AST_APP_ARG(options); - ); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "ChanIsAvail requires an argument (Zap/1&Zap/2)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - info = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, info); - - if (args.options) { - if (strchr(args.options, 's')) - option_state = 1; - if (strchr(args.options, 'j')) - priority_jump = 1; - } - peers = args.reqchans; - if (peers) { - cur = peers; - do { - /* remember where to start next time */ - rest = strchr(cur, '&'); - if (rest) { - *rest = 0; - rest++; - } - tech = cur; - number = strchr(tech, '/'); - if (!number) { - ast_log(LOG_WARNING, "ChanIsAvail argument takes format ([technology]/[device])\n"); - ast_module_user_remove(u); - return -1; - } - *number = '\0'; - number++; - - if (option_state) { - /* If the pbx says in use then don't bother trying further. - This is to permit testing if someone's on a call, even if the - channel can permit more calls (ie callwaiting, sip calls, etc). */ - - snprintf(trychan, sizeof(trychan), "%s/%s",cur,number); - status = inuse = ast_device_state(trychan); - } - if ((inuse <= 1) && (tempchan = ast_request(tech, chan->nativeformats, number, &status))) { - pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name); - /* Store the originally used channel too */ - snprintf(tmp, sizeof(tmp), "%s/%s", tech, number); - pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp); - snprintf(tmp, sizeof(tmp), "%d", status); - pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp); - ast_hangup(tempchan); - tempchan = NULL; - res = 1; - break; - } else { - snprintf(tmp, sizeof(tmp), "%d", status); - pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp); - } - cur = rest; - } while (cur); - } - if (res < 1) { - pbx_builtin_setvar_helper(chan, "AVAILCHAN", ""); - pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", ""); - if (priority_jump || ast_opt_priority_jumping) { - if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { - ast_module_user_remove(u); - return -1; - } - } - } - - ast_module_user_remove(u); - return 0; -} - -static int unload_module(void) -{ - int res = 0; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, chanavail_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Check channel availability"); diff --git a/apps/app_channelredirect.c b/apps/app_channelredirect.c deleted file mode 100644 index a8eedf9b4..000000000 --- a/apps/app_channelredirect.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/lock.h" -#include "asterisk/app.h" -#include "asterisk/features.h" -#include "asterisk/options.h" - -static char *app = "ChannelRedirect"; -static char *synopsis = "Redirects given channel to a dialplan target."; -static char *descrip = -"ChannelRedirect(channel|[[context|]extension|]priority):\n" -" Sends the specified channel to the specified extension priority\n"; - - -static int asyncgoto_exec(struct ast_channel *chan, void *data) -{ - int res = -1; - struct ast_module_user *u; - char *info, *context, *exten, *priority; - int prio = 1; - struct ast_channel *chan2 = NULL; - - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(channel); - AST_APP_ARG(label); - ); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "%s requires an argument (channel|[[context|]exten|]priority)\n", app); - return -1; - } - - u = ast_module_user_add(chan); - - info = ast_strdupa(data); - AST_STANDARD_APP_ARGS(args, info); - - if (ast_strlen_zero(args.channel) || ast_strlen_zero(args.label)) { - ast_log(LOG_WARNING, "%s requires an argument (channel|[[context|]exten|]priority)\n", app); - goto quit; - } - - chan2 = ast_get_channel_by_name_locked(args.channel); - if (!chan2) { - ast_log(LOG_WARNING, "No such channel: %s\n", args.channel); - goto quit; - } - - /* Parsed right to left, so standard parsing won't work */ - context = strsep(&args.label, "|"); - exten = strsep(&args.label, "|"); - if (exten) { - priority = strsep(&args.label, "|"); - if (!priority) { - priority = exten; - exten = context; - context = NULL; - } - } else { - priority = context; - context = NULL; - } - - /* ast_findlabel_extension does not convert numeric priorities; it only does a lookup */ - if (!(prio = atoi(priority)) && !(prio = ast_findlabel_extension(chan2, S_OR(context, chan2->context), - S_OR(exten, chan2->exten), priority, chan2->cid.cid_num))) { - ast_log(LOG_WARNING, "'%s' is not a known priority or label\n", priority); - goto chanquit; - } - - if (option_debug > 1) - ast_log(LOG_DEBUG, "Attempting async goto (%s) to %s|%s|%d\n", args.channel, S_OR(context, chan2->context), S_OR(exten, chan2->exten), prio); - - if (ast_async_goto_if_exists(chan2, S_OR(context, chan2->context), S_OR(exten, chan2->exten), prio)) - ast_log(LOG_WARNING, "%s failed for %s\n", app, args.channel); - else - res = 0; - - chanquit: - ast_mutex_unlock(&chan2->lock); - quit: - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, asyncgoto_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Channel Redirect"); diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c deleted file mode 100644 index 18e4972a5..000000000 --- a/apps/app_chanspy.c +++ /dev/null @@ -1,878 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2005 Anthony Minessale II (anthmct@yahoo.com) - * Copyright (C) 2005 - 2008, Digium, Inc. - * - * A license has been granted to Digium (via disclaimer) for the use of - * this code. - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief ChanSpy: Listen in on any channel. - * - * \author Anthony Minessale II - * \author Joshua Colp - * \author Russell Bryant - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/audiohook.h" -#include "asterisk/features.h" -#include "asterisk/options.h" -#include "asterisk/app.h" -#include "asterisk/utils.h" -#include "asterisk/say.h" -#include "asterisk/pbx.h" -#include "asterisk/translate.h" -#include "asterisk/module.h" -#include "asterisk/lock.h" - -#define AST_NAME_STRLEN 256 - -/* "Zap/pseudo" is ten characters. - * "DAHDI/pseudo" is twelve characters. - */ - -static const char *tdesc = "Listen to a channel, and optionally whisper into it"; -static const char *app_chan = "ChanSpy"; -static const char *desc_chan = -" ChanSpy([chanprefix][|options]): This application is used to listen to the\n" -"audio from an Asterisk channel. This includes the audio coming in and\n" -"out of the channel being spied on. If the 'chanprefix' parameter is specified,\n" -"only channels beginning with this string will be spied upon.\n" -" While spying, the following actions may be performed:\n" -" - Dialing # cycles the volume level.\n" -" - Dialing * will stop spying and look for another channel to spy on.\n" -" - Dialing a series of digits followed by # builds a channel name to append\n" -" to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n" -" the digits '1234#' while spying will begin spying on the channel\n" -" 'Agent/1234'.\n" -" Options:\n" -" b - Only spy on channels involved in a bridged call.\n" -" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n" -" contain 'grp' in an optional : delimited list.\n" -" q - Don't play a beep when beginning to spy on a channel, or speak the\n" -" selected channel name.\n" -" r[(basename)] - Record the session to the monitor spool directory. An\n" -" optional base for the filename may be specified. The\n" -" default is 'chanspy'.\n" -" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n" -" negative value refers to a quieter setting.\n" -" w - Enable 'whisper' mode, so the spying channel can talk to\n" -" the spied-on channel.\n" -" W - Enable 'private whisper' mode, so the spying channel can\n" -" talk to the spied-on channel but cannot listen to that\n" -" channel.\n" -; - -static const char *app_ext = "ExtenSpy"; -static const char *desc_ext = -" ExtenSpy(exten[@context][|options]): This application is used to listen to the\n" -"audio from an Asterisk channel. This includes the audio coming in and\n" -"out of the channel being spied on. Only channels created by outgoing calls for the\n" -"specified extension will be selected for spying. If the optional context is not\n" -"supplied, the current channel's context will be used.\n" -" While spying, the following actions may be performed:\n" -" - Dialing # cycles the volume level.\n" -" - Dialing * will stop spying and look for another channel to spy on.\n" -" Options:\n" -" b - Only spy on channels involved in a bridged call.\n" -" g(grp) - Match only channels where their ${SPYGROUP} variable is set to\n" -" contain 'grp' in an optional : delimited list.\n" -" q - Don't play a beep when beginning to spy on a channel, or speak the\n" -" selected channel name.\n" -" r[(basename)] - Record the session to the monitor spool directory. An\n" -" optional base for the filename may be specified. The\n" -" default is 'chanspy'.\n" -" v([value]) - Adjust the initial volume in the range from -4 to 4. A\n" -" negative value refers to a quieter setting.\n" -" w - Enable 'whisper' mode, so the spying channel can talk to\n" -" the spied-on channel.\n" -" W - Enable 'private whisper' mode, so the spying channel can\n" -" talk to the spied-on channel but cannot listen to that\n" -" channel.\n" -; - -enum { - OPTION_QUIET = (1 << 0), /* Quiet, no announcement */ - OPTION_BRIDGED = (1 << 1), /* Only look at bridged calls */ - OPTION_VOLUME = (1 << 2), /* Specify initial volume */ - OPTION_GROUP = (1 << 3), /* Only look at channels in group */ - OPTION_RECORD = (1 << 4), - OPTION_WHISPER = (1 << 5), - OPTION_PRIVATE = (1 << 6), /* Private Whisper mode */ -} chanspy_opt_flags; - -enum { - OPT_ARG_VOLUME = 0, - OPT_ARG_GROUP, - OPT_ARG_RECORD, - OPT_ARG_ARRAY_SIZE, -} chanspy_opt_args; - -AST_APP_OPTIONS(spy_opts, { - AST_APP_OPTION('q', OPTION_QUIET), - AST_APP_OPTION('b', OPTION_BRIDGED), - AST_APP_OPTION('w', OPTION_WHISPER), - AST_APP_OPTION('W', OPTION_PRIVATE), - AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME), - AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP), - AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD), -}); - -static int next_unique_id_to_use = 0; - -struct chanspy_translation_helper { - /* spy data */ - struct ast_audiohook spy_audiohook; - struct ast_audiohook whisper_audiohook; - int fd; - int volfactor; -}; - -static void *spy_alloc(struct ast_channel *chan, void *data) -{ - /* just store the data pointer in the channel structure */ - return data; -} - -static void spy_release(struct ast_channel *chan, void *data) -{ - /* nothing to do */ -} - -static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) -{ - struct chanspy_translation_helper *csth = data; - struct ast_frame *f; - - ast_audiohook_lock(&csth->spy_audiohook); - if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { - ast_audiohook_unlock(&csth->spy_audiohook); - return -1; - } - - f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR); - - ast_audiohook_unlock(&csth->spy_audiohook); - - if (!f) - return 0; - - if (ast_write(chan, f)) { - ast_frfree(f); - return -1; - } - - if (csth->fd) { - if (write(csth->fd, f->data, f->datalen) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - - ast_frfree(f); - - return 0; -} - -static struct ast_generator spygen = { - .alloc = spy_alloc, - .release = spy_release, - .generate = spy_generate, -}; - -static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) -{ - int res; - struct ast_channel *peer; - - ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); - - res = ast_audiohook_attach(chan, audiohook); - - if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) { - ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); - } - return res; -} - -struct chanspy_ds { - struct ast_channel *chan; - char unique_id[20]; - ast_mutex_t lock; -}; - -static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, - int *volfactor, int fd, const struct ast_flags *flags) -{ - struct chanspy_translation_helper csth; - int running = 0, res, x = 0; - char inp[24] = {0}; - char *name; - struct ast_frame *f; - struct ast_silence_generator *silgen = NULL; - struct ast_channel *spyee = NULL; - const char *spyer_name; - - ast_channel_lock(chan); - spyer_name = ast_strdupa(chan->name); - ast_channel_unlock(chan); - - ast_mutex_lock(&spyee_chanspy_ds->lock); - if (spyee_chanspy_ds->chan) { - spyee = spyee_chanspy_ds->chan; - ast_channel_lock(spyee); - } - ast_mutex_unlock(&spyee_chanspy_ds->lock); - - if (!spyee) - return 0; - - /* We now hold the channel lock on spyee */ - - if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { - ast_channel_unlock(spyee); - return 0; - } - - name = ast_strdupa(spyee->name); - if (option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); - - memset(&csth, 0, sizeof(csth)); - - ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); - - if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { - ast_audiohook_destroy(&csth.spy_audiohook); - ast_channel_unlock(spyee); - return 0; - } - - if (ast_test_flag(flags, OPTION_WHISPER)) { - ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); - start_spying(spyee, spyer_name, &csth.whisper_audiohook); - } - - ast_channel_unlock(spyee); - spyee = NULL; - - csth.volfactor = *volfactor; - - if (csth.volfactor) { - csth.spy_audiohook.options.read_volume = csth.volfactor; - csth.spy_audiohook.options.write_volume = csth.volfactor; - } - - csth.fd = fd; - - if (ast_test_flag(flags, OPTION_PRIVATE)) - silgen = ast_channel_start_silence_generator(chan); - else - ast_activate_generator(chan, &spygen, &csth); - - /* We can no longer rely on 'spyee' being an actual channel; - it can be hung up and freed out from under us. However, the - channel destructor will put NULL into our csth.spy.chan - field when that happens, so that is our signal that the spyee - channel has gone away. - */ - - /* Note: it is very important that the ast_waitfor() be the first - condition in this expression, so that if we wait for some period - of time before receiving a frame from our spying channel, we check - for hangup on the spied-on channel _after_ knowing that a frame - has arrived, since the spied-on channel could have gone away while - we were waiting - */ - while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { - if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { - running = -1; - break; - } - - if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) { - ast_audiohook_lock(&csth.whisper_audiohook); - ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); - ast_audiohook_unlock(&csth.whisper_audiohook); - ast_frfree(f); - continue; - } - - res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; - ast_frfree(f); - if (!res) - continue; - - if (x == sizeof(inp)) - x = 0; - - if (res < 0) { - running = -1; - break; - } - - if (res == '*') { - running = 0; - break; - } else if (res == '#') { - if (!ast_strlen_zero(inp)) { - running = atoi(inp); - break; - } - - (*volfactor)++; - if (*volfactor > 4) - *volfactor = -4; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); - csth.volfactor = *volfactor; - csth.spy_audiohook.options.read_volume = csth.volfactor; - csth.spy_audiohook.options.write_volume = csth.volfactor; - } else if (res >= '0' && res <= '9') { - inp[x++] = res; - } - } - - if (ast_test_flag(flags, OPTION_PRIVATE)) - ast_channel_stop_silence_generator(chan, silgen); - else - ast_deactivate_generator(chan); - - if (ast_test_flag(flags, OPTION_WHISPER)) { - ast_audiohook_lock(&csth.whisper_audiohook); - ast_audiohook_detach(&csth.whisper_audiohook); - ast_audiohook_unlock(&csth.whisper_audiohook); - ast_audiohook_destroy(&csth.whisper_audiohook); - } - - ast_audiohook_lock(&csth.spy_audiohook); - ast_audiohook_detach(&csth.spy_audiohook); - ast_audiohook_unlock(&csth.spy_audiohook); - ast_audiohook_destroy(&csth.spy_audiohook); - - if (option_verbose >= 2) - ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); - - return running; -} - -/*! - * \note This relies on the embedded lock to be recursive, as it may be called - * due to a call to chanspy_ds_free with the lock held there. - */ -static void chanspy_ds_destroy(void *data) -{ - struct chanspy_ds *chanspy_ds = data; - - /* Setting chan to be NULL is an atomic operation, but we don't want this - * value to change while this lock is held. The lock is held elsewhere - * while it performs non-atomic operations with this channel pointer */ - - ast_mutex_lock(&chanspy_ds->lock); - chanspy_ds->chan = NULL; - ast_mutex_unlock(&chanspy_ds->lock); -} - -static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - struct chanspy_ds *chanspy_ds = data; - - ast_mutex_lock(&chanspy_ds->lock); - chanspy_ds->chan = new_chan; - ast_mutex_unlock(&chanspy_ds->lock); -} - -static const struct ast_datastore_info chanspy_ds_info = { - .type = "chanspy", - .destroy = chanspy_ds_destroy, - .chan_fixup = chanspy_ds_chan_fixup, -}; - -static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds) -{ - if (!chanspy_ds) - return NULL; - - ast_mutex_lock(&chanspy_ds->lock); - if (chanspy_ds->chan) { - struct ast_datastore *datastore; - struct ast_channel *chan; - - chan = chanspy_ds->chan; - - ast_channel_lock(chan); - if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { - ast_channel_datastore_remove(chan, datastore); - /* chanspy_ds->chan is NULL after this call */ - chanspy_ds_destroy(datastore->data); - datastore->data = NULL; - ast_channel_datastore_free(datastore); - } - ast_channel_unlock(chan); - } - ast_mutex_unlock(&chanspy_ds->lock); - - return NULL; -} - -/*! \note Returns the channel in the chanspy_ds locked as well as the chanspy_ds locked */ -static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds) -{ - struct ast_datastore *datastore = NULL; - - ast_mutex_lock(&chanspy_ds->lock); - - if (!(datastore = ast_channel_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) { - ast_mutex_unlock(&chanspy_ds->lock); - chanspy_ds = chanspy_ds_free(chanspy_ds); - ast_channel_unlock(chan); - return NULL; - } - - chanspy_ds->chan = chan; - datastore->data = chanspy_ds; - ast_channel_datastore_add(chan, datastore); - - return chanspy_ds; -} - -static struct chanspy_ds *next_channel(struct ast_channel *chan, - const struct ast_channel *last, const char *spec, - const char *exten, const char *context, struct chanspy_ds *chanspy_ds) -{ - struct ast_channel *this; - char channel_name[AST_CHANNEL_NAME]; - static size_t PSEUDO_CHAN_LEN = 0; - - if (!PSEUDO_CHAN_LEN) { - PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo"); - } - -redo: - if (spec) - this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); - else if (exten) - this = ast_walk_channel_by_exten_locked(last, exten, context); - else - this = ast_channel_walk_locked(last); - - if (!this) - return NULL; - - snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name); - if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) { - last = this; - ast_channel_unlock(this); - goto redo; - } else if (this == chan) { - last = this; - ast_channel_unlock(this); - goto redo; - } - - return setup_chanspy_ds(this, chanspy_ds); -} - -static int common_exec(struct ast_channel *chan, const struct ast_flags *flags, - int volfactor, const int fd, const char *mygroup, const char *spec, - const char *exten, const char *context) -{ - char nameprefix[AST_NAME_STRLEN]; - char peer_name[AST_NAME_STRLEN + 5]; - signed char zero_volume = 0; - int waitms; - int res; - char *ptr; - int num; - int num_spyed_upon = 1; - struct chanspy_ds chanspy_ds = { 0, }; - - ast_mutex_init(&chanspy_ds.lock); - - snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); - - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - - ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ - - waitms = 100; - - for (;;) { - struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; - struct ast_channel *prev = NULL, *peer = NULL; - - if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { - res = ast_streamfile(chan, "beep", chan->language); - if (!res) - res = ast_waitstream(chan, ""); - else if (res < 0) { - ast_clear_flag(chan, AST_FLAG_SPYING); - break; - } - } - - res = ast_waitfordigit(chan, waitms); - if (res < 0) { - ast_clear_flag(chan, AST_FLAG_SPYING); - break; - } - - /* reset for the next loop around, unless overridden later */ - waitms = 100; - num_spyed_upon = 0; - - for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); - peer_chanspy_ds; - chanspy_ds_free(peer_chanspy_ds), prev = peer, - peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : - next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { - const char *group; - int igrp = !mygroup; - char *groups[25]; - int num_groups = 0; - char dup_group[512]; - int x; - char *s; - - peer = peer_chanspy_ds->chan; - - ast_mutex_unlock(&peer_chanspy_ds->lock); - - if (peer == prev) { - ast_channel_unlock(peer); - chanspy_ds_free(peer_chanspy_ds); - break; - } - - if (ast_check_hangup(chan)) { - ast_channel_unlock(peer); - chanspy_ds_free(peer_chanspy_ds); - break; - } - - if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { - ast_channel_unlock(peer); - continue; - } - - if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { - ast_channel_unlock(peer); - continue; - } - - if (mygroup) { - if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { - ast_copy_string(dup_group, group, sizeof(dup_group)); - num_groups = ast_app_separate_args(dup_group, ':', groups, - sizeof(groups) / sizeof(groups[0])); - } - - for (x = 0; x < num_groups; x++) { - if (!strcmp(mygroup, groups[x])) { - igrp = 1; - break; - } - } - } - - if (!igrp) { - ast_channel_unlock(peer); - continue; - } - - strcpy(peer_name, "spy-"); - strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); - ptr = strchr(peer_name, '/'); - *ptr++ = '\0'; - - for (s = peer_name; s < ptr; s++) - *s = tolower(*s); - - /* We have to unlock the peer channel here to avoid a deadlock. - * So, when we need to dereference it again, we have to lock the - * datastore and get the pointer from there to see if the channel - * is still valid. */ - ast_channel_unlock(peer); - - if (!ast_test_flag(flags, OPTION_QUIET)) { - if (ast_fileexists(peer_name, NULL, NULL) != -1) { - res = ast_streamfile(chan, peer_name, chan->language); - if (!res) - res = ast_waitstream(chan, ""); - if (res) { - chanspy_ds_free(peer_chanspy_ds); - break; - } - } else - res = ast_say_character_str(chan, peer_name, "", chan->language); - if ((num = atoi(ptr))) - ast_say_digits(chan, atoi(ptr), "", chan->language); - } - - res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags); - num_spyed_upon++; - - if (res == -1) { - chanspy_ds_free(peer_chanspy_ds); - break; - } else if (res > 1 && spec) { - struct ast_channel *next; - - snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); - - if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { - peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); - next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); - } else { - /* stay on this channel, if it is still valid */ - - ast_mutex_lock(&peer_chanspy_ds->lock); - if (peer_chanspy_ds->chan) { - ast_channel_lock(peer_chanspy_ds->chan); - next_chanspy_ds = peer_chanspy_ds; - peer_chanspy_ds = NULL; - } else { - /* the channel is gone */ - ast_mutex_unlock(&peer_chanspy_ds->lock); - next_chanspy_ds = NULL; - } - } - - peer = NULL; - } - } - if (res == -1 || ast_check_hangup(chan)) - break; - } - - ast_clear_flag(chan, AST_FLAG_SPYING); - - ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); - - ast_mutex_lock(&chanspy_ds.lock); - ast_mutex_unlock(&chanspy_ds.lock); - ast_mutex_destroy(&chanspy_ds.lock); - - return res; -} - -static int chanspy_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - char *options = NULL; - char *spec = NULL; - char *argv[2]; - char *mygroup = NULL; - char *recbase = NULL; - int fd = 0; - struct ast_flags flags; - int oldwf = 0; - int argc = 0; - int volfactor = 0; - int res; - - data = ast_strdupa(data); - - u = ast_module_user_add(chan); - - if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { - spec = argv[0]; - if (argc > 1) - options = argv[1]; - - if (ast_strlen_zero(spec) || !strcmp(spec, "all")) - spec = NULL; - } - - if (options) { - char *opts[OPT_ARG_ARRAY_SIZE]; - - ast_app_parse_options(spy_opts, &flags, opts, options); - if (ast_test_flag(&flags, OPTION_GROUP)) - mygroup = opts[OPT_ARG_GROUP]; - - if (ast_test_flag(&flags, OPTION_RECORD) && - !(recbase = opts[OPT_ARG_RECORD])) - recbase = "chanspy"; - - if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { - int vol; - - if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) - ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); - else - volfactor = vol; - } - - if (ast_test_flag(&flags, OPTION_PRIVATE)) - ast_set_flag(&flags, OPTION_WHISPER); - } else - ast_clear_flag(&flags, AST_FLAGS_ALL); - - oldwf = chan->writeformat; - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { - ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); - ast_module_user_remove(u); - return -1; - } - - if (recbase) { - char filename[PATH_MAX]; - - snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); - if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { - ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); - fd = 0; - } - } - - res = common_exec(chan, &flags, volfactor, fd, mygroup, spec, NULL, NULL); - - if (fd) - close(fd); - - if (oldwf && ast_set_write_format(chan, oldwf) < 0) - ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); - - ast_module_user_remove(u); - - return res; -} - -static int extenspy_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - char *options = NULL; - char *exten = NULL; - char *context = NULL; - char *argv[2]; - char *mygroup = NULL; - char *recbase = NULL; - int fd = 0; - struct ast_flags flags; - int oldwf = 0; - int argc = 0; - int volfactor = 0; - int res; - - data = ast_strdupa(data); - - u = ast_module_user_add(chan); - - if ((argc = ast_app_separate_args(data, '|', argv, sizeof(argv) / sizeof(argv[0])))) { - context = argv[0]; - if (!ast_strlen_zero(argv[0])) - exten = strsep(&context, "@"); - if (ast_strlen_zero(context)) - context = ast_strdupa(chan->context); - if (argc > 1) - options = argv[1]; - } - - if (options) { - char *opts[OPT_ARG_ARRAY_SIZE]; - - ast_app_parse_options(spy_opts, &flags, opts, options); - if (ast_test_flag(&flags, OPTION_GROUP)) - mygroup = opts[OPT_ARG_GROUP]; - - if (ast_test_flag(&flags, OPTION_RECORD) && - !(recbase = opts[OPT_ARG_RECORD])) - recbase = "chanspy"; - - if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) { - int vol; - - if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4)) - ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n"); - else - volfactor = vol; - } - - if (ast_test_flag(&flags, OPTION_PRIVATE)) - ast_set_flag(&flags, OPTION_WHISPER); - } else - ast_clear_flag(&flags, AST_FLAGS_ALL); - - oldwf = chan->writeformat; - if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) { - ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); - ast_module_user_remove(u); - return -1; - } - - if (recbase) { - char filename[PATH_MAX]; - - snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL)); - if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644)) <= 0) { - ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename); - fd = 0; - } - } - - res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, exten, context); - - if (fd) - close(fd); - - if (oldwf && ast_set_write_format(chan, oldwf) < 0) - ast_log(LOG_ERROR, "Could Not Set Write Format.\n"); - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res = 0; - - res |= ast_unregister_application(app_chan); - res |= ast_unregister_application(app_ext); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - int res = 0; - - res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan); - res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext); - - return res; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel"); diff --git a/apps/app_controlplayback.c b/apps/app_controlplayback.c deleted file mode 100644 index 6f2c03315..000000000 --- a/apps/app_controlplayback.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Trivial application to control playback of a sound file - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/app.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/utils.h" -#include "asterisk/options.h" - -static const char *app = "ControlPlayback"; - -static const char *synopsis = "Play a file with fast forward and rewind"; - -static const char *descrip = -" ControlPlayback(file[|skipms[|ff[|rew[|stop[|pause[|restart|options]]]]]]]):\n" -"This application will play back the given filename. By default, the '*' key\n" -"can be used to rewind, and the '#' key can be used to fast-forward.\n" -"Parameters:\n" -" skipms - This is number of milliseconds to skip when rewinding or\n" -" fast-forwarding.\n" -" ff - Fast-forward when this DTMF digit is received.\n" -" rew - Rewind when this DTMF digit is received.\n" -" stop - Stop playback when this DTMF digit is received.\n" -" pause - Pause playback when this DTMF digit is received.\n" -" restart - Restart playback when this DTMF digit is received.\n" -"Options:\n" -" j - Jump to priority n+101 if the requested file is not found.\n" -"This application sets the following channel variable upon completion:\n" -" CPLAYBACKSTATUS - This variable contains the status of the attempt as a text\n" -" string, one of: SUCCESS | USERSTOPPED | ERROR\n"; - - -static int is_on_phonepad(char key) -{ - return key == 35 || key == 42 || (key >= 48 && key <= 57); -} - -static int controlplayback_exec(struct ast_channel *chan, void *data) -{ - int res = 0, priority_jump = 0; - int skipms = 0; - struct ast_module_user *u; - char *tmp; - int argc; - char *argv[8]; - enum arg_ids { - arg_file = 0, - arg_skip = 1, - arg_fwd = 2, - arg_rev = 3, - arg_stop = 4, - arg_pause = 5, - arg_restart = 6, - options = 7, - }; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - tmp = ast_strdupa(data); - memset(argv, 0, sizeof(argv)); - - argc = ast_app_separate_args(tmp, '|', argv, sizeof(argv) / sizeof(argv[0])); - - if (argc < 1) { - ast_log(LOG_WARNING, "ControlPlayback requires an argument (filename)\n"); - ast_module_user_remove(u); - return -1; - } - - skipms = argv[arg_skip] ? atoi(argv[arg_skip]) : 3000; - if (!skipms) - skipms = 3000; - - if (!argv[arg_fwd] || !is_on_phonepad(*argv[arg_fwd])) - argv[arg_fwd] = "#"; - if (!argv[arg_rev] || !is_on_phonepad(*argv[arg_rev])) - argv[arg_rev] = "*"; - if (argv[arg_stop] && !is_on_phonepad(*argv[arg_stop])) - argv[arg_stop] = NULL; - if (argv[arg_pause] && !is_on_phonepad(*argv[arg_pause])) - argv[arg_pause] = NULL; - if (argv[arg_restart] && !is_on_phonepad(*argv[arg_restart])) - argv[arg_restart] = NULL; - - if (argv[options]) { - if (strchr(argv[options], 'j')) - priority_jump = 1; - } - - res = ast_control_streamfile(chan, argv[arg_file], argv[arg_fwd], argv[arg_rev], argv[arg_stop], argv[arg_pause], argv[arg_restart], skipms); - - /* If we stopped on one of our stop keys, return 0 */ - if (res > 0 && argv[arg_stop] && strchr(argv[arg_stop], res)) { - res = 0; - pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "USERSTOPPED"); - } else { - if (res < 0) { - if (priority_jump || ast_opt_priority_jumping) { - if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { - ast_log(LOG_WARNING, "ControlPlayback tried to jump to priority n+101 as requested, but priority didn't exist\n"); - } - } - res = 0; - pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "ERROR"); - } else - pbx_builtin_setvar_helper(chan, "CPLAYBACKSTATUS", "SUCCESS"); - } - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - res = ast_unregister_application(app); - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, controlplayback_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Control Playback Application"); diff --git a/apps/app_dahdibarge.c b/apps/app_dahdibarge.c deleted file mode 100644 index cba85a9b6..000000000 --- a/apps/app_dahdibarge.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer - * - * 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 - * - * \note Special thanks to comphealth.com for sponsoring this - * GPL application. - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/config.h" -#include "asterisk/app.h" -#include "asterisk/options.h" -#include "asterisk/cli.h" -#include "asterisk/say.h" -#include "asterisk/utils.h" - -#include "asterisk/dahdi_compat.h" - -static char *dahdi_app = "DAHDIBarge"; -static char *zap_app = "ZapBarge"; - -static char *dahdi_synopsis = "Barge in (monitor) DAHDI channel"; -static char *zap_synopsis = "Barge in (monitor) Zap channel"; - -static char *dahdi_descrip = -" DAHDIBarge([channel]): Barges in on a specified DAHDI\n" -"channel or prompts if one is not specified. Returns\n" -"-1 when caller user hangs up and is independent of the\n" -"state of the channel being monitored."; - -static char *zap_descrip = -" ZapBarge([channel]): Barges in on a specified Zaptel\n" -"channel or prompts if one is not specified. Returns\n" -"-1 when caller user hangs up and is independent of the\n" -"state of the channel being monitored."; - -#define CONF_SIZE 160 - -static int careful_write(int fd, unsigned char *data, int len) -{ - int res; - while(len) { - res = write(fd, data, len); - if (res < 1) { - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno)); - return -1; - } else - return 0; - } - len -= res; - data += res; - } - return 0; -} - -static int conf_run(struct ast_channel *chan, int confno, int confflags) -{ - int fd; - struct dahdi_confinfo ztc; - struct ast_frame *f; - struct ast_channel *c; - struct ast_frame fr; - int outfd; - int ms; - int nfds; - int res; - int flags; - int retryzap; - int origfd; - int ret = -1; - struct dahdi_bufferinfo bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; - char *buf = __buf + AST_FRIENDLY_OFFSET; - - /* Set it into U-law mode (write) */ - if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name); - goto outrun; - } - - /* Set it into U-law mode (read) */ - if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name); - goto outrun; - } - ast_indicate(chan, -1); - retryzap = strcasecmp(chan->tech->type, dahdi_chan_name); -zapretry: - origfd = chan->fds[0]; - if (retryzap) { - fd = open(DAHDI_FILE_PSEUDO, O_RDWR); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); - goto outrun; - } - /* Make non-blocking */ - flags = fcntl(fd, F_GETFL); - if (flags < 0) { - ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - /* Setup buffering information */ - memset(&bi, 0, sizeof(bi)); - bi.bufsize = CONF_SIZE; - bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.numbufs = 4; - if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { - ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - nfds = 1; - } else { - /* XXX Make sure we're not running on a pseudo channel XXX */ - fd = chan->fds[0]; - nfds = 0; - } - memset(&ztc, 0, sizeof(ztc)); - /* Check to see if we're in a conference... */ - ztc.chan = 0; - if (ioctl(fd, DAHDI_GETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error getting conference\n"); - close(fd); - goto outrun; - } - if (ztc.confmode) { - /* Whoa, already in a conference... Retry... */ - if (!retryzap) { - ast_log(LOG_DEBUG, "Channel is in a conference already, retrying with pseudo\n"); - retryzap = 1; - goto zapretry; - } - } - memset(&ztc, 0, sizeof(ztc)); - /* Add us to the conference */ - ztc.chan = 0; - ztc.confno = confno; - ztc.confmode = DAHDI_CONF_MONITORBOTH; - - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - goto outrun; - } - ast_log(LOG_DEBUG, "Placed channel %s in channel %d monitor\n", chan->name, confno); - - for(;;) { - outfd = -1; - ms = -1; - c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms); - if (c) { - if (c->fds[0] != origfd) { - if (retryzap) { - /* Kill old pseudo */ - close(fd); - } - ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n"); - retryzap = 0; - goto zapretry; - } - f = ast_read(c); - if (!f) - break; - if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#')) { - ret = 0; - ast_frfree(f); - break; - } else if (fd != chan->fds[0]) { - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass == AST_FORMAT_ULAW) { - /* Carefully write */ - careful_write(fd, f->data, f->datalen); - } else - ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass); - } - } - ast_frfree(f); - } else if (outfd > -1) { - res = read(outfd, buf, CONF_SIZE); - if (res > 0) { - memset(&fr, 0, sizeof(fr)); - fr.frametype = AST_FRAME_VOICE; - fr.subclass = AST_FORMAT_ULAW; - fr.datalen = res; - fr.samples = res; - fr.data = buf; - fr.offset = AST_FRIENDLY_OFFSET; - if (ast_write(chan, &fr) < 0) { - ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); - /* break; */ - } - } else - ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); - } - } - if (fd != chan->fds[0]) - close(fd); - else { - /* Take out of conference */ - /* Add us to the conference */ - ztc.chan = 0; - ztc.confno = 0; - ztc.confmode = 0; - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - } - } - -outrun: - - return ret; -} - -static int exec(struct ast_channel *chan, void *data, int dahdimode) -{ - int res=-1; - struct ast_module_user *u; - int retrycnt = 0; - int confflags = 0; - int confno = 0; - char confstr[80] = ""; - - u = ast_module_user_add(chan); - - if (!ast_strlen_zero(data)) { - if (dahdimode) { - if ((sscanf(data, "DAHDI/%d", &confno) != 1) && - (sscanf(data, "%d", &confno) != 1)) { - ast_log(LOG_WARNING, "Argument (if specified) must be a channel number, not '%s'\n", (char *) data); - ast_module_user_remove(u); - return 0; - } - } else { - if ((sscanf(data, "Zap/%d", &confno) != 1) && - (sscanf(data, "%d", &confno) != 1)) { - ast_log(LOG_WARNING, "Argument (if specified) must be a channel number, not '%s'\n", (char *) data); - ast_module_user_remove(u); - return 0; - } - } - } - - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - - while(!confno && (++retrycnt < 4)) { - /* Prompt user for conference number */ - confstr[0] = '\0'; - res = ast_app_getdata(chan, "conf-getchannel",confstr, sizeof(confstr) - 1, 0); - if (res <0) goto out; - if (sscanf(confstr, "%d", &confno) != 1) - confno = 0; - } - if (confno) { - /* XXX Should prompt user for pin if pin is required XXX */ - /* Run the conference */ - res = conf_run(chan, confno, confflags); - } -out: - /* Do the conference */ - ast_module_user_remove(u); - return res; -} - -static int exec_zap(struct ast_channel *chan, void *data) -{ - ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", zap_app, dahdi_app); - - return exec(chan, data, 0); -} - -static int exec_dahdi(struct ast_channel *chan, void *data) -{ - return exec(chan, data, 1); -} - -static int unload_module(void) -{ - int res = 0; - - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - res |= ast_unregister_application(dahdi_app); - } - - res |= ast_unregister_application(zap_app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - int res = 0; - - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - res |= ast_register_application(dahdi_app, exec_dahdi, dahdi_synopsis, dahdi_descrip); - } - - res |= ast_register_application(zap_app, exec_zap, zap_synopsis, zap_descrip); - - return res; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Barge in on channel application"); diff --git a/apps/app_dahdiras.c b/apps/app_dahdiras.c deleted file mode 100644 index 4ac5daa3d..000000000 --- a/apps/app_dahdiras.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Execute an ISDN RAS - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#ifdef __linux__ -#include -#else -#include -#endif /* __linux__ */ - -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/options.h" - -#include "asterisk/dahdi_compat.h" - -static char *dahdi_app = "DAHDIRAS"; -static char *zap_app = "ZapRAS"; - -static char *dahdi_synopsis = "Executes DAHDI ISDN RAS application"; -static char *zap_synopsis = "Executes Zaptel ISDN RAS application"; - -static char *dahdi_descrip = -" DAHDIRAS(args): Executes a RAS server using pppd on the given channel.\n" -"The channel must be a clear channel (i.e. PRI source) and a DAHDI\n" -"channel to be able to use this function (no modem emulation is included).\n" -"Your pppd must have the DAHDI plugin available. Arguments should be\n" -"separated by | characters.\n"; - -static char *zap_descrip = -" ZapRAS(args): Executes a RAS server using pppd on the given channel.\n" -"The channel must be a clear channel (i.e. PRI source) and a Zaptel\n" -"channel to be able to use this function (no modem emulation is included).\n" -"Your pppd must have the Zaptel plugin available. Arguments should be\n" -"separated by | characters.\n"; - -#define PPP_MAX_ARGS 32 -#define PPP_EXEC "/usr/sbin/pppd" - -static pid_t spawn_ras(struct ast_channel *chan, char *args) -{ - pid_t pid; - int x; - char *c; - - char *argv[PPP_MAX_ARGS]; - int argc = 0; - char *stringp=NULL; - sigset_t fullset, oldset; - - sigfillset(&fullset); - pthread_sigmask(SIG_BLOCK, &fullset, &oldset); - - /* Start by forking */ - pid = fork(); - if (pid) { - pthread_sigmask(SIG_SETMASK, &oldset, NULL); - return pid; - } - - /* Restore original signal handlers */ - for (x=0;xfds[0], STDIN_FILENO); - - /* Drop high priority */ - if (ast_opt_high_priority) - ast_set_priority(0); - - /* Close other file descriptors */ - for (x=STDERR_FILENO + 1;x<1024;x++) - close(x); - - /* Reset all arguments */ - memset(argv, 0, sizeof(argv)); - - /* First argument is executable, followed by standard - arguments for DAHDI PPP */ - argv[argc++] = PPP_EXEC; - argv[argc++] = "nodetach"; - - /* And all the other arguments */ - stringp=args; - c = strsep(&stringp, "|"); - while(c && strlen(c) && (argc < (PPP_MAX_ARGS - 4))) { - argv[argc++] = c; - c = strsep(&stringp, "|"); - } - - argv[argc++] = "plugin"; -#ifdef HAVE_ZAPTEL - argv[argc++] = "zaptel.so"; -#else - argv[argc++] = "dahdi.so"; -#endif - argv[argc++] = "stdin"; - - /* Finally launch PPP */ - execv(PPP_EXEC, argv); - fprintf(stderr, "Failed to exec PPPD!\n"); - exit(1); -} - -static void run_ras(struct ast_channel *chan, char *args) -{ - pid_t pid; - int status; - int res; - int signalled = 0; - struct dahdi_bufferinfo savebi; - int x; - - res = ioctl(chan->fds[0], DAHDI_GET_BUFINFO, &savebi); - if(res) { - ast_log(LOG_WARNING, "Unable to check buffer policy on channel %s\n", chan->name); - return; - } - - pid = spawn_ras(chan, args); - if (pid < 0) { - ast_log(LOG_WARNING, "Failed to spawn RAS\n"); - } else { - for (;;) { - res = wait4(pid, &status, WNOHANG, NULL); - if (!res) { - /* Check for hangup */ - if (chan->_softhangup && !signalled) { - ast_log(LOG_DEBUG, "Channel '%s' hungup. Signalling RAS at %d to die...\n", chan->name, pid); - kill(pid, SIGTERM); - signalled=1; - } - /* Try again */ - sleep(1); - continue; - } - if (res < 0) { - ast_log(LOG_WARNING, "wait4 returned %d: %s\n", res, strerror(errno)); - } - if (option_verbose > 2) { - if (WIFEXITED(status)) { - ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with status %d\n", chan->name, WEXITSTATUS(status)); - } else if (WIFSIGNALED(status)) { - ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated with signal %d\n", - chan->name, WTERMSIG(status)); - } else { - ast_verbose(VERBOSE_PREFIX_3 "RAS on %s terminated weirdly.\n", chan->name); - } - } - /* Throw back into audio mode */ - x = 1; - ioctl(chan->fds[0], DAHDI_AUDIOMODE, &x); - - /* Restore saved values */ - res = ioctl(chan->fds[0], DAHDI_SET_BUFINFO, &savebi); - if (res < 0) { - ast_log(LOG_WARNING, "Unable to set buffer policy on channel %s\n", chan->name); - } - break; - } - } -} - -static int exec(struct ast_channel *chan, void *data) -{ - int res=-1; - char *args; - struct ast_module_user *u; - struct dahdi_params ztp; - - if (!data) - data = ""; - - u = ast_module_user_add(chan); - - args = ast_strdupa(data); - - /* Answer the channel if it's not up */ - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - if (strcasecmp(chan->tech->type, dahdi_chan_name)) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a %s channel\n", chan->name, dahdi_chan_name); - sleep(2); - } else { - memset(&ztp, 0, sizeof(ztp)); - if (ioctl(chan->fds[0], DAHDI_GET_PARAMS, &ztp)) { - ast_log(LOG_WARNING, "Unable to get parameters\n"); - } else if (ztp.sigtype != DAHDI_SIG_CLEAR) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Channel %s is not a clear channel\n", chan->name); - } else { - /* Everything should be okay. Run PPP. */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Starting RAS on %s\n", chan->name); - /* Execute RAS */ - run_ras(chan, args); - } - } - ast_module_user_remove(u); - return res; -} - -static int exec_warn(struct ast_channel *chan, void *data) -{ - ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", zap_app, dahdi_app); - - return exec(chan, data); -} - -static int unload_module(void) -{ - int res = 0; - - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - res |= ast_unregister_application(dahdi_app); - } - - res |= ast_unregister_application(zap_app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - int res = 0; - - if (*dahdi_chan_mode == CHAN_DAHDI_PLUS_ZAP_MODE) { - res |= ast_register_application(dahdi_app, exec, dahdi_synopsis, dahdi_descrip); - } - - res |= ast_register_application(zap_app, exec_warn, zap_synopsis, zap_descrip); - - return res; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DAHDI RAS Application"); - diff --git a/apps/app_dahdiscan.c b/apps/app_dahdiscan.c deleted file mode 100644 index 6a600756e..000000000 --- a/apps/app_dahdiscan.c +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Mark Spencer - * - * Modified from app_zapbarge by David Troy - * - * 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 - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/config.h" -#include "asterisk/app.h" -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/cli.h" -#include "asterisk/say.h" - -#include "asterisk/dahdi_compat.h" - -static char *app = "DAHDIScan"; -static char *deprecated_app = "ZapScan"; - -static char *synopsis = "Scan Zap channels to monitor calls"; - -static char *descrip = -" ZapScan([group]) allows a call center manager to monitor Zap channels in\n" -"a convenient way. Use '#' to select the next channel and use '*' to exit\n" -"Limit scanning to a channel GROUP by setting the option group argument.\n"; - - -#define CONF_SIZE 160 - -static struct ast_channel *get_zap_channel_locked(int num) { - char name[80]; - - snprintf(name,sizeof(name),"%s/%d-1", dahdi_chan_name, num); - return ast_get_channel_by_name_locked(name); -} - -static int careful_write(int fd, unsigned char *data, int len) -{ - int res; - while(len) { - res = write(fd, data, len); - if (res < 1) { - if (errno != EAGAIN) { - ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno)); - return -1; - } else - return 0; - } - len -= res; - data += res; - } - return 0; -} - -static int conf_run(struct ast_channel *chan, int confno, int confflags) -{ - int fd; - struct dahdi_confinfo ztc; - struct ast_frame *f; - struct ast_channel *c; - struct ast_frame fr; - int outfd; - int ms; - int nfds; - int res; - int flags; - int retryzap; - int origfd; - int ret = -1; - char input[4]; - int ic=0; - struct dahdi_bufferinfo bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; - char *buf = __buf + AST_FRIENDLY_OFFSET; - - /* Set it into U-law mode (write) */ - if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name); - goto outrun; - } - - /* Set it into U-law mode (read) */ - if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) { - ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name); - goto outrun; - } - ast_indicate(chan, -1); - retryzap = strcasecmp(chan->tech->type, "Zap"); - zapretry: - origfd = chan->fds[0]; - if (retryzap) { - fd = open(DAHDI_FILE_PSEUDO, O_RDWR); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); - goto outrun; - } - /* Make non-blocking */ - flags = fcntl(fd, F_GETFL); - if (flags < 0) { - ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { - ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - /* Setup buffering information */ - memset(&bi, 0, sizeof(bi)); - bi.bufsize = CONF_SIZE; - bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.numbufs = 4; - if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { - ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno)); - close(fd); - goto outrun; - } - nfds = 1; - } else { - /* XXX Make sure we're not running on a pseudo channel XXX */ - fd = chan->fds[0]; - nfds = 0; - } - memset(&ztc, 0, sizeof(ztc)); - /* Check to see if we're in a conference... */ - ztc.chan = 0; - if (ioctl(fd, DAHDI_GETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error getting conference\n"); - close(fd); - goto outrun; - } - if (ztc.confmode) { - /* Whoa, already in a conference... Retry... */ - if (!retryzap) { - ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n"); - retryzap = 1; - goto zapretry; - } - } - memset(&ztc, 0, sizeof(ztc)); - /* Add us to the conference */ - ztc.chan = 0; - ztc.confno = confno; - ztc.confmode = DAHDI_CONF_MONITORBOTH; - - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - goto outrun; - } - ast_log(LOG_DEBUG, "Placed channel %s in ZAP channel %d monitor\n", chan->name, confno); - - for(;;) { - outfd = -1; - ms = -1; - c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms); - if (c) { - if (c->fds[0] != origfd) { - if (retryzap) { - /* Kill old pseudo */ - close(fd); - } - ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n"); - retryzap = 0; - goto zapretry; - } - f = ast_read(c); - if (!f) - break; - if(f->frametype == AST_FRAME_DTMF) { - if(f->subclass == '#') { - ret = 0; - break; - } - else if (f->subclass == '*') { - ret = -1; - break; - - } - else { - input[ic++] = f->subclass; - } - if(ic == 3) { - input[ic++] = '\0'; - ic=0; - ret = atoi(input); - ast_verbose(VERBOSE_PREFIX_3 "Zapscan: change channel to %d\n",ret); - break; - } - } - - if (fd != chan->fds[0]) { - if (f->frametype == AST_FRAME_VOICE) { - if (f->subclass == AST_FORMAT_ULAW) { - /* Carefully write */ - careful_write(fd, f->data, f->datalen); - } else - ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass); - } - } - ast_frfree(f); - } else if (outfd > -1) { - res = read(outfd, buf, CONF_SIZE); - if (res > 0) { - memset(&fr, 0, sizeof(fr)); - fr.frametype = AST_FRAME_VOICE; - fr.subclass = AST_FORMAT_ULAW; - fr.datalen = res; - fr.samples = res; - fr.data = buf; - fr.offset = AST_FRIENDLY_OFFSET; - if (ast_write(chan, &fr) < 0) { - ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); - /* break; */ - } - } else - ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); - } - } - if (f) - ast_frfree(f); - if (fd != chan->fds[0]) - close(fd); - else { - /* Take out of conference */ - /* Add us to the conference */ - ztc.chan = 0; - ztc.confno = 0; - ztc.confmode = 0; - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - } - } - - outrun: - - return ret; -} - -static int conf_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - struct ast_module_user *u; - int confflags = 0; - int confno = 0; - char confstr[80] = "", *tmp = NULL; - struct ast_channel *tempchan = NULL, *lastchan = NULL,*ichan = NULL; - struct ast_frame *f; - char *desired_group; - int input=0,search_group=0; - - u = ast_module_user_add(chan); - - if (chan->_state != AST_STATE_UP) - ast_answer(chan); - - desired_group = ast_strdupa(data); - if(!ast_strlen_zero(desired_group)) { - ast_verbose(VERBOSE_PREFIX_3 "Scanning for group %s\n", desired_group); - search_group = 1; - } - - for (;;) { - if (ast_waitfor(chan, 100) < 0) - break; - - f = ast_read(chan); - if (!f) - break; - if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*')) { - ast_frfree(f); - break; - } - ast_frfree(f); - ichan = NULL; - if(input) { - ichan = get_zap_channel_locked(input); - input = 0; - } - - tempchan = ichan ? ichan : ast_channel_walk_locked(tempchan); - - if ( !tempchan && !lastchan ) - break; - - if (tempchan && search_group) { - const char *mygroup; - if((mygroup = pbx_builtin_getvar_helper(tempchan, "GROUP")) && (!strcmp(mygroup, desired_group))) { - ast_verbose(VERBOSE_PREFIX_3 "Found Matching Channel %s in group %s\n", tempchan->name, desired_group); - } else { - ast_mutex_unlock(&tempchan->lock); - lastchan = tempchan; - continue; - } - } - if (tempchan && (!strcmp(tempchan->tech->type, "Zap")) && (tempchan != chan) ) { - ast_verbose(VERBOSE_PREFIX_3 "Zap channel %s is in-use, monitoring...\n", tempchan->name); - ast_copy_string(confstr, tempchan->name, sizeof(confstr)); - ast_mutex_unlock(&tempchan->lock); - if ((tmp = strchr(confstr,'-'))) { - *tmp = '\0'; - } - confno = atoi(strchr(confstr,'/') + 1); - ast_stopstream(chan); - ast_say_number(chan, confno, AST_DIGIT_ANY, chan->language, (char *) NULL); - res = conf_run(chan, confno, confflags); - if (res<0) break; - input = res; - } else if (tempchan) - ast_mutex_unlock(&tempchan->lock); - lastchan = tempchan; - } - ast_module_user_remove(u); - return res; -} - -static int conf_exec_warn(struct ast_channel *chan, void *data) -{ - ast_log(LOG_WARNING, "Use of the command %s is deprecated, please use %s instead.\n", deprecated_app, app); - return conf_exec(chan, data); -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - ast_register_application(deprecated_app, conf_exec_warn, synopsis, descrip); - return ast_register_application(app, conf_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Scan Zap channels application"); - diff --git a/apps/app_db.c b/apps/app_db.c deleted file mode 100644 index 0c8d0585b..000000000 --- a/apps/app_db.c +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * Copyright (C) 2003, Jefferson Noxon - * - * Mark Spencer - * Jefferson Noxon - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed 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 - * \author Jefferson Noxon - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/options.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/astdb.h" -#include "asterisk/lock.h" -#include "asterisk/options.h" - -/*! \todo XXX Remove this application after 1.4 is relased */ -static char *d_descrip = -" DBdel(family/key): This application will delete a key from the Asterisk\n" -"database.\n" -" This application has been DEPRECATED in favor of the DB_DELETE function.\n"; - -static char *dt_descrip = -" DBdeltree(family[/keytree]): This application will delete a family or keytree\n" -"from the Asterisk database\n"; - -static char *d_app = "DBdel"; -static char *dt_app = "DBdeltree"; - -static char *d_synopsis = "Delete a key from the database"; -static char *dt_synopsis = "Delete a family or keytree from the database"; - - -static int deltree_exec(struct ast_channel *chan, void *data) -{ - char *argv, *family, *keytree; - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - argv = ast_strdupa(data); - - if (strchr(argv, '/')) { - family = strsep(&argv, "/"); - keytree = strsep(&argv, "\0"); - if (!family || !keytree) { - ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n"); - ast_module_user_remove(u); - return 0; - } - if (ast_strlen_zero(keytree)) - keytree = 0; - } else { - family = argv; - keytree = 0; - } - - if (option_verbose > 2) { - if (keytree) - ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s, keytree=%s\n", family, keytree); - else - ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: family=%s\n", family); - } - - if (ast_db_deltree(family, keytree)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "DBdeltree: Error deleting key from database.\n"); - } - - ast_module_user_remove(u); - - return 0; -} - -static int del_exec(struct ast_channel *chan, void *data) -{ - char *argv, *family, *key; - struct ast_module_user *u; - static int deprecation_warning = 0; - - u = ast_module_user_add(chan); - - if (!deprecation_warning) { - deprecation_warning = 1; - ast_log(LOG_WARNING, "The DBdel application has been deprecated in favor of the DB_DELETE dialplan function!\n"); - } - - argv = ast_strdupa(data); - - if (strchr(argv, '/')) { - family = strsep(&argv, "/"); - key = strsep(&argv, "\0"); - if (!family || !key) { - ast_log(LOG_DEBUG, "Ignoring; Syntax error in argument\n"); - ast_module_user_remove(u); - return 0; - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "DBdel: family=%s, key=%s\n", family, key); - if (ast_db_del(family, key)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "DBdel: Error deleting key from database.\n"); - } - } else { - ast_log(LOG_DEBUG, "Ignoring, no parameters\n"); - } - - ast_module_user_remove(u); - - return 0; -} - -static int unload_module(void) -{ - int retval; - - retval = ast_unregister_application(dt_app); - retval |= ast_unregister_application(d_app); - - return retval; -} - -static int load_module(void) -{ - int retval; - - retval = ast_register_application(d_app, del_exec, d_synopsis, d_descrip); - retval |= ast_register_application(dt_app, deltree_exec, dt_synopsis, dt_descrip); - - return retval; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Database Access Functions"); diff --git a/apps/app_dial.c b/apps/app_dial.c deleted file mode 100644 index 303b36121..000000000 --- a/apps/app_dial.c +++ /dev/null @@ -1,2015 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * 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 dial() & retrydial() - Trivial application to dial a channel and send an URL on answer - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - chan_local - ***/ - - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/say.h" -#include "asterisk/config.h" -#include "asterisk/features.h" -#include "asterisk/musiconhold.h" -#include "asterisk/callerid.h" -#include "asterisk/utils.h" -#include "asterisk/app.h" -#include "asterisk/causes.h" -#include "asterisk/rtp.h" -#include "asterisk/cdr.h" -#include "asterisk/manager.h" -#include "asterisk/privacy.h" -#include "asterisk/stringfields.h" -#include "asterisk/global_datastores.h" - -static char *app = "Dial"; - -static char *synopsis = "Place a call and connect to the current channel"; - -static char *descrip = -" Dial(Technology/resource[&Tech2/resource2...][|timeout][|options][|URL]):\n" -"This application will place calls to one or more specified channels. As soon\n" -"as one of the requested channels answers, the originating channel will be\n" -"answered, if it has not already been answered. These two channels will then\n" -"be active in a bridged call. All other channels that were requested will then\n" -"be hung up.\n" -" Unless there is a timeout specified, the Dial application will wait\n" -"indefinitely until one of the called channels answers, the user hangs up, or\n" -"if all of the called channels are busy or unavailable. Dialplan executing will\n" -"continue if no requested channels can be called, or if the timeout expires.\n\n" -" This application sets the following channel variables upon completion:\n" -" DIALEDTIME - This is the time from dialing a channel until when it\n" -" is disconnected.\n" -" ANSWEREDTIME - This is the amount of time for actual call.\n" -" DIALSTATUS - This is the status of the call:\n" -" CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n" -" DONTCALL | TORTURE | INVALIDARGS\n" -" For the Privacy and Screening Modes, the DIALSTATUS variable will be set to\n" -"DONTCALL if the called party chooses to send the calling party to the 'Go Away'\n" -"script. The DIALSTATUS variable will be set to TORTURE if the called party\n" -"wants to send the caller to the 'torture' script.\n" -" This application will report normal termination if the originating channel\n" -"hangs up, or if the call is bridged and either of the parties in the bridge\n" -"ends the call.\n" -" The optional URL will be sent to the called party if the channel supports it.\n" -" If the OUTBOUND_GROUP variable is set, all peer channels created by this\n" -"application will be put into that group (as in Set(GROUP()=...).\n" -" If the OUTBOUND_GROUP_ONCE variable is set, all peer channels created by this\n" -"application will be put into that group (as in Set(GROUP()=...). Unlike OUTBOUND_GROUP,\n" -"however, the variable will be unset after use.\n\n" -" Options:\n" -" A(x) - Play an announcement to the called party, using 'x' as the file.\n" -" C - Reset the CDR for this call.\n" -" d - Allow the calling user to dial a 1 digit extension while waiting for\n" -" a call to be answered. Exit to that extension if it exists in the\n" -" current context, or the context defined in the EXITCONTEXT variable,\n" -" if it exists.\n" -" D([called][:calling]) - Send the specified DTMF strings *after* the called\n" -" party has answered, but before the call gets bridged. The 'called'\n" -" DTMF string is sent to the called party, and the 'calling' DTMF\n" -" string is sent to the calling party. Both parameters can be used\n" -" alone.\n" -" f - Force the callerid of the *calling* channel to be set as the\n" -" extension associated with the channel using a dialplan 'hint'.\n" -" For example, some PSTNs do not allow CallerID to be set to anything\n" -" other than the number assigned to the caller.\n" -" g - Proceed with dialplan execution at the current extension if the\n" -" destination channel hangs up.\n" -" G(context^exten^pri) - If the call is answered, transfer the calling party to\n" -" the specified priority and the called party to the specified priority+1.\n" -" Optionally, an extension, or extension and context may be specified. \n" -" Otherwise, the current extension is used. You cannot use any additional\n" -" action post answer options in conjunction with this option.\n" -" h - Allow the called party to hang up by sending the '*' DTMF digit, or\n" -" whatever sequence was defined in the featuremap section for\n" -" 'disconnect' in features.conf\n" -" H - Allow the calling party to hang up by hitting the '*' DTMF digit, or\n" -" whatever sequence was defined in the featuremap section for\n" -" 'disconnect' in features.conf\n" -" i - Asterisk will ignore any forwarding requests it may receive on this\n" -" dial attempt.\n" -" j - Jump to priority n+101 if all of the requested channels were busy.\n" -" k - Allow the called party to enable parking of the call by sending\n" -" the DTMF sequence defined for call parking in the featuremap section of features.conf.\n" -" K - Allow the calling party to enable parking of the call by sending\n" -" the DTMF sequence defined for call parking in the featuremap section of features.conf.\n" -" L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n" -" left. Repeat the warning every 'z' ms. The following special\n" -" variables can be used with this option:\n" -" * LIMIT_PLAYAUDIO_CALLER yes|no (default yes)\n" -" Play sounds to the caller.\n" -" * LIMIT_PLAYAUDIO_CALLEE yes|no\n" -" Play sounds to the callee.\n" -" * LIMIT_TIMEOUT_FILE File to play when time is up.\n" -" * LIMIT_CONNECT_FILE File to play when call begins.\n" -" * LIMIT_WARNING_FILE File to play as warning if 'y' is defined.\n" -" The default is to say the time remaining.\n" -" m([class]) - Provide hold music to the calling party until a requested\n" -" channel answers. A specific MusicOnHold class can be\n" -" specified.\n" -" M(x[^arg]) - Execute the Macro for the *called* channel before connecting\n" -" to the calling channel. Arguments can be specified to the Macro\n" -" using '^' as a delimeter. The Macro can set the variable\n" -" MACRO_RESULT to specify the following actions after the Macro is\n" -" finished executing.\n" -" * ABORT Hangup both legs of the call.\n" -" * CONGESTION Behave as if line congestion was encountered.\n" -" * BUSY Behave as if a busy signal was encountered. This will also\n" -" have the application jump to priority n+101 if the\n" -" 'j' option is set.\n" -" * CONTINUE Hangup the called party and allow the calling party\n" -" to continue dialplan execution at the next priority.\n" -" * GOTO:^^ - Transfer the call to the\n" -" specified priority. Optionally, an extension, or\n" -" extension and priority can be specified.\n" -" You cannot use any additional action post answer options in conjunction\n" -" with this option. Also, pbx services are not run on the peer (called) channel,\n" -" so you will not be able to set timeouts via the TIMEOUT() function in this macro.\n" -" n - This option is a modifier for the screen/privacy mode. It specifies\n" -" that no introductions are to be saved in the priv-callerintros\n" -" directory.\n" -" N - This option is a modifier for the screen/privacy mode. It specifies\n" -" that if callerID is present, do not screen the call.\n" -" o - Specify that the CallerID that was present on the *calling* channel\n" -" be set as the CallerID on the *called* channel. This was the\n" -" behavior of Asterisk 1.0 and earlier.\n" -" O([x]) - \"Operator Services\" mode (Zaptel channel to Zaptel channel\n" -" only, if specified on non-Zaptel interface, it will be ignored).\n" -" When the destination answers (presumably an operator services\n" -" station), the originator no longer has control of their line.\n" -" They may hang up, but the switch will not release their line\n" -" until the destination party hangs up (the operator). Specified\n" -" without an arg, or with 1 as an arg, the originator hanging up\n" -" will cause the phone to ring back immediately. With a 2 specified,\n" -" when the \"operator\" flashes the trunk, it will ring their phone\n" -" back.\n" -" p - This option enables screening mode. This is basically Privacy mode\n" -" without memory.\n" -" P([x]) - Enable privacy mode. Use 'x' as the family/key in the database if\n" -" it is provided. The current extension is used if a database\n" -" family/key is not specified.\n" -" r - Indicate ringing to the calling party. Pass no audio to the calling\n" -" party until the called channel has answered.\n" -" S(x) - Hang up the call after 'x' seconds *after* the called party has\n" -" answered the call.\n" -" t - Allow the called party to transfer the calling party by sending the\n" -" DTMF sequence defined in the blindxfer setting in the featuremap section\n" -" of features.conf.\n" -" T - Allow the calling party to transfer the called party by sending the\n" -" DTMF sequence defined in the blindxfer setting in the featuremap section\n" -" of features.conf.\n" -" w - Allow the called party to enable recording of the call by sending\n" -" the DTMF sequence defined in the automon setting in the featuremap section\n" -" of features.conf.\n" -" W - Allow the calling party to enable recording of the call by sending\n" -" the DTMF sequence defined in the automon setting in the featuremap section\n" -" of features.conf.\n"; - -/* RetryDial App by Anthony Minessale II Jan/2005 */ -static char *rapp = "RetryDial"; -static char *rsynopsis = "Place a call, retrying on failure allowing optional exit extension."; -static char *rdescrip = -" RetryDial(announce|sleep|retries|dialargs): This application will attempt to\n" -"place a call using the normal Dial application. If no channel can be reached,\n" -"the 'announce' file will be played. Then, it will wait 'sleep' number of\n" -"seconds before retrying the call. After 'retries' number of attempts, the\n" -"calling channel will continue at the next priority in the dialplan. If the\n" -"'retries' setting is set to 0, this application will retry endlessly.\n" -" While waiting to retry a call, a 1 digit extension may be dialed. If that\n" -"extension exists in either the context defined in ${EXITCONTEXT} or the current\n" -"one, The call will jump to that extension immediately.\n" -" The 'dialargs' are specified in the same format that arguments are provided\n" -"to the Dial application.\n"; - -enum { - OPT_ANNOUNCE = (1 << 0), - OPT_RESETCDR = (1 << 1), - OPT_DTMF_EXIT = (1 << 2), - OPT_SENDDTMF = (1 << 3), - OPT_FORCECLID = (1 << 4), - OPT_GO_ON = (1 << 5), - OPT_CALLEE_HANGUP = (1 << 6), - OPT_CALLER_HANGUP = (1 << 7), - OPT_PRIORITY_JUMP = (1 << 8), - OPT_DURATION_LIMIT = (1 << 9), - OPT_MUSICBACK = (1 << 10), - OPT_CALLEE_MACRO = (1 << 11), - OPT_SCREEN_NOINTRO = (1 << 12), - OPT_SCREEN_NOCLID = (1 << 13), - OPT_ORIGINAL_CLID = (1 << 14), - OPT_SCREENING = (1 << 15), - OPT_PRIVACY = (1 << 16), - OPT_RINGBACK = (1 << 17), - OPT_DURATION_STOP = (1 << 18), - OPT_CALLEE_TRANSFER = (1 << 19), - OPT_CALLER_TRANSFER = (1 << 20), - OPT_CALLEE_MONITOR = (1 << 21), - OPT_CALLER_MONITOR = (1 << 22), - OPT_GOTO = (1 << 23), - OPT_OPERMODE = (1 << 24), - OPT_CALLEE_PARK = (1 << 25), - OPT_CALLER_PARK = (1 << 26), - OPT_IGNORE_FORWARDING = (1 << 27), -} dial_exec_option_flags; - -#define DIAL_STILLGOING (1 << 30) -#define DIAL_NOFORWARDHTML (1 << 31) - -enum { - OPT_ARG_ANNOUNCE = 0, - OPT_ARG_SENDDTMF, - OPT_ARG_GOTO, - OPT_ARG_DURATION_LIMIT, - OPT_ARG_MUSICBACK, - OPT_ARG_CALLEE_MACRO, - OPT_ARG_PRIVACY, - OPT_ARG_DURATION_STOP, - OPT_ARG_OPERMODE, - /* note: this entry _MUST_ be the last one in the enum */ - OPT_ARG_ARRAY_SIZE, -} dial_exec_option_args; - -AST_APP_OPTIONS(dial_exec_options, { - AST_APP_OPTION_ARG('A', OPT_ANNOUNCE, OPT_ARG_ANNOUNCE), - AST_APP_OPTION('C', OPT_RESETCDR), - AST_APP_OPTION('d', OPT_DTMF_EXIT), - AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF), - AST_APP_OPTION('f', OPT_FORCECLID), - AST_APP_OPTION('g', OPT_GO_ON), - AST_APP_OPTION_ARG('G', OPT_GOTO, OPT_ARG_GOTO), - AST_APP_OPTION('h', OPT_CALLEE_HANGUP), - AST_APP_OPTION('H', OPT_CALLER_HANGUP), - AST_APP_OPTION('i', OPT_IGNORE_FORWARDING), - AST_APP_OPTION('j', OPT_PRIORITY_JUMP), - AST_APP_OPTION('k', OPT_CALLEE_PARK), - AST_APP_OPTION('K', OPT_CALLER_PARK), - AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT), - AST_APP_OPTION_ARG('m', OPT_MUSICBACK, OPT_ARG_MUSICBACK), - AST_APP_OPTION_ARG('M', OPT_CALLEE_MACRO, OPT_ARG_CALLEE_MACRO), - AST_APP_OPTION('n', OPT_SCREEN_NOINTRO), - AST_APP_OPTION('N', OPT_SCREEN_NOCLID), - AST_APP_OPTION('o', OPT_ORIGINAL_CLID), - AST_APP_OPTION_ARG('O', OPT_OPERMODE,OPT_ARG_OPERMODE), - AST_APP_OPTION('p', OPT_SCREENING), - AST_APP_OPTION_ARG('P', OPT_PRIVACY, OPT_ARG_PRIVACY), - AST_APP_OPTION('r', OPT_RINGBACK), - AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP), - AST_APP_OPTION('t', OPT_CALLEE_TRANSFER), - AST_APP_OPTION('T', OPT_CALLER_TRANSFER), - AST_APP_OPTION('w', OPT_CALLEE_MONITOR), - AST_APP_OPTION('W', OPT_CALLER_MONITOR), -}); - -#define CAN_EARLY_BRIDGE(flags,chan,peer) (!ast_test_flag(flags, OPT_CALLEE_HANGUP | \ - OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \ - OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | OPT_CALLER_PARK) && \ - !chan->audiohooks && !peer->audiohooks) - -/* We define a custom "local user" structure because we - use it not only for keeping track of what is in use but - also for keeping track of who we're dialing. */ - -struct dial_localuser { - struct ast_channel *chan; - unsigned int flags; - struct dial_localuser *next; -}; - - -static void hanguptree(struct dial_localuser *outgoing, struct ast_channel *exception) -{ - /* Hang up a tree of stuff */ - struct dial_localuser *oo; - while (outgoing) { - /* Hangup any existing lines we have open */ - if (outgoing->chan && (outgoing->chan != exception)) - ast_hangup(outgoing->chan); - oo = outgoing; - outgoing=outgoing->next; - free(oo); - } -} - -#define AST_MAX_WATCHERS 256 - -#define HANDLE_CAUSE(cause, chan) do { \ - switch(cause) { \ - case AST_CAUSE_BUSY: \ - if (chan->cdr) \ - ast_cdr_busy(chan->cdr); \ - numbusy++; \ - break; \ - case AST_CAUSE_CONGESTION: \ - if (chan->cdr) \ - ast_cdr_failed(chan->cdr); \ - numcongestion++; \ - break; \ - case AST_CAUSE_NO_ROUTE_DESTINATION: \ - case AST_CAUSE_UNREGISTERED: \ - if (chan->cdr) \ - ast_cdr_failed(chan->cdr); \ - numnochan++; \ - break; \ - case AST_CAUSE_NORMAL_CLEARING: \ - break; \ - default: \ - numnochan++; \ - break; \ - } \ -} while (0) - - -static int onedigit_goto(struct ast_channel *chan, const char *context, char exten, int pri) -{ - char rexten[2] = { exten, '\0' }; - - if (context) { - if (!ast_goto_if_exists(chan, context, rexten, pri)) - return 1; - } else { - if (!ast_goto_if_exists(chan, chan->context, rexten, pri)) - return 1; - else if (!ast_strlen_zero(chan->macrocontext)) { - if (!ast_goto_if_exists(chan, chan->macrocontext, rexten, pri)) - return 1; - } - } - return 0; -} - - -static const char *get_cid_name(char *name, int namelen, struct ast_channel *chan) -{ - const char *context = S_OR(chan->macrocontext, chan->context); - const char *exten = S_OR(chan->macroexten, chan->exten); - - return ast_get_hint(NULL, 0, name, namelen, chan, context, exten) ? name : ""; -} - -static void senddialevent(struct ast_channel *src, struct ast_channel *dst) -{ - /* XXX do we need also CallerIDnum ? */ - manager_event(EVENT_FLAG_CALL, "Dial", - "Source: %s\r\n" - "Destination: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "SrcUniqueID: %s\r\n" - "DestUniqueID: %s\r\n", - src->name, dst->name, S_OR(src->cid.cid_num, ""), - S_OR(src->cid.cid_name, ""), src->uniqueid, - dst->uniqueid); -} - -static struct ast_channel *wait_for_answer(struct ast_channel *in, struct dial_localuser *outgoing, int *to, struct ast_flags *peerflags, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart, int priority_jump, int *result) -{ - int numbusy = busystart; - int numcongestion = congestionstart; - int numnochan = nochanstart; - int prestart = busystart + congestionstart + nochanstart; - int orig = *to; - struct ast_channel *peer = NULL; - /* single is set if only one destination is enabled */ - int single = outgoing && !outgoing->next && !ast_test_flag(outgoing, OPT_MUSICBACK | OPT_RINGBACK); - - if (single) { - /* Turn off hold music, etc */ - ast_deactivate_generator(in); - /* If we are calling a single channel, make them compatible for in-band tone purpose */ - ast_channel_make_compatible(outgoing->chan, in); - } - - - while (*to && !peer) { - struct dial_localuser *o; - int pos = 0; /* how many channels do we handle */ - int numlines = prestart; - struct ast_channel *winner; - struct ast_channel *watchers[AST_MAX_WATCHERS]; - - watchers[pos++] = in; - for (o = outgoing; o; o = o->next) { - /* Keep track of important channels */ - if (ast_test_flag(o, DIAL_STILLGOING) && o->chan) - watchers[pos++] = o->chan; - numlines++; - } - if (pos == 1) { /* only the input channel is available */ - if (numlines == (numbusy + numcongestion + numnochan)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_2 "Everyone is busy/congested at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan); - if (numbusy) - strcpy(status, "BUSY"); - else if (numcongestion) - strcpy(status, "CONGESTION"); - else if (numnochan) - strcpy(status, "CHANUNAVAIL"); - if (ast_opt_priority_jumping || priority_jump) - ast_goto_if_exists(in, in->context, in->exten, in->priority + 101); - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "No one is available to answer at this time (%d:%d/%d/%d)\n", numlines, numbusy, numcongestion, numnochan); - } - *to = 0; - return NULL; - } - winner = ast_waitfor_n(watchers, pos, to); - for (o = outgoing; o; o = o->next) { - struct ast_frame *f; - struct ast_channel *c = o->chan; - - if (c == NULL) - continue; - if (ast_test_flag(o, DIAL_STILLGOING) && c->_state == AST_STATE_UP) { - if (!peer) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s answered %s\n", c->name, in->name); - peer = c; - ast_copy_flags(peerflags, o, - OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | - OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | - OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | - OPT_CALLEE_PARK | OPT_CALLER_PARK | - DIAL_NOFORWARDHTML); - ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext)); - ast_copy_string(c->exten, "", sizeof(c->exten)); - } - continue; - } - if (c != winner) - continue; - if (!ast_strlen_zero(c->call_forward)) { - char tmpchan[256]; - char *stuff; - char *tech; - int cause; - - ast_copy_string(tmpchan, c->call_forward, sizeof(tmpchan)); - if ((stuff = strchr(tmpchan, '/'))) { - *stuff++ = '\0'; - tech = tmpchan; - } else { - const char *forward_context = pbx_builtin_getvar_helper(c, "FORWARD_CONTEXT"); - snprintf(tmpchan, sizeof(tmpchan), "%s@%s", c->call_forward, forward_context ? forward_context : c->context); - stuff = tmpchan; - tech = "Local"; - } - /* Before processing channel, go ahead and check for forwarding */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, c->name); - /* If we have been told to ignore forwards, just set this channel to null and continue processing extensions normally */ - if (ast_test_flag(peerflags, OPT_IGNORE_FORWARDING)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' prevented.\n", in->name, tech, stuff); - c = o->chan = NULL; - cause = AST_CAUSE_BUSY; - } else { - /* Setup parameters */ - if ((c = o->chan = ast_request(tech, in->nativeformats, stuff, &cause))) { - if (single) - ast_channel_make_compatible(o->chan, in); - ast_channel_inherit_variables(in, o->chan); - ast_channel_datastore_inherit(in, o->chan); - } else - ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s' (cause = %d)\n", tech, stuff, cause); - } - if (!c) { - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(cause, in); - } else { - ast_rtp_make_compatible(c, in, single); - if (c->cid.cid_num) - free(c->cid.cid_num); - c->cid.cid_num = NULL; - if (c->cid.cid_name) - free(c->cid.cid_name); - c->cid.cid_name = NULL; - - if (ast_test_flag(o, OPT_FORCECLID)) { - c->cid.cid_num = ast_strdup(S_OR(in->macroexten, in->exten)); - ast_string_field_set(c, accountcode, winner->accountcode); - c->cdrflags = winner->cdrflags; - } else { - c->cid.cid_num = ast_strdup(in->cid.cid_num); - c->cid.cid_name = ast_strdup(in->cid.cid_name); - ast_string_field_set(c, accountcode, in->accountcode); - c->cdrflags = in->cdrflags; - } - - if (in->cid.cid_ani) { - if (c->cid.cid_ani) - free(c->cid.cid_ani); - c->cid.cid_ani = ast_strdup(in->cid.cid_ani); - } - if (c->cid.cid_rdnis) - free(c->cid.cid_rdnis); - c->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); - if (ast_call(c, tmpchan, 0)) { - ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); - ast_clear_flag(o, DIAL_STILLGOING); - ast_hangup(c); - c = o->chan = NULL; - numnochan++; - } else { - senddialevent(in, c); - /* After calling, set callerid to extension */ - if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) { - char cidname[AST_MAX_EXTENSION] = ""; - ast_set_callerid(c, S_OR(in->macroexten, in->exten), get_cid_name(cidname, sizeof(cidname), in), NULL); - } - } - } - /* Hangup the original channel now, in case we needed it */ - ast_hangup(winner); - continue; - } - f = ast_read(winner); - if (!f) { - in->hangupcause = c->hangupcause; - ast_hangup(c); - c = o->chan = NULL; - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(in->hangupcause, in); - continue; - } - if (f->frametype == AST_FRAME_CONTROL) { - switch(f->subclass) { - case AST_CONTROL_ANSWER: - /* This is our guy if someone answered. */ - if (!peer) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", c->name, in->name); - peer = c; - if (peer->cdr) { - peer->cdr->answer = ast_tvnow(); - peer->cdr->disposition = AST_CDR_ANSWERED; - } - ast_copy_flags(peerflags, o, - OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | - OPT_CALLEE_HANGUP | OPT_CALLER_HANGUP | - OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | - OPT_CALLEE_PARK | OPT_CALLER_PARK | - DIAL_NOFORWARDHTML); - ast_copy_string(c->dialcontext, "", sizeof(c->dialcontext)); - ast_copy_string(c->exten, "", sizeof(c->exten)); - /* Setup RTP early bridge if appropriate */ - if (CAN_EARLY_BRIDGE(peerflags, in, peer)) - ast_rtp_early_bridge(in, peer); - } - /* If call has been answered, then the eventual hangup is likely to be normal hangup */ - in->hangupcause = AST_CAUSE_NORMAL_CLEARING; - c->hangupcause = AST_CAUSE_NORMAL_CLEARING; - break; - case AST_CONTROL_BUSY: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s is busy\n", c->name); - in->hangupcause = c->hangupcause; - ast_hangup(c); - c = o->chan = NULL; - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(AST_CAUSE_BUSY, in); - break; - case AST_CONTROL_CONGESTION: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s is circuit-busy\n", c->name); - in->hangupcause = c->hangupcause; - ast_hangup(c); - c = o->chan = NULL; - ast_clear_flag(o, DIAL_STILLGOING); - HANDLE_CAUSE(AST_CAUSE_CONGESTION, in); - break; - case AST_CONTROL_RINGING: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s is ringing\n", c->name); - /* Setup early media if appropriate */ - if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) - ast_rtp_early_bridge(in, c); - if (!(*sentringing) && !ast_test_flag(outgoing, OPT_MUSICBACK)) { - ast_indicate(in, AST_CONTROL_RINGING); - (*sentringing)++; - } - break; - case AST_CONTROL_PROGRESS: - if (option_verbose > 2) - ast_verbose (VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", c->name, in->name); - /* Setup early media if appropriate */ - if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) - ast_rtp_early_bridge(in, c); - if (!ast_test_flag(outgoing, OPT_RINGBACK)) - ast_indicate(in, AST_CONTROL_PROGRESS); - break; - case AST_CONTROL_VIDUPDATE: - if (option_verbose > 2) - ast_verbose (VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", c->name, in->name); - ast_indicate(in, AST_CONTROL_VIDUPDATE); - break; - case AST_CONTROL_SRCUPDATE: - if (option_verbose > 2) - ast_verbose (VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", c->name, in->name); - ast_indicate(in, AST_CONTROL_SRCUPDATE); - break; - case AST_CONTROL_PROCEEDING: - if (option_verbose > 2) - ast_verbose (VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", c->name, in->name); - if (single && CAN_EARLY_BRIDGE(peerflags, in, c)) - ast_rtp_early_bridge(in, c); - if (!ast_test_flag(outgoing, OPT_RINGBACK)) - ast_indicate(in, AST_CONTROL_PROCEEDING); - break; - case AST_CONTROL_HOLD: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", c->name); - ast_indicate(in, AST_CONTROL_HOLD); - break; - case AST_CONTROL_UNHOLD: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Call on %s left from hold\n", c->name); - ast_indicate(in, AST_CONTROL_UNHOLD); - break; - case AST_CONTROL_OFFHOOK: - case AST_CONTROL_FLASH: - /* Ignore going off hook and flash */ - break; - case -1: - if (!ast_test_flag(outgoing, OPT_RINGBACK | OPT_MUSICBACK)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s stopped sounds\n", c->name); - ast_indicate(in, -1); - (*sentringing) = 0; - } - break; - default: - if (option_debug) - ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); - } - } else if (single) { - /* XXX are we sure the logic is correct ? or we should just switch on f->frametype ? */ - if (f->frametype == AST_FRAME_VOICE && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) { - if (ast_write(in, f)) - ast_log(LOG_WARNING, "Unable to forward voice frame\n"); - } else if (f->frametype == AST_FRAME_IMAGE && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) { - if (ast_write(in, f)) - ast_log(LOG_WARNING, "Unable to forward image\n"); - } else if (f->frametype == AST_FRAME_TEXT && !ast_test_flag(outgoing, OPT_RINGBACK|OPT_MUSICBACK)) { - if (ast_write(in, f)) - ast_log(LOG_WARNING, "Unable to send text\n"); - } else if (f->frametype == AST_FRAME_HTML && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) { - if (ast_channel_sendhtml(in, f->subclass, f->data, f->datalen) == -1) - ast_log(LOG_WARNING, "Unable to send URL\n"); - } - } - ast_frfree(f); - } /* end for */ - if (winner == in) { - struct ast_frame *f = ast_read(in); -#if 0 - if (f && (f->frametype != AST_FRAME_VOICE)) - printf("Frame type: %d, %d\n", f->frametype, f->subclass); - else if (!f || (f->frametype != AST_FRAME_VOICE)) - printf("Hangup received on %s\n", in->name); -#endif - if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { - /* Got hung up */ - *to = -1; - ast_cdr_noanswer(in->cdr); - strcpy(status, "CANCEL"); - if (f) - ast_frfree(f); - return NULL; - } - - if (f && (f->frametype == AST_FRAME_DTMF)) { - if (ast_test_flag(peerflags, OPT_DTMF_EXIT)) { - const char *context = pbx_builtin_getvar_helper(in, "EXITCONTEXT"); - if (onedigit_goto(in, context, (char) f->subclass, 1)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); - *to=0; - ast_cdr_noanswer(in->cdr); - *result = f->subclass; - strcpy(status, "CANCEL"); - ast_frfree(f); - return NULL; - } - } - - if (ast_test_flag(peerflags, OPT_CALLER_HANGUP) && - (f->subclass == '*')) { /* hmm it it not guaranteed to be '*' anymore. */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); - *to=0; - ast_cdr_noanswer(in->cdr); - strcpy(status, "CANCEL"); - ast_frfree(f); - return NULL; - } - } - - /* Forward HTML stuff */ - if (single && f && (f->frametype == AST_FRAME_HTML) && !ast_test_flag(outgoing, DIAL_NOFORWARDHTML)) - if(ast_channel_sendhtml(outgoing->chan, f->subclass, f->data, f->datalen) == -1) - ast_log(LOG_WARNING, "Unable to send URL\n"); - - - if (single && ((f->frametype == AST_FRAME_VOICE) || (f->frametype == AST_FRAME_DTMF_BEGIN) || (f->frametype == AST_FRAME_DTMF_END))) { - if (ast_write(outgoing->chan, f)) - ast_log(LOG_WARNING, "Unable to forward voice or dtmf\n"); - } - if (single && (f->frametype == AST_FRAME_CONTROL) && - ((f->subclass == AST_CONTROL_HOLD) || - (f->subclass == AST_CONTROL_UNHOLD) || - (f->subclass == AST_CONTROL_VIDUPDATE) || - (f->subclass == AST_CONTROL_SRCUPDATE))) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "%s requested special control %d, passing it to %s\n", in->name, f->subclass, outgoing->chan->name); - ast_indicate_data(outgoing->chan, f->subclass, f->data, f->datalen); - } - ast_frfree(f); - } - if (!*to && (option_verbose > 2)) - ast_verbose(VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", orig); - if (!*to || ast_check_hangup(in)) { - ast_cdr_noanswer(in->cdr); - } - - } - - return peer; -} - -static void replace_macro_delimiter(char *s) -{ - for (; *s; s++) - if (*s == '^') - *s = '|'; -} - - -/* returns true if there is a valid privacy reply */ -static int valid_priv_reply(struct ast_flags *opts, int res) -{ - if (res < '1') - return 0; - if (ast_test_flag(opts, OPT_PRIVACY) && res <= '5') - return 1; - if (ast_test_flag(opts, OPT_SCREENING) && res <= '4') - return 1; - return 0; -} - -static void set_dial_features(struct ast_flags *opts, struct ast_dial_features *features) -{ - struct ast_flags perm_opts = {.flags = 0}; - - ast_copy_flags(&perm_opts, opts, - OPT_CALLER_TRANSFER | OPT_CALLER_PARK | OPT_CALLER_MONITOR | OPT_CALLER_HANGUP | - OPT_CALLEE_TRANSFER | OPT_CALLEE_PARK | OPT_CALLEE_MONITOR | OPT_CALLEE_HANGUP); - - memset(features->options, 0, sizeof(features->options)); - - ast_app_options2str(dial_exec_options, &perm_opts, features->options, sizeof(features->options)); - if (ast_test_flag(&perm_opts, OPT_CALLEE_TRANSFER)) - ast_set_flag(&(features->features_callee), AST_FEATURE_REDIRECT); - if (ast_test_flag(&perm_opts, OPT_CALLER_TRANSFER)) - ast_set_flag(&(features->features_caller), AST_FEATURE_REDIRECT); - if (ast_test_flag(&perm_opts, OPT_CALLEE_HANGUP)) - ast_set_flag(&(features->features_callee), AST_FEATURE_DISCONNECT); - if (ast_test_flag(&perm_opts, OPT_CALLER_HANGUP)) - ast_set_flag(&(features->features_caller), AST_FEATURE_DISCONNECT); - if (ast_test_flag(&perm_opts, OPT_CALLEE_MONITOR)) - ast_set_flag(&(features->features_callee), AST_FEATURE_AUTOMON); - if (ast_test_flag(&perm_opts, OPT_CALLER_MONITOR)) - ast_set_flag(&(features->features_caller), AST_FEATURE_AUTOMON); - if (ast_test_flag(&perm_opts, OPT_CALLEE_PARK)) - ast_set_flag(&(features->features_callee), AST_FEATURE_PARKCALL); - if (ast_test_flag(&perm_opts, OPT_CALLER_PARK)) - ast_set_flag(&(features->features_caller), AST_FEATURE_PARKCALL); -} - -static void end_bridge_callback (void *data) -{ - char buf[80]; - time_t end; - struct ast_channel *chan = data; - - if (!chan->cdr) { - return; - } - - time(&end); - - ast_channel_lock(chan); - if (chan->cdr->answer.tv_sec) { - snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec); - pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); - } - - if (chan->cdr->start.tv_sec) { - snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec); - pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); - } - ast_channel_unlock(chan); -} - -static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) { - bconfig->end_bridge_callback_data = originator; -} - -static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags *peerflags, int *continue_exec) -{ - int res = -1; - struct ast_module_user *u; - char *rest, *cur; - struct dial_localuser *outgoing = NULL; - struct ast_channel *peer; - int to; - int numbusy = 0; - int numcongestion = 0; - int numnochan = 0; - int cause; - char numsubst[256]; - char cidname[AST_MAX_EXTENSION] = ""; - int privdb_val = 0; - int calldurationlimit = -1; - long timelimit = 0; - long play_warning = 0; - long warning_freq = 0; - const char *warning_sound = NULL; - const char *end_sound = NULL; - const char *start_sound = NULL; - char *dtmfcalled = NULL, *dtmfcalling = NULL; - char status[256] = "INVALIDARGS"; - int play_to_caller = 0, play_to_callee = 0; - int sentringing = 0, moh = 0; - const char *outbound_group = NULL; - int result = 0; - time_t start_time; - char privintro[1024]; - char privcid[256]; - char *parse; - int opermode = 0; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(peers); - AST_APP_ARG(timeout); - AST_APP_ARG(options); - AST_APP_ARG(url); - ); - struct ast_flags opts = { 0, }; - char *opt_args[OPT_ARG_ARRAY_SIZE]; - struct ast_datastore *datastore = NULL; - struct ast_datastore *ds_caller_features = NULL; - struct ast_datastore *ds_callee_features = NULL; - struct ast_dial_features *caller_features; - int fulldial = 0, num_dialed = 0; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n"); - pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); - return -1; - } - - /* Reset all DIAL variables back to blank, to prevent confusion (in case we don't reset all of them). */ - pbx_builtin_setvar_helper(chan, "DIALSTATUS", ""); - pbx_builtin_setvar_helper(chan, "DIALEDPEERNUMBER", ""); - pbx_builtin_setvar_helper(chan, "DIALEDPEERNAME", ""); - pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", ""); - pbx_builtin_setvar_helper(chan, "DIALEDTIME", ""); - - u = ast_module_user_add(chan); - - parse = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, parse); - - if (!ast_strlen_zero(args.options) && - ast_app_parse_options(dial_exec_options, &opts, opt_args, args.options)) { - pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); - goto done; - } - - if (ast_strlen_zero(args.peers)) { - ast_log(LOG_WARNING, "Dial requires an argument (technology/number)\n"); - pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); - goto done; - } - - if (ast_test_flag(&opts, OPT_OPERMODE)) { - if (ast_strlen_zero(opt_args[OPT_ARG_OPERMODE])) - opermode = 1; - else opermode = atoi(opt_args[OPT_ARG_OPERMODE]); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting operator services mode to %d.\n", opermode); - } - - if (ast_test_flag(&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", status); - goto done; - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n", calldurationlimit); - } - - if (ast_test_flag(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) { - dtmfcalling = opt_args[OPT_ARG_SENDDTMF]; - dtmfcalled = strsep(&dtmfcalling, ":"); - } - - if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) { - char *limit_str, *warning_str, *warnfreq_str; - const char *var; - - warnfreq_str = opt_args[OPT_ARG_DURATION_LIMIT]; - limit_str = strsep(&warnfreq_str, ":"); - warning_str = strsep(&warnfreq_str, ":"); - - timelimit = atol(limit_str); - if (warning_str) - play_warning = atol(warning_str); - if (warnfreq_str) - warning_freq = atol(warnfreq_str); - - if (!timelimit) { - ast_log(LOG_WARNING, "Dial does not accept L(%s), hanging up.\n", limit_str); - goto done; - } else if (play_warning > timelimit) { - /* 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. - */ - - if (!warning_freq) { - play_warning = 0; - } else { - /* XXX fix this!! */ - while (play_warning > timelimit) - play_warning -= warning_freq; - if (play_warning < 1) - play_warning = 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"); - warning_sound = S_OR(var, "timeleft"); - - var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE"); - end_sound = S_OR(var, NULL); /* XXX not much of a point in doing this! */ - - var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE"); - start_sound = S_OR(var, NULL); /* XXX not much of a point in doing this! */ - - /* undo effect of S(x) in case they are both used */ - calldurationlimit = -1; - /* more efficient to do it like S(x) does since no advanced opts */ - if (!play_warning && !start_sound && !end_sound && timelimit) { - calldurationlimit = timelimit / 1000; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n", calldurationlimit); - timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0; - } else if (option_verbose > 2) { - ast_verbose(VERBOSE_PREFIX_3 "Limit Data for this call:\n"); - ast_verbose(VERBOSE_PREFIX_4 "timelimit = %ld\n", timelimit); - ast_verbose(VERBOSE_PREFIX_4 "play_warning = %ld\n", play_warning); - ast_verbose(VERBOSE_PREFIX_4 "play_to_caller = %s\n", play_to_caller ? "yes" : "no"); - ast_verbose(VERBOSE_PREFIX_4 "play_to_callee = %s\n", play_to_callee ? "yes" : "no"); - ast_verbose(VERBOSE_PREFIX_4 "warning_freq = %ld\n", warning_freq); - ast_verbose(VERBOSE_PREFIX_4 "start_sound = %s\n", start_sound); - ast_verbose(VERBOSE_PREFIX_4 "warning_sound = %s\n", warning_sound); - ast_verbose(VERBOSE_PREFIX_4 "end_sound = %s\n", end_sound); - } - } - - if (ast_test_flag(&opts, OPT_RESETCDR) && chan->cdr) - ast_cdr_reset(chan->cdr, NULL); - if (ast_test_flag(&opts, OPT_PRIVACY) && ast_strlen_zero(opt_args[OPT_ARG_PRIVACY])) - opt_args[OPT_ARG_PRIVACY] = ast_strdupa(chan->exten); - if (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) { - char callerid[60]; - char *l = chan->cid.cid_num; /* XXX watch out, we are overwriting it */ - if (!ast_strlen_zero(l)) { - ast_shrink_phone_number(l); - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Privacy DB is '%s', clid is '%s'\n", - opt_args[OPT_ARG_PRIVACY], l); - privdb_val = ast_privacy_check(opt_args[OPT_ARG_PRIVACY], l); - } - else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Privacy Screening, clid is '%s'\n", l); - 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=='/') - *tn2 = '='; /* any other chars to be afraid of? */ - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Privacy-- callerid is empty\n"); - - snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam); - l = callerid; - privdb_val = AST_PRIVACY_UNKNOWN; - } - - ast_copy_string(privcid,l,sizeof(privcid)); - - if( strncmp(privcid,"NOCALLERID",10) != 0 && ast_test_flag(&opts, OPT_SCREEN_NOCLID) ) { /* if callerid is set, and ast_test_flag(&opts, OPT_SCREEN_NOCLID) is set also */ - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "CallerID set (%s); N option set; Screening should be off\n", privcid); - privdb_val = AST_PRIVACY_ALLOW; - } - else if(ast_test_flag(&opts, OPT_SCREEN_NOCLID) && strncmp(privcid,"NOCALLERID",10) == 0 ) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "CallerID blank; N option set; Screening should happen; dbval is %d\n", privdb_val); - } - - if(privdb_val == AST_PRIVACY_DENY ) { - ast_copy_string(status, "NOANSWER", sizeof(status)); - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n"); - res=0; - goto out; - } - else if(privdb_val == AST_PRIVACY_KILL ) { - ast_copy_string(status, "DONTCALL", sizeof(status)); - if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 201); - } - res = 0; - goto out; /* Is this right? */ - } - else if(privdb_val == AST_PRIVACY_TORTURE ) { - ast_copy_string(status, "TORTURE", sizeof(status)); - if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 301); - } - res = 0; - goto out; /* is this right??? */ - } - else if(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(privintro, sizeof(privintro), "%s/sounds/priv-callerintros", ast_config_AST_DATA_DIR); - if (mkdir(privintro, 0755) && errno != EEXIST) { - ast_log(LOG_WARNING, "privacy: can't create directory priv-callerintros: %s\n", strerror(errno)); - res = -1; - goto out; - } - - snprintf(privintro,sizeof(privintro), "priv-callerintros/%s", privcid); - if( ast_fileexists(privintro,NULL,NULL ) > 0 && strncmp(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", 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 privintro file */ - if (res == -1) { - /* Delete the file regardless since they hung up during recording */ - ast_filedelete(privintro, NULL); - if( ast_fileexists(privintro,NULL,NULL ) > 0 ) - ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", privintro); - else if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro); - goto out; - } - if( !ast_streamfile(chan, "vm-dialout", chan->language) ) - ast_waitstream(chan, ""); - } - } - } - - 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_flags(peerflags, &opts, OPT_DTMF_EXIT | OPT_GO_ON | OPT_ORIGINAL_CLID | OPT_CALLER_HANGUP | OPT_IGNORE_FORWARDING); - - /* Create datastore for channel dial features for caller */ - if (!(ds_caller_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for dial features. Aborting!\n"); - goto out; - } - - if (!(caller_features = ast_calloc(1, sizeof(*caller_features)))) { - ast_log(LOG_WARNING, "Unable to allocate memory for feature flags. Aborting!\n"); - goto out; - } - - ast_channel_lock(chan); - caller_features->is_caller = 1; - set_dial_features(&opts, caller_features); - ds_caller_features->inheritance = -1; - ds_caller_features->data = caller_features; - ast_channel_datastore_add(chan, ds_caller_features); - ast_channel_unlock(chan); - - /* loop through the list of dial destinations */ - rest = args.peers; - while ((cur = strsep(&rest, "&")) ) { - struct dial_localuser *tmp; - /* 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; - struct ast_dial_features *callee_features; - 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_flags(tmp, &opts, - 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_RINGBACK | OPT_MUSICBACK | OPT_FORCECLID); - ast_set2_flag(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"); - free(tmp); - goto out; - } - - datastore->inheritance = DATASTORE_INHERIT_FOREVER; - - if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { - 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++; - 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); - 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); - } - - tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause); - if (!tmp->chan) { - /* 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, chan); - if (!rest) /* we are on the last destination */ - chan->hangupcause = cause; - free(tmp); - continue; - } - - pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", numsubst); - - /* Setup outgoing SDP to match incoming one */ - ast_rtp_make_compatible(tmp->chan, chan, !outgoing && !rest); - - /* Inherit specially named variables from parent channel */ - ast_channel_inherit_variables(chan, tmp->chan); - - tmp->chan->appl = "AppDial"; - tmp->chan->data = "(Outgoing Line)"; - tmp->chan->whentohangup = 0; - - if (tmp->chan->cid.cid_num) - free(tmp->chan->cid.cid_num); - tmp->chan->cid.cid_num = ast_strdup(chan->cid.cid_num); - - if (tmp->chan->cid.cid_name) - free(tmp->chan->cid.cid_name); - tmp->chan->cid.cid_name = ast_strdup(chan->cid.cid_name); - - if (tmp->chan->cid.cid_ani) - free(tmp->chan->cid.cid_ani); - tmp->chan->cid.cid_ani = ast_strdup(chan->cid.cid_ani); - - /* Copy language from incoming to outgoing */ - ast_string_field_set(tmp->chan, language, chan->language); - ast_string_field_set(tmp->chan, accountcode, chan->accountcode); - tmp->chan->cdrflags = chan->cdrflags; - if (ast_strlen_zero(tmp->chan->musicclass)) - ast_string_field_set(tmp->chan, musicclass, chan->musicclass); - /* XXX don't we free previous values ? */ - tmp->chan->cid.cid_rdnis = ast_strdup(chan->cid.cid_rdnis); - /* Pass callingpres setting */ - tmp->chan->cid.cid_pres = chan->cid.cid_pres; - /* Pass type of number */ - tmp->chan->cid.cid_ton = chan->cid.cid_ton; - /* Pass type of tns */ - tmp->chan->cid.cid_tns = chan->cid.cid_tns; - /* Presense of ADSI CPE on outgoing channel follows ours */ - tmp->chan->adsicpe = chan->adsicpe; - /* Pass the transfer capability */ - tmp->chan->transfercapability = chan->transfercapability; - - /* If we have an outbound group, set this peer channel to it */ - if (outbound_group) - ast_app_group_set_channel(tmp->chan, outbound_group); - - /* Inherit context and extension */ - if (!ast_strlen_zero(chan->macrocontext)) - ast_copy_string(tmp->chan->dialcontext, chan->macrocontext, sizeof(tmp->chan->dialcontext)); - else - ast_copy_string(tmp->chan->dialcontext, chan->context, sizeof(tmp->chan->dialcontext)); - if (!ast_strlen_zero(chan->macroexten)) - ast_copy_string(tmp->chan->exten, chan->macroexten, sizeof(tmp->chan->exten)); - else - ast_copy_string(tmp->chan->exten, chan->exten, sizeof(tmp->chan->exten)); - - /* Save callee features */ - if (!(ds_callee_features = ast_channel_datastore_alloc(&dial_features_info, NULL))) { - ast_log(LOG_WARNING, "Unable to create channel datastore for dial features. Aborting!\n"); - ast_free(tmp); - goto out; - } - - if (!(callee_features = ast_calloc(1, sizeof(*callee_features)))) { - ast_log(LOG_WARNING, "Unable to allocate memory for feature flags. Aborting!\n"); - ast_free(tmp); - goto out; - } - - ast_channel_lock(tmp->chan); - callee_features->is_caller = 0; - set_dial_features(&opts, callee_features); - ds_callee_features->inheritance = -1; - ds_callee_features->data = callee_features; - ast_channel_datastore_add(tmp->chan, ds_callee_features); - ast_channel_unlock(tmp->chan); - - /* Place the call, but don't wait on the answer */ - res = ast_call(tmp->chan, numsubst, 0); - - /* Save the info in cdr's that we called them */ - if (chan->cdr) - ast_cdr_setdestchan(chan->cdr, tmp->chan->name); - - /* check the results of ast_call */ - if (res) { - /* Again, keep going even if there's an error */ - if (option_debug) - ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst); - if (tmp->chan->hangupcause) { - chan->hangupcause = tmp->chan->hangupcause; - } - ast_hangup(tmp->chan); - tmp->chan = NULL; - free(tmp); - continue; - } else { - senddialevent(chan, tmp->chan); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", numsubst); - if (!ast_test_flag(peerflags, OPT_ORIGINAL_CLID)) - ast_set_callerid(tmp->chan, 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_flag(tmp, DIAL_STILLGOING); - 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(status, "CHANUNAVAIL"); - if(fulldial == num_dialed) { - res = -1; - goto out; - } - } else { - /* Our status will at least be NOANSWER */ - strcpy(status, "NOANSWER"); - if (ast_test_flag(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_flag(outgoing, OPT_RINGBACK)) { - ast_indicate(chan, AST_CONTROL_RINGING); - sentringing++; - } - } - - time(&start_time); - peer = wait_for_answer(chan, outgoing, &to, peerflags, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion, ast_test_flag(&opts, OPT_PRIORITY_JUMP), &result); - - /* The ast_channel_datastore_remove() function could fail here if the - * datastore was moved to another channel during a masquerade. If this is - * the case, don't free the datastore here because later, when the channel - * to which the datastore was moved hangs up, it will attempt to free this - * datastore again, causing a crash - */ - if (!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; - - strcpy(status, "ANSWER"); - pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); - /* 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); - 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) ) { - if (option_debug) - ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", args.url); - ast_channel_sendurl( peer, args.url ); - } - if ( (ast_test_flag(&opts, OPT_PRIVACY) || ast_test_flag(&opts, OPT_SCREENING)) && privdb_val == AST_PRIVACY_UNKNOWN) { - 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_flag(&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_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, AST_CONTROL_RINGING); - 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,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_flag(&opts, OPT_PRIVACY) ) - res2 = ast_play_and_wait(peer,"priv-callee-options"); - if( ast_test_flag(&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_flag(&opts, OPT_MUSICBACK)) { - ast_moh_stop(chan); - } else if (ast_test_flag(&opts, OPT_RINGBACK)) { - ast_indicate(chan, -1); - sentringing=0; - } - ast_autoservice_stop(chan); - - switch (res2) { - case '1': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW); - } - break; - case '2': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to DENY\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_DENY); - } - ast_copy_string(status, "NOANSWER", sizeof(status)); - ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ - res=0; - goto out; - case '3': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to TORTURE\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_TORTURE); - } - ast_copy_string(status, "TORTURE", sizeof(status)); - - res = 0; - ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ - goto out; /* Is this right? */ - case '4': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to KILL\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_KILL); - } - - ast_copy_string(status, "DONTCALL", sizeof(status)); - res = 0; - ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ - goto out; /* Is this right? */ - case '5': - if( ast_test_flag(&opts, OPT_PRIVACY) ) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n", - opt_args[OPT_ARG_PRIVACY], privcid); - ast_privacy_set(opt_args[OPT_ARG_PRIVACY], privcid, AST_PRIVACY_ALLOW); - ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ - res=0; - goto out; - } /* 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"); - ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ - res=0; - goto out; - } - - /* XXX once again, this path is only taken in the case '1', so it could be - * moved there, although i am not really sure that this is correct - maybe - * the check applies to other cases as well. - */ - /* 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(privcid,"NOCALLERID",10) == 0 || ast_test_flag(&opts, OPT_SCREEN_NOINTRO) ) { - ast_filedelete(privintro, NULL); - if( ast_fileexists(privintro, NULL, NULL ) > 0 ) - ast_log(LOG_NOTICE, "privacy: ast_filedelete didn't do its job on %s\n", privintro); - else if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro); - } - } - if (!ast_test_flag(&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); - else - res = digit; - - } - - if (chan && peer && ast_test_flag(&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); - if (continue_exec) - *continue_exec = 1; - res = 0; - goto done; - } - - if (ast_test_flag(&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_log(LOG_DEBUG, "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(status, macro_result, sizeof(status)); - if (ast_opt_priority_jumping || ast_test_flag(&opts, OPT_PRIORITY_JUMP)) { - if (!ast_goto_if_exists(chan, NULL, NULL, chan->priority + 101)) { - ast_set_flag(peerflags, OPT_GO_ON); - } - } else - ast_set_flag(peerflags, OPT_GO_ON); - res = -1; - } else if (!strcasecmp(macro_result, "CONGESTION") || !strcasecmp(macro_result, "CHANUNAVAIL")) { - ast_copy_string(status, macro_result, sizeof(status)); - ast_set_flag(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_flag(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_flag(peerflags, OPT_GO_ON); - - } - } - } - } - - if (!res) { - if (calldurationlimit > 0) { - peer->whentohangup = time(NULL) + calldurationlimit; - } else if (calldurationlimit != -1 && timelimit > 0) { - /* Not enough granularity to make it less, but we can't use the special value 0 */ - peer->whentohangup = time(NULL) + 1; - } - 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); - } - 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); - } - } - - if (!res) { - struct ast_bridge_config config; - - memset(&config,0,sizeof(struct ast_bridge_config)); - 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); - if (ast_test_flag(peerflags, OPT_CALLEE_TRANSFER)) - ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLER_TRANSFER)) - ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT); - if (ast_test_flag(peerflags, OPT_CALLEE_HANGUP)) - ast_set_flag(&(config.features_callee), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLER_HANGUP)) - ast_set_flag(&(config.features_caller), AST_FEATURE_DISCONNECT); - if (ast_test_flag(peerflags, OPT_CALLEE_MONITOR)) - ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMON); - if (ast_test_flag(peerflags, OPT_CALLER_MONITOR)) - ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMON); - if (ast_test_flag(peerflags, OPT_CALLEE_PARK)) - ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL); - if (ast_test_flag(peerflags, OPT_CALLER_PARK)) - ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL); - if (ast_test_flag(peerflags, OPT_GO_ON)) - ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN); - - config.timelimit = timelimit; - config.play_warning = play_warning; - config.warning_freq = warning_freq; - config.warning_sound = warning_sound; - config.end_sound = end_sound; - config.start_sound = start_sound; - config.end_bridge_callback = end_bridge_callback; - config.end_bridge_callback_data = chan; - config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; - 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 && - (((!strncasecmp(chan->name,"Zap",3)) && (!strncasecmp(peer->name,"Zap",3))) || - ((!strncasecmp(chan->name,"Dahdi",5)) && (!strncasecmp(peer->name,"Dahdi",5))))) - { - struct oprmode oprmode; - - oprmode.peer = peer; - oprmode.mode = opermode; - - ast_channel_setoption(chan, - AST_OPTION_OPRMODE,&oprmode,sizeof(struct oprmode),0); - } - res = ast_bridge_call(chan,peer,&config); - } else { - res = -1; - } - - if (!chan->_softhangup) - 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_rtp_early_bridge(chan, NULL); - hanguptree(outgoing, NULL); - pbx_builtin_setvar_helper(chan, "DIALSTATUS", status); - if (option_debug) - ast_log(LOG_DEBUG, "Exiting with DIALSTATUS=%s.\n", status); - - if (ast_test_flag(peerflags, OPT_GO_ON) && !chan->_softhangup) { - if (calldurationlimit) - chan->whentohangup = 0; - res = 0; - } -done: - ast_module_user_remove(u); - return res; -} - -static int dial_exec(struct ast_channel *chan, void *data) -{ - struct ast_flags 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 *announce = NULL, *dialdata = NULL; - const char *context = NULL; - int sleep = 0, loops = 0, res = -1; - struct ast_module_user *u; - struct ast_flags peerflags; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "RetryDial requires an argument!\n"); - return -1; - } - - u = ast_module_user_add(chan); - - announce = ast_strdupa(data); - - memset(&peerflags, 0, sizeof(peerflags)); - - if ((dialdata = strchr(announce, '|'))) { - *dialdata++ = '\0'; - if (sscanf(dialdata, "%d", &sleep) == 1) { - sleep *= 1000; - } else { - ast_log(LOG_ERROR, "%s requires the numerical argument \n",rapp); - goto done; - } - if ((dialdata = strchr(dialdata, '|'))) { - *dialdata++ = '\0'; - if (sscanf(dialdata, "%d", &loops) != 1) { - ast_log(LOG_ERROR, "%s requires the numerical argument \n",rapp); - goto done; - } - } - } - - if ((dialdata = strchr(dialdata, '|'))) { - *dialdata++ = '\0'; - } else { - ast_log(LOG_ERROR, "%s requires more arguments\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, dialdata, &peerflags, &continue_exec); - if (continue_exec) - break; - - if (res == 0) { - if (ast_test_flag(&peerflags, OPT_DTMF_EXIT)) { - if (!ast_strlen_zero(announce)) { - if (ast_fileexists(announce, NULL, chan->language) > 0) { - if(!(res = ast_streamfile(chan, announce, chan->language))) - ast_waitstream(chan, AST_DIGIT_ANY); - } else - ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", 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(announce)) { - if (ast_fileexists(announce, NULL, chan->language) > 0) { - if (!(res = ast_streamfile(chan, announce, chan->language))) - res = ast_waitstream(chan, ""); - } else - ast_log(LOG_WARNING, "Announce file \"%s\" specified in Retrydial does not exist\n", 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: - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - res |= ast_unregister_application(rapp); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - int res; - - 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/apps/app_dictate.c b/apps/app_dictate.c deleted file mode 100644 index 7db747e12..000000000 --- a/apps/app_dictate.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2005, Anthony Minessale II - * - * Anthony Minessale II - * - * Donated by Sangoma Technologies - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed 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 - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/say.h" -#include "asterisk/lock.h" -#include "asterisk/app.h" - -static char *app = "Dictate"; -static char *synopsis = "Virtual Dictation Machine"; -static char *desc = " Dictate([[|]])\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; - struct ast_module_user *u; - int ffactor = 320 * 80, - res = 0, - done = 0, - oldr = 0, - lastop = 0, - samples = 0, - speed = 1, - digit = 0, - len = 0, - maxlen = 0, - mode = 0; - - u = ast_module_user_add(chan); - - 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"); - ast_module_user_remove(u); - 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 = ""; - } - 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, 0700); - 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, (char *) 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, 0700); - 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); - } - ast_module_user_remove(u); - 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/apps/app_directed_pickup.c b/apps/app_directed_pickup.c deleted file mode 100644 index 68d965244..000000000 --- a/apps/app_directed_pickup.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2005, Joshua Colp - * - * Joshua Colp - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed 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 - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#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" - -#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 pickup any ringing channel\n" -"that is calling the specified extension. If no context is specified, the current\n" -"context will be used. If you use the special string \"PICKUPMARK\" for the context parameter, for example\n" -"10@PICKUPMARK, this application tries to find a channel which has defined a channel variable with the same content\n" -"as \"extension\"."; - -/* Perform actual pickup between two channels */ -static int pickup_do(struct ast_channel *chan, struct ast_channel *target) -{ - int res = 0; - - if (option_debug) - ast_log(LOG_DEBUG, "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 || chan->_state == AST_STATE_DOWN)) - return 1; - else - return 0; -} - -/* Attempt to pick up specified extension with context */ -static int pickup_by_exten(struct ast_channel *chan, char *exten, 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, 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; - struct ast_module_user *u = NULL; - char *tmp = ast_strdupa(data); - char *exten = NULL, *context = NULL; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Pickup requires an argument (extension)!\n"); - return -1; - } - - u = ast_module_user_add(chan); - - /* Parse extension (and context if there) */ - while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) { - if ((context = strchr(exten, '@'))) - *context++ = '\0'; - if (context && !strcasecmp(context, PICKUPMARK)) { - if (!pickup_by_mark(chan, exten)) - break; - } else { - if (!pickup_by_exten(chan, exten, context ? context : chan->context)) - break; - } - ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten); - } - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, pickup_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application"); diff --git a/apps/app_directory.c b/apps/app_directory.c deleted file mode 100644 index 23d2b4d62..000000000 --- a/apps/app_directory.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Provide a directory of extensions - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/config.h" -#include "asterisk/say.h" -#include "asterisk/utils.h" -#include "asterisk/app.h" - -#ifdef ODBC_STORAGE -#include -#include -#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"; - -/* 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 - - -#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, 0770); - 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 char *convert(const char *lastname) -{ - char *tmp; - int lcount = 0; - tmp = ast_malloc(NUMDIGITS + 1); - if (tmp) { - while((*lastname > 32) && lcount < NUMDIGITS) { - switch(toupper(*lastname)) { - case '1': - tmp[lcount++] = '1'; - break; - case '2': - case 'A': - case 'B': - case 'C': - tmp[lcount++] = '2'; - break; - case '3': - case 'D': - case 'E': - case 'F': - tmp[lcount++] = '3'; - break; - case '4': - case 'G': - case 'H': - case 'I': - tmp[lcount++] = '4'; - break; - case '5': - case 'J': - case 'K': - case 'L': - tmp[lcount++] = '5'; - break; - case '6': - case 'M': - case 'N': - case 'O': - tmp[lcount++] = '6'; - break; - case '7': - case 'P': - case 'Q': - case 'R': - case 'S': - tmp[lcount++] = '7'; - break; - case '8': - case 'T': - case 'U': - case 'V': - tmp[lcount++] = '8'; - break; - case '9': - case 'W': - case 'X': - case 'Y': - case 'Z': - tmp[lcount++] = '9'; - break; - } - lastname++; - } - tmp[lcount] = '\0'; - } - return tmp; -} - -/* 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, char *context, - char *dialcontext, char *ext, char *name, int readext, - int fromappvm) -{ - int res = 0; - int loop; - 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, chan->language, AST_DIGIT_ANY); - ast_stopstream(chan); - /* If Option 'e' was specified, also read the extension number with the name */ - if (readext) { - ast_stream_and_wait(chan, "vm-extension", chan->language, 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) && readext) { - ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY); - res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language); - } - } -#ifdef ODBC_STORAGE - ast_filedelete(fn, NULL); -#endif - - for (loop = 3 ; loop > 0; loop--) { - if (!res) - res = ast_stream_and_wait(chan, "dir-instr", chan->language, AST_DIGIT_ANY); - if (!res) - res = ast_waitfordigit(chan, 3000); - ast_stopstream(chan); - - if (res < 0) /* User hungup, so jump out now */ - break; - if (res == '1') { /* Name selected */ - if (fromappvm) { - /* We still want to set the exten though */ - ast_copy_string(chan->exten, ext, sizeof(chan->exten)); - } else { - if (ast_goto_if_exists(chan, dialcontext, ext, 1)) { - ast_log(LOG_WARNING, - "Can't find extension '%s' in context '%s'. " - "Did you pass the wrong context to Directory?\n", - ext, dialcontext); - res = -1; - } - } - break; - } - if (res == '*') /* Skip to next match in list */ - break; - - /* Not '1', or '*', so decrement number of tries */ - res = 0; - } - - return(res); -} - -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]; - - /* Load flat file config. */ - cfg = ast_config_load(VOICEMAIL_CONFIG); - - 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); - if (!cat) { - ast_log(LOG_WARNING, "Out of memory\n"); - ast_config_destroy(cfg); - if (rtdata) { - ast_config_destroy(rtdata); - } - 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 do_directory(struct ast_channel *chan, struct ast_config *cfg, struct ast_config *ucfg, char *context, char *dialcontext, char digit, int last, int readext, int fromappvm) -{ - /* Read in the first three digits.. "digit" is the first digit, already read */ - char ext[NUMDIGITS + 1], *cat; - char name[80] = ""; - struct ast_variable *v; - int res; - int found=0; - int lastuserchoice = 0; - char *start, *conv, *stringp = NULL; - const char *pos; - int breakout = 0; - - 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') { - if (!ast_goto_if_exists(chan, dialcontext, "o", 1) || - (!ast_strlen_zero(chan->macrocontext) && - !ast_goto_if_exists(chan, chan->macrocontext, "o", 1))) { - return 0; - } else { - ast_log(LOG_WARNING, "Can't find extension 'o' in current context. " - "Not Exiting the Directory!\n"); - res = 0; - } - } - if (digit == '*') { - if (!ast_goto_if_exists(chan, dialcontext, "a", 1) || - (!ast_strlen_zero(chan->macrocontext) && - !ast_goto_if_exists(chan, chan->macrocontext, "a", 1))) { - return 0; - } else { - ast_log(LOG_WARNING, "Can't find extension 'a' in current context. " - "Not Exiting the Directory!\n"); - res = 0; - } - } - memset(ext, 0, sizeof(ext)); - ext[0] = digit; - res = 0; - if (ast_readstring(chan, ext + 1, NUMDIGITS - 1, 3000, 3000, "#") < 0) res = -1; - if (!res) { - /* Search for all names which start with those digits */ - v = ast_variable_browse(cfg, context); - while(v && !res) { - /* Find all candidate extensions */ - while(v) { - /* Find a candidate extension */ - start = strdup(v->value); - if (start && !strcasestr(start, "hidefromdir=yes")) { - stringp=start; - strsep(&stringp, ","); - pos = strsep(&stringp, ","); - if (pos) { - ast_copy_string(name, pos, sizeof(name)); - /* Grab the last name */ - if (last && strrchr(pos,' ')) - pos = strrchr(pos, ' ') + 1; - conv = convert(pos); - if (conv) { - if (!strncmp(conv, ext, strlen(ext))) { - /* Match! */ - found++; - free(conv); - free(start); - break; - } - free(conv); - } - } - free(start); - } - v = v->next; - } - - if (v) { - /* We have a match -- play a greeting if they have it */ - res = play_mailbox_owner(chan, context, dialcontext, v->name, name, readext, fromappvm); - switch (res) { - case -1: - /* user pressed '1' but extension does not exist, or - * user hungup - */ - lastuserchoice = 0; - break; - case '1': - /* user pressed '1' and extensions exists; - play_mailbox_owner will already have done - a goto() on the channel - */ - lastuserchoice = res; - break; - case '*': - /* user pressed '*' to skip something found */ - lastuserchoice = res; - res = 0; - break; - default: - break; - } - v = v->next; - } - } - - if (!res && ucfg) { - /* Search users.conf for all names which start with those digits */ - for (cat = ast_category_browse(ucfg, NULL); cat && !res ; cat = ast_category_browse(ucfg, cat)) { - if (!strcasecmp(cat, "general")) - continue; - if (!ast_true(ast_config_option(ucfg, cat, "hasdirectory"))) - continue; - - /* Find all candidate extensions */ - if ((pos = ast_variable_retrieve(ucfg, cat, "fullname"))) { - ast_copy_string(name, pos, sizeof(name)); - /* Grab the last name */ - if (last && strrchr(pos,' ')) - pos = strrchr(pos, ' ') + 1; - conv = convert(pos); - if (conv) { - if (!strcmp(conv, ext)) { - /* Match! */ - found++; - /* We have a match -- play a greeting if they have it */ - res = play_mailbox_owner(chan, context, dialcontext, cat, name, readext, fromappvm); - switch (res) { - case -1: - /* user pressed '1' but extension does not exist, or - * user hungup - */ - lastuserchoice = 0; - breakout = 1; - break; - case '1': - /* user pressed '1' and extensions exists; - play_mailbox_owner will already have done - a goto() on the channel - */ - lastuserchoice = res; - breakout = 1; - break; - case '*': - /* user pressed '*' to skip something found */ - lastuserchoice = res; - breakout = 0; - res = 0; - break; - default: - breakout = 1; - break; - } - free(conv); - if (breakout) - break; - } - else - free(conv); - } - } - } - } - - if (lastuserchoice != '1') { - res = ast_streamfile(chan, found ? "dir-nomore" : "dir-nomatch", chan->language); - if (!res) - res = 1; - return res; - } - return 0; - } - return res; -} - -static int directory_exec(struct ast_channel *chan, void *data) -{ - int res = 0; - struct ast_module_user *u; - struct ast_config *cfg, *ucfg; - int last = 1; - int readext = 0; - int fromappvm = 0; - const char *dirintro; - char *parse; - 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; - } - - u = ast_module_user_add(chan); - - parse = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, parse); - - if (args.options) { - if (strchr(args.options, 'f')) - last = 0; - if (strchr(args.options, 'e')) - readext = 1; - if (strchr(args.options, 'v')) - fromappvm = 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"); - ast_module_user_remove(u); - return -1; - } - - ucfg = ast_config_load("users.conf"); - - 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 = last ? "dir-intro" : "dir-intro-fn"; - - if (chan->_state != AST_STATE_UP) - res = ast_answer(chan); - - for (;;) { - if (!res) - res = ast_stream_and_wait(chan, dirintro, chan->language, AST_DIGIT_ANY); - ast_stopstream(chan); - if (!res) - res = ast_waitfordigit(chan, 5000); - if (res > 0) { - res = do_directory(chan, cfg, ucfg, args.vmcontext, args.dialcontext, res, last, readext, fromappvm); - if (res > 0) { - res = ast_waitstream(chan, AST_DIGIT_ANY); - ast_stopstream(chan); - if (res >= 0) - continue; - } - } - break; - } - if (ucfg) - ast_config_destroy(ucfg); - ast_config_destroy(cfg); - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - res = ast_unregister_application(app); - return res; -} - -static int load_module(void) -{ -#ifdef ODBC_STORAGE - struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG); - 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/apps/app_disa.c b/apps/app_disa.c deleted file mode 100644 index f49d19704..000000000 --- a/apps/app_disa.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * - * Made only slightly more sane by 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 DISA -- Direct Inward System Access Application - * - * \author Jim Dixon - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.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([|]) or DISA()\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 on which a call may be placed. Obviously, this type\n" - "of access has SERIOUS security implications, and GREAT care must be\n" - "taken NOT to compromise your security.\n\n" - "There is a possibility of accessing DISA without password. Simply\n" - "exchange your password with \"no-password\".\n\n" - " Example: exten => s,1,DISA(no-password|local)\n\n" - "Be aware that using this compromises 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. It also allows specification\n" - "of the context on which the user will be dialing. If no context is\n" - "specified, the DISA application defaults the context to \"disa\".\n" - "Presumably a normal system will have a special context set up\n" - "for DISA use with some or a lot of restrictions. \n\n" - "The file that contains the passcodes (if used) allows specification\n" - "of either just a passcode (defaulting to the \"disa\" context, or\n" - "passcode|context on each line of the file. The file may contain blank\n" - "lines, or comments starting with \"#\" or \";\". In addition, the\n" - "above arguments may have |new-callerid-string appended to them, to\n" - "specify a new (different) callerid to be used for this call, for\n" - "example: numeric-passcode|context|\"My Phone\" <(234) 123-4567> or \n" - "full-pathname-of-passcode-file|\"My Phone\" <(234) 123-4567>. Last\n" - "but not least, |mailbox[@context] may be appended, which will cause\n" - "a stutter-dialtone (indication \"dialrecall\") to be used, if the\n" - "specified mailbox contains any new messages, for example:\n" - "numeric-passcode|context||1234 (w/a changing callerid). Note that\n" - "in the case of specifying the numeric-passcode, the context must be\n" - "specified if the callerid is specified also.\n\n" - "If login is successful, the application looks up the dialed number in\n" - "the specified (or default) context, and executes it if found.\n" - "If the user enters an invalid extension and extension \"i\" (invalid) \n" - "exists in the context, it will be used. Also, if you set the 5th argument\n" - "to 'NOANSWER', the DISA application will not answer initially.\n"; - - -static void play_dialtone(struct ast_channel *chan, char *mailbox) -{ - const struct 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,j,k,x,did_ignore,special_noanswer; - int firstdigittimeout = 20000; - int digittimeout = 10000; - struct ast_module_user *u; - 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; - time_t rstart; - 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(noanswer); - ); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "DISA requires an argument (passcode/passcode file)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - if (chan->pbx) { - firstdigittimeout = chan->pbx->rtimeout*1000; - digittimeout = chan->pbx->dtimeout*1000; - } - - if (ast_set_write_format(chan,AST_FORMAT_ULAW)) { - ast_log(LOG_WARNING, "Unable to set write format to Mu-law on %s\n", chan->name); - ast_module_user_remove(u); - return -1; - } - if (ast_set_read_format(chan,AST_FORMAT_ULAW)) { - ast_log(LOG_WARNING, "Unable to set read format to Mu-law on %s\n", chan->name); - ast_module_user_remove(u); - return -1; - } - - ast_log(LOG_DEBUG, "Digittimeout: %d\n", digittimeout); - ast_log(LOG_DEBUG, "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 = ""; - - ast_log(LOG_DEBUG, "Mailbox: %s\n",args.mailbox); - - - special_noanswer = 0; - if ((!args.noanswer) || strcmp(args.noanswer,"NOANSWER")) - { - if (chan->_state != AST_STATE_UP) { - /* answer */ - ast_answer(chan); - } - } else special_noanswer = 1; - i = k = x = 0; /* k is 0 for pswd entry, 1 for ext entry */ - did_ignore = 0; - exten[0] = 0; - acctcode[0] = 0; - /* can we access DISA without password? */ - - ast_log(LOG_DEBUG, "Context: %s\n",args.context); - - if (!strcasecmp(args.passcode, "no-password")) { - k |= 1; /* We have the password */ - ast_log(LOG_DEBUG, "DISA no-password login success\n"); - } - lastdigittime = ast_tvnow(); - - play_dialtone(chan, args.mailbox); - - for (;;) { - /* if outa time, give em reorder */ - if (ast_tvdiff_ms(ast_tvnow(), lastdigittime) > - ((k&2) ? digittimeout : firstdigittimeout)) { - ast_log(LOG_DEBUG,"DISA %s entry timeout on chan %s\n", - ((k&1) ? "extension" : "password"),chan->name); - break; - } - if ((res = ast_waitfor(chan, -1) < 0)) { - ast_log(LOG_DEBUG, "Waitfor returned %d\n", res); - continue; - } - - f = ast_read(chan); - if (f == NULL) { - ast_module_user_remove(u); - return -1; - } - if ((f->frametype == AST_FRAME_CONTROL) && - (f->subclass == AST_CONTROL_HANGUP)) { - ast_frfree(f); - ast_module_user_remove(u); - return -1; - } - if (f->frametype == AST_FRAME_VOICE) { - ast_frfree(f); - continue; - } - - /* if not DTMF, just do it again */ - if (f->frametype != AST_FRAME_DTMF) { - ast_frfree(f); - continue; - } - - j = f->subclass; /* save digit */ - ast_frfree(f); - if (i == 0) { - 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_module_user_remove(u); - 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_log(LOG_DEBUG, "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_log(LOG_DEBUG,"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_log(LOG_DEBUG,"Successful DISA log-in on chan %s\n", chan->name); - continue; - } - } else { - if (j == '#') { /* end of extension .. maybe */ - if (i == 0 && - (ast_matchmore_extension(chan, args.context, "#", 1, chan->cid.cid_num) || - ast_exists_extension(chan, args.context, "#", 1, chan->cid.cid_num)) ) { - /* Let the # be the part of, or the entire extension */ - } else { - break; - } - } - } - - exten[i++] = j; /* save digit */ - exten[i] = 0; - if (!(k&1)) - continue; /* if getting password, continue doing it */ - /* if this exists */ - - 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; - } - } - } - - 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); - ast_module_user_remove(u); - return 0; - } - } - - /* Received invalid, but no "i" extension exists in the given context */ - -reorder: - - ast_indicate(chan,AST_CONTROL_CONGESTION); - /* something is invalid, give em reorder for several seconds */ - time(&rstart); - while(time(NULL) < rstart + 10) { - if (ast_waitfor(chan, -1) < 0) - break; - f = ast_read(chan); - if (!f) - break; - ast_frfree(f); - } - ast_playtones_stop(chan); - ast_module_user_remove(u); - return -1; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, disa_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DISA (Direct Inward System Access) Application"); diff --git a/apps/app_dumpchan.c b/apps/app_dumpchan.c deleted file mode 100644 index 426ba4eab..000000000 --- a/apps/app_dumpchan.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2004 - 2005, Anthony Minessale II. - * - * Anthony Minessale - * - * 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 - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/options.h" -#include "asterisk/utils.h" -#include "asterisk/lock.h" -#include "asterisk/utils.h" - -static char *app = "DumpChan"; -static char *synopsis = "Dump Info About The Calling Channel"; -static char *desc = - " DumpChan([])\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" - "CallerID= %s\n" - "CallerIDName= %s\n" - "DNIDDigits= %s\n" - "RDNIS= %s\n" - "State= %s (%d)\n" - "Rings= %d\n" - "NativeFormat= %s\n" - "WriteFormat= %s\n" - "ReadFormat= %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)"), - 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), - 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_module_user *u; - char vars[BUFSIZ * 4]; - char info[1024]; - int level = 0; - static char *line = "================================================================================"; - - u = ast_module_user_add(chan); - - if (!ast_strlen_zero(data)) - level = atoi(data); - - pbx_builtin_serialize_variables(chan, vars, sizeof(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, line); - - ast_module_user_remove(u); - - return 0; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, dumpchan_exec, synopsis, desc); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dump Info About The Calling Channel"); diff --git a/apps/app_echo.c b/apps/app_echo.c deleted file mode 100644 index 14f7c6d65..000000000 --- a/apps/app_echo.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Echo application -- play back what you hear to evaluate latency - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.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; - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - 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: - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, echo_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple Echo Application"); diff --git a/apps/app_exec.c b/apps/app_exec.c deleted file mode 100644 index 2ab96f593..000000000 --- a/apps/app_exec.c +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (c) 2004 - 2005, Tilghman Lesher. All rights reserved. - * Portions copyright (c) 2006, Philipp Dunkel. - * - * Tilghman Lesher - * - * 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 - * \author Philipp Dunkel - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/options.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.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 = -"Usage: 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 = -"Usage: 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:\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 = -"Usage: ExecIF (||)\n" -"If is true, execute and return the result of ().\n" -"If is true, but 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; - struct ast_module_user *u; - char *s, *appname, *endargs, args[MAXRESULT] = ""; - struct ast_app *app; - - u = ast_module_user_add(chan); - - /* Check and parse arguments */ - if (data) { - s = ast_strdupa(data); - 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; - } - } - } - - ast_module_user_remove(u); - return res; -} - -static int tryexec_exec(struct ast_channel *chan, void *data) -{ - int res=0; - struct ast_module_user *u; - char *s, *appname, *endargs, args[MAXRESULT] = ""; - struct ast_app *app; - - u = ast_module_user_add(chan); - - /* Check and parse arguments */ - if (data) { - s = ast_strdupa(data); - 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"); - } - } - } - - ast_module_user_remove(u); - return 0; -} - -static int execif_exec(struct ast_channel *chan, void *data) -{ - int res = 0; - struct ast_module_user *u; - char *myapp = NULL; - char *mydata = NULL; - char *expr = NULL; - struct ast_app *app = NULL; - - u = ast_module_user_add(chan); - - expr = ast_strdupa(data); - - if ((myapp = strchr(expr,'|'))) { - *myapp = '\0'; - myapp++; - if ((mydata = strchr(myapp,'|'))) { - *mydata = '\0'; - mydata++; - } else - mydata = ""; - - if (pbx_checkcondition(expr)) { - if ((app = pbx_findapp(myapp))) { - res = pbx_exec(chan, app, mydata); - } else { - ast_log(LOG_WARNING, "Could not find application! (%s)\n", myapp); - res = -1; - } - } - } else { - ast_log(LOG_ERROR,"Invalid Syntax.\n"); - res = -1; - } - - ast_module_user_remove(u); - - 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); - - ast_module_user_hangup_all(); - - 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/apps/app_externalivr.c b/apps/app_externalivr.c deleted file mode 100644 index 8004fecd1..000000000 --- a/apps/app_externalivr.c +++ /dev/null @@ -1,585 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * Kevin P. Fleming - * - * 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 - * - * \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 -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/linkedlists.h" -#include "asterisk/app.h" -#include "asterisk/utils.h" -#include "asterisk/options.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 a 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); - 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); - 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 ast_module_user *lu; - struct playlist_entry *entry; - const char *args = data; - int child_stdin[2] = { 0,0 }; - int child_stdout[2] = { 0,0 }; - int child_stderr[2] = { 0,0 }; - int res = -1; - int test_available_fd = -1; - int gen_active = 0; - int pid; - char *argv[32]; - int argc = 1; - 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; - - lu = ast_module_user_add(chan); - - sigfillset(&fullset); - pthread_sigmask(SIG_BLOCK, &fullset, &oldset); - - u->abort_current_sound = 0; - u->chan = chan; - - if (ast_strlen_zero(args)) { - ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); - ast_module_user_remove(lu); - return -1; - } - - buf = ast_strdupa(data); - - argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0])); - - 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(argv[0], argv); - fprintf(stderr, "Failed to execute '%s': %s\n", argv[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; - } - - test_available_fd = open("/dev/null", O_RDONLY); - - 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); - 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); - 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); - - 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); - 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] == 'H') { - ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); - send_child_event(child_events, 'H', NULL, chan); - 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 (ready_fd == child_errors_fd) { - char input[1024]; - - if (exception || (dup2(child_commands_fd, test_available_fd) == -1) || 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 (test_available_fd > -1) { - close(test_available_fd); - } - - 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))) - free(entry); - - ast_module_user_remove(lu); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, app_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application"); diff --git a/apps/app_festival.c b/apps/app_festival.c deleted file mode 100644 index ab05824f5..000000000 --- a/apps/app_festival.c +++ /dev/null @@ -1,566 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2002, Christos Ricudis - * - * Christos Ricudis - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed 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 - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.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/options.h" - -#define FESTIVAL_CONFIG "festival.conf" - -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," -"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 closeing the stream or */ - /* using OOB data */ - static char *file_stuff_key = "ft_StUfF_key"; /* must == Festival's key */ - char *buff; - int bufflen; - int n,k,i; - char c; - - bufflen = 1024; - if (!(buff = ast_malloc(bufflen))) - { - /* TODO: Handle memory allocation failure */ - } - *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 NULL if you want */ - bufflen += bufflen/4; - if (!(buff = ast_realloc(buff, bufflen))) - { - /* TODO: Handle memory allocation failure */ - } - } - 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); -/*IAS */ -#ifdef __PPC__ - for( x=0; x_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 (;;) { - ms = 1000; - res = ast_waitfor(chan, ms); - 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Last frame\n"); - res=0; - ast_frfree(f); - break; - } - } else { - ast_log(LOG_DEBUG, "No more waveform\n"); - res = 0; - } - } - 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; -} - -#define MAXLEN 180 -#define MAXFESTLEN 2048 - - - - -static int festival_exec(struct ast_channel *chan, void *vdata) -{ - int usecache; - int res=0; - struct ast_module_user *u; - 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; - char *intstr; - struct ast_config *cfg; - char *newfestivalcommand; - - if (ast_strlen_zero(vdata)) { - ast_log(LOG_WARNING, "festival requires an argument (text)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - cfg = ast_config_load(FESTIVAL_CONFIG); - if (!cfg) { - ast_log(LOG_WARNING, "No such configuration file %s\n", FESTIVAL_CONFIG); - ast_module_user_remove(u); - 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); - - intstr = strchr(data, '|'); - if (intstr) { - *intstr = '\0'; - intstr++; - if (!strcasecmp(intstr, "any")) - intstr = AST_DIGIT_ANY; - } - - ast_log(LOG_DEBUG, "Text passed to festival server : %s\n",(char *)data); - /* 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); - ast_module_user_remove(u); - 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 == (struct hostent *)0) { - ast_log(LOG_WARNING,"festival_client: gethostbyname failed\n"); - ast_config_destroy(cfg); - ast_module_user_remove(u); - 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); - ast_module_user_remove(u); - return -1; - } - - /* Compute MD5 sum of string */ - MD5Init(&md5ctx); - MD5Update(&md5ctx,(unsigned char const *)data,strlen(data)); - 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,0777); - if (fdesc!=-1) { - writecache=1; - strln=strlen((char *)data); - ast_log(LOG_DEBUG,"line length : %d\n",strln); - if (write(fdesc,&strln,sizeof(int)) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - if (write(fdesc,data,strln) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - seekpos=lseek(fdesc,0,SEEK_CUR); - ast_log(LOG_DEBUG,"Seek position : %d\n",seekpos); - } - } else { - if (read(fdesc,&strln,sizeof(int)) != sizeof(int)) { - ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); - } - ast_log(LOG_DEBUG,"Cache file exists, strln=%d, strlen=%d\n",strln,(int)strlen((char *)data)); - if (strlen((char *)data)==strln) { - ast_log(LOG_DEBUG,"Size OK\n"); - if (read(fdesc,&bigstring,strln) != strln) { - ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno)); - } - bigstring[strln] = 0; - if (strcmp(bigstring,data)==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_log(LOG_DEBUG,"Reading from cache...\n"); - } else { - ast_log(LOG_DEBUG,"Passing text to festival...\n"); - fs=fdopen(dup(fd),"wb"); - fprintf(fs,festivalcommand,(char *)data); - fflush(fs); - fclose(fs); - } - - /* Write to cache and then pass it down */ - if (writecache==1) { - ast_log(LOG_DEBUG,"Writing result to cache...\n"); - while ((strln=read(fd,buffer,16384))!=0) { - if (write(fdesc,buffer,strln) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); - } - } - close(fd); - close(fdesc); - fd=open(cachefile,O_RDWR); - lseek(fd,seekpos,SEEK_SET); - } - - ast_log(LOG_DEBUG,"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); - ast_module_user_remove(u); - return -1; - } - n += read_data; - } - ack[3] = '\0'; - if (strcmp(ack,"WV\n") == 0) { /* receive a waveform */ - ast_log(LOG_DEBUG,"Festival WV command\n"); - waveform = socket_receive_file_to_buff(fd,&filesize); - res = send_waveform_to_channel(chan,waveform,filesize, intstr); - free(waveform); - break; - } - else if (strcmp(ack,"LP\n") == 0) { /* receive an s-expr */ - ast_log(LOG_DEBUG,"Festival LP command\n"); - waveform = socket_receive_file_to_buff(fd,&filesize); - waveform[filesize]='\0'; - ast_log(LOG_WARNING,"Festival returned LP : %s\n",waveform); - 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); - ast_module_user_remove(u); - return res; - -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - struct ast_config *cfg = ast_config_load(FESTIVAL_CONFIG); - 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/apps/app_flash.c b/apps/app_flash.c deleted file mode 100644 index 813294583..000000000 --- a/apps/app_flash.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 App to flash a DAHDI trunk - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/image.h" -#include "asterisk/options.h" - -#include "asterisk/dahdi_compat.h" - -static char *app = "Flash"; - -static char *dahdi_synopsis = "Flashes a DAHDI trunk"; - -static char *dahdi_descrip = -"Performs a flash on a DAHDI 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 char *zap_synopsis = "Flashes a Zap trunk"; - -static char *zap_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 = DAHDI_IOMUX_SIGEVENT; - if (ioctl(fd, DAHDI_IOMUX, &i) == -1) return -1; - if (ioctl(fd, DAHDI_GETEVENT, &j) == -1) return -1; - return j; -} - -static int flash_exec(struct ast_channel *chan, void *data) -{ - int res = -1; - int x; - struct ast_module_user *u; - struct dahdi_params ztp; - u = ast_module_user_add(chan); - if (!strcasecmp(chan->tech->type, dahdi_chan_name)) { - memset(&ztp, 0, sizeof(ztp)); - res = ioctl(chan->fds[0], DAHDI_GET_PARAMS, &ztp); - if (!res) { - if (ztp.sigtype & __DAHDI_SIG_FXS) { - x = DAHDI_FLASH; - res = ioctl(chan->fds[0], DAHDI_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); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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)); - } else - ast_log(LOG_WARNING, "%s is not a DAHDI channel\n", chan->name); - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - if (dahdi_chan_mode == CHAN_ZAP_MODE) { - return ast_register_application(app, flash_exec, zap_synopsis, zap_descrip); - } else { - return ast_register_application(app, flash_exec, dahdi_synopsis, dahdi_descrip); - } -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Flash channel application"); diff --git a/apps/app_followme.c b/apps/app_followme.c deleted file mode 100644 index b5bb5588c..000000000 --- a/apps/app_followme.c +++ /dev/null @@ -1,1138 +0,0 @@ -/* - * 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 - * - * 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 - * - * \arg See \ref Config_followme - * - * \ingroup applications - */ - -/*** MODULEINFO - chan_local - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/say.h" -#include "asterisk/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 parameter in\n" -"followme.conf. If the specified 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. */ - char language[MAX_LANGUAGE]; /*!< The language to be used on this dial, 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 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_LIST_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 */ - free(prev); - AST_LIST_HEAD_INIT_NOLOCK(&f->numbers); - - while ((prev = AST_LIST_REMOVE_HEAD(&f->blnumbers, entry))) - /* Free the blacklisted number */ - free(prev); - AST_LIST_HEAD_INIT_NOLOCK(&f->blnumbers); - - while ((prev = AST_LIST_REMOVE_HEAD(&f->wlnumbers, entry))) - /* Free the whitelisted number */ - 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, char *language, 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)); - ast_copy_string(cur->language, language, sizeof(cur->language)); - cur->order = numorder; - if (option_debug) - ast_log(LOG_DEBUG, "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(void) -{ - struct call_followme *f; - struct ast_config *cfg; - char *cat = NULL, *tmp; - struct ast_variable *var; - struct number *cur, *nm; - int new, idx; - char numberstr[90]; - int timeout; - char *timeoutstr; - int numorder; - const char *takecallstr; - const char *declinecallstr; - const char *tmpstr; - - cfg = ast_config_load("followme.conf"); - if (!cfg) { - ast_log(LOG_WARNING, "No follow me config file (followme.conf), so no follow me\n"); - return 0; - } - - AST_LIST_LOCK(&followmes); - - /* Reset Global Var Values */ - featuredigittimeout = 5000; - - /* Mark all profiles as inactive for the moment */ - AST_LIST_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))) { - if (!strcasecmp(cat, "general")) - continue; - /* Define a new profile */ - /* Look for an existing one */ - AST_LIST_TRAVERSE(&followmes, f, entry) { - if (!strcasecmp(f->name, cat)) - break; - } - if (option_debug) - ast_log(LOG_DEBUG, "New profile %s.\n", cat); - if (!f) { - /* Make one then */ - f = alloc_profile(cat); - new = 1; - } else - new = 0; - - if (f) { - 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")) { - /* Add a new number */ - ast_copy_string(numberstr, var->value, sizeof(numberstr)); - if ((tmp = strchr(numberstr, ','))) { - *tmp = '\0'; - tmp++; - timeoutstr = ast_strdupa(tmp); - if ((tmp = strchr(timeoutstr, ','))) { - *tmp = '\0'; - tmp++; - 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); - if (option_debug > 1) - ast_log(LOG_DEBUG, "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_LIST_INSERT_HEAD(&followmes, f, entry); - } - } - ast_config_destroy(cfg); - - AST_LIST_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; - int dg; - struct findme_user *tmpuser; - int to = 0; - int livechannels = 0; - int tmpto; - long totalwait = 0, wtd, 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)) { - if (!caller) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Original caller hungup. Cleanup.\n"); - clear_calling_tree(findme_user_list); - return NULL; - } - ctstatus = 0; - totalwait = nm->timeout * 1000; - wtd = 0; - 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)) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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_verbose( VERBOSE_PREFIX_3 "%s received a hangup frame.\n", winner->name); - if (dg == 0) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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_verbose( VERBOSE_PREFIX_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; - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", winner->name); - break; - case AST_CONTROL_CONGESTION: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", winner->name); - break; - case AST_CONTROL_RINGING: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", winner->name); - break; - case AST_CONTROL_PROGRESS: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s is making progress passing it to %s\n", winner->name, caller->name); - break; - case AST_CONTROL_VIDUPDATE: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s requested a video update, passing it to %s\n", winner->name, caller->name); - break; - case AST_CONTROL_SRCUPDATE: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s requested a source update, passing it to %s\n", winner->name, caller->name); - break; - case AST_CONTROL_PROCEEDING: - if (option_verbose > 2) - ast_verbose ( VERBOSE_PREFIX_3 "%s is proceeding passing it to %s\n", winner->name,caller->name); - break; - case AST_CONTROL_HOLD: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Call on %s placed on hold\n", winner->name); - break; - case AST_CONTROL_UNHOLD: - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s stopped sounds\n", winner->name); - break; - default: - if (option_debug) - ast_log(LOG_DEBUG, "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; - if (option_debug) - ast_log(LOG_DEBUG, "DTMF received: %c\n",(char) f->subclass); - tmpuser->yn[tmpuser->ynidx] = (char) f->subclass; - tmpuser->ynidx++; - if (option_debug) - ast_log(LOG_DEBUG, "DTMF string: %s\n", tmpuser->yn); - if (tmpuser->ynidx >= ynlongest) { - if (option_debug) - ast_log(LOG_DEBUG, "reached longest possible match - doing evals\n"); - if (!strcmp(tmpuser->yn, tpargs->takecall)) { - if (option_debug) - ast_log(LOG_DEBUG, "Match to take the call!\n"); - ast_frfree(f); - return tmpuser->ochan; - } - if (!strcmp(tmpuser->yn, tpargs->nextindp)) { - if (option_debug) - ast_log(LOG_DEBUG, "Next in dial plan step requested.\n"); - *status = 1; - ast_frfree(f); - return NULL; - } - - } - } - - ast_frfree(f); - } else { - if (winner) { - if (option_debug) - ast_log(LOG_DEBUG, "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--; - if (option_debug) - ast_log(LOG_DEBUG, "live channels left %d\n", livechannels); - if (!livechannels) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "no live channels left. exiting.\n"); - return NULL; - } - } - } - } - - } else - if (option_debug) - ast_log(LOG_DEBUG, "timed out waiting for action\n"); - } - - } else { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "couldn't reach at this number.\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) { - - if (option_debug > 1) - ast_log(LOG_DEBUG, "Number %s timeout %ld\n", nm->number,nm->timeout); - - number = ast_strdupa(nm->number); - if (option_debug > 2) - ast_log(LOG_DEBUG, "examining %s\n", number); - do { - rest = strchr(number, '&'); - if (rest) { - *rest = 0; - rest++; - } - - if (!strcmp(tpargs->context, "")) - snprintf(dialarg, sizeof(dialarg), "%s", number); - else - snprintf(dialarg, sizeof(dialarg), "%s@%s", number, tpargs->context); - - tmpuser = ast_calloc(1, sizeof(*tmpuser)); - if (!tmpuser) { - ast_log(LOG_WARNING, "Out of memory!\n"); - 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); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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 { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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); - - - AST_LIST_TRAVERSE_SAFE_BEGIN(findme_user_list, fmuser, entry) { - if (!fmuser->cleared && fmuser->ochan != winner) - clear_caller(fmuser); - AST_LIST_REMOVE_CURRENT(findme_user_list, entry); - free(fmuser); - } - AST_LIST_TRAVERSE_SAFE_END - fmuser = NULL; - tmpuser = NULL; - headuser = NULL; - if (winner) - break; - - if (!caller) { - tpargs->status = 1; - free(findme_user_list); - return; - } - - idx++; - AST_LIST_TRAVERSE(&tpargs->cnumbers, nm, entry) - if (nm->order == idx) - break; - - } - free(findme_user_list); - if (!winner) - tpargs->status = 1; - else { - tpargs->status = 100; - tpargs->outbound = winner; - } - - - return; - -} - -static void end_bridge_callback (void *data) -{ - char buf[80]; - time_t end; - struct ast_channel *chan = data; - - time(&end); - - ast_channel_lock(chan); - if (chan->cdr->answer.tv_sec) { - snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->answer.tv_sec); - pbx_builtin_setvar_helper(chan, "ANSWEREDTIME", buf); - } - - if (chan->cdr->start.tv_sec) { - snprintf(buf, sizeof(buf), "%ld", end - chan->cdr->start.tv_sec); - pbx_builtin_setvar_helper(chan, "DIALEDTIME", buf); - } - ast_channel_unlock(chan); -} - -static void end_bridge_callback_data_fixup(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) -{ - bconfig->end_bridge_callback_data = originator; -} - -static int 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; - struct ast_module_user *u; - char *argstr; - char namerecloc[255]; - char *fname = NULL; - int duration = 0; - struct ast_channel *caller; - struct ast_channel *outbound; - - 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; - } - - u = ast_module_user_add(chan); - - AST_LIST_LOCK(&followmes); - AST_LIST_TRAVERSE(&followmes, f, entry) { - if (!strcasecmp(f->name, args.followmeid) && (f->active)) - break; - } - AST_LIST_UNLOCK(&followmes); - - if (option_debug) - ast_log(LOG_DEBUG, "New profile %s.\n", args.followmeid); - if (!f) { - ast_log(LOG_WARNING, "Profile requested, %s, not found in the configuration.\n", args.followmeid); - res = 0; - } else { - /* 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, chan->language, ""); - - 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); - - AST_LIST_TRAVERSE_SAFE_BEGIN(&targs.cnumbers, nm, entry) { - AST_LIST_REMOVE_CURRENT(&targs.cnumbers, entry); - free(nm); - } - AST_LIST_TRAVERSE_SAFE_END - if (targs.status != 100) { - ast_moh_stop(chan); - if (ast_test_flag(&targs.followmeflags, FOLLOWMEFLAG_UNREACHABLEMSG)) - ast_stream_and_wait(chan, targs.sorryprompt, chan->language, ""); - 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); - - config.end_bridge_callback = end_bridge_callback; - config.end_bridge_callback_data = chan; - config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; - - 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; - } - res = ast_bridge_call(caller,outbound,&config); - if (outbound) - ast_hangup(outbound); - } - } - outrun: - - if (!ast_strlen_zero(namerecloc)){ - fname = alloca(strlen(namerecloc) + 5); - sprintf(fname, "%s.sln", namerecloc); - unlink(fname); - } - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - struct call_followme *f; - - ast_module_user_hangup_all(); - - ast_unregister_application(app); - - /* Free Memory. Yeah! I'm free! */ - AST_LIST_LOCK(&followmes); - while ((f = AST_LIST_REMOVE_HEAD(&followmes, entry))) { - free_numbers(f); - free(f); - } - - AST_LIST_UNLOCK(&followmes); - - return 0; -} - -static int load_module(void) -{ - if(!reload_followme()) - return AST_MODULE_LOAD_DECLINE; - - return ast_register_application(app, app_exec, synopsis, descrip); -} - -static int reload(void) -{ - reload_followme(); - - 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/apps/app_forkcdr.c b/apps/app_forkcdr.c deleted file mode 100644 index dc53ef78b..000000000 --- a/apps/app_forkcdr.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * 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 -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/cdr.h" -#include "asterisk/app.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. This new cdr record will\n" -"be linked to end of the list of cdr records attached to the channel. The original CDR is\n" -"has a LOCKED flag set, which forces most cdr operations to skip it, except\n" -"for the functions that set the answer and end times, which ignore the LOCKED\n" -"flag. This allows all the cdr records in the channel to be 'ended' together\n" -"when the channel is closed.\n" -"The CDR() func (when setting CDR values) normally ignores the LOCKED flag also,\n" -"but has options to vary its behavior. The 'T' option (described below), can\n" -"override this behavior, but beware the risks.\n" -"\n" -"Detailed Behavior Description:\n" -"First, this app finds the last cdr record in the list, and makes\n" -"a copy of it. This new copy will be the newly forked cdr record.\n" -"Next, this new record is linked to the end of the cdr record list.\n" -"Next, The new cdr record is RESET (unless you use an option to prevent this)\n" -"This means that:\n" -" 1. All flags are unset on the cdr record\n" -" 2. the start, end, and answer times are all set to zero.\n" -" 3. the billsec and duration fields are set to zero.\n" -" 4. the start time is set to the current time.\n" -" 5. the disposition is set to NULL.\n" -"Next, unless you specified the 'v' option, all variables will be\n" -"removed from the original cdr record. Thus, the 'v' option allows\n" -"any CDR variables to be replicated to all new forked cdr records.\n" -"Without the 'v' option, the variables on the original are effectively\n" -"moved to the new forked cdr record.\n" -"Next, if the 's' option is set, the provided variable and value\n" -"are set on the original cdr record.\n" -"Next, if the 'a' option is given, and the original cdr record has an\n" -"answer time set, then the new forked cdr record will have its answer\n" -"time set to its start time. If the old answer time were carried forward,\n" -"the answer time would be earlier than the start time, giving strange\n" -"duration and billsec times.\n" -"Next, if the 'd' option was specified, the disposition is copied from\n" -"the original cdr record to the new forked cdr.\n" -"Next, if the 'D' option was specified, the destination channel field\n" -"in the new forked CDR is erased.\n" -"Next, if the 'e' option was specified, the 'end' time for the original\n" -"cdr record is set to the current time. Future hang-up or ending events\n" -"will not override this time stamp.\n" -"Next, If the 'A' option is specified, the original cdr record will have\n" -"it ANS_LOCKED flag set, which prevent future answer events\n" -"from updating the original cdr record's disposition. Normally, an\n" -"'ANSWERED' event would mark all cdr records in the chain as 'ANSWERED'.\n" -"Next, if the 'T' option is specified, the original cdr record will have\n" -"its 'DONT_TOUCH' flag set, which will force the cdr_answer, cdr_end, and\n" -"cdr_setvar functions to leave that cdr record alone.\n" -"And, last but not least, the original cdr record has its LOCKED flag\n" -"set. Almost all internal CDR functions (except for the funcs that set\n" -"the end, and answer times, and set a variable) will honor this flag\n" -"and leave a LOCKED cdr record alone.\n" -"This means that the newly created forked cdr record will affected\n" -"by events transpiring within Asterisk, with the previously noted\n" -"exceptions.\n" -" Options:\n" -" a - update the answer time on the NEW CDR just after it's been inited..\n" -" The new CDR may have been answered already, the reset that forkcdr.\n" -" does will erase the answer time. This will bring it back, but\n" -" the answer time will be a copy of the fork/start time. It will.\n" -" only do this if the initial cdr was indeed already answered..\n" -" A - Lock the original CDR against the answer time being updated.\n" -" This will allow the disposition on the original CDR to remain the same.\n" -" d - Copy the disposition forward from the old cdr, after the .\n" -" init..\n" -" D - Clear the dstchannel on the new CDR after reset..\n" -" e - end the original CDR. Do this after all the necc. data.\n" -" is copied from the original CDR to the new forked CDR..\n" -" R - do NOT reset the new cdr..\n" -" s(name=val) - Set the CDR var 'name' in the original CDR, with value.\n" -" 'val'.\n" -" T - Mark the original CDR with a DONT_TOUCH flag. setvar, answer, and end\n" -" cdr funcs will obey this flag; normally they don't honor the LOCKED\n" -" flag set on the original CDR record.\n" -" Beware-- using this flag may cause CDR's not to have their end times\n" -" updated! It is suggested that if you specify this flag, you might\n" -" wish to use the 'e' flag as well!\n" -" v - When the new CDR is forked, it gets a copy of the vars attached\n" -" to the current CDR. The vars attached to the original CDR are removed\n" -" unless this option is specified.\n"; - - -enum { - OPT_SETANS = (1 << 0), - OPT_SETDISP = (1 << 1), - OPT_RESETDEST = (1 << 2), - OPT_ENDCDR = (1 << 3), - OPT_NORESET = (1 << 4), - OPT_KEEPVARS = (1 << 5), - OPT_VARSET = (1 << 6), - OPT_ANSLOCK = (1 << 7), - OPT_DONTOUCH = (1 << 8), -}; - -enum { - OPT_ARG_VARSET = 0, - /* note: this entry _MUST_ be the last one in the enum */ - OPT_ARG_ARRAY_SIZE, -}; - -AST_APP_OPTIONS(forkcdr_exec_options, { - AST_APP_OPTION('a', OPT_SETANS), - AST_APP_OPTION('A', OPT_ANSLOCK), - AST_APP_OPTION('d', OPT_SETDISP), - AST_APP_OPTION('D', OPT_RESETDEST), - AST_APP_OPTION('e', OPT_ENDCDR), - AST_APP_OPTION('R', OPT_NORESET), - AST_APP_OPTION_ARG('s', OPT_VARSET, OPT_ARG_VARSET), - AST_APP_OPTION('T', OPT_DONTOUCH), - AST_APP_OPTION('v', OPT_KEEPVARS), -}); - -static void ast_cdr_fork(struct ast_channel *chan, struct ast_flags optflags, char *set) -{ - 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); - - if (!ast_test_flag(&optflags, OPT_NORESET)) - ast_cdr_reset(newcdr, &flags); - - if (!ast_test_flag(cdr, AST_CDR_FLAG_KEEP_VARS)) - ast_cdr_free_vars(cdr, 0); - - if (!ast_strlen_zero(set)) { - char *varname = ast_strdupa(set), *varval; - varval = strchr(varname,'='); - if (varval) { - *varval = 0; - varval++; - ast_cdr_setvar(cdr, varname, varval, 0); - } - } - - if (ast_test_flag(&optflags, OPT_SETANS) && !ast_tvzero(cdr->answer)) - newcdr->answer = newcdr->start; - - if (ast_test_flag(&optflags, OPT_SETDISP)) - newcdr->disposition = cdr->disposition; - - if (ast_test_flag(&optflags, OPT_RESETDEST)) - newcdr->dstchannel[0] = 0; - - if (ast_test_flag(&optflags, OPT_ENDCDR)) - ast_cdr_end(cdr); - - if (ast_test_flag(&optflags, OPT_ANSLOCK)) - ast_set_flag(cdr, AST_CDR_FLAG_ANSLOCKED); - - if (ast_test_flag(&optflags, OPT_DONTOUCH)) - ast_set_flag(cdr, AST_CDR_FLAG_DONT_TOUCH); - - 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; - struct ast_module_user *u; - char *argcopy = NULL; - struct ast_flags flags = {0}; - char *opts[OPT_ARG_ARRAY_SIZE]; - AST_DECLARE_APP_ARGS(arglist, - AST_APP_ARG(options); - ); - - if (!chan->cdr) { - ast_log(LOG_WARNING, "Channel does not have a CDR\n"); - return 0; - } - - u = ast_module_user_add(chan); - - argcopy = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(arglist, argcopy); - - opts[OPT_ARG_VARSET] = 0; - - if (!ast_strlen_zero(arglist.options)) - ast_app_parse_options(forkcdr_exec_options, &flags, opts, arglist.options); - - if (!ast_strlen_zero(data)) - ast_set2_flag(chan->cdr, ast_test_flag(&flags, OPT_KEEPVARS), AST_CDR_FLAG_KEEP_VARS); - - ast_cdr_fork(chan, flags, opts[OPT_ARG_VARSET]); - - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, forkcdr_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Fork The CDR into 2 separate entities"); diff --git a/apps/app_getcpeid.c b/apps/app_getcpeid.c deleted file mode 100644 index bebc60e9b..000000000 --- a/apps/app_getcpeid.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Get ADSI CPE ID - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/adsi.h" -#include "asterisk/options.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 chan_dahdi.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; - struct ast_module_user *u; - unsigned char cpeid[4]; - int gotgeometry = 0; - int gotcpeid = 0; - int width, height, buttons; - char *data[4]; - unsigned int x; - - u = ast_module_user_add(chan); - - 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; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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); - } - } - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, cpeid_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Get ADSI CPE ID"); diff --git a/apps/app_hasnewvoicemail.c b/apps/app_hasnewvoicemail.c deleted file mode 100644 index 8f3d33504..000000000 --- a/apps/app_hasnewvoicemail.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Changes Copyright (c) 2004 - 2006 Todd Freeman - * - * 95% based on HasNewVoicemail by: - * - * Copyright (c) 2003 Tilghman Lesher. All rights reserved. - * - * Tilghman Lesher - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \brief HasVoicemail application - * - * \author Todd Freeman - * - * \note 95% based on HasNewVoicemail by - * Tilghman Lesher - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include - -#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/utils.h" -#include "asterisk/app.h" -#include "asterisk/options.h" - -static char *app_hasvoicemail = "HasVoicemail"; -static char *hasvoicemail_synopsis = "Conditionally branches to priority + 101 with the right options set"; -static char *hasvoicemail_descrip = -"HasVoicemail(vmbox[/folder][@context][|varname[|options]])\n" -" Optionally sets to the number of messages in that folder." -" Assumes folder of INBOX if not specified.\n" -" The option string may contain zero or the following character:\n" -" 'j' -- jump to priority n+101, if there is voicemail in the folder indicated.\n" -" This application sets the following channel variable upon completion:\n" -" HASVMSTATUS The result of the voicemail check returned as a text string as follows\n" -" <# of messages in the folder, 0 for NONE>\n" -"\nThis application has been deprecated in favor of the VMCOUNT() function\n"; - -static char *app_hasnewvoicemail = "HasNewVoicemail"; -static char *hasnewvoicemail_synopsis = "Conditionally branches to priority + 101 with the right options set"; -static char *hasnewvoicemail_descrip = -"HasNewVoicemail(vmbox[/folder][@context][|varname[|options]])\n" -"Assumes folder 'INBOX' if folder is not specified. Optionally sets to the number of messages\n" -"in that folder.\n" -" The option string may contain zero of the following character:\n" -" 'j' -- jump to priority n+101, if there is new voicemail in folder 'folder' or INBOX\n" -" This application sets the following channel variable upon completion:\n" -" HASVMSTATUS The result of the new voicemail check returned as a text string as follows\n" -" <# of messages in the folder, 0 for NONE>\n" -"\nThis application has been deprecated in favor of the VMCOUNT() function\n"; - - -static int hasvoicemail_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - char *input, *varname = NULL, *vmbox, *context = "default"; - char *vmfolder; - int vmcount = 0; - static int dep_warning = 0; - int priority_jump = 0; - char tmp[12]; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(vmbox); - AST_APP_ARG(varname); - AST_APP_ARG(options); - ); - - if (!dep_warning) { - ast_log(LOG_WARNING, "The applications HasVoicemail and HasNewVoicemail have been deprecated. Please use the VMCOUNT() function instead.\n"); - dep_warning = 1; - } - - if (!data) { - ast_log(LOG_WARNING, "HasVoicemail requires an argument (vm-box[/folder][@context][|varname[|options]])\n"); - return -1; - } - - u = ast_module_user_add(chan); - - input = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, input); - - vmbox = strsep(&args.vmbox, "@"); - - if (!ast_strlen_zero(args.vmbox)) - context = args.vmbox; - - vmfolder = strchr(vmbox, '/'); - if (vmfolder) { - *vmfolder = '\0'; - vmfolder++; - } else { - vmfolder = "INBOX"; - } - - if (args.options) { - if (strchr(args.options, 'j')) - priority_jump = 1; - } - - vmcount = ast_app_messagecount(context, vmbox, vmfolder); - /* Set the count in the channel variable */ - if (varname) { - snprintf(tmp, sizeof(tmp), "%d", vmcount); - pbx_builtin_setvar_helper(chan, varname, tmp); - } - - if (vmcount > 0) { - /* Branch to the next extension */ - if (priority_jump || ast_opt_priority_jumping) { - if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) - ast_log(LOG_WARNING, "VM box %s@%s has new voicemail, but extension %s, priority %d doesn't exist\n", vmbox, context, chan->exten, chan->priority + 101); - } - } - - snprintf(tmp, sizeof(tmp), "%d", vmcount); - pbx_builtin_setvar_helper(chan, "HASVMSTATUS", tmp); - - ast_module_user_remove(u); - - return 0; -} - -static int acf_vmcount_exec(struct ast_channel *chan, char *cmd, char *argsstr, char *buf, size_t len) -{ - struct ast_module_user *u; - char *context; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(vmbox); - AST_APP_ARG(folder); - ); - - if (ast_strlen_zero(argsstr)) - return -1; - - u = ast_module_user_add(chan); - - buf[0] = '\0'; - - 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)); - - ast_module_user_remove(u); - - 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) -{ - int res; - - res = ast_custom_function_unregister(&acf_vmcount); - res |= ast_unregister_application(app_hasvoicemail); - res |= ast_unregister_application(app_hasnewvoicemail); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - int res; - - res = ast_custom_function_register(&acf_vmcount); - res |= ast_register_application(app_hasvoicemail, hasvoicemail_exec, hasvoicemail_synopsis, hasvoicemail_descrip); - res |= ast_register_application(app_hasnewvoicemail, hasvoicemail_exec, hasnewvoicemail_synopsis, hasnewvoicemail_descrip); - - return res; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Indicator for whether a voice mailbox has messages in a given folder."); diff --git a/apps/app_ices.c b/apps/app_ices.c deleted file mode 100644 index eb5980f93..000000000 --- a/apps/app_ices.c +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Stream to an icecast server via ICES (see contrib/asterisk-ices.xml) - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/frame.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/options.h" - -#define path_BIN "/usr/bin/" -#define path_LOCAL "/usr/local/bin/" - -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 contrib/asterisk-ices.xml). \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 - * But many places has it in /usr/bin - * As a last-ditch effort, try to use PATH - */ - execl(path_LOCAL "ices2", "ices", filename, (char *)NULL); - execl(path_BIN "ices2", "ices", filename, (char *)NULL); - execlp("ices2", "ices", filename, (char *)NULL); - - if (option_debug) - ast_log(LOG_DEBUG, "Couldn't find ices version 2, attempting to use ices version 1."); - - execl(path_LOCAL "ices", "ices", filename, (char *)NULL); - execl(path_BIN "ices", "ices", filename, (char *)NULL); - execlp("ices", "ices", filename, (char *)NULL); - - ast_log(LOG_WARNING, "Execute of ices failed, could not be found.\n"); - close(fd); - _exit(0); -} - -static int ices_exec(struct ast_channel *chan, void *data) -{ - int res=0; - struct ast_module_user *u; - 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; - } - - u = ast_module_user_add(chan); - - last = ast_tv(0, 0); - - if (pipe(fds)) { - ast_log(LOG_WARNING, "Unable to create pipe\n"); - ast_module_user_remove(u); - 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"); - ast_module_user_remove(u); - 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"); - ast_module_user_remove(u); - return -1; - } - if (((char *)data)[0] == '/') - ast_copy_string(filename, (char *) data, sizeof(filename)); - else - snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, (char *)data); - /* Placeholder for options */ - c = strchr(filename, '|'); - if (c) - *c = '\0'; - res = icesencode(filename, fds[0]); - if (res >= 0) { - pid = res; - for (;;) { - /* Wait for audio, and stream */ - ms = ast_waitfor(chan, -1); - if (ms < 0) { - ast_log(LOG_DEBUG, "Hangup detected\n"); - res = -1; - break; - } - f = ast_read(chan); - if (!f) { - ast_log(LOG_DEBUG, "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[0]); - close(fds[1]); - - if (pid > -1) - kill(pid, SIGKILL); - if (!res && oreadformat) - ast_set_read_format(chan, oreadformat); - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, ices_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Encode and Stream via icecast and ices"); diff --git a/apps/app_image.c b/apps/app_image.c deleted file mode 100644 index f5f327197..000000000 --- a/apps/app_image.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 App to transmit an image - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/image.h" -#include "asterisk/app.h" -#include "asterisk/options.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\n" -"fails, the channel will be hung up. Otherwise, the dialplan\n" -"continues execution.\n" -"The option string may contain the following character:\n" -" 'j' -- jump to priority n+101 if the channel doesn't support image transport\n" -"This application sets the following channel variable upon completion:\n" -" SENDIMAGESTATUS The status is the result of the attempt as a text string, one of\n" -" OK | NOSUPPORT \n"; - - -static int sendimage_exec(struct ast_channel *chan, void *data) -{ - int res = 0; - struct ast_module_user *u; - char *parse; - int priority_jump = 0; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(filename); - AST_APP_ARG(options); - ); - - u = ast_module_user_add(chan); - - parse = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, parse); - - if (ast_strlen_zero(args.filename)) { - ast_log(LOG_WARNING, "SendImage requires an argument (filename[|options])\n"); - return -1; - } - - if (args.options) { - if (strchr(args.options, 'j')) - priority_jump = 1; - } - - if (!ast_supports_images(chan)) { - /* Does not support transport */ - if (priority_jump || ast_opt_priority_jumping) - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "NOSUPPORT"); - ast_module_user_remove(u); - return 0; - } - - res = ast_send_image(chan, args.filename); - - if (!res) - pbx_builtin_setvar_helper(chan, "SENDIMAGESTATUS", "OK"); - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, sendimage_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Image Transmission Application"); diff --git a/apps/app_ivrdemo.c b/apps/app_ivrdemo.c deleted file mode 100644 index 933ae9040..000000000 --- a/apps/app_ivrdemo.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 IVR Demo application - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - no - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#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" - -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; - struct ast_module_user *u; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "skel requires an argument (filename)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - /* 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); - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, skel_exec, tdesc, synopsis); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "IVR Demo Application"); diff --git a/apps/app_lookupblacklist.c b/apps/app_lookupblacklist.c deleted file mode 100644 index 88ed52d86..000000000 --- a/apps/app_lookupblacklist.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 App to lookup the callerid number, and see if it is blacklisted - * - * \author Mark Spencer - * - * \ingroup applications - * - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/options.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/astdb.h" -#include "asterisk/options.h" - -static char *app = "LookupBlacklist"; - -static char *synopsis = "Look up Caller*ID name/number from blacklist database"; - -static char *descrip = - " LookupBlacklist(options): Looks up the Caller*ID number on the active\n" - "channel in the Asterisk database (family 'blacklist'). \n" - "The option string may contain the following character:\n" - " 'j' -- jump to n+101 priority if the number/name is found in the blacklist\n" - "This application sets the following channel variable upon completion:\n" - " LOOKUPBLSTATUS The status of the Blacklist lookup as a text string, one of\n" - " FOUND | NOTFOUND\n" - "Example: exten => 1234,1,LookupBlacklist()\n\n" - "This application is deprecated and may be removed from a future release.\n" - "Please use the dialplan function BLACKLIST() instead.\n"; - - -static int blacklist_read(struct ast_channel *chan, 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 -lookupblacklist_exec (struct ast_channel *chan, void *data) -{ - char blacklist[1]; - struct ast_module_user *u; - int bl = 0; - int priority_jump = 0; - static int dep_warning = 0; - - u = ast_module_user_add(chan); - - if (!dep_warning) { - dep_warning = 1; - ast_log(LOG_WARNING, "LookupBlacklist is deprecated. Please use ${BLACKLIST()} instead.\n"); - } - - if (!ast_strlen_zero(data)) { - if (strchr(data, 'j')) - priority_jump = 1; - } - - if (chan->cid.cid_num) { - if (!ast_db_get("blacklist", chan->cid.cid_num, blacklist, sizeof (blacklist))) { - if (option_verbose > 2) - ast_log(LOG_NOTICE, "Blacklisted number %s found\n",chan->cid.cid_num); - bl = 1; - } - } - if (chan->cid.cid_name) { - if (!ast_db_get("blacklist", chan->cid.cid_name, blacklist, sizeof (blacklist))) { - if (option_verbose > 2) - ast_log (LOG_NOTICE,"Blacklisted name \"%s\" found\n",chan->cid.cid_name); - bl = 1; - } - } - - if (bl) { - if (priority_jump || ast_opt_priority_jumping) - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - pbx_builtin_setvar_helper(chan, "LOOKUPBLSTATUS", "FOUND"); - } else - pbx_builtin_setvar_helper(chan, "LOOKUPBLSTATUS", "NOTFOUND"); - - ast_module_user_remove(u); - - return 0; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - res |= ast_custom_function_unregister(&blacklist_function); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - int res = ast_custom_function_register(&blacklist_function); - res |= ast_register_application (app, lookupblacklist_exec, synopsis,descrip); - return res; -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Look up Caller*ID name/number from blacklist database"); diff --git a/apps/app_lookupcidname.c b/apps/app_lookupcidname.c deleted file mode 100644 index 8b8d5207a..000000000 --- a/apps/app_lookupcidname.c +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 App to set callerid name from database, based on directory number - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/options.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/astdb.h" - -static char *app = "LookupCIDName"; - -static char *synopsis = "Look up CallerID Name from local database"; - -static char *descrip = - " LookupCIDName: Looks up the Caller*ID number on the active\n" - "channel in the Asterisk database (family 'cidname') and sets the\n" - "Caller*ID name. Does nothing if no Caller*ID was received on the\n" - "channel. This is useful if you do not subscribe to Caller*ID\n" - "name delivery, or if you want to change the names on some incoming\n" - "calls.\n\n" - "LookupCIDName is deprecated. Please use ${DB(cidname/${CALLERID(num)})}\n" - "instead.\n"; - - -static int lookupcidname_exec (struct ast_channel *chan, void *data) -{ - char dbname[64]; - struct ast_module_user *u; - static int dep_warning = 0; - - u = ast_module_user_add(chan); - if (!dep_warning) { - dep_warning = 1; - ast_log(LOG_WARNING, "LookupCIDName is deprecated. Please use ${DB(cidname/${CALLERID(num)})} instead.\n"); - } - if (chan->cid.cid_num) { - if (!ast_db_get ("cidname", chan->cid.cid_num, dbname, sizeof (dbname))) { - ast_set_callerid (chan, NULL, dbname, NULL); - if (option_verbose > 2) - ast_verbose (VERBOSE_PREFIX_3 "Changed Caller*ID name to %s\n", - dbname); - } - } - ast_module_user_remove(u); - - return 0; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application (app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application (app, lookupcidname_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Look up CallerID Name from local database"); diff --git a/apps/app_macro.c b/apps/app_macro.c deleted file mode 100644 index 5296e8723..000000000 --- a/apps/app_macro.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Dial plan macro Implementation - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/options.h" -#include "asterisk/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-', 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! (you can use 'catch' in AEL) \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.\n"; - -static char *if_descrip = -" MacroIf(?macroname_a[|arg1][:macroname_b[|arg1]])\n" -"Executes macro defined in if is true\n" -"(otherwise 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 void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); - -struct ast_datastore_info macro_ds_info = { - .type = "MACRO", - .chan_fixup = macro_fixup, -}; - -static void macro_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - int i; - char varname[10]; - pbx_builtin_setvar_helper(new_chan, "MACRO_DEPTH", "0"); - pbx_builtin_setvar_helper(new_chan, "MACRO_CONTEXT", NULL); - pbx_builtin_setvar_helper(new_chan, "MACRO_EXTEN", NULL); - pbx_builtin_setvar_helper(new_chan, "MACRO_PRIORITY", NULL); - pbx_builtin_setvar_helper(new_chan, "MACRO_OFFSET", NULL); - for (i = 1; i < 100; i++) { - snprintf(varname, sizeof(varname), "ARG%d", i); - while (pbx_builtin_getvar_helper(new_chan, varname)) { - /* Kill all levels of arguments */ - pbx_builtin_setvar_helper(new_chan, varname, NULL); - } - } -} - -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, inhangup = 0; - - char *save_macro_exten; - char *save_macro_context; - char *save_macro_priority; - char *save_macro_offset; - struct ast_module_user *u; - struct ast_datastore *macro_store = ast_channel_datastore_find(chan, ¯o_ds_info, NULL); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Macro() requires arguments. See \"show application macro\" for help.\n"); - return -1; - } - - u = ast_module_user_add(chan); - - do { - if (macro_store) { - break; - } - if (!(macro_store = ast_channel_datastore_alloc(¯o_ds_info, NULL))) { - ast_log(LOG_WARNING, "Unable to allocate new datastore.\n"); - break; - } - /* Just the existence of this datastore is enough. */ - macro_store->inheritance = DATASTORE_INHERIT_FOREVER; - ast_channel_datastore_add(chan, macro_store); - } while (0); - - /* 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"); - ast_module_user_remove(u); - 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"); - ast_module_user_remove(u); - 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); - ast_module_user_remove(u); - return 0; - } - - /* If we are to run the macro exclusively, take the mutex */ - if (exclusive) { - ast_log(LOG_DEBUG, "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); - ast_module_user_remove(u); - - 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; - 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_lock_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))) { - /* 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_log(LOG_DEBUG, "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: - if (option_debug) - ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE in macro %s on '%s'\n", chan->context, chan->exten, chan->priority, macro, chan->name); - else if (option_verbose > 1) - ast_verbose( VERBOSE_PREFIX_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; - default: - if (option_debug) - ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro); - else if (option_verbose > 1) - ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s' in macro '%s'\n", chan->context, chan->exten, chan->priority, chan->name, macro); - goto out; - } - } - - ast_log(LOG_DEBUG, "Executed application: %s\n", runningapp); - - if (!strcasecmp(runningapp, "GOSUB")) { - gosub_level++; - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Incrementing gosub_level\n"); - } - } else { - if (!ast_strlen_zero(app2)) { - gosub_level++; - ast_log(LOG_DEBUG, "Incrementing gosub_level\n"); - } - } - } else if (!strcasecmp(runningapp, "RETURN")) { - gosub_level--; - ast_log(LOG_DEBUG, "Decrementing gosub_level\n"); - } else if (!strcasecmp(runningapp, "STACKPOP")) { - gosub_level--; - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Last app: %s\n", tmp3); - - if (tmp3 && !strncasecmp(tmp3, "GOSUB", 5)) { - gosub_level++; - ast_log(LOG_DEBUG, "Incrementing gosub_level\n"); - } else if (tmp3 && !strncasecmp(tmp3, "RETURN", 6)) { - gosub_level--; - ast_log(LOG_DEBUG, "Decrementing gosub_level\n"); - } else if (tmp3 && !strncasecmp(tmp3, "STACKPOP", 8)) { - gosub_level--; - ast_log(LOG_DEBUG, "Decrementing gosub_level\n"); - } - } - - if (gosub_level == 0 && strcasecmp(chan->context, fullmacro)) { - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "Channel '%s' jumping out of macro '%s'\n", chan->name, macro); - break; - } - - /* don't stop executing extensions when we're in "h" */ - if (chan->_softhangup && !inhangup) { - ast_log(LOG_DEBUG, "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: - - /* Don't let the channel change now. */ - ast_channel_lock(chan); - - /* 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); - 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]) { - pbx_builtin_setvar_helper(chan, varname, oldargs[x]); - free(oldargs[x]); - } else { - pbx_builtin_setvar_helper(chan, varname, NULL); - } - } - - /* Restore macro variables */ - 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) - free(save_macro_exten); - if (save_macro_context) - free(save_macro_context); - if (save_macro_priority) - free(save_macro_priority); - - if (setmacrocontext) { - chan->macrocontext[0] = '\0'; - chan->macroexten[0] = '\0'; - chan->macropriority = 0; - } - - if (!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 (!(chan->_softhangup & 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; - } - } - } - } - } - - pbx_builtin_setvar_helper(chan, "MACRO_OFFSET", save_macro_offset); - if (save_macro_offset) - free(save_macro_offset); - - /* Unlock the macro */ - if (exclusive) { - ast_log(LOG_DEBUG, "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; - } - } - ast_channel_unlock(chan); - - ast_module_user_remove(u); - - 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; - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - if (!(expr = ast_strdupa(data))) { - ast_module_user_remove(u); - 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"); - - ast_module_user_remove(u); - - 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); - - ast_module_user_hangup_all(); - - 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/apps/app_meetme.c b/apps/app_meetme.c deleted file mode 100644 index 2503b0f38..000000000 --- a/apps/app_meetme.c +++ /dev/null @@ -1,5037 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2007, Digium, Inc. - * - * Mark Spencer - * - * SLA Implementation by: - * Russell Bryant - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed 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 - * \author (SLA) Russell Bryant - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/config.h" -#include "asterisk/app.h" -#include "asterisk/dsp.h" -#include "asterisk/musiconhold.h" -#include "asterisk/manager.h" -#include "asterisk/options.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/astobj2.h" -#include "asterisk/devicestate.h" -#include "asterisk/dial.h" -#include "asterisk/causes.h" - -#include "asterisk/dahdi_compat.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 - -enum { - ADMINFLAG_MUTED = (1 << 1), /*!< User is muted */ - ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */ - ADMINFLAG_KICKME = (1 << 3) /*!< User has been kicked */ -}; - -#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 '#' is pressed */ - CONFFLAG_POUNDEXIT = (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, treats talking users as muted users */ - CONFFLAG_OPTIMIZETALKER = (1 << 21), - /*! 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), - /*! This is a SLA station. (Only for use by the SLA applications.) */ - CONFFLAG_SLA_STATION = (1 << 26), - /*! This is a SLA trunk. (Only for use by the SLA applications.) */ - CONFFLAG_SLA_TRUNK = (1 << 27), - /*! Do not write any audio to this channel until the state is up. */ - CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 28), -}; - -enum { - OPT_ARG_WAITMARKED = 0, - OPT_ARG_ARRAY_SIZE = 1, -}; - -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('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('M', CONFFLAG_MOH ), - AST_APP_OPTION('m', CONFFLAG_STARTMUTED ), - AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ), - AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ), - AST_APP_OPTION('p', CONFFLAG_POUNDEXIT ), - 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 ), -END_OPTIONS ); - -static const char *app = "MeetMe"; -static const char *app2 = "MeetMeCount"; -static const char *app3 = "MeetMeAdmin"; -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 *slastation_synopsis = "Shared Line Appearance Station"; -static const char *slatrunk_synopsis = "Shared Line Appearance Trunk"; - -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 DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)\n" -" must be present for conferencing to operate properly. In addition, the chan_dahdi\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-DAHDI channels in the same conference)\n" -" 'c' -- announce user(s) count on joining a 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' -- enable music on hold when the conference has a single caller\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. Note that this option\n" -" will be removed in 1.6 and enabled by default.\n" -" 'p' -- allow user to exit the conference by pressing '#'\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[()]'\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"; - -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"; - -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 *slastation_desc = -" SLAStation(station):\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):\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" -""; - -#define MAX_CONFNUM 80 -#define MAX_PIN 80 - -enum announcetypes { - CONF_HASJOIN, - CONF_HASLEFT -}; - -struct announce_listitem { - AST_LIST_ENTRY(announce_listitem) entry; - char namerecloc[PATH_MAX]; /*!< Name Recorded file Location */ - char language[MAX_LANGUAGE]; - struct ast_channel *confchan; - int confusers; - enum announcetypes announcetype; -}; - -/*! \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 */ - 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 */ - 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; - /* announce_thread related data */ - pthread_t announcethread; - ast_mutex_t announcethreadlock; - unsigned int announcethread_stop:1; - ast_cond_t announcelist_addition; - AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist; - ast_mutex_t announcelistlock; -}; - -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) */ -}; - -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 */ - 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; -}; - -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; -}; - -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, -}; - -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; -} 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 = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT; - res = ioctl(fd, DAHDI_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 (!chan->_softhangup) - 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 - * - * \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) -{ - struct ast_conference *cnf; - struct dahdi_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); - cnf->announcethread = AST_PTHREADT_NULL; - ast_mutex_init(&cnf->announcethreadlock); - 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)); - - /* Setup a new zap conference */ - ztc.confno = -1; - ztc.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; - cnf->fd = open(DAHDI_FILE_PSEUDO, O_RDWR); - if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Unable to open pseudo device\n"); - if (cnf->fd >= 0) - close(cnf->fd); - free(cnf); - cnf = NULL; - goto cnfout; - } - - cnf->zapconf = ztc.confno; - - /* Setup a new channel for playback of audio files */ - cnf->chan = ast_request(dahdi_chan_name, 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 = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; - if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - if (cnf->chan) - ast_hangup(cnf->chan); - else - close(cnf->fd); - free(cnf); - cnf = NULL; - goto cnfout; - } - } - - /* Fill the conference struct */ - cnf->start = time(NULL); - cnf->isdynamic = dynamic ? 1 : 0; - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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 int meetme_cmd(int fd, int argc, char **argv) -{ - /* 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\n"; - char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n"; - char cmdline[1024] = ""; - - if (argc > 8) - ast_cli(fd, "Invalid Arguments.\n"); - /* Check for length so no buffer will overflow... */ - for (i = 0; i < argc; i++) { - if (strlen(argv[i]) > 100) - ast_cli(fd, "Invalid Arguments.\n"); - } - if (argc == 1) { - /* 'MeetMe': List all the conferences */ - now = time(NULL); - AST_LIST_LOCK(&confs); - if (AST_LIST_EMPTY(&confs)) { - ast_cli(fd, "No active MeetMe conferences.\n"); - AST_LIST_UNLOCK(&confs); - return RESULT_SUCCESS; - } - ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation"); - 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; - - ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static"); - - total += cnf->users; - } - AST_LIST_UNLOCK(&confs); - ast_cli(fd, "* Total number of MeetMe users: %d\n", total); - return RESULT_SUCCESS; - } - if (argc < 3) - return RESULT_SHOWUSAGE; - ast_copy_string(cmdline, argv[2], sizeof(cmdline)); /* Argv 2: conference number */ - if (strstr(argv[1], "lock")) { - if (strcmp(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(argv[1], "mute")) { - if (argc < 4) - return RESULT_SHOWUSAGE; - if (strcmp(argv[1], "mute") == 0) { - /* Mute */ - if (strcmp(argv[3], "all") == 0) { - strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1); - } else { - strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1); - strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1); - } - } else { - /* Unmute */ - if (strcmp(argv[3], "all") == 0) { - strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1); - } else { - strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1); - strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1); - } - } - } else if (strcmp(argv[1], "kick") == 0) { - if (argc < 4) - return RESULT_SHOWUSAGE; - if (strcmp(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, argv[3], sizeof(cmdline) - strlen(cmdline) - 1); - } - } else if(strcmp(argv[1], "list") == 0) { - int concise = ( 4 == argc && ( !strcasecmp(argv[3], "concise") ) ); - /* List all the users in a conference */ - if (AST_LIST_EMPTY(&confs)) { - if ( !concise ) - ast_cli(fd, "No active conferences.\n"); - return RESULT_SUCCESS; - } - /* Find the right conference */ - AST_LIST_LOCK(&confs); - AST_LIST_TRAVERSE(&confs, cnf, list) { - if (strcmp(cnf->confno, argv[2]) == 0) - break; - } - if (!cnf) { - if ( !concise ) - ast_cli(fd, "No such conference: %s.\n",argv[2]); - AST_LIST_UNLOCK(&confs); - return RESULT_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(fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %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 ? "(Admin)" : "", - user->userflags & CONFFLAG_MONITOR ? "(Listen only)" : "", - user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "", - istalking(user->talking), hr, min, sec); - else - ast_cli(fd, "%d!%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->talking, hr, min, sec); - - } - if ( !concise ) - ast_cli(fd,"%d users in that conference.\n",cnf->users); - AST_LIST_UNLOCK(&confs); - return RESULT_SUCCESS; - } else - return RESULT_SHOWUSAGE; - ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline); - admin_exec(NULL, cmdline); - - return 0; -} - -static char *complete_meetmecmd(const char *line, const char *word, int pos, int state) -{ - static char *cmds[] = {"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 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 ? strdup(usrno) : NULL; - } else if ( strstr(line, "list") && ( 0 == state ) ) - return strdup("concise"); - } - - return NULL; -} - -static char meetme_usage[] = -"Usage: meetme (un)lock|(un)mute|kick|list [concise] \n" -" Executes a command for the conference or on a conferee\n"; - -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 int sla_show_trunks(int fd, int argc, char **argv) -{ - const struct sla_trunk *trunk; - - ast_cli(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(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(fd, "=== ==> Station name: %s\n", station_ref->station->name); - AST_RWLIST_UNLOCK(&sla_stations); - ast_cli(fd, "=== ---------------------------------------------------------\n" - "===\n"); - } - AST_RWLIST_UNLOCK(&sla_trunks); - ast_cli(fd, "=============================================================\n" - "\n"); - - return RESULT_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 const char sla_show_trunks_usage[] = -"Usage: sla show trunks\n" -" This will list all trunks defined in sla.conf\n"; - -static int sla_show_stations(int fd, int argc, char **argv) -{ - const struct sla_station *station; - - ast_cli(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(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(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(fd, "=== ---------------------------------------------------------\n" - "===\n"); - } - AST_RWLIST_UNLOCK(&sla_stations); - ast_cli(fd, "============================================================\n" - "\n"); - - return RESULT_SUCCESS; -} - -static const char sla_show_stations_usage[] = -"Usage: sla show stations\n" -" This will list all stations defined in sla.conf\n"; - -static struct ast_cli_entry cli_meetme[] = { - { { "meetme", NULL, NULL }, - meetme_cmd, "Execute a command on a conference or conferee", - meetme_usage, complete_meetmecmd }, - - { { "sla", "show", "trunks", NULL }, - sla_show_trunks, "Show SLA Trunks", - sla_show_trunks_usage, NULL }, - - { { "sla", "show", "stations", NULL }, - sla_show_stations, "Show SLA Stations", - sla_show_stations_usage, NULL }, -}; - -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 = DAHDI_FLUSH_ALL; - if (ioctl(fd, DAHDI_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; - struct announce_listitem *item; - - AST_LIST_REMOVE(&confs, conf, list); - - 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;xtransframe[x]) - ast_frfree(conf->transframe[x]); - if (conf->transpath[x]) - ast_translator_free_path(conf->transpath[x]); - } - if (conf->announcethread != AST_PTHREADT_NULL) { - ast_mutex_lock(&conf->announcelistlock); - conf->announcethread_stop = 1; - ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT); - ast_cond_signal(&conf->announcelist_addition); - ast_mutex_unlock(&conf->announcelistlock); - pthread_join(conf->announcethread, NULL); - - while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) { - ast_filedelete(item->namerecloc, NULL); - ao2_ref(item, -1); - } - ast_mutex_destroy(&conf->announcelistlock); - } - 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_mutex_destroy(&conf->announcethreadlock); - - 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 (sla.thread == AST_PTHREADT_NULL) { - return; - } - - 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_log(LOG_DEBUG, "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 const char *get_announce_filename(enum announcetypes type) -{ - switch (type) { - case CONF_HASLEFT: - return "conf-hasleft"; - break; - case CONF_HASJOIN: - return "conf-hasjoin"; - break; - default: - return ""; - } -} - -static void *announce_thread(void *data) -{ - struct announce_listitem *current; - struct ast_conference *conf = data; - int res; - char filename[PATH_MAX] = ""; - AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list; - AST_LIST_HEAD_INIT_NOLOCK(&local_list); - - while (!conf->announcethread_stop) { - ast_mutex_lock(&conf->announcelistlock); - if (conf->announcethread_stop) { - ast_mutex_unlock(&conf->announcelistlock); - break; - } - if (AST_LIST_EMPTY(&conf->announcelist)) - ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock); - - AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry); - AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist); - - ast_mutex_unlock(&conf->announcelistlock); - if (conf->announcethread_stop) { - break; - } - - for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) { - ast_log(LOG_DEBUG, "About to play %s\n", current->namerecloc); - if (!ast_fileexists(current->namerecloc, NULL, NULL)) - continue; - if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) { - if (!ast_streamfile(current->confchan, current->namerecloc, current->language)) - res = ast_waitstream(current->confchan, ""); - if (!res) { - ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename)); - if (!ast_streamfile(current->confchan, filename, current->language)) - ast_waitstream(current->confchan, ""); - } - } - if (current->announcetype == CONF_HASLEFT) { - ast_filedelete(current->namerecloc, NULL); - } - } - } - - /* thread marked to stop, clean up */ - while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) { - ast_filedelete(current->namerecloc, NULL); - ao2_ref(current, -1); - } - return NULL; -} - -static int can_write(struct ast_channel *chan, int confflags) -{ - if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) { - return 1; - } - - return (chan->_state == AST_STATE_UP); -} - -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 dahdi_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 using_pseudo = 0; - int duration=20; - int hr, min, sec; - int sent_event = 0; - time_t 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; - struct dahdi_bufferinfo bi; - char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; - char *buf = __buf + AST_FRIENDLY_OFFSET; - 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_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) { - snprintf(recordingtmp, sizeof(recordingtmp), "wav"); - conf->recordingformat = ast_strdupa(recordingtmp); - } - ast_verbose(VERBOSE_PREFIX_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(dahdi_chan_name, 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 = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON; - if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error starting listen channel\n"); - ast_hangup(conf->lchan); - conf->lchan = NULL; - } else { - pthread_attr_init(&conf->attr); - pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED); - ast_pthread_create_background(&conf->recordthread, &conf->attr, recordthread, conf); - pthread_attr_destroy(&conf->attr); - } - } - ast_mutex_unlock(&conf->recordthreadlock); - - ast_mutex_lock(&conf->announcethreadlock); - if ((conf->announcethread == AST_PTHREADT_NULL) && !(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW))) { - ast_mutex_init(&conf->announcelistlock); - AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist); - ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf); - } - ast_mutex_unlock(&conf->announcethreadlock); - - time(&user->jointime); - - if (conf->locked && (!(confflags & CONFFLAG_ADMIN))) { - /* Sorry, but this confernce 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; - - 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))) { - char destdir[PATH_MAX]; - - snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR); - - if (mkdir(destdir, 0777) && errno != EEXIST) { - ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno)); - goto outrun; - } - - snprintf(user->namerecloc, sizeof(user->namerecloc), - "%s/meetme-username-%s-%d", destdir, - 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++; - /* 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_device_state_changed("meetme:%s", conf->confno); - - ast_mutex_unlock(&conf->playlock); - - 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; - } - } - } - - if (!(confflags & CONFFLAG_NO_AUDIO_UNTIL_UP)) { - /* We're leaving this alone until the state gets changed to up */ - 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, dahdi_chan_name) || (chan->audiohooks || chan->monitor) ? 1 : 0); - user->zapchannel = !retryzap; - - zapretry: - origfd = chan->fds[0]; - if (retryzap) { - fd = open(DAHDI_FILE_PSEUDO, O_RDWR); - if (fd < 0) { - ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); - goto outrun; - } - 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 = DAHDI_POLICY_IMMEDIATE; - bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE; - bi.numbufs = audio_buffers; - if (ioctl(fd, DAHDI_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, DAHDI_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, DAHDI_GETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error getting conference\n"); - close(fd); - goto outrun; - } - if (ztc.confmode) { - /* Whoa, already in a conference... Retry... */ - if (!retryzap) { - ast_log(LOG_DEBUG, "%s channel is in a conference already, retrying with pseudo\n", dahdi_chan_name); - retryzap = 1; - goto zapretry; - } - } - memset(&ztc, 0, sizeof(ztc)); - /* Add us to the conference */ - ztc.chan = 0; - ztc.confno = conf->zapconf; - - if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users > 1) { - struct announce_listitem *item; - if (!(item = ao2_alloc(sizeof(*item), NULL))) - return -1; - ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc)); - ast_copy_string(item->language, chan->language, sizeof(item->language)); - item->confchan = conf->chan; - item->confusers = conf->users; - item->announcetype = CONF_HASJOIN; - ast_mutex_lock(&conf->announcelistlock); - ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */ - AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry); - ast_cond_signal(&conf->announcelist_addition); - ast_mutex_unlock(&conf->announcelistlock); - - while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) { - ; - } - ao2_ref(item, -1); - } - - if (confflags & CONFFLAG_WAITMARKED && !conf->markedusers) - ztc.confmode = DAHDI_CONF_CONF; - else if (confflags & CONFFLAG_MONITOR) - ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER; - else if (confflags & CONFFLAG_TALKER) - ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER; - else - ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER; - - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - goto outrun; - } - ast_log(LOG_DEBUG, "Placed channel %s in %s conf %d\n", chan->name, dahdi_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", - chan->name, chan->uniqueid, conf->confno, user->user_no); - 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); - } - - 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_MONITORTALKER | CONFFLAG_OPTIMIZETALKER) && !(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; - - if (timeout && time(NULL) >= 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) - break; - else { - ztc.confmode = DAHDI_CONF_CONF; - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - goto outrun; - } - } - } - if (musiconhold == 0 && (confflags & CONFFLAG_MOH)) { - ast_moh_start(chan, NULL, NULL); - musiconhold = 1; - } - } else if(currentmarked >= 1 && lastmarked == 0) { - /* Marked user entered, so cancel timeout */ - timeout = 0; - if (confflags & CONFFLAG_MONITOR) - ztc.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER; - else if (confflags & CONFFLAG_TALKER) - ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER; - else - ztc.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER; - if (ioctl(fd, DAHDI_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 == 0) { - ast_moh_start(chan, NULL, NULL); - 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)) { - 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 & DAHDI_CONF_TALKER)) { - ztc.confmode ^= DAHDI_CONF_TALKER; - if (ioctl(fd, DAHDI_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 & DAHDI_CONF_TALKER)) { - ztc.confmode |= DAHDI_CONF_TALKER; - if (ioctl(fd, DAHDI_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 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) { - char dtmfstr[2] = ""; - - if (c->fds[0] != origfd || (user->zapchannel && (c->audiohooks || c->monitor))) { - if (using_pseudo) { - /* Kill old pseudo */ - close(fd); - using_pseudo = 0; - } - ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n"); - retryzap = (strcasecmp(c->tech->type, dahdi_chan_name) || (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_DTMF) { - dtmfstr[0] = f->subclass; - dtmfstr[1] = '\0'; - } - - 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_MONITORTALKER | CONFFLAG_OPTIMIZETALKER)) { - 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 || !(confflags & CONFFLAG_OPTIMIZETALKER)) - careful_write(fd, f->data, f->datalen, 0); - } - } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) { - 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, DAHDI_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 '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) - ast_moh_start(chan, NULL, NULL); - - if (ioctl(fd, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - close(fd); - ast_frfree(f); - goto outrun; - } - - conf_flush(fd, chan); - /* Since this option could absorb dtmf for the previous, we have to check this one last */ - } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT) && ast_exists_extension(chan, exitcontext, dtmfstr, 1, "")) { - if (confflags & CONFFLAG_PASS_DTMF) - conf_queue_dtmf(conf, user, f); - - if (!ast_goto_if_exists(chan, exitcontext, dtmfstr, 1)) { - ast_log(LOG_DEBUG, "Got DTMF %c, goto context %s\n", dtmfstr[0], exitcontext); - ret = 0; - ast_frfree(f); - break; - } else if (option_debug > 1) - ast_log(LOG_DEBUG, "Exit by single digit did not work in meetme. Extension '%s' does not exist in context '%s'\n", dtmfstr, exitcontext); - } 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 if (option_debug) { - ast_log(LOG_DEBUG, - "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 && (confflags & CONFFLAG_OPTIMIZETALKER)) - )) { - int index; - for (index=0;indexrawwriteformat & (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 (can_write(chan, confflags) && 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 (can_write(chan, confflags) && 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, DAHDI_SETCONF, &ztc)) { - ast_log(LOG_WARNING, "Error setting conference\n"); - } - } - - reset_volumes(user); - - 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)) && conf->users > 1) { - struct announce_listitem *item; - if (!(item = ao2_alloc(sizeof(*item), NULL))) - return -1; - ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc)); - ast_copy_string(item->language, chan->language, sizeof(item->language)); - item->confchan = conf->chan; - item->confusers = conf->users; - item->announcetype = CONF_HASLEFT; - ast_mutex_lock(&conf->announcelistlock); - AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry); - ast_cond_signal(&conf->announcelist_addition); - ast_mutex_unlock(&conf->announcelistlock); - } else if (!(confflags & CONFFLAG_QUIET) && ((confflags & CONFFLAG_INTROUSER) || (confflags & CONFFLAG_INTROUSERNOREVIEW)) && conf->users == 1) { - /* Last person is leaving, so no reason to try and announce, but should delete the name recording */ - ast_filedelete(user->namerecloc, NULL); - } - - outrun: - AST_LIST_LOCK(&confs); - - if (dsp) - ast_dsp_free(dsp); - - if (user->user_no) { /* Only cleanup users who really joined! */ - now = time(NULL); - hr = (now - user->jointime) / 3600; - min = ((now - user->jointime) % 3600) / 60; - sec = (now - 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, ""), - S_OR(user->chan->cid.cid_name, ""), - (long)(now - user->jointime)); - } - - if (setusercount) { - conf->users--; - /* 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_device_state_changed("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); - } - 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) -{ - struct ast_variable *var, *save; - struct ast_conference *cnf; - - /* 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 */ - - var = ast_load_realtime("meetme", "confno", confno, NULL); - - if (!var) - return NULL; - - save = var; - while (var) { - if (!strcasecmp(var->name, "pin")) { - pin = ast_strdupa(var->value); - } else if (!strcasecmp(var->name, "adminpin")) { - pinadmin = ast_strdupa(var->value); - } - var = var->next; - } - ast_variables_destroy(save); - - cnf = build_conf(confno, pin ? pin : "", pinadmin ? pinadmin : "", make, dynamic, refcount); - } - - if (cnf) { - if (confflags && !cnf->chan && - !ast_test_flag(confflags, CONFFLAG_QUIET) && - ast_test_flag(confflags, CONFFLAG_INTROUSER)) { - ast_log(LOG_WARNING, "No %s channel available for conference, user introduction disabled\n", dahdi_chan_name); - ast_clear_flag(confflags, CONFFLAG_INTROUSER); - } - - if (confflags && !cnf->chan && - ast_test_flag(confflags, CONFFLAG_RECORDCONF)) { - ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name); - 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_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_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) { - if (dynamic) { - /* No need to parse meetme.conf */ - ast_log(LOG_DEBUG, "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); - } else { - cnf = build_conf(confno, "", "", make, dynamic, refcount); - } - } else { - /* Check the config */ - cfg = ast_config_load(CONFIG_FILE_NAME); - 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_NONSTANDARD_APP_ARGS(args, parse, ','); - 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); - break; - } - } - if (!var) { - ast_log(LOG_DEBUG, "%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 %s channel available for conference, user introduction disabled\n", dahdi_chan_name); - ast_clear_flag(confflags, CONFFLAG_INTROUSER); - } - - if (confflags && !cnf->chan && - ast_test_flag(confflags, CONFFLAG_RECORDCONF)) { - ast_log(LOG_WARNING, "No %s channel available for conference, conference recording disabled\n", dahdi_chan_name); - ast_clear_flag(confflags, CONFFLAG_RECORDCONF); - } - } - - return cnf; -} - -/*! \brief The MeetmeCount application */ -static int count_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - 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; - } - - u = ast_module_user_add(chan); - - if (!(localdata = ast_strdupa(data))) { - ast_module_user_remove(u); - 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 */ - } - ast_module_user_remove(u); - - return res; -} - -/*! \brief The meetme() application */ -static int conf_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - struct ast_module_user *u; - char confno[MAX_CONFNUM] = ""; - int allowretry = 0; - int retrycnt = 0; - struct ast_conference *cnf = NULL; - struct ast_flags confflags = {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, }; - - u = ast_module_user_add(chan); - - 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) && ast_strlen_zero(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); - 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) { - 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) { - cnf = find_conf_realtime(chan, confno, 1, dynamic, - the_pin, sizeof(the_pin), 1, &confflags); - } - - if (!cnf) { - res = ast_streamfile(chan, "conf-invalid", chan->language); - if (!res) - ast_waitstream(chan, ""); - res = -1; - if (allowretry) - confno[0] = '\0'; - } 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); - - ast_module_user_remove(u); - - 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; - struct ast_module_user *u; - 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; - } - - u = ast_module_user_add(chan); - - AST_LIST_LOCK(&confs); - - params = ast_strdupa(data); - AST_STANDARD_APP_ARGS(args, params); - - if (!args.command) { - ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n"); - AST_LIST_UNLOCK(&confs); - ast_module_user_remove(u); - return -1; - } - 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); - ast_module_user_remove(u); - 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); - } 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); - 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); - - ast_module_user_remove(u); - - 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); /* 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 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, 0644); - 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;xtransframe[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 int 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; - const char *val; - - audio_buffers = DEFAULT_AUDIO_BUFFERS; - - if (!(cfg = ast_config_load(CONFIG_FILE_NAME))) - return; - - 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 < DAHDI_DEFAULT_NUM_BUFS) || (audio_buffers > DAHDI_MAX_NUM_BUFS)) { - ast_log(LOG_WARNING, "audiobuffers setting must be between %d and %d\n", - DAHDI_DEFAULT_NUM_BUFS, DAHDI_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); - } - - 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 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_device_state_changed("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 answer_trunk_chan(struct ast_channel *chan) -{ - ast_answer(chan); - ast_indicate(chan, -1); -} - -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); - answer_trunk_chan(trunk_ref->chan); - conf = build_conf(conf_name, "", "", 0, 0, 1); - 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))) - free(station_ref); - - 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: - 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(&sla.ringing_trunks, 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_attr_t attr; - 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(&sla.ringing_stations, entry); - sla_stop_ringing_station(ringing_station, SLA_STATION_HANGUP_NORMAL); - break; - case AST_DIAL_RESULT_ANSWERED: - AST_LIST_REMOVE_CURRENT(&sla.ringing_stations, 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_log(LOG_DEBUG, "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 */ - answer_trunk_chan(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; - free(ringing_trunk); - free(ringing_station); - ast_mutex_init(&cond_lock); - ast_cond_init(&cond, NULL); - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - ast_mutex_lock(&cond_lock); - ast_pthread_create_background(&dont_care, &attr, run_station, &args); - ast_cond_wait(&cond, &cond_lock); - ast_mutex_unlock(&cond_lock); - ast_mutex_destroy(&cond_lock); - ast_cond_destroy(&cond); - pthread_attr_destroy(&attr); - 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(&sla.failed_stations, entry); - 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); - 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); - 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 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(&sla.ringing_stations, entry); - ast_dial_join(ringing_station->station->dial); - ast_dial_destroy(ringing_station->station->dial); - ringing_station->station->dial = NULL; - 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_device_state_changed("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(&sla.ringing_trunks, 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(&sla.ringing_stations, 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 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; - } - free(event); - ast_mutex_lock(&sla.lock); - } - } - - ast_mutex_unlock(&sla.lock); - - while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) - free(ringing_station); - - while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) - 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); - 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); - 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); - - 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); - 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"); - 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"); - 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_device_state_changed("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(&sla.ringing_trunks, entry); - break; - } - } - AST_LIST_TRAVERSE_SAFE_END - ast_mutex_unlock(&sla.lock); - - if (ringing_trunk) { - answer_trunk_chan(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; - pthread_attr_t attr; - 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); - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - ast_mutex_lock(&cond_lock); - ast_pthread_create_background(&dont_care, &attr, dial_trunk, &args); - ast_cond_wait(&cond, &cond_lock); - ast_mutex_unlock(&cond_lock); - ast_mutex_destroy(&cond_lock); - ast_cond_destroy(&cond); - pthread_attr_destroy(&attr); - ast_autoservice_stop(chan); - if (!trunk_ref->trunk->chan) { - ast_log(LOG_DEBUG, "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; - 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); - 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"); - - 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; -} - -static int sla_trunk_exec(struct ast_channel *chan, void *data) -{ - const char *trunk_name = 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_RWLIST_RDLOCK(&sla_trunks); - trunk = sla_find_trunk(trunk_name); - AST_RWLIST_UNLOCK(&sla_trunks); - if (!trunk) { - ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", trunk_name); - pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE"); - return 0; - } - if (trunk->chan) { - ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n", - trunk_name); - pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE"); - return 0; - } - trunk->chan = chan; - - if (!(ringing_trunk = queue_ringing_trunk(trunk))) { - pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE"); - return 0; - } - - snprintf(conf_name, sizeof(conf_name), "SLA_%s", trunk_name); - conf = build_conf(conf_name, "", "", 1, 1, 1); - if (!conf) { - pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE"); - return 0; - } - ast_set_flag(&conf_flags, - CONFFLAG_QUIET | CONFFLAG_MARKEDEXIT | CONFFLAG_MARKEDUSER | CONFFLAG_PASS_DTMF | CONFFLAG_NO_AUDIO_UNTIL_UP); - ast_indicate(chan, AST_CONTROL_RINGING); - conf_run(chan, conf, conf_flags.flags, NULL); - dispose_conf(conf); - conf = NULL; - trunk->chan = NULL; - trunk->on_hold = 0; - - sla_change_trunk_state(trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL); - - 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(&sla.ringing_trunks, entry); - break; - } - } - AST_LIST_TRAVERSE_SAFE_END - ast_mutex_unlock(&sla.lock); - if (ringing_trunk) { - 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); - } - - return 0; -} - -static int sla_state(const char *data) -{ - char *buf, *station_name, *trunk_name; - struct sla_station *station; - struct sla_trunk_ref *trunk_ref; - int 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; - } - switch (trunk_ref->state) { - case SLA_TRUNK_STATE_IDLE: - res = AST_DEVICE_NOT_INUSE; - break; - case SLA_TRUNK_STATE_RINGING: - res = AST_DEVICE_RINGING; - break; - case SLA_TRUNK_STATE_UP: - res = AST_DEVICE_INUSE; - break; - case SLA_TRUNK_STATE_ONHOLD: - case SLA_TRUNK_STATE_ONHOLD_BYME: - res = AST_DEVICE_ONHOLD; - break; - } - 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))) - free(station_ref); - - ast_string_field_free_memory(trunk); - 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))) - free(trunk_ref); - - ast_string_field_free_memory(station); - 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)) { - 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, 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))) { - 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)) { - 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, 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, 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(void) -{ - struct ast_config *cfg; - const char *cat = NULL; - int res = 0; - const char *val; - - ast_mutex_init(&sla.lock); - ast_cond_init(&sla.cond, NULL); - - if (!(cfg = ast_config_load(SLA_CONFIG_FILE))) - return 0; /* Treat no config as normal */ - - 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 (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_stations)) - ast_pthread_create(&sla.thread, NULL, sla_thread, NULL); - - return res; -} - -static int load_config(int reload) -{ - int res = 0; - - load_config_meetme(); - if (!reload) - res = sla_load_config(); - - return res; -} - -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_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"); - - ast_module_user_hangup_all(); - - 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_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/apps/app_milliwatt.c b/apps/app_milliwatt.c deleted file mode 100644 index b63771c38..000000000 --- a/apps/app_milliwatt.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Digital Milliwatt Test - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - res_indications - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/utils.h" - -static char *app = "Milliwatt"; - -static char *synopsis = "Generate a Constant 1004Hz tone at 0dbm (mu-law)"; - -static char *descrip = -" Milliwatt([options]): Generate a Constant 1004Hz tone at 0dbm.\n" -"Previous versions of this application generated the tone at 1000Hz. If for\n" -"some reason you would prefer that behavior, supply the 'o' option to get the\n" -"old behavior.\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) -{ - 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 old_milliwatt_exec(struct ast_channel *chan) -{ - 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 milliwatt_exec(struct ast_channel *chan, void *data) -{ - const char *options = data; - struct ast_app *playtones_app; - struct ast_module_user *u; - int res = -1; - - u = ast_module_user_add(chan); - - if (!ast_strlen_zero(options) && strchr(options, 'o')) { - res = old_milliwatt_exec(chan); - goto exit_app; - } - - if (!(playtones_app = pbx_findapp("Playtones"))) { - ast_log(LOG_ERROR, "The Playtones application is required to run Milliwatt()\n"); - goto exit_app; - } - - res = pbx_exec(chan, playtones_app, "1004/1000"); - - while (!res) { - res = ast_safe_sleep(chan, 10000); - } - - res = 0; - -exit_app: - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, milliwatt_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Digital Milliwatt (mu-law) Test Application"); diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c deleted file mode 100644 index 3cf79b1fc..000000000 --- a/apps/app_mixmonitor.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2005, Anthony Minessale II - * Copyright (C) 2005 - 2006, Digium, Inc. - * - * Mark Spencer - * Kevin P. Fleming - * - * Based on app_muxmon.c provided by - * 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 MixMonitor() - Record a call and mix the audio during the recording - * \ingroup applications - * - * \author Mark Spencer - * \author Kevin P. Fleming - * - * \note Based on app_muxmon.c provided by - * Anthony Minessale II - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/audiohook.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/lock.h" -#include "asterisk/cli.h" -#include "asterisk/options.h" -#include "asterisk/app.h" -#include "asterisk/linkedlists.h" -#include "asterisk/utils.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(.[|[|]])\n\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() - Adjust the heard volume by a factor of (range -4 to 4)\n" -" V() - Adjust the spoken volume by a factor of (range -4 to 4)\n" -" W() - Adjust the both heard and spoken volumes by a factor of \n" -" (range -4 to 4)\n\n" -" 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\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; - int res; - - if (!chan) - return -1; - - 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; -} - -#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; - - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_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 the 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); - - if (option_verbose > 1) - ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); - - if (fs) - ast_closestream(fs); - - if (mixmonitor->post_process) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); - ast_safe_system(mixmonitor->post_process); - } - - 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_attr_t attr; - pthread_t thread; - struct mixmonitor *mixmonitor; - char postprocess2[1024] = ""; - size_t len; - - len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2; - - /* 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 = 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)) { - free(mixmonitor); - return; - } - - ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC); - - 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); - /* Since we couldn't add ourselves - bail out! */ - ast_audiohook_destroy(&mixmonitor->audiohook); - free(mixmonitor); - return; - } - - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor); - pthread_attr_destroy(&attr); - -} - -static int mixmonitor_exec(struct ast_channel *chan, void *data) -{ - int x, readvol = 0, writevol = 0; - struct ast_module_user *u; - struct ast_flags flags = {0}; - char *parse; - 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; - } - - u = ast_module_user_add(chan); - - 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"); - ast_module_user_remove(u); - 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; - } - - pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); - launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process); - - ast_module_user_remove(u); - - return 0; -} - -static int stop_mixmonitor_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - ast_audiohook_detach_source(chan, mixmonitor_spy_type); - - ast_module_user_remove(u); - - return 0; -} - -static int mixmonitor_cli(int fd, int argc, char **argv) -{ - struct ast_channel *chan; - - if (argc < 3) - return RESULT_SHOWUSAGE; - - if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) { - ast_cli(fd, "No channel matching '%s' found.\n", argv[2]); - return RESULT_SUCCESS; - } - - if (!strcasecmp(argv[1], "start")) - mixmonitor_exec(chan, argv[3]); - else if (!strcasecmp(argv[1], "stop")) - ast_audiohook_detach_source(chan, mixmonitor_spy_type); - - ast_channel_unlock(chan); - - return RESULT_SUCCESS; -} - -static char *complete_mixmonitor_cli(const char *line, const char *word, int pos, int state) -{ - return ast_complete_channels(line, word, pos, state, 2); -} - -static struct ast_cli_entry cli_mixmonitor[] = { - { { "mixmonitor", NULL, NULL }, - mixmonitor_cli, "Execute a MixMonitor command.", - "mixmonitor [args]\n\n" - "The optional arguments are passed to the\n" - "MixMonitor application when the 'start' command is used.\n", - complete_mixmonitor_cli }, -}; - -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); - - ast_module_user_hangup_all(); - - 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/apps/app_morsecode.c b/apps/app_morsecode.c deleted file mode 100644 index aec946a09..000000000 --- a/apps/app_morsecode.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (c) 2006, Tilghman Lesher. All rights reserved. - * - * Tilghman Lesher - * - * 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 - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/options.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 = -"Usage: Morsecode()\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 - */ - ".-.-.-", /* 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 - (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; - struct ast_module_user *u; - - u = ast_module_user_add(chan); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "Syntax: Morsecode() - no argument found\n"); - ast_module_user_remove(u); - 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); - } - - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app_morsecode); - - ast_module_user_hangup_all(); - - return res; -} - -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/apps/app_mp3.c b/apps/app_mp3.c deleted file mode 100644 index 55d50f011..000000000 --- a/apps/app_mp3.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Silly application to play an MP3 file -- uses mpg123 - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/frame.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/options.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; - struct ast_module_user *u; - 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; - } - - u = ast_module_user_add(chan); - - if (pipe(fds)) { - ast_log(LOG_WARNING, "Unable to create pipe\n"); - ast_module_user_remove(u); - 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"); - ast_module_user_remove(u); - 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Hangup detected\n"); - res = -1; - break; - } - if (ms) { - f = ast_read(chan); - if (!f) { - ast_log(LOG_DEBUG, "Null frame == hangup() detected\n"); - res = -1; - break; - } - if (f->frametype == AST_FRAME_DTMF) { - ast_log(LOG_DEBUG, "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); - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, mp3_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly MP3 Application"); diff --git a/apps/app_nbscat.c b/apps/app_nbscat.c deleted file mode 100644 index 5f3000404..000000000 --- a/apps/app_nbscat.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Silly application to play an NBScat file -- uses nbscat8k - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/frame.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/options.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; - struct ast_module_user *u; - 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; - - u = ast_module_user_add(chan); - - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds)) { - ast_log(LOG_WARNING, "Unable to create socketpair\n"); - ast_module_user_remove(u); - 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"); - ast_module_user_remove(u); - 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "Hangup detected\n"); - res = -1; - break; - } - if (ms) { - f = ast_read(chan); - if (!f) { - ast_log(LOG_DEBUG, "Null frame == hangup() detected\n"); - res = -1; - break; - } - if (f->frametype == AST_FRAME_DTMF) { - ast_log(LOG_DEBUG, "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); - - ast_module_user_remove(u); - - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application(app, NBScat_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Silly NBS Stream Application"); diff --git a/apps/app_osplookup.c b/apps/app_osplookup.c deleted file mode 100644 index ad2ce5065..000000000 --- a/apps/app_osplookup.c +++ /dev/null @@ -1,1677 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * 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 Open Settlement Protocol (OSP) Applications - * - * \author Mark Spencer - * - * \ingroup applications - */ - -/*** MODULEINFO - osptk - ssl - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include - -#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/options.h" -#include "asterisk/cli.h" -#include "asterisk/logger.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 */ - -/* 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 */ - -/* 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 */ -}; - -/* 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 */ - OSPTPROVHANDLE handle; /* OSP provider handle */ - struct osp_provider* next; /* Pointer to next OSP provider */ -}; - -/* 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[20]; /* Asterisk TECH string */ - char dest[OSP_NORSTR_SIZE]; /* Destination in called@IP format */ - char calling[OSP_NORSTR_SIZE]; /* Calling number, may be translated */ - char token[OSP_TOKSTR_SIZE]; /* Outbound OSP token */ - unsigned int numresults; /* Number of remain destinations */ -}; - -/* 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->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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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); - } - } - 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); - 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) { - free(privatekey.PrivateKeyData); - } - 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_log(LOG_DEBUG, "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) { - free(cacerts[j].CertData); - } - } - if (localcert.CertData) { - free(localcert.CertData); - } - if (privatekey.PrivateKeyData) { - free(privatekey.PrivateKeyData); - } - 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); - free(p); - res = -1; - } else { - ast_log(LOG_DEBUG, "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) { - free(cacerts[i].CertData); - } - } - if (localcert.CertData) { - free(localcert.CertData); - } - if (privatekey.PrivateKeyData) { - free(privatekey.PrivateKeyData); - } - - return res; -} - -/*! - * \brief Get OSP authenticiation policy of provider - * \param provider OSP provider context name - * \param policy OSP authentication policy, output - * \return 1 Success, 0 Failed, -1 Error - */ -static int osp_get_policy(const char* provider, int* policy) -{ - int res = 0; - struct osp_provider* p; - - ast_mutex_lock(&osplock); - p = ospproviders; - while(p) { - if (!strcasecmp(p->name, provider)) { - *policy = p->authpolicy; - ast_log(LOG_DEBUG, "OSP: authpolicy '%d'\n", *policy); - 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_log(LOG_DEBUG, "OSP: transaction '%d'\n", *transaction); - ast_copy_string(source, p->source, sourcesize); - ast_log(LOG_DEBUG, "OSP: source '%s'\n", source); - res = 1; - } else { - *transaction = OSP_INVALID_HANDLE; - ast_log(LOG_DEBUG, "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 dest 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* dest, 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(dest, 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_log(LOG_DEBUG, "OSP: Unable to validate inbound token\n"); - res = -1; - } else if (authorised) { - ast_log(LOG_DEBUG, "OSP: Authorised\n"); - res = 1; - } else { - ast_log(LOG_DEBUG, "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 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(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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 = OSPPTransactionGetDestProtocol(result->outhandle, &protocol)) != OSPC_ERR_NO_ERROR) { - ast_log(LOG_DEBUG, "OSP: Unable to get destination protocol, error '%d'\n", error); - *reason = OSPC_FAIL_NORMAL_UNSPECIFIED; - result->token[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_log(LOG_DEBUG, "OSP: protocol '%d'\n", protocol); - ast_copy_string(result->tech, "H323", sizeof(result->tech)); - snprintf(result->dest, sizeof(result->dest), "%s@%s", called, destination + 1); - ast_copy_string(result->calling, calling, sizeof(result->calling)); - break; - case OSPE_DEST_PROT_SIP: - ast_log(LOG_DEBUG, "OSP: protocol '%d'\n", protocol); - ast_copy_string(result->tech, "SIP", sizeof(result->tech)); - snprintf(result->dest, sizeof(result->dest), "%s@%s", called, destination + 1); - ast_copy_string(result->calling, calling, sizeof(result->calling)); - break; - case OSPE_DEST_PROT_IAX: - ast_log(LOG_DEBUG, "OSP: protocol '%d'\n", protocol); - ast_copy_string(result->tech, "IAX", sizeof(result->tech)); - snprintf(result->dest, sizeof(result->dest), "%s@%s", called, destination + 1); - ast_copy_string(result->calling, calling, sizeof(result->calling)); - break; - default: - ast_log(LOG_DEBUG, "OSP: Unknown protocol '%d'\n", protocol); - *reason = OSPC_FAIL_PROTOCOL_ERROR; - result->token[0] = '\0'; - res = 0; - } - - 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; - int policy = OSP_AUTH_YES; - char dest[OSP_NORSTR_SIZE]; - - *transaction = OSP_INVALID_HANDLE; - *timelimit = OSP_DEF_TIMELIMIT; - res = osp_get_policy(provider, &policy); - if (!res) { - ast_log(LOG_DEBUG, "OSP: Unabe to find OSP authentication policy\n"); - return res; - } - - switch (policy) { - 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 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 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, struct osp_result* result) -{ - int res; - char source[OSP_NORSTR_SIZE]; - unsigned int callidlen; - char callid[OSPC_CALLID_MAXSIZE]; - 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 dummy = 0; - enum OSPEFAILREASON reason; - int error; - - result->outhandle = OSP_INVALID_HANDLE; - result->tech[0] = '\0'; - result->dest[0] = '\0'; - result->calling[0] = '\0'; - result->token[0] = '\0'; - result->numresults = 0; - result->outtimelimit = OSP_DEF_TIMELIMIT; - - if ((res = osp_create_transaction(provider, &result->outhandle, sizeof(source), source)) <= 0) { - ast_log(LOG_DEBUG, "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; - } - - 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, 0, NULL, NULL, &result->numresults, &dummy, NULL); - if (error != OSPC_ERR_NO_ERROR) { - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "OSP: No more destination\n"); - if (result->inhandle != OSP_INVALID_HANDLE) { - OSPPTransactionRecordFailure(result->inhandle, OSPC_FAIL_NO_ROUTE_TO_DEST); - } - return 0; - } - - callidlen = sizeof(callid); - tokenlen = sizeof(token); - error = OSPPTransactionGetFirstDestination(result->outhandle, 0, NULL, NULL, &result->outtimelimit, &callidlen, callid, - sizeof(callednum), callednum, sizeof(callingnum), callingnum, sizeof(destination), destination, 0, NULL, &tokenlen, token); - if (error != OSPC_ERR_NO_ERROR) { - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "OSP: outtimelimit '%d'\n", result->outtimelimit); - ast_log(LOG_DEBUG, "OSP: called '%s'\n", callednum); - ast_log(LOG_DEBUG, "OSP: calling '%s'\n", callingnum); - ast_log(LOG_DEBUG, "OSP: destination '%s'\n", destination); - ast_log(LOG_DEBUG, "OSP: token size '%d'\n", tokenlen); - - if ((res = osp_check_destination(callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) { - return 1; - } - - if (!result->numresults) { - ast_log(LOG_DEBUG, "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) { - callidlen = sizeof(callid); - tokenlen = sizeof(token); - error = OSPPTransactionGetNextDestination(result->outhandle, reason, 0, NULL, NULL, &result->outtimelimit, &callidlen, callid, - 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_log(LOG_DEBUG, "OSP: outtimelimit '%d'\n", result->outtimelimit); - ast_log(LOG_DEBUG, "OSP: called '%s'\n", callednum); - ast_log(LOG_DEBUG, "OSP: calling '%s'\n", callingnum); - ast_log(LOG_DEBUG, "OSP: destination '%s'\n", destination); - ast_log(LOG_DEBUG, "OSP: token size '%d'\n", tokenlen); - if ((res = osp_check_destination(callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) { - break; - } else if (!result->numresults) { - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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 cause Asterisk hangup cuase - * \param result Lookup results, in/output - * \return 1 Found , 0 No route, -1 Error - */ -static int osp_next(int cause, struct osp_result* result) -{ - int res; - unsigned int callidlen; - char callid[OSPC_CALLID_MAXSIZE]; - 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->calling[0] = '\0'; - result->token[0] = '\0'; - result->outtimelimit = OSP_DEF_TIMELIMIT; - - if (result->outhandle == OSP_INVALID_HANDLE) { - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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) { - callidlen = sizeof(callid); - tokenlen = sizeof(token); - error = OSPPTransactionGetNextDestination(result->outhandle, reason, 0, NULL, NULL, &result->outtimelimit, &callidlen, - callid, 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_log(LOG_DEBUG, "OSP: outtimelimit '%d'\n", result->outtimelimit); - ast_log(LOG_DEBUG, "OSP: called '%s'\n", callednum); - ast_log(LOG_DEBUG, "OSP: calling '%s'\n", callingnum); - ast_log(LOG_DEBUG, "OSP: destination '%s'\n", destination); - ast_log(LOG_DEBUG, "OSP: token size '%d'\n", tokenlen); - if ((res = osp_check_destination(callednum, callingnum, destination, tokenlen, token, &reason, result)) > 0) { - res = 1; - break; - } else if (!result->numresults) { - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "OSP: Usage reported\n"); - res = 1; - } else { - ast_log(LOG_DEBUG, "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; - struct ast_module_user *u; - const char* provider = OSP_DEF_PROVIDER; - int priority_jump = 0; - 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); - ); - - u = ast_module_user_add(chan); - - if (!(tmp = ast_strdupa(data))) { - ast_log(LOG_ERROR, "Out of memory\n"); - ast_module_user_remove(u); - return -1; - } - - AST_STANDARD_APP_ARGS(args, tmp); - - if (!ast_strlen_zero(args.provider)) { - provider = args.provider; - } - ast_log(LOG_DEBUG, "OSPAuth: provider '%s'\n", provider); - - if ((args.options) && (strchr(args.options, 'j'))) { - priority_jump = 1; - } - ast_log(LOG_DEBUG, "OSPAuth: priority jump '%d'\n", priority_jump); - - 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_log(LOG_DEBUG, "OSPAuth: source '%s'\n", source); - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "OSPAuth: OSPINHANDLE '%s'\n", buffer); - snprintf(buffer, sizeof(buffer), "%d", timelimit); - pbx_builtin_setvar_helper(chan, "OSPINTIMELIMIT", buffer); - ast_log(LOG_DEBUG, "OSPAuth: OSPINTIMELIMIT '%s'\n", buffer); - pbx_builtin_setvar_helper(chan, "OSPAUTHSTATUS", status); - ast_log(LOG_DEBUG, "OSPAuth: %s\n", status); - - if(res <= 0) { - if (priority_jump || ast_opt_priority_jumping) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - res = 0; - } else { - res = -1; - } - } else { - res = 0; - } - - ast_module_user_remove(u); - - 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; - struct ast_module_user *u; - const char *provider = OSP_DEF_PROVIDER; - int priority_jump = 0; - struct varshead *headp; - struct ast_var_t* current; - const char *srcdev = ""; - char buffer[OSP_TOKSTR_SIZE]; - 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; - } - - u = ast_module_user_add(chan); - - if (!(tmp = ast_strdupa(data))) { - ast_log(LOG_ERROR, "Out of memory\n"); - ast_module_user_remove(u); - return -1; - } - - AST_STANDARD_APP_ARGS(args, tmp); - - ast_log(LOG_DEBUG, "OSPLookup: exten '%s'\n", args.exten); - - if (!ast_strlen_zero(args.provider)) { - provider = args.provider; - } - ast_log(LOG_DEBUG, "OSPlookup: provider '%s'\n", provider); - - if ((args.options) && (strchr(args.options, 'j'))) { - priority_jump = 1; - } - ast_log(LOG_DEBUG, "OSPLookup: priority jump '%d'\n", priority_jump); - - 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), "OSPPEERIP")) { - srcdev = ast_var_value(current); - } - } - ast_log(LOG_DEBUG, "OSPLookup: OSPINHANDLE '%d'\n", result.inhandle); - ast_log(LOG_DEBUG, "OSPLookup: OSPINTIMELIMIT '%d'\n", result.intimelimit); - ast_log(LOG_DEBUG, "OSPLookup: source device '%s'\n", srcdev); - - if ((cres = ast_autoservice_start(chan)) < 0) { - ast_module_user_remove(u); - return -1; - } - - if ((res = osp_lookup(provider, srcdev, chan->cid.cid_num, args.exten, &result)) > 0) { - status = AST_OSP_SUCCESS; - } else { - result.tech[0] = '\0'; - result.dest[0] = '\0'; - result.calling[0] = '\0'; - result.token[0] = '\0'; - result.numresults = 0; - result.outtimelimit = OSP_DEF_TIMELIMIT; - 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_log(LOG_DEBUG, "OSPLookup: OSPOUTHANDLE '%s'\n", buffer); - pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech); - ast_log(LOG_DEBUG, "OSPLookup: OSPTECH '%s'\n", result.tech); - pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest); - ast_log(LOG_DEBUG, "OSPLookup: OSPDEST '%s'\n", result.dest); - pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling); - ast_log(LOG_DEBUG, "OSPLookup: OSPCALLING '%s'\n", result.calling); - pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token); - ast_log(LOG_DEBUG, "OSPLookup: OSPOUTTOKEN size '%zd'\n", strlen(result.token)); - snprintf(buffer, sizeof(buffer), "%d", result.numresults); - pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer); - ast_log(LOG_DEBUG, "OSPLookup: OSPRESULTS '%s'\n", buffer); - snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit); - pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer); - ast_log(LOG_DEBUG, "OSPLookup: OSPOUTTIMELIMIT '%s'\n", buffer); - pbx_builtin_setvar_helper(chan, "OSPLOOKUPSTATUS", status); - ast_log(LOG_DEBUG, "OSPLookup: %s\n", status); - - if (!strcasecmp(result.tech, "SIP")) { - if (!ast_strlen_zero(result.token)) { - snprintf(buffer, sizeof(buffer), "P-OSP-Auth-Token: %s", result.token); - pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer); - ast_log(LOG_DEBUG, "OSPLookup: SIPADDHEADER size '%zd'\n", strlen(buffer)); - } - } else if (!strcasecmp(result.tech, "H323")) { - } else if (!strcasecmp(result.tech, "IAX")) { - } - - if ((cres = ast_autoservice_stop(chan)) < 0) { - ast_module_user_remove(u); - return -1; - } - - if(res <= 0) { - if (priority_jump || ast_opt_priority_jumping) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - res = 0; - } else { - res = -1; - } - } else { - res = 0; - } - - ast_module_user_remove(u); - - 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; - struct ast_module_user *u; - int priority_jump = 0; - int cause = 0; - struct varshead* headp; - struct ast_var_t* current; - struct osp_result result; - char buffer[OSP_TOKSTR_SIZE]; - const char* status; - char* tmp; - - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(cause); - AST_APP_ARG(options); - ); - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "OSPNext: Arg required, OSPNext(cause[|options])\n"); - return -1; - } - - u = ast_module_user_add(chan); - - if (!(tmp = ast_strdupa(data))) { - ast_log(LOG_ERROR, "Out of memory\n"); - ast_module_user_remove(u); - return -1; - } - - AST_STANDARD_APP_ARGS(args, tmp); - - if (!ast_strlen_zero(args.cause) && sscanf(args.cause, "%d", &cause) != 1) { - cause = 0; - } - ast_log(LOG_DEBUG, "OSPNext: cause '%d'\n", cause); - - if ((args.options) && (strchr(args.options, 'j'))) { - priority_jump = 1; - } - ast_log(LOG_DEBUG, "OSPNext: priority jump '%d'\n", priority_jump); - - 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), "OSPRESULTS")) { - if (sscanf(ast_var_value(current), "%d", &result.numresults) != 1) { - result.numresults = 0; - } - } - } - ast_log(LOG_DEBUG, "OSPNext: OSPINHANDLE '%d'\n", result.inhandle); - ast_log(LOG_DEBUG, "OSPNext: OSPOUTHANDLE '%d'\n", result.outhandle); - ast_log(LOG_DEBUG, "OSPNext: OSPINTIMELIMIT '%d'\n", result.intimelimit); - ast_log(LOG_DEBUG, "OSPNext: OSPRESULTS '%d'\n", result.numresults); - - if ((res = osp_next(cause, &result)) > 0) { - status = AST_OSP_SUCCESS; - } else { - result.tech[0] = '\0'; - result.dest[0] = '\0'; - result.calling[0] = '\0'; - result.token[0] = '\0'; - result.numresults = 0; - result.outtimelimit = OSP_DEF_TIMELIMIT; - if (!res) { - status = AST_OSP_FAILED; - } else { - status = AST_OSP_ERROR; - } - } - - pbx_builtin_setvar_helper(chan, "OSPTECH", result.tech); - ast_log(LOG_DEBUG, "OSPNext: OSPTECH '%s'\n", result.tech); - pbx_builtin_setvar_helper(chan, "OSPDEST", result.dest); - ast_log(LOG_DEBUG, "OSPNext: OSPDEST '%s'\n", result.dest); - pbx_builtin_setvar_helper(chan, "OSPCALLING", result.calling); - ast_log(LOG_DEBUG, "OSPNext: OSPCALLING '%s'\n", result.calling); - pbx_builtin_setvar_helper(chan, "OSPOUTTOKEN", result.token); - ast_log(LOG_DEBUG, "OSPNext: OSPOUTTOKEN size '%zd'\n", strlen(result.token)); - snprintf(buffer, sizeof(buffer), "%d", result.numresults); - pbx_builtin_setvar_helper(chan, "OSPRESULTS", buffer); - ast_log(LOG_DEBUG, "OSPNext: OSPRESULTS '%s'\n", buffer); - snprintf(buffer, sizeof(buffer), "%d", result.outtimelimit); - pbx_builtin_setvar_helper(chan, "OSPOUTTIMELIMIT", buffer); - ast_log(LOG_DEBUG, "OSPNext: OSPOUTTIMELIMIT '%s'\n", buffer); - pbx_builtin_setvar_helper(chan, "OSPNEXTSTATUS", status); - ast_log(LOG_DEBUG, "OSPNext: %s\n", status); - - if (!strcasecmp(result.tech, "SIP")) { - if (!ast_strlen_zero(result.token)) { - snprintf(buffer, sizeof(buffer), "P-OSP-Auth-Token: %s", result.token); - pbx_builtin_setvar_helper(chan, "_SIPADDHEADER", buffer); - ast_log(LOG_DEBUG, "OSPLookup: SIPADDHEADER size '%zd'\n", strlen(buffer)); - } - } else if (!strcasecmp(result.tech, "H323")) { - } else if (!strcasecmp(result.tech, "IAX")) { - } - - if(res <= 0) { - if (priority_jump || ast_opt_priority_jumping) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - res = 0; - } else { - res = -1; - } - } else { - res = 0; - } - - ast_module_user_remove(u); - - 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; - struct ast_module_user *u; - int priority_jump = 0; - 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); - ); - - u = ast_module_user_add(chan); - - if (!(tmp = ast_strdupa(data))) { - ast_log(LOG_ERROR, "Out of memory\n"); - ast_module_user_remove(u); - return -1; - } - - AST_STANDARD_APP_ARGS(args, tmp); - - if ((args.options) && (strchr(args.options, 'j'))) { - priority_jump = 1; - } - ast_log(LOG_DEBUG, "OSPFinish: priority jump '%d'\n", priority_jump); - - 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_log(LOG_DEBUG, "OSPFinish: OSPINHANDLE '%d'\n", inhandle); - ast_log(LOG_DEBUG, "OSPFinish: OSPOUTHANDLE '%d'\n", outhandle); - ast_log(LOG_DEBUG, "OSPFinish: recorded '%d'\n", recorded); - - if (!ast_strlen_zero(args.cause) && sscanf(args.cause, "%d", &cause) != 1) { - cause = 0; - } - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "OSPFinish: start '%ld'\n", start); - ast_log(LOG_DEBUG, "OSPFinish: connect '%ld'\n", connect); - ast_log(LOG_DEBUG, "OSPFinish: end '%ld'\n", end); - - release = chan->_softhangup ? 0 : 1; - - if (osp_finish(outhandle, recorded, cause, start, connect, end, release) <= 0) { - ast_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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) { - if (priority_jump || ast_opt_priority_jumping) { - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - res = 0; - } else { - res = -1; - } - } else { - res = 0; - } - - ast_module_user_remove(u); - - return res; -} - -/* OSP Module APIs */ - -static int osp_load(void) -{ - const char* t; - unsigned int v; - struct ast_config* cfg; - int error = OSPC_ERR_NO_ERROR; - - cfg = ast_config_load(OSP_CONFIG_FILE); - if (cfg) { - 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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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_log(LOG_DEBUG, "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); - 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 int osp_show(int fd, int argc, char* argv[]) -{ - int i; - int found = 0; - struct osp_provider* p; - const char* provider = NULL; - const char* tokenalgo; - - if ((argc < 2) || (argc > 3)) { - return RESULT_SHOWUSAGE; - } - if (argc > 2) { - provider = 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(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(fd, "\n"); - } - ast_cli(fd, " == OSP Provider '%s' == \n", p->name); - ast_cli(fd, "Local Private Key: %s\n", p->privatekey); - ast_cli(fd, "Local Certificate: %s\n", p->localcert); - for (i = 0; i < p->cacount; i++) { - ast_cli(fd, "CA Certificate %d: %s\n", i + 1, p->cacerts[i]); - } - for (i = 0; i < p->spcount; i++) { - ast_cli(fd, "Service Point %d: %s\n", i + 1, p->srvpoints[i]); - } - ast_cli(fd, "Max Connections: %d\n", p->maxconnections); - ast_cli(fd, "Retry Delay: %d seconds\n", p->retrydelay); - ast_cli(fd, "Retry Limit: %d\n", p->retrylimit); - ast_cli(fd, "Timeout: %d milliseconds\n", p->timeout); - ast_cli(fd, "Source: %s\n", strlen(p->source) ? p->source : ""); - ast_cli(fd, "Auth Policy %d\n", p->authpolicy); - ast_cli(fd, "OSP Handle: %d\n", p->handle); - found++; - } - p = p->next; - } - ast_mutex_unlock(&osplock); - - if (!found) { - if (provider) { - ast_cli(fd, "Unable to find OSP provider '%s'\n", provider); - } else { - ast_cli(fd, "No OSP providers configured\n"); - } - } - return RESULT_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" -"The option string may contain the following character:\n" -" 'j' -- jump to n+101 priority if the authentication was NOT successful\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" -" ${OSPCALLING}: The calling number to use for the call\n" -" ${OSPOUTTOKEN}: The actual OSP token as a string\n" -" ${OSPOUTTIMELIMIT}: The outbound call duration limit in seconds\n" -" ${OSPRESULTS}: The number of OSP results total remaining\n" -"\n" -"The option string may contain the following character:\n" -" 'j' -- jump to n+101 priority if the lookup was NOT successful\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[|options]): Looks up the next OSP Destination for ${OSPOUTHANDLE}\n" -"See OSPLookup for more information\n" -"\n" -"The option string may contain the following character:\n" -" 'j' -- jump to n+101 priority if the lookup was NOT successful\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" -"The option string may contain the following character:\n" -" 'j' -- jump to n+101 priority if the finish attempt was NOT successful\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 const char osp_usage[] = -"Usage: osp show\n" -" Displays information on Open Settlement Protocol support\n"; - -static struct ast_cli_entry cli_osp[] = { - { { "osp", "show", NULL}, - osp_show, "Displays OSP information", - osp_usage }, -}; - -static int load_module(void) -{ - int res; - - if(!osp_load()) - 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(); - - ast_module_user_hangup_all(); - - return res; -} - -static int reload(void) -{ - osp_unload(); - osp_load(); - - 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/apps/app_page.c b/apps/app_page.c deleted file mode 100644 index c94e1b11a..000000000 --- a/apps/app_page.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (c) 2004 - 2006 Digium, Inc. All rights reserved. - * - * Mark Spencer - * - * 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 - * - * \ingroup applications - */ - -/*** MODULEINFO - dahdi - app_meetme - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/options.h" -#include "asterisk/logger.h" -#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/dial.h" -#include "asterisk/devicestate.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"; - -enum { - PAGE_DUPLEX = (1 << 0), - PAGE_QUIET = (1 << 1), - PAGE_RECORD = (1 << 2), -} 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), -}); - - -static int page_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - char *options, *tech, *resource, *tmp, *tmp2; - char meetmeopts[88], originator[AST_CHANNEL_NAME]; - struct ast_flags flags = { 0 }; - unsigned int confid = ast_random(); - struct ast_app *app; - int res = 0, pos = 0, i = 0; - struct ast_dial **dial_list; - unsigned int num_dials; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n"); - return -1; - } - - u = ast_module_user_add(chan); - - if (!(app = pbx_findapp("MeetMe"))) { - ast_log(LOG_WARNING, "There is no MeetMe application available!\n"); - ast_module_user_remove(u); - 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, NULL, 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" : "") ); - - /* Count number of extensions in list by number of ampersands + 1 */ - num_dials = 1; - tmp2 = tmp; - while (*tmp2 && *tmp2++ == '&') { - num_dials++; - } - - if (!(dial_list = ast_calloc(num_dials, sizeof(void *)))) { - ast_log(LOG_ERROR, "Can't allocate %ld bytes for dial list\n", (long)(sizeof(void *) * num_dials)); - ast_module_user_remove(u); - return -1; - } - - /* Go through parsing/calling each device */ - while ((tech = strsep(&tmp, "&"))) { - 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; - } - - *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 */ - dial_list[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 = dial_list[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); - } - - ast_free(dial_list); - ast_module_user_remove(u); - - return -1; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app_page); - - ast_module_user_hangup_all(); - - return res; -} - -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/apps/app_parkandannounce.c b/apps/app_parkandannounce.c deleted file mode 100644 index 9e9f1604d..000000000 --- a/apps/app_parkandannounce.c +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * Mark Spencer - * - * Author: Ben Miller - * 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 - * \arg With TONS of help from Mark! - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/features.h" -#include "asterisk/options.h" -#include "asterisk/logger.h" -#include "asterisk/say.h" -#include "asterisk/lock.h" -#include "asterisk/utils.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 .\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) -{ - char *return_context; - int lot, timeout = 0, dres; - char *working, *context, *exten, *priority, *dial, *dialtech, *dialstr; - char *template, *tpl_working, *tpl_current; - char *tmp[100]; - char buf[13]; - int looptemp = 0,i = 0, res = 0; - char *s; - - struct ast_channel *dchan; - struct outgoing_helper oh; - int outstate; - - struct ast_module_user *u; - - if (ast_strlen_zero(data)) { - ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n"); - return -1; - } - - u = ast_module_user_add(chan); - - s = ast_strdupa(data); - - template = strsep(&s,"|"); - if(! template) { - ast_log(LOG_WARNING, "PARK: An announce template must be defined\n"); - ast_module_user_remove(u); - return -1; - } - - if(s) { - timeout = atoi(strsep(&s, "|")); - timeout *= 1000; - } - dial = strsep(&s, "|"); - if(!dial) { - ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n"); - ast_module_user_remove(u); - return -1; - } else { - dialtech = strsep(&dial, "/"); - dialstr = dial; - ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr); - } - - return_context = s; - - if(return_context != NULL) { - /* set the return context. Code borrowed from the Goto builtin */ - - working = return_context; - context = strsep(&working, "|"); - exten = strsep(&working, "|"); - if(!exten) { - /* Only a priority in this one */ - priority = context; - exten = NULL; - context = NULL; - } else { - priority = strsep(&working, "|"); - if(!priority) { - /* Only an extension and priority in this one */ - priority = exten; - exten = context; - context = NULL; - } - } - if(atoi(priority) < 0) { - ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority); - ast_module_user_remove(u); - return -1; - } - /* At this point we have a priority and maybe an extension and a context */ - chan->priority = atoi(priority); - if (exten) - ast_copy_string(chan->exten, exten, sizeof(chan->exten)); - if (context) - ast_copy_string(chan->context, context, sizeof(chan->context)); - } else { /* increment the priority by default*/ - chan->priority++; - } - - if(option_verbose > 2) { - ast_verbose( VERBOSE_PREFIX_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_verbose( VERBOSE_PREFIX_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. */ - - res = ast_masq_park_call(chan, NULL, timeout, &lot); - if (res == -1) { - goto finish; - } - - ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context); - - /* Now place the call to the extention */ - - snprintf(buf, sizeof(buf), "%d", lot); - memset(&oh, 0, sizeof(oh)); - oh.parent_channel = chan; - oh.vars = ast_variable_new("_PARKEDAT", buf); - dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh); - - if(dchan) { - if(dchan->_state == AST_STATE_UP) { - if(option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", dchan->name); - } else { - if(option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_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); - ast_module_user_remove(u); - return -1; - } - } else { - ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n"); - ast_module_user_remove(u); - return -1; - } - - ast_stopstream(dchan); - - /* now we have the call placed and are ready to play stuff to it */ - - ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template); - - tpl_working = template; - tpl_current = strsep(&tpl_working, ":"); - - while(tpl_current && looptemp < ARRAY_LEN(tmp)) { - tmp[looptemp]=tpl_current; - looptemp++; - tpl_current = strsep(&tpl_working,":"); - } - - for(i = 0; i < looptemp; i++) { - ast_verbose(VERBOSE_PREFIX_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); - -finish: - ast_module_user_remove(u); - return res; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application(app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - /* return ast_register_application(app, 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/apps/app_playback.c b/apps/app_playback.c deleted file mode 100644 index 8f78bef78..000000000 --- a/apps/app_playback.c +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Trivial application to playback a sound file - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" -#include "asterisk/translate.h" -#include "asterisk/utils.h" -#include "asterisk/options.h" -#include "asterisk/app.h" -#include "asterisk/cli.h" -#include "asterisk/localtime.h" -#include "asterisk/say.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 pipe symbol. The 'skip'\n" -"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. If 'j' is specified, the application\n" -"will jump to priority n+101 if present when a file specified to be played\n" -"does not exist.\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" -"See Also: Background (application) -- for playing soundfiles that are interruptible\n" -" WaitExten (application) -- wait for digits from caller, optionally play music on hold\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; - - 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 */ - 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; - 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 */ - memset(fn, 0, sizeof(fn)); /* XXX why isn't done in pbx_substitute_variables_helper! */ - pbx_substitute_variables_varshead(&head, x, fn, sizeof(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 tm tm; - say_args_t a = { chan, ints, lang, -1, -1 }; - if (format == NULL) - format = ""; - - ast_localtime(&t, &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(int fd, int argc, char *argv[]) -{ - const char *old_mode = say_api_buf[0] ? say_new : say_old; - char *mode; - - if (argc == 2) { - ast_cli(fd, "say mode is [%s]\n", old_mode); - return RESULT_SUCCESS; - } else if (argc != 3) - return RESULT_SHOWUSAGE; - mode = argv[2]; - - ast_log(LOG_WARNING, "init say.c from %s to %s\n", old_mode, mode); - - if (!strcmp(mode, old_mode)) { - ast_log(LOG_WARNING, "say mode is %s already\n", mode); - } else if (!strcmp(mode, say_new)) { - if (say_cfg == NULL) - say_cfg = ast_config_load("say.conf"); - 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 { - ast_log(LOG_WARNING, "unrecognized mode %s\n", mode); - } - return RESULT_SUCCESS; -} - -static struct ast_cli_entry cli_playback[] = { - { { "say", "load", NULL }, - __say_init, "set/show the say mode", - "Usage: say load [new|old]\n Set/show the say mode\n" }, -}; - -static int playback_exec(struct ast_channel *chan, void *data) -{ - int res = 0; - int mres = 0; - struct ast_module_user *u; - char *tmp; - int option_skip=0; - int option_say=0; - int option_noanswer = 0; - int priority_jump = 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); - - u = ast_module_user_add(chan); - 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 (strchr(args.options, 'j')) - priority_jump = 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); - if (priority_jump || ast_opt_priority_jumping) - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - res = 0; - mres = 1; - } - } - } -done: - pbx_builtin_setvar_helper(chan, "PLAYBACKSTATUS", mres ? "FAILED" : "SUCCESS"); - ast_module_user_remove(u); - return res; -} - -static int reload(void) -{ - if (say_cfg) { - ast_config_destroy(say_cfg); - ast_log(LOG_NOTICE, "Reloading say.conf\n"); - } - say_cfg = ast_config_load("say.conf"); - /* - * 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)); - - ast_module_user_hangup_all(); - - if (say_cfg) - ast_config_destroy(say_cfg); - - return res; -} - -static int load_module(void) -{ - say_cfg = ast_config_load("say.conf"); - 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/apps/app_privacy.c b/apps/app_privacy.c deleted file mode 100644 index 5da93eb40..000000000 --- a/apps/app_privacy.c +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2005, Digium, Inc. - * - * 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 Block all calls without Caller*ID, require phone # to be entered - * - * \author Mark Spencer - * - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/utils.h" -#include "asterisk/logger.h" -#include "asterisk/options.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" - -#define PRIV_CONFIG "privacy.conf" - -static char *app = "PrivacyManager"; - -static char *synopsis = "Require phone number to be entered, if no CallerID sent"; - -static char *descrip = - " PrivacyManager([maxretries[|minlength[|options]]]): 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 3 attempts to do so.\n" - "The application does nothing if Caller*ID was received on the channel.\n" - " Configuration file privacy.conf contains two variables:\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" - "If you don't want to use the config file and have an i/o operation with\n" - "every call, you can also specify maxretries and minlength as application\n" - "parameters. Doing so supercedes any values set in privacy.conf.\n" - "The option string may contain the following character: \n" - " 'j' -- jump to n+101 priority after failed attempts to collect\n" - " the minlength number of digits.\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; - const char *s; - char phone[30]; - struct ast_module_user *u; - struct ast_config *cfg = NULL; - char *parse = NULL; - int priority_jump = 0; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(maxretries); - AST_APP_ARG(minlength); - AST_APP_ARG(options); - ); - - u = ast_module_user_add(chan); - - 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) { - res = ast_answer(chan); - if (res) { - ast_module_user_remove(u); - 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"); - } - if (args.options) - if (strchr(args.options, 'j')) - priority_jump = 1; - - } - - if (!x) - { - /*Read in the config file*/ - cfg = ast_config_load(PRIV_CONFIG); - - if (cfg && (s = ast_variable_retrieve(cfg, "general", "maxretries"))) { - if (sscanf(s, "%d", &x) == 1) - maxretries = x; - else - ast_log(LOG_WARNING, "Invalid max retries argument\n"); - } - - if (cfg && (s = ast_variable_retrieve(cfg, "general", "minlength"))) { - if (sscanf(s, "%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 { - if (priority_jump || ast_opt_priority_jumping) - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - pbx_builtin_setvar_helper(chan, "PRIVACYMGRSTATUS", "FAILED"); - } - if (cfg) - ast_config_destroy(cfg); - } - - ast_module_user_remove(u); - - return 0; -} - -static int unload_module(void) -{ - int res; - - res = ast_unregister_application (app); - - ast_module_user_hangup_all(); - - return res; -} - -static int load_module(void) -{ - return ast_register_application (app, privacy_exec, synopsis, descrip); -} - -AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Require phone number to be entered, if no CallerID sent"); diff --git a/apps/app_queue.c b/apps/app_queue.c deleted file mode 100644 index 474ba9d88..000000000 --- a/apps/app_queue.c +++ /dev/null @@ -1,5140 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 1999 - 2006, Digium, Inc. - * - * 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 True call queues with optional send URL on answer - * - * \author Mark Spencer - * - * \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 - * - * 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 : - * - 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 - * Added Priority jumping code for adding and removing queue members by Jonathan Stanton - * - * Fixed to work with CVS as of 2004-02-25 and released as 1.07a - * by Matthew Enger - * - * \ingroup applications - */ - -/*** MODULEINFO - res_monitor - ***/ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "asterisk/lock.h" -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/options.h" -#include "asterisk/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/astobj2.h" -#include "asterisk/global_datastores.h" - -/* Please read before modifying this file. - * There are three locks which are regularly used - * throughout this file, the queue list lock, the lock - * for each individual queue, and the interface list lock. - * Please be extra careful to always lock in the following order - * 1) queue list lock - * 2) individual queue lock - * 3) interface list lock - * This order has sort of "evolved" over the lifetime of this - * application, but it is now in place this way, so please adhere - * to this order! - */ - - -enum { - QUEUE_STRATEGY_RINGALL = 0, - QUEUE_STRATEGY_ROUNDROBIN, - QUEUE_STRATEGY_LEASTRECENT, - QUEUE_STRATEGY_FEWESTCALLS, - QUEUE_STRATEGY_RANDOM, - QUEUE_STRATEGY_RRMEMORY -}; - -static struct strategy { - int strategy; - char *name; -} strategies[] = { - { QUEUE_STRATEGY_RINGALL, "ringall" }, - { QUEUE_STRATEGY_ROUNDROBIN, "roundrobin" }, - { QUEUE_STRATEGY_LEASTRECENT, "leastrecent" }, - { QUEUE_STRATEGY_FEWESTCALLS, "fewestcalls" }, - { QUEUE_STRATEGY_RANDOM, "random" }, - { QUEUE_STRATEGY_RRMEMORY, "rrmemory" }, -}; - -#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 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]):\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" -" 'd' -- data-quality (modem) call (minimum delay).\n" -" 'h' -- allow callee to hang up by hitting '*', or whatver disconnect sequence\n" -" defined in the featuremap section in features.conf.\n" -" 'H' -- allow caller to hang up by hitting '*', or whatever disconnect sequence\n" -" defined in the featuremap section in features.conf.\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\n" -" 't' -- allow the called user transfer the calling user by pressing '#' or\n" -" whatever blindxfer sequence defined in the featuremap section in\n" -" features.conf\n" -" 'T' -- to allow the calling user to transfer the call by pressing '#' or\n" -" whatever blindxfer sequence defined in the featuremap section in\n" -" features.conf\n" -" 'w' -- allow the called user to write the conversation to disk via Monitor\n" -" by pressing the automon sequence defined in the featuremap section in\n" -" features.conf\n" -" 'W' -- allow the calling user to write the conversation to disk via Monitor\n" -" by pressing the automon sequence defined in the featuremap section in\n" -" features.conf\n" -" In addition to transferring the call, a call may be parked and then picked\n" -"up by another user, by transferring to the parking lot extension. See features.conf.\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 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\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 and there exists an n+101 priority\n" -"then it will then jump to this priority. Otherwise it will return an error\n" -"The option string may contain zero or more of the following characters:\n" -" 'j' -- jump to +101 priority when appropriate.\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 and there exists an n+101 priority\n" -"then it will then jump to this priority. Otherwise it will return an error\n" -"The option string may contain zero or more of the following characters:\n" -" 'j' -- jump to +101 priority when appropriate.\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]):\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. If the interface is not in the named queue, or if no queue\n" -"is given and the interface is not in any queue, it will jump to\n" -"priority n+101, if it exists and the appropriate options are set.\n" -"The application will fail if the interface is not found and no extension\n" -"to jump to exists.\n" -"The option string may contain zero or more of the following characters:\n" -" 'j' -- jump to +101 priority when appropriate.\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]):\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 option string may contain zero or more of the following characters:\n" -" 'j' -- jump to +101 priority when appropriate.\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_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; - -enum queue_result { - QUEUE_UNKNOWN = 0, - QUEUE_TIMEOUT = 1, - QUEUE_JOINEMPTY = 2, - QUEUE_LEAVEEMPTY = 3, - QUEUE_JOINUNAVAIL = 4, - QUEUE_LEAVEUNAVAIL = 5, - QUEUE_FULL = 6, -}; - -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" }, -}; - -/*! \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 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 */ - 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 */ - struct queue_ent *next; /*!< The next queue entry */ -}; - -struct member { - char interface[80]; /*!< Technology/Location */ - 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 */ - 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 ANNOUNCEHOLDTIME_ALWAYS 1 -#define ANNOUNCEHOLDTIME_ONCE 2 -#define QUEUE_EVENT_VARIABLES 3 - -struct call_queue { - ast_mutex_t lock; - char name[80]; /*!< Name */ - char moh[80]; /*!< Music On Hold class to be used */ - char announce[80]; /*!< Announcement to play when call is answered */ - char context[AST_MAX_CONTEXT]; /*!< Exit context */ - unsigned int monjoin:1; - 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 reportholdtime:1; - unsigned int wrapped:1; - unsigned int timeoutrestart:1; - unsigned int announceholdtime:2; - int strategy:4; - unsigned int maskmemberstatus:1; - unsigned int realtime:1; - unsigned int found:1; - int announcefrequency; /*!< How often to announce their position */ - int periodicannouncefrequency; /*!< How often to play periodic announcement */ - int roundingseconds; /*!< How many seconds do we round to? */ - int holdtime; /*!< Current avg holdtime, based on an exponential average */ - 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 */ - char sound_next[80]; /*!< Sound file: "Your call is now first in line" (def. queue-youarenext) */ - char sound_thereare[80]; /*!< Sound file: "There are currently" (def. queue-thereare) */ - char sound_calls[80]; /*!< Sound file: "calls waiting to speak to a representative." (def. queue-callswaiting)*/ - char sound_holdtime[80]; /*!< Sound file: "The current estimated total holdtime is" (def. queue-holdtime) */ - char sound_minutes[80]; /*!< Sound file: "minutes." (def. queue-minutes) */ - char sound_lessthan[80]; /*!< Sound file: "less-than" (def. queue-lessthan) */ - char sound_seconds[80]; /*!< Sound file: "seconds." (def. queue-seconds) */ - char sound_thanks[80]; /*!< Sound file: "Thank you for your patience." (def. queue-thankyou) */ - char sound_reporthold[80]; /*!< Sound file: "Hold time" (def. queue-reporthold) */ - char sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS][80];/*!< Sound files: Custom announce, no default */ - - 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 */ -}; - -static AST_LIST_HEAD_STATIC(queues, call_queue); - -static int set_member_paused(const char *queuename, const char *interface, int paused); - -static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan); - -static void rr_dep_warning(void) -{ - static unsigned int warned = 0; - - if (!warned) { - ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n"); - warned = 1; - } -} - -static void monjoin_dep_warning(void) -{ - static unsigned int warned = 0; - if (!warned) { - ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n"); - warned = 1; - } -} -/*! \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 ""; -} - -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; -} - -/*! \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_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) -{ - struct member *member; - struct ao2_iterator mem_iter; - enum queue_member_status result = QUEUE_NO_MEMBERS; - - ast_mutex_lock(&q->lock); - mem_iter = ao2_iterator_init(q->members, 0); - while ((member = ao2_iterator_next(&mem_iter))) { - if (max_penalty && (member->penalty > max_penalty)) { - ao2_ref(member, -1); - continue; - } - - if (member->paused) { - ao2_ref(member, -1); - continue; - } - - switch (member->status) { - case AST_DEVICE_INVALID: - /* nothing to do */ - ao2_ref(member, -1); - break; - case AST_DEVICE_UNAVAILABLE: - result = QUEUE_NO_REACHABLE_MEMBERS; - ao2_ref(member, -1); - break; - default: - ast_mutex_unlock(&q->lock); - ao2_ref(member, -1); - return QUEUE_NORMAL; - } - } - - ast_mutex_unlock(&q->lock); - return result; -} - -struct statechange { - AST_LIST_ENTRY(statechange) entry; - int state; - char dev[0]; -}; - -static int update_status(const char *interface, const int status) -{ - struct member *cur; - struct ao2_iterator mem_iter; - struct call_queue *q; - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - mem_iter = ao2_iterator_init(q->members, 0); - while ((cur = ao2_iterator_next(&mem_iter))) { - char *tmp_interface; - char *slash_pos; - tmp_interface = ast_strdupa(cur->interface); - if ((slash_pos = strchr(tmp_interface, '/'))) - if ((slash_pos = strchr(slash_pos + 1, '/'))) - *slash_pos = '\0'; - - if (strcasecmp(interface, tmp_interface)) { - ao2_ref(cur, -1); - continue; - } - - if (cur->status != status) { - cur->status = status; - 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); - } - ast_mutex_unlock(&q->lock); - } - AST_LIST_UNLOCK(&queues); - - return 0; -} - -/*! \brief set a member's status based on device state of that member's interface*/ -static void *handle_statechange(struct statechange *sc) -{ - struct member_interface *curint; - 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) { - if (option_debug > 2) - ast_log(LOG_DEBUG, "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; - } - - if (option_debug) - ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state)); - - update_status(sc->dev, sc->state); - - 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); - - free(sc); - sc = NULL; - } - - if (sc) - free(sc); - - while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) - free(sc); - - return NULL; -} -/*! \brief Producer of the statechange queue */ -static int statechange_queue(const char *dev, int state, void *ign) -{ - 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; -} -/*! \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) -{ - 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(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(interface); - } - - return cur; -} - -static struct call_queue *alloc_queue(const char *queuename) -{ - struct call_queue *q; - - if ((q = ast_calloc(1, sizeof(*q)))) { - ast_mutex_init(&q->lock); - ast_copy_string(q->name, queuename, sizeof(q->name)); - } - return q; -} - -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 strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP; -} - -static void init_queue(struct call_queue *q) -{ - int i; - - q->dead = 0; - q->retry = DEFAULT_RETRY; - q->timeout = -1; - q->maxlen = 0; - q->announcefrequency = 0; - q->announceholdtime = 0; - q->roundingseconds = 0; /* Default - don't announce seconds */ - q->servicelevel = 0; - q->ringinuse = 1; - q->setinterfacevar = 0; - q->autofill = autofill_default; - q->montype = montype_default; - q->moh[0] = '\0'; - q->announce[0] = '\0'; - q->context[0] = '\0'; - q->monfmt[0] = '\0'; - q->periodicannouncefrequency = 0; - q->reportholdtime = 0; - q->monjoin = 0; - q->wrapuptime = 0; - q->joinempty = 0; - q->leavewhenempty = 0; - q->memberdelay = 0; - q->maskmemberstatus = 0; - q->eventwhencalled = 0; - q->weight = 0; - q->timeoutrestart = 0; - if (!q->members) - q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); - q->membercount = 0; - q->found = 1; - ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); - ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare)); - ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls)); - ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime)); - ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes)); - ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds)); - ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks)); - ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan)); - ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold)); - ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0])); - for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { - q->sound_periodicannounce[i][0]='\0'; - } -} - -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; - } - - if (option_debug) - ast_log(LOG_DEBUG, "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; - int ret = 0; - - ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { - ao2_ref(mem, -1); - ret = 1; - } - ast_mutex_unlock(&q->lock); - if (ret) - break; - } - AST_LIST_UNLOCK(&queues); - - return ret; -} - -static int remove_from_interfaces(const char *interface) -{ - struct member_interface *curint; - - if (interface_exists_global(interface)) - return 0; - - AST_LIST_LOCK(&interfaces); - AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) { - if (!strcasecmp(curint->interface, interface)) { - if (option_debug) - ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface); - AST_LIST_REMOVE_CURRENT(&interfaces, list); - 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))) - free(curint); - AST_LIST_UNLOCK(&interfaces); -} - -/*! \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_copy_string(q->moh, val, sizeof(q->moh)); - } else if (!strcasecmp(param, "announce")) { - ast_copy_string(q->announce, val, sizeof(q->announce)); - } else if (!strcasecmp(param, "context")) { - ast_copy_string(q->context, val, sizeof(q->context)); - } 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, "monitor-join")) { - monjoin_dep_warning(); - q->monjoin = ast_true(val); - } else if (!strcasecmp(param, "monitor-format")) { - ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); - } else if (!strcasecmp(param, "queue-youarenext")) { - ast_copy_string(q->sound_next, val, sizeof(q->sound_next)); - } else if (!strcasecmp(param, "queue-thereare")) { - ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare)); - } else if (!strcasecmp(param, "queue-callswaiting")) { - ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls)); - } else if (!strcasecmp(param, "queue-holdtime")) { - ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime)); - } else if (!strcasecmp(param, "queue-minutes")) { - ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes)); - } else if (!strcasecmp(param, "queue-seconds")) { - ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds)); - } else if (!strcasecmp(param, "queue-lessthan")) { - ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan)); - } else if (!strcasecmp(param, "queue-thankyou")) { - ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks)); - } else if (!strcasecmp(param, "queue-reporthold")) { - ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold)); - } else if (!strcasecmp(param, "announce-frequency")) { - q->announcefrequency = atoi(val); - } else if (!strcasecmp(param, "announce-round-seconds")) { - q->roundingseconds = atoi(val); - if (q->roundingseconds>60 || q->roundingseconds<0) { - 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, "periodic-announce")) { - if (strchr(val, '|')) { - char *s, *buf = ast_strdupa(val); - unsigned int i = 0; - - while ((s = strsep(&buf, "|"))) { - ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i])); - i++; - if (i == MAX_PERIODIC_ANNOUNCEMENTS) - break; - } - } else { - ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0])); - } - } 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")) { - q->strategy = strat2int(val); - if (q->strategy < 0) { - ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", - val, q->name); - q->strategy = QUEUE_STRATEGY_RINGALL; - } - } else if (!strcasecmp(param, "joinempty")) { - 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, "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 (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) -{ - 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))) { - m->dead = 0; - m->realtime = 1; - add_to_interfaces(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; - 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->interface); - q->membercount--; - } - ao2_ref(cur, -1); - } -} - -static void destroy_queue(struct call_queue *q) -{ - free_members(q, 1); - ast_mutex_destroy(&q->lock); - ao2_ref(q->members, -1); - free(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; - struct member *m; - struct ao2_iterator mem_iter; - char *interface = NULL; - char *tmp, *tmp_name; - char tmpbuf[64]; /* Must be longer than the longest queue param name. */ - - /* Find the queue in the in-core list (we will create a new one if not found). */ - AST_LIST_TRAVERSE(&queues, q, list) { - if (!strcasecmp(q->name, queuename)) - break; - } - - /* Static queues override realtime. */ - if (q) { - ast_mutex_lock(&q->lock); - if (!q->realtime) { - if (q->dead) { - ast_mutex_unlock(&q->lock); - return NULL; - } else { - ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); - ast_mutex_unlock(&q->lock); - return 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_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename); - - q->dead = 1; - /* Delete if unused (else will be deleted when last caller leaves). */ - if (!q->count) { - /* Delete. */ - AST_LIST_REMOVE(&queues, q, list); - ast_mutex_unlock(&q->lock); - destroy_queue(q); - } else - ast_mutex_unlock(&q->lock); - } - return NULL; - } - - /* Create a new queue if an in-core entry does not exist yet. */ - if (!q) { - if (!(q = alloc_queue(queuename))) - return NULL; - ast_mutex_lock(&q->lock); - clear_queue(q); - q->realtime = 1; - AST_LIST_INSERT_HEAD(&queues, q, list); - } - init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ - - 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 = tmp_name; - while ((tmp = strchr(tmp, '_'))) - *tmp++ = '-'; - } else - tmp_name = v->name; - - if (!ast_strlen_zero(v->value)) { - /* Don't want to try to set the option if the value is empty */ - queue_set_param(q, tmp_name, v->value, -1, 0); - } - } - - if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) - rr_dep_warning(); - - /* 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")); - } - - /* 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); - ast_mutex_unlock(&q->lock); - remove_from_interfaces(m->interface); - ast_mutex_lock(&q->lock); - q->membercount--; - } - ao2_ref(m, -1); - } - - ast_mutex_unlock(&q->lock); - - 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, *save; - int ret = -1; - - if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) - return ret; - save = var; - 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; - } - ast_variables_destroy(save); - 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; - - if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) { - /*This queue doesn't have realtime members*/ - if (option_debug > 2) - ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name); - return; - } - - ast_mutex_lock(&q->lock); - - /* 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")); - } - - /* 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); - ast_mutex_unlock(&q->lock); - remove_from_interfaces(m->interface); - ast_mutex_lock(&q->lock); - q->membercount--; - } - ao2_ref(m, -1); - } - ast_mutex_unlock(&q->lock); - ast_config_destroy(member_config); -} - -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; - - /* Find the queue in the in-core list first. */ - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - if (!strcasecmp(q->name, queuename)) { - break; - } - } - AST_LIST_UNLOCK(&queues); - - 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; - } - } - - AST_LIST_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); - - AST_LIST_UNLOCK(&queues); - } else { - update_realtime_members(q); - } - return 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; - - AST_LIST_LOCK(&queues); - ast_mutex_lock(&q->lock); - - /* This is our one */ - stat = get_member_status(q, qe->max_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_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 */ - S_OR(qe->chan->cid.cid_name, "unknown"), - q->name, qe->pos, q->count, qe->chan->uniqueid ); - if (option_debug) - ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); - } - ast_mutex_unlock(&q->lock); - AST_LIST_UNLOCK(&queues); - - return res; -} - -static int play_file(struct ast_channel *chan, 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 res = 0, avgholdmins, avgholdsecs; - time_t now; - - /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ - time(&now); - if ((now - qe->last_pos) < 15) - 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; - - ast_moh_stop(qe->chan); - /* 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, (char *) 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; - } - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) || - !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { - 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 (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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) - 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 an exponential average */ - /* Thanks to SRT for this contribution */ - /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ - - ast_mutex_lock(&qe->parent->lock); - oldvalue = qe->parent->holdtime; - qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; - ast_mutex_unlock(&qe->parent->lock); -} - - -static void leave_queue(struct queue_ent *qe) -{ - struct call_queue *q; - struct queue_ent *cur, *prev = NULL; - int pos = 0; - - if (!(q = qe->parent)) - return; - ast_mutex_lock(&q->lock); - - 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); - if (option_debug) - ast_log(LOG_DEBUG, "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; - } else { - /* Renumber the people after us in the queue based on a new count */ - cur->pos = ++pos; - prev = cur; - } - } - ast_mutex_unlock(&q->lock); - - if (q->dead && !q->count) { - /* It's dead and nobody is in it, so kill it */ - AST_LIST_LOCK(&queues); - AST_LIST_REMOVE(&queues, q, list); - AST_LIST_UNLOCK(&queues); - destroy_queue(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); - free(oo); - } -} - - -/* 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; - - /* &qlock and &rq->lock already set by try_calling() - * to solve deadlock */ - AST_LIST_TRAVERSE(&queues, q, list) { - if (q == rq) /* don't check myself, could deadlock */ - continue; - ast_mutex_lock(&q->lock); - if (q->count && q->members) { - if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { - ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); - if (q->weight > rq->weight) { - ast_log(LOG_DEBUG, "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); - } - } - ast_mutex_unlock(&q->lock); - if (found) - break; - } - 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) -{ - char *tmp = alloca(len); - - if (pbx_builtin_serialize_variables(chan, tmp, len)) { - int i, j; - - /* convert "\n" to "\nVariable: " */ - strcpy(vars, "Variable: "); - - 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 - 3) - j = len - 3; - vars[j++] = '\r'; - vars[j++] = '\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; - const char *macrocontext, *macroexten; - - /* on entry here, we know that tmp->chan == NULL */ - if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) { - if (option_debug) - ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", 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)) { - if (option_debug) - ast_log(LOG_DEBUG, "%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) { - if (option_debug) - ast_log(LOG_DEBUG, "%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_log(LOG_DEBUG, "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_status(tmp->member->interface, ast_device_state(tmp->member->interface)); - - ast_mutex_lock(&qe->parent->lock); - qe->parent->rrpos++; - ast_mutex_unlock(&qe->parent->lock); - - (*busies)++; - return 0; - } - - tmp->chan->appl = "AppQueue"; - tmp->chan->data = "(Outgoing Line)"; - tmp->chan->whentohangup = 0; - if (tmp->chan->cid.cid_num) - free(tmp->chan->cid.cid_num); - tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); - if (tmp->chan->cid.cid_name) - free(tmp->chan->cid.cid_name); - tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); - if (tmp->chan->cid.cid_ani) - 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 */ - ast_channel_lock(qe->chan); - macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); - if (!ast_strlen_zero(macrocontext)) - ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext)); - else - ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext)); - macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); - if (!ast_strlen_zero(macroexten)) - ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); - else - ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); - ast_channel_unlock(qe->chan); - - /* 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 */ - if (option_debug) - ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface); - do_hang(tmp); - (*busies)++; - update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); - return 0; - } else if (qe->parent->eventwhencalled) { - char vars[2048]; - - manager_event(EVENT_FLAG_AGENT, "AgentCalled", - "AgentCalled: %s\r\n" - "AgentName: %s\r\n" - "ChannelCalling: %s\r\n" - "CallerID: %s\r\n" - "CallerIDName: %s\r\n" - "Context: %s\r\n" - "Extension: %s\r\n" - "Priority: %d\r\n" - "%s", - tmp->interface, tmp->member->membername, qe->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)) : ""); - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface); - } - - update_status(tmp->member->interface, ast_device_state(tmp->member->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) { - if (option_debug) - ast_log(LOG_DEBUG, "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) { - if (option_debug) - ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); - ret |= ring_entry(qe, cur, busies); - } - } - } else { - /* Ring just the best channel */ - if (option_debug) - ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric); - ret = ring_entry(qe, best, busies); - } - } - - return ret; -} - -static int store_next(struct queue_ent *qe, struct callattempt *outgoing) -{ - struct callattempt *best = find_best(outgoing); - - if (best) { - /* Ring just the best channel */ - if (option_debug) - ast_log(LOG_DEBUG, "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 say_periodic_announcement(struct queue_ent *qe) -{ - 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 */ - ast_moh_stop(qe->chan); - - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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 || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) { - qe->last_periodic_announce_sound = 0; - } - - /* play the announcement */ - res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]); - - 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) - 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) -{ - ast_mutex_lock(&qe->parent->lock); - 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++; - ast_mutex_unlock(&qe->parent->lock); -} - -/*! \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) -{ - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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, 1)) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); - } else { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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) -{ - 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; - - starttime = (long) time(NULL); - - 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_log(LOG_DEBUG, "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) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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 */ - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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 (!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) - free(o->chan->cid.cid_num); - o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); - - if (o->chan->cid.cid_name) - 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) - free(o->chan->cid.cid_ani); - o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); - } - if (o->chan->cid.cid_rdnis) - 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) { - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); - peer = o; - } - break; - case AST_CONTROL_BUSY: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_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: - if (option_verbose > 2) - ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); - break; - case AST_CONTROL_OFFHOOK: - /* Ignore going off hook */ - break; - default: - ast_log(LOG_DEBUG, "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 == '*')) { - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_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)) { - if (option_verbose > 3) - ast_verbose(VERBOSE_PREFIX_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); - } - } - - 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) { - if (option_debug) - ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); - res = 1; - } else { - if (option_debug) - ast_log(LOG_DEBUG, "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? */ - ast_mutex_lock(&qe->parent->lock); - - ch = qe->parent->head; - - if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { - if (option_debug) - ast_log(LOG_DEBUG, "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_INUSE: - if (!qe->parent->ringinuse) - break; - /* else fall through */ - case AST_DEVICE_NOT_INUSE: - case AST_DEVICE_UNKNOWN: - if (!cur->paused) - avl++; - break; - } - ao2_ref(cur, -1); - } - } - - if (option_debug) - ast_log(LOG_DEBUG, "There are %d available members.\n", avl); - - while ((idx < avl) && (ch) && (ch != qe)) { - if (!ch->pending) - idx++; - ch = ch->next; - } - - /* If the queue entry is within avl [the number of available members] calls from the top ... */ - if (ch && idx < avl) { - if (option_debug) - ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); - res = 1; - } else { - if (option_debug) - ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); - res = 0; - } - - ast_mutex_unlock(&qe->parent->lock); - } - - return res; -} -/*! \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); - - /* 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)) { - *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 && !ringing && - (res = say_position(qe))) - break; - - /* If we have timed out, break out */ - if (qe->expire && (time(NULL) >= qe->expire)) { - *reason = QUEUE_TIMEOUT; - break; - } - - /* Make a periodic announcement, if enabled */ - if (qe->parent->periodicannouncefrequency && !ringing && - (res = say_periodic_announcement(qe))) - break; - - /* If we have timed out, break out */ - if (qe->expire && (time(NULL) >= qe->expire)) { - *reason = QUEUE_TIMEOUT; - break; - } - - /* 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; - } - - /* If we have timed out, break out */ - if (qe->expire && (time(NULL) >= qe->expire)) { - *reason = QUEUE_TIMEOUT; - break; - } - } - - return res; -} - -static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl) -{ - ast_mutex_lock(&q->lock); - time(&member->lastcall); - member->calls++; - q->callscompleted++; - if (callcompletedinsl) - q->callscompletedinsl++; - ast_mutex_unlock(&q->lock); - 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)) - return -1; - - switch (q->strategy) { - case QUEUE_STRATEGY_RINGALL: - /* Everyone equal, except for penalty */ - tmp->metric = mem->penalty * 1000000; - break; - case QUEUE_STRATEGY_ROUNDROBIN: - if (!pos) { - if (!q->wrapped) { - /* No more channels, start over */ - q->rrpos = 0; - } else { - /* Prioritize next entry */ - q->rrpos++; - } - q->wrapped = 0; - } - /* Fall through */ - 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_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; -} - -struct queue_transfer_ds { - struct queue_ent *qe; - struct member *member; - time_t starttime; - int callcompletedinsl; -}; - -static void queue_transfer_destroy(void *data) -{ - struct queue_transfer_ds *qtds = data; - ast_free(qtds); -} - -/*! \brief a datastore used to help correctly log attended transfers of queue callers - */ -static const struct ast_datastore_info queue_transfer_info = { - .type = "queue_transfer", - .chan_fixup = queue_transfer_fixup, - .destroy = queue_transfer_destroy, -}; - -/*! \brief Log an attended transfer when a queue caller channel is masqueraded - * - * When a caller is masqueraded, we want to log a transfer. Fixup time is the closest we can come to when - * the actual transfer occurs. This happens during the masquerade after datastores are moved from old_chan - * to new_chan. This is why new_chan is referenced for exten, context, and datastore information. - * - * At the end of this, we want to remove the datastore so that this fixup function is not called on any - * future masquerades of the caller during the current call. - */ -static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) -{ - struct queue_transfer_ds *qtds = data; - struct queue_ent *qe = qtds->qe; - struct member *member = qtds->member; - time_t callstart = qtds->starttime; - int callcompletedinsl = qtds->callcompletedinsl; - struct ast_datastore *datastore; - - ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", - new_chan->exten, new_chan->context, (long) (callstart - qe->start), - (long) (time(NULL) - callstart)); - - update_queue(qe->parent, member, callcompletedinsl); - - /* No need to lock the channels because they are already locked in ast_do_masquerade */ - if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { - ast_channel_datastore_remove(old_chan, datastore); - } else { - ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); - } -} - -/*! \brief mechanism to tell if a queue caller was atxferred by a queue member. - * - * When a caller is atxferred, then the queue_transfer_info datastore - * is removed from the channel. If it's still there after the bridge is - * broken, then the caller was not atxferred. - * - * \note Only call this with chan locked - */ -static int attended_transfer_occurred(struct ast_channel *chan) -{ - return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; -} - -/*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log - */ -static struct ast_datastore *setup_transfer_datastore(struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl) -{ - struct ast_datastore *ds; - struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); - - if (!qtds) { - ast_log(LOG_WARNING, "Memory allocation error!\n"); - return NULL; - } - - ast_channel_lock(qe->chan); - if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) { - ast_channel_unlock(qe->chan); - ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); - return NULL; - } - - qtds->qe = qe; - /* This member is refcounted in try_calling, so no need to add it here, too */ - qtds->member = member; - qtds->starttime = starttime; - qtds->callcompletedinsl = callcompletedinsl; - ds->data = qtds; - ast_channel_datastore_add(qe->chan, ds); - ast_channel_unlock(qe->chan); - return ds; -} - - -/*! \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 - */ - -static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi) -{ - struct member *cur; - struct callattempt *outgoing = NULL; /* the list of calls we are building */ - int to; - char oldexten[AST_MAX_EXTENSION]=""; - char oldcontext[AST_MAX_CONTEXT]=""; - char queuename[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; - 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, *transfer_ds; - - 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)); - time(&now); - - /* If we've already exceeded our timeout, then just stop - * This should be extremely rare. queue_exec will take care - * of removing the caller and reporting the timeout as the reason. - */ - if (qe->expire && now >= qe->expire) { - res = 0; - goto out; - } - - 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 'n': - if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) - (*tries)++; - else - *tries = qe->parent->membercount; - *noption = 1; - break; - case 'i': - forwardsallowed = 0; - break; - } - - /* Hold the lock while we setup the outgoing calls */ - if (use_weight) - AST_LIST_LOCK(&queues); - ast_mutex_lock(&qe->parent->lock); - if (option_debug) - ast_log(LOG_DEBUG, "%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); - ast_mutex_unlock(&qe->parent->lock); - if (use_weight) - AST_LIST_UNLOCK(&queues); - goto out; - } - if (!datastore) { - if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) { - ao2_ref(cur, -1); - ast_mutex_unlock(&qe->parent->lock); - if (use_weight) - AST_LIST_UNLOCK(&queues); - free(tmp); - goto out; - } - datastore->inheritance = DATASTORE_INHERIT_FOREVER; - if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { - ao2_ref(cur, -1); - ast_mutex_unlock(&qe->parent->lock); - if (use_weight) - AST_LIST_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); - ast_mutex_unlock(&qe->parent->lock); - if (use_weight) - AST_LIST_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; - 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); - 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; - ++qe->pending; - ast_mutex_unlock(&qe->parent->lock); - ring_one(qe, outgoing, &numbusies); - if (use_weight) - AST_LIST_UNLOCK(&queues); - lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); - /* The ast_channel_datastore_remove() function could fail here if the - * datastore was moved to another channel during a masquerade. If this is - * the case, don't free the datastore here because later, when the channel - * to which the datastore was moved hangs up, it will attempt to free this - * datastore again, causing a crash - */ - ast_channel_lock(qe->chan); - if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { - ast_channel_datastore_free(datastore); - } - ast_channel_unlock(qe->chan); - ast_mutex_lock(&qe->parent->lock); - if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { - store_next(qe, outgoing); - } - ast_mutex_unlock(&qe->parent->lock); - 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 (option_debug && res == -1) - ast_log(LOG_DEBUG, "%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. */ - 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)); - ast_mutex_lock(&qe->parent->lock); - callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); - ast_mutex_unlock(&qe->parent->lock); - 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 (peer->_softhangup) { - /* 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", ""); - 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 */ - 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; - } - - if (qe->parent->setinterfacevar) - pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface); - - /* Begin Monitoring */ - if (qe->parent->monfmt && *qe->parent->monfmt) { - if (!qe->parent->montype) { - if (option_debug) - ast_log(LOG_DEBUG, "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 ); - else if (qe->chan->cdr) - ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); - 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 ); - } - if (qe->parent->monjoin) - ast_monitor_setjoinfiles(which, 1); - } else { - if (option_debug) - ast_log(LOG_DEBUG, "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)-1); - else - snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); - } else { - ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1); - for (p = tmpid2; *p ; p++) { - if (*p == '^' && *(p+1) == '{') { - *p = '$'; - } - } - - memset(tmpid, 0, sizeof(tmpid)); - 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) { - ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1); - for (p = meid2; *p ; p++) { - if (*p == '^' && *(p+1) == '{') { - *p = '$'; - } - } - - memset(meid, 0, sizeof(meid)); - pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); - } - - snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt); - - mixmonapp = pbx_findapp("MixMonitor"); - - if (strchr(tmpid2, '|')) { - ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n"); - mixmonapp = NULL; - } - - if (!monitor_options) - monitor_options = ""; - - if (strchr(monitor_options, '|')) { - ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n"); - mixmonapp = NULL; - } - - if (mixmonapp) { - if (!ast_strlen_zero(monitor_exec)) - snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec); - else - snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options); - - if (option_debug) - ast_log(LOG_DEBUG, "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)) { - if (option_debug) - ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); - ast_channel_sendurl(peer, url); - } - if (!ast_strlen_zero(agi)) { - if (option_debug) - ast_log(LOG_DEBUG, "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"); - } - qe->handled++; - ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid); - 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" - "%s", - queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, - (long)time(NULL) - qe->start, peer->uniqueid, - 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); - - if (member->status == AST_DEVICE_NOT_INUSE) - ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername); - - transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); - bridge = ast_bridge_call(qe->chan,peer, &bridge_config); - - ast_channel_lock(qe->chan); - if (!attended_transfer_occurred(qe->chan)) { - struct ast_datastore *tds; - 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)); - } else if (qe->chan->_softhangup) { - ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", - (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); - if (qe->parent->eventwhencalled) - 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: caller\r\n" - "%s", - queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, - (long)(callstart - qe->start), (long)(time(NULL) - callstart), - qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); - } else { - ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", - (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); - if (qe->parent->eventwhencalled) - manager_event(EVENT_FLAG_AGENT, "AgentComplete", - "Queue: %s\r\n" - "Uniqueid: %s\r\n" - "Channel: %s\r\n" - "MemberName: %s\r\n" - "HoldTime: %ld\r\n" - "TalkTime: %ld\r\n" - "Reason: agent\r\n" - "%s", - queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start), - (long)(time(NULL) - callstart), - qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); - } - if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { - ast_channel_datastore_remove(qe->chan, tds); - } - update_queue(qe->parent, member, callcompletedinsl); - } - - if (transfer_ds) { - ast_channel_datastore_free(transfer_ds); - } - ast_channel_unlock(qe->chan); - ast_hangup(peer); - 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 - * - * / = ;;[|...] - * - */ -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; - struct member *mem, tmpmem; - int res = RES_NOSUCHQUEUE; - - ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - if (strcmp(q->name, queuename)) { - ast_mutex_unlock(&q->lock); - continue; - } - - if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { - /* XXX future changes should beware of this assumption!! */ - if (!mem->dynamic) { - res = RES_NOT_DYNAMIC; - ao2_ref(mem, -1); - ast_mutex_unlock(&q->lock); - break; - } - 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); - ao2_ref(mem, -1); - - if (queue_persistent_members) - dump_queue_members(q); - - res = RES_OKAY; - } else { - res = RES_EXISTS; - } - ast_mutex_unlock(&q->lock); - break; - } - - if (res == RES_OKAY) - remove_from_interfaces(interface); - - AST_LIST_UNLOCK(&queues); - - return res; -} - - -static int add_to_queue(const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump) -{ - 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; - - AST_LIST_LOCK(&queues); - - ast_mutex_lock(&q->lock); - if ((old_member = interface_exists(q, interface)) == NULL) { - add_to_interfaces(interface); - if ((new_member = create_queue_member(interface, membername, penalty, paused))) { - 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; - } - ast_mutex_unlock(&q->lock); - AST_LIST_UNLOCK(&queues); - - return res; -} - -static int set_member_paused(const char *queuename, const char *interface, int paused) -{ - int found = 0; - struct call_queue *q; - struct member *mem; - - /* 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", ""); - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { - if ((mem = interface_exists(q, interface))) { - found++; - if (mem->paused == paused) - ast_log(LOG_DEBUG, "%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", ""); - - 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); - } - } - ast_mutex_unlock(&q->lock); - } - AST_LIST_UNLOCK(&queues); - - return found ? RESULT_SUCCESS : RESULT_FAILURE; -} - -/* Reload dynamic queue members persisted into the astdb */ -static void reload_queue_members(void) -{ - char *cur_ptr; - char *queue_name; - char *member; - char *interface; - char *membername = NULL; - 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]; - - AST_LIST_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; - - AST_LIST_TRAVERSE(&queues, cur_queue, list) { - ast_mutex_lock(&cur_queue->lock); - if (!strcmp(queue_name, cur_queue->name)) - break; - ast_mutex_unlock(&cur_queue->lock); - } - - 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; - } else - ast_mutex_unlock(&cur_queue->lock); - - if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) - 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, ";"); - - 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; - } - if (ast_strlen_zero(membername)) - membername = interface; - - if (option_debug) - ast_log(LOG_DEBUG, "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) == RES_OUTOFMEMORY) { - ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); - break; - } - } - } - - AST_LIST_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) -{ - struct ast_module_user *lu; - char *parse; - int priority_jump = 0; - 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, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n"); - return -1; - } - - parse = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, parse); - - lu = ast_module_user_add(chan); - - if (args.options) { - if (strchr(args.options, 'j')) - priority_jump = 1; - } - - if (ast_strlen_zero(args.interface)) { - ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); - ast_module_user_remove(lu); - return -1; - } - - if (set_member_paused(args.queuename, args.interface, 1)) { - ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); - if (priority_jump || ast_opt_priority_jumping) { - if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { - pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); - ast_module_user_remove(lu); - return 0; - } - } - ast_module_user_remove(lu); - pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); - return 0; - } - - ast_module_user_remove(lu); - pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); - - return 0; -} - -static int upqm_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *lu; - char *parse; - int priority_jump = 0; - 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, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n"); - return -1; - } - - parse = ast_strdupa(data); - - AST_STANDARD_APP_ARGS(args, parse); - - lu = ast_module_user_add(chan); - - if (args.options) { - if (strchr(args.options, 'j')) - priority_jump = 1; - } - - if (ast_strlen_zero(args.interface)) { - ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); - ast_module_user_remove(lu); - return -1; - } - - if (set_member_paused(args.queuename, args.interface, 0)) { - ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); - if (priority_jump || ast_opt_priority_jumping) { - if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { - pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); - ast_module_user_remove(lu); - return 0; - } - } - ast_module_user_remove(lu); - pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); - return 0; - } - - ast_module_user_remove(lu); - pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); - - return 0; -} - -static int rqm_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - struct ast_module_user *lu; - char *parse, *temppos = NULL; - int priority_jump = 0; - 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); - - lu = ast_module_user_add(chan); - - if (ast_strlen_zero(args.interface)) { - args.interface = ast_strdupa(chan->name); - temppos = strrchr(args.interface, '-'); - if (temppos) - *temppos = '\0'; - } - - if (args.options) { - if (strchr(args.options, 'j')) - priority_jump = 1; - } - - 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_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); - if (priority_jump || ast_opt_priority_jumping) - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - 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; - } - - ast_module_user_remove(lu); - - return res; -} - -static int aqm_exec(struct ast_channel *chan, void *data) -{ - int res=-1; - struct ast_module_user *lu; - char *parse, *temppos = NULL; - int priority_jump = 0; - 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); - ); - 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); - - lu = ast_module_user_add(chan); - - 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; - } - } - - if (args.options) { - if (strchr(args.options, 'j')) - priority_jump = 1; - } - - switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) { - 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); - if (priority_jump || ast_opt_priority_jumping) - ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); - 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; - } - - ast_module_user_remove(lu); - - return res; -} - -static int ql_exec(struct ast_channel *chan, void *data) -{ - struct ast_module_user *u; - 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; - } - - u = ast_module_user_add(chan); - - 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"); - ast_module_user_remove(u); - return -1; - } - - ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, - "%s", args.params ? args.params : ""); - - ast_module_user_remove(u); - - return 0; -} - -/*!\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; - struct ast_module_user *lu; - const char *user_priority; - const char *max_penalty_str; - int prio; - int max_penalty; - enum queue_result reason = QUEUE_UNKNOWN; - /* whether to exit Queue application after the timeout hits */ - int tries = 0; - int noption = 0; - char *parse; - 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); - ); - /* 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); - - lu = ast_module_user_add(chan); - - /* Setup our queue entry */ - memset(&qe, 0, sizeof(qe)); - qe.start = time(NULL); - - /* set the expire time based on the supplied timeout; */ - if (!ast_strlen_zero(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) { - if (option_debug) - ast_log(LOG_DEBUG, "%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 { - if (option_debug > 2) - ast_log(LOG_DEBUG, "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) { - if (option_debug) - ast_log(LOG_DEBUG, "%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 (args.options && (strchr(args.options, 'r'))) - ringing = 1; - - if (option_debug) - ast_log(LOG_DEBUG, "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.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)) { - int makeannouncement = 0; - - ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), - S_OR(chan->cid.cid_num, "")); -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; - - 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", qe.pos); - break; - } - - if (makeannouncement) { - /* Make a position announcement, if enabled */ - if (qe.parent->announcefrequency && !ringing) - if ((res = say_position(&qe))) - goto stop; - - } - makeannouncement = 1; - - /* 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", qe.pos); - break; - } - /* Make a periodic announcement, if enabled */ - if (qe.parent->periodicannouncefrequency && !ringing) - if ((res = say_periodic_announcement(&qe))) - goto stop; - - /* 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", qe.pos); - break; - } - /* Try calling all queue members for 'timeout' seconds */ - res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi); - if (res) - goto stop; - - stat = get_member_status(qe.parent, qe.max_penalty); - - /* exit after 'timeout' cycle if 'n' option enabled */ - if (noption && tries >= qe.parent->membercount) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_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)) { - 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; - } - - /* 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", qe.pos); - 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)) { - if (option_debug) - ast_log(LOG_DEBUG, "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 = 0; - if (ringing) { - ast_indicate(chan, -1); - } else { - ast_moh_stop(chan); - } - ast_stopstream(chan); - } - leave_queue(&qe); - if (reason != QUEUE_UNKNOWN) - set_queue_result(chan, reason); - } else { - ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); - set_queue_result(chan, reason); - res = 0; - } - ast_module_user_remove(lu); - - return res; -} - -static int queue_function_qac(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - int count = 0; - struct call_queue *q; - struct ast_module_user *lu; - struct member *m; - struct ao2_iterator mem_iter; - - buf[0] = '\0'; - - if (ast_strlen_zero(data)) { - ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); - return -1; - } - - lu = ast_module_user_add(chan); - - if ((q = load_realtime_queue(data))) { - ast_mutex_lock(&q->lock); - 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); - } - ast_mutex_unlock(&q->lock); - } else - ast_log(LOG_WARNING, "queue %s was not found\n", data); - - snprintf(buf, len, "%d", count); - ast_module_user_remove(lu); - - return 0; -} - -static int queue_function_queuewaitingcount(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - int count = 0; - struct call_queue *q; - struct ast_module_user *lu; - struct ast_variable *var = NULL; - - buf[0] = '\0'; - - if (ast_strlen_zero(data)) { - ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); - return -1; - } - - lu = ast_module_user_add(chan); - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - if (!strcasecmp(q->name, data)) { - ast_mutex_lock(&q->lock); - break; - } - } - AST_LIST_UNLOCK(&queues); - - if (q) { - count = q->count; - ast_mutex_unlock(&q->lock); - } else if ((var = ast_load_realtime("queues", "name", data, NULL))) { - /* if the queue is realtime but was not found in memory, this - * means that the queue had been deleted from memory since it was - * "dead." This means it has a 0 waiting count - */ - count = 0; - ast_variables_destroy(var); - } else - ast_log(LOG_WARNING, "queue %s was not found\n", data); - - snprintf(buf, len, "%d", count); - ast_module_user_remove(lu); - return 0; -} - -static int queue_function_queuememberlist(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) -{ - struct ast_module_user *u; - struct call_queue *q; - 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; - } - - u = ast_module_user_add(chan); - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - if (!strcasecmp(q->name, data)) { - ast_mutex_lock(&q->lock); - break; - } - } - AST_LIST_UNLOCK(&queues); - - if (q) { - int buflen = 0, count = 0; - struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); - - 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); - } - ast_mutex_unlock(&q->lock); - } 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'; - ast_module_user_remove(u); - - return 0; -} - -static struct ast_custom_function queueagentcount_function = { - .name = "QUEUEAGENTCOUNT", - .synopsis = "Count number of agents answering a queue", - .syntax = "QUEUEAGENTCOUNT()", - .desc = -"Returns the number of members currently associated with the specified queue.\n" -"This function is deprecated. You should use QUEUE_MEMBER_COUNT() instead.\n", - .read = queue_function_qac, -}; - -static struct ast_custom_function queuemembercount_function = { - .name = "QUEUE_MEMBER_COUNT", - .synopsis = "Count number of members answering a queue", - .syntax = "QUEUE_MEMBER_COUNT()", - .desc = -"Returns the number of members currently associated with the specified queue.\n", - .read = queue_function_qac, -}; - -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()", - .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()", - .desc = -"Returns a comma-separated list of members associated with the specified queue.\n", - .read = queue_function_queuememberlist, -}; - -static int reload_queues(void) -{ - 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; - char *membername = NULL; - int penalty; - AST_DECLARE_APP_ARGS(args, - AST_APP_ARG(interface); - AST_APP_ARG(penalty); - AST_APP_ARG(membername); - ); - - if (!(cfg = ast_config_load("queues.conf"))) { - ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); - return 0; - } - AST_LIST_LOCK(&queues); - use_weight=0; - /* Mark all non-realtime queues as dead for the moment */ - AST_LIST_TRAVERSE(&queues, q, list) { - if (!q->realtime) { - q->dead = 1; - q->found = 0; - } - } - - /* Chug through config file */ - cat = NULL; - while ((cat = ast_category_browse(cfg, cat)) ) { - if (!strcasecmp(cat, "general")) { - /* Initialize global settings */ - 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; - } else { /* Define queue */ - /* Look for an existing one */ - AST_LIST_TRAVERSE(&queues, q, list) { - if (!strcmp(q->name, cat)) - break; - } - if (!q) { - /* Make one then */ - if (!(q = alloc_queue(cat))) { - /* TODO: Handle memory allocation failure */ - } - new = 1; - } else - new = 0; - if (q) { - if (!new) - ast_mutex_lock(&q->lock); - /* 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) - ast_mutex_unlock(&q->lock); - continue; - } - /* Re-initialize the queue, and clear statistics */ - init_queue(q); - 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_NONSTANDARD_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++; - } - - /* 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); - - newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); - ao2_link(q->members, newm); - ao2_ref(newm, -1); - newm = NULL; - - if (cur) - ao2_ref(cur, -1); - else { - /* Add them to the master int list if necessary */ - add_to_interfaces(interface); - 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; - } - - q->membercount--; - ao2_unlink(q->members, cur); - remove_from_interfaces(cur->interface); - ao2_ref(cur, -1); - } - - if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) - rr_dep_warning(); - - if (new) { - AST_LIST_INSERT_HEAD(&queues, q, list); - } else - ast_mutex_unlock(&q->lock); - } - } - } - ast_config_destroy(cfg); - AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) { - if (q->dead) { - AST_LIST_REMOVE_CURRENT(&queues, list); - if (!q->count) - destroy_queue(q); - else - ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n"); - } else { - ast_mutex_lock(&q->lock); - 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); - } - ast_mutex_unlock(&q->lock); - } - } - AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&queues); - return 1; -} - -static int __queues_show(struct mansession *s, int manager, int fd, int argc, char **argv) -{ - struct call_queue *q; - struct queue_ent *qe; - struct member *mem; - int pos, queue_show; - time_t now; - char max_buf[150]; - char *max; - size_t max_left; - float sl = 0; - char *term = manager ? "\r\n" : "\n"; - struct ao2_iterator mem_iter; - - time(&now); - if (argc == 2) - queue_show = 0; - else if (argc == 3) - queue_show = 1; - else - return RESULT_SHOWUSAGE; - - /* We only want to load realtime queues when a specific queue is asked for. */ - if (queue_show) { - load_realtime_queue(argv[2]); - } else if (ast_check_realtime("queues")) { - struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL); - char *queuename; - if (cfg) { - for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { - load_realtime_queue(queuename); - } - ast_config_destroy(cfg); - } - } - - AST_LIST_LOCK(&queues); - if (AST_LIST_EMPTY(&queues)) { - AST_LIST_UNLOCK(&queues); - if (queue_show) { - if (s) - astman_append(s, "No such queue: %s.%s",argv[2], term); - else - ast_cli(fd, "No such queue: %s.%s",argv[2], term); - } else { - if (s) - astman_append(s, "No queues.%s", term); - else - ast_cli(fd, "No queues.%s", term); - } - return RESULT_SUCCESS; - } - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - if (queue_show) { - if (strcasecmp(q->name, argv[2]) != 0) { - ast_mutex_unlock(&q->lock); - if (!AST_LIST_NEXT(q, list)) { - ast_cli(fd, "No such queue: %s.%s",argv[2], term); - break; - } - continue; - } - } - max_buf[0] = '\0'; - max = max_buf; - max_left = sizeof(max_buf); - if (q->maxlen) - ast_build_string(&max, &max_left, "%d", q->maxlen); - else - ast_build_string(&max, &max_left, "unlimited"); - sl = 0; - if (q->callscompleted > 0) - sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); - if (s) - astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", - q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, - q->callscompleted, q->callsabandoned,sl,q->servicelevel, term); - else - ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", - q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term); - if (ao2_container_count(q->members)) { - if (s) - astman_append(s, " Members: %s", term); - else - ast_cli(fd, " Members: %s", term); - mem_iter = ao2_iterator_init(q->members, 0); - while ((mem = ao2_iterator_next(&mem_iter))) { - max_buf[0] = '\0'; - max = max_buf; - max_left = sizeof(max_buf); - if (strcasecmp(mem->membername, mem->interface)) { - ast_build_string(&max, &max_left, " (%s)", mem->interface); - } - if (mem->penalty) - ast_build_string(&max, &max_left, " with penalty %d", mem->penalty); - if (mem->dynamic) - ast_build_string(&max, &max_left, " (dynamic)"); - if (mem->realtime) - ast_build_string(&max, &max_left, " (realtime)"); - if (mem->paused) - ast_build_string(&max, &max_left, " (paused)"); - ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status)); - if (mem->calls) { - ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)", - mem->calls, (long) (time(NULL) - mem->lastcall)); - } else - ast_build_string(&max, &max_left, " has taken no calls yet"); - if (s) - astman_append(s, " %s%s%s", mem->membername, max_buf, term); - else - ast_cli(fd, " %s%s%s", mem->membername, max_buf, term); - ao2_ref(mem, -1); - } - } else if (s) - astman_append(s, " No Members%s", term); - else - ast_cli(fd, " No Members%s", term); - if (q->head) { - pos = 1; - if (s) - astman_append(s, " Callers: %s", term); - else - ast_cli(fd, " Callers: %s", term); - for (qe = q->head; qe; qe = qe->next) { - if (s) - astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", - pos++, qe->chan->name, (long) (now - qe->start) / 60, - (long) (now - qe->start) % 60, qe->prio, term); - else - ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, - qe->chan->name, (long) (now - qe->start) / 60, - (long) (now - qe->start) % 60, qe->prio, term); - } - } else if (s) - astman_append(s, " No Callers%s", term); - else - ast_cli(fd, " No Callers%s", term); - if (s) - astman_append(s, "%s", term); - else - ast_cli(fd, "%s", term); - ast_mutex_unlock(&q->lock); - if (queue_show) - break; - } - AST_LIST_UNLOCK(&queues); - return RESULT_SUCCESS; -} - -static int queue_show(int fd, int argc, char **argv) -{ - return __queues_show(NULL, 0, fd, argc, argv); -} - -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); - - AST_LIST_LOCK(&queues); - AST_LIST_TRAVERSE(&queues, q, list) { - if (!strncasecmp(word, q->name, wordlen) && ++which > state) { - ret = ast_strdup(q->name); - break; - } - } - AST_LIST_UNLOCK(&queues); - - 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; -} - -/*!\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, -1, 2, a); - astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ - - 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 mem_iter; - - astman_send_ack(s, m, "Queue status will follow"); - time(&now); - AST_LIST_LOCK(&queues); - if (!ast_strlen_zero(id)) - snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); - - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - - /* 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" - "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, 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" - "CallerID: %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); - } - } - ast_mutex_unlock(&q->lock); - } - - astman_append(s, - "Event: QueueStatusComplete\r\n" - "%s" - "\r\n",idText); - - AST_LIST_UNLOCK(&queues); - - - 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; - 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"); - - 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)) { - 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; - 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 */ - - 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, 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 handle_queue_add_member(int fd, int argc, char *argv[]) -{ - char *queuename, *interface, *membername = NULL; - int penalty; - - if ((argc != 6) && (argc != 8) && (argc != 10)) { - return RESULT_SHOWUSAGE; - } else if (strcmp(argv[4], "to")) { - return RESULT_SHOWUSAGE; - } else if ((argc == 8) && strcmp(argv[6], "penalty")) { - return RESULT_SHOWUSAGE; - } else if ((argc == 10) && strcmp(argv[8], "as")) { - return RESULT_SHOWUSAGE; - } - - queuename = argv[5]; - interface = argv[3]; - if (argc >= 8) { - if (sscanf(argv[7], "%d", &penalty) == 1) { - if (penalty < 0) { - ast_cli(fd, "Penalty must be >= 0\n"); - penalty = 0; - } - } else { - ast_cli(fd, "Penalty must be an integer >= 0\n"); - penalty = 0; - } - } else { - penalty = 0; - } - - if (argc >= 10) { - membername = argv[9]; - } - - switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) { - case RES_OKAY: - ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); - ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); - return RESULT_SUCCESS; - case RES_EXISTS: - ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); - return RESULT_FAILURE; - case RES_NOSUCHQUEUE: - ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); - return RESULT_FAILURE; - case RES_OUTOFMEMORY: - ast_cli(fd, "Out of memory\n"); - return RESULT_FAILURE; - default: - return RESULT_FAILURE; - } -} - -static char *complete_queue_add_member(const char *line, const char *word, int pos, int state) -{ - /* 0 - queue; 1 - add; 2 - member; 3 - ; 4 - to; 5 - ; 6 - penalty; 7 - ; 8 - as; 9 - */ - 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: /* */ - 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 handle_queue_remove_member(int fd, int argc, char *argv[]) -{ - char *queuename, *interface; - - if (argc != 6) { - return RESULT_SHOWUSAGE; - } else if (strcmp(argv[4], "from")) { - return RESULT_SHOWUSAGE; - } - - queuename = argv[5]; - interface = argv[3]; - - switch (remove_from_queue(queuename, interface)) { - case RES_OKAY: - ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); - ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); - return RESULT_SUCCESS; - case RES_EXISTS: - ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); - return RESULT_FAILURE; - case RES_NOSUCHQUEUE: - ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); - return RESULT_FAILURE; - case RES_OUTOFMEMORY: - ast_cli(fd, "Out of memory\n"); - return RESULT_FAILURE; - case RES_NOT_DYNAMIC: - ast_cli(fd, "Member not dynamic\n"); - return RESULT_FAILURE; - default: - return RESULT_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 mem_iter; - - /* 0 - queue; 1 - remove; 2 - member; 3 - ; 4 - from; 5 - */ - 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, */ - if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ - AST_LIST_TRAVERSE(&queues, q, list) { - ast_mutex_lock(&q->lock); - mem_iter = ao2_iterator_init(q->members, 0); - while ((m = ao2_iterator_next(&mem_iter))) { - if (++which > state) { - char *tmp; - ast_mutex_unlock(&q->lock); - tmp = ast_strdup(m->interface); - ao2_ref(m, -1); - return tmp; - } - ao2_ref(m, -1); - } - ast_mutex_unlock(&q->lock); - } - } - - return NULL; -} - -static char queue_show_usage[] = -"Usage: queue show\n" -" Provides summary information on a specified queue.\n"; - -static char qam_cmd_usage[] = -"Usage: queue add member to [penalty ]\n"; - -static char qrm_cmd_usage[] = -"Usage: queue remove member from \n"; - -static struct ast_cli_entry cli_show_queue_deprecated = { - { "show", "queue", NULL }, - queue_show, NULL, - NULL, complete_queue_show }; - -static struct ast_cli_entry cli_add_queue_member_deprecated = { - { "add", "queue", "member", NULL }, - handle_queue_add_member, NULL, - NULL, complete_queue_add_member }; - -static struct ast_cli_entry cli_remove_queue_member_deprecated = { - { "remove", "queue", "member", NULL }, - handle_queue_remove_member, NULL, - NULL, complete_queue_remove_member }; - -static struct ast_cli_entry cli_queue[] = { - /* Deprecated */ - { { "show", "queues", NULL }, - queue_show, NULL, - NULL, NULL }, - - { { "queue", "show", NULL }, - queue_show, "Show status of a specified queue", - queue_show_usage, complete_queue_show, &cli_show_queue_deprecated }, - - { { "queue", "add", "member", NULL }, - handle_queue_add_member, "Add a channel to a specified queue", - qam_cmd_usage, complete_queue_add_member, &cli_add_queue_member_deprecated }, - - { { "queue", "remove", "member", NULL }, - handle_queue_remove_member, "Removes a channel from a specified queue", - qrm_cmd_usage, complete_queue_remove_member, &cli_remove_queue_member_deprecated }, -}; - -static int unload_module(void) -{ - int res; - - 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("QueueAdd"); - res |= ast_manager_unregister("QueueRemove"); - res |= ast_manager_unregister("QueuePause"); - 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(&queueagentcount_function); - res |= ast_custom_function_unregister(&queuemembercount_function); - res |= ast_custom_function_unregister(&queuememberlist_function); - res |= ast_custom_function_unregister(&queuewaitingcount_function); - ast_devstate_del(statechange_queue, NULL); - - ast_module_user_hangup_all(); - - clear_and_free_interfaces(); - - return res; -} - -static int load_module(void) -{ - int res; - - if (!reload_queues()) - return AST_MODULE_LOAD_DECLINE; - - 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("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_custom_function_register(&queueagentcount_function); - res |= ast_custom_function_register(&queuemembercount_function); - res |= ast_custom_function_register(&queuememberlist_function); - res |= ast_custom_function_register(&queuewaitingcount_function); - res |= ast_devstate_add(statechange_queue, NULL); - - return res; -} - -static int reload(void) -{ - reload_queues(); - 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/apps/app_random.c b/apps/app_random.c deleted file mode 100644 index 8484f656d..000000000 --- a/apps/app_random.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (c) 2003 - 2005 Tilghman Lesher. All rights reserved. - * - * Tilghman Lesher - * - * This code is released by the author with no restrictions on usage or distribution. - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly 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 Random application - * - * \author Tilghman Lesher - * \ingroup applications - */ - -#include "asterisk.h" - -ASTERISK_FILE_VERSION(__FILE__, "$Revision$") - -#include -#include -#include -#include - -#include "asterisk/file.h" -#include "asterisk/logger.h" -#include "asterisk/options.h" -#include "asterisk/channel.h" -#include "asterisk/pbx.h" -#include "asterisk/module.h" - -/*! \todo The Random() app should be removed from trunk following the release of 1.4 */ - -static char *app_random = "Random"; - -static char *random_synopsis = "Conditionally branches, based upon a probability"; - -static char *random_descrip = -"Random([probability]:[[context|]extension|]priority)\n" -" probability := INTEGER in the range 1 to 100\n" -"DEPRECATED: Use GotoIf($[${RAND(1,100)} > ]?