From df9992620d33daba30758dd15632b471bf01b3f6 Mon Sep 17 00:00:00 2001 From: Victor Seva Date: Fri, 6 Jun 2014 09:25:41 +0200 Subject: [PATCH 1/8] Imported Upstream version 1.6.0~20140516~eaa616 --- CMakeLists.txt | 34 +- Makefile | 39 +- Makefile.defs | 133 +- README | 13 +- apps/CMakeLists.txt | 13 +- apps/Makefile | 6 +- apps/ann_b2b/AnnounceB2B.cpp | 20 +- apps/ann_b2b/AnnounceB2B.h | 6 +- apps/announce_transfer/AnnounceTransfer.cpp | 68 +- apps/announce_transfer/AnnounceTransfer.h | 9 +- apps/announcement/Announcement.cpp | 55 +- apps/announcement/Announcement.h | 11 +- apps/annrecorder/AnnRecorder.cpp | 53 +- apps/annrecorder/AnnRecorder.h | 11 +- apps/callback/CallBack.cpp | 48 +- apps/callback/CallBack.h | 12 +- apps/click2dial/Click2Dial.cpp | 75 +- apps/click2dial/Click2Dial.h | 13 +- apps/conf_auth/conf_auth.py | 2 +- apps/conference/Conference.cpp | 254 +- apps/conference/Conference.h | 17 +- apps/db_reg_agent/CMakeLists.txt | 12 + apps/db_reg_agent/DBRegAgent.cpp | 1340 +++++ apps/db_reg_agent/DBRegAgent.h | 274 ++ apps/db_reg_agent/Makefile | 9 + apps/db_reg_agent/RegistrationTimer.cpp | 301 ++ apps/db_reg_agent/RegistrationTimer.h | 115 + apps/db_reg_agent/etc/db_reg_agent.conf | 99 + apps/diameter_client/Makefile | 6 +- apps/diameter_client/ServerConnection.cpp | 2 +- apps/diameter_client/lib_dbase/Makefile | 26 +- apps/dsm/DSM.cpp | 59 +- apps/dsm/DSM.h | 14 +- apps/dsm/DSMCall.cpp | 413 +- apps/dsm/DSMCall.h | 63 +- apps/dsm/DSMChartReader.cpp | 2 +- apps/dsm/DSMChartReader.h | 6 +- apps/dsm/DSMCoreModule.cpp | 256 +- apps/dsm/DSMCoreModule.h | 18 + apps/dsm/DSMModule.cpp | 18 +- apps/dsm/DSMModule.h | 35 +- apps/dsm/DSMSession.h | 47 +- apps/dsm/DSMStateEngine.cpp | 60 +- apps/dsm/DSMStateEngine.h | 59 +- apps/dsm/SystemDSM.cpp | 56 +- apps/dsm/SystemDSM.h | 15 +- apps/dsm/etc/dsm.conf | 4 +- apps/dsm/mods/Makefile | 4 +- apps/dsm/mods/Makefile.dsm_module | 37 +- .../dsm/mods/mod_conference/ModConference.cpp | 8 +- apps/dsm/mods/mod_conference/ModConference.h | 6 +- apps/dsm/mods/mod_curl/Makefile | 14 + apps/dsm/mods/mod_curl/ModCurl.cpp | 415 ++ apps/dsm/mods/mod_curl/ModCurl.h | 52 + apps/dsm/mods/mod_dlg/ModDlg.cpp | 358 +- apps/dsm/mods/mod_dlg/ModDlg.h | 17 + .../dsm/mods/mod_monitoring/ModMonitoring.cpp | 63 + apps/dsm/mods/mod_monitoring/ModMonitoring.h | 6 + apps/dsm/mods/mod_mysql/ModMysql.cpp | 84 +- apps/dsm/mods/mod_mysql/ModMysql.h | 7 +- apps/dsm/mods/mod_py/ModPy.cpp | 4 +- apps/dsm/mods/mod_py/ModPy.h | 4 +- apps/dsm/mods/mod_py/PyDSMSession.cpp | 21 +- apps/dsm/mods/mod_regex/CMakeLists.txt | 7 + apps/dsm/mods/mod_regex/Makefile | 10 + apps/dsm/mods/mod_regex/ModRegex.cpp | 181 + apps/dsm/mods/mod_regex/ModRegex.h | 65 + apps/dsm/mods/mod_regex/etc/mod_regex.conf | 6 + apps/dsm/mods/mod_sbc/Makefile | 12 + apps/dsm/mods/mod_sbc/ModSbc.cpp | 568 +++ apps/dsm/mods/mod_sbc/ModSbc.h | 65 + apps/dsm/mods/mod_subscription/Makefile | 10 + .../mods/mod_subscription/ModSubscription.cpp | 102 + .../mods/mod_subscription/ModSubscription.h} | 35 +- apps/dsm/mods/mod_sys/ModSys.cpp | 23 +- apps/dsm/mods/mod_uri/ModUri.cpp | 59 + apps/dsm/mods/mod_uri/ModUri.h | 4 + apps/dsm/mods/mod_utils/ModUtils.cpp | 31 + apps/dsm/mods/mod_utils/ModUtils.h | 3 + apps/dsm/mods/mod_xml/Makefile | 11 + apps/dsm/mods/mod_xml/ModXml.cpp | 280 ++ apps/dsm/mods/mod_xml/ModXml.h | 75 + apps/early_announce/EarlyAnnounce.cpp | 45 +- apps/early_announce/EarlyAnnounce.h | 7 +- apps/early_dbprompt/early_dbprompt.dsm | 70 + apps/early_dbprompt/etc/early_dbprompt.conf | 20 + apps/examples/announce_auth/AnnounceAuth.cpp | 184 +- apps/examples/announce_auth/AnnounceAuth.h | 78 +- apps/examples/b2b_connect/b2b_connect.cpp | 81 +- apps/examples/b2b_connect/b2b_connect.h | 12 +- apps/examples/cacheannounce/CacheAnnounce.cpp | 15 +- apps/examples/cacheannounce/CacheAnnounce.h | 6 +- apps/examples/call_gen/CallGen.cpp | 102 +- apps/examples/call_gen/CallGen.h | 18 +- apps/examples/call_gen/Readme.callgen | 2 + apps/examples/call_gen/etc/callgen.conf | 5 +- apps/examples/dtmftester/DtmfTester.cpp | 38 +- apps/examples/dtmftester/DtmfTester.h | 10 +- apps/examples/early_record/EarlyRecord.cpp | 34 +- apps/examples/early_record/EarlyRecord.h | 12 +- apps/examples/ivr_examples/b2b_out.py | 2 +- apps/examples/ivr_examples/b2bua.py | 4 +- apps/examples/ivr_examples/db_balance.py | 2 +- apps/examples/ivr_examples/play.py | 42 +- apps/examples/ivr_examples/rec.py | 2 +- apps/examples/ivr_examples/test_mp3.py | 2 +- apps/examples/ivr_examples/tmpfile.py | 2 +- apps/examples/jukecall/Jukecall.cpp | 15 +- apps/examples/jukecall/Jukecall.h | 5 +- apps/examples/out_of_tree/Makefile | 10 + apps/examples/out_of_tree/MyApp.cpp | 42 + apps/examples/out_of_tree/MyApp.h | 28 + apps/examples/out_of_tree/Readme.myapp | 15 + .../pinauthconference/PinAuthConference.cpp | 22 +- .../pinauthconference/PinAuthConference.h | 8 +- apps/examples/serviceline/ServiceLine.cpp | 27 +- apps/examples/serviceline/ServiceLine.h | 7 +- .../simple_conference/SimpleConference.cpp | 12 +- .../simple_conference/SimpleConference.h | 8 +- apps/examples/tutorial/cc_acc/CCAcc.cpp | 2 +- .../tutorial/myannounceapp/MyAnnounceApp.cpp | 6 +- .../tutorial/myannounceapp/MyAnnounceApp.h | 5 +- apps/examples/tutorial/myapp/MyApp.cpp | 5 +- apps/examples/tutorial/myapp/MyApp.h | 5 +- apps/examples/tutorial/mycc/MyCC.cpp | 17 +- apps/examples/tutorial/mycc/MyCC.h | 5 +- .../myconfigurableapp/MyConfigurableApp.cpp | 7 +- .../myconfigurableapp/MyConfigurableApp.h | 5 +- .../examples/tutorial/myjukebox/MyJukebox.cpp | 7 +- apps/examples/tutorial/myjukebox/MyJukebox.h | 5 +- apps/examples/urlcatcher/UrlCatcher.cpp | 6 +- apps/examples/urlcatcher/UrlCatcher.h | 3 +- apps/gateway/GWSession.cpp | 8 +- apps/gateway/GWSession.h | 4 +- apps/ivr/Ivr.cpp | 154 +- apps/ivr/Ivr.h | 12 +- apps/ivr/IvrDialogBase.cpp | 141 +- apps/ivr/IvrSipDialog.cpp | 51 +- apps/ivr/IvrSipReply.cpp | 20 +- apps/ivr/IvrSipRequest.cpp | 13 +- apps/ivr/IvrUAC.cpp | 4 +- apps/ivr/Makefile.ivr_application | 2 +- apps/jsonrpc/CMakeLists.txt | 2 +- apps/jsonrpc/JsonRPCEvents.h | 2 +- apps/mailbox/mailbox.py | 4 +- apps/mailbox/mailbox_query.py | 4 +- apps/mobile_push/etc/mobile_push.conf | 45 + apps/mobile_push/load_test/call_gen.py | 5 + apps/mobile_push/load_test/example_server.py | 36 + .../load_test/mobilepush_loadtest.txt | 105 + apps/mobile_push/load_test/reg_client.py | 20 + .../load_test/sems_cfg/core/etc/callgen.conf | 3 + .../sems_cfg/core/etc/db_reg_agent.conf | 99 + .../load_test/sems_cfg/core/etc/sems.conf | 544 ++ .../sems_cfg/core/etc/xmlrpc2di.conf | 42 + apps/mobile_push/mobile_push.dsm | 203 + apps/monitoring/CMakeLists.txt | 8 + apps/monitoring/Makefile | 5 + apps/monitoring/Monitoring.cpp | 287 +- apps/monitoring/Monitoring.h | 27 + apps/monitoring/etc/monitoring.conf | 6 + apps/monitoring/tools/Makefile | 17 + apps/monitoring/tools/sems-get-callproperties | 15 + apps/monitoring/tools/sems-list-active-calls | 23 + apps/monitoring/tools/sems-list-calls | 22 + .../monitoring/tools/sems-list-finished-calls | 7 + apps/msg_storage/MsgStorageAPI.h | 2 +- apps/mwi/mwi.cpp | 20 +- apps/pin_collect/pin_collect.py | 2 +- apps/precoded_announce/PrecodedAnnounce.cpp | 35 +- apps/precoded_announce/PrecodedAnnounce.h | 14 +- apps/py_sems/PySems.cpp | 10 - apps/py_sems/PySemsB2ABDialog.cpp | 14 +- apps/py_sems/PySemsB2ABDialog.h | 5 +- apps/py_sems/PySemsB2BDialog.cpp | 4 +- apps/py_sems/PySemsB2BDialog.h | 2 +- apps/py_sems/sip/AmMimeBody.sip | 133 + apps/py_sems/sip/AmSipDialog.sip | 38 +- apps/py_sems/sip/PySemsB2ABDialog.sip | 12 +- apps/py_sems/sip/PySemsB2BDialog.sip | 12 +- apps/py_sems/sip/PySemsDialog.sip | 8 +- apps/py_sems/sip/py_sems.sbf | 3 - apps/py_sems/sip/py_sems.sip | 1 + apps/reg_agent/RegistrationAgent.cpp | 30 +- apps/reg_agent/RegistrationAgent.h | 4 +- apps/reg_agent/etc/reg_agent.conf | 9 +- apps/reg_agent/etc/reg_agent.conf.cmake | 9 +- apps/registrar_client/CMakeLists.txt | 1 - apps/registrar_client/ContactInfo.cpp | 384 -- apps/registrar_client/SIPRegistrarClient.cpp | 344 +- apps/registrar_client/SIPRegistrarClient.h | 137 +- apps/rtmp/CMakeLists.txt | 15 + apps/rtmp/Makefile | 31 + apps/rtmp/Rtmp.cpp | 170 + apps/rtmp/Rtmp.h | 95 + apps/rtmp/RtmpAudio.cpp | 289 ++ apps/rtmp/RtmpAudio.h | 100 + apps/rtmp/RtmpConnection.cpp | 789 +++ apps/rtmp/RtmpConnection.h | 142 + apps/rtmp/RtmpSender.cpp | 521 ++ apps/rtmp/RtmpSender.h | 83 + apps/rtmp/RtmpServer.cpp | 172 + apps/rtmp/RtmpServer.h | 62 + apps/rtmp/RtmpSession.cpp | 202 + apps/rtmp/RtmpSession.h | 99 + apps/rtmp/RtmpUtils.cpp | 49 + apps/rtmp/RtmpUtils.h | 94 + apps/rtmp/etc/rtmp.conf | 41 + apps/rtmp/flash_phone/Makefile | 30 + apps/rtmp/flash_phone/sono.as | 264 + apps/rtmp/flash_phone/sono.mxml | 62 + apps/rtmp/librtmp/COPYING | 504 ++ apps/rtmp/librtmp/Makefile | 92 + apps/rtmp/librtmp/amf.c | 1186 +++++ apps/rtmp/librtmp/amf.h | 161 + apps/rtmp/librtmp/bytes.h | 91 + apps/rtmp/librtmp/dh.h | 334 ++ apps/rtmp/librtmp/dhgroups.h | 199 + apps/rtmp/librtmp/handshake.h | 1096 +++++ apps/rtmp/librtmp/hashswf.c | 665 +++ apps/rtmp/librtmp/http.h | 47 + apps/rtmp/librtmp/librtmp.3 | 202 + apps/rtmp/librtmp/librtmp.3.html | 301 ++ apps/rtmp/librtmp/librtmp.pc.in | 12 + apps/rtmp/librtmp/log.c | 220 + apps/rtmp/librtmp/log.h | 63 + apps/rtmp/librtmp/parseurl.c | 285 ++ apps/rtmp/librtmp/rtmp.c | 4366 +++++++++++++++++ apps/rtmp/librtmp/rtmp.h | 349 ++ apps/rtmp/librtmp/rtmp_sys.h | 112 + apps/rtmp/speex2pcm.cxx | 143 + apps/rtmp/test_srv.cxx | 64 + apps/sbc/CallLeg.cpp | 1756 +++++++ apps/sbc/CallLeg.h | 404 ++ apps/sbc/CallLegEvents.h | 176 + apps/sbc/ExtendedCCInterface.h | 104 + apps/sbc/HeaderFilter.cpp | 130 +- apps/sbc/HeaderFilter.h | 31 +- apps/sbc/Makefile | 20 +- apps/sbc/ParamReplacer.cpp | 538 +- apps/sbc/ParamReplacer.h | 45 +- apps/sbc/RateLimit.cpp | 39 + apps/sbc/RateLimit.h | 61 + apps/sbc/RegisterCache.cpp | 1117 +++++ apps/sbc/RegisterCache.h | 362 ++ apps/sbc/RegisterDialog.cpp | 660 +++ apps/sbc/RegisterDialog.h | 86 + apps/sbc/ReplacesMapper.cpp | 171 + apps/sbc/ReplacesMapper.h | 34 + apps/sbc/SBC.cpp | 1231 ++--- apps/sbc/SBC.h | 161 +- apps/sbc/SBCCallControlAPI.h | 81 + apps/sbc/SBCCallLeg.cpp | 1928 ++++++++ apps/sbc/SBCCallLeg.h | 261 + apps/sbc/SBCCallProfile.cpp | 1665 ++++++- apps/sbc/SBCCallProfile.h | 375 +- apps/sbc/SBCCallRegistry.cpp | 79 + apps/sbc/SBCCallRegistry.h | 61 + apps/sbc/SBCEventLog.cpp | 193 + apps/sbc/SBCEventLog.h | 77 + apps/sbc/SBCSimpleRelay.cpp | 479 ++ apps/sbc/SBCSimpleRelay.h | 157 + apps/sbc/SDPFilter.cpp | 188 +- apps/sbc/SDPFilter.h | 9 +- apps/sbc/SessionUpdate.cpp | 38 + apps/sbc/SessionUpdate.h | 94 + apps/sbc/SubscriptionDialog.cpp | 170 + apps/sbc/SubscriptionDialog.h | 78 + apps/sbc/arg_conversion.cpp | 179 + apps/sbc/arg_conversion.h | 9 + apps/sbc/call_control/Makefile | 58 + apps/sbc/call_control/bl_redis/BLRedis.cpp | 385 ++ apps/sbc/call_control/bl_redis/BLRedis.h | 77 + apps/sbc/call_control/bl_redis/Makefile | 15 + .../bl_redis/RedisConnectionPool.cpp | 146 + .../bl_redis/RedisConnectionPool.h | 53 + .../call_control/bl_redis/etc/bl_redis.conf | 18 + .../sbc/call_control/call_timer/CallTimer.cpp | 158 + apps/sbc/call_control/call_timer/CallTimer.h | 52 + apps/sbc/call_control/call_timer/Makefile | 8 + .../call_timer/etc/cc_call_timer.conf | 4 + apps/sbc/call_control/ctl/CCCtl.cpp | 218 + apps/sbc/call_control/ctl/CCCtl.h | 52 + apps/sbc/call_control/ctl/Makefile | 8 + apps/sbc/call_control/dsm/CCDSM.cpp | 285 ++ apps/sbc/call_control/dsm/CCDSM.h | 101 + apps/sbc/call_control/dsm/Makefile | 11 + apps/sbc/call_control/dsm/SBCDSMInstance.cpp | 740 +++ apps/sbc/call_control/dsm/SBCDSMInstance.h | 131 + apps/sbc/call_control/dsm/SBCDSMParams.h | 35 + .../parallel_calls/CCParallelCalls.cpp | 231 + .../parallel_calls/CCParallelCalls.h | 64 + apps/sbc/call_control/parallel_calls/Makefile | 8 + .../parallel_calls/etc/cc_pcalls.conf | 5 + apps/sbc/call_control/prepaid/Makefile | 8 + apps/sbc/call_control/prepaid/Prepaid.cpp | 328 ++ apps/sbc/call_control/prepaid/Prepaid.h | 76 + apps/sbc/call_control/prepaid_xmlrpc/Makefile | 21 + .../prepaid_xmlrpc/PrepaidXMLRPC.cpp | 316 ++ .../prepaid_xmlrpc/PrepaidXMLRPC.h | 80 + .../prepaid_xmlrpc/etc/cc_prepaid_xmlrpc.conf | 11 + .../prepaid_xmlrpc/server/xmlrpcserver.py | 32 + apps/sbc/call_control/registrar/Makefile | 8 + apps/sbc/call_control/registrar/Registrar.cpp | 278 ++ apps/sbc/call_control/registrar/Registrar.h | 59 + apps/sbc/call_control/rest/Makefile | 13 + apps/sbc/call_control/rest/RestModule.cpp | 300 ++ apps/sbc/call_control/rest/RestModule.h | 57 + apps/sbc/call_control/rest/RestParams.cpp | 167 + apps/sbc/call_control/rest/RestParams.h | 35 + apps/sbc/call_control/syslog_cdr/Makefile | 8 + .../sbc/call_control/syslog_cdr/SyslogCDR.cpp | 549 +++ apps/sbc/call_control/syslog_cdr/SyslogCDR.h | 76 + .../syslog_cdr/etc/cc_syslog_cdr.conf | 45 + apps/sbc/call_control/template/CCTemplate.cpp | 338 ++ apps/sbc/call_control/template/CCTemplate.h | 92 + apps/sbc/call_control/template/Makefile | 8 + .../template/Readme.cc_template.txt | 9 + .../template/etc/cc_template.conf | 0 apps/sbc/etc/auth_b2b.sbcprofile.conf | 8 +- apps/sbc/etc/auth_b2b.sbcprofile.conf.cmake | 8 +- apps/sbc/etc/call_timer.sbcprofile.conf | 34 +- apps/sbc/etc/call_timer.sbcprofile.conf.cmake | 34 +- apps/sbc/etc/codecfilter.sbcprofile.conf | 31 +- .../sbc/etc/codecfilter.sbcprofile.conf.cmake | 31 +- apps/sbc/etc/prepaid.sbcprofile.conf | 33 +- apps/sbc/etc/prepaid.sbcprofile.conf.cmake | 32 +- apps/sbc/etc/sbc.conf | 11 + apps/sbc/etc/sbc.conf.cmake | 7 + apps/sbc/etc/sst_b2b.sbcprofile.conf | 10 +- apps/sbc/etc/sst_b2b.sbcprofile.conf.cmake | 11 +- apps/sbc/etc/symmetricrtp.sbcprofile.conf | 9 + .../etc/symmetricrtp.sbcprofile.conf.cmake | 9 + apps/sbc/etc/transparent.sbcprofile.conf | 65 +- .../sbc/etc/transparent.sbcprofile.conf.cmake | 35 +- apps/sbc/sbc_events.h | 13 + apps/sbc/tools/Makefile | 6 +- .../tools/sems-sbc-load-callcontrol-modules | 12 + apps/sbc/tools/sems-sbc-teardown-call | 19 + apps/twit/twit.py | 10 +- apps/voicebox/Voicebox.cpp | 3 +- apps/voicebox/Voicebox.h | 3 +- apps/voicebox/VoiceboxDialog.cpp | 22 +- apps/voicebox/VoiceboxDialog.h | 2 +- apps/voicemail/AnswerMachine.cpp | 85 +- apps/voicemail/AnswerMachine.h | 7 +- apps/voicemail/etc/voicemail.conf.cmake | 2 +- apps/webconference/WebConference.cpp | 45 +- apps/webconference/WebConference.h | 10 +- apps/webconference/WebConferenceDialog.cpp | 347 +- apps/webconference/WebConferenceDialog.h | 26 +- apps/webconference/etc/webconference.conf | 9 + apps/xmlrpc2di/Makefile | 3 +- apps/xmlrpc2di/MultithreadXmlRpcServer.cpp | 10 +- apps/xmlrpc2di/XMLRPC2DI.cpp | 47 +- apps/xmlrpc2di/XMLRPC2DI.h | 6 + apps/xmlrpc2di/etc/xmlrpc2di.conf | 1 + apps/xmlrpc2di/etc/xmlrpc2di.conf.cmake | 2 +- apps/xmlrpc2di/xmlrpc++/Makefile | 3 +- apps/xmlrpc2di/xmlrpc++/src/XmlRpcSource.cpp | 10 +- cmake/CheckSymbolExists.cmake | 83 + cmake/FindIlbc.cmake | 17 + cmake/FindLibevent2.cmake | 17 + cmake/FindLibrtmp.cmake | 17 + cmake/FindMySQLpp.cmake | 9 + core/Am100rel.cpp | 230 + core/Am100rel.h | 47 + core/AmAdvancedAudio.cpp | 117 +- core/AmAdvancedAudio.h | 82 +- core/AmApi.cpp | 64 +- core/AmApi.h | 27 +- core/AmAppTimer.cpp | 258 + core/AmAppTimer.h | 100 + core/AmArg.cpp | 34 +- core/AmArg.h | 57 +- core/AmAudio.cpp | 426 +- core/AmAudio.h | 182 +- core/AmAudioFile.cpp | 36 +- core/AmAudioMixIn.cpp | 26 +- core/AmAudioMixIn.h | 13 +- core/AmAudioMixer.cpp | 31 +- core/AmAudioMixer.h | 10 +- core/AmB2ABSession.cpp | 77 +- core/AmB2ABSession.h | 36 +- core/AmB2BMedia.cpp | 1285 +++++ core/AmB2BMedia.h | 493 ++ core/AmB2BSession.cpp | 1162 +++-- core/AmB2BSession.h | 225 +- core/AmBasicSipDialog.cpp | 760 +++ core/AmBasicSipDialog.h | 465 ++ core/AmBufferedAudio.cpp | 24 +- core/AmBufferedAudio.h | 3 +- core/AmCachedAudioFile.cpp | 4 +- core/AmConferenceChannel.cpp | 28 +- core/AmConferenceChannel.h | 6 +- core/AmConferenceStatus.cpp | 8 +- core/AmConferenceStatus.h | 5 +- core/AmConfig.cpp | 750 ++- core/AmConfig.h | 135 +- core/AmConfigReader.cpp | 184 +- core/AmConfigReader.h | 17 +- core/AmDtmfDetector.cpp | 67 +- core/AmDtmfDetector.h | 33 +- core/AmDtmfSender.cpp | 123 + core/AmDtmfSender.h | 68 + core/AmEvent.cpp | 28 + core/AmEvent.h | 46 +- core/AmEventDispatcher.cpp | 157 +- core/AmEventDispatcher.h | 44 +- core/AmEventProcessingThread.cpp | 88 + core/AmEventProcessingThread.h | 75 + core/AmEventQueue.cpp | 21 +- core/AmEventQueue.h | 26 +- core/AmEventQueueProcessor.cpp | 154 + core/AmEventQueueProcessor.h | 91 + core/AmJitterBuffer.h | 4 +- core/AmMediaProcessor.cpp | 164 +- core/AmMediaProcessor.h | 131 +- core/AmMimeBody.cpp | 822 ++++ core/AmMimeBody.h | 187 + core/AmMultiPartyMixer.cpp | 288 +- core/AmMultiPartyMixer.h | 55 +- core/AmOfferAnswer.cpp | 509 ++ core/AmOfferAnswer.h | 88 + core/AmPeriodicThread.cpp | 97 + core/AmPeriodicThread.h | 62 + core/AmPlaylist.cpp | 44 +- core/AmPlaylist.h | 26 +- core/AmPlayoutBuffer.cpp | 30 +- core/AmPlayoutBuffer.h | 19 +- core/AmPlugIn.cpp | 259 +- core/AmPlugIn.h | 50 +- core/AmPrecodedFile.cpp | 68 +- core/AmPrecodedFile.h | 39 +- core/AmRingTone.cpp | 14 +- core/AmRtpAudio.cpp | 325 +- core/AmRtpAudio.h | 76 +- core/AmRtpPacket.cpp | 168 +- core/AmRtpPacket.h | 20 +- core/AmRtpReceiver.cpp | 233 +- core/AmRtpReceiver.h | 70 +- core/AmRtpStream.cpp | 1040 +++- core/AmRtpStream.h | 409 +- core/AmSdp.cpp | 955 ++-- core/AmSdp.h | 145 +- core/AmSession.cpp | 1045 ++-- core/AmSession.h | 322 +- core/AmSessionContainer.cpp | 189 +- core/AmSessionContainer.h | 50 +- core/AmSessionEventHandler.cpp | 54 - core/AmSessionEventHandler.h | 54 +- core/AmSipDialog.cpp | 1329 ++--- core/AmSipDialog.h | 337 +- core/AmSipDispatcher.cpp | 89 +- core/AmSipDispatcher.h | 2 +- core/AmSipEvent.cpp | 17 +- core/AmSipEvent.h | 12 +- core/AmSipHeaders.h | 56 +- core/AmSipMsg.cpp | 43 +- core/AmSipMsg.h | 67 +- core/AmSipRegistration.cpp | 364 ++ core/AmSipRegistration.h | 154 + core/AmSipSubscription.cpp | 800 +++ core/AmSipSubscription.h | 309 ++ core/AmSipSubscriptionContainer.cpp | 191 + core/AmSipSubscriptionContainer.h | 66 + core/AmThread.cpp | 61 +- core/AmThread.h | 45 +- core/AmUAC.cpp | 4 +- core/AmUACAuth.cpp | 48 + core/AmUACAuth.h | 26 + core/AmUriParser.cpp | 200 +- core/AmUriParser.h | 26 +- core/AmUtils.cpp | 334 +- core/AmUtils.h | 46 +- core/CMakeLists.txt | 2 + core/LowcFE.cpp | 32 +- core/LowcFE.h | 22 +- core/Makefile | 101 +- core/SampleArray.cc | 7 +- core/SampleArray.h | 11 +- core/SipCtrlInterface.cpp | 638 ++- core/SipCtrlInterface.h | 41 +- core/amci/amci.h | 12 +- core/amci/codecs.h | 32 +- core/ampi/MonitoringAPI.h | 97 + core/ampi/SIPRegistrarClientAPI.h | 28 +- core/ampi/UACAuthAPI.h | 11 +- core/atomic_types.h | 54 +- core/compat/getarch | Bin 8379 -> 0 bytes core/compat/getos | Bin 8377 -> 0 bytes core/etc/sems.conf.cmake | 10 + core/etc/sems.conf.sample | 344 +- core/hash_table.h | 75 +- core/jsonArg.cpp | 68 +- core/jsonArg.h | 4 + core/jsonxx.cpp | 67 +- core/log.cpp | 26 +- core/log.h | 42 +- core/md5.h | 1 - core/plug-in/CMakeLists.txt | 2 + core/plug-in/Makefile.app_module | 45 +- core/plug-in/Makefile.audio_module | 27 +- core/plug-in/adpcm/adpcm.c | 26 +- core/plug-in/echo/Echo.cpp | 21 +- core/plug-in/echo/Echo.h | 7 +- core/plug-in/g722/g722.c | 16 +- core/plug-in/g729/Makefile | 2 +- core/plug-in/g729/g729.c | 2 +- core/plug-in/gsm/gsm.c | 2 +- core/plug-in/ilbc/CMakeLists.txt | 8 +- core/plug-in/ilbc/Makefile | 25 - core/plug-in/ilbc/iLBC_rfc3951/LPCencode.c | 4 +- core/plug-in/ilbc/iLBC_rfc3951/Makefile | 17 +- core/plug-in/ilbc/iLBC_rfc3951/iLBC_decode.c | 4 +- core/plug-in/ilbc/ilbc.c | 14 +- core/plug-in/isac/Makefile | 17 + core/plug-in/isac/isac.c | 145 + core/plug-in/isac/libisac/LICENSE | 29 + core/plug-in/isac/libisac/Makefile | 33 + core/plug-in/isac/libisac/PATENTS | 24 + core/plug-in/isac/libisac/arith_routines.c | 60 + core/plug-in/isac/libisac/arith_routines.h | 63 + .../isac/libisac/arith_routines_hist.c | 291 ++ .../isac/libisac/arith_routines_logist.c | 294 ++ .../isac/libisac/auto_corr_to_refl_coef.c | 103 + core/plug-in/isac/libisac/auto_correlation.c | 141 + .../isac/libisac/bandwidth_estimator.c | 1024 ++++ .../isac/libisac/bandwidth_estimator.h | 177 + core/plug-in/isac/libisac/codec.h | 292 ++ .../isac/libisac/complex_bit_reverse.c | 51 + core/plug-in/isac/libisac/complex_fft.c | 425 ++ .../isac/libisac/copy_set_operations.c | 108 + core/plug-in/isac/libisac/crc.c | 110 + core/plug-in/isac/libisac/crc.h | 46 + core/plug-in/isac/libisac/cross_correlation.c | 271 + core/plug-in/isac/libisac/decode.c | 330 ++ core/plug-in/isac/libisac/decode_bwe.c | 88 + .../isac/libisac/division_operations.c | 144 + .../isac/libisac/dot_product_with_scale.c | 91 + core/plug-in/isac/libisac/downsample_fast.c | 59 + core/plug-in/isac/libisac/encode.c | 1451 ++++++ core/plug-in/isac/libisac/encode_lpc_swb.c | 708 +++ core/plug-in/isac/libisac/encode_lpc_swb.h | 283 ++ core/plug-in/isac/libisac/energy.c | 36 + core/plug-in/isac/libisac/entropy_coding.c | 2748 +++++++++++ core/plug-in/isac/libisac/entropy_coding.h | 412 ++ core/plug-in/isac/libisac/fft.c | 947 ++++ core/plug-in/isac/libisac/fft.h | 45 + core/plug-in/isac/libisac/filter_ar.c | 89 + .../plug-in/isac/libisac/filter_ar_fast_q12.c | 49 + core/plug-in/isac/libisac/filter_functions.c | 271 + .../plug-in/isac/libisac/filter_ma_fast_q12.c | 49 + core/plug-in/isac/libisac/filterbank_tables.c | 37 + core/plug-in/isac/libisac/filterbank_tables.h | 46 + core/plug-in/isac/libisac/filterbanks.c | 346 ++ .../plug-in/isac/libisac/get_hanning_window.c | 77 + .../plug-in/isac/libisac/get_scaling_square.c | 44 + .../isac/libisac/ilbc_specific_functions.c | 120 + core/plug-in/isac/libisac/intialize.c | 175 + core/plug-in/isac/libisac/isac.c | 2797 +++++++++++ core/plug-in/isac/libisac/isac.gypi | 91 + core/plug-in/isac/libisac/isac.h | 729 +++ core/plug-in/isac/libisac/lattice.c | 217 + core/plug-in/isac/libisac/levinson_durbin.c | 259 + core/plug-in/isac/libisac/lpc_analysis.c | 535 ++ core/plug-in/isac/libisac/lpc_analysis.h | 50 + .../isac/libisac/lpc_gain_swb_tables.c | 137 + .../isac/libisac/lpc_gain_swb_tables.h | 49 + .../isac/libisac/lpc_shape_swb12_tables.c | 159 + .../isac/libisac/lpc_shape_swb12_tables.h | 65 + .../isac/libisac/lpc_shape_swb16_tables.c | 248 + .../isac/libisac/lpc_shape_swb16_tables.h | 79 + core/plug-in/isac/libisac/lpc_tables.c | 1129 +++++ core/plug-in/isac/libisac/lpc_tables.h | 114 + core/plug-in/isac/libisac/lpc_to_refl_coef.c | 57 + .../plug-in/isac/libisac/min_max_operations.c | 265 + .../isac/libisac/min_max_operations_neon.c | 47 + .../plug-in/isac/libisac/os_specific_inline.h | 42 + core/plug-in/isac/libisac/pitch_estimator.c | 622 +++ core/plug-in/isac/libisac/pitch_estimator.h | 71 + core/plug-in/isac/libisac/pitch_filter.c | 469 ++ core/plug-in/isac/libisac/pitch_gain_tables.c | 105 + core/plug-in/isac/libisac/pitch_gain_tables.h | 45 + core/plug-in/isac/libisac/pitch_lag_tables.c | 277 ++ core/plug-in/isac/libisac/pitch_lag_tables.h | 114 + .../isac/libisac/randomization_functions.c | 119 + core/plug-in/isac/libisac/refl_coef_to_lpc.c | 60 + core/plug-in/isac/libisac/resample.c | 505 ++ core/plug-in/isac/libisac/resample_48khz.c | 186 + core/plug-in/isac/libisac/resample_by_2.c | 181 + .../isac/libisac/resample_by_2_internal.c | 679 +++ .../isac/libisac/resample_by_2_internal.h | 47 + .../isac/libisac/resample_fractional.c | 242 + core/plug-in/isac/libisac/settings.h | 199 + .../isac/libisac/signal_processing_library.h | 1686 +++++++ .../isac/libisac/spectrum_ar_model_tables.c | 138 + .../isac/libisac/spectrum_ar_model_tables.h | 76 + core/plug-in/isac/libisac/spl_inl.h | 159 + core/plug-in/isac/libisac/spl_inl_armv7.h | 137 + core/plug-in/isac/libisac/spl_sqrt.c | 184 + core/plug-in/isac/libisac/spl_sqrt_floor.c | 53 + core/plug-in/isac/libisac/spl_version.c | 25 + core/plug-in/isac/libisac/splitting_filter.c | 198 + .../libisac/sqrt_of_one_minus_x_squared.c | 35 + core/plug-in/isac/libisac/structs.h | 478 ++ core/plug-in/isac/libisac/transform.c | 131 + core/plug-in/isac/libisac/typedefs.h | 142 + .../isac/libisac/vector_scaling_operations.c | 151 + .../isac/libisac/webrtc_fft_t_1024_8.c | 704 +++ core/plug-in/isac/libisac/webrtc_fft_t_rad.c | 27 + core/plug-in/l16/l16.c | 10 +- core/plug-in/session_timer/CMakeLists.txt | 1 - core/plug-in/session_timer/SessionTimer.cpp | 148 +- core/plug-in/session_timer/SessionTimer.h | 57 +- core/plug-in/session_timer/UserTimer.cpp | 233 - core/plug-in/session_timer/UserTimer.h | 112 - core/plug-in/silk/Makefile | 19 + core/plug-in/silk/silk.c | 281 ++ core/plug-in/speex/speex.c | 307 +- core/plug-in/stats/Makefile | 16 +- core/plug-in/stats/StatsUDPServer.cpp | 25 +- core/plug-in/stats/query_stats.cxx | 16 +- core/plug-in/uac_auth/UACAuth.cpp | 385 +- core/plug-in/uac_auth/UACAuth.h | 104 +- core/plug-in/wav/wav.c | 8 +- core/resample/Makefile | 29 + core/resample/resample.cpp | 804 +++ core/resample/resample.h | 113 + core/sems.cpp | 150 +- core/sems.h | 2 + sems.h.cmake => core/sems.h.cmake | 4 + core/singleton.h | 23 +- core/sip/Makefile | 47 +- core/sip/cstring.h | 10 +- core/sip/defs.h | 84 + core/sip/ip_util.cpp | 135 + core/sip/ip_util.h | 85 + core/sip/msg_fline.cpp | 15 +- core/sip/msg_hdrs.cpp | 74 +- core/sip/msg_hdrs.h | 26 +- core/sip/msg_logger.cpp | 136 + core/sip/msg_logger.h | 68 + core/sip/parse_common.cpp | 63 +- core/sip/parse_common.h | 61 +- core/sip/parse_dns.cpp | 24 +- core/sip/parse_dns.h | 17 +- core/sip/parse_from_to.cpp | 212 +- core/sip/parse_from_to.h | 26 +- core/sip/parse_header.cpp | 132 +- core/sip/parse_header.h | 6 +- core/sip/parse_nameaddr.cpp | 390 ++ core/sip/parse_nameaddr.h | 52 + core/sip/parse_next_hop.cpp | 169 + core/sip/parse_next_hop.h | 23 + core/sip/parse_route.cpp | 26 + core/sip/parse_uri.cpp | 15 + core/sip/parse_uri.h | 2 + core/sip/parse_via.cpp | 10 +- core/sip/pcap_logger.cpp | 154 + core/sip/pcap_logger.h | 24 + core/sip/raw_sender.cpp | 52 + core/sip/raw_sender.h | 19 + core/sip/raw_sock.cpp | 671 +++ core/sip/raw_sock.h | 42 + core/sip/resolver.cpp | 772 ++- core/sip/resolver.h | 158 +- core/sip/sip_parser.cpp | 187 +- core/sip/sip_parser.h | 20 +- core/sip/sip_parser_async.cpp | 290 ++ core/sip/sip_parser_async.h | 44 + core/sip/sip_timers.cpp | 74 + core/sip/sip_timers.h | 82 +- core/sip/sip_trans.cpp | 132 +- core/sip/sip_trans.h | 69 +- core/sip/sip_ua.h | 2 +- core/sip/tcp_trsp.cpp | 823 ++++ core/sip/tcp_trsp.h | 287 ++ core/sip/tr_blacklist.cpp | 129 + core/sip/tr_blacklist.h | 102 + core/sip/trans_layer.cpp | 1911 ++++++-- core/sip/trans_layer.h | 207 +- core/sip/trans_table.cpp | 129 +- core/sip/trans_table.h | 10 +- core/sip/transport.cpp | 70 +- core/sip/transport.h | 77 +- core/sip/udp_trsp.cpp | 255 +- core/sip/udp_trsp.h | 20 +- core/sip/wheeltimer.cpp | 14 +- core/sip/wheeltimer.h | 30 +- core/tests/.gitignore | 1 + core/tests/Makefile | 39 +- core/tests/fct.h | 1162 +++-- core/tests/sems_tests.cpp | 4 + core/tests/test_auth.cpp | 81 + core/tests/test_auth.h | 1 + core/tests/test_headers.cpp | 14 +- core/tests/test_jsonarg.cpp | 3 +- core/tests/test_replaces.cpp | 103 + core/tests/test_replaces.h | 1 + core/tests/test_uriparser.cpp | 136 + core/tests/test_uriparser.h | 0 doc/CHANGELOG | 113 +- doc/COMPILING | 8 + doc/Readme.conference.txt | 6 + doc/Readme.db_reg_agent.txt | 120 + doc/Readme.reg_agent.txt | 7 +- doc/Readme.registrar_client.txt | 3 + doc/Readme.sbc.txt | 612 ++- doc/Readme.sbc_call_control.txt | 180 + doc/Readme.uac_auth.txt | 83 +- doc/Readme.xmlrpc2di.txt | 8 +- doc/dsm/Readme.dsm.txt | 2 +- doc/dsm/dsm_sbc_syntax.txt | 245 + doc/dsm/dsm_syntax.txt | 131 +- doc/dsm/examples/183_early_establish.dsm | 62 +- .../examples/b2b_connect_audio/aas_caller.dsm | 4 +- .../connect_extension.dsm | 7 +- doc/dsm/examples/call/calltest.dsm | 2 +- doc/dsm/examples/dsm_di_monit/Readme_ddm.txt | 4 +- .../dsm_di_monit/dsm_di_monitoring.dsm | 6 +- doc/dsm/examples/early_media.dsm | 43 +- doc/dsm/examples/pin/main.dsm | 6 +- doc/dsm/examples/sbc/sbc_test_app/howto.txt | 18 + .../sbc_test_app_profile.sbcprofile.conf | 27 + .../sbc/sbc_test_app/sbc_test_app.conf | 16 + .../examples/sbc/sbc_test_app/test_sbc.dsm | 63 + doc/dsm/examples/test.dsm | 20 +- doc/dsm/examples/test_b2b.dsm | 11 +- doc/dsm/examples/test_byehdr.dsm | 16 +- doc/dsm/examples/test_conference.dsm | 43 +- doc/dsm/examples/test_conference_mute.dsm | 18 +- doc/dsm/examples/test_exception.dsm | 15 +- doc/dsm/examples/test_remoteunreachable.dsm | 13 + doc/dsm/examples/test_rtptimeout.dsm | 13 + doc/dsm/examples/test_teeconf.dsm | 9 +- doc/dsm/mods/Readme.mod_conference.txt | 2 +- doc/dsm/mods/Readme.mod_curl.txt | 54 + doc/dsm/mods/Readme.mod_dlg.txt | 53 +- doc/dsm/mods/Readme.mod_monitoring.txt | 21 +- doc/dsm/mods/Readme.mod_mysql.txt | 22 +- doc/dsm/mods/Readme.mod_py.txt | 2 +- doc/dsm/mods/Readme.mod_regex.txt | 30 + doc/dsm/mods/Readme.mod_subscription.txt | 43 + doc/dsm/mods/Readme.mod_uri.txt | 23 + doc/dsm/mods/Readme.mod_utils.txt | 7 + doc/dsm/mods/Readme.mod_xml.txt | 32 + .../quizconnect/quizconnect_callee.dsm | 4 +- .../quizconnect/quizconnect_caller.dsm | 2 +- .../quizconnect/quizconnect_tutorial.txt | 8 +- doc/munin-sems-stats-monitoring.pl | 36 + doc/sbc/Readme.bl_redis.txt | 44 + doc/sbc/Readme.cc_acc_xmlrpc | 75 + doc/sbc/Readme.cc_ctl.txt | 51 + doc/sbc/Readme.cc_dsm.txt | 1 + doc/sbc/Readme.cc_registrar.txt | 35 + doc/sbc/Readme.cc_rest.txt | 69 + doc/src/doc_applications.h | 6 + doc/src/doxyref.h | 55 +- pkg/deb/debian/changelog | 112 + pkg/deb/debian/compat | 1 + pkg/deb/debian/control | 54 + pkg/deb/debian/copyright | 34 + pkg/deb/debian/libsems1-dev.install | 13 + pkg/deb/debian/rules | 42 + pkg/deb/debian/sems.default | 25 + pkg/{ => deb}/debian/sems.init | 55 +- pkg/deb/debian/sems.lintian-overrides | 1 + pkg/deb/debian/sems.postinst | 25 + pkg/deb/debian/sems.postrm | 18 + pkg/deb/debian/shlibs.local | 0 pkg/deb/debian/source/format | 1 + pkg/deb/jessie/changelog | 112 + pkg/deb/jessie/compat | 1 + pkg/deb/jessie/control | 54 + pkg/deb/jessie/copyright | 34 + pkg/deb/jessie/libsems1-dev.install | 13 + pkg/deb/jessie/rules | 42 + pkg/deb/jessie/sems.default | 25 + pkg/deb/jessie/sems.init | 108 + pkg/deb/jessie/sems.lintian-overrides | 1 + pkg/deb/jessie/sems.postinst | 25 + pkg/deb/jessie/sems.postrm | 18 + pkg/deb/jessie/shlibs.local | 0 pkg/deb/jessie/source/format | 1 + pkg/deb/precise/changelog | 112 + pkg/deb/precise/compat | 1 + pkg/deb/precise/control | 54 + pkg/deb/precise/copyright | 34 + pkg/deb/precise/libsems1-dev.install | 13 + pkg/deb/precise/rules | 42 + pkg/deb/precise/sems.default | 25 + pkg/deb/precise/sems.init | 108 + pkg/deb/precise/sems.lintian-overrides | 1 + pkg/deb/precise/sems.postinst | 25 + pkg/deb/precise/sems.postrm | 18 + pkg/deb/precise/shlibs.local | 0 pkg/deb/precise/source/format | 1 + pkg/deb/wheezy/changelog | 112 + pkg/deb/wheezy/compat | 1 + pkg/deb/wheezy/control | 54 + pkg/deb/wheezy/copyright | 34 + pkg/deb/wheezy/libsems1-dev.install | 13 + pkg/deb/wheezy/rules | 42 + pkg/deb/wheezy/sems.default | 25 + pkg/deb/wheezy/sems.init | 108 + pkg/deb/wheezy/sems.lintian-overrides | 1 + pkg/deb/wheezy/sems.postinst | 25 + pkg/deb/wheezy/sems.postrm | 18 + pkg/deb/wheezy/shlibs.local | 0 pkg/deb/wheezy/source/format | 1 + pkg/debian/changelog | 143 - pkg/debian/compat | 1 - pkg/debian/control | 26 - pkg/debian/copyright | 19 - pkg/debian/postinst | 192 - pkg/debian/postrm | 13 - pkg/debian/rules | 159 - pkg/debian/sems.config | 26 - pkg/debian/sems.default | 34 - pkg/debian/sems.templates | 44 - pkg/debian/watch | 7 - pkg/rpm/sems.init | 13 +- pkg/rpm/sems.sysconfig | 1 + pkg/rpm/sems.systemd.service | 3 + tools/Makefile | 8 +- tools/logfile-splitter.cpp | 6 +- 827 files changed, 100501 insertions(+), 13550 deletions(-) create mode 100644 apps/db_reg_agent/CMakeLists.txt create mode 100644 apps/db_reg_agent/DBRegAgent.cpp create mode 100644 apps/db_reg_agent/DBRegAgent.h create mode 100644 apps/db_reg_agent/Makefile create mode 100644 apps/db_reg_agent/RegistrationTimer.cpp create mode 100644 apps/db_reg_agent/RegistrationTimer.h create mode 100644 apps/db_reg_agent/etc/db_reg_agent.conf create mode 100644 apps/dsm/mods/mod_curl/Makefile create mode 100644 apps/dsm/mods/mod_curl/ModCurl.cpp create mode 100644 apps/dsm/mods/mod_curl/ModCurl.h create mode 100644 apps/dsm/mods/mod_regex/CMakeLists.txt create mode 100644 apps/dsm/mods/mod_regex/Makefile create mode 100644 apps/dsm/mods/mod_regex/ModRegex.cpp create mode 100644 apps/dsm/mods/mod_regex/ModRegex.h create mode 100644 apps/dsm/mods/mod_regex/etc/mod_regex.conf create mode 100644 apps/dsm/mods/mod_sbc/Makefile create mode 100644 apps/dsm/mods/mod_sbc/ModSbc.cpp create mode 100644 apps/dsm/mods/mod_sbc/ModSbc.h create mode 100644 apps/dsm/mods/mod_subscription/Makefile create mode 100644 apps/dsm/mods/mod_subscription/ModSubscription.cpp rename apps/{registrar_client/ContactInfo.h => dsm/mods/mod_subscription/ModSubscription.h} (70%) create mode 100644 apps/dsm/mods/mod_xml/Makefile create mode 100644 apps/dsm/mods/mod_xml/ModXml.cpp create mode 100644 apps/dsm/mods/mod_xml/ModXml.h create mode 100644 apps/early_dbprompt/early_dbprompt.dsm create mode 100644 apps/early_dbprompt/etc/early_dbprompt.conf create mode 100644 apps/examples/out_of_tree/Makefile create mode 100644 apps/examples/out_of_tree/MyApp.cpp create mode 100644 apps/examples/out_of_tree/MyApp.h create mode 100644 apps/examples/out_of_tree/Readme.myapp create mode 100644 apps/mobile_push/etc/mobile_push.conf create mode 100755 apps/mobile_push/load_test/call_gen.py create mode 100644 apps/mobile_push/load_test/example_server.py create mode 100644 apps/mobile_push/load_test/mobilepush_loadtest.txt create mode 100755 apps/mobile_push/load_test/reg_client.py create mode 100644 apps/mobile_push/load_test/sems_cfg/core/etc/callgen.conf create mode 100644 apps/mobile_push/load_test/sems_cfg/core/etc/db_reg_agent.conf create mode 100644 apps/mobile_push/load_test/sems_cfg/core/etc/sems.conf create mode 100644 apps/mobile_push/load_test/sems_cfg/core/etc/xmlrpc2di.conf create mode 100644 apps/mobile_push/mobile_push.dsm create mode 100644 apps/monitoring/tools/Makefile create mode 100755 apps/monitoring/tools/sems-get-callproperties create mode 100755 apps/monitoring/tools/sems-list-active-calls create mode 100755 apps/monitoring/tools/sems-list-calls create mode 100755 apps/monitoring/tools/sems-list-finished-calls create mode 100644 apps/py_sems/sip/AmMimeBody.sip delete mode 100644 apps/py_sems/sip/py_sems.sbf delete mode 100644 apps/registrar_client/ContactInfo.cpp create mode 100644 apps/rtmp/CMakeLists.txt create mode 100644 apps/rtmp/Makefile create mode 100644 apps/rtmp/Rtmp.cpp create mode 100644 apps/rtmp/Rtmp.h create mode 100644 apps/rtmp/RtmpAudio.cpp create mode 100644 apps/rtmp/RtmpAudio.h create mode 100644 apps/rtmp/RtmpConnection.cpp create mode 100644 apps/rtmp/RtmpConnection.h create mode 100644 apps/rtmp/RtmpSender.cpp create mode 100644 apps/rtmp/RtmpSender.h create mode 100644 apps/rtmp/RtmpServer.cpp create mode 100644 apps/rtmp/RtmpServer.h create mode 100644 apps/rtmp/RtmpSession.cpp create mode 100644 apps/rtmp/RtmpSession.h create mode 100644 apps/rtmp/RtmpUtils.cpp create mode 100644 apps/rtmp/RtmpUtils.h create mode 100644 apps/rtmp/etc/rtmp.conf create mode 100644 apps/rtmp/flash_phone/Makefile create mode 100644 apps/rtmp/flash_phone/sono.as create mode 100644 apps/rtmp/flash_phone/sono.mxml create mode 100644 apps/rtmp/librtmp/COPYING create mode 100644 apps/rtmp/librtmp/Makefile create mode 100644 apps/rtmp/librtmp/amf.c create mode 100644 apps/rtmp/librtmp/amf.h create mode 100644 apps/rtmp/librtmp/bytes.h create mode 100644 apps/rtmp/librtmp/dh.h create mode 100644 apps/rtmp/librtmp/dhgroups.h create mode 100644 apps/rtmp/librtmp/handshake.h create mode 100644 apps/rtmp/librtmp/hashswf.c create mode 100644 apps/rtmp/librtmp/http.h create mode 100644 apps/rtmp/librtmp/librtmp.3 create mode 100644 apps/rtmp/librtmp/librtmp.3.html create mode 100644 apps/rtmp/librtmp/librtmp.pc.in create mode 100644 apps/rtmp/librtmp/log.c create mode 100644 apps/rtmp/librtmp/log.h create mode 100644 apps/rtmp/librtmp/parseurl.c create mode 100644 apps/rtmp/librtmp/rtmp.c create mode 100644 apps/rtmp/librtmp/rtmp.h create mode 100644 apps/rtmp/librtmp/rtmp_sys.h create mode 100644 apps/rtmp/speex2pcm.cxx create mode 100644 apps/rtmp/test_srv.cxx create mode 100644 apps/sbc/CallLeg.cpp create mode 100644 apps/sbc/CallLeg.h create mode 100644 apps/sbc/CallLegEvents.h create mode 100644 apps/sbc/ExtendedCCInterface.h create mode 100644 apps/sbc/RateLimit.cpp create mode 100644 apps/sbc/RateLimit.h create mode 100644 apps/sbc/RegisterCache.cpp create mode 100644 apps/sbc/RegisterCache.h create mode 100644 apps/sbc/RegisterDialog.cpp create mode 100644 apps/sbc/RegisterDialog.h create mode 100644 apps/sbc/ReplacesMapper.cpp create mode 100644 apps/sbc/ReplacesMapper.h create mode 100644 apps/sbc/SBCCallControlAPI.h create mode 100644 apps/sbc/SBCCallLeg.cpp create mode 100644 apps/sbc/SBCCallLeg.h create mode 100644 apps/sbc/SBCCallRegistry.cpp create mode 100644 apps/sbc/SBCCallRegistry.h create mode 100644 apps/sbc/SBCEventLog.cpp create mode 100644 apps/sbc/SBCEventLog.h create mode 100644 apps/sbc/SBCSimpleRelay.cpp create mode 100644 apps/sbc/SBCSimpleRelay.h create mode 100644 apps/sbc/SessionUpdate.cpp create mode 100644 apps/sbc/SessionUpdate.h create mode 100644 apps/sbc/SubscriptionDialog.cpp create mode 100644 apps/sbc/SubscriptionDialog.h create mode 100644 apps/sbc/arg_conversion.cpp create mode 100644 apps/sbc/arg_conversion.h create mode 100644 apps/sbc/call_control/Makefile create mode 100644 apps/sbc/call_control/bl_redis/BLRedis.cpp create mode 100644 apps/sbc/call_control/bl_redis/BLRedis.h create mode 100644 apps/sbc/call_control/bl_redis/Makefile create mode 100644 apps/sbc/call_control/bl_redis/RedisConnectionPool.cpp create mode 100644 apps/sbc/call_control/bl_redis/RedisConnectionPool.h create mode 100644 apps/sbc/call_control/bl_redis/etc/bl_redis.conf create mode 100644 apps/sbc/call_control/call_timer/CallTimer.cpp create mode 100644 apps/sbc/call_control/call_timer/CallTimer.h create mode 100644 apps/sbc/call_control/call_timer/Makefile create mode 100644 apps/sbc/call_control/call_timer/etc/cc_call_timer.conf create mode 100644 apps/sbc/call_control/ctl/CCCtl.cpp create mode 100644 apps/sbc/call_control/ctl/CCCtl.h create mode 100644 apps/sbc/call_control/ctl/Makefile create mode 100644 apps/sbc/call_control/dsm/CCDSM.cpp create mode 100644 apps/sbc/call_control/dsm/CCDSM.h create mode 100644 apps/sbc/call_control/dsm/Makefile create mode 100644 apps/sbc/call_control/dsm/SBCDSMInstance.cpp create mode 100644 apps/sbc/call_control/dsm/SBCDSMInstance.h create mode 100644 apps/sbc/call_control/dsm/SBCDSMParams.h create mode 100644 apps/sbc/call_control/parallel_calls/CCParallelCalls.cpp create mode 100644 apps/sbc/call_control/parallel_calls/CCParallelCalls.h create mode 100644 apps/sbc/call_control/parallel_calls/Makefile create mode 100644 apps/sbc/call_control/parallel_calls/etc/cc_pcalls.conf create mode 100644 apps/sbc/call_control/prepaid/Makefile create mode 100644 apps/sbc/call_control/prepaid/Prepaid.cpp create mode 100644 apps/sbc/call_control/prepaid/Prepaid.h create mode 100644 apps/sbc/call_control/prepaid_xmlrpc/Makefile create mode 100644 apps/sbc/call_control/prepaid_xmlrpc/PrepaidXMLRPC.cpp create mode 100644 apps/sbc/call_control/prepaid_xmlrpc/PrepaidXMLRPC.h create mode 100644 apps/sbc/call_control/prepaid_xmlrpc/etc/cc_prepaid_xmlrpc.conf create mode 100644 apps/sbc/call_control/prepaid_xmlrpc/server/xmlrpcserver.py create mode 100644 apps/sbc/call_control/registrar/Makefile create mode 100644 apps/sbc/call_control/registrar/Registrar.cpp create mode 100644 apps/sbc/call_control/registrar/Registrar.h create mode 100644 apps/sbc/call_control/rest/Makefile create mode 100644 apps/sbc/call_control/rest/RestModule.cpp create mode 100644 apps/sbc/call_control/rest/RestModule.h create mode 100644 apps/sbc/call_control/rest/RestParams.cpp create mode 100644 apps/sbc/call_control/rest/RestParams.h create mode 100644 apps/sbc/call_control/syslog_cdr/Makefile create mode 100644 apps/sbc/call_control/syslog_cdr/SyslogCDR.cpp create mode 100644 apps/sbc/call_control/syslog_cdr/SyslogCDR.h create mode 100644 apps/sbc/call_control/syslog_cdr/etc/cc_syslog_cdr.conf create mode 100644 apps/sbc/call_control/template/CCTemplate.cpp create mode 100644 apps/sbc/call_control/template/CCTemplate.h create mode 100644 apps/sbc/call_control/template/Makefile create mode 100644 apps/sbc/call_control/template/Readme.cc_template.txt rename pkg/debian/shlibs.local => apps/sbc/call_control/template/etc/cc_template.conf (100%) create mode 100644 apps/sbc/sbc_events.h create mode 100755 apps/sbc/tools/sems-sbc-load-callcontrol-modules create mode 100755 apps/sbc/tools/sems-sbc-teardown-call create mode 100644 cmake/CheckSymbolExists.cmake create mode 100644 cmake/FindIlbc.cmake create mode 100644 cmake/FindLibevent2.cmake create mode 100644 cmake/FindLibrtmp.cmake create mode 100644 core/Am100rel.cpp create mode 100644 core/Am100rel.h create mode 100644 core/AmAppTimer.cpp create mode 100644 core/AmAppTimer.h create mode 100644 core/AmB2BMedia.cpp create mode 100644 core/AmB2BMedia.h create mode 100644 core/AmBasicSipDialog.cpp create mode 100644 core/AmBasicSipDialog.h create mode 100644 core/AmDtmfSender.cpp create mode 100644 core/AmDtmfSender.h create mode 100644 core/AmEventProcessingThread.cpp create mode 100644 core/AmEventProcessingThread.h create mode 100644 core/AmEventQueueProcessor.cpp create mode 100644 core/AmEventQueueProcessor.h create mode 100644 core/AmMimeBody.cpp create mode 100644 core/AmMimeBody.h create mode 100644 core/AmOfferAnswer.cpp create mode 100644 core/AmOfferAnswer.h create mode 100644 core/AmPeriodicThread.cpp create mode 100644 core/AmPeriodicThread.h create mode 100644 core/AmSipRegistration.cpp create mode 100644 core/AmSipRegistration.h create mode 100644 core/AmSipSubscription.cpp create mode 100644 core/AmSipSubscription.h create mode 100644 core/AmSipSubscriptionContainer.cpp create mode 100644 core/AmSipSubscriptionContainer.h create mode 100644 core/AmUACAuth.cpp create mode 100644 core/AmUACAuth.h delete mode 100755 core/compat/getarch delete mode 100755 core/compat/getos create mode 100644 core/plug-in/isac/Makefile create mode 100644 core/plug-in/isac/isac.c create mode 100644 core/plug-in/isac/libisac/LICENSE create mode 100644 core/plug-in/isac/libisac/Makefile create mode 100644 core/plug-in/isac/libisac/PATENTS create mode 100644 core/plug-in/isac/libisac/arith_routines.c create mode 100644 core/plug-in/isac/libisac/arith_routines.h create mode 100644 core/plug-in/isac/libisac/arith_routines_hist.c create mode 100644 core/plug-in/isac/libisac/arith_routines_logist.c create mode 100644 core/plug-in/isac/libisac/auto_corr_to_refl_coef.c create mode 100644 core/plug-in/isac/libisac/auto_correlation.c create mode 100644 core/plug-in/isac/libisac/bandwidth_estimator.c create mode 100644 core/plug-in/isac/libisac/bandwidth_estimator.h create mode 100644 core/plug-in/isac/libisac/codec.h create mode 100644 core/plug-in/isac/libisac/complex_bit_reverse.c create mode 100644 core/plug-in/isac/libisac/complex_fft.c create mode 100644 core/plug-in/isac/libisac/copy_set_operations.c create mode 100644 core/plug-in/isac/libisac/crc.c create mode 100644 core/plug-in/isac/libisac/crc.h create mode 100644 core/plug-in/isac/libisac/cross_correlation.c create mode 100644 core/plug-in/isac/libisac/decode.c create mode 100644 core/plug-in/isac/libisac/decode_bwe.c create mode 100644 core/plug-in/isac/libisac/division_operations.c create mode 100644 core/plug-in/isac/libisac/dot_product_with_scale.c create mode 100644 core/plug-in/isac/libisac/downsample_fast.c create mode 100644 core/plug-in/isac/libisac/encode.c create mode 100644 core/plug-in/isac/libisac/encode_lpc_swb.c create mode 100644 core/plug-in/isac/libisac/encode_lpc_swb.h create mode 100644 core/plug-in/isac/libisac/energy.c create mode 100644 core/plug-in/isac/libisac/entropy_coding.c create mode 100644 core/plug-in/isac/libisac/entropy_coding.h create mode 100644 core/plug-in/isac/libisac/fft.c create mode 100644 core/plug-in/isac/libisac/fft.h create mode 100644 core/plug-in/isac/libisac/filter_ar.c create mode 100644 core/plug-in/isac/libisac/filter_ar_fast_q12.c create mode 100644 core/plug-in/isac/libisac/filter_functions.c create mode 100644 core/plug-in/isac/libisac/filter_ma_fast_q12.c create mode 100644 core/plug-in/isac/libisac/filterbank_tables.c create mode 100644 core/plug-in/isac/libisac/filterbank_tables.h create mode 100644 core/plug-in/isac/libisac/filterbanks.c create mode 100644 core/plug-in/isac/libisac/get_hanning_window.c create mode 100644 core/plug-in/isac/libisac/get_scaling_square.c create mode 100644 core/plug-in/isac/libisac/ilbc_specific_functions.c create mode 100644 core/plug-in/isac/libisac/intialize.c create mode 100644 core/plug-in/isac/libisac/isac.c create mode 100644 core/plug-in/isac/libisac/isac.gypi create mode 100644 core/plug-in/isac/libisac/isac.h create mode 100644 core/plug-in/isac/libisac/lattice.c create mode 100644 core/plug-in/isac/libisac/levinson_durbin.c create mode 100644 core/plug-in/isac/libisac/lpc_analysis.c create mode 100644 core/plug-in/isac/libisac/lpc_analysis.h create mode 100644 core/plug-in/isac/libisac/lpc_gain_swb_tables.c create mode 100644 core/plug-in/isac/libisac/lpc_gain_swb_tables.h create mode 100644 core/plug-in/isac/libisac/lpc_shape_swb12_tables.c create mode 100644 core/plug-in/isac/libisac/lpc_shape_swb12_tables.h create mode 100644 core/plug-in/isac/libisac/lpc_shape_swb16_tables.c create mode 100644 core/plug-in/isac/libisac/lpc_shape_swb16_tables.h create mode 100644 core/plug-in/isac/libisac/lpc_tables.c create mode 100644 core/plug-in/isac/libisac/lpc_tables.h create mode 100644 core/plug-in/isac/libisac/lpc_to_refl_coef.c create mode 100644 core/plug-in/isac/libisac/min_max_operations.c create mode 100644 core/plug-in/isac/libisac/min_max_operations_neon.c create mode 100644 core/plug-in/isac/libisac/os_specific_inline.h create mode 100644 core/plug-in/isac/libisac/pitch_estimator.c create mode 100644 core/plug-in/isac/libisac/pitch_estimator.h create mode 100644 core/plug-in/isac/libisac/pitch_filter.c create mode 100644 core/plug-in/isac/libisac/pitch_gain_tables.c create mode 100644 core/plug-in/isac/libisac/pitch_gain_tables.h create mode 100644 core/plug-in/isac/libisac/pitch_lag_tables.c create mode 100644 core/plug-in/isac/libisac/pitch_lag_tables.h create mode 100644 core/plug-in/isac/libisac/randomization_functions.c create mode 100644 core/plug-in/isac/libisac/refl_coef_to_lpc.c create mode 100644 core/plug-in/isac/libisac/resample.c create mode 100644 core/plug-in/isac/libisac/resample_48khz.c create mode 100644 core/plug-in/isac/libisac/resample_by_2.c create mode 100644 core/plug-in/isac/libisac/resample_by_2_internal.c create mode 100644 core/plug-in/isac/libisac/resample_by_2_internal.h create mode 100644 core/plug-in/isac/libisac/resample_fractional.c create mode 100644 core/plug-in/isac/libisac/settings.h create mode 100644 core/plug-in/isac/libisac/signal_processing_library.h create mode 100644 core/plug-in/isac/libisac/spectrum_ar_model_tables.c create mode 100644 core/plug-in/isac/libisac/spectrum_ar_model_tables.h create mode 100644 core/plug-in/isac/libisac/spl_inl.h create mode 100644 core/plug-in/isac/libisac/spl_inl_armv7.h create mode 100644 core/plug-in/isac/libisac/spl_sqrt.c create mode 100644 core/plug-in/isac/libisac/spl_sqrt_floor.c create mode 100644 core/plug-in/isac/libisac/spl_version.c create mode 100644 core/plug-in/isac/libisac/splitting_filter.c create mode 100644 core/plug-in/isac/libisac/sqrt_of_one_minus_x_squared.c create mode 100644 core/plug-in/isac/libisac/structs.h create mode 100644 core/plug-in/isac/libisac/transform.c create mode 100644 core/plug-in/isac/libisac/typedefs.h create mode 100644 core/plug-in/isac/libisac/vector_scaling_operations.c create mode 100644 core/plug-in/isac/libisac/webrtc_fft_t_1024_8.c create mode 100644 core/plug-in/isac/libisac/webrtc_fft_t_rad.c delete mode 100644 core/plug-in/session_timer/UserTimer.cpp delete mode 100644 core/plug-in/session_timer/UserTimer.h create mode 100644 core/plug-in/silk/Makefile create mode 100644 core/plug-in/silk/silk.c create mode 100644 core/resample/Makefile create mode 100644 core/resample/resample.cpp create mode 100644 core/resample/resample.h rename sems.h.cmake => core/sems.h.cmake (96%) create mode 100644 core/sip/defs.h create mode 100644 core/sip/ip_util.cpp create mode 100644 core/sip/ip_util.h create mode 100644 core/sip/msg_logger.cpp create mode 100644 core/sip/msg_logger.h create mode 100644 core/sip/parse_nameaddr.cpp create mode 100644 core/sip/parse_nameaddr.h create mode 100644 core/sip/parse_next_hop.cpp create mode 100644 core/sip/parse_next_hop.h create mode 100644 core/sip/pcap_logger.cpp create mode 100644 core/sip/pcap_logger.h create mode 100644 core/sip/raw_sender.cpp create mode 100644 core/sip/raw_sender.h create mode 100644 core/sip/raw_sock.cpp create mode 100644 core/sip/raw_sock.h create mode 100644 core/sip/sip_parser_async.cpp create mode 100644 core/sip/sip_parser_async.h create mode 100644 core/sip/sip_timers.cpp create mode 100644 core/sip/tcp_trsp.cpp create mode 100644 core/sip/tcp_trsp.h create mode 100644 core/sip/tr_blacklist.cpp create mode 100644 core/sip/tr_blacklist.h create mode 100644 core/tests/.gitignore create mode 100644 core/tests/test_auth.cpp create mode 100644 core/tests/test_auth.h create mode 100644 core/tests/test_replaces.cpp create mode 100644 core/tests/test_replaces.h create mode 100644 core/tests/test_uriparser.cpp create mode 100644 core/tests/test_uriparser.h create mode 100644 doc/Readme.db_reg_agent.txt create mode 100644 doc/Readme.sbc_call_control.txt create mode 100644 doc/dsm/dsm_sbc_syntax.txt create mode 100644 doc/dsm/examples/sbc/sbc_test_app/howto.txt create mode 100644 doc/dsm/examples/sbc/sbc_test_app/profile/sbc_test_app_profile.sbcprofile.conf create mode 100644 doc/dsm/examples/sbc/sbc_test_app/sbc_test_app.conf create mode 100644 doc/dsm/examples/sbc/sbc_test_app/test_sbc.dsm create mode 100644 doc/dsm/examples/test_remoteunreachable.dsm create mode 100644 doc/dsm/examples/test_rtptimeout.dsm create mode 100644 doc/dsm/mods/Readme.mod_curl.txt create mode 100644 doc/dsm/mods/Readme.mod_regex.txt create mode 100644 doc/dsm/mods/Readme.mod_subscription.txt create mode 100644 doc/dsm/mods/Readme.mod_xml.txt create mode 100644 doc/munin-sems-stats-monitoring.pl create mode 100644 doc/sbc/Readme.bl_redis.txt create mode 100644 doc/sbc/Readme.cc_acc_xmlrpc create mode 100644 doc/sbc/Readme.cc_ctl.txt create mode 100644 doc/sbc/Readme.cc_dsm.txt create mode 100644 doc/sbc/Readme.cc_registrar.txt create mode 100644 doc/sbc/Readme.cc_rest.txt create mode 100644 pkg/deb/debian/changelog create mode 100644 pkg/deb/debian/compat create mode 100644 pkg/deb/debian/control create mode 100644 pkg/deb/debian/copyright create mode 100644 pkg/deb/debian/libsems1-dev.install create mode 100755 pkg/deb/debian/rules create mode 100644 pkg/deb/debian/sems.default rename pkg/{ => deb}/debian/sems.init (63%) create mode 100644 pkg/deb/debian/sems.lintian-overrides create mode 100644 pkg/deb/debian/sems.postinst create mode 100644 pkg/deb/debian/sems.postrm create mode 100644 pkg/deb/debian/shlibs.local create mode 100644 pkg/deb/debian/source/format create mode 100644 pkg/deb/jessie/changelog create mode 100644 pkg/deb/jessie/compat create mode 100644 pkg/deb/jessie/control create mode 100644 pkg/deb/jessie/copyright create mode 100644 pkg/deb/jessie/libsems1-dev.install create mode 100755 pkg/deb/jessie/rules create mode 100644 pkg/deb/jessie/sems.default create mode 100644 pkg/deb/jessie/sems.init create mode 100644 pkg/deb/jessie/sems.lintian-overrides create mode 100644 pkg/deb/jessie/sems.postinst create mode 100644 pkg/deb/jessie/sems.postrm create mode 100644 pkg/deb/jessie/shlibs.local create mode 100644 pkg/deb/jessie/source/format create mode 100644 pkg/deb/precise/changelog create mode 100644 pkg/deb/precise/compat create mode 100644 pkg/deb/precise/control create mode 100644 pkg/deb/precise/copyright create mode 100644 pkg/deb/precise/libsems1-dev.install create mode 100755 pkg/deb/precise/rules create mode 100644 pkg/deb/precise/sems.default create mode 100644 pkg/deb/precise/sems.init create mode 100644 pkg/deb/precise/sems.lintian-overrides create mode 100644 pkg/deb/precise/sems.postinst create mode 100644 pkg/deb/precise/sems.postrm create mode 100644 pkg/deb/precise/shlibs.local create mode 100644 pkg/deb/precise/source/format create mode 100644 pkg/deb/wheezy/changelog create mode 100644 pkg/deb/wheezy/compat create mode 100644 pkg/deb/wheezy/control create mode 100644 pkg/deb/wheezy/copyright create mode 100644 pkg/deb/wheezy/libsems1-dev.install create mode 100755 pkg/deb/wheezy/rules create mode 100644 pkg/deb/wheezy/sems.default create mode 100644 pkg/deb/wheezy/sems.init create mode 100644 pkg/deb/wheezy/sems.lintian-overrides create mode 100644 pkg/deb/wheezy/sems.postinst create mode 100644 pkg/deb/wheezy/sems.postrm create mode 100644 pkg/deb/wheezy/shlibs.local create mode 100644 pkg/deb/wheezy/source/format delete mode 100644 pkg/debian/changelog delete mode 100644 pkg/debian/compat delete mode 100644 pkg/debian/control delete mode 100644 pkg/debian/copyright delete mode 100644 pkg/debian/postinst delete mode 100644 pkg/debian/postrm delete mode 100755 pkg/debian/rules delete mode 100644 pkg/debian/sems.config delete mode 100644 pkg/debian/sems.default delete mode 100644 pkg/debian/sems.templates delete mode 100644 pkg/debian/watch create mode 100644 pkg/rpm/sems.sysconfig diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f83c5d8..def471f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,8 @@ set( CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) PROJECT (SEMS) SET(SEMS_MAJOR_VERSION 1) -SET(SEMS_MINOR_VERSION 4) -SET(SEMS_BUILD_VERSION 2) +SET(SEMS_MINOR_VERSION 6) +SET(SEMS_BUILD_VERSION 0) SET(SEMS_EXTRA_VERSION dev) IF(EXISTS ${CMAKE_SOURCE_DIR}/.git) @@ -27,6 +27,8 @@ ENDIF(EXISTS ${CMAKE_SOURCE_DIR}/.git) MESSAGE(STATUS "Configuring ${CMAKE_PROJECT_NAME} v. ${SEMS_VERSION}") +SET(SEMS_APP_NAME "sems") + IF(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND NOT MSVC_IDE) message(FATAL_ERROR "In-source builds are not allowed. CMake would overwrite all makefiles distributed with SEMS. @@ -40,7 +42,7 @@ OPTION(SEMS_USE_SPANDSP "Build with spandsp" OFF) OPTION(SEMS_USE_LIBSAMPLERATE "Build with libsamplerate" OFF) OPTION(SEMS_USE_ZRTP "Build with ZRTP" OFF) OPTION(SEMS_USE_MP3 "Build with Lame MP3" OFF) -OPTION(SEMS_USE_ILBC "Build with bundled iLBC library" ON) +OPTION(SEMS_USE_ILBC "Build with iLBC library (fallback to bundled)" ON) OPTION(SEMS_USE_TTS "Build with Text-to-speech support (requires Flite)" OFF) OPTION(SEMS_USE_OPENSSL "Build with OpenSSL" OFF) OPTION(SEMS_USE_MONITORING "Build with monitoring support" OFF) @@ -84,6 +86,8 @@ MESSAGE(STATUS "SEMS doc prefix was set to: ${SEMS_DOC_PREFIX}") SET(CMAKE_VERBOSE_MAKEFILE ON) +FIND_PACKAGE(Libevent2 REQUIRED) + # compile with spandsp DTMF detection? see soft-switch.org # this needs a fairly new version of spandsp - tested with 0.0.4pre11 # will not work with spandsp 0.0.2 . @@ -124,9 +128,14 @@ ELSE(SEMS_USE_MP3) ENDIF(SEMS_USE_MP3) IF(SEMS_USE_ILBC) - MESSAGE(STATUS "Using bundled iLBC library: YES (default)") + FIND_PACKAGE(Ilbc) + IF(ILBC_FOUND) + MESSAGE(STATUS "Using iLBC library: YES (external)") + ELSE(ILBC_FOUND) + MESSAGE(STATUS "Using iLBC library: YES (bundled, default)") + ENDIF(ILBC_FOUND) ELSE(SEMS_USE_ILBC) - MESSAGE(STATUS "Using bundled iLBC library: NO") + MESSAGE(STATUS "Uusing iLBC library: NO") ENDIF(SEMS_USE_ILBC) # build in support for monitoring? @@ -221,12 +230,25 @@ ELSE(LIBEV_FOUND) MESSAGE(STATUS "Using libev: NO") ENDIF(LIBEV_FOUND) +# Let's try to find librtmp development files +FIND_PACKAGE(Librtmp) +IF(RTMP_FOUND) + MESSAGE(STATUS "Using librtmp: YES") +ELSE(RTMP_FOUND) + MESSAGE(STATUS "Using librtmp: NO") +ENDIF(RTMP_FOUND) + +# Check for res_search in -lresolv +include(${CMAKE_SOURCE_DIR}/cmake/CheckSymbolExists.cmake) +SET(CMAKE_REQUIRED_LIBRARIES resolv) +CHECK_SYMBOL_EXISTS(res_search resolv.h HAVE_RES_SEARCH) +MESSAGE(STATUS "Have res_search: ${HAVE_RES_SEARCH} ") INCLUDE_DIRECTORIES (${SEMS_SOURCE_DIR}/core/ampi) INCLUDE_DIRECTORIES (${SEMS_SOURCE_DIR}/core/amci) INCLUDE_DIRECTORIES (${SEMS_SOURCE_DIR}/core) -CONFIGURE_FILE( sems.h.cmake ${SEMS_SOURCE_DIR}/core/sems.h ) +CONFIGURE_FILE( ${SEMS_SOURCE_DIR}/core/sems.h.cmake ${SEMS_SOURCE_DIR}/core/sems.h ) IF(CMAKE_COMPILER_IS_GNUCC) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_THREAD_SAFE -D_REENTRANT -Wall -fPIC -g -O2") diff --git a/Makefile b/Makefile index 1d023400..dad8d7ec 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,3 @@ -NAME=sems - .DEFAULT_GOAL:=all .PHONY: all @@ -8,6 +6,8 @@ all: modules COREPATH=core include Makefile.defs +NAME=$(APP_NAME) + subdirs = core apps tools .PHONY: clean @@ -43,7 +43,7 @@ install: @echo "" @echo "*** install complete. Run SEMS with " @echo "*** " - @echo "*** $(bin-target)sems -f $(cfg-target)sems.conf" + @echo "*** $(bin-target)$(NAME) -f $(cfg-target)sems.conf" .PHONY: dist @@ -76,6 +76,39 @@ tar: "$(NAME)-$(RELEASE)" ) ; \ rm -rf tmp +# the rpmtar target creates source tar.gz file, with versions taken from rpm spec file +# the tarball can be used for rpm building +.PHONY: rpmtar +rpmtar: + RPM_VERSION=`cat pkg/rpm/sems.spec|grep -e "^Version:"|awk '{print $$2}'`; \ + RPM_RELEASE=`cat pkg/rpm/sems.spec|grep -e "^Release:"|awk '{print $$2}'`; \ + echo "RPM_VERSION=$${RPM_VERSION}"; \ + echo "RPM_RELEASE=$${RPM_RELEASE}"; \ + $(TAR) -C .. \ + --exclude=$(notdir $(CURDIR))/tmp \ + --exclude=core/$(notdir $(CURDIR))/tmp \ + --exclude=.svn* \ + --exclude=.git* \ + --exclude=.\#* \ + --exclude=*.[do] \ + --exclude=*.la \ + --exclude=*.lo \ + --exclude=*.so \ + --exclude=*.il \ + --exclude=*.gz \ + --exclude=*.bz2 \ + --exclude=*.tar \ + --exclude=*~ \ + -cf - $(notdir $(CURDIR)) | \ + (mkdir -p tmp/_tar1; mkdir -p tmp/_tar2 ; \ + cd tmp/_tar1; $(TAR) -xf - ) && \ + mv tmp/_tar1/$(notdir $(CURDIR)) \ + tmp/_tar2/"$(NAME)-$${RPM_VERSION}" && \ + (cd tmp/_tar2 && $(TAR) \ + -zcf ../../"$(NAME)-$${RPM_VERSION}-$${RPM_RELEASE}".tar.gz \ + "$(NAME)-$${RPM_VERSION}" ) ; \ + rm -rf tmp; + .PHONY: doc doc: make -C doc/ doc diff --git a/Makefile.defs b/Makefile.defs index 0d2ea587..0131d769 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -1,39 +1,36 @@ -ifeq ($(makefile_defs),1) +ifeq ($(makefile_defs),"1") else -makefile_defs=1 +makefile_defs="1" export makefile_defs -#GIT = $(shell which git) -#SVNVERSION = $(shell which svnversion) -# -#ifneq ($(GIT),) -# SCM_REV = $(shell if git --help describe |grep dirty 2>&1 >/dev/null ; \ -# then git describe --always --dirty 2>/dev/null; \ -# else git describe --always 2>/dev/null; fi) -#endif -# -#ifeq ($(SCM_REV),) -#ifneq ($(SVNVERSION),) -# SCM_REV = r$(shell svnversion -n .) -#endif -#endif +GIT := $(shell which git) + +ifneq ($(GIT),) + SCM_REV := $(shell if git --help describe |grep dirty 2>&1 >/dev/null ; \ + then git describe --always --dirty 2>/dev/null; \ + else git describe --always 2>/dev/null; fi) +endif #version number VERSION = 1 -PATCHLEVEL = 4 -SUBLEVEL = 3 +PATCHLEVEL = 6 +SUBLEVEL = 0 ifneq ($(SCM_REV),) -RELEASE = $(SCM_REV) +RELEASE := $(SCM_REV) else -REL_VERSION=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL) -RELEASE=$(REL_VERSION)$(EXTRAVERSION) +REL_VERSION := $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) +RELEASE := $(REL_VERSION)$(EXTRAVERSION) endif +APP_NAME ?= sems + CPPFLAGS += -D_DEBUG \ -D_THREAD_SAFE -D_REENTRANT \ -DSEMS_VERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"'\ -DOS='"$(OS)"' \ + -DSYSTEM_SAMPLECLOCK_RATE=32000LL \ + -DSEMS_APP_NAME='"$(APP_NAME)"' # -DMAX_RTP_SESSIONS=8192 \ # -DSUPPORT_IPV6 \ # -DNO_THREADID_LOG \ @@ -68,6 +65,11 @@ CPPFLAGS += -D_DEBUG \ # #USE_LIBSAMPLERATE = yes +# compile with internal resampler? +# +# +USE_INTERNAL_RESAMPLER = yes + # # ZRTP support? (see zfoneproject.com) #WITH_ZRTP = yes @@ -76,7 +78,7 @@ CPPFLAGS += -D_DEBUG \ # exclude some modules from compilation? # e.g. python modules: -exclude_core_modules = sipctrl g729 +exclude_core_modules = g729 silk #exclude_apps_modules = py_sems ivr mailbox pin_collect conf_auth mp3 examples # build in support for monitoring? @@ -92,6 +94,11 @@ LONG_DEBUG_MESSAGE=yes # Is this a debug build or not? debug=no +# Propagate all unparsed SIP headers into AmSipReply. Disabled by default +# because only special applications might need this and it brings higher memory +# demands and affects performance. +#PROPAGATE_UNPARSED_REPLY_HEADERS=no + ################### end of configuration section ####################### @@ -100,39 +107,29 @@ LDFLAGS += -lm GETOS=$(COREPATH)/compat/getos GETARCH=$(COREPATH)/compat/getarch -OS := $(shell $(CC) $(EXTRA_CFLAGS) -o $(GETOS) $(GETOS).c && $(GETOS)) -ARCH := $(shell $(CC) $(EXTRA_CFLAGS) -o $(GETARCH) $(GETARCH).c && $(GETARCH)) + +OS := $(shell if [ -f $(GETOS) ] ; then $(GETOS); else $(CC) $(EXTRA_CFLAGS) -o $(GETOS) $(GETOS).c && $(GETOS); fi) +ARCH := $(shell if [ -f $(GETARCH) ] ; then $(GETARCH); else $(CC) $(EXTRA_CFLAGS) -o $(GETARCH) $(GETARCH).c && $(GETARCH); fi) ifdef USE_THREADPOOL CPPFLAGS += -DSESSION_THREADPOOL endif -ifdef USE_SPANDSP -ifneq ($(spandsp_defs), 1) -spandsp_defs=1 -export spandsp_defs -CPPFLAGS += -DUSE_SPANDSP -D__STDC_LIMIT_MACROS -endif -else +ifndef USE_SPANDSP exclude_core_modules += g722 endif -ifdef USE_LIBSAMPLERATE -ifneq ($(libsrc_defs), 1) -libsrc_defs=1 -export libsrc_defs -CPPFLAGS += -DUSE_LIBSAMPLERATE -endif -endif - ifdef USE_MONITORING CPPFLAGS += -DUSE_MONITORING endif -ifdef LONG_DEBUG_MESSAGE +ifndef LONG_DEBUG_MESSAGE CPPFLAGS += -DLOG_BUFFER_LEN=2048 endif +ifeq ($(PROPAGATE_UNPARSED_REPLY_HEADERS),yes) +CPPFLAGS += -DPROPAGATE_UNPARSED_REPLY_HEADERS +endif # Additions for Solaris support. ifeq ($(OS),solaris) @@ -163,7 +160,7 @@ CXX = g++ CC = gcc LD = $(CC) -CXXFLAGS += -Wall -Wno-reorder -fPIC -g $(EXTRA_CXXFLAGS) +CXXFLAGS += -Wall -Wno-reorder -Wno-unused -fPIC -g $(EXTRA_CXXFLAGS) CFLAGS += -Wall -fPIC -g $(EXTRA_CFLAGS) # only optimize if releasing, as it slows down the build process @@ -186,20 +183,29 @@ endif TARGET= LIB_LDFLAGS = -shared +ifeq ($(LD), clang) +LDFLAG_DYN= +LDFLAG_PTHREAD= +else +LDFLAG_DYN = -rdynamic +LDFLAG_PTHREAD = -lpthread +endif + ifeq ($(OS), linux) - LDFLAGS += -ldl -rdynamic -lpthread + LDFLAGS += -ldl $(LDFLAG_DYN) $(LDFLAG_PTHREAD) + CPPFLAGS += -DPROPAGATE_COREDUMP_SETTINGS else ifeq ($(OS), freebsd) - LDFLAGS += -rdynamic -pthread + LDFLAGS += $(LDFLAG_DYN) $(LDFLAG_PTHREAD) else ifeq ($(OS), openbsd) - LDFLAGS += -rdynamic -pthread + LDFLAGS += $(LDFLAG_DYN) $(LDFLAG_PTHREAD) else ifeq ($(OS), netbsd) - LDFLAGS += -rdynamic -pthread + LDFLAGS += $(LDFLAG_DYN) $(LDFLAG_PTHREAD) else ifeq ($(OS), macosx) - LDFLAGS += -rdynamic -pthread + LDFLAGS += $(LDFLAG_DYN) $(LDFLAG_PTHREAD) LIB_LDFLAGS = -flat_namespace -undefined suppress -bundle CXXFLAGS += -fno-common CFLAGS += -fno-common @@ -216,7 +222,7 @@ ifeq ($(OS), macosx) endif else ifeq ($(OS), solaris) - LDFLAGS+= -fPIC -ldl -lsocket -lnsl -lpthread + LDFLAGS+= -fPIC -ldl -lsocket -lnsl $(LDFLAG_PTHREAD) TARGET=solaris # These don't build on Solaris because of missing Python, invalid make args, or other. exclude_app_modules += py_sems py_comp pin_collect mailbox ivr diameter_client conf_auth mp3 cc_acc_xmlrpc xmlrpc2di @@ -234,18 +240,6 @@ ifeq ($(ARCH),iphone) LDFLAGS += -lgcc_eh endif -ifdef USE_SPANDSP -ifdef LIBSPANDSP_STATIC -LDFLAGS += $(LIBSPANDSP_LDIR)libspandsp.a -else -LDFLAGS += -lspandsp -endif -endif - -ifdef USE_LIBSAMPLERATE -LDFLAGS +=-lsamplerate -endif - ifdef WITH_ZRTP LDFLAGS +=-lzrtp -lbn endif @@ -278,30 +272,30 @@ audio-prefix = $(basedir)$(prefix) lib-prefix = $(basedir)$(prefix) # dirs -cfg-dir = etc/sems/ +cfg-dir = etc/$(APP_NAME)/ bin-dir = sbin/ -modules-dir = lib/sems/plug-in/ -audio-dir = lib/sems/audio/ -lib-dir = lib/sems/ +modules-dir = lib/$(APP_NAME)/plug-in/ +audio-dir = lib/$(APP_NAME)/audio/ +lib-dir = lib/$(APP_NAME)/ ifeq ($(OS), linux) - doc-dir = share/doc/sems/ + doc-dir = share/doc/$(APP_NAME)/ man-dir = share/man/ else ifeq ($(OS), freebsd) - doc-dir = share/doc/sems/ + doc-dir = share/doc/$(APP_NAME)/ man-dir = man/ else ifeq ($(OS), openbsd) - doc-dir = share/doc/sems/ + doc-dir = share/doc/$(APP_NAME)/ man-dir = man/ else ifeq ($(OS), netbsd) - doc-dir = share/doc/sems + doc-dir = share/doc/$(APP_NAME) man-dir = man/ else - doc-dir = doc/sems/ + doc-dir = doc/$(APP_NAME)/ man-dir = man/ endif endif @@ -343,9 +337,10 @@ export cfg-prefix bin-prefix modules-prefix doc-prefix audio-prefix lib-prefix export cfg-dir bin-dir modules-dir audio-dir lib-dir doc-dir man-dir export bin-target cfg-target app-cfg-target modules-target audio-target lib-target export INSTALL TAR INSTALL-TOUCH INSTALL-CFG INSTALL-BIN INSTALL-MODULES INSTALL-DOC INSTALL-AUDIO +export APP_NAME export USE_SPANDSP LIBSPANDSP_STATIC LIBSPANDSP_LDIR -export USE_LIBSAMPLERATE +export USE_LIBSAMPLERATE USE_INTERNAL_RESAMPLER export WITH_ZRTP export USE_MONITORING export exclude_core_modules exclude_app_modules diff --git a/README b/README index cf4aa18d..7760aeee 100644 --- a/README +++ b/README @@ -215,22 +215,27 @@ How to get started with SEMS: applications, see the explanation of 'application' parameter in sems.conf. -Creating packages on debian (ubuntu, ...): +Creating packages on debian (ubuntu, ...), here for wheezy: install debian package build tools: $ sudo apt-get install debhelper devscripts install dependencies: $ sudo apt-get install g++ make libspandsp-dev flite-dev libspeex-dev \ - libssl-dev python-dev python-sip4-dev openssl sip4 libev-dev \ - libmysql++-dev + libssl-dev python-dev python-sip-dev openssl libev-dev \ + libmysql++-dev libevent-dev libxml2-dev libcurl4-openssl-dev get the source: $ wget ftp.iptel.org/pub/sems/sems-x.y.z.tar.gz ; tar xzvf sems-x.y.z.tar.gz - $ cd sems-x.y.z ; ln -s pkg/debian . + or, for git master: + $ git clone git://git.sip-router.org/sems + + $ cd sems-x.y.z ; ln -s pkg/deb/debian . set version in changelog if not correct $ dch -v x.y.z "SEMS x.y.z release" + or: + $ dch -b -v `git describe --always` "sems git master" build package: $ dpkg-buildpackage -rfakeroot -us -uc diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 5b082b4e..e2668eb9 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -8,6 +8,9 @@ IF(PYTHONLIBS_FOUND) ADD_SUBDIRECTORY(conf_auth) ENDIF(PYTHONLIBS_FOUND) ADD_SUBDIRECTORY(conference) +IF(MYSQLPP_FOUND) + ADD_SUBDIRECTORY(db_reg_agent) +ENDIF(MYSQLPP_FOUND) IF(OPENSSL_FOUND) ADD_SUBDIRECTORY(diameter_client) ENDIF(OPENSSL_FOUND) @@ -15,7 +18,7 @@ ADD_SUBDIRECTORY(dsm) ADD_SUBDIRECTORY(early_announce) #ADD_SUBDIRECTORY(examples) IF(MISDN_FOUND) - # ADD_SUBDIRECTORY(gateway) +# ADD_SUBDIRECTORY(gateway) ENDIF(MISDN_FOUND) IF(PYTHONLIBS_FOUND) ADD_SUBDIRECTORY(ivr) @@ -31,6 +34,7 @@ IF(LAME_FOUND) ADD_SUBDIRECTORY(mp3) ENDIF(LAME_FOUND) ADD_SUBDIRECTORY(msg_storage) +ADD_SUBDIRECTORY(mwi) IF(PYTHONLIBS_FOUND) ADD_SUBDIRECTORY(pin_collect) ENDIF(PYTHONLIBS_FOUND) @@ -42,10 +46,13 @@ ENDIF(SIP_FOUND) ENDIF(PYTHONLIBS_FOUND) ADD_SUBDIRECTORY(reg_agent) ADD_SUBDIRECTORY(registrar_client) +IF(RTMP_FOUND) + ADD_SUBDIRECTORY(rtmp) +ENDIF(RTMP_FOUND) ADD_SUBDIRECTORY(sbc) -#IF(PYTHONLIBS_FOUND) +IF(PYTHONLIBS_FOUND) # ADD_SUBDIRECTORY(twit) -#ENDIF(PYTHONLIBS_FOUND) +ENDIF(PYTHONLIBS_FOUND) ADD_SUBDIRECTORY(voicebox) ADD_SUBDIRECTORY(voicemail) ADD_SUBDIRECTORY(webconference) diff --git a/apps/Makefile b/apps/Makefile index f9470590..42b0509f 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -23,7 +23,7 @@ else ifneq ($(local_exclude_app_modules),) exclude_app_modules = $(local_exclude_app_modules) else - exclude_app_modules += mp3 examples py_sems xmlrpc2di gateway jsonrpc twit + exclude_app_modules += mp3 examples py_sems gateway jsonrpc twit db_reg_agent ifneq ($(USE_MONITORING), yes) exclude_app_modules += monitoring @@ -51,6 +51,7 @@ clean: -@for r in $(app_modules) "" ; do \ if [ -n "$$r" ]; then \ echo "" ; \ + echo "Cleaning $$r" ; \ echo "" ; \ COREPATH=../$(COREPATH) $(MAKE) -C $$r clean ; \ fi ; \ @@ -61,6 +62,7 @@ modules: -@for r in $(app_modules) "" ; do \ if [ -n "$$r" ]; then \ echo "" ; \ + echo "Making $$r" ; \ echo "" ; \ COREPATH=../$(COREPATH) $(MAKE) -C $$r all; \ fi ; \ @@ -74,6 +76,7 @@ install-bin: -@for r in $(app_modules) "" ; do \ if [ -n "$$r" ]; then \ echo "" ; \ + echo "Installing $$r binaries" ; \ echo "" ; \ COREPATH=../$(COREPATH) $(MAKE) -C $$r install; \ fi ; \ @@ -84,6 +87,7 @@ install-cfg: $(DESTDIR)$(cfg-target) -@for r in $(app_modules) "" ; do \ if [ -n "$$r" ]; then \ echo "" ; \ + echo "Installing $$r configuration" ; \ echo "" ; \ COREPATH=../$(COREPATH) $(MAKE) -C $$r install-cfg; \ fi ; \ diff --git a/apps/ann_b2b/AnnounceB2B.cpp b/apps/ann_b2b/AnnounceB2B.cpp index ec285721..1ff855a1 100644 --- a/apps/ann_b2b/AnnounceB2B.cpp +++ b/apps/ann_b2b/AnnounceB2B.cpp @@ -73,7 +73,8 @@ int AnnounceB2BFactory::onLoad() return 0; } -AmSession* AnnounceB2BFactory::onInvite(const AmSipRequest& req) +AmSession* AnnounceB2BFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { string announce_path = AnnouncePath; string announce_file = announce_path + req.domain @@ -101,21 +102,26 @@ AnnounceCallerDialog::AnnounceCallerDialog(const string& filename) set_sip_relay_only(false); } -void AnnounceCallerDialog::onSessionStart(const AmSipRequest& req) +void AnnounceCallerDialog::onInvite(const AmSipRequest& req) { - // we can drop all received packets - // this disables DTMF detection as well - setReceiving(false); - callee_addr = req.to; callee_uri = req.r_uri; - AmB2BCallerSession::onSessionStart(req); + AmB2BCallerSession::onInvite(req); +} + +void AnnounceCallerDialog::onSessionStart() +{ + // we can drop all received packets + // this disables DTMF detection as well + setReceiving(false); if(wav_file.open(filename,AmAudioFile::Read)) throw string("AnnouncementDialog::onSessionStart: Cannot open file\n"); setOutput(&wav_file); + + AmB2BCallerSession::onSessionStart(); } void AnnounceCallerDialog::process(AmEvent* event) diff --git a/apps/ann_b2b/AnnounceB2B.h b/apps/ann_b2b/AnnounceB2B.h index cc39802e..f2bd1046 100644 --- a/apps/ann_b2b/AnnounceB2B.h +++ b/apps/ann_b2b/AnnounceB2B.h @@ -44,7 +44,8 @@ public: AnnounceB2BFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; /** \brief Session logic implementation of A leg in announce_b2b sessions */ @@ -60,7 +61,8 @@ public: AnnounceCallerDialog(const string& filename); void process(AmEvent* event); - void onSessionStart(const AmSipRequest& req); + void onInvite(const AmSipRequest& req); + void onSessionStart(); }; #endif diff --git a/apps/announce_transfer/AnnounceTransfer.cpp b/apps/announce_transfer/AnnounceTransfer.cpp index 6965a0fe..89b8bbc5 100644 --- a/apps/announce_transfer/AnnounceTransfer.cpp +++ b/apps/announce_transfer/AnnounceTransfer.cpp @@ -68,7 +68,8 @@ int AnnounceTransferFactory::onLoad() return 0; } -AmSession* AnnounceTransferFactory::onInvite(const AmSipRequest& req) +AmSession* AnnounceTransferFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { string announce_path = AnnouncePath; string announce_file = announce_path + req.domain @@ -99,15 +100,8 @@ AnnounceTransferDialog::~AnnounceTransferDialog() { } -void AnnounceTransferDialog::onSessionStart(const AmSipRequest& req) -{ - // we can drop all received packets - // this disables DTMF detection as well - setReceiving(false); - - DBG("AnnounceTransferDialog::onSessionStart\n"); +void AnnounceTransferDialog::onInvite(const AmSipRequest& req) { if (status == Disconnected) { - status = Announcing; callee_uri = get_session_param(req.hdrs, "Refer-To"); if (!callee_uri.length()) { callee_uri = getHeader(req.hdrs, "P-Refer-To", true); @@ -119,11 +113,27 @@ void AnnounceTransferDialog::onSessionStart(const AmSipRequest& req) if (!callee_uri.length()) callee_uri = req.r_uri; DBG("transfer uri set to '%s'\n", callee_uri.c_str()); + } + + AmSession::onInvite(req); +} + +void AnnounceTransferDialog::onSessionStart() +{ + // we can drop all received packets + // this disables DTMF detection as well + setReceiving(false); + + DBG("AnnounceTransferDialog::onSessionStart\n"); + if (status == Disconnected) { + status = Announcing; startSession(); } + + AmSession::onSessionStart(); } -void AnnounceTransferDialog::startSession(){ +void AnnounceTransferDialog::startSession() { if(wav_file.open(filename,AmAudioFile::Read)) throw string("AnnounceTransferDialog::onSessionStart: Cannot open file\n"); @@ -132,8 +142,6 @@ void AnnounceTransferDialog::startSession(){ void AnnounceTransferDialog::onSipRequest(const AmSipRequest& req) { - AmSession::onSipRequest(req); - if((status == Transfering || status == Hangup) && (req.method == "NOTIFY")) { try { @@ -141,19 +149,21 @@ void AnnounceTransferDialog::onSipRequest(const AmSipRequest& req) if (strip_header_params(getHeader(req.hdrs,"Event", "o", true)) != "refer") throw AmSession::Exception(481, "Subscription does not exist"); - if ((strip_header_params(req.content_type) != "message/sipfrag")) + if (!req.body.isContentType("message/sipfrag")) throw AmSession::Exception(415, "Unsupported Media Type"); - if (req.body.length()<8) + string body((const char*)req.body.getPayload(), + req.body.getLen()); + + if (body.length()<8) throw AmSession::Exception(400, "Short Body"); - string sipfrag_sline = req.body.substr(8, req.body.find("\n") - 8); + string sipfrag_sline = body.substr(8, body.find("\n") - 8); DBG("extracted start line from sipfrag '%s'\n", sipfrag_sline.c_str()); unsigned int code; string res_msg; - - if ((req.body.length() < 11) + if ((body.length() < 11) || (parse_return_code(sipfrag_sline.c_str(), code, res_msg))) { throw AmSession::Exception(400, "Bad Request"); } @@ -161,35 +171,39 @@ void AnnounceTransferDialog::onSipRequest(const AmSipRequest& req) if ((code >= 200)&&(code < 300)) { if (status != Hangup) { status = Hangup; - dlg.bye(); + dlg->bye(); } DBG("refer succeeded... stop session\n"); setStopped(); } else if (code > 300) { DBG("refer failed...\n"); if (status != Hangup) - dlg.bye(); + dlg->bye(); setStopped(); } - dlg.reply(req, 200, "OK", "", ""); + dlg->reply(req, 200, "OK", NULL); } catch (const AmSession::Exception& e) { - dlg.reply(req, e.code, e.reason, "", ""); + dlg->reply(req, e.code, e.reason, NULL); } + } else { + AmSession::onSipRequest(req); } - } -void AnnounceTransferDialog::onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method) { +void AnnounceTransferDialog::onSipReply(const AmSipRequest& req, + const AmSipReply& rep, + AmBasicSipDialog::Status old_dlg_status) +{ if ((status==Transfering ||status==Hangup) && - dlg.get_uac_trans_method(rep.cseq) == "REFER") { + req.method == SIP_METH_REFER) { if (rep.code >= 300) { DBG("refer not accepted, stop session.\n"); - dlg.bye(); + dlg->bye(); setStopped(); } } - AmSession::onSipReply(rep, old_dlg_status, trans_method); + AmSession::onSipReply(req, rep, old_dlg_status); } void AnnounceTransferDialog::onBye(const AmSipRequest& req) @@ -211,7 +225,7 @@ void AnnounceTransferDialog::process(AmEvent* event) if(audio_event && (audio_event->event_id == AmAudioEvent::cleared) && (status == Announcing)){ - dlg.refer(callee_uri); + dlg->refer(callee_uri); status = Transfering; return; } diff --git a/apps/announce_transfer/AnnounceTransfer.h b/apps/announce_transfer/AnnounceTransfer.h index 0000b006..1126cf39 100644 --- a/apps/announce_transfer/AnnounceTransfer.h +++ b/apps/announce_transfer/AnnounceTransfer.h @@ -44,7 +44,8 @@ public: AnnounceTransferFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; /** \brief session logic implementation for announce_transfer sessions */ @@ -66,11 +67,13 @@ public: AnnounceTransferDialog(const string& filename); ~AnnounceTransferDialog(); - void onSessionStart(const AmSipRequest& req); + void onInvite(const AmSipRequest& req); + void onSessionStart(); void startSession(); void onBye(const AmSipRequest& req); void onSipRequest(const AmSipRequest& req); - void onSipReply(const AmSipReply& rep, int old_dlg_status, const string& trans_method); + void onSipReply(const AmSipRequest& req, const AmSipReply& rep, + AmBasicSipDialog::Status old_dlg_status); void onDtmf(int event, int duration_msec) {} void process(AmEvent* event); diff --git a/apps/announcement/Announcement.cpp b/apps/announcement/Announcement.cpp index 846d16eb..2141f2ac 100644 --- a/apps/announcement/Announcement.cpp +++ b/apps/announcement/Announcement.cpp @@ -27,6 +27,7 @@ #include "AmConfig.h" #include "AmUtils.h" #include "AmPlugIn.h" +#include "AmUACAuth.h" #include "sems.h" #include "log.h" @@ -92,37 +93,22 @@ string AnnouncementFactory::getAnnounceFile(const AmSipRequest& req) { return announce_file; } -AmSession* AnnouncementFactory::onInvite(const AmSipRequest& req) +AmSession* AnnouncementFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new AnnouncementDialog(getAnnounceFile(req), NULL); } -AmSession* AnnouncementFactory::onInvite(const AmSipRequest& req, +AmSession* AnnouncementFactory::onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params) { - UACAuthCred* cred = NULL; - if (session_params.getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.asObject(); - if (cred_obj) - cred = dynamic_cast(cred_obj); - } - - AmSession* s = new AnnouncementDialog(getAnnounceFile(req), cred); + UACAuthCred* cred = AmUACAuth::unpackCredentials(session_params); + AmSession* s = new AnnouncementDialog(getAnnounceFile(req), cred); if (NULL == cred) { WARN("discarding unknown session parameters.\n"); } else { - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new announcement session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(s); - if (h != NULL ) - s->addHandler(h); - } else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated dialout.\n"); - } + AmUACAuth::enable(s); } return s; @@ -132,28 +118,17 @@ AnnouncementDialog::AnnouncementDialog(const string& filename, UACAuthCred* credentials) : filename(filename), cred(credentials) { + // we can drop all received packets + // this disables DTMF detection as well + setReceiving(false); } AnnouncementDialog::~AnnouncementDialog() { } -void AnnouncementDialog::onSessionStart(const AmSipRequest& req) -{ - DBG("AnnouncementDialog::onSessionStart\n"); - startSession(); -} - -void AnnouncementDialog::onSessionStart(const AmSipReply& rep) -{ - DBG("AnnouncementDialog::onSessionStart (SEMS originator mode)\n"); - startSession(); -} - -void AnnouncementDialog::startSession(){ - // we can drop all received packets - // this disables DTMF detection as well - setReceiving(false); +void AnnouncementDialog::onSessionStart() { + DBG("AnnouncementDialog::onSessionStart()...\n"); if(wav_file.open(filename,AmAudioFile::Read)) { ERROR("Couldn't open file %s.\n", filename.c_str()); @@ -164,12 +139,14 @@ void AnnouncementDialog::startSession(){ wav_file.loop.set(true); setOutput(&wav_file); + + AmSession::onSessionStart(); } void AnnouncementDialog::onBye(const AmSipRequest& req) { DBG("onBye: stopSession\n"); - setStopped(); + AmSession::onBye(req); } @@ -178,7 +155,7 @@ void AnnouncementDialog::process(AmEvent* event) AmAudioEvent* audio_event = dynamic_cast(event); if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){ - dlg.bye(); + dlg->bye(); setStopped(); return; } diff --git a/apps/announcement/Announcement.h b/apps/announcement/Announcement.h index 08a66190..b07b045c 100644 --- a/apps/announcement/Announcement.h +++ b/apps/announcement/Announcement.h @@ -30,7 +30,7 @@ #include "AmAudioFile.h" #include "AmConfigReader.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include using std::string; @@ -49,8 +49,9 @@ public: AnnouncementFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); - AmSession* onInvite(const AmSipRequest& req, + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); }; @@ -68,9 +69,7 @@ public: UACAuthCred* credentials = NULL); ~AnnouncementDialog(); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); - void startSession(); + void onSessionStart(); void onBye(const AmSipRequest& req); void onDtmf(int event, int duration_msec) {} diff --git a/apps/annrecorder/AnnRecorder.cpp b/apps/annrecorder/AnnRecorder.cpp index 2a26f723..11b6f1fb 100644 --- a/apps/annrecorder/AnnRecorder.cpp +++ b/apps/annrecorder/AnnRecorder.cpp @@ -104,12 +104,6 @@ int AnnRecorderFactory::onLoad() AM_PROMPT_ADD(BEEP, ANNREC_ANNOUNCE_PATH BEEP".wav"); AM_PROMPT_END(prompts, cfg, MOD_NAME); - - if (!AmSession::timersSupported()) { - ERROR("sorry, could not load user_timer from session_timer plug-in\n"); - return -1; - } - message_storage_fact = AmPlugIn::instance()->getFactory4Di("msg_storage"); if(!message_storage_fact) { ERROR("sorry, could not get msg_storage, please load a suitable plug-in\n"); @@ -204,22 +198,18 @@ void AnnRecorderFactory::getAppParams(const AmSipRequest& req, map& app_params) { map params; getAppParams(req, params); return new AnnRecorderDialog(params, prompts, NULL); } -AmSession* AnnRecorderFactory::onInvite(const AmSipRequest& req, +AmSession* AnnRecorderFactory::onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params) { - UACAuthCred* cred = NULL; - if (session_params.getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.asObject(); - if (cred_obj) - cred = dynamic_cast(cred_obj); - } + UACAuthCred* cred = AmUACAuth::unpackCredentials(session_params); map params; getAppParams(req, params); @@ -228,17 +218,7 @@ AmSession* AnnRecorderFactory::onInvite(const AmSipRequest& req, if (NULL == cred) { WARN("discarding unknown session parameters.\n"); } else { - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new announcement session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(s); - if (h != NULL ) - s->addHandler(h); - } else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated dialout.\n"); - } + AmUACAuth::enable(s); } return s; @@ -265,19 +245,10 @@ AnnRecorderDialog::~AnnRecorderDialog() unlink(msg_filename.c_str()); } -void AnnRecorderDialog::onSessionStart(const AmSipRequest& req) +void AnnRecorderDialog::onSessionStart() { DBG("AnnRecorderDialog::onSessionStart\n"); - startSession(); -} -void AnnRecorderDialog::onSessionStart(const AmSipReply& rep) -{ - DBG("AnnRecorderDialog::onSessionStart (SEMS originator mode)\n"); - startSession(); -} - -void AnnRecorderDialog::startSession(){ prompts.addToPlaylist(WELCOME, (long)this, playlist); prompts.addToPlaylist(YOUR_PROMPT, (long)this, playlist); enqueueCurrent(); @@ -287,6 +258,8 @@ void AnnRecorderDialog::startSession(){ // set the playlist as input and output setInOut(&playlist,&playlist); state = S_WAIT_START; + + AmSession::onSessionStart(); } void AnnRecorderDialog::enqueueCurrent() { @@ -319,14 +292,14 @@ void AnnRecorderDialog::onDtmf(int event, int duration_msec) { switch (state) { case S_WAIT_START: { DBG("received key %d in state S_WAIT_START: start recording\n", event); - playlist.close(false); + playlist.flush(); wav_file.close(); msg_filename = "/tmp/" + getLocalTag() + ".wav"; if(wav_file.open(msg_filename,AmAudioFile::Write,false)) { ERROR("AnnRecorder: couldn't open %s for writing\n", msg_filename.c_str()); - dlg.bye(); + dlg->bye(); setStopped(); } wav_file.setRecordTime(MAX_MESSAGE_TIME*1000); @@ -339,14 +312,14 @@ void AnnRecorderDialog::onDtmf(int event, int duration_msec) { case S_RECORDING: { DBG("received key %d in state S_RECORDING: replay recording\n", event); prompts.addToPlaylist(BEEP, (long)this, playlist); - playlist.close(false); + playlist.flush(); replayRecording(); } break; case S_CONFIRM: { DBG("received key %d in state S_CONFIRM save or redo\n", event); - playlist.close(false); + playlist.flush(); wav_file.close(); if (event == 1) { @@ -409,7 +382,7 @@ void AnnRecorderDialog::process(AmEvent* event) if(audio_event && (audio_event->event_id == AmAudioEvent::noAudio)){ if (S_BYE == state) { - dlg.bye(); + dlg->bye(); setStopped(); return; } diff --git a/apps/annrecorder/AnnRecorder.h b/apps/annrecorder/AnnRecorder.h index 09c0ca1f..9cad5869 100644 --- a/apps/annrecorder/AnnRecorder.h +++ b/apps/annrecorder/AnnRecorder.h @@ -30,7 +30,7 @@ #include "AmAudioFile.h" #include "AmConfigReader.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include "AmPromptCollection.h" @@ -74,8 +74,9 @@ public: AnnRecorderFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); - AmSession* onInvite(const AmSipRequest& req, + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); }; @@ -120,9 +121,7 @@ public: UACAuthCred* credentials = NULL); ~AnnRecorderDialog(); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); - void startSession(); + void onSessionStart(); void onBye(const AmSipRequest& req); void onDtmf(int event, int duration_msec); diff --git a/apps/callback/CallBack.cpp b/apps/callback/CallBack.cpp index 7f4d742a..4e93b5c4 100644 --- a/apps/callback/CallBack.cpp +++ b/apps/callback/CallBack.cpp @@ -125,7 +125,8 @@ int CallBackFactory::onLoad() } // incoming calls -AmSession* CallBackFactory::onInvite(const AmSipRequest& req) +AmSession* CallBackFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { // or req.from -> with display name ? DBG("received INVITE from '%s'\n", req.from_uri.c_str()); @@ -156,22 +157,23 @@ AmSession* CallBackFactory::onInvite(const AmSipRequest& req) } // outgoing calls -AmSession* CallBackFactory::onInvite(const AmSipRequest& req, +AmSession* CallBackFactory::onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params) { UACAuthCred* cred = NULL; if (session_params.getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.asObject(); + AmObject* cred_obj = session_params.asObject(); if (cred_obj) cred = dynamic_cast(cred_obj); } - AmSession* s = new CallBackDialog(prompts, cred); - addAuthHandler(s); + AmSession* s = new CallBackDialog(prompts, cred); + AmUACAuth::enable(s); + return s; } -// this could have been made easier with a user_timer. +// this could have been made easier with a timer... void CallBackFactory::run() { DBG("running CallBack thread.\n"); while (true) { @@ -209,9 +211,10 @@ void CallBackFactory::createCall(const string& number) { string luser = "cb"; string to = "sip:"+ number + "@" + gw_domain; string from = "sip:"+ gw_user + "@" + gw_domain; + string app_name = string(MOD_NAME); AmUAC::dialout(luser, - MOD_NAME, + app_name, to, "<" + from + ">", from, "<" + to + ">", @@ -232,27 +235,30 @@ CallBackDialog::CallBackDialog(AmPromptCollection& prompts, CallBackDialog::~CallBackDialog() { prompts.cleanup((long)this); - play_list.close(false); } -void CallBackDialog::onSessionStart(const AmSipRequest& req) { +void CallBackDialog::onInvite(const AmSipRequest& req) +{ if (state != CBNone) { // reinvite - AmB2ABCallerSession::onSessionStart(req); + AmB2ABCallerSession::onInvite(req); return; } ERROR("incoming calls not supported!\n"); setStopped(); - dlg.bye(); + dlg->bye(); } -void CallBackDialog::onSessionStart(const AmSipReply& rep) { +void CallBackDialog::onSessionStart() +{ state = CBEnteringNumber; prompts.addToPlaylist(WELCOME_PROMPT, (long)this, play_list); // set the playlist as input and output setInOut(&play_list,&play_list); + + AmB2ABCallerSession::onSessionStart(); } void CallBackDialog::onDtmf(int event, int duration) @@ -273,7 +279,7 @@ void CallBackDialog::onDtmf(int event, int duration) prompts.addToPlaylist(WELCOME_PROMPT, (long)this, play_list); } else { state = CBTellingNumber; - play_list.close(); + play_list.flush(); for (size_t i=0;igetFactory4Seh("uac_auth"); - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(s); - if (h != NULL ) - s->addHandler(h); - } else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated calls.\n"); - } -} diff --git a/apps/callback/CallBack.h b/apps/callback/CallBack.h index c84b40ce..c9dbd846 100644 --- a/apps/callback/CallBack.h +++ b/apps/callback/CallBack.h @@ -30,7 +30,7 @@ #include "AmAudio.h" #include "AmPlaylist.h" #include "AmPromptCollection.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include #include @@ -72,8 +72,9 @@ public: static PlayoutType m_PlayoutType; CallBackFactory(const string& _app_name); - AmSession* onInvite(const AmSipRequest&); - AmSession* onInvite(const AmSipRequest& req, + AmSession* onInvite(const AmSipRequest&, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); int onLoad(); @@ -109,8 +110,8 @@ public: ~CallBackDialog(); void process(AmEvent* ev); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); + void onInvite(const AmSipRequest& req); + void onSessionStart(); void onDtmf(int event, int duration); UACAuthCred* getCredentials() { return cred; } @@ -131,7 +132,6 @@ public: UACAuthCred* getCredentials() { return cred; } }; -void addAuthHandler(AmSession* s); #endif // Local Variables: // mode:C++ diff --git a/apps/click2dial/Click2Dial.cpp b/apps/click2dial/Click2Dial.cpp index cdc79b40..93b3168c 100644 --- a/apps/click2dial/Click2Dial.cpp +++ b/apps/click2dial/Click2Dial.cpp @@ -102,7 +102,7 @@ string Click2DialFactory::getAnnounceFile(const AmSipRequest& req) } -AmSession* Click2DialFactory::onInvite(const AmSipRequest& req, AmArg& session_params) +AmSession* Click2DialFactory::onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params) { UACAuthCred* cred = NULL; string callee_uri, a_realm, a_user, a_pwd; @@ -155,24 +155,9 @@ AmSession* Click2DialFactory::onInvite(const AmSipRequest& req, AmArg& session_p return NULL; } - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - if(uac_auth_f != NULL) { - DBG("UAC Auth enabled for new announcement session.\n"); - AmSessionEventHandler *h = uac_auth_f->getHandler(s); - if (h != NULL) { - s->addHandler(h); - } - else { - ERROR("Failed to get authentication event handler"); - delete s; - return NULL; - } - } - else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated dialout.\n"); - + if (!AmUACAuth::enable(s)) { + ERROR("Failed to get authentication event handler"); + delete s; return NULL; } @@ -180,7 +165,8 @@ AmSession* Click2DialFactory::onInvite(const AmSipRequest& req, AmArg& session_p } -AmSession* Click2DialFactory::onInvite(const AmSipRequest& req) +AmSession* Click2DialFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new C2DCallerDialog(req, getAnnounceFile(req), "", NULL); } @@ -195,25 +181,42 @@ AmB2BCallerSession() set_sip_relay_early_media_sdp(Click2DialFactory::relay_early_media_sdp); } +void C2DCallerDialog::onInvite(const AmSipRequest& req) +{ + ERROR("incoming calls not supported in click2dial app!\n"); + dlg->reply(req,606,"Not Acceptable"); + setStopped(); +} -void C2DCallerDialog::onSessionStart(const AmSipReply& rep) +void C2DCallerDialog::onInvite2xx(const AmSipReply& reply) { - setReceiving(false); - invite_req.body = rep.body; - invite_req.content_type = rep.content_type; - invite_req.cseq = rep.cseq; + invite_req.body = reply.body; + invite_req.cseq = reply.cseq; + est_invite_cseq = reply.cseq; +} +void C2DCallerDialog::onSessionStart() +{ + setReceiving(false); if(wav_file.open(filename,AmAudioFile::Read)) throw string("AnnouncementDialog::onSessionStart: Cannot open file\n"); setOutput(&wav_file); + + AmB2BCallerSession::onSessionStart(); } +void C2DCallerDialog::updateUACTransCSeq(unsigned int old_cseq, unsigned int new_cseq) { + if (old_cseq == invite_req.cseq) { + DBG("updating invite_req.cseq %u -> %u\n", old_cseq, new_cseq); + invite_req.cseq = new_cseq; + } + if (old_cseq == est_invite_cseq) { + DBG("updating est_invite_cseq %u -> %u\n", old_cseq, new_cseq); + est_invite_cseq = new_cseq; + } -void C2DCallerDialog::onSessionStart(const AmSipRequest& req) -{ } - void C2DCallerDialog::process(AmEvent* event) { AmAudioEvent* audio_event = dynamic_cast(event); @@ -242,20 +245,20 @@ void C2DCallerDialog::createCalleeSession() } AmB2BCalleeSession* callee_session = new C2DCalleeDialog(this, c); - AmSipDialog& callee_dlg = callee_session->dlg; + AmSipDialog* callee_dlg = callee_session->dlg; - other_id = AmSession::getNewId(); + AmB2BSession::setOtherId(AmSession::getNewId()); - callee_dlg.local_tag = other_id; - callee_dlg.callid = AmSession::getNewId(); - callee_dlg.local_party = dlg.local_party; - callee_dlg.remote_party = dlg.remote_party; - callee_dlg.remote_uri = dlg.remote_uri; + callee_dlg->setLocalTag(AmB2BSession::getOtherId()); + callee_dlg->setCallid(AmSession::getNewId()); + callee_dlg->setLocalParty(dlg->getLocalParty()); + callee_dlg->setRemoteParty(dlg->getRemoteParty()); + callee_dlg->setRemoteUri(dlg->getRemoteUri()); callee_session->start(); AmSessionContainer* sess_cont = AmSessionContainer::instance(); - sess_cont->addSession(other_id,callee_session); + sess_cont->addSession(AmB2BSession::getOtherId(),callee_session); } diff --git a/apps/click2dial/Click2Dial.h b/apps/click2dial/Click2Dial.h index f6e73039..4a47d443 100644 --- a/apps/click2dial/Click2Dial.h +++ b/apps/click2dial/Click2Dial.h @@ -32,7 +32,7 @@ #include "AmAudioFile.h" #include "AmB2BSession.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include using std::string; @@ -52,8 +52,9 @@ class Click2DialFactory: public AmSessionFactory Click2DialFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); - AmSession* onInvite(const AmSipRequest& req, AmArg& session_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); }; class C2DCallerDialog: public AmB2BCallerSession, public CredentialHolder @@ -69,11 +70,13 @@ class C2DCallerDialog: public AmB2BCallerSession, public CredentialHolder const string& callee_uri, UACAuthCred* credentials = NULL); void process(AmEvent* event); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); + void onInvite(const AmSipRequest& req); + void onInvite2xx(const AmSipReply& reply); + void onSessionStart(); void createCalleeSession(); inline UACAuthCred* getCredentials() { return cred.get(); } void onB2BEvent(B2BEvent*); + void updateUACTransCSeq(unsigned int old_cseq, unsigned int new_cseq); }; class C2DCalleeDialog : public AmB2BCalleeSession, public CredentialHolder diff --git a/apps/conf_auth/conf_auth.py b/apps/conf_auth/conf_auth.py index 168a0611..fc2a5ac7 100644 --- a/apps/conf_auth/conf_auth.py +++ b/apps/conf_auth/conf_auth.py @@ -56,7 +56,7 @@ class IvrDialog(IvrDialogBase): print " next_hop: ", self.dialog.next_hop print " cseq: ", self.dialog.cseq - def onSessionStart(self, hdrs): + def onSessionStart(self): self.sessionInfo() self.setNoRelayonly() self.welcome_msg = IvrAudioFile.tts("Welcome to the conferencing server. "+ \ diff --git a/apps/conference/Conference.cpp b/apps/conference/Conference.cpp index f42ab350..23d19e0a 100644 --- a/apps/conference/Conference.cpp +++ b/apps/conference/Conference.cpp @@ -308,7 +308,8 @@ int ConferenceFactory::onLoad() return 0; } -AmSession* ConferenceFactory::onInvite(const AmSipRequest& req) +AmSession* ConferenceFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { if ((ConferenceFactory::MaxParticipants > 0) && (AmConferenceStatus::getConferenceSize(req.user) >= @@ -353,17 +354,18 @@ void ConferenceFactory::setupSessionTimer(AmSession* s) { } } -AmSession* ConferenceFactory::onRefer(const AmSipRequest& req) +AmSession* ConferenceFactory::onRefer(const AmSipRequest& req, const string& app_name, + const map& app_params) { if(req.to_tag.empty()) throw AmSession::Exception(488,"Not accepted here"); AmSession* s = new ConferenceDialog(req.user); - s->dlg.local_tag = req.from_tag; + s->dlg->setLocalTag(req.from_tag); setupSessionTimer(s); - DBG("ConferenceFactory::onRefer: local_tag = %s\n",s->dlg.local_tag.c_str()); + DBG("ConferenceFactory::onRefer: local_tag = %s\n",s->dlg->getLocalTag().c_str()); return s; } @@ -389,7 +391,7 @@ ConferenceDialog::~ConferenceDialog() DBG("ConferenceDialog::~ConferenceDialog()\n"); // clean playlist items - play_list.close(false); + play_list.flush(); #ifdef WITH_SAS_TTS // garbage collect tts files - TODO: delete files @@ -403,17 +405,24 @@ ConferenceDialog::~ConferenceDialog() void ConferenceDialog::onStart() { } -void ConferenceDialog::onSessionStart(const AmSipRequest& req) +void ConferenceDialog::onInvite(const AmSipRequest& req) { + if(dlg->getStatus() == AmSipDialog::Connected){ + AmSession::onInvite(req); + return; + } + int i, len; string lonely_user_file; string app_param_hdr = getHeader(req.hdrs, PARAM_HDR, true); + string listen_only_str = ""; if (app_param_hdr.length()) { from_header = get_header_keyvalue(app_param_hdr, "Dialout-From"); extra_headers = get_header_keyvalue(app_param_hdr, "Dialout-Extra"); dialout_suffix = get_header_keyvalue(app_param_hdr, "Dialout-Suffix"); - language = get_header_keyvalue(app_param_hdr, "Language"); + language = get_header_keyvalue(app_param_hdr, "Language"); + listen_only_str = get_header_keyvalue(app_param_hdr, "Listen-Only"); } else { from_header = getHeader(req.hdrs, "P-Dialout-From", true); extra_headers = getHeader(req.hdrs, "P-Dialout-Extra", true); @@ -448,6 +457,8 @@ void ConferenceDialog::onSessionStart(const AmSipRequest& req) allow_dialout = dialout_suffix.length() > 0; + listen_only = listen_only_str.length() > 0; + if (!language.empty()) { #ifdef USE_MYSQL @@ -481,14 +492,24 @@ void ConferenceDialog::onSessionStart(const AmSipRequest& req) DBG("Using LonelyUserFile <%s>\n", ConferenceFactory::LonelyUserFile.c_str()); - - setupAudio(); + + AmSession::onInvite(req); } -// void ConferenceDialog::onSessionStart(const AmSipReply& reply) -// { -// setupAudio(); -// } +void ConferenceDialog::onSessionStart() +{ + setupAudio(); + + if(dialedout) { + // send connect event + AmSessionContainer::instance() + ->postEvent(dialout_channel->getConfID(), + new DialoutConfEvent(DoConfConnect, + dialout_channel->getConfID())); + } + + AmSession::onSessionStart(); +} void ConferenceDialog::setupAudio() { @@ -509,20 +530,29 @@ void ConferenceDialog::setupAudio() } - play_list.close();// !!! + play_list.flush(); if(dialout_channel.get()){ DBG("adding dialout_channel to the playlist (dialedout = %i)\n",dialedout); - play_list.addToPlaylist(new AmPlaylistItem(dialout_channel.get(), - dialout_channel.get())); + if (listen_only) + play_list.addToPlaylist(new AmPlaylistItem(dialout_channel.get(), + (AmAudio*)NULL)); + else + play_list.addToPlaylist(new AmPlaylistItem(dialout_channel.get(), + dialout_channel.get())); } else { - channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag())); + channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag(),RTPStream()->getSampleRate())); - play_list.addToPlaylist(new AmPlaylistItem(channel.get(), - channel.get())); + if (listen_only) { + play_list.addToPlaylist(new AmPlaylistItem(channel.get(), + (AmAudio*)NULL)); + } + else + play_list.addToPlaylist(new AmPlaylistItem(channel.get(), + channel.get())); } setInOut(&play_list,&play_list); @@ -607,7 +637,7 @@ void ConferenceDialog::process(AmEvent* ev) case DoConfDisconnect: - dlg.bye(); + dlg->bye(); closeChannels(); setStopped(); break; @@ -631,7 +661,7 @@ void ConferenceDialog::process(AmEvent* ev) state = CS_dialout_connected; - play_list.close(); // !!! + play_list.flush(); play_list.addToPlaylist(new AmPlaylistItem(dialout_channel.get(), dialout_channel.get())); break; @@ -642,7 +672,7 @@ void ConferenceDialog::process(AmEvent* ev) RingTone.reset(new AmRingTone(0,2000,4000,440,480)); // US DBG("adding ring tone to the playlist (dialedout = %i)\n",dialedout); - play_list.close(); + play_list.flush(); play_list.addToPlaylist(new AmPlaylistItem(RingTone.get(),NULL)); break; @@ -653,7 +683,6 @@ void ConferenceDialog::process(AmEvent* ev) ErrorTone.reset(new AmRingTone(2000,250,250,440,480)); DBG("adding error tone to the playlist (dialedout = %i)\n",dialedout); - //play_list.close(); play_list.addToPlayListFront(new AmPlaylistItem(ErrorTone.get(),NULL)); break; @@ -687,7 +716,7 @@ void ConferenceDialog::onDtmf(int event, int duration) DBG("ConferenceDialog::onDtmf\n"); if (dialedout || !allow_dialout || ((ConferenceFactory::MaxParticipants > 0) && - (AmConferenceStatus::getConferenceSize(dlg.user) >= + (AmConferenceStatus::getConferenceSize(dlg->getUser()) >= ConferenceFactory::MaxParticipants))) return; @@ -769,39 +798,32 @@ void ConferenceDialog::createDialoutParticipant(const string& uri_user) uri = "sip:" + uri_user + dialout_suffix; - dialout_channel.reset(AmConferenceStatus::getChannel(getLocalTag(),getLocalTag())); + dialout_channel.reset(AmConferenceStatus::getChannel(getLocalTag(),getLocalTag(),RTPStream()->getSampleRate())); dialout_id = AmSession::getNewId(); ConferenceDialog* dialout_session = new ConferenceDialog(conf_id, AmConferenceStatus::getChannel(getLocalTag(), - dialout_id)); + dialout_id,RTPStream()->getSampleRate())); ConferenceFactory::setupSessionTimer(dialout_session); - AmSipDialog& dialout_dlg = dialout_session->dlg; + AmSipDialog* dialout_dlg = dialout_session->dlg; - dialout_dlg.local_tag = dialout_id; - dialout_dlg.callid = AmSession::getNewId(); + dialout_dlg->setLocalTag(dialout_id); + dialout_dlg->setCallid(AmSession::getNewId()); if (from_header.length() > 0) { - dialout_dlg.local_party = from_header; + dialout_dlg->setLocalParty(from_header); } else { - dialout_dlg.local_party = dlg.local_party; + dialout_dlg->setLocalParty(dlg->getLocalParty()); } - dialout_dlg.remote_party = uri; - dialout_dlg.remote_uri = uri; + dialout_dlg->setRemoteParty(uri); + dialout_dlg->setRemoteUri(uri); - string body; - int local_port = dialout_session->RTPStream()->getLocalPort(); - dialout_session->sdp.genRequest(dialout_session->advertisedIP(),local_port,body); - - if (extra_headers.length() == 0) { - extra_headers = ""; - } - - dialout_dlg.sendRequest("INVITE","application/sdp",body,extra_headers); + dialout_dlg->sendRequest(SIP_METH_INVITE,NULL, + extra_headers); dialout_session->start(); @@ -838,12 +860,12 @@ void ConferenceDialog::connectMainChannel() dialedout = false; dialout_channel.reset(NULL); - play_list.close(); + play_list.flush(); if(!channel.get()) channel.reset(AmConferenceStatus ::getChannel(conf_id, - getLocalTag())); + getLocalTag(),RTPStream()->getSampleRate())); play_list.addToPlaylist(new AmPlaylistItem(channel.get(), channel.get())); @@ -851,7 +873,7 @@ void ConferenceDialog::connectMainChannel() void ConferenceDialog::closeChannels() { - play_list.close(); + play_list.flush(); setInOut(NULL,NULL); channel.reset(NULL); dialout_channel.reset(NULL); @@ -860,134 +882,98 @@ void ConferenceDialog::closeChannels() void ConferenceDialog::onSipRequest(const AmSipRequest& req) { AmSession::onSipRequest(req); - if((dlg.getStatus() >= AmSipDialog::Connected) || + if((dlg->getStatus() >= AmSipDialog::Connected) || (req.method != "REFER")) return; - std::swap(dlg.local_party,dlg.remote_party); - dlg.remote_tag = ""; + string local_party(dlg->getLocalParty()); + dlg->setLocalParty(dlg->getRemoteParty()); + dlg->setRemoteParty(local_party); + dlg->setRemoteTag(""); // get route set and next hop string iptel_app_param = getHeader(req.hdrs, PARAM_HDR, true); if (iptel_app_param.length()) { - dlg.route = get_header_keyvalue(iptel_app_param,"Transfer-RR"); + dlg->setRouteSet(get_header_keyvalue(iptel_app_param,"Transfer-RR")); } else { INFO("Use of P-Transfer-RR/P-Transfer-NH is deprecated. " "Use '%s: Transfer-RR=;Transfer-NH=' instead.\n",PARAM_HDR); - dlg.route = getHeader(req.hdrs,"P-Transfer-RR", true); + dlg->setRouteSet(getHeader(req.hdrs,"P-Transfer-RR", true)); } - DBG("ConferenceDialog::onSipRequest: local_party = %s\n",dlg.local_party.c_str()); - DBG("ConferenceDialog::onSipRequest: local_tag = %s\n",dlg.local_tag.c_str()); - DBG("ConferenceDialog::onSipRequest: remote_party = %s\n",dlg.remote_party.c_str()); - DBG("ConferenceDialog::onSipRequest: remote_tag = %s\n",dlg.remote_tag.c_str()); + DBG("ConferenceDialog::onSipRequest: local_party = %s\n",dlg->getLocalParty().c_str()); + DBG("ConferenceDialog::onSipRequest: local_tag = %s\n",dlg->getLocalTag().c_str()); + DBG("ConferenceDialog::onSipRequest: remote_party = %s\n",dlg->getRemoteParty().c_str()); + DBG("ConferenceDialog::onSipRequest: remote_tag = %s\n",dlg->getRemoteTag().c_str()); - string body; - int local_port = RTPStream()->getLocalPort(); - sdp.genRequest(advertisedIP(),local_port,body); - dlg.sendRequest("INVITE","application/sdp",body,""); + dlg->sendRequest(SIP_METH_INVITE); transfer_req.reset(new AmSipRequest(req)); return; } -void ConferenceDialog::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) +void ConferenceDialog::onSipReply(const AmSipRequest& req, + const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status) { - int status = dlg.getStatus(); - AmSession::onSipReply(reply, old_dlg_status, trans_method); + AmSession::onSipReply(req, reply, old_dlg_status); DBG("ConferenceDialog::onSipReply: code = %i, reason = %s\n, status = %i\n", - reply.code,reply.reason.c_str(),dlg.getStatus()); + reply.code,reply.reason.c_str(),dlg->getStatus()); - if(!dialedout && - !transfer_req.get()) + if(!dialedout /*&& !transfer_req.get()*/) return; - if(status < AmSipDialog::Connected){ - - switch(dlg.getStatus()){ - - case AmSipDialog::Connected: - - // connected! - try { - - acceptAudio(reply.body,reply.hdrs); - - if(getDetached() && !getStopped()){ - - setupAudio(); - - if(getInput() || getOutput()) - AmMediaProcessor::instance()->addSession(this, - getCallgroup()); - else { - ERROR("missing audio input and/or ouput.\n"); - return; - } - - if(!transfer_req.get()){ - - // send connect event - AmSessionContainer::instance() - ->postEvent(dialout_channel->getConfID(), - new DialoutConfEvent(DoConfConnect, - dialout_channel->getConfID())); - } - else { - dlg.reply(*(transfer_req.get()),202,"Accepted"); - transfer_req.reset(0); - connectMainChannel(); - } - } - - } - catch(const AmSession::Exception& e){ - ERROR("%i %s\n",e.code,e.reason.c_str()); - dlg.bye(); - setStopped(); - } - break; + if((old_dlg_status < AmSipDialog::Connected) && + (reply.cseq_method == SIP_METH_INVITE)){ - case AmSipDialog::Pending: + switch(dlg->getStatus()){ + + case AmSipDialog::Proceeding: + case AmSipDialog::Early: switch(reply.code){ case 180: + case 183: break;//TODO: remote ring tone. - // send ringing event - AmSessionContainer::instance() - ->postEvent(dialout_channel->getConfID(), - new DialoutConfEvent(DoConfRinging, - dialout_channel->getConfID())); + if(dialout_channel.get()){ + // send ringing event + AmSessionContainer::instance() + ->postEvent(dialout_channel->getConfID(), + new DialoutConfEvent(DoConfRinging, + dialout_channel->getConfID())); + } break; - case 183: break;//TODO: remote ring tone. default: break;// continue waiting. } break; - default: break; - } - - } -} + case AmSipDialog::Disconnected: -void ConferenceDialog::onOutboundCallFailed(const AmSipReply& reply) { - DBG("Outbound call failed with reply %d %s.\n", - reply.code, reply.reason.c_str()); + // if(!transfer_req.get()){ + + if(dialout_channel.get()){ + disconnectDialout(); + AmSessionContainer::instance() + ->postEvent(dialout_channel->getConfID(), + new DialoutConfEvent(DoConfError, + dialout_channel->getConfID())); + } + setStopped(); + + // } + // else { + // dlg->reply(*(transfer_req.get()),reply.code,reply.reason); + // transfer_req.reset(0); + // setStopped(); + // } + break; - if(!transfer_req.get()){ - disconnectDialout(); - AmSessionContainer::instance() - ->postEvent(dialout_channel->getConfID(), - new DialoutConfEvent(DoConfError, - dialout_channel->getConfID())); - } else { - dlg.reply(*(transfer_req.get()),reply.code,reply.reason); - transfer_req.reset(0); - setStopped(); + default: break; + } } } diff --git a/apps/conference/Conference.h b/apps/conference/Conference.h index 801d87a9..bed0da7d 100644 --- a/apps/conference/Conference.h +++ b/apps/conference/Conference.h @@ -93,8 +93,10 @@ public: #endif ConferenceFactory(const string& _app_name); - virtual AmSession* onInvite(const AmSipRequest&); - virtual AmSession* onRefer(const AmSipRequest& req); + virtual AmSession* onInvite(const AmSipRequest&, const string& app_name, + const map& app_params); + virtual AmSession* onRefer(const AmSipRequest& req, const string& app_name, + const map& app_params); virtual int onLoad(); }; @@ -126,6 +128,8 @@ class ConferenceDialog : public AmSession string extra_headers; string language; + bool listen_only; + auto_ptr transfer_req; @@ -151,13 +155,14 @@ public: void process(AmEvent* ev); void onStart(); void onDtmf(int event, int duration); - void onSessionStart(const AmSipRequest& req); + void onInvite(const AmSipRequest& req); + void onSessionStart(); void onBye(const AmSipRequest& req); - void onOutboundCallFailed(const AmSipReply& reply); void onSipRequest(const AmSipRequest& req); - void onSipReply(const AmSipReply& reply, int old_dlg_status, - const string& trans_method); + void onSipReply(const AmSipRequest& req, + const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status); #ifdef WITH_SAS_TTS void onZRTPEvent(zrtp_event_t event, zrtp_stream_ctx_t *stream_ctx); diff --git a/apps/db_reg_agent/CMakeLists.txt b/apps/db_reg_agent/CMakeLists.txt new file mode 100644 index 00000000..d264cab9 --- /dev/null +++ b/apps/db_reg_agent/CMakeLists.txt @@ -0,0 +1,12 @@ +set (db_reg_agent_SRCS +DBRegAgent.cpp +RegistrationTimer.cpp +) + +INCLUDE_DIRECTORIES(/usr/include/mysql) +INCLUDE_DIRECTORIES(${MYSQLPP_INCLUDE_DIR}/mysql++) +ADD_DEFINITIONS(-DHAS_OFFER_ANSWER) +SET(sems_module_libs ${sems_module_libs} mysqlpp) + +SET(sems_module_name db_reg_agent) +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/module.rules.txt) diff --git a/apps/db_reg_agent/DBRegAgent.cpp b/apps/db_reg_agent/DBRegAgent.cpp new file mode 100644 index 00000000..168f89aa --- /dev/null +++ b/apps/db_reg_agent/DBRegAgent.cpp @@ -0,0 +1,1340 @@ +/* + * Copyright (C) 2011 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ + +#include "DBRegAgent.h" +#include "AmSession.h" +#include "AmEventDispatcher.h" + +#include +#include + +EXPORT_MODULE_FACTORY(DBRegAgent); +DEFINE_MODULE_INSTANCE(DBRegAgent, MOD_NAME); + +mysqlpp::Connection DBRegAgent::MainDBConnection(mysqlpp::use_exceptions); +mysqlpp::Connection DBRegAgent::ProcessorDBConnection(mysqlpp::use_exceptions); + +string DBRegAgent::joined_query; +string DBRegAgent::registrations_table = "registrations"; + +double DBRegAgent::reregister_interval = 0.5; +double DBRegAgent::minimum_reregister_interval = -1; + +bool DBRegAgent::enable_ratelimiting = false; +unsigned int DBRegAgent::ratelimit_rate = 0; +unsigned int DBRegAgent::ratelimit_per = 0; +bool DBRegAgent::ratelimit_slowstart = false; + +bool DBRegAgent::delete_removed_registrations = true; +bool DBRegAgent::delete_failed_deregistrations = false; +bool DBRegAgent::save_contacts = true; +bool DBRegAgent::db_read_contact = false; +string DBRegAgent::contact_hostport; +string DBRegAgent::outbound_proxy; +bool DBRegAgent::save_auth_replies = false; + +unsigned int DBRegAgent::error_retry_interval = 300; + +static void _timer_cb(RegTimer* timer, long subscriber_id, int data2) { + DBRegAgent::instance()->timer_cb(timer, subscriber_id, data2); +} + +DBRegAgent::DBRegAgent(const string& _app_name) + : AmDynInvokeFactory(_app_name), + AmEventQueue(this), + uac_auth_i(NULL) +{ +} + +DBRegAgent::~DBRegAgent() { +} + +int DBRegAgent::onLoad() +{ + + DBG("loading db_reg_agent....\n"); + + AmDynInvokeFactory* uac_auth_f = AmPlugIn::instance()->getFactory4Di("uac_auth"); + if (uac_auth_f == NULL) { + WARN("unable to get a uac_auth factory. " + "registrations will not be authenticated.\n"); + WARN("(do you want to load uac_auth module?)\n"); + } else { + uac_auth_i = uac_auth_f->getInstance(); + } + + AmConfigReader cfg; + if(cfg.loadFile(add2path(AmConfig::ModConfigPath,1, MOD_NAME ".conf"))) + return -1; + + expires = cfg.getParameterInt("expires", 7200); + DBG("requesting registration expires of %u seconds\n", expires); + + if (cfg.hasParameter("reregister_interval")) { + reregister_interval = -1; + reregister_interval = atof(cfg.getParameter("reregister_interval").c_str()); + if (reregister_interval <= 0 || reregister_interval > 1) { + ERROR("configuration value 'reregister_interval' could not be read. " + "needs to be 0 .. 1.0 (recommended: 0.5)\n"); + return -1; + } + } + + if (cfg.hasParameter("minimum_reregister_interval")) { + minimum_reregister_interval = -1; + minimum_reregister_interval = atof(cfg.getParameter("minimum_reregister_interval").c_str()); + if (minimum_reregister_interval <= 0 || minimum_reregister_interval > 1) { + ERROR("configuration value 'minimum_reregister_interval' could not be read. " + "needs to be 0 .. reregister_interval (recommended: 0.4)\n"); + return -1; + } + + if (minimum_reregister_interval >= reregister_interval) { + ERROR("configuration value 'minimum_reregister_interval' must be smaller " + "than reregister_interval (recommended: 0.4)\n"); + return -1; + } + } + + enable_ratelimiting = cfg.getParameter("enable_ratelimiting") == "yes"; + if (enable_ratelimiting) { + if (!cfg.hasParameter("ratelimit_rate") || !cfg.hasParameter("ratelimit_per")) { + ERROR("if ratelimiting is enabled, ratelimit_rate and ratelimit_per must be set\n"); + return -1; + } + ratelimit_rate = cfg.getParameterInt("ratelimit_rate", 0); + ratelimit_per = cfg.getParameterInt("ratelimit_per", 0); + if (!ratelimit_rate || !ratelimit_per) { + ERROR("ratelimit_rate and ratelimit_per must be > 0\n"); + return -1; + } + ratelimit_slowstart = cfg.getParameter("ratelimit_slowstart") == "yes"; + + } + + delete_removed_registrations = + cfg.getParameter("delete_removed_registrations", "yes") == "yes"; + + delete_failed_deregistrations = + cfg.getParameter("delete_failed_deregistrations", "no") == "yes"; + + save_contacts = + cfg.getParameter("save_contacts", "yes") == "yes"; + + db_read_contact = + cfg.getParameter("db_read_contact", "no") == "yes"; + + save_auth_replies = + cfg.getParameter("save_auth_replies", "no") == "yes"; + + contact_hostport = cfg.getParameter("contact_hostport"); + + outbound_proxy = cfg.getParameter("outbound_proxy"); + + error_retry_interval = cfg.getParameterInt("error_retry_interval", 300); + if (!error_retry_interval) { + WARN("disabled retry on errors!\n"); + } + + string mysql_server, mysql_user, mysql_passwd, mysql_db; + + mysql_server = cfg.getParameter("mysql_server", "localhost"); + + mysql_user = cfg.getParameter("mysql_user"); + if (mysql_user.empty()) { + ERROR(MOD_NAME ".conf parameter 'mysql_user' is missing.\n"); + return -1; + } + + mysql_passwd = cfg.getParameter("mysql_passwd"); + if (mysql_passwd.empty()) { + ERROR(MOD_NAME ".conf parameter 'mysql_passwd' is missing.\n"); + return -1; + } + + mysql_db = cfg.getParameter("mysql_db", "sems"); + + try { + + MainDBConnection.set_option(new mysqlpp::ReconnectOption(true)); + // matched instead of changed rows in result, so we know when to create DB entry + MainDBConnection.set_option(new mysqlpp::FoundRowsOption(true)); + MainDBConnection.connect(mysql_db.c_str(), mysql_server.c_str(), + mysql_user.c_str(), mysql_passwd.c_str()); + if (!MainDBConnection) { + ERROR("Database connection failed: %s\n", MainDBConnection.error()); + return -1; + } + + ProcessorDBConnection.set_option(new mysqlpp::ReconnectOption(true)); + // matched instead of changed rows in result, so we know when to create DB entry + ProcessorDBConnection.set_option(new mysqlpp::FoundRowsOption(true)); + ProcessorDBConnection.connect(mysql_db.c_str(), mysql_server.c_str(), + mysql_user.c_str(), mysql_passwd.c_str()); + if (!ProcessorDBConnection) { + ERROR("Database connection failed: %s\n", ProcessorDBConnection.error()); + return -1; + } + + } catch (const mysqlpp::Exception& er) { + // Catch-all for any MySQL++ exceptions + ERROR("MySQL++ error: %s\n", er.what()); + return -1; + } + + // register us as SIP event receiver for MOD_NAME + AmEventDispatcher::instance()->addEventQueue(MOD_NAME,this); + + if (!AmPlugIn::registerDIInterface(MOD_NAME, this)) { + ERROR("registering "MOD_NAME" DI interface\n"); + return -1; + } + + joined_query = cfg.getParameter("joined_query"); + if (joined_query.empty()) { + // todo: name! + ERROR("joined_query must be set\n"); + return -1; + } + + if (cfg.hasParameter("registrations_table")) { + registrations_table = cfg.getParameter("registrations_table"); + } + DBG("using registrations table '%s'\n", registrations_table.c_str()); + + if (!loadRegistrations()) { + ERROR("loading registrations from DB\n"); + return -1; + } + + DBG("starting registration timer thread...\n"); + registration_scheduler.start(); + + // run_tests(); + + start(); + + return 0; +} + +void DBRegAgent::onUnload() { + if (running) { + running = false; + registration_scheduler._timer_thread_running = false; + DBG("unclean shutdown. Waiting for processing thread to stop.\n"); + for (int i=0;i<400;i++) { + if (shutdown_finished && registration_scheduler._shutdown_finished) + break; + usleep(2000); // 2ms + } + + if (!shutdown_finished || !registration_scheduler._shutdown_finished) { + WARN("processing thread could not be stopped, process will probably crash\n"); + } + } + + DBG("closing main DB connection\n"); + MainDBConnection.disconnect(); + DBG("closing auxiliary DB connection\n"); + ProcessorDBConnection.disconnect(); +} + +bool DBRegAgent::loadRegistrations() { + try { + time_t now_time = time(NULL); + + mysqlpp::Query query = DBRegAgent::MainDBConnection.query(); + + string query_string, table; + + query_string = joined_query; + + DBG("querying all registrations with : '%s'\n", + query_string.c_str()); + + query << query_string; + mysqlpp::UseQueryResult res = query.use(); + + // mysqlpp::Row::size_type row_count = res.num_rows(); + // DBG("got %zd subscriptions\n", row_count); + + while (mysqlpp::Row row = res.fetch_row()) { + int status = 0; + long subscriber_id = row[COLNAME_SUBSCRIBER_ID]; + + string contact_uri; + if (db_read_contact && row[COLNAME_CONTACT] != mysqlpp::null) { + contact_uri = (string) row[COLNAME_CONTACT]; + } + + if (row[COLNAME_STATUS] != mysqlpp::null) + status = row[COLNAME_STATUS]; + else { + DBG("registration status entry for id %ld does not exist, creating...\n", + subscriber_id); + createDBRegistration(subscriber_id, ProcessorDBConnection); + } + + DBG("got subscriber '%s@%s' status %i\n", + string(row[COLNAME_USER]).c_str(), string(row[COLNAME_REALM]).c_str(), + status); + + switch (status) { + case REG_STATUS_INACTIVE: + case REG_STATUS_PENDING: // try again + case REG_STATUS_FAILED: // try again + { + createRegistration(subscriber_id, + (string)row[COLNAME_USER], + (string)row[COLNAME_PASS], + (string)row[COLNAME_REALM], + contact_uri + ); + scheduleRegistration(subscriber_id); + }; break; + + case REG_STATUS_ACTIVE: + { + createRegistration(subscriber_id, + (string)row[COLNAME_USER], + (string)row[COLNAME_PASS], + (string)row[COLNAME_REALM], + contact_uri + ); + + time_t dt_expiry = now_time; + if (row[COLNAME_EXPIRY] != mysqlpp::null) { + dt_expiry = (time_t)((mysqlpp::DateTime)row[COLNAME_EXPIRY]); + } + + time_t dt_registration_ts = now_time; + if (row[COLNAME_REGISTRATION_TS] != mysqlpp::null) { + dt_registration_ts = (time_t)((mysqlpp::DateTime)row[COLNAME_REGISTRATION_TS]); + } + + DBG("got expiry '%ld, registration_ts %ld, now %ld'\n", + dt_expiry, dt_registration_ts, now_time); + + if (dt_registration_ts > now_time) { + WARN("needed to sanitize last_registration timestamp TS from the %ld (now %ld) - " + "DB host time mismatch?\n", dt_registration_ts, now_time); + dt_registration_ts = now_time; + } + + // if expired add to pending registrations, else schedule re-regstration + if (dt_expiry <= now_time) { + DBG("scheduling imminent re-registration for subscriber %ld\n", subscriber_id); + scheduleRegistration(subscriber_id); + } else { + setRegistrationTimer(subscriber_id, dt_expiry, dt_registration_ts, now_time); + } + + }; break; + case REG_STATUS_REMOVED: + { + DBG("ignoring removed registration %ld %s@%s", subscriber_id, + ((string)row[COLNAME_USER]).c_str(), ((string)row[COLNAME_REALM]).c_str()); + } break; + + case REG_STATUS_TO_BE_REMOVED: + { + DBG("Scheduling Deregister of registration %ld %s@%s", subscriber_id, + ((string)row[COLNAME_USER]).c_str(), ((string)row[COLNAME_REALM]).c_str()); + createRegistration(subscriber_id, + (string)row[COLNAME_USER], + (string)row[COLNAME_PASS], + (string)row[COLNAME_REALM], + contact_uri + ); + scheduleDeregistration(subscriber_id); + }; + } + } + + + } catch (const mysqlpp::Exception& er) { + // Catch-all for any MySQL++ exceptions + ERROR("MySQL++ error: %s\n", er.what()); + return false; + } + + return true; +} + +/** create registration in our list */ +void DBRegAgent::createRegistration(long subscriber_id, + const string& user, + const string& pass, + const string& realm, + const string& contact) { + + string contact_uri = contact; + if (contact_uri.empty() && !contact_hostport.empty()) { + contact_uri = "sip:"+ user + "@" + contact_hostport; + } + + string handle = AmSession::getNewId(); + SIPRegistrationInfo reg_info(realm, user, + user, // name + user, // auth_user + pass, + outbound_proxy, // proxy + contact_uri // contact + ); + + registrations_mut.lock(); + try { + if (registrations.find(subscriber_id) != registrations.end()) { + registrations_mut.unlock(); + WARN("registration with ID %ld already exists, removing\n", subscriber_id); + removeRegistration(subscriber_id); + clearRegistrationTimer(subscriber_id); + registrations_mut.lock(); + } + + AmSIPRegistration* reg = new AmSIPRegistration(handle, reg_info, "" /*MOD_NAME*/); + reg->setExpiresInterval(expires); + + registrations[subscriber_id] = reg; + registration_ltags[handle] = subscriber_id; + + if (NULL != uac_auth_i) { + DBG("enabling UAC Auth for new registration.\n"); + + // get a sessionEventHandler from uac_auth + AmArg di_args,ret; + AmArg a; + a.setBorrowedPointer(reg); + di_args.push(a); + di_args.push(a); + + uac_auth_i->invoke("getHandler", di_args, ret); + if (!ret.size()) { + ERROR("Can not add auth handler to new registration!\n"); + } else { + AmObject* p = ret.get(0).asObject(); + if (p != NULL) { + AmSessionEventHandler* h = dynamic_cast(p); + if (h != NULL) + reg->setSessionEventHandler(h); + } + } + } + } catch (const AmArg::OutOfBoundsException& e) { + ERROR("OutOfBoundsException"); + } catch (const AmArg::TypeMismatchException& e) { + ERROR("TypeMismatchException"); + } catch (...) { + ERROR("unknown exception occured\n"); + } + + registrations_mut.unlock(); + + // register us as SIP event receiver for this ltag + AmEventDispatcher::instance()->addEventQueue(handle,this); + + DBG("created new registration with ID %ld and ltag '%s'\n", + subscriber_id, handle.c_str()); +} + +void DBRegAgent::updateRegistration(long subscriber_id, + const string& user, + const string& pass, + const string& realm, + const string& contact) { + + registrations_mut.lock(); + map::iterator it=registrations.find(subscriber_id); + if (it == registrations.end()) { + registrations_mut.unlock(); + WARN("updateRegistration - registration %ld %s@%s unknown, creating\n", + subscriber_id, user.c_str(), realm.c_str()); + createRegistration(subscriber_id, user, pass, realm, contact); + scheduleRegistration(subscriber_id); + return; + } + + bool need_reregister = it->second->getInfo().domain != realm + || it->second->getInfo().user != user + || it->second->getInfo().pwd != pass + || it->second->getInfo().contact != contact; + + string old_realm = it->second->getInfo().domain; + string old_user = it->second->getInfo().user; + it->second->setRegistrationInfo(SIPRegistrationInfo(realm, user, + user, // name + user, // auth_user + pass, + outbound_proxy, // proxy + contact)); // contact + registrations_mut.unlock(); + if (need_reregister) { + DBG("user/realm for registration %ld changed (%s@%s -> %s@%s). " + "Triggering immediate re-registration\n", + subscriber_id, old_user.c_str(), old_realm.c_str(), user.c_str(), realm.c_str()); + scheduleRegistration(subscriber_id); + } +} + +/** remove registration from our list */ +void DBRegAgent::removeRegistration(long subscriber_id) { + bool res = false; + string handle; + registrations_mut.lock(); + map::iterator it = registrations.find(subscriber_id); + if (it != registrations.end()) { + handle = it->second->getHandle(); + registration_ltags.erase(handle); + delete it->second; + registrations.erase(it); + res = true; + } + registrations_mut.unlock(); + + if (res) { + // deregister us as SIP event receiver for this ltag + AmEventDispatcher::instance()->delEventQueue(handle); + + DBG("removed registration with ID %ld\n", subscriber_id); + } else { + DBG("registration with ID %ld not found for removing\n", subscriber_id); + } +} + +/** schedule this registration to REGISTER (immediately) */ +void DBRegAgent::scheduleRegistration(long subscriber_id) { + if (enable_ratelimiting) { + registration_processor. + postEvent(new RegistrationActionEvent(RegistrationActionEvent::Register, + subscriber_id)); + } else { + // use our own thread + postEvent(new RegistrationActionEvent(RegistrationActionEvent::Register, + subscriber_id)); + } + DBG("added to pending actions: REGISTER of %ld\n", subscriber_id); +} + +/** schedule this registration to de-REGISTER (immediately) */ +void DBRegAgent::scheduleDeregistration(long subscriber_id) { + if (enable_ratelimiting) { + registration_processor. + postEvent(new RegistrationActionEvent(RegistrationActionEvent::Deregister, + subscriber_id)); + } else { + // use our own thread + postEvent(new RegistrationActionEvent(RegistrationActionEvent::Deregister, + subscriber_id)); + } + DBG("added to pending actions: DEREGISTER of %ld\n", subscriber_id); +} + +void DBRegAgent::process(AmEvent* ev) { + + if (ev->event_id == RegistrationActionEventID) { + RegistrationActionEvent* reg_action_ev = + dynamic_cast(ev); + if (reg_action_ev) { + onRegistrationActionEvent(reg_action_ev); + return; + } + } + + AmSipReplyEvent* sip_rep = dynamic_cast(ev); + if (sip_rep) { + onSipReplyEvent(sip_rep); + return; + } + + if (ev->event_id == E_SYSTEM) { + AmSystemEvent* sys_ev = dynamic_cast(ev); + if(sys_ev){ + DBG("Session received system Event\n"); + if (sys_ev->sys_event == AmSystemEvent::ServerShutdown) { + running = false; + registration_scheduler._timer_thread_running = false; + } + return; + } + } + + ERROR("unknown event received!\n"); +} + +// uses ProcessorDBConnection +void DBRegAgent::onRegistrationActionEvent(RegistrationActionEvent* reg_action_ev) { + switch (reg_action_ev->action) { + case RegistrationActionEvent::Register: + { + DBG("REGISTER of registration %ld\n", reg_action_ev->subscriber_id); + registrations_mut.lock(); + map::iterator it= + registrations.find(reg_action_ev->subscriber_id); + if (it==registrations.end()) { + DBG("ignoring scheduled REGISTER of unknown registration %ld\n", + reg_action_ev->subscriber_id); + } else { + if (!it->second->doRegistration()) { + updateDBRegistration(ProcessorDBConnection, + reg_action_ev->subscriber_id, + 480, ERR_REASON_UNABLE_TO_SEND_REQUEST, + true, REG_STATUS_FAILED); + if (error_retry_interval) { + // schedule register-refresh after error_retry_interval + setRegistrationTimer(reg_action_ev->subscriber_id, error_retry_interval, + RegistrationActionEvent::Register); + } + } + } + registrations_mut.unlock(); + } break; + case RegistrationActionEvent::Deregister: + { + DBG("De-REGISTER of registration %ld\n", reg_action_ev->subscriber_id); + registrations_mut.lock(); + map::iterator it= + registrations.find(reg_action_ev->subscriber_id); + if (it==registrations.end()) { + DBG("ignoring scheduled De-REGISTER of unknown registration %ld\n", + reg_action_ev->subscriber_id); + } else { + if (!it->second->doUnregister()) { + if (delete_removed_registrations && delete_failed_deregistrations) { + DBG("sending de-Register failed - deleting registration %ld " + "(delete_failed_deregistrations=yes)\n", reg_action_ev->subscriber_id); + deleteDBRegistration(reg_action_ev->subscriber_id, ProcessorDBConnection); + } else { + DBG("failed sending de-register, updating DB with REG_STATUS_TO_BE_REMOVED " + ERR_REASON_UNABLE_TO_SEND_REQUEST "for subscriber %ld\n", + reg_action_ev->subscriber_id); + updateDBRegistration(ProcessorDBConnection, + reg_action_ev->subscriber_id, + 480, ERR_REASON_UNABLE_TO_SEND_REQUEST, + true, REG_STATUS_TO_BE_REMOVED); + // don't re-try de-registrations if sending failed + // if (error_retry_interval) { + // // schedule register-refresh after error_retry_interval + // setRegistrationTimer(reg_action_ev->subscriber_id, error_retry_interval, + // RegistrationActionEvent::Deregister); + // } + } + } + } + registrations_mut.unlock(); + } break; + } +} + +void DBRegAgent::createDBRegistration(long subscriber_id, mysqlpp::Connection& conn) { + string insert_query = "insert into "+registrations_table+ + " (subscriber_id) values ("+ + long2str(subscriber_id)+");"; + + try { + mysqlpp::Query query = conn.query(); + query << insert_query; + + mysqlpp::SimpleResult res = query.execute(); + if (!res) { + WARN("creating registration in DB with query '%s' failed: '%s'\n", + insert_query.c_str(), res.info()); + } + } catch (const mysqlpp::Exception& er) { + // Catch-all for any MySQL++ exceptions + ERROR("MySQL++ error: %s\n", er.what()); + return; + } +} + +void DBRegAgent::deleteDBRegistration(long subscriber_id, mysqlpp::Connection& conn) { + string insert_query = "delete from "+registrations_table+ + " where subscriber_id=" + long2str(subscriber_id)+";"; + + try { + mysqlpp::Query query = conn.query(); + query << insert_query; + + mysqlpp::SimpleResult res = query.execute(); + if (!res) { + WARN("removing registration in DB with query '%s' failed: '%s'\n", + insert_query.c_str(), res.info()); + } + } catch (const mysqlpp::Exception& er) { + // Catch-all for any MySQL++ exceptions + ERROR("MySQL++ error: %s\n", er.what()); + return; + } +} + +void DBRegAgent::updateDBRegistration(mysqlpp::Connection& db_connection, + long subscriber_id, int last_code, + const string& last_reason, + bool update_status, int status, + bool update_ts, unsigned int expiry, + bool update_contacts, const string& contacts) { + try { + + mysqlpp::Query query = db_connection.query(); + + query << "update "+registrations_table+" set last_code="+ int2str(last_code) +", "; + query << "last_reason="; + query << mysqlpp::quote << last_reason; + + if (update_status) { + query << ", registration_status="+int2str(status); + } + + if (update_ts) { + query << ", last_registration=NOW(), " + "expiry=TIMESTAMPADD(SECOND,"+int2str(expiry)+", NOW())"; + } + + if (update_contacts) { + query << ", contacts=" << mysqlpp::quote << contacts; + } + + query << " where " COLNAME_SUBSCRIBER_ID "="+long2str(subscriber_id) + ";"; + string query_str = query.str(); + DBG("updating registration in DB with query '%s'\n", query_str.c_str()); + + mysqlpp::SimpleResult res = query.execute(); + if (!res) { + WARN("updating registration in DB with query '%s' failed: '%s'\n", + query_str.c_str(), res.info()); + } else { + if (!res.rows()) { + // should not happen - DB entry is created on load or on createRegistration + DBG("creating registration DB entry for subscriber %ld\n", subscriber_id); + createDBRegistration(subscriber_id, db_connection); + query.reset(); + query << query_str; + + mysqlpp::SimpleResult res = query.execute(); + if (!res || !res.rows()) { + WARN("updating registration in DB with query '%s' failed: '%s'\n", + query_str.c_str(), res.info()); + } + } + } + + } catch (const mysqlpp::Exception& er) { + // Catch-all for any MySQL++ exceptions + ERROR("MySQL++ error: %s\n", er.what()); + return; + } + +} + +// uses MainDBConnection +void DBRegAgent::onSipReplyEvent(AmSipReplyEvent* ev) { + if (!ev) return; + + DBG("received SIP reply event for '%s'\n", +#ifdef HAS_OFFER_ANSWER + ev->reply.from_tag.c_str() +#else + ev->reply.local_tag.c_str() +#endif + ); + registrations_mut.lock(); + + string local_tag = +#ifdef HAS_OFFER_ANSWER + ev->reply.from_tag; +#else + ev->reply.local_tag; +#endif + + map::iterator it=registration_ltags.find(local_tag); + if (it!=registration_ltags.end()) { + long subscriber_id = it->second; + map::iterator r_it=registrations.find(subscriber_id); + if (r_it != registrations.end()) { + AmSIPRegistration* registration = r_it->second; + if (!registration) { + ERROR("Internal error: registration object missing\n"); + return; + } + unsigned int cseq_before = registration->getDlg()->cseq; + +#ifdef HAS_OFFER_ANSWER + registration->getDlg()->onRxReply(ev->reply); +#else + registration->getDlg()->updateStatus(ev->reply); +#endif + + //update registrations set + bool update_status = false; + int status = 0; + bool update_ts = false; + unsigned int expiry = 0; + bool delete_status = false; + bool auth_pending = false; + + if (ev->reply.code >= 300) { + // REGISTER or de-REGISTER failed + if ((ev->reply.code == 401 || ev->reply.code == 407) && + // auth response codes + // processing reply triggered sending request: resent by auth + (cseq_before != registration->getDlg()->cseq)) { + DBG("received negative reply, but still in pending state (auth).\n"); + auth_pending = true; + } else { + if (!registration->getUnregistering()) { + // REGISTER failed - mark in DB + DBG("registration failed - mark in DB\n"); + update_status = true; + status = REG_STATUS_FAILED; + if (error_retry_interval) { + // schedule register-refresh after error_retry_interval + setRegistrationTimer(subscriber_id, error_retry_interval, + RegistrationActionEvent::Register); + } + } else { + // de-REGISTER failed + if (delete_removed_registrations && delete_failed_deregistrations) { + DBG("de-Register failed - deleting registration %ld " + "(delete_failed_deregistrations=yes)\n", subscriber_id); + delete_status = true; + } else { + update_status = true; + status = REG_STATUS_TO_BE_REMOVED; + } + } + } + } else if (ev->reply.code >= 200) { + // positive reply + if (!registration->getUnregistering()) { + time_t now_time = time(0); + setRegistrationTimer(subscriber_id, registration->getExpiresTS(), + now_time, now_time); + + update_status = true; + status = REG_STATUS_ACTIVE; + + update_ts = true; + expiry = registration->getExpiresLeft(); + } else { + if (delete_removed_registrations) { + delete_status = true; + } else { + update_status = true; + status = REG_STATUS_REMOVED; + } + } + } + + // skip provisional replies & auth + if (ev->reply.code >= 200 && !auth_pending) { + // remove unregistered + if (registration->getUnregistering()) { + registrations_mut.unlock(); + removeRegistration(subscriber_id); + registrations_mut.lock(); + } + } + + if (!delete_status) { + if (auth_pending && !save_auth_replies) { + DBG("not updating DB with auth reply %u %s\n", + ev->reply.code, ev->reply.reason.c_str()); + } else { + DBG("update DB with reply %u %s\n", ev->reply.code, ev->reply.reason.c_str()); + updateDBRegistration(MainDBConnection, + subscriber_id, ev->reply.code, ev->reply.reason, + update_status, status, update_ts, expiry, + save_contacts, ev->reply.contact); + } + } else { + DBG("delete DB registration of subscriber %ld\n", subscriber_id); + deleteDBRegistration(subscriber_id, MainDBConnection); + } + + } else { + ERROR("internal: inconsistent registration list\n"); + } + } else { + DBG("ignoring reply for unknown registration\n"); + } + registrations_mut.unlock(); +} + +void DBRegAgent::run() { + running = shutdown_finished = true; + + DBG("DBRegAgent thread: waiting 2 sec for server startup ...\n"); + sleep(2); + + mysqlpp::Connection::thread_start(); + + if (enable_ratelimiting) { + DBG("starting processor thread\n"); + registration_processor.start(); + } + + DBG("running DBRegAgent thread...\n"); + shutdown_finished = false; + while (running) { + processEvents(); + + usleep(1000); // 1ms + } + + DBG("DBRegAgent done, removing all registrations from Event Dispatcher...\n"); + registrations_mut.lock(); + for (map::iterator it=registration_ltags.begin(); + it != registration_ltags.end(); it++) { + AmEventDispatcher::instance()->delEventQueue(it->first); + } + registrations_mut.unlock(); + + DBG("removing "MOD_NAME" registrations from Event Dispatcher...\n"); + AmEventDispatcher::instance()->delEventQueue(MOD_NAME); + + mysqlpp::Connection::thread_end(); + + DBG("DBRegAgent thread stopped.\n"); + shutdown_finished = true; +} + +void DBRegAgent::on_stop() { + DBG("DBRegAgent on_stop()...\n"); + running = false; +} + +void DBRegAgent::setRegistrationTimer(long subscriber_id, unsigned int timeout, + RegistrationActionEvent::RegAction reg_action) { + DBG("setting Register timer for subscription %ld, timeout %u, reg_action %u\n", + subscriber_id, timeout, reg_action); + + RegTimer* timer = NULL; + map::iterator it=registration_timers.find(subscriber_id); + if (it==registration_timers.end()) { + DBG("timer object for subscription %ld not found\n", subscriber_id); + timer = new RegTimer(); + timer->data1 = subscriber_id; + timer->cb = _timer_cb; + DBG("created timer object [%p] for subscription %ld\n", timer, subscriber_id); + } else { + timer = it->second; + DBG("removing scheduled timer...\n"); + registration_scheduler.remove_timer(timer); + } + + timer->data2 = reg_action; + timer->expires = time(0) + timeout; + + DBG("placing timer for %ld in T-%u\n", subscriber_id, timeout); + registration_scheduler.insert_timer(timer); + + registration_timers.insert(std::make_pair(subscriber_id, timer)); + +} + +void DBRegAgent::setRegistrationTimer(long subscriber_id, + time_t expiry, time_t reg_start_ts, + time_t now_time) { + DBG("setting re-Register timer for subscription %ld, expiry %ld, reg_start_t %ld\n", + subscriber_id, expiry, reg_start_ts); + + RegTimer* timer = NULL; + map::iterator it=registration_timers.find(subscriber_id); + if (it==registration_timers.end()) { + DBG("timer object for subscription %ld not found\n", subscriber_id); + timer = new RegTimer(); + timer->data1 = subscriber_id; + timer->cb = _timer_cb; + DBG("created timer object [%p] for subscription %ld\n", timer, subscriber_id); + registration_timers.insert(std::make_pair(subscriber_id, timer)); + } else { + timer = it->second; + DBG("removing scheduled timer...\n"); + registration_scheduler.remove_timer(timer); + } + + timer->data2 = RegistrationActionEvent::Register; + + if (minimum_reregister_interval>0.0) { + time_t t_expiry_max = reg_start_ts; + time_t t_expiry_min = reg_start_ts; + if (expiry > reg_start_ts) + t_expiry_max+=(expiry - reg_start_ts) * reregister_interval; + if (expiry > reg_start_ts) + t_expiry_min+=(expiry - reg_start_ts) * minimum_reregister_interval; + + if (t_expiry_max < now_time) { + // calculated interval completely in the past - immediate re-registration + // by setting the timer to now + t_expiry_max = now_time; + } + + if (t_expiry_min > t_expiry_max) + t_expiry_min = t_expiry_max; + + timer->expires = t_expiry_max; + + if (t_expiry_max == now_time) { + // immediate re-registration + DBG("calculated re-registration at TS (%ld)" + "(reg_start_ts=%ld, reg_expiry=%ld, reregister_interval=%f, " + "minimum_reregister_interval=%f)\n", + t_expiry_max, reg_start_ts, expiry, + reregister_interval, minimum_reregister_interval); + registration_scheduler.insert_timer(timer); + } else { + DBG("calculated re-registration at TS %ld .. %ld" + "(reg_start_ts=%ld, reg_expiry=%ld, reregister_interval=%f, " + "minimum_reregister_interval=%f)\n", + t_expiry_min, t_expiry_max, reg_start_ts, expiry, + reregister_interval, minimum_reregister_interval); + + registration_scheduler.insert_timer_leastloaded(timer, t_expiry_min, t_expiry_max); + } + } else { + time_t t_expiry = reg_start_ts; + if (expiry > reg_start_ts) + t_expiry+=(expiry - reg_start_ts) * reregister_interval; + + if (t_expiry < now_time) { + t_expiry = now_time; + DBG("re-registering at TS (%ld)\n", now_time); + } + + DBG("calculated re-registration at TS %ld " + "(reg_start_ts=%ld, reg_expiry=%ld, reregister_interval=%f)\n", + t_expiry, reg_start_ts, expiry, reregister_interval); + + timer->expires = t_expiry; + registration_scheduler.insert_timer(timer); + } +} + +void DBRegAgent::clearRegistrationTimer(long subscriber_id) { + DBG("removing timer for subscription %ld", subscriber_id); + + map::iterator it=registration_timers.find(subscriber_id); + if (it==registration_timers.end()) { + DBG("timer object for subscription %ld not found\n", subscriber_id); + return; + } + DBG("removing timer [%p] from scheduler\n", it->second); + registration_scheduler.remove_timer(it->second); + + DBG("deleting timer object [%p]\n", it->second); + delete it->second; + + registration_timers.erase(it); +} + +void DBRegAgent::removeRegistrationTimer(long subscriber_id) { + DBG("removing timer object for subscription %ld", subscriber_id); + + map::iterator it=registration_timers.find(subscriber_id); + if (it==registration_timers.end()) { + DBG("timer object for subscription %ld not found\n", subscriber_id); + return; + } + + DBG("deleting timer object [%p]\n", it->second); + delete it->second; + + registration_timers.erase(it); +} + +void DBRegAgent::timer_cb(RegTimer* timer, long subscriber_id, int reg_action) { + DBG("re-registration timer expired: subscriber %ld, timer=[%p], action %d\n", + subscriber_id, timer, reg_action); + + registrations_mut.lock(); + removeRegistrationTimer(subscriber_id); + registrations_mut.unlock(); + switch (reg_action) { + case RegistrationActionEvent::Register: + scheduleRegistration(subscriber_id); break; + case RegistrationActionEvent::Deregister: + scheduleDeregistration(subscriber_id); break; + default: ERROR("internal: unknown reg_action %d for subscriber %ld timer event\n", + reg_action, subscriber_id); + }; +} + + +void DBRegAgent::DIcreateRegistration(int subscriber_id, const string& user, + const string& pass, const string& realm, + const string& contact, + AmArg& ret) { + DBG("DI method: createRegistration(%i, %s, %s, %s, %s)\n", + subscriber_id, user.c_str(), + pass.c_str(), realm.c_str(), contact.c_str()); + + createRegistration(subscriber_id, user, pass, realm, contact); + scheduleRegistration(subscriber_id); + ret.push(200); + ret.push("OK"); +} + +void DBRegAgent::DIupdateRegistration(int subscriber_id, const string& user, + const string& pass, const string& realm, + const string& contact, + AmArg& ret) { + DBG("DI method: updateRegistration(%i, %s, %s, %s)\n", + subscriber_id, user.c_str(), + pass.c_str(), realm.c_str()); + + string contact_uri = contact; + if (contact_uri.empty() && !contact_hostport.empty()) { + contact_uri = "sip:"+ user + "@" + contact_hostport; + } + + updateRegistration(subscriber_id, user, pass, realm, contact_uri); + + ret.push(200); + ret.push("OK"); +} + +void DBRegAgent::DIremoveRegistration(int subscriber_id, AmArg& ret) { + DBG("DI method: removeRegistration(%i)\n", + subscriber_id); + scheduleDeregistration(subscriber_id); + + registrations_mut.lock(); + clearRegistrationTimer(subscriber_id); + registrations_mut.unlock(); + + ret.push(200); + ret.push("OK"); +} + +void DBRegAgent::DIrefreshRegistration(int subscriber_id, AmArg& ret) { + DBG("DI method: refreshRegistration(%i)\n", subscriber_id); + scheduleRegistration(subscriber_id); + + ret.push(200); + ret.push("OK"); +} + +// ///////// DI API /////////////////// + +void DBRegAgent::invoke(const string& method, + const AmArg& args, AmArg& ret) +{ + if (method == "createRegistration"){ + args.assertArrayFmt("isss"); // subscriber_id, user, pass, realm + string contact; + if (args.size() > 4) { + assertArgCStr(args.get(4)); + contact = args.get(4).asCStr(); + } + DIcreateRegistration(args.get(0).asInt(), args.get(1).asCStr(), + args.get(2).asCStr(),args.get(3).asCStr(), + contact, ret); + } else if (method == "updateRegistration"){ + args.assertArrayFmt("isss"); // subscriber_id, user, pass, realm + string contact; + if (args.size() > 4) { + assertArgCStr(args.get(4)); + contact = args.get(4).asCStr(); + } + DIupdateRegistration(args.get(0).asInt(), args.get(1).asCStr(), + args.get(2).asCStr(),args.get(3).asCStr(), + contact, ret); + } else if (method == "removeRegistration"){ + args.assertArrayFmt("i"); // subscriber_id + DIremoveRegistration(args.get(0).asInt(), ret); + } else if (method == "refreshRegistration"){ + args.assertArrayFmt("i"); // subscriber_id + DIrefreshRegistration(args.get(0).asInt(), ret); + } else if(method == "_list"){ + ret.push(AmArg("createRegistration")); + ret.push(AmArg("updateRegistration")); + ret.push(AmArg("removeRegistration")); + ret.push(AmArg("refreshRegistration")); + } else + throw AmDynInvoke::NotImplemented(method); +} + +// /////////////// processor thread ///////////////// + +DBRegAgentProcessorThread::DBRegAgentProcessorThread() + : AmEventQueue(this), stopped(false) { +} + +DBRegAgentProcessorThread::~DBRegAgentProcessorThread() { +} + +void DBRegAgentProcessorThread::on_stop() { +} + +void DBRegAgentProcessorThread::rateLimitWait() { + DBG("applying rate limit %u initial requests per %us\n", + DBRegAgent::ratelimit_rate, DBRegAgent::ratelimit_per); + + DBG("allowance before ratelimit: %f\n", allowance); + + struct timeval current; + struct timeval time_passed; + gettimeofday(¤t, 0); + timersub(¤t, &last_check, &time_passed); + memcpy(&last_check, ¤t, sizeof(struct timeval)); + double seconds_passed = (double)time_passed.tv_sec + + (double)time_passed.tv_usec / 1000000.0; + allowance += seconds_passed * + (double) DBRegAgent::ratelimit_rate / (double)DBRegAgent::ratelimit_per; + + if (allowance > (double)DBRegAgent::ratelimit_rate) + allowance = (double)DBRegAgent::ratelimit_rate; // enough time passed, but limit to max + if (allowance < 1.0) { + useconds_t sleep_time = 1000000.0 * (1.0 - allowance) * + ((double)DBRegAgent::ratelimit_per/(double)DBRegAgent::ratelimit_rate); + DBG("not enough allowance (%f), sleeping %d useconds\n", allowance, sleep_time); + usleep(sleep_time); + allowance=0.0; + gettimeofday(&last_check, 0); + } else { + allowance -= 1.0; + } + + DBG("allowance left: %f\n", allowance); +} + +void DBRegAgentProcessorThread::run() { + DBG("DBRegAgentProcessorThread thread started\n"); + + // register us as SIP event receiver for MOD_NAME_processor + AmEventDispatcher::instance()->addEventQueue(MOD_NAME "_processor",this); + + mysqlpp::Connection::thread_start(); + + // initialize ratelimit + gettimeofday(&last_check, NULL); + if (DBRegAgent::ratelimit_slowstart) + allowance = 0.0; + else + allowance = DBRegAgent::ratelimit_rate; + + reg_agent = DBRegAgent::instance(); + while (!stopped) { + waitForEvent(); + while (eventPending()) { + rateLimitWait(); + processSingleEvent(); + } + } + + mysqlpp::Connection::thread_end(); + + DBG("DBRegAgentProcessorThread thread stopped\n"); +} + +void DBRegAgentProcessorThread::process(AmEvent* ev) { + + if (ev->event_id == E_SYSTEM) { + AmSystemEvent* sys_ev = dynamic_cast(ev); + if(sys_ev){ + DBG("Session received system Event\n"); + if (sys_ev->sys_event == AmSystemEvent::ServerShutdown) { + DBG("stopping processor thread\n"); + stopped = true; + } + return; + } + } + + + if (ev->event_id == RegistrationActionEventID) { + RegistrationActionEvent* reg_action_ev = + dynamic_cast(ev); + if (reg_action_ev) { + reg_agent->onRegistrationActionEvent(reg_action_ev); + return; + } + } + + ERROR("unknown event received!\n"); +} +#if 0 +void test_cb(RegTimer* tr, long data1, void* data2) { + DBG("cb called: [%p], data %ld / [%p]\n", tr, data1, data2); +} + +void DBRegAgent::run_tests() { + + registration_timer.start(); + + struct timeval now; + gettimeofday(&now, 0); + + RegTimer rt; + rt.expires = now.tv_sec + 10; + rt.cb=test_cb; + registration_scheduler.insert_timer(&rt); + + RegTimer rt2; + rt2.expires = now.tv_sec + 5; + rt2.cb=test_cb; + registration_scheduler.insert_timer(&rt2); + + RegTimer rt3; + rt3.expires = now.tv_sec + 15; + rt3.cb=test_cb; + registration_scheduler.insert_timer(&rt3); + + RegTimer rt4; + rt4.expires = now.tv_sec - 1; + rt4.cb=test_cb; + registration_scheduler.insert_timer(&rt4); + + RegTimer rt5; + rt5.expires = now.tv_sec + 100000; + rt5.cb=test_cb; + registration_scheduler.insert_timer(&rt5); + + RegTimer rt6; + rt6.expires = now.tv_sec + 100; + rt6.cb=test_cb; + registration_scheduler.insert_timer_leastloaded(&rt6, now.tv_sec+5, now.tv_sec+50); + + + sleep(30); + gettimeofday(&now, 0); + + RegTimer rt7; + rt6.expires = now.tv_sec + 980; + rt6.cb=test_cb; + registration_scheduler.insert_timer_leastloaded(&rt6, now.tv_sec+9980, now.tv_sec+9990); + + vector rts; + + for (int i=0;i<1000;i++) { + RegTimer* t = new RegTimer(); + rts.push_back(t); + t->expires = now.tv_sec + i; + t->cb=test_cb; + registration_scheduler.insert_timer_leastloaded(t, now.tv_sec, now.tv_sec+1000); + } + + sleep(200); +} +#endif diff --git a/apps/db_reg_agent/DBRegAgent.h b/apps/db_reg_agent/DBRegAgent.h new file mode 100644 index 00000000..868d999e --- /dev/null +++ b/apps/db_reg_agent/DBRegAgent.h @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2011 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ + +#ifndef _DB_REGAgent_h_ +#define _DB_REGAgent_h_ +#include + +#include + + +#include +using std::map; +#include +using std::queue; + +#include "AmApi.h" +#include "AmSipRegistration.h" + +#include "RegistrationTimer.h" + +#define REG_STATUS_INACTIVE 0 +#define REG_STATUS_PENDING 1 +#define REG_STATUS_ACTIVE 2 +#define REG_STATUS_FAILED 3 +#define REG_STATUS_REMOVED 4 +#define REG_STATUS_TO_BE_REMOVED 5 + +#define REG_STATUS_INACTIVE_S "0" +#define REG_STATUS_PENDING_S "1" +#define REG_STATUS_ACTIVE_S "2" +#define REG_STATUS_FAILED_S "3" +#define REG_STATUS_REMOVED_S "4" +#define REG_STATUS_TO_BE_REMOVED_S "5" + +#define COLNAME_SUBSCRIBER_ID "subscriber_id" +#define COLNAME_USER "user" +#define COLNAME_PASS "pass" +#define COLNAME_REALM "realm" +#define COLNAME_CONTACT "contact" + +#define COLNAME_STATUS "registration_status" +#define COLNAME_EXPIRY "expiry" +#define COLNAME_REGISTRATION_TS "last_registration" +#define COLNAME_LAST_CODE "last_code" +#define COLNAME_LAST_REASON "last_reason" + +#define RegistrationActionEventID 117 + +#define ERR_REASON_UNABLE_TO_SEND_REQUEST "unable to send request" + +struct RegistrationActionEvent : public AmEvent { + + enum RegAction { Register=0, Deregister }; + +RegistrationActionEvent(RegAction action, long subscriber_id) + : AmEvent(RegistrationActionEventID), + action(action), subscriber_id(subscriber_id) { } + + RegAction action; + long subscriber_id; +}; + +class DBRegAgent; + +// separate thread for REGISTER sending, which can block for rate limiting +class DBRegAgentProcessorThread +: public AmThread, + public AmEventQueue, + public AmEventHandler +{ + + DBRegAgent* reg_agent; + bool stopped; + + void rateLimitWait(); + + double allowance; + struct timeval last_check; + + protected: + void process(AmEvent* ev); + + public: + DBRegAgentProcessorThread(); + ~DBRegAgentProcessorThread(); + + void run(); + void on_stop(); + +}; + +class DBRegAgent +: public AmDynInvokeFactory, + public AmDynInvoke, + public AmThread, + public AmEventQueue, + public AmEventHandler +{ + + static string joined_query; + static string registrations_table; + + static double reregister_interval; + static double minimum_reregister_interval; + + static bool enable_ratelimiting; + static unsigned int ratelimit_rate; + static unsigned int ratelimit_per; + static bool ratelimit_slowstart; + + static bool delete_removed_registrations; + static bool delete_failed_deregistrations; + static bool save_contacts; + + static bool db_read_contact; + + static string contact_hostport; + + static string outbound_proxy; + + static bool save_auth_replies; + + static unsigned int error_retry_interval; + + map registrations; + map registration_ltags; + map registration_timers; + AmMutex registrations_mut; + + // connection used in main DBRegAgent thread + static mysqlpp::Connection MainDBConnection; + + // connection used in other thread (processor thread) + static mysqlpp::Connection ProcessorDBConnection; + + int onLoad(); + + // atomic_ref_cnt interface + void on_destroy() { + onUnload(); + } + + void onUnload(); + + RegistrationTimer registration_scheduler; + DBRegAgentProcessorThread registration_processor; + + bool loadRegistrations(); + + void createDBRegistration(long subscriber_id, mysqlpp::Connection& conn); + void deleteDBRegistration(long subscriber_id, mysqlpp::Connection& conn); + void updateDBRegistration(mysqlpp::Connection& db_connection, + long subscriber_id, int last_code, + const string& last_reason, + bool update_status = false, int status = 0, + bool update_ts=false, unsigned int expiry = 0, + bool update_contacts=false, const string& contacts = ""); + + /** create registration in our list */ + void createRegistration(long subscriber_id, + const string& user, + const string& pass, + const string& realm, + const string& contact); + /** update registration in our list */ + void updateRegistration(long subscriber_id, + const string& user, + const string& pass, + const string& realm, + const string& contact); + + /** remove registration */ + void removeRegistration(long subscriber_id); + + /** schedule this subscriber to REGISTER imminently */ + void scheduleRegistration(long subscriber_id); + + /** schedule this subscriber to de-REGISTER imminently*/ + void scheduleDeregistration(long subscriber_id); + + /** create a timer for the registration - fixed expiry + action */ + void setRegistrationTimer(long subscriber_id, unsigned int timeout, + RegistrationActionEvent::RegAction reg_action); + + /** create a registration refresh timer for that registration + @param subscriber_id - ID of subscription + @param expiry - SIP registration expiry time + @param reg_start_ts - start TS of the SIP registration + @param now_time - current time + */ + void setRegistrationTimer(long subscriber_id, + time_t expiry, time_t reg_start_ts, time_t now_time); + + /** clear re-registration timer and remove timer object */ + void clearRegistrationTimer(long subscriber_id); + + /** remove timer object */ + void removeRegistrationTimer(long subscriber_id); + + // void run_tests(); + + // amThread + void run(); + void on_stop(); + + // AmEventHandler + void process(AmEvent* ev); + + void onSipReplyEvent(AmSipReplyEvent* ev); + + void onRegistrationActionEvent(RegistrationActionEvent* reg_action_ev); + + + unsigned int expires; + + /** processing thread running? */ + bool running; + + /** processing thread shutdown finished? */ + bool shutdown_finished; + + AmDynInvoke* uac_auth_i; + + void DIcreateRegistration(int subscriber_id, const string& user, + const string& pass, const string& realm, + const string& contact, AmArg& ret); + void DIupdateRegistration(int subscriber_id, const string& user, + const string& pass, const string& realm, + const string& contact, AmArg& ret); + void DIremoveRegistration(int subscriber_id, AmArg& ret); + void DIrefreshRegistration(int subscriber_id, AmArg& ret); + + + public: + DBRegAgent(const string& _app_name); + ~DBRegAgent(); + + DECLARE_MODULE_INSTANCE(DBRegAgent); + + // DI + // DI factory + AmDynInvoke* getInstance() { return instance(); } + // DI API + void invoke(const string& method, + const AmArg& args, AmArg& ret); + /** re-registration timer callback */ + void timer_cb(RegTimer* timer, long subscriber_id, int data2); + + friend class DBRegAgentProcessorThread; +}; + +#endif diff --git a/apps/db_reg_agent/Makefile b/apps/db_reg_agent/Makefile new file mode 100644 index 00000000..9cdbbc1e --- /dev/null +++ b/apps/db_reg_agent/Makefile @@ -0,0 +1,9 @@ +plug_in_name = db_reg_agent + +MYSQLPP_DEFS = -DMYSQLPP_MYSQL_HEADERS_BURIED + +module_ldflags = -lmysqlpp +module_cflags = -DHAS_OFFER_ANSWER -DMOD_NAME=\"$(plug_in_name)\" $(MYSQLPP_DEFS) + +COREPATH ?=../../core +include $(COREPATH)/plug-in/Makefile.app_module diff --git a/apps/db_reg_agent/RegistrationTimer.cpp b/apps/db_reg_agent/RegistrationTimer.cpp new file mode 100644 index 00000000..79d5be36 --- /dev/null +++ b/apps/db_reg_agent/RegistrationTimer.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2011 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ + +#include "RegistrationTimer.h" +#include + +RegistrationTimer::RegistrationTimer() + : current_bucket(0) +{ + struct timeval now; + gettimeofday(&now, 0); + current_bucket_start = now.tv_sec; +} + +// unsafe! +int RegistrationTimer::get_bucket_index(time_t tv) { + time_t buckets_start_time = current_bucket_start; + + if (tv < buckets_start_time) + return -1; + + // offset + int bucket_index = (tv - buckets_start_time); + bucket_index /= TIMER_BUCKET_LENGTH; + + if (bucket_index > TIMER_BUCKETS) { // too far in the future + ERROR("requested timer too far in the future (index %d vs %d TIMER_BUCKETS)\n", + bucket_index, TIMER_BUCKETS); + return -2; + } + + bucket_index += current_bucket; + bucket_index %= TIMER_BUCKETS; // circular array + + return bucket_index; +} + +void RegistrationTimer::place_timer(RegTimer* timer, int bucket_index) { + if (bucket_index < 0) { + ERROR("trying to place_timer with negative index (%i)\n", bucket_index); + return; + } + + if (bucket_index > TIMER_BUCKETS) { + ERROR("trying to place_timer with too high index (%i vs %i)\n", + bucket_index, TIMER_BUCKETS); + return; + } + + std::list::iterator it = buckets[bucket_index].timers.begin(); + while (it != buckets[bucket_index].timers.end() && + (timer->expires > (*it)->expires)) + it++; + + buckets[bucket_index].timers.insert(it, timer); + size_t b_size = buckets[bucket_index].timers.size(); + + DBG("inserted timer [%p] in bucket %i (now sized %zd)\n", + timer, bucket_index, b_size); +} + +void RegistrationTimer::fire_timer(RegTimer* timer) { + if (timer && timer->cb) { + DBG("firing timer [%p]\n", timer); + timer->cb(timer, timer->data1, timer->data2); + } +} + +bool RegistrationTimer::insert_timer(RegTimer* timer) { + if (!timer) + return false; + + buckets_mut.lock(); + int bucket_index = get_bucket_index(timer->expires); + + if (bucket_index == -1) { + // already expired, fire timer + buckets_mut.unlock(); + DBG("inserting already expired timer [%p], firing\n", timer); + fire_timer(timer); + return false; + } + + if (bucket_index == -2) { + ERROR("trying to place timer too far in the future\n"); + buckets_mut.unlock(); + return false; + } + + place_timer(timer, bucket_index); + + buckets_mut.unlock(); + + return true; +} + +bool RegistrationTimer::remove_timer(RegTimer* timer) { + if (!timer) + return false; + + bool res = false; + + buckets_mut.lock(); + int bucket_index = get_bucket_index(timer->expires); + + if (bucket_index < 0) { + buckets_mut.unlock(); + return false; + } + + std::list& timerlist = buckets[bucket_index].timers; + + for (std::list::iterator it = timerlist.begin(); + it != timerlist.end(); it++) { + if (*it == timer) { + timerlist.erase(it); + res = true; + break; + } + } + + buckets_mut.unlock(); + + if (res) { + DBG("successfully removed timer [%p]\n", timer); + } else { + DBG("timer [%p] not found for removing\n", timer); + } + return res; +} + +void RegistrationTimer::run_timers() { + std::list timers_tbf; + + struct timeval now; + gettimeofday(&now, 0); + + buckets_mut.lock(); + + // bucket over? + if (now.tv_sec > current_bucket_start + TIMER_BUCKET_LENGTH) { + timers_tbf.insert(timers_tbf.begin(), + buckets[current_bucket].timers.begin(), + buckets[current_bucket].timers.end()); + buckets[current_bucket].timers.clear(); + current_bucket++; + current_bucket %= TIMER_BUCKETS; + current_bucket_start += TIMER_BUCKET_LENGTH; + // DBG("turned bucket to %i\n", current_bucket); + } + + // move timers from current_bucket + RegTimerBucket& bucket = buckets[current_bucket]; + std::list::iterator it = bucket.timers.begin(); + while (it != bucket.timers.end() && + now.tv_sec > (*it)->expires) { + std::list::iterator c_it = it; + it++; + timers_tbf.push_back(*c_it); + bucket.timers.erase(c_it); + } + + buckets_mut.unlock(); + + if (!timers_tbf.empty()) { + DBG("firing %zd timers\n", timers_tbf.size()); + for (std::list::iterator it=timers_tbf.begin(); + it != timers_tbf.end(); it++) { + fire_timer(*it); + } + } +} + +void RegistrationTimer::run() +{ + struct timeval now,next_tick,diff,tick; + _shutdown_finished = false; + + tick.tv_sec = 0; + tick.tv_usec = TIMER_RESOLUTION; + + gettimeofday(&now, NULL); + timeradd(&tick,&now,&next_tick); + + _timer_thread_running = true; + + while(_timer_thread_running){ + + gettimeofday(&now,NULL); + + if(timercmp(&now,&next_tick,<)){ + + struct timespec sdiff,rem; + timersub(&next_tick, &now,&diff); + + sdiff.tv_sec = diff.tv_sec; + sdiff.tv_nsec = diff.tv_usec * 1000; + + if(sdiff.tv_nsec > 2000000) // 2 ms + nanosleep(&sdiff,&rem); + } + //else { + //printf("missed one tick\n"); + //} + + run_timers(); + timeradd(&tick,&next_tick,&next_tick); + } + + DBG("RegistrationTimer thread finishing.\n"); + _shutdown_finished = true; +} + +void RegistrationTimer::on_stop() { +} + +bool RegistrationTimer::insert_timer_leastloaded(RegTimer* timer, + time_t from_time, + time_t to_time) { + + buckets_mut.lock(); + + int from_index = get_bucket_index(from_time); + int to_index = get_bucket_index(to_time); + + if (from_index < 0 && to_index < 0) { + ERROR("could not find timer bucket indices - " + "from_index = %d, to_index = %d, from_time = %ld, to_time %ld, " + "current_bucket_start = %ld\n", + from_index, to_index, from_time, to_time, current_bucket_start); + buckets_mut.unlock(); + return false; + } + + if (from_index < 0) { + // use now .. to_index + DBG("from_time (%ld) in the past - searching load loaded from now()\n", from_time); + from_index = current_bucket; + } + // find least loaded bucket + int res_index = from_index; + size_t least_load = buckets[from_index].timers.size(); + + int i = from_index; + while (i != to_index) { + if (buckets[i].timers.size() <= least_load) { + least_load = buckets[i].timers.size(); + res_index = i; + } + + i++; + i %= TIMER_BUCKETS; + } + DBG("found bucket %i with least load %zd (between %i and %i)\n", + res_index, least_load, from_index, to_index); + + // update expires to some random value inside the selected bucket + int diff = (unsigned)res_index - current_bucket; + + if ((unsigned)res_index < current_bucket) { + diff+=TIMER_BUCKETS; + } + + timer->expires = current_bucket_start + + diff * TIMER_BUCKET_LENGTH + // bucket start + rand() % TIMER_BUCKET_LENGTH; + DBG("setting expires to %ld (between %ld and %ld)\n", + timer->expires, from_time, to_time); + + place_timer(timer, res_index); + + buckets_mut.unlock(); + + return false; +} + + + + diff --git a/apps/db_reg_agent/RegistrationTimer.h b/apps/db_reg_agent/RegistrationTimer.h new file mode 100644 index 00000000..c39fb1e5 --- /dev/null +++ b/apps/db_reg_agent/RegistrationTimer.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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. + * + * For a license to use the sems software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ + +#ifndef _RegistrationTimer_h_ +#define _RegistrationTimer_h_ + +#include +#include + +#include + +#include "log.h" +#include "AmThread.h" + +#define TIMER_BUCKET_LENGTH 10 // 10 sec +#define TIMER_BUCKETS 40000 // 40000 buckets (400000 sec, 111 hrs) + +// 100 ms == 100000 us +#define TIMER_RESOLUTION 100000 + +class RegTimer; +typedef void (*timer_cb)(RegTimer*, long /*data1*/,int /*data2*/); + +class RegTimerBucket; + +class RegTimer { + public: + time_t expires; + + timer_cb cb; + long data1; + int data2; + + RegTimer() + : expires(0), cb(0), data1(0), data2(0) { } +}; + +class RegTimerBucket { + public: + std::list timers; + + RegTimerBucket() { } +}; + +/** + Additionally to normal timer operation (setting and removing timer, + fire the timer when it is expired), this RegistrationTimer timers + class needs to support insert_timer_leastloaded() which should insert + the timer in some least loaded interval between from_time and to_time + in order to flatten out re-register spikes (due to restart etc). + + Timer granularity is seconds. + + Timers are saved in buckets of TIMER_BUCKET_LENGTH seconds. the buckets + array is a circular one, the current bucket starts from the time + current_bucket_start (in seconds as in time(2)). + + The timer object is owned by the caller, and MUST be valid until it is + fired or removed. + */ + +class RegistrationTimer +: public AmThread +{ + time_t current_bucket_start; + // every bucket contains TIMER_BUCKET_LENGTH seconds of timers + RegTimerBucket buckets[TIMER_BUCKETS]; + unsigned int current_bucket; + AmMutex buckets_mut; + + int get_bucket_index(time_t tv); + void place_timer(RegTimer* timer, int bucket_index); + void fire_timer(RegTimer* timer); + void run_timers(); + + protected: + void run(); + void on_stop(); + + public: + bool insert_timer(RegTimer* timer); + bool remove_timer(RegTimer* timer); + + bool insert_timer_leastloaded(RegTimer* timer, + time_t from_time, + time_t to_time); + + RegistrationTimer(); + bool _timer_thread_running; + bool _shutdown_finished; +}; + +#endif diff --git a/apps/db_reg_agent/etc/db_reg_agent.conf b/apps/db_reg_agent/etc/db_reg_agent.conf new file mode 100644 index 00000000..a77abf07 --- /dev/null +++ b/apps/db_reg_agent/etc/db_reg_agent.conf @@ -0,0 +1,99 @@ +# Database connection +mysql_user=root +mysql_passwd=mypass123 + +# mysql_server, default: localhost +# mysql_server=localhost + +#mysql_db, default: sems +# mysql_db=sems + +# table for registration status +# default: registrations +# registrations_table="registrations" + +# query joining subscriber info with registration table +joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;" + +#example with contact: +# joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, subscribers.contact as contact, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;" + +# outbound_proxy= +# set the outbound proxy to send registrations through +# +#outbound_proxy=sip:10.0.0.2:5070 + +# contact_hostport= - overriding contact host:port +# sets the contact host:port portion that is registered +# default: empty +#contact_hostport=10.0.0.50:5080 + +# db_read_contact=[yes, no] : read individual contact to register from DB? +# note: joined_query must select the contact, too +# default: no +#db_read_contact=yes + +#save_contacts=[yes, no] : save contacts? +# for monitoring and troubleshooting, all contacts returned with a positive reply +# may be saved to DB in the registrations.contacts colums +# default: yes +#save_contacts=yes + +#save_auth_replies=[yes, no] : update status on 401 replies (auth)? +# usually, 401 replies which are resent with auth are not saved. if save_auth_replies=yes, +# last_code and last_reason in DB is updated with the 401 +#save_auth_replies=yes + +# expires: desired expires, i.e. expires value that is requested +# default: 7200 +# expires=300 + +# error_retry_interval +# +# default: 300 + +# reregister_interval: fraction of actual expires after which register is refreshed +# default: reregister_interval=0.5 +#reregister_interval=0.5 + +# minimum_reregister_interval: if set, re-register is scheduled in least loaded time +# in minimum_reregister_interval .. reregister_interval i order to smooth load spikes +# must be smaller than reregister_interval +# default: off +# +# example: +# reregister_interval=0.5 +# minimum_reregister_interval=0.4 +# on a registration expiring in 3600s, the least loaded spot between 1440s and 1800s +# is chosen +# +#minimum_reregister_interval=0.4 + +# enable_ratelimiting=yes : Enable ratelimiting? +# default: no +# if enabled, the amount of initial REGISTER requests is limited (not counting re-trans- +# missions and requests re-sent for authentication) +#enable_ratelimiting=yes + +# ratelimit_rate=300 : rate of initial REGISTER requests to send as maximum +#ratelimit_rate=2 + +# ratelimit_per=1 : per time unit (in seconds, e.g. 300 REGISTER in 1 second) +#ratelimit_per=1 + +#ratelimit_slowstart=[yes|no] : on startup send initially ratelimit_rate or 0 requests +#default: no +#ratelimit_slowstart=yes + +# delete_removed_registrations=yes : delete removed registrations from registrations +# table in DB? (otherwise they will stay with STATUS_REMOVED) +# default: yes +# +#delete_removed_registrations=no + +# delete_failed_deregistrations=yes : delete failed de-registrations from registrations +# table in DB? (otherwise they will stay with STATUS_TO_BE_REMOVED) +# only applicable if delete_removed_registrations=yes +# default: no +# +#delete_failed_deregistrations=no diff --git a/apps/diameter_client/Makefile b/apps/diameter_client/Makefile index c9fadb17..f0d41af7 100644 --- a/apps/diameter_client/Makefile +++ b/apps/diameter_client/Makefile @@ -12,7 +12,7 @@ module_cflags = -I $(DIAMETER_BASE_LIBDIR) ifdef WITH_OPENSSL module_ldflags += -lssl -lcrypto -module_cflags += -DWITH_OPENSSL +#module_cflags += -DWITH_OPENSSL endif module_extra_objs = $(DIAMETER_BASE_LIBDIR)$(DIAMETER_BASE_LIBNAME) @@ -22,7 +22,7 @@ COREPATH ?=../../core include $(COREPATH)/plug-in/Makefile.app_module $(DIAMETER_BASE_LIBDIR)$(DIAMETER_BASE_LIBNAME): - make -C $(DIAMETER_BASE_LIBDIR) + $(MAKE) -C $(DIAMETER_BASE_LIBDIR) baseclean: - make -C $(DIAMETER_BASE_LIBDIR) clean + $(MAKE) -C $(DIAMETER_BASE_LIBDIR) clean diff --git a/apps/diameter_client/ServerConnection.cpp b/apps/diameter_client/ServerConnection.cpp index 5faa5f11..fe4c8d47 100644 --- a/apps/diameter_client/ServerConnection.cpp +++ b/apps/diameter_client/ServerConnection.cpp @@ -323,7 +323,7 @@ AAAMessage* ServerConnection::ReqEvent2AAAMessage(DiameterRequestEvent* re) { } AAA_AVP *avp; - if( (avp=AAACreateAVP(avp_id, (AAA_AVPFlag)flags, vendor, data->data, + if( (avp=AAACreateAVP(avp_id, (AAA_AVPFlag)flags, vendor, (const char*)data->data, data->len, AVP_DUPLICATE_DATA)) == 0) { ERROR( M_NAME ": addDataAVP() no more free memory!\n"); continue; diff --git a/apps/diameter_client/lib_dbase/Makefile b/apps/diameter_client/lib_dbase/Makefile index 1bd3047e..1657c844 100644 --- a/apps/diameter_client/lib_dbase/Makefile +++ b/apps/diameter_client/lib_dbase/Makefile @@ -3,21 +3,20 @@ lib_name = lib_dbase.a .PHONY: all all: $(lib_name) -MCOREPATH ?=../../../core -include $(MCOREPATH)/../Makefile.defs - +COREPATH = ../../../core +include $(COREPATH)/../Makefile.defs srcs = $(wildcard *.c) hrds = $(wildcard *.h) objs = $(srcs:.c=.o) depends = $(srcs:.c=.d) -cflags ?= $(CPPFLAGS) $(C_FLAGS) -I $(MCOREPATH) -Wall $(module_cflags) +cflags ?= $(CPPFLAGS) $(CFLAGS) -I $(COREPATH) WITH_OPENSSL = 1 ifdef WITH_OPENSSL -cflags+= -D WITH_OPENSSL +cflags+= -D WITH_OPENSSL -Wno-deprecated-declarations endif AR = ar @@ -28,21 +27,12 @@ RANLIB = ranlib clean: -@rm -f $(objs) $(depends) $(lib_name) -.PHONY: deps -deps: $(depends) - -%.d: %.c %.h Makefile - $(CC) -MM $< $(cflags) > $@ +-include $(depends) -%.o: %.c %.d - $(CC) $(cflags) -c $< -o $@ -fPIC +%.o: %.c Makefile + $(CC) -MMD $(cflags) -c $< -o $@ -fPIC -$(lib_name): deps $(objs) Makefile +$(lib_name): $(objs) Makefile if [ -f $(lib_name) ]; then rm $(RMFLAGS) $(lib_name) ; fi $(AR) $(ARFLAGS) $(lib_name) $(objs) $(RANLIB) $(lib_name) - -ifeq ($(lib_name),$(MAKECMDGOALS)) -include $(depends) -endif - diff --git a/apps/dsm/DSM.cpp b/apps/dsm/DSM.cpp index 7f50d9f1..971d31ef 100644 --- a/apps/dsm/DSM.cpp +++ b/apps/dsm/DSM.cpp @@ -683,12 +683,13 @@ void DSMFactory::runMonitorAppSelect(const AmSipRequest& req, string& start_diag #undef FALLBACK_OR_EXCEPTION } -AmSession* DSMFactory::onInvite(const AmSipRequest& req) +AmSession* DSMFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { string start_diag; map vars; - if (req.cmd == MOD_NAME) { + if (app_name == MOD_NAME) { if (InboundStartDiag.empty()) { ERROR("no inbound calls allowed\n"); throw AmSession::Exception(488, "Not Acceptable Here"); @@ -699,9 +700,11 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req) start_diag = InboundStartDiag; } } else { - start_diag = req.cmd; + start_diag = app_name; } + DBG("start_diag = %s\n",start_diag.c_str()); + // determine run configuration for script DSMScriptConfig call_config; ScriptConfigs_mut.lock(); @@ -728,36 +731,32 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req) } // outgoing call -AmSession* DSMFactory::onInvite(const AmSipRequest& req, +AmSession* DSMFactory::onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params) { string start_diag; - if (req.cmd == MOD_NAME) { + if (app_name == MOD_NAME) { if (OutboundStartDiag.empty()) { ERROR("no outbound calls allowed\n"); - throw AmSession::Exception(488, "Not Acceptable Here"); + throw AmSession::Exception(488, "Not Acceptable Here"); } } else { - start_diag = req.cmd; + start_diag = app_name; } UACAuthCred* cred = NULL; map vars; // Creds if (session_params.getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.asObject(); + AmObject* cred_obj = session_params.asObject(); if (cred_obj) cred = dynamic_cast(cred_obj); } else if (session_params.getType() == AmArg::Array) { DBG("session params is array - size %zd\n", session_params.size()); // Creds - if (session_params.get(0).getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.get(0).asObject(); - if (cred_obj) - cred = dynamic_cast(cred_obj); - } + cred = AmUACAuth::unpackCredentials(session_params.get(0)); // Creds + vars if (session_params.size()>1 && session_params.get(1).getType() == AmArg::Struct) { @@ -792,17 +791,7 @@ AmSession* DSMFactory::onInvite(const AmSipRequest& req, if (NULL == cred) { DBG("outgoing DSM call will not be authenticated.\n"); } else { - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new DSM session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(s); - if (h != NULL ) - s->addHandler(h); - } else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated dialout.\n"); - } + AmUACAuth::enable(s); } return s; @@ -1110,6 +1099,28 @@ void DSMFactory::loadDSMWithPaths(const AmArg& args, AmArg& ret) { ScriptConfigs_mut.unlock(); } +bool DSMFactory::addScriptDiagsToEngine(const string& config_set, + DSMStateEngine* engine, + map& config_vars, + bool& SetParamVariables) { + bool res = false; + ScriptConfigs_mut.lock(); + try { + map::iterator it=Name2ScriptConfig.find(config_set); + if (it!=Name2ScriptConfig.end()) { + res = true; + it->second.diags->addToEngine(engine); + config_vars = it->second.config_vars; + SetParamVariables = it->second.SetParamVariables; + } + } catch(...) { + ScriptConfigs_mut.unlock(); + throw; + } + ScriptConfigs_mut.unlock(); + return res; +} + void DSMFactory::invoke(const string& method, const AmArg& args, AmArg& ret) { diff --git a/apps/dsm/DSM.h b/apps/dsm/DSM.h index 0cdeb411..20933fba 100644 --- a/apps/dsm/DSM.h +++ b/apps/dsm/DSM.h @@ -136,8 +136,9 @@ public: int onLoad(); - AmSession* onInvite(const AmSipRequest& req); - AmSession* onInvite(const AmSipRequest& req, + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); // DI // DI factory @@ -150,6 +151,15 @@ public: bool createSystemDSM(const string& config_name, const string& start_diag, bool reload, string& status); + /** + add script diags from config set to DSM engine + @return true on success (config set found) + */ + bool addScriptDiagsToEngine(const string& config_set, + DSMStateEngine* engine, + map& config_vars, + bool& SetParamVariables); + }; #endif diff --git a/apps/dsm/DSMCall.cpp b/apps/dsm/DSMCall.cpp index d48f2fc4..589d93ae 100644 --- a/apps/dsm/DSMCall.cpp +++ b/apps/dsm/DSMCall.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 iptego GmbH + * Copyright (C) 2012 Stefan Sayer * * This file is part of SEMS, a free SIP media server. * @@ -29,6 +30,9 @@ #include "AmUtils.h" #include "AmMediaProcessor.h" #include "DSM.h" +#include "AmConferenceStatus.h" +#include "AmAdvancedAudio.h" +#include "AmSipSubscription.h" #include "../apps/jsonrpc/JsonRPCEvents.h" // todo! @@ -70,6 +74,13 @@ bool DSMCall::checkVar(const string& var_name, const string& var_val) { return (it != var.end()) && (it->second == var_val); } +string DSMCall::getVar(const string& var_name) { + map::iterator it = var.find(var_name); + if (it != var.end()) + return it->second; + return ""; +} + /** returns whether params, param exists && param==value*/ bool checkParam(const string& par_name, const string& par_val, map* params) { if (NULL == params) @@ -79,6 +90,11 @@ bool checkParam(const string& par_name, const string& par_val, mapend()) && (it->second == par_val); } +void DSMCall::onStart() +{ + engine.init(this, this, startDiagName, DSMCondition::Start); +} + void DSMCall::onInvite(const AmSipRequest& req) { // make B2B dialogs work in onInvite as well invite_req = req; @@ -92,16 +108,19 @@ void DSMCall::onInvite(const AmSipRequest& req) { bool run_session_invite = engine.onInvite(req, this); - if (run_invite_event) { - if (!engine.init(this, this, startDiagName, DSMCondition::Invite)) - run_session_invite =false; + avar[DSM_AVAR_REQUEST] = AmArg(&req); - if (checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) { - DBG("session choose to not connect media\n"); - run_session_invite = false; // don't accept audio - } + DBG("before runEvent(this, this, DSMCondition::Invite);\n"); + AmSipDialog::Status old_st = dlg->getStatus(); + engine.runEvent(this, this, DSMCondition::Invite, NULL); + avar.erase(DSM_AVAR_REQUEST); - } + if ( old_st != dlg->getStatus() + //checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE) + ) { + DBG("session choose to not connect media\n"); + run_session_invite = false; // don't accept audio + } if (run_session_invite) AmB2BCallerSession::onInvite(req); @@ -117,30 +136,24 @@ void DSMCall::onOutgoingInvite(const string& headers) { // TODO: construct correct request of outgoing INVITE AmSipRequest req; req.hdrs = headers; - bool run_session_invite = engine.onInvite(req, this); - - if (run_invite_event) { - if (!engine.init(this, this, startDiagName, DSMCondition::Invite)) - run_session_invite =false; - - if (checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) { - DBG("session choose to not connect media\n"); - // TODO: set flag to not connect RTP on session start - run_session_invite = false; // don't accept audio - } - - if (checkVar(DSM_ACCEPT_EARLY_SESSION, DSM_ACCEPT_EARLY_SESSION_FALSE)) { - DBG("session choose to not accept early session\n"); - accept_early_session = false; - } else { - DBG("session choose to accept early session\n"); - accept_early_session = true; - } + bool run_session_invite = engine.onInvite(req, this); + if (checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) { + DBG("session choose to not connect media\n"); + // TODO: set flag to not connect RTP on session start + run_session_invite = false; // don't accept audio + } + + if (checkVar(DSM_ACCEPT_EARLY_SESSION, DSM_ACCEPT_EARLY_SESSION_FALSE)) { + DBG("session choose to not accept early session\n"); + accept_early_session = false; + } else { + DBG("session choose to accept early session\n"); + accept_early_session = true; } } - void DSMCall::onRinging(const AmSipReply& reply) { +void DSMCall::onRinging(const AmSipReply& reply) { map params; params["code"] = int2str(reply.code); params["reason"] = reply.reason; @@ -150,13 +163,8 @@ void DSMCall::onOutgoingInvite(const string& headers) { // todo: local ringbacktone } -void DSMCall::onEarlySessionStart(const AmSipReply& reply) { - map params; - params["code"] = int2str(reply.code); - params["reason"] = reply.reason; - params["has_body"] = reply.body.empty() ? - "false" : "true"; - engine.runEvent(this, this, DSMCondition::EarlySession, ¶ms); +void DSMCall::onEarlySessionStart() { + engine.runEvent(this, this, DSMCondition::EarlySession, NULL); if (checkVar(DSM_CONNECT_EARLY_SESSION, DSM_CONNECT_EARLY_SESSION_FALSE)) { DBG("call does not connect early session\n"); @@ -165,35 +173,64 @@ void DSMCall::onEarlySessionStart(const AmSipReply& reply) { setInput(&playlist); if (!getOutput()) - setOutput(&playlist); + setOutput(&playlist); + + AmB2BCallerSession::onEarlySessionStart(); } } -void DSMCall::onSessionStart(const AmSipRequest& req) +void DSMCall::onSessionStart() { if (process_sessionstart) { process_sessionstart = false; - AmB2BCallerSession::onSessionStart(req); DBG("DSMCall::onSessionStart\n"); startSession(); } + + AmB2BCallerSession::onSessionStart(); } -void DSMCall::onSessionStart(const AmSipReply& rep) +int DSMCall::onSdpCompleted(const AmSdp& offer, const AmSdp& answer) { - if (process_sessionstart) { - process_sessionstart = false; - DBG("DSMCall::onSessionStart (SEMS originator mode)\n"); - invite_req.body = rep.body; - - startSession(); + AmMimeBody* sdp_body = invite_req.body.hasContentType(SIP_APPLICATION_SDP); + if(!sdp_body) { + sdp_body = invite_req.body.addPart(SIP_APPLICATION_SDP); + } + + if(sdp_body) { + string sdp_buf; + answer.print(sdp_buf); + sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(), + sdp_buf.length()); } + + return AmB2BCallerSession::onSdpCompleted(offer,answer); +} + +bool DSMCall::getSdpOffer(AmSdp& offer) +{ + if (!AmB2BCallerSession::getSdpOffer(offer)) { + return false; + } + + engine.processSdpOffer(offer); + return true; +} + +bool DSMCall::getSdpAnswer(const AmSdp& offer, AmSdp& answer) +{ + if (!AmB2BCallerSession::getSdpAnswer(offer, answer)) { + return false; + } + + engine.processSdpAnswer(offer, answer); + return true; } void DSMCall::startSession(){ - engine.init(this, this, startDiagName, DSMCondition::SessionStart); + engine.runEvent(this, this, DSMCondition::SessionStart, NULL); setReceiving(true); if (!checkVar(DSM_CONNECT_SESSION, DSM_CONNECT_SESSION_FALSE)) { @@ -242,12 +279,19 @@ void DSMCall::onBye(const AmSipRequest& req) params["headers"] = req.hdrs; engine.runEvent(this, this, DSMCondition::Hangup, ¶ms); + + clearRtpReceiverRelay(); } -void DSMCall::onCancel() { +void DSMCall::onCancel(const AmSipRequest& cancel) { DBG("onCancel\n"); - if (dlg.getStatus() < AmSipDialog::Connected) - engine.runEvent(this, this, DSMCondition::Hangup, NULL); + if (dlg->getStatus() < AmSipDialog::Connected) { + //TODO: pass the cancel request as a parameter? + DBG("hangup event!!!\n"); + map params; + params["headers"] = cancel.hdrs; + engine.runEvent(this, this, DSMCondition::Hangup, ¶ms); + } else { DBG("ignoring onCancel event in established dialog\n"); } @@ -262,10 +306,6 @@ void DSMCall::onSipRequest(const AmSipRequest& req) { params["from"] = req.from; params["to"] = req.to; params["hdrs"] = req.hdrs; - - params["content_type"] = req.content_type; - params["body"] = req.body; - params["cseq"] = int2str(req.cseq); // pass AmSipRequest for use by mod_dlg @@ -287,20 +327,20 @@ void DSMCall::onSipRequest(const AmSipRequest& req) { AmB2BCallerSession::onSipRequest(req); } -void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) { +void DSMCall::onSipReply(const AmSipRequest& req, + const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status) +{ if (checkVar(DSM_ENABLE_REPLY_EVENTS, DSM_TRUE)) { map params; params["code"] = int2str(reply.code); params["reason"] = reply.reason; params["hdrs"] = reply.hdrs; - params["content_type"] = reply.content_type; - params["body"] = reply.body; - params["cseq"] = int2str(reply.cseq); - params["dlg_status"] = int2str(dlg.getStatus()); - params["old_dlg_status"] = int2str(old_dlg_status); + params["dlg_status"] = dlg->getStatusStr(); + params["old_dlg_status"] = AmBasicSipDialog::getStatusStr(old_dlg_status); // pass AmSipReply for use by mod_dlg (? sending ACK?) DSMSipReply* dsm_reply = new DSMSipReply(&reply); @@ -318,18 +358,82 @@ void DSMCall::onSipReply(const AmSipReply& reply, int old_dlg_status, const stri } } - AmB2BCallerSession::onSipReply(reply,old_dlg_status,trans_method); + AmB2BCallerSession::onSipReply(req, reply, old_dlg_status); + if ((old_dlg_status < AmSipDialog::Connected) && + (dlg->getStatus() == AmSipDialog::Disconnected)) { + DBG("Outbound call failed with reply %d %s.\n", + reply.code, reply.reason.c_str()); + map params; + params["code"] = int2str(reply.code); + params["reason"] = reply.reason; + engine.runEvent(this, this, DSMCondition::FailedCall, ¶ms); + setStopped(); + } } -void DSMCall::onOutboundCallFailed(const AmSipReply& reply) { - DBG("Outbound call failed with reply %d %s.\n", - reply.code, reply.reason.c_str()); +void DSMCall::onRemoteDisappeared(const AmSipReply& reply) { map params; params["code"] = int2str(reply.code); params["reason"] = reply.reason; - engine.runEvent(this, this, DSMCondition::FailedCall, ¶ms); - setStopped(); + params["hdrs"] = reply.hdrs; + params["cseq"] = int2str(reply.cseq); + + params["dlg_status"] = dlg->getStatusStr(); + + // pass AmSipReply for use by modules + DSMSipReply* dsm_reply = new DSMSipReply(&reply); + avar[DSM_AVAR_REPLY] = AmArg(dsm_reply); + + engine.runEvent(this, this, DSMCondition::RemoteDisappeared, ¶ms); + + delete dsm_reply; + avar.erase(DSM_AVAR_REPLY); + + if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) { + DBG("DSM script processed SIP onRemoteDisappeared reply '%u %s', returning\n", + reply.code, reply.reason.c_str()); + return; + } + + AmB2BCallerSession::onRemoteDisappeared(reply); +} + +void DSMCall::onSessionTimeout() { + map params; + + engine.runEvent(this, this, DSMCondition::SessionTimeout, ¶ms); + + if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) { + DBG("DSM script processed onSessionTimeout, returning\n"); + return; + } + + AmB2BCallerSession::onSessionTimeout(); +} + +void DSMCall::onRtpTimeout() { + map params; + + engine.runEvent(this, this, DSMCondition::RtpTimeout, ¶ms); + + if (checkParam(DSM_PROCESSED, DSM_TRUE, ¶ms)) { + DBG("DSM script processed onRtpTimeout, returning\n"); + return; + } + + AmB2BCallerSession::onRtpTimeout(); +} + +void DSMCall::onNoAck(unsigned int cseq) +{ + DBG("onNoAck\n"); + map params; + params["headers"] = ""; + params["reason"] = "onNoAck"; + + engine.runEvent(this, this, DSMCondition::Hangup, ¶ms); + AmB2BCallerSession::onNoAck(cseq); } void DSMCall::onSystemEvent(AmSystemEvent* ev) { @@ -383,6 +487,14 @@ void DSMCall::process(AmEvent* event) engine.runEvent(this, this, DSMCondition::PlaylistSeparator, ¶ms); } + ConferenceEvent * conf_ev = dynamic_cast(event); + if (conf_ev) { + map params; + params["type"] = "conference_event"; + params["id"] = int2str(conf_ev->event_id); + engine.runEvent(this, this, DSMCondition::DSMEvent, ¶ms); + } + // todo: give modules the possibility to define/process events JsonRpcEvent* jsonrpc_ev = dynamic_cast(event); if (jsonrpc_ev) { @@ -439,6 +551,41 @@ void DSMCall::process(AmEvent* event) } + if (event->event_id == E_SIP_SUBSCRIPTION) { + SIPSubscriptionEvent* sub_ev = dynamic_cast(event); + if (sub_ev) { + DBG("DSM Call received SIP Subscription Event\n"); + map params; + params["status"] = sub_ev->getStatusText(); + params["code"] = int2str(sub_ev->code); + params["reason"] = sub_ev->reason; + params["expires"] = int2str(sub_ev->expires); + params["has_body"] = sub_ev->notify_body.get()?"true":"false"; + if (sub_ev->notify_body.get()) { + avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body.get()); + } + engine.runEvent(this, this, DSMCondition::SIPSubscription, ¶ms); + avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY); + } + } + + AmRtpTimeoutEvent* timeout_ev = dynamic_cast(event); + if (timeout_ev) { + map params; + params["type"] = "rtp_timeout"; + params["timeout_value"] = int2str(AmConfig::DeadRtpTime); + engine.runEvent(this, this, DSMCondition::RTPTimeout, ¶ms); + return; + } + + if (event->event_id == E_B2B_APP) { + B2BEvent* b2b_ev = dynamic_cast(event); + if(b2b_ev && b2b_ev->ev_type == B2BEvent::B2BApplication) { + engine.runEvent(this, this, DSMCondition::B2BEvent, &b2b_ev->params); + return; + } + } + AmB2BCallerSession::process(event); } @@ -446,13 +593,13 @@ inline UACAuthCred* DSMCall::getCredentials() { return cred.get(); } -void DSMCall::playPrompt(const string& name, bool loop) { +void DSMCall::playPrompt(const string& name, bool loop, bool front) { DBG("playing prompt '%s'\n", name.c_str()); if (prompts->addToPlaylist(name, (long)this, playlist, - /*front =*/ false, loop)) { + front, loop)) { if ((var["prompts.default_fallback"] != "yes") || default_prompts->addToPlaylist(name, (long)this, playlist, - /*front =*/ false, loop)) { + front, loop)) { DBG("checked [%p]\n", default_prompts); throw DSMException("prompt", "name", name); } else { @@ -464,14 +611,17 @@ void DSMCall::playPrompt(const string& name, bool loop) { } } -void DSMCall::closePlaylist(bool notify) { - DBG("close playlist\n"); - playlist.close(notify); +void DSMCall::flushPlaylist() { + DBG("flush playlist\n"); + playlist.flush(); } -void DSMCall::addToPlaylist(AmPlaylistItem* item) { +void DSMCall::addToPlaylist(AmPlaylistItem* item, bool front) { DBG("add item to playlist\n"); - playlist.addToPlaylist(item); + if (front) + playlist.addToPlayListFront(item); + else + playlist.addToPlaylist(item); } void DSMCall::playFile(const string& name, bool loop, bool front) { @@ -497,6 +647,18 @@ void DSMCall::playFile(const string& name, bool loop, bool front) { CLR_ERRNO; } +void DSMCall::playSilence(unsigned int length, bool front) { + AmNullAudio* af = new AmNullAudio(); + af->setReadLength(length); + if (front) + playlist.addToPlayListFront(new AmPlaylistItem(af, NULL)); + else + playlist.addToPlaylist(new AmPlaylistItem(af, NULL)); + + audiofiles.push_back(af); + CLR_ERRNO; +} + void DSMCall::recordFile(const string& name) { if (rec_file) stopRecord(); @@ -619,10 +781,14 @@ void DSMCall::addSeparator(const string& name, bool front) { } void DSMCall::transferOwnership(DSMDisposable* d) { + if (d == NULL) + return; gc_trash.insert(d); } void DSMCall::releaseOwnership(DSMDisposable* d) { + if (d == NULL) + return; gc_trash.erase(d); } @@ -659,12 +825,46 @@ void DSMCall::B2BconnectCallee(const string& remote_party, connectCallee(remote_party, remote_uri, relayed_invite); } +AmB2BCalleeSession* DSMCall::newCalleeSession() { + DSMCallCalleeSession* s = new DSMCallCalleeSession(this); + s->dlg->setLocalParty(getVar(DSM_B2B_LOCAL_PARTY)); + s->dlg->setLocalUri(getVar(DSM_B2B_LOCAL_URI)); + + string user = getVar(DSM_B2B_AUTH_USER); + string pwd = getVar(DSM_B2B_AUTH_PWD); + if (!user.empty() && !pwd.empty()) { + s->setCredentials("", user, pwd); + + // adding auth handler + AmSessionEventHandlerFactory* uac_auth_f = + AmPlugIn::instance()->getFactory4Seh("uac_auth"); + if (NULL == uac_auth_f) { + INFO("uac_auth module not loaded. uac auth NOT enabled for B2B b leg in DSM.\n"); + } else { + AmSessionEventHandler* h = uac_auth_f->getHandler(s); + + // we cannot use the generic AmSessionEventHandler hooks, + // because the hooks don't work in AmB2BSession + s->setAuthHandler(h); + DBG("uac auth enabled for DSM callee session.\n"); + } + } + + s->dlg->setCallid(getVar(DSM_B2B_CALLID)); + + return s; +} + void DSMCall::B2BaddReceivedRequest(const AmSipRequest& req) { DBG("inserting request '%s' with CSeq %d in list of received requests\n", req.method.c_str(), req.cseq); recvd_req.insert(std::make_pair(req.cseq, req)); } +void DSMCall::B2BsetRelayEarlyMediaSDP(bool enabled) { + set_sip_relay_early_media_sdp(enabled); +} + void DSMCall::B2BsetHeaders(const string& hdr, bool replaceCRLF) { if (!replaceCRLF) { invite_req.hdrs = hdr; @@ -699,3 +899,70 @@ void DSMCall::B2BclearHeaders() { invite_req.hdrs.clear(); } +void DSMCall::B2BremoveHeader(const string& hdr) { + removeHeader(invite_req.hdrs, hdr); +} + +/* --- B2B second leg -------------------------------------------------- */ + +DSMCallCalleeSession::DSMCallCalleeSession(const string& other_local_tag) + : AmB2BCalleeSession(other_local_tag) { +} + +DSMCallCalleeSession::DSMCallCalleeSession(const AmB2BCallerSession* caller) + : AmB2BCalleeSession(caller) { +} + +void DSMCallCalleeSession::setCredentials(const string& realm, + const string& user, + const string& pwd) { + cred.reset(new UACAuthCred(realm, user, pwd)); +} + +UACAuthCred* DSMCallCalleeSession::getCredentials() { + return cred.get(); +} + +void DSMCallCalleeSession::setAuthHandler(AmSessionEventHandler* h) { + auth.reset(h); +} + +void DSMCallCalleeSession::onSendRequest(AmSipRequest& req, int& flags) +{ + if (NULL != auth.get()) { + DBG("auth->onSendRequest cseq = %d\n", req.cseq); + auth->onSendRequest(req, flags); + } + + AmB2BCalleeSession::onSendRequest(req, flags); +} + +void DSMCallCalleeSession::onSipReply(const AmSipRequest& req, const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status) +{ + // call event handlers where it is not done + TransMap::iterator t = relayed_req.find(reply.cseq); + bool fwd = t != relayed_req.end(); + DBG("onSipReply: %i %s (fwd=%i)\n",reply.code,reply.reason.c_str(),fwd); + DBG("onSipReply: content-type = %s\n",reply.body.getCTStr().c_str()); + if(fwd) { + CALL_EVENT_H(onSipReply, req, reply, old_dlg_status); + } + + + if (NULL == auth.get()) { + AmB2BCalleeSession::onSipReply(req, reply, old_dlg_status); + return; + } + + unsigned int cseq_before = dlg->cseq; + if (!auth->onSipReply(req, reply, old_dlg_status)) { + AmB2BCalleeSession::onSipReply(req, reply, old_dlg_status); + } else { + if (cseq_before != dlg->cseq) { + DBG("uac_auth consumed reply with cseq %d and resent with cseq %d; " + "updating relayed_req map\n", reply.cseq, cseq_before); + updateUACTransCSeq(reply.cseq, cseq_before); + } + } +} diff --git a/apps/dsm/DSMCall.h b/apps/dsm/DSMCall.h index 7e7405ac..0aafbf8c 100644 --- a/apps/dsm/DSMCall.h +++ b/apps/dsm/DSMCall.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 iptego GmbH + * Copyright (C) 2012 Stefan Sayer * * This file is part of SEMS, a free SIP media server. * @@ -28,8 +29,7 @@ #define _DSM_CALL_H #include "AmB2BSession.h" #include "AmPromptCollection.h" - -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include "DSMSession.h" #include "DSMStateEngine.h" @@ -63,6 +63,8 @@ class DSMCall : public AmB2BCallerSession, std::set gc_trash; bool checkVar(const string& var_name, const string& var_val); + string getVar(const string& var_name); + public: DSMCall(const DSMScriptConfig& config, AmPromptCollection* prompts, @@ -71,21 +73,32 @@ public: UACAuthCred* credentials = NULL); ~DSMCall(); + void onStart(); void onInvite(const AmSipRequest& req); void onOutgoingInvite(const string& headers); void onRinging(const AmSipReply& reply); - void onEarlySessionStart(const AmSipReply& reply); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); + void onEarlySessionStart(); + void onSessionStart(); + int onSdpCompleted(const AmSdp& offer, const AmSdp& answer); void startSession(); - void onCancel(); + void onCancel(const AmSipRequest& cancel); void onBye(const AmSipRequest& req); - void onOutboundCallFailed(const AmSipReply& reply); void onDtmf(int event, int duration_msec); void onBeforeDestroy(); + void onSessionTimeout(); + void onRtpTimeout(); + void onRemoteDisappeared(const AmSipReply& reply); + void onSipRequest(const AmSipRequest& req); - void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method); + void onSipReply(const AmSipRequest& req, + const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status); + + bool getSdpOffer(AmSdp& offer); + bool getSdpAnswer(const AmSdp& offer, AmSdp& answer); + + virtual void onNoAck(unsigned int cseq); void onSystemEvent(AmSystemEvent* ev); @@ -97,10 +110,11 @@ public: void setPromptSets(map& new_prompt_sets); // DSMSession interface - void playPrompt(const string& name, bool loop = false); - void closePlaylist(bool notify); - void addToPlaylist(AmPlaylistItem* item); + void playPrompt(const string& name, bool loop = false, bool front = false); + void flushPlaylist(); + void addToPlaylist(AmPlaylistItem* item, bool front = false); void playFile(const string& name, bool loop, bool front=false); + void playSilence(unsigned int length, bool front=false); void recordFile(const string& name); unsigned int getRecordLength(); unsigned int getRecordDataSize(); @@ -124,15 +138,42 @@ protected: void onOtherBye(const AmSipRequest& req); bool onOtherReply(const AmSipReply& reply); public: + AmB2BCalleeSession* newCalleeSession(); + void B2BterminateOtherLeg(); void B2BconnectCallee(const string& remote_party, const string& remote_uri, bool relayed_invite = false); void B2BaddReceivedRequest(const AmSipRequest& req); + void B2BsetRelayEarlyMediaSDP(bool enabled); void B2BsetHeaders(const string& hdr, bool replaceCRLF); void B2BclearHeaders(); void B2BaddHeader(const string& hdr); + void B2BremoveHeader(const string& hdr); +}; + +class DSMCallCalleeSession : public AmB2BCalleeSession, + public CredentialHolder +{ + std::auto_ptr cred; + std::auto_ptr auth; + + +protected: + + void onSendRequest(AmSipRequest& req, int& flags); + void onSipReply(const AmSipRequest& req, const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status); + +public: + DSMCallCalleeSession(const string& other_local_tag); + DSMCallCalleeSession(const AmB2BCallerSession* caller); + + void setCredentials(const string& realm, const string& user, const string& pwd); + UACAuthCred* getCredentials(); + void setAuthHandler(AmSessionEventHandler* h); + }; #endif diff --git a/apps/dsm/DSMChartReader.cpp b/apps/dsm/DSMChartReader.cpp index 6c67657c..b03d4d8f 100644 --- a/apps/dsm/DSMChartReader.cpp +++ b/apps/dsm/DSMChartReader.cpp @@ -143,7 +143,7 @@ DSMFunction* DSMChartReader::functionFromToken(const string& str) { for (vector::iterator it=funcs.begin(); it!= funcs.end(); it++) { if((*it)->name == cmd) { - DBG("found function '%s' in fuction list\n", cmd.c_str()); + DBG("found function '%s' in function list\n", cmd.c_str()); return *it; } } diff --git a/apps/dsm/DSMChartReader.h b/apps/dsm/DSMChartReader.h index 04530efb..34db5ba2 100644 --- a/apps/dsm/DSMChartReader.h +++ b/apps/dsm/DSMChartReader.h @@ -39,7 +39,11 @@ class NamedAction : public DSMAction { NamedAction(const string& m_name) { name = m_name; } - void execute(AmSession* sess) { }; + bool execute(AmSession* sess, DSMSession* sc_sess, + DSMCondition::EventType event, + map* event_params) { + return false; + }; }; class AttribInitial : public DSMElement { diff --git a/apps/dsm/DSMCoreModule.cpp b/apps/dsm/DSMCoreModule.cpp index ae34ac48..29b58b15 100644 --- a/apps/dsm/DSMCoreModule.cpp +++ b/apps/dsm/DSMCoreModule.cpp @@ -33,6 +33,7 @@ #include "AmUtils.h" #include "AmEventDispatcher.h" #include "DSM.h" +#include "AmB2BSession.h" #include "jsonArg.h" @@ -55,18 +56,29 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) { DEF_CMD("stop", SCStopAction); DEF_CMD("playPrompt", SCPlayPromptAction); + DEF_CMD("playPromptFront", SCPlayPromptFrontAction); DEF_CMD("playPromptLooped", SCPlayPromptLoopedAction); DEF_CMD("playFile", SCPlayFileAction); DEF_CMD("playFileFront", SCPlayFileFrontAction); + DEF_CMD("playSilence", SCPlaySilenceAction); + DEF_CMD("playSilenceFront", SCPlaySilenceFrontAction); DEF_CMD("recordFile", SCRecordFileAction); DEF_CMD("stopRecord", SCStopRecordAction); DEF_CMD("getRecordLength", SCGetRecordLengthAction); DEF_CMD("getRecordDataSize", SCGetRecordDataSizeAction); - DEF_CMD("closePlaylist", SCClosePlaylistAction); + DEF_CMD("flushPlaylist", SCFlushPlaylistAction); + DEF_CMD("closePlaylist", SCClosePlaylistAction); // deprecated DEF_CMD("setInOutPlaylist", SCSetInOutPlaylistAction); + DEF_CMD("setInputPlaylist", SCSetInputPlaylistAction); + DEF_CMD("setOutputPlaylist", SCSetOutputPlaylistAction); DEF_CMD("addSeparator", SCAddSeparatorAction); DEF_CMD("connectMedia", SCConnectMediaAction); DEF_CMD("disconnectMedia", SCDisconnectMediaAction); + DEF_CMD("enableReceiving", SCEnableReceivingAction); + DEF_CMD("disableReceiving", SCDisableReceivingAction); + DEF_CMD("enableForceDTMFReceiving", SCEnableForceDTMFReceiving); + DEF_CMD("disableForceDTMFReceiving", SCDisableForceDTMFReceiving); + DEF_CMD("monitorRTPTimeout", SCMonitorRTPTimeoutAction); DEF_CMD("mute", SCMuteAction); DEF_CMD("unmute", SCUnmuteAction); DEF_CMD("enableDTMFDetection", SCEnableDTMFDetection); @@ -119,9 +131,16 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) { DEF_CMD("B2B.connectCallee", SCB2BConnectCalleeAction); DEF_CMD("B2B.terminateOtherLeg", SCB2BTerminateOtherLegAction); DEF_CMD("B2B.sendReinvite", SCB2BReinviteAction); + DEF_CMD("B2B.enableEarlyMediaRelay", SCB2BEnableEarlyMediaRelayAction); DEF_CMD("B2B.addHeader", SCB2BAddHeaderAction); + DEF_CMD("B2B.removeHeader", SCB2BRemoveHeaderAction); DEF_CMD("B2B.clearHeaders", SCB2BClearHeadersAction); DEF_CMD("B2B.setHeaders", SCB2BSetHeadersAction); + DEF_CMD("B2B.relayEvent", SCRelayB2BEventAction); + + DEF_CMD("trackObject", SCTrackObjectAction); + DEF_CMD("releaseObject", SCReleaseObjectAction); + DEF_CMD("freeObject", SCFreeObjectAction); return NULL; } @@ -160,9 +179,15 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) { if ((cmd == "eventTest") || (cmd == "event")) return new TestDSMCondition(params, DSMCondition::DSMEvent); + if (cmd == "B2Bevent") + return new TestDSMCondition(params, DSMCondition::B2BEvent); + if (cmd == "invite") return new TestDSMCondition(params, DSMCondition::Invite); + if (cmd == "earlySession") + return new TestDSMCondition(params, DSMCondition::EarlySession); + if (cmd == "sessionStart") return new TestDSMCondition(params, DSMCondition::SessionStart); @@ -175,6 +200,9 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) { if (cmd == "failed") return new TestDSMCondition(params, DSMCondition::FailedCall); + if (cmd == "B2B.otherRequest") + return new TestDSMCondition(params, DSMCondition::B2BOtherRequest); + if (cmd == "B2B.otherReply") return new TestDSMCondition(params, DSMCondition::B2BOtherReply); @@ -187,21 +215,39 @@ DSMCondition* DSMCoreModule::getCondition(const string& from_str) { if (cmd == "sipReply") return new TestDSMCondition(params, DSMCondition::SipReply); + if (cmd == "remoteDisappeared") + return new TestDSMCondition(params, DSMCondition::RemoteDisappeared); + + if (cmd == "sessionTimeout") + return new TestDSMCondition(params, DSMCondition::SessionTimeout); + + if (cmd == "rtpTimeout") + return new TestDSMCondition(params, DSMCondition::RtpTimeout); + if (cmd == "jsonRpcRequest") return new TestDSMCondition(params, DSMCondition::JsonRpcRequest); if (cmd == "jsonRpcResponse") return new TestDSMCondition(params, DSMCondition::JsonRpcResponse); + if (cmd == "subscription") + return new TestDSMCondition(params, DSMCondition::SIPSubscription); + if (cmd == "startup") return new TestDSMCondition(params, DSMCondition::Startup); + if (cmd == "start") + return new TestDSMCondition(params, DSMCondition::Start); + if (cmd == "reload") return new TestDSMCondition(params, DSMCondition::Reload); if (cmd == "system") return new TestDSMCondition(params, DSMCondition::System); + if (cmd == "rtpTimeout") + return new TestDSMCondition(params, DSMCondition::RTPTimeout); + return NULL; } @@ -209,6 +255,10 @@ EXEC_ACTION_START(SCPlayPromptAction) { sc_sess->playPrompt(resolveVars(arg, sess, sc_sess, event_params)); } EXEC_ACTION_END; +EXEC_ACTION_START(SCPlayPromptFrontAction) { + sc_sess->playPrompt(resolveVars(arg, sess, sc_sess, event_params), false, true); +} EXEC_ACTION_END; + EXEC_ACTION_START(SCSetPromptsAction) { sc_sess->setPromptSet(resolveVars(arg, sess, sc_sess, event_params)); } EXEC_ACTION_END; @@ -223,39 +273,46 @@ EXEC_ACTION_START(SCPlayPromptLoopedAction){ sc_sess->playPrompt(resolveVars(arg, sess, sc_sess, event_params), true); } EXEC_ACTION_END; -CONST_ACTION_2P(SCPostEventAction, ',', true); -EXEC_ACTION_START(SCPostEventAction){ - string sess_id = resolveVars(par1, sess, sc_sess, event_params); - string var = resolveVars(par2, sess, sc_sess, event_params); - DSMEvent* ev = new DSMEvent(); - if (!var.empty()) { - if (var == "var") - ev->params = sc_sess->var; - else { - vector vars = explode(var, ";"); - for (vector::iterator it = - vars.begin(); it != vars.end(); it++) { - string varname = *it; +void setEventParameters(const DSMSession* sc_sess, const string& var, VarMapT& params) { + if (var.empty()) + return; + + if (var == "var") { + params = sc_sess->var; + } else { + vector vars = explode(var, ";"); + for (vector::iterator it = vars.begin(); it != vars.end(); it++) { + string varname = *it; - if (varname.length() && varname[varname.length()-1]=='.') { - DBG("adding postEvent param %s (struct)\n", varname.c_str()); + if (varname.length() && varname[varname.length()-1]=='.') { + DBG("adding postEvent param %s (struct)\n", varname.c_str()); - map::iterator lb = sc_sess->var.lower_bound(varname); - while (lb != sc_sess->var.end()) { - if ((lb->first.length() < varname.length()) || - strncmp(lb->first.c_str(), varname.c_str(), varname.length())) - break; - ev->params[lb->first] = lb->second; - lb++; - } - } else { + map::const_iterator lb = sc_sess->var.lower_bound(varname); + while (lb != sc_sess->var.end()) { + if ((lb->first.length() < varname.length()) || + strncmp(lb->first.c_str(), varname.c_str(), varname.length())) + break; + params[lb->first] = lb->second; + lb++; + } + } else { + VarMapT::const_iterator v_it = sc_sess->var.find(varname); + if (v_it != sc_sess->var.end()) { DBG("adding postEvent param %s=%s\n", - it->c_str(), sc_sess->var[*it].c_str()); - ev->params[*it] = sc_sess->var[*it]; + it->c_str(), v_it->second.c_str()); + params[varname] = v_it->second; } } } } +} + +CONST_ACTION_2P(SCPostEventAction, ',', true); +EXEC_ACTION_START(SCPostEventAction){ + string sess_id = resolveVars(par1, sess, sc_sess, event_params); + string var = resolveVars(par2, sess, sc_sess, event_params); + DSMEvent* ev = new DSMEvent(); + setEventParameters(sc_sess, var, ev->params); DBG("posting event to session '%s'\n", sess_id.c_str()); if (!AmSessionContainer::instance()->postEvent(sess_id, ev)) { @@ -266,6 +323,20 @@ EXEC_ACTION_START(SCPostEventAction){ } } EXEC_ACTION_END; +EXEC_ACTION_START(SCRelayB2BEventAction) { + AmB2BSession* b2b_sess = dynamic_cast(sess); + if (NULL == b2b_sess) { + throw DSMException("script", "cause", "relayEvent used without B2B call"); + } + + string var = resolveVars(arg, sess, sc_sess, event_params); + B2BEvent* ev = new B2BEvent(E_B2B_APP, B2BEvent::B2BApplication); + setEventParameters(sc_sess, var, ev->params); + + b2b_sess->relayEvent(ev); + +} EXEC_ACTION_END; + CONST_ACTION_2P(SCPlayFileAction, ',', true); EXEC_ACTION_START(SCPlayFileAction) { bool loop = @@ -284,6 +355,24 @@ EXEC_ACTION_START(SCPlayFileFrontAction) { loop, true); } EXEC_ACTION_END; +EXEC_ACTION_START(SCPlaySilenceAction) { + int length; + string length_str = resolveVars(arg, sess, sc_sess, event_params); + if (!str2int(length_str, length)) { + throw DSMException("core", "cause", "cannot parse number"); + } + sc_sess->playSilence(length); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCPlaySilenceFrontAction) { + int length; + string length_str = resolveVars(arg, sess, sc_sess, event_params); + if (!str2int(length_str, length)) { + throw DSMException("core", "cause", "cannot parse number"); + } + sc_sess->playSilence(length, true); +} EXEC_ACTION_END; + EXEC_ACTION_START(SCRecordFileAction) { sc_sess->recordFile(resolveVars(arg, sess, sc_sess, event_params)); } EXEC_ACTION_END; @@ -306,10 +395,13 @@ EXEC_ACTION_START(SCGetRecordDataSizeAction) { sc_sess->var[varname]=int2str(sc_sess->getRecordDataSize()); } EXEC_ACTION_END; +EXEC_ACTION_START(SCFlushPlaylistAction) { + sc_sess->flushPlaylist(); +} EXEC_ACTION_END; + EXEC_ACTION_START(SCClosePlaylistAction) { - bool notify = - resolveVars(arg, sess, sc_sess, event_params) == "true"; - sc_sess->closePlaylist(notify); + WARN("closePlaylist() is deprecated - please use flushPlaylist() instead\n"); + sc_sess->flushPlaylist(); } EXEC_ACTION_END; @@ -317,6 +409,14 @@ EXEC_ACTION_START(SCSetInOutPlaylistAction) { sc_sess->setInOutPlaylist(); } EXEC_ACTION_END; +EXEC_ACTION_START(SCSetInputPlaylistAction) { + sc_sess->setInputPlaylist(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCSetOutputPlaylistAction) { + sc_sess->setOutputPlaylist(); +} EXEC_ACTION_END; + EXEC_ACTION_START(SCConnectMediaAction) { sc_sess->connectMedia(); } EXEC_ACTION_END; @@ -325,6 +425,32 @@ EXEC_ACTION_START(SCDisconnectMediaAction) { sc_sess->disconnectMedia(); } EXEC_ACTION_END; +EXEC_ACTION_START(SCEnableReceivingAction) { + DBG("enabling RTP receving in session\nb"); + sess->setReceiving(true); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCDisableReceivingAction) { + DBG("disabling RTP receving in session\nb"); + sess->setReceiving(false); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCEnableForceDTMFReceiving) { + DBG("enabling forced DTMF RTP receving in session\nb"); + sess->setForceDtmfReceiving(true); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCDisableForceDTMFReceiving) { + DBG("disabling forced DTMF RTP receving in session\nb"); + sess->setForceDtmfReceiving(false); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCMonitorRTPTimeoutAction) { + string e = resolveVars(arg, sess, sc_sess, event_params); + DBG("setting RTP stream to %smonitor RTP timeout\n", e=="true"?"":"not"); + sess->RTPStream()->setMonitorRTPTimeout(e=="true"); +} EXEC_ACTION_END; + EXEC_ACTION_START(SCMuteAction) { sc_sess->mute(); } EXEC_ACTION_END; @@ -382,7 +508,7 @@ EXEC_ACTION_START(SCThrowOnErrorAction) { EXEC_ACTION_START(SCStopAction) { if (resolveVars(arg, sess, sc_sess, event_params) == "true") { DBG("sending bye\n"); - sess->dlg.bye(); + sess->dlg->bye(); } sess->setStopped(); } EXEC_ACTION_END; @@ -509,7 +635,9 @@ void log_selects(const string& l_arg, AmSession* sess, SELECT_LOG("remote_tag"); SELECT_LOG("callid"); SELECT_LOG("local_uri"); + SELECT_LOG("local_party"); SELECT_LOG("remote_uri"); + SELECT_LOG("remote_party"); #undef SELECT_LOG _LOG((int)lvl, "FSM: selects end ---\n"); } @@ -1201,7 +1329,13 @@ CONST_ACTION_2P(SCB2BConnectCalleeAction,',', false); EXEC_ACTION_START(SCB2BConnectCalleeAction) { string remote_party = resolveVars(par1, sess, sc_sess, event_params); string remote_uri = resolveVars(par2, sess, sc_sess, event_params); - sc_sess->B2BconnectCallee(remote_party, remote_uri); + bool relayed_invite = false; + VarMapT::iterator it = sc_sess->var.find(DSM_B2B_RELAYED_INVITE); + if (it != sc_sess->var.end() && it->second == "true") + relayed_invite = true; + DBG("B2B connecting callee '%s', URI '%s', relayed: %s\n", + remote_party.c_str(), remote_uri.c_str(), relayed_invite?"yes":"no"); + sc_sess->B2BconnectCallee(remote_party, remote_uri, relayed_invite); } EXEC_ACTION_END; EXEC_ACTION_START(SCB2BTerminateOtherLegAction) { @@ -1214,12 +1348,24 @@ EXEC_ACTION_START(SCB2BReinviteAction) { sess->sendReinvite(updateSDP, par2); } EXEC_ACTION_END; +EXEC_ACTION_START(SCB2BEnableEarlyMediaRelayAction) { + string val = resolveVars(arg, sess, sc_sess, event_params); + DBG("B2B: %sabling early media SDP relay as re-Invite\n", (val=="true")?"En":"Dis"); + sc_sess->B2BsetRelayEarlyMediaSDP(val=="true"); +} EXEC_ACTION_END; + EXEC_ACTION_START(SCB2BAddHeaderAction) { string val = resolveVars(arg, sess, sc_sess, event_params); DBG("adding B2B header '%s'\n", val.c_str()); sc_sess->B2BaddHeader(val); } EXEC_ACTION_END; +EXEC_ACTION_START(SCB2BRemoveHeaderAction) { + string val = resolveVars(arg, sess, sc_sess, event_params); + DBG("removing B2B header '%s'\n", val.c_str()); + sc_sess->B2BremoveHeader(val); +} EXEC_ACTION_END; + CONST_ACTION_2P(SCB2BSetHeadersAction,',', true); EXEC_ACTION_START(SCB2BSetHeadersAction) { string val = resolveVars(par1, sess, sc_sess, event_params); @@ -1333,3 +1479,49 @@ EXEC_ACTION_START(SCCreateSystemDSMAction) { } EXEC_ACTION_END; +DSMDisposable* getObjectFromVariable(DSMSession* sc_sess, const string& var_name) { + AVarMapT::iterator it = sc_sess->avar.find(var_name); + if (it == sc_sess->avar.end()) { + DBG("object '%s' not found\n", var_name.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("object '"+var_name+"' not found\n"); + return NULL; + } + + DSMDisposable* disp = dynamic_cast(it->second.asObject()); + if (NULL == disp) { + DBG("object '%s' is not a DSMDisposable\n", var_name.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("object '"+var_name+"' is not a DSMDisposable\n"); + return NULL; + } + return disp; +} + +EXEC_ACTION_START(SCTrackObjectAction) { + string var_name = resolveVars(arg, sess, sc_sess, event_params); + DSMDisposable* disp = getObjectFromVariable(sc_sess, var_name); + if (NULL == disp) { + EXEC_ACTION_STOP; + } + sc_sess->transferOwnership(disp); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCReleaseObjectAction) { + string var_name = resolveVars(arg, sess, sc_sess, event_params); + DSMDisposable* disp = getObjectFromVariable(sc_sess, var_name); + if (NULL == disp) { + EXEC_ACTION_STOP; + } + sc_sess->releaseOwnership(disp); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCFreeObjectAction) { + string var_name = resolveVars(arg, sess, sc_sess, event_params); + DSMDisposable* disp = getObjectFromVariable(sc_sess, var_name); + if (NULL == disp) { + EXEC_ACTION_STOP; + } + delete disp; + sc_sess->avar.erase(var_name); +} EXEC_ACTION_END; diff --git a/apps/dsm/DSMCoreModule.h b/apps/dsm/DSMCoreModule.h index c00c2581..62efe1aa 100644 --- a/apps/dsm/DSMCoreModule.h +++ b/apps/dsm/DSMCoreModule.h @@ -48,16 +48,25 @@ class DSMCoreModule }; DEF_ACTION_1P(SCPlayPromptAction); +DEF_ACTION_1P(SCPlayPromptFrontAction); DEF_ACTION_1P(SCPlayPromptLoopedAction); DEF_ACTION_1P(SCRecordFileAction); DEF_ACTION_1P(SCStopRecordAction); DEF_ACTION_1P(SCGetRecordDataSizeAction); DEF_ACTION_1P(SCGetRecordLengthAction); +DEF_ACTION_1P(SCFlushPlaylistAction); DEF_ACTION_1P(SCClosePlaylistAction); DEF_ACTION_1P(SCSetInOutPlaylistAction); +DEF_ACTION_1P(SCSetInputPlaylistAction); +DEF_ACTION_1P(SCSetOutputPlaylistAction); DEF_ACTION_1P(SCStopAction); DEF_ACTION_1P(SCConnectMediaAction); DEF_ACTION_1P(SCDisconnectMediaAction); +DEF_ACTION_1P(SCEnableReceivingAction); +DEF_ACTION_1P(SCDisableReceivingAction); +DEF_ACTION_1P(SCEnableForceDTMFReceiving); +DEF_ACTION_1P(SCDisableForceDTMFReceiving); +DEF_ACTION_1P(SCMonitorRTPTimeoutAction); DEF_ACTION_1P(SCMuteAction); DEF_ACTION_1P(SCUnmuteAction); DEF_ACTION_1P(SCEnableDTMFDetection); @@ -98,21 +107,30 @@ DEF_ACTION_2P(SCGetParamAction); DEF_ACTION_2P(SCSetVarAction); DEF_ACTION_2P(SCPlayFileAction); DEF_ACTION_2P(SCPlayFileFrontAction); +DEF_ACTION_1P(SCPlaySilenceAction); +DEF_ACTION_1P(SCPlaySilenceFrontAction); DEF_ACTION_2P(SCPostEventAction); +DEF_ACTION_1P(SCRelayB2BEventAction); DEF_ACTION_2P(SCB2BConnectCalleeAction); DEF_ACTION_1P(SCB2BTerminateOtherLegAction); DEF_ACTION_2P(SCB2BReinviteAction); +DEF_ACTION_1P(SCB2BEnableEarlyMediaRelayAction); DEF_ACTION_1P(SCB2BAddHeaderAction); DEF_ACTION_1P(SCB2BClearHeadersAction); DEF_ACTION_2P(SCB2BSetHeadersAction); +DEF_ACTION_1P(SCB2BRemoveHeaderAction); DEF_ACTION_1P(SCRegisterEventQueueAction); DEF_ACTION_1P(SCUnregisterEventQueueAction); DEF_ACTION_2P(SCCreateSystemDSMAction); +DEF_ACTION_1P(SCTrackObjectAction); +DEF_ACTION_1P(SCReleaseObjectAction); +DEF_ACTION_1P(SCFreeObjectAction); + class SCDIAction : public DSMAction { vector params; diff --git a/apps/dsm/DSMModule.cpp b/apps/dsm/DSMModule.cpp index 016e6e3f..6c8003e5 100644 --- a/apps/dsm/DSMModule.cpp +++ b/apps/dsm/DSMModule.cpp @@ -91,12 +91,16 @@ string resolveVars(const string ts, AmSession* sess, switch(s[0]) { case '$': { + if (s.substr(1, 1)=="$") + return "$"; map::iterator it = sc_sess->var.find(s.substr(1)); if (it != sc_sess->var.end()) return it->second; return ""; } case '#': + if (s.substr(1, 1)=="#") + return "#"; if (event_params) { map::iterator it = event_params->find(s.substr(1)); if (it != event_params->end()) @@ -105,6 +109,8 @@ string resolveVars(const string ts, AmSession* sess, }else return string(); case '@': { + if (s.substr(1, 1)=="@") + return "@"; if (s.length() < 2) return "@"; @@ -112,17 +118,21 @@ string resolveVars(const string ts, AmSession* sess, if (s1 == "local_tag") return sess->getLocalTag(); else if (s1 == "user") - return sess->dlg.user; + return sess->dlg->getUser(); else if (s1 == "domain") - return sess->dlg.domain; + return sess->dlg->getDomain(); else if (s1 == "remote_tag") return sess->getRemoteTag(); else if (s1 == "callid") return sess->getCallID(); else if (s1 == "local_uri") - return sess->dlg.local_uri; + return sess->dlg->getLocalUri(); + else if (s1 == "local_party") + return sess->dlg->getLocalParty(); else if (s1 == "remote_uri") - return sess->dlg.remote_uri; + return sess->dlg->getRemoteUri(); + else if (s1 == "remote_party") + return sess->dlg->getRemoteParty(); else return string(); } diff --git a/apps/dsm/DSMModule.h b/apps/dsm/DSMModule.h index 7f7f8e8a..0153971e 100644 --- a/apps/dsm/DSMModule.h +++ b/apps/dsm/DSMModule.h @@ -52,6 +52,8 @@ class DSMModule { virtual int preload() { return 0; } virtual bool onInvite(const AmSipRequest& req, DSMSession* sess) { return true; } virtual void onBeforeDestroy(DSMSession* sc_sess, AmSession* sess) { } + virtual void processSdpOffer(AmSdp& offer) { } + virtual void processSdpAnswer(const AmSdp& offer, AmSdp& answer) { } }; typedef map EventParamT; @@ -130,8 +132,7 @@ class SCStrArgAction /* bool xsplit(const string& arg, char sep, bool optional, string& par1, string& par2); */ -#define CONST_ACTION_2P(CL_name, sep, optional) \ - CL_name::CL_name(const string& arg) { \ +#define SPLIT_ARGS(sep, optional) \ size_t p = 0; \ char last_c = ' '; \ bool quot=false; \ @@ -194,9 +195,14 @@ class SCStrArgAction ERROR("expected two parameters separated with '%c' in expression '%s' for %s\n", \ sep,arg.c_str(),typeid(this).name()); \ return; \ - } \ - } \ - + } + + +#define CONST_ACTION_2P(CL_name, _sep, _optional) \ + CL_name::CL_name(const string& arg) { \ + SPLIT_ARGS(_sep, _optional); \ + } + #define EXEC_ACTION_START(act_name) \ bool act_name::execute(AmSession* sess, DSMSession* sc_sess, \ @@ -241,7 +247,24 @@ void splitCmd(const string& from_str, bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \ map* event_params); \ }; - + +#define DEF_CONDITION_2P(cond_name) \ + class cond_name \ + : public DSMCondition { \ + string par1; \ + string par2; \ + bool inv; \ + public: \ + cond_name(const string& arg, bool inv); \ + bool match(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \ + map* event_params); \ + }; + +#define CONST_CONDITION_2P(cond_name, _sep, _optional) \ + cond_name::cond_name(const string& arg, bool inv) \ + : inv(inv) { \ + SPLIT_ARGS(_sep, _optional); \ + } #define MATCH_CONDITION_START(cond_clsname) \ bool cond_clsname::match(AmSession* sess, DSMSession* sc_sess, \ diff --git a/apps/dsm/DSMSession.h b/apps/dsm/DSMSession.h index a7475b2f..c33fa62f 100644 --- a/apps/dsm/DSMSession.h +++ b/apps/dsm/DSMSession.h @@ -58,6 +58,13 @@ using std::map; #define DSM_ENABLE_REQUEST_EVENTS "enable_request_events" #define DSM_ENABLE_REPLY_EVENTS "enable_reply_events" +#define DSM_B2B_RELAYED_INVITE "b2b_relayed_invite" +#define DSM_B2B_LOCAL_PARTY "b2b_local_party" // From in outgoing call +#define DSM_B2B_LOCAL_URI "b2b_local_uri" +#define DSM_B2B_AUTH_USER "b2b_auth_user" +#define DSM_B2B_AUTH_PWD "b2b_auth_pwd" + +#define DSM_B2B_CALLID "b2b_callid" #define DSM_AVAR_REQUEST "request" #define DSM_AVAR_REPLY "reply" @@ -66,6 +73,8 @@ using std::map; #define DSM_AVAR_JSONRPCRESPONSEDATA "JsonRpcResponseParameters" #define DSM_AVAR_JSONRPCRESPONSEUDATA "JsonRpcResponseUData" +#define DSM_AVAR_SIPSUBSCRIPTION_BODY "SipSubscriptionBody" + #define DSM_ERRNO_FILE "file" #define DSM_ERRNO_UNKNOWN_ARG "arg" #define DSM_ERRNO_SCRIPT "script" @@ -100,8 +109,9 @@ class DSMSession { DSMSession(); virtual ~DSMSession(); - virtual void playPrompt(const string& name, bool loop = false) = 0; + virtual void playPrompt(const string& name, bool loop = false, bool front = false) = 0; virtual void playFile(const string& name, bool loop, bool front = false) = 0; + virtual void playSilence(unsigned int length, bool front = false) = 0; virtual void recordFile(const string& name) = 0; virtual unsigned int getRecordLength() = 0; virtual unsigned int getRecordDataSize() = 0; @@ -110,8 +120,8 @@ class DSMSession { virtual void setInputPlaylist() = 0; virtual void setOutputPlaylist() = 0; - virtual void addToPlaylist(AmPlaylistItem* item) = 0; - virtual void closePlaylist(bool notify) = 0; + virtual void addToPlaylist(AmPlaylistItem* item, bool front = false) = 0; + virtual void flushPlaylist() = 0; virtual void setPromptSet(const string& name) = 0; virtual void addSeparator(const string& name, bool front = false) = 0; virtual void connectMedia() = 0; @@ -128,15 +138,21 @@ class DSMSession { /** insert request in list of received ones */ virtual void B2BaddReceivedRequest(const AmSipRequest& req) = 0; + /** enable early media relay as reinvite */ + virtual void B2BsetRelayEarlyMediaSDP(bool enabled) = 0; + /** set headers of outgoing INVITE */ virtual void B2BsetHeaders(const string& hdr, bool replaceCRLF) = 0; /** set headers of outgoing INVITE */ virtual void B2BclearHeaders() = 0; - /** add a header to the headers of outgoing INVITE */ + /** add a header to the headers of the outgoing INVITE */ virtual void B2BaddHeader(const string& hdr) = 0; + /** remove a header to the headers of the outgoing INVITE */ + virtual void B2BremoveHeader(const string& hdr) = 0; + /** transfer ownership of object to this session instance */ virtual void transferOwnership(DSMDisposable* d) = 0; @@ -182,7 +198,7 @@ class DSMDisposableAudioFile }; class DSMSipRequest -: public ArgObject { +: public AmObject { public: const AmSipRequest* req; @@ -191,8 +207,18 @@ class DSMSipRequest ~DSMSipRequest() { } }; +class DSMMutableSipRequest +: public DSMSipRequest { + public: + AmSipRequest* mutable_req; + + DSMMutableSipRequest(AmSipRequest* req) + : DSMSipRequest(req), mutable_req(req) { } + ~DSMMutableSipRequest() { } +}; + class DSMSipReply -: public ArgObject { +: public AmObject { public: const AmSipReply* reply; @@ -201,6 +227,15 @@ class DSMSipReply ~DSMSipReply() { } }; +class DSMMutableSipReply +: public DSMSipReply { + public: + AmSipReply* mutable_reply; + + DSMMutableSipReply(AmSipReply* reply) + : DSMSipReply(reply), mutable_reply(reply) { } + ~DSMMutableSipReply() { } +}; #define DSM_EVENT_ID -10 /** generic event for passing events between DSM sessions */ diff --git a/apps/dsm/DSMStateEngine.cpp b/apps/dsm/DSMStateEngine.cpp index f36a2f7e..3475c047 100644 --- a/apps/dsm/DSMStateEngine.cpp +++ b/apps/dsm/DSMStateEngine.cpp @@ -224,6 +224,18 @@ void DSMStateEngine::onBeforeDestroy(DSMSession* sc_sess, AmSession* sess) { (*it)->onBeforeDestroy(sc_sess, sess); } +void DSMStateEngine::processSdpOffer(AmSdp& offer) { + for (vector::iterator it = + mods.begin(); it != mods.end(); it++) + (*it)->processSdpOffer(offer); +} + +void DSMStateEngine::processSdpAnswer(const AmSdp& offer, AmSdp& answer) { + for (vector::iterator it = + mods.begin(); it != mods.end(); it++) + (*it)->processSdpAnswer(offer, answer); +} + bool DSMStateEngine::runactions(vector::iterator from, vector::iterator to, AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, @@ -250,13 +262,13 @@ bool DSMStateEngine::runactions(vector::iterator from, break; case DSMAction::Call: DBG("calling %s\n", se_modifier.c_str()); - if (callDiag(se_modifier, sess, sc_sess, event, event_params)) { + if (callDiag(se_modifier, sess, sc_sess, event, event_params, it+1, to)) { // is_consumed = false; return true; } break; case DSMAction::Return: - if (returnDiag(sess, sc_sess)) { + if (returnDiag(sess, sc_sess, event, event_params)) { //is_consumed = false; return true; } @@ -436,7 +448,7 @@ bool DSMStateEngine::runactions(vector::iterator from, continue; } - ERROR("DSMElement typenot understood\n"); + ERROR("DSMElement type not understood\n"); } return false; } @@ -514,17 +526,20 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, map exception_params; bool is_exception = run_exception; + DBG("o v DSM processing event, current state '%s' v\n", current->name.c_str()); bool is_consumed = true; do { try { is_consumed = true; + + DBG(" > state '%s'\n", current->name.c_str()); for (vector::iterator tr = current->transitions.begin(); tr != current->transitions.end();tr++) { if (tr->is_exception != is_exception) continue; - DBG("checking transition '%s'\n", tr->name.c_str()); + DBG(" ...checking transition '%s'\n", tr->name.c_str()); vector::iterator con=tr->precond.begin(); while (con!=tr->precond.end()) { @@ -533,7 +548,7 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, con++; } if (con == tr->precond.end()) { - DBG("transition '%s' matched.\n", tr->name.c_str()); + DBG(" .>>transition '%s' matched.\n", tr->name.c_str()); // matched all preconditions // find target state @@ -548,7 +563,7 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, // run post-actions if (current->post_actions.size()) { - DBG("running %zd post_actions of state '%s'\n", + DBG(" >>>running %zd post_actions of state '%s'\n", current->post_actions.size(), current->name.c_str()); if (runactions(current->post_actions.begin(), current->post_actions.end(), @@ -559,7 +574,7 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, // run transition actions if (tr->actions.size()) { - DBG("running %zd actions of transition '%s'\n", + DBG(" >>>running %zd actions of transition '%s'\n", tr->actions.size(), tr->name.c_str()); if (runactions(tr->actions.begin(), tr->actions.end(), @@ -572,7 +587,7 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, if (!target_st) { break; } - DBG("changing to new state '%s'\n", target_st->name.c_str()); + DBG(" >>>changing to new state '%s'\n", target_st->name.c_str()); #ifdef USE_MONITORING MONITORING_LOG(sess->getLocalTag().c_str(), "dsm_state", target_st->name.c_str()); @@ -594,7 +609,7 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, // execute pre-actions if (current->pre_actions.size()) { - DBG("running %zd pre_actions of state '%s'\n", + DBG(" >>>running %zd pre_actions of state '%s'\n", current->pre_actions.size(), current->name.c_str()); if (runactions(current->pre_actions.begin(), current->pre_actions.end(), @@ -602,6 +617,7 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, break; } } + DBG(" >>o arrived in state '%s'\n", current->name.c_str()); break; } @@ -617,16 +633,22 @@ void DSMStateEngine::runEvent(AmSession* sess, DSMSession* sc_sess, } } while (!is_consumed); + + DBG("o ^ DSM event processed/consumed; current state '%s' ^\n", current->name.c_str()); } bool DSMStateEngine::callDiag(const string& diag_name, AmSession* sess, DSMSession* sc_sess, - DSMCondition::EventType event, - map* event_params) { + DSMCondition::EventType event, + map* event_params, + vector::iterator actions_from, vector::iterator actions_to) { if (!current || !current_diag) { ERROR("no current diag to push\n"); return false; } - stack.push_back(std::make_pair(current_diag, current)); + stack.push_back(DSMStackElement(current_diag, current)); + for (vector::iterator it = actions_from; it != actions_to; it++) + stack.back().actions.push_back(*it); + return jumpDiag(diag_name, sess, sc_sess, event, event_params); } @@ -673,15 +695,23 @@ bool DSMStateEngine::jumpDiag(const string& diag_name, AmSession* sess, DSMSessi return false; } -bool DSMStateEngine::returnDiag(AmSession* sess, DSMSession* sc_sess) { +bool DSMStateEngine::returnDiag(AmSession* sess, DSMSession* sc_sess, + DSMCondition::EventType event, map* event_params) { if (stack.empty()) { ERROR("returning from empty stack\n"); return false; } - current_diag = stack.back().first; - current = stack.back().second; + current_diag = stack.back().diag; + current = stack.back().state; + vector actions = stack.back().actions; stack.pop_back(); + bool is_consumed; //? + DBG("executing %zd action elements from stack\n", actions.size()); + if (actions.size()) { + runactions(actions.begin(), actions.end(), sess, sc_sess, event, event_params, is_consumed); + } + MONITORING_LOG2(sess->getLocalTag().c_str(), "dsm_diag", current_diag->getName().c_str(), "dsm_state", current->name.c_str()); diff --git a/apps/dsm/DSMStateEngine.h b/apps/dsm/DSMStateEngine.h index e3351094..36a5b9de 100644 --- a/apps/dsm/DSMStateEngine.h +++ b/apps/dsm/DSMStateEngine.h @@ -30,6 +30,7 @@ #include "DSMElemContainer.h" #include "AmSipMsg.h" #include "AmArg.h" +#include "AmSdp.h" class AmSession; class DSMSession; @@ -59,6 +60,7 @@ class DSMCondition enum EventType { Any, + Start, Invite, SessionStart, Ringing, @@ -71,9 +73,14 @@ class DSMCondition Hold, UnHold, + B2BOtherRequest, B2BOtherReply, B2BOtherBye, + SessionTimeout, + RtpTimeout, + RemoteDisappeared, + Key, Timer, @@ -81,6 +88,7 @@ class DSMCondition PlaylistSeparator, DSMEvent, + B2BEvent, DSMException, XmlrpcResponse, @@ -90,7 +98,29 @@ class DSMCondition Startup, Reload, - System + System, + + SIPSubscription, + + RTPTimeout, + + // SBC related + LegStateChange, + BLegRefused, + + PutOnHold, + ResumeHeld, + CreateHoldRequest, + HandleHoldReply, + + RelayInit, + RelayInitUAC, + RelayInitUAS, + RelayFinalize, + RelayOnSipRequest, + RelayOnSipReply, + RelayOnB2BRequest, + RelayOnB2BReply }; bool invert; @@ -124,7 +154,8 @@ class DSMAction virtual ~DSMAction() { /* DBG("dest action\n"); */ } /** @return whether state engine is to be modified (via getSEAction) */ - virtual bool execute(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, \ + virtual bool execute(AmSession* sess, DSMSession* sc_sess, + DSMCondition::EventType event, map* event_params) = 0; /** @return state engine modification */ @@ -244,20 +275,35 @@ class DSMException { map params; }; +struct DSMStackElement { + DSMStateDiagram* diag; + State* state; + vector actions; + + DSMStackElement(DSMStateDiagram* diag, State* state) + : diag(diag), state(state) { } + + DSMStackElement(DSMStateDiagram* diag, State* state, const vector& actions) + : diag(diag), state(state), actions(actions) { } + +}; + class DSMStateEngine { State* current; DSMStateDiagram* current_diag; vector diags; - vector > stack; + // vector > stack; + vector stack; bool callDiag(const string& diag_name, AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, - map* event_params); + map* event_params, + vector::iterator actions_from, vector::iterator actions_to); bool jumpDiag(const string& diag_name, AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, map* event_params); - bool returnDiag(AmSession* sess, DSMSession* sc_sess); + bool returnDiag(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, map* event_params); bool runactions(vector::iterator from, vector::iterator to, AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, @@ -284,6 +330,9 @@ class DSMStateEngine { /** @return whether call should be accepted */ bool onInvite(const AmSipRequest& req, DSMSession* sess); void onBeforeDestroy(DSMSession* sc_sess, AmSession* sess); + + void processSdpOffer(AmSdp& offer); + void processSdpAnswer(const AmSdp& offer, AmSdp& answer); }; extern void varPrintArg(const AmArg& a, map& dst, const string& name); diff --git a/apps/dsm/SystemDSM.cpp b/apps/dsm/SystemDSM.cpp index ae2dc08b..4e950983 100644 --- a/apps/dsm/SystemDSM.cpp +++ b/apps/dsm/SystemDSM.cpp @@ -6,6 +6,9 @@ #include "DSMStateDiagramCollection.h" #include "../apps/jsonrpc/JsonRPCEvents.h" // todo! +#include "AmSipSubscription.h" +#include "AmSessionContainer.h" +#include "ampi/MonitoringAPI.h" SystemDSM::SystemDSM(const DSMScriptConfig& config, const string& startDiagName, @@ -27,6 +30,14 @@ SystemDSM::SystemDSM(const DSMScriptConfig& config, } SystemDSM::~SystemDSM() { + for (std::set::iterator it= + gc_trash.begin(); it != gc_trash.end(); it++) + delete *it; + +#ifdef USE_MONITORING + MONITORING_MARK_FINISHED(dummy_session.getLocalTag()); +#endif + } void SystemDSM::run() { @@ -127,10 +138,28 @@ void SystemDSM::process(AmEvent* event) { } + if (event->event_id == E_SIP_SUBSCRIPTION) { + SIPSubscriptionEvent* sub_ev = dynamic_cast(event); + if (sub_ev) { + DBG("SystemDSM received SIP Subscription Event\n"); + map params; + params["status"] = sub_ev->getStatusText(); + params["code"] = int2str(sub_ev->code); + params["reason"] = sub_ev->reason; + params["expires"] = int2str(sub_ev->expires); + params["has_body"] = sub_ev->notify_body.get()?"true":"false"; + if (sub_ev->notify_body.get()) { + avar[DSM_AVAR_SIPSUBSCRIPTION_BODY] = AmArg(sub_ev->notify_body.get()); + } + engine.runEvent(&dummy_session, this, DSMCondition::SIPSubscription, ¶ms); + avar.erase(DSM_AVAR_SIPSUBSCRIPTION_BODY); + } + } + if (event->event_id == E_SYSTEM) { AmSystemEvent* sys_ev = dynamic_cast(event); if(sys_ev){ - DBG("SystemDSM received system Event\n"); + DBG("SystemDSM received system Event\n"); map params; params["type"] = AmSystemEvent::getDescription(sys_ev->sys_event); engine.runEvent(&dummy_session, this, DSMCondition::System, ¶ms); @@ -141,18 +170,29 @@ void SystemDSM::process(AmEvent* event) { } + /** transfer ownership of object to this session instance */ +void SystemDSM::transferOwnership(DSMDisposable* d) { + gc_trash.insert(d); +} + + /** release ownership of object from this session instance */ +void SystemDSM::releaseOwnership(DSMDisposable* d) { + gc_trash.erase(d); +} + #define NOT_IMPLEMENTED(_func) \ void SystemDSM::_func { \ - throw DSMException("core", "cause", "not implemented"); \ + throw DSMException("core", "cause", "not implemented in SystemDSM"); \ } #define NOT_IMPLEMENTED_UINT(_func) \ unsigned int SystemDSM::_func { \ - throw DSMException("core", "cause", "not implemented"); \ + throw DSMException("core", "cause", "not implemented in SystemDSM"); \ } -NOT_IMPLEMENTED(playPrompt(const string& name, bool loop)); +NOT_IMPLEMENTED(playPrompt(const string& name, bool loop, bool front)); NOT_IMPLEMENTED(playFile(const string& name, bool loop, bool front)); +NOT_IMPLEMENTED(playSilence(unsigned int length, bool front)); NOT_IMPLEMENTED(recordFile(const string& name)); NOT_IMPLEMENTED_UINT(getRecordLength()); NOT_IMPLEMENTED_UINT(getRecordDataSize()); @@ -161,8 +201,8 @@ NOT_IMPLEMENTED(setInOutPlaylist()); NOT_IMPLEMENTED(setInputPlaylist()); NOT_IMPLEMENTED(setOutputPlaylist()); -NOT_IMPLEMENTED(addToPlaylist(AmPlaylistItem* item)); -NOT_IMPLEMENTED(closePlaylist(bool notify)); +NOT_IMPLEMENTED(addToPlaylist(AmPlaylistItem* item, bool front)); +NOT_IMPLEMENTED(flushPlaylist()); NOT_IMPLEMENTED(setPromptSet(const string& name)); NOT_IMPLEMENTED(addSeparator(const string& name, bool front)); NOT_IMPLEMENTED(connectMedia()); @@ -176,11 +216,11 @@ NOT_IMPLEMENTED(B2BconnectCallee(const string& remote_party, bool relayed_invite)); NOT_IMPLEMENTED(B2BterminateOtherLeg()); NOT_IMPLEMENTED(B2BaddReceivedRequest(const AmSipRequest& req)); +NOT_IMPLEMENTED(B2BsetRelayEarlyMediaSDP(bool enabled)); NOT_IMPLEMENTED(B2BsetHeaders(const string& hdr, bool replaceCRLF)); NOT_IMPLEMENTED(B2BclearHeaders()); NOT_IMPLEMENTED(B2BaddHeader(const string& hdr)); -NOT_IMPLEMENTED(transferOwnership(DSMDisposable* d)); -NOT_IMPLEMENTED(releaseOwnership(DSMDisposable* d)); +NOT_IMPLEMENTED(B2BremoveHeader(const string& hdr)); #undef NOT_IMPLEMENTED #undef NOT_IMPLEMENTED_UINT diff --git a/apps/dsm/SystemDSM.h b/apps/dsm/SystemDSM.h index cb8b2eb7..19f444b5 100644 --- a/apps/dsm/SystemDSM.h +++ b/apps/dsm/SystemDSM.h @@ -38,6 +38,9 @@ class SystemDSM string startDiagName; bool reload; + // owned by this instance + std::set gc_trash; + public: SystemDSM(const DSMScriptConfig& config, @@ -52,8 +55,9 @@ class SystemDSM void process(AmEvent* event); // DSMSession interface - void playPrompt(const string& name, bool loop = false); + void playPrompt(const string& name, bool loop = false, bool front = false); void playFile(const string& name, bool loop, bool front = false); + void playSilence(unsigned int length, bool front = false); void recordFile(const string& name); unsigned int getRecordLength(); unsigned int getRecordDataSize(); @@ -62,8 +66,8 @@ class SystemDSM void setInputPlaylist(); void setOutputPlaylist(); - void addToPlaylist(AmPlaylistItem* item); - void closePlaylist(bool notify); + void addToPlaylist(AmPlaylistItem* item, bool front = false); + void flushPlaylist(); void setPromptSet(const string& name); void addSeparator(const string& name, bool front = false); void connectMedia(); @@ -80,6 +84,8 @@ class SystemDSM /** insert request in list of received ones */ void B2BaddReceivedRequest(const AmSipRequest& req); + void B2BsetRelayEarlyMediaSDP(bool enabled); + /** set headers of outgoing INVITE */ void B2BsetHeaders(const string& hdr, bool replaceCRLF); @@ -89,6 +95,9 @@ class SystemDSM /** add a header to the headers of outgoing INVITE */ void B2BaddHeader(const string& hdr); + /** remove a header to the headers of outgoing INVITE */ + void B2BremoveHeader(const string& hdr); + /** transfer ownership of object to this session instance */ void transferOwnership(DSMDisposable* d); diff --git a/apps/dsm/etc/dsm.conf b/apps/dsm/etc/dsm.conf index e13775bd..f5df0ae2 100644 --- a/apps/dsm/etc/dsm.conf +++ b/apps/dsm/etc/dsm.conf @@ -127,8 +127,8 @@ load_prompts=/usr/local/etc/sems/dsm_in_prompts.conf,/usr/local/etc/sems/dsm_out # conf_dir= # -# is scanned for *.conf files. Every .conf -# file is loaded, and in the .conf file, the following +# is scanned for *.conf files configuring application bundles ("app bundles"). +# Every .conf file is loaded as app bundle, and in the .conf file, the following # DSM config parameters are processed: # diag_path= # load_diags= diff --git a/apps/dsm/mods/Makefile b/apps/dsm/mods/Makefile index 7752d49a..a89f9e3f 100644 --- a/apps/dsm/mods/Makefile +++ b/apps/dsm/mods/Makefile @@ -1,10 +1,10 @@ -DSMPATH = ../ +DSMPATH = .. COREPATH = $(DSMPATH)/../../core .DEFAULT_GOAL := all include $(COREPATH)/../Makefile.defs -exclude_dsm_modules ?= mod_mysql mod_aws +exclude_dsm_modules ?= mod_mysql mod_aws mod_curl mod_xml ifneq ($(USE_MONITORING), yes) exclude_dsm_modules += mod_monitoring diff --git a/apps/dsm/mods/Makefile.dsm_module b/apps/dsm/mods/Makefile.dsm_module index 0d1cd573..3a424709 100644 --- a/apps/dsm/mods/Makefile.dsm_module +++ b/apps/dsm/mods/Makefile.dsm_module @@ -26,23 +26,15 @@ objs = $(srcs:.cpp=.o) depends = $(srcs:.cpp=.d) .PHONY: all -all: $(extra_target) - -@$(MAKE) deps && \ - $(MAKE) $(lib_full_name) +all: $(extra_target) $(lib_full_name) .PHONY: module_package -module_package: $(extra_target) - -@$(MAKE) deps && \ - $(MAKE) $(lib_name) - +module_package: $(extra_target) $(lib_name) .PHONY: clean clean: $(extra_clean) -@rm -f $(objs) $(depends) $(lib_name) -.PHONY: deps -deps: $(depends) $(extra_depends) - .PHONY: install install: all $(extra_install) mkdir -p $(DESTDIR)$(lib-prefix)/$(lib-dir)/dsm @@ -89,29 +81,18 @@ $(plug_in_name)_lib: fi ; \ done -%.d: %.cpp Makefile $(COREPATH)/plug-in/Makefile.app_module $(COREPATH)/../Makefile.defs - $(CXX) -MM $< $(CXXFLAGS) $(CPPFLAGS) > $@ - -%.d: %.c Makefile $(COREPATH)/plug-in/Makefile.app_module $(COREPATH)/../Makefile.defs - $(CC) -MM $< $(CFLAGS) $(CPPFLAGS) > $@ +-include $(depends) -%.o: %.cpp %.d $(COREPATH)/../Makefile.defs - $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ - -%.o: %.c %.d $(COREPATH)/../Makefile.defs - $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ +%.o: %.cpp $(COREPATH)/../Makefile.defs + $(CXX) -MMD $(CXXFLAGS) $(CPPFLAGS) -c $< -o $@ +%.o: %.c $(COREPATH)/../Makefile.defs + $(CC) -MMD $(CFLAGS) $(CPPFLAGS) -c $< -o $@ +ifneq ($(lib_full_name),$(lib_name)) $(lib_full_name): $(lib_name) cp $(lib_name) $(lib_full_name) +endif $(lib_name): $(objs) $(module_extra_objs) Makefile $(LD) -o $(lib_name) $(objs) $(module_extra_objs) $(LIB_LDFLAGS) - -ifeq ($(lib_full_name),$(MAKECMDGOALS)) -include $(depends) -endif - -ifeq (,$(MAKECMDGOALS)) -include $(depends) -endif diff --git a/apps/dsm/mods/mod_conference/ModConference.cpp b/apps/dsm/mods/mod_conference/ModConference.cpp index 7119da5c..07a3e395 100644 --- a/apps/dsm/mods/mod_conference/ModConference.cpp +++ b/apps/dsm/mods/mod_conference/ModConference.cpp @@ -130,7 +130,7 @@ static bool ConferenceJoinChannel(DSMConfChannel** dsm_chan, connect_record?"true":"false"); AmConferenceChannel* chan = AmConferenceStatus::getChannel(channel_id, - sess->getLocalTag()); + sess->getLocalTag(), sess->RTPStream()->getSampleRate()); if (NULL == chan) { ERROR("obtaining conference channel\n"); throw DSMException("conference"); @@ -181,7 +181,7 @@ static T* getDSMConfChannel(DSMSession* sc_sess, const char* key_name) { if (sc_sess->avar.find(key_name) == sc_sess->avar.end()) { return NULL; } - ArgObject* ao = NULL; T* res = NULL; + AmObject* ao = NULL; T* res = NULL; try { if (!isArgAObject(sc_sess->avar[key_name])) { return NULL; @@ -255,7 +255,7 @@ EXEC_ACTION_START(ConfTeeJoinAction) { if (NULL == chan) { DBG("not previously in tee-channel, creating new\n"); AmConferenceChannel* conf_channel = AmConferenceStatus::getChannel(channel_id, - sess->getLocalTag()); + sess->getLocalTag(), sess->RTPStream()->getSampleRate()); if (NULL == conf_channel) { ERROR("obtaining conference channel\n"); throw DSMException("conference"); @@ -287,7 +287,7 @@ EXEC_ACTION_START(ConfTeeJoinAction) { sc_sess->setInputPlaylist(); AmConferenceChannel* conf_channel = AmConferenceStatus::getChannel(channel_id, - sess->getLocalTag()); + sess->getLocalTag(), sess->RTPStream()->getSampleRate()); if (NULL == conf_channel) { ERROR("obtaining conference channel\n"); throw DSMException("conference"); diff --git a/apps/dsm/mods/mod_conference/ModConference.h b/apps/dsm/mods/mod_conference/ModConference.h index 4feecf60..c1e29b20 100644 --- a/apps/dsm/mods/mod_conference/ModConference.h +++ b/apps/dsm/mods/mod_conference/ModConference.h @@ -43,7 +43,7 @@ DECLARE_MODULE(MOD_CLS_NAME); /** holds a conference channel */ class DSMConfChannel : public DSMDisposable, - public ArgObject { + public AmObject { std::auto_ptr chan; public: @@ -56,7 +56,7 @@ class DSMConfChannel /** hold conference channel and audio queue */ class DSMTeeConfChannel : public DSMDisposable, - public ArgObject { + public AmObject { std::auto_ptr chan; AmAudioQueue audio_queue; @@ -71,7 +71,7 @@ class DSMTeeConfChannel template class DSMDisposableT : public DSMDisposable, - public ArgObject { + public AmObject { std::auto_ptr pobj; public: diff --git a/apps/dsm/mods/mod_curl/Makefile b/apps/dsm/mods/mod_curl/Makefile new file mode 100644 index 00000000..7031885b --- /dev/null +++ b/apps/dsm/mods/mod_curl/Makefile @@ -0,0 +1,14 @@ +plug_in_name = mod_curl + +DSMPATH ?= ../.. + +module_ldflags = `curl-config --libs` +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) `curl-config --cflags` + +# for local libcurl: +#module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) -Icurl-7.15.5/include +#module_ldflags = curl-7.15.5/lib/.libs/libcurl.a + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_curl/ModCurl.cpp b/apps/dsm/mods/mod_curl/ModCurl.cpp new file mode 100644 index 00000000..e550f31c --- /dev/null +++ b/apps/dsm/mods/mod_curl/ModCurl.cpp @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2009 TelTech Systems Inc. + * Copyright (C) 2011 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * sems 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 + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ + +#include "ModCurl.h" +#include "log.h" +#include "AmUtils.h" + +#include "DSMSession.h" +#include "AmSession.h" + +#include +#include +#include "AmConfigReader.h" + +bool CurlModule::curl_initialized = false; + +SC_EXPORT(CurlModule); + +CurlModule::CurlModule() { + if (!curl_initialized) { + curl_initialized = true; + if (curl_global_init(CURL_GLOBAL_ALL)) { + ERROR("Initializing libcurl\n"); + throw string("Initializing libcurl"); + } + + curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); + if (data && data->version >=0) { + DBG("using libcurl version '%s'\n", + data->version); + if (data->features & CURL_VERSION_SSL) { + DBG("libcurl with SSL version '%s'\n", data->ssl_version); + } else { + DBG("libcurl without SSL support\n"); + } + } + } +} + +CurlModule::~CurlModule() { +} + + +DSMAction* CurlModule::getAction(const string& from_str) { + string cmd; + string params; + splitCmd(from_str, cmd, params); + + DEF_CMD("curl.get", SCJCurlGetAction); + DEF_CMD("curl.getDiscardResult", SCJCurlGetNoresultAction); + DEF_CMD("curl.getFile", SCJCurlGetFileAction); + DEF_CMD("curl.getForm", SCJCurlGetFormAction); + DEF_CMD("curl.post", SCJCurlPOSTGetResultAction); + DEF_CMD("curl.postDiscardResult", SCJCurlPOSTAction); + + return NULL; +} + +DSMCondition* CurlModule::getCondition(const string& from_str) { + return NULL; +} + +size_t debug_output_func(void *ptr, size_t size, size_t + nmemb, void *stream) { + string data((char*)ptr, size*nmemb); + DBG("server out: <<%s>>\n", data.c_str()); + return size*nmemb; +} + +/** append output to $curl.out */ +size_t var_output_func(void *ptr, size_t size, size_t + nmemb, void *stream) { + if (NULL == stream) + return size*nmemb; + + string data((char*)ptr, size*nmemb); + DBG("server out: <<%s>>\n", data.c_str()); + DSMSession* sc_sess = reinterpret_cast(stream); + if (sc_sess) { + sc_sess->var["curl.out"]+=data; + } + return size*nmemb; +} + +bool curl_run_get(DSMSession* sc_sess, const string& url, + bool get_result) { + CURL* m_curl_handle = curl_easy_init(); + if (!m_curl_handle) { + ERROR("getting curl handle\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + return false; + } + + char* enc_url = curl_easy_escape(m_curl_handle, url.c_str(), url.length()); + if (NULL == enc_url) { + ERROR("URL-encoding url '%s'\n", url.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + return false; + } + + if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str()) + != CURLE_OK) { + ERROR("setting URL '%s'\n", url.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + free(enc_url); + return false; + } + + if (!sc_sess->var["curl.timeout"].empty()) { + unsigned int curl_timeout = 0; + if (str2i(sc_sess->var["curl.timeout"], curl_timeout)) { + WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str()); + } else { + if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) || + (curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) { + ERROR("setting timeout '%u'\n", curl_timeout); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + free(enc_url); + return false; + } + } + } + + if (!get_result) { + if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, debug_output_func) + != CURLE_OK) { + ERROR("setting curl write function\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + curl_easy_cleanup(m_curl_handle); + free(enc_url); + return false; + } + } else { + if ((curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, var_output_func) + != CURLE_OK)|| + (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, sc_sess) + != CURLE_OK)) { + ERROR("setting curl write function\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + curl_easy_cleanup(m_curl_handle); + free(enc_url); + return false; + } + } + + char curl_err[CURL_ERROR_SIZE]; + curl_err[0]='\0'; + if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err) + != CURLE_OK) { + ERROR("setting curl error buffer\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + curl_easy_cleanup(m_curl_handle); + free(enc_url); + return false; + } + + CURLcode rescode = curl_easy_perform(m_curl_handle); + + if (rescode) { + DBG("Error while trying to retrieve '%s': '%s'\n", + url.c_str(), curl_err); + sc_sess->var["curl.err"] = string(curl_err); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + } else { + sc_sess->SET_ERRNO(DSM_ERRNO_OK); + } + + curl_easy_cleanup(m_curl_handle); + free(enc_url); + return false; +} + + +EXEC_ACTION_START(SCJCurlGetAction) { + sc_sess->var.erase("curl.out"); + return curl_run_get(sc_sess, resolveVars(arg, sess, sc_sess, event_params), true); +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCJCurlGetNoresultAction) { + return curl_run_get(sc_sess, resolveVars(arg, sess, sc_sess, event_params), false); +} EXEC_ACTION_END; + + +CONST_ACTION_2P(SCJCurlGetFormAction, ',', true); +EXEC_ACTION_START(SCJCurlGetFormAction) { + sc_sess->var.erase("curl.out"); + string form_url = resolveVars(par1, sess, sc_sess, event_params);; + bool url_has_qmark = form_url.find('?')!=string::npos; + + vector p_vars=explode(par2, ";"); + for (vector::iterator it= + p_vars.begin();it != p_vars.end();it++) { + string varname = (it->size() && ((*it)[0]=='$')) ? (it->substr(1)) : (*it); + DBG("adding '%s' = '%s'\n", varname.c_str(), sc_sess->var[varname].c_str()); + if (!url_has_qmark && it == p_vars.begin()) + form_url+= "?"; + else + form_url+= "&"; + form_url += varname + "=" + sc_sess->var[varname]; + } + + return curl_run_get(sc_sess, form_url, true); +} EXEC_ACTION_END; + +void curl_run_getfile(DSMSession* sc_sess, const string& url, const string& outfile) { + CURL* m_curl_handle = curl_easy_init(); + if (!m_curl_handle) { + ERROR("getting curl handle\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + return; + } + + if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, url.c_str()) + != CURLE_OK) { + ERROR("setting URL '%s'\n", url.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + return; + } + + if (!sc_sess->var["curl.timeout"].empty()) { + unsigned int curl_timeout = 0; + if (str2i(sc_sess->var["curl.timeout"], curl_timeout)) { + WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str()); + } else { + if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) || + (curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) { + ERROR("setting timeout '%u'\n", curl_timeout); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + return; + } + } + } + + FILE* f = fopen(outfile.c_str(), "wb"); + if (NULL == f) { + DBG("Error opening file '%s' for writing\n", outfile.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + return; + } + + if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, f) + != CURLE_OK) { + ERROR("setting curl data file\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + fclose(f); + return; + } + + char curl_err[CURL_ERROR_SIZE]; + curl_err[0]='\0'; + if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err) + != CURLE_OK) { + ERROR("setting URL '%s'\n", url.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + fclose(f); + return; + } + + CURLcode rescode = curl_easy_perform(m_curl_handle); + + if (rescode) { + DBG("Error while trying to retrieve '%s' to '%s': '%s'\n", + url.c_str(), outfile.c_str(), curl_err); + sc_sess->var["curl.err"] = string(curl_err); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + }else { + sc_sess->SET_ERRNO(DSM_ERRNO_OK); + } + + fclose(f); + curl_easy_cleanup(m_curl_handle); +} + + +CONST_ACTION_2P(SCJCurlGetFileAction, ',', true); +EXEC_ACTION_START(SCJCurlGetFileAction) { + curl_run_getfile(sc_sess, + resolveVars(par1, sess, sc_sess, event_params), + resolveVars(par2, sess, sc_sess, event_params)); +} EXEC_ACTION_END; + +bool curl_run_post(DSMSession* sc_sess, const string& par1, const string& par2, + bool get_result) { + CURL* m_curl_handle = curl_easy_init(); + if (!m_curl_handle) { + ERROR("getting curl handle\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + return false; + } + + if (curl_easy_setopt(m_curl_handle, CURLOPT_URL, par1.c_str()) + != CURLE_OK) { + ERROR("setting URL '%s'\n", par1.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + return false; + } + + if (!sc_sess->var["curl.timeout"].empty()) { + unsigned int curl_timeout = 0; + if (str2i(sc_sess->var["curl.timeout"], curl_timeout)) { + WARN("curl.timeout '%s' not understood\n", sc_sess->var["curl.timeout"].c_str()); + } else { + if ((curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT, curl_timeout) != CURLE_OK) || + (curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 1L) != CURLE_OK)) { + ERROR("setting timeout '%u'\n", curl_timeout); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + curl_easy_cleanup(m_curl_handle); + return false; + } + } + } + + if (!get_result) { + if (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, debug_output_func) + != CURLE_OK) { + ERROR("setting curl write function\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + curl_easy_cleanup(m_curl_handle); + return false; + } + } else { + if ((curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, var_output_func) + != CURLE_OK)|| + (curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, sc_sess) + != CURLE_OK)) { + ERROR("setting curl write function\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_FILE); + curl_easy_cleanup(m_curl_handle); + return false; + } + } + + char curl_err[CURL_ERROR_SIZE]; + curl_err[0]='\0'; + if (curl_easy_setopt(m_curl_handle, CURLOPT_ERRORBUFFER, curl_err) + != CURLE_OK) { + ERROR("setting curl error buffer\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + curl_easy_cleanup(m_curl_handle); + return false; + } + + struct curl_httppost *post=NULL; + struct curl_httppost *last=NULL; + string post_vars; + vector p_vars=explode(par2, ";"); + for (vector::iterator it= + p_vars.begin();it != p_vars.end();it++) { + string varname = (it->size() && ((*it)[0]=='$')) ? (it->substr(1)) : (*it); + DBG("adding '%s' = '%s'\n", varname.c_str(), sc_sess->var[varname].c_str()); + curl_formadd(&post, &last, + CURLFORM_COPYNAME, varname.c_str(), + CURLFORM_COPYCONTENTS, sc_sess->var[varname].c_str(), CURLFORM_END); + } + + CURLcode rescode = curl_easy_perform(m_curl_handle); + + bool res = false; + if (rescode) { + DBG("Error while trying to POST to '%s': '%s'\n", + par1.c_str(), curl_err); + sc_sess->var["curl.err"] = string(curl_err); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + } else { + sc_sess->SET_ERRNO(DSM_ERRNO_OK); + res = true; + } + curl_formfree(post); + curl_easy_cleanup(m_curl_handle); + return false; +} + +CONST_ACTION_2P(SCJCurlPOSTAction, ',', true); +EXEC_ACTION_START(SCJCurlPOSTAction) { + curl_run_post(sc_sess, resolveVars(par1, sess, sc_sess, event_params), + par2, false); + return false; +} EXEC_ACTION_END; + +CONST_ACTION_2P(SCJCurlPOSTGetResultAction, ',', true); +EXEC_ACTION_START(SCJCurlPOSTGetResultAction) { + sc_sess->var.erase("curl.out"); + curl_run_post(sc_sess, resolveVars(par1, sess, sc_sess, event_params), + par2, true); + return false; +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_curl/ModCurl.h b/apps/dsm/mods/mod_curl/ModCurl.h new file mode 100644 index 00000000..4f1ec6b2 --- /dev/null +++ b/apps/dsm/mods/mod_curl/ModCurl.h @@ -0,0 +1,52 @@ +/* + * + * Copyright (C) 2009 TelTech Systems + * Copyright (C) 2011 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * sems 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 + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#ifndef _MOD_CURL_H +#define _MOD_CURL_H +#include "DSMModule.h" +#include "DSMSession.h" + +class CurlModule +: public DSMModule { + + public: + CurlModule(); + ~CurlModule(); + + DSMAction* getAction(const string& from_str); + DSMCondition* getCondition(const string& from_str); + + static bool curl_initialized; +}; + +DEF_ACTION_1P(SCJCurlGetAction); +DEF_ACTION_1P(SCJCurlGetNoresultAction); +DEF_ACTION_2P(SCJCurlGetFileAction); +DEF_ACTION_2P(SCJCurlGetFormAction); +DEF_ACTION_2P(SCJCurlPOSTAction); +DEF_ACTION_2P(SCJCurlPOSTGetResultAction); + +#endif diff --git a/apps/dsm/mods/mod_dlg/ModDlg.cpp b/apps/dsm/mods/mod_dlg/ModDlg.cpp index ec0c11c1..2257abf9 100644 --- a/apps/dsm/mods/mod_dlg/ModDlg.cpp +++ b/apps/dsm/mods/mod_dlg/ModDlg.cpp @@ -49,15 +49,55 @@ MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { DEF_CMD("dlg.connectCalleeRelayed", DLGConnectCalleeRelayedAction); DEF_CMD("dlg.dialout", DLGDialoutAction); + DEF_CMD("dlg.getRequestBody", DLGGetRequestBodyAction) + DEF_CMD("dlg.getReplyBody", DLGGetReplyBodyAction) + + DEF_CMD("dlg.getOtherId", DLGGetOtherIdAction) + DEF_CMD("dlg.getRtpRelayMode", DLGGetRtpRelayModeAction) + + DEF_CMD("dlg.refer", DLGReferAction); + DEF_CMD("dlg.info", DLGInfoAction); + DEF_CMD("dlg.relayError", DLGB2BRelayErrorAction); + + DEF_CMD("dlg.addReplyBodyPart", DLGAddReplyBodyPartAction); + DEF_CMD("dlg.deleteReplyBodyPart", DLGDeleteReplyBodyPartAction); + } MOD_ACTIONEXPORT_END; -MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); +//MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); + + +MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) { + if (cmd == "dlg.replyHasContentType") + return new DLGReplyHasContentTypeCondition(params, false); + + if (cmd == "dlg.requestHasContentType") + return new DLGRequestHasContentTypeCondition(params, false); + +} MOD_CONDITIONEXPORT_END; bool DLGModule::onInvite(const AmSipRequest& req, DSMSession* sess) { // save inivital invite to last_req // todo: save this in avar sess->last_req.reset(new AmSipRequest(req)); - return true; + return true; +} + +string replaceLineEnds(string input) +{ + string result; + size_t last = 0; + size_t pos; + while ((pos = input.find("\\r\\n", last)) != string::npos) { + result += input.substr(last, pos-last); + result += "\r\n"; + last = pos + 4; + } + if (!input.substr(last).empty()) { + result += input.substr(last); + result += "\r\n"; + } + return result; } // todo: convert errors to exceptions @@ -67,6 +107,7 @@ void replyRequest(DSMSession* sc_sess, AmSession* sess, const AmSipRequest& req) { string code = resolveVars(par1, sess, sc_sess, event_params); string reason = resolveVars(par2, sess, sc_sess, event_params); + string hdrs = replaceLineEnds(resolveVars("$dlg.reply.hdrs", sess, sc_sess, event_params)); unsigned int code_i; if (str2i(code, code_i)) { ERROR("decoding reply code '%s'\n", code.c_str()); @@ -74,14 +115,9 @@ void replyRequest(DSMSession* sc_sess, AmSession* sess, return; } - if (!sc_sess->last_req.get()) { - ERROR("no last request to reply\n"); - sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); - sc_sess->SET_STRERROR("no last request to reply"); - return; - } + DBG("replying with %i %s, hdrs='%s'\n", code_i, reason.c_str(), hdrs.c_str()); - if (sess->dlg.reply(req, code_i, reason)) { + if (sess->dlg->reply(req, code_i, reason, NULL, hdrs)) { sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); sc_sess->SET_STRERROR("error sending reply"); } else @@ -90,6 +126,14 @@ void replyRequest(DSMSession* sc_sess, AmSession* sess, CONST_ACTION_2P(DLGReplyAction, ',', true); EXEC_ACTION_START(DLGReplyAction) { + + if (!sc_sess->last_req.get()) { + ERROR("no last request to reply\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + sc_sess->SET_STRERROR("no last request to reply"); + return false; + } + replyRequest(sc_sess, sess, event_params, par1, par2, *sc_sess->last_req.get()); } EXEC_ACTION_END; @@ -114,7 +158,7 @@ EXEC_ACTION_START(DLGAcceptInviteAction) { unsigned int code_i=200; string reason = "OK"; string code = resolveVars(par1, sess, sc_sess, event_params); - DBG("replying with code %s\n", code.c_str()); + string hdrs = replaceLineEnds(resolveVars("$dlg.reply.hdrs", sess, sc_sess, event_params)); if (code.length()) { reason = resolveVars(par2, sess, sc_sess, event_params); @@ -127,6 +171,8 @@ EXEC_ACTION_START(DLGAcceptInviteAction) { } } + DBG("replying with %i %s, hdrs='%s'\n", code_i, reason.c_str(), hdrs.c_str()); + if (!sc_sess->last_req.get()) { ERROR("no last request to reply\n"); sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); @@ -135,26 +181,26 @@ EXEC_ACTION_START(DLGAcceptInviteAction) { } try { - string sdp_reply; - - sess->acceptAudio(sc_sess->last_req.get()->body, - sc_sess->last_req.get()->hdrs,&sdp_reply); - if(sess->dlg.reply(*sc_sess->last_req.get(),code_i, reason, - "application/sdp",sdp_reply) != 0) + AmMimeBody sdp_body; + if(sess->dlg->reply(*sc_sess->last_req.get(),code_i, reason, + sdp_body.addPart(SIP_APPLICATION_SDP), hdrs) != 0) throw AmSession::Exception(500,"could not send response"); - + }catch(const AmSession::Exception& e){ ERROR("%i %s\n",e.code,e.reason.c_str()); sess->setStopped(); - sess->dlg.reply(*sc_sess->last_req.get(),e.code,e.reason); + sess->dlg->reply(*sc_sess->last_req.get(),e.code,e.reason); + + sc_sess->SET_ERRNO(DSM_ERRNO_DLG); + sc_sess->SET_STRERROR("Error accepting call: "+ int2str(e.code) + " "+ e.reason); } } EXEC_ACTION_END; EXEC_ACTION_START(DLGByeAction) { string hdrs = resolveVars(arg, sess, sc_sess, event_params); - if (sess->dlg.bye(hdrs)) { + if (sess->dlg->bye(hdrs)) { sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); sc_sess->SET_STRERROR("Error sending bye"); } else { @@ -292,3 +338,277 @@ EXEC_ACTION_START(DLGDialoutAction) { } } EXEC_ACTION_END; + +MATCH_CONDITION_START(DLGReplyHasContentTypeCondition) { + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY); + if (it == sc_sess->avar.end()) { + ERROR("DSM script error: dlg.replyHasContentType condition used for " + "other event than sipReply event\n"); + return false; + } + + DSMSipReply* dsm_reply = NULL; + if (!isArgAObject(sc_sess->avar[DSM_AVAR_REPLY]) || + (NULL == + (dsm_reply = dynamic_cast(sc_sess->avar[DSM_AVAR_REPLY].asObject())))) { + ERROR("internal: DSM could not get DSMSipReply\n"); + return false; + } + + bool res = dsm_reply->reply->body.hasContentType(arg); + + DBG("checking for content_type '%s': %s\n", arg.c_str(), res?"has it":"doesn't have it"); + return res; +} MATCH_CONDITION_END; + +MATCH_CONDITION_START(DLGRequestHasContentTypeCondition) { + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST); + if (it == sc_sess->avar.end()) { + ERROR("DSM script error: dlg.requestHasContentType condition used for " + "other event than sipRequest event\n"); + return false; + } + + DSMSipRequest* dsm_req = NULL; + if (!isArgAObject(sc_sess->avar[DSM_AVAR_REQUEST]) || + (NULL == + (dsm_req = dynamic_cast(sc_sess->avar[DSM_AVAR_REQUEST].asObject())))) { + ERROR("internal: DSM could not get DSMSipRequest\n"); + return false; + } + + bool res = dsm_req->req->body.hasContentType(arg); + + DBG("checking for content_type '%s': %s\n", arg.c_str(), res?"has it":"doesn't have it"); + return res; +} MATCH_CONDITION_END; + +CONST_ACTION_2P(DLGGetRequestBodyAction, ',', false); +EXEC_ACTION_START(DLGGetRequestBodyAction) { + DSMSipRequest* sip_req; + + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST); + if (it == sc_sess->avar.end() || + !isArgAObject(it->second) || + !(sip_req = dynamic_cast(it->second.asObject()))) { + throw DSMException("dlg", "cause", "no request"); + } + + string content_type = resolveVars(par1, sess, sc_sess, event_params); + string dstvar = resolveVars(par2, sess, sc_sess, event_params); + + const AmMimeBody* msg_body = sip_req->req->body.hasContentType(content_type); + if (NULL == msg_body) { + DBG("body with content_type %s not found\n", content_type.c_str()); + sc_sess->var.erase(dstvar); + } else { + sc_sess->var[dstvar] = string((const char*)msg_body->getPayload()); + DBG("set $%s='%s'\n", dstvar.c_str(), sc_sess->var[dstvar].c_str()); + } +} EXEC_ACTION_END; + +CONST_ACTION_2P(DLGGetReplyBodyAction, ',', false); +EXEC_ACTION_START(DLGGetReplyBodyAction) { + DSMSipReply* sip_req; + + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY); + if (it == sc_sess->avar.end() || + !isArgAObject(it->second) || + !(sip_req = dynamic_cast(it->second.asObject()))) { + throw DSMException("dlg", "cause", "no reply"); + } + + string content_type = resolveVars(par1, sess, sc_sess, event_params); + string dstvar = resolveVars(par2, sess, sc_sess, event_params); + + const AmMimeBody* msg_body = sip_req->reply->body.hasContentType(content_type); + if (NULL == msg_body) { + DBG("body with content_type %s not found\n", content_type.c_str()); + sc_sess->var.erase(dstvar); + } else { + sc_sess->var[dstvar] = string((const char*)msg_body->getPayload()); + DBG("set $%s='%s'\n", dstvar.c_str(), sc_sess->var[dstvar].c_str()); + } +} EXEC_ACTION_END; + +EXEC_ACTION_START(DLGGetOtherIdAction) { + string varname = arg; + AmB2BSession* b2b_sess = dynamic_cast(sess); + if (NULL == b2b_sess) { + DBG("script writer error: dlg.getOtherId used without B2B session object.\n"); + EXEC_ACTION_STOP; + } + + if (varname.size() && varname[0] == '$') + varname.erase(0, 1); + sc_sess->var[varname] = b2b_sess->getOtherId(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(DLGGetRtpRelayModeAction) { + string varname = arg; + AmB2BSession* b2b_sess = dynamic_cast(sess); + if (NULL == b2b_sess) { + DBG("script writer error: dlg.getOtherId used without B2B session object.\n"); + EXEC_ACTION_STOP; + } + + if (varname.size() && varname[0] == '$') + varname.erase(0, 1); + switch (b2b_sess->getRtpRelayMode()) { + case AmB2BSession::RTP_Direct: sc_sess->var[varname] = "RTP_Direct"; break; + case AmB2BSession::RTP_Relay: sc_sess->var[varname] = "RTP_Relay"; break; + case AmB2BSession::RTP_Transcoding: sc_sess->var[varname] = "RTP_Transcoding"; break; + default: sc_sess->var[varname] = "Unknown"; break; + } + + DBG("get RTP relay mode: %s='%s'\n", varname.c_str(), sc_sess->var[varname].c_str()); +} EXEC_ACTION_END; + +CONST_ACTION_2P(DLGReferAction, ',', true); +EXEC_ACTION_START(DLGReferAction) { + + AmSession* b2b_sess = dynamic_cast(sess); + if (NULL == b2b_sess) { + throw DSMException("sbc", "type", "param", "cause", + "dlg.refer used on non-session"); + } + + string refer_to = resolveVars(par1, sess, sc_sess, event_params); + string expires_s = resolveVars(par2, sess, sc_sess, event_params); + + int expires = -1; + if (!expires_s.empty()) { + if (!str2int(expires_s, expires)) { + throw DSMException("sbc", "type", "param", "cause", + "expires "+expires_s+" not valid"); + } + } + + if (NULL == b2b_sess->dlg) { + throw DSMException("sbc", "type", "param", "cause", + "call doesn't have SIP dialog (OOPS!)"); + } + + if (b2b_sess->dlg->refer(refer_to, expires)) { + sc_sess->SET_ERRNO(DSM_ERRNO_DLG); + sc_sess->SET_STRERROR("sending REFER failed"); + } else { + sc_sess->CLR_ERRNO; + } +} EXEC_ACTION_END; + +CONST_ACTION_2P(DLGInfoAction, ',', true); +EXEC_ACTION_START(DLGInfoAction) { + + AmSession* b2b_sess = dynamic_cast(sess); + if (NULL == b2b_sess) { + throw DSMException("sbc", "type", "param", "cause", + "dlg.info used on non-session"); + } + + string content_type = resolveVars(par1, sess, sc_sess, event_params); + string body_str = resolveVars(par2, sess, sc_sess, event_params); + + if (NULL == b2b_sess->dlg) { + throw DSMException("sbc", "type", "param", "cause", + "call doesn't have SIP dialog (OOPS!)"); + } + + string body_crlf = body_str; + AmMimeBody *body = new AmMimeBody(); + if (!content_type.empty()) { + DBG("body_crlf is '%s'\n", body_crlf.c_str()); + while (true) { + size_t p = body_crlf.find("\\r\\n"); + if (p==string::npos) + break; + body_crlf.replace(p, 4, "\r\n"); + } + DBG("-> body_crlf is '%s'\n", body_crlf.c_str()); + if (body->parse(content_type, + reinterpret_cast(body_crlf.c_str()), + body_crlf.length())) { + throw DSMException("sbc", "type", "param", "cause", + "parsing of INFO body failed"); + } + } + + if (b2b_sess->dlg->info("", body)) { + sc_sess->SET_ERRNO(DSM_ERRNO_DLG); + sc_sess->SET_STRERROR("sending INFO failed"); + } else { + sc_sess->CLR_ERRNO; + } + +} EXEC_ACTION_END; + +#define GET_B2B_SESSION(action) \ + AmB2BSession* b2b_sess = dynamic_cast(sess); \ + if (NULL == b2b_sess) { \ + throw DSMException("sbc", "type", "param", "cause", \ + #action " used on non-b2b-session"); \ + } + +CONST_ACTION_2P(DLGB2BRelayErrorAction, ',', false); +EXEC_ACTION_START(DLGB2BRelayErrorAction) { + DSMSipRequest* sip_req; + + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REQUEST); + if (it == sc_sess->avar.end() || + !isArgAObject(it->second) || + !(sip_req = dynamic_cast(it->second.asObject()))) { + throw DSMException("dlg", "cause", "no request"); + } + GET_B2B_SESSION(dlg.refer); + + string code = resolveVars(par1, sess, sc_sess, event_params); + string reason = resolveVars(par2, sess, sc_sess, event_params); + unsigned int code_i; + if (str2i(code, code_i)) { + ERROR("decoding reply code '%s'\n", code.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + EXEC_ACTION_STOP; + } + + b2b_sess->relayError(sip_req->req->method, sip_req->req->cseq, true, code_i, reason.c_str()); +} EXEC_ACTION_END; + +CONST_ACTION_2P(DLGAddReplyBodyPartAction, ',', false); +EXEC_ACTION_START(DLGAddReplyBodyPartAction) { + DSMMutableSipReply* sip_reply; + + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY); + if (it == sc_sess->avar.end() || + !isArgAObject(it->second) || + !(sip_reply = dynamic_cast(it->second.asObject()))) { + throw DSMException("dlg", "cause", "no reply"); + } + + string content_type = resolveVars(par1, sess, sc_sess, event_params); + string body_part = resolveVars(par2, sess, sc_sess, event_params); + + AmMimeBody* new_part; + + new_part = sip_reply->mutable_reply->body.addPart(content_type); + new_part->setPayload((const unsigned char*)body_part.c_str(), + body_part.length()); + DBG("added to reply body part %s='%s'\n", + content_type.c_str(), body_part.c_str()); +} EXEC_ACTION_END; + +EXEC_ACTION_START(DLGDeleteReplyBodyPartAction) { + DSMMutableSipReply* sip_reply; + + AVarMapT::iterator it = sc_sess->avar.find(DSM_AVAR_REPLY); + if (it == sc_sess->avar.end() || + !isArgAObject(it->second) || + !(sip_reply = dynamic_cast(it->second.asObject()))) { + throw DSMException("dlg", "cause", "no reply"); + } + + if (sip_reply->mutable_reply->body.deletePart(arg)) { + DBG("failed to delete reply body part '%s'\n", arg.c_str()); + } else { + DBG("deleted reply body part '%s'\n", arg.c_str()); + } +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_dlg/ModDlg.h b/apps/dsm/mods/mod_dlg/ModDlg.h index 978288b1..23cb104b 100644 --- a/apps/dsm/mods/mod_dlg/ModDlg.h +++ b/apps/dsm/mods/mod_dlg/ModDlg.h @@ -30,6 +30,8 @@ #define MOD_CLS_NAME DLGModule +#define DSM_ERRNO_DLG "dlg" + DECLARE_MODULE_BEGIN(MOD_CLS_NAME); bool onInvite(const AmSipRequest& req, DSMSession* sess); DECLARE_MODULE_END; @@ -40,4 +42,19 @@ DEF_ACTION_2P(DLGAcceptInviteAction); DEF_ACTION_2P(DLGConnectCalleeRelayedAction); DEF_ACTION_1P(DLGByeAction); DEF_ACTION_1P(DLGDialoutAction); + +DEF_SCCondition(DLGReplyHasContentTypeCondition); +DEF_SCCondition(DLGRequestHasContentTypeCondition); +DEF_ACTION_2P(DLGGetRequestBodyAction); +DEF_ACTION_2P(DLGGetReplyBodyAction); + +DEF_ACTION_1P(DLGGetOtherIdAction); +DEF_ACTION_1P(DLGGetRtpRelayModeAction); + +DEF_ACTION_2P(DLGReferAction); +DEF_ACTION_2P(DLGB2BRelayErrorAction); + +DEF_ACTION_2P(DLGAddReplyBodyPartAction); +DEF_ACTION_1P(DLGDeleteReplyBodyPartAction); + #endif diff --git a/apps/dsm/mods/mod_monitoring/ModMonitoring.cpp b/apps/dsm/mods/mod_monitoring/ModMonitoring.cpp index c3bb9acc..2b5d6ee7 100644 --- a/apps/dsm/mods/mod_monitoring/ModMonitoring.cpp +++ b/apps/dsm/mods/mod_monitoring/ModMonitoring.cpp @@ -62,9 +62,17 @@ DSMAction* MonitoringModule::getAction(const string& from_str) { } DEF_CMD("monitoring.log", MonLogAction); + DEF_CMD("monitoring.set", MonLogAction); + DEF_CMD("monitoring.add", MonLogAddAction); DEF_CMD("monitoring.logAdd", MonLogAddAction); DEF_CMD("monitoring.logVars", MonLogVarsAction); + DEF_CMD("monitoring.setGlobal", MonLogGlobalAction); + DEF_CMD("monitoring.addGlobal", MonLogAddGlobalAction); + + DEF_CMD("monitoring.inc", MonLogIncAction); + DEF_CMD("monitoring.dec", MonLogDecAction); + return NULL; } @@ -115,4 +123,59 @@ bool MonLogVarsAction::execute(AmSession* sess, DSMSession* sc_sess, } +CONST_ACTION_2P(MonLogGlobalAction, ',', true); +EXEC_ACTION_START(MonLogGlobalAction) { + string id = resolveVars(par1, sess, sc_sess, event_params); + string prop; + string val; + + size_t c = par2.find(','); + if (c != string::npos) { + prop = resolveVars(par2.substr(0, c), sess, sc_sess, event_params); + val = resolveVars(par2.substr(c+1), sess, sc_sess, event_params); + } else { + prop = resolveVars(par2, sess, sc_sess, event_params); + } + + MONITORING_LOG(id.c_str(), prop.c_str(), val.c_str()); + +} EXEC_ACTION_END; + + +CONST_ACTION_2P(MonLogAddGlobalAction, ',', true); +EXEC_ACTION_START(MonLogAddGlobalAction) { + string id = resolveVars(par1, sess, sc_sess, event_params); + string prop; + string val; + + size_t c = par2.find(','); + if (c != string::npos) { + prop = resolveVars(par2.substr(0, c), sess, sc_sess, event_params); + val = resolveVars(par2.substr(c+1), sess, sc_sess, event_params); + } else { + prop = resolveVars(par2, sess, sc_sess, event_params); + } + + MONITORING_LOG_ADD(id.c_str(), prop.c_str(), val.c_str()); +} EXEC_ACTION_END; + +CONST_ACTION_2P(MonLogIncAction, ',', true); +bool MonLogIncAction::execute(AmSession* sess, DSMSession* sc_sess, + DSMCondition::EventType event, + map* event_params) { + string type = resolveVars(par1, sess, sc_sess, event_params); + string name = resolveVars(par2, sess, sc_sess, event_params); + MONITORING_INC(type.c_str(), name.c_str()); + return false; +} + +CONST_ACTION_2P(MonLogDecAction, ',', true); +bool MonLogDecAction::execute(AmSession* sess, DSMSession* sc_sess, + DSMCondition::EventType event, + map* event_params) { + string type = resolveVars(par1, sess, sc_sess, event_params); + string name = resolveVars(par2, sess, sc_sess, event_params); + MONITORING_DEC(type.c_str(), name.c_str()); + return false; +} diff --git a/apps/dsm/mods/mod_monitoring/ModMonitoring.h b/apps/dsm/mods/mod_monitoring/ModMonitoring.h index 06a746fc..5c38413c 100644 --- a/apps/dsm/mods/mod_monitoring/ModMonitoring.h +++ b/apps/dsm/mods/mod_monitoring/ModMonitoring.h @@ -44,4 +44,10 @@ DEF_ACTION_2P(MonLogAction); DEF_ACTION_2P(MonLogAddAction); DEF_ACTION_1P(MonLogVarsAction); +DEF_ACTION_2P(MonLogGlobalAction); +DEF_ACTION_2P(MonLogAddGlobalAction); + +DEF_ACTION_2P(MonLogIncAction); +DEF_ACTION_2P(MonLogDecAction); + #endif diff --git a/apps/dsm/mods/mod_mysql/ModMysql.cpp b/apps/dsm/mods/mod_mysql/ModMysql.cpp index 6bec11dd..6ffd0e00 100644 --- a/apps/dsm/mods/mod_mysql/ModMysql.cpp +++ b/apps/dsm/mods/mod_mysql/ModMysql.cpp @@ -61,9 +61,11 @@ DSMAction* SCMysqlModule::getAction(const string& from_str) { DEF_CMD("mysql.saveResult", SCMySaveResultAction); DEF_CMD("mysql.useResult", SCMyUseResultAction); DEF_CMD("mysql.playDBAudio", SCMyPlayDBAudioAction); + DEF_CMD("mysql.playDBAudioFront", SCMyPlayDBAudioFrontAction); + DEF_CMD("mysql.playDBAudioLooped", SCMyPlayDBAudioLoopedAction); DEF_CMD("mysql.getFileFromDB", SCMyGetFileFromDBAction); DEF_CMD("mysql.putFileToDB", SCMyPutFileToDBAction); - + DEF_CMD("mysql.escape", SCMyEscapeAction); return NULL; } @@ -89,17 +91,17 @@ mysqlpp::Connection* getMyDSMSessionConnection(DSMSession* sc_sess) { sc_sess->SET_STRERROR("No connection to database"); return NULL; } - ArgObject* ao = NULL; mysqlpp::Connection* res = NULL; + AmObject* ao = NULL; mysqlpp::Connection* res = NULL; try { if (!isArgAObject(sc_sess->avar[MY_AKEY_CONNECTION])) { sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION); - sc_sess->SET_STRERROR("No connection to database (not ArgObject)"); + sc_sess->SET_STRERROR("No connection to database (not AmObject)"); return NULL; } ao = sc_sess->avar[MY_AKEY_CONNECTION].asObject(); } catch (...){ sc_sess->SET_ERRNO(DSM_ERRNO_MY_CONNECTION); - sc_sess->SET_STRERROR("No connection to database (not ArgObject)"); + sc_sess->SET_STRERROR("No connection to database (not AmObject)"); return NULL; } @@ -117,7 +119,7 @@ mysqlpp::StoreQueryResult* getMyDSMQueryResult(DSMSession* sc_sess) { sc_sess->SET_STRERROR("No result available"); return NULL; } - ArgObject* ao = NULL; mysqlpp::StoreQueryResult* res = NULL; + AmObject* ao = NULL; mysqlpp::StoreQueryResult* res = NULL; try { assertArgAObject(sc_sess->avar[MY_AKEY_RESULT]); ao = sc_sess->avar[MY_AKEY_RESULT].asObject(); @@ -178,7 +180,8 @@ string str_between(const string s, char b, char e) { } EXEC_ACTION_START(SCMyConnectAction) { - string db_url = arg.length()?arg:sc_sess->var["config.db_url"]; + string f_arg = resolveVars(arg, sess, sc_sess, event_params); + string db_url = f_arg.length()?f_arg:sc_sess->var["config.db_url"]; if (db_url.empty() || db_url.length() < 11 || db_url.substr(0, 8) != "mysql://") { ERROR("missing correct db_url config or connect parameter\n"); sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); @@ -450,12 +453,14 @@ EXEC_ACTION_START(SCMyUseResultAction) { } EXEC_ACTION_END; -CONST_ACTION_2P(SCMyPlayDBAudioAction, ',', true); -EXEC_ACTION_START(SCMyPlayDBAudioAction) { +bool playDBAudio(AmSession* sess, DSMSession* sc_sess, DSMCondition::EventType event, + map* event_params, const string& par1, const string& par2, + bool looped, bool front) { mysqlpp::Connection* conn = getMyDSMSessionConnection(sc_sess); - if (NULL == conn) - return false; + if (NULL == conn) + EXEC_ACTION_STOP; + string qstr = replaceQueryParams(par1, sc_sess, event_params); try { @@ -466,14 +471,14 @@ EXEC_ACTION_START(SCMyPlayDBAudioAction) { mysqlpp::Row row = res.fetch_row(); if (!row) { sc_sess->SET_ERRNO(DSM_ERRNO_MY_NOROW); - sc_sess->SET_STRERROR("result does not have row"); - return false; + sc_sess->SET_STRERROR("result does not have row"); + EXEC_ACTION_STOP; } FILE *t_file = tmpfile(); if (NULL == t_file) { sc_sess->SET_ERRNO(DSM_ERRNO_FILE); sc_sess->SET_STRERROR("tmpfile() failed: "+string(strerror(errno))); - return false; + EXEC_ACTION_STOP; } fwrite(row.at(0).data(), 1, row.at(0).size(), t_file); @@ -483,10 +488,12 @@ EXEC_ACTION_START(SCMyPlayDBAudioAction) { if (a_file->fpopen(par2, AmAudioFile::Read, t_file)) { sc_sess->SET_ERRNO(DSM_ERRNO_FILE); sc_sess->SET_STRERROR("fpopen failed!"); - return false; + EXEC_ACTION_STOP; } - sc_sess->addToPlaylist(new AmPlaylistItem(a_file, NULL)); + a_file->loop.set(looped); + + sc_sess->addToPlaylist(new AmPlaylistItem(a_file, NULL), front); sc_sess->transferOwnership(a_file); sc_sess->CLR_ERRNO; @@ -501,6 +508,25 @@ EXEC_ACTION_START(SCMyPlayDBAudioAction) { sc_sess->SET_STRERROR(e.what()); sc_sess->var["db.ereason"] = e.what(); } + return false; +} + +CONST_ACTION_2P(SCMyPlayDBAudioAction, ',', true); +EXEC_ACTION_START(SCMyPlayDBAudioAction) { + playDBAudio(sess, sc_sess, event, event_params, par1, par2, + /*looped = */ false, /*front = */ false); +} EXEC_ACTION_END; + +CONST_ACTION_2P(SCMyPlayDBAudioFrontAction, ',', true); +EXEC_ACTION_START(SCMyPlayDBAudioFrontAction) { + playDBAudio(sess, sc_sess, event, event_params, par1, par2, + /*looped = */ false, /*front = */ true); +} EXEC_ACTION_END; + +CONST_ACTION_2P(SCMyPlayDBAudioLoopedAction, ',', true); +EXEC_ACTION_START(SCMyPlayDBAudioLoopedAction) { + playDBAudio(sess, sc_sess, event, event_params, par1, par2, + /*looped = */ true, /*front = */ false); } EXEC_ACTION_END; CONST_ACTION_2P(SCMyGetFileFromDBAction, ',', true); @@ -510,6 +536,7 @@ EXEC_ACTION_START(SCMyGetFileFromDBAction) { if (NULL == conn) return false; string qstr = replaceQueryParams(par1, sc_sess, event_params); + string fname = resolveVars(par2, sess, sc_sess, event_params); try { mysqlpp::Query query = conn->query(qstr.c_str()); @@ -521,10 +548,10 @@ EXEC_ACTION_START(SCMyGetFileFromDBAction) { sc_sess->SET_STRERROR("result does not have row"); return false; } - FILE *t_file = fopen(par2.c_str(), "wb"); + FILE *t_file = fopen(fname.c_str(), "wb"); if (NULL == t_file) { sc_sess->SET_ERRNO(DSM_ERRNO_FILE); - sc_sess->SET_STRERROR("fopen() failed: "+string(strerror(errno))); + sc_sess->SET_STRERROR("fopen() failed for file '"+fname+"': "+string(strerror(errno))); return false; } @@ -608,3 +635,26 @@ EXEC_ACTION_START(SCMyPutFileToDBAction) { sc_sess->var["db.ereason"] = e.what(); } } EXEC_ACTION_END; + +CONST_ACTION_2P(SCMyEscapeAction, '=', false); +EXEC_ACTION_START(SCMyEscapeAction) { + mysqlpp::Connection* conn = + getMyDSMSessionConnection(sc_sess); + + if (NULL == conn) + return false; + + mysqlpp::Query query = conn->query(); + + string val = resolveVars(par2, sess, sc_sess, event_params); + + string dstvar = par1; + if (dstvar.size() && dstvar[0] == '$') { + dstvar = dstvar.substr(1); + } + string res; + query.escape_string(&res, val.c_str(), val.length()); + sc_sess->var[dstvar] = res; + DBG("escaped: $%s = escape(%s) = %s\n", + dstvar.c_str(), val.c_str(), res.c_str()); +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_mysql/ModMysql.h b/apps/dsm/mods/mod_mysql/ModMysql.h index 1c7e5d7e..7bf13fbb 100644 --- a/apps/dsm/mods/mod_mysql/ModMysql.h +++ b/apps/dsm/mods/mod_mysql/ModMysql.h @@ -53,7 +53,7 @@ class SCMysqlModule class DSMMyConnection : public mysqlpp::Connection, - public ArgObject, + public AmObject, public DSMDisposable { public: @@ -65,7 +65,7 @@ class DSMMyConnection class DSMMyStoreQueryResult : public mysqlpp::StoreQueryResult, - public ArgObject, + public AmObject, public DSMDisposable { public: @@ -88,7 +88,10 @@ DEF_SCCondition(MyConnectedCondition); DEF_ACTION_1P(SCMySaveResultAction); DEF_ACTION_1P(SCMyUseResultAction); DEF_ACTION_2P(SCMyPlayDBAudioAction); +DEF_ACTION_2P(SCMyPlayDBAudioFrontAction); +DEF_ACTION_2P(SCMyPlayDBAudioLoopedAction); DEF_ACTION_2P(SCMyGetFileFromDBAction); DEF_ACTION_2P(SCMyPutFileToDBAction); +DEF_ACTION_2P(SCMyEscapeAction); #endif diff --git a/apps/dsm/mods/mod_py/ModPy.cpp b/apps/dsm/mods/mod_py/ModPy.cpp index 4ded52a6..fc888cfd 100644 --- a/apps/dsm/mods/mod_py/ModPy.cpp +++ b/apps/dsm/mods/mod_py/ModPy.cpp @@ -98,7 +98,7 @@ int SCPyModule::preload() { MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { if (NULL==dsm_module) { - ERROR("mod_py must be preloaded! add preload=mod_py to dsm.conf\n"); + ERROR("mod_py must be preloaded! add preload_mods=mod_py to dsm.conf\n"); return NULL; } @@ -150,7 +150,7 @@ SCPyDictArg::~SCPyDictArg() { PyObject* getPyLocals(DSMSession* sc_sess) { map::iterator l_it; SCPyDictArg* py_arg = NULL; - ArgObject* py_locals_obj; + AmObject* py_locals_obj; if (((l_it=sc_sess->avar.find("py_locals")) != sc_sess->avar.end()) && (l_it->second.getType() == AmArg::AObject) && diff --git a/apps/dsm/mods/mod_py/ModPy.h b/apps/dsm/mods/mod_py/ModPy.h index d791aea5..5ae096b1 100644 --- a/apps/dsm/mods/mod_py/ModPy.h +++ b/apps/dsm/mods/mod_py/ModPy.h @@ -51,9 +51,9 @@ class SCPyModule static PyThreadState* tstate; }; -/** smart ArgObject that "owns" a python dictionary reference */ +/** smart AmObject that "owns" a python dictionary reference */ struct SCPyDictArg - : public ArgObject, + : public AmObject, public DSMDisposable { SCPyDictArg(); diff --git a/apps/dsm/mods/mod_py/PyDSMSession.cpp b/apps/dsm/mods/mod_py/PyDSMSession.cpp index bcbef54d..172d1dfb 100644 --- a/apps/dsm/mods/mod_py/PyDSMSession.cpp +++ b/apps/dsm/mods/mod_py/PyDSMSession.cpp @@ -95,17 +95,17 @@ extern "C" { if (!strcmp("local_tag", varname)) res = sc_sess->getLocalTag(); else if (!strcmp("user", varname)) - res = sc_sess->dlg.user; + res = sc_sess->dlg->getUser(); else if (!strcmp("domain", varname)) - res = sc_sess->dlg.domain; + res = sc_sess->dlg->getDomain(); else if (!strcmp("remote_tag", varname)) res = sc_sess->getRemoteTag(); else if (!strcmp("callid", varname)) res = sc_sess->getCallID(); else if (!strcmp("local_uri", varname)) - res = sc_sess->dlg.local_uri; + res = sc_sess->dlg->getLocalUri(); else if (!strcmp("remote_uri", varname)) - res = sc_sess->dlg.remote_uri; + res = sc_sess->dlg->getRemoteUri(); DBG("returning '%s'\n", res.c_str()); @@ -218,17 +218,12 @@ extern "C" { return Py_None; } - static PyObject* closePlaylist(PyObject*, PyObject* args) + static PyObject* flushPlaylist(PyObject*, PyObject* args) { - int notify = 0; - - if(!PyArg_ParseTuple(args,"i",¬ify)) - return NULL; - GET_SESS_PTR; - DBG("playFile(notify=%s)\n", notify?"true":"false"); - sess->closePlaylist(notify); + DBG("flushPlaylist()\n"); + sess->flushPlaylist(); Py_INCREF(Py_None); return Py_None; } @@ -347,7 +342,7 @@ extern "C" { {"getRecordLength", (PyCFunction)getRecordLength, METH_NOARGS,"get the length of the current recording"}, {"getRecordDataSize",(PyCFunction)getRecordDataSize, METH_NOARGS,"get the data size of the current recording"}, {"stopRecord", (PyCFunction)stopRecord, METH_NOARGS,"stop the running recording"}, - {"closePlaylist", (PyCFunction)closePlaylist, METH_VARARGS,"close the playlist"}, + {"flushPlaylist", (PyCFunction)flushPlaylist, METH_NOARGS,"flush the playlist"}, {"setPromptSet", (PyCFunction)setPromptSet, METH_VARARGS,"set prompt set"}, {"addSeparator", (PyCFunction)addSeparator, METH_VARARGS,"add a named separator to playlist"}, {"connectMedia", (PyCFunction)connectMedia, METH_NOARGS,"connect media (RTP processing)"}, diff --git a/apps/dsm/mods/mod_regex/CMakeLists.txt b/apps/dsm/mods/mod_regex/CMakeLists.txt new file mode 100644 index 00000000..007a582e --- /dev/null +++ b/apps/dsm/mods/mod_regex/CMakeLists.txt @@ -0,0 +1,7 @@ +set (mod_regex_SRCS +ModRegex.cpp +) + +SET(sems_dsm_module_name mod_regex) +INCLUDE(${CMAKE_SOURCE_DIR}/cmake/dsm.lib.rules.txt) + diff --git a/apps/dsm/mods/mod_regex/Makefile b/apps/dsm/mods/mod_regex/Makefile new file mode 100644 index 00000000..8cd44a40 --- /dev/null +++ b/apps/dsm/mods/mod_regex/Makefile @@ -0,0 +1,10 @@ +plug_in_name = mod_regex + +DSMPATH ?= ../.. + +module_ldflags = +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_regex/ModRegex.cpp b/apps/dsm/mods/mod_regex/ModRegex.cpp new file mode 100644 index 00000000..e0a3b7a2 --- /dev/null +++ b/apps/dsm/mods/mod_regex/ModRegex.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2012 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ + +#include "ModRegex.h" + +#include "log.h" +#include "AmUtils.h" +#include "AmConfigReader.h" + +SC_EXPORT(MOD_CLS_NAME); + +map MOD_CLS_NAME::regexes; + +MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { + + DEF_CMD("regex.compile", SCCompileRegexAction); + DEF_CMD("regex.match", SCExecRegexAction); + DEF_CMD("regex.clear", SCClearRegexAction); +} MOD_ACTIONEXPORT_END; + +MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) { + + if (cmd == "regex.match") { + return new SCExecRegexCondition(params, false); + } + +} MOD_CONDITIONEXPORT_END; + +int MOD_CLS_NAME::preload() { + AmConfigReader cfg; + if(cfg.loadPluginConf(MOD_NAME)) { + INFO("no module configuration for '%s' found, not preloading regular expressions\n", + MOD_NAME); + return 0; + } + + bool failed = false; + for (std::map::const_iterator it = + cfg.begin(); it != cfg.end(); it++) { + if (add_regex(it->first, it->second)) { + ERROR("compiling regex '%s' for '%s'\n", + it->second.c_str(), it->first.c_str()); + failed = true; + } else { + DBG("compiled regex '%s' as '%s'\n", it->second.c_str(), it->first.c_str()); + } + } + + return failed? -1 : 0; +} + +int MOD_CLS_NAME::add_regex(const string& r_name, const string& r_reg) { + if (regexes[r_name].regcomp(r_reg.c_str(), REG_NOSUB | REG_EXTENDED)) { + ERROR("compiling '%s' for regex '%s'\n", r_reg.c_str(), r_name.c_str()); + regexes.erase(r_name); + return -1; + } + return 0; +} + + +CONST_CONDITION_2P(SCExecRegexCondition, ',', false); +MATCH_CONDITION_START(SCExecRegexCondition) { + DBG("checking condition '%s' '%s'\n", par1.c_str(), par2.c_str()); + + string rname = resolveVars(par1, sess, sc_sess, event_params); + string val = resolveVars(par2, sess, sc_sess, event_params); + + DBG("matching '%s' on regex '%s'\n", val.c_str(), rname.c_str()); + map::iterator it=MOD_CLS_NAME::regexes.find(rname); + if (it == MOD_CLS_NAME::regexes.end()) { + ERROR("regex '%s' not found for matching '%s'\n", rname.c_str(), val.c_str()); + return false; + } + + int res = it->second.regexec(val.c_str(), 1, NULL, 0); + // res==0 -> match + DBG("regex did %smatch\n", res==0?"":"not "); + if (inv) { + return res != 0; + } else { + return res == 0; + } + } MATCH_CONDITION_END; + + +CONST_ACTION_2P(SCCompileRegexAction, ',', false); +EXEC_ACTION_START(SCCompileRegexAction) { + string rname = resolveVars(par1, sess, sc_sess, event_params); + string rval = par2; //resolveVars(par2, sess, sc_sess, event_params); + DBG("compiling '%s' for regex '%s'\n", rval.c_str(), rname.c_str()); + + if (MOD_CLS_NAME::add_regex(rname, rval)) { + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + ERROR("compiling '%s' for regex '%s'\n", rval.c_str(), rname.c_str()); + } +} EXEC_ACTION_END; + +CONST_ACTION_2P(SCExecRegexAction, ',', false); +EXEC_ACTION_START(SCExecRegexAction) { + string rname = resolveVars(par1, sess, sc_sess, event_params); + string val = resolveVars(par2, sess, sc_sess, event_params); + DBG("matching '%s' on regex '%s'\n", val.c_str(), rname.c_str()); + map::iterator it=MOD_CLS_NAME::regexes.find(rname); + if (it == MOD_CLS_NAME::regexes.end()) { + ERROR("regex '%s' not found for matching '%s'\n", rname.c_str(), val.c_str()); + EXEC_ACTION_STOP; + } + + int res = it->second.regexec(val.c_str(), 1, NULL, 0); + if (!res) { + // yeah side effects + sc_sess->var["regex.match"] = "1"; + } else { + sc_sess->var["regex.match"] = "0"; + } +} EXEC_ACTION_END; + +EXEC_ACTION_START(SCClearRegexAction) { + string r_name = resolveVars(arg, sess, sc_sess, event_params); + DBG("clearing regex '%s'\n", r_name.c_str()); + MOD_CLS_NAME::regexes.erase(r_name); +} EXEC_ACTION_END; + +TsRegex::TsRegex() + : i(false) { } + +TsRegex::~TsRegex() +{ + if (i) { + regfree(®); + } +} + +int TsRegex::regcomp(const char *regex, int cflags) { + m.lock(); + if (i) { + regfree(®); + } + int res = ::regcomp(®, regex, cflags); + if (!res) + i=true; + m.unlock(); + return res; +} + +int TsRegex::regexec(const char *_string, size_t nmatch, + regmatch_t pmatch[], int eflags) { + if (!i) { + ERROR("uninitialized regex when matching '%s'\n", _string); + return -1; + } + m.lock(); + int res = ::regexec(®, _string, nmatch, pmatch, eflags); + m.unlock(); + return res; +} diff --git a/apps/dsm/mods/mod_regex/ModRegex.h b/apps/dsm/mods/mod_regex/ModRegex.h new file mode 100644 index 00000000..f553389c --- /dev/null +++ b/apps/dsm/mods/mod_regex/ModRegex.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#ifndef _MOD_REGEX_H +#define _MOD_REGEX_H +#include "DSMModule.h" +#include "DSMSession.h" + +#define MOD_CLS_NAME SCRegexModule + +#include +#include +#include + +// in some earlier versions of libc apparently regex were inot thread-safe +class TsRegex { + regex_t reg; + AmMutex m; + bool i; + + public: + TsRegex(); + ~TsRegex(); + int regcomp(const char *regex, int cflags); + int regexec(const char *_string, size_t nmatch, regmatch_t pmatch[], int eflags); + +}; + +DECLARE_MODULE_BEGIN(MOD_CLS_NAME); +int preload(); +static map regexes; +static int add_regex(const string& r_name, const string& r_reg); +DECLARE_MODULE_END; + /* }; */ + + +DEF_ACTION_2P(SCCompileRegexAction); +DEF_ACTION_2P(SCExecRegexAction); +DEF_ACTION_1P(SCClearRegexAction); +DEF_CONDITION_2P(SCExecRegexCondition); + +#endif diff --git a/apps/dsm/mods/mod_regex/etc/mod_regex.conf b/apps/dsm/mods/mod_regex/etc/mod_regex.conf new file mode 100644 index 00000000..91503daa --- /dev/null +++ b/apps/dsm/mods/mod_regex/etc/mod_regex.conf @@ -0,0 +1,6 @@ +# list of regex's to compile on start +# use preload_mods=mod_regex in dsm.conf if you want to use those + +# ex: +#my_r1=^sip: +#my_r2=mydomain\.net \ No newline at end of file diff --git a/apps/dsm/mods/mod_sbc/Makefile b/apps/dsm/mods/mod_sbc/Makefile new file mode 100644 index 00000000..92da4e24 --- /dev/null +++ b/apps/dsm/mods/mod_sbc/Makefile @@ -0,0 +1,12 @@ +plug_in_name = mod_sbc + +DSMPATH ?= ../.. +SBCPATH ?= $(DSMPATH)/../sbc + +module_ldflags = +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) \ + -I$(SBCPATH) -I$(SBCPATH)/call_control/dsm + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_sbc/ModSbc.cpp b/apps/dsm/mods/mod_sbc/ModSbc.cpp new file mode 100644 index 00000000..a2f3a9b0 --- /dev/null +++ b/apps/dsm/mods/mod_sbc/ModSbc.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2013 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#include "ModSbc.h" +#include "log.h" +#include "AmUtils.h" + +#include "DSMCoreModule.h" + +#include "SBCCallProfile.h" +#include "SBCDSMParams.h" +#include "SBCCallLeg.h" + +SC_EXPORT(MOD_CLS_NAME); + +int MOD_CLS_NAME::preload() { + DBG("initializing mod_sbc...\n"); + return 0; +} + +MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { + DEF_CMD("sbc.profileSet", MODSBCActionProfileSet); + + DEF_CMD("sbc.stopCall", MODSBCActionStopCall); + DEF_CMD("sbc.disconnect", MODSBCActionDisconnect); + DEF_CMD("sbc.putOnHold", MODSBCActionPutOnHold); + DEF_CMD("sbc.resumeHeld", MODSBCActionResumeHeld); + DEF_CMD("sbc.sendDisconnectEvent", MODSBCActionSendDisconnectEvent); + + DEF_CMD("sbc.getCallStatus", MODSBCActionGetCallStatus); + DEF_CMD("sbc.relayReliableEvent", MODSBCActionB2BRelayReliable); + + DEF_CMD("sbc.addCallee", MODSBCActionAddCallee); + + DEF_CMD("sbc.enableRelayDTMFReceiving", MODSBCEnableRelayDTMFReceiving); + DEF_CMD("sbc.addToMediaProcessor", MODSBCAddToMediaProcessor); + DEF_CMD("sbc.removeFromMediaProcessor", MODSBCRemoveFromMediaProcessor); + + DEF_CMD("sbc.streamsSetReceiving", MODSBCRtpStreamsSetReceiving); + +} MOD_ACTIONEXPORT_END; + +MOD_CONDITIONEXPORT_BEGIN(MOD_CLS_NAME) { + if (cmd == "legStateChange") + return new TestDSMCondition(params, DSMCondition::LegStateChange); + + if (cmd == "bLegRefused") + return new TestDSMCondition(params, DSMCondition::BLegRefused); + + // HOLD related + if (cmd == "PutOnHold") + return new TestDSMCondition(params, DSMCondition::PutOnHold); + + if (cmd == "ResumeHeld") + return new TestDSMCondition(params, DSMCondition::ResumeHeld); + + if (cmd == "CreateHoldRequest") + return new TestDSMCondition(params, DSMCondition::CreateHoldRequest); + + if (cmd == "HandleHoldReply") + return new TestDSMCondition(params, DSMCondition::HandleHoldReply); + + // simple relay related + if (cmd == "RelayInit") + return new TestDSMCondition(params, DSMCondition::RelayInit); + + if (cmd == "RelayInitUAC") + return new TestDSMCondition(params, DSMCondition::RelayInitUAC); + + if (cmd == "RelayInitUAS") + return new TestDSMCondition(params, DSMCondition::RelayInitUAS); + + if (cmd == "RelayFinalize") + return new TestDSMCondition(params, DSMCondition::RelayFinalize); + + if (cmd == "RelayOnSipRequest") + return new TestDSMCondition(params, DSMCondition::RelayOnSipRequest); + + if (cmd == "RelayOnSipReply") + return new TestDSMCondition(params, DSMCondition::RelayOnSipReply); + + if (cmd == "RelayOnB2BRequest") + return new TestDSMCondition(params, DSMCondition::RelayOnB2BRequest); + + if (cmd == "RelayOnB2BReply") + return new TestDSMCondition(params, DSMCondition::RelayOnB2BReply); + + if (cmd == "RelayOnB2BReply") + return new TestDSMCondition(params, DSMCondition::RelayOnB2BReply); + + if (cmd == "sbc.isALeg") + return new SBCIsALegCondition(params, false); + + if (cmd == "sbc.isOnHold") + return new SBCIsOnHoldCondition(params, false); + + if (cmd == "sbc.isDisconnected") + return new SBCIsDisconnectedCondition(params, false); + + if (cmd == "sbc.isNoReply") + return new SBCIsNoReplyCondition(params, false); + + if (cmd == "sbc.isRinging") + return new SBCIsRingingCondition(params, false); + + if (cmd == "sbc.isConnected") + return new SBCIsConnectedCondition(params, false); + + if (cmd == "sbc.isDisconnecting") + return new SBCIsDisconnectingCondition(params, false); + + +} MOD_CONDITIONEXPORT_END + + +MATCH_CONDITION_START(SBCIsALegCondition) { + SBCCallLeg* call_leg = dynamic_cast(sess); + if (NULL == call_leg) { + DBG("script writer error: DSM condition sbc.isALeg" + " used without call leg\n"); + return false; + } + + bool b = call_leg->isALeg(); + bool res = inv ^ b; + DBG("SBC: isALeg() == %s (res = %s)\n", + b ? "true":"false", res ? "true":"false"); + return res; +} MATCH_CONDITION_END; + +MATCH_CONDITION_START(SBCIsOnHoldCondition) { + SBCCallLeg* call_leg = dynamic_cast(sess); + if (NULL == call_leg) { + DBG("script writer error: DSM condition sbc.isOnHold" + " used without call leg\n"); + return false; + } + + bool b = call_leg->isOnHold(); + bool res = inv ^ b; + DBG("SBC: isOnHold() == %s (res = %s)\n", + b ? "true":"false", res ? "true":"false"); + return res; +} MATCH_CONDITION_END; + +#define DEF_CALLSTATUS_COND(cond_name, cond_desc, call_status) \ + \ + MATCH_CONDITION_START(cond_name) { \ + SBCCallLeg* call_leg = dynamic_cast(sess); \ + if (NULL == call_leg) { \ + DBG("script writer error: DSM condition used without call leg\n"); \ + return false; \ + } \ + \ + bool b = call_leg->getCallStatus() == call_status; \ + bool res = inv ^ b; \ + DBG("SBC: " cond_desc " == %s (res = %s)\n", \ + b ? "true":"false", res ? "true":"false"); \ + return res; \ + } MATCH_CONDITION_END + + +DEF_CALLSTATUS_COND(SBCIsDisconnectedCondition, "sbc.isDisconnected", CallLeg::Disconnected); +DEF_CALLSTATUS_COND(SBCIsNoReplyCondition, "sbc.isNoReply", CallLeg::NoReply); +DEF_CALLSTATUS_COND(SBCIsRingingCondition, "sbc.isRinging", CallLeg::Ringing); +DEF_CALLSTATUS_COND(SBCIsConnectedCondition, "sbc.isConnected", CallLeg::Connected); +DEF_CALLSTATUS_COND(SBCIsDisconnectingCondition, "sbc.isDisconnecting", CallLeg::Disconnecting); + +#define ACTION_GET_PROFILE \ + SBCCallProfile* profile = NULL; \ + AVarMapT::iterator it = sc_sess->avar.find(DSM_SBC_AVAR_PROFILE); \ + if (it != sc_sess->avar.end()) { \ + profile = dynamic_cast(it->second.asObject()); \ + } \ + if (NULL == profile) { \ + SBCCallLeg* call = dynamic_cast(sess); \ + if (NULL != call) \ + profile = &call->getCallProfile(); \ + } \ + \ + if (NULL == profile) { \ + ERROR("internal: Call profile object not found\n"); \ + EXEC_ACTION_STOP; \ + } + +CONST_ACTION_2P(MODSBCActionProfileSet, ',', false); +EXEC_ACTION_START(MODSBCActionProfileSet) { + string profile_param = resolveVars(par1, sess, sc_sess, event_params); + string value = resolveVars(par2, sess, sc_sess, event_params); + + ACTION_GET_PROFILE; + +#define SET_TO_CALL_PROFILE(cfgparam, member) \ + if (profile_param == cfgparam) { \ + profile->member=value; \ + DBG(cfgparam " set to '%s'\n", value.c_str()); \ + EXEC_ACTION_STOP; \ + } + +#define SET_TO_CALL_PROFILE_OPTION(cfgparam, member) \ + if (profile_param == cfgparam) { \ + profile->member=(value == "true"); \ + DBG(cfgparam " set to '%s'\n", profile->member?"true":"false"); \ + EXEC_ACTION_STOP; \ + } + + switch (profile_param.length()) { + case 2: + SET_TO_CALL_PROFILE("To", to); + break; + + case 4: + SET_TO_CALL_PROFILE("RURI", ruri); + SET_TO_CALL_PROFILE("From", from); + break; + + + case 7: + SET_TO_CALL_PROFILE("Call-ID", callid); + break; + + case 8: + SET_TO_CALL_PROFILE("next_hop", next_hop); + break; + + case 9: + SET_TO_CALL_PROFILE("RURI_host", ruri_host); + break; + + + case 11: + SET_TO_CALL_PROFILE("refuse_with", refuse_with); + break; + + default: + + SET_TO_CALL_PROFILE("outbound_proxy", outbound_proxy); + SET_TO_CALL_PROFILE_OPTION("force_outbound_proxy", force_outbound_proxy); + + SET_TO_CALL_PROFILE("aleg_outbound_proxy", aleg_outbound_proxy); + SET_TO_CALL_PROFILE_OPTION("aleg_force_outbound_proxy", aleg_force_outbound_proxy); + + SET_TO_CALL_PROFILE_OPTION("next_hop_1st_req", next_hop_1st_req); + SET_TO_CALL_PROFILE_OPTION("patch_ruri_next_hop", patch_ruri_next_hop); + + SET_TO_CALL_PROFILE("aleg_next_hop", aleg_next_hop); + + // TODO: message_filter + // TODO: header_filter + // TODO: sdp_filter + + // TODO: auth + // TODO: aleg_auth + + SET_TO_CALL_PROFILE("append_headers", append_headers); + SET_TO_CALL_PROFILE("append_headers_req", append_headers_req); + SET_TO_CALL_PROFILE("aleg_append_headers_req", aleg_append_headers_req); + + SET_TO_CALL_PROFILE("rtprelay_enabled", rtprelay_enabled); + + SET_TO_CALL_PROFILE_OPTION("force_symmetric_rtp", force_symmetric_rtp_value); + SET_TO_CALL_PROFILE_OPTION("aleg_force_symmetric_rtp", aleg_force_symmetric_rtp_value); + SET_TO_CALL_PROFILE_OPTION("msgflags_symmetric_rtp", msgflags_symmetric_rtp); + SET_TO_CALL_PROFILE_OPTION("rtprelay_transparent_seqno", rtprelay_transparent_seqno); + SET_TO_CALL_PROFILE_OPTION("rtprelay_transparent_ssrc", rtprelay_transparent_ssrc); + + SET_TO_CALL_PROFILE_OPTION("rtprelay_dtmf_filtering", rtprelay_dtmf_filtering); + SET_TO_CALL_PROFILE_OPTION("rtprelay_dtmf_detection", rtprelay_dtmf_detection); + + if (profile_param == "rtprelay_interface") { + profile->rtprelay_interface=value; + if (!profile->evaluateRTPRelayInterface()) { + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("rtprelay_interface '"+value+"' not present"); + } else { + DBG("rtprelay_interface set to '%s'\n", value.c_str()); + } + EXEC_ACTION_STOP; + } + + if (profile_param == "aleg_rtprelay_interface") { + profile->aleg_rtprelay_interface=value; + if (!profile->evaluateRTPRelayAlegInterface()) { + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("aleg_rtprelay_interface '"+value+"' not present"); + } else { + DBG("aleg_rtprelay_interface set to '%s'\n", value.c_str()); + } + EXEC_ACTION_STOP; + } + + } + + // TODO: Transcoder Settings + // TODO: CODEC Prefs + + // TODO: contact hiding + // TODO: reg_caching + + DBG("script writer: Call profile property '%s' not known\n", profile_param.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("Call profile property '"+profile_param+"' not known"); +} EXEC_ACTION_END; + + +#define GET_CALL_LEG(action) \ + CallLeg* call_leg = dynamic_cast(sess); \ + if (NULL == call_leg) { \ + DBG("script writer error: DSM action " #action \ + " used without call leg\n"); \ + throw DSMException("sbc", "type", "param", "cause", \ + "script writer error: DSM action " #action \ + " used without call leg"); \ + } + +#define GET_SBC_CALL_LEG(action) \ + SBCCallLeg* sbc_call_leg = dynamic_cast(sess); \ + if (NULL == sbc_call_leg) { \ + DBG("script writer error: DSM action " #action \ + " used without sbc call leg\n"); \ + throw DSMException("sbc", "type", "param", "cause", \ + "script writer error: DSM action " #action \ + " used without sbc call leg"); \ + } + +#define GET_B2B_MEDIA \ + AmB2BMedia* b2b_media = sbc_call_leg->getMediaSession(); \ + DBG("session: %p, media: %p\n", sbc_call_leg, b2b_media); \ + if (NULL == b2b_media) { \ + DBG("No B2BMedia in current SBC call leg, sorry\n"); \ + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); \ + sc_sess->SET_STRERROR("No B2BMedia in current SBC call leg, sorry"); \ + EXEC_ACTION_STOP; \ + } + +EXEC_ACTION_START(MODSBCActionStopCall) { + GET_CALL_LEG(StopCall); + string cause = resolveVars(arg, sess, sc_sess, event_params); + call_leg->stopCall(cause.c_str()); +} EXEC_ACTION_END; + +CONST_ACTION_2P(MODSBCActionDisconnect, ',', true); +EXEC_ACTION_START(MODSBCActionDisconnect) { + GET_CALL_LEG(Disconnect); + string hold_remote = resolveVars(par1, sess, sc_sess, event_params); + string preserve_media_session = resolveVars(par2, sess, sc_sess, event_params); + call_leg->disconnect(hold_remote == DSM_TRUE, preserve_media_session == DSM_TRUE); +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODSBCActionSendDisconnectEvent) { + GET_CALL_LEG(SendDisconnectEvent); + string hold_remote = resolveVars(arg, sess, sc_sess, event_params); + if (!AmSessionContainer::instance()->postEvent(call_leg->getLocalTag(), + new DisconnectLegEvent(hold_remote == DSM_TRUE))) { + ERROR("couldn't self-post event\n"); + } +} EXEC_ACTION_END; + + +EXEC_ACTION_START(MODSBCActionPutOnHold) { + GET_CALL_LEG(PutOnHold); + call_leg->putOnHold(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODSBCActionResumeHeld) { + GET_CALL_LEG(ResumeHeld); + call_leg->resumeHeld(); +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODSBCActionGetCallStatus) { + GET_CALL_LEG(GetCallStatus); + string varname = arg; + if (varname.size() && varname[0] == '$') + varname.erase(0, 1); + sc_sess->var[varname] = call_leg->getCallStatusStr(); + DBG("set $%s='%s'\n", varname.c_str(), sc_sess->var[varname].c_str()); +} EXEC_ACTION_END; + +void setReliableEventParameters(const DSMSession* sc_sess, const string& var, VarMapT& params) { + vector vars = explode(var, ";"); + for (vector::iterator it = vars.begin(); it != vars.end(); it++) { + string varname = *it; + + if (varname.length() && varname[varname.length()-1]=='.') { + DBG("adding postEvent param %s (struct)\n", varname.c_str()); + + map::const_iterator lb = sc_sess->var.lower_bound(varname); + while (lb != sc_sess->var.end()) { + if ((lb->first.length() < varname.length()) || + strncmp(lb->first.c_str(), varname.c_str(), varname.length())) + break; + params[lb->first] = lb->second; + lb++; + } + } else { + VarMapT::const_iterator v_it = sc_sess->var.find(varname); + if (v_it != sc_sess->var.end()) { + DBG("adding reliableEvent param %s=%s\n", + it->c_str(), v_it->second.c_str()); + params[varname] = v_it->second; + } + } + } +} + +CONST_ACTION_2P(MODSBCActionB2BRelayReliable, ',', false); +EXEC_ACTION_START(MODSBCActionB2BRelayReliable) { + GET_CALL_LEG(B2BRelayReliable); + string ev_params = par1; + vector success_params = explode(par2, ","); // q&d... + B2BEvent* processed = new B2BEvent(E_B2B_APP, B2BEvent::B2BApplication); + if (success_params.size()) { + setReliableEventParameters(sc_sess, trim(success_params[0], " "), processed->params); + } + B2BEvent* unprocessed = new B2BEvent(E_B2B_APP, B2BEvent::B2BApplication); + if (success_params.size() > 1) { + DBG("p='%s'\n", success_params[1].c_str()); + setReliableEventParameters(sc_sess, trim(success_params[1], " "), unprocessed->params); + } + + ReliableB2BEvent* rel_b2b_ev = new ReliableB2BEvent(E_B2B_APP, B2BEvent::B2BApplication, processed, unprocessed); + setReliableEventParameters(sc_sess, ev_params, rel_b2b_ev->params); + + rel_b2b_ev->setSender(call_leg->getLocalTag()); + call_leg->relayEvent(rel_b2b_ev); +} EXEC_ACTION_END; + +CONST_ACTION_2P(MODSBCActionAddCallee, ',', false); +EXEC_ACTION_START(MODSBCActionAddCallee) { + GET_SBC_CALL_LEG(sbc.addCallee); + + string mode = resolveVars(par1, sess, sc_sess, event_params); + string varname = par2; + + if (mode == DSM_SBC_PARAM_ADDCALLEE_MODE_STR) { + string hdrs; + + VarMapT::iterator it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_TRANSPARENT_DLG_ID); + if (it != sc_sess->var.end()) { + sbc_call_leg->getCallProfile().transparent_dlg_id = it->second == DSM_TRUE; + } else { + // default to false + sbc_call_leg->getCallProfile().transparent_dlg_id = false; + } + + DBG("Using %stransparent dialog IDs for new call leg\n", + sbc_call_leg->getCallProfile().transparent_dlg_id ? "":"non-"); + + SBCCallLeg* peer = new SBCCallLeg(sbc_call_leg); + + SBCCallProfile &p = peer->getCallProfile(); + AmB2BSession::RTPRelayMode rtp_mode = sbc_call_leg->getRtpRelayMode(); + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_LOCAL_PARTY); + if (it != sc_sess->var.end()) + peer->setLocalParty(it->second, it->second); + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_REMOTE_PARTY); + if (it != sc_sess->var.end()) + peer->setRemoteParty(it->second, it->second); + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_HDRS); + if (it != sc_sess->var.end()) + hdrs = it->second; + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_OUTBOUND_PROXY); + if (it != sc_sess->var.end()) + p.outbound_proxy = it->second; + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP); + if (it != sc_sess->var.end()) + p.next_hop = it->second; + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP_1ST_REQ); + if (it != sc_sess->var.end()) + p.next_hop_1st_req = (it->second == DSM_TRUE); + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP_PATCH_RURI); + if (it != sc_sess->var.end()) + p.patch_ruri_next_hop = (it->second == DSM_TRUE); + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_NEXT_HOP_FIXED); + if (it != sc_sess->var.end()) + p.next_hop_fixed = (it->second == DSM_TRUE); + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_OUTBOUND_INTERFACE); + if (it != sc_sess->var.end()) { + p.outbound_interface = it->second; + p.evaluateOutboundInterface(); + } + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_RTP_INTERFACE); + if (it != sc_sess->var.end()) { + p.rtprelay_interface = it->second; + p.evaluateRTPRelayInterface(); + } + + sbc_call_leg->addCallee(peer, hdrs); + } else if (mode == DSM_SBC_PARAM_ADDCALLEE_MODE_LTAG) { + string ltag; + string hdrs; + + VarMapT::iterator it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_LTAG); + if (it != sc_sess->var.end()) + ltag = it->second; + + it = sc_sess->var.find(varname+"." DSM_SBC_PARAM_ADDCALLEE_HDRS); + if (it != sc_sess->var.end()) + hdrs = it->second; + + sbc_call_leg->addCallee(ltag, hdrs); + } + +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODSBCEnableRelayDTMFReceiving) { + bool enable = (resolveVars(arg, sess, sc_sess, event_params)==DSM_TRUE); + + GET_SBC_CALL_LEG(AddCallee); + GET_B2B_MEDIA; + + b2b_media->setRelayDTMFReceiving(enable); +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODSBCAddToMediaProcessor) { + GET_CALL_LEG(AddToMediaProcessor); + AmMediaProcessor::instance()->addSession(call_leg, call_leg->getCallgroup()); + +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODSBCRemoveFromMediaProcessor) { + GET_CALL_LEG(RemoveFromMediaProcessor); + AmMediaProcessor::instance()->removeSession(call_leg); +} EXEC_ACTION_END; + +CONST_ACTION_2P(MODSBCRtpStreamsSetReceiving, ',', false); +EXEC_ACTION_START(MODSBCRtpStreamsSetReceiving) { + bool p_a = (resolveVars(par1, sess, sc_sess, event_params)==DSM_TRUE); + bool p_b = (resolveVars(par2, sess, sc_sess, event_params)==DSM_TRUE); + + GET_SBC_CALL_LEG(RtpStreamsSetReceiving); + GET_B2B_MEDIA; + + b2b_media->setReceiving(p_a, p_b); +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_sbc/ModSbc.h b/apps/dsm/mods/mod_sbc/ModSbc.h new file mode 100644 index 00000000..f17bec5a --- /dev/null +++ b/apps/dsm/mods/mod_sbc/ModSbc.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 Stefan Sayer + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#ifndef _MOD_SBC_H +#define _MOD_SBC_H +#include "DSMModule.h" +#include "DSMSession.h" + +#define MOD_CLS_NAME SCSBCModule + +DECLARE_MODULE_BEGIN(MOD_CLS_NAME); +int preload(); +DECLARE_MODULE_END; + +DEF_ACTION_2P(MODSBCActionProfileSet); +DEF_ACTION_1P(MODSBCActionStopCall); +DEF_ACTION_2P(MODSBCActionDisconnect); +DEF_ACTION_1P(MODSBCActionSendDisconnectEvent); + +DEF_ACTION_1P(MODSBCActionPutOnHold); +DEF_ACTION_1P(MODSBCActionResumeHeld); +DEF_ACTION_1P(MODSBCActionGetCallStatus); + +DEF_SCCondition(SBCIsALegCondition); +DEF_SCCondition(SBCIsOnHoldCondition); + +DEF_SCCondition(SBCIsDisconnectedCondition); +DEF_SCCondition(SBCIsNoReplyCondition); +DEF_SCCondition(SBCIsRingingCondition); +DEF_SCCondition(SBCIsConnectedCondition); +DEF_SCCondition(SBCIsDisconnectingCondition); + +DEF_ACTION_2P(MODSBCActionB2BRelayReliable); +DEF_ACTION_2P(MODSBCActionAddCallee); + +DEF_ACTION_1P(MODSBCEnableRelayDTMFReceiving); +DEF_ACTION_1P(MODSBCAddToMediaProcessor); +DEF_ACTION_1P(MODSBCRemoveFromMediaProcessor); + +DEF_ACTION_2P(MODSBCRtpStreamsSetReceiving); + +#endif diff --git a/apps/dsm/mods/mod_subscription/Makefile b/apps/dsm/mods/mod_subscription/Makefile new file mode 100644 index 00000000..9381e846 --- /dev/null +++ b/apps/dsm/mods/mod_subscription/Makefile @@ -0,0 +1,10 @@ +plug_in_name = mod_subscription + +DSMPATH ?= ../.. + +module_ldflags = +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_subscription/ModSubscription.cpp b/apps/dsm/mods/mod_subscription/ModSubscription.cpp new file mode 100644 index 00000000..4a05d2fc --- /dev/null +++ b/apps/dsm/mods/mod_subscription/ModSubscription.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 FRAFOS GmbH + * + * Development sponsored by Sipwise GmbH. + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#include "ModSubscription.h" +#include "log.h" +#include "AmUtils.h" + +#include "AmSipSubscriptionContainer.h" + +SC_EXPORT(MOD_CLS_NAME); + +MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { + + DEF_CMD("subscription.create", SIPSUBCreateAction); + DEF_CMD("subscription.refresh", SIPSUBRefreshAction); + DEF_CMD("subscription.remove", SIPSUBRemoveAction); + +} MOD_ACTIONEXPORT_END; + +MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); + +CONST_ACTION_2P(SIPSUBCreateAction, ',', true); +EXEC_ACTION_START(SIPSUBCreateAction) { + string params_s = resolveVars(par1, sess, sc_sess, event_params); +// string basedir = resolveVars(par2, sess, sc_sess, event_params); + + AmSipSubscriptionInfo info(sc_sess->var[params_s+".domain"], + sc_sess->var[params_s+".user"], + sc_sess->var[params_s+".from_user"], + sc_sess->var[params_s+".pwd"], + sc_sess->var[params_s+".proxy"], + sc_sess->var[params_s+".event"]); + info.accept = sc_sess->var[params_s+".accept"]; + info.id = sc_sess->var[params_s+".id"]; + unsigned int expires = 0; + + if (sc_sess->var.find(params_s+".expires") != sc_sess->var.end()) { + str2i(sc_sess->var[params_s+".expires"], expires); + } + + string handle = AmSipSubscriptionContainer::instance()-> + createSubscription(info, sess->getLocalTag(), expires); + + DBG("got handle '%s'\n", handle.c_str()); + sc_sess->var[params_s+".handle"] = handle; + if (handle.empty()) { + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("could not create subscription\n"); + } else { + sc_sess->CLR_ERRNO; + } +} EXEC_ACTION_END; + + + +CONST_ACTION_2P(SIPSUBRefreshAction, ',', true); +EXEC_ACTION_START(SIPSUBRefreshAction) { + string handle = resolveVars(par1, sess, sc_sess, event_params); + string expires = resolveVars(par2, sess, sc_sess, event_params); + unsigned int expires_i = 0; + if (!expires.empty()) + str2i(expires, expires_i); + + DBG("refreshing subscription with handle '%s'\n", handle.c_str()); + if (!AmSipSubscriptionContainer::instance()->refreshSubscription(handle, expires_i)) { + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + sc_sess->SET_STRERROR("could not refresh subscription\n"); + } else { + sc_sess->CLR_ERRNO; + } +} EXEC_ACTION_END; + +EXEC_ACTION_START(SIPSUBRemoveAction) { + string handle = resolveVars(arg, sess, sc_sess, event_params); + DBG("removing subscription with handle '%s'\n", handle.c_str()); + AmSipSubscriptionContainer::instance()->removeSubscription(handle); +} EXEC_ACTION_END; diff --git a/apps/registrar_client/ContactInfo.h b/apps/dsm/mods/mod_subscription/ModSubscription.h similarity index 70% rename from apps/registrar_client/ContactInfo.h rename to apps/dsm/mods/mod_subscription/ModSubscription.h index e4ecf5cb..4557c229 100644 --- a/apps/registrar_client/ContactInfo.h +++ b/apps/dsm/mods/mod_subscription/ModSubscription.h @@ -1,5 +1,7 @@ /* - * Copyright (C) 2006 iptego GmbH + * Copyright (C) 2012 FRAFOS GmbH + * + * Development sponsored by Sipwise GmbH. * * This file is part of SEMS, a free SIP media server. * @@ -24,27 +26,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _MOD_SUBSCRIPTION_H +#define _MOD_SUBSCRIPTION_H +#include "DSMModule.h" +#include "DSMSession.h" -#include -#include -using std::map; -using std::string; - -struct ContactInfo { - string display_name; - string uri; - - string uri_user; - string uri_host; - string uri_port; - string uri_param; +#define MOD_CLS_NAME SCSubscriptionModule - map params; +DECLARE_MODULE(MOD_CLS_NAME); - bool isEqual(const ContactInfo& c) const; - bool parse_contact(string& line, size_t pos, size_t& end); - bool parse_uri(); - bool parse_params(string& line, int& pos); - void dump(); - ContactInfo() { } -}; +DEF_ACTION_2P(SIPSUBCreateAction); +DEF_ACTION_2P(SIPSUBRefreshAction); +DEF_ACTION_1P(SIPSUBRemoveAction); +#endif diff --git a/apps/dsm/mods/mod_sys/ModSys.cpp b/apps/dsm/mods/mod_sys/ModSys.cpp index ea0b77f3..60b60821 100644 --- a/apps/dsm/mods/mod_sys/ModSys.cpp +++ b/apps/dsm/mods/mod_sys/ModSys.cpp @@ -101,13 +101,7 @@ EXEC_ACTION_START(SCMkDirAction) { bool sys_get_parent_dir(const char* path, char* parentPath) { - //size_t pos = strcspn(dirPath, "/\\"); -#ifdef __CORRECT_ISO_CPP_STRING_H_PROTO - const char* ptr; -#else - char* ptr; -#endif - ptr = strrchr(path, '/'); // search char from end reverse + const char* ptr = strrchr(path, '/'); // search char from end reverse if (ptr == NULL) { ptr = strrchr(path, '\\'); // search char from end reverse if (ptr == NULL) { @@ -127,14 +121,17 @@ bool sys_get_parent_dir(const char* path, char* parentPath) { bool sys_mkdir_recursive(const char* p) { if (!file_exists(p)) { - char parent_dir[strlen(p)+1]; + char* parent_dir = new char[strlen(p)+1]; bool has_parent = sys_get_parent_dir(p, parent_dir); if (has_parent) { bool parent_exists = sys_mkdir_recursive(parent_dir); if (parent_exists) { - return sys_mkdir(p); + bool ret = sys_mkdir(p); + delete [] parent_dir; + return ret; } } + delete [] parent_dir; return false; } return true; @@ -312,10 +309,10 @@ EXEC_ACTION_START(SCSysGetTimestampAction) { // long unsigned msecs = tv.tv_sec * 1000 + tv.tv_usec / 1000; char ms_buf[40]; - snprintf(ms_buf, 40, "%lu", tv.tv_sec); + snprintf(ms_buf, 40, "%li", tv.tv_sec); sc_sess->var[varname+".tv_sec"] = ms_buf; - snprintf(ms_buf, 40, "%lu", tv.tv_usec); + snprintf(ms_buf, 40, "%li", (long int)tv.tv_usec); sc_sess->var[varname+".tv_usec"] = ms_buf; DBG("got timestamp $%s=%s, $%s=%s, \n", @@ -343,10 +340,10 @@ EXEC_ACTION_START(SCSysSubTimestampAction) { timersub(&tv1,&tv2,&diff); char ms_buf[40]; - snprintf(ms_buf, 40, "%lu", diff.tv_sec); + snprintf(ms_buf, 40, "%li", diff.tv_sec); sc_sess->var[t1+".tv_sec"] = ms_buf; - snprintf(ms_buf, 40, "%lu", diff.tv_usec); + snprintf(ms_buf, 40, "%li", (long int)diff.tv_usec); sc_sess->var[t1+".tv_usec"] = ms_buf; // may be overflowing - use only if timestamps known diff --git a/apps/dsm/mods/mod_uri/ModUri.cpp b/apps/dsm/mods/mod_uri/ModUri.cpp index 29449281..05688da2 100644 --- a/apps/dsm/mods/mod_uri/ModUri.cpp +++ b/apps/dsm/mods/mod_uri/ModUri.cpp @@ -41,8 +41,12 @@ SC_EXPORT(MOD_CLS_NAME); MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { DEF_CMD("uri.parse", URIParseAction); + DEF_CMD("uri.parseNameaddr", URIParseNameaddrAction); DEF_CMD("uri.getHeader", URIGetHeaderAction); + DEF_CMD("uri.encode", URIEncodeAction); + DEF_CMD("uri.decode", URIDecodeAction); + } MOD_ACTIONEXPORT_END; MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); @@ -67,11 +71,41 @@ EXEC_ACTION_START(URIParseAction) { return false; } + sc_sess->var[prefix+"user"] = p.uri_user; + sc_sess->var[prefix+"host"] = p.uri_host; + sc_sess->var[prefix+"port"] = p.uri_port; + sc_sess->var[prefix+"headers"] = p.uri_headers; + sc_sess->var[prefix+"param"] = p.uri_param; + + sc_sess->CLR_ERRNO; +} EXEC_ACTION_END; + +CONST_ACTION_2P(URIParseNameaddrAction, ',', true); +EXEC_ACTION_START(URIParseNameaddrAction) { + + string uri = resolveVars(par1, sess, sc_sess, event_params); + string prefix = resolveVars(par2, sess, sc_sess, event_params); + + AmUriParser p; + if (!p.parse_nameaddr(uri)) { + DBG("parsing nameaddr '%s' failed\n", uri.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_GENERAL); + sc_sess->SET_STRERROR("parsing nameaddr '"+uri+"%s' failed"); + return false; + } + sc_sess->var[prefix+"display_name"] = p.display_name; sc_sess->var[prefix+"user"] = p.uri_user; sc_sess->var[prefix+"host"] = p.uri_host; + sc_sess->var[prefix+"port"] = p.uri_port; + sc_sess->var[prefix+"headers"] = p.uri_headers; sc_sess->var[prefix+"param"] = p.uri_param; + for (map::iterator it=p.params.begin(); it != p.params.end(); it++) { + sc_sess->var[prefix+"uri_param."+it->first] = it->second; + } + + sc_sess->CLR_ERRNO; } EXEC_ACTION_END; @@ -86,3 +120,28 @@ EXEC_ACTION_START(URIGetHeaderAction) { hname.c_str(), sc_sess->var[dstname].c_str(), dstname.c_str()); } EXEC_ACTION_END; + + +CONST_ACTION_2P(URIEncodeAction, '=', false); +EXEC_ACTION_START(URIEncodeAction) { + + string varname = par1; + if (varname.size() && varname[0]=='$') + varname.erase(0,1); + string str = resolveVars(par2, sess, sc_sess, event_params); + + sc_sess->var[varname] = URL_encode(str); + DBG("URL-encoded: $%s=\"%s\"\n", varname.c_str(), sc_sess->var[varname].c_str()); +} EXEC_ACTION_END; + +CONST_ACTION_2P(URIDecodeAction, '=', false); +EXEC_ACTION_START(URIDecodeAction) { + + string varname = par1; + if (varname.size() && varname[0]=='$') + varname.erase(0,1); + string str = resolveVars(par2, sess, sc_sess, event_params); + + sc_sess->var[varname] = URL_decode(str); + DBG("URL-decoded: $%s=\"%s\"\n", varname.c_str(), sc_sess->var[varname].c_str()); +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_uri/ModUri.h b/apps/dsm/mods/mod_uri/ModUri.h index a6f6d7ed..3e31ed71 100644 --- a/apps/dsm/mods/mod_uri/ModUri.h +++ b/apps/dsm/mods/mod_uri/ModUri.h @@ -35,6 +35,10 @@ bool onInvite(const AmSipRequest& req, DSMSession* sess); DECLARE_MODULE_END; DEF_ACTION_2P(URIParseAction); +DEF_ACTION_2P(URIParseNameaddrAction); DEF_ACTION_2P(URIGetHeaderAction); +DEF_ACTION_2P(URIEncodeAction); +DEF_ACTION_2P(URIDecodeAction); + #endif diff --git a/apps/dsm/mods/mod_utils/ModUtils.cpp b/apps/dsm/mods/mod_utils/ModUtils.cpp index 6c472d05..987ed797 100644 --- a/apps/dsm/mods/mod_utils/ModUtils.cpp +++ b/apps/dsm/mods/mod_utils/ModUtils.cpp @@ -49,6 +49,8 @@ MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { DEF_CMD("utils.sub", SCUSSubAction); DEF_CMD("utils.int", SCUIntAction); DEF_CMD("utils.splitStringCR", SCUSplitStringAction); + DEF_CMD("utils.escapeCRLF", SCUEscapeCRLFAction); + DEF_CMD("utils.unescapeCRLF", SCUUnescapeCRLFAction); DEF_CMD("utils.playRingTone", SCUPlayRingToneAction); } MOD_ACTIONEXPORT_END; @@ -242,7 +244,36 @@ EXEC_ACTION_START(SCUSplitStringAction) { } } EXEC_ACTION_END; +EXEC_ACTION_START(SCUEscapeCRLFAction) { + string varname = arg; + if (varname.length() && varname[0]=='$') + varname.erase(0,1); + while (true) { + size_t p = sc_sess->var[varname].find("\r\n"); + if (p==string::npos) + break; + sc_sess->var[varname].replace(p, 2, "\\r\\n"); + } + + DBG("escaped: $%s='%s'\n", varname.c_str(), sc_sess->var[varname].c_str()); +} EXEC_ACTION_END; + + +EXEC_ACTION_START(SCUUnescapeCRLFAction) { + string varname = arg; + if (varname.length() && varname[0]=='$') + varname.erase(0,1); + + while (true) { + size_t p = sc_sess->var[varname].find("\\r\\n"); + if (p==string::npos) + break; + sc_sess->var[varname].replace(p, 4, "\r\n"); + } + + DBG("unescaped: $%s='%s'\n", varname.c_str(), sc_sess->var[varname].c_str()); +} EXEC_ACTION_END; CONST_ACTION_2P(SCUPlayRingToneAction, ',', true); diff --git a/apps/dsm/mods/mod_utils/ModUtils.h b/apps/dsm/mods/mod_utils/ModUtils.h index 24311bf2..aaf89c7e 100644 --- a/apps/dsm/mods/mod_utils/ModUtils.h +++ b/apps/dsm/mods/mod_utils/ModUtils.h @@ -46,6 +46,9 @@ DEF_ACTION_2P(SCUSSubAction); DEF_ACTION_2P(SCUIntAction); DEF_ACTION_2P(SCUSplitStringAction); +DEF_ACTION_1P(SCUEscapeCRLFAction); +DEF_ACTION_1P(SCUUnescapeCRLFAction); + DEF_ACTION_2P(SCUPlayRingToneAction); diff --git a/apps/dsm/mods/mod_xml/Makefile b/apps/dsm/mods/mod_xml/Makefile new file mode 100644 index 00000000..308df766 --- /dev/null +++ b/apps/dsm/mods/mod_xml/Makefile @@ -0,0 +1,11 @@ +plug_in_name = mod_xml + +DSMPATH ?= ../.. + +module_ldflags = `xml2-config --libs` +module_cflags = -DMOD_NAME=\"$(plug_in_name)\" -I$(DSMPATH) \ + `xml2-config --cflags` + +COREPATH ?=$(DSMPATH)/../../core +lib_full_name = $(DSMPATH)/mods/lib/$(lib_name) +include $(DSMPATH)/mods/Makefile.dsm_module diff --git a/apps/dsm/mods/mod_xml/ModXml.cpp b/apps/dsm/mods/mod_xml/ModXml.cpp new file mode 100644 index 00000000..de658844 --- /dev/null +++ b/apps/dsm/mods/mod_xml/ModXml.cpp @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2012 FRAFOS GmbH + * + * Development sponsored by Sipwise GmbH. + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#include "ModXml.h" +#include "log.h" +#include "AmUtils.h" + +SC_EXPORT(MOD_CLS_NAME); + +void xml_err_func(void *ctx, const char *msg, ...); +xmlGenericErrorFunc handler = (xmlGenericErrorFunc)xml_err_func; +int xml_log_level = L_ERR; + +int MOD_CLS_NAME::preload() { + DBG("initializing libxml2...\n"); + xmlInitParser(); + initGenericErrorDefaultFunc(&handler); + handler = (xmlGenericErrorFunc)xml_err_func; + xmlSetGenericErrorFunc(NULL, &xml_err_func); + return 0; +} + +MOD_ACTIONEXPORT_BEGIN(MOD_CLS_NAME) { + DEF_CMD("xml.parse", MODXMLParseAction); + DEF_CMD("xml.parseSIPMsgBody", MODXMLParseSIPMsgBodyAction); + + DEF_CMD("xml.evalXPath", MODXMLEvalXPathAction); + DEF_CMD("xml.XPathResultCount", MODXMLXPathResultNodeCount); + + DEF_CMD("xml.setLoglevel", MODXMLSetLogLevelAction); + +} MOD_ACTIONEXPORT_END; + +MOD_CONDITIONEXPORT_NONE(MOD_CLS_NAME); + +ModXmlDoc::~ModXmlDoc() { + if (NULL != doc) { + DBG("freeing XML document [%p]\n", doc); + xmlFreeDoc(doc); + } +} + +ModXmlXPathObj::~ModXmlXPathObj() { + if (NULL != xpathObj) { + DBG("freeing XML xpath obj [%p]\n", xpathObj); + xmlXPathFreeObject(xpathObj); + } + if (NULL != xpathCtx) { + DBG("freeing XML xpath ctx [%p]\n", xpathCtx); + xmlXPathFreeContext(xpathCtx); + } +} + +#define TMP_BUF_SIZE 256 +void xml_err_func(void *ctx, const char *msg, ...) { + char _string[TMP_BUF_SIZE]; + va_list arg_ptr; + va_start(arg_ptr, msg); + vsnprintf(_string, TMP_BUF_SIZE, msg, arg_ptr); + va_end(arg_ptr); + + _LOG(xml_log_level, "%s", _string); +} + +CONST_ACTION_2P(MODXMLParseSIPMsgBodyAction, ',', false); +EXEC_ACTION_START(MODXMLParseSIPMsgBodyAction) { + string msgbody_var = resolveVars(par1, sess, sc_sess, event_params); + string dstname = resolveVars(par2, sess, sc_sess, event_params); + AVarMapT::iterator it = sc_sess->avar.find(msgbody_var); + if (it==sc_sess->avar.end()) { + DBG("no message body in avar '%s'\n", msgbody_var.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("no message body in avar " + msgbody_var); + EXEC_ACTION_STOP; + } + AmMimeBody* msgbody = dynamic_cast(it->second.asObject()); + if (NULL == msgbody) { + DBG("no AmMimeBody in avar '%s'\n", msgbody_var.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("no AmMimeBody in avar " + msgbody_var); + EXEC_ACTION_STOP; + } + const unsigned char* b = msgbody->getPayload(); + if (b==NULL) { + DBG("empty AmMimeBody in avar '%s'\n", msgbody_var.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("no AmMimeBody in avar " + msgbody_var); + EXEC_ACTION_STOP; + } + + xmlSetGenericErrorFunc(NULL, &xml_err_func); + + xmlDocPtr doc = + xmlReadMemory((const char*)b, msgbody->getLen(), "noname.xml", NULL, 0); + if (doc == NULL) { + DBG("failed parsing XML document from '%s'\n", msgbody_var.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("failed parsing XML document from " + msgbody_var); + EXEC_ACTION_STOP; + } + + xmlSetGenericErrorFunc(doc, &xml_err_func); + + ModXmlDoc* xml_doc = new ModXmlDoc(doc); + sc_sess->avar[dstname] = xml_doc; + DBG("parsed XML body document to '%s'\n", dstname.c_str()); + +// string basedir = resolveVars(par2, sess, sc_sess, event_params); +} EXEC_ACTION_END; + +CONST_ACTION_2P(MODXMLParseAction, ',', false); +EXEC_ACTION_START(MODXMLParseAction) { + string xml_doc = resolveVars(par1, sess, sc_sess, event_params); + string dstname = resolveVars(par2, sess, sc_sess, event_params); + + xmlSetGenericErrorFunc(NULL, &xml_err_func); + + xmlDocPtr doc = + xmlReadMemory(xml_doc.c_str(), xml_doc.length(), "noname.xml", NULL, 0); + if (doc == NULL) { + DBG("failed parsing XML document from '%s'\n", xml_doc.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("failed parsing XML document from " + xml_doc); + EXEC_ACTION_STOP; + } + xmlSetGenericErrorFunc(doc, &xml_err_func); + + ModXmlDoc* xml_doc_var = new ModXmlDoc(doc); + sc_sess->avar[dstname] = xml_doc_var; + DBG("parsed XML body document to '%s'\n", dstname.c_str()); +} EXEC_ACTION_END; + +template +T* getXMLElemFromVariable(DSMSession* sc_sess, const string& var_name) { + AVarMapT::iterator it = sc_sess->avar.find(var_name); + if (it == sc_sess->avar.end()) { + DBG("object '%s' not found\n", var_name.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("object '"+var_name+"' not found\n"); + return NULL; + } + + T* doc = dynamic_cast(it->second.asObject()); + if (NULL == doc) { + DBG("object '%s' is not the right type\n", var_name.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("object '"+var_name+"' is not the right type\n"); + return NULL; + } + return doc; +} + +CONST_ACTION_2P(MODXMLEvalXPathAction, ',', false); +EXEC_ACTION_START(MODXMLEvalXPathAction) { + string xpath_expr = resolveVars(par1, sess, sc_sess, event_params); + string xml_doc_var = resolveVars(par2, sess, sc_sess, event_params); + + xmlSetGenericErrorFunc(NULL, &xml_err_func); + + ModXmlDoc* xml_doc = getXMLElemFromVariable(sc_sess, xml_doc_var); + if (NULL == xml_doc) + EXEC_ACTION_STOP; + + xmlDocPtr doc = xml_doc->doc; + + xmlXPathContextPtr xpathCtx = xmlXPathNewContext(doc); + if(xpathCtx == NULL) { + DBG("unable to create new XPath context\n"); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("unable to create new XPath context"); + EXEC_ACTION_STOP; + } + xmlSetGenericErrorFunc(xpathCtx, &xml_err_func); + + string xml_doc_ns = sc_sess->var[xml_doc_var+".ns"]; + vector ns_entries = explode(xml_doc_ns, " "); + for (vector::iterator it=ns_entries.begin(); it != ns_entries.end(); it++) { + vector ns = explode(*it, "="); + if (ns.size() != 2) { + DBG("script writer error: namespace entry must be prefix=href (got '%s')\n", + it->c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("script writer error: namespace entry must be prefix=href\n"); + xmlXPathFreeContext(xpathCtx); + EXEC_ACTION_STOP; + } + + if(xmlXPathRegisterNs(xpathCtx, (const xmlChar*)ns[0].c_str(), + (const xmlChar*)ns[1].c_str()) != 0) { + DBG("unable to register namespace %s=%s\n", ns[0].c_str(), ns[1].c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("unable to register namespace\n"); + xmlXPathFreeContext(xpathCtx); + EXEC_ACTION_STOP; + } + DBG("registered namespace %s=%s\n", ns[0].c_str(), ns[1].c_str()); + } + + xmlXPathObjectPtr xpathObj = xmlXPathEvalExpression((const xmlChar*)xpath_expr.c_str(), + xpathCtx); + if(xpathObj == NULL) { + DBG("unable to evaluate xpath expression \"%s\"\n", xpath_expr.c_str()); + sc_sess->SET_ERRNO(DSM_ERRNO_UNKNOWN_ARG); + sc_sess->SET_STRERROR("unable to evaluate xpath expression"); + xmlXPathFreeContext(xpathCtx); + EXEC_ACTION_STOP; + } + + ModXmlXPathObj* xpath_obj = new ModXmlXPathObj(xpathObj, xpathCtx); + sc_sess->avar[xml_doc_var+".xpath"] = xpath_obj; + DBG("evaluated XPath expression on '%s' to '%s'\n", + xml_doc_var.c_str(), (xml_doc_var+".xpath").c_str()); + +} EXEC_ACTION_END; + +CONST_ACTION_2P(MODXMLXPathResultNodeCount, '=', false); +EXEC_ACTION_START(MODXMLXPathResultNodeCount) { + string cnt_var = par1; + string xpath_res_var = resolveVars(par2, sess, sc_sess, event_params); + + if (cnt_var.size() && cnt_var[0]=='$') { + cnt_var.erase(0,1); + } + + ModXmlXPathObj* xpath_obj = + getXMLElemFromVariable(sc_sess, xpath_res_var); + if (NULL == xpath_obj){ + DBG("no xpath result found in '%s'\n", xpath_res_var.c_str()); + sc_sess->var[cnt_var] = "0"; + EXEC_ACTION_STOP; + } + + unsigned int res = (xpath_obj->xpathObj->nodesetval) ? + xpath_obj->xpathObj->nodesetval->nodeNr : 0; + + sc_sess->var[cnt_var] = int2str(res); + DBG("set count $%s=%u\n", cnt_var.c_str(), res); + +} EXEC_ACTION_END; + +EXEC_ACTION_START(MODXMLSetLogLevelAction) { + string xml_log_level_s = resolveVars(arg, sess, sc_sess, event_params); + if (xml_log_level_s == "error") + xml_log_level = L_ERR; + else if (xml_log_level_s == "warn") + xml_log_level = L_WARN; + else if (xml_log_level_s == "info") + xml_log_level = L_INFO; + else if (xml_log_level_s == "debug") + xml_log_level = L_DBG; + else { + ERROR("script writer error: '%s' is no valid log level (error, warn, info, debug)\n", + xml_log_level_s.c_str()); + } +} EXEC_ACTION_END; diff --git a/apps/dsm/mods/mod_xml/ModXml.h b/apps/dsm/mods/mod_xml/ModXml.h new file mode 100644 index 00000000..5b9ac212 --- /dev/null +++ b/apps/dsm/mods/mod_xml/ModXml.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2012 FRAFOS GmbH + * + * Development sponsored by Sipwise GmbH. + * + * This file is part of SEMS, a free SIP media server. + * + * SEMS 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 released under + * the GPL with the additional exemption that compiling, linking, + * and/or using OpenSSL is allowed. + * + * For a license to use the SEMS software under conditions + * other than those described here, or to purchase support for this + * software, please contact iptel.org by e-mail at the following addresses: + * info@iptel.org + * + * SEMS 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 + */ +#ifndef _MOD_XML_H +#define _MOD_XML_H +#include "DSMModule.h" +#include "DSMSession.h" + + +#include +#include +#include +#include + +#define MOD_CLS_NAME SCXMLModule + +DECLARE_MODULE_BEGIN(MOD_CLS_NAME); +int preload(); +DECLARE_MODULE_END; + +DEF_ACTION_2P(MODXMLParseSIPMsgBodyAction); +DEF_ACTION_2P(MODXMLParseAction); +DEF_ACTION_2P(MODXMLEvalXPathAction); +DEF_ACTION_2P(MODXMLXPathResultNodeCount); +DEF_ACTION_1P(MODXMLSetLogLevelAction); + +class ModXmlDoc +: public DSMDisposable, + public AmObject { + public: + ModXmlDoc() : doc(NULL) {} + ModXmlDoc(xmlDocPtr doc) : doc(doc) {} + ~ModXmlDoc(); + xmlDocPtr doc; +}; + +class ModXmlXPathObj +: public DSMDisposable, + public AmObject { + public: + ModXmlXPathObj() : xpathObj(NULL), xpathCtx(NULL) {} + ModXmlXPathObj(xmlXPathObjectPtr xpathObj, xmlXPathContextPtr xpathCtx) + : xpathObj(xpathObj), xpathCtx(xpathCtx) { } + ~ModXmlXPathObj(); + + xmlXPathObjectPtr xpathObj; + xmlXPathContextPtr xpathCtx; +}; + +#endif diff --git a/apps/early_announce/EarlyAnnounce.cpp b/apps/early_announce/EarlyAnnounce.cpp index 507ef4ba..53c0896a 100644 --- a/apps/early_announce/EarlyAnnounce.cpp +++ b/apps/early_announce/EarlyAnnounce.cpp @@ -268,30 +268,20 @@ int EarlyAnnounceFactory::onLoad() void EarlyAnnounceDialog::onInvite(const AmSipRequest& req) { - try { - - string sdp_reply; - acceptAudio(req.body,req.hdrs,&sdp_reply); - - if(dlg.reply(req,183,"Session Progress", - "application/sdp",sdp_reply) != 0){ - - throw AmSession::Exception(500,"could not reply"); - } - else { - invite_req = req; - } - - } catch(const AmSession::Exception& e) { - - ERROR("%i %s\n",e.code,e.reason.c_str()); - setStopped(); - AmSipDialog::reply_error(req,e.code,e.reason); + AmMimeBody sdp_body; + sdp_body.addPart(SIP_APPLICATION_SDP); + + if(dlg->reply(req,183,"Session Progress", + &sdp_body) != 0){ + throw AmSession::Exception(500,"could not reply"); + } else { + invite_req = req; } } -AmSession* EarlyAnnounceFactory::onInvite(const AmSipRequest& req) +AmSession* EarlyAnnounceFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { #ifdef USE_MYSQL @@ -344,17 +334,20 @@ EarlyAnnounceDialog::~EarlyAnnounceDialog() { } -void EarlyAnnounceDialog::onSessionStart(const AmSipRequest& req) +void EarlyAnnounceDialog::onEarlySessionStart() { // we can drop all received packets // this disables DTMF detection as well setReceiving(false); - DBG("EarlyAnnounceDialog::onSessionStart\n"); + DBG("EarlyAnnounceDialog::onEarlySessionStart\n"); + if(wav_file.open(filename,AmAudioFile::Read)) - throw string("EarlyAnnounceDialog::onSessionStart: Cannot open file\n"); + throw string("EarlyAnnounceDialog::onEarlySessionStart: Cannot open file"); setOutput(&wav_file); + + AmB2BCallerSession::onEarlySessionStart(); } void EarlyAnnounceDialog::onBye(const AmSipRequest& req) @@ -363,9 +356,9 @@ void EarlyAnnounceDialog::onBye(const AmSipRequest& req) setStopped(); } -void EarlyAnnounceDialog::onCancel() +void EarlyAnnounceDialog::onCancel(const AmSipRequest& req) { - dlg.reply(invite_req,487,"Call terminated"); + dlg->reply(invite_req,487,"Call terminated"); setStopped(); } @@ -420,7 +413,7 @@ void EarlyAnnounceDialog::process(AmEvent* event) } DBG("Replying with code %d %s\n", code_i, reason.c_str()); - dlg.reply(invite_req, code_i, reason); + dlg->reply(invite_req, code_i, reason); setStopped(); } else { diff --git a/apps/early_announce/EarlyAnnounce.h b/apps/early_announce/EarlyAnnounce.h index f4ede8b9..c5d3785e 100644 --- a/apps/early_announce/EarlyAnnounce.h +++ b/apps/early_announce/EarlyAnnounce.h @@ -61,7 +61,8 @@ public: EarlyAnnounceFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; /** \brief session logic implementation for early_announce sessions */ @@ -75,9 +76,9 @@ public: ~EarlyAnnounceDialog(); void onInvite(const AmSipRequest& req); - void onSessionStart(const AmSipRequest& req); + void onEarlySessionStart(); void onBye(const AmSipRequest& req); - void onCancel(); + void onCancel(const AmSipRequest& req); void onDtmf(int event, int duration_msec) {} void process(AmEvent* event); diff --git a/apps/early_dbprompt/early_dbprompt.dsm b/apps/early_dbprompt/early_dbprompt.dsm new file mode 100644 index 00000000..9abc4909 --- /dev/null +++ b/apps/early_dbprompt/early_dbprompt.dsm @@ -0,0 +1,70 @@ +-- +-- This DSM app +-- o plays early media from a file in the DB +-- o replies with final reply code/reason from P-App-Param +-- +-- Parameters (P-App-Param): +-- audio_id - indicating audio file in DB; must be wav 16 bit pcm format (with wav header) +-- fr_code - final reply code +-- fr_reason - final reply reason +-- play_looped - "yes" to play the prompt in loop +-- +-- Example: P-App-Param: audio_id=rbt;play_looped=yes;fr_code=404;fr_reason="Not found" + +import(mod_utils); +import(mod_dlg); +import(mod_mysql); + +initial state START; + +transition "DB exception" START - exception / { + log(1, "Error in initializing :"); + logAll(1); + + -- reply with fr_code/reason immediately + dlg.reply($fr_code, $fr_reason); + + -- alternatively, reply with 500: + -- dlg.reply(500, "Server Internal Error"); + stop(false); +} -> END; + +transition "invite" START - invite / { + if test($audio_id != "") { + mysql.connect(); + throwOnError(); + -- play waiting file from DB + if test($play_looped=="yes") { + mysql.playDBAudioLooped(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav); + } else { + mysql.playDBAudio(SELECT data FROM provisioning.audio_files WHERE id="$audio_id", rbt.wav); + } + throwOnError(); + mysql.disconnect(); + } + + -- send 183 with early media + dlg.acceptInvite(183, "Progress"); + + -- no default 200 OK + set($connect_session=0); + + -- start RTP processing + connectMedia(); +} -> WAITING; + +state WAITING; + +transition "CANCEL received" WAITING - hangup / { + dlg.reply(487, "Request Terminated"); + removeTimers(); + stop(false); +} -> END; + +transition "end of prompt" WAITING - noAudio / { + dlg.reply($fr_code, $fr_reason); + removeTimers(); + stop(false); +} -> END; + +state END; \ No newline at end of file diff --git a/apps/early_dbprompt/etc/early_dbprompt.conf b/apps/early_dbprompt/etc/early_dbprompt.conf new file mode 100644 index 00000000..eb0c5810 --- /dev/null +++ b/apps/early_dbprompt/etc/early_dbprompt.conf @@ -0,0 +1,20 @@ + +# configuration for early_dbprompt app + +# DSM configuration +diag_path=/usr/lib/sems/early_dbprompt +mod_path=/usr/lib/sems/plug-in/ +load_diags=early_dbprompt +register_apps=early_dbprompt +run_invite_event=yes +set_param_variables=yes + +# application configuration: + +# database connection: +db_url="mysql://root:frafos@localhost/provisioning" + + +# local testing: +#diag_path=../apps/early_dbprompt +#mod_path=../apps/dsm/mods/lib diff --git a/apps/examples/announce_auth/AnnounceAuth.cpp b/apps/examples/announce_auth/AnnounceAuth.cpp index a47e3d89..23154111 100644 --- a/apps/examples/announce_auth/AnnounceAuth.cpp +++ b/apps/examples/announce_auth/AnnounceAuth.cpp @@ -49,158 +49,144 @@ AnnounceAuthFactory::AnnounceAuthFactory(const string& _app_name) int AnnounceAuthFactory::onLoad() { - AmConfigReader cfg; - if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) - return -1; + AmConfigReader cfg; + if(cfg.loadFile(AmConfig::ModConfigPath + string(MOD_NAME ".conf"))) + return -1; - // get application specific global parameters - configureModule(cfg); + // get application specific global parameters + configureModule(cfg); - AnnouncePath = cfg.getParameter("announce_path",ANNOUNCE_PATH); - if( !AnnouncePath.empty() - && AnnouncePath[AnnouncePath.length()-1] != '/' ) - AnnouncePath += "/"; + AnnouncePath = cfg.getParameter("announce_path",ANNOUNCE_PATH); + if( !AnnouncePath.empty() + && AnnouncePath[AnnouncePath.length()-1] != '/' ) + AnnouncePath += "/"; - AnnounceFile = cfg.getParameter("default_announce",ANNOUNCE_FILE); + AnnounceFile = cfg.getParameter("default_announce",ANNOUNCE_FILE); - string announce_file = AnnouncePath + AnnounceFile; - if(!file_exists(announce_file)){ - ERROR("default file for ann_b2b module does not exist ('%s').\n", - announce_file.c_str()); - return -1; - } + string announce_file = AnnouncePath + AnnounceFile; + if(!file_exists(announce_file)){ + ERROR("default file for ann_b2b module does not exist ('%s').\n", + announce_file.c_str()); + return -1; + } - auth_realm = cfg.getParameter("auth_realm", ""); - auth_user = cfg.getParameter("auth_user", ""); - auth_pwd = cfg.getParameter("auth_pwd", ""); + auth_realm = cfg.getParameter("auth_realm", ""); + auth_user = cfg.getParameter("auth_user", ""); + auth_pwd = cfg.getParameter("auth_pwd", ""); - uac_auth_f = AmPlugIn::instance()->getFactory4Seh("uac_auth"); - DBG("uac_auth_f == 0x%.16lX\n",(unsigned long)uac_auth_f); + dialer.set_dial(cfg.getParameter("dial_ruri","default ruri"), + cfg.getParameter("dial_from","default from"), + cfg.getParameter("dial_fromuri","default fromuri"), + cfg.getParameter("dial_to","default to")); - dialer.set_dial(cfg.getParameter("dial_ruri","default ruri"), - cfg.getParameter("dial_from","default from"), - cfg.getParameter("dial_fromuri","default fromuri"), - cfg.getParameter("dial_to","default to")); + dialer.start(); - dialer.start(); - - return 0; + return 0; } -AmSession* AnnounceAuthFactory::onInvite(const AmSipRequest& req) +AmSession* AnnounceAuthFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { - string announce_path = AnnouncePath; - string announce_file = announce_path + req.domain - + "/" + req.user + ".wav"; + string announce_path = AnnouncePath; + string announce_file = announce_path + req.domain + + "/" + req.user + ".wav"; - DBG("trying '%s'\n",announce_file.c_str()); - if(file_exists(announce_file)) - goto end; + DBG("trying '%s'\n",announce_file.c_str()); + if(file_exists(announce_file)) + goto end; - announce_file = announce_path + req.user + ".wav"; - DBG("trying '%s'\n",announce_file.c_str()); - if(file_exists(announce_file)) - goto end; + announce_file = announce_path + req.user + ".wav"; + DBG("trying '%s'\n",announce_file.c_str()); + if(file_exists(announce_file)) + goto end; - announce_file = AnnouncePath + AnnounceFile; + announce_file = AnnouncePath + AnnounceFile; -end: - AnnounceAuthDialog* dlg = new AnnounceAuthDialog(announce_file, - auth_realm, - auth_user, - auth_pwd); - - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new announcement session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(dlg); - if (h != NULL ) - dlg->addHandler(h); - } - - return dlg; + end: + AnnounceAuthDialog* dlg = + new AnnounceAuthDialog(announce_file,auth_realm, auth_user, auth_pwd); + + AmUACAuth::enable(dlg); + + return dlg; } AnnounceAuthDialog::AnnounceAuthDialog(const string& filename, - const string& auth_realm, - const string& auth_user, - const string& auth_pwd) - : filename(filename), - credentials(auth_realm, auth_user, auth_pwd) + const string& auth_realm, + const string& auth_user, + const string& auth_pwd) + : filename(filename), credentials(auth_realm, auth_user, auth_pwd) { - } UACAuthCred* AnnounceAuthDialog::getCredentials() { - return &credentials; + return &credentials; } AnnounceAuthDialog::~AnnounceAuthDialog() { } -void AnnounceAuthDialog::onSessionStart(const AmSipRequest& req) +void AnnounceAuthDialog::onSessionStart() { - DBG("AnnounceAuthDialog::onSessionStart\n"); - startSession(); -} + DBG("AnnounceAuthDialog::onSessionStart\n"); + startSession(); -void AnnounceAuthDialog::onSessionStart(const AmSipReply& rep) -{ - DBG("AnnounceAuthDialog::onSessionStart (SEMS originator mode)\n"); - startSession(); + AmSession::onSessionStart(); } -void AnnounceAuthDialog::startSession(){ - // disable DTMF detection - don't use DTMF here - setDtmfDetectionEnabled(false); +void AnnounceAuthDialog::startSession() +{ + // disable DTMF detection - don't use DTMF here + setDtmfDetectionEnabled(false); - if(wav_file.open(filename,AmAudioFile::Read)) - throw string("AnnounceAuthDialog::onSessionStart: Cannot open file\n"); + if(wav_file.open(filename,AmAudioFile::Read)) + throw string("AnnounceAuthDialog::onSessionStart: Cannot open file\n"); - setOutput(&wav_file); + setOutput(&wav_file); } void AnnounceAuthDialog::onBye(const AmSipRequest& req) { - DBG("onBye: stopSession\n"); - setStopped(); + DBG("onBye: stopSession\n"); + setStopped(); } void AnnounceAuthDialog::process(AmEvent* event) { - AmAudioEvent* audio_event = dynamic_cast(event); - if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){ - dlg.bye(); - setStopped(); - return; - } + AmAudioEvent* audio_event = dynamic_cast(event); + if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){ + dlg->bye(); + setStopped(); + return; + } - AmSession::process(event); + AmSession::process(event); } void DialerThread::set_dial(const string& r, const string& f, - const string& fu, const string& t) { - r_uri = r; - from = f; - from_uri = fu; - to = t; + const string& fu, const string& t) { + r_uri = r; + from = f; + from_uri = fu; + to = t; } void DialerThread::run() { - sleep(15); // wait for sems to completely start up - while (!is_stopped()) { - DBG("dialing..."); - AmUAC::dialout("blibla", "announce_auth", - r_uri, from, from_uri, to); - // every 10 minutes - sleep(100); - - } + sleep(15); // wait for sems to completely start up + while (!is_stopped()) { + DBG("dialing..."); + AmUAC::dialout("blibla", "announce_auth", + r_uri, from, from_uri, to); + // every 10 minutes + sleep(100); + + } } void DialerThread::on_stop() { - DBG("stopping...\n"); + DBG("stopping...\n"); } diff --git a/apps/examples/announce_auth/AnnounceAuth.h b/apps/examples/announce_auth/AnnounceAuth.h index d6825587..2bac1816 100644 --- a/apps/examples/announce_auth/AnnounceAuth.h +++ b/apps/examples/announce_auth/AnnounceAuth.h @@ -30,68 +30,66 @@ #include "AmSession.h" #include "AmAudioFile.h" #include "AmConfigReader.h" - -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include using std::string; class DialerThread : public AmThread { - string r_uri; - string from; - string from_uri; - string to; + string r_uri; + string from; + string from_uri; + string to; protected: - void run(); - void on_stop(); + void run(); + void on_stop(); public: - void set_dial(const string& r, const string& f, - const string& fu, const string& t); + void set_dial(const string& r, const string& f, + const string& fu, const string& t); }; class AnnounceAuthFactory: public AmSessionFactory { - DialerThread dialer; - AmSessionEventHandlerFactory* uac_auth_f; + DialerThread dialer; - string auth_realm; - string auth_user; - string auth_pwd; + string auth_realm; + string auth_user; + string auth_pwd; public: - static string AnnouncePath; - static string AnnounceFile; + static string AnnouncePath; + static string AnnounceFile; - AnnounceAuthFactory(const string& _app_name); + AnnounceAuthFactory(const string& _app_name); - int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + int onLoad(); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class AnnounceAuthDialog : public AmSession, - public CredentialHolder + public CredentialHolder { - AmAudioFile wav_file; - string filename; - UACAuthCred credentials; - - public: - AnnounceAuthDialog(const string& filename, - const string& auth_realm, - const string& auth_user, - const string& auth_pwd); - ~AnnounceAuthDialog(); - - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); - void startSession(); - void onBye(const AmSipRequest& req); - void onDtmf(int event, int duration_msec) {} - - void process(AmEvent* event); - inline UACAuthCred* getCredentials(); + AmAudioFile wav_file; + string filename; + UACAuthCred credentials; + +public: + AnnounceAuthDialog(const string& filename, + const string& auth_realm, + const string& auth_user, + const string& auth_pwd); + ~AnnounceAuthDialog(); + + void onSessionStart(); + void startSession(); + void onBye(const AmSipRequest& req); + void onDtmf(int event, int duration_msec) {} + + void process(AmEvent* event); + inline UACAuthCred* getCredentials(); }; diff --git a/apps/examples/b2b_connect/b2b_connect.cpp b/apps/examples/b2b_connect/b2b_connect.cpp index ce8e4b98..b0d11c21 100644 --- a/apps/examples/b2b_connect/b2b_connect.cpp +++ b/apps/examples/b2b_connect/b2b_connect.cpp @@ -38,7 +38,6 @@ EXPORT_SESSION_FACTORY(b2b_connectFactory,MOD_NAME); b2b_connectFactory::b2b_connectFactory(const string& _app_name) : AmSessionFactory(_app_name) -// , user_timer_fact(NULL) { } @@ -58,20 +57,14 @@ int b2b_connectFactory::onLoad() if (cfg.getParameter("transparent_ruri")=="true") TransparentDestination = true; -// if (!AmSession::timersSupported()) { -// ERROR("load session_timer plug-in for timers\n"); -// return -1; -// } - return 0; } -AmSession* b2b_connectFactory::onInvite(const AmSipRequest& req) +AmSession* b2b_connectFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { - string app_param = getHeader(req.hdrs, PARAM_HDR, true); - - if (!app_param.length()) { + if (!app_params.size()) { throw AmSession::Exception(500, "b2b_connect: parameters not found"); } @@ -92,22 +85,21 @@ b2b_connectDialog::~b2b_connectDialog() void b2b_connectDialog::onInvite(const AmSipRequest& req) { - if (dlg.getStatus() == AmSipDialog::Connected) { + if (dlg->getStatus() == AmSipDialog::Connected) { // reinvites AmB2ABCallerSession::onInvite(req); return; } - string app_param = getHeader(req.hdrs, PARAM_HDR, true); string remote_party, remote_uri; - if (!app_param.length()) { - throw AmSession::Exception(500, "b2b_connect: parameters not found"); - } + domain = getAppParam("d"); + user = getAppParam("u"); + password = getAppParam("p"); - domain = get_header_keyvalue(app_param,"d"); - user = get_header_keyvalue(app_param,"u"); - password = get_header_keyvalue(app_param,"p"); + if (domain.empty() || user.empty()) { + throw AmSession::Exception(500, "b2b_connect: domain parameters not found"); + } from = "sip:"+user+"@"+domain; if (b2b_connectFactory::TransparentDestination) { @@ -117,7 +109,7 @@ void b2b_connectDialog::onInvite(const AmSipRequest& req) remote_uri = to = "sip:"+req.user+"@"+domain; remote_party = "<" + to + ">"; } - if(dlg.reply(req, 100, "Connecting") != 0) { + if(dlg->reply(req, 100, "Connecting") != 0) { throw AmSession::Exception(500,"Failed to reply 100"); } @@ -129,7 +121,6 @@ void b2b_connectDialog::onInvite(const AmSipRequest& req) removeHeader(invite_req.hdrs, "Max-Forwards"); } - dlg.updateStatus(req); recvd_req.insert(std::make_pair(req.cseq,req)); connectCallee(remote_party, remote_uri, from, from, @@ -138,12 +129,11 @@ void b2b_connectDialog::onInvite(const AmSipRequest& req) MONITORING_LOG(other_id.c_str(), "app", MOD_NAME); - } -void b2b_connectDialog::onSessionStart(const AmSipRequest& req) +void b2b_connectDialog::onSessionStart() { - AmB2ABCallerSession::onSessionStart(req); + AmB2ABCallerSession::onSessionStart(); } void b2b_connectDialog::onB2ABEvent(B2ABEvent* ev) @@ -151,10 +141,10 @@ void b2b_connectDialog::onB2ABEvent(B2ABEvent* ev) if (ev->event_id == B2ABConnectOtherLegException) { B2ABConnectOtherLegExceptionEvent* ex_ev = dynamic_cast(ev); - if (ex_ev && dlg.getStatus() == AmSipDialog::Pending) { + if (ex_ev && dlg->getStatus() < AmSipDialog::Connected) { DBG("callee leg creation failed with exception '%d %s'\n", ex_ev->code, ex_ev->reason.c_str()); - dlg.reply(invite_req, ex_ev->code, ex_ev->reason); + dlg->reply(invite_req, ex_ev->code, ex_ev->reason); setStopped(); return; } @@ -163,10 +153,10 @@ void b2b_connectDialog::onB2ABEvent(B2ABEvent* ev) if (ev->event_id == B2ABConnectOtherLegFailed) { B2ABConnectOtherLegFailedEvent* f_ev = dynamic_cast(ev); - if (f_ev && dlg.getStatus() == AmSipDialog::Pending) { + if (f_ev && dlg->getStatus() < AmSipDialog::Connected) { DBG("callee leg creation failed with reply '%d %s'\n", f_ev->code, f_ev->reason.c_str()); - dlg.reply(invite_req, f_ev->code, f_ev->reason); + dlg->reply(invite_req, f_ev->code, f_ev->reason); setStopped(); return; } @@ -220,14 +210,14 @@ void b2b_connectDialog::onBye(const AmSipRequest& req) } -void b2b_connectDialog::onCancel() +void b2b_connectDialog::onCancel(const AmSipRequest& req) { - if(dlg.getStatus() == AmSipDialog::Pending) { + if(dlg->getStatus() < AmSipDialog::Connected) { DBG("Wait for leg B to terminate"); } else { DBG("Canceling leg A on CANCEL since dialog is not pending"); - dlg.reply(invite_req, 487, "Request terminated"); + dlg->reply(invite_req, 487, "Request terminated"); setStopped(); } } @@ -235,25 +225,10 @@ void b2b_connectDialog::onCancel() AmB2ABCalleeSession* b2b_connectDialog::createCalleeSession() { - b2b_connectCalleeSession* sess = new b2b_connectCalleeSession(getLocalTag(), - connector, - user, password); + b2b_connectCalleeSession* sess = + new b2b_connectCalleeSession(getLocalTag(), connector, user, password); - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - - if (NULL != uac_auth_f) { - DBG("UAC Auth enabled for new b2b_connect session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(sess); - if (h != NULL ) - sess->addHandler(h); - else { - ERROR("unable to set SIP UAC auth for new session."); - } - } else { - ERROR("unable to get SIP UAC auth." - "(uac_auth module loaded?)\n"); - } + AmUACAuth::enable(sess); return sess; } @@ -277,12 +252,12 @@ inline UACAuthCred* b2b_connectCalleeSession::getCredentials() { } void b2b_connectCalleeSession::onSipReply(const AmSipReply& reply, - int old_dlg_status, - const string& trans_method) { - AmB2ABCalleeSession::onSipReply(reply, old_dlg_status, trans_method); + AmSipDialog::Status old_dlg_status) { + + AmB2ABCalleeSession::onSipReply(reply, old_dlg_status); - if ((old_dlg_status == AmSipDialog::Pending)&& - (dlg.getStatus() == AmSipDialog::Disconnected)) { + if ((old_dlg_status < AmSipDialog::Connected) && + (dlg->getStatus() == AmSipDialog::Disconnected)) { DBG("status change Pending -> Disconnected. Stopping session.\n"); setStopped(); } diff --git a/apps/examples/b2b_connect/b2b_connect.h b/apps/examples/b2b_connect/b2b_connect.h index e195a1fa..c31c1db4 100644 --- a/apps/examples/b2b_connect/b2b_connect.h +++ b/apps/examples/b2b_connect/b2b_connect.h @@ -29,7 +29,7 @@ #define _B2B_CONNECT_H #include "AmB2ABSession.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" using std::string; @@ -40,7 +40,8 @@ class b2b_connectFactory: public AmSessionFactory b2b_connectFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); static bool TransparentHeaders; // default static bool TransparentDestination; // default @@ -62,13 +63,13 @@ class b2b_connectDialog : public AmB2ABCallerSession b2b_connectDialog(); ~b2b_connectDialog(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onB2ABEvent(B2ABEvent* ev); void process(AmEvent* ev); void onDtmf(int event, int duration); void onBye(const AmSipRequest& req); void onInvite(const AmSipRequest& req); - void onCancel(); + void onCancel(const AmSipRequest& req); protected: @@ -83,8 +84,7 @@ class b2b_connectCalleeSession AmSipRequest invite_req; protected: - void onSipReply(const AmSipReply& reply, int old_dlg_status, - const string& trans_method); + void onSipReply(const AmSipReply& reply, AmSipDialog::Status old_dlg_status); public: b2b_connectCalleeSession(const string& other_tag, diff --git a/apps/examples/cacheannounce/CacheAnnounce.cpp b/apps/examples/cacheannounce/CacheAnnounce.cpp index fca76bde..b9ec7a6b 100644 --- a/apps/examples/cacheannounce/CacheAnnounce.cpp +++ b/apps/examples/cacheannounce/CacheAnnounce.cpp @@ -75,7 +75,8 @@ int CacheAnnounceFactory::onLoad() return 0; } -AmSession* CacheAnnounceFactory::onInvite(const AmSipRequest& req) +AmSession* CacheAnnounceFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new CacheAnnounceDialog(&ann_cache); } @@ -89,24 +90,20 @@ CacheAnnounceDialog::~CacheAnnounceDialog() { } -void CacheAnnounceDialog::onSessionStart(const AmSipRequest& req) +void CacheAnnounceDialog::onSessionStart() { DBG("CacheAnnounceDialog::onSessionStart\n"); startSession(); -} -void CacheAnnounceDialog::onSessionStart(const AmSipReply& rep) -{ - DBG("CacheAnnounceDialog::onSessionStart (SEMS originator mode)\n"); - startSession(); + AmSession::onSessionStart(); } void CacheAnnounceDialog::startSession(){ setDtmfDetectionEnabled(false); - wav_file.reset(new AmCachedAudioFile(announce)); + wav_file.reset(new AmCachedAudioFile(announce)); if (!wav_file->is_good()) - throw AmSession::Exception(500, "Internal Err"); + throw AmSession::Exception(500, "Internal Err"); setOutput(wav_file.get()); } diff --git a/apps/examples/cacheannounce/CacheAnnounce.h b/apps/examples/cacheannounce/CacheAnnounce.h index 9d18ed23..ba2b550e 100644 --- a/apps/examples/cacheannounce/CacheAnnounce.h +++ b/apps/examples/cacheannounce/CacheAnnounce.h @@ -50,7 +50,8 @@ public: CacheAnnounceFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class CacheAnnounceDialog : public AmSession @@ -62,8 +63,7 @@ class CacheAnnounceDialog : public AmSession CacheAnnounceDialog(AmFileCache* announce); ~CacheAnnounceDialog(); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); + void onSessionStart(); void startSession(); void onBye(const AmSipRequest& req); void onDtmf(int event, int duration_msec) {} diff --git a/apps/examples/call_gen/CallGen.cpp b/apps/examples/call_gen/CallGen.cpp index 2e7e0390..f902a15d 100644 --- a/apps/examples/call_gen/CallGen.cpp +++ b/apps/examples/call_gen/CallGen.cpp @@ -48,6 +48,7 @@ EXPORT_PLUGIN_CLASS_FACTORY(CallGenFactory,APP_NAME); string CallGenFactory::DigitsDir; AmFileCache CallGenFactory::play_file; +string CallGenFactory::from_host; CallGenFactory::CallGenFactory(const string& _app_name) : AmSessionFactory(_app_name), @@ -86,6 +87,8 @@ int CallGenFactory::load() { return -1; } + from_host = cfg.getParameter("from_host", "callgen.example.net"); + // get prompts AM_PROMPT_START; AM_PROMPT_END(prompts, cfg, APP_NAME); @@ -141,7 +144,7 @@ void CallGenFactory::checkTarget() { if (!target_args) return; - DBG("%zd active calls, %d current target, %d already scheduled\n", + DBG("%zd active calls, %d current target, %d already scheduled\n", active_calls.size(), target_args->get(0).asInt(), scheduled); int missing_calls = @@ -161,14 +164,15 @@ void CallGenFactory::on_stop(){ ERROR("not stoppable!\n"); } -AmSession* CallGenFactory::onInvite(const AmSipRequest& req) -{ - ERROR("DUH - need session params!.\n"); +AmSession* CallGenFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { + ERROR("incoming calls not supported!\n"); return NULL; } // outgoing calls -AmSession* CallGenFactory::onInvite(const AmSipRequest& req, AmArg& args) +AmSession* CallGenFactory::onInvite(const AmSipRequest& req, const string& app_name, + AmArg& args) { size_t cnt = 0; cnt++; // int ncalls = args.get(cnt++).asInt(); @@ -244,8 +248,7 @@ void CallGenFactory::createCall(const AmArg& args) { cnt++; // int call_time_base = args.get(cnt++).asInt(); cnt++; // int call_time_rand = args.get(cnt++).asInt(); - string from = "sip:callgen@"+ (AmConfig::Ifs.size() ? - AmConfig::Ifs[0].LocalSIPIP : "localhost"); + string from = "sip:callgen@"+from_host; string call_ruri = "sip:"+ruri_user; for (int i=0;i9) + ncalls_per_sec = args[9].asInt(); + + DBG("scheduling %d calls (%d/s)\n", ncalls, ncalls_per_sec); time_t now; time(&now); actions_mut.lock(); - for (int i=0;i0) wait_nsec+=(rand()%wait_time_rand); @@ -329,12 +343,12 @@ void CallGenFactory::callGenStats(const AmArg& args, AmArg& ret) { target = target_args->get(0).asInt(); string res = "CallGen statistics: \n " + - int2str((int)active_calls.size()) + " active calls\n " + + int2str((unsigned int)active_calls.size()) + " active calls\n " + int2str(target) + " current target\n " + int2str(scheduled) +" scheduled\n "; calls_list_mut.lock(); - res += int2str((int)past_calls.size()) + " total calls\n "; + res += int2str((unsigned int)past_calls.size()) + " total calls\n "; calls_list_mut.unlock(); ret.push(res.c_str()); } @@ -368,20 +382,20 @@ CallGenDialog::CallGenDialog(AmPromptCollection& prompts, play_rand_digits(play_rand_digits), call_time_base(call_time_base), call_time_rand(call_time_rand), - play_file(&CallGenFactory::play_file) + play_file(&CallGenFactory::play_file), + timer_started(false) { } CallGenDialog::~CallGenDialog() { prompts.cleanup((long)this); - play_list.close(false); + play_list.flush(); report(CGDestroy); } -void CallGenDialog::onInvite(const AmSipRequest& r) { +void CallGenDialog::onStart() { report(CGCreate); - AmSession::onInvite(r); } void CallGenDialog::report(CallGenEvent what) { @@ -391,7 +405,32 @@ void CallGenDialog::report(CallGenEvent what) { disconnect_ts); } -void CallGenDialog::onSessionStart(const AmSipReply& rep) { +void CallGenDialog::setCallTimer() { + if (timer_started) + return; + timer_started = true; + + int call_timer = call_time_base; + if (call_time_rand>0) + call_timer+=rand()%call_time_rand; + + if (call_timer > 0) { + DBG("setting timer %d %d\n", CALL_TIMER, call_timer); + if (!setTimer(CALL_TIMER, call_timer)) { + ERROR("internal: setting timer!\n"); + return; + } + } + +} + +void CallGenDialog::onEarlySessionStart() { + setCallTimer(); + + AmSession::onEarlySessionStart(); +} + +void CallGenDialog::onSessionStart() { time(&connect_ts); report(CGConnect); @@ -409,16 +448,10 @@ void CallGenDialog::onSessionStart(const AmSipReply& rep) { // prompts.addToPlaylist(PLAY_FILE, (long)this, play_list); setInOut(&play_list, &play_list); - int call_timer = call_time_base; - if (call_time_rand>0) - call_timer+=rand()%call_time_rand; - if (call_timer > 0) { - if (!setTimer(CALL_TIMER, call_timer)) { - ERROR("could not load user_timer from session_timer plug-in\n"); - return; - } - } + setCallTimer(); + + AmSession::onSessionStart(); } void CallGenDialog::process(AmEvent* event) @@ -429,10 +462,10 @@ void CallGenDialog::process(AmEvent* event) time(&disconnect_ts); report(CGDisconnect); - play_list.close(); + play_list.flush(); setInOut(NULL,NULL); setStopped(); - dlg.bye(); + dlg->bye(); } else AmSession::process(event); @@ -443,7 +476,16 @@ void CallGenDialog::onBye(const AmSipRequest& req) { time(&disconnect_ts); report(CGDisconnect); - play_list.close(); + play_list.flush(); setInOut(NULL,NULL); setStopped(); } + +void CallGenDialog::onSipReply(const AmSipRequest& req, const AmSipReply& reply, AmBasicSipDialog::Status old_dlg_status) { + AmSession::onSipReply(req, reply, old_dlg_status); + if ((old_dlg_status < AmSipDialog::Connected) && + dlg->getStatus() == AmSipDialog::Disconnected) { + DBG("SIP dialog status change: < Connected -> Disconnected, stopping call\n"); + setStopped(); + } +} diff --git a/apps/examples/call_gen/CallGen.h b/apps/examples/call_gen/CallGen.h index 1a1cb231..cbcfa435 100644 --- a/apps/examples/call_gen/CallGen.h +++ b/apps/examples/call_gen/CallGen.h @@ -84,9 +84,13 @@ class CallGenFactory public: static string DigitsDir; static AmFileCache play_file; + static string from_host; CallGenFactory(const string& _app_name); - AmSession* onInvite(const AmSipRequest& req, AmArg& args); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + AmArg& args); + int onLoad(); // DI API @@ -134,17 +138,23 @@ private: int call_time_base; int call_time_rand; + bool timer_started; + void report(CallGenEvent what); + void setCallTimer(); public: CallGenDialog(AmPromptCollection& prompts, int play_rand_digits, int call_time_base, int call_time_rand); ~CallGenDialog(); - void onInvite(const AmSipRequest& r); - void onSessionStart(const AmSipReply& rep); + void onStart(); + void onEarlySessionStart(); + void onSessionStart(); void onBye(const AmSipRequest& req); void process(AmEvent* event); + void onSipReply(const AmSipRequest& req, const AmSipReply& reply, AmBasicSipDialog::Status old_dlg_status); + }; #endif diff --git a/apps/examples/call_gen/Readme.callgen b/apps/examples/call_gen/Readme.callgen index 2d0d4533..20df6f17 100644 --- a/apps/examples/call_gen/Readme.callgen +++ b/apps/examples/call_gen/Readme.callgen @@ -32,6 +32,8 @@ callgen - simple call generator int call_time_base - call timer, base value int call_time_rand - call timer, random add (total = base + rand) + [int ncalls_per_sec - n calls to schedule per sec, only setTarget ] + method: callGenStats - return some statistics diff --git a/apps/examples/call_gen/etc/callgen.conf b/apps/examples/call_gen/etc/callgen.conf index 6801caf5..a3d1723a 100644 --- a/apps/examples/call_gen/etc/callgen.conf +++ b/apps/examples/call_gen/etc/callgen.conf @@ -1,2 +1,3 @@ -play_file=../apps/call_gen/wav/default.wav -digits_dir=../apps/call_gen/wav/ \ No newline at end of file +play_file=../apps/examples/call_gen/wav/default.wav +digits_dir=../apps/examples/call_gen/wav/ +from_host=callgen.example.net diff --git a/apps/examples/dtmftester/DtmfTester.cpp b/apps/examples/dtmftester/DtmfTester.cpp index 64b676b4..ec59ebc7 100644 --- a/apps/examples/dtmftester/DtmfTester.cpp +++ b/apps/examples/dtmftester/DtmfTester.cpp @@ -89,37 +89,23 @@ string DtmfTesterFactory::getAnnounceFile(const AmSipRequest& req) { return announce_file; } -AmSession* DtmfTesterFactory::onInvite(const AmSipRequest& req) +AmSession* DtmfTesterFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new DtmfTesterDialog(getAnnounceFile(req), NULL); } -AmSession* DtmfTesterFactory::onInvite(const AmSipRequest& req, - AmArg& session_params) +AmSession* DtmfTesterFactory::onInvite(const AmSipRequest& req, const string& app_name, + AmArg& session_params) { - UACAuthCred* cred = NULL; - if (session_params.getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.asObject(); - if (cred_obj) - cred = dynamic_cast(cred_obj); - } + UACAuthCred* cred = AmUACAuth::unpackCredentials(session_params); AmSession* s = new DtmfTesterDialog(getAnnounceFile(req), cred); if (NULL == cred) { WARN("discarding unknown session parameters.\n"); } else { - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new announcement session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(s); - if (h != NULL ) - s->addHandler(h); - } else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated dialout.\n"); - } + AmUACAuth::enable(s); } return s; @@ -141,16 +127,12 @@ DtmfTesterDialog::~DtmfTesterDialog() } -void DtmfTesterDialog::onSessionStart(const AmSipRequest& req) +void DtmfTesterDialog::onSessionStart() { DBG("DtmfTesterDialog::onSessionStart\n"); startSession(); -} - -void DtmfTesterDialog::onSessionStart(const AmSipReply& rep) -{ - DBG("DtmfTesterDialog::onSessionStart (SEMS originator mode)\n"); - startSession(); + + AmSession::onSessionStart(); } void DtmfTesterDialog::startSession(){ @@ -178,7 +160,7 @@ void DtmfTesterDialog::process(AmEvent* event) AmAudioEvent* audio_event = dynamic_cast(event); if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){ - dlg.bye(); + dlg->bye(); setStopped(); return; } diff --git a/apps/examples/dtmftester/DtmfTester.h b/apps/examples/dtmftester/DtmfTester.h index c4c1c98e..e5856aec 100644 --- a/apps/examples/dtmftester/DtmfTester.h +++ b/apps/examples/dtmftester/DtmfTester.h @@ -31,7 +31,7 @@ #include "AmConfigReader.h" #include "AmPlaylist.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include using std::string; @@ -49,8 +49,9 @@ public: DtmfTesterFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); - AmSession* onInvite(const AmSipRequest& req, + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); }; @@ -74,8 +75,7 @@ public: UACAuthCred* credentials = NULL); ~DtmfTesterDialog(); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); + void onSessionStart(); void startSession(); void onBye(const AmSipRequest& req); void onDtmf(int event, int duration_msec); diff --git a/apps/examples/early_record/EarlyRecord.cpp b/apps/examples/early_record/EarlyRecord.cpp index de2ceb4f..a4569c39 100644 --- a/apps/examples/early_record/EarlyRecord.cpp +++ b/apps/examples/early_record/EarlyRecord.cpp @@ -22,38 +22,24 @@ int EarlyRecordFactory::onLoad() return 0; } -AmSession* EarlyRecordFactory::onInvite(const AmSipRequest& req) +AmSession* EarlyRecordFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new EarlyRecordDialog(NULL); } // auth with di_dial -AmSession* EarlyRecordFactory::onInvite(const AmSipRequest& req, +AmSession* EarlyRecordFactory::onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params) { - UACAuthCred* cred = NULL; - if (session_params.getType() == AmArg::AObject) { - ArgObject* cred_obj = session_params.asObject(); - if (cred_obj) - cred = dynamic_cast(cred_obj); - } + UACAuthCred* cred = AmUACAuth::unpackCredentials(session_params); AmSession* s = new EarlyRecordDialog(cred); if (NULL == cred) { WARN("discarding unknown session parameters.\n"); } else { - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - if (uac_auth_f != NULL) { - DBG("UAC Auth enabled for new announcement session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(s); - if (h != NULL ) - s->addHandler(h); - } else { - ERROR("uac_auth interface not accessible. " - "Load uac_auth for authenticated dialout.\n"); - } + AmUACAuth::enable(s); } return s; @@ -70,7 +56,7 @@ EarlyRecordDialog::~EarlyRecordDialog() { } -void EarlyRecordDialog::onEarlySessionStart(const AmSipReply& req) { +void EarlyRecordDialog::onEarlySessionStart() { DBG("Early Session Start\n"); msg_filename = "/tmp/" + getLocalTag() + ".wav"; @@ -80,9 +66,11 @@ void EarlyRecordDialog::onEarlySessionStart(const AmSipReply& req) { setInput(&a_msg); setMute(true); + + AmSession::onEarlySessionStart(); } -void EarlyRecordDialog::onSessionStart(const AmSipReply& req) +void EarlyRecordDialog::onSessionStart() { setInOut(NULL, NULL); @@ -96,6 +84,8 @@ void EarlyRecordDialog::onSessionStart(const AmSipReply& req) msg_filename + string(" for writing"); setOutput(&a_msg); + + AmSession::onSessionStart(); } void EarlyRecordDialog::onBye(const AmSipRequest& req) @@ -109,7 +99,7 @@ void EarlyRecordDialog::process(AmEvent* event) AmAudioEvent* audio_event = dynamic_cast(event); if(audio_event && (audio_event->event_id == AmAudioEvent::cleared)){ - dlg.bye(); + dlg->bye(); setStopped(); return; } diff --git a/apps/examples/early_record/EarlyRecord.h b/apps/examples/early_record/EarlyRecord.h index 694a7ec5..3f4636ac 100644 --- a/apps/examples/early_record/EarlyRecord.h +++ b/apps/examples/early_record/EarlyRecord.h @@ -3,7 +3,7 @@ #include "AmSession.h" #include "AmAudioFile.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include using std::string; @@ -16,8 +16,9 @@ public: EarlyRecordFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); - AmSession* onInvite(const AmSipRequest& req, + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, AmArg& session_params); }; @@ -37,8 +38,9 @@ class EarlyRecordDialog public: EarlyRecordDialog(UACAuthCred* credentials); ~EarlyRecordDialog(); - void onEarlySessionStart(const AmSipReply& req); - void onSessionStart(const AmSipReply& req); + + void onEarlySessionStart(); + void onSessionStart(); void onBye(const AmSipRequest& req); inline UACAuthCred* getCredentials(); }; diff --git a/apps/examples/ivr_examples/b2b_out.py b/apps/examples/ivr_examples/b2b_out.py index 016f1f8a..e2a73376 100644 --- a/apps/examples/ivr_examples/b2b_out.py +++ b/apps/examples/ivr_examples/b2b_out.py @@ -6,7 +6,7 @@ from ivr import * class IvrDialog(IvrDialogBase): - def onSessionStart(self, hdrs): + def onSessionStart(self): info("starting b2bua app ...") self.setNoRelayonly() diff --git a/apps/examples/ivr_examples/b2bua.py b/apps/examples/ivr_examples/b2bua.py index 8f6cc9b0..77951f51 100644 --- a/apps/examples/ivr_examples/b2bua.py +++ b/apps/examples/ivr_examples/b2bua.py @@ -5,11 +5,11 @@ from log import * from ivr import * WELLCOME_MSG = "wav/default_en.wav" -CALLEE_URI = "sip:gong@iptel.org" +CALLEE_URI = "sip:music@iptel.org" class IvrDialog(IvrDialogBase): - def onSessionStart(self, hdrs): + def onSessionStart(self): info("starting b2bua test ...") diff --git a/apps/examples/ivr_examples/db_balance.py b/apps/examples/ivr_examples/db_balance.py index 8843dea6..f1041f26 100644 --- a/apps/examples/ivr_examples/db_balance.py +++ b/apps/examples/ivr_examples/db_balance.py @@ -26,7 +26,7 @@ db = MySQLdb.connection(host="127.0.0.1", user="root", passwd="sa07", db="busine class IvrDialog(IvrDialogBase) : ttsfile = None - def onSessionStart( self, hdrs ) : + def onSessionStart(self) : db.query("select bal_int, bal_decimal from accounts where user='%s'" % self.dialog.user) r = db.store_result() res = r.fetch_row(1) diff --git a/apps/examples/ivr_examples/play.py b/apps/examples/ivr_examples/play.py index 1fc29f18..93a1eed1 100644 --- a/apps/examples/ivr_examples/play.py +++ b/apps/examples/ivr_examples/play.py @@ -1,26 +1,44 @@ # -# just playing file +# just play a file whose name may be given in P-App-Param header # + from log import * from ivr import * +from p_app_param.p_app_param import * -class IvrDialog(IvrDialogBase) : - - def onSessionStart( self, hdrs ) : - self.audio_pl = IvrAudioFile() - self.audio_pl.open("wav/default_en.wav", AUDIO_READ) +class IvrDialog(IvrDialogBase): - self.enqueue(self.audio_pl,None) + def onInvite(self, hdrs): + if self.dialog.status_str == "Connected": + debug("Received play re-INVITE") + return True + else: + debug("Received play INVITE") + self.params = P_App_Param(getHeader(hdrs, "P-App-Param")) + self.file = self.params.getKeyValue("File") + if not self.file: + self.file = "/tmp/default.wav" + return True + + def onSessionStart(self): + self.announcement = IvrAudioFile() + self.announcement.open(self.file, ivr.AUDIO_READ, False) + self.enqueue(self.announcement, None) + return - def onBye(self): - self.stopSession() - def onEmptyQueue(self): + if not self.queueIsEmpty(): + return self.bye() self.stopSession() + return + + def onBye(self): + self.stopSession() + return - def onSipReply( self, reply ) : + def onSipRequest(self, hdrs): pass - def onSipRequest( self, reply ) : + def onSipReply(self, reply): pass diff --git a/apps/examples/ivr_examples/rec.py b/apps/examples/ivr_examples/rec.py index b37a6386..2485f005 100644 --- a/apps/examples/ivr_examples/rec.py +++ b/apps/examples/ivr_examples/rec.py @@ -6,7 +6,7 @@ from ivr import * class IvrDialog(IvrDialogBase) : - def onSessionStart( self, hdrs ) : + def onSessionStart(self) : self.audio_msg = IvrAudioFile() self.audio_msg.open("record.wav", AUDIO_WRITE) diff --git a/apps/examples/ivr_examples/test_mp3.py b/apps/examples/ivr_examples/test_mp3.py index 2572f5af..c8e0156e 100644 --- a/apps/examples/ivr_examples/test_mp3.py +++ b/apps/examples/ivr_examples/test_mp3.py @@ -6,7 +6,7 @@ from log import * from ivr import * class IvrDialog(IvrDialogBase): - def onSessionStart(self, hdrs): + def onSessionStart(self): self.voice_msg = IvrAudioFile() self.voice_msg.open("play.mp3", AUDIO_READ, False) self.enqueue(self.voice_msg, None) diff --git a/apps/examples/ivr_examples/tmpfile.py b/apps/examples/ivr_examples/tmpfile.py index e98585b6..f2d63bfb 100644 --- a/apps/examples/ivr_examples/tmpfile.py +++ b/apps/examples/ivr_examples/tmpfile.py @@ -7,7 +7,7 @@ import os class IvrDialog(IvrDialogBase): - def onSessionStart(self, hdrs): + def onSessionStart(self): info("starting tmpfile.py") f = open("/tmp/default_en.wav") diff --git a/apps/examples/jukecall/Jukecall.cpp b/apps/examples/jukecall/Jukecall.cpp index e3771aea..017ef7b4 100644 --- a/apps/examples/jukecall/Jukecall.cpp +++ b/apps/examples/jukecall/Jukecall.cpp @@ -51,7 +51,8 @@ int JukecallFactory::onLoad() return 0; } -AmSession* JukecallFactory::onInvite(const AmSipRequest& req) +AmSession* JukecallFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { if (req.user.length() <= 3) { throw AmSession::Exception(403, "Need a number to call"); @@ -71,11 +72,11 @@ JukecallSession::~JukecallSession() { } -void JukecallSession::onSessionStart(const AmSipRequest& req) +void JukecallSession::onSessionStart() { if (state != JC_none) { // reinvite - AmB2ABCallerSession::onSessionStart(req); + AmB2ABCallerSession::onSessionStart(); return; } @@ -83,7 +84,7 @@ void JukecallSession::onSessionStart(const AmSipRequest& req) DBG("playing file\n"); if(initial_announcement.open(INITIAL_ANNOUNCEMENT,AmAudioFile::Read)) { - dlg.bye(); + dlg->bye(); throw string("CTConfDDialog::onSessionStart: Cannot open file '%s'\n", INITIAL_ANNOUNCEMENT); } @@ -91,6 +92,8 @@ void JukecallSession::onSessionStart(const AmSipRequest& req) setOutput(&initial_announcement); state = JC_initial_announcement; + + AmB2ABCallerSession::onSessionStart(); } void JukecallSession::onDtmf(int event, int duration_msec) { @@ -125,10 +128,10 @@ void JukecallSession::process(AmEvent* event) switch(state) { case JC_initial_announcement: { state = JC_connect; - string callee = "sip:" + dlg.user.substr(3) + "@" + dlg.domain; + string callee = "sip:" + dlg->user.substr(3) + "@" + dlg->domain; DBG("-------------------------- connecting %s ------------------------\n", callee.c_str()); connectCallee(callee, callee, - dlg.remote_party, dlg.remote_uri); + dlg->remote_party, dlg->remote_uri); return; diff --git a/apps/examples/jukecall/Jukecall.h b/apps/examples/jukecall/Jukecall.h index cccd8348..fd018a01 100644 --- a/apps/examples/jukecall/Jukecall.h +++ b/apps/examples/jukecall/Jukecall.h @@ -41,7 +41,8 @@ public: int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class JukecallSession @@ -71,7 +72,7 @@ public: JukecallSession(); ~JukecallSession(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void process(AmEvent* event); void onDtmf(int event, int duration_msec); diff --git a/apps/examples/out_of_tree/Makefile b/apps/examples/out_of_tree/Makefile new file mode 100644 index 00000000..e19445f0 --- /dev/null +++ b/apps/examples/out_of_tree/Makefile @@ -0,0 +1,10 @@ +plug_in_name = myapp + +module_ldflags = +module_cflags = + +COREPATH ?=/usr/include/sems +DEFSPATH ?=/usr/include/sems +app_module_dir=. + +include $(COREPATH)/plug-in/Makefile.app_module diff --git a/apps/examples/out_of_tree/MyApp.cpp b/apps/examples/out_of_tree/MyApp.cpp new file mode 100644 index 00000000..5c6b279b --- /dev/null +++ b/apps/examples/out_of_tree/MyApp.cpp @@ -0,0 +1,42 @@ +#include "MyApp.h" +#include "log.h" + +#define MOD_NAME "myapp" + +EXPORT_SESSION_FACTORY(MyAppFactory,MOD_NAME); + +MyAppFactory::MyAppFactory(const string& _app_name) + : AmSessionFactory(_app_name) +{ +} + +int MyAppFactory::onLoad() +{ + return 0; +} + +AmSession* MyAppFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) +{ + return new MyAppDialog(); +} + +MyAppDialog::MyAppDialog() +{ +} + +MyAppDialog::~MyAppDialog() +{ +} + +void MyAppDialog::onSessionStart() +{ + DBG("MyAppDialog::onSessionStart: Hello World!\n"); +} + +void MyAppDialog::onBye(const AmSipRequest& req) +{ + DBG("onBye: stopSession\n"); + setStopped(); +} + diff --git a/apps/examples/out_of_tree/MyApp.h b/apps/examples/out_of_tree/MyApp.h new file mode 100644 index 00000000..9fc18d5a --- /dev/null +++ b/apps/examples/out_of_tree/MyApp.h @@ -0,0 +1,28 @@ +#ifndef _MYAPP_H_ +#define _MYAPP_H_ + +#include "AmSession.h" + +class MyAppFactory: public AmSessionFactory +{ +public: + MyAppFactory(const string& _app_name); + + int onLoad(); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); +}; + +class MyAppDialog : public AmSession +{ + + public: + MyAppDialog(); + ~MyAppDialog(); + + void onSessionStart(); + void onBye(const AmSipRequest& req); +}; + +#endif + diff --git a/apps/examples/out_of_tree/Readme.myapp b/apps/examples/out_of_tree/Readme.myapp new file mode 100644 index 00000000..d0d82ba1 --- /dev/null +++ b/apps/examples/out_of_tree/Readme.myapp @@ -0,0 +1,15 @@ + +MyApp + +This is an empty template for a SEMS application plug-in written in C++. + +It can reside out of the SEMS source tree and just needs the libsems1-dev +package installed to be compiled. For this to work, in the Makefile the +following needs to be defined: + plug_in_name = myapp + COREPATH ?=/usr/include/sems + DEFSPATH ?=/usr/include/sems + app_module_dir=. + include $(COREPATH)/plug-in/Makefile.app_module + + diff --git a/apps/examples/pinauthconference/PinAuthConference.cpp b/apps/examples/pinauthconference/PinAuthConference.cpp index 320d91b4..7c449708 100644 --- a/apps/examples/pinauthconference/PinAuthConference.cpp +++ b/apps/examples/pinauthconference/PinAuthConference.cpp @@ -83,14 +83,16 @@ int PinAuthConferenceFactory::onLoad() return 0; } -// incoming calls - req is INVITE -AmSession* PinAuthConferenceFactory::onInvite(const AmSipRequest& req) +// incoming calls +AmSession* PinAuthConferenceFactory::onInvite(const AmSipRequest&, const string& app_name, + const map& app_params) { return new PinAuthConferenceDialog(prompts); } -// outgoing calls - rep is 200 class response to INVITE -AmSession* PinAuthConferenceFactory::onInvite(const AmSipReply& rep) +// outgoing calls +AmSession* PinAuthConferenceFactory::onInvite(const AmSipRequest& req, const string& app_name, + AmArg& session_params) { return new PinAuthConferenceDialog(prompts); } @@ -104,7 +106,7 @@ PinAuthConferenceDialog::PinAuthConferenceDialog(AmPromptCollection& prompts) PinAuthConferenceDialog::~PinAuthConferenceDialog() { - play_list.close(false); + play_list.flush(); prompts.cleanup((long)this); } @@ -124,7 +126,7 @@ void PinAuthConferenceDialog::connectConference(const string& room) { channel.reset(AmConferenceStatus::getChannel(conf_id,getLocalTag())); // clear the playlist - play_list.close(false); + play_list.flush(); // add the channel to our playlist play_list.addToPlaylist(new AmPlaylistItem(channel.get(), @@ -134,7 +136,7 @@ void PinAuthConferenceDialog::connectConference(const string& room) { setInOut(&play_list,&play_list); } -void PinAuthConferenceDialog::onSessionStart(const AmSipRequest& req) +void PinAuthConferenceDialog::onSessionStart() { state = EnteringPin; @@ -142,11 +144,13 @@ void PinAuthConferenceDialog::onSessionStart(const AmSipRequest& req) // set the playlist as input and output setInOut(&play_list,&play_list); + + AmSession::onSessionStart(); } void PinAuthConferenceDialog::onBye(const AmSipRequest& req) { - play_list.close(); + play_list.flush(); setInOut(NULL,NULL); channel.reset(NULL); setStopped(); @@ -220,7 +224,7 @@ void PinAuthConferenceDialog::onDtmf(int event, int duration) } else { state = EnteringConference; setInOut(NULL, NULL); - play_list.close(); + play_list.flush(); for (size_t i=0;i& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + AmArg& session_params); int onLoad(); }; @@ -95,7 +97,7 @@ public: ~PinAuthConferenceDialog(); void process(AmEvent* ev); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onDtmf(int event, int duration); void onBye(const AmSipRequest& req); }; diff --git a/apps/examples/serviceline/ServiceLine.cpp b/apps/examples/serviceline/ServiceLine.cpp index 2b8e5ac9..3898cd38 100644 --- a/apps/examples/serviceline/ServiceLine.cpp +++ b/apps/examples/serviceline/ServiceLine.cpp @@ -95,7 +95,8 @@ int ServiceLineFactory::onLoad() return 0; } -AmSession* ServiceLineFactory::onInvite(const AmSipRequest& req) +AmSession* ServiceLineFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { string announce_path = AnnouncePath; string announce_file = announce_path + req.domain @@ -122,12 +123,11 @@ ServiceLineCallerDialog::ServiceLineCallerDialog(const string& filename) { } -void ServiceLineCallerDialog::onSessionStart(const AmSipRequest& req) +void ServiceLineCallerDialog::onSessionStart() { - if (started) { // reinvite - AmB2ABCallerSession::onSessionStart(req); + AmB2ABCallerSession::onSessionStart(); return; } started = true; @@ -137,6 +137,8 @@ void ServiceLineCallerDialog::onSessionStart(const AmSipRequest& req) setInOut(&playlist, &playlist); playlist.addToPlaylist(new AmPlaylistItem(&wav_file, NULL)); + + AmB2ABCallerSession::onSessionStart(); } void ServiceLineCallerDialog::process(AmEvent* event) @@ -174,21 +176,8 @@ AmB2ABCalleeSession* ServiceLineCallerDialog::createCalleeSession() { ServiceLineCalleeDialog* sess = new ServiceLineCalleeDialog(getLocalTag(), connector); - AmSessionEventHandlerFactory* uac_auth_f = - AmPlugIn::instance()->getFactory4Seh("uac_auth"); - - if (NULL != uac_auth_f) { - DBG("UAC Auth enabled for new serviceline session.\n"); - AmSessionEventHandler* h = uac_auth_f->getHandler(sess); - if (h != NULL ) - sess->addHandler(h); - else { - ERROR("unable to set SIP UAC auth for new session."); - } - } else { - ERROR("unable to get SIP UAC auth." - "(uac_auth module loaded?)\n"); - } + AmUACAuth::enable(sess); + return sess; } diff --git a/apps/examples/serviceline/ServiceLine.h b/apps/examples/serviceline/ServiceLine.h index e7823852..6e29da5a 100644 --- a/apps/examples/serviceline/ServiceLine.h +++ b/apps/examples/serviceline/ServiceLine.h @@ -30,7 +30,7 @@ #include "AmConfigReader.h" #include "AmAudioFile.h" #include "AmB2ABSession.h" -#include "ampi/UACAuthAPI.h" +#include "AmUACAuth.h" #include using std::string; @@ -54,7 +54,8 @@ public: ServiceLineFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class ServiceLineCallerDialog: public AmB2ABCallerSession @@ -75,7 +76,7 @@ public: void process(AmEvent* event); void onDtmf(int event, int duration); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); virtual AmB2ABCalleeSession* createCalleeSession(); }; diff --git a/apps/examples/simple_conference/SimpleConference.cpp b/apps/examples/simple_conference/SimpleConference.cpp index 316bf9c7..3d895296 100644 --- a/apps/examples/simple_conference/SimpleConference.cpp +++ b/apps/examples/simple_conference/SimpleConference.cpp @@ -43,13 +43,15 @@ int SimpleConferenceFactory::onLoad() } // incoming calls - req is INVITE -AmSession* SimpleConferenceFactory::onInvite(const AmSipRequest& req) +AmSession* SimpleConferenceFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new SimpleConferenceDialog(); } // outgoing calls - rep is 200 class response to INVITE -AmSession* SimpleConferenceFactory::onInvite(const AmSipReply& rep) +AmSession* SimpleConferenceFactory::onInvite(const AmSipRequest& req, const string& app_name, + AmArg& session_params) { return new SimpleConferenceDialog(); } @@ -67,10 +69,10 @@ SimpleConferenceDialog::~SimpleConferenceDialog() play_list.close(false); } -void SimpleConferenceDialog::onSessionStart(const AmSipRequest& req) +void SimpleConferenceDialog::onSessionStart() { // set the conference id ('conference room') to user part of ruri - conf_id = dlg.user; + conf_id = dlg->user; // open the beep file BeepSound.reset(new AmAudioFile()); @@ -92,6 +94,8 @@ void SimpleConferenceDialog::onSessionStart(const AmSipRequest& req) // people in the conference (important if we have multiple // MediaProcessor threads setCallgroup(conf_id); + + AmSession::onSessionStart(); } void SimpleConferenceDialog::onBye(const AmSipRequest& req) diff --git a/apps/examples/simple_conference/SimpleConference.h b/apps/examples/simple_conference/SimpleConference.h index 18803f86..69411b6c 100644 --- a/apps/examples/simple_conference/SimpleConference.h +++ b/apps/examples/simple_conference/SimpleConference.h @@ -47,8 +47,10 @@ class SimpleConferenceFactory : public AmSessionFactory { public: SimpleConferenceFactory(const string& _app_name); - AmSession* onInvite(const AmSipRequest&); - AmSession* onInvite(const AmSipReply&); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + AmArg& session_params); int onLoad(); }; @@ -71,7 +73,7 @@ public: ~SimpleConferenceDialog(); void process(AmEvent* ev); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onDtmf(int event, int duration); void onBye(const AmSipRequest& req); }; diff --git a/apps/examples/tutorial/cc_acc/CCAcc.cpp b/apps/examples/tutorial/cc_acc/CCAcc.cpp index 4ed6e1d6..7832417f 100644 --- a/apps/examples/tutorial/cc_acc/CCAcc.cpp +++ b/apps/examples/tutorial/cc_acc/CCAcc.cpp @@ -108,7 +108,7 @@ int CCAcc::subtractCredit(string pin, int amount) { credits_mut.unlock(); return -1; } - if (it->second - amount < 0) { + if ((int)it->second - amount < 0) { credits[pin] = 0; } else { credits[pin] = it->second - amount; diff --git a/apps/examples/tutorial/myannounceapp/MyAnnounceApp.cpp b/apps/examples/tutorial/myannounceapp/MyAnnounceApp.cpp index 1d6ccefc..19f3bf45 100644 --- a/apps/examples/tutorial/myannounceapp/MyAnnounceApp.cpp +++ b/apps/examples/tutorial/myannounceapp/MyAnnounceApp.cpp @@ -33,7 +33,8 @@ int MyAnnounceAppFactory::onLoad() return 0; } -AmSession* MyAnnounceAppFactory::onInvite(const AmSipRequest& req) +AmSession* MyAnnounceAppFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new MyAnnounceAppDialog(); } @@ -46,7 +47,7 @@ MyAnnounceAppDialog::~MyAnnounceAppDialog() { } -void MyAnnounceAppDialog::onSessionStart(const AmSipRequest& req) +void MyAnnounceAppDialog::onSessionStart() { DBG("MyAnnounceAppDialog::onSessionStart - file is '%s'\n", MyAnnounceAppFactory::AnnouncementFile.c_str()); @@ -56,6 +57,7 @@ void MyAnnounceAppDialog::onSessionStart(const AmSipRequest& req) setOutput(&wav_file); + AmSession::onSessionStart(); } void MyAnnounceAppDialog::onBye(const AmSipRequest& req) diff --git a/apps/examples/tutorial/myannounceapp/MyAnnounceApp.h b/apps/examples/tutorial/myannounceapp/MyAnnounceApp.h index 34abcec5..ec757431 100644 --- a/apps/examples/tutorial/myannounceapp/MyAnnounceApp.h +++ b/apps/examples/tutorial/myannounceapp/MyAnnounceApp.h @@ -15,7 +15,8 @@ public: MyAnnounceAppFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class MyAnnounceAppDialog : public AmSession @@ -27,7 +28,7 @@ class MyAnnounceAppDialog : public AmSession MyAnnounceAppDialog(); ~MyAnnounceAppDialog(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onBye(const AmSipRequest& req); }; diff --git a/apps/examples/tutorial/myapp/MyApp.cpp b/apps/examples/tutorial/myapp/MyApp.cpp index c7b8a0c2..5c6b279b 100644 --- a/apps/examples/tutorial/myapp/MyApp.cpp +++ b/apps/examples/tutorial/myapp/MyApp.cpp @@ -15,7 +15,8 @@ int MyAppFactory::onLoad() return 0; } -AmSession* MyAppFactory::onInvite(const AmSipRequest& req) +AmSession* MyAppFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new MyAppDialog(); } @@ -28,7 +29,7 @@ MyAppDialog::~MyAppDialog() { } -void MyAppDialog::onSessionStart(const AmSipRequest& req) +void MyAppDialog::onSessionStart() { DBG("MyAppDialog::onSessionStart: Hello World!\n"); } diff --git a/apps/examples/tutorial/myapp/MyApp.h b/apps/examples/tutorial/myapp/MyApp.h index 2dcb02b0..9fc18d5a 100644 --- a/apps/examples/tutorial/myapp/MyApp.h +++ b/apps/examples/tutorial/myapp/MyApp.h @@ -9,7 +9,8 @@ public: MyAppFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class MyAppDialog : public AmSession @@ -19,7 +20,7 @@ class MyAppDialog : public AmSession MyAppDialog(); ~MyAppDialog(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onBye(const AmSipRequest& req); }; diff --git a/apps/examples/tutorial/mycc/MyCC.cpp b/apps/examples/tutorial/mycc/MyCC.cpp index 1a654a08..e5334197 100644 --- a/apps/examples/tutorial/mycc/MyCC.cpp +++ b/apps/examples/tutorial/mycc/MyCC.cpp @@ -44,10 +44,6 @@ int MyCCFactory::onLoad() EnterNumber = cfg.getParameter("enter_number", "/tmp/enter_number.wav"); ConnectSuffix = cfg.getParameter("connect_suffix", "@127.0.0.1"); - if (!AmSession::timersSupported()) { - ERROR("load session_timer plug-in for timers\n"); - return -1; - } cc_acc_fact = AmPlugIn::instance()->getFactory4Di("cc_acc"); if(!cc_acc_fact){ ERROR("could not load cc_acc accounting, please provide a module\n"); @@ -57,7 +53,8 @@ int MyCCFactory::onLoad() return 0; } -AmSession* MyCCFactory::onInvite(const AmSipRequest& req) +AmSession* MyCCFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { AmDynInvoke* cc_acc = cc_acc_fact->getInstance(); @@ -97,16 +94,18 @@ void MyCCDialog::addToPlaylist(string fname) { } } -void MyCCDialog::onSessionStart(const AmSipRequest& req) +void MyCCDialog::onSessionStart() { DBG("MyCCDialog::onSessionStart"); - AmB2BCallerSession::onSessionStart(req); + AmB2BCallerSession::onSessionStart(); setInOut(&playlist, &playlist); addToPlaylist(MyCCFactory::InitialAnnouncement); setDtmfDetectionEnabled(true); + + AmB2BCallerSession::onSessionStart(); } void MyCCDialog::onDtmf(int event, int duration) { @@ -116,7 +115,7 @@ void MyCCDialog::onDtmf(int event, int duration) { case CC_Collecting_PIN: { // flush the playlist (stop playing) // if a key is entered - playlist.close(); + playlist.flush(); if(event <10) { pin +=int2str(event); @@ -142,7 +141,7 @@ void MyCCDialog::onDtmf(int event, int duration) { case CC_Collecting_Number: { // flush the playlist (stop playing) // if a key is entered - playlist.close(); + playlist.flush(); if(event <10) { number +=int2str(event); DBG("number is now '%s'\n", number.c_str()); diff --git a/apps/examples/tutorial/mycc/MyCC.h b/apps/examples/tutorial/mycc/MyCC.h index 777761a4..df8ec911 100644 --- a/apps/examples/tutorial/mycc/MyCC.h +++ b/apps/examples/tutorial/mycc/MyCC.h @@ -24,7 +24,8 @@ public: MyCCFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class MyCCDialog : public AmB2BCallerSession @@ -54,7 +55,7 @@ class MyCCDialog : public AmB2BCallerSession MyCCDialog(AmDynInvoke* cc_acc); ~MyCCDialog(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onDtmf(int event, int duration); void process(AmEvent* ev); void onBye(const AmSipRequest& req); diff --git a/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.cpp b/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.cpp index 7bde13c4..ae0490a7 100644 --- a/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.cpp +++ b/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.cpp @@ -33,7 +33,8 @@ int MyConfigurableAppFactory::onLoad() return 0; } -AmSession* MyConfigurableAppFactory::onInvite(const AmSipRequest& req) +AmSession* MyConfigurableAppFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new MyConfigurableAppDialog(); } @@ -46,10 +47,12 @@ MyConfigurableAppDialog::~MyConfigurableAppDialog() { } -void MyConfigurableAppDialog::onSessionStart(const AmSipRequest& req) +void MyConfigurableAppDialog::onSessionStart() { DBG("MyConfigurableAppDialog::onSessionStart - file is '%s'\n", MyConfigurableAppFactory::AnnouncementFile.c_str()); + + AmSession::onSessionStart(); } void MyConfigurableAppDialog::onBye(const AmSipRequest& req) diff --git a/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.h b/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.h index 8c5cb355..46cffe08 100644 --- a/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.h +++ b/apps/examples/tutorial/myconfigurableapp/MyConfigurableApp.h @@ -14,7 +14,8 @@ public: MyConfigurableAppFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class MyConfigurableAppDialog : public AmSession @@ -24,7 +25,7 @@ class MyConfigurableAppDialog : public AmSession MyConfigurableAppDialog(); ~MyConfigurableAppDialog(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onBye(const AmSipRequest& req); }; diff --git a/apps/examples/tutorial/myjukebox/MyJukebox.cpp b/apps/examples/tutorial/myjukebox/MyJukebox.cpp index 143d02ac..2a60bd98 100644 --- a/apps/examples/tutorial/myjukebox/MyJukebox.cpp +++ b/apps/examples/tutorial/myjukebox/MyJukebox.cpp @@ -31,7 +31,8 @@ int MyJukeboxFactory::onLoad() return 0; } -AmSession* MyJukeboxFactory::onInvite(const AmSipRequest& req) +AmSession* MyJukeboxFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { return new MyJukeboxDialog(); } @@ -44,14 +45,14 @@ MyJukeboxDialog::MyJukeboxDialog() MyJukeboxDialog::~MyJukeboxDialog() { // clean playlist items - playlist.close(false); + playlist.flush(); // clean used AmAudioFile objects for (vector::iterator it= used_audio_files.begin(); it != used_audio_files.end();it++) delete *it; } -void MyJukeboxDialog::onSessionStart(const AmSipRequest& req) +void MyJukeboxDialog::onSessionStart() { DBG("MyJukeboxDialog::onSessionStart - jukedir is '%s'\n", MyJukeboxFactory::JukeboxDir.c_str()); diff --git a/apps/examples/tutorial/myjukebox/MyJukebox.h b/apps/examples/tutorial/myjukebox/MyJukebox.h index ad6e99c2..63006b73 100644 --- a/apps/examples/tutorial/myjukebox/MyJukebox.h +++ b/apps/examples/tutorial/myjukebox/MyJukebox.h @@ -19,7 +19,8 @@ public: MyJukeboxFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; class MyJukeboxDialog : public AmSession @@ -32,7 +33,7 @@ class MyJukeboxDialog : public AmSession MyJukeboxDialog(); ~MyJukeboxDialog(); - void onSessionStart(const AmSipRequest& req); + void onSessionStart(); void onDtmf(int event, int duration); void process(AmEvent* ev); void onBye(const AmSipRequest& req); diff --git a/apps/examples/urlcatcher/UrlCatcher.cpp b/apps/examples/urlcatcher/UrlCatcher.cpp index d5a524ba..dd694d20 100644 --- a/apps/examples/urlcatcher/UrlCatcher.cpp +++ b/apps/examples/urlcatcher/UrlCatcher.cpp @@ -60,11 +60,11 @@ int UrlCatcherFactory::onLoad() } -AmSession* UrlCatcherFactory::onInvite(const AmSipRequest& req) +AmSession* UrlCatcherFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { AmSdp sdp; - sdp.setBody(req.body.c_str()); - if (sdp.parse()) { + if (sdp.parse(req.body.c_str())) { ERROR("SDP parsing error\n"); throw AmSession::Exception(404, "Not Found Here (SDP parse error)"); } diff --git a/apps/examples/urlcatcher/UrlCatcher.h b/apps/examples/urlcatcher/UrlCatcher.h index 4da90066..5af31607 100644 --- a/apps/examples/urlcatcher/UrlCatcher.h +++ b/apps/examples/urlcatcher/UrlCatcher.h @@ -42,7 +42,8 @@ public: UrlCatcherFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); }; #endif diff --git a/apps/gateway/GWSession.cpp b/apps/gateway/GWSession.cpp index 5720d33a..116e4e96 100644 --- a/apps/gateway/GWSession.cpp +++ b/apps/gateway/GWSession.cpp @@ -65,10 +65,10 @@ void GWSession::onBye(const AmSipRequest& req) { AmSession::onBye(req); } -void GWSession::onCancel() { +void GWSession::onCancel(const AmSipRequest& req) { DBG("GWSession::onCancel\n"); int ret=((mISDNChannel*)m_OtherLeg)->hangup(); - AmSession::onCancel(); + AmSession::onCancel(req); } @@ -90,7 +90,7 @@ void GWSession::onSipRequest(const AmSipRequest& req) } } -void GWSession::onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method) { +void GWSession::onSipReply(const AmSipReply& reply, AmSipDialog::Status old_dlg_status) { int status = dlg.getStatus(); DBG("GWSession::onSipReply: code = %i, reason = %s\n, status = %i\n", reply.code,reply.reason.c_str(),dlg.getStatus()); @@ -99,7 +99,7 @@ void GWSession::onSipReply(const AmSipReply& reply, int old_dlg_status, const st int ret=((mISDNChannel*)m_OtherLeg)->hangup(); } DBG("GWSession::onSipReply calling parent\n"); - AmSession::onSipReply(reply, old_dlg_status, trans_method); + AmSession::onSipReply(reply, old_dlg_status); } void GWSession::on_stop() { diff --git a/apps/gateway/GWSession.h b/apps/gateway/GWSession.h index 8126f782..f0fabd4d 100644 --- a/apps/gateway/GWSession.h +++ b/apps/gateway/GWSession.h @@ -25,7 +25,7 @@ static GWSession* CallFromOutside(std::string &fromnumber, std::string &tonum //virtual void onDtmf(int event, int duration); //virtual void onStart(){} void onInvite(const AmSipRequest& req); - void onCancel(); + void onCancel(const AmSipRequest& req); void onSessionStart(const AmSipRequest& req); void onSessionStart(const AmSipReply& reply); //virtual void onEarlySessionStart(const AmSipReply& reply){} @@ -33,7 +33,7 @@ static GWSession* CallFromOutside(std::string &fromnumber, std::string &tonum void onBye(const AmSipRequest& req); //virtual void onSipEvent(AmSipEvent* sip_ev); void onSipRequest(const AmSipRequest& req); - void onSipReply(const AmSipReply& reply, int old_dlg_status, const string& trans_method); + void onSipReply(const AmSipReply& reply, AmSipDialog::Status old_dlg_status); //virtual void onRtpTimeout(); //virtual void onSendRequest(const string& method, const string& content_type, const string& body, string& hdrs, int flags, unsigned int cseq); //virtual void onSendReply(const AmSipRequest& req, unsigned int code,const string& reason,const string& content_type, const string& body,string& hdrs,int flags) diff --git a/apps/ivr/Ivr.cpp b/apps/ivr/Ivr.cpp index 7f6e215b..b7797f6b 100644 --- a/apps/ivr/Ivr.cpp +++ b/apps/ivr/Ivr.cpp @@ -41,7 +41,9 @@ #include #include +#include using std::set; +using std::string; #define PYFILE_REGEX "(.+)\\.(py|pyc|pyo)$" @@ -105,28 +107,6 @@ extern "C" { return Py_None; } - static PyObject* ivr_getSessionParam(PyObject*, PyObject* args) - { - char* headers; - char* header_name; - if(!PyArg_ParseTuple(args,"ss",&headers,&header_name)) - return NULL; - - string res; - string iptel_app_param = getHeader(headers, PARAM_HDR, true); - if (iptel_app_param.length()) { - res = get_header_keyvalue(iptel_app_param,header_name); - } else { - INFO("Use of P-%s is deprecated. \n", header_name); - INFO("Use '%s: %s=' instead.\n", PARAM_HDR, header_name); - - res = getHeader(headers,string("P-") + header_name, true); - } - - - return PyString_FromString(res.c_str()); - } - static PyObject* ivr_createThread(PyObject*, PyObject* args) { PyObject* py_thread_object = NULL; @@ -155,7 +135,6 @@ extern "C" { static PyMethodDef ivr_methods[] = { {"log", (PyCFunction)ivr_log, METH_VARARGS,"Log a message using Sems' logging system"}, {"getHeader", (PyCFunction)ivr_getHeader, METH_VARARGS,"Python getHeader wrapper"}, - {"getSessionParam", (PyCFunction)ivr_getSessionParam, METH_VARARGS,"Python getSessionParam wrapper"}, {"createThread", (PyCFunction)ivr_createThread, METH_VARARGS, "Create another interpreter thread"}, {"setIgnoreSigchld", (PyCFunction)ivr_ignoreSigchld, METH_VARARGS, "ignore SIGCHLD signal"}, {NULL} /* Sentinel */ @@ -360,7 +339,6 @@ bool IvrFactory::loadScript(const string& path) modName = PyString_FromString(path.c_str()); mod = PyImport_Import(modName); - Py_DECREF(modName); if (NULL != config) { // remove config ivr ivr_module while loading PyObject_DelAttrString(ivr_module, "config"); @@ -371,14 +349,21 @@ bool IvrFactory::loadScript(const string& path) PyErr_Print(); WARN("IvrFactory: Failed to load \"%s\"\n", path.c_str()); + // before python 2.4, + // it can happen that the module + // is still in the dictionnary. dict = PyImport_GetModuleDict(); Py_INCREF(dict); - PyDict_DelItemString(dict,path.c_str()); + if(PyDict_Contains(dict,modName)){ + PyDict_DelItem(dict,modName); + } Py_DECREF(dict); + Py_DECREF(modName); return false; } + Py_DECREF(modName); dict = PyModule_GetDict(mod); dlg_class = PyDict_GetItemString(dict, "IvrDialog"); @@ -418,11 +403,6 @@ bool IvrFactory::loadScript(const string& path) */ int IvrFactory::onLoad() { - if (!AmSession::timersSupported()) { - ERROR("load session_timer plug-in (for timers)\n"); - return -1; - } - if(cfg.loadFile(add2path(AmConfig::ModConfigPath,1,MOD_NAME ".conf"))) return -1; @@ -461,7 +441,7 @@ int IvrFactory::onLoad() DBG("directory '%s' opened\n",script_path.c_str()); - set unique_entries; + std::set unique_entries; regmatch_t pmatch[2]; struct dirent* entry=0; @@ -479,7 +459,7 @@ int IvrFactory::onLoad() regfree(®); AmPlugIn* plugin = AmPlugIn::instance(); - for(set::iterator it = unique_entries.begin(); + for(std::set::iterator it = unique_entries.begin(); it != unique_entries.end(); it++) { if(loadScript(*it)){ @@ -522,16 +502,16 @@ void IvrFactory::start_deferred_threads() { int IvrDialog::transfer(const string& target) { - return dlg.transfer(target); + return dlg->transfer(target); } int IvrDialog::refer(const string& target, int expires) { - return dlg.refer(target, expires); + return dlg->refer(target, expires); } int IvrDialog::drop() { - int res = dlg.drop(); + int res = dlg->drop(); if (res) setStopped(); @@ -555,15 +535,13 @@ void IvrFactory::setupSessionTimer(AmSession* s) { } /** - * Load a script using user name from URI. + * Load a script using the app_name. * Note: there is no default script. */ -AmSession* IvrFactory::onInvite(const AmSipRequest& req) +AmSession* IvrFactory::onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params) { - if(req.cmd != MOD_NAME) - return newDlg(req.cmd); - else - return newDlg(req.user); + return newDlg(app_name); } IvrDialog::IvrDialog() @@ -578,7 +556,7 @@ IvrDialog::~IvrDialog() { DBG("----------- IvrDialog::~IvrDialog() ------------- \n"); - playlist.close(false); + playlist.flush(); PYLOCK; Py_XDECREF(py_mod); @@ -619,8 +597,12 @@ PyObject_VaCallMethod(PyObject *o, char *name, char *format, va_list va) func = PyObject_GetAttrString(o, name); if (func == NULL) { - PyErr_SetString(PyExc_AttributeError, name); - return 0; + + DBG("method %s is not implemented, " + "trying default one (params: '%s')\n", + name,format); + + Py_RETURN_TRUE; } if (!PyCallable_Check(func)) @@ -666,18 +648,10 @@ bool IvrDialog::callPyEventHandler(const char* name, const char* fmt, ...) va_end(va); if(!o) { - - if(PyErr_ExceptionMatches(PyExc_AttributeError)){ - - DBG("method %s is not implemented, trying default one\n",name); - return true; - } - - PyErr_Print(); + if(PyErr_Occurred()) PyErr_Print(); } else { if(o && PyBool_Check(o) && (o == Py_True)) { - ret = true; } @@ -687,44 +661,59 @@ bool IvrDialog::callPyEventHandler(const char* name, const char* fmt, ...) return ret; } -void IvrDialog::onSessionStart(const AmSipRequest& req) +void IvrDialog::onInvite(const AmSipRequest& req) { - callPyEventHandler("onSessionStart","(s)",req.hdrs.c_str()); - setInOut(&playlist,&playlist); - AmB2BCallerSession::onSessionStart(req); + if(callPyEventHandler("onInvite","(s)",req.hdrs.c_str())) + AmB2BCallerSession::onInvite(req); } -void IvrDialog::onSessionStart(const AmSipReply& rep) +void IvrDialog::onSessionStart() { - invite_req.body = rep.body; - callPyEventHandler("onSessionStart","(s)",rep.hdrs.c_str()); + callPyEventHandler("onSessionStart",NULL); setInOut(&playlist,&playlist); - AmB2BSession::onSessionStart(rep); + AmB2BCallerSession::onSessionStart(); +} + +int IvrDialog::onSdpCompleted(const AmSdp& offer, const AmSdp& answer) +{ + AmMimeBody* sdp_body = invite_req.body.hasContentType(SIP_APPLICATION_SDP); + if(!sdp_body) { + sdp_body = invite_req.body.addPart(SIP_APPLICATION_SDP); + } + + if(sdp_body) { + string sdp_buf; + answer.print(sdp_buf); + sdp_body->setPayload((const unsigned char*)sdp_buf.c_str(), + sdp_buf.length()); + } + + return AmB2BCallerSession::onSdpCompleted(offer,answer); } void IvrDialog::onBye(const AmSipRequest& req) { if(callPyEventHandler("onBye",NULL)) - AmB2BSession::onBye(req); + AmB2BCallerSession::onBye(req); } void IvrDialog::onDtmf(int event, int duration_msec) { if(callPyEventHandler("onDtmf","(ii)",event,duration_msec)) - AmB2BSession::onDtmf(event,duration_msec); + AmB2BCallerSession::onDtmf(event,duration_msec); } void IvrDialog::onOtherBye(const AmSipRequest& req) { if(callPyEventHandler("onOtherBye",NULL)) - AmB2BSession::onOtherBye(req); + AmB2BCallerSession::onOtherBye(req); } bool IvrDialog::onOtherReply(const AmSipReply& r) { if(callPyEventHandler("onOtherReply","(is)", r.code,r.reason.c_str())) - AmB2BSession::onOtherReply(r); + AmB2BCallerSession::onOtherReply(r); return false; } @@ -745,18 +734,21 @@ void safe_Py_DECREF(PyObject* pyo) { Py_DECREF(pyo); } -void IvrDialog::onSipReply(const AmSipReply& r, int old_dlg_status, const string& trans_method) { - PyObject* pyo = getPySipReply(r); +void IvrDialog::onSipReply(const AmSipRequest& req, + const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status) +{ + PyObject* pyo = getPySipReply(reply); callPyEventHandler("onSipReply","(O)", pyo); safe_Py_DECREF(pyo); - AmB2BSession::onSipReply(r,old_dlg_status,trans_method); + AmB2BCallerSession::onSipReply(req,reply,old_dlg_status); } void IvrDialog::onSipRequest(const AmSipRequest& r){ PyObject* pyo = getPySipRequest(r); callPyEventHandler("onSipRequest","(O)", pyo); safe_Py_DECREF(pyo); - AmB2BSession::onSipRequest(r); + AmB2BCallerSession::onSipRequest(r); } void IvrDialog::onRtpTimeout() { @@ -799,32 +791,32 @@ void IvrDialog::connectCallee(const string& remote_party, const string& remote_u void IvrDialog::createCalleeSession() { AmB2BCalleeSession* callee_session = new AmB2BCalleeSession(this); - AmSipDialog& callee_dlg = callee_session->dlg; + AmSipDialog* callee_dlg = callee_session->dlg; - other_id = AmSession::getNewId(); + setOtherId(AmSession::getNewId()); - callee_dlg.local_tag = other_id; - callee_dlg.callid = AmSession::getNewId(); + callee_dlg->setLocalTag(getOtherId()); + callee_dlg->setCallid(AmSession::getNewId()); // this will be overwritten by ConnectLeg event - callee_dlg.remote_party = dlg.local_party; - callee_dlg.remote_uri = dlg.local_uri; + callee_dlg->setRemoteParty(dlg->getLocalParty()); + callee_dlg->setRemoteUri(dlg->getLocalUri()); if (b2b_callee_from_party.empty() && b2b_callee_from_uri.empty()) { // default: use the original To as From in the callee leg - callee_dlg.local_party = dlg.remote_party; - callee_dlg.local_uri = dlg.remote_uri; + callee_dlg->setLocalParty(dlg->getRemoteParty()); + callee_dlg->setLocalUri(dlg->getRemoteUri()); } else { // if given as parameters, use these - callee_dlg.local_party = b2b_callee_from_party; - callee_dlg.local_uri = b2b_callee_from_uri; + callee_dlg->setLocalParty(b2b_callee_from_party); + callee_dlg->setLocalUri(b2b_callee_from_uri); } DBG("Created B2BUA callee leg, From: %s\n", - callee_dlg.local_party.c_str()); + callee_dlg->getLocalParty().c_str()); callee_session->start(); AmSessionContainer* sess_cont = AmSessionContainer::instance(); - sess_cont->addSession(other_id,callee_session); + sess_cont->addSession(getOtherId(),callee_session); } diff --git a/apps/ivr/Ivr.h b/apps/ivr/Ivr.h index 636d3d54..c26c2613 100644 --- a/apps/ivr/Ivr.h +++ b/apps/ivr/Ivr.h @@ -108,7 +108,8 @@ class IvrFactory: public AmSessionFactory IvrFactory(const string& _app_name); int onLoad(); - AmSession* onInvite(const AmSipRequest& req); + AmSession* onInvite(const AmSipRequest& req, const string& app_name, + const map& app_params); void addDeferredThread(PyObject* pyCallable); @@ -142,8 +143,9 @@ class IvrDialog : public AmB2BCallerSession int refer(const string& target, int expires); int drop(); - void onSessionStart(const AmSipRequest& req); - void onSessionStart(const AmSipReply& rep); + void onInvite(const AmSipRequest& req); + int onSdpCompleted(const AmSdp& offer, const AmSdp& answer); + void onSessionStart(); void onBye(const AmSipRequest& req); void onDtmf(int event, int duration_msec); @@ -151,7 +153,9 @@ class IvrDialog : public AmB2BCallerSession void onOtherBye(const AmSipRequest& req); bool onOtherReply(const AmSipReply& r); - void onSipReply(const AmSipReply& r,int old_dlg_status, const string& trans_method); + void onSipReply(const AmSipRequest& req, + const AmSipReply& reply, + AmBasicSipDialog::Status old_dlg_status); void onSipRequest(const AmSipRequest& r); void onRtpTimeout(); diff --git a/apps/ivr/IvrDialogBase.cpp b/apps/ivr/IvrDialogBase.cpp index 63f6bd19..28a1d3a8 100644 --- a/apps/ivr/IvrDialogBase.cpp +++ b/apps/ivr/IvrDialogBase.cpp @@ -45,7 +45,7 @@ static PyObject* IvrDialogBase_new(PyTypeObject *type, PyObject *args, PyObject self->p_dlg = (IvrDialog*)PyCObject_AsVoidPtr(o_dlg); // initialize self.dialog - self->dialog = IvrSipDialog_FromPtr(&self->p_dlg->dlg); + self->dialog = IvrSipDialog_FromPtr(self->p_dlg->dlg); if(!self->dialog){ PyErr_Print(); ERROR("IvrDialogBase: while creating IvrSipDialog instance\n"); @@ -94,73 +94,6 @@ static PyObject* IvrDialogBase_onRtpTimeout(IvrDialogBase* self, PyObject*) return Py_None; } -// static PyObject* IvrDialogBase_onSessionStart(IvrDialogBase* self, PyObject*) -// { -// DBG("no script implementation for onSessionStart(self,hdrs) !!!\n"); - -// PyErr_SetNone(PyExc_NotImplementedError); -// return NULL; // no return value -// } - -// static PyObject* IvrDialogBase_onBye(IvrDialogBase* self, PyObject*) -// { -// DBG("no script implementation for onBye(self) !!!\n"); - -// PyErr_SetNone(PyExc_NotImplementedError); -// return NULL; // no return value -// } - -// static PyObject* IvrDialogBase_onEmptyQueue(IvrDialogBase* self, PyObject*) -// { -// DBG("no script implementation for onEmptyQueue(self) !!!\n"); - -// PyErr_SetNone(PyExc_NotImplementedError); -// return NULL; // no return value -// } - -// static PyObject* IvrDialogBase_onDtmf(IvrDialogBase* self, PyObject* args) -// { -// int key, duration; -// if(!PyArg_ParseTuple(args,"ii",&key,&duration)) -// return NULL; - -// DBG("IvrDialogBase_onDtmf(%i,%i)\n",key,duration); - -// Py_INCREF(Py_None); -// return Py_None; -// } - -// static PyObject* IvrDialogBase_onTimer(IvrDialogBase* self, PyObject* args) -// { -// DBG("IvrDialog::onTimer: no script implementation!!!\n"); - -// PyErr_SetNone(PyExc_NotImplementedError); -// return NULL; // no return value -// } - -// static PyObject* IvrDialogBase_onOtherBye(IvrDialogBase* self, PyObject*) -// { -// DBG("IvrDialogBase_onOtherBye()\n"); - -// Py_INCREF(Py_None); -// return Py_None; -// } - -// static PyObject* IvrDialogBase_onOtherReply(IvrDialogBase* self, PyObject* args) -// { -// DBG("IvrDialogBase_onOtherReply()\n"); - -// int code; -// char* reason; - -// if(!PyArg_ParseTuple(args,"is",&code,&reason)) -// return NULL; - - -// Py_INCREF(Py_None); -// return Py_None; -// } - // // Call control // @@ -191,7 +124,7 @@ static PyObject* IvrDialogBase_bye(IvrDialogBase* self, PyObject* args) if(!PyArg_ParseTuple(args,"|s", &hdrs)) return NULL; - self->p_dlg->dlg.bye(hdrs); + self->p_dlg->dlg->bye(hdrs); Py_INCREF(Py_None); return Py_None; } @@ -248,7 +181,7 @@ static PyObject* IvrDialogBase_flush(IvrDialogBase* self, PyObject* args) { assert(self->p_dlg); - self->p_dlg->playlist.close(); + self->p_dlg->playlist.flush(); Py_INCREF(Py_None); return Py_None; @@ -281,6 +214,46 @@ static PyObject* IvrDialogBase_unmute(IvrDialogBase* self, PyObject* args) return Py_None; } +static PyObject* IvrDialogBase_enableReceiving(IvrDialogBase* self, PyObject* args) +{ + assert(self->p_dlg); + + self->p_dlg->setReceiving(true); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* IvrDialogBase_disableReceiving(IvrDialogBase* self, PyObject* args) +{ + assert(self->p_dlg); + + self->p_dlg->setReceiving(false); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* IvrDialogBase_enableDTMFReceiving(IvrDialogBase* self, PyObject* args) +{ + assert(self->p_dlg); + + self->p_dlg->setForceDtmfReceiving(true); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyObject* IvrDialogBase_disableDTMFReceiving(IvrDialogBase* self, PyObject* args) +{ + assert(self->p_dlg); + + self->p_dlg->setForceDtmfReceiving(false); + + Py_INCREF(Py_None); + return Py_None; +} + static PyObject* IvrDialogBase_remove_mediaprocessor(IvrDialogBase* self, PyObject* args) { @@ -500,6 +473,18 @@ IvrDialogBase_refer(IvrDialogBase *self, PyObject* args) } +static PyObject* +IvrDialogBase_getAppParam(IvrDialogBase *self, PyObject* args) +{ + const char* param_name; + if(!PyArg_ParseTuple(args,"s",¶m_name)) + return NULL; + + string app_param = self->p_dlg->getAppParam(param_name); + return PyString_FromString(app_param.c_str()); +} + + static PyMethodDef IvrDialogBase_methods[] = { @@ -557,6 +542,18 @@ static PyMethodDef IvrDialogBase_methods[] = { {"unmute", (PyCFunction)IvrDialogBase_unmute, METH_NOARGS, "unmute the RTP stream (send packets)" }, + {"enableReceiving", (PyCFunction)IvrDialogBase_enableReceiving, METH_NOARGS, + "enable receiving of RTP packets" + }, + {"disableReceiving", (PyCFunction)IvrDialogBase_disableReceiving, METH_NOARGS, + "disable receiving of RTP packets" + }, + {"enableDTMFReceiving", (PyCFunction)IvrDialogBase_enableDTMFReceiving, METH_NOARGS, + "enable receiving of RFC-2833 DTMF packets even if RTP receiving is disabled" + }, + {"disableDTMFReceiving", (PyCFunction)IvrDialogBase_disableDTMFReceiving, METH_NOARGS, + "disable receiving of RFC-2833 DTMF packets when RTP receiving is disabled" + }, {"connectMedia", (PyCFunction)IvrDialogBase_add_mediaprocessor, METH_NOARGS, "enable the processing of audio and RTP" }, @@ -598,6 +595,10 @@ static PyMethodDef IvrDialogBase_methods[] = { {"removeTimers", (PyCFunction)IvrDialogBase_removeTimers, METH_NOARGS, "remove all timers" }, + // App params + {"getAppParam", (PyCFunction)IvrDialogBase_getAppParam, METH_VARARGS, + "retrieves an application parameter" + }, {NULL} /* Sentinel */ }; diff --git a/apps/ivr/IvrSipDialog.cpp b/apps/ivr/IvrSipDialog.cpp index 3088cfd2..1b788bc9 100644 --- a/apps/ivr/IvrSipDialog.cpp +++ b/apps/ivr/IvrSipDialog.cpp @@ -52,17 +52,17 @@ static PyObject* IvrSipDialog_new(PyTypeObject *type, PyObject *args, PyObject * return PyString_FromString(self->p_dlg->attr.c_str()); \ } \ -def_IvrSipDialog_GETTER(IvrSipDialog_getuser, user) -def_IvrSipDialog_GETTER(IvrSipDialog_getdomain, domain) -def_IvrSipDialog_GETTER(IvrSipDialog_getlocal_uri, local_uri) -def_IvrSipDialog_GETTER(IvrSipDialog_getremote_uri, remote_uri) -def_IvrSipDialog_GETTER(IvrSipDialog_getcontact_uri, contact_uri) -def_IvrSipDialog_GETTER(IvrSipDialog_getcallid, callid) -def_IvrSipDialog_GETTER(IvrSipDialog_getremote_tag, remote_tag) -def_IvrSipDialog_GETTER(IvrSipDialog_getlocal_tag, local_tag) -def_IvrSipDialog_GETTER(IvrSipDialog_getremote_party, remote_party) -def_IvrSipDialog_GETTER(IvrSipDialog_getlocal_party, local_party) -def_IvrSipDialog_GETTER(IvrSipDialog_getroute, route) +def_IvrSipDialog_GETTER(IvrSipDialog_getuser, getUser()) +def_IvrSipDialog_GETTER(IvrSipDialog_getdomain, getDomain()) +def_IvrSipDialog_GETTER(IvrSipDialog_getlocal_uri, getLocalUri()) +def_IvrSipDialog_GETTER(IvrSipDialog_getremote_uri, getRemoteUri()) +//def_IvrSipDialog_GETTER(IvrSipDialog_getcontact_uri, contact_uri) +def_IvrSipDialog_GETTER(IvrSipDialog_getcallid, getCallid()) +def_IvrSipDialog_GETTER(IvrSipDialog_getremote_tag, getRemoteTag()) +def_IvrSipDialog_GETTER(IvrSipDialog_getlocal_tag, getLocalTag()) +def_IvrSipDialog_GETTER(IvrSipDialog_getremote_party, getRemoteParty()) +def_IvrSipDialog_GETTER(IvrSipDialog_getlocal_party, getLocalParty()) +def_IvrSipDialog_GETTER(IvrSipDialog_getroute, getRoute()) def_IvrSipDialog_GETTER(IvrSipDialog_getoutbound_proxy, outbound_proxy) #define def_IvrSipDialog_SETTER(setter_name, attr) \ @@ -73,17 +73,11 @@ def_IvrSipDialog_GETTER(IvrSipDialog_getoutbound_proxy, outbound_proxy) if(!PyArg_Parse(value,"s",&text)) \ return -1; \ \ - self->p_dlg->attr = text; \ + self->p_dlg->attr(text); \ return 0; \ } -def_IvrSipDialog_SETTER(IvrSipDialog_setremote_uri, remote_uri) - -// static PyObject* -// IvrSipDialog_getuser(IvrSipDialog *self, void *closure) -// { -// return PyString_FromString(self->p_dlg->user.c_str()); -// } +def_IvrSipDialog_SETTER(IvrSipDialog_setremote_uri, setRemoteUri) static PyObject* IvrSipDialog_getcseq(IvrSipDialog *self, void *closure) @@ -91,12 +85,24 @@ IvrSipDialog_getcseq(IvrSipDialog *self, void *closure) return PyInt_FromLong(self->p_dlg->cseq); } +static PyObject* +IvrSipDialog_getstatus(IvrSipDialog *self, void *closure) +{ + return PyInt_FromLong((int)self->p_dlg->getStatus()); +} + +static PyObject* +IvrSipDialog_getstatusstr(IvrSipDialog *self, void *closure) +{ + return PyString_FromString((char*)self->p_dlg->getStatusStr()); +} + static PyGetSetDef IvrSipDialog_getset[] = { {(char*)"user", (getter)IvrSipDialog_getuser, NULL, (char*)"local user", NULL}, {(char*)"domain", (getter)IvrSipDialog_getdomain, NULL, (char*)"local domain", NULL}, {(char*)"local_uri", (getter)IvrSipDialog_getlocal_uri, NULL, (char*)"local uri", NULL}, {(char*)"remote_uri", (getter)IvrSipDialog_getremote_uri, (setter)IvrSipDialog_setremote_uri, (char*)"remote uri", NULL}, - {(char*)"contact_uri", (getter)IvrSipDialog_getcontact_uri, NULL, (char*)"pre-calculated contact uri", NULL}, + //{(char*)"contact_uri", (getter)IvrSipDialog_getcontact_uri, NULL, (char*)"pre-calculated contact uri", NULL}, {(char*)"callid", (getter)IvrSipDialog_getcallid, NULL, (char*)"call id", NULL}, {(char*)"remote_tag", (getter)IvrSipDialog_getremote_tag, NULL, (char*)"remote tag", NULL}, {(char*)"local_tag", (getter)IvrSipDialog_getlocal_tag, NULL, (char*)"local tag", NULL}, @@ -105,6 +111,11 @@ static PyGetSetDef IvrSipDialog_getset[] = { {(char*)"route", (getter)IvrSipDialog_getroute, NULL, (char*)"record routing", NULL}, {(char*)"outbound_proxy", (getter)IvrSipDialog_getoutbound_proxy, NULL, (char*)"outbound proxy", NULL}, {(char*)"cseq", (getter)IvrSipDialog_getcseq, NULL, (char*)"CSeq for next request", NULL}, + + {(char*)"status_str", (getter)IvrSipDialog_getstatusstr, NULL, (char*)"Dialog status " + "(Disconnected, Trying, Proceeding, Cancelling, Early, Connected, Disconnecting)", NULL}, + {(char*)"status", (getter)IvrSipDialog_getstatus, NULL, (char*)"Dialog status (0..6)", NULL}, + {NULL} /* Sentinel */ }; diff --git a/apps/ivr/IvrSipReply.cpp b/apps/ivr/IvrSipReply.cpp index 2fac517e..b66942c3 100644 --- a/apps/ivr/IvrSipReply.cpp +++ b/apps/ivr/IvrSipReply.cpp @@ -75,12 +75,12 @@ IvrSipReply_dealloc(IvrSipReply* self) def_IvrSipReply_GETTER(IvrSipReply_getreason, reason) -def_IvrSipReply_GETTER(IvrSipReply_getnext_request_uri, next_request_uri) -def_IvrSipReply_GETTER(IvrSipReply_gethdrs, hdrs) -def_IvrSipReply_GETTER(IvrSipReply_getremote_tag, remote_tag) -def_IvrSipReply_GETTER(IvrSipReply_getlocal_tag, local_tag) -def_IvrSipReply_GETTER(IvrSipReply_getroute, route) -def_IvrSipReply_GETTER(IvrSipReply_getbody, body) +def_IvrSipReply_GETTER(IvrSipReply_getcontact, contact) +def_IvrSipReply_GETTER(IvrSipReply_gethdrs, hdrs) +def_IvrSipReply_GETTER(IvrSipReply_getfrom_tag, from_tag) +def_IvrSipReply_GETTER(IvrSipReply_getto_tag, to_tag) +def_IvrSipReply_GETTER(IvrSipReply_getroute, route) +//def_IvrSipReply_GETTER(IvrSipReply_getbody, body) static PyObject* IvrSipReply_getcseq(IvrSipReply *self, void *closure) @@ -97,12 +97,12 @@ IvrSipReply_getcode(IvrSipReply *self, void *closure) static PyGetSetDef IvrSipReply_getset[] = { {(char*)"code", (getter)IvrSipReply_getcode, NULL, (char*)"code", NULL}, {(char*)"reason", (getter)IvrSipReply_getreason, NULL, (char*)"reason", NULL}, - {(char*)"next_request_uri",(getter)IvrSipReply_getnext_request_uri, NULL, (char*)"next_request_uri", NULL}, + {(char*)"contact",(getter)IvrSipReply_getcontact, NULL, (char*)"contact", NULL}, {(char*)"route", (getter)IvrSipReply_getroute, NULL, (char*)"route", NULL}, {(char*)"hdrs", (getter)IvrSipReply_gethdrs, NULL, (char*)"hdrs", NULL}, - {(char*)"body", (getter)IvrSipReply_getbody, NULL, (char*)"body", NULL}, - {(char*)"remote_tag", (getter)IvrSipReply_getremote_tag, NULL, (char*)"remote_tag", NULL}, - {(char*)"local_tag", (getter)IvrSipReply_getlocal_tag, NULL, (char*)"local_tag", NULL}, + //{(char*)"body", (getter)IvrSipReply_getbody, NULL, (char*)"body", NULL}, + {(char*)"from_tag", (getter)IvrSipReply_getfrom_tag, NULL, (char*)"from_tag", NULL}, + {(char*)"to_tag", (getter)IvrSipReply_getto_tag, NULL, (char*)"to_tag", NULL}, {(char*)"cseq", (getter)IvrSipReply_getcseq, NULL, (char*)"cseq", NULL}, {NULL} /* Sentinel */ }; diff --git a/apps/ivr/IvrSipRequest.cpp b/apps/ivr/IvrSipRequest.cpp index 4ce23743..f07cfe8f 100644 --- a/apps/ivr/IvrSipRequest.cpp +++ b/apps/ivr/IvrSipRequest.cpp @@ -120,7 +120,6 @@ def_IvrSipRequest_GETTER(IvrSipRequest_getcallid, callid) def_IvrSipRequest_GETTER(IvrSipRequest_getfrom_tag, from_tag) def_IvrSipRequest_GETTER(IvrSipRequest_getto_tag, to_tag) def_IvrSipRequest_GETTER(IvrSipRequest_getroute, route) -def_IvrSipRequest_GETTER(IvrSipRequest_getbody, body) def_IvrSipRequest_GETTER(IvrSipRequest_gethdrs, hdrs) #undef def_IvrSipRequest_GETTER @@ -136,6 +135,18 @@ IvrSipRequest_getcseq(IvrSipRequest *self, void *closure) return PyInt_FromLong(self->p_req->cseq); } +static PyObject* +IvrSipRequest_getbody(IvrSipRequest *self, void *closure) +{ + if(self->p_req->body.getLen()) + { + string body; + self->p_req->body.print(body); + return PyString_FromString(body.c_str()); + } + return PyString_FromString(""); +} + #define def_IvrSipRequest_SETTER(setter_name, attr) \ static int \ setter_name(IvrSipRequest *self, PyObject* value, void *closure) \ diff --git a/apps/ivr/IvrUAC.cpp b/apps/ivr/IvrUAC.cpp index 658e96c5..5b2e10e9 100644 --- a/apps/ivr/IvrUAC.cpp +++ b/apps/ivr/IvrUAC.cpp @@ -49,7 +49,9 @@ static PyObject* IvrUAC_dialout(IvrUAC* self, PyObject* args) &from, &from_uri, &to)) return NULL; - AmUAC::dialout(user, app_name, r_uri, + string app_name_s(app_name); + + AmUAC::dialout(user, app_name_s, r_uri, from, from_uri, to); Py_INCREF(Py_None); diff --git a/apps/ivr/Makefile.ivr_application b/apps/ivr/Makefile.ivr_application index aff01fad..981349ba 100644 --- a/apps/ivr/Makefile.ivr_application +++ b/apps/ivr/Makefile.ivr_application @@ -10,7 +10,7 @@ include $(COREPATH)/../Makefile.defs include $(IVRPATH)/Makefile.defs # -ivr-modules-dir?=lib/sems/ivr +ivr-modules-dir?=lib/$(APP_NAME)/ivr LIB_INSTALLDIR?=$(modules-prefix)/$(ivr-modules-dir) TARBALL_PREFIX=sems-app-${NAME} diff --git a/apps/jsonrpc/CMakeLists.txt b/apps/jsonrpc/CMakeLists.txt index 1de45d93..3d053fff 100644 --- a/apps/jsonrpc/CMakeLists.txt +++ b/apps/jsonrpc/CMakeLists.txt @@ -8,5 +8,5 @@ RpcServerThread.cpp ADD_DEFINITIONS(-I${LIBEV_INCLUDE_DIR}) SET(sems_module_name jsonrpc) -SET(sems_module_libs ${sems_module_libs} ${LIBEV_LIBRARIES}) +SET(sems_module_libs ${sems_module_libs} ev) INCLUDE(${CMAKE_SOURCE_DIR}/cmake/module.rules.txt) diff --git a/apps/jsonrpc/JsonRPCEvents.h b/apps/jsonrpc/JsonRPCEvents.h index 172050f7..ea033684 100644 --- a/apps/jsonrpc/JsonRPCEvents.h +++ b/apps/jsonrpc/JsonRPCEvents.h @@ -39,7 +39,7 @@ using std::string; #define JSONRPC_MSG_REQUEST 0 #define JSONRPC_MSG_RESPONSE 1 #define JSONRPC_MSG_ERROR 2 -class JsonrpcNetstringsConnection; +struct JsonrpcNetstringsConnection; struct JsonRpcEvent : public AmEvent { diff --git a/apps/mailbox/mailbox.py b/apps/mailbox/mailbox.py index 5ab5a1c3..d4aa00ab 100644 --- a/apps/mailbox/mailbox.py +++ b/apps/mailbox/mailbox.py @@ -13,10 +13,10 @@ class IvrDialog(IvrDialogBase): voice_msg = None mailbox = None - def onSessionStart(self,hdrs): + def onSessionStart(self): debug("config: %s" % repr(config)) - self.mailbox = IMAP4_Mailbox(getSessionParam(hdrs,"Mailbox-URL")); + self.mailbox = IMAP4_Mailbox(getAppParam("Mailbox-URL")); debug("***** Mailbox Url: ******\n" + str(self.mailbox.url)) self.announcement = IvrAudioFile() diff --git a/apps/mailbox/mailbox_query.py b/apps/mailbox/mailbox_query.py index 7654df8c..14e5b929 100644 --- a/apps/mailbox/mailbox_query.py +++ b/apps/mailbox/mailbox_query.py @@ -106,12 +106,12 @@ class IvrDialog(IvrDialogBase): return msg_list - def onSessionStart(self,hdrs): + def onSessionStart(self): self.__init__() self.loadVoiceLib() - self.mailbox = IMAP4_Mailbox(getSessionParam(hdrs,"Mailbox-URL")); + self.mailbox = IMAP4_Mailbox(getAppParam("Mailbox-URL")); debug("***** Mailbox Url: ******\n" + str(self.mailbox.url)) self.new_msgs = self.loadMsgList('UNSEEN') diff --git a/apps/mobile_push/etc/mobile_push.conf b/apps/mobile_push/etc/mobile_push.conf new file mode 100644 index 00000000..088ec27a --- /dev/null +++ b/apps/mobile_push/etc/mobile_push.conf @@ -0,0 +1,45 @@ + +# configuration for mobile_push app + +# DSM configuration +diag_path=/usr/lib/sems/mobile_push +load_diags=mobile_push +register_apps=mobile_push +mod_path=/usr/lib/sems/plug-in/ +preload_mods=mod_xml +run_invite_event=yes +set_param_variables=yes +#run_system_dsms= + +# application configuration: + +# enable_rbt=[yes|no] - enable playing of ring-back tone from DB? +enable_rbt=yes + +# database connection: +db_url="mysql://root:frafos@localhost/provisioning" + +# play_looped=[yes|no] - loop file? +play_looped=yes + +# mobile push web app server URL: +push_server_url=http://127.0.0.1:8000/mobilepush/ + +# use_post=[yes|no] - use HTTP POST? (or GET) +use_post=no + +# subscription user/pwd (From in subscription) +subscription_user=sub_user +subscription_pwd=sub_pwd + +# proxy for subscription (outbound-proxy setting) +#proxy=sip:127.0.0.1:5060 + +# extra_3xx_uri_append - append extra string to Contact in 300 (e.g. port) +# extra_3xx_uri_append=":5090" + +# use_subscription_id=[yes|no] - use an ID for SUBSCRIBE? (problems with kamailio :( ) +use_subscription_id=no + + + diff --git a/apps/mobile_push/load_test/call_gen.py b/apps/mobile_push/load_test/call_gen.py new file mode 100755 index 00000000..49501802 --- /dev/null +++ b/apps/mobile_push/load_test/call_gen.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +from xmlrpclib import * +s = ServerProxy('http://127.0.0.1:8092') +print s.setTarget(100, 1, 0, '4', '192.168.5.110', 2, 0, 30, 40) + diff --git a/apps/mobile_push/load_test/example_server.py b/apps/mobile_push/load_test/example_server.py new file mode 100644 index 00000000..b5643087 --- /dev/null +++ b/apps/mobile_push/load_test/example_server.py @@ -0,0 +1,36 @@ +import time +import BaseHTTPServer + + +HOST_NAME = '127.0.0.1' # !!!REMEMBER TO CHANGE THIS!!! +PORT_NUMBER = 8000 # Maybe set this to 9000. + + +class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): + def do_HEAD(s): + s.send_response(200) + s.send_header("Content-type", "text/html") + s.end_headers() + def do_GET(s): + """Respond to a GET request.""" + s.send_response(200) + s.send_header("Content-type", "text/html") + s.end_headers() + s.wfile.write("Title goes here.") + s.wfile.write("

This is a test.

") + # If someone went to "http://something.somewhere.net/foo/bar/", + # then s.path equals "/foo/bar/". + s.wfile.write("

You accessed path: %s

" % s.path) + s.wfile.write("") + +if __name__ == '__main__': + server_class = BaseHTTPServer.HTTPServer + httpd = server_class((HOST_NAME, PORT_NUMBER), MyHandler) + print time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER) + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + httpd.server_close() + print time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER) + diff --git a/apps/mobile_push/load_test/mobilepush_loadtest.txt b/apps/mobile_push/load_test/mobilepush_loadtest.txt new file mode 100644 index 00000000..ff0d549e --- /dev/null +++ b/apps/mobile_push/load_test/mobilepush_loadtest.txt @@ -0,0 +1,105 @@ +this is a small set of scripts and config files to do load tests on the +mobile-push application. It uses another SEMS instance to register/deregister +users using db_reg_agent module, and place calls using call_gen application. + +both sems instances are expected to run on 192.168.5.110 - if that IP is +different, it needs to be changed in some (many) places. + +1. create users 400..499 (pwd 400..499) in kamailio with + for ((i=400;i<500;i++)); do kamctl add $i $i; done + + 1.b for Kamailio from packages (default config), + - enable: + #!define WITH_MYSQL + #!define WITH_PRESENCE + undefine WITH_AUTH + + - add to presence loading section: + #!ifdef WITH_PRESENCE + loadmodule "presence.so" + loadmodule "presence_xml.so" + + # reginfo related + loadmodule "presence_reginfo.so" + loadmodule "pua.so" + loadmodule "pua_reginfo.so" + #!endif + + - add to presence config section: + #!ifdef WITH_PRESENCE + # ----- presence params ----- + modparam("presence", "db_url", DBURL) + + # ----- presence_xml params ----- + modparam("presence_xml", "db_url", DBURL) + modparam("presence_xml", "force_active", 1) + + # reginfo related + modparam("pua_reginfo", "default_domain", "192.168.5.110") + modparam("pua_reginfo", "publish_reginfo", 1) + modparam("pua_reginfo", "server_address", "sip:reginfo@192.168.5.110") + + - remove auth check in request_route (easier) + # authentication + # if (!is_method("SUBSCRIBE|NOTIFY")) { + # route(AUTH); + # } + + - add SERVICES route to route calls to sems: + # dispatch requests to foreign domains + route(SIPOUT); + + route(SERVICES); + + ... + + route[SERVICES] { + + if (method=="INVITE") { + if ((uri=~"sip:38") || (uri=~"sip:4")) + { + + # $ru = "sip:" + $rU + "@" + "192.168.5.110:5080"; + append_hf("P-App-Param: audio_id=connecting;caller=+492222222222;callee=+491111111111;domain=sip.sipwise.com\r\n"); + append_hf("P-hint: services\r\n"); + t_relay_to("192.168.5.110:5080"); + exit(); + } + } + } + + +2. create database tables for sems with: + +CREATE TABLE `registrations` ( + `subscriber_id` int(11) NOT NULL, + `registration_status` tinyint(1) NOT NULL DEFAULT '0', + `last_registration` datetime NOT NULL, + `expiry` datetime NOT NULL, + `last_code` smallint(2) NOT NULL, + `last_reason` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `contacts` varchar(512) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + PRIMARY KEY (`subscriber_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `subscribers` ( + `subscriber_id` int(10) NOT NULL AUTO_INCREMENT, + `user` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `pass` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `realm` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `contact` varchar(256) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + PRIMARY KEY (`subscriber_id`) +) ENGINE=MyISAM AUTO_INCREMENT=20005 DEFAULT CHARSET=latin1; + +3. set mysql pwd in db_reg_agent.conf + +4. start load gen sems instance with the configuration given here. + (git clone git://git.sip-router.org/sems ; cp -R sems_cfg/* sems ; cd sems ; make -C apps/xmlrpc2di ; + make -C apps/examples/call_gen; make -C apps/db_reg_agent; make -C core ; cd core ; + ./sems -f etc/sems.conf -D 3 -E ) + +5. run reg_client.py, which will very quickly register and deregister subscribers + by calling removeRegistration/createRegistration on the load gen sems instance + +6. run call_gen.py to set a target of 100 calls (or run sth like +s.createCalls(1, 1, 0, '4', '192.168.5.110', 2, 0, 30, 40) to create one call for testing first diff --git a/apps/mobile_push/load_test/reg_client.py b/apps/mobile_push/load_test/reg_client.py new file mode 100755 index 00000000..01a4972b --- /dev/null +++ b/apps/mobile_push/load_test/reg_client.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +from xmlrpclib import * +from random import * +import time + +s = ServerProxy('http://127.0.0.1:8092') + +r = set() + +while True: + n = randint(400, 499) + if n in r: + print s.removeRegistration(n) + r.remove(n) + else: + print s.createRegistration(n, str(n), str(n), '192.168.5.110') + r.add(n) + + time.sleep(.1) \ No newline at end of file diff --git a/apps/mobile_push/load_test/sems_cfg/core/etc/callgen.conf b/apps/mobile_push/load_test/sems_cfg/core/etc/callgen.conf new file mode 100644 index 00000000..401615ad --- /dev/null +++ b/apps/mobile_push/load_test/sems_cfg/core/etc/callgen.conf @@ -0,0 +1,3 @@ +play_file=wav/default_en.wav +digits_dir=../apps/examples/call_gen/wav/ +from_host=callgen.example.net \ No newline at end of file diff --git a/apps/mobile_push/load_test/sems_cfg/core/etc/db_reg_agent.conf b/apps/mobile_push/load_test/sems_cfg/core/etc/db_reg_agent.conf new file mode 100644 index 00000000..4f878350 --- /dev/null +++ b/apps/mobile_push/load_test/sems_cfg/core/etc/db_reg_agent.conf @@ -0,0 +1,99 @@ +# Database connection +mysql_user=root +mysql_passwd=mysqlrootpwd + +# mysql_server, default: localhost +# mysql_server=localhost + +#mysql_db, default: sems +# mysql_db=sems + +# table for registration status +# default: registrations +# registrations_table="registrations" + +# query joining subscriber info with registration table +joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;" + +#example with contact: +# joined_query="select subscribers.subscriber_id as subscriber_id, subscribers.user as user, subscribers.pass as pass, subscribers.realm as realm, subscribers.contact as contact, registrations.registration_status as registration_status, registrations.expiry as expiry, registrations.last_registration as last_registration from subscribers left join registrations on subscribers.subscriber_id=registrations.subscriber_id;" + +# outbound_proxy= +# set the outbound proxy to send registrations through +# +#outbound_proxy=sip:10.0.0.2:5070 + +# contact_hostport= - overriding contact host:port +# sets the contact host:port portion that is registered +# default: empty +#contact_hostport=10.0.0.50:5080 + +# db_read_contact=[yes, no] : read individual contact to register from DB? +# note: joined_query must select the contact, too +# default: no +#db_read_contact=yes + +#save_contacts=[yes, no] : save contacts? +# for monitoring and troubleshooting, all contacts returned with a positive reply +# may be saved to DB in the registrations.contacts colums +# default: yes +#save_contacts=yes + +#save_auth_replies=[yes, no] : update status on 401 replies (auth)? +# usually, 401 replies which are resent with auth are not saved. if save_auth_replies=yes, +# last_code and last_reason in DB is updated with the 401 +#save_auth_replies=yes + +# expires: desired expires, i.e. expires value that is requested +# default: 7200 +# expires=300 + +# error_retry_interval +# +# default: 300 + +# reregister_interval: fraction of actual expires after which register is refreshed +# default: reregister_interval=0.5 +#reregister_interval=0.5 + +# minimum_reregister_interval: if set, re-register is scheduled in least loaded time +# in minimum_reregister_interval .. reregister_interval i order to smooth load spikes +# must be smaller than reregister_interval +# default: off +# +# example: +# reregister_interval=0.5 +# minimum_reregister_interval=0.4 +# on a registration expiring in 3600s, the least loaded spot between 1440s and 1800s +# is chosen +# +#minimum_reregister_interval=0.4 + +# enable_ratelimiting=yes : Enable ratelimiting? +# default: no +# if enabled, the amount of initial REGISTER requests is limited (not counting re-trans- +# missions and requests re-sent for authentication) +#enable_ratelimiting=yes + +# ratelimit_rate=300 : rate of initial REGISTER requests to send as maximum +#ratelimit_rate=2 + +# ratelimit_per=1 : per time unit (in seconds, e.g. 300 REGISTER in 1 second) +#ratelimit_per=1 + +#ratelimit_slowstart=[yes|no] : on startup send initially ratelimit_rate or 0 requests +#default: no +#ratelimit_slowstart=yes + +# delete_removed_registrations=yes : delete removed registrations from registrations +# table in DB? (otherwise they will stay with STATUS_REMOVED) +# default: yes +# +#delete_removed_registrations=no + +# delete_failed_deregistrations=yes : delete failed de-registrations from registrations +# table in DB? (otherwise they will stay with STATUS_TO_BE_REMOVED) +# only applicable if delete_removed_registrations=yes +# default: no +# +#delete_failed_deregistrations=no diff --git a/apps/mobile_push/load_test/sems_cfg/core/etc/sems.conf b/apps/mobile_push/load_test/sems_cfg/core/etc/sems.conf new file mode 100644 index 00000000..a28fa2c8 --- /dev/null +++ b/apps/mobile_push/load_test/sems_cfg/core/etc/sems.conf @@ -0,0 +1,544 @@ +# $Id$ +# +# sems.conf.sample +# +# Sip Express Media Server (sems) +# +# sample configuration file +# +# +# whitespaces (spaces and tabs) are ignored +# comments start with a "#" and may be used inline +# +# example: option=value # i like this option +# +# @filename includes mod_config_path/filename +# @/absolute/path/to/file includes file + +############################################################ +# Network configuration + +# optional parameter: media_ip=| +# +# - this informs SEMS about the IP address or interface that +# SEMS uses to send and receive media. +# - If neither 'media_ip' nor 'sip_ip' are set, defaults +# to first non-loopback interface. If 'sip_ip' is set, +# 'media_ip' defaults to 'sip_ip. +# +# Examples: +# media_ip=10.0.0.34 +# media_ip=eth0 + +# optional parameter: sip_ip=| +# +# - this informs SEMS about the SIP IP where its SIP stack is +# bound to or should be bound to. This also sets +# the value used for contact header in outgoing calls and +# registrations. +# - If neither 'media_ip' nor 'sip_ip' are set, defaults +# to first non-loopback interface. If 'media_ip' is set, +# 'sip_ip' defaults to 'media_ip. +# +# Example: +# sip_ip=10.0.0.34 +# sip_ip=en0 +# + +# optional parameter: public_ip= +# +# - when running SEMS behind certain simple NAT configurations, +# you can use this parameter to inform SEMS of its public IP +# address. If this parameter is set, SEMS will write this value +# into SDP bodies. +# If this parameter is not set, the local IP address is used. +# N.B., there is no support for port translation; the local +# RTP port is advertised in SDP in either case. +# +# Example: +# public_ip=75.101.219.48 +# + +# optional parameter: sip_port= +# +# - this informs SEMS about the port where its SIP stack is +# bound to or should be bound to. SEMS needs this information +# to correctly set the contact header in outgoing calls +# and registrations. Should be set to equal the 'port' +# configuration option in ser_sems.cfg. +# +# default: 5060 +# +sip_port=5080 + +# optional parameter: outbound_proxy=uri +# +# - this sets an outbound proxy for calls and registrations initiated +# by SEMS. This does not apply to requests in a dialog that +# is initiated by someone else and incoming to SEMS. +# If this is not set (default setting), then for dialogs +# initiated by SEMS the r-uri is resolved and the request +# is sent there directly. +# This is resolved by the SIP stack with DNS if a name is given. +# Warning: If the value set here can not be resolved, no +# requests will be sent out at all! +# +# default: empty +# +# Example: +# outbound_proxy=sip:proxy.mydomain.net + +# optional parameter: force_outbound_proxy={yes|no} +# +# - forces SEMS to send any request to the outbound proxy in any +# situation, by adding an extra first Route to the outbound_proxy. +# This option will only have an effect if the outbound_proxy +# option has been set, and it will break 3261 compatibility +# in some cases; better use next_hop_ip/next_hop_port. +# +# default: no +# +# Example: +# force_outbound_proxy=yes + +# optional parameter: next_hop_ip +# - if this is set, all outgoing requests will be sent to +# this IP, regardless of R-URI etc. +# +#next_hop_ip=192.168.5.106 + +# optional parameter: next_hop_port +# defaults to 5060 +#next_hop_port=5060 + +# optional parameter:next_hop_for_replies +# - use next_hop for replies, too? +# +#next_hop_for_replies=yes + +# optional parameter: rtp_low_port= +# +# - sets lowest for RTP used port +rtp_low_port=10000 + +# optional parameter: rtp_high_port= +# +# - sets highest for RTP used port +rtp_high_port=60000 + +# Additional IFs (optional): +# additional_interfaces = +# +# additional_interfaces must be set if more than one +# interface is to be used for the same purpose (e.g. +# more than one interface for SIP). Configure additional +# interfaces if networks should be bridged or separate +# networks should be served. +# +# For each additional interface, a set of parameters +# suffixed with the interface name should be listed +# with the 'additional_interfaces' parameter. +# +# Please note that for each additional interface, +# 'sip_ip_[if_name]' is mandatory (but can be the interface +# name, then the first assigned IP is used). The other +# parameters are optional. 'media_ip_[if_name]' +# is derived from 'sip_ip_[if_name]' if not set. +# 'public_ip_[ip_name]' is also based on 'sip_ip_[if_name]' +# if not set explicitly. +# +# Example: +# additional_interfaces=intern,extern +# +# sip_ip_intern=192.168.0.5 +# sip_port_intern=5060 +# media_ip_intern=192.168.10.5 +# rtp_low_port_intern=2000 +# rtp_high_port_intern=5000 +# +# sip_ip_extern=213.192.59.73 +# sip_port_extern=5060 +# media_ip_extern=213.192.59.73 +# rtp_low_port_extern=2000 +# rtp_high_port_extern=5000 +# public_ip_extern=213.192.35.73 +# + +############################################################ +# modules and application configuration +# +# Configuration of plugin (module) loading: +# - if load_plugins is set, only those are loaded. +# - if load_plugins is not set, all modules in the plugin_path +# directory are loaded, except those which are listed +# in exclude_plugins. +# + +# optional parameter: plugin_path= +# +# - sets the path to the plug-ins' binaries +# - may be absolute or relative to CWD +plugin_path=/usr/local/lib/sems/plug-in/ + +# optional parameter: load_plugins= +# +# semicolon-separated list of modules to load. +# If empty, all modules in plugin_path are loaded. +# +# example for announcement with only g711 and ilbc codecs +# load_plugins=wav;ilbc;announcement + +# optional parameter: exclude_plugins= +# +# semicolon-separated list of modules to exclude from loading +# ('blacklist'). If empty, all modules in plugin_path are loaded. +# This has only effect it load_plugins is not set. +# +# o precoded_announce: no precoded sample files present +# o py_sems: conflicts with ivr (in some cases) +# o db_reg_agent: needs DB tables +exclude_plugins=precoded_announce;py_sems;db_reg_agent + +# optional: load_plugins_rtld_global= +# +# load these plugins with RTLD_GLOBAL (by default py_sems, +# dsm, ivr, sbc, diameter_client, registrar_client, uac_auth) + +# optional parameter: application +# +# This controls which application is to be executed if there +# is no explicit application requested from the SIP stack +# (i.e. unixsockctrl and second parameter of t_write_unix). +# +# This can be one of +# $(ruri.user) - user part of ruri is taken as application, +# e.g. sip:announcement@host +# $(ruri.param) - uri parameter "app", e.g. +# sip:joe@host.net;app=announcement +# $(apphdr) - the value of the P-App-Name header is used +# +# $(mapping) - regex=>application mapping is read from +# app_mapping.conf (see app_mapping.conf) +# - application name configured here, e.g. +# application=announcement +# +# examples: +# application = conference +# application = $(mapping) +# application = $(ruri.user) +# application = $(ruri.param) +application = $(apphdr) + +# parameter: plugin_config_path= +# +# - in this path configuration files of the applications +# (e.g. announcement.conf) are searched +plugin_config_path=/usr/local/etc/sems/etc/ + +# optional parameter: exclude_payloads= +# +# semicolon-separated list of payloads to exclude from loading +# ('blacklist'). +# +# For example, to only use low bandwidth codecs: +# exclude_payloads=PCMU;PCMA;G726-40;G726-32;G721;L16 +# or, to use only codecs which are not CPU-intensive: +# exclude_payloads=iLBC;speex; +# only use G711 (exclude everything else): +# exclude_payloads=iLBC;speex;G726-40;G726-32;G721;G726-24;G726-16;GSM;L16 + +############################################################ +# logging and running + +# optional parameter: fork={yes|no} +# +# - specifies if sems should run in daemon mode (background) +# (fork=no is the same as -E) +fork=yes + +# optional parameter: stderr={yes|no} +# +# - debug mode: do not fork and log to stderr +# (stderr=yes is the same as -E) +stderr=no + +# optional parameter: loglevel={0|1|2|3} +# +# - sets log level (error=0, warning=1, info=2, debug=3) +# (same as -D) +loglevel=2 + +# optional parameter: syslog_facility={DAEMON|USER|LOCAL[0-7]} +# +# - sets the log facility that is used for syslog. Using this, +# the log can for example be filtered into a special file +# by the syslog daemon. +# +# Default: DAEMON +# +# Example: +# syslog_facility=LOCAL0 + +# optional parameter: log_sessions=[yes|no] +# +# Default: no +# +# If log_sessions=yes is set, INFO level log messages are generated +# for each session when it is started and stopped. +# +# log_sessions=yes + +# optional parameter: log_events=[yes|no] +# +# Default: no +# +# If log_eventy=yes is set, generic DBG level log messages are +# generated for each event that is posted into an event queue. +# +# log_events=yes + +# optional parameter: max_shutdown_time=