diff --git a/Makefile.am b/Makefile.am index 10e35c8694..50cf65fc7a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -300,7 +300,7 @@ endif fs_encode_SOURCES = src/fs_encode.c fs_encode_CFLAGS = $(AM_CFLAGS) fs_encode_LDFLAGS = $(AM_LDFLAGS) $(CORE_LIBS) -fs_encode_LDADD = libfreeswitch.la +fs_encode_LDADD = libfreeswitch.la -lcrypt -lrt ## ## tone2wav () @@ -308,7 +308,7 @@ fs_encode_LDADD = libfreeswitch.la tone2wav_SOURCES = src/tone2wav.c tone2wav_CFLAGS = $(AM_CFLAGS) tone2wav_LDFLAGS = $(AM_LDFLAGS) $(CORE_LIBS) -tone2wav_LDADD = libfreeswitch.la +tone2wav_LDADD = libfreeswitch.la -lcrypt -lrt ## ## fs_ivrd () diff --git a/build/freeswitch.init.redhat b/build/freeswitch.init.redhat index b9dc3d8db5..95d15c27ea 100644 --- a/build/freeswitch.init.redhat +++ b/build/freeswitch.init.redhat @@ -43,8 +43,8 @@ start() { fi cd $FS_HOME daemon --user $FS_USER --pidfile $PID_FILE "$FS_FILE $FREESWITCH_ARGS $FREESWITCH_PARAMS >/dev/null 2>&1" - echo RETVAL=$? + echo [ $RETVAL -eq 0 ] && touch $LOCK_FILE; echo return $RETVAL diff --git a/build/modules.conf.in b/build/modules.conf.in index ef3e7eb5bd..b22d627b7c 100644 --- a/build/modules.conf.in +++ b/build/modules.conf.in @@ -2,6 +2,7 @@ loggers/mod_console loggers/mod_logfile loggers/mod_syslog #applications/mod_cidlookup +#applications/mod_blacklist applications/mod_commands applications/mod_conference applications/mod_dptools @@ -38,6 +39,7 @@ applications/mod_valet_parking #applications/mod_fsk #applications/mod_ladspa #applications/mod_mongo +applications/mod_sms codecs/mod_g723_1 codecs/mod_amr #codecs/mod_amrwb @@ -81,6 +83,7 @@ event_handlers/mod_event_socket #event_handlers/mod_event_zmq event_handlers/mod_cdr_csv event_handlers/mod_cdr_sqlite +#event_handlers/mod_cdr_mongodb #event_handlers/mod_cdr_pg_csv #event_handlers/mod_radius_cdr #event_handlers/mod_erlang_event diff --git a/conf/autoload_configs/blacklist.conf.xml b/conf/autoload_configs/blacklist.conf.xml new file mode 100644 index 0000000000..a981ebbdb0 --- /dev/null +++ b/conf/autoload_configs/blacklist.conf.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/conf/autoload_configs/cdr_mongodb.conf.xml b/conf/autoload_configs/cdr_mongodb.conf.xml new file mode 100644 index 0000000000..ce366d1bd0 --- /dev/null +++ b/conf/autoload_configs/cdr_mongodb.conf.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/conf/autoload_configs/mongo.conf.xml b/conf/autoload_configs/mongo.conf.xml index ff8daed72a..8423645e95 100644 --- a/conf/autoload_configs/mongo.conf.xml +++ b/conf/autoload_configs/mongo.conf.xml @@ -1,6 +1,12 @@ - + + diff --git a/conf/chatplan/default.xml b/conf/chatplan/default.xml new file mode 100644 index 0000000000..b67bb638fa --- /dev/null +++ b/conf/chatplan/default.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/conf/dialplan/default.xml b/conf/dialplan/default.xml index 36f0e52a2e..6d46eb069c 100644 --- a/conf/dialplan/default.xml +++ b/conf/dialplan/default.xml @@ -70,8 +70,8 @@ - - + + @@ -181,7 +181,7 @@ - + diff --git a/conf/freeswitch.xml b/conf/freeswitch.xml index 42eede31ee..7a1b1d3d15 100644 --- a/conf/freeswitch.xml +++ b/conf/freeswitch.xml @@ -46,6 +46,10 @@ +
+ +
+
diff --git a/conf/lang/en/ivr/sounds.xml b/conf/lang/en/ivr/sounds.xml index e436e294a3..b5ccead13a 100644 --- a/conf/lang/en/ivr/sounds.xml +++ b/conf/lang/en/ivr/sounds.xml @@ -9,6 +9,26 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/configure.in b/configure.in index 6a7d7dec4d..62977f5fb5 100644 --- a/configure.in +++ b/configure.in @@ -428,7 +428,7 @@ case "$host" in fi ;; *-solaris2*) - APR_ADDTO(SWITCH_AM_CFLAGS, -DPATH_MAX=2048) + APR_ADDTO(SWITCH_AM_CFLAGS, -DPATH_MAX=2048 -D__EXTENSIONS__) APR_ADDTO(SWITCH_AM_LDFLAGS, -lsendfile -lresolv -lsocket -lnsl -luuid) APR_ADDTO(ESL_LDFLAGS, -lnsl -lsocket) ;; @@ -474,7 +474,7 @@ AC_PROG_GCC_TRADITIONAL AC_FUNC_MALLOC AC_TYPE_SIGNAL AC_FUNC_STRFTIME -AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep getifaddrs timerfd_create]) +AC_CHECK_FUNCS([gethostname vasprintf mmap mlock mlockall usleep getifaddrs timerfd_create getdtablesize]) AC_CHECK_FUNCS([sched_setscheduler setpriority setrlimit setgroups initgroups]) AC_CHECK_FUNCS([wcsncmp setgroups asprintf setenv pselect gettimeofday localtime_r gmtime_r strcasecmp stricmp _stricmp]) @@ -871,7 +871,7 @@ then if test "$python_has_distutils" != "no" ; then AC_MSG_CHECKING([location of site-packages]) - PYTHON_SITE_DIR="`$PYTHON -c 'from distutils import sysconfig; print sysconfig.get_python_lib(0);'`" + PYTHON_SITE_DIR="`$PYTHON -c 'from distutils import sysconfig; print(sysconfig.get_python_lib(0));'`" if test -z "$PYTHON_SITE_DIR" ; then AC_MSG_ERROR([Unable to detect python site-packages path]) @@ -884,10 +884,10 @@ then # # python distutils found, get settings from python directly # - PYTHON_CFLAGS="`$PYTHON -c 'from distutils import sysconfig; flags = [[\"-I\" + sysconfig.get_python_inc(0), \"-I\" + sysconfig.get_python_inc(1), \" \".join(sysconfig.get_config_var(\"CFLAGS\").split())]]; print \" \".join(flags);'`" - PYTHON_LDFLAGS="`$PYTHON -c 'from distutils import sysconfig; libs = sysconfig.get_config_var(\"LIBS\").split() + sysconfig.get_config_var(\"SYSLIBS\").split(); libs.append(\"-lpython\"+sysconfig.get_config_var(\"VERSION\")); print \" \".join(libs);'`" - PYTHON_LIB="`$PYTHON -c 'from distutils import sysconfig; print \"python\" + sysconfig.get_config_var(\"VERSION\");'`" - PYTHON_LIBDIR="`$PYTHON -c 'from distutils import sysconfig; print sysconfig.get_config_var(\"LIBDIR\");'`" + PYTHON_CFLAGS="`$PYTHON -c 'from distutils import sysconfig; flags = [[\"-I\" + sysconfig.get_python_inc(0), \"-I\" + sysconfig.get_python_inc(1), \" \".join(sysconfig.get_config_var(\"CFLAGS\").split())]]; print(\" \".join(flags));'`" + PYTHON_LDFLAGS="`$PYTHON -c 'from distutils import sysconfig; libs = sysconfig.get_config_var(\"LIBS\").split() + sysconfig.get_config_var(\"SYSLIBS\").split(); libs.append(\"-lpython\"+sysconfig.get_config_var(\"VERSION\")); print(\" \".join(libs));'`" + PYTHON_LIB="`$PYTHON -c 'from distutils import sysconfig; print(\"python\" + sysconfig.get_config_var(\"VERSION\"));'`" + PYTHON_LIBDIR="`$PYTHON -c 'from distutils import sysconfig; print(sysconfig.get_config_var(\"LIBDIR\"));'`" # handle python being installed into /usr/local AC_MSG_CHECKING([python libdir]) diff --git a/docs/ChangeLog b/docs/ChangeLog index d785d07f46..89ac7f0231 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -26,6 +26,10 @@ freeswitch (1.0.7) build: add support for bz2 to getlibs (r:b61fc396) build: Bump callie sounds to 1.0.15 (r:c8eaef60) build: always use our includes first so we use our srcdir headers over installed versions (r:15c79424) + build: pocketsphinx build for 0.7 windows vs2008 (r:a7613c06/FS-3348) + build: They no longer ship the wsj model in pocketsphinx... and seems the dictionary has moved a bit. (r:23571680) + build: unimrcp vs2010 build fixes for new version (r:2dcca5f4) + build: add sqlite to clean on make current or update-clean (r:2366f429) codec2: working prototype, still for testing only (r:04ca0751) config: move limit.conf to db.conf config: Update VM phrase macros to voice option then action on main, config menus @@ -279,10 +283,38 @@ freeswitch (1.0.7) core: Add the ability to issue a break to switch_ivr_sleep when media is not ready, allowing continuation of processing of the dialplan. (r:dfc30b2e/FS-3373) core: parse events and messages in channel_ready (r:94148095) core: add last_hold_time and hold_accum vars for cdr data (r:676ef808) + core: avoid recursion loop in parse_all_events vs channel_ready (r:22d89943) + core: auto populate global origination_caller_id_name/number from effective_caller_id_name/number in enterprise originate (r:f8c029a1) + core: add --enable-timerfd-wrapper to wrap timefd syscalls for platforms with the right kernel and wrong libc (r:306b332d) + core: don't parse events in channel_ready during hold (r:cad68d53) + core: only parse messages from channel_ready when its a session calling channel ready on itself not when another thread calls it (r:1d12519d) + core: Fix single quote stripping and add %y to turn ' into \' (r:3b5a0ae5/FS-3359) + core: push out signal data into its own queue system (r:f1ee225c) + core: When in a dialplan hunt and we have a custom caller_profile, ${destination_number} and other variable kept the previous value of the original dialplan parsing. This correct this so it take the custom created caller_profile for that hunt (r:b0e0dd22) + core: pause traffic if sql_queue gets to big (r:2939262e) + core: fix detection of tones in monitor_early_media_fail (r:3cbae3fb/FS-3413) + core: use rwlock for global vars to reduce contention (r:0521886d) + core: Fix separate_string_blank_delim to handle strings with '&' (r:f3a42258/FS-3099) + core: Fix setting display on wrong channel on eavesdrop (r:3dc4b530) + core: add new detailed_calls view a version of the channels table that shows only one legged calls or bridged calls (r:beecd937) + core: display update on flip_cid (r:0fc8050c) + core: make sql stmt more portable (r:6b948cf1) + core: print ip:port on rtp bind err (r:11d2cd1b) + core: display fixes and add 2 new cols to channels to store last sent display data (r:d364e9f2) + core: sanitize outbound caller id number on one-legged calls (r:dee0f540/FS-3483) + core: clean up originator/ee profile so the right one is prevelant in events (r:3e2c662a) + core: check for answer flag in bridge to do display update properly (r:0f459d4b) + core: add event subclasses in switch_event.c (r:3696ced7/FS-3497) + core: add max_sessions to heartbeat event (r:9c8437a1/FS-3415) + core: fix event firing for CHANNEL_PROGRESS_MEDIA event (r:e2a4fb11/FS-3396) + core: add emulation for asterisk DIALSTATUS magic var (r:9d98d49f) + core: the new code requires accurate timestamps, we were incrementing it by the interval (20) instead of the samples (160) (r:f10566af/FS-3181) docs: Major clean up of doxygen generated core API documentation (r:794246e1) docs: Add libteletone back to core API documentation (r:c35c138d) embedded languages: Provide core level support for conditional Set Global Variable (r:c017c24b/FSCORE-612) embedded languages: add insertFile front end to switch_ivr_insert_file and reswig (r:c4e350ab) + flex client: check in basic flex demo as basis to develop a client application (r:25be760b) + flex client: the hotkeys js is broken, get rid of it (r:2f6f71d4) fs_cli: block control-z from fs cli and print a warning how to exit properly (r:dc436b82) fs_cli: skip blocking writes on fs_cli to avoid backing up event socket (r:2ec2a9b0) fs_cli: let ctl-c work until you are connected (r:986f258d) @@ -294,6 +326,7 @@ freeswitch (1.0.7) libdingaling: fix race on shutdown causing crash (FSMOD-47) libdingaling: Fix crash in new GV interface when exceeding 24 calls (r:be00609a/FS-2171) libdingaling: fix crash when GV call ends (r:687140b5/FS-3139) + libdingaling: fix small leak (r:d3ea42d8/FS-3334) libesl: Fix potential race condition (ESL-36) libesl: Add /uuid command to fs_cli to filter logs by uuid libesl: Increase buffer in fs_cli for Win (r:d1d6be88/FSCORE-611) @@ -324,6 +357,7 @@ freeswitch (1.0.7) libesl: use poll instead of select in ESL client lib because select is not your friend.... (r:ae595cd5) libesl: Add digit_timeout to ESL::IVR's playAndGetDigits method (r:f564d383) libesl: add array manipulation to the wraper code (r:ffa0a071) + libesl: fix mem leak - good catch, Jlenk! (r:e420e17f/FS-3386) libfreetdm: implemented freetdm config nodes and ss7 initial configuration libfreetdm: fix codec for CAS signaling (r:b76e7f18) libfreetdm: freetdm: ss7- added support for incoming group blocks, started adding support for ansi (r:c219a73c) @@ -342,6 +376,11 @@ freeswitch (1.0.7) libsofiasip: fix bad assert (r:56404641/FS-3133) libsofiasip: lower stack and boost priority of sofia schedule thread (r:257bc9ff) libsofiasip: Fix for issue reported on the mailing list with a Chinese locale and windows. This commit removes a hidden char that should not have been there anyway. (r:7adaceb8) + libsofiasip: resolve edge case in the 3rd party sofia sip stack library when dealing with a malformed contact and missing ack. Will push upstream to sofia devs (r:d68605f5/FS-3394) + libsofiasip: use individual pools instead of sub-pools for nua handles to avoid pool swell (r:f7612413) + libsofiasip: Fix segfault in sofia's stun code (r:7403db70) + libsofiasip: add homer capture hooks to libsofia (r:3e029f0d) + libsofiasip: Fix mem leak when homer capture server not available (r:bc177a4b/FS-3475) libspandsp: Fixed a typo in spandsp's msvc/inttypes.h Updated sig_tone processing in spandsp to the latest, to allow moy to proceed with his signaling work. libspandsp: removed a saturate16 from spandsp that was causing problems fixed a typo in the MSVC inttypes.h file for spandsp libspandsp: Changes to the signaling tone detector to detect concurrent 2400Hz + 2600Hz tones. This passes voice immunity and other key tests, but it bounces a bit when transitions like 2400 -> 2400+2600 -> 2600 occur. Transitions between tone off and tone on are clean. (r:bc13e944) @@ -351,9 +390,15 @@ freeswitch (1.0.7) libspandsp: Added missing error codes when an ECM FAX is abandoned with the T30_ERR message (r:ec57dc7a) libspandsp: Fixed a vulnerability in T.4 and T.6 processing which is similar to http://bugzilla.maptools.org/show_bug.cgi?id=2297 in libtiff. A really screwed up 2D T.4 image, or a maliciously constructed T.4 2D or T.6 image should potential run off the end of an image decoder buffer. (r:c6f67322) libspandsp: Changed T.38 terminal handling, so errors from the user's packet transmit routine properly filter up the chain, cause termination of the FAX session, and are reported to the caller. (r:c890fbfa) + libspandsp: Numerous little changes to spandsp that haven't been pushed to Freeswitch for a while. The only big changes are a majorly rewritten V.42 and V.42bis which are now basically functional. (r:d30e82e2) + libspandsp: Another round of tweaks for spandsp. There should be no functional changes, although quite a few things have changed in the test suite (r:4a7bbf4e) libstfu: add param to jb to try to recapture latency (disabled by default) (r:d59d41d7) libsqlite: fix issue on mailing list mod_crd_sqlite entry limit and sqlite segfaults on triggers (r:1badec17) + libsqlite: make strdup NULL return strdup("") in sqlite for mac bug (r:b6bed14f) + libsqlite: force an update on sqlite build (r:71dd3ca8) libunimrcp: Update to latest UniMRCP version. MRCP requests can no timeout if there is no server response. (r:17099473) + libunimrcp: unimrcp lib does not notify mod_unimrcp of RTSP TEARDOWN timeouts (r:3484f338) + libunimrcp: fixed unimrcp to prevent double destroy of connection (r:493085bb) mod_avmd: Initial check in - Advanced Voicemail Detect (r:10c6a30a) (by Eric Des Courtis) mod_avmd: Add to windows build (r:df4bd935) mod_avmd: Fix mem leak (r:cd951384/FS-2839) @@ -425,6 +470,9 @@ freeswitch (1.0.7) mod_commands: add moh by default to uuid_broadcast when only broadcasting to A leg use aleg arg to disable this (r:d164a797) mod_commands: add API uuid_limit - thanks to Francois Delawarde (r:98a95016/FS-1792) mod_commands: omit file_string:// prefix if input begins with ~ (r:f12ab59e) + mod_commands: fix crash when uuid_break all cannot find bonded uuid channel (r:69e61f76/FS-3468) + mod_commands: fix uuid_dual_transfer for inline dialplan (r:5d84efc3/FS-3403) + mod_commands: update show calls to show both 1 legged calls and bridged calls, also show bridged_calls for previous behaviour of show calls (r:c16c74d9) mod_conference: Fix reporting of volume up/down (MODAPP-419) mod_conference: add last talking time per member to conference xml list mod_conference: add terminate-on-silence conference param @@ -465,6 +513,7 @@ freeswitch (1.0.7) mod_conference: wait for thread to start in mod conference to avoid one in a million race on heavy traffic (r:b1cf5bee) mod_conference: add conference member flag nomoh (r:f35a6814) mod_conference: add hup command to conference (kick without the kick sound) (r:492db906) + mod_conference: see H.264 iFrames (r:765be8c9/FS-3406) mod_curl: use method=post when post requested (r:c6a4ddd0/FSMOD-69) mod_db: fix stack corruption (MODAPP-407) mod_dialplan_xml: Add in the INFO log the caller id number when processing a request (Currenly only show the caller name) (r:e1df5e13) @@ -475,7 +524,9 @@ freeswitch (1.0.7) mod_dingaling: fix leak in chat_send (r:eb109a85) mod_dingaling: use the login as message source when not in component mode. (chat_send) (r:58c28aab) mod_dingaling: fix mod_dingaling/iksemel/gnutls link error when using newer autotools (r:294b0779/FS-3182) + mod_dingaling: fix segmentation fault on mod_dingaling when receiving a discovery from the server (r:2e651c8f/FS-3391) mod_directory: Add variable directory_search_order to allow to search by first name by default is set to "first_name" (r:163ca31f) + mod_directory: let mod_directory use non-XML dialplans (r:8895de1b) mod_distributor: Add mod_distributor to VS2010 - not built by default (r:bac79ba1) mod_dptools: add eavesdrop_enable_dtmf chan var (r:596c0012) mod_dptools: Make park app not send 183 session progress (r:76932995/FSCORE-567) @@ -493,6 +544,11 @@ freeswitch (1.0.7) mod_dptools: Set the default lang if not supplied (mod_say_en) (r:5382972a/FS-3215) mod_dptools: add capture dp app (r:860d2a6c) mod_dptools: Allow redefinition of continue_on_fail and failure_causes during bridge execution. (r:01d0250e/FS-1986) + mod_dptools: Fix "dial 0" 3-way call on att x-fer (r:d4fe85ed/FS-3275) + mod_dptools: fix campon to play music even on first run and cancel faster (r:9cf44f3a) + mod_dptools: fix small leak in strftime (r:bbbd67ba) + mod_dptools: resolve Heap corruption in strftime_api_function -thanks (r:707bd05b/FS-3417) + mod_dptools: fix seg on user_recurse_variables reported on the mailing list (r:01b2bd04) mod_easyroute: Fix possible segfaults and memory leak during unload, and add new setting odbc-retries (r:7fbc47f8/FS-2973) mod_enum: switch mod_enum to use new portable in-tree version (r:2bbc37e3) mod_enum: fix race condition between ldns configure creating ldns/util.h and mod_enum (r:87884c5c) @@ -506,6 +562,7 @@ freeswitch (1.0.7) mod_erlang_event: Rewrite XML fetch conditional wait to be more sane (Reported by James Aimonetti) (r:6941c6eb/FS-2775) mod_erlang_event: Don't urlencode events (and destroy an event after use) (r:4eccdfef) mod_erlang_event Add proper locking for the list of XML bindings (r:9fe440b2) + mod_event_multicast: make multicast loopback configurable (r:97a7668c/FS-3416) mod_event_socket: fix up other users of switch_event_xmlize() to use SWITCH_EVENT_NONE (r:d6eb7562) mod_event_socket: Fix small mem leaks (r:e4f90584/MODEVENT-68) mod_event_socket: Add "-ERR" to api cmd response when failure occurs (r:58759052/FS-2827) @@ -526,6 +583,7 @@ freeswitch (1.0.7) mod_fifo: Fix crash when using fifo_destroy_after_use (r:ee562c82/FS-2879) mod_fifo: don't seg in edge case error conditions (r:9ee13b72) mod_fifo: set tracking data before enabling hooks (r:34267869) + mod_fifo: Fix fifo orbit timeout when not using a chime tested with and without chime (r:7fee1fd1) mod_file_string: Fix segfault when using file string in conference (r:9c40e8e9/FS-3122) mod_freetdm: Fix for TON and NPI not passed through to channel variables on incoming calls mod_freetdm: add pvt data to freetdm channels fix fxs features (r:9d456900) @@ -596,6 +654,7 @@ freeswitch (1.0.7) mod_khomp: Removed alternative contexts / extensions - New struct for matchs - On calls originated from an FXS branch, the Endpoint searches for a valid extension (digits sent) after the DTMF '#' or after the timeout (option fxs-digit-timeout). That search is done in the context defined in section , or if no context configured, the search is done in context defined in context-fxs. - Added "dialplan" configuration: Name of the dialplan module in use (default XML) - Group context enabled. If set, the search for a valid extension is done only in that context. - Updated documentation (r:1ef3fc9a) mod_ladspa: Add mod_ladspa (Audio plugin framework for linux) (r:2d3d8f8d) mod_ladspa: add string params to ladspa so you can connect files to audio ports (string params don't count towards number params) (r:b7891511) + mod_ladspa: putenv() breaks the process environment variables, use setenv() instead. (r:f6dadb58) mod_lcr: Expand variables (MODAPP-418) mod_lcr: add enable_sip_redir parameter (r:70bf7a0a/MODAPP-427) mod_lcr: don't validate profiles with ${} vars since they are dynamic and we can't guess what the proper value should be (r:af33afaa) @@ -607,6 +666,7 @@ freeswitch (1.0.7) mod_lcr: don't add routes that have no rate of the desired type (r:82e3ccf8) mod_lcr: fix "as xml" for larger number of arguments (r:3dca2ebb/FS-3283) mod_lcr: fix malformed XML when has embedded %s (r:5fa9619f/FS-3284) + mod_lcr: initial addition of very basic LRN (r:6d1d4a9c) mod_loopback: add loopback_bowout_on_execute var to make 1 legged loopback calls bow out of the picture mod_loopback: only execute app once in app mode (r:64f58f2d) mod_loopback: fix bug in mod_loopback where bowout=false (r:e9ab5368) @@ -614,6 +674,7 @@ freeswitch (1.0.7) mod_loopback: fix voicemail failure (r:1a1881e8/FS-2795) mod_loopback: pass ring_ready like we do with pre_answer (r:9d087d45) mod_loopback: refactor mod_loopback timeout handling (r:43442e4f) + mod_loopback: Fix loopback_bowout_on_execute failure when doing txfax calls (r:895b505f/FS-3494) mod_lua: Add switch_core_sqldb functionality from inside Lua script (r:26f2e095/FS-1384) mod_lua: Made 2nd arg to freeswitch.Dbh:query (cb func) optional (r:87db11af) mod_lua: Added SAF_ROUTING_EXEC flag to lua app, so it can be run inline (r:7d5ca1c0) @@ -625,7 +686,9 @@ freeswitch (1.0.7) mod_managed: Added wrapper for switch_event_bind for .net (r:a5f07a80/MODLANG-165) mod_managed: add additional support (r:5be58aac) mod_managed: add mono 2.8 patch file see FS-2774 (r:6a948bd9/FS-2774) + mod_managed: resolve Memory leak in mod_managed by EventBinding and swig delete_switch_event (r:c6048134/FS-3381) mod_mongo: New mod, initial commit; module for MongoDB (http://www.mongodb.org/) (r:dc6ca6f8/FS-3278) + mod_mongo: add mapreduce API (r:7c5b5797/FS-3357) mod_mp4v: MP4V-ES passthru for washibechi on IRC mod_mp4: New module. Supports playback of MP4 files. Depends on libmp4v2 (originally compiled against v1.6.1) mod_nibblebill: free allocated mem at shutdown; free properly if using custom_sql @@ -648,6 +711,9 @@ freeswitch (1.0.7) mod_portaudio: Fix inbound state (CS_ROUTING not CS_INIT) (MODENDP-302) mod_portaudio: mod_portaudio improvements and bug fixes (r:33b74ca8/FS-3006) mod_portaudio: Add pa devlist to portaudio webapi (r:e8f10ea3) + mod_portaudio: fix crash on bad init (r:6f49e6ba/FS-3361) + mod_portaudio: move load_config a bit lower since it needs to use the hashtables (r:1529c0ec) + mod_portaudio: Fix Windows crash (r:94c9cbf6/FS-3498) mod_portaudio_stream: update to specify the channel index (r:d1169d6e) mod_protovm: This is a very early new prototype voicemail ivr system. You need to copy the sounds.xml and make it loadale in the language folder and protovm.conf.xml inside the autoload_configs folder. Configs file will most definitly change. Once stabilized, we make it install those file by default. (r:fb549777) mod_radius_cdr: Add 'Freeswitch-Direction' av pair (r:a5170df0) @@ -658,6 +724,14 @@ freeswitch (1.0.7) mod_rtmp: Make all sockets non-blocking (r:affcdb0a) mod_rtmp: mod_rtmp for windows (r:f8cda539/FS-3355) mod_rtmp: flush buffer to avoid lag and enable plc (r:4bb76831) + mod_rtmp: add conf (r:4eaabd28) + mod_rtmp: set variables based on input hash (r:3815d188) + mod_rtmp: Remove duplicate output from rtmp status profile xxx API command (r:2e016541) + mod_rtmp: Make all sockets non-blocking (r:affcdb0a) + mod_rtmp: Lower default buffer size to 50ms (r:d52a254d) + mod_rtmp: CNG frames need to have codec set too (r:36f812d9) + mod_rtmp: remove superfluous hangup (r:50817655) + mod_rtmp: fix crash when call made from user not in domain (r:a5452174/FS-3353) mod_sangoma_codec: Add sample config file mod_sangoma_codec: added load/noload options for the supported codecs mod_sangoma_codec: rename load/noload to register/noregister @@ -685,6 +759,7 @@ freeswitch (1.0.7) mod_shell_stream: Fix defunct processes being left behind (r:89666f44/FS-3316) mod_shout: bump mod_shout to use mpg123-1.13.2 to hopefully address unwanted calls to exit() and inherit other upstream fixes (r:079f3f73) mod_shout: add append flag to mod_shout, can append MP3's (r:0419c4e0) + mod_shout: add ability to set bitrate, samplerate, and encoder quality in config file (r:8ea3cbd5/FS-1231) mod_silk: Fix mod_silk compliance and performance issues (r:2ddbc457/MODCODEC-20) mod_skinny: Add the missing api files mod_skinny: add example dialplan and directory config (r:1bfcc17e) @@ -714,6 +789,9 @@ freeswitch (1.0.7) mod_skypopen: deleted osscuse subdir (r:4842a620) mod_skypopen: adding installer and Skype client configuration directories (to be announced :) ) (r:25ebf715) mod_skypopen: refining INTERACTIVE INSTALLER for Linux (to be announced :) ) (r:aa7f47ac) + mod_skypopen: refining oss driver, removing audio sync during call (was each 20 secs), audio sync at the tcp interfacing with the skype client (reading more than 20ms worth) (r:891015e6) + mod_skypopen: fixed a demented bug (incrementing a variable zeroed in the same loop) maybe responsible for moh sputtering under load on virtual machines (r:43eeeb82) + mod_skypopen: avoid accumulating delay on VMs, better debug logging (r:1b4c78bf) mod_snapshot: fix bad codepaths in mod_snapshot (r:844ac220) mod_sndfile: Add support for .alaw and .ulaw to mod_sndfile (r:facf09b8/MODFORM-41) mod_sndfile: return break in mod_sndfile when seek returns failure (r:564dc7e4) @@ -897,6 +975,31 @@ freeswitch (1.0.7) mod_sofia: removed the vid refresh thing (r:49e52b4c/FS-3362) mod_sofia: add sip_liberal_dtmf chanvar and liberal-dtmf profile param to use the maximum methods of DTMF avoiding sticking to the spec which leads to incompatability (r:bc7cb400) mod_sofia: support final response in response header passing (r:acd0898e) + mod_sofia: Fix failure to fall back to g.711 when t.38 attempt fails (r:07a79752/FS-3214) + mod_sofia: pop :: off the domain name in mwi events to hint at the profile (r:e2ed8c08) + mod_sofia: dig into the database to figure out what profile to send mwi on when they are not willing to alais the domain to the profile =/ (r:b14340a5) + mod_sofia: Fix 3pcc codec negotiation issue with bypass_media (r:c5a2275f/FS-3340) + mod_sofia: re-add not-so-superfluous-after-all NUTAG_AUTOANSWER(0) (r:927fde18/FS-3349) + mod_sofia: add mutex around gateway access on per-profile basis and token based access to global profiles to prevent hanging on to the hash mutex while doing sql stmts which may cause issues/slowdowns (r:9df8169d) + mod_sofia: add parallelism to sofia by offsetting sip messages to the concerned sessions and using multiple queue threads for message handling (r:fb68746e) + mod_sofia: Fix sofia hang on shutdown (r:3be64cbf/FS-3354) + mod_sofia: remove vid refresh from SDP on declined video connection (r:49e52b4c/FS-3362) + mod_sofia: fix small mem leak in sofia (r:6f62f391/FS-3386) + mod_sofia: add proxy tag to UPDATE packets if it was set by INVITE (r:e6605139) + mod_sofia: resolve attended transfers, it fails to parse the Replaces when encoded (r:d9bbf129/FS-3304) + mod_sofia: if user has set presence_id, don't override it (r:7cdc8342) + mod_sofia: only list real profiles not aliases in presence code (r:f9969f38) + mod_sofia: Fix 200 OK not passed for Callee-Initiated ReInvite for T.38 (r:b2299035/FS-3421) + mod_sofia: destroy nh if SIP transaction terminated by a 488 (r:a0cec8ab/FS-3444) + mod_sofia: use register contact to determine proper contact in 200 ok response to register (r:f9612fec) + mod_sofia: add NDLB-allow-nondup-sdp to indicate you want to parse a differnt sdp in 200 ok from 1xx (previous default) this is a RFC violation so I decided not to support it by default anymore. Enable this if you want that broken behaviour (r:3f489a2a) + mod_sofia: add homer capture hooks to mod_sofia (r:98473085) + mod_sofia: sdp_m_per_ptime is now implied to be true, if you don't like this set it to false but its going to be undefined behaviour. This basically means if you call in with ptime 30 then you have a bunch of ptime 20 codecs in your outbound list that there will be one m= line with 30 and the original inbound codec and more m= lines for each discinct ptime in your list. This is, of course, will depend on disable_trancoding or absolute_codec_string as well (r:56d67ead) + mod_sofia: filter re-transmission of extra SIP headers (r:9e399c19/FS-3439) + mod_sofia: Fix RTP handling bug to allow goofy & undefined behavior (r:77413ba9/FS-3451) + mod_sofia: don't allow auto answer on an non-outbound call (r:61ee7fdc) + mod_sofia: use the call_id of the original register in the unsolicited notify for MWI (r:53b0ecce) + mod_soundtouch: updated soundtouch to library 1.5.0 to fix gcc > 4.3 incompatibilities (r:dfb5c629) mod_spandsp: initial checkin of mod_fax/mod_voipcodecs merge into mod_spandsp (r:fa9a59a8) mod_spandsp: rework of new mod_spandsp to have functions broken up into different c files (r:65400642) mod_spandsp: improve duplicate digit detection and add 'min_dup_digit_spacing_ms' channel variable for use with the dtmf detector (r:eab4f246/FSMOD-45) @@ -916,6 +1019,8 @@ freeswitch (1.0.7) mod_spandsp: add more fax event information (r:0555b702/FS-3345) mod_spandsp: fix memory issue in spandsp_tone_detect (r:8793c2ed) mod_spandsp: add proper tone detect stop (r:8beb10d2/FS-3367) + mod_spandsp: add more fax event information (r:0555b702/FS-3345) + mod_spandsp: fix memory issue in spandsp_tone_detect (r:8793c2ed) mod_spidermonkey: allow vars to be set containing vars from languages (r:5cd072a3) mod_spidermonkey: fix seg in js hangup (r:7d554c11) mod_spidermonkey: Fix mod_spidermonkey build on FreeBSD, (Undefined symbol PR_LocalTimeParameters). (r:3edb8419) @@ -935,6 +1040,7 @@ freeswitch (1.0.7) mod_valet_parking: add event for Valet Parking action exit mod_valet_parking: pass hold class on transfer (r:76a065ec) mod_valet_parking: add valet_announce_slot variable (r:293d7254) + mod_valet_parking: make valet parking reserve a space for 10 seconds to allow time for an attended transfer switcharoo (r:308f44af) mod_voicemail: Fix vm_prefs profile lock (MODAPP-417) mod_voicemail: add 'vm-enabled' param (default true) mod_voicemail: fix vm msg being deleted when pressing key to forward to email (MODAPP-403) @@ -952,6 +1058,10 @@ freeswitch (1.0.7) mod_voicemail: vm-skip-instructions param in xml directory to disable instructions how to record a file (r:ed7e1f39) mod_voicemail: Implement 10 new standard api function call that allow you to control fs voicemail storage system. The goal is to have a standard API set for any additional storage system we wish the voicemail to run off. Current list of added api name are : vm_fsdb_msg_count, vm_fsdb_msg_list, vm_fsdb_msg_get, vm_fsdb_msg_delete, vm_fsdb_msg_undelete, vm_fsdb_msg_purge, vm_fsdb_msg_save, vm_fsdb_pref_greeting_set, vm_fsdb_pref_recname_set, vm_fsdb_pref_password_set. (r:1f4cb488) mod_voicemail: Adding a new voicemail fsdb api vm_fsdb_auth_login that does basic login authentication for a user (r:bfdfac5e) + mod_voicemail: Fix vm to email dial 8 option (r:8592b6d9/FS-3382) + mod_voicemail: Add 2 new profile settings, db-password-override and allow-empty-password-auth. By default, they have value of their previous behavior. If db-password-override=true, the db password will only be used if present, if not present fallback to the xml config file vm-password. If allow-empty-password-auth=false, it will disable login via a authentication method if there is no password set in the user account (This wont affect voicemail_authorize=true login). (r:a9db642a) + mod_voicemail: remove pointless update_mwi() in vm_list api command (r:b952b2b2) + mod_voicemail: add message_len to output of vm_list api command (r:77c5000d) mod_xml_cdr: add force_process_cdr var to process b leg cdr on a case by case basis when b leg cdr is disabled (XML-17) mod_xml_cdr: add leg param to query string (XML-24) mod_xml_cdr: fix locked sessions (XML-26) diff --git a/docs/phrase/phrase_en.xml b/docs/phrase/phrase_en.xml index 22929edcd5..ecdebc9fdf 100644 --- a/docs/phrase/phrase_en.xml +++ b/docs/phrase/phrase_en.xml @@ -5,23 +5,23 @@ - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + @@ -239,30 +239,30 @@ - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - + @@ -287,14 +287,14 @@ - - - - - - - - + + + + + + + + @@ -398,21 +398,21 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + @@ -439,67 +439,64 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - @@ -1046,3 +1043,9 @@ + diff --git a/freeswitch-sounds-music.spec b/freeswitch-sounds-music.spec new file mode 100644 index 0000000000..6864e10fa3 --- /dev/null +++ b/freeswitch-sounds-music.spec @@ -0,0 +1,126 @@ +%define prefix /opt/freeswitch +%define _prefix %{prefix} + + +Summary: FreeSWITCH Music on Hold soundfiles +Name: freeswitch-sounds-music +Version: 1.0.8 +Release: 1%{?dist} +License: MPL +Group: Productivity/Telephony/Servers +Packager: Joseph L. Casale +URL: http://www.freeswitch.org +Source0: http://files.freeswitch.org/%{name}-8000-%{version}.tar.gz +Source1: http://files.freeswitch.org/%{name}-16000-%{version}.tar.gz +Source2: http://files.freeswitch.org/%{name}-32000-%{version}.tar.gz +Source3: http://files.freeswitch.org/%{name}-48000-%{version}.tar.gz +BuildArch: noarch +BuildRequires: bash +Requires: freeswitch +Requires: freeswitch-sounds-music-8000 = %{version} +Requires: freeswitch-sounds-music-16000 = %{version} +Requires: freeswitch-sounds-music-32000 = %{version} +Requires: freeswitch-sounds-music-48000 = %{version} +BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX) + +%description +FreeSWITCH Music On Hold soundfiles package that installs the 8KHz, 16KHz, +32KHz and 48KHz RPMs + + +%package -n freeswitch-sounds-music-8000 +Summary: FreeSWITCH 8kHz Music On Hold soundfiles +Group: Productivity/Telephony/Servers +BuildArch: noarch +Requires: freeswitch + +%description -n freeswitch-sounds-music-8000 +FreeSWITCH 8kHz Music On Hold soundfiles + + +%package -n freeswitch-sounds-music-16000 +Summary: FreeSWITCH 16kHz Music On Hold soundfiles +Group: Productivity/Telephony/Servers +BuildArch: noarch +Requires: freeswitch + +%description -n freeswitch-sounds-music-16000 +FreeSWITCH 16kHz Music On Hold soundfiles + + +%package -n freeswitch-sounds-music-32000 +Summary: FreeSWITCH 32kHz Music On Hold soundfiles +Group: Productivity/Telephony/Servers +BuildArch: noarch +Requires: freeswitch + +%description -n freeswitch-sounds-music-32000 +FreeSWITCH 32kHz Music On Hold soundfiles + + +%package -n freeswitch-sounds-music-48000 +Summary: FreeSWITCH 48kHz Music On Hold soundfiles +Group: Productivity/Telephony/Servers +BuildArch: noarch +Requires: freeswitch + +%description -n freeswitch-sounds-music-48000 +FreeSWITCH 48kHz Music On Hold soundfiles + + +%prep +%setup -n music +%setup -T -D -b 1 -n music +%setup -T -D -b 2 -n music +%setup -T -D -b 3 -n music + + +%build + + +%install +%{__rm} -rf %{buildroot} +%{__install} -d -m 0750 %{buildroot}/%{_prefix}/sounds/music/{8000,16000,32000,48000} +%{__cp} -prv ./{8000,16000,32000,48000} %{buildroot}%{_prefix}/sounds/music + + +%clean +%{__rm} -rf %{buildroot} + + +%post + + +%postun + + +%files + + +%files -n freeswitch-sounds-music-8000 +%defattr(-,root,root,-) +%dir %{_prefix}/sounds/music/8000 +%{_prefix}/sounds/music/8000/*.wav + + +%files -n freeswitch-sounds-music-16000 +%defattr(-,root,root,-) +%dir %{_prefix}/sounds/music/16000 +%{_prefix}/sounds/music/16000/*.wav + + +%files -n freeswitch-sounds-music-32000 +%defattr(-,root,root,-) +%dir %{_prefix}/sounds/music/32000 +%{_prefix}/sounds/music/32000/*.wav + + +%files -n freeswitch-sounds-music-48000 +%defattr(-,root,root,-) +%dir %{_prefix}/sounds/music/48000 +%{_prefix}/sounds/music/48000/*.wav + + +%changelog +* Sat Jul 16 2011 Joseph Casale 1.0.8-1 +- Initial release \ No newline at end of file diff --git a/freeswitch.spec b/freeswitch.spec index 8fa7dedab6..416bfd6a8f 100644 --- a/freeswitch.spec +++ b/freeswitch.spec @@ -432,12 +432,12 @@ export QA_RPATHS=$[ 0x0001|0x0002 ] APPLICATION_MODULES_AE="applications/mod_avmd applications/mod_callcenter applications/mod_cidlookup applications/mod_cluechoo \ applications/mod_commands applications/mod_conference applications/mod_curl applications/mod_db applications/mod_directory \ applications/mod_distributor applications/mod_dptools applications/mod_easyroute applications/mod_enum \ - applications/mod_esf applications/mod_expr" + applications/mod_esf applications/mod_expr applications/mod_blacklist" APPLICATION_MODULES_FM="applications/mod_fifo applications/mod_fsv applications/mod_hash applications/mod_lcr applications/mod_limit \ applications/mod_memcache" APPLICATION_MODULES_NY="applications/mod_nibblebill applications/mod_redis applications/mod_rss applications/mod_snom \ applications/mod_soundtouch applications/mod_spandsp applications/mod_spy applications/mod_stress \ - applications/mod_valet_parking applications/mod_vmd applications/mod_voicemail" + applications/mod_valet_parking applications/mod_vmd applications/mod_voicemail applications/mod_sms" APPLICATIONS_MODULES="$APPLICATION_MODULES_AE $APPLICATION_MODULES_FM $APPLICATION_MODULES_NY $APPLICATION_MODULES_VZ" ###################################################################################################################### @@ -751,8 +751,10 @@ fi %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/mime.types %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/acl.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/alsa.conf.xml +%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/blacklist.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/callcenter.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cdr_csv.conf.xml +%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cdr_mongodb.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cdr_pg_csv.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cdr_sqlite.conf.xml %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/cepstral.conf.xml @@ -807,6 +809,12 @@ fi %config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/autoload_configs/zeroconf.conf.xml ###################################################################################################################### # +# Chatplans +# +###################################################################################################################### +%config(noreplace) %attr(0640, freeswitch, daemon) %{prefix}/conf/chatplan/default.xml +###################################################################################################################### +# # Dialplans # ###################################################################################################################### @@ -880,6 +888,7 @@ fi ###################################################################################################################### %{prefix}/mod/mod_amrwb.so* %{prefix}/mod/mod_avmd.so* +%{prefix}/mod/mod_blacklist.so* %{prefix}/mod/mod_bv.so* %{prefix}/mod/mod_callcenter.so* %{prefix}/mod/mod_cdr_csv.so* @@ -932,6 +941,7 @@ fi %{prefix}/mod/mod_silk.so* %{prefix}/mod/mod_siren.so* %{prefix}/mod/mod_skinny.so* +%{prefix}/mod/mod_sms.so* %{prefix}/mod/mod_sndfile.so* %{prefix}/mod/mod_snom.so* %{prefix}/mod/mod_sofia.so* diff --git a/libs/.gitignore b/libs/.gitignore index 0435752435..8dd2b4db57 100644 --- a/libs/.gitignore +++ b/libs/.gitignore @@ -1,281 +1,237 @@ -/libg723_1/ -/libg729/ -/apr-util/.make.dirs -/apr-util/Makefile -/apr-util/apr-util.pc +aclocal.m4 +BuildLog*.htm +config.guess +config.h.in +config.log +config.nice +config.status +config.sub +configure +depcomp +install-sh +libtool +ltmain.sh +missing + +*_manifest.rc +*.pc + +/*.tar.* + +/apr/apr-1-config +/apr/apr.exp +/apr/build/apr_rules.mk +/apr/build/libtool.m4 +/apr/build/pkg/pkginfo +/apr/exports.c +/apr/export_vars.c +/apr/include/apr.h +/apr/include/arch/unix/apr_private.h +/apr/include/arch/unix/apr_private.h.in +/apr/.make.dirs +/apr/Makefile +/apr/test/internal/Makefile +/apr/test/Makefile /apr-util/aprutil.exp /apr-util/apu-1-config /apr-util/build/apr_common.m4 -/apr-util/build/config.guess -/apr-util/build/config.sub /apr-util/build/find_apr.m4 /apr-util/build/get-version.sh /apr-util/build/install.sh /apr-util/build/pkg/pkginfo /apr-util/build/rules.mk -/apr-util/config.log -/apr-util/config.nice -/apr-util/config.status -/apr-util/configure +/apr-util/exports.c /apr-util/export_vars.c /apr-util/export_vars.sh -/apr-util/exports.c /apr-util/include/apr_ldap.h +/apr-util/include/apu_config.h /apr-util/include/apu.h +/apr-util/include/apu_select_dbm.h /apr-util/include/apu_want.h /apr-util/include/private/apu_config.h /apr-util/include/private/apu_config.h.in /apr-util/include/private/apu_select_dbm.h +/apr-util/.make.dirs +/apr-util/Makefile /apr-util/test/Makefile -/apr-util/xml/expat/Makefile -/apr-util/xml/expat/aclocal.m4 /apr-util/xml/expat/config.h -/apr-util/xml/expat/config.h.in -/apr-util/xml/expat/config.log -/apr-util/xml/expat/config.status -/apr-util/xml/expat/configure -/apr-util/xml/expat/conftools/ltmain.sh -/apr-util/xml/expat/lib/Makefile +/apr-util/xml/expat/lib/config.h /apr-util/xml/expat/lib/expat.h -/apr-util/xml/expat/libtool -/apr/.make.dirs -/apr/Makefile -/apr/aclocal.m4 -/apr/apr-1-config -/apr/apr.exp -/apr/apr.pc -/apr/build/apr_rules.mk -/apr/build/config.guess -/apr/build/config.sub -/apr/build/libtool.m4 -/apr/build/ltmain.sh -/apr/build/pkg/pkginfo -/apr/config.log -/apr/config.nice -/apr/config.status -/apr/configure -/apr/export_vars.c -/apr/exports.c -/apr/include/apr.h -/apr/include/arch/unix/apr_private.h -/apr/include/arch/unix/apr_private.h.in -/apr/libtool -/apr/test/Makefile -/apr/test/internal/Makefile -/broadvoice/Makefile -/broadvoice/Makefile.in -/broadvoice/aclocal.m4 -/broadvoice/broadvoice.pc +/apr-util/xml/expat/lib/Makefile +/apr-util/xml/expat/Makefile /broadvoice/config-h.in -/broadvoice/config.log -/broadvoice/config.status -/broadvoice/config/config.guess -/broadvoice/config/config.sub -/broadvoice/config/depcomp -/broadvoice/config/install-sh -/broadvoice/config/ltmain.sh -/broadvoice/config/missing -/broadvoice/configure /broadvoice/doc/Makefile /broadvoice/doc/Makefile.in -/broadvoice/libtool -/broadvoice/src/Makefile -/broadvoice/src/Makefile.in +/broadvoice/Makefile +/broadvoice/Makefile.in /broadvoice/src/broadvoice.h /broadvoice/src/config.h +/broadvoice/src/Makefile +/broadvoice/src/Makefile.in /broadvoice/src/stamp-h1 /broadvoice/tests/regression_tests.sh -/curl/Makefile -/curl/Makefile.in -/curl/aclocal.m4 -/curl/config.guess -/curl/config.log -/curl/config.status -/curl/config.sub -/curl/configure +/celt-0.10.0/ +/celt-0.7.1/ +/Communicator_semi_40.cd_semi_6000/ /curl/curl-config -/curl/depcomp -/curl/include/Makefile -/curl/include/Makefile.in /curl/include/curl/Makefile /curl/include/curl/Makefile.in -/curl/install-sh +/curl/include/Makefile +/curl/include/Makefile.in +/curl/lib/config.h /curl/lib/Makefile /curl/lib/Makefile.in -/curl/lib/config.h -/curl/lib/config.h.in /curl/lib/stamp-h1 -/curl/libcurl.pc -/curl/libtool -/curl/ltmain.sh -/curl/missing +/curl/Makefile +/curl/Makefile.in /curl/packages/AIX/Makefile /curl/packages/AIX/Makefile.in +/curl/packages/AIX/RPM/curl.spec /curl/packages/AIX/RPM/Makefile /curl/packages/AIX/RPM/Makefile.in -/curl/packages/AIX/RPM/curl.spec /curl/packages/DOS/Makefile /curl/packages/DOS/Makefile.in +/curl/packages/EPM/curl.list /curl/packages/EPM/Makefile /curl/packages/EPM/Makefile.in -/curl/packages/EPM/curl.list /curl/packages/Linux/Makefile /curl/packages/Linux/Makefile.in +/curl/packages/Linux/RPM/curl.spec +/curl/packages/Linux/RPM/curl-ssl.spec /curl/packages/Linux/RPM/Makefile /curl/packages/Linux/RPM/Makefile.in -/curl/packages/Linux/RPM/curl-ssl.spec -/curl/packages/Linux/RPM/curl.spec /curl/packages/Makefile /curl/packages/Makefile.in /curl/packages/Solaris/Makefile /curl/packages/Solaris/Makefile.in -/curl/packages/Win32/Makefile -/curl/packages/Win32/Makefile.in -/curl/packages/Win32/cygwin/Makefile -/curl/packages/Win32/cygwin/Makefile.in /curl/packages/vms/Makefile /curl/packages/vms/Makefile.in +/curl/packages/Win32/cygwin/Makefile +/curl/packages/Win32/cygwin/Makefile.in +/curl/packages/Win32/Makefile +/curl/packages/Win32/Makefile.in +/curl/src/config.h /curl/src/Makefile /curl/src/Makefile.in -/curl/src/config.h /curl/src/stamp-h2 -/iksemel/Makefile -/iksemel/Makefile.in -/iksemel/aclocal.m4 +/esl/Debug/ +/esl/Release/ +/flite-1.3.99/ +/freeradius-client-1.1.6/ +/freetdm/build/compile +/freetdm/COPYING +/freetdm/INSTALL +/freetdm/Makefile.in +/freetdm/msvc/Debug/ +/freetdm/msvc/Release/ /iksemel/build/compile -/iksemel/build/config.guess -/iksemel/build/config.sub -/iksemel/build/depcomp -/iksemel/build/install-sh -/iksemel/build/ltmain.sh -/iksemel/build/missing -/iksemel/config.log -/iksemel/config.status -/iksemel/configure /iksemel/doc/Makefile /iksemel/doc/Makefile.in -/iksemel/iksemel.pc +/iksemel/include/config.h /iksemel/include/Makefile /iksemel/include/Makefile.in -/iksemel/include/config.h -/iksemel/include/config.h.in /iksemel/include/stamp-h1 -/iksemel/libtool +/iksemel/Makefile +/iksemel/Makefile.in /iksemel/src/Makefile /iksemel/src/Makefile.in /iksemel/test/Makefile /iksemel/test/Makefile.in +/iksemel/test/tst-dom +/iksemel/test/tst-filter +/iksemel/test/tst-iks +/iksemel/test/tst-ikstack +/iksemel/test/tst-jid +/iksemel/test/tst-md5 +/iksemel/test/tst-sax +/iksemel/test/tst-sha +/iksemel/tools/ikslint +/iksemel/tools/iksperf +/iksemel/tools/iksroster /iksemel/tools/Makefile /iksemel/tools/Makefile.in -/ilbc/Makefile -/ilbc/Makefile.in -/ilbc/aclocal.m4 /ilbc/config-h.in -/ilbc/config.log -/ilbc/config.status -/ilbc/config/config.guess -/ilbc/config/config.sub -/ilbc/config/depcomp -/ilbc/config/install-sh -/ilbc/config/ltmain.sh -/ilbc/config/missing -/ilbc/configure /ilbc/doc/Makefile /ilbc/doc/Makefile.in -/ilbc/libtool /ilbc/localtests/Makefile /ilbc/localtests/Makefile.in -/ilbc/src/Makefile -/ilbc/src/Makefile.in +/ilbc/Makefile +/ilbc/Makefile.in /ilbc/src/config.h /ilbc/src/ilbc.h +/ilbc/src/Makefile +/ilbc/src/Makefile.in /ilbc/src/stamp-h1 /ilbc/tests/Makefile /ilbc/tests/Makefile.in -/js/nsprpub/lib/ds/libplds4.so.1 -/js/nsprpub/lib/libc/src/libplc4.so.1 -/js/nsprpub/pr/src/libnspr4.so.1 -/js/Makefile -/js/aclocal.m4 -/js/config.guess +/include/ /js/config.h -/js/config.h.in -/js/config.log -/js/config.status -/js/config.sub -/js/configure /js/js /js/js-config.sh -/js/js.pc /js/jscpucfg -/js/libtool -/js/ltmain.sh -/js/nsprpub/Makefile -/js/nsprpub/aclocal.m4 -/js/nsprpub/build/autoconf/config.guess -/js/nsprpub/build/autoconf/config.sub -/js/nsprpub/build/autoconf/install-sh -/js/nsprpub/build/autoconf/ltmain.sh -/js/nsprpub/build/autoconf/missing -/js/nsprpub/config.log -/js/nsprpub/config.status -/js/nsprpub/config/Makefile +/js/Makefile /js/nsprpub/config/autoconf.mk +/js/nsprpub/config/Makefile /js/nsprpub/config/nfspwd /js/nsprpub/config/now /js/nsprpub/config/nsinstall /js/nsprpub/config/nspr-config /js/nsprpub/config/nsprincl.mk /js/nsprpub/config/nsprincl.sh -/js/nsprpub/configure /js/nsprpub/dist/ -/js/nsprpub/lib/Makefile +/js/nsprpub/lib/ds/libplds4.dylib +/js/nsprpub/lib/ds/libplds4.so.1 /js/nsprpub/lib/ds/Makefile /js/nsprpub/lib/ds/_pl_bld.h -/js/nsprpub/lib/libc/Makefile /js/nsprpub/lib/libc/include/Makefile +/js/nsprpub/lib/libc/Makefile +/js/nsprpub/lib/libc/src/libplc4.dylib +/js/nsprpub/lib/libc/src/libplc4.so.1 /js/nsprpub/lib/libc/src/Makefile /js/nsprpub/lib/libc/src/_pl_bld.h +/js/nsprpub/lib/Makefile /js/nsprpub/lib/tests/Makefile -/js/nsprpub/libtool -/js/nsprpub/pkg/Makefile +/js/nsprpub/Makefile /js/nsprpub/pkg/linux/Makefile +/js/nsprpub/pkg/Makefile /js/nsprpub/pkg/solaris/Makefile -/js/nsprpub/pkg/solaris/SUNWpr/Makefile /js/nsprpub/pkg/solaris/SUNWprd/Makefile -/js/nsprpub/pr/Makefile +/js/nsprpub/pkg/solaris/SUNWpr/Makefile /js/nsprpub/pr/include/Makefile /js/nsprpub/pr/include/md/Makefile /js/nsprpub/pr/include/obsolete/Makefile +/js/nsprpub/pr/include/prcpucfg.h /js/nsprpub/pr/include/private/Makefile -/js/nsprpub/pr/src/Makefile -/js/nsprpub/pr/src/_pr_bld.h +/js/nsprpub/pr/Makefile /js/nsprpub/pr/src/io/Makefile +/js/nsprpub/pr/src/libnspr4.dylib +/js/nsprpub/pr/src/libnspr4.so.1 /js/nsprpub/pr/src/linking/Makefile +/js/nsprpub/pr/src/Makefile /js/nsprpub/pr/src/malloc/Makefile /js/nsprpub/pr/src/md/Makefile /js/nsprpub/pr/src/md/unix/Makefile /js/nsprpub/pr/src/memory/Makefile /js/nsprpub/pr/src/misc/Makefile +/js/nsprpub/pr/src/_pr_bld.h /js/nsprpub/pr/src/pthreads/Makefile /js/nsprpub/pr/src/threads/Makefile -/js/nsprpub/pr/tests/Makefile /js/nsprpub/pr/tests/dll/Makefile +/js/nsprpub/pr/tests/Makefile +/json-c-0.9/ /js/src/jsautocfg.h /js/src/perlconnect/Makefile.PL -/ldns/Makefile +/lame-3.97/ +/ldns-1.6.9/ /ldns/doc/ldns_manpages /ldns/include/ /ldns/ldns/config.h /ldns/ldns/net.h /ldns/ldns/util.h /ldns/lib -/ldns/libtool /ldns/linktest -/ldns/linktest.dSYM/ +/ldns/Makefile /ldns/packaging/ldns-config -/ldns/packaging/libldns.pc -/ldns-1.6.9/ /libcodec2/src/c2dec /libcodec2/src/c2enc /libcodec2/src/c2sim @@ -287,221 +243,135 @@ /libcodec2/unittest/tnlp /libcodec2/unittest/tquant /libcodec2/unittest/vqtrain +/libdingaling/build/compile /libdingaling/Makefile /libdingaling/Makefile.in -/libdingaling/aclocal.m4 -/libdingaling/build/compile -/libdingaling/build/config.guess -/libdingaling/build/config.sub -/libdingaling/build/depcomp -/libdingaling/build/install-sh -/libdingaling/build/ltmain.sh -/libdingaling/build/missing -/libdingaling/config.log -/libdingaling/config.status -/libdingaling/configure -/libdingaling/libtool /libdingaling/src/config.h -/libdingaling/src/config.h.in /libdingaling/src/stamp-h1 -/libedit/Makefile -/libedit/Makefile.in -/libedit/aclocal.m4 -/libedit/config.guess /libedit/config.h -/libedit/config.h.in -/libedit/config.log -/libedit/config.status -/libedit/config.sub -/libedit/configure -/libedit/depcomp -/libedit/doc/Makefile -/libedit/doc/Makefile.in /libedit/doc/editline.3 /libedit/doc/editrc.5 +/libedit/doc/Makefile +/libedit/doc/Makefile.in +/libedit/examples/fileman /libedit/examples/Makefile /libedit/examples/Makefile.in -/libedit/examples/fileman /libedit/examples/test -/libedit/install-sh -/libedit/libedit.pc -/libedit/libtool -/libedit/ltmain.sh -/libedit/missing -/libedit/src/Makefile -/libedit/src/Makefile.in +/libedit/Makefile +/libedit/Makefile.in /libedit/src/common.h /libedit/src/emacs.h /libedit/src/fcns.c /libedit/src/fcns.h /libedit/src/help.c /libedit/src/help.h +/libedit/src/Makefile +/libedit/src/Makefile.in /libedit/src/vi.h /libedit/stamp-h1 -/libg722_1/Makefile -/libg722_1/Makefile.in -/libg722_1/aclocal.m4 /libg722_1/config-h.in -/libg722_1/config.log -/libg722_1/config.status -/libg722_1/config/config.guess -/libg722_1/config/config.sub -/libg722_1/config/install-sh -/libg722_1/config/ltmain.sh -/libg722_1/configure /libg722_1/doc/Makefile /libg722_1/doc/Makefile.in /libg722_1/g722_1.spec -/libg722_1/libtool -/libg722_1/src/Makefile -/libg722_1/src/Makefile.in +/libg722_1/Makefile +/libg722_1/Makefile.in /libg722_1/src/config.h /libg722_1/src/dct4.h /libg722_1/src/g722_1.h /libg722_1/src/make_dct4_tables +/libg722_1/src/Makefile +/libg722_1/src/Makefile.in /libg722_1/src/make_tables /libg722_1/src/stamp-h1 -/libg722_1/test-data/Makefile -/libg722_1/test-data/Makefile.in /libg722_1/test-data/itu/Makefile /libg722_1/test-data/itu/Makefile.in /libg722_1/test-data/local/Makefile /libg722_1/test-data/local/Makefile.in +/libg722_1/test-data/Makefile +/libg722_1/test-data/Makefile.in /libg722_1/tests/Makefile /libg722_1/tests/Makefile.in /libg722_1/tests/regression_tests.sh -/libg722_1/g722_1.pc +/libg723_1/ +/libg729/ +/libmemcached-0.32/ +/libogg-1.1.3/ +/libshout-2.2.2/ /libsndfile/Cfg/compile -/libsndfile/Cfg/config.guess -/libsndfile/Cfg/config.sub -/libsndfile/Cfg/depcomp -/libsndfile/Cfg/install-sh -/libsndfile/Cfg/ltmain.sh -/libsndfile/Cfg/missing -/libsndfile/M4/Makefile -/libsndfile/M4/Makefile.in -/libsndfile/M4/lt~obsolete.m4 -/libsndfile/Makefile -/libsndfile/Makefile.in -/libsndfile/Octave/Makefile -/libsndfile/Octave/Makefile.in -/libsndfile/Win32/Makefile -/libsndfile/Win32/Makefile.in -/libsndfile/aclocal.m4 -/libsndfile/config.log -/libsndfile/config.status -/libsndfile/configure /libsndfile/doc/Makefile /libsndfile/doc/Makefile.in /libsndfile/examples/Makefile /libsndfile/examples/Makefile.in /libsndfile/libsndfile.spec -/libsndfile/libtool +/libsndfile/M4/libtool.m4 +/libsndfile/M4/lt~obsolete.m4 +/libsndfile/M4/ltoptions.m4 +/libsndfile/M4/ltsugar.m4 +/libsndfile/M4/ltversion.m4 +/libsndfile/M4/Makefile +/libsndfile/M4/Makefile.in +/libsndfile/Makefile +/libsndfile/Makefile.in /libsndfile/man/Makefile /libsndfile/man/Makefile.in +/libsndfile/Octave/Makefile +/libsndfile/Octave/Makefile.in /libsndfile/programs/Makefile /libsndfile/programs/Makefile.in /libsndfile/regtest/Makefile /libsndfile/regtest/Makefile.in -/libsndfile/sndfile.pc +/libsndfile/src/config.h +/libsndfile/src/G72x/g72x_test /libsndfile/src/G72x/Makefile /libsndfile/src/G72x/Makefile.in -/libsndfile/src/G72x/g72x_test /libsndfile/src/GSM610/Makefile /libsndfile/src/GSM610/Makefile.in /libsndfile/src/Makefile /libsndfile/src/Makefile.in -/libsndfile/src/config.h -/libsndfile/src/config.h.in /libsndfile/src/sndfile.h /libsndfile/src/stamp-h1 /libsndfile/src/test_main /libsndfile/tests/Makefile /libsndfile/tests/Makefile.in /libsndfile/tests/test_wrapper.sh +/libsndfile/Win32/Makefile +/libsndfile/Win32/Makefile.in +/mongo-cxx-driver-v1.8/ +/mpg123/ +/openldap-2.4.19/ +/openssl-1.0.0a/ +/openzap/build/compile /openzap/COPYING /openzap/INSTALL /openzap/Makefile.in -/openzap/build/compile -/openzap/build/depcomp -/openzap/build/install-sh -/openzap/build/ltmain.sh -/openzap/build/missing -/openzap/openzap.pc +/opus-0.9.0/ +/pcre/config.h /pcre/Makefile /pcre/Makefile.in -/pcre/aclocal.m4 -/pcre/config.guess -/pcre/config.h -/pcre/config.h.in -/pcre/config.log -/pcre/config.status -/pcre/config.sub -/pcre/configure -/pcre/install-sh -/pcre/libpcre.pc -/pcre/libpcrecpp.pc -/pcre/libtool -/pcre/ltmain.sh -/pcre/missing -/pcre/pcre-config /pcre/pcre_chartables.c +/pcre/pcre-config +/pcre/pcrecpparg.h +/pcre/pcrecpp_unittest +/pcre/pcregrep /pcre/pcre_scanner_unittest /pcre/pcre_stringpiece.h /pcre/pcre_stringpiece_unittest -/pcre/pcrecpp_unittest -/pcre/pcrecpparg.h -/pcre/pcregrep /pcre/pcretest /pcre/stamp-h1 +/pocketsphinx-0.7/ +/portaudio/bin/ +/portaudio/bin-stamp +/portaudio/lib-stamp /portaudio/Makefile -/portaudio/aclocal.m4 -/portaudio/config.guess -/portaudio/config.log -/portaudio/config.status -/portaudio/config.sub -/portaudio/configure -/portaudio/install-sh -/portaudio/libtool -/portaudio/ltmain.sh -/portaudio/missing -/portaudio/portaudio-2.0.pc -!/portaudio/bindings/cpp/build/gnu/aclocal.m4 -!/portaudio/bindings/cpp/build/gnu/configure +/pthreads-w32-2-7-0-release/ +/silk/compile +/silk/Decoder +/silk/Encoder /silk/Makefile /silk/Makefile.in -/silk/aclocal.m4 -/silk/compile -/silk/config.guess -/silk/config.log -/silk/config.status -/silk/config.sub -/silk/configure -/silk/depcomp -/silk/install-sh -/silk/libtool -/silk/ltmain.sh -/silk/missing -/sofia-sip/Makefile -/sofia-sip/Makefile.in -/sofia-sip/aclocal.m4 +/silk/signalCompare /sofia-sip/compile -/sofia-sip/config.guess /sofia-sip/config.h -/sofia-sip/config.h.in -/sofia-sip/config.log -/sofia-sip/config.status -/sofia-sip/config.sub -/sofia-sip/configure -/sofia-sip/depcomp -/sofia-sip/install-sh -/sofia-sip/libsofia-sip-ua-glib/Makefile -/sofia-sip/libsofia-sip-ua-glib/Makefile.in -/sofia-sip/libsofia-sip-ua-glib/su-glib/Doxyfile -/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile -/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile.in -/sofia-sip/libsofia-sip-ua/Makefile -/sofia-sip/libsofia-sip-ua/Makefile.in /sofia-sip/libsofia-sip-ua/bnf/Doxyfile /sofia-sip/libsofia-sip-ua/bnf/Makefile /sofia-sip/libsofia-sip-ua/bnf/Makefile.in @@ -515,25 +385,36 @@ /sofia-sip/libsofia-sip-ua/features/Makefile /sofia-sip/libsofia-sip-ua/features/Makefile.in /sofia-sip/libsofia-sip-ua/features/sofia-sip/sofia_features.h +/sofia-sip/libsofia-sip-ua-glib/Makefile +/sofia-sip/libsofia-sip-ua-glib/Makefile.in +/sofia-sip/libsofia-sip-ua-glib/su-glib/Doxyfile +/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile +/sofia-sip/libsofia-sip-ua-glib/su-glib/Makefile.in /sofia-sip/libsofia-sip-ua/http/Doxyfile -/sofia-sip/libsofia-sip-ua/http/Makefile -/sofia-sip/libsofia-sip-ua/http/Makefile.in /sofia-sip/libsofia-sip-ua/http/http_parser_table.c /sofia-sip/libsofia-sip-ua/http/http_tag.c +/sofia-sip/libsofia-sip-ua/http/http_tag_dll.c /sofia-sip/libsofia-sip-ua/http/http_tag_ref.c +/sofia-sip/libsofia-sip-ua/http/Makefile +/sofia-sip/libsofia-sip-ua/http/Makefile.in /sofia-sip/libsofia-sip-ua/http/sofia-sip/http_protos.h /sofia-sip/libsofia-sip-ua/http/sofia-sip/http_tag.h /sofia-sip/libsofia-sip-ua/ipt/Doxyfile /sofia-sip/libsofia-sip-ua/ipt/Makefile /sofia-sip/libsofia-sip-ua/ipt/Makefile.in +/sofia-sip/libsofia-sip-ua/iptsec/auth_tag_dll.c +/sofia-sip/libsofia-sip-ua/iptsec/auth_tag_ref.c /sofia-sip/libsofia-sip-ua/iptsec/Doxyfile /sofia-sip/libsofia-sip-ua/iptsec/Makefile /sofia-sip/libsofia-sip-ua/iptsec/Makefile.in -/sofia-sip/libsofia-sip-ua/iptsec/auth_tag_ref.c +/sofia-sip/libsofia-sip-ua/Makefile +/sofia-sip/libsofia-sip-ua/Makefile.in /sofia-sip/libsofia-sip-ua/msg/Doxyfile /sofia-sip/libsofia-sip-ua/msg/Makefile /sofia-sip/libsofia-sip-ua/msg/Makefile.in /sofia-sip/libsofia-sip-ua/msg/msg_mime_table.c +/sofia-sip/libsofia-sip-ua/msg/msg_tag_dll.c +/sofia-sip/libsofia-sip-ua/msg/msg_tag_ref.c /sofia-sip/libsofia-sip-ua/msg/sofia-sip/msg_mime_protos.h /sofia-sip/libsofia-sip-ua/msg/sofia-sip/msg_protos.h /sofia-sip/libsofia-sip-ua/msg/test_protos.h @@ -541,28 +422,34 @@ /sofia-sip/libsofia-sip-ua/nea/Doxyfile /sofia-sip/libsofia-sip-ua/nea/Makefile /sofia-sip/libsofia-sip-ua/nea/Makefile.in +/sofia-sip/libsofia-sip-ua/nea/nea_tag_dll.c /sofia-sip/libsofia-sip-ua/nea/nea_tag_ref.c /sofia-sip/libsofia-sip-ua/nta/Doxyfile /sofia-sip/libsofia-sip-ua/nta/Makefile /sofia-sip/libsofia-sip-ua/nta/Makefile.in +/sofia-sip/libsofia-sip-ua/nta/nta_tag_dll.c /sofia-sip/libsofia-sip-ua/nta/nta_tag_ref.c /sofia-sip/libsofia-sip-ua/nth/Doxyfile /sofia-sip/libsofia-sip-ua/nth/Makefile /sofia-sip/libsofia-sip-ua/nth/Makefile.in +/sofia-sip/libsofia-sip-ua/nth/nth_tag_dll.c /sofia-sip/libsofia-sip-ua/nth/nth_tag_ref.c /sofia-sip/libsofia-sip-ua/nua/Doxyfile /sofia-sip/libsofia-sip-ua/nua/Makefile /sofia-sip/libsofia-sip-ua/nua/Makefile.in +/sofia-sip/libsofia-sip-ua/nua/nua_tag_dll.c /sofia-sip/libsofia-sip-ua/nua/nua_tag_ref.c /sofia-sip/libsofia-sip-ua/sdp/Doxyfile /sofia-sip/libsofia-sip-ua/sdp/Makefile /sofia-sip/libsofia-sip-ua/sdp/Makefile.in +/sofia-sip/libsofia-sip-ua/sdp/sdp_tag_dll.c /sofia-sip/libsofia-sip-ua/sdp/sdp_tag_ref.c /sofia-sip/libsofia-sip-ua/sip/Doxyfile /sofia-sip/libsofia-sip-ua/sip/Makefile /sofia-sip/libsofia-sip-ua/sip/Makefile.in /sofia-sip/libsofia-sip-ua/sip/sip_parser_table.c /sofia-sip/libsofia-sip-ua/sip/sip_tag.c +/sofia-sip/libsofia-sip-ua/sip/sip_tag_dll.c /sofia-sip/libsofia-sip-ua/sip/sip_tag_ref.c /sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_extra.h /sofia-sip/libsofia-sip-ua/sip/sofia-sip/sip_hclasses.h @@ -571,6 +458,7 @@ /sofia-sip/libsofia-sip-ua/soa/Doxyfile /sofia-sip/libsofia-sip-ua/soa/Makefile /sofia-sip/libsofia-sip-ua/soa/Makefile.in +/sofia-sip/libsofia-sip-ua/soa/soa_tag_dll.c /sofia-sip/libsofia-sip-ua/soa/soa_tag_ref.c /sofia-sip/libsofia-sip-ua/sresolv/Doxyfile /sofia-sip/libsofia-sip-ua/sresolv/Makefile @@ -578,35 +466,42 @@ /sofia-sip/libsofia-sip-ua/stun/Doxyfile /sofia-sip/libsofia-sip-ua/stun/Makefile /sofia-sip/libsofia-sip-ua/stun/Makefile.in -/sofia-sip/libsofia-sip-ua/stun/stun_tag_ref.c /sofia-sip/libsofia-sip-ua/stun/stunc +/sofia-sip/libsofia-sip-ua/stun/stun_tag_dll.c +/sofia-sip/libsofia-sip-ua/stun/stun_tag_ref.c +/sofia-sip/libsofia-sip-ua/su/addrinfo /sofia-sip/libsofia-sip-ua/su/Doxyfile +/sofia-sip/libsofia-sip-ua/su/localinfo /sofia-sip/libsofia-sip-ua/su/Makefile /sofia-sip/libsofia-sip-ua/su/Makefile.in -/sofia-sip/libsofia-sip-ua/su/addrinfo -/sofia-sip/libsofia-sip-ua/su/localinfo /sofia-sip/libsofia-sip-ua/su/sofia-sip/stamp-h2 /sofia-sip/libsofia-sip-ua/su/sofia-sip/su_configure.h +/sofia-sip/libsofia-sip-ua/su/su_tag_dll.c /sofia-sip/libsofia-sip-ua/su/su_tag_ref.c /sofia-sip/libsofia-sip-ua/tport/Doxyfile /sofia-sip/libsofia-sip-ua/tport/Makefile /sofia-sip/libsofia-sip-ua/tport/Makefile.in +/sofia-sip/libsofia-sip-ua/tport/tport_tag_dll.c /sofia-sip/libsofia-sip-ua/tport/tport_tag_ref.c /sofia-sip/libsofia-sip-ua/url/Doxyfile /sofia-sip/libsofia-sip-ua/url/Makefile /sofia-sip/libsofia-sip-ua/url/Makefile.in +/sofia-sip/libsofia-sip-ua/url/url_tag_dll.c /sofia-sip/libsofia-sip-ua/url/url_tag_ref.c -/sofia-sip/libtool -/sofia-sip/ltmain.sh -/sofia-sip/missing +/sofia-sip/m4/libtool.m4 +/sofia-sip/m4/lt~obsolete.m4 +/sofia-sip/m4/ltoptions.m4 +/sofia-sip/m4/ltsugar.m4 +/sofia-sip/m4/ltversion.m4 +/sofia-sip/Makefile +/sofia-sip/Makefile.in +/sofia-sip/open_c/config.h +!/sofia-sip/open_c/config.h.in /sofia-sip/open_c/Makefile /sofia-sip/open_c/Makefile.in -/sofia-sip/open_c/config.h /sofia-sip/packages/Makefile /sofia-sip/packages/Makefile.in /sofia-sip/packages/sofia-sip-1.12.10devel.spec -/sofia-sip/packages/sofia-sip-ua-glib.pc -/sofia-sip/packages/sofia-sip-ua.pc /sofia-sip/s2check/Makefile /sofia-sip/s2check/Makefile.in /sofia-sip/stamp-h1 @@ -615,37 +510,31 @@ /sofia-sip/utils/Doxyfile /sofia-sip/utils/Makefile /sofia-sip/utils/Makefile.in +/sofia-sip/win32/config.h +!/sofia-sip/win32/config.h.in /sofia-sip/win32/Makefile /sofia-sip/win32/Makefile.in -/sofia-sip/win32/config.h -/spandsp/Makefile -/spandsp/Makefile.in -/spandsp/aclocal.m4 +/sounds/ +/soundtouch/ +/soundtouch-1.3.1/ /spandsp/config-h.in -/spandsp/config.log -/spandsp/config.status -/spandsp/config/config.guess -/spandsp/config/config.sub -/spandsp/config/depcomp -/spandsp/config/install-sh -/spandsp/config/ltmain.sh -/spandsp/config/missing -/spandsp/configure +/spandsp/doc/doxygen /spandsp/doc/Makefile /spandsp/doc/Makefile.in -/spandsp/doc/doxygen -/spandsp/libtool +/spandsp/Makefile +/spandsp/Makefile.in /spandsp/spandsp-sim/Makefile /spandsp/spandsp-sim/Makefile.in -/spandsp/spandsp.pc -/spandsp/src/Makefile -/spandsp/src/Makefile.in /spandsp/src/at_interpreter_dictionary.h /spandsp/src/config.h /spandsp/src/make_at_dictionary +/spandsp/src/Makefile +/spandsp/src/Makefile.in /spandsp/src/make_math_fixed_tables /spandsp/src/make_modem_filter /spandsp/src/math_fixed_tables.h +/spandsp/src/msvc/All/BuildLog make_at_dictionary.htm +/spandsp/src/msvc/All/BuildLog make_modem_filter.htm /spandsp/src/spandsp.h /spandsp/src/stamp-h1 /spandsp/src/v17_v32bis_rx_fixed_rrc.h @@ -670,44 +559,30 @@ /spandsp/src/v29rx_floating_rrc.h /spandsp/src/v29tx_fixed_rrc.h /spandsp/src/v29tx_floating_rrc.h -/spandsp/test-data/Makefile -/spandsp/test-data/Makefile.in -/spandsp/test-data/etsi/Makefile -/spandsp/test-data/etsi/Makefile.in /spandsp/test-data/etsi/fax/Makefile /spandsp/test-data/etsi/fax/Makefile.in -/spandsp/test-data/itu/Makefile -/spandsp/test-data/itu/Makefile.in +/spandsp/test-data/etsi/Makefile +/spandsp/test-data/etsi/Makefile.in /spandsp/test-data/itu/fax/Makefile /spandsp/test-data/itu/fax/Makefile.in +/spandsp/test-data/itu/Makefile +/spandsp/test-data/itu/Makefile.in /spandsp/test-data/local/Makefile /spandsp/test-data/local/Makefile.in +/spandsp/test-data/Makefile +/spandsp/test-data/Makefile.in /spandsp/tests/Makefile /spandsp/tests/Makefile.in -/speex/Makefile -/speex/Makefile.in -/speex/aclocal.m4 -/speex/config.guess /speex/config.h -/speex/config.h.in -/speex/config.log -/speex/config.status -/speex/config.sub -/speex/configure -/speex/depcomp /speex/include/Makefile /speex/include/Makefile.in /speex/include/speex/Makefile /speex/include/speex/Makefile.in /speex/include/speex/speex_config_types.h -/speex/install-sh /speex/libspeex/Makefile /speex/libspeex/Makefile.in -/speex/libtool -/speex/ltmain.sh -/speex/missing -/speex/speex.pc -/speex/speexdsp.pc +/speex/Makefile +/speex/Makefile.in /speex/src/Makefile /speex/src/Makefile.in /speex/stamp-h1 @@ -721,64 +596,57 @@ /speex/ti/speex_C55_test/Makefile.in /speex/ti/speex_C64_test/Makefile /speex/ti/speex_C64_test/Makefile.in +/speex/win32/libspeex/Makefile +/speex/win32/libspeex/Makefile.in /speex/win32/Makefile /speex/win32/Makefile.in -/speex/win32/VS2003/Makefile -/speex/win32/VS2003/Makefile.in -/speex/win32/VS2003/libspeex/Makefile -/speex/win32/VS2003/libspeex/Makefile.in +/speex/win32/speexdec/Makefile +/speex/win32/speexdec/Makefile.in +/speex/win32/speexenc/Makefile +/speex/win32/speexenc/Makefile.in /speex/win32/VS2003/libspeexdsp/Makefile /speex/win32/VS2003/libspeexdsp/Makefile.in +/speex/win32/VS2003/libspeex/Makefile +/speex/win32/VS2003/libspeex/Makefile.in +/speex/win32/VS2003/Makefile +/speex/win32/VS2003/Makefile.in /speex/win32/VS2003/speexdec/Makefile /speex/win32/VS2003/speexdec/Makefile.in /speex/win32/VS2003/speexenc/Makefile /speex/win32/VS2003/speexenc/Makefile.in /speex/win32/VS2003/tests/Makefile /speex/win32/VS2003/tests/Makefile.in -/speex/win32/VS2005/Makefile -/speex/win32/VS2005/Makefile.in -/speex/win32/VS2005/libspeex/Makefile -/speex/win32/VS2005/libspeex/Makefile.in /speex/win32/VS2005/libspeexdsp/Makefile /speex/win32/VS2005/libspeexdsp/Makefile.in +/speex/win32/VS2005/libspeex/Makefile +/speex/win32/VS2005/libspeex/Makefile.in +/speex/win32/VS2005/Makefile +/speex/win32/VS2005/Makefile.in /speex/win32/VS2005/speexdec/Makefile /speex/win32/VS2005/speexdec/Makefile.in /speex/win32/VS2005/speexenc/Makefile /speex/win32/VS2005/speexenc/Makefile.in /speex/win32/VS2005/tests/Makefile /speex/win32/VS2005/tests/Makefile.in -/speex/win32/VS2008/Makefile -/speex/win32/VS2008/Makefile.in -/speex/win32/VS2008/libspeex/Makefile -/speex/win32/VS2008/libspeex/Makefile.in +/speex/win32/VS2008/libspeexdsp/*/*/libspeexdsp.log /speex/win32/VS2008/libspeexdsp/Makefile /speex/win32/VS2008/libspeexdsp/Makefile.in +/speex/win32/VS2008/libspeex/*/*/libspeex.log +/speex/win32/VS2008/libspeex/Makefile +/speex/win32/VS2008/libspeex/Makefile.in +/speex/win32/VS2008/Makefile +/speex/win32/VS2008/Makefile.in /speex/win32/VS2008/speexdec/Makefile /speex/win32/VS2008/speexdec/Makefile.in /speex/win32/VS2008/speexenc/Makefile /speex/win32/VS2008/speexenc/Makefile.in /speex/win32/VS2008/tests/Makefile /speex/win32/VS2008/tests/Makefile.in -/speex/win32/libspeex/Makefile -/speex/win32/libspeex/Makefile.in -/speex/win32/speexdec/Makefile -/speex/win32/speexdec/Makefile.in -/speex/win32/speexenc/Makefile -/speex/win32/speexenc/Makefile.in -/sqlite/Makefile -/sqlite/aclocal.m4 -/sqlite/config.guess -/sqlite/config.log -/sqlite/config.status -/sqlite/config.sub -/sqlite/configure -/sqlite/install-sh +/sphinxbase-0.7/ /sqlite/keywordhash.h /sqlite/lemon /sqlite/lempar.c -/sqlite/libtool -/sqlite/ltmain.sh -/sqlite/missing +/sqlite/Makefile /sqlite/mkkeywordhash /sqlite/opcodes.c /sqlite/opcodes.h @@ -789,46 +657,21 @@ /sqlite/parse.y /sqlite/sqlite3 /sqlite/sqlite3.h -/sqlite/sqlite3.pc -/srtp/Makefile -/srtp/Makefile.in -/srtp/aclocal.m4 /srtp/aes_tables -/srtp/build/config.guess -/srtp/build/config.sub -/srtp/build/depcomp -/srtp/build/install-sh -/srtp/build/ltmain.sh -/srtp/build/missing -/srtp/config.log -/srtp/config.status /srtp/config_in.h -/srtp/configure -/srtp/crypto/Makefile /srtp/crypto/include/config.h /srtp/crypto/include/stamp-h1 +/srtp/crypto/Makefile /srtp/doc/Makefile -/srtp/libtool -/srtp/srtp-1.42.pc +/srtp/Makefile +/srtp/Makefile.in /srtp/test/Makefile /srtp/test/Makefile.in -/tiff-3.8.2/Makefile -/tiff-3.8.2/Makefile.in -/tiff-3.8.2/aclocal.m4 -/tiff-3.8.2/config.log -/tiff-3.8.2/config.status -/tiff-3.8.2/config/config.guess -/tiff-3.8.2/config/config.sub -/tiff-3.8.2/config/install-sh -/tiff-3.8.2/config/ltmain.sh -/tiff-3.8.2/configure -/tiff-3.8.2/contrib/Makefile -/tiff-3.8.2/contrib/Makefile.in /tiff-3.8.2/contrib/acorn/Makefile /tiff-3.8.2/contrib/acorn/Makefile.in +/tiff-3.8.2/contrib/addtiffo/addtiffo /tiff-3.8.2/contrib/addtiffo/Makefile /tiff-3.8.2/contrib/addtiffo/Makefile.in -/tiff-3.8.2/contrib/addtiffo/addtiffo /tiff-3.8.2/contrib/dbs/Makefile /tiff-3.8.2/contrib/dbs/Makefile.in /tiff-3.8.2/contrib/dbs/tiff-bi @@ -837,13 +680,15 @@ /tiff-3.8.2/contrib/dbs/tiff-rgb /tiff-3.8.2/contrib/dbs/xtiff/Makefile /tiff-3.8.2/contrib/dbs/xtiff/Makefile.in +/tiff-3.8.2/contrib/iptcutil/iptcutil /tiff-3.8.2/contrib/iptcutil/Makefile /tiff-3.8.2/contrib/iptcutil/Makefile.in -/tiff-3.8.2/contrib/iptcutil/iptcutil /tiff-3.8.2/contrib/mac-cw/Makefile /tiff-3.8.2/contrib/mac-cw/Makefile.in /tiff-3.8.2/contrib/mac-mpw/Makefile /tiff-3.8.2/contrib/mac-mpw/Makefile.in +/tiff-3.8.2/contrib/Makefile +/tiff-3.8.2/contrib/Makefile.in /tiff-3.8.2/contrib/mfs/Makefile /tiff-3.8.2/contrib/mfs/Makefile.in /tiff-3.8.2/contrib/ojpeg/Makefile @@ -858,10 +703,10 @@ /tiff-3.8.2/contrib/tags/Makefile.in /tiff-3.8.2/contrib/win_dib/Makefile /tiff-3.8.2/contrib/win_dib/Makefile.in -/tiff-3.8.2/html/Makefile -/tiff-3.8.2/html/Makefile.in /tiff-3.8.2/html/images/Makefile /tiff-3.8.2/html/images/Makefile.in +/tiff-3.8.2/html/Makefile +/tiff-3.8.2/html/Makefile.in /tiff-3.8.2/html/man/Makefile /tiff-3.8.2/html/man/Makefile.in /tiff-3.8.2/libtiff/Makefile @@ -872,19 +717,26 @@ /tiff-3.8.2/libtiff/tif_config.h /tiff-3.8.2/libtiff/tif_config.h.in /tiff-3.8.2/libtiff/tiffconf.h -/tiff-3.8.2/libtool +/tiff-3.8.2/m4/libtool.m4 +/tiff-3.8.2/m4/lt~obsolete.m4 +/tiff-3.8.2/m4/ltoptions.m4 +/tiff-3.8.2/m4/ltsugar.m4 +/tiff-3.8.2/m4/ltversion.m4 +/tiff-3.8.2/Makefile +/tiff-3.8.2/Makefile.in /tiff-3.8.2/man/Makefile /tiff-3.8.2/man/Makefile.in /tiff-3.8.2/port/Makefile /tiff-3.8.2/port/Makefile.in /tiff-3.8.2/test/Makefile /tiff-3.8.2/test/Makefile.in -/tiff-3.8.2/tools/Makefile -/tiff-3.8.2/tools/Makefile.in +/tiff-3.8.2/tiff-Makefile.tgz /tiff-3.8.2/tools/bmp2tiff /tiff-3.8.2/tools/fax2ps /tiff-3.8.2/tools/fax2tiff /tiff-3.8.2/tools/gif2tiff +/tiff-3.8.2/tools/Makefile +/tiff-3.8.2/tools/Makefile.in /tiff-3.8.2/tools/pal2rgb /tiff-3.8.2/tools/ppm2tiff /tiff-3.8.2/tools/ras2tiff @@ -899,65 +751,60 @@ /tiff-3.8.2/tools/tiffcp /tiff-3.8.2/tools/tiffdither /tiff-3.8.2/tools/tiffdump +/tiff-3.8.2/tools/tiffgt /tiff-3.8.2/tools/tiffinfo /tiff-3.8.2/tools/tiffmedian /tiff-3.8.2/tools/tiffset /tiff-3.8.2/tools/tiffsplit -/unimrcp/Makefile -/unimrcp/Makefile.in -/unimrcp/aclocal.m4 +/unimrcp/build/acmacros/libtool.m4 +/unimrcp/build/acmacros/lt~obsolete.m4 +/unimrcp/build/acmacros/ltoptions.m4 +/unimrcp/build/acmacros/ltsugar.m4 +/unimrcp/build/acmacros/ltversion.m4 /unimrcp/build/Makefile /unimrcp/build/Makefile.in -/unimrcp/build/config.guess -/unimrcp/build/config.sub -/unimrcp/build/depcomp -/unimrcp/build/install-sh -/unimrcp/build/ltmain.sh -/unimrcp/build/missing /unimrcp/build/pkgconfig/Makefile /unimrcp/build/pkgconfig/Makefile.in -/unimrcp/build/pkgconfig/unimrcpclient.pc -/unimrcp/build/pkgconfig/unimrcpplugin.pc -/unimrcp/build/pkgconfig/unimrcpserver.pc /unimrcp/build/svnrev/Makefile /unimrcp/build/svnrev/Makefile.in +/unimrcp/build/svnrev/svnrev +!/unimrcp/build/tools/unimrcpservice.exe.manifest +/unimrcp/build/uni_revision.h +!/unimrcp/configure.gnu /unimrcp/conf/Makefile /unimrcp/conf/Makefile.in -/unimrcp/config.log -/unimrcp/config.status -/unimrcp/configure /unimrcp/data/Makefile /unimrcp/data/Makefile.in /unimrcp/docs/doxygen.conf -/unimrcp/libs/Makefile -/unimrcp/libs/Makefile.in /unimrcp/libs/apr-toolkit/Makefile /unimrcp/libs/apr-toolkit/Makefile.in +/unimrcp/libs/Makefile +/unimrcp/libs/Makefile.in /unimrcp/libs/mpf/Makefile /unimrcp/libs/mpf/Makefile.in /unimrcp/libs/mrcp-client/Makefile /unimrcp/libs/mrcp-client/Makefile.in /unimrcp/libs/mrcp-engine/Makefile /unimrcp/libs/mrcp-engine/Makefile.in +/unimrcp/libs/mrcp/Makefile +/unimrcp/libs/mrcp/Makefile.in /unimrcp/libs/mrcp-server/Makefile /unimrcp/libs/mrcp-server/Makefile.in /unimrcp/libs/mrcp-signaling/Makefile /unimrcp/libs/mrcp-signaling/Makefile.in -/unimrcp/libs/mrcp/Makefile -/unimrcp/libs/mrcp/Makefile.in /unimrcp/libs/mrcpv2-transport/Makefile /unimrcp/libs/mrcpv2-transport/Makefile.in /unimrcp/libs/uni-rtsp/Makefile /unimrcp/libs/uni-rtsp/Makefile.in -/unimrcp/libtool +/unimrcp/Makefile +/unimrcp/Makefile.in /unimrcp/modules/Makefile /unimrcp/modules/Makefile.in /unimrcp/modules/mrcp-sofiasip/Makefile /unimrcp/modules/mrcp-sofiasip/Makefile.in /unimrcp/modules/mrcp-unirtsp/Makefile /unimrcp/modules/mrcp-unirtsp/Makefile.in -/unimrcp/platforms/Makefile -/unimrcp/platforms/Makefile.in +/unimrcp/platforms/asr-client/asrclient /unimrcp/platforms/asr-client/Makefile /unimrcp/platforms/asr-client/Makefile.in /unimrcp/platforms/libasr-client/Makefile @@ -966,18 +813,25 @@ /unimrcp/platforms/libunimrcp-client/Makefile.in /unimrcp/platforms/libunimrcp-server/Makefile /unimrcp/platforms/libunimrcp-server/Makefile.in +/unimrcp/platforms/Makefile +/unimrcp/platforms/Makefile.in /unimrcp/platforms/umc/Makefile /unimrcp/platforms/umc/Makefile.in +/unimrcp/platforms/umc/umc /unimrcp/platforms/unimrcp-client/Makefile /unimrcp/platforms/unimrcp-client/Makefile.in +/unimrcp/platforms/unimrcp-client/unimrcpclient /unimrcp/platforms/unimrcp-server/Makefile /unimrcp/platforms/unimrcp-server/Makefile.in -/unimrcp/plugins/Makefile -/unimrcp/plugins/Makefile.in +/unimrcp/platforms/unimrcp-server/unimrcpserver /unimrcp/plugins/demo-recog/Makefile /unimrcp/plugins/demo-recog/Makefile.in /unimrcp/plugins/demo-synth/Makefile /unimrcp/plugins/demo-synth/Makefile.in +/unimrcp/plugins/demo-verifier/Makefile +/unimrcp/plugins/demo-verifier/Makefile.in +/unimrcp/plugins/Makefile +/unimrcp/plugins/Makefile.in /unimrcp/plugins/mrcp-cepstral/Makefile /unimrcp/plugins/mrcp-cepstral/Makefile.in /unimrcp/plugins/mrcp-flite/Makefile @@ -986,12 +840,10 @@ /unimrcp/plugins/mrcp-pocketsphinx/Makefile.in /unimrcp/plugins/mrcp-recorder/Makefile /unimrcp/plugins/mrcp-recorder/Makefile.in -/unimrcp/plugins/demo-verifier/Makefile -/unimrcp/plugins/demo-verifier/Makefile.in -/unimrcp/tests/Makefile -/unimrcp/tests/Makefile.in /unimrcp/tests/apttest/Makefile /unimrcp/tests/apttest/Makefile.in +/unimrcp/tests/Makefile +/unimrcp/tests/Makefile.in /unimrcp/tests/mpftest/Makefile /unimrcp/tests/mpftest/Makefile.in /unimrcp/tests/mrcptest/Makefile @@ -1000,75 +852,17 @@ /unimrcp/tests/rtsptest/Makefile.in /unimrcp/tests/strtablegen/Makefile /unimrcp/tests/strtablegen/Makefile.in +/win32/celt/*/*/libcelt.log +/win32/libg722_1/*/*/libg722_1.log +/win32/libshout/*/*/libshout.log +/win32/pcre/pcre_chartables.c +/win32/tmp*.bat /xmlrpc-c/include/xmlrpc-c/config.h +!/xmlrpc-c/include/xmlrpc-c/config.h.in /xmlrpc-c/stamp-h2 /xmlrpc-c/xmlrpc_amconfig.h /xmlrpc-c/xmlrpc_config.h -/zeromq-*/ -/apr/build/install-sh -/js/install-sh -/js/nsprpub/lib/ds/libplds4.dylib -/js/nsprpub/lib/libc/src/libplc4.dylib -/js/nsprpub/pr/src/libnspr4.dylib -/libsndfile/M4/libtool.m4 -/libsndfile/M4/ltoptions.m4 -/libsndfile/M4/ltsugar.m4 -/libsndfile/M4/ltversion.m4 -/sofia-sip/m4/libtool.m4 -/sofia-sip/m4/ltoptions.m4 -/sofia-sip/m4/ltsugar.m4 -/sofia-sip/m4/ltversion.m4 -/sofia-sip/m4/lt~obsolete.m4 -/sqlite/lemon.dSYM/ -/sqlite/mkkeywordhash.dSYM/ -/sqlite/sqlite3.dSYM/ -/tiff-3.8.2/m4/libtool.m4 -/tiff-3.8.2/m4/ltoptions.m4 -/tiff-3.8.2/m4/ltsugar.m4 -/tiff-3.8.2/m4/ltversion.m4 -/tiff-3.8.2/m4/lt~obsolete.m4 -/unimrcp/build/acmacros/libtool.m4 -/unimrcp/build/acmacros/ltoptions.m4 -/unimrcp/build/acmacros/ltsugar.m4 -/unimrcp/build/acmacros/ltversion.m4 -/unimrcp/build/acmacros/lt~obsolete.m4 -/*.tar.gz -/celt-0.7.1/ -/celt-0.10.0/ -/flite-1.3.99/ -/freeradius-client-1.1.6/ -/iksemel/test/tst-dom -/iksemel/test/tst-filter -/iksemel/test/tst-iks -/iksemel/test/tst-ikstack -/iksemel/test/tst-jid -/iksemel/test/tst-md5 -/iksemel/test/tst-sax -/iksemel/test/tst-sha -/iksemel/tools/ikslint -/iksemel/tools/iksperf -/iksemel/tools/iksroster -/json-c-0.9/ -/lame-3.97/ -/libmemcached-0.32/ -/libshout-2.2.2/ -/mpg123/ -/openldap-2.4.19/ -/pocketsphinx-0.7/ -/portaudio/bin-stamp -/portaudio/bin/ -/portaudio/lib-stamp -/silk/Decoder -/silk/Encoder -/silk/signalCompare -/soundtouch-1.3.1/ -/sphinxbase-0.7/ -/unimrcp/platforms/asr-client/asrclient -/unimrcp/platforms/umc/umc -/unimrcp/platforms/unimrcp-client/unimrcpclient -/unimrcp/platforms/unimrcp-server/unimrcpserver -!/unimrcp/configure.gnu -!/unimrcp/build/tools/unimrcpservice.exe.manifest +/yaml/ /yaml/config.h /yaml/stamp-h1 /yaml/tests/example-deconstructor @@ -1080,84 +874,47 @@ /yaml/tests/run-loader /yaml/tests/run-parser /yaml/tests/run-scanner +/zeromq-*/ + +# build products we should remove +!/apr-util/xml/expat/conftools/config.guess +!/apr-util/xml/expat/conftools/config.sub +!/apr-util/xml/expat/conftools/install-sh +!/apr-util/xml/expat/conftools/missing +!/curl/src/config.h.in +!/iksemel/ltmain.sh +!/ldns/drill/config.h.in +!/ldns/drill/configure +!/ldns/drill/install-sh +!/ldns/examples/config.h.in +!/ldns/examples/configure +!/ldns/install-sh +!/ldns/ldns/config.h.in +!/libdingaling/config.guess +!/libdingaling/config.sub +!/libdingaling/depcomp +!/libdingaling/install-sh +!/libdingaling/ltmain.sh +!/libdingaling/missing +!/libg722_1/config/depcomp +!/libg722_1/config/missing +!/pcre/depcomp +!/portaudio/bindings/cpp/build/gnu/aclocal.m4 +!/portaudio/bindings/cpp/build/gnu/config.guess +!/portaudio/bindings/cpp/build/gnu/config.sub +!/portaudio/bindings/cpp/build/gnu/configure +!/portaudio/bindings/cpp/build/gnu/install-sh +!/tiff-3.8.2/config/depcomp +!/tiff-3.8.2/config/missing +!/xmlrpc-c/install-sh +!/xmlrpc-c/missing !/yaml/aclocal.m4 +!/yaml/config.h.in +!/yaml/config/config.guess +!/yaml/config/config.sub +!/yaml/config/depcomp +!/yaml/config/install-sh +!/yaml/config/ltmain.sh +!/yaml/config/missing !/yaml/configure -/Communicator_semi_40.cd_semi_6000/ -/libogg-1.1.3/ -/pthreads-w32-2-7-0-release/ -/sounds/ -/sofia-sip/libsofia-sip-ua/http/http_tag_dll.c -/sofia-sip/libsofia-sip-ua/iptsec/auth_tag_dll.c -/sofia-sip/libsofia-sip-ua/msg/msg_tag_dll.c -/sofia-sip/libsofia-sip-ua/msg/msg_tag_ref.c -/sofia-sip/libsofia-sip-ua/nea/nea_tag_dll.c -/sofia-sip/libsofia-sip-ua/nta/nta_tag_dll.c -/sofia-sip/libsofia-sip-ua/nth/nth_tag_dll.c -/sofia-sip/libsofia-sip-ua/nua/nua_tag_dll.c -/sofia-sip/libsofia-sip-ua/sdp/sdp_tag_dll.c -/sofia-sip/libsofia-sip-ua/sip/sip_tag_dll.c -/sofia-sip/libsofia-sip-ua/soa/soa_tag_dll.c -/sofia-sip/libsofia-sip-ua/stun/stun_tag_dll.c -/sofia-sip/libsofia-sip-ua/su/su_tag_dll.c -/sofia-sip/libsofia-sip-ua/tport/tport_tag_dll.c -/sofia-sip/libsofia-sip-ua/url/url_tag_dll.c -/win32/pcre/pcre_chartables.c -/apr-util/include/apu_config.h -/apr-util/include/apu_select_dbm.h -/include/ -/js/nsprpub/pr/include/prcpucfg.h -/apr-util/xml/expat/lib/config.h -/apr-util/a.out.dSYM/ -/apr/a.out.dSYM/ -/broadvoice/a.out.dSYM/ -/curl/a.out.dSYM/ -/freetdm/COPYING -/freetdm/INSTALL -/freetdm/Makefile.in -/freetdm/build/compile -/freetdm/build/depcomp -/freetdm/build/install-sh -/freetdm/build/ltmain.sh -/freetdm/build/missing -/freetdm/freetdm.pc -/iksemel/a.out.dSYM/ -/ilbc/a.out.dSYM/ -/js/a.out.dSYM/ -/libdingaling/a.out.dSYM/ -/libedit/a.out.dSYM/ -/libg722_1/a.out.dSYM/ -/libsndfile/a.out.dSYM/ -/pcre/a.out.dSYM/ -/portaudio/a.out.dSYM/ -/sofia-sip/a.out.dSYM/ -/spandsp/a.out.dSYM/ -/speex/a.out.dSYM/ -/srtp/a.out.dSYM/ -/tiff-3.8.2/a.out.dSYM/ -/tiff-3.8.2/tiff-Makefile.tgz -/unimrcp/a.out.dSYM/ -/esl/fs_cli.dSYM/ -/esl/ivrd.dSYM/ -/esl/testclient.dSYM/ -/esl/testserver.dSYM/ -/openssl-1.0.0a/ -/esl/Debug/ -/esl/Release/ -/freetdm/msvc/Release/ -/freetdm/msvc/Debug/ - -/spandsp/src/msvc/All/BuildLog make_at_dictionary.htm -/spandsp/src/msvc/All/BuildLog make_modem_filter.htm - -BuildLog*.htm -/win32/tmp*.bat - -/speex/win32/VS2008/libspeex/*/*/libspeex.log -/speex/win32/VS2008/libspeexdsp/*/*/libspeexdsp.log -/win32/celt/*/*/libcelt.log -/win32/libg722_1/*/*/libg722_1.log -/win32/libshout/*/*/libshout.log -openssl_manifest.rc -libeay32_manifest.rc -ssleay32_manifest.rc diff --git a/libs/esl/Makefile b/libs/esl/Makefile index 0e6471a700..746f730124 100644 --- a/libs/esl/Makefile +++ b/libs/esl/Makefile @@ -7,7 +7,7 @@ PICKY=-O2 CFLAGS=$(BASE_FLAGS) $(PICKY) CXXFLAGS=$(BASE_FLAGS) MYLIB=libesl.a -LIBS=-lncurses -lpthread -lesl -lm +LIBS=-lncurses -lesl -lpthread -lm LDFLAGS=-L. OBJS=src/esl.o src/esl_event.o src/esl_threadmutex.o src/esl_config.o src/esl_json.o src/esl_buffer.o SRC=src/esl.c src/esl_json.c src/esl_event.c src/esl_threadmutex.c src/esl_config.c src/esl_oop.cpp src/esl_json.c src/esl_buffer.c @@ -32,7 +32,7 @@ testclient: $(MYLIB) testclient.c $(CC) $(CC_CFLAGS) $(CFLAGS) testclient.c -o testclient $(LDFLAGS) $(LIBS) fs_cli: $(MYLIB) fs_cli.c - $(CC) $(CC_CFLAGS) $(CFLAGS) fs_cli.c -o fs_cli $(LDFLAGS) -L$(LIBEDIT_DIR)/src/.libs $(LIBS) -ledit + $(CC) $(CC_CFLAGS) $(CFLAGS) fs_cli.c -o fs_cli $(LDFLAGS) -L$(LIBEDIT_DIR)/src/.libs -ledit $(LIBS) %.o: %.c $(HEADERS) $(CC) $(CC_CFLAGS) $(CFLAGS) -c $< -o $@ diff --git a/libs/esl/fs_cli.c b/libs/esl/fs_cli.c index 5135f222ac..22906e710f 100644 --- a/libs/esl/fs_cli.c +++ b/libs/esl/fs_cli.c @@ -6,17 +6,19 @@ #include #include #include +#include #define CMD_BUFLEN 1024 -static int WARN_STOP = 0; -#ifdef WIN32 +#ifndef WIN32 +#include +#include +#include +#else #define strdup(src) _strdup(src) -#define usleep(time) Sleep(time/1000) #define fileno _fileno #define read _read #include - #define CC_NORM 0 #define CC_NEWLINE 1 #define CC_EOF 2 @@ -27,7 +29,6 @@ static int WARN_STOP = 0; #define CC_FATAL 7 #define CC_REDISPLAY 8 #define CC_REFRESH_BEEP 9 - #define HISTLEN 10 #define KEY_UP 1 #define KEY_DOWN 2 @@ -38,23 +39,14 @@ static int WARN_STOP = 0; #define KEY_RIGHT 7 #define KEY_INSERT 8 #define PROMPT_OP 9 - -static int console_bufferInput (char* buf, int len, char *cmd, int key); -static unsigned char esl_console_complete(const char *buffer, const char *cursor); -#else -#include -#include +static int console_bufferInput (char *buf, int len, char *cmd, int key); +static unsigned char esl_console_complete(const char *buffer, const char *cursor, const char *lastchar); #endif -#include - #ifdef HAVE_EDITLINE #include #endif -static char prompt_str[512] = ""; -static int CONNECTED = 0; - typedef struct { char name[128]; char host[128]; @@ -65,109 +57,96 @@ typedef struct { const char *console_fnkeys[12]; char loglevel[128]; int quiet; + char prompt_color[12]; + char input_text_color[12]; + char output_text_color[12]; } cli_profile_t; +static int warn_stop = 0; +static int connected = 0; +static int allow_ctl_c = 0; +static char bare_prompt_str[512] = ""; +static int bare_prompt_str_len = 0; +static char prompt_str[512] = ""; +static char prompt_color[12] = {ESL_SEQ_DEFAULT_COLOR}; +static char input_text_color[12] = {ESL_SEQ_DEFAULT_COLOR}; +static char output_text_color[12] = {ESL_SEQ_DEFAULT_COLOR}; static cli_profile_t profiles[128] = {{{0}}}; static cli_profile_t internal_profile = {{ 0 }}; static int pcount = 0; - static esl_handle_t *global_handle; static cli_profile_t *global_profile; - -static int process_command(esl_handle_t *handle, const char *cmd); - static int running = 1; static int thread_running = 0; static char *filter_uuid; +#ifndef WIN32 +static EditLine *el; +static History *myhistory; +static HistEvent ev; +#endif +static void _sleep_ns(int secs, long nsecs) { +#ifndef WIN32 + if (nsecs > 999999999) { + secs += nsecs/1000000000; + nsecs = nsecs % 1000000000; + } + { + struct timespec ts = { secs, nsecs }; + nanosleep(&ts, NULL); + } +#else + Sleep(secs*1000 + nsecs/1000000); +#endif +} -/* - * If a fnkey is configured then process the command - */ +static void sleep_ns(long nsecs) { _sleep_ns(0, nsecs); } +static void sleep_ms(int msecs) { sleep_ns(msecs*1000000); } +static void sleep_s(int secs) { _sleep_ns(secs, 0); } + +static int process_command(esl_handle_t *handle, const char *cmd); + +static void clear_cli(void) { + putchar('\r'); + printf("\033[%dC", bare_prompt_str_len); + printf("\033[K"); + fflush(stdout); +} + +/* If a fnkey is configured then process the command */ static unsigned char console_fnkey_pressed(int i) { const char *c; - assert((i > 0) && (i <= 12)); - - c = global_profile->console_fnkeys[i - 1]; - - /* This new line is necessary to avoid output to begin after the ">" of the CLI's prompt */ - printf("%s\n", c); - printf("\n"); - - if (c == NULL) { + if (!(c = global_profile->console_fnkeys[i - 1])) { + printf("\n"); esl_log(ESL_LOG_ERROR, "FUNCTION KEY F%d IS NOT BOUND, please edit your config.\n", i); return CC_REDISPLAY; } - + clear_cli(); + printf("%s\n", c); if (process_command(global_handle, c)) { running = thread_running = 0; } - return CC_REDISPLAY; } #ifdef HAVE_EDITLINE -static char *prompt(EditLine * e) -{ - return prompt_str; -} +static char *prompt(EditLine *e) { return prompt_str; } +static unsigned char console_f1key(EditLine *el, int ch) { return console_fnkey_pressed(1); } +static unsigned char console_f2key(EditLine *el, int ch) { return console_fnkey_pressed(2); } +static unsigned char console_f3key(EditLine *el, int ch) { return console_fnkey_pressed(3); } +static unsigned char console_f4key(EditLine *el, int ch) { return console_fnkey_pressed(4); } +static unsigned char console_f5key(EditLine *el, int ch) { return console_fnkey_pressed(5); } +static unsigned char console_f6key(EditLine *el, int ch) { return console_fnkey_pressed(6); } +static unsigned char console_f7key(EditLine *el, int ch) { return console_fnkey_pressed(7); } +static unsigned char console_f8key(EditLine *el, int ch) { return console_fnkey_pressed(8); } +static unsigned char console_f9key(EditLine *el, int ch) { return console_fnkey_pressed(9); } +static unsigned char console_f10key(EditLine *el, int ch) { return console_fnkey_pressed(10); } +static unsigned char console_f11key(EditLine *el, int ch) { return console_fnkey_pressed(11); } +static unsigned char console_f12key(EditLine *el, int ch) { return console_fnkey_pressed(12); } -static EditLine *el; -static History *myhistory; -static HistEvent ev; - -static unsigned char console_f1key(EditLine * el, int ch) -{ - return console_fnkey_pressed(1); -} -static unsigned char console_f2key(EditLine * el, int ch) -{ - return console_fnkey_pressed(2); -} -static unsigned char console_f3key(EditLine * el, int ch) -{ - return console_fnkey_pressed(3); -} -static unsigned char console_f4key(EditLine * el, int ch) -{ - return console_fnkey_pressed(4); -} -static unsigned char console_f5key(EditLine * el, int ch) -{ - return console_fnkey_pressed(5); -} -static unsigned char console_f6key(EditLine * el, int ch) -{ - return console_fnkey_pressed(6); -} -static unsigned char console_f7key(EditLine * el, int ch) -{ - return console_fnkey_pressed(7); -} -static unsigned char console_f8key(EditLine * el, int ch) -{ - return console_fnkey_pressed(8); -} -static unsigned char console_f9key(EditLine * el, int ch) -{ - return console_fnkey_pressed(9); -} -static unsigned char console_f10key(EditLine * el, int ch) -{ - return console_fnkey_pressed(10); -} -static unsigned char console_f11key(EditLine * el, int ch) -{ - return console_fnkey_pressed(11); -} -static unsigned char console_f12key(EditLine * el, int ch) -{ - return console_fnkey_pressed(12); -} - -static unsigned char console_eofkey(EditLine * el, int ch) +static unsigned char console_eofkey(EditLine *el, int ch) { LineInfo *line; /* only exit if empty line */ @@ -194,7 +173,6 @@ static int console_history (char *cmd, int direction) { int i; static int first; - if (direction == 0) { first = 1; if (iHistory < HISTLEN) { @@ -209,8 +187,7 @@ static int console_history (char *cmd, int direction) } else { iHistory = HISTLEN-1; - for (i = 0; i < HISTLEN-1; i++) - { + for (i = 0; i < HISTLEN-1; i++) { strcpy(history[i], history[i+1]); } iHistorySel = iHistory; @@ -233,17 +210,16 @@ static int console_history (char *cmd, int direction) return (0); } -static int console_bufferInput (char* addchars, int len, char *cmd, int key) +static int console_bufferInput (char *addchars, int len, char *cmd, int key) { - static int iCmdBuffer = 0; + static int iCmdBuffer = 0; static int iCmdCursor = 0; - static int ignoreNext = 0; + static int ignoreNext = 0; static int insertMode = 1; static COORD orgPosition; static char prompt [80]; - int iBuf; + int iBuf; int i; - HANDLE hOut; CONSOLE_SCREEN_BUFFER_INFO info; COORD position; @@ -253,16 +229,14 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) if (iCmdCursor == 0) { orgPosition = position; } - if (key == PROMPT_OP) { if (strlen(cmd) < sizeof(prompt)) { strcpy(prompt, cmd); } return 0; } - if (key == KEY_TAB) { - esl_console_complete(cmd, cmd+iCmdBuffer); + esl_console_complete(cmd, cmd+iCmdBuffer, &cmd[iCmdBuffer-1]); return 0; } if (key == KEY_UP || key == KEY_DOWN || key == CLEAR_OP) { @@ -287,7 +261,6 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) printf("%s", cmd); return 0; } - if (key == KEY_LEFT) { if (iCmdCursor) { if (position.X == 0) { @@ -297,7 +270,6 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) else { position.X -= 1; } - SetConsoleCursorPosition(hOut, position); iCmdCursor--; } @@ -311,7 +283,6 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) else { position.X += 1; } - SetConsoleCursorPosition(hOut, position); iCmdCursor++; } @@ -319,7 +290,7 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) if (key == KEY_INSERT) { insertMode = !insertMode; } - for (iBuf = 0; iBuf < len; iBuf++) { + for (iBuf = 0; iBuf < len; iBuf++) { switch (addchars[iBuf]) { case '\r': case '\n': @@ -355,20 +326,19 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) } printf(" "); if (iCmdCursor < iCmdBuffer) { - int pos; - iCmdCursor--; - for (pos = iCmdCursor; pos < iCmdBuffer; pos++) { - cmd[pos] = cmd[pos+1]; - } - cmd[pos] = 0; - iCmdBuffer--; - - SetConsoleCursorPosition(hOut, position); - for (pos = iCmdCursor; pos < iCmdBuffer; pos++) { - printf("%c", cmd[pos]); - } - printf(" "); - SetConsoleCursorPosition(hOut, position); + int pos; + iCmdCursor--; + for (pos = iCmdCursor; pos < iCmdBuffer; pos++) { + cmd[pos] = cmd[pos+1]; + } + cmd[pos] = 0; + iCmdBuffer--; + SetConsoleCursorPosition(hOut, position); + for (pos = iCmdCursor; pos < iCmdBuffer; pos++) { + printf("%c", cmd[pos]); + } + printf(" "); + SetConsoleCursorPosition(hOut, position); } else { SetConsoleCursorPosition(hOut, position); @@ -382,7 +352,6 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) if (!ignoreNext) { if (iCmdCursor < iCmdBuffer) { int pos; - if (position.X == info.dwSize.X-1) { position.Y += 1; position.X = 0; @@ -390,7 +359,6 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) else { position.X += 1; } - if (insertMode) { for (pos = iCmdBuffer-1; pos >= iCmdCursor; pos--) { cmd[pos+1] = cmd[pos]; @@ -424,33 +392,29 @@ static int console_bufferInput (char* addchars, int len, char *cmd, int key) iCmdBuffer = 0; ignoreNext = 1; } - } - return (0); + } + return (0); } - -static BOOL console_readConsole(HANDLE conIn, char* buf, int len, int* pRed, int *key) +static BOOL console_readConsole(HANDLE conIn, char *buf, int len, int *pRed, int *key) { - DWORD recordIndex, bufferIndex, toRead, red; - PINPUT_RECORD pInput; - - GetNumberOfConsoleInputEvents(conIn, &toRead); + DWORD recordIndex, bufferIndex, toRead, red; + PINPUT_RECORD pInput; + GetNumberOfConsoleInputEvents(conIn, &toRead); if (len < (int)toRead) { toRead = len; } if (toRead == 0) { return(FALSE); } - if ((pInput = (PINPUT_RECORD) malloc(toRead * sizeof(INPUT_RECORD))) == NULL) { return (FALSE); } *key = 0; - ReadConsoleInput(conIn, pInput, toRead, &red); - - for (recordIndex = bufferIndex = 0; recordIndex < red; recordIndex++) { - KEY_EVENT_RECORD keyEvent = pInput[recordIndex].Event.KeyEvent; - if (pInput[recordIndex].EventType == KEY_EVENT && keyEvent.bKeyDown) { + ReadConsoleInput(conIn, pInput, toRead, &red); + for (recordIndex = bufferIndex = 0; recordIndex < red; recordIndex++) { + KEY_EVENT_RECORD keyEvent = pInput[recordIndex].Event.KeyEvent; + if (pInput[recordIndex].EventType == KEY_EVENT && keyEvent.bKeyDown) { if (keyEvent.wVirtualKeyCode == 38 && keyEvent.wVirtualScanCode == 72) { buf[0] = 0; console_history(buf, -1); @@ -516,35 +480,30 @@ static BOOL console_readConsole(HANDLE conIn, char* buf, int len, int* pRed, int if (keyEvent.wVirtualKeyCode == 45 && keyEvent.wVirtualScanCode == 82) { *key = KEY_INSERT; } - while (keyEvent.wRepeatCount && keyEvent.uChar.AsciiChar) { - buf[bufferIndex] = keyEvent.uChar.AsciiChar; + while (keyEvent.wRepeatCount && keyEvent.uChar.AsciiChar) { + buf[bufferIndex] = keyEvent.uChar.AsciiChar; if (buf[bufferIndex] == '\r') { - buf[bufferIndex] = '\n'; + buf[bufferIndex] = '\n'; } - bufferIndex++; - keyEvent.wRepeatCount--; - } - } - } - - free(pInput); - *pRed = bufferIndex; - return (TRUE); + bufferIndex++; + keyEvent.wRepeatCount--; + } + } + } + free(pInput); + *pRed = bufferIndex; + return (TRUE); } #endif #endif static void handle_SIGINT(int sig) { - if (sig); - - if (!CONNECTED) { + if (!connected || allow_ctl_c) { fprintf(stdout, "Interrupted.\n"); exit(1); } - - WARN_STOP = 1; - + warn_stop = 1; signal(SIGINT, handle_SIGINT); #ifdef SIGTSTP signal(SIGTSTP, handle_SIGINT); @@ -562,154 +521,237 @@ static void handle_SIGQUIT(int sig) static HANDLE hStdout; static WORD wOldColorAttrs; static CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - -static WORD -#else -static const char* #endif -COLORS[] = { ESL_SEQ_DEFAULT_COLOR, ESL_SEQ_FRED, ESL_SEQ_FRED, - ESL_SEQ_FRED, ESL_SEQ_FMAGEN, ESL_SEQ_FCYAN, ESL_SEQ_FGREEN, ESL_SEQ_FYELLOW }; + +#ifdef WIN32 +static WORD colors[] +#else +static const char *colors[] +#endif += { + ESL_SEQ_DEFAULT_COLOR, ESL_SEQ_FRED, ESL_SEQ_FRED, ESL_SEQ_FRED, + ESL_SEQ_FMAGEN, ESL_SEQ_FCYAN, ESL_SEQ_FGREEN, ESL_SEQ_FYELLOW +}; + +static const char *usage_str = + "Usage: %s [-H ] [-P ] [-p ] [-d ] [-x command] [-t ] [profile]\n\n" + " -?,-h --help Usage Information\n" + " -H, --host=hostname Host to connect\n" + " -P, --port=port Port to connect (1 - 65535)\n" + " -u, --user=user@domain user@domain\n" + " -p, --password=password Password\n" + " -i, --interrupt Allow Control-c to interrupt\n" + " -x, --execute=command Execute Command and Exit\n" + " -l, --loglevel=command Log Level\n" + " -q, --quiet Disable logging\n" + " -r, --retry Retry connection on failure\n" + " -R, --reconnect Reconnect if disconnected\n" + " -d, --debug=level Debug Level (0 - 7)\n" + " -t, --timeout Timeout for API commands (in miliseconds)\n\n"; static int usage(char *name){ - printf("Usage: %s [-H ] [-P ] [-p ] [-d ] [-x command] [-t ] [profile]\n\n", name); - printf(" -?,-h --help Usage Information\n"); - printf(" -H, --host=hostname Host to connect\n"); - printf(" -P, --port=port Port to connect (1 - 65535)\n"); - printf(" -u, --user=user@domain user@domain\n"); - printf(" -p, --password=password Password\n"); - printf(" -i, --interrupt Allow Control-c to interrupt\n"); - printf(" -x, --execute=command Execute Command and Exit\n"); - printf(" -l, --loglevel=command Log Level\n"); - printf(" -q, --quiet Disable logging\n"); - printf(" -r, --retry Retry connection on failure\n"); - printf(" -R, --reconnect Reconnect if disconnected\n"); - printf(" -d, --debug=level Debug Level (0 - 7)\n"); - printf(" -t, --timeout Timeout for API commands (in miliseconds)\n\n"); + printf(usage_str, name); return 1; } +static int stdout_writable(void) +{ +#ifndef WIN32 + fd_set set; + int fd = fileno(stdout); + struct timeval to; + memset(&to, 0, sizeof(to)); + FD_ZERO(&set); + FD_SET(fd, &set); + to.tv_sec = 0; + to.tv_usec = 100000; + if (select(fd + 1, NULL, &set, NULL, &to) > 0) { + return FD_ISSET(fd, &set); + } else { + return 0; + } +#else + return 1; +#endif +} + +#ifndef WIN32 +static int write_str(const char *s) { + int n, left = strlen(s); + while (1) { + n = write(STDOUT_FILENO, s, left); + if (n == left) return 1; + if (n < 0) return 0; + s += n; left -= n; + } + return 1; +} + +static int write_char(int c) { + char s[2] = { c, 0 }; + return write_str(s); +} +#endif + +#ifdef WIN32 +static void clear_line(void) +{ + putchar('\r'); + printf("\033[K"); + fflush(stdout); + return; +} +#else +static void clear_line(void) +{ + if (!(write_char('\r'))) goto done; + if (!(write_str("\033[K"))) goto done; + done: + return; +} +#endif + +static void redisplay(void) +{ +#ifdef WIN32 +#else + const LineInfo *lf = el_line(el); + const char *c = lf->buffer; + if (!(write_str(prompt_str))) goto done; + while (c < lf->lastchar && *c) { + if (!(write_char(*c))) goto done; + c++; + } + { + int pos = (int)(lf->cursor - lf->buffer); + char s1[12], s2[12]; + if (!(write_char('\r'))) goto done; + snprintf(s1, sizeof(s1), "\033[%dC", bare_prompt_str_len); + snprintf(s2, sizeof(s2), "\033[%dC", pos); + if (!(write_str(s1))) goto done; + if (pos > 0) if (!(write_str(s2))) goto done; + } + done: + return; +#endif +} + +static int output_printf(const char *fmt, ...) +{ + va_list ap; + int r; + va_start(ap, fmt); +#ifndef WIN32 + printf("%s", output_text_color); +#endif + r = vprintf(fmt, ap); + va_end(ap); + return r; +} + static void *msg_thread_run(esl_thread_t *me, void *obj) { - esl_handle_t *handle = (esl_handle_t *) obj; - thread_running = 1; - while(thread_running && handle->connected) { + int aok = 1; esl_status_t status = esl_recv_event_timed(handle, 10, 1, NULL); - int aok = 1; - if (status == ESL_FAIL) { - if (aok) esl_log(ESL_LOG_WARNING, "Disconnected.\n"); + esl_log(ESL_LOG_WARNING, "Disconnected.\n"); running = -1; thread_running = 0; } else if (status == ESL_SUCCESS) { -#ifndef WIN32 - fd_set can_write; - int fd; - struct timeval to; - - fd = fileno(stdout); - memset(&to, 0, sizeof(to)); - FD_ZERO(&can_write); - FD_SET(fd, &can_write); - to.tv_sec = 0; - to.tv_usec = 100000; - if (select(fd + 1, NULL, &can_write, NULL, &to) > 0) { - aok = FD_ISSET(fd, &can_write); - } else { - aok = 0; - } -#endif - + aok = stdout_writable(); if (handle->last_event) { + int known = 1; const char *type = esl_event_get_header(handle->last_event, "content-type"); - int known = 0; - if (!esl_strlen_zero(type)) { - if (aok && !strcasecmp(type, "log/data")) { + if (!strcasecmp(type, "log/data")) { const char *userdata = esl_event_get_header(handle->last_event, "user-data"); - if (esl_strlen_zero(userdata) || esl_strlen_zero(filter_uuid) || !strcasecmp(filter_uuid, userdata)) { int level = 0; const char *lname = esl_event_get_header(handle->last_event, "log-level"); - #ifdef WIN32 +#ifdef WIN32 DWORD len = (DWORD) strlen(handle->last_event->body); DWORD outbytes = 0; - #endif +#endif if (lname) { level = atoi(lname); } - - - #ifdef WIN32 - SetConsoleTextAttribute(hStdout, COLORS[level]); - WriteFile(hStdout, handle->last_event->body, len, &outbytes, NULL); - SetConsoleTextAttribute(hStdout, wOldColorAttrs); - #else - printf("%s%s%s", COLORS[level], handle->last_event->body, ESL_SEQ_DEFAULT_COLOR); - #endif +#ifndef WIN32 + if (aok) { + clear_line(); + printf("%s%s", colors[level], handle->last_event->body); + redisplay(); + } +#else + if (aok) { + SetConsoleTextAttribute(hStdout, colors[level]); + WriteFile(hStdout, handle->last_event->body, len, &outbytes, NULL); + SetConsoleTextAttribute(hStdout, wOldColorAttrs); + } +#endif } - known++; } else if (!strcasecmp(type, "text/disconnect-notice")) { running = -1; thread_running = 0; - known++; - } else if (aok && !strcasecmp(type, "text/event-plain")) { - char *foo; - esl_event_serialize(handle->last_ievent, &foo, ESL_FALSE); - printf("RECV EVENT\n%s\n", foo); - free(foo); - - known++; + } else if (!strcasecmp(type, "text/event-plain")) { + char *s; + esl_event_serialize(handle->last_ievent, &s, ESL_FALSE); + if (aok) { + clear_line(); + output_printf("RECV EVENT\n%s\n", s); + redisplay(); + } + free(s); + } else { + known = 0; } } - if (aok && !known) { - char *foo; - printf("INCOMING DATA [%s]\n%s\n", type, handle->last_event->body ? handle->last_event->body : ""); - esl_event_serialize(handle->last_event, &foo, ESL_FALSE); - printf("RECV EVENT\n%s\n", foo); - free(foo); + char *s; + output_printf("INCOMING DATA [%s]\n%s\n", type, handle->last_event->body ? handle->last_event->body : ""); + esl_event_serialize(handle->last_event, &s, ESL_FALSE); + output_printf("RECV EVENT\n%s\n", s); + redisplay(); + free(s); } } } - - if (WARN_STOP) { - if (aok) printf("Type control-D or /exit or /quit or /bye to exit.\n\n"); - WARN_STOP = 0; + if (warn_stop) { + if (aok) { + clear_line(); + output_printf("Type control-D or /exit or /quit or /bye to exit.\n\n"); + redisplay(); + } + warn_stop = 0; } - - usleep(1000); + sleep_ms(1); } - thread_running = 0; esl_log(ESL_LOG_DEBUG, "Thread Done\n"); - return NULL; } -static int process_command(esl_handle_t *handle, const char *cmd) -{ - if ((*cmd == '/' && cmd++) || !strncasecmp(cmd, "...", 3)) { - - if (!strcasecmp(cmd, "help")) { - printf( - "Command \tDescription\n" - "-----------------------------------------------\n" - "/help \tHelp\n" - "/exit, /quit, /bye, ... \tExit the program.\n" - "/event, /noevents, /nixevent\tEvent commands.\n" - "/log, /nolog \tLog commands.\n" - "/uuid \tFilter logs for a single call uuid\n" - "/filter \tFilter commands.\n" - "/debug [0-7] \tSet debug level.\n" - "\n" - ); +static const char *cli_usage = + "Command \tDescription\n" + "-----------------------------------------------\n" + "/help \tHelp\n" + "/exit, /quit, /bye, ... \tExit the program.\n" + "/event, /noevents, /nixevent\tEvent commands.\n" + "/log, /nolog \tLog commands.\n" + "/uuid \tFilter logs for a single call uuid\n" + "/filter \tFilter commands.\n" + "/debug [0-7] \tSet debug level.\n" + "\n"; +static int process_command(esl_handle_t *handle, const char *cmd) +{ + while (*cmd == ' ') cmd++; + if ((*cmd == '/' && cmd++) || !strncasecmp(cmd, "...", 3)) { + if (!strcasecmp(cmd, "help")) { + output_printf("%s", cli_usage); goto end; } - - if ( - !strcasecmp(cmd, "exit") || + if (!strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit") || !strcasecmp(cmd, "...") || !strcasecmp(cmd, "bye") @@ -718,77 +760,64 @@ static int process_command(esl_handle_t *handle, const char *cmd) return -1; } else if (!strncasecmp(cmd, "uuid", 4)) { cmd += 4; - while (*cmd && *cmd == ' ') { cmd++; } - if (!esl_strlen_zero(cmd)) { filter_uuid = strdup(cmd); } else { esl_safe_free(filter_uuid); } - - printf("UUID filtering %s\n", filter_uuid ? "enabled" : "disabled"); - - } else if ( - !strncasecmp(cmd, "event", 5) || - !strncasecmp(cmd, "noevents", 8) || - !strncasecmp(cmd, "nixevent", 8) || - !strncasecmp(cmd, "log", 3) || - !strncasecmp(cmd, "nolog", 5) || - !strncasecmp(cmd, "filter", 6) - ) { - - esl_send_recv(handle, cmd); - + output_printf("UUID filtering %s\n", filter_uuid ? "enabled" : "disabled"); + } else if (!strncasecmp(cmd, "event", 5) || + !strncasecmp(cmd, "noevents", 8) || + !strncasecmp(cmd, "nixevent", 8) || + !strncasecmp(cmd, "log", 3) || + !strncasecmp(cmd, "nolog", 5) || + !strncasecmp(cmd, "filter", 6) + ) { + esl_send_recv(handle, cmd); printf("%s\n", handle->last_sr_reply); - } else if (!strncasecmp(cmd, "debug", 5)){ + } else if (!strncasecmp(cmd, "debug", 5)) { int tmp_debug = atoi(cmd+6); - if (tmp_debug > -1 && tmp_debug < 8){ + if (tmp_debug > -1 && tmp_debug < 8) { esl_global_set_default_logger(tmp_debug); - printf("fs_cli debug level set to %d\n", tmp_debug); + output_printf("fs_cli debug level set to %d\n", tmp_debug); } else { - printf("fs_cli debug level must be 0 - 7\n"); + output_printf("fs_cli debug level must be 0 - 7\n"); } } else { - printf("Unknown command [%s]\n", cmd); + output_printf("Unknown command [%s]\n", cmd); } } else { char cmd_str[1024] = ""; const char *err = NULL; - snprintf(cmd_str, sizeof(cmd_str), "api %s\nconsole_execute: true\n\n", cmd); if (esl_send_recv(handle, cmd_str)) { - printf("Socket interrupted, bye!\n"); + output_printf("Socket interrupted, bye!\n"); return -1; } if (handle->last_sr_event) { if (handle->last_sr_event->body) { - printf("%s\n", handle->last_sr_event->body); + output_printf("%s\n", handle->last_sr_event->body); } else if ((err = esl_event_get_header(handle->last_sr_event, "reply-text")) && !strncasecmp(err, "-err", 3)) { - printf("Error: %s!\n", err + 4); + output_printf("Error: %s!\n", err + 4); } } } - end: - return 0; - } static int get_profile(const char *name, cli_profile_t **profile) { int x; - for (x = 0; x < pcount; x++) { if (!strcmp(profiles[x].name, name)) { *profile = &profiles[x]; return 0; } } - return -1; } @@ -799,9 +828,7 @@ static const char *basic_gets(int *cnt) { #ifndef _MSC_VER int x = 0; - printf("%s", prompt_str); - memset(&command_buf, 0, sizeof(command_buf)); for (x = 0; x < (sizeof(command_buf) - 1); x++) { int c = getchar(); @@ -810,27 +837,21 @@ static const char *basic_gets(int *cnt) command_buf[y - 1] = '\0'; break; } - command_buf[x] = (char) c; - if (command_buf[x] == '\n') { command_buf[x] = '\0'; break; } } - *cnt = x; #else int read, key; char keys[CMD_BUFLEN]; HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); - console_bufferInput (0, 0, prompt_str, PROMPT_OP); printf("%s", prompt_str); - *cnt = 0; memset(&command_buf, 0, sizeof(command_buf)); - while (!*cnt) { if (console_readConsole(stdinHandle, keys, (int)sizeof(keys), &read, &key)) { *cnt = console_bufferInput(keys, read, command_buf, key); @@ -838,37 +859,31 @@ static const char *basic_gets(int *cnt) command_buf[0] = 0; } } - Sleep(20); + sleep_ms(20); } #endif - return command_buf; } #endif +static const char *banner = + " _____ ____ ____ _ ___ \n" + " | ___/ ___| / ___| | |_ _| \n" + " | |_ \\___ \\ | | | | | | \n" + " | _| ___) | | |___| |___ | | \n" + " |_| |____/ \\____|_____|___| \n" + "\n" + "*******************************************************\n" + "* Anthony Minessale II, Ken Rice, *\n" + "* Michael Jerris, Travis Cross *\n" + "* FreeSWITCH (http://www.freeswitch.org) *\n" + "* Paypal Donations Appreciated: paypal@freeswitch.org *\n" + "* Brought to you by ClueCon http://www.cluecon.com/ *\n" + "*******************************************************\n" + "\n" + "Type /help to see a list of commands\n\n\n"; -static void print_banner(FILE *stream) -{ - fprintf(stream, - - - " _____ ____ ____ _ ___ \n" - " | ___/ ___| / ___| | |_ _| \n" - " | |_ \\___ \\ | | | | | | \n" - " | _| ___) | | |___| |___ | | \n" - " |_| |____/ \\____|_____|___| \n" - "\n" - "*******************************************************\n" - "* Anthony Minessale II, Ken Rice, Michael Jerris *\n" - "* FreeSWITCH (http://www.freeswitch.org) *\n" - "* Paypal Donations Appreciated: paypal@freeswitch.org *\n" - "* Brought to you by ClueCon http://www.cluecon.com/ *\n" - "*******************************************************\n" - "\n" - "Type /help to see a list of commands\n\n\n" - ); -} - +static void print_banner(FILE *stream) { fprintf(stream, "%s%s", output_text_color, banner); } static void set_fn_keys(cli_profile_t *profile) { @@ -886,82 +901,73 @@ static void set_fn_keys(cli_profile_t *profile) profile->console_fnkeys[11] = "version"; } -#define end_of_p(_s) (*_s == '\0' ? _s : _s + strlen(_s) - 1) +static char* end_of_str(char *s) { return (*s == '\0' ? s : s + strlen(s) - 1); } -static unsigned char esl_console_complete(const char *buffer, const char *cursor) +static char* _strndup(const char *s, int n) +{ + char *r = (char*)malloc(n + 1), *d=r; + while (n > 0 && *s) { + *d = *s; + d++; s++; n--; + } + *d = 0; + return r; +} + +static unsigned char esl_console_complete(const char *buffer, const char *cursor, const char *lastchar) { char cmd_str[2048] = ""; unsigned char ret = CC_REDISPLAY; - char *dup = strdup(buffer); + char *dup = _strndup(buffer, (int)(lastchar - buffer)); char *buf = dup; - int pos = 0, sc = 0; + int sc = 0, offset = (int)(cursor - buffer), pos = (offset > 0) ? offset : 0; char *p; - - if (!esl_strlen_zero(cursor) && !esl_strlen_zero(buffer)) { - pos = (int)(cursor - buffer); - } if (pos > 0) { *(buf + pos) = '\0'; } - if ((p = strchr(buf, '\r')) || (p = strchr(buf, '\n'))) { *p = '\0'; } - while (*buf == ' ') { buf++; sc++; } - #ifdef HAVE_EDITLINE if (!*buf && sc) { el_deletestr(el, sc); + sc = 0; } #endif - - sc = 0; - - p = end_of_p(buf); + p = end_of_str(buf); while(p >= buf && *p == ' ') { sc++; p--; } - #ifdef HAVE_EDITLINE if (sc > 1) { el_deletestr(el, sc - 1); *(p + 2) = '\0'; } #endif - - if (*cursor) { snprintf(cmd_str, sizeof(cmd_str), "api console_complete c=%ld;%s\n\n", (long)pos, buf); } else { snprintf(cmd_str, sizeof(cmd_str), "api console_complete %s\n\n", buf); } - esl_send_recv(global_handle, cmd_str); - - if (global_handle->last_sr_event && global_handle->last_sr_event->body) { char *r = global_handle->last_sr_event->body; char *w, *p1; - if (r) { if ((w = strstr(r, "\n\nwrite="))) { int len = 0; *w = '\0'; w += 8; - len = atoi(w); - if ((p1= strchr(w, ':'))) { w = p1+ 1; } - printf("%s\n\n\n", r); - #ifdef HAVE_EDITLINE el_deletestr(el, len); el_insertstr(el, w); @@ -971,7 +977,6 @@ static unsigned char esl_console_complete(const char *buffer, const char *cursor console_bufferInput(w, (int)strlen(w), (char*)buffer, 0); #endif #endif - } else { printf("%s\n", r); #ifdef _MSC_VER @@ -979,24 +984,119 @@ static unsigned char esl_console_complete(const char *buffer, const char *cursor #endif } } - fflush(stdout); - } - + } esl_safe_free(dup); - return ret; } #ifdef HAVE_EDITLINE -static unsigned char complete(EditLine * el, int ch) +static unsigned char complete(EditLine *el, int ch) { const LineInfo *lf = el_line(el); - - return esl_console_complete(lf->buffer, lf->cursor); + return esl_console_complete(lf->buffer, lf->cursor, lf->lastchar); } #endif +struct color_map_el { + char name[32]; + char seq[12]; +}; + +struct color_map_el color_map[] = { + {"black", ESL_SEQ_FBLACK}, {"bold-black", ESL_SEQ_BBLACK}, + {"red", ESL_SEQ_FRED}, {"bold-red", ESL_SEQ_BRED}, + {"green", ESL_SEQ_FGREEN}, {"bold-green", ESL_SEQ_BGREEN}, + {"yellow", ESL_SEQ_FYELLOW}, {"bold-yellow", ESL_SEQ_BYELLOW}, + {"blue", ESL_SEQ_FBLUE}, {"bold-blue", ESL_SEQ_BBLUE}, + {"magenta", ESL_SEQ_FMAGEN}, {"bold-magenta", ESL_SEQ_BMAGEN}, + {"cyan", ESL_SEQ_FCYAN}, {"bold-cyan", ESL_SEQ_BCYAN}, + {"white", ESL_SEQ_FWHITE}, {"bold-white", ESL_SEQ_BWHITE}, + {{0}}}; + +static const char* match_color(const char *s) { + struct color_map_el *map = color_map; + while (map->name) { + if (!(strcasecmp(s, map->name))) { + return map->seq; + } + map++; + } +#ifdef WIN32 + return "white"; +#else + return ESL_SEQ_DEFAULT_COLOR; +#endif +} + +static void read_config(const char *dft_cfile, const char *cfile) { + esl_config_t cfg; + if (esl_config_open_file(&cfg, cfile) || + esl_config_open_file(&cfg, dft_cfile)) { + char *var, *val; + char cur_cat[128] = ""; + while (esl_config_next_pair(&cfg, &var, &val)) { + if (strcmp(cur_cat, cfg.category)) { + esl_set_string(cur_cat, cfg.category); + esl_set_string(profiles[pcount].name, cur_cat); + esl_set_string(profiles[pcount].host, "localhost"); + esl_set_string(profiles[pcount].pass, "ClueCon"); + profiles[pcount].port = 8021; + set_fn_keys(&profiles[pcount]); + esl_set_string(profiles[pcount].prompt_color, prompt_color); + esl_set_string(profiles[pcount].input_text_color, input_text_color); + esl_set_string(profiles[pcount].output_text_color, output_text_color); + esl_log(ESL_LOG_DEBUG, "Found Profile [%s]\n", profiles[pcount].name); + pcount++; + } + if (!strcasecmp(var, "host")) { + esl_set_string(profiles[pcount-1].host, val); + } else if (!strcasecmp(var, "user")) { + esl_set_string(profiles[pcount-1].user, val); + } else if (!strcasecmp(var, "password")) { + esl_set_string(profiles[pcount-1].pass, val); + } else if (!strcasecmp(var, "port")) { + int pt = atoi(val); + if (pt > 0) { + profiles[pcount-1].port = (esl_port_t)pt; + } + } else if (!strcasecmp(var, "debug")) { + int dt = atoi(val); + if (dt > -1 && dt < 8){ + profiles[pcount-1].debug = dt; + } + } else if(!strcasecmp(var, "loglevel")) { + esl_set_string(profiles[pcount-1].loglevel, val); + } else if(!strcasecmp(var, "quiet")) { + profiles[pcount-1].quiet = esl_true(val); + } else if(!strcasecmp(var, "prompt-color")) { + esl_set_string(profiles[pcount-1].prompt_color, match_color(val)); + } else if(!strcasecmp(var, "input-text-color")) { + esl_set_string(profiles[pcount-1].input_text_color, match_color(val)); + } else if(!strcasecmp(var, "output-text-color")) { + esl_set_string(profiles[pcount-1].output_text_color, match_color(val)); + } else if (!strncasecmp(var, "key_F", 5)) { + char *key = var + 5; + if (key) { + int i = atoi(key); + if (i > 0 && i < 13) { + profiles[pcount-1].console_fnkeys[i - 1] = strdup(val); + } + } + } + } + esl_config_close_file(&cfg); + } +} + +static void clear_el_buffer(void) { +#ifdef HAVE_EDITLINE + const LineInfo *lf = el_line(el); + int len = (int)(lf->lastchar - lf->buffer); + el_deletestr(el, len); + memset((char*)lf->buffer, 0, len); +#endif +} int main(int argc, char *argv[]) { @@ -1004,12 +1104,9 @@ int main(int argc, char *argv[]) int count = 0; const char *line = NULL; char cmd_str[1024] = ""; - esl_config_t cfg; cli_profile_t *profile = NULL; - int rv = 0; - #ifndef WIN32 - char hfile[512] = "/etc/fs_cli_history"; + char hfile[512] = "/tmp/fs_cli_history"; char cfile[512] = "/etc/fs_cli.conf"; char dft_cfile[512] = "/etc/fs_cli.conf"; #else @@ -1036,7 +1133,6 @@ int main(int argc, char *argv[]) {"timeout", 1, 0, 't'}, {0, 0, 0, 0} }; - char temp_host[128]; int argv_host = 0; char temp_user[256]; @@ -1048,7 +1144,6 @@ int main(int argc, char *argv[]) int temp_log = -1; int argv_error = 0; int argv_exec = 0; - int ctl_c = 0; char argv_command[1024] = ""; char argv_loglevel[128] = ""; int argv_quiet = 0; @@ -1059,13 +1154,13 @@ int main(int argc, char *argv[]) strncpy(internal_profile.name, "internal", sizeof(internal_profile.name)); internal_profile.port = 8021; set_fn_keys(&internal_profile); - - + esl_set_string(internal_profile.prompt_color, prompt_color); + esl_set_string(internal_profile.input_text_color, input_text_color); + esl_set_string(internal_profile.output_text_color, output_text_color); if (home) { snprintf(hfile, sizeof(hfile), "%s/.fs_cli_history", home); snprintf(cfile, sizeof(cfile), "%s/.fs_cli_conf", home); } - signal(SIGINT, handle_SIGINT); #ifdef SIGTSTP signal(SIGTSTP, handle_SIGINT); @@ -1074,20 +1169,18 @@ int main(int argc, char *argv[]) signal(SIGQUIT, handle_SIGQUIT); #endif esl_global_set_default_logger(6); /* default debug level to 6 (info) */ - for(;;) { int option_index = 0; opt = getopt_long(argc, argv, "H:U:P:S:u:p:d:x:l:t:qrRhi?", options, &option_index); if (opt == -1) break; - switch (opt) - { + switch (opt) { case 'H': esl_set_string(temp_host, optarg); argv_host = 1; break; case 'P': temp_port= atoi(optarg); - if (temp_port > 0 && temp_port < 65536){ + if (temp_port > 0 && temp_port < 65536) { argv_port = 1; } else { printf("ERROR: Port must be in range 1 - 65535\n"); @@ -1104,7 +1197,7 @@ int main(int argc, char *argv[]) break; case 'd': temp_log=atoi(optarg); - if (temp_log < 0 || temp_log > 7){ + if (temp_log < 0 || temp_log > 7) { printf("ERROR: Debug level should be 0 - 7.\n"); argv_error = 1; } else { @@ -1122,12 +1215,12 @@ int main(int argc, char *argv[]) argv_quiet = 1; break; case 'i': - ctl_c = 1; + allow_ctl_c = 1; break; - case 'r': + case 'r': loops += 120; break; - case 'R': + case 'R': reconnect = 1; break; case 't': @@ -1142,118 +1235,60 @@ int main(int argc, char *argv[]) opt = 0; } } - - if (argv_error){ + if (argv_error) { printf("\n"); return usage(argv[0]); } - - if (!(rv = esl_config_open_file(&cfg, cfile))) { - rv = esl_config_open_file(&cfg, dft_cfile); - } - - if (rv) { - char *var, *val; - char cur_cat[128] = ""; - - while (esl_config_next_pair(&cfg, &var, &val)) { - if (strcmp(cur_cat, cfg.category)) { - esl_set_string(cur_cat, cfg.category); - esl_set_string(profiles[pcount].name, cur_cat); - esl_set_string(profiles[pcount].host, "localhost"); - esl_set_string(profiles[pcount].pass, "ClueCon"); - profiles[pcount].port = 8021; - set_fn_keys(&profiles[pcount]); - esl_log(ESL_LOG_DEBUG, "Found Profile [%s]\n", profiles[pcount].name); - pcount++; - } - - if (!strcasecmp(var, "host")) { - esl_set_string(profiles[pcount-1].host, val); - } else if (!strcasecmp(var, "user")) { - esl_set_string(profiles[pcount-1].user, val); - } else if (!strcasecmp(var, "password")) { - esl_set_string(profiles[pcount-1].pass, val); - } else if (!strcasecmp(var, "port")) { - int pt = atoi(val); - if (pt > 0) { - profiles[pcount-1].port = (esl_port_t)pt; - } - } else if (!strcasecmp(var, "debug")) { - int dt = atoi(val); - if (dt > -1 && dt < 8){ - profiles[pcount-1].debug = dt; - } - } else if(!strcasecmp(var, "loglevel")) { - esl_set_string(profiles[pcount-1].loglevel, val); - } else if(!strcasecmp(var, "quiet")) { - profiles[pcount-1].quiet = esl_true(val); - } else if (!strncasecmp(var, "key_F", 5)) { - char *key = var + 5; - - if (key) { - int i = atoi(key); - - if (i > 0 && i < 13) { - profiles[pcount-1].console_fnkeys[i - 1] = strdup(val); - } - } - } - } - esl_config_close_file(&cfg); - } - + read_config(dft_cfile, cfile); if (optind < argc) { get_profile(argv[optind], &profile); } - if (!profile) { if (get_profile("default", &profile)) { esl_log(ESL_LOG_DEBUG, "profile default does not exist using builtin profile\n"); profile = &internal_profile; } } - if (temp_log < 0 ) { esl_global_set_default_logger(profile->debug); - } - + } if (argv_host) { esl_set_string(profile->host, temp_host); } if (argv_port) { profile->port = (esl_port_t)temp_port; } - if (argv_user) { esl_set_string(profile->user, temp_user); } - if (argv_pass) { esl_set_string(profile->pass, temp_pass); } - if (*argv_loglevel) { esl_set_string(profile->loglevel, argv_loglevel); profile->quiet = 0; } - esl_log(ESL_LOG_DEBUG, "Using profile %s [%s]\n", profile->name, profile->host); - + esl_set_string(prompt_color, profile->prompt_color); + esl_set_string(input_text_color, profile->input_text_color); + esl_set_string(output_text_color, profile->output_text_color); if (argv_host) { if (argv_port && profile->port != 8021) { - snprintf(prompt_str, sizeof(prompt_str), "freeswitch@%s:%u@%s> ", profile->host, profile->port, profile->name); + snprintf(bare_prompt_str, sizeof(bare_prompt_str), "freeswitch@%s:%u@%s> ", profile->host, profile->port, profile->name); } else { - snprintf(prompt_str, sizeof(prompt_str), "freeswitch@%s@%s> ", profile->host, profile->name); + snprintf(bare_prompt_str, sizeof(bare_prompt_str), "freeswitch@%s@%s> ", profile->host, profile->name); } } else { - snprintf(prompt_str, sizeof(prompt_str), "freeswitch@%s> ", profile->name); + snprintf(bare_prompt_str, sizeof(bare_prompt_str), "freeswitch@%s> ", profile->name); } - + bare_prompt_str_len = (int)strlen(bare_prompt_str); +#ifdef WIN32 + snprintf(prompt_str, sizeof(prompt_str), "%s", bare_prompt_str); /* Not supporting this for now */ +#else + snprintf(prompt_str, sizeof(prompt_str), "%s%s%s", prompt_color, bare_prompt_str, input_text_color); +#endif connect: - - CONNECTED = 0; - + connected = 0; while (--loops > 0) { memset(&handle, 0, sizeof(handle)); if (esl_connect(&handle, profile->host, profile->port, profile->user, profile->pass)) { @@ -1263,18 +1298,11 @@ int main(int argc, char *argv[]) if (!argv_exec) usage(argv[0]); return -1; } else { -#ifndef WIN32 - sleep(1); -#else - Sleep(1000); -#endif + sleep_s(1); esl_log(ESL_LOG_INFO, "Retrying\n"); } } else { - if (!ctl_c) { - CONNECTED = 1; - } - + connected = 1; if (temp_log < 0 ) { esl_global_set_default_logger(profile->debug); } else { @@ -1283,11 +1311,8 @@ int main(int argc, char *argv[]) break; } } - - - if (argv_exec){ + if (argv_exec) { const char *err = NULL; - snprintf(cmd_str, sizeof(cmd_str), "api %s\n\n", argv_command); if (timeout) { esl_status_t status = esl_send_recv_timed(&handle, cmd_str, timeout); @@ -1295,11 +1320,10 @@ int main(int argc, char *argv[]) printf("Request timed out.\n"); esl_disconnect(&handle); return -2; - } + } } else { esl_send_recv(&handle, cmd_str); } - if (handle.last_sr_event) { if (handle.last_sr_event->body) { printf("%s\n", handle.last_sr_event->body); @@ -1307,23 +1331,18 @@ int main(int argc, char *argv[]) printf("Error: %s!\n", err + 4); } } - esl_disconnect(&handle); return 0; - } - + } global_handle = &handle; global_profile = profile; - esl_thread_create_detached(msg_thread_run, &handle); #ifdef HAVE_EDITLINE - el = el_init(__FILE__, stdout, stdout, stdout); + el = el_init(__FILE__, stdin, stdout, stderr); el_set(el, EL_PROMPT, &prompt); el_set(el, EL_EDITOR, "emacs"); - myhistory = history_init(); - el_set(el, EL_ADDFN, "f1-key", "F1 KEY PRESS", console_f1key); el_set(el, EL_ADDFN, "f2-key", "F2 KEY PRESS", console_f2key); el_set(el, EL_ADDFN, "f3-key", "F3 KEY PRESS", console_f3key); @@ -1336,7 +1355,6 @@ int main(int argc, char *argv[]) el_set(el, EL_ADDFN, "f10-key", "F10 KEY PRESS", console_f10key); el_set(el, EL_ADDFN, "f11-key", "F11 KEY PRESS", console_f11key); el_set(el, EL_ADDFN, "f12-key", "F12 KEY PRESS", console_f12key); - el_set(el, EL_ADDFN, "EOF-key", "EOF (^D) KEY PRESS", console_eofkey); el_set(el, EL_BIND, "\033OP", "f1-key", NULL); @@ -1344,7 +1362,6 @@ int main(int argc, char *argv[]) el_set(el, EL_BIND, "\033OR", "f3-key", NULL); el_set(el, EL_BIND, "\033OS", "f4-key", NULL); - el_set(el, EL_BIND, "\033[11~", "f1-key", NULL); el_set(el, EL_BIND, "\033[12~", "f2-key", NULL); el_set(el, EL_BIND, "\033[13~", "f3-key", NULL); @@ -1363,17 +1380,14 @@ int main(int argc, char *argv[]) el_set(el, EL_ADDFN, "ed-complete", "Complete argument", complete); el_set(el, EL_BIND, "^I", "ed-complete", NULL); - if (myhistory == 0) { + if (!(myhistory = history_init())) { esl_log(ESL_LOG_ERROR, "history could not be initialized\n"); goto done; } - history(myhistory, &ev, H_SETSIZE, 800); el_set(el, EL_HIST, history, myhistory); history(myhistory, &ev, H_LOAD, hfile); - el_source(el, NULL); - #endif #ifdef WIN32 hStdout = GetStdHandle(STD_OUTPUT_HANDLE); @@ -1381,80 +1395,60 @@ int main(int argc, char *argv[]) wOldColorAttrs = csbiInfo.wAttributes; } #endif - if (!argv_quiet && !profile->quiet) { - snprintf(cmd_str, sizeof(cmd_str), "log %s\n\n", profile->loglevel); + snprintf(cmd_str, sizeof(cmd_str), "log %s\n\n", profile->loglevel); esl_send_recv(&handle, cmd_str); } - print_banner(stdout); - esl_log(ESL_LOG_INFO, "FS CLI Ready.\nenter /help for a list of commands.\n"); - printf("%s\n", handle.last_sr_reply); - + output_printf("%s\n", handle.last_sr_reply); while (running > 0) { int r; - #ifdef HAVE_EDITLINE line = el_gets(el, &count); #else line = basic_gets(&count); #endif - - if (count > 1) { - if (!esl_strlen_zero(line)) { - char *cmd = strdup(line); - char *p; - -#ifdef HAVE_EDITLINE - const LineInfo *lf = el_line(el); - char *foo = (char *) lf->buffer; -#endif - - if ((p = strrchr(cmd, '\r')) || (p = strrchr(cmd, '\n'))) { - *p = '\0'; - } - assert(cmd != NULL); - -#ifdef HAVE_EDITLINE - history(myhistory, &ev, H_ENTER, line); -#endif - - if ((r = process_command(&handle, cmd))) { - running = r; - } - -#ifdef HAVE_EDITLINE - el_deletestr(el, strlen(foo) + 1); - memset(foo, 0, strlen(foo)); -#endif - free(cmd); + if (count > 1 && !esl_strlen_zero(line)) { + char *p, *cmd = strdup(line); + assert(cmd); + if ((p = strrchr(cmd, '\r')) || (p = strrchr(cmd, '\n'))) { + *p = '\0'; } +#ifdef HAVE_EDITLINE + history(myhistory, &ev, H_ENTER, line); +#endif + if ((r = process_command(&handle, cmd))) { + running = r; + } + free(cmd); + clear_el_buffer(); } - - usleep(1000); - + sleep_ms(1); } - if (running < 0 && reconnect) { running = 1; loops = 120; goto connect; } - - #ifdef HAVE_EDITLINE done: history(myhistory, &ev, H_SAVE, hfile); - - /* Clean up our memory */ history_end(myhistory); el_end(el); #endif - esl_disconnect(&handle); - thread_running = 0; - return 0; } + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/libs/esl/java/esl_wrap.cpp b/libs/esl/java/esl_wrap.cpp index 52c5065d00..0550231dde 100644 --- a/libs/esl/java/esl_wrap.cpp +++ b/libs/esl/java/esl_wrap.cpp @@ -904,6 +904,31 @@ SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1sendEvent } +SWIGEXPORT jint JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1sendMSG(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_, jstring jarg3) { + jint jresult = 0 ; + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + + (void)jenv; + (void)jcls; + (void)jarg1_; + (void)jarg2_; + arg1 = *(ESLconnection **)&jarg1; + arg2 = *(ESLevent **)&jarg2; + arg3 = 0; + if (jarg3) { + arg3 = (char *)jenv->GetStringUTFChars(jarg3, 0); + if (!arg3) return 0; + } + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + jresult = (jint)result; + if (arg3) jenv->ReleaseStringUTFChars(jarg3, (const char *)arg3); + return jresult; +} + + SWIGEXPORT jlong JNICALL Java_org_freeswitch_esl_eslJNI_ESLconnection_1recvEvent(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) { jlong jresult = 0 ; ESLconnection *arg1 = (ESLconnection *) 0 ; diff --git a/libs/esl/java/org/freeswitch/esl/ESLconnection.java b/libs/esl/java/org/freeswitch/esl/ESLconnection.java index e20509bf10..588cefcc99 100644 --- a/libs/esl/java/org/freeswitch/esl/ESLconnection.java +++ b/libs/esl/java/org/freeswitch/esl/ESLconnection.java @@ -82,6 +82,10 @@ public class ESLconnection { return (cPtr == 0) ? null : new ESLevent(cPtr, true); } + public int sendMSG(ESLevent send_me, String uuid) { + return eslJNI.ESLconnection_sendMSG(swigCPtr, this, ESLevent.getCPtr(send_me), send_me, uuid); + } + public ESLevent recvEvent() { long cPtr = eslJNI.ESLconnection_recvEvent(swigCPtr, this); return (cPtr == 0) ? null : new ESLevent(cPtr, true); diff --git a/libs/esl/java/org/freeswitch/esl/eslJNI.java b/libs/esl/java/org/freeswitch/esl/eslJNI.java index dfdedff60f..ff3744e9df 100644 --- a/libs/esl/java/org/freeswitch/esl/eslJNI.java +++ b/libs/esl/java/org/freeswitch/esl/eslJNI.java @@ -43,6 +43,7 @@ class eslJNI { public final static native long ESLconnection_api(long jarg1, ESLconnection jarg1_, String jarg2, String jarg3); public final static native long ESLconnection_bgapi(long jarg1, ESLconnection jarg1_, String jarg2, String jarg3, String jarg4); public final static native long ESLconnection_sendEvent(long jarg1, ESLconnection jarg1_, long jarg2, ESLevent jarg2_); + public final static native int ESLconnection_sendMSG(long jarg1, ESLconnection jarg1_, long jarg2, ESLevent jarg2_, String jarg3); public final static native long ESLconnection_recvEvent(long jarg1, ESLconnection jarg1_); public final static native long ESLconnection_recvEventTimed(long jarg1, ESLconnection jarg1_, int jarg2); public final static native long ESLconnection_filter(long jarg1, ESLconnection jarg1_, String jarg2, String jarg3); diff --git a/libs/esl/lua/esl_wrap.cpp b/libs/esl/lua/esl_wrap.cpp index c3ee30811e..9420f853bb 100644 --- a/libs/esl/lua/esl_wrap.cpp +++ b/libs/esl/lua/esl_wrap.cpp @@ -2632,6 +2632,43 @@ fail: } +static int _wrap_ESLconnection_sendMSG(lua_State* L) { + int SWIG_arg = -1; + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + + SWIG_check_num_args("sendMSG",2,3) + if(!SWIG_isptrtype(L,1)) SWIG_fail_arg("sendMSG",1,"ESLconnection *"); + if(!SWIG_isptrtype(L,2)) SWIG_fail_arg("sendMSG",2,"ESLevent *"); + if(lua_gettop(L)>=3 && !lua_isstring(L,3)) SWIG_fail_arg("sendMSG",3,"char const *"); + + if (!SWIG_IsOK(SWIG_ConvertPtr(L,1,(void**)&arg1,SWIGTYPE_p_ESLconnection,0))){ + SWIG_fail_ptr("ESLconnection_sendMSG",1,SWIGTYPE_p_ESLconnection); + } + + + if (!SWIG_IsOK(SWIG_ConvertPtr(L,2,(void**)&arg2,SWIGTYPE_p_ESLevent,0))){ + SWIG_fail_ptr("ESLconnection_sendMSG",2,SWIGTYPE_p_ESLevent); + } + + if(lua_gettop(L)>=3){ + arg3 = (char *)lua_tostring(L, 3); + } + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + SWIG_arg=0; + lua_pushnumber(L, (lua_Number) result); SWIG_arg++; + return SWIG_arg; + + if(0) SWIG_fail; + +fail: + lua_error(L); + return SWIG_arg; +} + + static int _wrap_ESLconnection_recvEvent(lua_State* L) { int SWIG_arg = -1; ESLconnection *arg1 = (ESLconnection *) 0 ; @@ -2917,6 +2954,7 @@ static swig_lua_method swig_ESLconnection_methods[] = { {"api", _wrap_ESLconnection_api}, {"bgapi", _wrap_ESLconnection_bgapi}, {"sendEvent", _wrap_ESLconnection_sendEvent}, + {"sendMSG", _wrap_ESLconnection_sendMSG}, {"recvEvent", _wrap_ESLconnection_recvEvent}, {"recvEventTimed", _wrap_ESLconnection_recvEventTimed}, {"filter", _wrap_ESLconnection_filter}, diff --git a/libs/esl/managed/ESLPINVOKE.cs b/libs/esl/managed/ESLPINVOKE.cs index ee15a2b702..5ada2c148d 100644 --- a/libs/esl/managed/ESLPINVOKE.cs +++ b/libs/esl/managed/ESLPINVOKE.cs @@ -286,6 +286,9 @@ class ESLPINVOKE { [DllImport("ESL", EntryPoint="CSharp_ESLconnection_SendEvent")] public static extern IntPtr ESLconnection_SendEvent(HandleRef jarg1, HandleRef jarg2); + [DllImport("ESL", EntryPoint="CSharp_ESLconnection_sendMSG")] + public static extern int ESLconnection_sendMSG(HandleRef jarg1, HandleRef jarg2, string jarg3); + [DllImport("ESL", EntryPoint="CSharp_ESLconnection_RecvEvent")] public static extern IntPtr ESLconnection_RecvEvent(HandleRef jarg1); diff --git a/libs/esl/managed/ESLconnection.cs b/libs/esl/managed/ESLconnection.cs index 88435830ad..2d1e6d4dc3 100644 --- a/libs/esl/managed/ESLconnection.cs +++ b/libs/esl/managed/ESLconnection.cs @@ -92,6 +92,11 @@ public class ESLconnection : IDisposable { return ret; } + public int sendMSG(ESLevent send_me, string uuid) { + int ret = ESLPINVOKE.ESLconnection_sendMSG(swigCPtr, ESLevent.getCPtr(send_me), uuid); + return ret; + } + public ESLevent RecvEvent() { IntPtr cPtr = ESLPINVOKE.ESLconnection_RecvEvent(swigCPtr); ESLevent ret = (cPtr == IntPtr.Zero) ? null : new ESLevent(cPtr, true); diff --git a/libs/esl/managed/esl_wrap.cpp b/libs/esl/managed/esl_wrap.cpp index f07b4b3d3f..65da98a15a 100644 --- a/libs/esl/managed/esl_wrap.cpp +++ b/libs/esl/managed/esl_wrap.cpp @@ -743,6 +743,22 @@ SWIGEXPORT void * SWIGSTDCALL CSharp_ESLconnection_SendEvent(void * jarg1, void } +SWIGEXPORT int SWIGSTDCALL CSharp_ESLconnection_sendMSG(void * jarg1, void * jarg2, char * jarg3) { + int jresult ; + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + + arg1 = (ESLconnection *)jarg1; + arg2 = (ESLevent *)jarg2; + arg3 = (char *)jarg3; + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + jresult = result; + return jresult; +} + + SWIGEXPORT void * SWIGSTDCALL CSharp_ESLconnection_RecvEvent(void * jarg1) { void * jresult ; ESLconnection *arg1 = (ESLconnection *) 0 ; diff --git a/libs/esl/perl/ESL.pm b/libs/esl/perl/ESL.pm index b291d002d7..69c012a577 100644 --- a/libs/esl/perl/ESL.pm +++ b/libs/esl/perl/ESL.pm @@ -138,6 +138,7 @@ sub DESTROY { *api = *ESLc::ESLconnection_api; *bgapi = *ESLc::ESLconnection_bgapi; *sendEvent = *ESLc::ESLconnection_sendEvent; +*sendMSG = *ESLc::ESLconnection_sendMSG; *recvEvent = *ESLc::ESLconnection_recvEvent; *recvEventTimed = *ESLc::ESLconnection_recvEventTimed; *filter = *ESLc::ESLconnection_filter; diff --git a/libs/esl/perl/esl_wrap.cpp b/libs/esl/perl/esl_wrap.cpp index 1bc45fa2e1..832289f156 100644 --- a/libs/esl/perl/esl_wrap.cpp +++ b/libs/esl/perl/esl_wrap.cpp @@ -3282,6 +3282,57 @@ XS(_wrap_ESLconnection_sendEvent) { } +XS(_wrap_ESLconnection_sendMSG) { + { + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + int argvi = 0; + dXSARGS; + + if ((items < 2) || (items > 3)) { + SWIG_croak("Usage: ESLconnection_sendMSG(self,send_me,uuid);"); + } + res1 = SWIG_ConvertPtr(ST(0), &argp1,SWIGTYPE_p_ESLconnection, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ESLconnection_sendMSG" "', argument " "1"" of type '" "ESLconnection *""'"); + } + arg1 = reinterpret_cast< ESLconnection * >(argp1); + res2 = SWIG_ConvertPtr(ST(1), &argp2,SWIGTYPE_p_ESLevent, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ESLconnection_sendMSG" "', argument " "2"" of type '" "ESLevent *""'"); + } + arg2 = reinterpret_cast< ESLevent * >(argp2); + if (items > 2) { + res3 = SWIG_AsCharPtrAndSize(ST(2), &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "ESLconnection_sendMSG" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + } + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + ST(argvi) = SWIG_From_int SWIG_PERL_CALL_ARGS_1(static_cast< int >(result)); argvi++ ; + + + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + XSRETURN(argvi); + fail: + + + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + SWIG_croak_null(); + } +} + + XS(_wrap_ESLconnection_recvEvent) { { ESLconnection *arg1 = (ESLconnection *) 0 ; @@ -3785,6 +3836,7 @@ static swig_command_info swig_commands[] = { {"ESLc::ESLconnection_api", _wrap_ESLconnection_api}, {"ESLc::ESLconnection_bgapi", _wrap_ESLconnection_bgapi}, {"ESLc::ESLconnection_sendEvent", _wrap_ESLconnection_sendEvent}, +{"ESLc::ESLconnection_sendMSG", _wrap_ESLconnection_sendMSG}, {"ESLc::ESLconnection_recvEvent", _wrap_ESLconnection_recvEvent}, {"ESLc::ESLconnection_recvEventTimed", _wrap_ESLconnection_recvEventTimed}, {"ESLc::ESLconnection_filter", _wrap_ESLconnection_filter}, diff --git a/libs/esl/php/ESL.php b/libs/esl/php/ESL.php index 0bc8c4e724..a35e8feb32 100644 --- a/libs/esl/php/ESL.php +++ b/libs/esl/php/ESL.php @@ -174,6 +174,14 @@ class ESLconnection { return is_resource($r) ? new ESLevent($r) : $r; } + function sendMSG($send_me,$uuid=null) { + switch (func_num_args()) { + case 1: $r=ESLconnection_sendMSG($this->_cPtr,$send_me); break; + default: $r=ESLconnection_sendMSG($this->_cPtr,$send_me,$uuid); + } + return $r; + } + function recvEvent() { $r=ESLconnection_recvEvent($this->_cPtr); return is_resource($r) ? new ESLevent($r) : $r; diff --git a/libs/esl/php/esl_wrap.cpp b/libs/esl/php/esl_wrap.cpp index 9e49c7c080..b5fd7a7d52 100644 --- a/libs/esl/php/esl_wrap.cpp +++ b/libs/esl/php/esl_wrap.cpp @@ -2244,6 +2244,48 @@ fail: } +ZEND_NAMED_FUNCTION(_wrap_ESLconnection_sendMSG) { + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + zval **args[3]; + int arg_count; + + SWIG_ResetError(); + arg_count = ZEND_NUM_ARGS(); + if(arg_count<2 || arg_count>3 || + zend_get_parameters_array_ex(arg_count,args)!=SUCCESS) + WRONG_PARAM_COUNT; + + { + if(SWIG_ConvertPtr(*args[0], (void **) &arg1, SWIGTYPE_p_ESLconnection, 0) < 0) { + SWIG_PHP_Error(E_ERROR, "Type error in argument 1 of ESLconnection_sendMSG. Expected SWIGTYPE_p_ESLconnection"); + } + } + if(!arg1) SWIG_PHP_Error(E_ERROR, "this pointer is NULL"); + { + if(SWIG_ConvertPtr(*args[1], (void **) &arg2, SWIGTYPE_p_ESLevent, 0) < 0) { + SWIG_PHP_Error(E_ERROR, "Type error in argument 2 of ESLconnection_sendMSG. Expected SWIGTYPE_p_ESLevent"); + } + } + if(arg_count > 2) { + /*@SWIG:/usr/local/share/swig/1.3.35/php4/utils.i,26,CONVERT_STRING_IN@*/ + convert_to_string_ex(args[2]); + arg3 = (char *) Z_STRVAL_PP(args[2]); + /*@SWIG@*/; + + } + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + { + ZVAL_LONG(return_value,result); + } + return; +fail: + zend_error(SWIG_ErrorCode(),SWIG_ErrorMsg()); +} + + ZEND_NAMED_FUNCTION(_wrap_ESLconnection_recvEvent) { ESLconnection *arg1 = (ESLconnection *) 0 ; ESLevent *result = 0 ; @@ -2651,6 +2693,7 @@ static zend_function_entry ESL_functions[] = { SWIG_ZEND_NAMED_FE(eslconnection_api,_wrap_ESLconnection_api,NULL) SWIG_ZEND_NAMED_FE(eslconnection_bgapi,_wrap_ESLconnection_bgapi,NULL) SWIG_ZEND_NAMED_FE(eslconnection_sendevent,_wrap_ESLconnection_sendEvent,NULL) + SWIG_ZEND_NAMED_FE(eslconnection_sendmsg,_wrap_ESLconnection_sendMSG,NULL) SWIG_ZEND_NAMED_FE(eslconnection_recvevent,_wrap_ESLconnection_recvEvent,NULL) SWIG_ZEND_NAMED_FE(eslconnection_recveventtimed,_wrap_ESLconnection_recvEventTimed,NULL) SWIG_ZEND_NAMED_FE(eslconnection_filter,_wrap_ESLconnection_filter,NULL) diff --git a/libs/esl/php/php_ESL.h b/libs/esl/php/php_ESL.h index eceac5dd70..41056b3dda 100644 --- a/libs/esl/php/php_ESL.h +++ b/libs/esl/php/php_ESL.h @@ -60,6 +60,7 @@ ZEND_NAMED_FUNCTION(_wrap_ESLconnection_sendRecv); ZEND_NAMED_FUNCTION(_wrap_ESLconnection_api); ZEND_NAMED_FUNCTION(_wrap_ESLconnection_bgapi); ZEND_NAMED_FUNCTION(_wrap_ESLconnection_sendEvent); +ZEND_NAMED_FUNCTION(_wrap_ESLconnection_sendMSG); ZEND_NAMED_FUNCTION(_wrap_ESLconnection_recvEvent); ZEND_NAMED_FUNCTION(_wrap_ESLconnection_recvEventTimed); ZEND_NAMED_FUNCTION(_wrap_ESLconnection_filter); diff --git a/libs/esl/python/ESL.py b/libs/esl/python/ESL.py index 6bb4216692..bb2de43a87 100644 --- a/libs/esl/python/ESL.py +++ b/libs/esl/python/ESL.py @@ -87,6 +87,7 @@ class ESLconnection: def api(*args): return apply(_ESL.ESLconnection_api, args) def bgapi(*args): return apply(_ESL.ESLconnection_bgapi, args) def sendEvent(*args): return apply(_ESL.ESLconnection_sendEvent, args) + def sendMSG(*args): return apply(_ESL.ESLconnection_sendMSG, args) def recvEvent(*args): return apply(_ESL.ESLconnection_recvEvent, args) def recvEventTimed(*args): return apply(_ESL.ESLconnection_recvEventTimed, args) def filter(*args): return apply(_ESL.ESLconnection_filter, args) diff --git a/libs/esl/python/esl_wrap.cpp b/libs/esl/python/esl_wrap.cpp index 90f6ed2c79..35d600e0b0 100644 --- a/libs/esl/python/esl_wrap.cpp +++ b/libs/esl/python/esl_wrap.cpp @@ -4370,6 +4370,58 @@ fail: } +SWIGINTERN PyObject *_wrap_ESLconnection_sendMSG(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { + PyObject *resultobj = 0; + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + PyObject * obj0 = 0 ; + PyObject * obj1 = 0 ; + PyObject * obj2 = 0 ; + + SWIG_PYTHON_THREAD_BEGIN_BLOCK; + if (!PyArg_ParseTuple(args,(char *)"OO|O:ESLconnection_sendMSG",&obj0,&obj1,&obj2)) SWIG_fail; + res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_ESLconnection, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ESLconnection_sendMSG" "', argument " "1"" of type '" "ESLconnection *""'"); + } + arg1 = reinterpret_cast< ESLconnection * >(argp1); + res2 = SWIG_ConvertPtr(obj1, &argp2,SWIGTYPE_p_ESLevent, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "ESLconnection_sendMSG" "', argument " "2"" of type '" "ESLevent *""'"); + } + arg2 = reinterpret_cast< ESLevent * >(argp2); + if (obj2) { + res3 = SWIG_AsCharPtrAndSize(obj2, &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "ESLconnection_sendMSG" "', argument " "3"" of type '" "char const *""'"); + } + arg3 = reinterpret_cast< char * >(buf3); + } + { + SWIG_PYTHON_THREAD_BEGIN_ALLOW; + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + SWIG_PYTHON_THREAD_END_ALLOW; + } + resultobj = SWIG_From_int(static_cast< int >(result)); + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + SWIG_PYTHON_THREAD_END_BLOCK; + return resultobj; +fail: + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + SWIG_PYTHON_THREAD_END_BLOCK; + return NULL; +} + + SWIGINTERN PyObject *_wrap_ESLconnection_recvEvent(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { PyObject *resultobj = 0; ESLconnection *arg1 = (ESLconnection *) 0 ; @@ -4859,6 +4911,7 @@ static PyMethodDef SwigMethods[] = { { (char *)"ESLconnection_api", _wrap_ESLconnection_api, METH_VARARGS, NULL}, { (char *)"ESLconnection_bgapi", _wrap_ESLconnection_bgapi, METH_VARARGS, NULL}, { (char *)"ESLconnection_sendEvent", _wrap_ESLconnection_sendEvent, METH_VARARGS, NULL}, + { (char *)"ESLconnection_sendMSG", _wrap_ESLconnection_sendMSG, METH_VARARGS, NULL}, { (char *)"ESLconnection_recvEvent", _wrap_ESLconnection_recvEvent, METH_VARARGS, NULL}, { (char *)"ESLconnection_recvEventTimed", _wrap_ESLconnection_recvEventTimed, METH_VARARGS, NULL}, { (char *)"ESLconnection_filter", _wrap_ESLconnection_filter, METH_VARARGS, NULL}, diff --git a/libs/esl/ruby/esl_wrap.cpp b/libs/esl/ruby/esl_wrap.cpp index 5afce64cf9..223ba3ea34 100644 --- a/libs/esl/ruby/esl_wrap.cpp +++ b/libs/esl/ruby/esl_wrap.cpp @@ -3245,6 +3245,51 @@ fail: } +SWIGINTERN VALUE +_wrap_ESLconnection_sendMSG(int argc, VALUE *argv, VALUE self) { + ESLconnection *arg1 = (ESLconnection *) 0 ; + ESLevent *arg2 = (ESLevent *) 0 ; + char *arg3 = (char *) NULL ; + int result; + void *argp1 = 0 ; + int res1 = 0 ; + void *argp2 = 0 ; + int res2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + VALUE vresult = Qnil; + + if ((argc < 1) || (argc > 2)) { + rb_raise(rb_eArgError, "wrong # of arguments(%d for 1)",argc); SWIG_fail; + } + res1 = SWIG_ConvertPtr(self, &argp1,SWIGTYPE_p_ESLconnection, 0 | 0 ); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), Ruby_Format_TypeError( "", "ESLconnection *","sendMSG", 1, self )); + } + arg1 = reinterpret_cast< ESLconnection * >(argp1); + res2 = SWIG_ConvertPtr(argv[0], &argp2,SWIGTYPE_p_ESLevent, 0 | 0 ); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), Ruby_Format_TypeError( "", "ESLevent *","sendMSG", 2, argv[0] )); + } + arg2 = reinterpret_cast< ESLevent * >(argp2); + if (argc > 1) { + res3 = SWIG_AsCharPtrAndSize(argv[1], &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), Ruby_Format_TypeError( "", "char const *","sendMSG", 3, argv[1] )); + } + arg3 = reinterpret_cast< char * >(buf3); + } + result = (int)(arg1)->sendMSG(arg2,(char const *)arg3); + vresult = SWIG_From_int(static_cast< int >(result)); + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + return vresult; +fail: + if (alloc3 == SWIG_NEWOBJ) delete[] buf3; + return Qnil; +} + + SWIGINTERN VALUE _wrap_ESLconnection_recvEvent(int argc, VALUE *argv, VALUE self) { ESLconnection *arg1 = (ESLconnection *) 0 ; @@ -3958,6 +4003,7 @@ SWIGEXPORT void Init_ESL(void) { rb_define_method(cESLconnection.klass, "api", VALUEFUNC(_wrap_ESLconnection_api), -1); rb_define_method(cESLconnection.klass, "bgapi", VALUEFUNC(_wrap_ESLconnection_bgapi), -1); rb_define_method(cESLconnection.klass, "sendEvent", VALUEFUNC(_wrap_ESLconnection_sendEvent), -1); + rb_define_method(cESLconnection.klass, "sendMSG", VALUEFUNC(_wrap_ESLconnection_sendMSG), -1); rb_define_method(cESLconnection.klass, "recvEvent", VALUEFUNC(_wrap_ESLconnection_recvEvent), -1); rb_define_method(cESLconnection.klass, "recvEventTimed", VALUEFUNC(_wrap_ESLconnection_recvEventTimed), -1); rb_define_method(cESLconnection.klass, "filter", VALUEFUNC(_wrap_ESLconnection_filter), -1); diff --git a/libs/esl/src/esl.c b/libs/esl/src/esl.c index ef8d7c1cb1..0c387e79ef 100644 --- a/libs/esl/src/esl.c +++ b/libs/esl/src/esl.c @@ -534,6 +534,39 @@ ESL_DECLARE(esl_status_t) esl_execute(esl_handle_t *handle, const char *app, con } +ESL_DECLARE(esl_status_t) esl_sendmsg(esl_handle_t *handle, esl_event_t *event, const char *uuid) +{ + char cmd_buf[128] = "sendmsg\n"; + char *txt; + + if (!handle || !handle->connected || handle->sock == ESL_SOCK_INVALID) { + return ESL_FAIL; + } + + if (uuid) { + snprintf(cmd_buf, sizeof(cmd_buf), "sendmsg %s\n", uuid); + } + + esl_event_serialize(event, &txt, ESL_FALSE); + esl_log(ESL_LOG_DEBUG, "%s%s\n", cmd_buf, txt); + + if (send(handle->sock, cmd_buf, strlen(cmd_buf), 0) <= 0) goto fail; + if (send(handle->sock, txt, strlen(txt), 0) <= 0) goto fail; + + free(txt); + + return esl_recv(handle); + + fail: + + handle->connected = 0; + + free(txt); + + return ESL_FAIL; +} + + ESL_DECLARE(esl_status_t) esl_filter(esl_handle_t *handle, const char *header, const char *value) { char send_buf[1024] = ""; diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index abd3c2be90..ead853ea46 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -270,8 +270,9 @@ ESL_DECLARE(char *) esl_event_get_header_idx(esl_event_t *event, const char *hea } return hp->value; - - } + } else if (!strcmp(header_name, "_body")) { + return event->body; + } return NULL; } @@ -414,6 +415,10 @@ static esl_status_t esl_event_base_add_header(esl_event_t *event, esl_stack_t st int index = 0; char *real_header_name = NULL; + if (!strcmp(header_name, "_body")) { + esl_event_set_body(event, data); + } + if ((index_ptr = strchr(header_name, '['))) { index_ptr++; index = atoi(index_ptr); @@ -604,6 +609,17 @@ ESL_DECLARE(esl_status_t) esl_event_add_header_string(esl_event_t *event, esl_st return ESL_FAIL; } +ESL_DECLARE(esl_status_t) esl_event_set_body(esl_event_t *event, const char *body) +{ + esl_safe_free(event->body); + + if (body) { + event->body = DUP(body); + } + + return ESL_SUCCESS; +} + ESL_DECLARE(esl_status_t) esl_event_add_body(esl_event_t *event, const char *fmt, ...) { int ret = 0; @@ -791,7 +807,7 @@ ESL_DECLARE(esl_status_t) esl_event_serialize(esl_event_t *event, char **str, es if (encode) { esl_url_encode(hp->value, encode_buf, encode_len); } else { - esl_snprintf(encode_buf, encode_len, "[%s]", hp->value); + esl_snprintf(encode_buf, encode_len, "%s", hp->value); } diff --git a/libs/esl/src/esl_oop.cpp b/libs/esl/src/esl_oop.cpp index 0c4bbd6f90..4f60a928ee 100644 --- a/libs/esl/src/esl_oop.cpp +++ b/libs/esl/src/esl_oop.cpp @@ -205,6 +205,15 @@ ESLevent *ESLconnection::sendEvent(ESLevent *send_me) return new ESLevent("server_disconnected"); } +int ESLconnection::sendMSG(ESLevent *send_me, const char *uuid) +{ + if (esl_sendmsg(&handle, send_me->event, uuid) == ESL_SUCCESS) { + return 0; + } + + return 1; +} + ESLevent *ESLconnection::recvEvent() { if (esl_recv_event(&handle, 1, NULL) == ESL_SUCCESS) { diff --git a/libs/esl/src/include/esl.h b/libs/esl/src/include/esl.h index 095e814a0e..74fd2342a5 100644 --- a/libs/esl/src/include/esl.h +++ b/libs/esl/src/include/esl.h @@ -61,12 +61,22 @@ typedef enum { #ifdef WIN32 #define ESL_SEQ_FWHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define ESL_SEQ_BWHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE #define ESL_SEQ_FRED FOREGROUND_RED | FOREGROUND_INTENSITY -#define ESL_SEQ_FMAGEN FOREGROUND_BLUE | FOREGROUND_RED -#define ESL_SEQ_FCYAN FOREGROUND_GREEN | FOREGROUND_BLUE -#define ESL_SEQ_FGREEN FOREGROUND_GREEN +#define ESL_SEQ_BRED FOREGROUND_RED +#define ESL_SEQ_FMAGEN FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY +#define ESL_SEQ_BMAGEN FOREGROUND_BLUE | FOREGROUND_RED +#define ESL_SEQ_FCYAN FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define ESL_SEQ_BCYAN FOREGROUND_GREEN | FOREGROUND_BLUE +#define ESL_SEQ_FGREEN FOREGROUND_GREEN | FOREGROUND_INTENSITY +#define ESL_SEQ_BGREEN FOREGROUND_GREEN #define ESL_SEQ_FYELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY +#define ESL_SEQ_BYELLOW FOREGROUND_RED | FOREGROUND_GREEN #define ESL_SEQ_DEFAULT_COLOR ESL_SEQ_FWHITE +#define ESL_SEQ_FBLUE FOREGROUND_BLUE | FOREGROUND_INTENSITY +#define ESL_SEQ_BBLUE FOREGROUND_BLUE +#define ESL_SEQ_FBLACK 0 | FOREGROUND_INTENSITY +#define ESL_SEQ_BBLACK 0 #else #define ESL_SEQ_ESC "\033[" /* Ansi Control character suffixes */ @@ -397,6 +407,14 @@ ESL_DECLARE(esl_status_t) esl_execute(esl_handle_t *handle, const char *app, con */ ESL_DECLARE(esl_status_t) esl_sendevent(esl_handle_t *handle, esl_event_t *event); +/*! + \brief Send an event as a message to be parsed + \param handle Handle to which the event should be sent + \param event Event to be sent + \param uuid a specific uuid if not the default +*/ +ESL_DECLARE(esl_status_t) esl_sendmsg(esl_handle_t *handle, esl_event_t *event, const char *uuid); + /*! \brief Connect a handle to a host/port with a specific password. This will also authenticate against the server \param handle Handle to connect diff --git a/libs/esl/src/include/esl_event.h b/libs/esl/src/include/esl_event.h index d6be0470e8..1cc6134e42 100644 --- a/libs/esl/src/include/esl_event.h +++ b/libs/esl/src/include/esl_event.h @@ -298,6 +298,7 @@ ESL_DECLARE(esl_status_t) esl_event_create_json(esl_event_t **event, const char \note the body parameter can be shadowed by the esl_event_reserve_subclass_detailed function */ ESL_DECLARE(esl_status_t) esl_event_add_body(esl_event_t *event, const char *fmt, ...); +ESL_DECLARE(esl_status_t) esl_event_set_body(esl_event_t *event, const char *body); /*! \brief Create a new event assuming it will not be custom event and therefore hiding the unused parameters diff --git a/libs/esl/src/include/esl_oop.h b/libs/esl/src/include/esl_oop.h index 947fe453d7..0ea1e5da89 100644 --- a/libs/esl/src/include/esl_oop.h +++ b/libs/esl/src/include/esl_oop.h @@ -86,6 +86,7 @@ class ESLconnection { ESLevent *api(const char *cmd, const char *arg = NULL); ESLevent *bgapi(const char *cmd, const char *arg = NULL, const char *job_uuid = NULL); ESLevent *sendEvent(ESLevent *send_me); + int sendMSG(ESLevent *send_me, const char *uuid = NULL); ESLevent *recvEvent(); ESLevent *recvEventTimed(int ms); ESLevent *filter(const char *header, const char *value); diff --git a/libs/freetdm/Makefile.am b/libs/freetdm/Makefile.am index b4eb76d6fd..3a88b5f521 100644 --- a/libs/freetdm/Makefile.am +++ b/libs/freetdm/Makefile.am @@ -248,6 +248,14 @@ ftmod_r2_la_LDFLAGS = -shared -module -avoid-version -lopenr2 ftmod_r2_la_LIBADD = libfreetdm.la endif +if HAVE_MISDN +mod_LTLIBRARIES += ftmod_misdn.la +ftmod_misdn_la_SOURCES = $(SRC)/ftmod/ftmod_misdn/ftmod_misdn.c +ftmod_misdn_la_CFLAGS = $(FTDM_CFLAGS) $(AM_CFLAGS) $(MISDN_CFLAGS) +ftmod_misdn_la_LDFLAGS = -shared -module -avoid-version +ftmod_misdn_la_LIBADD = libfreetdm.la +endif + dox doxygen: doxygen $(FT_SRCDIR)/docs/Doxygen.conf diff --git a/libs/freetdm/configure.ac b/libs/freetdm/configure.ac index 493f0da3b0..a69618ba6f 100644 --- a/libs/freetdm/configure.ac +++ b/libs/freetdm/configure.ac @@ -54,6 +54,9 @@ fi AC_SUBST([confdir]) +DEFAULT_INCLUDES="-I. -I./src/include -I$(srcdir)" +AC_SUBST([DEFAULT_INCLUDES]) + # Where to install the modules AC_ARG_WITH([modinstdir], [AS_HELP_STRING([--with-modinstdir=DIR], [Install modules into this location (default: ${prefix}/mod)])], @@ -364,6 +367,42 @@ then fi AM_CONDITIONAL([HAVE_LIBISDN], [test "${HAVE_LIBISDN}" = "yes"]) + +## +# mISDN dependencies +# +HAVE_MISDN="no" +AC_ARG_WITH([misdn], + [AS_HELP_STRING([--with-misdn], [Install ftmod_misdn (mISDN I/O plugin)])], + [case "${withval}" in + no|yes) with_misdn="${withval}" ;; + *) AC_MSG_ERROR([Invalid value \"${with_misdn}\" for --with-misdn option]) ;; + esac], + [with_misdn="auto"] +) +AS_IF([test "${with_misdn}" != "no"], + [AC_MSG_RESULT([${as_nl}<<>> ftmod_misdn (Linux mISDN I/O plugin)]) + AC_CHECK_FUNCS([timerfd_create],, + [AS_IF([test "${with_misdn}" = "yes"], + [AC_MSG_ERROR([no timerfd support in libc])], + [AC_MSG_NOTICE([no timerfd support in libc])] + )] + ) + AC_CHECK_HEADER([mISDN/mISDNif.h],, + [AS_IF([test "${with_misdn}" = "yes"], + [AC_MSG_ERROR([mISDN/mISDNif.h not found])], + [AC_MSG_NOTICE([mISDN/mISDNif.h not found])] + )], + [#include ] + ) + AS_IF([test "${ac_cv_func_timerfd_create}" = "yes" -a "${ac_cv_header_mISDN_mISDNif_h}" = "yes"], + [HAVE_MISDN="yes"], + [AC_MSG_NOTICE([Some required dependencies are missing, module disabled])] + )] +) +AM_CONDITIONAL([HAVE_MISDN], [test "${HAVE_MISDN}" = "yes"]) + + AC_MSG_RESULT([${as_nl}<<>> Creating output files]) AC_CONFIG_FILES([ Makefile @@ -389,6 +428,7 @@ AC_MSG_RESULT([ ftmod_pritap....................... ${HAVE_PRITAP} I/O: ftmod_wanpipe...................... ${HAVE_LIBSANGOMA} + ftmod_misdn........................ ${HAVE_MISDN} =============================================================================== ]) diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index c17b28cb77..44accde0dd 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -34,6 +34,15 @@ #include #include "freetdm.h" +//#define CUDATEL_DEBUG +#ifdef CUDATEL_DEBUG +#ifndef _BSD_SOURCE +#define _BSD_SOURCE +#endif +#include +#include +#endif + #ifndef __FUNCTION__ #define __FUNCTION__ __SWITCH_FUNC__ #endif @@ -495,7 +504,14 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) switch_channel_t *channel = NULL; private_t *tech_pvt = NULL; ftdm_chan_type_t chantype; + const char *name = NULL; + int span_id = 0; + int chan_id = 0; + int t = 0; uint32_t tokencnt; + char *uuid = NULL; + const char *token = NULL; + uint8_t uuid_found = 0; channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -503,13 +519,64 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); + /* ignore any further I/O requests, we're hanging up already! */ + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + + name = switch_channel_get_name(channel); + + span_id = tech_pvt->ftdmchan ? ftdm_channel_get_span_id(tech_pvt->ftdmchan) : 0; + chan_id = tech_pvt->ftdmchan ? ftdm_channel_get_id(tech_pvt->ftdmchan) : 0; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%d:%d] %s CHANNEL HANGUP ENTER\n", span_id, chan_id, name); + + /* First verify this call has a device attached */ if (!tech_pvt->ftdmchan) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s does not have any ftdmchan attached\n", name); goto end; - } + } + + /* Now verify the device is still attached to this call :-) + * Sometimes the FS core takes too long (more than 3 seconds) in calling + * channel_on_hangup() and the FreeTDM core decides to take the brute + * force approach and hangup and detach themselves from the call. Later + * when FS finally comes around, we might end up hanging up the device + * attached to another call, this verification avoids that. */ + uuid = switch_core_session_get_uuid(session); + tokencnt = ftdm_channel_get_token_count(tech_pvt->ftdmchan); + for (t = 0; t < tokencnt; t++) { + token = ftdm_channel_get_token(tech_pvt->ftdmchan, t); + if (!zstr(token) && !strcasecmp(uuid, token)) { + uuid_found = 1; + break; + } + } + + if (!uuid_found) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Device [%d:%d] is no longer attached to %s. Nothing to do.\n", span_id, chan_id, name); + goto end; + } + +#ifdef CUDATEL_DEBUG + { + pid_t tid = 0; + size_t size = 0; + char **symbols = NULL; + void *stacktrace[50]; + int si = 0; + size = backtrace(stacktrace, ftdm_array_len(stacktrace)); + symbols = backtrace_symbols(stacktrace, size); + tid = syscall(SYS_gettid); + for (si = 0; si < size; si++) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%d:%d][tid:%d] %s -> %s\n", + span_id, chan_id, tid, name, symbols[si]); + } + free(symbols); + } +#endif ftdm_channel_clear_token(tech_pvt->ftdmchan, switch_core_session_get_uuid(session)); - chantype = ftdm_channel_get_type(tech_pvt->ftdmchan); + chantype = ftdm_channel_get_type(tech_pvt->ftdmchan); switch (chantype) { case FTDM_CHAN_TYPE_FXO: case FTDM_CHAN_TYPE_EM: @@ -520,7 +587,6 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) case FTDM_CHAN_TYPE_FXS: { if (!ftdm_channel_call_check_busy(tech_pvt->ftdmchan) && !ftdm_channel_call_check_done(tech_pvt->ftdmchan)) { - tokencnt = ftdm_channel_get_token_count(tech_pvt->ftdmchan); if (tokencnt) { cycle_foreground(tech_pvt->ftdmchan, 0, NULL); } else { @@ -548,9 +614,6 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) end: - switch_clear_flag_locked(tech_pvt, TFLAG_IO); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); switch_mutex_lock(globals.mutex); globals.calls--; if (globals.calls < 0) { @@ -558,6 +621,7 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session) } switch_mutex_unlock(globals.mutex); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[%d:%d] %s CHANNEL HANGUP EXIT\n", span_id, chan_id, name); return SWITCH_STATUS_SUCCESS; } @@ -629,6 +693,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch int total_to; int chunk, do_break = 0; uint32_t span_id, chan_id; + const char *name = NULL; channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -636,17 +701,18 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); - if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { - ftdm_log(FTDM_LOG_DEBUG, "TFLAG_DEAD is set\n"); - return SWITCH_STATUS_FALSE; - } - + name = switch_channel_get_name(channel); if (!tech_pvt->ftdmchan) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no ftdmchan set in channel %s!\n", name); return SWITCH_STATUS_FALSE; } span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan); chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan); + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_DEAD is set in channel %s device %d:%d!\n", name, span_id, chan_id); + return SWITCH_STATUS_FALSE; + } /* Digium Cards sometimes timeout several times in a row here. Yes, we support digium cards, ain't we nice....... @@ -678,7 +744,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch } if (!switch_test_flag(tech_pvt, TFLAG_IO)) { - ftdm_log(FTDM_LOG_DEBUG, "TFLAG_IO is not set\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_IO is not set in channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } @@ -686,7 +752,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch status = ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, chunk); if (status == FTDM_FAIL) { - ftdm_log(FTDM_LOG_ERROR, "Failed to wait for I/O\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } @@ -694,7 +760,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch if (!switch_test_flag(tech_pvt, TFLAG_HOLD)) { total_to -= chunk; if (total_to <= 0) { - ftdm_log(FTDM_LOG_WARNING, "Too many timeouts while waiting for I/O\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Too many timeouts while waiting I/O in channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } } @@ -707,9 +773,9 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch len = tech_pvt->read_frame.buflen; if (ftdm_channel_read(tech_pvt->ftdmchan, tech_pvt->read_frame.data, &len) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_WARNING, "failed to read from device %d:%d\n", span_id, chan_id); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id); if (++tech_pvt->read_error > FTDM_MAX_READ_WRITE_ERRORS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "too many I/O read errors on device %d:%d!\n", span_id, chan_id); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "too many I/O read errors on channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } } else { @@ -730,18 +796,18 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch for (p = dtmf; p && *p; p++) { if (is_dtmf(*p)) { _dtmf.digit = *p; - ftdm_log(FTDM_LOG_DEBUG, "Queuing DTMF [%c] in channel %s\n", *p, switch_channel_get_name(channel)); + ftdm_log(FTDM_LOG_DEBUG, "Queuing DTMF [%c] in channel %s device %d:%d\n", *p, name, span_id, chan_id); switch_channel_queue_dtmf(channel, &_dtmf); } } } + return SWITCH_STATUS_SUCCESS; - fail: +fail: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "clearing IO in channel %s device %d:%d!\n", name, span_id, chan_id); switch_clear_flag_locked(tech_pvt, TFLAG_IO); return SWITCH_STATUS_GENERR; - - } static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) @@ -750,8 +816,9 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc private_t *tech_pvt = NULL; ftdm_size_t len; unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; + const char *name = "(none)"; ftdm_wait_flag_t wflags = FTDM_WRITE; - uint32_t span_id, chan_id; + uint32_t span_id = 0, chan_id = 0; channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -759,7 +826,17 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); + name = switch_channel_get_name(channel); + if (!tech_pvt->ftdmchan) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no ftdmchan set in channel %s!\n", name); + return SWITCH_STATUS_FALSE; + } + + span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan); + chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan); + if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_DEAD is set in channel %s device %d:%d!\n", name, span_id, chan_id); return SWITCH_STATUS_FALSE; } @@ -768,17 +845,10 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc } if (!switch_test_flag(tech_pvt, TFLAG_IO)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_IO is not set in channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } - if (!tech_pvt->ftdmchan) { - return SWITCH_STATUS_FALSE; - } - - span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan); - chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan); - - if (switch_test_flag(frame, SFF_CNG)) { frame->data = data; frame->buflen = sizeof(data); @@ -793,16 +863,16 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, ftdm_channel_get_io_interval(tech_pvt->ftdmchan) * 10); if (!(wflags & FTDM_WRITE)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write not ready)\n"); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write note ready) in channel %s device %d:%d!\n", name, span_id, chan_id); return SWITCH_STATUS_SUCCESS; } len = frame->datalen; if (ftdm_channel_write(tech_pvt->ftdmchan, frame->data, frame->buflen, &len) != FTDM_SUCCESS) { - ftdm_log(FTDM_LOG_WARNING, "failed to write to device %d:%d\n", span_id, chan_id); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Failed to write to channel %s device %d:%d!\n", name, span_id, chan_id); if (++tech_pvt->write_error > FTDM_MAX_READ_WRITE_ERRORS) { switch_log_printf(SWITCH_CHANNEL_LOG, - SWITCH_LOG_ERROR, "too many I/O write errors on device %d:%d!\n", span_id, chan_id); + SWITCH_LOG_ERROR, "Too many I/O write errors on channel %s device %d:%d!\n", name, span_id, chan_id); goto fail; } } else { @@ -812,7 +882,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc return SWITCH_STATUS_SUCCESS; fail: - + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error writing to channel %s device %d:%d!\n", name, span_id, chan_id); switch_clear_flag_locked(tech_pvt, TFLAG_IO); return SWITCH_STATUS_GENERR; @@ -2370,7 +2440,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal) switch_set_flag_locked(tech_pvt, TFLAG_DEAD); channel = switch_core_session_get_channel(session); switch_channel_hangup(channel, caller_data->hangup_cause); - ftdm_channel_clear_token(sigmsg->channel, switch_core_session_get_uuid(session)); switch_core_session_rwunlock(session); } } @@ -3802,9 +3871,139 @@ typedef switch_status_t (*ftdm_cli_function_t)(ftdm_cli_entry_t *cli, const char int argc, char *argv[]) static void print_usage(switch_stream_handle_t *stream, ftdm_cli_entry_t *cli); -FTDM_CLI_DECLARE(ftdm_cmd_voice_detect) + +typedef struct cmd_ioread_data { + int num_times; + uint32_t interval; + ftdm_span_t *span; + ftdm_channel_t *fchan; + switch_memory_pool_t *pool; + int already_open; +} cmd_ioread_data_t; + +static void *SWITCH_THREAD_FUNC ioread_thread(switch_thread_t *thread, void *obj) { - print_usage(stream, cli); + ftdm_wait_flag_t wflags = FTDM_READ; + ftdm_status_t status = FTDM_FAIL; + unsigned char iobuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; + cmd_ioread_data_t *data = obj; + int span_id = ftdm_span_get_id(data->span); + int chan_id = ftdm_channel_get_id(data->fchan); + ftdm_size_t len = ftdm_channel_get_io_packet_len(data->fchan); + ftdm_size_t origlen = len; + unsigned int pbuf[5]; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Started ioread thread (times = %d, interval = %ums, len = %"FTDM_SIZE_FMT", span = %d, chan = %d\n", + data->num_times, data->interval, len, span_id, chan_id); + + while (ftdm_running() && data->num_times > 0) { + data->num_times--; + + wflags = FTDM_READ; + + status = ftdm_channel_wait(data->fchan, &wflags, (data->interval * 10)); + + if (status == FTDM_FAIL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to wait for IO in device %d:%d!\n", span_id, chan_id); + continue; + } + + if (status == FTDM_TIMEOUT) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Timed out while waiting I/O in device %d:%d!\n", span_id, chan_id); + continue; + } + + len = origlen; + if (ftdm_channel_read(data->fchan, iobuf, &len) != FTDM_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to read from device %d:%d!\n", span_id, chan_id); + continue; + } + pbuf[0] = iobuf[0]; + pbuf[1] = iobuf[1]; + pbuf[2] = iobuf[2]; + pbuf[3] = iobuf[3]; + pbuf[4] = iobuf[4]; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Read 0x%1X 0x%1X 0x%1X 0x%1X 0x%1X\n", + pbuf[0], pbuf[1], pbuf[2], pbuf[3], pbuf[4]); + } + + if (!data->already_open) { + ftdm_channel_close(&data->fchan); + } + + switch_core_destroy_memory_pool(&data->pool); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Done ioread thread (times = %d, interval = %ums, len = %"FTDM_SIZE_FMT", span = %d, chan = %d\n", + data->num_times, data->interval, origlen, span_id, chan_id); + return NULL; +} + +FTDM_CLI_DECLARE(ftdm_cmd_ioread) +{ + char *span_name = NULL; + int channo = 0; + ftdm_status_t status = FTDM_SUCCESS; + switch_threadattr_t *attr = NULL; + switch_thread_t *thread = NULL; + + cmd_ioread_data_t *thdata; + cmd_ioread_data_t data; + + memset(&data, 0, sizeof(data)); + data.num_times = 1; + + if (argc < 3) { + print_usage(stream, cli); + goto end; + } + + span_name = argv[1]; + + ftdm_span_find_by_name(span_name, &data.span); + if (!data.span) { + stream->write_function(stream, "-ERR span %s not found\n", span_name); + goto end; + } + + channo = atoi(argv[2]); + + status = ftdm_channel_open(ftdm_span_get_id(data.span), channo, &data.fchan); + if (!data.fchan || (status != FTDM_SUCCESS && status != FTDM_EBUSY)) { + stream->write_function(stream, "-ERR Failed to open channel %d in span %s\n", channo, span_name); + goto end; + } + + if (status == FTDM_EBUSY) { + data.already_open = 1; + } + + if (argc > 3) { + data.num_times = atoi(argv[3]); + if (data.num_times < 1) { + data.num_times = 1; + } + } + + if (argc > 4) { + data.interval = atoi(argv[4]); + } + + if (data.interval <= 0 || data.interval > 10000) { + data.interval = ftdm_channel_get_io_interval(data.fchan); + } + + switch_core_new_memory_pool(&data.pool); + + thdata = switch_core_alloc(data.pool, sizeof(data)); + memcpy(thdata, &data, sizeof(*thdata)); + + switch_threadattr_create(&attr, data.pool); + switch_threadattr_detach_set(attr, 1); + switch_threadattr_stacksize_set(attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, attr, ioread_thread, thdata, data.pool); + +end: return SWITCH_STATUS_SUCCESS; } @@ -4452,7 +4651,7 @@ static ftdm_cli_entry_t ftdm_cli_options[] = { "dtmf", "on|off []", "::[on:off", ftdm_cmd_dtmf }, { "queuesize", " []", "", ftdm_cmd_queuesize }, { "iostats", "enable|disable|flush|print ", "::[enable:disable:flush:print", ftdm_cmd_iostats }, - { "voice_detect", "[on|off] []", "::[on:off", ftdm_cmd_voice_detect }, + { "ioread", " [num_times] [interval]", "", ftdm_cmd_ioread }, /* Fake handlers as they are handled within freetdm library, * we should provide a way inside freetdm to query for completions from signaling modules */ diff --git a/libs/freetdm/src/detect_dtmf.c b/libs/freetdm/src/detect_dtmf.c index e7d9648192..34335256f0 100644 --- a/libs/freetdm/src/detect_dtmf.c +++ b/libs/freetdm/src/detect_dtmf.c @@ -6,7 +6,7 @@ int main(int argc, char *argv[]) int fd, b; short sln[512] = {0}; teletone_dtmf_detect_state_t dtmf_detect = {0}; - char digit_str[128] = ""; + teletone_hit_type_t hit; if (argc < 2) { fprintf(stderr, "Arg Error!\n"); @@ -21,10 +21,31 @@ int main(int argc, char *argv[]) } while((b = read(fd, sln, 320)) > 0) { + char digit_char; + unsigned int dur; + teletone_dtmf_detect(&dtmf_detect, sln, b / 2); - teletone_dtmf_get(&dtmf_detect, digit_str, sizeof(digit_str)); - if (*digit_str) { - printf("digit: %s\n", digit_str); + if ((hit = teletone_dtmf_get(&dtmf_detect, &digit_char, &dur))) { + const char *hs = NULL; + + + switch(hit) { + case TT_HIT_BEGIN: + hs = "begin"; + break; + + case TT_HIT_MIDDLE: + hs = "middle"; + break; + + case TT_HIT_END: + hs = "end"; + break; + default: + break; + } + + printf("%s digit: %c\n", hs, digit_char); } } close(fd); diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 0205ab36b1..9da6e1e903 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -649,9 +649,6 @@ static ftdm_status_t ftdm_span_destroy(ftdm_span_t *span) if (span->fio->span_destroy(span) != FTDM_SUCCESS) { status = FTDM_FAIL; } - ftdm_safe_free(span->type); - ftdm_safe_free(span->name); - ftdm_safe_free(span->dtmf_hangup); } /* destroy final basic resources of the span data structure */ @@ -1858,7 +1855,14 @@ static ftdm_status_t _ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm goto done; } - ftdm_mutex_lock(check->mutex); + ftdm_channel_lock(check); + + if (ftdm_test_flag(check, FTDM_CHANNEL_OPEN)) { + /* let them know is already open, but return the channel anyway */ + status = FTDM_EBUSY; + *ftdmchan = check; + goto unlockchan; + } /* The following if's and gotos replace a big if (this || this || this || this) else { nothing; } */ @@ -1905,7 +1909,7 @@ openchan: goto done; unlockchan: - ftdm_mutex_unlock(check->mutex); + ftdm_channel_unlock(check); done: ftdm_mutex_unlock(globals.mutex); @@ -3855,7 +3859,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, v uint8_t sln_buf[1024] = {0}; int16_t *sln; ftdm_size_t slen = 0; - char digit_str[80] = ""; if (ftdmchan->effective_codec == FTDM_CODEC_SLIN) { sln = data; @@ -3956,13 +3959,18 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, v } if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) && !ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) { - teletone_dtmf_detect(&ftdmchan->dtmf_detect, sln, (int)slen); - teletone_dtmf_get(&ftdmchan->dtmf_detect, digit_str, sizeof(digit_str)); + teletone_hit_type_t hit; + char digit_char; + uint32_t dur; - if(*digit_str) { - if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) { + if ((hit = teletone_dtmf_detect(&ftdmchan->dtmf_detect, sln, (int)slen)) == TT_HIT_END) { + teletone_dtmf_get(&ftdmchan->dtmf_detect, &digit_char, &dur); + + if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (digit_char == 'D' || digit_char == 'A')) { ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++; } else { + char digit_str[2] = { digit_char, 0}; + if (!ftdmchan->span->sig_dtmf || (ftdmchan->span->sig_dtmf(ftdmchan, (const char*)digit_str) != FTDM_BREAK)) { ftdm_channel_queue_dtmf(ftdmchan, digit_str); } @@ -4568,7 +4576,7 @@ FT_DECLARE(ftdm_status_t) ftdm_configure_span_channels(ftdm_span_t *span, const *configured = 0; *configured = span->fio->configure_span(span, str, chan_config->type, chan_config->name, chan_config->number); if (!*configured) { - ftdm_log(FTDM_LOG_ERROR, "%d:Failed to configure span", span->span_id); + ftdm_log(FTDM_LOG_ERROR, "%d:Failed to configure span\n", span->span_id); return FTDM_FAIL; } @@ -5468,10 +5476,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t if (sigmsg->channel) { fchan = sigmsg->channel; ftdm_channel_lock(fchan); - sigmsg->chan_id = fchan->chan_id; - sigmsg->span_id = fchan->span_id; - sigmsg->call_id = fchan->caller_data.call_id; - sigmsg->call_priv = fchan->caller_data.priv; } /* some core things to do on special events */ @@ -5561,6 +5565,14 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t } + if (fchan) { + /* set members of the sigmsg that must be present for all events */ + sigmsg->chan_id = fchan->chan_id; + sigmsg->span_id = fchan->span_id; + sigmsg->call_id = fchan->caller_data.call_id; + sigmsg->call_priv = fchan->caller_data.priv; + } + /* if the signaling module uses a queue for signaling notifications, then enqueue it */ if (ftdm_test_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE)) { ftdm_span_queue_signal(span, sigmsg); @@ -5774,6 +5786,7 @@ FT_DECLARE(ftdm_status_t) ftdm_global_destroy(void) } hashtable_remove(globals.span_hash, (void *)cur_span->name); + ftdm_safe_free(cur_span->dtmf_hangup); ftdm_safe_free(cur_span->type); ftdm_safe_free(cur_span->name); ftdm_safe_free(cur_span); diff --git a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c index a379aeda9c..f31eafd42e 100644 --- a/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c +++ b/libs/freetdm/src/ftmod/ftmod_libpri/ftmod_libpri.c @@ -1938,6 +1938,14 @@ static void *ftdm_libpri_run(ftdm_thread_t *me, void *obj) ftdm_sleep(5000); } out: + /* close d-channel, if set */ + if (isdn_data->dchan) { + if (ftdm_channel_close(&isdn_data->dchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to close D-Channel %d:%d\n", + ftdm_channel_get_span_id(isdn_data->dchan), ftdm_channel_get_id(isdn_data->dchan)); + } + } + ftdm_log(FTDM_LOG_DEBUG, "PRI thread ended on span %d\n", ftdm_span_get_id(span)); ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD); diff --git a/libs/freetdm/src/ftmod/ftmod_misdn/ftmod_misdn.c b/libs/freetdm/src/ftmod/ftmod_misdn/ftmod_misdn.c new file mode 100644 index 0000000000..9673549f79 --- /dev/null +++ b/libs/freetdm/src/ftmod/ftmod_misdn/ftmod_misdn.c @@ -0,0 +1,1807 @@ +/** + * mISDN HW interface + * + * Copyright (c) 2011, Stefan Knoblich + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * NOTE: This is intended as a Layer 1 interface only, signaling + * is handled by other modules (e.g. ftmod_libpri or ftmod_isdn). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* this is how it should have been... +#ifdef HAVE_FREETDM_FREETDM_H +#include +#else +#include +#endif +*/ +/* ... and this is how it is */ +#include + +#include + +/* + * mISDNcompat.h replaces these with references to an extern int, + * which is exported by libmisdn... unset them and use the official + * AF ID "34" + */ +#undef PF_ISDN +#undef AF_ISDN +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN + +//#define MISDN_DEBUG_EVENTS +//#define MISDN_DEBUG_IO + +#ifndef MIN +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#endif + +typedef enum { + MISDN_CAPS_NONE = 0, + + /* D-Channel */ + MISDN_CAPS_PRI = (1 << 0), + MISDN_CAPS_BRI = (1 << 1), + MISDN_CAPS_UP0 = (1 << 2), + MISDN_CAPS_NT = (1 << 3), + MISDN_CAPS_TE = (1 << 4), + + /* B-Channel */ + MISDN_CAPS_RAW = (1 << 10), + MISDN_CAPS_HDLC = (1 << 11), +} misdn_capability_flags_t; + +#define MISDN_IS_BRI(x) (x & MISDN_CAPS_BRI) +#define MISDN_IS_PRI(x) (x & MISDN_CAPS_PRI) + +#define MISDN_IS_TE(x) (x & MISDN_CAPS_TE) +#define MISDN_IS_NT(x) (x & MISDN_CAPS_NT) + +#define MISDN_IS_RAW(x) (x & MISDN_CAPS_RAW) +#define MISDN_IS_HDLC(x) (x & MISDN_CAPS_HDLC) + + +const static struct { + const int id; + const char *name; +} misdn_event_types[] = { + { PH_DATA_REQ, "PH_DATA_REQ" }, + { PH_DATA_IND, "PH_DATA_IND" }, + { PH_DATA_CNF, "PH_DATA_CNF" }, + { PH_CONTROL_REQ, "PH_CONTROL_REQ" }, + { PH_CONTROL_IND, "PH_CONTROL_IND" }, + { PH_CONTROL_CNF, "PH_CONTROL_CNF" }, + { PH_ACTIVATE_REQ, "PH_ACTIVATE_REQ" }, + { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" }, + { PH_ACTIVATE_CNF, "PH_ACTIVATE_CNF" }, + { PH_DEACTIVATE_REQ, "PH_DEACTIVATE_REQ" }, + { PH_DEACTIVATE_IND, "PH_DEACTIVATE_IND" }, + { PH_DEACTIVATE_CNF, "PH_DEACTIVATE_CNF" }, +}; + +static const char *misdn_event2str(const int event) +{ + int x; + + for (x = 0; x < ftdm_array_len(misdn_event_types); x++) { + if (event == misdn_event_types[x].id) + return misdn_event_types[x].name; + } + return "unknown"; +} + + +const static struct { + const int id; + const char *name; +} misdn_control_types[] = { +#define MISDN_CONTROL_TYPE(x) { x, #x } + MISDN_CONTROL_TYPE(DTMF_HFC_COEF), +}; + +#if 0 /* unused for now */ +static const char *misdn_control2str(const int ctrl) +{ + int x; + + for (x = 0; x < ftdm_array_len(misdn_control_types); x++) { + if (ctrl == misdn_control_types[x].id) + return misdn_control_types[x].name; + } + return "unknown"; +} +#endif + +/*********************************************************************************** + * mISDN <-> FreeTDM data structures + ***********************************************************************************/ + +enum { + MISDN_SPAN_NONE = 0, + MISDN_SPAN_RUNNING = (1 << 0), + MISDN_SPAN_STOPPED = (1 << 1) +}; + +struct misdn_span_private { + int flags; + + /* event conditional */ + pthread_mutex_t event_cond_mutex; + pthread_cond_t event_cond; +}; + +#define MISDN_CHAN_STATE_CLOSED 0 +#define MISDN_CHAN_STATE_OPEN 1 + +struct misdn_event_queue; + +struct misdn_chan_private { + /* */ + int state; + int debugfd; + int timerfd; + + /* hw addr of channel */ + struct sockaddr_mISDN addr; + + /* counters */ + unsigned long tx_cnt; + unsigned long tx_ack_cnt; + unsigned long rx_cnt; + unsigned long slip_rx_cnt; + unsigned long slip_tx_cnt; + + struct misdn_event_queue *events; +}; + +#define ftdm_chan_io_private(x) ((x)->io_data) +#define ftdm_span_io_private(x) ((x)->io_data) + +static ftdm_status_t misdn_handle_incoming(ftdm_channel_t *ftdmchan, const char *rbuf, const int size); + +/*********************************************************************************** + * mISDN interface functions + ***********************************************************************************/ + +/* + * Event Queue + */ +#define MISDN_EVENTS_MAX 8 + +struct misdn_event { + int id; +}; + +struct misdn_event_queue { + int read_pos; + int write_pos; + pthread_mutex_t mutex; + + struct misdn_event events[MISDN_EVENTS_MAX]; +}; + +/** + * Initialize event queue + */ +static ftdm_status_t misdn_event_queue_create(struct misdn_event_queue **queue) +{ + struct misdn_event_queue *tmp = NULL; + + if (!queue) + return FTDM_FAIL; + + tmp = calloc(1, sizeof(*tmp)); + if (!tmp) + return FTDM_FAIL; + + pthread_mutex_init(&tmp->mutex, NULL); + + *queue = tmp; + return FTDM_SUCCESS; +} + +/** + * Destroy event queue + */ +static ftdm_status_t misdn_event_queue_destroy(struct misdn_event_queue **queue) +{ + if (!queue || !*queue) + return FTDM_FAIL; + + pthread_mutex_destroy(&(*queue)->mutex); + ftdm_safe_free(*queue); + *queue = NULL; + + return FTDM_SUCCESS; +} + +static ftdm_status_t misdn_event_queue_reset(struct misdn_event_queue *queue) +{ + if (!queue) + return FTDM_FAIL; + pthread_mutex_lock(&queue->mutex); + + memset(queue->events, 0, sizeof(queue->events)); + queue->read_pos = queue->write_pos = 0; + + pthread_mutex_unlock(&queue->mutex); + return FTDM_SUCCESS; +} + +static ftdm_status_t misdn_event_queue_has_data(const struct misdn_event_queue *queue) +{ + if (!queue) + return FTDM_FALSE; + + return (queue->read_pos == queue->write_pos) ? FTDM_FALSE : FTDM_TRUE; +} + +static struct misdn_event *misdn_event_queue_pop(struct misdn_event_queue *queue) +{ + struct misdn_event *evt = NULL; + int next_idx = 0; + + if (!queue) + return NULL; + + pthread_mutex_lock(&queue->mutex); + + next_idx = (queue->read_pos + 1) % MISDN_EVENTS_MAX; + + if (queue->read_pos == queue->write_pos) { +#ifdef MISDN_DEBUG_EVENTS + ftdm_log(FTDM_LOG_DEBUG, "mISDN queue %p: empty\n", queue); +#endif + pthread_mutex_unlock(&queue->mutex); + return NULL; + } + +#ifdef MISDN_DEBUG_EVENTS + ftdm_log(FTDM_LOG_DEBUG, "mISDN queue %p: read event (read_pos: %d, write_pos: %d, next_write_pos: %d)\n", + queue, queue->read_pos, queue->write_pos, next_idx); +#endif + /* update read pos */ + evt = &queue->events[queue->read_pos]; + queue->read_pos = next_idx; + + pthread_mutex_unlock(&queue->mutex); + return evt; +} + +static ftdm_status_t misdn_event_queue_push(struct misdn_event_queue *queue, struct misdn_event *evt) +{ + int next_idx = 0; + + if (!queue || !evt) + return FTDM_FAIL; + + pthread_mutex_lock(&queue->mutex); + + next_idx = (queue->write_pos + 1) % MISDN_EVENTS_MAX; + + if (next_idx == queue->read_pos) { +#ifdef MISDN_DEBUG_EVENTS + ftdm_log(FTDM_LOG_DEBUG, "mISDN queue %p: full\n", queue); +#endif + pthread_mutex_unlock(&queue->mutex); + return FTDM_FAIL; + } + +#ifdef MISDN_DEBUG_EVENTS + ftdm_log(FTDM_LOG_DEBUG, "mISDN queue %p: wrote event (read_pos: %d, write_pos: %d, next_write_pos: %d)\n", + queue, queue->read_pos, queue->write_pos, next_idx); +#endif + memcpy(&queue->events[queue->write_pos], evt, sizeof(*evt)); + queue->write_pos = next_idx; + + pthread_mutex_unlock(&queue->mutex); + return FTDM_SUCCESS; +} + +#if 0 /* unused for now */ +static void misdn_event_queue_print_info(const struct misdn_event_queue *queue) +{ + ftdm_log(FTDM_LOG_DEBUG, "Queue %p\n\tread idx: %d\n\twrite idx: %d\n", queue, + queue->read_pos, queue->write_pos); +} +#endif + +/*********************************************************************************** + * mISDN helper functions + ***********************************************************************************/ + +#define MISDN_PH_ACTIVATE_TIMEOUT_MS 10000 +#define MISDN_MPH_INFORMATION_TIMEOUT_MS 3000 + +static inline void ts_add_msec(struct timespec *a, int msec) +{ + a->tv_sec += (msec / 1000); + a->tv_nsec += (msec % 1000) * 1000000; + if (a->tv_nsec >= 1000000000) { + a->tv_sec++; + a->tv_nsec -= 1000000000; + } +} + +static inline int ts_sub_msec(struct timespec *a, struct timespec *b) +{ + int msec = 0; + msec += (a->tv_sec - b->tv_sec) * 1000; + msec += (a->tv_nsec - b->tv_nsec) / 1000000; + return msec; +} + +static inline int ts_after(struct timespec *a, struct timespec *b) +{ + if (a->tv_sec > b->tv_sec) return 1; + if (a->tv_sec == b->tv_sec && a->tv_nsec > b->tv_nsec) return 1; + return 0; +} + +static inline int ts_before(struct timespec *a, struct timespec *b) +{ + if (a->tv_sec < b->tv_sec) return 1; + if (a->tv_sec == b->tv_sec && a->tv_nsec < b->tv_nsec) return 1; + return 0; +} + +static ftdm_status_t misdn_activate_channel(ftdm_channel_t *chan, int activate) +{ + char buf[MAX_DATA_MEM] = { 0 }; + struct mISDNhead *hh = (struct mISDNhead *) buf; + struct timespec abstimeout; + int req = 0, resp = 0, ms_left = MISDN_PH_ACTIVATE_TIMEOUT_MS; + int retval; + + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN sending %s request\n", + (activate) ? "activation" : "deactivation"); + + /* prepare + send request primitive */ + req = (activate) ? PH_ACTIVATE_REQ : PH_DEACTIVATE_REQ; + hh->prim = req; + hh->id = MISDN_ID_ANY; + + if ((retval = sendto(chan->sockfd, hh, sizeof(*hh), 0, NULL, 0)) < sizeof(*hh)) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN failed to send activation request: %s\n", + strerror(errno)); + return FTDM_FAIL; + } + + clock_gettime(CLOCK_MONOTONIC, &abstimeout); + ts_add_msec(&abstimeout, ms_left); + + /* wait for answer */ + while (1) { + struct timespec now; + struct pollfd pfd; + + pfd.fd = chan->sockfd; + pfd.events = POLLIN /* | POLLPRI */; + pfd.revents = 0; + + switch ((retval = poll(&pfd, 1, ms_left))) { + case 0: /* timeout */ + goto out; + case -1: /* error */ + if (!(retval == EAGAIN || retval == EINTR)) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN polling for activation confirmation failed: %s\n", + strerror(errno)); + return FTDM_FAIL; + } + break; + default: /* read data */ + break; + } + + if (pfd.revents & (POLLIN | POLLPRI)) { + /* handle incoming message */ + if ((retval = recvfrom(chan->sockfd, buf, sizeof(buf), 0, NULL, NULL)) <= 0) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN failed to receive possible answer for %s request: %s\n", + (activate) ? "activation" : "deactivation", strerror(errno)); + return FTDM_FAIL; + } +//#ifdef MISDN_DEBUG_EVENTS + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN got event '%s' while waiting for %s confirmation\n", + misdn_event2str(hh->prim), (activate) ? "activation" : "deactivation"); +//#endif + switch (hh->prim) { + case PH_ACTIVATE_IND: /* success (or not): save last response, */ + case PH_DEACTIVATE_IND: /* stop looping if it's the one we've been waiting for */ + resp = hh->prim; + if (hh->prim == (activate) ? PH_ACTIVATE_IND : PH_DEACTIVATE_IND) goto out; + break; + case PH_ACTIVATE_CNF: + case PH_DEACTIVATE_CNF: + resp = hh->prim; + if (hh->prim == (activate) ? PH_ACTIVATE_CNF : PH_DEACTIVATE_CNF) goto out; + break; + case PH_ACTIVATE_REQ: /* REQ echo, ignore */ + case PH_DEACTIVATE_REQ: + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN got '%s' echo while waiting for %s confirmation (id: %#x)\n", + misdn_event2str(hh->prim), (activate) ? "activation" : "deactivation", hh->id); + break; + default: /* other messages, ignore */ + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN ignoring event '%s' while waiting for %s confirmation\n", + misdn_event2str(hh->prim), (activate) ? "activation" : "deactivation"); + break; + } + } + + /* check timeout */ + clock_gettime(CLOCK_MONOTONIC, &now); + + if (ts_after(&now, &abstimeout) || (ms_left = ts_sub_msec(&abstimeout, &now)) <= 0) + goto out; + } +out: + if (resp == 0) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN timeout waiting for %s confirmation\n", + (activate) ? "activation" : "deactivation"); + return FTDM_TIMEOUT; + } + if ((req == PH_ACTIVATE_IND && !(resp == PH_ACTIVATE_CNF || resp == PH_ACTIVATE_IND)) || + (req == PH_DEACTIVATE_IND && !(resp == PH_DEACTIVATE_CNF || resp == PH_DEACTIVATE_CNF))) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN received '%s' while waiting for %s\n", + misdn_event2str(resp), (activate) ? "activation" : "deactivation"); + return FTDM_FAIL; + } + + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN received %s confirmation\n", + (activate) ? "activation" : "deactivation"); + return FTDM_SUCCESS; +} + + +#if 0 /* unused for now */ +static ftdm_status_t misdn_get_ph_info(ftdm_channel_t *chan, struct ph_info *info) +{ + char buf[MAX_DATA_MEM] = { 0 }; + struct mISDNhead *hh; + struct timespec abstimeout; + int req = 0, resp = 0, ms_left = MISDN_MPH_INFORMATION_TIMEOUT_MS; + int retval; + + /* prepare + send request primitive */ + req = MPH_INFORMATION_REQ; + hh = (struct mISDNhead *)buf; + hh->prim = req; + hh->id = MISDN_ID_ANY; + + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN sending %s request\n", + misdn_event2str(req)); + + if ((retval = sendto(chan->sockfd, &hh, sizeof(hh), 0, NULL, 0)) < sizeof(hh)) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN failed to send %s request: %s\n", + misdn_event2str(req), strerror(errno)); + return FTDM_FAIL; + } + + clock_gettime(CLOCK_MONOTONIC, &abstimeout); + ts_add_msec(&abstimeout, ms_left); + + /* wait for answer */ + while (1) { + struct timespec now; + struct pollfd pfd; + + pfd.fd = chan->sockfd; + pfd.events = POLLIN /* | POLLPRI */; + pfd.revents = 0; + + switch ((retval = poll(&pfd, 1, ms_left))) { + case 0: /* timeout */ + goto out; + case -1: /* error */ + if (!(retval == EAGAIN || retval == EINTR)) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN polling for %s answer failed: %s\n", + misdn_event2str(req), strerror(errno)); + return FTDM_FAIL; + } + break; + default: /* read data */ + break; + } + + if (pfd.revents & (POLLIN | POLLPRI)) { + /* handle incoming message */ + if ((retval = recvfrom(chan->sockfd, buf, sizeof(buf), 0, NULL, NULL)) <= 0) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN failed to receive possible answer for %s request: %s\n", + misdn_event2str(req), strerror(errno)); + return FTDM_FAIL; + } +//#ifdef MISDN_DEBUG_EVENTS + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN got event '%s' while waiting for %s answer\n", + misdn_event2str(hh->prim), misdn_event2str(req)); +//#endif + switch (hh->prim) { + case MPH_INFORMATION_IND: /* success */ + if (retval < MISDN_HEADER_LEN + sizeof(*info)) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN answer for %s is too short\n", + misdn_event2str(req)); + return FTDM_FAIL; + } + resp = hh->prim; + /* TODO */ + goto out; + case MPH_INFORMATION_REQ: /* REQ echo, ignore */ + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN got '%s' echo while waiting for %s answer\n", + misdn_event2str(hh->prim), misdn_event2str(req)); + break; + default: /* other messages, ignore */ + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN ignoring event '%s' while waiting for %s answer\n", + misdn_event2str(hh->prim), misdn_event2str(req)); + break; + } + } + + /* check timeout */ + clock_gettime(CLOCK_MONOTONIC, &now); + + if (ts_after(&now, &abstimeout) || (ms_left = ts_sub_msec(&abstimeout, &now)) <= 0) + goto out; + } +out: + if (resp == 0) { + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN timeout waiting for %s answer\n", + misdn_event2str(req)); + return FTDM_TIMEOUT; + } + + return FTDM_SUCCESS; +} +#endif + +static int misdn_handle_ph_control_ind(ftdm_channel_t *chan, const struct mISDNhead *hh, const void *data, const int data_len) +{ +#ifdef MISDN_DEBUG_EVENTS + ftdm_log_chan(chan, FTDM_LOG_DEBUG, + "PH_CONTROL_IND:\n" + "\tMessage:\t%s\n" + "\tPayload:\t%d\n", + misdn_control2str(hh->id), data_len); +#endif + + switch (hh->id) { + case DTMF_HFC_COEF: + break; + default: + break; + } + + return FTDM_SUCCESS; +} + +static int misdn_handle_mph_information_ind(ftdm_channel_t *chan, const struct mISDNhead *hh, const void *data, const int data_len) +{ + struct misdn_chan_private *priv = ftdm_chan_io_private(chan); + int alarm_flags, value; + + if (data_len < sizeof(value)) { + ftdm_log_chan_msg(chan, FTDM_LOG_ERROR, "mISDN MPH_INFORMATION_IND message is too short\n"); + return FTDM_FAIL; + } + value = *(int *)data; + alarm_flags = chan->alarm_flags; + + switch (value) { + case L1_SIGNAL_LOS_ON: + alarm_flags |= FTDM_ALARM_RED; + break; + case L1_SIGNAL_LOS_OFF: + alarm_flags &= ~FTDM_ALARM_RED; + break; + case L1_SIGNAL_AIS_ON: + alarm_flags |= FTDM_ALARM_AIS; + break; + case L1_SIGNAL_AIS_OFF: + alarm_flags &= ~FTDM_ALARM_AIS; + break; + case L1_SIGNAL_RDI_ON: + alarm_flags |= FTDM_ALARM_YELLOW; + break; + case L1_SIGNAL_RDI_OFF: + alarm_flags &= ~FTDM_ALARM_YELLOW; + break; + case L1_SIGNAL_SLIP_RX: + priv->slip_rx_cnt++; + break; + case L1_SIGNAL_SLIP_TX: + priv->slip_tx_cnt++; + break; + default: + ftdm_log_chan(chan, FTDM_LOG_ERROR, "mISDN unknown MPH_INFORMATION_IND message: %d\n", + value); + return FTDM_FAIL; + } + if ((value = (alarm_flags ^ chan->alarm_flags))) { + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "mISDN alarm flags have changed %#x -> %#x\n", + chan->alarm_flags, alarm_flags); + chan->alarm_flags ^= value; + } + return FTDM_SUCCESS; +} + +/*********************************************************************************** + * mISDN <-> FreeTDM interface functions + ***********************************************************************************/ + +struct misdn_globals { + int sockfd; +} globals; + +/** + * \brief Open channel + * \param ftdmchan FreeTDM channel to open + */ +static FIO_OPEN_FUNCTION(misdn_open) +{ + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(ftdmchan); + ftdm_span_t *span = ftdm_channel_get_span(ftdmchan); + struct misdn_span_private *span_priv = ftdm_span_io_private(span); + ftdm_status_t ret = 0; + + assert(chan_priv); + assert(span_priv); + + if (chan_priv->state == MISDN_CHAN_STATE_OPEN) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "mISDN channel is already open, skipping activation\n"); + return FTDM_SUCCESS; + } + + /* flush all events */ + misdn_event_queue_reset(chan_priv->events); + + /* + * Send activation request + */ + ret = misdn_activate_channel(ftdmchan, 1); + if (ret != FTDM_SUCCESS) { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to activate channel (socket: %d)\n", + ftdmchan->sockfd); + return FTDM_FAIL; + } + + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "mISDN channel activation request sent\n"); + + switch (ftdmchan->type) { + case FTDM_CHAN_TYPE_B: { + struct itimerspec its = { + .it_interval = { 0, 0 }, + .it_value = { 0, 0 }, + }; + + its.it_interval.tv_nsec = (ftdmchan->effective_interval * 1000000); + its.it_value.tv_nsec = (ftdmchan->effective_interval * 1000000); + + /* create tx timerfd */ + chan_priv->timerfd = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK); + if (chan_priv->timerfd < 0) { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "mISDN failed to create b-channel tx interval timer: %s\n", + strerror(errno)); + return FTDM_FAIL; + } + + /* start tx timerfd */ + ret = timerfd_settime(chan_priv->timerfd, 0, &its, NULL); + if (ret < 0) { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "mISDN failed to start b-channel tx interval timer: %s\n", + strerror(errno)); + return FTDM_FAIL; + } + + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "mISDN created tx interval (%d ms) timer\n", + ftdmchan->effective_interval); + } + case FTDM_CHAN_TYPE_DQ921: + chan_priv->state = MISDN_CHAN_STATE_OPEN; + break; + default: + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "mISDN invalid channel type '%s'\n", + ftdm_channel_get_type(ftdmchan)); + break; + } + return FTDM_SUCCESS; +} + +/** + * \brief Close channel + * \param ftdmchan FreeTDM channel to close + */ +static FIO_CLOSE_FUNCTION(misdn_close) +{ + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(ftdmchan); + ftdm_status_t ret = 0; + + assert(chan_priv); + + /* deactivate b-channels on close */ + if (ftdm_channel_get_type(ftdmchan) == FTDM_CHAN_TYPE_B) { + /* + * Stop tx timerfd + */ + if (chan_priv->timerfd >= 0) { + close(chan_priv->timerfd); + chan_priv->timerfd = -1; + } + + /* + * Send deactivation request (don't wait for answer) + */ + ret = misdn_activate_channel(ftdmchan, 0); + if (ret != FTDM_SUCCESS) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to deactivate channel\n"); + return FTDM_FAIL; + } + + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_INFO, "mISDN channel deactivated\n"); + chan_priv->state = MISDN_CHAN_STATE_CLOSED; + } + + return FTDM_SUCCESS; +} + +/** + * \brief Execute command + * \param ftdmchan FreeTDM channel + * \param command Command to execute + * \param obj Additional command data + */ +static FIO_COMMAND_FUNCTION(misdn_command) +{ + switch (command) { + case FTDM_COMMAND_NOOP: + break; + case FTDM_COMMAND_SET_INTERVAL: +// case FTDM_COMMAND_GET_INTERVAL: + case FTDM_COMMAND_SET_CODEC: + case FTDM_COMMAND_GET_CODEC: + case FTDM_COMMAND_SET_NATIVE_CODEC: + case FTDM_COMMAND_GET_NATIVE_CODEC: + case FTDM_COMMAND_ENABLE_DTMF_DETECT: + case FTDM_COMMAND_DISABLE_DTMF_DETECT: + case FTDM_COMMAND_SEND_DTMF: + case FTDM_COMMAND_SET_DTMF_ON_PERIOD: + case FTDM_COMMAND_GET_DTMF_ON_PERIOD: + case FTDM_COMMAND_SET_DTMF_OFF_PERIOD: + case FTDM_COMMAND_GET_DTMF_OFF_PERIOD: + case FTDM_COMMAND_SET_RX_GAIN: /* DSP_VOL_CHANGE_RX / HFC_VOL_CHANGE_RX */ + case FTDM_COMMAND_GET_RX_GAIN: + case FTDM_COMMAND_SET_TX_GAIN: /* DSP_VOL_CHANGE_TX / HFC_VOL_CHANGE_TX */ + case FTDM_COMMAND_GET_TX_GAIN: + case FTDM_COMMAND_FLUSH_TX_BUFFERS: + case FTDM_COMMAND_FLUSH_RX_BUFFERS: + case FTDM_COMMAND_FLUSH_BUFFERS: + case FTDM_COMMAND_FLUSH_IOSTATS: + case FTDM_COMMAND_SET_PRE_BUFFER_SIZE: + case FTDM_COMMAND_SET_LINK_STATUS: + case FTDM_COMMAND_GET_LINK_STATUS: + case FTDM_COMMAND_SET_RX_QUEUE_SIZE: + case FTDM_COMMAND_SET_TX_QUEUE_SIZE: + case FTDM_COMMAND_START_MF_PLAYBACK: + case FTDM_COMMAND_STOP_MF_PLAYBACK: + case FTDM_COMMAND_GET_IOSTATS: + case FTDM_COMMAND_SWITCH_IOSTATS: + /* Supported by mISDN */ + case FTDM_COMMAND_ENABLE_ECHOCANCEL: /* DSP_ECHO_ON */ + case FTDM_COMMAND_DISABLE_ECHOCANCEL: /* DSP_ECHO_OFF */ + case FTDM_COMMAND_ENABLE_LOOP: + case FTDM_COMMAND_DISABLE_LOOP: + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Received unimplemented command: %d\n", + command); + break; + + case FTDM_COMMAND_GET_INTERVAL: + FTDM_COMMAND_OBJ_INT = ftdm_channel_get_io_interval(ftdmchan); + ftdm_log(FTDM_LOG_NOTICE, "Interval %d ms [%d:%d]\n", + ftdm_channel_get_io_interval(ftdmchan), ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan)); + break; + + default: + ftdm_log(FTDM_LOG_ERROR, "Unknown command %d\n", command); + } + return FTDM_SUCCESS; +} + + +/** + * \brief Wait for new data + * \param ftdmchan FreeTDM channel to wait on + * \param flags Wait flags + * \param to Timeout + */ +static FIO_WAIT_FUNCTION(misdn_wait) +{ + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(ftdmchan); + struct pollfd pfds[2]; + int nr_fds = 0; + int retval; + + memset(pfds, 0, sizeof(pfds)); + + switch (ftdm_channel_get_type(ftdmchan)) { + case FTDM_CHAN_TYPE_B: + if (*flags & FTDM_WRITE) { + pfds[nr_fds].fd = chan_priv->timerfd; + pfds[nr_fds].events = POLLIN; + nr_fds++; + } + if (*flags & (FTDM_READ | FTDM_EVENTS)) { + pfds[nr_fds].fd = ftdmchan->sockfd; + pfds[nr_fds].events |= (*flags & FTDM_READ) ? POLLIN : 0; + pfds[nr_fds].events |= (*flags & FTDM_EVENTS) ? POLLPRI : 0; + nr_fds++; + } + break; + default: + if (*flags & FTDM_READ) + pfds[0].events |= POLLIN; + if (*flags & FTDM_WRITE) + pfds[0].events |= POLLOUT; + if (*flags & FTDM_EVENTS) + pfds[0].events |= POLLPRI; + pfds[0].fd = ftdmchan->sockfd; + nr_fds++; + break; + } + + *flags = FTDM_NO_FLAGS; + + if (!(pfds[0].events || pfds[1].events)) + return FTDM_SUCCESS; + if ((retval = poll(pfds, nr_fds, to)) < 0) { + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "mISDN poll() failed: %s\n", + strerror(errno)); + return FTDM_FAIL; + } + if (retval == 0) + return FTDM_TIMEOUT; + + switch (ftdm_channel_get_type(ftdmchan)) { + case FTDM_CHAN_TYPE_B: + if (pfds[0].fd == chan_priv->timerfd) { + if (pfds[0].revents & POLLIN) { + uint64_t tmp = 0; /* clear pending events on timerfd */ + retval = read(pfds[0].fd, &tmp, sizeof(tmp)); + *flags |= FTDM_WRITE; + } + if (pfds[1].revents & POLLIN) + *flags |= FTDM_READ; + if (pfds[1].revents & POLLPRI) + *flags |= FTDM_EVENTS; + break; + } + default: + if (pfds[0].revents & POLLIN) + *flags |= FTDM_READ; + if (pfds[0].revents & POLLOUT) + *flags |= FTDM_WRITE; + if (pfds[0].revents & POLLPRI) + *flags |= FTDM_EVENTS; + break; + } + return FTDM_SUCCESS; +} + +/** + * \brief Read data + * \param ftdmchan FreeTDM channel + * \param data Buffer for data + * \param datalen Number of bytes to read (contains bytes read after return) + */ +static FIO_READ_FUNCTION(misdn_read) +{ + struct misdn_chan_private *priv = ftdm_chan_io_private(ftdmchan); + char rbuf[MAX_DATA_MEM] = { 0 }; + struct mISDNhead *hh = (struct mISDNhead *)rbuf; + int bytes = *datalen; + int retval; + + if (priv->state == MISDN_CHAN_STATE_CLOSED) { + /* ignore */ + *datalen = 0; + return FTDM_SUCCESS; + } + + /* + * try to read all messages, as long as we haven't received a PH_DATA_IND one + * we'll get a lot of "mISDN_send: error -12" message in dmesg otherwise + * (= b-channel receive queue overflowing) + */ + while (1) { + if ((retval = recvfrom(ftdmchan->sockfd, rbuf, sizeof(rbuf), 0, NULL, NULL)) < 0) { + if (errno == EWOULDBLOCK) break; + if (errno == EAGAIN) continue; + ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "mISDN failed to receive incoming message: %s\n", + strerror(errno)); + return FTDM_FAIL; + } + + if (retval < MISDN_HEADER_LEN) { + ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "mISDN received message too small\n"); + return FTDM_FAIL; + } + + if (hh->prim == PH_DATA_IND) { + *datalen = MIN(bytes, retval - MISDN_HEADER_LEN); + memcpy(data, rbuf + MISDN_HEADER_LEN, *datalen); +#ifdef MISDN_DEBUG_IO + if (*datalen > 0) { + char hbuf[MAX_DATA_MEM] = { 0 }; + print_hex_bytes(data, *datalen, hbuf, sizeof(hbuf)); + ftdm_log(FTDM_LOG_DEBUG, "mISDN read data: %s\n", hbuf); + } +#endif + return FTDM_SUCCESS; + } else { + *datalen = 0; + /* event */ + misdn_handle_incoming(ftdmchan, rbuf, retval); + } + } + return FTDM_SUCCESS; +} + +/** + * \brief Write data + * \param ftdmchan FreeTDM channel + * \param data Buffer for data + * \param datalen Number of bytes to write (contains bytes written after return) + */ +static FIO_WRITE_FUNCTION(misdn_write) +{ + struct misdn_chan_private *priv = ftdm_chan_io_private(ftdmchan); + char wbuf[MAX_DATA_MEM]; + struct mISDNhead *hh = (struct mISDNhead *)wbuf; + int size = *datalen; + int retval = 0; + + assert(priv); + + /* ignore empty writes */ + if (*datalen <= 0) + return FTDM_SUCCESS; + +#ifdef MISDN_DEBUG_IO + { + char hbuf[MAX_DATA_MEM] = { 0 }; + print_hex_bytes(data, *datalen, hbuf, sizeof(hbuf)); + ftdm_log(FTDM_LOG_DEBUG, "mISDN write data: %s\n", hbuf); + } +#endif + hh->prim = PH_DATA_REQ; + hh->id = MISDN_ID_ANY; + + /* avoid buffer overflow */ + size = MIN(size, MAX_DATA_MEM); + + memcpy(wbuf + MISDN_HEADER_LEN, data, size); + size += MISDN_HEADER_LEN; + +#ifdef MISDN_DEBUG_IO + ftdm_log(FTDM_LOG_DEBUG, "mISDN writing %d bytes to channel %d:%d socket %d\n", + size, ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan), ftdmchan->sockfd); +#endif + if ((retval = sendto(ftdmchan->sockfd, wbuf, size, 0, NULL, 0)) != size) { + ftdm_log(FTDM_LOG_ERROR, "mISDN channel %d:%d socket write error: %s\n", + ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan), + strerror(errno)); + return FTDM_FAIL; + } + *datalen = retval; + +// if (priv->debugfd >= 0) { +// write(priv->debugfd, wbuf + MISDN_HEADER_LEN, size - MISDN_HEADER_LEN); +// } + + priv->tx_cnt++; + return FTDM_SUCCESS; +} + + +static ftdm_status_t misdn_open_range(ftdm_span_t *span, ftdm_chan_type_t type, struct mISDN_devinfo *devinfo, int start, int end) +{ + int num_configured = 0; + int d_protocol, d_channel; + int x; + + ftdm_log(FTDM_LOG_DEBUG, "mISDN configuring card:range %d:%d->%d\n", + devinfo->id, start, end); + + switch (ftdm_span_get_trunk_type(span)) { + case FTDM_TRUNK_E1: + d_protocol = ISDN_P_TE_E1; + d_channel = 16; + break; + case FTDM_TRUNK_BRI: + case FTDM_TRUNK_BRI_PTMP: + d_protocol = ISDN_P_TE_S0; + d_channel = 0; + break; + default: + ftdm_log(FTDM_LOG_ERROR, "Unsupported span type %s\n", + ftdm_span_get_trunk_type_str(span)); + return FTDM_FAIL; + } + + for (x = start; x <= end; x++) { + struct misdn_chan_private *priv; + struct sockaddr_mISDN addr; + ftdm_channel_t *ftdmchan = NULL; + ftdm_socket_t sockfd = -1; + + ftdm_log(FTDM_LOG_DEBUG, "mISDN configuring card:channel => %d:%d\n", + devinfo->id, x); + + memset(&addr, 0, sizeof(addr)); + addr.family = AF_ISDN; + addr.dev = devinfo->id; + + switch (type) { + case FTDM_CHAN_TYPE_DQ931: /* unsupported */ + ftdm_log(FTDM_LOG_ERROR, "Unsupported channel type '%s'\n", + ftdm_chan_type2str(type)); + return FTDM_FAIL; + + case FTDM_CHAN_TYPE_DQ921: + /* No NT-mode support, since we have no idea which mode we should run in at this point */ + sockfd = socket(PF_ISDN, SOCK_DGRAM, d_protocol); + addr.channel = d_channel; /* 0 for S0 and 16 for E1 */ + break; + + case FTDM_CHAN_TYPE_B: + if (!test_channelmap(x, devinfo->channelmap)) { + ftdm_log(FTDM_LOG_ERROR, "Invalid B-Channel specified: %d\n", x); + return FTDM_FAIL; + } + sockfd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); + addr.channel = x; + break; + + default: + ftdm_log(FTDM_LOG_ERROR, "Invalid/unsupported channel type '%s' (%d)\n", + ftdm_chan_type2str(type), type); + return FTDM_FAIL; + } + + if (sockfd < 0) { + ftdm_log(FTDM_LOG_ERROR, "Failed to open socket: %s\n", strerror(errno)); + return FTDM_FAIL; + } + + ftdm_log(FTDM_LOG_DEBUG, "mISDN opened socket (on chan:dev => %d:%d): %d\n", + addr.dev, addr.channel, sockfd); + + /* set non-blocking */ + if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN Failed to set socket fd to non-blocking: %s\n", strerror(errno)); + return FTDM_FAIL; + } + + /* + * Bind socket to card:channel + */ + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + ftdm_log(FTDM_LOG_ERROR, "Failed to bind mISDN socket [%d:%d]: %s\n", + addr.dev, x, strerror(errno)); + close(sockfd); + return FTDM_FAIL; + } + + /* + * Add channel to span + */ + if (ftdm_span_add_channel(span, sockfd, type, &ftdmchan) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "Failed to add mISDN ftdmchan to span\n"); + close(sockfd); + return FTDM_FAIL; + } + + priv = calloc(1, sizeof(*priv)); + if (!priv) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to allocate channel private data\n"); + close(sockfd); + return FTDM_FAIL; + } + ftdm_chan_io_private(ftdmchan) = priv; + + priv->addr = addr; + priv->debugfd = -1; + priv->timerfd = -1; + + /* + * Create event queue + */ + misdn_event_queue_create(&priv->events); + + ftdmchan->rate = 8000; + ftdmchan->physical_span_id = devinfo->id; + ftdmchan->physical_chan_id = x; + + if (ftdmchan->type == FTDM_CHAN_TYPE_B) { + ftdmchan->packet_len = 10 /* ms */ * (ftdmchan->rate / 1000); + ftdmchan->effective_interval = ftdmchan->native_interval = ftdmchan->packet_len / 8; + ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_ALAW; + + ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL); + } else { + ftdmchan->native_codec = ftdmchan->effective_codec = FTDM_CODEC_NONE; + } + num_configured++; + } + + return num_configured; +} + +static int misdn_find_device(const char *name, int nr_devices, struct mISDN_devinfo *info) +{ + struct mISDN_devinfo devinfo; + char *endp = NULL; + int port_id = -1; + int i; + + port_id = strtoul(name, &endp, 10); + if (endp == name || errno == EINVAL) + port_id = -1; + if (port_id < 0 || port_id >= nr_devices) + port_id = -1; + + for (i = 0; i < nr_devices; i++) { + memset(&devinfo, 0, sizeof(devinfo)); + devinfo.id = i; + + if (ioctl(globals.sockfd, IMGETDEVINFO, &devinfo) < 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN unable to get device %d info: %s\n", + devinfo.id, strerror(errno)); + return FTDM_FAIL; + } + if (devinfo.id == port_id) + break; + if (strlen(devinfo.name) <= 0) + continue; + if (!strcasecmp(devinfo.name, name)) + break; + } + if (i == nr_devices) + return FTDM_FAIL; + + if (info) *info = devinfo; + return FTDM_SUCCESS; +} + +#define MISDN_PH_TE_PROTOCOLS(x) \ + ((x) & ((1 << ISDN_P_TE_S0) | (1 << ISDN_P_TE_E1) | (1 << ISDN_P_TE_UP0))) +#define MISDN_PH_NT_PROTOCOLS(x) \ + ((x) & ((1 << ISDN_P_NT_S0) | (1 << ISDN_P_NT_E1) | (1 << ISDN_P_NT_UP0))) + +/** + * \brief Configure/open span ftmod_misdn settings + */ +static FIO_CONFIGURE_SPAN_FUNCTION(misdn_configure_span) +{ + struct misdn_span_private *span_priv = ftdm_span_io_private(span); + struct mISDN_devinfo devinfo; + int range_start = 0, range_end = 0; + int nr_ports = 0, nr_items = 0; + int res = 0, i; + char *chan_str, *ptr; + char *data = strdup(str); + char *item_list[10]; + + /* only these are supported */ + switch (ftdm_span_get_trunk_type(span)) { + case FTDM_TRUNK_E1: + case FTDM_TRUNK_BRI: + case FTDM_TRUNK_BRI_PTMP: + break; + default: + ftdm_log(FTDM_LOG_ERROR, "Unsupported span type %s\n", + ftdm_span_get_trunk_type_str(span)); + return FTDM_FAIL; + } + + /* get port count */ + if (ioctl(globals.sockfd, IMGETCOUNT, &nr_ports) < 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN unable to get port count: %s\n", + strerror(errno)); + goto error; + } + if (nr_ports <= 0) { + ftdm_log(FTDM_LOG_ERROR, "No mISDN devices found\n"); + goto error; + } + + /* split configuration string into port ID and channel list */ + if (!(chan_str = strchr(data, ':'))) { + ftdm_log(FTDM_LOG_ERROR, "Invalid configuration string: %s\nExpected format :[-]\n", str); + goto error; + } + *chan_str++ = '\0'; + + /* lookup port id, by number first, then by name */ + if (misdn_find_device(data, nr_ports, &devinfo) != FTDM_SUCCESS) { + ftdm_log(FTDM_LOG_ERROR, "No such mISDN device/port: %s\n", + data); + goto error; + } + if (devinfo.nrbchan == 0 || devinfo.channelmap == 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN device '%s' has no b-channels\n", + data); + goto error; + } + if (!MISDN_PH_TE_PROTOCOLS(devinfo.Dprotocols)) { + ftdm_log(FTDM_LOG_ERROR, "mISDN device '%s' does not support any ISDN TE modes\n", + data); + goto error; + } + + /* allocate span private */ + if (!span_priv) { + /* + * Not perfect, there should be something like span_create too + */ + span_priv = calloc(1, sizeof(*span_priv)); + if (!span_priv) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to allocate span private data\n"); + return FTDM_FAIL; + } + ftdm_span_io_private(span) = span_priv; + + /* init event condition */ + pthread_cond_init(&span_priv->event_cond, NULL); + pthread_mutex_init(&span_priv->event_cond_mutex, NULL); + } + + /* split channel list by ',' */ + nr_items = ftdm_separate_string(chan_str, ',', item_list, ftdm_array_len(item_list)); + + for (i = 0; i < nr_items; i++) { + /* */ + if (!(ptr = strchr(item_list[i], '-'))) { + /* single channel */ + range_start = atoi(item_list[i]); + range_end = range_start; + } else { + *ptr++ = '\0'; + /* channel range */ + range_start = atoi(item_list[i]); + range_end = atoi(ptr); + } + + /* check if channel range/id is valid */ + if (range_start <= 0 || range_end <= 0 || range_end < range_start) { + ftdm_log(FTDM_LOG_ERROR, "Invalid configuration string: %s\n", + item_list[i]); + goto error; + } + + /* add range to span */ + res = misdn_open_range(span, type, &devinfo, range_start, range_end); + if (res <= 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to configure channel(s)\n"); + goto error; + } + } + + ftdm_safe_free(data); + return res; +error: + ftdm_span_io_private(span) = NULL; + ftdm_safe_free(span_priv); + ftdm_safe_free(data); + return res; +} + +/** + * \brief Configure global ftmod_misdn settings + */ +static FIO_CONFIGURE_FUNCTION(misdn_configure) +{ + return FTDM_SUCCESS; +} + +/** + * \brief Retrieve alarm event information (if any) + * \param ftdmchan FreeTDM channel + */ +static FIO_GET_ALARMS_FUNCTION(misdn_get_alarms) +{ +#if 0 +/* + Nope, this won't work... + + There's no way to create a separate "control" socket for a device + that can be used to send / receive MPH_INFORMATION_REQ/_IND without + having to care about PH_* messages in between... + + ... well, unless we use our own event loop (= thread) and + add event queues and data fifos, so we can sift all the + messages we get to forward them to the right receiver +*/ + ftdm_span_t *span = ftdm_channel_get_span(ftdmchan); + struct misdn_span_private *span_priv = ftdm_span_io_private(span); + char buf[MAX_DATA_MEM] = { 0 }; + struct sockaddr_mISDN addr; + struct mISDNhead *hh; + struct ph_info *phi = NULL; + struct pollfd pfd; + socklen_t addrlen = sizeof(addr); + int retval; + + /* use the global socket to query alarms */ + ftdm_log(FTDM_LOG_DEBUG, "mISDN getting alarms for channel %d:%d [%d:%d]\n", + ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan), + ftdm_channel_get_ph_span_id(ftdmchan), ftdm_channel_get_ph_id(ftdmchan)); + + memset(&addr, 0, sizeof(addr)); + addr.family = AF_ISDN; + addr.dev = ftdm_channel_get_ph_span_id(ftdmchan) - 1; + addr.channel = ftdm_channel_get_ph_id(ftdmchan) - 1; + + hh = (struct mISDNhead *)buf; + hh->prim = MPH_INFORMATION_REQ; + hh->id = MISDN_ID_ANY; + + /* */ + if ((retval = sendto(span_priv->ctrlsock, hh, sizeof(*hh), 0, (struct sockaddr *)&addr, addrlen)) <= 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to send '%s' to channel %d:%d: %s\n", + misdn_event2str(hh->prim), ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan), strerror(errno)); + return FTDM_FAIL; + } + + pfd.fd = span_priv->ctrlsock; + pfd.events = POLLIN /*| POLLPRI*/; + pfd.revents = 0; + + if ((retval = poll(&pfd, 1, -1)) <= 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to poll for '%s' answer on channel %d:%d: %s\n", + misdn_event2str(hh->prim), ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan), strerror(errno)); + return FTDM_FAIL; + } + + if (!(pfd.revents & (POLLIN | POLLPRI))) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to poll for '%s' answer on channel %d:%d: %s\n", + misdn_event2str(hh->prim), ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan), "No read/pri flag"); + return FTDM_FAIL; + } + + if ((retval = recvfrom(span_priv->ctrlsock, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &addrlen)) < 0) { + ftdm_log(FTDM_LOG_ERROR, "mISDN failed to receive answer for '%s' on channel %d:%d: %s\n", + misdn_event2str(hh->prim), ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan), strerror(errno)); + return FTDM_FAIL; + } + if (retval < MISDN_HEADER_LEN) { + ftdm_log(FTDM_LOG_ERROR, "mISDN short read on channel %d:%d\n", + ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan)); + return FTDM_FAIL; + } + + switch (hh->prim) { + case MPH_INFORMATION_IND: + ftdm_log(FTDM_LOG_DEBUG, "mISDN received '%s' on channel %d:%d, size %d bytes\n", + misdn_event2str(hh->prim), ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan), retval); + break; + default: + ftdm_log(FTDM_LOG_ERROR, "mISDN received unexpected answer '%s' on channel %d:%d: %s\n", + misdn_event2str(hh->prim), ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan), strerror(errno)); + return FTDM_FAIL; + } +#endif + return FTDM_SUCCESS; +} + + +/** + * \brief Poll for new events + * \param span FreeTDM span + * \param ms Timeout (in ms) + */ +static FIO_SPAN_POLL_EVENT_FUNCTION(misdn_poll_event) +{ + struct misdn_span_private *span_priv = ftdm_span_io_private(span); + struct timespec ts; + int retval = 0, nr_events = 0; + int i; + + clock_gettime(CLOCK_REALTIME, &ts); + ts_add_msec(&ts, ms); + + for (i = 1; i <= ftdm_span_get_chan_count(span); i++) { + ftdm_channel_t *chan = ftdm_span_get_channel(span, i); + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(chan); + + if (misdn_event_queue_has_data(chan_priv->events)) { +#ifdef MISDN_DEBUG_EVENTS + ftdm_log(FTDM_LOG_DEBUG, "mISDN channel %d:%d has event(s)\n", + ftdm_channel_get_span_id(chan), ftdm_channel_get_id(chan)); +#endif + ftdm_set_flag(chan, FTDM_CHANNEL_IO_EVENT); + chan->last_event_time = ftdm_current_time_in_ms(); + nr_events++; + } + } + if (nr_events) + return FTDM_SUCCESS; + + if ((retval = pthread_cond_timedwait(&span_priv->event_cond, &span_priv->event_cond_mutex, &ts))) { + switch (retval) { + case ETIMEDOUT: +// ftdm_log(FTDM_LOG_DEBUG, "mISDN span %d: No events within %d ms\n", +// ftdm_span_get_id(span), ms); + return FTDM_TIMEOUT; + default: + ftdm_log(FTDM_LOG_DEBUG, "mISDN failed to poll for events on span %d: %s\n", + ftdm_span_get_id(span), strerror(retval)); + return FTDM_FAIL; + } + } + + for (i = 1; i <= ftdm_span_get_chan_count(span); i++) { + ftdm_channel_t *chan = ftdm_span_get_channel(span, i); + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(chan); + + if (misdn_event_queue_has_data(chan_priv->events)) { + ftdm_set_flag(chan, FTDM_CHANNEL_IO_EVENT); + chan->last_event_time = ftdm_current_time_in_ms(); + nr_events++; + } + } + return (nr_events) ? FTDM_SUCCESS : FTDM_TIMEOUT; /* no events? => timeout */ +} + +/** + * \brief Retrieve event + * \param span FreeTDM span + * \param event FreeTDM event + */ +static FIO_SPAN_NEXT_EVENT_FUNCTION(misdn_next_event) +{ + int32_t event_id = FTDM_OOB_INVALID; + int i; + + ftdm_log(FTDM_LOG_DEBUG, "Reading next event from span %d\n", + ftdm_span_get_id(span)); + + for (i = 1; i <= ftdm_span_get_chan_count(span); i++) { + ftdm_channel_t *chan = ftdm_span_get_channel(span, i); + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(chan); + struct misdn_event *evt = NULL; + + if (!(evt = misdn_event_queue_pop(chan_priv->events))) { +#ifdef MISDN_DEBUG_EVENTS + ftdm_log_chan_msg(chan, FTDM_LOG_DEBUG, "mISDN channel event queue has no events\n"); +#endif + ftdm_clear_io_flag(chan, FTDM_CHANNEL_IO_EVENT); + continue; + } + +#ifdef MISDN_DEBUG_EVENTS + ftdm_log_chan(chan, FTDM_LOG_DEBUG, "Got event '%s' from channel event queue\n", + misdn_event2str(evt->id)); +#endif + switch (evt->id) { + case PH_DEACTIVATE_IND: + event_id = FTDM_OOB_ALARM_TRAP; + chan->alarm_flags |= FTDM_ALARM_RED; + break; + case PH_ACTIVATE_IND: + event_id = FTDM_OOB_ALARM_CLEAR; + chan->alarm_flags &= ~FTDM_ALARM_RED; + break; + default: + ftdm_log(FTDM_LOG_ERROR, "Unhandled event id %d (0x%x) %s\n", + evt->id, evt->id, misdn_event2str(evt->id)); + continue; + } + + chan->last_event_time = 0; + span->event_header.e_type = FTDM_EVENT_OOB; + span->event_header.enum_id = event_id; + span->event_header.channel = chan; + *event = &span->event_header; + return FTDM_SUCCESS; + } + return FTDM_FAIL; +} + +/** + * \brief Shutdown ftmod_misdn channel + * \param ftdmchan FreeTDM channel + */ +static FIO_CHANNEL_DESTROY_FUNCTION(misdn_channel_destroy) +{ + struct misdn_chan_private *chan_priv = ftdm_chan_io_private(ftdmchan); + assert(chan_priv); + + ftdm_log(FTDM_LOG_DEBUG, "Destroying channel %d:%d\n", + ftdm_channel_get_span_id(ftdmchan), + ftdm_channel_get_id(ftdmchan)); + + if (ftdmchan->sockfd >= 0) { + close(ftdmchan->sockfd); + ftdmchan->sockfd = -1; + } + + /* + * Destroy fifo + event queue + */ + if (chan_priv->events) + misdn_event_queue_destroy(&chan_priv->events); + + ftdm_chan_io_private(ftdmchan) = NULL; + ftdm_safe_free(chan_priv); + + ftdm_log(FTDM_LOG_DEBUG, "mISDN channel %d:%d destroyed\n", + ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan)); + return FTDM_SUCCESS; +} + +/** + * \brief Shutdown ftmod_misdn span + * \param span FreeTDM span + */ +static FIO_SPAN_DESTROY_FUNCTION(misdn_span_destroy) +{ + struct misdn_span_private *span_priv = ftdm_span_io_private(span); + + ftdm_span_io_private(span) = NULL; + ftdm_safe_free(span_priv); + + ftdm_log(FTDM_LOG_DEBUG, "mISDN span %d (%s) destroyed\n", + ftdm_span_get_id(span), ftdm_span_get_name(span)); + return FTDM_SUCCESS; +} + + +static ftdm_status_t misdn_handle_incoming(ftdm_channel_t *ftdmchan, const char *rbuf, const int size) +{ + struct mISDNhead *hh = (struct mISDNhead *)rbuf; + struct misdn_chan_private *priv = ftdm_chan_io_private(ftdmchan); + const char *data = rbuf + sizeof(*hh); + int data_len = size - sizeof(*hh); + + assert(priv); + +#ifdef MISDN_DEBUG_EVENTS + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "mISDN channel received '%s' message (additional data: %d bytes)\n", + misdn_event2str(hh->prim), data_len); +#endif + + switch (hh->prim) { + /* data events */ + case PH_DATA_CNF: /* TX ack */ + priv->tx_ack_cnt++; + break; + case PH_DATA_REQ: /* request echo? */ + break; + case PH_DATA_E_IND: /* TX/RX ERR(?) */ + break; + + /* control events */ + case PH_ACTIVATE_REQ: + case PH_DEACTIVATE_REQ: + /* + * Echoed(?) L2->L1 requests, ignore... + * (something broken in mISDN or the way we setup the channel?) + */ + break; + case PH_CONTROL_IND: + return misdn_handle_ph_control_ind(ftdmchan, hh, data, data_len); + case PH_CONTROL_REQ: + case PH_CONTROL_CNF: + break; + + case MPH_INFORMATION_IND: + return misdn_handle_mph_information_ind(ftdmchan, hh, data, data_len); + + case PH_ACTIVATE_IND: + case PH_DEACTIVATE_IND: + { + /* other events, enqueue and let misdn_event_next handle it */ + struct misdn_span_private *span_priv = ftdm_span_io_private(ftdmchan->span); + struct misdn_event evt = { 0 }; + evt.id = hh->prim; + + misdn_event_queue_push(priv->events, &evt); + + /* wake possible readers */ + pthread_cond_signal(&span_priv->event_cond); + } + break; + + default: /* error? */ + ftdm_log(FTDM_LOG_DEBUG, "mISDN channel %d:%d received unknown event %d\n", + ftdm_channel_get_span_id(ftdmchan), ftdm_channel_get_id(ftdmchan), hh->prim); + break; + } + return FTDM_SUCCESS; +} + + +/** + * \brief ftmod_misdn interface + */ +//static const ftdm_io_interface_t misdn_interface = { +static const ftdm_io_interface_t misdn_interface = { + .name = "misdn", + + .open = misdn_open, + .close = misdn_close, + .wait = misdn_wait, + .read = misdn_read, + .write = misdn_write, + + .poll_event = misdn_poll_event, + .next_event = misdn_next_event, + + .command = misdn_command, + .get_alarms = misdn_get_alarms, + .configure = misdn_configure, /* configure global parameters */ + .configure_span = misdn_configure_span, /* assign channels to span */ + .channel_destroy = misdn_channel_destroy, /* clean up channel */ + .span_destroy = misdn_span_destroy, /* clean up span */ +}; + + +/** + * \brief ftmod_misdn module init function + */ +static FIO_IO_LOAD_FUNCTION(misdn_load) +{ + struct mISDNversion ver; + struct mISDN_devinfo devinfo; + int devcnt, usecnt; + int i; + + /* */ + globals.sockfd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (globals.sockfd < 0) { + ftdm_log(FTDM_LOG_CRIT, "Unable to create mISDN base socket (are you sure this kernel has mISDN support?)\n"); + return FTDM_FAIL; + } + + if (ioctl(globals.sockfd, IMGETVERSION, &ver) < 0) { + ftdm_log(FTDM_LOG_CRIT, "Unable to retrieve mISDN version\n"); + goto error; + } + + ftdm_log(FTDM_LOG_INFO, "mISDN Interface version %hhd.%hhd.%hd\n", ver.major, ver.minor, ver.release); + + devcnt = 0; + if (ioctl(globals.sockfd, IMGETCOUNT, &devcnt) < 0) { + ftdm_log(FTDM_LOG_CRIT, "Unable to retrieve number of mISDN devices\n"); + goto error; + + } + + if (!devcnt) { + ftdm_log(FTDM_LOG_CRIT, "No mISDN devices found\n"); + goto error; + } + usecnt = devcnt; + + ftdm_log(FTDM_LOG_INFO, "Found %d mISDN devices:\n", devcnt); + + /* Output most important device information */ + for (i = 0; i < devcnt; i++) { + int caps = MISDN_CAPS_NONE; + + devinfo.id = i; + if (ioctl(globals.sockfd, IMGETDEVINFO, &devinfo) < 0) { + ftdm_log(FTDM_LOG_ERROR, "Failed to retrieve information for device %d\n", i); + continue; + } + + /* print */ + ftdm_log(FTDM_LOG_INFO, "<%d> Name: %s, B-Channels: %d\n", + devinfo.id, + ftdm_strlen_zero_buf(devinfo.name) ? "Unknown" : devinfo.name, + devinfo.nrbchan); + + /* D-Channels capabilities */ + if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) + caps |= MISDN_CAPS_TE | MISDN_CAPS_PRI; + if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1)) + caps |= MISDN_CAPS_NT | MISDN_CAPS_PRI; + if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) + caps |= MISDN_CAPS_TE | MISDN_CAPS_BRI; + if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0)) + caps |= MISDN_CAPS_NT | MISDN_CAPS_BRI; +#ifdef ISDN_P_TE_UP0 + if (devinfo.Dprotocols & (1 << ISDN_P_TE_UP0)) + caps |= MISDN_CAPS_TE | MISDN_CAPS_UP0 | MISDN_CAPS_BRI; +#endif +#ifdef ISDN_P_NT_UP0 + if (devinfo.Dprotocols & (1 << ISDN_P_NT_UP0)) + caps |= MISDN_CAPS_NT | MISDN_CAPS_UP0 | MISDN_CAPS_BRI; +#endif + /* B-Channel capabilities */ + if (devinfo.Bprotocols & (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK))) + caps |= MISDN_CAPS_RAW; + if (devinfo.Bprotocols & (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK))) + caps |= MISDN_CAPS_HDLC; + + ftdm_log(FTDM_LOG_INFO, " Type: %s, Modes: %s %s\n", + MISDN_IS_PRI(caps) ? "PRI" : "BRI", + MISDN_IS_NT(caps) ? "NT" : "", + MISDN_IS_TE(caps) ? "TE" : ""); + + ftdm_log(FTDM_LOG_INFO, " B-Channel modes: %s %s\n", + MISDN_IS_RAW(caps) ? "RAW" : "", + MISDN_IS_HDLC(caps) ? "HDLC" : ""); + + if (!(MISDN_IS_NT(caps) || MISDN_IS_TE(caps)) && !MISDN_IS_RAW(caps)) { + ftdm_log(FTDM_LOG_ERROR, " This device is unusable!\n"); + usecnt--; + } + } + if (!usecnt) { + ftdm_log(FTDM_LOG_CRIT, "No useable devices found!\n"); + goto error; + } + + ftdm_log(FTDM_LOG_INFO, "Found %d useable mISDN devices\n", usecnt); + + /* assign interface struct */ + *fio = (ftdm_io_interface_t *)&misdn_interface; + return FTDM_SUCCESS; +error: + if (globals.sockfd >= 0) + close(globals.sockfd); + return FTDM_FAIL; +} + +/** + * \brief ftmod_misdn module shutdown + */ +static FIO_IO_UNLOAD_FUNCTION(misdn_unload) +{ + if (globals.sockfd >= 0) + close(globals.sockfd); + return FTDM_SUCCESS; +} + +/** + * \brief ftmod_misdn module + */ +ftdm_module_t ftdm_module = { + .name = "misdn", + .io_load = misdn_load, + .io_unload = misdn_unload +}; diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c index c2305e445f..add4d3fad0 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_out.c @@ -69,6 +69,8 @@ void sngisdn_snd_setup(ftdm_channel_t *ftdmchan) set_calling_subaddr(ftdmchan, &conEvnt.cgPtySad); set_redir_num(ftdmchan, &conEvnt.redirNmb); set_calling_name(ftdmchan, &conEvnt); + + /* set_facility_ie will overwrite Calling Name for NI-2 if user specifies custom Facility IE */ set_facility_ie(ftdmchan, &conEvnt.facilityStr); set_prog_ind_ie(ftdmchan, &conEvnt.progInd, prog_ind); diff --git a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c index 9a633b902a..d744ad2942 100644 --- a/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c +++ b/libs/freetdm/src/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c @@ -719,12 +719,19 @@ ftdm_status_t set_calling_name(ftdm_channel_t *ftdmchan, ConEvnt *conEvnt) } else { switch (signal_data->switchtype) { case SNGISDN_SWITCH_NI2: - /* TODO: Need to send the caller ID as a facility IE */ - +#ifdef SNGISDN_SUPPORT_CALLING_NAME_IN_FACILITY + { + if (signal_data->signalling == SNGISDN_SIGNALING_NET) { + sng_isdn_encode_facility_caller_name(caller_data->cid_name, conEvnt->facilityStr.facilityStr.val, &conEvnt->facilityStr.facilityStr.len); + conEvnt->facilityStr.eh.pres = PRSNT_NODEF; + conEvnt->facilityStr.facilityStr.pres = PRSNT_NODEF; + } + } +#endif break; case SNGISDN_SWITCH_EUROISDN: if (signal_data->signalling != SNGISDN_SIGNALING_NET) { - break; + break; } /* follow through */ case SNGISDN_SWITCH_5ESS: diff --git a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c index 6c945e1512..d0d66205a5 100644 --- a/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c +++ b/libs/freetdm/src/ftmod/ftmod_wanpipe/ftmod_wanpipe.c @@ -39,6 +39,11 @@ * */ +#ifdef WP_DEBUG_IO +#define _BSD_SOURCE +#include +#endif + #ifdef __sun #include #include @@ -99,6 +104,17 @@ static struct { uint32_t ring_off_ms; } wp_globals; +typedef struct { + sangoma_wait_obj_t *waitobj; +#ifdef WP_DEBUG_IO + /* record the last reader threads */ + pid_t readers[10]; + int rindex; + ftdm_time_t last_read; +#endif +} wp_channel_t; +#define WP_GET_WAITABLE(fchan) ((wp_channel_t *)((fchan)->io_data))->waitobj + /* a bunch of this stuff should go into the wanpipe_tdm_api_iface.h */ FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event); @@ -123,7 +139,7 @@ static __inline__ int tdmv_api_wait_socket(ftdm_channel_t *ftdmchan, int timeout int err; uint32_t inflags = *flags; uint32_t outflags = 0; - sangoma_wait_obj_t *sangoma_wait_obj = ftdmchan->io_data; + sangoma_wait_obj_t *sangoma_wait_obj = WP_GET_WAITABLE(ftdmchan); if (timeout == -1) { timeout = SANGOMA_WAIT_INFINITE; @@ -254,9 +270,13 @@ static unsigned wp_open_range(ftdm_span_t *span, unsigned spanno, unsigned start } if (ftdm_span_add_channel(span, sockfd, type, &chan) == FTDM_SUCCESS) { + wp_channel_t *wpchan = NULL; wanpipe_tdm_api_t tdm_api; memset(&tdm_api, 0, sizeof(tdm_api)); #ifdef LIBSANGOMA_VERSION + wpchan = ftdm_calloc(1, sizeof(*wpchan)); + ftdm_assert(wpchan != NULL, "wpchan alloc failed\n"); + chan->io_data = wpchan; /* we need SANGOMA_DEVICE_WAIT_OBJ_SIG and not SANGOMA_DEVICE_WAIT_OBJ alone because we need to call * sangoma_wait_obj_sig to wake up any I/O waiters when closing the channel (typically on ftdm shutdown) * this adds an extra pair of file descriptors to the waitable object @@ -266,7 +286,7 @@ static unsigned wp_open_range(ftdm_span_t *span, unsigned spanno, unsigned start ftdm_log(FTDM_LOG_ERROR, "failure create waitable object for s%dc%d\n", spanno, x); continue; } - chan->io_data = sangoma_wait_obj; + WP_GET_WAITABLE(chan) = sangoma_wait_obj; #endif chan->physical_span_id = spanno; @@ -580,9 +600,16 @@ static FIO_OPEN_FUNCTION(wanpipe_open) static FIO_CLOSE_FUNCTION(wanpipe_close) { #ifdef LIBSANGOMA_VERSION - sangoma_wait_obj_t *waitobj = ftdmchan->io_data; + sangoma_wait_obj_t *waitobj = WP_GET_WAITABLE(ftdmchan); /* kick any I/O waiters */ sangoma_wait_obj_signal(waitobj); +#ifdef WP_DEBUG_IO + { + wp_channel_t *wchan = ftdmchan->io_data; + memset(wchan->readers, 0, sizeof(wchan->readers)); + wchan->rindex = 0; + } +#endif #endif return FTDM_SUCCESS; } @@ -860,7 +887,7 @@ static void wanpipe_write_stats(ftdm_channel_t *ftdmchan, wp_tdm_api_tx_hdr_t *t /* HDLC channels do not always transmit, so its ok for drivers to fill with idle * also do not report idle warning when we just started transmitting */ if (ftdmchan->iostats.tx.packets && FTDM_IS_VOICE_CHANNEL(ftdmchan)) { - ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Tx idle changed from %d to %d\n", + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Tx idle changed from %d to %d\n", ftdmchan->iostats.tx.idle_packets, tx_stats->wp_api_tx_hdr_tx_idle_packets); } ftdmchan->iostats.tx.idle_packets = tx_stats->wp_api_tx_hdr_tx_idle_packets; @@ -913,21 +940,21 @@ static void wanpipe_read_stats(ftdm_channel_t *ftdmchan, wp_tdm_api_rx_hdr_t *rx } if (ftdmchan->iostats.rx.queue_len >= (0.8 * ftdmchan->iostats.rx.queue_size)) { - ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Rx Queue length exceeded 80% threshold (%d/%d)\n", + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Rx Queue length exceeded 80% threshold (%d/%d)\n", ftdmchan->iostats.rx.queue_len, ftdmchan->iostats.rx.queue_size); ftdm_set_flag(&(ftdmchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_THRES); } else if (ftdm_test_flag(&(ftdmchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_THRES)){ - ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Rx Queue length reduced 80% threshold (%d/%d)\n", + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Rx Queue length reduced 80% threshold (%d/%d)\n", ftdmchan->iostats.rx.queue_len, ftdmchan->iostats.rx.queue_size); ftdm_clear_flag(&(ftdmchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_THRES); } if (ftdmchan->iostats.rx.queue_len >= ftdmchan->iostats.rx.queue_size) { - ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "Rx Queue Full (%d/%d)\n", + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Rx Queue Full (%d/%d)\n", ftdmchan->iostats.rx.queue_len, ftdmchan->iostats.rx.queue_size); ftdm_set_flag(&(ftdmchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_FULL); } else if (ftdm_test_flag(&(ftdmchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_FULL)){ - ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Rx Queue no longer full (%d/%d)\n", + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Rx Queue no longer full (%d/%d)\n", ftdmchan->iostats.rx.queue_len, ftdmchan->iostats.rx.queue_size); ftdm_clear_flag(&(ftdmchan->iostats.rx), FTDM_IOSTATS_ERROR_QUEUE_FULL); } @@ -950,10 +977,44 @@ static void wanpipe_read_stats(ftdm_channel_t *ftdmchan, wp_tdm_api_rx_hdr_t *rx static FIO_READ_FUNCTION(wanpipe_read) { int rx_len = 0; + int rq_len = (int)*datalen; wp_tdm_api_rx_hdr_t hdrframe; - memset(&hdrframe, 0, sizeof(hdrframe)); +#ifdef WP_DEBUG_IO + wp_channel_t *wchan = ftdmchan->io_data; + ftdm_time_t time_diff = 0; + pid_t previous_thread = 1; + pid_t current_thread = 0; + int previous_thread_index = 0; + previous_thread_index = wchan->rindex == 0 ? (ftdm_array_len(wchan->readers) - 1) : wchan->rindex - 1; + previous_thread = wchan->readers[previous_thread_index]; + current_thread = syscall(SYS_gettid); + if (current_thread && current_thread != wchan->readers[wchan->rindex]) { + if (!wchan->readers[wchan->rindex]) { + wchan->readers[wchan->rindex] = current_thread; + /* first read */ + ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Initial reader thread is %d\n", current_thread); + previous_thread = current_thread; + } else { + previous_thread = wchan->readers[wchan->rindex]; + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Changed reader thread from %d to %d (rindex = %d)\n", + previous_thread, current_thread, wchan->rindex); + if (wchan->rindex == (ftdm_array_len(wchan->readers) - 1)) { + wchan->rindex = 0; + } else { + wchan->rindex++; + } + wchan->readers[wchan->rindex] = current_thread; + } + } + ftdm_time_t curr = ftdm_current_time_in_ms(); + if (wchan->last_read) { + time_diff = curr - wchan->last_read; + } +#endif + + memset(&hdrframe, 0, sizeof(hdrframe)); rx_len = sangoma_readmsg_tdm(ftdmchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (int)*datalen, 0); *datalen = rx_len; @@ -963,14 +1024,20 @@ static FIO_READ_FUNCTION(wanpipe_read) } if (rx_len < 0) { - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "%s", strerror(errno)); - ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Failed to read from sangoma device: %s (%d)\n", strerror(errno), rx_len); +#ifdef WP_DEBUG_IO + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Failed to read %d bytes from sangoma device: %s (%d) " + "(read time diff = %llums, prev thread = %d, curr thread = %d)\n", rq_len, strerror(errno), rx_len, + time_diff, previous_thread, current_thread); +#else + ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Failed to read %d bytes from sangoma device: %s (%d)\n", rq_len, strerror(errno), rx_len); +#endif return FTDM_FAIL; } if (ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS)) { wanpipe_read_stats(ftdmchan, &hdrframe); } + return FTDM_SUCCESS; } @@ -1116,7 +1183,7 @@ FIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event) if (!ftdmchan->io_data) { continue; /* should never happen but happens when shutting down */ } - pfds[j] = ftdmchan->io_data; + pfds[j] = WP_GET_WAITABLE(ftdmchan); inflags[j] = chan_events; #else memset(&pfds[j], 0, sizeof(pfds[j])); @@ -1578,10 +1645,10 @@ static FIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy) { #ifdef LIBSANGOMA_VERSION if (ftdmchan->io_data) { - sangoma_wait_obj_t *sangoma_wait_obj; - sangoma_wait_obj = ftdmchan->io_data; - ftdmchan->io_data = NULL; + sangoma_wait_obj_t *sangoma_wait_obj = WP_GET_WAITABLE(ftdmchan); sangoma_wait_obj_delete(&sangoma_wait_obj); + ftdm_safe_free(ftdmchan->io_data); + ftdmchan->io_data = NULL; } #endif @@ -1612,7 +1679,8 @@ static FIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy) */ static FIO_IO_LOAD_FUNCTION(wanpipe_init) { - assert(fio != NULL); + ftdm_assert(fio != NULL, "fio should not be null\n"); + memset(&wanpipe_interface, 0, sizeof(wanpipe_interface)); wp_globals.codec_ms = 20; diff --git a/libs/freetdm/src/include/private/libteletone.h b/libs/freetdm/src/include/private/libteletone.h index dc98c8cb6c..b039f1f078 100644 --- a/libs/freetdm/src/include/private/libteletone.h +++ b/libs/freetdm/src/include/private/libteletone.h @@ -4,10 +4,10 @@ * * Version: MPL 1.1 * - * The contents of this file are subject to the Mftilla Public License Version + * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * http://www.mftilla.org/MPL/ + * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License @@ -33,8 +33,8 @@ * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the freetdm library. Any use or distribution of this - * source code outside the scope of the freetdm library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II diff --git a/libs/freetdm/src/include/private/libteletone_detect.h b/libs/freetdm/src/include/private/libteletone_detect.h index 71bf067514..28ae2f5492 100644 --- a/libs/freetdm/src/include/private/libteletone_detect.h +++ b/libs/freetdm/src/include/private/libteletone_detect.h @@ -2,13 +2,60 @@ * libteletone * Copyright (C) 2005-2011, Anthony Minessale II * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is tone_detect.c - General telephony tone detection, and specific detection of DTMF. + * + * + * The Initial Developer of the Original Code is + * Stephen Underwood + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * The the original interface designed by Steve Underwood was preserved to retain + *the optimizations when considering DTMF tones though the names were changed in the interest + * of namespace. + * + * Much less efficient expansion interface was added to allow for the detection of + * a single arbitrary tone combination which may also exceed 2 simultaneous tones. + * (controlled by compile time constant TELETONE_MAX_TONES) + * + * Copyright (C) 2006 Anthony Minessale II + * + * * libteletone_detect.c Tone Detection Code * + * + ********************************************************************************* + * + * Derived from tone_detect.h - General telephony tone detection, and specific + * detection of DTMF. + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the freetdm library. Any use or distribution of this - * source code outside the scope of the freetdm library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II @@ -41,20 +88,6 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ********************************************************************************* - * - * Derived from tone_detect.h - General telephony tone detection, and specific - * detection of DTMF. - * - * Copyright (C) 2001 Steve Underwood - * - * Despite my general liking of the GPL, I place this code in the - * public domain for the benefit of all mankind - even the slimy - * ones who might try to proprietize my work and use it to my - * detriment. - * - * */ #ifndef LIBTELETONE_DETECT_H @@ -101,6 +134,14 @@ extern "C" { #define BLOCK_LEN 102 #define M_TWO_PI 2.0*M_PI + typedef enum { + TT_HIT_NONE = 0, + TT_HIT_BEGIN = 1, + TT_HIT_MIDDLE = 2, + TT_HIT_END = 3 + } teletone_hit_type_t; + + /*! \brief A continer for the elements of a Goertzel Algorithm (The names are from his formula) */ typedef struct { float v2; @@ -114,16 +155,19 @@ extern "C" { int hit2; int hit3; int hit4; - int mhit; + int dur; + int zc; + teletone_goertzel_state_t row_out[GRID_FACTOR]; teletone_goertzel_state_t col_out[GRID_FACTOR]; teletone_goertzel_state_t row_out2nd[GRID_FACTOR]; teletone_goertzel_state_t col_out2nd[GRID_FACTOR]; float energy; + float lenergy; int current_sample; - char digits[TELETONE_MAX_DTMF_DIGITS + 1]; + char digit; int current_digits; int detected_digits; int lost_digits; @@ -196,7 +240,7 @@ TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf \param samples the number of samples present in sample_buffer \return true when DTMF was detected or false when it is not */ -TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, +TELETONE_API(teletone_hit_type_t) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, int16_t sample_buffer[], int samples); /*! @@ -206,9 +250,7 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec \param max the maximum length of buf \return the number of characters written to buf */ -TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, - char *buf, - int max); +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, char *buf, unsigned int *dur); /*! \brief Step through the Goertzel Algorithm for each sample in a buffer diff --git a/libs/freetdm/src/include/private/libteletone_generate.h b/libs/freetdm/src/include/private/libteletone_generate.h index 6d71694761..8e49afaabe 100644 --- a/libs/freetdm/src/include/private/libteletone_generate.h +++ b/libs/freetdm/src/include/private/libteletone_generate.h @@ -2,6 +2,41 @@ * libteletone * Copyright (C) 2005-2011, Anthony Minessale II * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libteletone + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * libteletone.h -- Tone Generator + * + * + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the + * following license and reinact the MPL 1.1 as stated above. + * * Copyright (c) 2007, Anthony Minessale II * All rights reserved. * diff --git a/libs/freetdm/src/libteletone_detect.c b/libs/freetdm/src/libteletone_detect.c index 0436c25953..0d0fd27eec 100644 --- a/libs/freetdm/src/libteletone_detect.c +++ b/libs/freetdm/src/libteletone_detect.c @@ -2,14 +2,62 @@ * libteletone * Copyright (C) 2005-2011, Anthony Minessale II * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is tone_detect.c - General telephony tone detection, and specific detection of DTMF. + * + * + * The Initial Developer of the Original Code is + * Stephen Underwood + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * The the original interface designed by Steve Underwood was preserved to retain + *the optimizations when considering DTMF tones though the names were changed in the interest + * of namespace. + * * Much less efficient expansion interface was added to allow for the detection of * a single arbitrary tone combination which may also exceed 2 simultaneous tones. * (controlled by compile time constant TELETONE_MAX_TONES) * * Copyright (C) 2006 Anthony Minessale II * + * * libteletone_detect.c Tone Detection Code * + * + ********************************************************************************* + * + * Derived from tone_detect.c - General telephony tone detection, and specific + * detection of DTMF. + * + * Copyright (C) 2001 Steve Underwood + * + * Despite my general liking of the GPL, I place this code in the + * public domain for the benefit of all mankind - even the slimy + * ones who might try to proprietize my work and use it to my + * detriment. + * + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the + * following license and reinact the MPL 1.1 as stated above. + * * Copyright (c) 2007, Anthony Minessale II * All rights reserved. * @@ -40,21 +88,6 @@ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ********************************************************************************* - * - * Derived from tone_detect.c - General telephony tone detection, and specific - * detection of DTMF. - * - * Copyright (C) 2001 Steve Underwood - * - * Despite my general liking of the GPL, I place this code in the - * public domain for the benefit of all mankind - even the slimy - * ones who might try to proprietize my work and use it to my - * detriment. - * - * - * */ #include @@ -67,7 +100,8 @@ #include #include - +#define LOW_ENG 10000000 +#define ZC 2 static teletone_detection_descriptor_t dtmf_detect_row[GRID_FACTOR]; static teletone_detection_descriptor_t dtmf_detect_col[GRID_FACTOR]; static teletone_detection_descriptor_t dtmf_detect_row_2nd[GRID_FACTOR]; @@ -132,8 +166,8 @@ TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf dtmf_detect_state->current_sample = 0; dtmf_detect_state->detected_digits = 0; dtmf_detect_state->lost_digits = 0; - dtmf_detect_state->digits[0] = '\0'; - dtmf_detect_state->mhit = 0; + dtmf_detect_state->digit = 0; + dtmf_detect_state->dur = 0; } TELETONE_API(void) teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map) @@ -266,7 +300,7 @@ TELETONE_API(int) teletone_multi_tone_detect (teletone_multi_tone_t *mt, } -TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, +TELETONE_API(teletone_hit_type_t) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, int16_t sample_buffer[], int samples) { @@ -281,6 +315,7 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec int best_col; char hit; int limit; + teletone_hit_type_t r = 0; hit = 0; for (sample = 0; sample < samples; sample = limit) { @@ -317,6 +352,32 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec } + if (dtmf_detect_state->zc > 0) { + if (dtmf_detect_state->energy < LOW_ENG && dtmf_detect_state->lenergy < LOW_ENG) { + if (!--dtmf_detect_state->zc) { + /* Reinitialise the detector for the next block */ + dtmf_detect_state->hit1 = dtmf_detect_state->hit2 = 0; + for (i = 0; i < GRID_FACTOR; i++) { + goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); + } + dtmf_detect_state->dur -= samples; + return TT_HIT_END; + } + } + + dtmf_detect_state->dur += samples; + dtmf_detect_state->lenergy = dtmf_detect_state->energy; + dtmf_detect_state->energy = 0.0; + dtmf_detect_state->current_sample = 0; + return TT_HIT_MIDDLE; + } else if (dtmf_detect_state->digit) { + return TT_HIT_END; + } + + dtmf_detect_state->current_sample += (limit - sample); if (dtmf_detect_state->current_sample < BLOCK_LEN) { continue; @@ -361,58 +422,55 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec back to back differing digits. More importantly, it can work with nasty phones that give a very wobbly start to a digit. */ - if (hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) { - dtmf_detect_state->mhit = hit; + if (! r && hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) { dtmf_detect_state->digit_hits[(best_row << 2) + best_col]++; dtmf_detect_state->detected_digits++; if (dtmf_detect_state->current_digits < TELETONE_MAX_DTMF_DIGITS) { - dtmf_detect_state->digits[dtmf_detect_state->current_digits++] = hit; - dtmf_detect_state->digits[dtmf_detect_state->current_digits] = '\0'; + dtmf_detect_state->digit = hit; + } else { + dtmf_detect_state->lost_digits++; } - else - { - dtmf_detect_state->lost_digits++; - } + + if (!dtmf_detect_state->zc) { + dtmf_detect_state->zc = ZC; + dtmf_detect_state->dur = 0; + r = TT_HIT_BEGIN; + break; + } + } } } + dtmf_detect_state->hit1 = dtmf_detect_state->hit2; dtmf_detect_state->hit2 = dtmf_detect_state->hit3; dtmf_detect_state->hit3 = hit; - /* Reinitialise the detector for the next block */ - for (i = 0; i < GRID_FACTOR; i++) { - goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); - goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); - goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); - goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); - } + dtmf_detect_state->energy = 0.0; dtmf_detect_state->current_sample = 0; + } - if ((!dtmf_detect_state->mhit) || (dtmf_detect_state->mhit != hit)) { - dtmf_detect_state->mhit = 0; - return(0); - } - return (hit); + + return r; } -TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, - char *buf, - int max) +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, char *buf, unsigned int *dur) { - teletone_assert(dtmf_detect_state->current_digits <= TELETONE_MAX_DTMF_DIGITS); + if (!dtmf_detect_state->digit) { + return 0; + } - if (max > dtmf_detect_state->current_digits) { - max = dtmf_detect_state->current_digits; + *buf = dtmf_detect_state->digit; + + *dur = dtmf_detect_state->dur; + + if (!dtmf_detect_state->zc) { + dtmf_detect_state->dur = 0; + dtmf_detect_state->digit = 0; } - if (max > 0) { - memcpy (buf, dtmf_detect_state->digits, max); - memmove (dtmf_detect_state->digits, dtmf_detect_state->digits + max, dtmf_detect_state->current_digits - max); - dtmf_detect_state->current_digits -= max; - } - buf[max] = '\0'; - return max; + + return 1; } /* For Emacs: diff --git a/libs/freetdm/src/libteletone_generate.c b/libs/freetdm/src/libteletone_generate.c index c0d6a54349..7448508944 100644 --- a/libs/freetdm/src/libteletone_generate.c +++ b/libs/freetdm/src/libteletone_generate.c @@ -1,5 +1,41 @@ /* - * libteletone_generate.c -- Tone Generator + * libteletone + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is libteletone + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * libteletone.c -- Tone Generator + * + * + * + * Exception: + * The author hereby grants the use of this source code under the + * following license if and only if the source code is distributed + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the + * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II * All rights reserved. @@ -34,7 +70,6 @@ */ #include -#include "private/ftdm_core.h" #define SMAX 32767 #define SMIN -32768 @@ -111,7 +146,7 @@ TELETONE_API(int) teletone_init_session(teletone_generation_session_t *ts, int b ts->decay_step = 0; ts->decay_factor = 1; if (buflen) { - if ((ts->buffer = ftdm_calloc(buflen, sizeof(teletone_audio_t))) == 0) { + if ((ts->buffer = calloc(buflen, sizeof(teletone_audio_t))) == 0) { return -1; } ts->datalen = buflen; @@ -142,7 +177,7 @@ TELETONE_API(int) teletone_init_session(teletone_generation_session_t *ts, int b TELETONE_API(int) teletone_destroy_session(teletone_generation_session_t *ts) { if (ts->buffer) { - ftdm_safe_free(ts->buffer); + free(ts->buffer); ts->buffer = NULL; ts->samples = 0; } @@ -270,6 +305,19 @@ TELETONE_API(int) teletone_mux_tones(teletone_generation_session_t *ts, teletone return ts->samples / ts->channels; } +/* don't ask */ +static char *my_strdup (const char *s) +{ + size_t len = strlen (s) + 1; + void *new = malloc (len); + + if (new == NULL) { + return NULL; + } + + return (char *) memcpy (new, s, len); +} + TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cmd) { char *data = NULL, *cur = NULL, *end = NULL; @@ -280,7 +328,7 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm } do { - if (!(data = ftdm_strdup(cmd))) { + if (!(data = my_strdup(cmd))) { return -1; } @@ -363,6 +411,9 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm *e++ = '\0'; } do { + if (!p) { + break; + } if ((next = strchr(p, ',')) != 0) { *next++ = '\0'; } @@ -427,7 +478,7 @@ TELETONE_API(int) teletone_run(teletone_generation_session_t *ts, const char *cm } } bottom: - ftdm_safe_free(data); + free(data); data = NULL; if (ts->LOOPS > 0) { ts->LOOPS--; diff --git a/libs/libteletone/src/libteletone.h b/libs/libteletone/src/libteletone.h index b54098c8ee..61078b4984 100644 --- a/libs/libteletone/src/libteletone.h +++ b/libs/libteletone/src/libteletone.h @@ -33,8 +33,8 @@ * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the openzap library. Any use or distribution of this - * source code outside the scope of the openzap library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II diff --git a/libs/libteletone/src/libteletone_detect.c b/libs/libteletone/src/libteletone_detect.c index ae959535b5..0d0fd27eec 100644 --- a/libs/libteletone/src/libteletone_detect.c +++ b/libs/libteletone/src/libteletone_detect.c @@ -54,8 +54,8 @@ * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the openzap library. Any use or distribution of this - * source code outside the scope of the openzap library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II @@ -100,7 +100,8 @@ #include #include - +#define LOW_ENG 10000000 +#define ZC 2 static teletone_detection_descriptor_t dtmf_detect_row[GRID_FACTOR]; static teletone_detection_descriptor_t dtmf_detect_col[GRID_FACTOR]; static teletone_detection_descriptor_t dtmf_detect_row_2nd[GRID_FACTOR]; @@ -165,8 +166,8 @@ TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf dtmf_detect_state->current_sample = 0; dtmf_detect_state->detected_digits = 0; dtmf_detect_state->lost_digits = 0; - dtmf_detect_state->digits[0] = '\0'; - dtmf_detect_state->mhit = 0; + dtmf_detect_state->digit = 0; + dtmf_detect_state->dur = 0; } TELETONE_API(void) teletone_multi_tone_init(teletone_multi_tone_t *mt, teletone_tone_map_t *map) @@ -299,7 +300,7 @@ TELETONE_API(int) teletone_multi_tone_detect (teletone_multi_tone_t *mt, } -TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, +TELETONE_API(teletone_hit_type_t) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, int16_t sample_buffer[], int samples) { @@ -314,6 +315,7 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec int best_col; char hit; int limit; + teletone_hit_type_t r = 0; hit = 0; for (sample = 0; sample < samples; sample = limit) { @@ -350,6 +352,32 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec } + if (dtmf_detect_state->zc > 0) { + if (dtmf_detect_state->energy < LOW_ENG && dtmf_detect_state->lenergy < LOW_ENG) { + if (!--dtmf_detect_state->zc) { + /* Reinitialise the detector for the next block */ + dtmf_detect_state->hit1 = dtmf_detect_state->hit2 = 0; + for (i = 0; i < GRID_FACTOR; i++) { + goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); + goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); + goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); + goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); + } + dtmf_detect_state->dur -= samples; + return TT_HIT_END; + } + } + + dtmf_detect_state->dur += samples; + dtmf_detect_state->lenergy = dtmf_detect_state->energy; + dtmf_detect_state->energy = 0.0; + dtmf_detect_state->current_sample = 0; + return TT_HIT_MIDDLE; + } else if (dtmf_detect_state->digit) { + return TT_HIT_END; + } + + dtmf_detect_state->current_sample += (limit - sample); if (dtmf_detect_state->current_sample < BLOCK_LEN) { continue; @@ -394,58 +422,55 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec back to back differing digits. More importantly, it can work with nasty phones that give a very wobbly start to a digit. */ - if (hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) { - dtmf_detect_state->mhit = hit; + if (! r && hit == dtmf_detect_state->hit3 && dtmf_detect_state->hit3 != dtmf_detect_state->hit2) { dtmf_detect_state->digit_hits[(best_row << 2) + best_col]++; dtmf_detect_state->detected_digits++; if (dtmf_detect_state->current_digits < TELETONE_MAX_DTMF_DIGITS) { - dtmf_detect_state->digits[dtmf_detect_state->current_digits++] = hit; - dtmf_detect_state->digits[dtmf_detect_state->current_digits] = '\0'; + dtmf_detect_state->digit = hit; + } else { + dtmf_detect_state->lost_digits++; } - else - { - dtmf_detect_state->lost_digits++; - } + + if (!dtmf_detect_state->zc) { + dtmf_detect_state->zc = ZC; + dtmf_detect_state->dur = 0; + r = TT_HIT_BEGIN; + break; + } + } } } + dtmf_detect_state->hit1 = dtmf_detect_state->hit2; dtmf_detect_state->hit2 = dtmf_detect_state->hit3; dtmf_detect_state->hit3 = hit; - /* Reinitialise the detector for the next block */ - for (i = 0; i < GRID_FACTOR; i++) { - goertzel_init (&dtmf_detect_state->row_out[i], &dtmf_detect_row[i]); - goertzel_init (&dtmf_detect_state->col_out[i], &dtmf_detect_col[i]); - goertzel_init (&dtmf_detect_state->row_out2nd[i], &dtmf_detect_row_2nd[i]); - goertzel_init (&dtmf_detect_state->col_out2nd[i], &dtmf_detect_col_2nd[i]); - } + dtmf_detect_state->energy = 0.0; dtmf_detect_state->current_sample = 0; + } - if ((!dtmf_detect_state->mhit) || (dtmf_detect_state->mhit != hit)) { - dtmf_detect_state->mhit = 0; - return(0); - } - return (hit); + + return r; } -TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, - char *buf, - int max) +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, char *buf, unsigned int *dur) { - teletone_assert(dtmf_detect_state->current_digits <= TELETONE_MAX_DTMF_DIGITS); + if (!dtmf_detect_state->digit) { + return 0; + } - if (max > dtmf_detect_state->current_digits) { - max = dtmf_detect_state->current_digits; + *buf = dtmf_detect_state->digit; + + *dur = dtmf_detect_state->dur; + + if (!dtmf_detect_state->zc) { + dtmf_detect_state->dur = 0; + dtmf_detect_state->digit = 0; } - if (max > 0) { - memcpy (buf, dtmf_detect_state->digits, max); - memmove (dtmf_detect_state->digits, dtmf_detect_state->digits + max, dtmf_detect_state->current_digits - max); - dtmf_detect_state->current_digits -= max; - } - buf[max] = '\0'; - return max; + + return 1; } /* For Emacs: diff --git a/libs/libteletone/src/libteletone_detect.h b/libs/libteletone/src/libteletone_detect.h index e2a8c20b33..4da368969c 100644 --- a/libs/libteletone/src/libteletone_detect.h +++ b/libs/libteletone/src/libteletone_detect.h @@ -54,8 +54,8 @@ * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the openzap library. Any use or distribution of this - * source code outside the scope of the openzap library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II @@ -134,6 +134,14 @@ extern "C" { #define BLOCK_LEN 102 #define M_TWO_PI 2.0*M_PI + typedef enum { + TT_HIT_NONE = 0, + TT_HIT_BEGIN = 1, + TT_HIT_MIDDLE = 2, + TT_HIT_END = 3 + } teletone_hit_type_t; + + /*! \brief A continer for the elements of a Goertzel Algorithm (The names are from his formula) */ typedef struct { float v2; @@ -147,16 +155,19 @@ extern "C" { int hit2; int hit3; int hit4; - int mhit; + int dur; + int zc; + teletone_goertzel_state_t row_out[GRID_FACTOR]; teletone_goertzel_state_t col_out[GRID_FACTOR]; teletone_goertzel_state_t row_out2nd[GRID_FACTOR]; teletone_goertzel_state_t col_out2nd[GRID_FACTOR]; float energy; + float lenergy; int current_sample; - char digits[TELETONE_MAX_DTMF_DIGITS + 1]; + char digit; int current_digits; int detected_digits; int lost_digits; @@ -229,7 +240,7 @@ TELETONE_API(void) teletone_dtmf_detect_init (teletone_dtmf_detect_state_t *dtmf \param samples the number of samples present in sample_buffer \return true when DTMF was detected or false when it is not */ -TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, +TELETONE_API(teletone_hit_type_t) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detect_state, int16_t sample_buffer[], int samples); /*! @@ -239,9 +250,7 @@ TELETONE_API(int) teletone_dtmf_detect (teletone_dtmf_detect_state_t *dtmf_detec \param max the maximum length of buf \return the number of characters written to buf */ -TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, - char *buf, - int max); +TELETONE_API(int) teletone_dtmf_get (teletone_dtmf_detect_state_t *dtmf_detect_state, char *buf, unsigned int *dur); /*! \brief Step through the Goertzel Algorithm for each sample in a buffer diff --git a/libs/libteletone/src/libteletone_generate.c b/libs/libteletone/src/libteletone_generate.c index a5cefac98d..7448508944 100644 --- a/libs/libteletone/src/libteletone_generate.c +++ b/libs/libteletone/src/libteletone_generate.c @@ -33,8 +33,8 @@ * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the openzap library. Any use or distribution of this - * source code outside the scope of the openzap library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II diff --git a/libs/libteletone/src/libteletone_generate.h b/libs/libteletone/src/libteletone_generate.h index 5e6444b0c4..0ff39e0dfa 100644 --- a/libs/libteletone/src/libteletone_generate.h +++ b/libs/libteletone/src/libteletone_generate.h @@ -33,8 +33,8 @@ * Exception: * The author hereby grants the use of this source code under the * following license if and only if the source code is distributed - * as part of the openzap library. Any use or distribution of this - * source code outside the scope of the openzap library will nullify the + * as part of the OpenZAP or FreeTDM library. Any use or distribution of this + * source code outside the scope of the OpenZAP or FreeTDM library will nullify the * following license and reinact the MPL 1.1 as stated above. * * Copyright (c) 2007, Anthony Minessale II diff --git a/libs/openzap/configure.ac b/libs/openzap/configure.ac index c2b9e0bd67..d2c23c1091 100644 --- a/libs/openzap/configure.ac +++ b/libs/openzap/configure.ac @@ -107,7 +107,10 @@ case "$host" in ;; esac +DEFAULT_INCLUDES="-I. -I./src/include -I$(srcdir)" + AC_SUBST(SOLINK) +AC_SUBST(DEFAULT_INCLUDES) AC_SUBST(DYNAMIC_LIB_EXTEN) AC_CHECK_LIB([dl], [dlopen]) diff --git a/libs/silk/Makefile.am b/libs/silk/Makefile.am index 235e468913..071f1ecd64 100644 --- a/libs/silk/Makefile.am +++ b/libs/silk/Makefile.am @@ -1,5 +1,4 @@ - -AM_CFLAGS = -Isrc -Iinterface -fPIC -Wall -O3 +AM_CFLAGS = -Isrc -Iinterface -fPIC -O3 AUTOMAKE_OPTS = gnu NAME = libSKP_SILK_SDK AM_CPPFLAGS = $(AM_CFLAGS) diff --git a/src/include/private/switch_core_pvt.h b/src/include/private/switch_core_pvt.h index 423969fac8..31da5a9bf1 100644 --- a/src/include/private/switch_core_pvt.h +++ b/src/include/private/switch_core_pvt.h @@ -169,7 +169,7 @@ struct switch_core_session { uint32_t track_id; switch_log_level_t loglevel; uint32_t soft_lock; - switch_ivr_dmachine_t *dmachine; + switch_ivr_dmachine_t *dmachine[2]; plc_state_t *plc; }; @@ -255,6 +255,7 @@ struct switch_runtime { uint32_t db_handle_timeout; int curl_count; int ssl_count; + int cpu_count; }; extern struct switch_runtime runtime; diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index 8fa0439b47..d388777c33 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -87,6 +87,10 @@ SWITCH_DECLARE(int) switch_channel_test_ready(switch_channel_t *channel, switch_ #define switch_channel_up(_channel) (switch_channel_check_signal(_channel, SWITCH_TRUE) || switch_channel_get_state(_channel) < CS_HANGUP) #define switch_channel_down(_channel) (switch_channel_check_signal(_channel, SWITCH_TRUE) || switch_channel_get_state(_channel) >= CS_HANGUP) + +#define switch_channel_up_nosig(_channel) switch_channel_get_state(_channel) < CS_HANGUP +#define switch_channel_down_nosig(_channel) switch_channel_get_state(_channel) >= CS_HANGUP + #define switch_channel_media_ack(_channel) (!switch_channel_test_cap(_channel, CC_MEDIA_ACK) || switch_channel_test_flag(_channel, CF_MEDIA_ACK)) SWITCH_DECLARE(void) switch_channel_wait_for_state(switch_channel_t *channel, switch_channel_t *other_channel, switch_channel_state_t want_state); @@ -627,6 +631,9 @@ SWITCH_DECLARE(void) switch_channel_mark_hold(switch_channel_t *channel, switch_ SWITCH_DECLARE(switch_status_t) switch_channel_execute_on(switch_channel_t *channel, const char *variable_prefix); SWITCH_DECLARE(switch_status_t) switch_channel_api_on(switch_channel_t *channel, const char *variable_prefix); +SWITCH_DECLARE(switch_caller_extension_t *) switch_channel_get_queued_extension(switch_channel_t *channel); +SWITCH_DECLARE(void) switch_channel_transfer_to_extension(switch_channel_t *channel, switch_caller_extension_t *caller_extension); + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 8d4a0cd122..7f1db105f8 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -712,12 +712,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_set_loglevel(switch_core_ses \return the log level */ SWITCH_DECLARE(switch_log_level_t) switch_core_session_get_loglevel(switch_core_session_t *session); - + SWITCH_DECLARE(void) switch_core_session_soft_lock(switch_core_session_t *session, uint32_t sec); SWITCH_DECLARE(void) switch_core_session_soft_unlock(switch_core_session_t *session); -SWITCH_DECLARE(void) switch_core_session_set_dmachine(switch_core_session_t *session, switch_ivr_dmachine_t *dmachine); -SWITCH_DECLARE(switch_ivr_dmachine_t *) switch_core_session_get_dmachine(switch_core_session_t *session); +SWITCH_DECLARE(void) switch_core_session_set_dmachine(switch_core_session_t *session, switch_ivr_dmachine_t *dmachine, switch_digit_action_target_t target); +SWITCH_DECLARE(switch_ivr_dmachine_t *) switch_core_session_get_dmachine(switch_core_session_t *session, switch_digit_action_target_t target); +SWITCH_DECLARE(switch_digit_action_target_t) switch_ivr_dmachine_get_target(switch_ivr_dmachine_t *dmachine); +SWITCH_DECLARE(void) switch_ivr_dmachine_set_target(switch_ivr_dmachine_t *dmachine, switch_digit_action_target_t target); + SWITCH_DECLARE(switch_status_t) switch_core_session_set_codec_slin(switch_core_session_t *session, switch_slin_data_t *data); /*! @@ -1975,8 +1978,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_management_exec(char *relative_oid, \brief Set the maximum priority the process can obtain \return 0 on success */ -SWITCH_DECLARE(int32_t) set_high_priority(void); + SWITCH_DECLARE(int32_t) set_normal_priority(void); +SWITCH_DECLARE(int32_t) set_auto_priority(void); +SWITCH_DECLARE(int32_t) set_realtime_priority(void); +SWITCH_DECLARE(int32_t) set_low_priority(void); /*! \brief Change user and/or group of the running process @@ -2101,8 +2107,10 @@ SWITCH_DECLARE(switch_status_t) switch_console_set_alias(const char *string); SWITCH_DECLARE(int) switch_system(const char *cmd, switch_bool_t wait); SWITCH_DECLARE(void) switch_cond_yield(switch_interval_time_t t); SWITCH_DECLARE(void) switch_cond_next(void); -SWITCH_DECLARE(switch_status_t) switch_core_chat_send(const char *name, const char *proto, const char *from, const char *to, - const char *subject, const char *body, const char *type, const char *hint); +SWITCH_DECLARE(switch_status_t) switch_core_chat_send_args(const char *dest_proto, const char *proto, const char *from, const char *to, + const char *subject, const char *body, const char *type, const char *hint); +SWITCH_DECLARE(switch_status_t) switch_core_chat_send(const char *dest_proto, switch_event_t *message_event); +SWITCH_DECLARE(switch_status_t) switch_core_chat_deliver(const char *dest_proto, switch_event_t **message_event); SWITCH_DECLARE(switch_status_t) switch_ivr_preprocess_session(switch_core_session_t *session, const char *cmds); diff --git a/src/include/switch_core_db.h b/src/include/switch_core_db.h index 6ea9b4a3fb..f72c3c5f45 100644 --- a/src/include/switch_core_db.h +++ b/src/include/switch_core_db.h @@ -540,6 +540,8 @@ SWITCH_DECLARE(int) switch_core_db_changes(switch_core_db_t *db); * literal. */ +SWITCH_DECLARE(char*)switch_sql_concat(void); + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_cpp.h b/src/include/switch_cpp.h index e9f5c29d92..70e484bf51 100644 --- a/src/include/switch_cpp.h +++ b/src/include/switch_cpp.h @@ -156,6 +156,8 @@ SWITCH_DECLARE(bool) email(char *to, char *from, char *headers = NULL, char *bod SWITCH_DECLARE_CONSTRUCTOR Event(const char *type, const char *subclass_name = NULL); SWITCH_DECLARE_CONSTRUCTOR Event(switch_event_t *wrap_me, int free_me = 0); virtual SWITCH_DECLARE_CONSTRUCTOR ~ Event(); + SWITCH_DECLARE(int) chat_execute(const char *app, const char *data = NULL); + SWITCH_DECLARE(int) chat_send(const char *dest_proto = NULL); SWITCH_DECLARE(const char *) serialize(const char *format = NULL); SWITCH_DECLARE(bool) setPriority(switch_priority_t priority = SWITCH_PRIORITY_NORMAL); SWITCH_DECLARE(const char *) getHeader(const char *header_name); diff --git a/src/include/switch_event.h b/src/include/switch_event.h index c6b1d64c35..23cd0825b5 100644 --- a/src/include/switch_event.h +++ b/src/include/switch_event.h @@ -103,7 +103,8 @@ struct switch_event { }; typedef enum { - EF_UNIQ_HEADERS = (1 << 0) + EF_UNIQ_HEADERS = (1 << 0), + EF_NO_CHAT_EXEC = (1 << 1) } switch_event_flag_t; @@ -157,6 +158,8 @@ _Ret_opt_z_ SWITCH_DECLARE(char *) switch_event_get_header_idx(switch_event_t *e #define switch_event_get_header_nil(e, h) switch_str_nil(switch_event_get_header(e,h)) +SWITCH_DECLARE(switch_status_t) switch_event_rename_header(switch_event_t *event, const char *header_name, const char *new_header_name); + /*! \brief Retrieve the body value from an event \param event the event to read the body from @@ -207,6 +210,8 @@ SWITCH_DECLARE(void) switch_event_destroy(switch_event_t **event); */ SWITCH_DECLARE(switch_status_t) switch_event_dup(switch_event_t **event, switch_event_t *todup); SWITCH_DECLARE(void) switch_event_merge(switch_event_t *event, switch_event_t *tomerge); +SWITCH_DECLARE(switch_status_t) switch_event_dup_reply(switch_event_t **event, switch_event_t *todup); + /*! \brief Fire an event with full arguement list \param file the calling file @@ -321,6 +326,9 @@ SWITCH_DECLARE(switch_status_t) switch_event_running(void); */ SWITCH_DECLARE(switch_status_t) switch_event_add_body(switch_event_t *event, const char *fmt, ...) PRINTF_FUNCTION(2, 3); #endif + +SWITCH_DECLARE(switch_status_t) switch_event_set_body(switch_event_t *event, const char *body); + SWITCH_DECLARE(char *) switch_event_expand_headers(switch_event_t *event, const char *in); SWITCH_DECLARE(switch_status_t) switch_event_create_pres_in_detailed(_In_z_ char *file, _In_z_ char *func, _In_ int line, diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 5ce8c882d2..901519f7dd 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -464,7 +464,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_enterprise_originate(switch_core_sess const char *cid_name_override, const char *cid_num_override, switch_caller_profile_t *caller_profile_override, - switch_event_t *ovars, switch_originate_flag_t flags); + switch_event_t *ovars, switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause); SWITCH_DECLARE(void) switch_ivr_bridge_display(switch_core_session_t *session, switch_core_session_t *peer_session); @@ -930,6 +931,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_release_file_handle(switch_core_sessi SWITCH_DECLARE(switch_status_t) switch_ivr_process_fh(switch_core_session_t *session, const char *cmd, switch_file_handle_t *fhp); SWITCH_DECLARE(switch_status_t) switch_ivr_insert_file(switch_core_session_t *session, const char *file, const char *insert_file, switch_size_t sample_point); +SWITCH_DECLARE(switch_status_t) switch_ivr_create_message_reply(switch_event_t **reply, switch_event_t *message, const char *new_proto); + /** @} */ SWITCH_END_EXTERN_C diff --git a/src/include/switch_loadable_module.h b/src/include/switch_loadable_module.h index 4b81a1f25e..62fcac2343 100644 --- a/src/include/switch_loadable_module.h +++ b/src/include/switch_loadable_module.h @@ -55,33 +55,35 @@ SWITCH_BEGIN_EXTERN_C struct switch_loadable_module_interface { /*! the name of the module */ const char *module_name; - /*! the table of endpoints the module has implmented */ + /*! the table of endpoints the module has implemented */ switch_endpoint_interface_t *endpoint_interface; - /*! the table of timers the module has implmented */ + /*! the table of timers the module has implemented */ switch_timer_interface_t *timer_interface; - /*! the table of dialplans the module has implmented */ + /*! the table of dialplans the module has implemented */ switch_dialplan_interface_t *dialplan_interface; - /*! the table of codecs the module has implmented */ + /*! the table of codecs the module has implemented */ switch_codec_interface_t *codec_interface; - /*! the table of applications the module has implmented */ + /*! the table of applications the module has implemented */ switch_application_interface_t *application_interface; - /*! the table of api functions the module has implmented */ + /*! the table of chat applications the module has implemented */ + switch_chat_application_interface_t *chat_application_interface; + /*! the table of api functions the module has implemented */ switch_api_interface_t *api_interface; - /*! the table of file formats the module has implmented */ + /*! the table of file formats the module has implemented */ switch_file_interface_t *file_interface; - /*! the table of speech interfaces the module has implmented */ + /*! the table of speech interfaces the module has implemented */ switch_speech_interface_t *speech_interface; - /*! the table of directory interfaces the module has implmented */ + /*! the table of directory interfaces the module has implemented */ switch_directory_interface_t *directory_interface; - /*! the table of chat interfaces the module has implmented */ + /*! the table of chat interfaces the module has implemented */ switch_chat_interface_t *chat_interface; - /*! the table of say interfaces the module has implmented */ + /*! the table of say interfaces the module has implemented */ switch_say_interface_t *say_interface; - /*! the table of asr interfaces the module has implmented */ + /*! the table of asr interfaces the module has implemented */ switch_asr_interface_t *asr_interface; - /*! the table of management interfaces the module has implmented */ + /*! the table of management interfaces the module has implemented */ switch_management_interface_t *management_interface; - /*! the table of limit interfaces the module has implmented */ + /*! the table of limit interfaces the module has implemented */ switch_limit_interface_t *limit_interface; switch_thread_rwlock_t *rwlock; int refs; @@ -169,6 +171,15 @@ SWITCH_DECLARE(switch_timer_interface_t *) switch_loadable_module_get_timer_inte */ SWITCH_DECLARE(switch_application_interface_t *) switch_loadable_module_get_application_interface(const char *name); +/*! + \brief Retrieve the chat application interface by it's registered name + \param name the name of the chat application + \return the desired chat application interface + */ +SWITCH_DECLARE(switch_chat_application_interface_t *) switch_loadable_module_get_chat_application_interface(const char *name); + +SWITCH_DECLARE(switch_status_t) switch_core_execute_chat_app(switch_event_t *message, const char *app, const char *data); + /*! \brief Retrieve the API interface by it's registered name \param name the name of the API @@ -335,6 +346,18 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void); break; \ } +#define SWITCH_ADD_CHAT_APP(app_int, int_name, short_descript, long_descript, funcptr, syntax_string, app_flags) \ + for (;;) { \ + app_int = (switch_chat_application_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_CHAT_APPLICATION_INTERFACE); \ + app_int->interface_name = int_name; \ + app_int->chat_application_function = funcptr; \ + app_int->short_desc = short_descript; \ + app_int->long_desc = long_descript; \ + app_int->syntax = syntax_string; \ + app_int->flags = app_flags; \ + break; \ + } + #define SWITCH_ADD_DIALPLAN(dp_int, int_name, funcptr) \ for (;;) { \ dp_int = (switch_dialplan_interface_t *)switch_loadable_module_create_interface(*module_interface, SWITCH_DIALPLAN_INTERFACE); \ diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 33c4c22bac..acb85407c7 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -483,8 +483,8 @@ struct switch_chat_interface { /*! the name of the interface */ const char *interface_name; /*! function to open the directory interface */ - switch_status_t (*chat_send) (const char *proto, const char *from, const char *to, - const char *subject, const char *body, const char *type, const char *hint); + switch_status_t (*chat_send) (switch_event_t *message_event); + switch_thread_rwlock_t *rwlock; int refs; switch_mutex_t *reflock; @@ -681,6 +681,27 @@ struct switch_application_interface { struct switch_application_interface *next; }; +/*! \brief A module interface to implement a chat application */ +struct switch_chat_application_interface { + /*! the name of the interface */ + const char *interface_name; + /*! function the application implements */ + switch_chat_application_function_t chat_application_function; + /*! the long winded description of the application */ + const char *long_desc; + /*! the short and sweet description of the application */ + const char *short_desc; + /*! an example of the application syntax */ + const char *syntax; + /*! flags to control behaviour */ + uint32_t flags; + switch_thread_rwlock_t *rwlock; + int refs; + switch_mutex_t *reflock; + switch_loadable_module_interface_t *parent; + struct switch_chat_application_interface *next; +}; + /*! \brief A module interface to implement an api function */ struct switch_api_interface { /*! the name of the interface */ diff --git a/src/include/switch_regex.h b/src/include/switch_regex.h index 27f4852966..6558299b53 100644 --- a/src/include/switch_regex.h +++ b/src/include/switch_regex.h @@ -74,6 +74,7 @@ SWITCH_DECLARE(void) switch_capture_regex(switch_regex_t *re, int match_count, c int *ovector, const char *var, switch_cap_callback_t callback, void *user_data); SWITCH_DECLARE_NONSTD(void) switch_regex_set_var_callback(const char *var, const char *val, void *user_data); +SWITCH_DECLARE_NONSTD(void) switch_regex_set_event_header_callback(const char *var, const char *val, void *user_data); #define switch_regex_safe_free(re) if (re) {\ switch_regex_free(re);\ diff --git a/src/include/switch_types.h b/src/include/switch_types.h index f0c6dfed01..21dfa51339 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -182,6 +182,7 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_ORIGINATOR_VIDEO_CODEC_VARIABLE "originator_video_codec" #define SWITCH_LOCAL_MEDIA_IP_VARIABLE "local_media_ip" #define SWITCH_LOCAL_MEDIA_PORT_VARIABLE "local_media_port" +#define SWITCH_ADVERTISED_MEDIA_IP_VARIABLE "advertised_media_ip" #define SWITCH_REMOTE_MEDIA_IP_VARIABLE "remote_media_ip" #define SWITCH_REMOTE_MEDIA_PORT_VARIABLE "remote_media_port" #define SWITCH_REMOTE_VIDEO_IP_VARIABLE "remote_video_ip" @@ -221,6 +222,14 @@ typedef enum { SWITCH_DTMF_APP } switch_dtmf_source_t; +typedef enum { + DIGIT_TARGET_SELF, + DIGIT_TARGET_PEER, + DIGIT_TARGET_BOTH +} switch_digit_action_target_t; + + + typedef enum { DTMF_FLAG_SKIP_PROCESS = (1 << 0) } dtmf_flag_t; @@ -297,7 +306,8 @@ typedef enum { SCF_AUTO_SCHEMAS = (1 << 13), SCF_MINIMAL = (1 << 14), SCF_USE_NAT_MAPPING = (1 << 15), - SCF_CLEAR_SQL = (1 << 16) + SCF_CLEAR_SQL = (1 << 16), + SCF_THREADED_SYSTEM_EXEC = (1 << 17) } switch_core_flag_enum_t; typedef uint32_t switch_core_flag_t; @@ -315,7 +325,8 @@ typedef enum { SWITCH_SAY_INTERFACE, SWITCH_ASR_INTERFACE, SWITCH_MANAGEMENT_INTERFACE, - SWITCH_LIMIT_INTERFACE + SWITCH_LIMIT_INTERFACE, + SWITCH_CHAT_APPLICATION_INTERFACE } switch_module_interface_name_t; typedef enum { @@ -485,6 +496,7 @@ typedef enum { SWITCH_XML_SECTION_DIRECTORY = (1 << 1), SWITCH_XML_SECTION_DIALPLAN = (1 << 2), SWITCH_XML_SECTION_PHRASES = (1 << 3), + SWITCH_XML_SECTION_CHATPLAN = (1 << 4), /* Nothing after this line */ SWITCH_XML_SECTION_MAX = (1 << 4) @@ -672,15 +684,16 @@ typedef enum { This flag will treat every dtmf as if it were 50ms and queue it on recipt of the leading packet rather than at the end. */ - RTP_BUG_PAUSE_BETWEEN_DTMF = (1 << 7) + + RTP_BUG_ACCEPT_ANY_PACKETS = (1 << 7) /* - Sonus says they need time to generate the dtmf so we should not send it so fast so with this flag we will wait a few clicks after each send to - start sending the next one. + Oracle's Contact Center Anywhere (CCA) likes to use a single RTP socket to send all its outbound audio. + This messes up our ability to auto adjust to NATTED RTP and causes us to ignore its audio packets. + This flag will allow compatibility with this dying product. */ - } switch_rtp_bug_flag_t; #ifdef _MSC_VER @@ -857,6 +870,7 @@ typedef struct { uint16_t remote_port; const char *local_ip; uint16_t local_port; + const char *sdp_o_line; } switch_t38_options_t; /*! @@ -1156,6 +1170,7 @@ typedef enum { CF_LAZY_ATTENDED_TRANSFER, CF_SIGNAL_DATA, CF_SIMPLIFY, + CF_ZOMBIE_EXEC, /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */ CF_FLAG_MAX @@ -1166,7 +1181,8 @@ typedef enum { CF_APP_TAGGED = (1 << 0), CF_APP_T38 = (1 << 1), CF_APP_T38_REQ = (1 << 2), - CF_APP_T38_FAIL = (1 << 3) + CF_APP_T38_FAIL = (1 << 3), + CF_APP_T38_NEGOTIATED = (1 << 4) } switch_channel_app_flag_t; @@ -1193,7 +1209,8 @@ typedef enum { SFF_PROXY_PACKET = (1 << 5), SFF_DYNAMIC = (1 << 6), SFF_ZRTP = (1 << 7), - SFF_UDPTL_PACKET = (1 << 8) + SFF_UDPTL_PACKET = (1 << 8), + SFF_NOT_AUDIO = (1 << 9) } switch_frame_flag_enum_t; typedef uint32_t switch_frame_flag_t; @@ -1202,10 +1219,17 @@ typedef enum { SAF_NONE = 0, SAF_SUPPORT_NOMEDIA = (1 << 0), SAF_ROUTING_EXEC = (1 << 1), - SAF_MEDIA_TAP = (1 << 2) + SAF_MEDIA_TAP = (1 << 2), + SAF_ZOMBIE_EXEC = (1 << 3) } switch_application_flag_enum_t; typedef uint32_t switch_application_flag_t; +typedef enum { + SCAF_NONE = 0 +} switch_chat_application_flag_enum_t; +typedef uint32_t switch_chat_application_flag_t; + + /*! \enum switch_signal_t \brief Signals to send to channels @@ -1666,7 +1690,8 @@ typedef enum { SCSC_VERBOSE_EVENTS, SCSC_SHUTDOWN_CHECK, SCSC_PAUSE_CHECK, - SCSC_READY_CHECK + SCSC_READY_CHECK, + SCSC_THREADED_SYSTEM_EXEC } switch_session_ctl_t; typedef enum { @@ -1713,6 +1738,7 @@ typedef struct switch_timer_interface switch_timer_interface_t; typedef struct switch_dialplan_interface switch_dialplan_interface_t; typedef struct switch_codec_interface switch_codec_interface_t; typedef struct switch_application_interface switch_application_interface_t; +typedef struct switch_chat_application_interface switch_chat_application_interface_t; typedef struct switch_api_interface switch_api_interface_t; typedef struct switch_file_interface switch_file_interface_t; typedef struct switch_speech_interface switch_speech_interface_t; @@ -1764,7 +1790,8 @@ typedef switch_status_t (*switch_core_codec_fmtp_parse_func_t) (const char *fmtp typedef switch_status_t (*switch_core_codec_destroy_func_t) (switch_codec_t *); - +typedef switch_status_t (*switch_chat_application_function_t) (switch_event_t *, const char *); +#define SWITCH_STANDARD_CHAT_APP(name) static switch_status_t name (switch_event_t *message, const char *data) typedef void (*switch_application_function_t) (switch_core_session_t *, const char *); #define SWITCH_STANDARD_APP(name) static void name (switch_core_session_t *session, const char *data) diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index b5a1fa1868..33b4173513 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -525,6 +525,18 @@ static inline char *switch_clean_name_string(char *s) +/*! + \brief Turn a string into a number (default if NULL) + \param nptr the string + \param dft the default + \return the number version of the string or the default +*/ +static inline int switch_safe_atoi(const char *nptr, int dft) +{ + return nptr ? atoi(nptr) : dft; +} + + /*! \brief Free a pointer and set it to NULL unless it already is NULL \param it the pointer diff --git a/src/mod/.gitignore b/src/mod/.gitignore index 7102317b2b..f7a1c41eea 100644 --- a/src/mod/.gitignore +++ b/src/mod/.gitignore @@ -1,5 +1,6 @@ /Makefile /Makefile.in +/applications/mod_blacklist/Makefile /applications/mod_commands/Makefile /applications/mod_conference/Makefile /applications/mod_db/Makefile @@ -14,6 +15,7 @@ /applications/mod_fifo/Makefile /applications/mod_fsv/Makefile /applications/mod_limit/Makefile +/applications/mod_sms/Makefile /applications/mod_spandsp/Makefile /applications/mod_spandsp/Makefile.in /applications/mod_spandsp/mod_spandsp.log diff --git a/src/mod/applications/mod_blacklist/mod_blacklist.c b/src/mod/applications/mod_blacklist/mod_blacklist.c new file mode 100644 index 0000000000..bb2cd7828e --- /dev/null +++ b/src/mod/applications/mod_blacklist/mod_blacklist.c @@ -0,0 +1,343 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Mathieu Rene + * Raymond Chandler + * + * mod_blacklist.c -- Blacklist module + * + */ +#include + +/* Prototypes */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_blacklist_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_blacklist_runtime); +SWITCH_MODULE_LOAD_FUNCTION(mod_blacklist_load); + + +SWITCH_MODULE_DEFINITION(mod_blacklist, mod_blacklist_load, mod_blacklist_shutdown, NULL); + +typedef struct { + switch_hash_t *list; + switch_mutex_t *list_mutex; + switch_memory_pool_t *pool; +} blacklist_t; + +static struct { + switch_hash_t *files; + switch_hash_t *lists; + switch_mutex_t *files_mutex; + switch_mutex_t *lists_mutex; + switch_memory_pool_t *pool; +} globals; + +blacklist_t *blacklist_create(const char *name) +{ + switch_memory_pool_t *pool = NULL; + blacklist_t *bl = NULL; + + switch_core_new_memory_pool(&pool); + bl = switch_core_alloc(pool, sizeof(*bl)); + switch_assert(bl); + bl->pool = pool; + + switch_core_hash_init(&bl->list, pool); + switch_mutex_init(&bl->list_mutex, SWITCH_MUTEX_NESTED, pool); + + return bl; +} + +void blacklist_free(blacklist_t *bl) +{ + switch_core_destroy_memory_pool(&bl->pool); +} + + +void trim(char *string) +{ + char *p; + if ((p = strchr(string, '\n'))) { + *p = '\0'; + } + if ((p = strchr(string, '\r'))) { + *p = '\0'; + } +} + +static switch_status_t load_list(const char *name, const char *filename) +{ + FILE *f; + + if ((f = fopen(filename, "r"))) { + char buf[1024] = {0}; + blacklist_t *bl = blacklist_create(name); /* Create a hashtable + mutex for that list */ + + while (fgets(buf, 1024, f)) { + trim(buf); + switch_core_hash_insert(bl->list, buf, (void *)SWITCH_TRUE); + } + + switch_core_hash_insert(globals.files, name, filename); + switch_core_hash_insert(globals.lists, name, bl); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Loaded list [%s]\n", name); + + fclose(f); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't open [%s] to load list [%s]\n", filename, name); + return SWITCH_STATUS_FALSE; + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t do_config(switch_bool_t reload) +{ + /* Load up blacklists */ + switch_xml_t xml, cfg, lists, list; + switch_hash_index_t *hi; + + if (!(xml = switch_xml_open_cfg("mod_blacklist.conf", &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't load configuration section\n"); + return SWITCH_STATUS_FALSE; + } + + switch_mutex_lock(globals.lists_mutex); + + /* Destroy any active lists */ + while ((hi = switch_hash_first(NULL, globals.lists))) { + const void *key; + void *val; + switch_hash_this(hi, &key, NULL, &val); + blacklist_free((blacklist_t*)val); + switch_core_hash_delete(globals.lists, (const char*)key); + } + + if ((lists = switch_xml_child(cfg, "lists"))) { + for (list = switch_xml_child(lists, "list"); list; list = list->next) { + const char *name = switch_xml_attr_soft(list, "name"); + const char *filename = switch_xml_attr_soft(list, "filename"); + + if (zstr(name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "list has no name\n"); + continue; + } + if (zstr(filename)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "list [%s] has no filename\n", name); + continue; + } + + load_list(name, filename); + } + } + + switch_mutex_unlock(globals.lists_mutex); + + if (xml) { + switch_xml_free(xml); + xml = NULL; + } + + return SWITCH_STATUS_SUCCESS; +} + +#define BLACKLIST_API_SYNTAX \ + "blacklist check \n" \ + "blacklist add \n" \ + "blacklist del \n" \ + "blacklist reload\n" \ + "blacklist help\n" + +SWITCH_STANDARD_API(blacklist_api_function) +{ + char *data; + int argc; + char *argv[3]; + + data = strdup(cmd); + trim(data); + if (!(argc = switch_separate_string(data, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid usage\n"); + goto done; + } + + if (!strcasecmp(argv[0], "check")) { + blacklist_t *bl = NULL; + switch_bool_t result; + + if (argc < 2 || zstr(argv[1]) || zstr(argv[2])) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Wrong syntax"); + goto done; + } + + switch_mutex_lock(globals.lists_mutex); + bl = switch_core_hash_find(globals.lists, argv[1]); + switch_mutex_unlock(globals.lists_mutex); + + if (!bl) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]); + stream->write_function(stream, "false"); + goto done; + } + + switch_mutex_lock(bl->list_mutex); + result = (switch_bool_t)(intptr_t)switch_core_hash_find(bl->list, argv[2]); + stream->write_function(stream, "%s", result ? "true" : "false"); + switch_mutex_unlock(bl->list_mutex); + } else if (!strcasecmp(argv[0], "add")) { + blacklist_t *bl = NULL; + if (argc < 2 || zstr(argv[1]) || zstr(argv[2])) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Wrong syntax"); + goto done; + } + + switch_mutex_lock(globals.lists_mutex); + bl = switch_core_hash_find(globals.lists, argv[1]); + switch_mutex_unlock(globals.lists_mutex); + + if (!bl) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]); + stream->write_function(stream, "-ERR Unknown blacklist\n"); + goto done; + } + + switch_mutex_lock(bl->list_mutex); + switch_core_hash_insert(bl->list, argv[2], (void*)SWITCH_TRUE); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Added [%s] to list [%s]\n", argv[2], argv[1]); + switch_mutex_unlock(bl->list_mutex); + stream->write_function(stream, "+OK\n"); + } else if (!strcasecmp(argv[0], "del")) { + blacklist_t *bl = NULL; + if (argc < 2 || zstr(argv[1]) || zstr(argv[2])) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Wrong syntax"); + goto done; + } + + switch_mutex_lock(globals.lists_mutex); + bl = switch_core_hash_find(globals.lists, argv[1]); + switch_mutex_unlock(globals.lists_mutex); + + if (!bl) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]); + stream->write_function(stream, "-ERR Unknown blacklist\n"); + goto done; + } + + switch_mutex_lock(bl->list_mutex); + switch_core_hash_insert(bl->list, argv[2], NULL); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Removed [%s] from list [%s]\n", argv[2], argv[1]); + switch_mutex_unlock(bl->list_mutex); + stream->write_function(stream, "+OK\n"); + } else if (!strcasecmp(argv[0], "dump")) { + switch_hash_index_t *hi; + void *val; + const void *var; + blacklist_t *bl = NULL; + char *filename = NULL; + switch_file_t *fd; + + if (argc < 1 || zstr(argv[1])) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Missing blacklist name"); + goto done; + } + + switch_mutex_lock(globals.lists_mutex); + bl = switch_core_hash_find(globals.lists, argv[1]); + switch_mutex_unlock(globals.lists_mutex); + + switch_mutex_lock(globals.files_mutex); + filename = switch_core_hash_find(globals.files, argv[1]); + switch_mutex_unlock(globals.files_mutex); + + if (!bl) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown blacklist [%s]\n", argv[1]); + stream->write_function(stream, "-ERR Unknown blacklist\n"); + goto done; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Dumping %s to %s\n", argv[1], filename); + + switch_mutex_lock(globals.lists_mutex); + if (switch_file_open(&fd, filename, SWITCH_FOPEN_WRITE, SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE, globals.pool) == SWITCH_STATUS_SUCCESS) { + for (hi = switch_hash_first(NULL, bl->list); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &var, NULL, &val); + switch_file_printf(fd, "%s\n", (char *)var); + } + stream->write_function(stream, "+OK\n"); + } + switch_mutex_unlock(globals.lists_mutex); + } else if (!strcasecmp(argv[0], "reload")) { + do_config(SWITCH_TRUE); + stream->write_function(stream, "+OK\n"); + } else if (!strcasecmp(argv[0], "help")) { + stream->write_function(stream, BLACKLIST_API_SYNTAX "+OK\n"); + } else if (!zstr(argv[0])) { + stream->write_function(stream, "-ERR: No such command: %s (see 'blacklist help')\n", argv[0]); + } + +done: + switch_safe_free(data); + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_MODULE_LOAD_FUNCTION(mod_blacklist_load) +{ + switch_api_interface_t *api_interface; + //switch_application_interface_t *app_interface; + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + memset(&globals, 0, sizeof(globals)); + globals.pool = pool; + + switch_core_hash_init(&globals.files, globals.pool); + switch_mutex_init(&globals.files_mutex, SWITCH_MUTEX_NESTED, globals.pool); + + switch_core_hash_init(&globals.lists, globals.pool); + switch_mutex_init(&globals.lists_mutex, SWITCH_MUTEX_NESTED, globals.pool); + + do_config(SWITCH_FALSE); + + SWITCH_ADD_API(api_interface, "blacklist", "Control blacklists", blacklist_api_function, ""); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_blacklist_shutdown) +{ + return SWITCH_STATUS_SUCCESS; +} + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 8d28e3bea2..efc4603d52 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -1840,6 +1840,15 @@ SWITCH_STANDARD_API(ctl_function) switch_core_session_ctl(SCSC_VERBOSE_EVENTS, &arg); stream->write_function(stream, "+OK verbose_events is %s \n", arg ? "on" : "off"); + } else if (!strcasecmp(argv[0], "threaded_system_exec")) { + arg = -1; + if (argv[1]) { + arg = switch_true(argv[1]); + } + + switch_core_session_ctl(SCSC_THREADED_SYSTEM_EXEC, &arg); + + stream->write_function(stream, "+OK threaded_system_exec is %s \n", arg ? "true" : "false"); } else if (!strcasecmp(argv[0], "save_history")) { switch_core_session_ctl(SCSC_SAVE_HISTORY, NULL); @@ -4020,12 +4029,12 @@ SWITCH_STANDARD_API(show_function) } if (strchr(argv[2], '%')) { sprintf(sql, - "select * from channels where hostname='%s' and uuid like '%s' or name like '%s' or cid_name like '%s' or cid_num like '%s' order by created_epoch", - hostname, argv[2], argv[2], argv[2], argv[2]); + "select * from channels where hostname='%s' and uuid like '%s' or name like '%s' or cid_name like '%s' or cid_num like '%s' or presence_data like '%s' order by created_epoch", + hostname, argv[2], argv[2], argv[2], argv[2], argv[2]); } else { sprintf(sql, - "select * from channels where hostname='%s' and uuid like '%%%s%%' or name like '%%%s%%' or cid_name like '%%%s%%' or cid_num like '%%%s%%' order by created_epoch", - hostname, argv[2], argv[2], argv[2], argv[2]); + "select * from channels where hostname='%s' and uuid like '%%%s%%' or name like '%%%s%%' or cid_name like '%%%s%%' or cid_num like '%%%s%%' or presence_data like '%%%s%%' order by created_epoch", + hostname, argv[2], argv[2], argv[2], argv[2], argv[2]); } @@ -4069,11 +4078,11 @@ SWITCH_STANDARD_API(show_function) holder.print_title = 0; if ((cmdname = strchr(command, ' ')) && strcasecmp(cmdname, "as")) { *cmdname++ = '\0'; - switch_snprintf(sql, sizeof(sql) - 1, - "select name, syntax, description, ikey from interfaces where hostname='%s' and type = 'api' and name = '%s' order by name", + switch_snprintfv(sql, sizeof(sql), + "select name, syntax, description, ikey from interfaces where hostname='%s' and type = 'api' and name = '%q' order by name", hostname, cmdname); } else { - switch_snprintf(sql, sizeof(sql) - 1, "select name, syntax, description, ikey from interfaces where hostname='%s' and type = 'api' order by name", hostname); + switch_snprintfv(sql, sizeof(sql), "select name, syntax, description, ikey from interfaces where hostname='%q' and type = 'api' order by name", hostname); } } else if (!strcasecmp(command, "nat_map")) { switch_snprintf(sql, sizeof(sql) - 1, @@ -4262,6 +4271,21 @@ SWITCH_STANDARD_API(uuid_flush_dtmf_function) return SWITCH_STATUS_SUCCESS; } +SWITCH_STANDARD_API(uuid_zombie_exec_function) +{ + switch_core_session_t *fsession; + + if (!zstr(cmd) && (fsession = switch_core_session_locate(cmd))) { + switch_channel_set_flag(switch_core_session_get_channel(fsession), CF_ZOMBIE_EXEC); + switch_core_session_rwunlock(fsession); + stream->write_function(stream, "+OK MMM Brains...\n"); + } else { + stream->write_function(stream, "-ERR no such session\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + #define SETVAR_SYNTAX " [value]" SWITCH_STANDARD_API(uuid_setvar_function) { @@ -5322,6 +5346,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "uuid_simplify", "Try to cut out of a call path / attended xfer", uuid_simplify_function, SIMPLIFY_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_jitterbuffer", "Try to cut out of a call path / attended xfer", uuid_jitterbuffer_function, JITTERBUFFER_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "uuid_zombie_exec", "Set zombie_exec flag on the specified uuid", uuid_zombie_exec_function, ""); SWITCH_ADD_API(commands_api_interface, "xml_flush_cache", "clear xml cache", xml_flush_function, " "); SWITCH_ADD_API(commands_api_interface, "xml_locate", "find some xml", xml_locate_function, "[root |
]"); SWITCH_ADD_API(commands_api_interface, "xml_wrap", "Wrap another api command in xml", xml_wrap_api_function, " "); @@ -5389,7 +5414,9 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) switch_console_set_complete("add show codec"); switch_console_set_complete("add show complete"); switch_console_set_complete("add show dialplan"); - switch_console_set_complete("add show distinct_channels"); + switch_console_set_complete("add show detailed_calls"); + switch_console_set_complete("add show bridged_calls"); + switch_console_set_complete("add show detailed_bridged_calls"); switch_console_set_complete("add show endpoint"); switch_console_set_complete("add show file"); switch_console_set_complete("add show interfaces"); diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index b99ee84160..53d2f3f34a 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -436,8 +436,8 @@ static switch_status_t conference_member_play_file(conference_member_t *member, static switch_status_t conference_member_say(conference_member_t *member, char *text, uint32_t leadin); static uint32_t conference_member_stop_file(conference_member_t *member, file_stop_t stop); static conference_obj_t *conference_new(char *name, conf_xml_cfg_t cfg, switch_core_session_t *session, switch_memory_pool_t *pool); -static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint); +static switch_status_t chat_send(switch_event_t *message_event); + static void launch_conference_record_thread(conference_obj_t *conference, char *path); static void launch_conference_video_bridge_thread(conference_member_t *member_a, conference_member_t *member_b); @@ -2705,19 +2705,16 @@ static void conference_loop_output(conference_member_t *member) if (event->event_id == SWITCH_EVENT_MESSAGE) { char *from = switch_event_get_header(event, "from"); char *to = switch_event_get_header(event, "to"); - char *proto = switch_event_get_header(event, "proto"); - char *subject = switch_event_get_header(event, "subject"); - char *hint = switch_event_get_header(event, "hint"); char *body = switch_event_get_body(event); - char *p, *freeme = NULL; + char *p; if (to && from && body) { if ((p = strchr(to, '+')) && strncmp(to, CONF_CHAT_PROTO, strlen(CONF_CHAT_PROTO))) { - freeme = switch_mprintf("%s+%s@%s", CONF_CHAT_PROTO, member->conference->name, member->conference->domain); - to = freeme; + switch_event_del_header(event, "to"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, + "to", "%s+%s@%s", CONF_CHAT_PROTO, member->conference->name, member->conference->domain); } - chat_send(proto, from, to, subject, body, NULL, hint); - switch_safe_free(freeme); + chat_send(event); } } switch_event_destroy(&event); @@ -6379,12 +6376,26 @@ static void launch_conference_record_thread(conference_obj_t *conference, char * switch_thread_create(&thread, thd_attr, conference_record_thread_run, rec, rec->pool); } -static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint) +static switch_status_t chat_send(switch_event_t *message_event) { char name[512] = "", *p, *lbuf = NULL; conference_obj_t *conference = NULL; switch_stream_handle_t stream = { 0 }; + const char *proto; + const char *from; + const char *to; + //const char *subject; + const char *body; + //const char *type; + const char *hint; + + proto = switch_event_get_header(message_event, "proto"); + from = switch_event_get_header(message_event, "from"); + to = switch_event_get_header(message_event, "to"); + //subject = switch_event_get_header(message_event, "subject"); + body = switch_event_get_body(message_event); + //type = switch_event_get_header(message_event, "type"); + hint = switch_event_get_header(message_event, "hint"); if ((p = strchr(to, '+'))) { to = ++p; @@ -6401,7 +6412,7 @@ static switch_status_t chat_send(const char *proto, const char *from, const char } if (!(conference = conference_find(name))) { - switch_core_chat_send(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", "Conference not active.", NULL, NULL); + switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", "Conference not active.", NULL, NULL); return SWITCH_STATUS_FALSE; } @@ -6419,7 +6430,7 @@ static switch_status_t chat_send(const char *proto, const char *from, const char switch_safe_free(lbuf); - switch_core_chat_send(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", stream.data, NULL, NULL); + switch_core_chat_send_args(proto, CONF_CHAT_PROTO, to, hint && strchr(hint, '/') ? hint : from, "", stream.data, NULL, NULL); switch_safe_free(stream.data); return SWITCH_STATUS_SUCCESS; diff --git a/src/mod/applications/mod_db/mod_db.c b/src/mod/applications/mod_db/mod_db.c index 165e02e949..dccc6c9a2f 100644 --- a/src/mod/applications/mod_db/mod_db.c +++ b/src/mod/applications/mod_db/mod_db.c @@ -622,8 +622,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_db_load) /* register limit interfaces */ SWITCH_ADD_LIMIT(limit_interface, "db", limit_incr_db, limit_release_db, limit_usage_db, limit_reset_db, limit_status_db, NULL); - SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA); - SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA); + SWITCH_ADD_APP(app_interface, "db", "Insert to the db", DB_DESC, db_function, DB_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "group", "Manage a group", GROUP_DESC, group_function, GROUP_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC); SWITCH_ADD_API(commands_api_interface, "db", "db get/set", db_api_function, "[insert|delete|select]///"); switch_console_set_complete("add db insert"); switch_console_set_complete("add db delete"); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 271421a9d3..c088d81111 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -102,37 +102,50 @@ struct action_binding { char *input; char *string; char *value; + switch_digit_action_target_t target; switch_core_session_t *session; }; static switch_status_t digit_nomatch_action_callback(switch_ivr_dmachine_match_t *match) { switch_core_session_t *session = (switch_core_session_t *) match->user_data; - switch_channel_t *channel = switch_core_session_get_channel(session); - char str[DMACHINE_MAX_DIGIT_LEN + 2]; + switch_channel_t *channel; switch_event_t *event; switch_status_t status; + switch_core_session_t *use_session = session; + + if (switch_ivr_dmachine_get_target(match->dmachine) == DIGIT_TARGET_PEER) { + if (switch_core_session_get_partner(session, &use_session) != SWITCH_STATUS_SUCCESS) { + use_session = session; + } + } + + channel = switch_core_session_get_channel(use_session); + switch_channel_set_variable(channel, "last_non_matching_digits", match->match_digits); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s Digit NOT match binding [%s]\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(use_session), SWITCH_LOG_DEBUG, "%s Digit NOT match binding [%s]\n", switch_channel_get_name(channel), match->match_digits); if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "digits", match->match_digits); - if ((status = switch_core_session_queue_event(session, &event)) != SWITCH_STATUS_SUCCESS) { + if ((status = switch_core_session_queue_event(use_session, &event)) != SWITCH_STATUS_SUCCESS) { switch_event_destroy(&event); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s event queue faiure.\n", - switch_core_session_get_name(session)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(use_session), SWITCH_LOG_WARNING, "%s event queue failure.\n", + switch_core_session_get_name(use_session)); } } - /* send it back around flagged to skip the dmachine */ - switch_snprintf(str, sizeof(str), "!%s", match->match_digits); - - switch_channel_queue_dtmf_string(channel, str); + /* send it back around and skip the dmachine */ + switch_channel_queue_dtmf_string(channel, match->match_digits); + if (use_session != session) { + switch_core_session_rwunlock(use_session); + } + + return SWITCH_STATUS_SUCCESS; } @@ -143,9 +156,25 @@ static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match) switch_status_t status; int exec = 0; char *string = act->string; - switch_channel_t *channel = switch_core_session_get_channel(act->session); + switch_channel_t *channel; + switch_core_session_t *use_session = act->session; + int x = 0; + if (switch_ivr_dmachine_get_target(match->dmachine) == DIGIT_TARGET_PEER || act->target == DIGIT_TARGET_PEER || act->target == DIGIT_TARGET_BOTH) { + if (switch_core_session_get_partner(act->session, &use_session) != SWITCH_STATUS_SUCCESS) { + use_session = act->session; + } + } + + top: + x++; + + string = act->string; + exec = 0; + + channel = switch_core_session_get_channel(use_session); switch_channel_set_variable(channel, "last_matching_digits", match->match_digits); + if (switch_event_create_plain(&event, SWITCH_EVENT_CHANNEL_DATA) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(act->session), SWITCH_LOG_DEBUG, "%s Digit match binding [%s][%s]\n", @@ -163,31 +192,62 @@ static switch_status_t digit_action_callback(switch_ivr_dmachine_match_t *match) switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "execute", exec == 2 ? "non-blocking" : "blocking"); } - if ((status = switch_core_session_queue_event(act->session, &event)) != SWITCH_STATUS_SUCCESS) { + if ((status = switch_core_session_queue_event(use_session, &event)) != SWITCH_STATUS_SUCCESS) { switch_event_destroy(&event); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(act->session), SWITCH_LOG_WARNING, "%s event queue faiure.\n", - switch_core_session_get_name(act->session)); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(use_session), SWITCH_LOG_WARNING, "%s event queue faiure.\n", + switch_core_session_get_name(use_session)); } } if (exec) { - char *cmd = switch_core_session_sprintf(act->session, "%s::%s", string, act->value); - switch_ivr_broadcast_in_thread(act->session, cmd, SMF_ECHO_ALEG|SMF_HOLD_BLEG); + char *cmd = switch_core_session_sprintf(use_session, "%s::%s", string, act->value); + switch_ivr_broadcast_in_thread(use_session, cmd, SMF_ECHO_ALEG | (act->target == DIGIT_TARGET_BOTH ? 0 : SMF_HOLD_BLEG)); } + + if (use_session != act->session) { + switch_core_session_rwunlock(use_session); + + if (act->target == DIGIT_TARGET_BOTH) { + use_session = act->session; + goto top; + } + } + return SWITCH_STATUS_SUCCESS; } -#define CLEAR_DIGIT_ACTION_USAGE "|all" +static switch_digit_action_target_t str2target(const char *target_str) +{ + if (!strcasecmp(target_str, "peer")) { + return DIGIT_TARGET_PEER; + } + + if (!strcasecmp(target_str, "both")) { + return DIGIT_TARGET_BOTH; + } + + return DIGIT_TARGET_SELF; +} + +#define CLEAR_DIGIT_ACTION_USAGE "|all[,target]" SWITCH_STANDARD_APP(clear_digit_action_function) { //switch_channel_t *channel = switch_core_session_get_channel(session); switch_ivr_dmachine_t *dmachine; - char *realm = (char *) data; + char *realm = switch_core_session_strdup(session, data); + char *target_str; + switch_digit_action_target_t target = DIGIT_TARGET_SELF; - if ((dmachine = switch_core_session_get_dmachine(session))) { + if ((target_str = strchr(realm, ','))) { + *target_str++ = '\0'; + target = str2target(target_str); + } + + + if ((dmachine = switch_core_session_get_dmachine(session, target))) { if (zstr(realm) || !strcasecmp(realm, "all")) { - switch_core_session_set_dmachine(session, NULL); + switch_core_session_set_dmachine(session, NULL, target); switch_ivr_dmachine_destroy(&dmachine); } else { switch_ivr_dmachine_clear_realm(dmachine, realm); @@ -195,49 +255,41 @@ SWITCH_STANDARD_APP(clear_digit_action_function) } } -#define DIGIT_ACTION_SET_REALM_USAGE "" +#define DIGIT_ACTION_SET_REALM_USAGE "[,]" SWITCH_STANDARD_APP(digit_action_set_realm_function) { switch_ivr_dmachine_t *dmachine; - char *realm = (char *) data; + char *realm = switch_core_session_strdup(session, data); + char *target_str; + switch_digit_action_target_t target = DIGIT_TARGET_SELF; + + if ((target_str = strchr(realm, ','))) { + *target_str++ = '\0'; + target = str2target(target_str); + } if (zstr(data)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", DIGIT_ACTION_SET_REALM_USAGE); return; } + - if ((dmachine = switch_core_session_get_dmachine(session))) { + if ((dmachine = switch_core_session_get_dmachine(session, target))) { switch_ivr_dmachine_set_realm(dmachine, realm); } } -#define BIND_DIGIT_ACTION_USAGE ",,," -SWITCH_STANDARD_APP(bind_digit_action_function) + +static void bind_to_session(switch_core_session_t *session, + const char *arg0, const char *arg1, const char *arg2, const char *arg3, + switch_digit_action_target_t target, switch_digit_action_target_t bind_target) { - switch_channel_t *channel = switch_core_session_get_channel(session); - switch_ivr_dmachine_t *dmachine; - char *mydata; - int argc = 0; - char *argv[4] = { 0 }; struct action_binding *act; + switch_ivr_dmachine_t *dmachine; + switch_channel_t *channel = switch_core_session_get_channel(session); - if (zstr(data)) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); - return; - } - - mydata = switch_core_session_strdup(session, data); - - argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); - - if (argc < 4 || zstr(argv[0]) || zstr(argv[1]) || zstr(argv[2]) || zstr(argv[3])) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); - return; - } - - - if (!(dmachine = switch_core_session_get_dmachine(session))) { + if (!(dmachine = switch_core_session_get_dmachine(session, target))) { uint32_t digit_timeout = 1500; uint32_t input_timeout = 0; const char *var; @@ -256,20 +308,69 @@ SWITCH_STANDARD_APP(bind_digit_action_function) } switch_ivr_dmachine_create(&dmachine, "DPTOOLS", NULL, digit_timeout, input_timeout, NULL, digit_nomatch_action_callback, session); - switch_core_session_set_dmachine(session, dmachine); + switch_core_session_set_dmachine(session, dmachine, target); } act = switch_core_session_alloc(session, sizeof(*act)); - act->realm = argv[0]; - act->input = argv[1]; - act->string = argv[2]; - act->value = argv[3]; + act->realm = switch_core_session_strdup(session, arg0); + act->input = switch_core_session_strdup(session, arg1); + act->string = switch_core_session_strdup(session, arg2); + act->value = switch_core_session_strdup(session, arg3); + act->target = bind_target; act->session = session; - switch_ivr_dmachine_bind(dmachine, act->realm, act->input, 0, digit_action_callback, act); } +#define BIND_DIGIT_ACTION_USAGE ",,,[,][,]" +SWITCH_STANDARD_APP(bind_digit_action_function) +{ + + char *mydata; + int argc = 0; + char *argv[6] = { 0 }; + switch_digit_action_target_t target, bind_target; + char *target_str = "self", *bind_target_str = "self"; + + if (zstr(data)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); + return; + } + + mydata = switch_core_session_strdup(session, data); + + argc = switch_separate_string(mydata, ',', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (argc < 4 || zstr(argv[0]) || zstr(argv[1]) || zstr(argv[2]) || zstr(argv[3])) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Syntax Error, USAGE %s\n", BIND_DIGIT_ACTION_USAGE); + return; + } + + if (argv[4]) { + target_str = argv[4]; + } + + if (argv[5]) { + bind_target_str = argv[5]; + } + + target = str2target(target_str); + bind_target = str2target(bind_target_str); + + + switch(target) { + case DIGIT_TARGET_PEER: + bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_PEER, bind_target); + break; + case DIGIT_TARGET_BOTH: + bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_PEER, bind_target); + bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_SELF, bind_target); + break; + default: + bind_to_session(session, argv[0], argv[1], argv[2], argv[3], DIGIT_TARGET_SELF, bind_target); + break; + } +} #define DETECT_SPEECH_SYNTAX " [] OR grammar [] OR nogrammar OR grammaron/grammaroff OR grammarsalloff OR pause OR resume OR start_input_timers OR stop OR param " SWITCH_STANDARD_APP(detect_speech_function) @@ -904,6 +1005,18 @@ SWITCH_STANDARD_APP(eval_function) return; } +SWITCH_STANDARD_APP(zombie_function) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (switch_channel_up(channel)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s MMM Brains....\n", switch_channel_get_name(channel)); + switch_channel_set_flag(channel, CF_ZOMBIE_EXEC); + } + + return; +} + SWITCH_STANDARD_APP(hangup_function) { @@ -1271,6 +1384,7 @@ SWITCH_STANDARD_APP(event_function) switch_assert(new); memcpy(new, val, len); event->subclass_name = new; + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, var, val); } else { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, var, val); } @@ -1447,7 +1561,7 @@ SWITCH_STANDARD_API(chat_api_function) if (!zstr(cmd) && (lbuf = strdup(cmd)) && (argc = switch_separate_string(lbuf, '|', argv, (sizeof(argv) / sizeof(argv[0])))) >= 4) { - if (switch_core_chat_send(argv[0], "dp", argv[1], argv[2], "", argv[3], !zstr(argv[4]) ? argv[4] : NULL, "") == SWITCH_STATUS_SUCCESS) { + if (switch_core_chat_send_args(argv[0], "dp", argv[1], argv[2], "", argv[3], !zstr(argv[4]) ? argv[4] : NULL, "") == SWITCH_STATUS_SUCCESS) { stream->write_function(stream, "Sent"); } else { stream->write_function(stream, "Error! Message Not Sent"); @@ -3270,44 +3384,51 @@ SWITCH_STANDARD_APP(wait_for_silence_function) switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Usage: %s\n", WAIT_FOR_SILENCE_SYNTAX); } -static switch_status_t event_chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint) +static switch_status_t event_chat_send(switch_event_t *message_event) + { switch_event_t *event; + const char *to; - if (switch_event_create(&event, SWITCH_EVENT_RECV_MESSAGE) == SWITCH_STATUS_SUCCESS) { - if (proto) - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Proto", proto); - if (from) - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "From", from); - if (subject) - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Subject", subject); - if (hint) - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Hint", hint); - if (body) - switch_event_add_body(event, "%s", body); - if (to) { - char *v; - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "To", to); - if ((v = switch_core_get_variable_dup(to))) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command", v); - free(v); - } + switch_event_dup(&event, message_event); + event->event_id = SWITCH_EVENT_RECV_MESSAGE; + + if ((to = switch_event_get_header(event, "to"))) { + char *v; + if ((v = switch_core_get_variable_dup(to))) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Command", v); + free(v); } - - if (switch_event_fire(&event) == SWITCH_STATUS_SUCCESS) { - return SWITCH_STATUS_SUCCESS; - } - - switch_event_destroy(&event); } + if (switch_event_fire(&event) == SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_SUCCESS; + } + + switch_event_destroy(&event); + return SWITCH_STATUS_MEMERR; } -static switch_status_t api_chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint) +static switch_status_t api_chat_send(switch_event_t *message_event) { + const char *proto; + const char *from; + const char *to; + //const char *subject; + //const char *body; + const char *type; + const char *hint; + + proto = switch_event_get_header(message_event, "proto"); + from = switch_event_get_header(message_event, "from"); + to = switch_event_get_header(message_event, "to"); + //subject = switch_event_get_header(message_event, "subject"); + //body = switch_event_get_body(message_event); + type = switch_event_get_header(message_event, "type"); + hint = switch_event_get_header(message_event, "hint"); + + if (to) { char *v = NULL; switch_stream_handle_t stream = { 0 }; @@ -3330,7 +3451,7 @@ static switch_status_t api_chat_send(const char *proto, const char *from, const switch_api_execute(cmd, arg, NULL, &stream); if (proto) { - switch_core_chat_send(proto, "api", to, hint && strchr(hint, '/') ? hint : from, !zstr(type) ? type : NULL, (char *) stream.data, NULL, NULL); + switch_core_chat_send_args(proto, "api", to, hint && strchr(hint, '/') ? hint : from, !zstr(type) ? type : NULL, (char *) stream.data, NULL, NULL); } switch_safe_free(stream.data); @@ -3788,7 +3909,6 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "digit_action_set_realm", "change binding realm", "", digit_action_set_realm_function, DIGIT_ACTION_SET_REALM_USAGE, SAF_SUPPORT_NOMEDIA); - SWITCH_ADD_APP(app_interface, "privacy", "Set privacy on calls", "Set caller privacy on calls.", privacy_function, "off|on|name|full|number", SAF_SUPPORT_NOMEDIA); @@ -3812,7 +3932,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "delay_echo", "echo audio at a specified delay", "Delay n ms", delay_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "strftime", "strftime", "strftime", strftime_function, "[|]", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "phrase", "Say a Phrase", "Say a Phrase", phrase_function, ",", SAF_NONE); - SWITCH_ADD_APP(app_interface, "eval", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SWITCH_ADD_APP(app_interface, "eval", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "stop", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SWITCH_ADD_APP(app_interface, "set_zombie_exec", "Enable Zombie Execution", "Enable Zombie Execution", + zombie_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); SWITCH_ADD_APP(app_interface, "pre_answer", "Pre-Answer the call", "Pre-Answer the call for a channel.", pre_answer_function, "", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "answer", "Answer the call", "Answer the call for a channel.", answer_function, "", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "hangup", "Hangup the call", "Hangup the call for a channel.", hangup_function, "[]", SAF_SUPPORT_NOMEDIA); @@ -3820,29 +3943,29 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "presence", "Send Presence", "Send Presence.", presence_function, " []", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); SWITCH_ADD_APP(app_interface, "log", "Logs to the logger", LOG_LONG_DESC, log_function, " ", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); - SWITCH_ADD_APP(app_interface, "info", "Display Call Info", "Display Call Info", info_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); - SWITCH_ADD_APP(app_interface, "event", "Fire an event", "Fire an event", event_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "info", "Display Call Info", "Display Call Info", info_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "event", "Fire an event", "Fire an event", event_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "sound_test", "Analyze Audio", "Analyze Audio", sound_test_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "export", "Export a channel variable across a bridge", EXPORT_LONG_DESC, export_function, "=", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "bridge_export", "Export a channel variable across a bridge", EXPORT_LONG_DESC, bridge_export_function, "=", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "set", "Set a channel variable", SET_LONG_DESC, set_function, "=", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "push", "Set a channel variable", SET_LONG_DESC, push_function, "=", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "unshift", "Set a channel variable", SET_LONG_DESC, unshift_function, "=", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "set_global", "Set a global variable", SET_GLOBAL_LONG_DESC, set_global_function, "=", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "set_profile_var", "Set a caller profile variable", SET_PROFILE_VAR_LONG_DESC, set_profile_var_function, - "=", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + "=", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "unset", "Unset a channel variable", UNSET_LONG_DESC, unset_function, "", - SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); + SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "ring_ready", "Indicate Ring_Ready", "Indicate Ring_Ready on a channel.", ring_ready_function, "", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "remove_bugs", "Remove media bugs", "Remove all media bugs from a channel.", remove_bugs_function, "", SAF_NONE); SWITCH_ADD_APP(app_interface, "break", "Break", "Set the break flag.", break_function, "", SAF_SUPPORT_NOMEDIA); diff --git a/src/mod/applications/mod_fax/mod_fax.c b/src/mod/applications/mod_fax/mod_fax.c index a899d270fc..340a497a23 100644 --- a/src/mod/applications/mod_fax/mod_fax.c +++ b/src/mod/applications/mod_fax/mod_fax.c @@ -1329,7 +1329,7 @@ static switch_bool_t inband_dtmf_callback(switch_media_bug_t *bug, void *user_da if (digit_str[0]) { char *p = digit_str; while (p && *p) { - switch_dtmf_t dtmf; + switch_dtmf_t dtmf = {0}; dtmf.digit = *p; dtmf.duration = switch_core_default_dtmf_duration(0); switch_channel_queue_dtmf(channel, &dtmf); diff --git a/src/mod/applications/mod_fifo/mod_fifo.c b/src/mod/applications/mod_fifo/mod_fifo.c index 007ee221a3..3d1504fa37 100644 --- a/src/mod/applications/mod_fifo/mod_fifo.c +++ b/src/mod/applications/mod_fifo/mod_fifo.c @@ -1264,8 +1264,9 @@ static void *SWITCH_THREAD_FUNC ringall_thread_run(switch_thread_t *thread, void if (timeout < h->timeout) timeout = h->timeout; if (use_ent) { - stream.write_function(&stream, "{ignore_early_media=true,outbound_redirect_fatal=true,leg_timeout=%d,fifo_outbound_uuid=%s,fifo_name=%s}%s:_:", - h->timeout, h->uuid, node->name, parsed ? parsed : h->originate_string); + stream.write_function(&stream, "{ignore_early_media=true,outbound_redirect_fatal=true,leg_timeout=%d,fifo_outbound_uuid=%s,fifo_name=%s}%s%s", + h->timeout, h->uuid, node->name, + parsed ? parsed : h->originate_string, (i == cbh->rowcount - 1) ? "" : SWITCH_ENT_ORIGINATE_DELIM); } else { stream.write_function(&stream, "[leg_timeout=%d,fifo_outbound_uuid=%s,fifo_name=%s]%s,", h->timeout, h->uuid, node->name, parsed ? parsed : h->originate_string); @@ -1278,10 +1279,6 @@ static void *SWITCH_THREAD_FUNC ringall_thread_run(switch_thread_t *thread, void originate_string = (char *) stream.data; - if (originate_string) { - end_of(originate_string) = '\0'; - } - uuid_list = (char *) stream2.data; if (uuid_list) { @@ -2537,7 +2534,10 @@ SWITCH_STANDARD_APP(fifo_function) if (orbit_ann) { switch_ivr_play_file(session, NULL, orbit_ann, NULL); } - switch_ivr_session_transfer(session, cd.orbit_exten, cd.orbit_dialplan, cd.orbit_context); + + if (strcmp(cd.orbit_exten, "_continue_")) { + switch_ivr_session_transfer(session, cd.orbit_exten, cd.orbit_dialplan, cd.orbit_context); + } } check_ocancel(session); @@ -2971,6 +2971,9 @@ SWITCH_STANDARD_APP(fifo_function) switch_safe_free(sql); + switch_channel_set_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(other_session)); + switch_channel_set_variable(other_channel, SWITCH_SIGNAL_BOND_VARIABLE, switch_core_session_get_uuid(session)); + switch_ivr_multi_threaded_bridge(session, other_session, on_dtmf, other_session, session); if (outbound_id) { @@ -3677,7 +3680,6 @@ void node_dump(switch_stream_handle_t *stream) #define FIFO_API_SYNTAX "list|list_verbose|count|debug|status|importance []|reparse [del_all]" SWITCH_STANDARD_API(fifo_api_function) { - int len = 0; fifo_node_t *node; char *data = NULL; int argc = 0; @@ -3772,9 +3774,8 @@ SWITCH_STANDARD_API(fifo_api_function) for (hi = switch_hash_first(NULL, globals.fifo_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); node = (fifo_node_t *) val; - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); - stream->write_function(stream, "%s:%d:%d:%d\n", (char *) var, node->consumer_count, node_caller_count(node), len); + stream->write_function(stream, "%s:%d:%d\n", (char *) var, node->consumer_count, node_caller_count(node)); switch_mutex_unlock(node->update_mutex); x++; } @@ -3783,9 +3784,8 @@ SWITCH_STANDARD_API(fifo_api_function) stream->write_function(stream, "none\n"); } } else if ((node = switch_core_hash_find(globals.fifo_hash, argv[1]))) { - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); - stream->write_function(stream, "%s:%d:%d:%d\n", argv[1], node->consumer_count, node_caller_count(node), len); + stream->write_function(stream, "%s:%d:%d\n", argv[1], node->consumer_count, node_caller_count(node)); switch_mutex_unlock(node->update_mutex); } else { stream->write_function(stream, "none\n"); @@ -3795,7 +3795,6 @@ SWITCH_STANDARD_API(fifo_api_function) for (hi = switch_hash_first(NULL, globals.fifo_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); node = (fifo_node_t *) val; - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); stream->write_function(stream, "%s:%d\n", (char *) var, node->has_outbound); switch_mutex_unlock(node->update_mutex); @@ -3806,7 +3805,6 @@ SWITCH_STANDARD_API(fifo_api_function) stream->write_function(stream, "none\n"); } } else if ((node = switch_core_hash_find(globals.fifo_hash, argv[1]))) { - len = node_caller_count(node); switch_mutex_lock(node->update_mutex); stream->write_function(stream, "%s:%d\n", argv[1], node->has_outbound); switch_mutex_unlock(node->update_mutex); diff --git a/src/mod/applications/mod_hash/mod_hash.c b/src/mod/applications/mod_hash/mod_hash.c index f9b808fad1..25b3c8abfc 100644 --- a/src/mod/applications/mod_hash/mod_hash.c +++ b/src/mod/applications/mod_hash/mod_hash.c @@ -945,7 +945,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_hash_load) switch_scheduler_add_task(switch_epoch_time_now(NULL) + LIMIT_HASH_CLEANUP_INTERVAL, limit_hash_cleanup_callback, "limit_hash_cleanup", "mod_hash", 0, NULL, SSHF_NONE); - SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA) + SWITCH_ADD_APP(app_interface, "hash", "Insert into the hashtable", HASH_DESC, hash_function, HASH_USAGE, SAF_SUPPORT_NOMEDIA | SAF_ZOMBIE_EXEC) SWITCH_ADD_API(commands_api_interface, "hash", "hash get/set", hash_api_function, "[insert|delete|select]///"); SWITCH_ADD_API(commands_api_interface, "hash_dump", "dump hash/limit_hash data (used for synchronization)", hash_dump_function, HASH_DUMP_SYNTAX); SWITCH_ADD_API(commands_api_interface, "hash_remote", "hash remote", hash_remote_function, HASH_REMOTE_SYNTAX); diff --git a/src/mod/applications/mod_lcr/mod_lcr.c b/src/mod/applications/mod_lcr/mod_lcr.c index a12f9a0ed6..f3e18cc175 100644 --- a/src/mod/applications/mod_lcr/mod_lcr.c +++ b/src/mod/applications/mod_lcr/mod_lcr.c @@ -605,6 +605,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa char *key = NULL; int i = 0; int r = 0; + switch_bool_t lcr_skipped = SWITCH_TRUE; /* assume we'll throw it away, paranoid about leak */ switch_memory_pool_t *pool = cbt->pool; @@ -669,6 +670,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; r = 0; goto end; } @@ -698,6 +700,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; break; } } else { @@ -719,6 +722,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; break; } else if (current->next == NULL) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "adding %s to end of list after %s\n", @@ -729,6 +733,7 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error inserting into dedup hash\n"); r = -1; goto end; } + lcr_skipped = SWITCH_FALSE; break; } } @@ -736,8 +741,13 @@ static int route_add_callback(void *pArg, int argc, char **argv, char **columnNa end: - /* event is freed in lcr_destroy() switch_event_destroy(&additional->fields); */ - + /* lcr was not added to any lists, so destroy lcr object here */ + if (lcr_skipped == SWITCH_TRUE) { + /* ensure we didn't accidentally add additional to the list */ + switch_assert(additional->prev == NULL && current->next != additional); + lcr_destroy(additional); + } + return r; } diff --git a/src/mod/applications/mod_mongo/mod_mongo.cpp b/src/mod/applications/mod_mongo/mod_mongo.cpp index 52e1eadf31..e0b93d85b4 100644 --- a/src/mod/applications/mod_mongo/mod_mongo.cpp +++ b/src/mod/applications/mod_mongo/mod_mongo.cpp @@ -15,7 +15,7 @@ static struct { SWITCH_STANDARD_API(mongo_mapreduce_function) { switch_status_t status = SWITCH_STATUS_SUCCESS; - DBClientConnection *conn = NULL; + DBClientBase *conn = NULL; char *ns = NULL, *json_query = NULL; ns = strdup(cmd); @@ -88,7 +88,7 @@ SWITCH_STANDARD_API(mongo_find_one_function) if (!zstr(ns) && !zstr(json_query) && !zstr(json_fields)) { - DBClientConnection *conn = NULL; + DBClientBase *conn = NULL; try { BSONObj query = fromjson(json_query); @@ -124,7 +124,7 @@ static switch_status_t config(void) const char *cf = "mongo.conf"; switch_xml_t cfg, xml, settings, param; switch_status_t status = SWITCH_STATUS_SUCCESS; - const char *host = "127.0.0.1"; + const char *conn_str = "127.0.0.1"; switch_size_t min_connections = 1, max_connections = 1; if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { @@ -139,7 +139,10 @@ static switch_status_t config(void) int tmp; if (!strcmp(var, "host")) { - host = val; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "'host' is deprecated. use 'connection-string'\n"); + conn_str = val; + } else if (!strcmp(var, "connection-string")) { + conn_str = val; } else if (!strcmp(var, "min-connections")) { if ((tmp = atoi(val)) > 0) { min_connections = tmp; @@ -158,11 +161,11 @@ static switch_status_t config(void) } } - if (mongo_connection_pool_create(&globals.conn_pool, min_connections, max_connections, host) != SWITCH_STATUS_SUCCESS) { + if (mongo_connection_pool_create(&globals.conn_pool, min_connections, max_connections, conn_str) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Can't create connection pool\n"); status = SWITCH_STATUS_GENERR; } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Mongo connection pool created [%s %d/%d]\n", host, (int)min_connections, (int)max_connections); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Mongo connection pool created [%s %d/%d]\n", conn_str, (int)min_connections, (int)max_connections); } switch_xml_free(xml); diff --git a/src/mod/applications/mod_mongo/mod_mongo.h b/src/mod/applications/mod_mongo/mod_mongo.h index 91c14bb29b..d264464294 100644 --- a/src/mod/applications/mod_mongo/mod_mongo.h +++ b/src/mod/applications/mod_mongo/mod_mongo.h @@ -1,7 +1,6 @@ #ifndef MOD_MONGO_H #define MOD_MONGO_H - #include #include #include @@ -10,7 +9,7 @@ using namespace mongo; typedef struct { - char *host; + char *conn_str; switch_size_t min_connections; switch_size_t max_connections; @@ -22,16 +21,16 @@ typedef struct { } mongo_connection_pool_t; -switch_status_t mongo_connection_create(DBClientConnection **connection, const char *host); -void mongo_connection_destroy(DBClientConnection **conn); +switch_status_t mongo_connection_create(DBClientBase **connection, const char *conn_str); +void mongo_connection_destroy(DBClientBase **conn); switch_status_t mongo_connection_pool_create(mongo_connection_pool_t **conn_pool, switch_size_t min_connections, switch_size_t max_connections, - const char *host); + const char *conn_str); void mongo_connection_pool_destroy(mongo_connection_pool_t **conn_pool); -DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool); -switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientConnection *conn); +DBClientBase *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool); +switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientBase *conn); #endif diff --git a/src/mod/applications/mod_mongo/mongo_conn.cpp b/src/mod/applications/mod_mongo/mongo_conn.cpp index ee702b8470..ffcf1200d0 100644 --- a/src/mod/applications/mod_mongo/mongo_conn.cpp +++ b/src/mod/applications/mod_mongo/mongo_conn.cpp @@ -10,24 +10,31 @@ scoped_conn.done(); */ -switch_status_t mongo_connection_create(DBClientConnection **connection, const char *host) +switch_status_t mongo_connection_create(DBClientBase **connection, const char *conn_str) { - DBClientConnection *conn = new DBClientConnection(); + DBClientBase *conn = NULL; + string conn_string(conn_str), err_msg; + ConnectionString cs = ConnectionString::parse(conn_string, err_msg); + + if (!cs.isValid()) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't parse url: %s\n", err_msg.c_str()); + return SWITCH_STATUS_GENERR; + } try { - conn->connect(host); + conn = cs.connect(err_msg); } catch (DBException &e) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't connect to mongo [%s]\n", host); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't connect to mongo [%s]: %s\n", conn_str, err_msg.c_str()); return SWITCH_STATUS_GENERR; } *connection = conn; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to mongo [%s]\n", host); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to mongo [%s]\n", conn_str); return SWITCH_STATUS_SUCCESS; } -void mongo_connection_destroy(DBClientConnection **conn) +void mongo_connection_destroy(DBClientBase **conn) { switch_assert(*conn != NULL); delete *conn; @@ -36,12 +43,12 @@ void mongo_connection_destroy(DBClientConnection **conn) } switch_status_t mongo_connection_pool_create(mongo_connection_pool_t **conn_pool, switch_size_t min_connections, switch_size_t max_connections, - const char *host) + const char *conn_str) { switch_memory_pool_t *pool = NULL; switch_status_t status = SWITCH_STATUS_SUCCESS; mongo_connection_pool_t *cpool = NULL; - DBClientConnection *conn = NULL; + DBClientBase *conn = NULL; if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { return status; @@ -61,13 +68,13 @@ switch_status_t mongo_connection_pool_create(mongo_connection_pool_t **conn_pool cpool->min_connections = min_connections; cpool->max_connections = max_connections; - cpool->host = switch_core_strdup(pool, host); + cpool->conn_str = switch_core_strdup(pool, conn_str); cpool->pool = pool; for (cpool->size = 0; cpool->size < min_connections; cpool->size++) { - if (mongo_connection_create(&conn, host) == SWITCH_STATUS_SUCCESS) { + if (mongo_connection_create(&conn, conn_str) == SWITCH_STATUS_SUCCESS) { mongo_connection_pool_put(cpool, conn); } else { break; @@ -94,7 +101,7 @@ void mongo_connection_pool_destroy(mongo_connection_pool_t **conn_pool) switch_assert(cpool != NULL); while (switch_queue_trypop(cpool->connections, &data) == SWITCH_STATUS_SUCCESS) { - mongo_connection_destroy((DBClientConnection **)&data); + mongo_connection_destroy((DBClientBase **)&data); } switch_mutex_destroy(cpool->mutex); @@ -104,9 +111,9 @@ void mongo_connection_pool_destroy(mongo_connection_pool_t **conn_pool) } -DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool) +DBClientBase *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool) { - DBClientConnection *conn = NULL; + DBClientBase *conn = NULL; void *data = NULL; switch_assert(conn_pool != NULL); @@ -114,8 +121,8 @@ DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool switch_mutex_lock(conn_pool->mutex); if (switch_queue_trypop(conn_pool->connections, &data) == SWITCH_STATUS_SUCCESS) { - conn = (DBClientConnection *) data; - } else if (mongo_connection_create(&conn, conn_pool->host) == SWITCH_STATUS_SUCCESS) { + conn = (DBClientBase *) data; + } else if (mongo_connection_create(&conn, conn_pool->conn_str) == SWITCH_STATUS_SUCCESS) { if (++conn_pool->size > conn_pool->max_connections) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Connection pool is empty. You may want to increase 'max-connections'\n"); } @@ -130,7 +137,7 @@ DBClientConnection *mongo_connection_pool_get(mongo_connection_pool_t *conn_pool return conn; } -switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientConnection *conn) +switch_status_t mongo_connection_pool_put(mongo_connection_pool_t *conn_pool, DBClientBase *conn) { switch_status_t status = SWITCH_STATUS_SUCCESS; diff --git a/src/mod/applications/mod_sms/mod_sms.2008.vcproj b/src/mod/applications/mod_sms/mod_sms.2008.vcproj new file mode 100644 index 0000000000..6d9109b4bb --- /dev/null +++ b/src/mod/applications/mod_sms/mod_sms.2008.vcproj @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/mod/applications/mod_sms/mod_sms.2010.vcxproj b/src/mod/applications/mod_sms/mod_sms.2010.vcxproj new file mode 100644 index 0000000000..4dbbf47874 --- /dev/null +++ b/src/mod/applications/mod_sms/mod_sms.2010.vcxproj @@ -0,0 +1,135 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + mod_sms + {11C9BC3D-45E9-46E3-BE84-B8CEE4685E39} + mod_sms + Win32Proj + + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + DynamicLibrary + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + $(Platform)\$(Configuration)\ + + + + + + + + false + + + + + + + X64 + + + + + + + false + + + MachineX64 + + + + + + + + + false + + + + + + + X64 + + + + + + + false + + + MachineX64 + + + + + + + + {202d7a4e-760d-4d0e-afa1-d7459ced30ff} + false + + + + + + diff --git a/src/mod/applications/mod_sms/mod_sms.c b/src/mod/applications/mod_sms/mod_sms.c new file mode 100644 index 0000000000..70db75a5e3 --- /dev/null +++ b/src/mod/applications/mod_sms/mod_sms.c @@ -0,0 +1,508 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * mod_sms.c -- Abstract SMS + * + */ +#include +#define SMS_CHAT_PROTO "GLOBAL_SMS" +#define MY_EVENT_SEND_MESSAGE "SMS::SEND_MESSAGE" + +/* Prototypes */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown); +SWITCH_MODULE_RUNTIME_FUNCTION(mod_sms_runtime); +SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load); +SWITCH_MODULE_DEFINITION(mod_sms, mod_sms_load, mod_sms_shutdown, NULL); + + +static void event_handler(switch_event_t *event) +{ + const char *dest_proto = switch_event_get_header(event, "dest_proto"); + switch_core_chat_send(dest_proto, event); +} + +typedef enum { + BREAK_ON_TRUE, + BREAK_ON_FALSE, + BREAK_ALWAYS, + BREAK_NEVER +} break_t; + + + +static int parse_exten(switch_event_t *event, switch_xml_t xexten, switch_event_t **extension) +{ + switch_xml_t xcond, xaction, xexpression; + char *exten_name = (char *) switch_xml_attr(xexten, "name"); + int proceed = 0; + char *expression_expanded = NULL, *field_expanded = NULL; + switch_regex_t *re = NULL; + const char *to = switch_event_get_header(event, "to"); + + if (!to) { + to = "nobody"; + } + + if (!exten_name) { + exten_name = "_anon_"; + } + + for (xcond = switch_xml_child(xexten, "condition"); xcond; xcond = xcond->next) { + char *field = NULL; + char *do_break_a = NULL; + char *expression = NULL; + const char *field_data = NULL; + int ovector[30]; + switch_bool_t anti_action = SWITCH_TRUE; + break_t do_break_i = BREAK_ON_FALSE; + + int time_match = switch_xml_std_datetime_check(xcond); + + switch_safe_free(field_expanded); + switch_safe_free(expression_expanded); + + if (switch_xml_child(xcond, "condition")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Nested conditions are not allowed!\n"); + proceed = 1; + goto done; + } + + field = (char *) switch_xml_attr(xcond, "field"); + + if ((xexpression = switch_xml_child(xcond, "expression"))) { + expression = switch_str_nil(xexpression->txt); + } else { + expression = (char *) switch_xml_attr_soft(xcond, "expression"); + } + + if ((expression_expanded = switch_event_expand_headers(event, expression)) == expression) { + expression_expanded = NULL; + } else { + expression = expression_expanded; + } + + if ((do_break_a = (char *) switch_xml_attr(xcond, "break"))) { + if (!strcasecmp(do_break_a, "on-true")) { + do_break_i = BREAK_ON_TRUE; + } else if (!strcasecmp(do_break_a, "on-false")) { + do_break_i = BREAK_ON_FALSE; + } else if (!strcasecmp(do_break_a, "always")) { + do_break_i = BREAK_ALWAYS; + } else if (!strcasecmp(do_break_a, "never")) { + do_break_i = BREAK_NEVER; + } else { + do_break_a = NULL; + } + } + + if (time_match == 1) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s Date/Time Match (PASS) [%s] break=%s\n", + to, exten_name, do_break_a ? do_break_a : "on-false"); + anti_action = SWITCH_FALSE; + } else if (time_match == 0) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s Date/Time Match (FAIL) [%s] break=%s\n", + to, exten_name, do_break_a ? do_break_a : "on-false"); + } + + if (field) { + if (strchr(field, '$')) { + if ((field_expanded = switch_event_expand_headers(event, field)) == field) { + field_expanded = NULL; + field_data = field; + } else { + field_data = field_expanded; + } + } else { + field_data = switch_event_get_header(event, field); + } + if (!field_data) { + field_data = ""; + } + + if ((proceed = switch_regex_perform(field_data, expression, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s Regex (PASS) [%s] %s(%s) =~ /%s/ break=%s\n", + to, exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false"); + anti_action = SWITCH_FALSE; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s Regex (FAIL) [%s] %s(%s) =~ /%s/ break=%s\n", + to, exten_name, field, field_data, expression, do_break_a ? do_break_a : "on-false"); + } + } else if (time_match == -1) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s Absolute Condition [%s]\n", to, exten_name); + anti_action = SWITCH_FALSE; + } + + if (anti_action) { + for (xaction = switch_xml_child(xcond, "anti-action"); xaction; xaction = xaction->next) { + const char *application = switch_xml_attr_soft(xaction, "application"); + const char *loop = switch_xml_attr(xaction, "loop"); + const char *data; + const char *inline_ = switch_xml_attr_soft(xaction, "inline"); + int xinline = switch_true(inline_); + int loop_count = 1; + + if (!zstr(xaction->txt)) { + data = xaction->txt; + } else { + data = (char *) switch_xml_attr_soft(xaction, "data"); + } + + if (!*extension) { + if ((switch_event_create(extension, SWITCH_EVENT_CLONE)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); + abort(); + } + } + + if (loop) { + loop_count = atoi(loop); + } + + for (;loop_count > 0; loop_count--) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s ANTI-Action %s(%s) %s\n", to, application, data, xinline ? "INLINE" : ""); + + if (xinline) { + switch_core_execute_chat_app(event, application, data); + } else { + switch_event_add_header_string(*extension, SWITCH_STACK_BOTTOM, application, zstr(data) ? "__undef" : data); + } + } + proceed = 1; + } + } else { + if (field && strchr(expression, '(')) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "DP_MATCH", NULL); + switch_capture_regex(re, proceed, field_data, ovector, "DP_MATCH", switch_regex_set_event_header_callback, event); + } + + for (xaction = switch_xml_child(xcond, "action"); xaction; xaction = xaction->next) { + char *application = (char *) switch_xml_attr_soft(xaction, "application"); + const char *loop = switch_xml_attr(xaction, "loop"); + char *data = NULL; + char *substituted = NULL; + uint32_t len = 0; + char *app_data = NULL; + const char *inline_ = switch_xml_attr_soft(xaction, "inline"); + int xinline = switch_true(inline_); + int loop_count = 1; + + if (!zstr(xaction->txt)) { + data = xaction->txt; + } else { + data = (char *) switch_xml_attr_soft(xaction, "data"); + } + + if (field && strchr(expression, '(')) { + len = (uint32_t) (strlen(data) + strlen(field_data) + 10) * proceed; + if (!(substituted = malloc(len))) { + abort(); + } + memset(substituted, 0, len); + switch_perform_substitution(re, proceed, data, field_data, substituted, len, ovector); + app_data = substituted; + } else { + app_data = data; + } + + if (!*extension) { + if ((switch_event_create(extension, SWITCH_EVENT_CLONE)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); + abort(); + } + } + + if (loop) { + loop_count = atoi(loop); + } + for (;loop_count > 0; loop_count--) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s Action %s(%s) %s\n", to, application, app_data, xinline ? "INLINE" : ""); + + if (xinline) { + switch_core_execute_chat_app(event, application, app_data); + } else { + switch_event_add_header_string(*extension, SWITCH_STACK_BOTTOM, application, zstr(data) ? "__undef" : data); + } + } + switch_safe_free(substituted); + } + } + switch_regex_safe_free(re); + + if (((anti_action == SWITCH_FALSE && do_break_i == BREAK_ON_TRUE) || + (anti_action == SWITCH_TRUE && do_break_i == BREAK_ON_FALSE)) || do_break_i == BREAK_ALWAYS) { + break; + } + } + + done: + switch_regex_safe_free(re); + switch_safe_free(field_expanded); + switch_safe_free(expression_expanded); + return proceed; +} + + +static switch_event_t *chatplan_hunt(switch_event_t *event) +{ + switch_event_t *extension = NULL; + switch_xml_t alt_root = NULL, cfg, xml = NULL, xcontext, xexten = NULL; + const char *alt_path; + const char *context; + const char *from; + const char *to; + + if (!(context = switch_event_get_header(event, "context"))) { + context = "default"; + } + + if (!(from = switch_event_get_header(event, "from_user"))) { + from = switch_event_get_header(event, "from"); + } + + if (!(to = switch_event_get_header(event, "to_user"))) { + to = switch_event_get_header(event, "to"); + } + + alt_path = switch_event_get_header(event, "alt_path"); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Processing text message %s->%s in context %s\n", from, to, context); + + /* get our handle to the "chatplan" section of the config */ + + if (!zstr(alt_path)) { + switch_xml_t conf = NULL, tag = NULL; + if (!(alt_root = switch_xml_parse_file_simple(alt_path))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of [%s] failed\n", alt_path); + goto done; + } + + if ((conf = switch_xml_find_child(alt_root, "section", "name", "chatplan")) && (tag = switch_xml_find_child(conf, "chatplan", NULL, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Getting chatplan from alternate path: %s\n", alt_path); + xml = alt_root; + cfg = tag; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of chatplan failed\n"); + goto done; + } + } else { + if (switch_xml_locate("chatplan", NULL, NULL, NULL, &xml, &cfg, event, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of chatplan failed\n"); + goto done; + } + } + + /* get a handle to the context tag */ + if (!(xcontext = switch_xml_find_child(cfg, "context", "name", context))) { + if (!(xcontext = switch_xml_find_child(cfg, "context", "name", "global"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Context %s not found\n", context); + goto done; + } + } + + xexten = switch_xml_child(xcontext, "extension"); + + while (xexten) { + int proceed = 0; + const char *cont = switch_xml_attr(xexten, "continue"); + const char *exten_name = switch_xml_attr(xexten, "name"); + + if (!exten_name) { + exten_name = "UNKNOWN"; + } + + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_DEBUG, + "Chatplan: %s parsing [%s->%s] continue=%s\n", + to, context, exten_name, cont ? cont : "false"); + + proceed = parse_exten(event, xexten, &extension); + + if (proceed && !switch_true(cont)) { + break; + } + + xexten = xexten->next; + } + + switch_xml_free(xml); + xml = NULL; + + done: + switch_xml_free(xml); + return extension; +} + + +static switch_status_t chat_send(switch_event_t *message_event) + { + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_event_t *exten; + + if ((exten = chatplan_hunt(message_event))) { + switch_event_header_t *hp; + + for (hp = exten->headers; hp; hp = hp->next) { + status = switch_core_execute_chat_app(message_event, hp->name, hp->value); + if (!SWITCH_READ_ACCEPTABLE(status)) { + break; + } + } + + switch_event_destroy(&exten); + status = SWITCH_STATUS_BREAK; + } + + + return status; + +} + +SWITCH_STANDARD_CHAT_APP(stop_function) +{ + switch_set_flag(message, EF_NO_CHAT_EXEC); + return SWITCH_STATUS_FALSE; +} + +SWITCH_STANDARD_CHAT_APP(send_function) +{ + const char *dest_proto = data; + + if (zstr(dest_proto)) { + dest_proto = switch_event_get_header(message, "dest_proto"); + } + + switch_core_chat_send(dest_proto, message); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_STANDARD_CHAT_APP(set_function) +{ + char *var, *val; + + if (data) { + var = strdup(data); + if ((val = strchr(var, '='))) { + *val++ = '\0'; + } + + if (zstr(val)) { + switch_event_del_header(message, var); + } else { + switch_event_add_header_string(message, SWITCH_STACK_BOTTOM, var, val); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_STANDARD_CHAT_APP(fire_function) +{ + switch_event_t *fireme; + + switch_event_dup(&fireme, message); + switch_event_fire(&fireme); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_STANDARD_CHAT_APP(reply_function) +{ + switch_event_t *reply; + const char *proto = switch_event_get_header(message, "proto"); + + if (proto) { + switch_ivr_create_message_reply(&reply, message, SMS_CHAT_PROTO); + + if (!zstr(data)) { + switch_event_set_body(reply, data); + } + + switch_core_chat_deliver(proto, &reply); + + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_SUCCESS; +} + +/* Macro expands to: switch_status_t mod_sms_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ +SWITCH_MODULE_LOAD_FUNCTION(mod_sms_load) +{ + switch_chat_interface_t *chat_interface; + switch_chat_application_interface_t *chat_app_interface; + + + if (switch_event_bind(modname, SWITCH_EVENT_CUSTOM, MY_EVENT_SEND_MESSAGE, event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); + return SWITCH_STATUS_GENERR; + } + + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + SWITCH_ADD_CHAT(chat_interface, SMS_CHAT_PROTO, chat_send); + + SWITCH_ADD_CHAT_APP(chat_app_interface, "reply", "reply to a message", "reply to a message", reply_function, "", SCAF_NONE); + SWITCH_ADD_CHAT_APP(chat_app_interface, "stop", "stop execution", "stop execution", stop_function, "", SCAF_NONE); + SWITCH_ADD_CHAT_APP(chat_app_interface, "set", "set a variable", "set a variable", set_function, "", SCAF_NONE); + SWITCH_ADD_CHAT_APP(chat_app_interface, "send", "send the message as-is", "send the message as-is", send_function, "", SCAF_NONE); + SWITCH_ADD_CHAT_APP(chat_app_interface, "fire", "fire the message", "fire the message", fire_function, "", SCAF_NONE); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + +/* + Called when the system shuts down + Macro expands to: switch_status_t mod_sms_shutdown() */ +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_sms_shutdown) +{ + switch_event_unbind_callback(event_handler); + + return SWITCH_STATUS_SUCCESS; +} + + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 + */ diff --git a/src/mod/applications/mod_spandsp/Makefile.am b/src/mod/applications/mod_spandsp/Makefile.am index 7d2b30f69d..7865ce5164 100644 --- a/src/mod/applications/mod_spandsp/Makefile.am +++ b/src/mod/applications/mod_spandsp/Makefile.am @@ -12,8 +12,8 @@ SPANDSP_LA=$(SPANDSP_BUILDDIR)/src/libspandsp.la mod_LTLIBRARIES = mod_spandsp.la mod_spandsp_la_SOURCES = mod_spandsp.c udptl.c mod_spandsp_fax.c mod_spandsp_dsp.c mod_spandsp_codecs.c mod_spandsp_la_CFLAGS = $(AM_CFLAGS) -I$(SPANDSP_DIR)/src -I$(TIFF_DIR)/libtiff -I$(SPANDSP_BUILDDIR)/src -I$(TIFF_BUILDDIR)/libtiff -I. -mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA) -mod_spandsp_la_LDFLAGS = -avoid-version -module -no-undefined -shared -ljpeg +mod_spandsp_la_LIBADD = $(switch_builddir)/libfreeswitch.la $(SPANDSP_LA) $(TIFF_LA) -ljpeg -lz +mod_spandsp_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(SPANDSP_LA): $(TIFF_LA) $(SPANDSP_DIR) $(SPANDSP_DIR)/.update cd $(SPANDSP_BUILDDIR) && $(MAKE) -j1 diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c index 8a31a9272d..bed6c1021b 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_dsp.c @@ -52,7 +52,7 @@ static void spandsp_dtmf_rx_realtime_callback(void *user_data, int code, int lev if (digit) { /* prevent duplicate DTMF */ if (digit != pvt->last_digit || (pvt->samples - pvt->last_digit_end) > pvt->min_dup_digit_spacing) { - switch_dtmf_t dtmf; + switch_dtmf_t dtmf = {0}; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(pvt->session), SWITCH_LOG_DEBUG, "DTMF BEGIN DETECTED: [%c]\n", digit); pvt->last_digit = digit; dtmf.digit = digit; diff --git a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c index 0504b0b082..23cb9031bf 100644 --- a/src/mod/applications/mod_spandsp/mod_spandsp_fax.c +++ b/src/mod/applications/mod_spandsp/mod_spandsp_fax.c @@ -806,7 +806,9 @@ static t38_mode_t negotiate_t38(pvt_t *pvt) switch_channel_set_private(channel, "t38_options", NULL); } else { pvt->t38_mode = T38_MODE_NEGOTIATED; - + switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38 SDP Origin = %s\n", t38_options->sdp_o_line); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxVersion = %d\n", t38_options->T38FaxVersion); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38MaxBitRate = %d\n", t38_options->T38MaxBitRate); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "T38FaxFillBitRemoval = %d\n", t38_options->T38FaxFillBitRemoval); @@ -827,7 +829,12 @@ static t38_mode_t negotiate_t38(pvt_t *pvt) t38_options->T38FaxVersion = 3; } t38_options->T38MaxBitRate = (pvt->disable_v17) ? 9600 : 14400; - t38_options->T38FaxFillBitRemoval = 1; + + /* cisco gets mad when we set this to one in a response where they set it to 0, are we allowed to hardcode this to 1 on responses? */ + if (!zstr(t38_options->sdp_o_line) && !switch_stristr("cisco", t38_options->sdp_o_line)) { + t38_options->T38FaxFillBitRemoval = 1; + } + t38_options->T38FaxTranscodingMMR = 0; t38_options->T38FaxTranscodingJBIG = 0; t38_options->T38FaxRateManagement = "transferredTCF"; @@ -1204,6 +1211,7 @@ void mod_spandsp_fax_process_fax(switch_core_session_t *session, const char *dat } else if (switch_channel_test_app_flag_key("T38", channel, CF_APP_T38)) { switch_core_session_message_t msg = { 0 }; pvt->t38_mode = T38_MODE_NEGOTIATED; + switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED); spanfax_init(pvt, T38_MODE); configure_t38(pvt); @@ -1486,13 +1494,14 @@ static switch_status_t t38_gateway_on_soft_execute(switch_core_session_t *sessio spanfax_init(pvt, T38_GATEWAY_MODE); configure_t38(pvt); pvt->t38_mode = T38_MODE_NEGOTIATED; + switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED); } else { if (negotiate_t38(pvt) != T38_MODE_NEGOTIATED) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "%s Could not negotiate T38\n", switch_channel_get_name(channel)); switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); goto end_unlock; } - + switch_channel_set_app_flag_key("T38", channel, CF_APP_T38_NEGOTIATED); spanfax_init(pvt, T38_GATEWAY_MODE); } diff --git a/src/mod/applications/mod_valet_parking/mod_valet_parking.c b/src/mod/applications/mod_valet_parking/mod_valet_parking.c index 0b25a47e94..23196c5226 100644 --- a/src/mod/applications/mod_valet_parking/mod_valet_parking.c +++ b/src/mod/applications/mod_valet_parking/mod_valet_parking.c @@ -39,6 +39,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load); SWITCH_MODULE_DEFINITION(mod_valet_parking, mod_valet_parking_load, NULL, NULL); typedef struct { + char ext[256]; char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1]; time_t timeout; } valet_token_t; @@ -47,6 +48,7 @@ typedef struct { switch_hash_t *hash; switch_mutex_t *mutex; switch_memory_pool_t *pool; + time_t last_timeout_check; } valet_lot_t; static valet_lot_t globals = { 0 }; @@ -92,46 +94,61 @@ static void check_timeouts(void) void *val; time_t now; valet_lot_t *lot; + switch_console_callback_match_t *matches = NULL; + switch_console_callback_match_node_t *m; + switch_hash_index_t *i_hi; + const void *i_var; + void *i_val; + char *i_ext; + valet_token_t *token; now = switch_epoch_time_now(NULL); switch_mutex_lock(globals.mutex); + if (now - globals.last_timeout_check < 30) { + switch_mutex_unlock(globals.mutex); + return; + } - + globals.last_timeout_check = now; for (hi = switch_hash_first(NULL, globals.hash); hi; hi = switch_hash_next(hi)) { - switch_hash_index_t *i_hi; - const void *i_var; - void *i_val; - char *i_ext; - valet_token_t *token; - switch_hash_this(hi, &var, NULL, &val); - lot = (valet_lot_t *) val; - - switch_mutex_lock(lot->mutex); - - top: - - for (i_hi = switch_hash_first(NULL, lot->hash); i_hi; i_hi = switch_hash_next(i_hi)) { - switch_hash_this(i_hi, &i_var, NULL, &i_val); - i_ext = (char *) i_var; - token = (valet_token_t *) i_val; - if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) { - switch_core_hash_delete(lot->hash, i_ext); - switch_safe_free(token); - goto top; - } - } - - switch_mutex_unlock(lot->mutex); + switch_console_push_match(&matches, (const char *) var); } switch_mutex_unlock(globals.mutex); + + + if (matches) { + for (m = matches->head; m; m = m->next) { + + lot = valet_find_lot(m->val); + switch_mutex_lock(lot->mutex); + + top: + + for (i_hi = switch_hash_first(NULL, lot->hash); i_hi; i_hi = switch_hash_next(i_hi)) { + switch_hash_this(i_hi, &i_var, NULL, &i_val); + i_ext = (char *) i_var; + token = (valet_token_t *) i_val; + if (token->timeout > 0 && (token->timeout < now || token->timeout == 1)) { + switch_core_hash_delete(lot->hash, i_ext); + switch_safe_free(token); + goto top; + } + } + + switch_mutex_unlock(lot->mutex); + } + + switch_console_free_matches(&matches); + } + } -static int next_id(valet_lot_t *lot, int min, int max, int in) +static valet_token_t *next_id(switch_core_session_t *session, valet_lot_t *lot, int min, int max, int in) { int i, r = 0; - char buf[128] = ""; + char buf[256] = ""; valet_token_t *token; if (!min) { @@ -148,9 +165,20 @@ static int next_id(valet_lot_t *lot, int min, int max, int in) break; } } + + token = NULL; + + if (r) { + switch_snprintf(buf, sizeof(buf), "%d", r); + switch_zmalloc(token, sizeof(*token)); + switch_set_string(token->uuid, switch_core_session_get_uuid(session)); + switch_set_string(token->ext, buf); + switch_core_hash_insert(lot->hash, buf, token); + } + switch_mutex_unlock(globals.mutex); - return r; + return token; } @@ -166,12 +194,13 @@ SWITCH_STANDARD_APP(valet_parking_function) const char *var; valet_token_t *token = NULL; + + check_timeouts(); + if ((var = switch_channel_get_variable(channel, "valet_announce_slot"))) { play_announce = switch_true(var); } - check_timeouts(); - if (!zstr(data) && (lbuf = switch_core_session_strdup(session, data)) && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) { char *lot_name = argv[0]; @@ -192,7 +221,7 @@ SWITCH_STANDARD_APP(valet_parking_function) const char *io = argv[2]; const char *min = argv[3]; const char *max = argv[4]; - int min_i, max_i, id, in = -1; + int min_i, max_i, in = -1; if (argc < 5) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Usage: %s\n", VALET_APP_SYNTAX); @@ -217,13 +246,13 @@ SWITCH_STANDARD_APP(valet_parking_function) min_i = atoi(min); max_i = atoi(max); - if (!(id = next_id(lot, min_i, max_i, in))) { + if (!(token = next_id(session, lot, min_i, max_i, in))) { switch_ivr_phrase_macro(session, in ? "valet_lot_full" : "valet_lot_empty", "", NULL, NULL); switch_mutex_unlock(lot->mutex); return; } - switch_snprintf(dtmf_buf, sizeof(dtmf_buf), "%d", id); + switch_snprintf(dtmf_buf, sizeof(dtmf_buf), "%s", token->ext); ext = dtmf_buf; } else if (!strcasecmp(ext, "ask")) { const char *prompt = "ivr/ivr-enter_ext_pound.wav"; @@ -269,60 +298,63 @@ SWITCH_STANDARD_APP(valet_parking_function) } } - switch_mutex_lock(lot->mutex); - if ((token = (valet_token_t *) switch_core_hash_find(lot->hash, ext))) { - switch_core_session_t *b_session; + if (!token) { + switch_mutex_lock(lot->mutex); + if ((token = (valet_token_t *) switch_core_hash_find(lot->hash, ext))) { + switch_core_session_t *b_session; - if (token->timeout) { - const char *var = switch_channel_get_variable(channel, "valet_ticket"); + if (token->timeout) { + const char *var = switch_channel_get_variable(channel, "valet_ticket"); - if (!zstr(var)) { - if (!strcmp(var, token->uuid)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Valet ticket %s accepted.\n", var); + if (!zstr(var)) { + if (!strcmp(var, token->uuid)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Valet ticket %s accepted.\n", var); + token->timeout = 0; + switch_channel_set_variable(channel, "valet_ticket", NULL); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid token %s\n", token->uuid); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + return; + } + } + } + + if (!zstr(token->uuid) && (b_session = switch_core_session_locate(token->uuid))) { + if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bridge"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-To-UUID", switch_core_session_get_uuid(session)); + switch_channel_event_set_data(switch_core_session_get_channel(b_session), event); + switch_event_fire(&event); + switch_core_session_rwunlock(b_session); token->timeout = 0; - switch_channel_set_variable(channel, "valet_ticket", NULL); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid token %s\n", token->uuid); - switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + switch_ivr_uuid_bridge(switch_core_session_get_uuid(session), token->uuid); + switch_mutex_unlock(lot->mutex); return; } } } - if (!zstr(token->uuid) && (b_session = switch_core_session_locate(token->uuid))) { - if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VALET_EVENT) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Lot-Name", lot_name); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Valet-Extension", ext); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "bridge"); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Bridge-To-UUID", switch_core_session_get_uuid(session)); - switch_channel_event_set_data(switch_core_session_get_channel(b_session), event); - switch_event_fire(&event); - switch_core_session_rwunlock(b_session); - token->timeout = 0; - switch_ivr_uuid_bridge(switch_core_session_get_uuid(session), token->uuid); - switch_mutex_unlock(lot->mutex); - return; - } - } + token = NULL; + + switch_zmalloc(token, sizeof(*token)); + switch_set_string(token->uuid, switch_core_session_get_uuid(session)); + switch_core_hash_insert(lot->hash, ext, token); } - - token = NULL; - if (!(tmp = switch_channel_get_variable(channel, "valet_hold_music"))) { tmp = switch_channel_get_hold_music(channel); } - if (tmp) + + if (tmp) { music = tmp; + } if (!strcasecmp(music, "silence")) { music = "silence_stream://-1"; } - - switch_zmalloc(token, sizeof(*token)); - switch_set_string(token->uuid, switch_core_session_get_uuid(session)); - switch_core_hash_insert(lot->hash, ext, token); - + dest = switch_core_session_sprintf(session, "set:valet_ticket=%s,set:valet_hold_music=%s,sleep:1000,valet_park:%s %s", token->uuid, music, lot_name, ext); switch_channel_set_variable(channel, "inline_destination", dest); @@ -337,6 +369,7 @@ SWITCH_STANDARD_APP(valet_parking_function) if ((b_session = switch_core_session_locate(uuid))) { token->timeout = switch_epoch_time_now(NULL) + 10; if (play_announce) { + switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL); switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL); } switch_ivr_session_transfer(b_session, dest, "inline", NULL); @@ -348,6 +381,7 @@ SWITCH_STANDARD_APP(valet_parking_function) } if (play_announce) { + switch_ivr_sleep(session, 1500, SWITCH_TRUE, NULL); switch_ivr_phrase_macro(session, "valet_announce_ext", tmp, NULL, NULL); } } @@ -452,6 +486,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_valet_parking_load) return SWITCH_STATUS_TERM; } + memset(&globals, 0, sizeof(globals)); + globals.pool = pool; switch_core_hash_init(&globals.hash, NULL); switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool); diff --git a/src/mod/applications/mod_voicemail/mod_voicemail.c b/src/mod/applications/mod_voicemail/mod_voicemail.c index d21fb47ad8..16d3afa7e1 100644 --- a/src/mod/applications/mod_voicemail/mod_voicemail.c +++ b/src/mod/applications/mod_voicemail/mod_voicemail.c @@ -853,6 +853,7 @@ struct call_control { switch_file_handle_t *fh; char buf[4]; int noexit; + int playback_controls_active; }; typedef struct call_control cc_t; @@ -871,7 +872,13 @@ static switch_status_t control_playback(switch_core_session_t *session, void *in || dtmf->digit == *cc->profile->prev_msg_key || dtmf->digit == *cc->profile->next_msg_key || dtmf->digit == *cc->profile->repeat_msg_key || dtmf->digit == *cc->profile->terminator_key || dtmf->digit == *cc->profile->skip_info_key - || dtmf->digit == *cc->profile->email_key || dtmf->digit == *cc->profile->forward_key)) { + || dtmf->digit == *cc->profile->forward_key)) { + *cc->buf = dtmf->digit; + return SWITCH_STATUS_BREAK; + } + + if (!cc->playback_controls_active + && (dtmf->digit == *cc->profile->email_key)) { *cc->buf = dtmf->digit; return SWITCH_STATUS_BREAK; } @@ -907,10 +914,6 @@ static switch_status_t control_playback(switch_core_session_t *session, void *in switch_core_file_seek(fh, &pos, samps, SEEK_CUR); return SWITCH_STATUS_SUCCESS; } - if (!cc->noexit && dtmf->digit == *cc->profile->terminator_key) { - *cc->buf = dtmf->digit; - return SWITCH_STATUS_BREAK; - } } break; default: @@ -1535,6 +1538,9 @@ static switch_status_t listen_file(switch_core_session_t *session, vm_profile_t msg.from = __FILE__; msg.string_arg = cid_buf; msg.message_id = SWITCH_MESSAGE_INDICATE_DISPLAY; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sending display update [%s] to %s\n", + cid_buf, switch_channel_get_name(channel)); switch_core_session_receive_message(session, &msg); if (!zstr(cbt->cid_number) && (vm_announce_cid = switch_channel_get_variable(channel, "vm_announce_cid"))) { @@ -1568,11 +1574,11 @@ static switch_status_t listen_file(switch_core_session_t *session, vm_profile_t *cc.buf = '\0'; memset(&fh, 0, sizeof(fh)); cc.fh = &fh; - cc.noexit = 1; + cc.playback_controls_active = 1; if (switch_file_exists(cbt->file_path, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { TRY_CODE(switch_ivr_play_file(session, &fh, cbt->file_path, &args)); } - cc.noexit = 0; + cc.playback_controls_active = 0; } if (!*cc.buf && (profile->play_date_announcement == VM_DATE_LAST)) { @@ -2010,10 +2016,10 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p "username='%s' and domain='%s' and flags='save'", (long) switch_epoch_time_now(NULL), myid, domain_name); vm_execute_sql(profile, sql, profile->mutex); - switch_snprintf(sql, sizeof(sql), "select file_path from voicemail_msgs where username='%s' and domain='%s' and flags='delete'", myid, + switch_snprintfv(sql, sizeof(sql), "select file_path from voicemail_msgs where username='%q' and domain='%q' and flags='delete'", myid, domain_name); vm_execute_sql_callback(profile, profile->mutex, sql, unlink_callback, NULL); - switch_snprintf(sql, sizeof(sql), "delete from voicemail_msgs where username='%s' and domain='%s' and flags='delete'", myid, domain_name); + switch_snprintfv(sql, sizeof(sql), "delete from voicemail_msgs where username='%q' and domain='%q' and flags='delete'", myid, domain_name); vm_execute_sql(profile, sql, profile->mutex); vm_check_state = VM_CHECK_FOLDER_SUMMARY; @@ -2299,7 +2305,7 @@ static void voicemail_check_main(switch_core_session_t *session, vm_profile_t *p } thepass = thehash = NULL; - switch_snprintf(sql, sizeof(sql), "select * from voicemail_prefs where username='%s' and domain='%s'", myid, domain_name); + switch_snprintfv(sql, sizeof(sql), "select * from voicemail_prefs where username='%q' and domain='%q'", myid, domain_name); vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt); x_params = switch_xml_child(x_user, "variables"); @@ -2773,6 +2779,10 @@ static switch_status_t deliver_vm(vm_profile_t *profile, if (send_notify) { + if (zstr(vm_notify_email)) { + vm_notify_email = vm_email; + } + if (zstr(profile->notify_email_headers)) { headers = switch_mprintf("From: FreeSWITCH mod_voicemail <%s@%s>\n" "Subject: Voicemail from %s %s\nX-Priority: %d", myid, domain_name, caller_id_name, caller_id_number, priority); @@ -2841,7 +2851,7 @@ static switch_status_t deliver_vm(vm_profile_t *profile, failed: - if (del_file && file_path) { + if (del_file && file_path && switch_file_exists(file_path, pool)) { if (unlink(file_path) != 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to delete file [%s]\n", file_path); } @@ -3215,7 +3225,7 @@ static switch_status_t voicemail_leave_main(switch_core_session_t *session, vm_p goto end; } - switch_snprintf(sql, sizeof(sql), "select * from voicemail_prefs where username='%s' and domain='%s'", id, domain_name); + switch_snprintfv(sql, sizeof(sql), "select * from voicemail_prefs where username='%q' and domain='%q'", id, domain_name); vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt); if (!vm_ext) { @@ -3317,7 +3327,7 @@ static switch_status_t voicemail_leave_main(switch_core_session_t *session, vm_p callback.buf = disk_usage; callback.len = sizeof(disk_usage); - switch_snprintf(sqlstmt, sizeof(sqlstmt), "select sum(message_len) from voicemail_msgs where username='%s' and domain='%s'", id, domain_name); + switch_snprintfv(sqlstmt, sizeof(sqlstmt), "select sum(message_len) from voicemail_msgs where username='%q' and domain='%q'", id, domain_name); vm_execute_sql_callback(profile, profile->mutex, sqlstmt, sql2str_callback, &callback); if (atoi(disk_usage) >= disk_quota) { @@ -3623,7 +3633,7 @@ SWITCH_STANDARD_API(prefs_api_function) } - switch_snprintf(sql, sizeof(sql), "select * from voicemail_prefs where username='%s' and domain='%s'", id, domain); + switch_snprintfv(sql, sizeof(sql), "select * from voicemail_prefs where username='%q' and domain='%q'", id, domain); vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt); if (!strcasecmp(how, "greeting_path")) { diff --git a/src/mod/endpoints/mod_dingaling/mod_dingaling.c b/src/mod/endpoints/mod_dingaling/mod_dingaling.c index 77dab2d092..1a7dd221ef 100644 --- a/src/mod/endpoints/mod_dingaling/mod_dingaling.c +++ b/src/mod/endpoints/mod_dingaling/mod_dingaling.c @@ -489,11 +489,21 @@ static void pres_event_handler(switch_event_t *event) switch_safe_free(sql); } -static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint) +static switch_status_t chat_send(switch_event_t *message_event) { char *user, *host, *f_user = NULL, *ffrom = NULL, *f_host = NULL, *f_resource = NULL; mdl_profile_t *profile = NULL; + const char *proto; + const char *from; + const char *to; + const char *body; + const char *hint; + + proto = switch_event_get_header(message_event, "proto"); + from = switch_event_get_header(message_event, "from"); + to = switch_event_get_header(message_event, "to"); + body = switch_event_get_body(message_event); + hint = switch_event_get_header(message_event, "hint"); switch_assert(proto != NULL); @@ -2876,6 +2886,8 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi char *proto = MDL_CHAT_PROTO; char *pproto = NULL, *ffrom = NULL; char *hint; + switch_event_t *event; + char *from_user, *from_host; #ifdef AUTO_REPLY if (profile->auto_reply) { ldl_handle_send_msg(handle, @@ -2902,10 +2914,41 @@ static ldl_status handle_signalling(ldl_handle_t *handle, ldl_session_t *dlsessi from = ffrom; } - if (strcasecmp(proto, MDL_CHAT_PROTO)) { /* yes no ! on purpose */ - switch_core_chat_send(proto, MDL_CHAT_PROTO, from, to, subject, switch_str_nil(msg), NULL, hint); + from_user = strdup(from); + if ((from_host = strchr(from_user, '@'))) { + *from_host++ = '\0'; } + + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", from_user); + if (from_host) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", from_host); + } + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", subject); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain"); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", hint); + + if (msg) { + switch_event_add_body(event, "%s", msg); + } + } else { + abort(); + } + + switch_safe_free(from_user); + + if (strcasecmp(proto, MDL_CHAT_PROTO)) { /* yes no ! on purpose */ + switch_core_chat_send(proto, event); + } + + switch_core_chat_send("GLOBAL", event); + + switch_event_destroy(&event); + switch_safe_free(pproto); switch_safe_free(ffrom); } diff --git a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp index fe1f7cb8c0..4033577f7c 100644 --- a/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp +++ b/src/mod/endpoints/mod_gsmopen/mod_gsmopen.cpp @@ -928,7 +928,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch switch_channel_t *channel = switch_core_session_get_channel(session); while (p && *p) { - switch_dtmf_t dtmf; + switch_dtmf_t dtmf = {0}; dtmf.digit = *p; dtmf.duration = SWITCH_DEFAULT_DTMF_DURATION; switch_channel_queue_dtmf(channel, &dtmf); diff --git a/src/mod/endpoints/mod_h323/changes.txt b/src/mod/endpoints/mod_h323/changes.txt index 0076291621..80d9119e4d 100644 --- a/src/mod/endpoints/mod_h323/changes.txt +++ b/src/mod/endpoints/mod_h323/changes.txt @@ -1,3 +1,6 @@ +moves the switch_rtp_request_port() call from the contructor to FSH323Connection::CreateRealTimeLogicalChannel() - fix + rtp port leak. tnx to Peter Olsson. +fix log printing fix small interoperability issues if remote endpoint send progress twice make sure dtmfinband gets initialized make gk-identifier and gk-interface settings optional (documentation sayed that about gk-identifier already) diff --git a/src/mod/endpoints/mod_h323/mod_h323.cpp b/src/mod/endpoints/mod_h323/mod_h323.cpp index 72846b07dc..26323ddaea 100644 --- a/src/mod/endpoints/mod_h323/mod_h323.cpp +++ b/src/mod/endpoints/mod_h323/mod_h323.cpp @@ -413,7 +413,7 @@ bool FSH323EndPoint::Initialise(switch_loadable_module_interface_t *iface) } } } - + if (m_fax_old_asn) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "--->fax_old_asn\n"); SetT38_IFP_PRE(); @@ -714,6 +714,7 @@ FSH323Connection::FSH323Connection(FSH323EndPoint& endpoint, H323Transport* tran , m_rtp_resetting(0) , m_isRequst_fax(false) , m_channel_hangup(false) + , m_RTPlocalPort(0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"======>FSH323Connection::FSH323Connection [%p]\n",this); @@ -739,8 +740,6 @@ FSH323Connection::FSH323Connection(FSH323EndPoint& endpoint, H323Transport* tran switch_channel_set_state(m_fsChannel, CS_INIT); } - - m_RTPlocalPort = switch_rtp_request_port((const char *)m_RTPlocalIP.AsString()); } FSH323Connection::~FSH323Connection() @@ -974,6 +973,9 @@ H323Channel* FSH323Connection::CreateRealTimeLogicalChannel(const H323Capability H323TransportAddress m_h323transportadd = GetSignallingChannel()->GetLocalAddress(); m_h323transportadd.GetIpAddress(m_RTPlocalIP); + if (!m_RTPlocalPort) { + m_RTPlocalPort = switch_rtp_request_port((const char *)m_RTPlocalIP.AsString()); + } return new FSH323_ExternalRTPChannel(*this, capability, dir, sessionID,m_RTPlocalIP,m_RTPlocalPort); } @@ -2044,7 +2046,7 @@ PBoolean FSH323_ExternalRTPChannel::Start() , GetMainTypes[m_capability->GetMainType()],(const char*)(m_capability->GetFormatName()),this); } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"%s initialise %s codec %s for connection [%p]\n",switch_channel_get_name(m_fsChannel),((GetDirection() == IsReceiver)? " read" : " write") + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"%s Unsupported ptime of %u on %s %s codec %s for connection [%p]\n",switch_channel_get_name(m_fsChannel),((GetDirection() == IsReceiver)? " read" : " write") , GetMainTypes[m_capability->GetMainType()],(const char*)(m_capability->GetFormatName()),this); if (GetDirection() == IsReceiver) { diff --git a/src/mod/endpoints/mod_rtmp/rtmp.c b/src/mod/endpoints/mod_rtmp/rtmp.c index 1db027405c..10a48b3748 100644 --- a/src/mod/endpoints/mod_rtmp/rtmp.c +++ b/src/mod/endpoints/mod_rtmp/rtmp.c @@ -188,9 +188,14 @@ switch_status_t rtmp_check_auth(rtmp_session_t *rsession, const char *user, cons switch_xml_t xml = NULL, x_param, x_params; switch_bool_t allow_empty_password = SWITCH_FALSE; const char *passwd = NULL; + switch_event_t *locate_params; + + switch_event_create(&locate_params, SWITCH_EVENT_GENERAL); + switch_assert(locate_params); + switch_event_add_header_string(locate_params, SWITCH_STACK_BOTTOM, "source", "mod_rtmp"); /* Locate user */ - if (switch_xml_locate_user_merged("id", user, domain, NULL, &xml, NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_xml_locate_user_merged("id", user, domain, NULL, &xml, locate_params) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_WARNING, "Authentication failed. No such user %s@%s\n", user, domain); goto done; } @@ -231,6 +236,9 @@ done: if (xml) { switch_xml_free(xml); } + + switch_event_destroy(&locate_params); + return status; } diff --git a/src/mod/endpoints/mod_skypopen/mod_skypopen.c b/src/mod/endpoints/mod_skypopen/mod_skypopen.c index 4f095ff886..0360913eb0 100644 --- a/src/mod/endpoints/mod_skypopen/mod_skypopen.c +++ b/src/mod/endpoints/mod_skypopen/mod_skypopen.c @@ -35,7 +35,7 @@ */ #include "skypopen.h" -#define MDL_CHAT_PROTO "skype" +#define SKYPE_CHAT_PROTO "skype" #ifdef WIN32 /***************/ @@ -943,7 +943,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch if(channel){ while (p && *p) { - switch_dtmf_t dtmf; + switch_dtmf_t dtmf = {0}; dtmf.digit = *p; dtmf.duration = SWITCH_DEFAULT_DTMF_DURATION; switch_channel_queue_dtmf(channel, &dtmf); @@ -1858,17 +1858,35 @@ static switch_status_t load_config(int reload_type) return SWITCH_STATUS_SUCCESS; } -static switch_status_t chat_send(const char *proto, const char *from, const char *to, const char *subject, const char *body, const char *type, - const char *hint) +static switch_status_t chat_send(switch_event_t *message_event) + { char *user = NULL, *host, *f_user = NULL, *f_host = NULL, *f_resource = NULL; private_t *tech_pvt = NULL; int i = 0, found = 0, tried = 0; char skype_msg[1024]; + const char *proto; + const char *from; + const char *to; + const char *subject; + const char *body; + //const char *type; + const char *hint; + + proto = switch_event_get_header(message_event, "proto"); + from = switch_event_get_header(message_event, "from"); + to = switch_event_get_header(message_event, "to"); + subject = switch_event_get_header(message_event, "subject"); + body = switch_event_get_body(message_event); + //type = switch_event_get_header(message_event, "type"); + hint = switch_event_get_header(message_event, "hint"); + switch_assert(proto != NULL); - DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type, + //DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type, + // hint ? hint : "NULL"); + DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, hint ? hint : "NULL"); if (!to || !strlen(to)) { @@ -1895,7 +1913,9 @@ static switch_status_t chat_send(const char *proto, const char *from, const char *host++ = '\0'; } - DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type, + //DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, type, + // hint ? hint : "NULL"); + DEBUGA_SKYPE("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, hint=%s)\n", SKYPOPEN_P_LOG, proto, from, to, subject, body, hint ? hint : "NULL"); if (hint && strlen(hint)) { //in hint we receive the interface name to use @@ -1998,7 +2018,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skypopen_load) SWITCH_ADD_API(commands_api_interface, "skypopen", "Skypopen interface commands", skypopen_function, SKYPOPEN_SYNTAX); SWITCH_ADD_API(commands_api_interface, "skypopen_chat", "Skypopen_chat interface remote_skypename TEXT", skypopen_chat_function, SKYPOPEN_CHAT_SYNTAX); - SWITCH_ADD_CHAT(chat_interface, MDL_CHAT_PROTO, chat_send); + SWITCH_ADD_CHAT(chat_interface, SKYPE_CHAT_PROTO, chat_send); if (switch_event_reserve_subclass(MY_EVENT_INCOMING_CHATMESSAGE) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass!\n"); @@ -2944,7 +2964,7 @@ int incoming_chatmessage(private_t *tech_pvt, int which) session = switch_core_session_locate(tech_pvt->session_uuid_str); } if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SKYPE_CHAT_PROTO); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle); @@ -2971,7 +2991,7 @@ int incoming_chatmessage(private_t *tech_pvt, int which) if (!event_sent_to_esl) { if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", MDL_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SKYPE_CHAT_PROTO); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", tech_pvt->name); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", tech_pvt->chatmessages[which].from_dispname); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", tech_pvt->chatmessages[which].from_handle); @@ -2997,6 +3017,33 @@ int incoming_chatmessage(private_t *tech_pvt, int which) return 0; } +static switch_status_t compat_chat_send(const char *proto, const char *from, const char *to, + const char *subject, const char *body, const char *type, const char *hint) +{ + switch_event_t *message_event; + switch_status_t status; + + if (switch_event_create(&message_event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "proto", proto); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "from", from); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "to", to); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "subject", subject); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "type", type); + switch_event_add_header_string(message_event, SWITCH_STACK_BOTTOM, "hint", hint); + + if (body) { + switch_event_add_body(message_event, "%s", body); + } + } else { + abort(); + } + + status = chat_send(message_event); + switch_event_destroy(&message_event); + + return status; + +} SWITCH_STANDARD_API(skypopen_chat_function) { @@ -3037,11 +3084,11 @@ SWITCH_STANDARD_API(skypopen_chat_function) goto end; } else { - NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", SKYPOPEN_P_LOG, MDL_CHAT_PROTO, tech_pvt->skype_user, + NOTICA("chat_send(proto=%s, from=%s, to=%s, subject=%s, body=%s, type=NULL, hint=%s)\n", SKYPOPEN_P_LOG, SKYPE_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE", switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), tech_pvt->name); - chat_send(MDL_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE", - switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, tech_pvt->name); + compat_chat_send(SKYPE_CHAT_PROTO, tech_pvt->skype_user, argv[1], "SIMPLE MESSAGE", + switch_str_nil((char *) &cmd[strlen(argv[0]) + 1 + strlen(argv[1]) + 1]), NULL, tech_pvt->name); } } else { diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 1043c6ea83..0ab0b152ff 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -282,6 +282,8 @@ char *generate_pai_str(private_object_t *tech_pvt) header = (tech_pvt->cid_type == CID_TYPE_RPID && !switch_stristr("aastra", ua)) ? "Remote-Party-ID" : "P-Asserted-Identity"; if (!zstr(callee_name) && !zstr(callee_number)) { + check_decode(callee_name, tech_pvt->session); + if (switch_stristr("update_display", tech_pvt->x_freeswitch_support_remote)) { pai = switch_core_session_sprintf(tech_pvt->session, "%s: \"%s\" <%s>%s\n" "X-FS-Display-Name: %s\nX-FS-Display-Number: %s\n", @@ -1981,12 +1983,15 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi char message[256] = ""; const char *ua = switch_channel_get_variable(tech_pvt->channel, "sip_user_agent"); switch_event_t *event; - + if (zstr(number)) { number = tech_pvt->caller_profile->destination_number; } - if (!sofia_test_flag(tech_pvt, TFLAG_UPDATING_DISPLAY)) { + if (sofia_test_flag(tech_pvt, TFLAG_UPDATING_DISPLAY)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_ERROR, + "Cannot send display update to %s Did not receive reply to last update\n", switch_channel_get_name(tech_pvt->channel)); + } else { if (zstr(tech_pvt->last_sent_callee_id_name) || strcmp(tech_pvt->last_sent_callee_id_name, name) || zstr(tech_pvt->last_sent_callee_id_number) || strcmp(tech_pvt->last_sent_callee_id_number, number)) { @@ -2047,7 +2052,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi switch_channel_set_variable(channel, "last_sent_callee_id_name", name); switch_channel_set_variable(channel, "last_sent_callee_id_number", number); - + if (switch_event_create(&event, SWITCH_EVENT_CALL_UPDATE) == SWITCH_STATUS_SUCCESS) { const char *uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Direction", "SEND"); @@ -3616,12 +3621,12 @@ SWITCH_STANDARD_API(sofia_count_reg_function) if (zstr(user)) { sql = switch_mprintf("select count(*) " "from sip_registrations where (sip_host='%q' or presence_hosts like '%%%q%%')", - (concat != NULL) ? concat : "", domain, domain); + domain, domain); } else { sql = switch_mprintf("select count(*) " - "from sip_registrations where (sip_user='%q' or dir_user='%q') and (sip_host='%q' or presence_hosts like '%%%q%%')", - (concat != NULL) ? concat : "", user, user, domain, domain); + "from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", + user, domain, domain); } switch_assert(sql); sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sql2str_callback, &cb); diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h index b6df8170f3..74de4aab3e 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.h +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -98,6 +98,7 @@ typedef struct private_object private_object_t; #define SOFIA_SIP_RESPONSE_HEADER_PREFIX "sip_rh_" #define SOFIA_SIP_RESPONSE_HEADER_PREFIX_T "~sip_rh_" #define SOFIA_SIP_BYE_HEADER_PREFIX "sip_bye_h_" +#define SOFIA_SIP_BYE_HEADER_PREFIX_T "~sip_bye_h_" #define SOFIA_SIP_PROGRESS_HEADER_PREFIX "sip_ph_" #define SOFIA_SIP_PROGRESS_HEADER_PREFIX_T "~sip_ph_" #define SOFIA_SIP_HEADER_PREFIX_T "~sip_h_" @@ -251,6 +252,7 @@ typedef enum { PFLAG_LIBERAL_DTMF, PFLAG_AUTO_ASSIGN_PORT, PFLAG_AUTO_ASSIGN_TLS_PORT, + PFLAG_SHUTDOWN, /* No new flags below this line */ PFLAG_MAX } PFLAGS; @@ -871,8 +873,8 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void void launch_sofia_profile_thread(sofia_profile_t *profile); -switch_status_t sofia_presence_chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint); +switch_status_t sofia_presence_chat_send(switch_event_t *message_event); + void sofia_glue_tech_absorb_sdp(private_object_t *tech_pvt); /* diff --git a/src/mod/endpoints/mod_sofia/sofia.c b/src/mod/endpoints/mod_sofia/sofia.c index 9c453cbbf8..9601697ab4 100644 --- a/src/mod/endpoints/mod_sofia/sofia.c +++ b/src/mod/endpoints/mod_sofia/sofia.c @@ -506,6 +506,7 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status, private_object_t *tech_pvt; char *extra_headers; const char *call_info = NULL; + const char *vval = NULL; #ifdef MANUAL_BYE int cause; char st[80] = ""; @@ -557,6 +558,18 @@ void sofia_handle_sip_i_bye(switch_core_session_t *session, int status, extra_headers = sofia_glue_get_extra_headers(channel, SOFIA_SIP_BYE_HEADER_PREFIX); sofia_glue_set_extra_headers(channel, sip, SOFIA_SIP_BYE_HEADER_PREFIX); + if (!(vval = switch_channel_get_variable(channel, "sip_copy_custom_headers")) || switch_true(vval)) { + switch_core_session_t *nsession = NULL; + + switch_core_session_get_partner(session, &nsession); + + if (nsession) { + switch_ivr_transfer_variable(session, nsession, SOFIA_SIP_BYE_HEADER_PREFIX_T); + switch_core_session_rwunlock(nsession); + } + } + + switch_channel_hangup(channel, cause); nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_IF(call_info, SIPTAG_CALL_INFO_STR(call_info)), TAG_IF(!zstr(extra_headers), SIPTAG_HEADER_STR(extra_headers)), TAG_END()); @@ -682,6 +695,7 @@ void sofia_update_callee_id(switch_core_session_t *session, sofia_profile_t *pro if ((val = sofia_glue_get_unknown_header(sip, "X-FS-Display-Name"))) { name = (char *) val; + check_decode(name, session); fs++; } @@ -959,6 +973,7 @@ static void our_sofia_event_callback(nua_event_t event, break; case nua_r_shutdown: if (status >= 200) { + sofia_set_pflag(profile, PFLAG_SHUTDOWN); su_root_break(profile->s_root); } break; @@ -1955,9 +1970,17 @@ void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t *thread, void switch_thread_rwlock_wrlock(profile->rwlock); sofia_reg_unregister(profile); nua_shutdown(profile->nua); - su_root_run(profile->s_root); + sanity = 10; + while (!sofia_test_pflag(profile, PFLAG_SHUTDOWN)) { + su_root_step(profile->s_root, 1000); + if (!--sanity) { + break; + } + } + sofia_clear_pflag_locked(profile, PFLAG_RUNNING); + sofia_clear_pflag_locked(profile, PFLAG_SHUTDOWN); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Waiting for worker thread\n"); switch_thread_join(&st, worker_thread); @@ -2197,7 +2220,7 @@ static void parse_gateways(sofia_profile_t *profile, switch_xml_t gateways_tag) } switch_mutex_lock(mod_sofia_globals.hash_mutex); - if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, name)) && (gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey))) { + if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, name)) && (gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) && !gp->deleted) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", name); switch_mutex_unlock(mod_sofia_globals.hash_mutex); free(pkey); @@ -2561,7 +2584,7 @@ static void parse_domain_tag(sofia_profile_t *profile, switch_xml_t x_domain_tag if (sofia_glue_add_profile(switch_core_strdup(profile->pool, dname), profile) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Alias [%s] for profile [%s]\n", dname, profile->name); } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Alias [%s] for profile [%s] (already exists)\n", dname, profile->name); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Alias [%s] for profile [%s] (already exists)\n", dname, profile->name); } } @@ -4181,10 +4204,14 @@ switch_status_t config_sofia(int reload, char *profile_name) } } else if (!strcasecmp(var, "alias")) { sip_alias_node_t *node; - if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) { - if ((node->url = switch_core_strdup(profile->pool, val))) { - node->next = profile->aliases; - profile->aliases = node; + if (zstr(val)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Alias Param has no data...\n"); + } else { + if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) { + if ((node->url = switch_core_strdup(profile->pool, val))) { + node->next = profile->aliases; + profile->aliases = node; + } } } } else if (!strcasecmp(var, "dialplan")) { @@ -5272,6 +5299,9 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, break; case nua_callstate_completing: { + const char *invite_full_from = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_from"); + const char *invite_full_to = switch_channel_get_variable(tech_pvt->channel, "sip_invite_full_to"); + if (r_sdp && sofia_test_flag(tech_pvt, TFLAG_3PCC_INVITE) && !sofia_test_flag(tech_pvt, TFLAG_SDP)) { sofia_set_flag(tech_pvt, TFLAG_SDP); @@ -5295,12 +5325,18 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, const char *invite_route_uri = switch_channel_get_variable(tech_pvt->channel, "sip_invite_route_uri"); nua_ack(nh, - TAG_IF(!zstr(invite_full_via), SIPTAG_VIA_STR(invite_full_via)), - TAG_IF(!zstr(invite_route_uri), SIPTAG_ROUTE_STR(invite_route_uri)), - TAG_END()); + TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)), + TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)), + TAG_IF(!zstr(invite_full_via), SIPTAG_VIA_STR(invite_full_via)), + TAG_IF(!zstr(invite_route_uri), SIPTAG_ROUTE_STR(invite_route_uri)), + TAG_END()); } else { - nua_ack(nh, TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), TAG_END()); + nua_ack(nh, + TAG_IF(invite_full_from, SIPTAG_FROM_STR(invite_full_from)), + TAG_IF(invite_full_to, SIPTAG_TO_STR(invite_full_to)), + TAG_IF(!zstr(tech_pvt->user_via), SIPTAG_VIA_STR(tech_pvt->user_via)), + TAG_END()); } } goto done; @@ -5587,6 +5623,12 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, } goto done; } else { + + if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_NEGOTIATED)) { + nua_respond(tech_pvt->nh, SIP_200_OK, TAG_END()); + goto done; + } + sofia_set_flag_locked(tech_pvt, TFLAG_REINVITE); if (tech_pvt->num_codecs) { @@ -5602,6 +5644,7 @@ static void sofia_handle_sip_i_state(switch_core_session_t *session, int status, if (sofia_glue_tech_choose_port(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { goto done; } + sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); if (sofia_glue_activate_rtp(tech_pvt, 0) != SWITCH_STATUS_SUCCESS) { @@ -7691,6 +7734,12 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ } } + check_decode(displayname, session); + tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), + from_user, + dialplan, + displayname, from_user, network_ip, from_user, aniii, NULL, MODNAME, context, destination_number); + if (!bnh && sip->sip_replaces) { if (!(bnh = nua_handle_by_replaces(nua, sip->sip_replaces))) { if (!(bnh = nua_handle_by_call_id(nua, sip->sip_replaces->rp_call_id))) { @@ -7711,10 +7760,32 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ private_object_t *b_tech_pvt = NULL; const char *app = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_VARIABLE); const char *data = switch_channel_get_variable(b_channel, SWITCH_CURRENT_APPLICATION_DATA_VARIABLE); + switch_caller_profile_t *orig_cp; + const char *sent_name, *sent_number; + orig_cp = switch_channel_get_caller_profile(b_channel); + + sent_name = switch_channel_get_variable(b_channel, "last_sent_callee_id_name"); + sent_number = switch_channel_get_variable(b_channel, "last_sent_callee_id_number"); + + if (!zstr(sent_name) && !zstr(sent_number)) { + tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, sent_name); + tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, sent_number); + } else { + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) { + tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_name); + tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->callee_id_number); + } else { + tech_pvt->caller_profile->callee_id_name = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_name); + tech_pvt->caller_profile->callee_id_number = switch_core_strdup(tech_pvt->caller_profile->pool, orig_cp->caller_id_number); + } + } + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Setting NAT mode based on %s\n", is_nat); + if (app && data && !strcasecmp(app, "conference")) { - destination_number = switch_core_session_sprintf(session, "answer,conference:%s", data); - dialplan = "inline"; + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, "answer,conference:%s", data); + tech_pvt->caller_profile->dialplan = "inline"; } else { if (switch_core_session_check_interface(b_session, sofia_endpoint_interface)) { b_tech_pvt = switch_core_session_get_private(b_session); @@ -7744,7 +7815,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ if (switch_core_session_check_interface(c_session, sofia_endpoint_interface)) { c_tech_pvt = switch_core_session_get_private(c_session); } - + if (!one_leg && (!b_tech_pvt || !sofia_test_flag(b_tech_pvt, TFLAG_SIP_HOLD)) && @@ -7761,21 +7832,25 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ } if (do_conf) { - destination_number = switch_core_session_sprintf(session, "answer,conference:%s@sla+flags{mintwo}", uuid); + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + "answer,conference:%s@sla+flags{mintwo}", uuid); } else { if (one_leg && c_app) { if (c_data) { - destination_number = switch_core_session_sprintf(session, "answer,%s:%s", c_app, c_data); + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + "answer,%s:%s", c_app, c_data); } else { - destination_number = switch_core_session_sprintf(session, "answer,%s", c_app); + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + "answer,%s", c_app); } } else { switch_channel_mark_hold(b_channel, SWITCH_FALSE); - destination_number = switch_core_session_sprintf(session, "answer,intercept:%s", uuid); + tech_pvt->caller_profile->destination_number = switch_core_sprintf(tech_pvt->caller_profile->pool, + "answer,intercept:%s", uuid); } } - dialplan = "inline"; + tech_pvt->caller_profile->dialplan = "inline"; } } switch_core_session_rwunlock(b_session); @@ -7784,11 +7859,7 @@ void sofia_handle_sip_i_invite(nua_t *nua, sofia_profile_t *profile, nua_handle_ nua_handle_unref(bnh); } - check_decode(displayname, session); - tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), - from_user, - dialplan, - displayname, from_user, network_ip, from_user, aniii, NULL, MODNAME, context, destination_number); + if (tech_pvt->caller_profile) { diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c index b9f4ca0f4f..518f1065b8 100644 --- a/src/mod/endpoints/mod_sofia/sofia_glue.c +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -48,12 +48,23 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t * uint32_t port = t38_options->local_port; const char *family = "IP4"; const char *username = tech_pvt->profile->username; - char MMR[32] = ""; - char JBIG[32] = ""; - char FILLBIT[32] = ""; + const char *bit_removal_on = "a=T38FaxFillBitRemoval\n"; + const char *bit_removal_off = ""; + + const char *mmr_on = "a=T38FaxTranscodingMMR\n"; + const char *mmr_off = ""; + + const char *jbig_on = "a=T38FaxTranscodingJBIG\n"; + const char *jbig_off = ""; + const char *var; + int broken_boolean; //sofia_clear_flag(tech_pvt, TFLAG_ENABLE_SOA); + var = switch_channel_get_variable(tech_pvt->channel, "t38_broken_boolean"); + + broken_boolean = switch_true(var); + if (!ip) { if (!(ip = tech_pvt->adv_sdp_audio_ip)) { ip = tech_pvt->proxy_sdp_audio_ip; @@ -94,48 +105,6 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t * "o=%s %010u %010u IN %s %s\n" "s=%s\n" "c=IN %s %s\n" "t=0 0\n", username, tech_pvt->owner_id, tech_pvt->session_id, family, ip, username, family, ip); - if(t38_options->T38FaxFillBitRemoval) { - if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval"))) { - switch_snprintf(FILLBIT, sizeof(FILLBIT), "a=T38FaxFillBitRemoval:1\n"); - } else { - switch_set_string(FILLBIT, "a=T38FaxFillBitRemoval\n"); - } - } else { - if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval"))) { - switch_snprintf(FILLBIT, sizeof(FILLBIT), "a=T38FaxFillBitRemoval:0\n"); - } else { - switch_set_string(FILLBIT, ""); - } - } - - if( t38_options->T38FaxTranscodingMMR) { - if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR"))) { - switch_snprintf(MMR, sizeof(MMR), "a=T38FaxTranscodingMMR:1\n"); - } else { - switch_set_string(MMR, "a=T38FaxTranscodingMMR\n"); - } - } else { - if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR"))) { - switch_snprintf(MMR, sizeof(MMR), "a=T38FaxTranscodingMMR:0\n"); - } else { - switch_set_string(MMR, ""); - } - } - - if( t38_options->T38FaxTranscodingJBIG) { - if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG"))) { - switch_snprintf(JBIG, sizeof(JBIG), "a=T38FaxTranscodingJBIG:1\n"); - } else { - switch_set_string(JBIG, "a=T38FaxTranscodingJBIG\n"); - } - } else { - if (switch_true(switch_channel_get_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG"))) { - switch_snprintf(JBIG, sizeof(JBIG), "a=T38FaxTranscodingJBIG:0\n"); - } else { - switch_set_string(JBIG, ""); - } - } - if (t38_options->T38FaxMaxBuffer) { switch_snprintf(max_buf, sizeof(max_buf), "a=T38FaxMaxBuffer:%d\n", t38_options->T38FaxMaxBuffer); }; @@ -145,6 +114,21 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t * }; + + + if (broken_boolean) { + bit_removal_on = "a=T38FaxFillBitRemoval:1\n"; + bit_removal_off = "a=T38FaxFillBitRemoval:0\n"; + + mmr_on = "a=T38FaxTranscodingMMR:1\n"; + mmr_off = "a=T38FaxTranscodingMMR:0\n"; + + jbig_on = "a=T38FaxTranscodingJBIG:1\n"; + jbig_off = "a=T38FaxTranscodingJBIG:0\n"; + + } + + switch_snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "m=image %d udptl t38\n" "a=T38FaxVersion:%d\n" @@ -160,9 +144,9 @@ void sofia_glue_set_image_sdp(private_object_t *tech_pvt, switch_t38_options_t * port, t38_options->T38FaxVersion, t38_options->T38MaxBitRate, - FILLBIT, - MMR, - JBIG, + t38_options->T38FaxFillBitRemoval ? bit_removal_on : bit_removal_off, + t38_options->T38FaxTranscodingMMR ? mmr_on : mmr_off, + t38_options->T38FaxTranscodingJBIG ? jbig_on : jbig_off, t38_options->T38FaxRateManagement, max_buf, max_data, @@ -201,7 +185,12 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen, for (i = 0; i < tech_pvt->num_codecs; i++) { const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; - + int this_ptime = (imp->microseconds_per_packet / 1000); + + if (!strcasecmp(imp->iananame, "ilbc")) { + this_ptime = 20; + } + if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) { continue; } @@ -210,12 +199,12 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen, if (!cur_ptime) { #if 0 if (ptime) { - if (ptime != imp->microseconds_per_packet / 1000) { + if (ptime != this_ptime) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Codec %s payload %d added to sdp wanting ptime %d but it's already %d (%s:%d:%d), disabling ptime.\n", imp->iananame, tech_pvt->ianacodes[i], - imp->microseconds_per_packet / 1000, + this_ptime, ptime, tech_pvt->codecs[0]->iananame, tech_pvt->codecs[0]->ianacode, @@ -224,15 +213,15 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen, noptime = 1; } } else { - ptime = imp->microseconds_per_packet / 1000; + ptime = this_ptime; } #else if (!ptime) { - ptime = imp->microseconds_per_packet / 1000; + ptime = this_ptime; } #endif } else { - if ((imp->microseconds_per_packet / 1000) != cur_ptime) { + if (this_ptime != cur_ptime) { continue; } } @@ -266,18 +255,23 @@ static void generate_m(private_object_t *tech_pvt, char *buf, size_t buflen, for (i = 0; i < tech_pvt->num_codecs; i++) { const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; char *fmtp = imp->fmtp; - + int this_ptime = imp->microseconds_per_packet / 1000; + if (imp->codec_type != SWITCH_CODEC_TYPE_AUDIO) { continue; } + if (!strcasecmp(imp->iananame, "ilbc")) { + this_ptime = 20; + } + if (!noptime) { if (!cur_ptime) { if (!ptime) { - ptime = imp->microseconds_per_packet / 1000; + ptime = this_ptime; } } else { - if ((imp->microseconds_per_packet / 1000) != cur_ptime) { + if (this_ptime != cur_ptime) { continue; } } @@ -583,6 +577,10 @@ void sofia_glue_set_local_sdp(private_object_t *tech_pvt, const char *ip, switch this_ptime = imp->microseconds_per_packet / 1000; + if (!strcasecmp(imp->iananame, "ilbc")) { + this_ptime = 20; + } + if (cur_ptime != this_ptime) { char *bp = buf; cur_ptime = this_ptime; @@ -1058,8 +1056,9 @@ switch_status_t sofia_glue_tech_choose_port(private_object_t *tech_pvt, int forc tech_pvt->adv_sdp_audio_port = sdp_port; tech_pvt->adv_sdp_audio_ip = tech_pvt->extrtpip = switch_core_session_strdup(tech_pvt->session, use_ip); - switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->local_sdp_audio_ip); switch_channel_set_variable_printf(tech_pvt->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, "%d", sdp_port); + switch_channel_set_variable(tech_pvt->channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); return SWITCH_STATUS_SUCCESS; } @@ -1910,7 +1909,7 @@ void sofia_glue_set_extra_headers(switch_channel_t *channel, sip_t const *sip, c } for (un = sip->sip_unknown; un; un = un->un_next) { - if (!strncasecmp(un->un_name, "X-", 2) || !strncasecmp(un->un_name, "P-", 2)) { + if ((!strncasecmp(un->un_name, "X-", 2) && strncasecmp(un->un_name, "X-FS-", 5)) || !strncasecmp(un->un_name, "P-", 2)) { if (!zstr(un->un_value)) { switch_snprintf(name, sizeof(name), "%s%s", prefix, un->un_name); switch_channel_set_variable(channel, name, un->un_value); @@ -2271,6 +2270,7 @@ switch_status_t sofia_glue_do_invite(switch_core_session_t *session) use_number = tech_pvt->caller_profile->caller_id_number; } + check_decode(use_name, session); switch (cid_type) { case CID_TYPE_PID: @@ -3034,7 +3034,7 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f } if (!(sofia_test_pflag(tech_pvt->profile, PFLAG_REWRITE_TIMESTAMPS) || - ((val = switch_channel_get_variable(tech_pvt->channel, "rtp_rewrite_timestamps")) && !switch_true(val)))) { + ((val = switch_channel_get_variable(tech_pvt->channel, "rtp_rewrite_timestamps")) && switch_true(val)))) { flags |= SWITCH_RTP_FLAG_RAW_WRITE; } @@ -3114,8 +3114,9 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f } switch_snprintf(tmp, sizeof(tmp), "%d", tech_pvt->local_sdp_audio_port); - switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->local_sdp_audio_ip); switch_channel_set_variable(tech_pvt->channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); + switch_channel_set_variable(tech_pvt->channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); if (tech_pvt->rtp_session && sofia_test_flag(tech_pvt, TFLAG_REINVITE)) { const char *rport = NULL; @@ -3355,11 +3356,13 @@ switch_status_t sofia_glue_activate_rtp(private_object_t *tech_pvt, switch_rtp_f if (tech_pvt->te) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send payload to %u\n", tech_pvt->te); switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te); + switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_send_payload", "%d", tech_pvt->te); } if (tech_pvt->recv_te) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Set 2833 dtmf receive payload to %u\n", tech_pvt->recv_te); switch_rtp_set_telephony_recv_event(tech_pvt->rtp_session, tech_pvt->recv_te); + switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_recv_payload", "%d", tech_pvt->recv_te); } if (tech_pvt->audio_recv_pt != tech_pvt->agreed_pt) { @@ -3988,6 +3991,7 @@ void sofia_glue_copy_t38_options(switch_t38_options_t *t38_options, switch_core_ switch_channel_set_private(channel, "t38_options", local_t38_options); } + static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_session_t *sdp, sdp_media_t *m) { switch_t38_options_t *t38_options = switch_channel_get_private(tech_pvt->channel, "t38_options"); @@ -4007,6 +4011,12 @@ static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_ t38_options->remote_port = (switch_port_t)m->m_port; + if (sdp->sdp_origin) { + t38_options->sdp_o_line = switch_core_session_strdup(tech_pvt->session, sdp->sdp_origin->o_username); + } else { + t38_options->sdp_o_line = "unknown"; + } + if (m->m_connections && m->m_connections->c_address) { t38_options->remote_ip = switch_core_session_strdup(tech_pvt->session, m->m_connections->c_address); } else if (sdp && sdp->sdp_connection && sdp->sdp_connection->c_address) { @@ -4019,41 +4029,11 @@ static switch_t38_options_t *tech_process_udptl(private_object_t *tech_pvt, sdp_ } else if (!strcasecmp(attr->a_name, "T38MaxBitRate") && attr->a_value) { t38_options->T38MaxBitRate = (uint32_t) atoi(attr->a_value); } else if (!strcasecmp(attr->a_name, "T38FaxFillBitRemoval")) { - if (switch_stristr("T38FaxFillBitRemoval:", tech_pvt->remote_sdp_str)) { - switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval", "true"); - if (atoi(attr->a_value) == 0) { - t38_options->T38FaxFillBitRemoval = SWITCH_FALSE; - } else { - t38_options->T38FaxFillBitRemoval = SWITCH_TRUE; - } - } else { - switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxFillBitRemoval", "false"); - t38_options->T38FaxFillBitRemoval = SWITCH_TRUE; - } + t38_options->T38FaxFillBitRemoval = switch_safe_atoi(attr->a_value, 1); } else if (!strcasecmp(attr->a_name, "T38FaxTranscodingMMR")) { - if (switch_stristr("T38FaxTranscodingMMR:", tech_pvt->remote_sdp_str)) { - switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR", "true"); - if (atoi(attr->a_value) == 0) { - t38_options->T38FaxTranscodingMMR = SWITCH_FALSE; - } else { - t38_options->T38FaxTranscodingMMR = SWITCH_TRUE; - } - } else { - switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingMMR", "false"); - t38_options->T38FaxTranscodingMMR = SWITCH_TRUE; - } + t38_options->T38FaxTranscodingMMR = switch_safe_atoi(attr->a_value, 1); } else if (!strcasecmp(attr->a_name, "T38FaxTranscodingJBIG")) { - if (switch_stristr("T38FaxTranscodingJBIG:", tech_pvt->remote_sdp_str)) { - switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG", "true"); - if (atoi(attr->a_value) == 0) { - t38_options->T38FaxTranscodingJBIG = SWITCH_FALSE; - } else { - t38_options->T38FaxTranscodingJBIG = SWITCH_TRUE; - } - } else { - switch_channel_set_variable(tech_pvt->channel, "broken_T38FaxTranscodingJBIG", "false"); - t38_options->T38FaxTranscodingJBIG = SWITCH_TRUE; - } + t38_options->T38FaxTranscodingJBIG = switch_safe_atoi(attr->a_value, 1); } else if (!strcasecmp(attr->a_name, "T38FaxRateManagement") && attr->a_value) { t38_options->T38FaxRateManagement = switch_core_session_strdup(tech_pvt->session, attr->a_value); } else if (!strcasecmp(attr->a_name, "T38FaxMaxBuffer") && attr->a_value) { @@ -4331,7 +4311,9 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s } } - if ((m = sdp->sdp_media) && (m->m_mode == sdp_sendonly || m->m_mode == sdp_inactive)) { + if ((m = sdp->sdp_media) && + (m->m_mode == sdp_sendonly || m->m_mode == sdp_inactive || + (m->m_connections && m->m_connections->c_address && !strcmp(m->m_connections->c_address, "0.0.0.0")))) { sendonly = 2; /* global sendonly always wins */ } @@ -4400,6 +4382,11 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s total_codecs = tech_pvt->num_negotiated_codecs; } + if (switch_stristr("T38FaxFillBitRemoval:", r_sdp) || switch_stristr("T38FaxTranscodingMMR:", r_sdp) || + switch_stristr("T38FaxTranscodingJBIG:", r_sdp)) { + switch_channel_set_variable(tech_pvt->channel, "t38_broken_boolean", "true"); + } + for (m = sdp->sdp_media; m; m = m->m_next) { sdp_connection_t *connection; switch_core_session_t *other_session; @@ -4418,6 +4405,11 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s if (got_udptl && m->m_type == sdp_media_image && m->m_port) { switch_t38_options_t *t38_options = tech_process_udptl(tech_pvt, sdp, m); + if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38_NEGOTIATED)) { + match = 1; + goto done; + } + if (switch_channel_test_app_flag_key("T38", tech_pvt->channel, CF_APP_T38)) { sofia_set_flag(tech_pvt, TFLAG_NOREPLY); } @@ -4434,10 +4426,12 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s pass = switch_true(var); } + /* can't remember if this is necessary but its causing a bug so i'll leave this comment here to remind me if (sofia_test_flag(tech_pvt, TFLAG_T38_PASSTHRU)) { pass = 0; } - + */ + if (switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MODE) || switch_channel_test_flag(tech_pvt->channel, CF_PROXY_MEDIA) || !switch_rtp_ready(tech_pvt->rtp_session)) { pass = 0; @@ -4835,13 +4829,16 @@ uint8_t sofia_glue_negotiate_sdp(switch_core_session_t *session, const char *r_s switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send payload to %u\n", best_te); if (tech_pvt->rtp_session) { switch_rtp_set_telephony_event(tech_pvt->rtp_session, (switch_payload_t) best_te); + switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_send_payload", "%d", best_te); } } else { te = tech_pvt->recv_te = tech_pvt->te = (switch_payload_t) best_te; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set 2833 dtmf send/recv payload to %u\n", te); if (tech_pvt->rtp_session) { switch_rtp_set_telephony_event(tech_pvt->rtp_session, te); + switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_send_payload", "%d", te); switch_rtp_set_telephony_recv_event(tech_pvt->rtp_session, te); + switch_channel_set_variable_printf(tech_pvt->channel, "sip_2833_recv_payload", "%d", te); } } } else { @@ -5384,9 +5381,19 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName tech_pvt->remote_port = atoi(switch_str_nil(switch_channel_get_variable(channel, "sip_network_port"))); tech_pvt->caller_profile = switch_channel_get_caller_profile(channel); + if ((tmp = switch_channel_get_variable(tech_pvt->channel, "sip_2833_send_payload"))) { + int te = atoi(tmp); + if (te > 64) { + tech_pvt->te = te; + } + } - - + if ((tmp = switch_channel_get_variable(tech_pvt->channel, "sip_2833_recv_payload"))) { + int te = atoi(tmp); + if (te > 64) { + tech_pvt->recv_te = te; + } + } if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { tech_pvt->dest = switch_core_session_sprintf(session, "sip:%s", switch_channel_get_variable(channel, "sip_req_uri")); @@ -5440,6 +5447,7 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName if (session) { switch_caller_extension_t *extension = NULL; const char *ip = switch_channel_get_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE); + const char *a_ip = switch_channel_get_variable(channel, SWITCH_ADVERTISED_MEDIA_IP_VARIABLE); const char *port = switch_channel_get_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE); const char *r_ip = switch_channel_get_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE); const char *r_port = switch_channel_get_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE); @@ -5479,11 +5487,15 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName tech_pvt->adv_sdp_audio_ip = tech_pvt->extrtpip = (char *) ip; tech_pvt->adv_sdp_audio_port = tech_pvt->local_sdp_audio_port = (switch_port_t)atoi(port); - if ((tmp = switch_channel_get_variable(channel, "local_media_ip"))) { - tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, tmp); + if (!zstr(ip)) { + tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, ip); tech_pvt->rtpip = tech_pvt->local_sdp_audio_ip; } + if (!zstr(a_ip)) { + tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(session, a_ip); + } + if (r_ip && r_port) { tech_pvt->remote_sdp_audio_ip = (char *) r_ip; tech_pvt->remote_sdp_audio_port = (switch_port_t)atoi(r_port); @@ -5541,6 +5553,14 @@ static int recover_callback(void *pArg, int argc, char **argv, char **columnName } } + if (tech_pvt->te) { + switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te); + } + + if (tech_pvt->recv_te) { + switch_rtp_set_telephony_recv_event(tech_pvt->rtp_session, tech_pvt->recv_te); + } + } if (switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) { @@ -6550,12 +6570,12 @@ void sofia_glue_parse_rtp_bugs(switch_rtp_bug_flag_t *flag_pole, const char *str *flag_pole &= ~RTP_BUG_IGNORE_DTMF_DURATION; } - if (switch_stristr("PAUSE_BETWEEN_DTMF", str)) { - *flag_pole |= RTP_BUG_PAUSE_BETWEEN_DTMF; + if (switch_stristr("ACCEPT_ANY_PACKETS", str)) { + *flag_pole |= RTP_BUG_ACCEPT_ANY_PACKETS; } - if (switch_stristr("~PAUSE_BETWEEN_DTMF", str)) { - *flag_pole &= ~RTP_BUG_PAUSE_BETWEEN_DTMF; + if (switch_stristr("~ACCEPT_ANY_PACKETS", str)) { + *flag_pole &= ~RTP_BUG_ACCEPT_ANY_PACKETS; } } diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c index a85138b8f6..06b1ce496e 100644 --- a/src/mod/endpoints/mod_sofia/sofia_presence.c +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -72,8 +72,8 @@ struct presence_helper { char last_uuid[512]; }; -switch_status_t sofia_presence_chat_send(const char *proto, const char *from, const char *to, const char *subject, - const char *body, const char *type, const char *hint) +switch_status_t sofia_presence_chat_send(switch_event_t *message_event) + { char *prof = NULL, *user = NULL, *host = NULL; sofia_profile_t *profile = NULL; @@ -93,6 +93,27 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co char *dup_dest = NULL; char *p = NULL; char *remote_host = NULL; + const char *proto; + const char *from; + const char *to; + //const char *subject; + const char *body; + const char *type; + const char *from_full; + char header[256] = ""; + char *route_uri = NULL; + const char *network_ip = NULL, *network_port = NULL; + + proto = switch_event_get_header(message_event, "proto"); + from = switch_event_get_header(message_event, "from"); + to = switch_event_get_header(message_event, "to"); + //subject = switch_event_get_header(message_event, "subject"); + body = switch_event_get_body(message_event); + type = switch_event_get_header(message_event, "type"); + from_full = switch_event_get_header(message_event, "from_full"); + + network_ip = switch_event_get_header(message_event, "to_sip_ip"); + network_port = switch_event_get_header(message_event, "to_sip_port"); if (!to) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing To: header.\n"); @@ -114,6 +135,10 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co user = prof; prof = NULL; } + + if (!prof) { + prof = switch_event_get_header(message_event, "sip_profile"); + } if (!strncasecmp(user, "sip:", 4)) { to_uri = user; @@ -125,13 +150,14 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co } else { host++; } - if (!prof) + if (!prof) { prof = host; + } } if (!prof || !(profile = sofia_glue_find_profile(prof))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to, + "Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to, body ? body : "[no body]", prof ? prof : "NULL"); goto end; } @@ -143,24 +169,34 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co } } + if (to_uri) { switch_console_push_match(&list, to_uri); } else if (!(list = sofia_reg_find_reg_url_multi(profile, user, host))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't find registered user %s@%s\n", user, host); - goto end; + sofia_profile_t *test; + + if ((test = sofia_glue_find_profile(host))) { + sofia_glue_release_profile(test); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not sending to local box for %s@%s\n", user, host); + /* our box let's not send it */ + } else { + char *tmp; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Can't find registered user %s@%s\n", user, host); + tmp = switch_mprintf("sip:%s@%s", user, host); + switch_console_push_match(&list, tmp); + free(tmp); + } + } if (!strcasecmp(proto, SOFIA_CHAT_PROTO)) { - from = hint; + from = from_full; } else { char *fp, *p = NULL; fp = strdup(from); + switch_assert(fp); - if (!fp) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); - goto end; - } if ((p = strchr(fp, '@'))) { *p++ = '\0'; @@ -173,12 +209,23 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co } } - ffrom = switch_mprintf("\"%s\" ", fp, proto, fp, p); + if (switch_stristr("global", proto)) { + ffrom = switch_mprintf("\"%s\" ", fp, fp, p); + } else { + ffrom = switch_mprintf("\"%s\" ", fp, proto, fp, p); + } from = ffrom; switch_safe_free(fp); } + if (!list) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nNobody to send to: Profile %s\n", proto, from, to, + body ? body : "[no body]", prof ? prof : "NULL"); + goto end; + } + for (m = list->head; m; m = m->next) { if (!(dst = sofia_glue_get_destination(m->val))) { @@ -233,22 +280,35 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co } /* if this cries, add contact here too, change the 1 to 0 and omit the safe_free */ - + + //printf("DEBUG To: [%s] From: [%s] Contact: [%s] RURI [%s] ip [%s] port [%s]\n", to, from, contact, dst->route_uri, network_ip, network_port); + + //DUMP_EVENT(message_event); + + if (zstr(dst->route_uri) && !zstr(user) && !zstr(network_ip) && (zstr(host) || strcmp(network_ip, host))) { + route_uri = switch_mprintf("sip:%s@%s:%s", user, network_ip, network_port); + } + msg_nh = nua_handle(profile->nua, NULL, - TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)), - TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)), - SIPTAG_FROM_STR(from), - TAG_IF(contact, NUTAG_URL(contact)), - SIPTAG_TO_STR(dup_dest), - SIPTAG_CONTACT_STR(contact_str), TAG_END()); nua_handle_bind(msg_nh, &mod_sofia_globals.destroy_private); + switch_snprintf(header, sizeof(header), "X-FS-Sending-Message: %s", switch_core_get_uuid()); + nua_message(msg_nh, + TAG_IF(dst->route_uri, NUTAG_PROXY(dst->route_uri)), + TAG_IF(route_uri, NUTAG_PROXY(route_uri)), + TAG_IF(dst->route, SIPTAG_ROUTE_STR(dst->route)), + SIPTAG_FROM_STR(from), + TAG_IF(contact, NUTAG_URL(contact)), + SIPTAG_TO_STR(dup_dest), + SIPTAG_CONTACT_STR(contact_str), + TAG_IF(user_via, SIPTAG_VIA_STR(user_via)), SIPTAG_CONTENT_TYPE_STR(ct), SIPTAG_PAYLOAD_STR(body), + SIPTAG_HEADER_STR(header), TAG_END()); sofia_glue_free_destination(dst); @@ -261,6 +321,7 @@ switch_status_t sofia_presence_chat_send(const char *proto, const char *from, co end: switch_safe_free(contact); + switch_safe_free(route_uri); switch_safe_free(ffrom); switch_safe_free(dup); @@ -702,7 +763,7 @@ static void actual_sofia_presence_event_handler(switch_event_t *event) "from sip_registrations " "left join sip_dialogs on " - "sip_dialogs.presence_id = sip_registrations.sip_user || '@' || sip_registrations.sip_host " + "sip_dialogs.presence_id = sip_registrations.sip_user %q '@' %q sip_registrations.sip_host " "or (sip_dialogs.sip_from_user = sip_registrations.sip_user " "and sip_dialogs.sip_from_host = sip_registrations.sip_host) " @@ -713,6 +774,7 @@ static void actual_sofia_presence_event_handler(switch_event_t *event) "(sip_registrations.orig_server_host='%q' or sip_registrations.sip_host='%q' " "or sip_registrations.presence_hosts like '%%%q%%'))", dh.status, dh.rpid, + switch_sql_concat(), switch_sql_concat(), probe_euser, probe_host, probe_euser, probe_host, probe_host, probe_host); switch_assert(sql); @@ -1983,14 +2045,21 @@ static int broadsoft_sla_gather_state_callback(void *pArg, int argc, char **argv if (uuid && (session = switch_core_session_locate(uuid))) { switch_channel_t *channel = switch_core_session_get_channel(session); - if (zstr((callee_name = switch_channel_get_variable(channel, "effective_callee_id_name"))) && - zstr((callee_name = switch_channel_get_variable(channel, "sip_callee_id_name")))) { - callee_name = switch_channel_get_variable(channel, "callee_id_name"); - } - - if (zstr((callee_number = switch_channel_get_variable(channel, "effective_callee_id_number"))) && - zstr((callee_number = switch_channel_get_variable(channel, "sip_callee_id_number")))) { - callee_number = switch_channel_get_variable(channel, "destination_number"); + if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) { + + if (zstr((callee_name = switch_channel_get_variable(channel, "effective_callee_id_name"))) && + zstr((callee_name = switch_channel_get_variable(channel, "sip_callee_id_name")))) { + callee_name = switch_channel_get_variable(channel, "callee_id_name"); + } + + if (zstr((callee_number = switch_channel_get_variable(channel, "effective_callee_id_number"))) && + zstr((callee_number = switch_channel_get_variable(channel, "sip_callee_id_number"))) && + zstr((callee_number = switch_channel_get_variable(channel, "callee_id_number")))) { + callee_number = switch_channel_get_variable(channel, "destination_number"); + } + } else { + callee_name = switch_channel_get_variable(channel, "caller_id_name"); + callee_number = switch_channel_get_variable(channel, "caller_id_number"); } if (zstr(callee_name) && !zstr(callee_number)) { @@ -2849,13 +2918,25 @@ void sofia_presence_handle_sip_i_message(int status, const char *to_host = NULL; sip_payload_t *payload = sip->sip_payload; char *msg = NULL; + const char *us; + char network_ip[80]; + int network_port = 0; + + if ((us = sofia_glue_get_unknown_header(sip, "X-FS-Sending-Message")) && !strcmp(us, switch_core_get_uuid())) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Not sending message to ourselves!\n"); + goto end; + } if (sip->sip_content_type && sip->sip_content_type->c_subtype) { if (strstr(sip->sip_content_type->c_subtype, "composing")) { - return; + goto end; } } + + sofia_glue_get_addr(de->data->e_msg, network_ip, sizeof(network_ip), &network_port); + + if (from) { from_user = from->a_url->url_user; from_host = from->a_url->url_host; @@ -2867,7 +2948,7 @@ void sofia_presence_handle_sip_i_message(int status, } if (!to_user) { - return; + goto end; } if (payload) { @@ -2906,33 +2987,56 @@ void sofia_presence_handle_sip_i_message(int status, sofia_presence_set_hash_key(hash_key, sizeof(hash_key), sip); } - if (sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT) && (tech_pvt = (private_object_t *) switch_core_hash_find(profile->chat_hash, hash_key))) { - if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from_addr); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "hint", full_from); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to_addr); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); - if (msg) { - switch_event_add_body(event, "%s", msg); - } + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "login", profile->url); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from", from_addr); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_user", from_user); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_host", from_host); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_user", to_user); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to_host", to_host); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_sip_ip", network_ip); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from_sip_port", "%d", network_port); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "to", to_addr); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); - if (switch_core_session_queue_event(tech_pvt->session, &event) != SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); - switch_event_fire(&event); - } + + if (sip->sip_content_type && sip->sip_content_type->c_subtype) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", sip->sip_content_type->c_type); + } else { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "text/plain"); + } + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "from_full", full_from); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "sip_profile", profile->name); + + if (msg) { + switch_event_add_body(event, "%s", msg); } } else { - switch_core_chat_send(proto, SOFIA_CHAT_PROTO, from_addr, to_addr, "", msg, NULL, full_from); + abort(); } + + if (sofia_test_pflag(profile, PFLAG_IN_DIALOG_CHAT) && (tech_pvt = (private_object_t *) switch_core_hash_find(profile->chat_hash, hash_key))) { + switch_core_session_queue_event(tech_pvt->session, &event); + } else { + switch_core_chat_send(proto, event); + switch_event_destroy(&event); + } + switch_safe_free(to_addr); switch_safe_free(from_addr); + if (full_from) { su_free(nh->nh_home, full_from); } } } + + end: + + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(de->data->e_msg), TAG_END()); + } void sofia_presence_set_chat_hash(private_object_t *tech_pvt, sip_t const *sip) diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c index 1c9715d5a1..a65c205b8f 100644 --- a/src/mod/endpoints/mod_sofia/sofia_reg.c +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -241,37 +241,41 @@ void sofia_sub_check_gateway(sofia_profile_t *profile, time_t now) void sofia_reg_check_gateway(sofia_profile_t *profile, time_t now) { - sofia_gateway_t *gateway_ptr, *last = NULL; + sofia_gateway_t *check, *gateway_ptr, *last = NULL; switch_event_t *event; - char *pkey; switch_mutex_lock(profile->gw_mutex); for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { - if (gateway_ptr->deleted && gateway_ptr->state == REG_STATE_NOREG) { - if (last) { - last->next = gateway_ptr->next; - } else { - profile->gateways = gateway_ptr->next; + if (gateway_ptr->deleted) { + if ((check = switch_core_hash_find(mod_sofia_globals.gateway_hash, gateway_ptr->name)) && check == gateway_ptr) { + char *pkey = switch_mprintf("%s::%s", profile->name, gateway_ptr->name); + switch_assert(pkey); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing gateway %s from hash.\n", pkey); + switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); + switch_core_hash_delete(mod_sofia_globals.gateway_hash, gateway_ptr->name); + free(pkey); } - pkey = switch_mprintf("%s::%s", profile->name, gateway_ptr->name); + if (gateway_ptr->state == REG_STATE_NOREG) { - switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); - switch_core_hash_delete(mod_sofia_globals.gateway_hash, gateway_ptr->name); - - free(pkey); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleted gateway %s\n", gateway_ptr->name); - if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_GATEWAY_DEL) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile-name", gateway_ptr->profile->name); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Gateway", gateway_ptr->name); - switch_event_fire(&event); - } - if (gateway_ptr->ob_vars) { - switch_event_destroy(&gateway_ptr->ob_vars); - } - if (gateway_ptr->ib_vars) { - switch_event_destroy(&gateway_ptr->ib_vars); + if (last) { + last->next = gateway_ptr->next; + } else { + profile->gateways = gateway_ptr->next; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Deleted gateway %s\n", gateway_ptr->name); + if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, MY_EVENT_GATEWAY_DEL) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "profile-name", gateway_ptr->profile->name); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Gateway", gateway_ptr->name); + switch_event_fire(&event); + } + if (gateway_ptr->ob_vars) { + switch_event_destroy(&gateway_ptr->ob_vars); + } + if (gateway_ptr->ib_vars) { + switch_event_destroy(&gateway_ptr->ib_vars); + } } } else { last = gateway_ptr; @@ -672,10 +676,10 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_reg_del_callback, profile); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and expires <= %ld and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and expires <= %ld and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); @@ -683,11 +687,11 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) if (now) { - switch_snprintf(sql, sizeof(sql), "select call_id from sip_shared_appearance_dialogs where hostname='%s' " + switch_snprintfv(sql, sizeof(sql), "select call_id from sip_shared_appearance_dialogs where hostname='%q' " "and profile_name='%s' and expires <= %ld", mod_sofia_globals.hostname, profile->name, (long) now); sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sla_dialog_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_shared_appearance_dialogs where expires > 0 and hostname='%s' and expires <= %ld", + switch_snprintfv(sql, sizeof(sql), "delete from sip_shared_appearance_dialogs where expires > 0 and hostname='%q' and expires <= %ld", mod_sofia_globals.hostname, (long) now); @@ -696,19 +700,19 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_presence where expires > 0 and expires <= %ld and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_presence where expires > 0 and expires <= %ld and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and expires <= %ld and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and expires <= %ld and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); @@ -718,27 +722,27 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot) "select call_id from sip_subscriptions where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%s'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sub_del_callback, profile); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_subscriptions where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_subscriptions where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); if (now) { - switch_snprintf(sql, sizeof(sql), "delete from sip_dialogs where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "delete from sip_dialogs where (expires = -1 or (expires > 0 and expires <= %ld)) and hostname='%q'", (long) now, mod_sofia_globals.hostname); } else { - switch_snprintf(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); } sofia_glue_actually_execute_sql(profile, sql, NULL); @@ -829,24 +833,24 @@ void sofia_reg_check_sync(sofia_profile_t *profile) sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_reg_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_presence where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%s'", + switch_snprintfv(sql, sizeof(sql), "select sub_to_user,sub_to_host,call_id from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_execute_sql_callback(profile, NULL, sql, sofia_sub_del_callback, profile); - switch_snprintf(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_subscriptions where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); - switch_snprintf(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%s'", mod_sofia_globals.hostname); + switch_snprintfv(sql, sizeof(sql), "delete from sip_dialogs where expires >= -1 and hostname='%q'", mod_sofia_globals.hostname); sofia_glue_actually_execute_sql(profile, sql, NULL); switch_mutex_unlock(profile->ireg_mutex); @@ -867,10 +871,10 @@ char *sofia_reg_find_reg_url(sofia_profile_t *profile, const char *user, const c cbt.len = len; if (host) { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')", + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", user, host, host); } else { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s'", user); + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q'", user); } @@ -896,10 +900,10 @@ switch_console_callback_match_t *sofia_reg_find_reg_url_multi(sofia_profile_t *p } if (host) { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')", + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", user, host, host); } else { - switch_snprintf(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%s'", user); + switch_snprintfv(sql, sizeof(sql), "select contact from sip_registrations where sip_user='%q'", user); } @@ -920,10 +924,10 @@ switch_console_callback_match_t *sofia_reg_find_reg_url_with_positive_expires_mu } if (host) { - switch_snprintf(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%s' and (sip_host='%s' or presence_hosts like '%%%s%%')", + switch_snprintfv(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%q' and (sip_host='%q' or presence_hosts like '%%%q%%')", user, host, host); } else { - switch_snprintf(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%s'", user); + switch_snprintfv(sql, sizeof(sql), "select contact,expires from sip_registrations where sip_user='%q'", user); } sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_reg_find_reg_with_positive_expires_callback, &cbt); @@ -1397,7 +1401,7 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand } /* Log line added to support Fail2Ban */ if (sofia_test_pflag(profile, PFLAG_LOG_AUTH_FAIL)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "SIP auth challenge (%s) on sofia profile '%s' " + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG10, "SIP auth challenge (%s) on sofia profile '%s' " "for [%s@%s] from ip %s\n", (regtype == REG_INVITE) ? "INVITE" : "REGISTER", profile->name, to_user, to_host, network_ip); } @@ -1708,10 +1712,16 @@ uint8_t sofia_reg_handle_register(nua_t *nua, sofia_profile_t *profile, nua_hand } } } else { + const char *username = "unknown"; + if (auth_params) { + username = switch_event_get_header(auth_params, "sip_auth_username"); + } + switch_core_del_registration(to_user, reg_host, call_id); if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_UNREGISTER) == SWITCH_STATUS_SUCCESS) { switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", profile->name); + switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "username", username); switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-user", to_user); switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-host", reg_host); switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "contact", contact_str); @@ -2809,7 +2819,7 @@ sofia_gateway_t *sofia_reg_find_gateway_by_realm__(const char *file, const char switch_mutex_lock(mod_sofia_globals.hash_mutex); for (hi = switch_hash_first(NULL, mod_sofia_globals.gateway_hash); hi; hi = switch_hash_next(hi)) { switch_hash_this(hi, &var, NULL, &val); - if (key && (gateway = (sofia_gateway_t *) val) && gateway->register_realm && !strcasecmp(gateway->register_realm, key)) { + if (key && (gateway = (sofia_gateway_t *) val) && !gateway->deleted && gateway->register_realm && !strcasecmp(gateway->register_realm, key)) { break; } else { gateway = NULL; @@ -2863,6 +2873,7 @@ switch_status_t sofia_reg_add_gateway(sofia_profile_t *profile, const char *key, { switch_status_t status = SWITCH_STATUS_FALSE; char *pkey = switch_mprintf("%s::%s", profile->name, key); + sofia_gateway_t *gp; switch_mutex_lock(profile->gw_mutex); @@ -2872,12 +2883,21 @@ switch_status_t sofia_reg_add_gateway(sofia_profile_t *profile, const char *key, switch_mutex_unlock(profile->gw_mutex); switch_mutex_lock(mod_sofia_globals.hash_mutex); - if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, key)) { - status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, key, gateway); + + if ((gp = switch_core_hash_find(mod_sofia_globals.gateway_hash, key))) { + if (gp->deleted) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing deleted gateway from hash.\n"); + switch_core_hash_delete(mod_sofia_globals.gateway_hash, gp->name); + switch_core_hash_delete(mod_sofia_globals.gateway_hash, pkey); + switch_core_hash_delete(mod_sofia_globals.gateway_hash, key); + } } - if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) { + if (!switch_core_hash_find(mod_sofia_globals.gateway_hash, key) && !switch_core_hash_find(mod_sofia_globals.gateway_hash, pkey)) { + status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, key, gateway); status = switch_core_hash_insert(mod_sofia_globals.gateway_hash, pkey, gateway); + } else { + status = SWITCH_STATUS_FALSE; } switch_mutex_unlock(mod_sofia_globals.hash_mutex); diff --git a/src/mod/event_handlers/mod_cdr_mongodb/Makefile b/src/mod/event_handlers/mod_cdr_mongodb/Makefile new file mode 100644 index 0000000000..b9e04b983d --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/Makefile @@ -0,0 +1,9 @@ +include ../../../../build/modmake.rules + +MONGODB_DRIVER=./driver/src +LOCAL_CFLAGS=-I$(MONGODB_DRIVER) +LOCAL_OBJS=$(MONGODB_DRIVER)/md5.o \ + $(MONGODB_DRIVER)/mongo.o $(MONGODB_DRIVER)/net.o \ + $(MONGODB_DRIVER)/bson.o $(MONGODB_DRIVER)/numbers.o $(MONGODB_DRIVER)/encoding.o \ + +local_depend: $(LOCAL_OBJS) diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt b/src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/APACHE-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md b/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md new file mode 100644 index 0000000000..f3f0fab21d --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/HISTORY.md @@ -0,0 +1,139 @@ +# MongoDB C Driver History + +## 0.4 + +THIS RELEASE INCLUDES NUMEROUS BACKWARD-BREAKING CHANGES. +These changes have been made for extensibility, consistency, +and ease of use. Please read the following release notes +carefully, and study the updated tutorial. + +API Principles: + +1. Present a consistent interface for all objects: connections, + cursors, bson objects, and bson iterators. +2. Require no knowledge of an object's implementation to use the API. +3. Allow users to allocate objects on the stack or on the heap. +4. Integrate API with new error reporting strategy. +5. Be concise, except where it impairs clarity. + +Changes: + +* mongo_replset_init_conn has been renamed to mongo_replset_init. +* bson_buffer has been removed. All functions for building bson + objects now take objects of type bson. The new pattern looks like this: + + Example: + + bson b[1]; + bson_init( b ); + bson_append_int( b, "foo", 1 ); + bson_finish( b ); + /* The object is ready to use. + When finished, destroy it. */ + bson_destroy( b ); + +* mongo_connection has been renamed to mongo. + + Example: + + mongo conn[1]; + mongo_connect( conn, '127.0.0.1', 27017 ); + /* Connection is ready. Destroy when down. */ + mongo_destroy( conn ); + +* New cursor builder API for clearer code: + + Example: + + mongo_cursor cursor[1]; + mongo_cursor_init( cursor, conn, "test.foo" ); + + bson query[1]; + + bson_init( query ); + bson_append_int( query, "bar", 1 ); + bson_finish( query ); + + bson fields[1]; + + bson_init( fields ); + bson_append_int( fields, "baz", 1 ); + bson_finish( fields ); + + mongo_cursor_set_query( cursor, query ); + mongo_cursor_set_fields( cursor, fields ); + mongo_cursor_set_limit( cursor, 10 ); + mongo_cursor_set_skip( cursor, 10 ); + + while( mongo_cursor_next( cursor ) == MONGO_OK ) + bson_print( mongo_cursor_bson( cursor ) ); + +* bson_iterator_init now takes a (bson*) instead of a (const char*). This is consistent + with bson_find, which also takes a (bson*). If you want to initiate a bson iterator + with a buffer, use the new function bson_iterator_from_buffer. +* With the addition of the mongo_cursor_bson function, it's now no + longer necessary to know how bson and mongo_cursor objects are implemented. + + Example: + + bson b[1]; + bson_iterator i[1]; + + bson_iterator_init( i, b ); + + /* With a cursor */ + bson_iterator_init( i, mongo_cursor_bson( cursor ) ); + +* Added mongo_cursor_data and bson_data functions, which return the + raw bson buffer as a (const char *). +* All constants that were once lower case are now + upper case. These include: MONGO_OP_MSG, MONGO_OP_UPDATE, MONGO_OP_INSERT, + MONGO_OP_QUERY, MONGO_OP_GET_MORE, MONGO_OP_DELETE, MONGO_OP_KILL_CURSORS + BSON_EOO, BSON_DOUBLE, BSON_STRING, BSON_OBJECT, BSON_ARRAY, BSON_BINDATA, + BSON_UNDEFINED, BSON_OID, BSON_BOOL, BSON_DATE, BSON_NULL, BSON_REGEX, BSON_DBREF, + BSON_CODE, BSON_SYMBOL, BSON_CODEWSCOPE, BSON_INT, BSON_TIMESTAMP, BSON_LONG, + MONGO_CONN_SUCCESS, MONGO_CONN_BAD_ARG, MONGO_CONN_NO_SOCKET, MONGO_CONN_FAIL, + MONGO_CONN_NOT_MASTER, MONGO_CONN_BAD_SET_NAME, MONGO_CONN_CANNOT_FIND_PRIMARY + If your programs use any of these constants, you must convert them to their + upper case forms, or you will see compile errors. +* The error handling strategy has been changed. Exceptions are not longer being used. +* Functions taking a mongo_connection object now return either MONGO_OK or MONGO_ERROR. + In case of an error, an error code of type mongo_error_t will be indicated on the + mongo_connection->err field. +* Functions taking a bson object now return either BSON_OK or BSON_ERROR. + In case of an error, an error code of type bson_validity_t will be indicated on the + bson->err or bson_buffer->err field. +* Calls to mongo_cmd_get_last_error store the error status on the + mongo->lasterrcode and mongo->lasterrstr fields. +* bson_print now prints all types. +* Users may now set custom malloc, realloc, free, printf, sprintf, and fprintf fields. +* Groundwork for modules for supporting platform-specific features (e.g., socket timeouts). +* Added mongo_set_op_timeout for setting socket timeout. To take advantage of this, you must + compile with --use-platform=LINUX. The compiles with platform/linux/net.h instead of the + top-level net.h. +* Fixed tailable cursors. +* GridFS API is now in-line with the new driver API. In particular, all of the + following functions now return MONGO_OK or MONGO_ERROR: gridfs_init, + gridfile_init, gridfile_writer_done, gridfs_store_buffer, gridfs_store_file, + and gridfs_find_query. +* Fixed a few memory leaks. + +## 0.3 +2011-4-14 + +* Support replica sets. +* Better standard connection API. +* GridFS write buffers iteratively. +* Fixes for working with large GridFS files (> 3GB) +* bson_append_string_n and family (Gergely Nagy) + +## 0.2 +2011-2-11 + +* GridFS support (Chris Triolo). +* BSON Timestamp type support. + +## 0.1 +2009-11-30 + +* Initial release. diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md b/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md new file mode 100644 index 0000000000..1afe39e69c --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/README.md @@ -0,0 +1,60 @@ +# MongoDB C Driver + +This is then 10gen-supported MongoDB C driver. There are two goals for this driver. +The first is to provide a strict, default compilation option for ultimate portability, +no dependencies, and generic embeddability. + +The second is to support more advanced, platform-specific features, like socket timeout, +by providing an interface for platform-specific modules. + +Until the 1.0 release, this driver should be considered alpha. Keep in mind that the API will be in flux until then. + +# Building + +First check out the version you want to build. *Always build from a particular tag, since HEAD may be +a work in progress.* For example, to build version 0.4, run: + + git checkout v0.4 + +You can then build the driver with scons: + + scons + +## Running the tests +Make sure that you're running mongod on 127.0.0.1 on the default port (27017). The replica set +test assumes a replica set with at least three nodes running at 127.0.0.1 and starting at port +30000. Note that the driver does not recognize 'localhost' as a valid host name. + +To compile and run the tests: + + scons test + +# Error Handling +Most functions return MONGO_OK or BSON_OK on success and MONGO_ERROR or BSON_ERROR on failure. +Specific error codes and error strings are then stored in the `err` and `errstr` fields of the +`mongo` and `bson` objects. It is the client's responsibility to check for errors and handle +them appropriately. + +# Docs +The docs are built using Sphinx and Doxygen. If you have these tools installed, then +you can build the docs with scons: + + scons docs + +The html docs will appear in docs/html. + +# ISSUES + +You can report bugs, request new features, and view this driver's roadmap +using [JIRA](http://jira.mongodb.org/browse/CDRIVER). + +# CREDITS + +* Gergely Nagy - Non-null-terminated string support. +* Josh Rotenberg - Initial Doxygen setup and a significant chunk of documentation. + +# LICENSE + +Unless otherwise specified in a source file, sources in this +repository are published under the terms of the Apache License version +2.0, a copy of which is in this repository as APACHE-2.0.txt. diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct b/src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct new file mode 100644 index 0000000000..d3be2d89a6 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/SConstruct @@ -0,0 +1,178 @@ +# -*- mode: python; -*- + +VERSION = "0.4" + +# --- options ---- +AddOption('--test-server', + dest='test_server', + default='127.0.0.1', + type='string', + nargs=1, + action='store', + help='IP address of server to use for testing') + +AddOption('--seed-start-port', + dest='seed_start_port', + default=30000, + type='int', + nargs=1, + action='store', + help='IP address of server to use for testing') + +AddOption('--c99', + dest='use_c99', + default=False, + action='store_true', + help='Compile with c99 (recommended for gcc)') + +AddOption('--d', + dest='optimize', + default=True, + action='store_false', + help='disable optimizations') + +AddOption('--use-platform', + dest='compile_platform', + default='GENERIC', + type='string', + nargs=1, + action='store', + help='Compile for a specific platform to take advantage ' + ' of particular system features. For the moment, this include timeouts only.' + ' Current options include LINUX, ' + ' GENERIC, and CUSTOM. If you specific CUSTOM, you must place a' + ' system-specific implementation of net.h and net.c in src/platform/custom/') + +import os, sys + +env = Environment( ENV=os.environ ) + +# ---- Docs ---- +def build_docs(env, target, source): + buildscript_path = os.path.join(os.path.abspath("docs")) + sys.path.insert(0, buildscript_path) + import buildscripts + from buildscripts import docs + docs.main() + +env.Alias("docs", [], [build_docs]) +env.AlwaysBuild("docs") + +# ---- Platforms ---- +PLATFORM_TEST_DIR = None +if "LINUX" == GetOption('compile_platform'): + env.Append( CPPFLAGS=" -D_MONGO_USE_LINUX_SYSTEM" ) + NET_LIB = "src/platform/linux/net.c" + PLATFORM_TEST_DIR = "test/platform/linux/" + PLATFORM_TESTS = [ "timeouts" ] +elif "CUSTOM" == GetOption('compile_platform'): + env.Append( CPPFLAGS=" -D_MONGO_USE_CUSTOM_SYSTEM" ) + NET_LIB = "src/platform/custom/net.c" +else: + NET_LIB = "src/net.c" + +# ---- Libraries ---- +if os.sys.platform in ["darwin", "linux2"]: + env.Append( CPPFLAGS=" -pedantic -Wall -ggdb -DMONGO_HAVE_STDINT" ) + env.Append( CPPPATH=["/opt/local/include/"] ) + env.Append( LIBPATH=["/opt/local/lib/"] ) + + if GetOption('use_c99'): + env.Append( CFLAGS=" -std=c99 " ) + env.Append( CXXDEFINES="MONGO_HAVE_STDINT" ) + else: + env.Append( CFLAGS=" -ansi " ) + + if GetOption('optimize'): + env.Append( CPPFLAGS=" -O3 " ) + # -O3 benchmarks *significantly* faster than -O2 when disabling networking +elif 'win32' == os.sys.platform: + env.Append( LIBS='ws2_32' ) + +#we shouldn't need these options in c99 mode +if not GetOption('use_c99'): + conf = Configure(env) + + if not conf.CheckType('int64_t'): + if conf.CheckType('int64_t', '#include \n'): + conf.env.Append( CPPDEFINES="MONGO_HAVE_STDINT" ) + elif conf.CheckType('int64_t', '#include \n'): + conf.env.Append( CPPDEFINES="MONGO_HAVE_UNISTD" ) + elif conf.CheckType('__int64'): + conf.env.Append( CPPDEFINES="MONGO_USE__INT64" ) + elif conf.CheckType('long long int'): + conf.env.Append( CPPDEFINES="MONGO_USE_LONG_LONG_INT" ) + else: + print "*** what is your 64 bit int type? ****" + Exit(1) + + env = conf.Finish() + +have_libjson = False +conf = Configure(env) +if conf.CheckLib('json'): + have_libjson = True +env = conf.Finish() + +if sys.byteorder == 'big': + env.Append( CPPDEFINES="MONGO_BIG_ENDIAN" ) + +env.Append( CPPPATH=["src/"] ) + +coreFiles = ["src/md5.c" ] +mFiles = [ "src/mongo.c", NET_LIB, "src/gridfs.c"] +bFiles = [ "src/bson.c", "src/numbers.c", "src/encoding.c"] +mLibFiles = coreFiles + mFiles + bFiles +bLibFiles = coreFiles + bFiles +m = env.Library( "mongoc" , mLibFiles ) +b = env.Library( "bson" , bLibFiles ) +env.Default( env.Alias( "lib" , [ m[0] , b[0] ] ) ) + +if os.sys.platform == "linux2": + env.Append( SHLINKFLAGS="-shared -Wl,-soname,libmongoc.so." + VERSION ) + env.Append( SHLINKFLAGS = "-shared -Wl,-soname,libbson.so." + VERSION ) + +dynm = env.SharedLibrary( "mongoc" , mLibFiles ) +dynb = env.SharedLibrary( "bson" , bLibFiles ) +env.Default( env.Alias( "sharedlib" , [ dynm[0] , dynb[0] ] ) ) + + + +# ---- Benchmarking ---- +benchmarkEnv = env.Clone() +benchmarkEnv.Append( CPPDEFINES=[('TEST_SERVER', r'\"%s\"'%GetOption('test_server')), +('SEED_START_PORT', r'%d'%GetOption('seed_start_port'))] ) +benchmarkEnv.Append( LIBS=[m, b] ) +benchmarkEnv.Prepend( LIBPATH=["."] ) +benchmarkEnv.Program( "benchmark" , [ "test/benchmark.c"] ) + +# ---- Tests ---- +testEnv = benchmarkEnv.Clone() +testCoreFiles = [ ] + +def run_tests( root, tests ): + for name in tests: + filename = "%s/%s.c" % (root, name) + exe = "test_" + name + test = testEnv.Program( exe , testCoreFiles + [filename] ) + test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> ' + os.path.devnull) + AlwaysBuild(test_alias) + +tests = Split("sizes resize endian_swap bson bson_subobject simple update errors " +"count_delete auth gridfs validate examples helpers oid functions cursors replica_set") + +# Run standard tests +run_tests("test", tests) + +# Run platform tests +if not PLATFORM_TEST_DIR is None: + run_tests( PLATFORM_TEST_DIR, PLATFORM_TESTS ) + +if have_libjson: + tests.append('json') + testEnv.Append( LIBS=["json"] ) + +# special case for cpptest +test = testEnv.Program( 'test_cpp' , testCoreFiles + ['test/cpptest.cpp'] ) +test_alias = testEnv.Alias('test', [test], test[0].abspath + ' 2> '+ os.path.devnull) +AlwaysBuild(test_alias) diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c new file mode 100644 index 0000000000..349f42284c --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.c @@ -0,0 +1,978 @@ +/* bson.c */ + +/* Copyright 2009, 2010 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include "bson.h" +#include "encoding.h" + +const int initialBufferSize = 128; + +/* only need one of these */ +static const int zero = 0; + +/* Custom standard function pointers. */ +void *( *bson_malloc_func )( size_t ) = malloc; +void *( *bson_realloc_func )( void *, size_t ) = realloc; +void ( *bson_free )( void * ) = free; +bson_printf_func bson_printf = printf; +bson_fprintf_func bson_fprintf = fprintf; +bson_sprintf_func bson_sprintf = sprintf; + +static int _bson_errprintf( const char *, ... ); +bson_printf_func bson_errprintf = _bson_errprintf; + +/* ObjectId fuzz functions. */ +static int ( *oid_fuzz_func )( void ) = NULL; +static int ( *oid_inc_func )( void ) = NULL; + +/* ---------------------------- + READING + ------------------------------ */ + +bson *bson_empty( bson *obj ) { + static char *data = "\005\0\0\0\0"; + bson_init_data( obj, data ); + obj->finished = 1; + obj->err = 0; + obj->stackPos = 0; + return obj; +} + +void bson_copy_basic( bson *out, const bson *in ) { + if ( !out ) return; + bson_init_size( out, bson_size( in ) ); + memcpy( out->data, in->data, bson_size( in ) ); +} + +void bson_copy( bson *out, const bson *in ) { + int i; + + if ( !out ) return; + bson_copy_basic( out, in ); + out->cur = out->data + ( in->cur - in->data ); + out->dataSize = in->dataSize; + out->finished = in->finished; + out->stackPos = in->stackPos; + out->err = in->err; + for( i=0; istackPos; i++ ) + out->stack[i] = in->stack[i]; +} + +int bson_init_data( bson *b, char *data ) { + b->data = data; + return BSON_OK; +} + +static void _bson_reset( bson *b ) { + b->finished = 0; + b->stackPos = 0; + b->err = 0; + b->errstr = NULL; +} + +int bson_size( const bson *b ) { + int i; + if ( ! b || ! b->data ) + return 0; + bson_little_endian32( &i, b->data ); + return i; +} + +const char *bson_data( bson *b ) { + return (const char *)b->data; +} + +static char hexbyte( char hex ) { + switch ( hex ) { + case '0': + return 0x0; + case '1': + return 0x1; + case '2': + return 0x2; + case '3': + return 0x3; + case '4': + return 0x4; + case '5': + return 0x5; + case '6': + return 0x6; + case '7': + return 0x7; + case '8': + return 0x8; + case '9': + return 0x9; + case 'a': + case 'A': + return 0xa; + case 'b': + case 'B': + return 0xb; + case 'c': + case 'C': + return 0xc; + case 'd': + case 'D': + return 0xd; + case 'e': + case 'E': + return 0xe; + case 'f': + case 'F': + return 0xf; + default: + return 0x0; /* something smarter? */ + } +} + +void bson_oid_from_string( bson_oid_t *oid, const char *str ) { + int i; + for ( i=0; i<12; i++ ) { + oid->bytes[i] = ( hexbyte( str[2*i] ) << 4 ) | hexbyte( str[2*i + 1] ); + } +} + +void bson_oid_to_string( const bson_oid_t *oid, char *str ) { + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + int i; + for ( i=0; i<12; i++ ) { + str[2*i] = hex[( oid->bytes[i] & 0xf0 ) >> 4]; + str[2*i + 1] = hex[ oid->bytes[i] & 0x0f ]; + } + str[24] = '\0'; +} + +void bson_set_oid_fuzz( int ( *func )( void ) ) { + oid_fuzz_func = func; +} + +void bson_set_oid_inc( int ( *func )( void ) ) { + oid_inc_func = func; +} + +void bson_oid_gen( bson_oid_t *oid ) { + static int incr = 0; + static int fuzz = 0; + int i; + int t = time( NULL ); + + if( oid_inc_func ) + i = oid_inc_func(); + else + i = incr++; + + if ( !fuzz ) { + if ( oid_fuzz_func ) + fuzz = oid_fuzz_func(); + else { + srand( t ); + fuzz = rand(); + } + } + + bson_big_endian32( &oid->ints[0], &t ); + oid->ints[1] = fuzz; + bson_big_endian32( &oid->ints[2], &i ); +} + +time_t bson_oid_generated_time( bson_oid_t *oid ) { + time_t out; + bson_big_endian32( &out, &oid->ints[0] ); + + return out; +} + +void bson_print( bson *b ) { + bson_print_raw( b->data , 0 ); +} + +void bson_print_raw( const char *data , int depth ) { + bson_iterator i; + const char *key; + int temp; + bson_timestamp_t ts; + char oidhex[25]; + bson scope; + bson_iterator_from_buffer( &i, data ); + + while ( bson_iterator_next( &i ) ) { + bson_type t = bson_iterator_type( &i ); + if ( t == 0 ) + break; + key = bson_iterator_key( &i ); + + for ( temp=0; temp<=depth; temp++ ) + printf( "\t" ); + bson_printf( "%s : %d \t " , key , t ); + switch ( t ) { + case BSON_DOUBLE: + printf( "%f" , bson_iterator_double( &i ) ); + break; + case BSON_STRING: + printf( "%s" , bson_iterator_string( &i ) ); + break; + case BSON_SYMBOL: + printf( "SYMBOL: %s" , bson_iterator_string( &i ) ); + break; + case BSON_OID: + bson_oid_to_string( bson_iterator_oid( &i ), oidhex ); + printf( "%s" , oidhex ); + break; + case BSON_BOOL: + printf( "%s" , bson_iterator_bool( &i ) ? "true" : "false" ); + break; + case BSON_DATE: + printf( "%ld" , ( long int )bson_iterator_date( &i ) ); + break; + case BSON_BINDATA: + printf( "BSON_BINDATA" ); + break; + case BSON_UNDEFINED: + printf( "BSON_UNDEFINED" ); + break; + case BSON_NULL: + printf( "BSON_NULL" ); + break; + case BSON_REGEX: + printf( "BSON_REGEX: %s", bson_iterator_regex( &i ) ); + break; + case BSON_CODE: + printf( "BSON_CODE: %s", bson_iterator_code( &i ) ); + break; + case BSON_CODEWSCOPE: + printf( "BSON_CODE_W_SCOPE: %s", bson_iterator_code( &i ) ); + bson_init( &scope ); + bson_iterator_code_scope( &i, &scope ); + printf( "\n\t SCOPE: " ); + bson_print( &scope ); + break; + case BSON_INT: + printf( "%d" , bson_iterator_int( &i ) ); + break; + case BSON_LONG: + printf( "%lld" , ( long long int )bson_iterator_long( &i ) ); + break; + case BSON_TIMESTAMP: + ts = bson_iterator_timestamp( &i ); + printf( "i: %d, t: %d", ts.i, ts.t ); + break; + case BSON_OBJECT: + case BSON_ARRAY: + printf( "\n" ); + bson_print_raw( bson_iterator_value( &i ) , depth + 1 ); + break; + default: + bson_errprintf( "can't print type : %d\n" , t ); + } + printf( "\n" ); + } +} + +/* ---------------------------- + ITERATOR + ------------------------------ */ + +void bson_iterator_init( bson_iterator *i, const bson *b ) { + i->cur = b->data + 4; + i->first = 1; +} + +void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ) { + i->cur = buffer + 4; + i->first = 1; +} + +bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ) { + bson_iterator_init( it, (bson *)obj ); + while( bson_iterator_next( it ) ) { + if ( strcmp( name, bson_iterator_key( it ) ) == 0 ) + break; + } + return bson_iterator_type( it ); +} + +bson_bool_t bson_iterator_more( const bson_iterator *i ) { + return *( i->cur ); +} + +bson_type bson_iterator_next( bson_iterator *i ) { + int ds; + + if ( i->first ) { + i->first = 0; + return ( bson_type )( *i->cur ); + } + + switch ( bson_iterator_type( i ) ) { + case BSON_EOO: + return BSON_EOO; /* don't advance */ + case BSON_UNDEFINED: + case BSON_NULL: + ds = 0; + break; + case BSON_BOOL: + ds = 1; + break; + case BSON_INT: + ds = 4; + break; + case BSON_LONG: + case BSON_DOUBLE: + case BSON_TIMESTAMP: + case BSON_DATE: + ds = 8; + break; + case BSON_OID: + ds = 12; + break; + case BSON_STRING: + case BSON_SYMBOL: + case BSON_CODE: + ds = 4 + bson_iterator_int_raw( i ); + break; + case BSON_BINDATA: + ds = 5 + bson_iterator_int_raw( i ); + break; + case BSON_OBJECT: + case BSON_ARRAY: + case BSON_CODEWSCOPE: + ds = bson_iterator_int_raw( i ); + break; + case BSON_DBREF: + ds = 4+12 + bson_iterator_int_raw( i ); + break; + case BSON_REGEX: { + const char *s = bson_iterator_value( i ); + const char *p = s; + p += strlen( p )+1; + p += strlen( p )+1; + ds = p-s; + break; + } + + default: { + char msg[] = "unknown type: 000000000000"; + bson_numstr( msg+14, ( unsigned )( i->cur[0] ) ); + bson_fatal_msg( 0, msg ); + return 0; + } + } + + i->cur += 1 + strlen( i->cur + 1 ) + 1 + ds; + + return ( bson_type )( *i->cur ); +} + +bson_type bson_iterator_type( const bson_iterator *i ) { + return ( bson_type )i->cur[0]; +} + +const char *bson_iterator_key( const bson_iterator *i ) { + return i->cur + 1; +} + +const char *bson_iterator_value( const bson_iterator *i ) { + const char *t = i->cur + 1; + t += strlen( t ) + 1; + return t; +} + +/* types */ + +int bson_iterator_int_raw( const bson_iterator *i ) { + int out; + bson_little_endian32( &out, bson_iterator_value( i ) ); + return out; +} + +double bson_iterator_double_raw( const bson_iterator *i ) { + double out; + bson_little_endian64( &out, bson_iterator_value( i ) ); + return out; +} + +int64_t bson_iterator_long_raw( const bson_iterator *i ) { + int64_t out; + bson_little_endian64( &out, bson_iterator_value( i ) ); + return out; +} + +bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ) { + return bson_iterator_value( i )[0]; +} + +bson_oid_t *bson_iterator_oid( const bson_iterator *i ) { + return ( bson_oid_t * )bson_iterator_value( i ); +} + +int bson_iterator_int( const bson_iterator *i ) { + switch ( bson_iterator_type( i ) ) { + case BSON_INT: + return bson_iterator_int_raw( i ); + case BSON_LONG: + return bson_iterator_long_raw( i ); + case BSON_DOUBLE: + return bson_iterator_double_raw( i ); + default: + return 0; + } +} + +double bson_iterator_double( const bson_iterator *i ) { + switch ( bson_iterator_type( i ) ) { + case BSON_INT: + return bson_iterator_int_raw( i ); + case BSON_LONG: + return bson_iterator_long_raw( i ); + case BSON_DOUBLE: + return bson_iterator_double_raw( i ); + default: + return 0; + } +} + +int64_t bson_iterator_long( const bson_iterator *i ) { + switch ( bson_iterator_type( i ) ) { + case BSON_INT: + return bson_iterator_int_raw( i ); + case BSON_LONG: + return bson_iterator_long_raw( i ); + case BSON_DOUBLE: + return bson_iterator_double_raw( i ); + default: + return 0; + } +} + +bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ) { + bson_timestamp_t ts; + bson_little_endian32( &( ts.i ), bson_iterator_value( i ) ); + bson_little_endian32( &( ts.t ), bson_iterator_value( i ) + 4 ); + return ts; +} + +bson_bool_t bson_iterator_bool( const bson_iterator *i ) { + switch ( bson_iterator_type( i ) ) { + case BSON_BOOL: + return bson_iterator_bool_raw( i ); + case BSON_INT: + return bson_iterator_int_raw( i ) != 0; + case BSON_LONG: + return bson_iterator_long_raw( i ) != 0; + case BSON_DOUBLE: + return bson_iterator_double_raw( i ) != 0; + case BSON_EOO: + case BSON_NULL: + return 0; + default: + return 1; + } +} + +const char *bson_iterator_string( const bson_iterator *i ) { + return bson_iterator_value( i ) + 4; +} + +int bson_iterator_string_len( const bson_iterator *i ) { + return bson_iterator_int_raw( i ); +} + +const char *bson_iterator_code( const bson_iterator *i ) { + switch ( bson_iterator_type( i ) ) { + case BSON_STRING: + case BSON_CODE: + return bson_iterator_value( i ) + 4; + case BSON_CODEWSCOPE: + return bson_iterator_value( i ) + 8; + default: + return NULL; + } +} + +void bson_iterator_code_scope( const bson_iterator *i, bson *scope ) { + if ( bson_iterator_type( i ) == BSON_CODEWSCOPE ) { + int code_len; + bson_little_endian32( &code_len, bson_iterator_value( i )+4 ); + bson_init_data( scope, ( void * )( bson_iterator_value( i )+8+code_len ) ); + } else { + bson_empty( scope ); + } +} + +bson_date_t bson_iterator_date( const bson_iterator *i ) { + return bson_iterator_long_raw( i ); +} + +time_t bson_iterator_time_t( const bson_iterator *i ) { + return bson_iterator_date( i ) / 1000; +} + +int bson_iterator_bin_len( const bson_iterator *i ) { + return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD ) + ? bson_iterator_int_raw( i ) - 4 + : bson_iterator_int_raw( i ); +} + +char bson_iterator_bin_type( const bson_iterator *i ) { + return bson_iterator_value( i )[4]; +} + +const char *bson_iterator_bin_data( const bson_iterator *i ) { + return ( bson_iterator_bin_type( i ) == BSON_BIN_BINARY_OLD ) + ? bson_iterator_value( i ) + 9 + : bson_iterator_value( i ) + 5; +} + +const char *bson_iterator_regex( const bson_iterator *i ) { + return bson_iterator_value( i ); +} + +const char *bson_iterator_regex_opts( const bson_iterator *i ) { + const char *p = bson_iterator_value( i ); + return p + strlen( p ) + 1; + +} + +void bson_iterator_subobject( const bson_iterator *i, bson *sub ) { + bson_init_data( sub, ( char * )bson_iterator_value( i ) ); + _bson_reset( sub ); + sub->finished = 1; +} + +void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ) { + bson_iterator_from_buffer( sub, bson_iterator_value( i ) ); +} + +/* ---------------------------- + BUILDING + ------------------------------ */ + +static void _bson_init_size( bson *b, int size ) { + if( size == 0 ) + b->data = NULL; + else + b->data = ( char * )bson_malloc( size ); + b->dataSize = size; + b->cur = b->data + 4; + _bson_reset( b ); +} + +void bson_init( bson *b ) { + _bson_init_size( b, initialBufferSize ); +} + +void bson_init_size( bson *b, int size ) { + _bson_init_size( b, size ); +} + +void bson_append_byte( bson *b, char c ) { + b->cur[0] = c; + b->cur++; +} + +void bson_append( bson *b, const void *data, int len ) { + memcpy( b->cur , data , len ); + b->cur += len; +} + +void bson_append32( bson *b, const void *data ) { + bson_little_endian32( b->cur, data ); + b->cur += 4; +} + +void bson_append64( bson *b, const void *data ) { + bson_little_endian64( b->cur, data ); + b->cur += 8; +} + +int bson_ensure_space( bson *b, const int bytesNeeded ) { + int pos = b->cur - b->data; + char *orig = b->data; + int new_size; + + if ( pos + bytesNeeded <= b->dataSize ) + return BSON_OK; + + new_size = 1.5 * ( b->dataSize + bytesNeeded ); + + if( new_size < b->dataSize ) { + if( ( b->dataSize + bytesNeeded ) < INT_MAX ) + new_size = INT_MAX; + else { + b->err = BSON_SIZE_OVERFLOW; + return BSON_ERROR; + } + } + + b->data = bson_realloc( b->data, new_size ); + if ( !b->data ) + bson_fatal_msg( !!b->data, "realloc() failed" ); + + b->dataSize = new_size; + b->cur += b->data - orig; + + return BSON_OK; +} + +int bson_finish( bson *b ) { + int i; + + if( b->err & BSON_NOT_UTF8 ) + return BSON_ERROR; + + if ( ! b->finished ) { + if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR; + bson_append_byte( b, 0 ); + i = b->cur - b->data; + bson_little_endian32( b->data, &i ); + b->finished = 1; + } + + return BSON_OK; +} + +void bson_destroy( bson *b ) { + bson_free( b->data ); + b->err = 0; + b->data = 0; + b->cur = 0; + b->finished = 1; +} + +static int bson_append_estart( bson *b, int type, const char *name, const int dataSize ) { + const int len = strlen( name ) + 1; + + if ( b->finished ) { + b->err |= BSON_ALREADY_FINISHED; + return BSON_ERROR; + } + + if ( bson_ensure_space( b, 1 + len + dataSize ) == BSON_ERROR ) { + return BSON_ERROR; + } + + if( bson_check_field_name( b, ( const char * )name, len - 1 ) == BSON_ERROR ) { + bson_builder_error( b ); + return BSON_ERROR; + } + + bson_append_byte( b, ( char )type ); + bson_append( b, name, len ); + return BSON_OK; +} + +/* ---------------------------- + BUILDING TYPES + ------------------------------ */ + +int bson_append_int( bson *b, const char *name, const int i ) { + if ( bson_append_estart( b, BSON_INT, name, 4 ) == BSON_ERROR ) + return BSON_ERROR; + bson_append32( b , &i ); + return BSON_OK; +} + +int bson_append_long( bson *b, const char *name, const int64_t i ) { + if ( bson_append_estart( b , BSON_LONG, name, 8 ) == BSON_ERROR ) + return BSON_ERROR; + bson_append64( b , &i ); + return BSON_OK; +} + +int bson_append_double( bson *b, const char *name, const double d ) { + if ( bson_append_estart( b, BSON_DOUBLE, name, 8 ) == BSON_ERROR ) + return BSON_ERROR; + bson_append64( b , &d ); + return BSON_OK; +} + +int bson_append_bool( bson *b, const char *name, const bson_bool_t i ) { + if ( bson_append_estart( b, BSON_BOOL, name, 1 ) == BSON_ERROR ) + return BSON_ERROR; + bson_append_byte( b , i != 0 ); + return BSON_OK; +} + +int bson_append_null( bson *b, const char *name ) { + if ( bson_append_estart( b , BSON_NULL, name, 0 ) == BSON_ERROR ) + return BSON_ERROR; + return BSON_OK; +} + +int bson_append_undefined( bson *b, const char *name ) { + if ( bson_append_estart( b, BSON_UNDEFINED, name, 0 ) == BSON_ERROR ) + return BSON_ERROR; + return BSON_OK; +} + +int bson_append_string_base( bson *b, const char *name, + const char *value, int len, bson_type type ) { + + int sl = len + 1; + if ( bson_check_string( b, ( const char * )value, sl - 1 ) == BSON_ERROR ) + return BSON_ERROR; + if ( bson_append_estart( b, type, name, 4 + sl ) == BSON_ERROR ) { + return BSON_ERROR; + } + bson_append32( b , &sl ); + bson_append( b , value , sl - 1 ); + bson_append( b , "\0" , 1 ); + return BSON_OK; +} + +int bson_append_string( bson *b, const char *name, const char *value ) { + return bson_append_string_base( b, name, value, strlen ( value ), BSON_STRING ); +} + +int bson_append_symbol( bson *b, const char *name, const char *value ) { + return bson_append_string_base( b, name, value, strlen ( value ), BSON_SYMBOL ); +} + +int bson_append_code( bson *b, const char *name, const char *value ) { + return bson_append_string_base( b, name, value, strlen ( value ), BSON_CODE ); +} + +int bson_append_string_n( bson *b, const char *name, const char *value, int len ) { + return bson_append_string_base( b, name, value, len, BSON_STRING ); +} + +int bson_append_symbol_n( bson *b, const char *name, const char *value, int len ) { + return bson_append_string_base( b, name, value, len, BSON_SYMBOL ); +} + +int bson_append_code_n( bson *b, const char *name, const char *value, int len ) { + return bson_append_string_base( b, name, value, len, BSON_CODE ); +} + +int bson_append_code_w_scope_n( bson *b, const char *name, + const char *code, int len, const bson *scope ) { + + int sl = len + 1; + int size = 4 + 4 + sl + bson_size( scope ); + if ( bson_append_estart( b, BSON_CODEWSCOPE, name, size ) == BSON_ERROR ) + return BSON_ERROR; + bson_append32( b, &size ); + bson_append32( b, &sl ); + bson_append( b, code, sl ); + bson_append( b, scope->data, bson_size( scope ) ); + return BSON_OK; +} + +int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ) { + return bson_append_code_w_scope_n( b, name, code, strlen ( code ), scope ); +} + +int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ) { + if ( type == BSON_BIN_BINARY_OLD ) { + int subtwolen = len + 4; + if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+4+len ) == BSON_ERROR ) + return BSON_ERROR; + bson_append32( b, &subtwolen ); + bson_append_byte( b, type ); + bson_append32( b, &len ); + bson_append( b, str, len ); + } else { + if ( bson_append_estart( b, BSON_BINDATA, name, 4+1+len ) == BSON_ERROR ) + return BSON_ERROR; + bson_append32( b, &len ); + bson_append_byte( b, type ); + bson_append( b, str, len ); + } + return BSON_OK; +} + +int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ) { + if ( bson_append_estart( b, BSON_OID, name, 12 ) == BSON_ERROR ) + return BSON_ERROR; + bson_append( b , oid , 12 ); + return BSON_OK; +} + +int bson_append_new_oid( bson *b, const char *name ) { + bson_oid_t oid; + bson_oid_gen( &oid ); + return bson_append_oid( b, name, &oid ); +} + +int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ) { + const int plen = strlen( pattern )+1; + const int olen = strlen( opts )+1; + if ( bson_append_estart( b, BSON_REGEX, name, plen + olen ) == BSON_ERROR ) + return BSON_ERROR; + if ( bson_check_string( b, pattern, plen - 1 ) == BSON_ERROR ) + return BSON_ERROR; + bson_append( b , pattern , plen ); + bson_append( b , opts , olen ); + return BSON_OK; +} + +int bson_append_bson( bson *b, const char *name, const bson *bson ) { + if ( bson_append_estart( b, BSON_OBJECT, name, bson_size( bson ) ) == BSON_ERROR ) + return BSON_ERROR; + bson_append( b , bson->data , bson_size( bson ) ); + return BSON_OK; +} + +int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ) { + bson_iterator next = *elem; + int size; + + bson_iterator_next( &next ); + size = next.cur - elem->cur; + + if ( name_or_null == NULL ) { + if( bson_ensure_space( b, size ) == BSON_ERROR ) + return BSON_ERROR; + bson_append( b, elem->cur, size ); + } else { + int data_size = size - 2 - strlen( bson_iterator_key( elem ) ); + bson_append_estart( b, elem->cur[0], name_or_null, data_size ); + bson_append( b, bson_iterator_value( elem ), data_size ); + } + + return BSON_OK; +} + +int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ) { + if ( bson_append_estart( b, BSON_TIMESTAMP, name, 8 ) == BSON_ERROR ) return BSON_ERROR; + + bson_append32( b , &( ts->i ) ); + bson_append32( b , &( ts->t ) ); + + return BSON_OK; +} + +int bson_append_date( bson *b, const char *name, bson_date_t millis ) { + if ( bson_append_estart( b, BSON_DATE, name, 8 ) == BSON_ERROR ) return BSON_ERROR; + bson_append64( b , &millis ); + return BSON_OK; +} + +int bson_append_time_t( bson *b, const char *name, time_t secs ) { + return bson_append_date( b, name, ( bson_date_t )secs * 1000 ); +} + +int bson_append_start_object( bson *b, const char *name ) { + if ( bson_append_estart( b, BSON_OBJECT, name, 5 ) == BSON_ERROR ) return BSON_ERROR; + b->stack[ b->stackPos++ ] = b->cur - b->data; + bson_append32( b , &zero ); + return BSON_OK; +} + +int bson_append_start_array( bson *b, const char *name ) { + if ( bson_append_estart( b, BSON_ARRAY, name, 5 ) == BSON_ERROR ) return BSON_ERROR; + b->stack[ b->stackPos++ ] = b->cur - b->data; + bson_append32( b , &zero ); + return BSON_OK; +} + +int bson_append_finish_object( bson *b ) { + char *start; + int i; + if ( bson_ensure_space( b, 1 ) == BSON_ERROR ) return BSON_ERROR; + bson_append_byte( b , 0 ); + + start = b->data + b->stack[ --b->stackPos ]; + i = b->cur - start; + bson_little_endian32( start, &i ); + + return BSON_OK; +} + +int bson_append_finish_array( bson *b ) { + return bson_append_finish_object( b ); +} + + +/* Error handling and allocators. */ + +static bson_err_handler err_handler = NULL; + +bson_err_handler set_bson_err_handler( bson_err_handler func ) { + bson_err_handler old = err_handler; + err_handler = func; + return old; +} + +void *bson_malloc( int size ) { + void *p; + p = bson_malloc_func( size ); + bson_fatal_msg( !!p, "malloc() failed" ); + return p; +} + +void *bson_realloc( void *ptr, int size ) { + void *p; + p = bson_realloc_func( ptr, size ); + bson_fatal_msg( !!p, "realloc() failed" ); + return p; +} + +int _bson_errprintf( const char *format, ... ) { + va_list ap; + int ret; + va_start( ap, format ); + ret = vfprintf( stderr, format, ap ); + va_end( ap ); + + return ret; +} + +/** + * This method is invoked when a non-fatal bson error is encountered. + * Calls the error handler if available. + * + * @param + */ +void bson_builder_error( bson *b ) { + if( err_handler ) + err_handler( "BSON error." ); +} + +void bson_fatal( int ok ) { + bson_fatal_msg( ok, "" ); +} + +void bson_fatal_msg( int ok , const char *msg ) { + if ( ok ) + return; + + if ( err_handler ) { + err_handler( msg ); + } + + bson_errprintf( "error: %s\n" , msg ); + exit( -5 ); +} + + +/* Efficiently copy an integer to a string. */ +extern const char bson_numstrs[1000][4]; + +void bson_numstr( char *str, int i ) { + if( i < 1000 ) + memcpy( str, bson_numstrs[i], 4 ); + else + bson_sprintf( str,"%d", i ); +} diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h new file mode 100644 index 0000000000..2e385b9160 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/bson.h @@ -0,0 +1,975 @@ +/** + * @file bson.h + * @brief BSON Declarations + */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BSON_H_ +#define _BSON_H_ + +#include "platform.h" +#include +#include +#include +#include +#include + +MONGO_EXTERN_C_START + +#define BSON_OK 0 +#define BSON_ERROR -1 + +enum bson_error_t { + BSON_SIZE_OVERFLOW = 1 /**< Trying to create a BSON object larger than INT_MAX. */ +}; + +enum bson_validity_t { + BSON_VALID = 0, /**< BSON is valid and UTF-8 compliant. */ + BSON_NOT_UTF8 = ( 1<<1 ), /**< A key or a string is not valid UTF-8. */ + BSON_FIELD_HAS_DOT = ( 1<<2 ), /**< Warning: key contains '.' character. */ + BSON_FIELD_INIT_DOLLAR = ( 1<<3 ), /**< Warning: key starts with '$' character. */ + BSON_ALREADY_FINISHED = ( 1<<4 ) /**< Trying to modify a finished BSON object. */ +}; + +enum bson_binary_subtype_t { + BSON_BIN_BINARY = 0, + BSON_BIN_FUNC = 1, + BSON_BIN_BINARY_OLD = 2, + BSON_BIN_UUID = 3, + BSON_BIN_MD5 = 5, + BSON_BIN_USER = 128 +}; + +typedef enum { + BSON_EOO = 0, + BSON_DOUBLE = 1, + BSON_STRING = 2, + BSON_OBJECT = 3, + BSON_ARRAY = 4, + BSON_BINDATA = 5, + BSON_UNDEFINED = 6, + BSON_OID = 7, + BSON_BOOL = 8, + BSON_DATE = 9, + BSON_NULL = 10, + BSON_REGEX = 11, + BSON_DBREF = 12, /**< Deprecated. */ + BSON_CODE = 13, + BSON_SYMBOL = 14, + BSON_CODEWSCOPE = 15, + BSON_INT = 16, + BSON_TIMESTAMP = 17, + BSON_LONG = 18 +} bson_type; + +typedef int bson_bool_t; + +typedef struct { + const char *cur; + bson_bool_t first; +} bson_iterator; + +typedef struct { + char *data; + char *cur; + int dataSize; + bson_bool_t finished; + int stack[32]; + int stackPos; + int err; /**< Bitfield representing errors or warnings on this buffer */ + char *errstr; /**< A string representation of the most recent error or warning. */ +} bson; + +#pragma pack(1) +typedef union { + char bytes[12]; + int ints[3]; +} bson_oid_t; +#pragma pack() + +typedef int64_t bson_date_t; /* milliseconds since epoch UTC */ + +typedef struct { + int i; /* increment */ + int t; /* time in seconds */ +} bson_timestamp_t; + +/* ---------------------------- + READING + ------------------------------ */ + +/** + * Size of a BSON object. + * + * @param b the BSON object. + * + * @return the size. + */ +int bson_size( const bson *b ); + +/** + * Print a string representation of a BSON object. + * + * @param b the BSON object to print. + */ +void bson_print( bson *b ); + +/** + * Return a pointer to the raw buffer stored by this bson object. + * + * @param b a BSON object + */ +const char *bson_data( bson *b ); + +/** + * Print a string representation of a BSON object. + * + * @param bson the raw data to print. + * @param depth the depth to recurse the object.x + */ +void bson_print_raw( const char *bson , int depth ); + +/** + * Advance a bson_iterator to the named field. + * + * @param it the bson_iterator to use. + * @param obj the BSON object to use. + * @param name the name of the field to find. + * + * @return the type of the found object or BSON_EOO if it is not found. + */ +bson_type bson_find( bson_iterator *it, const bson *obj, const char *name ); + +/** + * Initialize a bson_iterator. + * + * @param i the bson_iterator to initialize. + * @param bson the BSON object to associate with the iterator. + */ +void bson_iterator_init( bson_iterator *i , const bson *b ); + +/** + * Initialize a bson iterator from a const char* buffer. Note + * that this is mostly used internally. + * + * @param i the bson_iterator to initialize. + * @param buffer the buffer to point to. + */ +void bson_iterator_from_buffer( bson_iterator *i, const char *buffer ); + +/* more returns true for eoo. best to loop with bson_iterator_next(&it) */ +/** + * Check to see if the bson_iterator has more data. + * + * @param i the iterator. + * + * @return returns true if there is more data. + */ +bson_bool_t bson_iterator_more( const bson_iterator *i ); + +/** + * Point the iterator at the next BSON object. + * + * @param i the bson_iterator. + * + * @return the type of the next BSON object. + */ +bson_type bson_iterator_next( bson_iterator *i ); + +/** + * Get the type of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the type of the current BSON object. + */ +bson_type bson_iterator_type( const bson_iterator *i ); + +/** + * Get the key of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the key of the current BSON object. + */ +const char *bson_iterator_key( const bson_iterator *i ); + +/** + * Get the value of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +const char *bson_iterator_value( const bson_iterator *i ); + +/* these convert to the right type (return 0 if non-numeric) */ +/** + * Get the double value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +double bson_iterator_double( const bson_iterator *i ); + +/** + * Get the int value of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +int bson_iterator_int( const bson_iterator *i ); + +/** + * Get the long value of the BSON object currently pointed to by the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +int64_t bson_iterator_long( const bson_iterator *i ); + +/* return the bson timestamp as a whole or in parts */ +/** + * Get the timestamp value of the BSON object currently pointed to by + * the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +bson_timestamp_t bson_iterator_timestamp( const bson_iterator *i ); + +/** + * Get the boolean value of the BSON object currently pointed to by + * the iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +/* false: boolean false, 0 in any type, or null */ +/* true: anything else (even empty strings and objects) */ +bson_bool_t bson_iterator_bool( const bson_iterator *i ); + +/** + * Get the double value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +/* these assume you are using the right type */ +double bson_iterator_double_raw( const bson_iterator *i ); + +/** + * Get the int value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +int bson_iterator_int_raw( const bson_iterator *i ); + +/** + * Get the long value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +int64_t bson_iterator_long_raw( const bson_iterator *i ); + +/** + * Get the bson_bool_t value of the BSON object currently pointed to by the + * iterator. Assumes the correct type is used. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +bson_bool_t bson_iterator_bool_raw( const bson_iterator *i ); + +/** + * Get the bson_oid_t value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +bson_oid_t *bson_iterator_oid( const bson_iterator *i ); + +/** + * Get the string value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON object. + */ +/* these can also be used with bson_code and bson_symbol*/ +const char *bson_iterator_string( const bson_iterator *i ); + +/** + * Get the string length of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the length of the current BSON object. + */ +int bson_iterator_string_len( const bson_iterator *i ); + +/** + * Get the code value of the BSON object currently pointed to by the + * iterator. Works with bson_code, bson_codewscope, and BSON_STRING + * returns NULL for everything else. + * + * @param i the bson_iterator + * + * @return the code value of the current BSON object. + */ +/* works with bson_code, bson_codewscope, and BSON_STRING */ +/* returns NULL for everything else */ +const char *bson_iterator_code( const bson_iterator *i ); + +/** + * Calls bson_empty on scope if not a bson_codewscope + * + * @param i the bson_iterator. + * @param scope the bson scope. + */ +/* calls bson_empty on scope if not a bson_codewscope */ +void bson_iterator_code_scope( const bson_iterator *i, bson *scope ); + +/** + * Get the date value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the date value of the current BSON object. + */ +/* both of these only work with bson_date */ +bson_date_t bson_iterator_date( const bson_iterator *i ); + +/** + * Get the time value of the BSON object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the time value of the current BSON object. + */ +time_t bson_iterator_time_t( const bson_iterator *i ); + +/** + * Get the length of the BSON binary object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the length of the current BSON binary object. + */ +int bson_iterator_bin_len( const bson_iterator *i ); + +/** + * Get the type of the BSON binary object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the type of the current BSON binary object. + */ +char bson_iterator_bin_type( const bson_iterator *i ); + +/** + * Get the value of the BSON binary object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON binary object. + */ +const char *bson_iterator_bin_data( const bson_iterator *i ); + +/** + * Get the value of the BSON regex object currently pointed to by the + * iterator. + * + * @param i the bson_iterator + * + * @return the value of the current BSON regex object. + */ +const char *bson_iterator_regex( const bson_iterator *i ); + +/** + * Get the options of the BSON regex object currently pointed to by the + * iterator. + * + * @param i the bson_iterator. + * + * @return the options of the current BSON regex object. + */ +const char *bson_iterator_regex_opts( const bson_iterator *i ); + +/* these work with BSON_OBJECT and BSON_ARRAY */ +/** + * Get the BSON subobject currently pointed to by the + * iterator. + * + * @param i the bson_iterator. + * @param sub the BSON subobject destination. + */ +void bson_iterator_subobject( const bson_iterator *i, bson *sub ); + +/** + * Get a bson_iterator that on the BSON subobject. + * + * @param i the bson_iterator. + * @param sub the iterator to point at the BSON subobject. + */ +void bson_iterator_subiterator( const bson_iterator *i, bson_iterator *sub ); + +/* str must be at least 24 hex chars + null byte */ +/** + * Create a bson_oid_t from a string. + * + * @param oid the bson_oid_t destination. + * @param str a null terminated string comprised of at least 24 hex chars. + */ +void bson_oid_from_string( bson_oid_t *oid, const char *str ); + +/** + * Create a string representation of the bson_oid_t. + * + * @param oid the bson_oid_t source. + * @param str the string representation destination. + */ +void bson_oid_to_string( const bson_oid_t *oid, char *str ); + +/** + * Create a bson_oid object. + * + * @param oid the destination for the newly created bson_oid_t. + */ +void bson_oid_gen( bson_oid_t *oid ); + +/** + * Set a function to be used to generate the second four bytes + * of an object id. + * + * @param func a pointer to a function that returns an int. + */ +void bson_set_oid_fuzz( int ( *func )( void ) ); + +/** + * Set a function to be used to generate the incrementing part + * of an object id (last four bytes). If you need thread-safety + * in generating object ids, you should set this function. + * + * @param func a pointer to a function that returns an int. + */ +void bson_set_oid_inc( int ( *func )( void ) ); + +/** + * Get the time a bson_oid_t was created. + * + * @param oid the bson_oid_t. + */ +time_t bson_oid_generated_time( bson_oid_t *oid ); /* Gives the time the OID was created */ + +/* ---------------------------- + BUILDING + ------------------------------ */ + +/** + * Initialize a new bson object. If not created + * with bson_new, you must initialize each new bson + * object using this function. + * + * @note When finished, you must pass the bson object to + * bson_destroy( ). + */ +void bson_init( bson *b ); + +/** + * Initialize a BSON object, and point its data + * pointer to the provided char*. + * + * @param b the BSON object to initialize. + * @param data the raw BSON data. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_init_data( bson *b , char *data ); + +/** + * Initialize a BSON object, and set its + * buffer to the given size. + * + * @param b the BSON object to initialize. + * @param size the initial size of the buffer. + * + * @return BSON_OK or BSON_ERROR. + */ +void bson_init_size( bson *b, int size ); + +/** + * Grow a bson object. + * + * @param b the bson to grow. + * @param bytesNeeded the additional number of bytes needed. + * + * @return BSON_OK or BSON_ERROR with the bson error object set. + * Exits if allocation fails. + */ +int bson_ensure_space( bson *b, const int bytesNeeded ); + +/** + * Finalize a bson object. + * + * @param b the bson object to finalize. + * + * @return the standard error code. To deallocate memory, + * call bson_destroy on the bson object. + */ +int bson_finish( bson *b ); + +/** + * Destroy a bson object. + * + * @param b the bson object to destroy. + * + */ +void bson_destroy( bson *b ); + +/** + * Returns a pointer to a static empty BSON object. + * + * @param obj the BSON object to initialize. + * + * @return the empty initialized BSON object. + */ +/* returns pointer to static empty bson object */ +bson *bson_empty( bson *obj ); + +/** + * Copy BSON data only from one object to another. + * + * @param out the copy destination BSON object. + * @param in the copy source BSON object. + */ +void bson_copy_basic( bson *out, const bson *in ); + +/** + * Make a complete copy of the a BSON object. + * + * @param out the copy destination BSON object. + * @param in the copy source BSON object. + */ +void bson_copy( bson *out, const bson *in ); /* puts data in new buffer. NOOP if out==NULL */ + +/** + * Append a previously created bson_oid_t to a bson object. + * + * @param b the bson to append to. + * @param name the key for the bson_oid_t. + * @param oid the bson_oid_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_oid( bson *b, const char *name, const bson_oid_t *oid ); + +/** + * Append a bson_oid_t to a bson. + * + * @param b the bson to append to. + * @param name the key for the bson_oid_t. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_new_oid( bson *b, const char *name ); + +/** + * Append an int to a bson. + * + * @param b the bson to append to. + * @param name the key for the int. + * @param i the int to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_int( bson *b, const char *name, const int i ); + +/** + * Append an long to a bson. + * + * @param b the bson to append to. + * @param name the key for the long. + * @param i the long to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_long( bson *b, const char *name, const int64_t i ); + +/** + * Append an double to a bson. + * + * @param b the bson to append to. + * @param name the key for the double. + * @param d the double to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_double( bson *b, const char *name, const double d ); + +/** + * Append a string to a bson. + * + * @param b the bson to append to. + * @param name the key for the string. + * @param str the string to append. + * + * @return BSON_OK or BSON_ERROR. +*/ +int bson_append_string( bson *b, const char *name, const char *str ); + +/** + * Append len bytes of a string to a bson. + * + * @param b the bson to append to. + * @param name the key for the string. + * @param str the string to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_string_n( bson *b, const char *name, const char *str, int len ); + +/** + * Append a symbol to a bson. + * + * @param b the bson to append to. + * @param name the key for the symbol. + * @param str the symbol to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_symbol( bson *b, const char *name, const char *str ); + +/** + * Append len bytes of a symbol to a bson. + * + * @param b the bson to append to. + * @param name the key for the symbol. + * @param str the symbol to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_symbol_n( bson *b, const char *name, const char *str, int len ); + +/** + * Append code to a bson. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the code to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_code( bson *b, const char *name, const char *str ); + +/** + * Append len bytes of code to a bson. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the code to append. + * @param len the number of bytes from str to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_code_n( bson *b, const char *name, const char *str, int len ); + +/** + * Append code to a bson with scope. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the string to append. + * @param scope a BSON object containing the scope. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_code_w_scope( bson *b, const char *name, const char *code, const bson *scope ); + +/** + * Append len bytes of code to a bson with scope. + * + * @param b the bson to append to. + * @param name the key for the code. + * @param str the string to append. + * @param len the number of bytes from str to append. + * @param scope a BSON object containing the scope. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_code_w_scope_n( bson *b, const char *name, const char *code, int size, const bson *scope ); + +/** + * Append binary data to a bson. + * + * @param b the bson to append to. + * @param name the key for the data. + * @param type the binary data type. + * @param str the binary data. + * @param len the length of the data. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_binary( bson *b, const char *name, char type, const char *str, int len ); + +/** + * Append a bson_bool_t to a bson. + * + * @param b the bson to append to. + * @param name the key for the boolean value. + * @param v the bson_bool_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_bool( bson *b, const char *name, const bson_bool_t v ); + +/** + * Append a null value to a bson. + * + * @param b the bson to append to. + * @param name the key for the null value. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_null( bson *b, const char *name ); + +/** + * Append an undefined value to a bson. + * + * @param b the bson to append to. + * @param name the key for the undefined value. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_undefined( bson *b, const char *name ); + +/** + * Append a regex value to a bson. + * + * @param b the bson to append to. + * @param name the key for the regex value. + * @param pattern the regex pattern to append. + * @param the regex options. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_regex( bson *b, const char *name, const char *pattern, const char *opts ); + +/** + * Append bson data to a bson. + * + * @param b the bson to append to. + * @param name the key for the bson data. + * @param bson the bson object to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_bson( bson *b, const char *name, const bson *bson ); + +/** + * Append a BSON element to a bson from the current point of an iterator. + * + * @param b the bson to append to. + * @param name_or_null the key for the BSON element, or NULL. + * @param elem the bson_iterator. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_element( bson *b, const char *name_or_null, const bson_iterator *elem ); + +/** + * Append a bson_timestamp_t value to a bson. + * + * @param b the bson to append to. + * @param name the key for the timestampe value. + * @param ts the bson_timestamp_t value to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_timestamp( bson *b, const char *name, bson_timestamp_t *ts ); + +/* these both append a bson_date */ +/** + * Append a bson_date_t value to a bson. + * + * @param b the bson to append to. + * @param name the key for the date value. + * @param millis the bson_date_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_date( bson *b, const char *name, bson_date_t millis ); + +/** + * Append a time_t value to a bson. + * + * @param b the bson to append to. + * @param name the key for the date value. + * @param secs the time_t to append. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_time_t( bson *b, const char *name, time_t secs ); + +/** + * Start appending a new object to a bson. + * + * @param b the bson to append to. + * @param name the name of the new object. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_start_object( bson *b, const char *name ); + +/** + * Start appending a new array to a bson. + * + * @param b the bson to append to. + * @param name the name of the new array. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_start_array( bson *b, const char *name ); + +/** + * Finish appending a new object or array to a bson. + * + * @param b the bson to append to. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_finish_object( bson *b ); + +/** + * Finish appending a new object or array to a bson. This + * is simply an alias for bson_append_finish_object. + * + * @param b the bson to append to. + * + * @return BSON_OK or BSON_ERROR. + */ +int bson_append_finish_array( bson *b ); + +void bson_numstr( char *str, int i ); + +void bson_incnumstr( char *str ); + +/* Error handling and stadard library function over-riding. */ +/* -------------------------------------------------------- */ + +/* bson_err_handlers shouldn't return!!! */ +typedef void( *bson_err_handler )( const char *errmsg ); + +typedef int (*bson_printf_func)( const char *, ... ); +typedef int (*bson_fprintf_func)( FILE *, const char *, ... ); +typedef int (*bson_sprintf_func)( char *, const char *, ... ); + +extern void *( *bson_malloc_func )( size_t ); +extern void *( *bson_realloc_func )( void *, size_t ); +extern void ( *bson_free )( void * ); + +extern bson_printf_func bson_printf; +extern bson_fprintf_func bson_fprintf; +extern bson_sprintf_func bson_sprintf; + +extern bson_printf_func bson_errprintf; + +/** + * Allocates memory and checks return value, exiting fatally if malloc() fails. + * + * @param size bytes to allocate. + * + * @return a pointer to the allocated memory. + * + * @sa malloc(3) + */ +void *bson_malloc( int size ); + +/** + * Changes the size of allocated memory and checks return value, + * exiting fatally if realloc() fails. + * + * @param ptr pointer to the space to reallocate. + * @param size bytes to allocate. + * + * @return a pointer to the allocated memory. + * + * @sa realloc() + */ +void *bson_realloc( void *ptr, int size ); + +/** + * Set a function for error handling. + * + * @param func a bson_err_handler function. + * + * @return the old error handling function, or NULL. + */ +bson_err_handler set_bson_err_handler( bson_err_handler func ); + +/* does nothing if ok != 0 */ +/** + * Exit fatally. + * + * @param ok exits if ok is equal to 0. + */ +void bson_fatal( int ok ); + +/** + * Exit fatally with an error message. + * + * @param ok exits if ok is equal to 0. + * @param msg prints to stderr before exiting. + */ +void bson_fatal_msg( int ok, const char *msg ); + +/** + * Invoke the error handler, but do not exit. + * + * @param b the buffer object. + */ +void bson_builder_error( bson *b ); + +MONGO_EXTERN_C_END +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c new file mode 100644 index 0000000000..8d2da1502f --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.c @@ -0,0 +1,148 @@ +/* + * Copyright 2009-2011 10gen, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * Portions Copyright 2001 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + + +#include "bson.h" +#include "encoding.h" + +/* + * Index into the table below with the first byte of a UTF-8 sequence to + * get the number of trailing bytes that are supposed to follow it. + */ +static const char trailingBytesForUTF8[256] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 +}; + +/* --------------------------------------------------------------------- */ + +/* + * Utility routine to tell whether a sequence of bytes is legal UTF-8. + * This must be called with the length pre-determined by the first byte. + * The length can be set by: + * length = trailingBytesForUTF8[*source]+1; + * and the sequence is illegal right away if there aren't that many bytes + * available. + * If presented with a length > 4, this returns 0. The Unicode + * definition of UTF-8 goes up to 4-byte sequences. + */ +static int isLegalUTF8( const unsigned char *source, int length ) { + unsigned char a; + const unsigned char *srcptr = source + length; + switch ( length ) { + default: + return 0; + /* Everything else falls through when "true"... */ + case 4: + if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0; + case 3: + if ( ( a = ( *--srcptr ) ) < 0x80 || a > 0xBF ) return 0; + case 2: + if ( ( a = ( *--srcptr ) ) > 0xBF ) return 0; + switch ( *source ) { + /* no fall-through in this inner switch */ + case 0xE0: + if ( a < 0xA0 ) return 0; + break; + case 0xF0: + if ( a < 0x90 ) return 0; + break; + case 0xF4: + if ( a > 0x8F ) return 0; + break; + default: + if ( a < 0x80 ) return 0; + } + case 1: + if ( *source >= 0x80 && *source < 0xC2 ) return 0; + if ( *source > 0xF4 ) return 0; + } + return 1; +} + +static int bson_validate_string( bson *b, const unsigned char *string, + const int length, const char check_utf8, const char check_dot, + const char check_dollar ) { + + int position = 0; + int sequence_length = 1; + + if( check_dollar && string[0] == '$' ) { + b->err |= BSON_FIELD_INIT_DOLLAR; + } + + while ( position < length ) { + if ( check_dot && *( string + position ) == '.' ) { + b->err |= BSON_FIELD_HAS_DOT; + } + + if ( check_utf8 ) { + sequence_length = trailingBytesForUTF8[*( string + position )] + 1; + if ( ( position + sequence_length ) > length ) { + b->err |= BSON_NOT_UTF8; + return BSON_ERROR; + } + if ( !isLegalUTF8( string + position, sequence_length ) ) { + b->err |= BSON_NOT_UTF8; + return BSON_ERROR; + } + } + position += sequence_length; + } + + return BSON_OK; +} + + +int bson_check_string( bson *b, const char *string, + const int length ) { + + return bson_validate_string( b, ( const unsigned char * )string, length, 1, 0, 0 ); +} + +int bson_check_field_name( bson *b, const char *string, + const int length ) { + + return bson_validate_string( b, ( const unsigned char * )string, length, 1, 1, 1 ); +} diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h new file mode 100644 index 0000000000..a2a07c6d5d --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/encoding.h @@ -0,0 +1,54 @@ +/* + * Copyright 2009-2011 10gen, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _BSON_ENCODING_H_ +#define _BSON_ENCODING_H_ + +MONGO_EXTERN_C_START + +/** + * Check that a field name is valid UTF8, does not start with a '$', + * and contains no '.' characters. Set bson bit field appropriately. + * Note that we don't need to check for '\0' because we're using + * strlen(3), which stops at '\0'. + * + * @param b The bson object to which field name will be appended. + * @param string The field name as char*. + * @param length The length of the field name. + * + * @return BSON_OK if valid UTF8 and BSON_ERROR if not. All BSON strings must be + * valid UTF8. This function will also check whether the string + * contains '.' or starts with '$', since the validity of this depends on context. + * Set the value of b->err appropriately. + */ +int bson_check_field_name( bson *b, const char *string, + const int length ); + +/** + * Check that a string is valid UTF8. Sets the buffer bit field appropriately. + * + * @param b The bson object to which string will be appended. + * @param string The string to check. + * @param length The length of the string. + * + * @return BSON_OK if valid UTF-8; otherwise, BSON_ERROR. + * Sets b->err on error. + */ +bson_bool_t bson_check_string( bson *b, const char *string, + const int length ); + +MONGO_EXTERN_C_END +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c new file mode 100644 index 0000000000..f51b397d3f --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.c @@ -0,0 +1,685 @@ +/* gridfs.c */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gridfs.h" +#include +#include +#include +#include + +static bson *chunk_new( bson_oid_t id, int chunkNumber, + const char *data, int len ) { + bson *b = bson_malloc( sizeof( bson ) ); + + bson_init( b ); + bson_append_oid( b, "files_id", &id ); + bson_append_int( b, "n", chunkNumber ); + bson_append_binary( b, "data", BSON_BIN_BINARY, data, len ); + bson_finish( b ); + return b; +} + +static void chunk_free( bson *oChunk ) { + bson_destroy( oChunk ); + bson_free( oChunk ); +} + +int gridfs_init( mongo *client, const char *dbname, const char *prefix, + gridfs *gfs ) { + + int options; + bson b; + bson_bool_t success; + + gfs->client = client; + + /* Allocate space to own the dbname */ + gfs->dbname = ( const char * )bson_malloc( strlen( dbname )+1 ); + strcpy( ( char * )gfs->dbname, dbname ); + + /* Allocate space to own the prefix */ + if ( prefix == NULL ) prefix = "fs"; + gfs->prefix = ( const char * )bson_malloc( strlen( prefix )+1 ); + strcpy( ( char * )gfs->prefix, prefix ); + + /* Allocate space to own files_ns */ + gfs->files_ns = + ( const char * ) bson_malloc ( strlen( prefix )+strlen( dbname )+strlen( ".files" )+2 ); + strcpy( ( char * )gfs->files_ns, dbname ); + strcat( ( char * )gfs->files_ns, "." ); + strcat( ( char * )gfs->files_ns, prefix ); + strcat( ( char * )gfs->files_ns, ".files" ); + + /* Allocate space to own chunks_ns */ + gfs->chunks_ns = ( const char * ) bson_malloc( strlen( prefix ) + strlen( dbname ) + + strlen( ".chunks" ) + 2 ); + strcpy( ( char * )gfs->chunks_ns, dbname ); + strcat( ( char * )gfs->chunks_ns, "." ); + strcat( ( char * )gfs->chunks_ns, prefix ); + strcat( ( char * )gfs->chunks_ns, ".chunks" ); + + bson_init( &b ); + bson_append_int( &b, "filename", 1 ); + bson_finish( &b ); + options = 0; + success = ( mongo_create_index( gfs->client, gfs->files_ns, &b, options, NULL ) == MONGO_OK ); + bson_destroy( &b ); + if ( !success ) { + bson_free( ( char * )gfs->dbname ); + bson_free( ( char * )gfs->prefix ); + bson_free( ( char * )gfs->files_ns ); + bson_free( ( char * )gfs->chunks_ns ); + return MONGO_ERROR; + } + + bson_init( &b ); + bson_append_int( &b, "files_id", 1 ); + bson_append_int( &b, "n", 1 ); + bson_finish( &b ); + options = MONGO_INDEX_UNIQUE; + success = ( mongo_create_index( gfs->client, gfs->chunks_ns, &b, options, NULL ) == MONGO_OK ); + bson_destroy( &b ); + if ( !success ) { + bson_free( ( char * )gfs->dbname ); + bson_free( ( char * )gfs->prefix ); + bson_free( ( char * )gfs->files_ns ); + bson_free( ( char * )gfs->chunks_ns ); + return MONGO_ERROR; + } + + return MONGO_OK; +} + +void gridfs_destroy( gridfs *gfs ) { + if ( gfs == NULL ) return; + if ( gfs->dbname ) bson_free( ( char * )gfs->dbname ); + if ( gfs->prefix ) bson_free( ( char * )gfs->prefix ); + if ( gfs->files_ns ) bson_free( ( char * )gfs->files_ns ); + if ( gfs->chunks_ns ) bson_free( ( char * )gfs->chunks_ns ); +} + +static int gridfs_insert_file( gridfs *gfs, const char *name, + const bson_oid_t id, gridfs_offset length, + const char *contenttype ) { + bson command; + bson ret; + bson res; + bson_iterator it; + int result; + + /* Check run md5 */ + bson_init( &command ); + bson_append_oid( &command, "filemd5", &id ); + bson_append_string( &command, "root", gfs->prefix ); + bson_finish( &command ); + assert( mongo_run_command( gfs->client, gfs->dbname, &command, &res ) == MONGO_OK ); + bson_destroy( &command ); + + /* Create and insert BSON for file metadata */ + bson_init( &ret ); + bson_append_oid( &ret, "_id", &id ); + if ( name != NULL && *name != '\0' ) { + bson_append_string( &ret, "filename", name ); + } + bson_append_long( &ret, "length", length ); + bson_append_int( &ret, "chunkSize", DEFAULT_CHUNK_SIZE ); + bson_append_date( &ret, "uploadDate", ( bson_date_t )1000*time( NULL ) ); + bson_find( &it, &res, "md5" ); + bson_append_string( &ret, "md5", bson_iterator_string( &it ) ); + bson_destroy( &res ); + if ( contenttype != NULL && *contenttype != '\0' ) { + bson_append_string( &ret, "contentType", contenttype ); + } + bson_finish( &ret ); + result = mongo_insert( gfs->client, gfs->files_ns, &ret ); + bson_destroy( &ret ); + + return result; +} + +int gridfs_store_buffer( gridfs *gfs, const char *data, + gridfs_offset length, const char *remotename, + const char *contenttype ) { + + char const *end = data + length; + const char *data_ptr = data; + bson_oid_t id; + int chunkNumber = 0; + int chunkLen; + bson *oChunk; + + /* Large files Assertion */ + assert( length <= 0xffffffff ); + + /* Generate and append an oid*/ + bson_oid_gen( &id ); + + /* Insert the file's data chunk by chunk */ + while ( data_ptr < end ) { + chunkLen = DEFAULT_CHUNK_SIZE < ( unsigned int )( end - data_ptr ) ? + DEFAULT_CHUNK_SIZE : ( unsigned int )( end - data_ptr ); + oChunk = chunk_new( id, chunkNumber, data_ptr, chunkLen ); + mongo_insert( gfs->client, gfs->chunks_ns, oChunk ); + chunk_free( oChunk ); + chunkNumber++; + data_ptr += chunkLen; + } + + /* Inserts file's metadata */ + return gridfs_insert_file( gfs, remotename, id, length, contenttype ); +} + +void gridfile_writer_init( gridfile *gfile, gridfs *gfs, + const char *remote_name, const char *content_type ) { + gfile->gfs = gfs; + + bson_oid_gen( &( gfile->id ) ); + gfile->chunk_num = 0; + gfile->length = 0; + gfile->pending_len = 0; + gfile->pending_data = NULL; + + gfile->remote_name = ( char * )bson_malloc( strlen( remote_name ) + 1 ); + strcpy( ( char * )gfile->remote_name, remote_name ); + + gfile->content_type = ( char * )bson_malloc( strlen( content_type ) + 1 ); + strcpy( ( char * )gfile->content_type, content_type ); +} + +void gridfile_write_buffer( gridfile *gfile, const char *data, + gridfs_offset length ) { + + int bytes_left = 0; + int data_partial_len = 0; + int chunks_to_write = 0; + char *buffer; + bson *oChunk; + gridfs_offset to_write = length + gfile->pending_len; + + if ( to_write < DEFAULT_CHUNK_SIZE ) { /* Less than one chunk to write */ + if( gfile->pending_data ) { + gfile->pending_data = ( char * )bson_realloc( ( void * )gfile->pending_data, gfile->pending_len + to_write ); + memcpy( gfile->pending_data + gfile->pending_len, data, length ); + } else if ( to_write > 0 ) { + gfile->pending_data = ( char * )bson_malloc( to_write ); + memcpy( gfile->pending_data, data, length ); + } + gfile->pending_len += length; + + } else { /* At least one chunk of data to write */ + + /* If there's a pending chunk to be written, we need to combine + * the buffer provided up to DEFAULT_CHUNK_SIZE. + */ + if ( gfile->pending_len > 0 ) { + chunks_to_write = to_write / DEFAULT_CHUNK_SIZE; + bytes_left = to_write % DEFAULT_CHUNK_SIZE; + + data_partial_len = DEFAULT_CHUNK_SIZE - gfile->pending_len; + buffer = ( char * )bson_malloc( DEFAULT_CHUNK_SIZE ); + memcpy( buffer, gfile->pending_data, gfile->pending_len ); + memcpy( buffer + gfile->pending_len, data, data_partial_len ); + + oChunk = chunk_new( gfile->id, gfile->chunk_num, buffer, DEFAULT_CHUNK_SIZE ); + mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk ); + chunk_free( oChunk ); + gfile->chunk_num++; + gfile->length += DEFAULT_CHUNK_SIZE; + data += data_partial_len; + + chunks_to_write--; + + bson_free( buffer ); + } + + while( chunks_to_write > 0 ) { + oChunk = chunk_new( gfile->id, gfile->chunk_num, data, DEFAULT_CHUNK_SIZE ); + mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk ); + chunk_free( oChunk ); + gfile->chunk_num++; + chunks_to_write--; + gfile->length += DEFAULT_CHUNK_SIZE; + data += DEFAULT_CHUNK_SIZE; + } + + bson_free( gfile->pending_data ); + + /* If there are any leftover bytes, store them as pending data. */ + if( bytes_left == 0 ) + gfile->pending_data = NULL; + else { + gfile->pending_data = ( char * )bson_malloc( bytes_left ); + memcpy( gfile->pending_data, data, bytes_left ); + } + + gfile->pending_len = bytes_left; + } +} + +int gridfile_writer_done( gridfile *gfile ) { + + /* write any remaining pending chunk data. + * pending data will always take up less than one chunk */ + bson *oChunk; + int response; + if( gfile->pending_data ) { + oChunk = chunk_new( gfile->id, gfile->chunk_num, gfile->pending_data, gfile->pending_len ); + mongo_insert( gfile->gfs->client, gfile->gfs->chunks_ns, oChunk ); + chunk_free( oChunk ); + bson_free( gfile->pending_data ); + gfile->length += gfile->pending_len; + } + + /* insert into files collection */ + response = gridfs_insert_file( gfile->gfs, gfile->remote_name, gfile->id, + gfile->length, gfile->content_type ); + + bson_free( gfile->remote_name ); + bson_free( gfile->content_type ); + + return response; +} + +int gridfs_store_file( gridfs *gfs, const char *filename, + const char *remotename, const char *contenttype ) { + + char buffer[DEFAULT_CHUNK_SIZE]; + FILE *fd; + bson_oid_t id; + int chunkNumber = 0; + gridfs_offset length = 0; + gridfs_offset chunkLen = 0; + bson *oChunk; + + /* Open the file and the correct stream */ + if ( strcmp( filename, "-" ) == 0 ) fd = stdin; + else fd = fopen( filename, "rb" ); + assert( fd != NULL ); /* No such file */ + + /* Generate and append an oid*/ + bson_oid_gen( &id ); + + /* Insert the file chunk by chunk */ + chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd ); + do { + oChunk = chunk_new( id, chunkNumber, buffer, chunkLen ); + mongo_insert( gfs->client, gfs->chunks_ns, oChunk ); + chunk_free( oChunk ); + length += chunkLen; + chunkNumber++; + chunkLen = fread( buffer, 1, DEFAULT_CHUNK_SIZE, fd ); + } while ( chunkLen != 0 ); + + /* Close the file stream */ + if ( fd != stdin ) fclose( fd ); + + /* Large files Assertion */ + /* assert(length <= 0xffffffff); */ + + /* Optional Remote Name */ + if ( remotename == NULL || *remotename == '\0' ) { + remotename = filename; + } + + /* Inserts file's metadata */ + return gridfs_insert_file( gfs, remotename, id, length, contenttype ); +} + +void gridfs_remove_filename( gridfs *gfs, const char *filename ) { + bson query; + mongo_cursor *files; + bson file; + bson_iterator it; + bson_oid_t id; + bson b; + + bson_init( &query ); + bson_append_string( &query, "filename", filename ); + bson_finish( &query ); + files = mongo_find( gfs->client, gfs->files_ns, &query, NULL, 0, 0, 0 ); + bson_destroy( &query ); + + /* Remove each file and it's chunks from files named filename */ + while ( mongo_cursor_next( files ) == MONGO_OK ) { + file = files->current; + bson_find( &it, &file, "_id" ); + id = *bson_iterator_oid( &it ); + + /* Remove the file with the specified id */ + bson_init( &b ); + bson_append_oid( &b, "_id", &id ); + bson_finish( &b ); + mongo_remove( gfs->client, gfs->files_ns, &b ); + bson_destroy( &b ); + + /* Remove all chunks from the file with the specified id */ + bson_init( &b ); + bson_append_oid( &b, "files_id", &id ); + bson_finish( &b ); + mongo_remove( gfs->client, gfs->chunks_ns, &b ); + bson_destroy( &b ); + } + + mongo_cursor_destroy( files ); +} + +int gridfs_find_query( gridfs *gfs, bson *query, + gridfile *gfile ) { + + bson uploadDate; + bson finalQuery; + bson out; + int i; + + bson_init( &uploadDate ); + bson_append_int( &uploadDate, "uploadDate", -1 ); + bson_finish( &uploadDate ); + + bson_init( &finalQuery ); + bson_append_bson( &finalQuery, "query", query ); + bson_append_bson( &finalQuery, "orderby", &uploadDate ); + bson_finish( &finalQuery ); + + i = ( mongo_find_one( gfs->client, gfs->files_ns, + &finalQuery, NULL, &out ) == MONGO_OK ); + bson_destroy( &uploadDate ); + bson_destroy( &finalQuery ); + if ( !i ) + return MONGO_ERROR; + else { + gridfile_init( gfs, &out, gfile ); + bson_destroy( &out ); + return MONGO_OK; + } +} + +int gridfs_find_filename( gridfs *gfs, const char *filename, + gridfile *gfile ) + +{ + bson query; + int i; + + bson_init( &query ); + bson_append_string( &query, "filename", filename ); + bson_finish( &query ); + i = gridfs_find_query( gfs, &query, gfile ); + bson_destroy( &query ); + return i; +} + +int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile ) + +{ + gfile->gfs = gfs; + gfile->pos = 0; + gfile->meta = ( bson * )bson_malloc( sizeof( bson ) ); + if ( gfile->meta == NULL ) return MONGO_ERROR; + bson_copy( gfile->meta, meta ); + return MONGO_OK; +} + +void gridfile_destroy( gridfile *gfile ) + +{ + bson_destroy( gfile->meta ); + bson_free( gfile->meta ); +} + +bson_bool_t gridfile_exists( gridfile *gfile ) { + return ( bson_bool_t )( gfile != NULL || gfile->meta == NULL ); +} + +const char *gridfile_get_filename( gridfile *gfile ) { + bson_iterator it; + + bson_find( &it, gfile->meta, "filename" ); + return bson_iterator_string( &it ); +} + +int gridfile_get_chunksize( gridfile *gfile ) { + bson_iterator it; + + bson_find( &it, gfile->meta, "chunkSize" ); + return bson_iterator_int( &it ); +} + +gridfs_offset gridfile_get_contentlength( gridfile *gfile ) { + bson_iterator it; + + bson_find( &it, gfile->meta, "length" ); + + if( bson_iterator_type( &it ) == BSON_INT ) + return ( gridfs_offset )bson_iterator_int( &it ); + else + return ( gridfs_offset )bson_iterator_long( &it ); +} + +const char *gridfile_get_contenttype( gridfile *gfile ) { + bson_iterator it; + + if ( bson_find( &it, gfile->meta, "contentType" ) ) + return bson_iterator_string( &it ); + else return NULL; +} + +bson_date_t gridfile_get_uploaddate( gridfile *gfile ) { + bson_iterator it; + + bson_find( &it, gfile->meta, "uploadDate" ); + return bson_iterator_date( &it ); +} + +const char *gridfile_get_md5( gridfile *gfile ) { + bson_iterator it; + + bson_find( &it, gfile->meta, "md5" ); + return bson_iterator_string( &it ); +} + +const char *gridfile_get_field( gridfile *gfile, const char *name ) { + bson_iterator it; + + bson_find( &it, gfile->meta, name ); + return bson_iterator_value( &it ); +} + +bson_bool_t gridfile_get_boolean( gridfile *gfile, const char *name ) { + bson_iterator it; + + bson_find( &it, gfile->meta, name ); + return bson_iterator_bool( &it ); +} + +bson gridfile_get_metadata( gridfile *gfile ) { + bson sub; + bson_iterator it; + + if ( bson_find( &it, gfile->meta, "metadata" ) ) { + bson_iterator_subobject( &it, &sub ); + return sub; + } else { + bson_empty( &sub ); + return sub; + } +} + +int gridfile_get_numchunks( gridfile *gfile ) { + bson_iterator it; + gridfs_offset length; + gridfs_offset chunkSize; + double numchunks; + + bson_find( &it, gfile->meta, "length" ); + + if( bson_iterator_type( &it ) == BSON_INT ) + length = ( gridfs_offset )bson_iterator_int( &it ); + else + length = ( gridfs_offset )bson_iterator_long( &it ); + + bson_find( &it, gfile->meta, "chunkSize" ); + chunkSize = bson_iterator_int( &it ); + numchunks = ( ( double )length/( double )chunkSize ); + return ( numchunks - ( int )numchunks > 0 ) + ? ( int )( numchunks+1 ) + : ( int )( numchunks ); +} + +bson gridfile_get_chunk( gridfile *gfile, int n ) { + bson query; + bson out; + bson_iterator it; + bson_oid_t id; + + bson_init( &query ); + bson_find( &it, gfile->meta, "_id" ); + id = *bson_iterator_oid( &it ); + bson_append_oid( &query, "files_id", &id ); + bson_append_int( &query, "n", n ); + bson_finish( &query ); + + assert( mongo_find_one( gfile->gfs->client, + gfile->gfs->chunks_ns, + &query, NULL, &out ) == MONGO_OK ); + + bson_destroy( &query ); + return out; +} + +mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ) { + bson_iterator it; + bson_oid_t id; + bson gte; + bson query; + bson orderby; + bson command; + mongo_cursor *cursor; + + bson_find( &it, gfile->meta, "_id" ); + id = *bson_iterator_oid( &it ); + + bson_init( &query ); + bson_append_oid( &query, "files_id", &id ); + if ( size == 1 ) { + bson_append_int( &query, "n", start ); + } else { + bson_init( >e ); + bson_append_int( >e, "$gte", start ); + bson_finish( >e ); + bson_append_bson( &query, "n", >e ); + bson_destroy( >e ); + } + bson_finish( &query ); + + bson_init( &orderby ); + bson_append_int( &orderby, "n", 1 ); + bson_finish( &orderby ); + + bson_init( &command ); + bson_append_bson( &command, "query", &query ); + bson_append_bson( &command, "orderby", &orderby ); + bson_finish( &command ); + + cursor = mongo_find( gfile->gfs->client, gfile->gfs->chunks_ns, + &command, NULL, size, 0, 0 ); + + bson_destroy( &command ); + bson_destroy( &query ); + bson_destroy( &orderby ); + + return cursor; +} + +gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ) { + int i; + size_t len; + bson chunk; + bson_iterator it; + const char *data; + const int num = gridfile_get_numchunks( gfile ); + + for ( i=0; ipos < size ) + ? contentlength - gfile->pos + : size; + bytes_left = size; + + first_chunk = ( gfile->pos )/chunksize; + last_chunk = ( gfile->pos+size-1 )/chunksize; + total_chunks = last_chunk - first_chunk + 1; + chunks = gridfile_get_chunks( gfile, first_chunk, total_chunks ); + + for ( i = 0; i < total_chunks; i++ ) { + mongo_cursor_next( chunks ); + chunk = chunks->current; + bson_find( &it, &chunk, "data" ); + chunk_len = bson_iterator_bin_len( &it ); + chunk_data = bson_iterator_bin_data( &it ); + if ( i == 0 ) { + chunk_data += ( gfile->pos )%chunksize; + chunk_len -= ( gfile->pos )%chunksize; + } + if ( bytes_left > chunk_len ) { + memcpy( buf, chunk_data, chunk_len ); + bytes_left -= chunk_len; + buf += chunk_len; + } else { + memcpy( buf, chunk_data, bytes_left ); + } + } + + mongo_cursor_destroy( chunks ); + gfile->pos = gfile->pos + size; + + return size; +} + +gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ) { + gridfs_offset length; + + length = gridfile_get_contentlength( gfile ); + gfile->pos = length < offset ? length : offset; + return gfile->pos; +} diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h new file mode 100644 index 0000000000..36595ee341 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/gridfs.h @@ -0,0 +1,326 @@ +/** @file gridfs.h + * + * @brief GridFS declarations + * + * */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mongo.h" + +#ifndef GRIDFS_INCLUDED +#define GRIDFS_INCLUDED + +enum {DEFAULT_CHUNK_SIZE = 256 * 1024}; + +typedef uint64_t gridfs_offset; + +/* A GridFS represents a single collection of GridFS files in the database. */ +typedef struct { + mongo *client; /**> The client to db-connection. */ + const char *dbname; /**> The root database name */ + const char *prefix; /**> The prefix of the GridFS's collections, default is NULL */ + const char *files_ns; /**> The namespace where the file's metadata is stored */ + const char *chunks_ns; /**. The namespace where the files's data is stored in chunks */ +} gridfs; + +/* A GridFile is a single GridFS file. */ +typedef struct { + gridfs *gfs; /**> The GridFS where the GridFile is located */ + bson *meta; /**> The GridFile's bson object where all its metadata is located */ + gridfs_offset pos; /**> The position is the offset in the file */ + bson_oid_t id; /**> The files_id of the gridfile */ + char *remote_name; /**> The name of the gridfile as a string */ + char *content_type; /**> The gridfile's content type */ + gridfs_offset length; /**> The length of this gridfile */ + int chunk_num; /**> The number of the current chunk being written to */ + char *pending_data; /**> A buffer storing data still to be written to chunks */ + int pending_len; /**> Length of pending_data buffer */ +} gridfile; + +/** + * Initializes a GridFS object + * @param client - db connection + * @param dbname - database name + * @param prefix - collection prefix, default is fs if NULL or empty + * @param gfs - the GridFS object to initialize + * + * @return - MONGO_OK or MONGO_ERROR. + */ +int gridfs_init( mongo *client, const char *dbname, + const char *prefix, gridfs *gfs ); + +/** + * Destroys a GridFS object. Call this when finished with + * the object.. + * + * @param gfs a grid + */ +void gridfs_destroy( gridfs *gfs ); + +/** + * Initializes a gridfile for writing incrementally with gridfs_write_buffer. + * Once initialized, you can write any number of buffers with gridfs_write_buffer. + * When done, you must call gridfs_writer_done to save the file metadata. + * + */ +void gridfile_writer_init( gridfile *gfile, gridfs *gfs, const char *remote_name, + const char *content_type ); + +/** + * Write to a GridFS file incrementally. You can call this function any number + * of times with a new buffer each time. This allows you to effectively + * stream to a GridFS file. When finished, be sure to call gridfs_writer_done. + * + */ +void gridfile_write_buffer( gridfile *gfile, const char *data, + gridfs_offset length ); + +/** + * Signal that writing of this gridfile is complete by + * writing any buffered chunks along with the entry in the + * files collection. + * + * @return - MONGO_OK or MONGO_ERROR. + */ +int gridfile_writer_done( gridfile *gfile ); + +/** + * Store a buffer as a GridFS file. + * @param gfs - the working GridFS + * @param data - pointer to buffer to store in GridFS + * @param length - length of the buffer + * @param remotename - filename for use in the database + * @param contenttype - optional MIME type for this object + * + * @return - MONGO_OK or MONGO_ERROR. + */ +int gridfs_store_buffer( gridfs *gfs, const char *data, gridfs_offset length, + const char *remotename, + const char *contenttype ); + +/** + * Open the file referenced by filename and store it as a GridFS file. + * @param gfs - the working GridFS + * @param filename - local filename relative to the process + * @param remotename - optional filename for use in the database + * @param contenttype - optional MIME type for this object + * + * @return - MONGO_OK or MONGO_ERROR. + */ +int gridfs_store_file( gridfs *gfs, const char *filename, + const char *remotename, const char *contenttype ); + +/** + * Removes the files referenced by filename from the db + * @param gfs - the working GridFS + * @param filename - the filename of the file/s to be removed + */ +void gridfs_remove_filename( gridfs *gfs, const char *filename ); + +/** + * Find the first file matching the provided query within the + * GridFS files collection, and return the file as a GridFile. + * + * @param gfs - the working GridFS + * @param query - a pointer to the bson with the query data + * @param gfile - the output GridFile to be initialized + * + * @return MONGO_OK if successful, MONGO_ERROR otherwise + */ +int gridfs_find_query( gridfs *gfs, bson *query, gridfile *gfile ); + +/** + * Find the first file referenced by filename within the GridFS + * and return it as a GridFile + * @param gfs - the working GridFS + * @param filename - filename of the file to find + * @param gfile - the output GridFile to be intialized + * + * @return MONGO_OK or MONGO_ERROR. + */ +int gridfs_find_filename( gridfs *gfs, const char *filename, gridfile *gfile ); + +/** + * Initializes a GridFile containing the GridFS and file bson + * @param gfs - the GridFS where the GridFile is located + * @param meta - the file object + * @param gfile - the output GridFile that is being initialized + * + * @return - MONGO_OK or MONGO_ERROR. + */ +int gridfile_init( gridfs *gfs, bson *meta, gridfile *gfile ); + +/** + * Destroys the GridFile + * + * @param oGridFIle - the GridFile being destroyed + */ +void gridfile_destroy( gridfile *gfile ); + +/** + * Returns whether or not the GridFile exists + * @param gfile - the GridFile being examined + */ +bson_bool_t gridfile_exists( gridfile *gfile ); + +/** + * Returns the filename of GridFile + * @param gfile - the working GridFile + * + * @return - the filename of the Gridfile + */ +const char *gridfile_get_filename( gridfile *gfile ); + +/** + * Returns the size of the chunks of the GridFile + * @param gfile - the working GridFile + * + * @return - the size of the chunks of the Gridfile + */ +int gridfile_get_chunksize( gridfile *gfile ); + +/** + * Returns the length of GridFile's data + * + * @param gfile - the working GridFile + * + * @return - the length of the Gridfile's data + */ +gridfs_offset gridfile_get_contentlength( gridfile *gfile ); + +/** + * Returns the MIME type of the GridFile + * + * @param gfile - the working GridFile + * + * @return - the MIME type of the Gridfile + * (NULL if no type specified) + */ +const char *gridfile_get_contenttype( gridfile *gfile ); + +/** + * Returns the upload date of GridFile + * + * @param gfile - the working GridFile + * + * @return - the upload date of the Gridfile + */ +bson_date_t gridfile_get_uploaddate( gridfile *gfile ); + +/** + * Returns the MD5 of GridFile + * + * @param gfile - the working GridFile + * + * @return - the MD5 of the Gridfile + */ +const char *gridfile_get_md5( gridfile *gfile ); + +/** + * Returns the field in GridFile specified by name + * + * @param gfile - the working GridFile + * @param name - the name of the field to be returned + * + * @return - the data of the field specified + * (NULL if none exists) + */ +const char *gridfile_get_field( gridfile *gfile, + const char *name ); + +/** + * Returns a boolean field in GridFile specified by name + * @param gfile - the working GridFile + * @param name - the name of the field to be returned + * + * @return - the boolean of the field specified + * (NULL if none exists) + */ +bson_bool_t gridfile_get_boolean( gridfile *gfile, + const char *name ); + +/** + * Returns the metadata of GridFile + * @param gfile - the working GridFile + * + * @return - the metadata of the Gridfile in a bson object + * (an empty bson is returned if none exists) + */ +bson gridfile_get_metadata( gridfile *gfile ); + +/** + * Returns the number of chunks in the GridFile + * @param gfile - the working GridFile + * + * @return - the number of chunks in the Gridfile + */ +int gridfile_get_numchunks( gridfile *gfile ); + +/** + * Returns chunk n of GridFile + * @param gfile - the working GridFile + * + * @return - the nth chunk of the Gridfile + */ +bson gridfile_get_chunk( gridfile *gfile, int n ); + +/** + * Returns a mongo_cursor of *size* chunks starting with chunk *start* + * + * @param gfile - the working GridFile + * @param start - the first chunk in the cursor + * @param size - the number of chunks to be returned + * + * @return - mongo_cursor of the chunks (must be destroyed after use) + */ +mongo_cursor *gridfile_get_chunks( gridfile *gfile, int start, int size ); + +/** + * Writes the GridFile to a stream + * + * @param gfile - the working GridFile + * @param stream - the file stream to write to + */ +gridfs_offset gridfile_write_file( gridfile *gfile, FILE *stream ); + +/** + * Reads length bytes from the GridFile to a buffer + * and updates the position in the file. + * (assumes the buffer is large enough) + * (if size is greater than EOF gridfile_read reads until EOF) + * + * @param gfile - the working GridFile + * @param size - the amount of bytes to be read + * @param buf - the buffer to read to + * + * @return - the number of bytes read + */ +gridfs_offset gridfile_read( gridfile *gfile, gridfs_offset size, char *buf ); + +/** + * Updates the position in the file + * (If the offset goes beyond the contentlength, + * the position is updated to the end of the file.) + * + * @param gfile - the working GridFile + * @param offset - the position to update to + * + * @return - resulting offset location + */ +gridfs_offset gridfile_seek( gridfile *gfile, gridfs_offset offset ); + +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c new file mode 100644 index 0000000000..d1c1e3ab0b --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.c @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef MONGO_BIG_ENDIAN +# define BYTE_ORDER 1 +#else +# define BYTE_ORDER -1 +#endif + +#define T_MASK ((mongo_md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +mongo_md5_process(mongo_md5_state_t *pms, const mongo_md5_byte_t *data /*[64]*/) +{ + mongo_md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + mongo_md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + mongo_md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + mongo_md5_word_t xbuf[16]; + const mongo_md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const mongo_md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const mongo_md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const mongo_md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const mongo_md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +mongo_md5_init(mongo_md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes) +{ + const mongo_md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + mongo_md5_word_t nbits = (mongo_md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + mongo_md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + mongo_md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]) +{ + static const mongo_md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + mongo_md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (mongo_md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + mongo_md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + mongo_md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (mongo_md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h new file mode 100644 index 0000000000..540da3a584 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char mongo_md5_byte_t; /* 8-bit byte */ +typedef unsigned int mongo_md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct mongo_md5_state_s { + mongo_md5_word_t count[2]; /* message length in bits, lsw first */ + mongo_md5_word_t abcd[4]; /* digest buffer */ + mongo_md5_byte_t buf[64]; /* accumulate block */ +} mongo_md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Initialize the algorithm. */ + void mongo_md5_init(mongo_md5_state_t *pms); + + /* Append a string to the message. */ + void mongo_md5_append(mongo_md5_state_t *pms, const mongo_md5_byte_t *data, int nbytes); + + /* Finish the message and return the digest. */ + void mongo_md5_finish(mongo_md5_state_t *pms, mongo_md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c new file mode 100644 index 0000000000..2090e74312 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.c @@ -0,0 +1,1217 @@ +/* mongo.c */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mongo.h" +#include "md5.h" + +#include +#include +#include +#include + +#ifdef _USE_LINUX_SYSTEM +#include "platform/linux/net.h" +#elif defined _USE_CUSTOM_SYSTEM +#include "platform/custom/net.h" +#else +#include "net.h" +#endif + +static const int ZERO = 0; +static const int ONE = 1; +mongo_message *mongo_message_create( int len , int id , int responseTo , int op ) { + mongo_message *mm = ( mongo_message * )bson_malloc( len ); + + if ( !id ) + id = rand(); + + /* native endian (converted on send) */ + mm->head.len = len; + mm->head.id = id; + mm->head.responseTo = responseTo; + mm->head.op = op; + + return mm; +} + +/* Always calls bson_free(mm) */ +int mongo_message_send( mongo *conn, mongo_message *mm ) { + mongo_header head; /* little endian */ + int res; + bson_little_endian32( &head.len, &mm->head.len ); + bson_little_endian32( &head.id, &mm->head.id ); + bson_little_endian32( &head.responseTo, &mm->head.responseTo ); + bson_little_endian32( &head.op, &mm->head.op ); + + res = mongo_write_socket( conn, &head, sizeof( head ) ); + if( res != MONGO_OK ) { + bson_free( mm ); + return res; + } + + res = mongo_write_socket( conn, &mm->data, mm->head.len - sizeof( head ) ); + if( res != MONGO_OK ) { + bson_free( mm ); + return res; + } + + bson_free( mm ); + return MONGO_OK; +} + +int mongo_read_response( mongo *conn, mongo_reply **reply ) { + mongo_header head; /* header from network */ + mongo_reply_fields fields; /* header from network */ + mongo_reply *out; /* native endian */ + unsigned int len; + int res; + + mongo_read_socket( conn, &head, sizeof( head ) ); + mongo_read_socket( conn, &fields, sizeof( fields ) ); + + bson_little_endian32( &len, &head.len ); + + if ( len < sizeof( head )+sizeof( fields ) || len > 64*1024*1024 ) + return MONGO_READ_SIZE_ERROR; /* most likely corruption */ + + out = ( mongo_reply * )bson_malloc( len ); + + out->head.len = len; + bson_little_endian32( &out->head.id, &head.id ); + bson_little_endian32( &out->head.responseTo, &head.responseTo ); + bson_little_endian32( &out->head.op, &head.op ); + + bson_little_endian32( &out->fields.flag, &fields.flag ); + bson_little_endian64( &out->fields.cursorID, &fields.cursorID ); + bson_little_endian32( &out->fields.start, &fields.start ); + bson_little_endian32( &out->fields.num, &fields.num ); + + res = mongo_read_socket( conn, &out->objs, len-sizeof( head )-sizeof( fields ) ); + if( res != MONGO_OK ) { + bson_free( out ); + return res; + } + + *reply = out; + + return MONGO_OK; +} + + +char *mongo_data_append( char *start , const void *data , int len ) { + memcpy( start , data , len ); + return start + len; +} + +char *mongo_data_append32( char *start , const void *data ) { + bson_little_endian32( start , data ); + return start + 4; +} + +char *mongo_data_append64( char *start , const void *data ) { + bson_little_endian64( start , data ); + return start + 8; +} + +/* Connection API */ + +static int mongo_check_is_master( mongo *conn ) { + bson out; + bson_iterator it; + bson_bool_t ismaster = 0; + + out.data = NULL; + + if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { + if( bson_find( &it, &out, "ismaster" ) ) + ismaster = bson_iterator_bool( &it ); + } else { + return MONGO_ERROR; + } + + bson_destroy( &out ); + + if( ismaster ) + return MONGO_OK; + else { + conn->err = MONGO_CONN_NOT_MASTER; + return MONGO_ERROR; + } +} + +void mongo_init( mongo *conn ) { + conn->replset = NULL; + conn->err = 0; + conn->errstr = NULL; + conn->lasterrcode = 0; + conn->lasterrstr = NULL; + + conn->conn_timeout_ms = 0; + conn->op_timeout_ms = 0; +} + +int mongo_connect( mongo *conn , const char *host, int port ) { + conn->primary = bson_malloc( sizeof( mongo_host_port ) ); + strncpy( conn->primary->host, host, strlen( host ) + 1 ); + conn->primary->port = port; + conn->primary->next = NULL; + + mongo_init( conn ); + if( mongo_socket_connect( conn, host, port ) != MONGO_OK ) + return MONGO_ERROR; + + if( mongo_check_is_master( conn ) != MONGO_OK ) + return MONGO_ERROR; + else + return MONGO_OK; +} + +void mongo_replset_init( mongo *conn, const char *name ) { + mongo_init( conn ); + + conn->replset = bson_malloc( sizeof( mongo_replset ) ); + conn->replset->primary_connected = 0; + conn->replset->seeds = NULL; + conn->replset->hosts = NULL; + conn->replset->name = ( char * )bson_malloc( strlen( name ) + 1 ); + memcpy( conn->replset->name, name, strlen( name ) + 1 ); + + conn->primary = bson_malloc( sizeof( mongo_host_port ) ); +} + +static void mongo_replset_add_node( mongo_host_port **list, const char *host, int port ) { + mongo_host_port *host_port = bson_malloc( sizeof( mongo_host_port ) ); + host_port->port = port; + host_port->next = NULL; + strncpy( host_port->host, host, strlen( host ) + 1 ); + + if( *list == NULL ) + *list = host_port; + else { + mongo_host_port *p = *list; + while( p->next != NULL ) + p = p->next; + p->next = host_port; + } +} + +static void mongo_replset_free_list( mongo_host_port **list ) { + mongo_host_port *node = *list; + mongo_host_port *prev; + + while( node != NULL ) { + prev = node; + node = node->next; + bson_free( prev ); + } + + *list = NULL; +} + +void mongo_replset_add_seed( mongo *conn, const char *host, int port ) { + mongo_replset_add_node( &conn->replset->seeds, host, port ); +} + +void mongo_parse_host( const char *host_string, mongo_host_port *host_port ) { + int len, idx, split; + len = split = idx = 0; + + /* Split the host_port string at the ':' */ + while( 1 ) { + if( *( host_string + len ) == '\0' ) + break; + if( *( host_string + len ) == ':' ) + split = len; + + len++; + } + + /* If 'split' is set, we know the that port exists; + * Otherwise, we set the default port. */ + idx = split ? split : len; + memcpy( host_port->host, host_string, idx ); + memcpy( host_port->host + idx, "\0", 1 ); + if( split ) + host_port->port = atoi( host_string + idx + 1 ); + else + host_port->port = MONGO_DEFAULT_PORT; +} + +static void mongo_replset_check_seed( mongo *conn ) { + bson out; + bson hosts; + const char *data; + bson_iterator it; + bson_iterator it_sub; + const char *host_string; + mongo_host_port *host_port = NULL; + + out.data = NULL; + + hosts.data = NULL; + + if( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { + + if( bson_find( &it, &out, "hosts" ) ) { + data = bson_iterator_value( &it ); + bson_iterator_from_buffer( &it_sub, data ); + + /* Iterate over host list, adding each host to the + * connection's host list. */ + while( bson_iterator_next( &it_sub ) ) { + host_string = bson_iterator_string( &it_sub ); + + host_port = bson_malloc( sizeof( mongo_host_port ) ); + mongo_parse_host( host_string, host_port ); + + if( host_port ) { + mongo_replset_add_node( &conn->replset->hosts, + host_port->host, host_port->port ); + + bson_free( host_port ); + host_port = NULL; + } + } + } + } + + bson_destroy( &out ); + bson_destroy( &hosts ); + mongo_close_socket( conn->sock ); + conn->sock = 0; + conn->connected = 0; + +} + +/* Find out whether the current connected node is master, and + * verify that the node's replica set name matched the provided name + */ +static int mongo_replset_check_host( mongo *conn ) { + + bson out; + bson_iterator it; + bson_bool_t ismaster = 0; + const char *set_name; + + out.data = NULL; + + if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { + if( bson_find( &it, &out, "ismaster" ) ) + ismaster = bson_iterator_bool( &it ); + + if( bson_find( &it, &out, "setName" ) ) { + set_name = bson_iterator_string( &it ); + if( strcmp( set_name, conn->replset->name ) != 0 ) { + bson_destroy( &out ); + conn->err = MONGO_CONN_BAD_SET_NAME; + return MONGO_ERROR; + } + } + } + + bson_destroy( &out ); + + if( ismaster ) { + conn->replset->primary_connected = 1; + } else { + mongo_close_socket( conn->sock ); + } + + return MONGO_OK; +} + +int mongo_replset_connect( mongo *conn ) { + + int res = 0; + mongo_host_port *node; + + conn->sock = 0; + conn->connected = 0; + + /* First iterate over the seed nodes to get the canonical list of hosts + * from the replica set. Break out once we have a host list. + */ + node = conn->replset->seeds; + while( node != NULL ) { + res = mongo_socket_connect( conn, ( const char * )&node->host, node->port ); + if( res != MONGO_OK ) + return MONGO_ERROR; + + mongo_replset_check_seed( conn ); + + if( conn->replset->hosts ) + break; + + node = node->next; + } + + /* Iterate over the host list, checking for the primary node. */ + if( !conn->replset->hosts ) { + conn->err = MONGO_CONN_NO_PRIMARY; + return MONGO_ERROR; + } else { + node = conn->replset->hosts; + + while( node != NULL ) { + res = mongo_socket_connect( conn, ( const char * )&node->host, node->port ); + + if( res == MONGO_OK ) { + if( mongo_replset_check_host( conn ) != MONGO_OK ) + return MONGO_ERROR; + + /* Primary found, so return. */ + else if( conn->replset->primary_connected ) + return MONGO_OK; + + /* No primary, so close the connection. */ + else { + mongo_close_socket( conn->sock ); + conn->sock = 0; + conn->connected = 0; + } + } + + node = node->next; + } + } + + + conn->err = MONGO_CONN_NO_PRIMARY; + return MONGO_ERROR; +} + +int mongo_set_op_timeout( mongo *conn, int millis ) { + conn->op_timeout_ms = millis; + if( conn->sock && conn->connected ) + mongo_set_socket_op_timeout( conn, millis ); + + return MONGO_OK; +} + +int mongo_reconnect( mongo *conn ) { + int res; + mongo_disconnect( conn ); + + if( conn->replset ) { + conn->replset->primary_connected = 0; + mongo_replset_free_list( &conn->replset->hosts ); + conn->replset->hosts = NULL; + res = mongo_replset_connect( conn ); + return res; + } else + return mongo_socket_connect( conn, conn->primary->host, conn->primary->port ); +} + +int mongo_check_connection( mongo *conn ) { + if( ! conn->connected ) + return MONGO_ERROR; + + if( mongo_simple_int_command( conn, "admin", "ping", 1, NULL ) == MONGO_OK ) + return MONGO_OK; + else + return MONGO_ERROR; +} + +void mongo_disconnect( mongo *conn ) { + if( ! conn->connected ) + return; + + if( conn->replset ) { + conn->replset->primary_connected = 0; + mongo_replset_free_list( &conn->replset->hosts ); + conn->replset->hosts = NULL; + } + + mongo_close_socket( conn->sock ); + + conn->sock = 0; + conn->connected = 0; +} + +void mongo_destroy( mongo *conn ) { + mongo_disconnect( conn ); + + if( conn->replset ) { + mongo_replset_free_list( &conn->replset->seeds ); + mongo_replset_free_list( &conn->replset->hosts ); + bson_free( conn->replset->name ); + bson_free( conn->replset ); + conn->replset = NULL; + } + + bson_free( conn->primary ); + bson_free( conn->errstr ); + bson_free( conn->lasterrstr ); + + conn->err = 0; + conn->errstr = NULL; + conn->lasterrcode = 0; + conn->lasterrstr = NULL; +} + +/* Determine whether this BSON object is valid for the given operation. */ +static int mongo_bson_valid( mongo *conn, bson *bson, int write ) { + if( ! bson->finished ) { + conn->err = MONGO_BSON_NOT_FINISHED; + return MONGO_ERROR; + } + + if( bson->err & BSON_NOT_UTF8 ) { + conn->err = MONGO_BSON_INVALID; + return MONGO_ERROR; + } + + if( write ) { + if( ( bson->err & BSON_FIELD_HAS_DOT ) || + ( bson->err & BSON_FIELD_INIT_DOLLAR ) ) { + + conn->err = MONGO_BSON_INVALID; + return MONGO_ERROR; + + } + } + + conn->err = 0; + conn->errstr = NULL; + + return MONGO_OK; +} + +/* Determine whether this BSON object is valid for the given operation. */ +static int mongo_cursor_bson_valid( mongo_cursor *cursor, bson *bson ) { + if( ! bson->finished ) { + cursor->err = MONGO_BSON_NOT_FINISHED; + return MONGO_ERROR; + } + + if( bson->err & BSON_NOT_UTF8 ) { + cursor->err = MONGO_BSON_INVALID; + return MONGO_ERROR; + } + + return MONGO_OK; +} + +/* MongoDB CRUD API */ + +int mongo_insert_batch( mongo *conn, const char *ns, + bson **bsons, int count ) { + + int size = 16 + 4 + strlen( ns ) + 1; + int i; + mongo_message *mm; + char *data; + + for( i=0; idata; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, ns, strlen( ns ) + 1 ); + + for( i=0; idata, bson_size( bsons[i] ) ); + } + + return mongo_message_send( conn, mm ); +} + +int mongo_insert( mongo *conn , const char *ns , bson *bson ) { + + char *data; + mongo_message *mm; + + /* Make sure that BSON is valid for insert. */ + if( mongo_bson_valid( conn, bson, 1 ) != MONGO_OK ) { + return MONGO_ERROR; + } + + mm = mongo_message_create( 16 /* header */ + + 4 /* ZERO */ + + strlen( ns ) + + 1 + bson_size( bson ) + , 0, 0, MONGO_OP_INSERT ); + + data = &mm->data; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, ns, strlen( ns ) + 1 ); + data = mongo_data_append( data, bson->data, bson_size( bson ) ); + + return mongo_message_send( conn, mm ); +} + +int mongo_update( mongo *conn, const char *ns, const bson *cond, + const bson *op, int flags ) { + + char *data; + mongo_message *mm; + + /* Make sure that the op BSON is valid UTF-8. + * TODO: decide whether to check cond as well. + * */ + if( mongo_bson_valid( conn, ( bson * )op, 0 ) != MONGO_OK ) { + return MONGO_ERROR; + } + + mm = mongo_message_create( 16 /* header */ + + 4 /* ZERO */ + + strlen( ns ) + 1 + + 4 /* flags */ + + bson_size( cond ) + + bson_size( op ) + , 0 , 0 , MONGO_OP_UPDATE ); + + data = &mm->data; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, ns, strlen( ns ) + 1 ); + data = mongo_data_append32( data, &flags ); + data = mongo_data_append( data, cond->data, bson_size( cond ) ); + data = mongo_data_append( data, op->data, bson_size( op ) ); + + return mongo_message_send( conn, mm ); +} + +int mongo_remove( mongo *conn, const char *ns, const bson *cond ) { + char *data; + mongo_message *mm = mongo_message_create( 16 /* header */ + + 4 /* ZERO */ + + strlen( ns ) + 1 + + 4 /* ZERO */ + + bson_size( cond ) + , 0 , 0 , MONGO_OP_DELETE ); + + /* Make sure that the BSON is valid UTF-8. + * TODO: decide whether to check cond as well. + * */ + if( mongo_bson_valid( conn, ( bson * )cond, 0 ) != MONGO_OK ) { + return MONGO_ERROR; + } + + data = &mm->data; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, ns, strlen( ns ) + 1 ); + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, cond->data, bson_size( cond ) ); + + return mongo_message_send( conn, mm ); +} + + +static int mongo_cursor_op_query( mongo_cursor *cursor ) { + int res; + bson empty; + char *data; + mongo_message *mm; + + /* Set up default values for query and fields, if necessary. */ + if( ! cursor->query ) + cursor->query = bson_empty( &empty ); + else if( mongo_cursor_bson_valid( cursor, cursor->query ) != MONGO_OK ) + return MONGO_ERROR; + + if( ! cursor->fields ) + cursor->fields = bson_empty( &empty ); + else if( mongo_cursor_bson_valid( cursor, cursor->fields ) != MONGO_OK ) + return MONGO_ERROR; + + mm = mongo_message_create( 16 + /* header */ + 4 + /* options */ + strlen( cursor->ns ) + 1 + /* ns */ + 4 + 4 + /* skip,return */ + bson_size( cursor->query ) + + bson_size( cursor->fields ) , + 0 , 0 , MONGO_OP_QUERY ); + + data = &mm->data; + data = mongo_data_append32( data , &cursor->options ); + data = mongo_data_append( data , cursor->ns , strlen( cursor->ns ) + 1 ); + data = mongo_data_append32( data , &cursor->skip ); + data = mongo_data_append32( data , &cursor->limit ); + data = mongo_data_append( data , cursor->query->data , bson_size( cursor->query ) ); + if ( cursor->fields ) + data = mongo_data_append( data , cursor->fields->data , bson_size( cursor->fields ) ); + + bson_fatal_msg( ( data == ( ( char * )mm ) + mm->head.len ), "query building fail!" ); + + res = mongo_message_send( cursor->conn , mm ); + if( res != MONGO_OK ) { + return MONGO_ERROR; + } + + res = mongo_read_response( cursor->conn, ( mongo_reply ** )&( cursor->reply ) ); + if( res != MONGO_OK ) { + return MONGO_ERROR; + } + + cursor->seen += cursor->reply->fields.num; + cursor->flags |= MONGO_CURSOR_QUERY_SENT; + return MONGO_OK; +} + +static int mongo_cursor_get_more( mongo_cursor *cursor ) { + int res; + + if( cursor->limit > 0 && cursor->seen >= cursor->limit ) { + cursor->err = MONGO_CURSOR_EXHAUSTED; + return MONGO_ERROR; + } else if( ! cursor->reply ) { + cursor->err = MONGO_CURSOR_INVALID; + return MONGO_ERROR; + } else if( ! cursor->reply->fields.cursorID ) { + cursor->err = MONGO_CURSOR_EXHAUSTED; + return MONGO_ERROR; + } else { + char *data; + int sl = strlen( cursor->ns )+1; + int limit = 0; + mongo_message *mm; + + if( cursor->limit > 0 ) + limit = cursor->limit - cursor->seen; + + mm = mongo_message_create( 16 /*header*/ + +4 /*ZERO*/ + +sl + +4 /*numToReturn*/ + +8 /*cursorID*/ + , 0, 0, MONGO_OP_GET_MORE ); + data = &mm->data; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append( data, cursor->ns, sl ); + data = mongo_data_append32( data, &limit ); + data = mongo_data_append64( data, &cursor->reply->fields.cursorID ); + + bson_free( cursor->reply ); + res = mongo_message_send( cursor->conn, mm ); + if( res != MONGO_OK ) { + mongo_cursor_destroy( cursor ); + return MONGO_ERROR; + } + + res = mongo_read_response( cursor->conn, &( cursor->reply ) ); + if( res != MONGO_OK ) { + mongo_cursor_destroy( cursor ); + return MONGO_ERROR; + } + cursor->current.data = NULL; + cursor->seen += cursor->reply->fields.num; + + return MONGO_OK; + } +} + +mongo_cursor *mongo_find( mongo *conn, const char *ns, bson *query, + bson *fields, int limit, int skip, int options ) { + + mongo_cursor *cursor = ( mongo_cursor * )bson_malloc( sizeof( mongo_cursor ) ); + mongo_cursor_init( cursor, conn, ns ); + cursor->flags |= MONGO_CURSOR_MUST_FREE; + + mongo_cursor_set_query( cursor, query ); + mongo_cursor_set_fields( cursor, fields ); + mongo_cursor_set_limit( cursor, limit ); + mongo_cursor_set_skip( cursor, skip ); + mongo_cursor_set_options( cursor, options ); + + if( mongo_cursor_op_query( cursor ) == MONGO_OK ) + return cursor; + else { + mongo_cursor_destroy( cursor ); + return NULL; + } +} + +int mongo_find_one( mongo *conn, const char *ns, bson *query, + bson *fields, bson *out ) { + + mongo_cursor *cursor = mongo_find( conn, ns, query, fields, 1, 0, 0 ); + + if ( cursor && mongo_cursor_next( cursor ) == MONGO_OK ) { + bson_copy_basic( out, &cursor->current ); + mongo_cursor_destroy( cursor ); + return MONGO_OK; + } else { + mongo_cursor_destroy( cursor ); + return MONGO_ERROR; + } +} + +void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns ) { + cursor->conn = conn; + cursor->ns = ( const char * )bson_malloc( strlen( ns ) + 1 ); + strncpy( ( char * )cursor->ns, ns, strlen( ns ) + 1 ); + cursor->current.data = NULL; + cursor->reply = NULL; + cursor->flags = 0; + cursor->seen = 0; + cursor->err = 0; + cursor->options = 0; + cursor->query = NULL; + cursor->fields = NULL; + cursor->skip = 0; + cursor->limit = 0; +} + +void mongo_cursor_set_query( mongo_cursor *cursor, bson *query ) { + cursor->query = query; +} + +void mongo_cursor_set_fields( mongo_cursor *cursor, bson *fields ) { + cursor->fields = fields; +} + +void mongo_cursor_set_skip( mongo_cursor *cursor, int skip ) { + cursor->skip = skip; +} + +void mongo_cursor_set_limit( mongo_cursor *cursor, int limit ) { + cursor->limit = limit; +} + +void mongo_cursor_set_options( mongo_cursor *cursor, int options ) { + cursor->options = options; +} + +const char *mongo_cursor_data( mongo_cursor *cursor ) { + return cursor->current.data; +} + +const bson *mongo_cursor_bson( mongo_cursor *cursor ) { + return (const bson *)&(cursor->current); +} + +int mongo_cursor_next( mongo_cursor *cursor ) { + char *next_object; + char *message_end; + + if( ! ( cursor->flags & MONGO_CURSOR_QUERY_SENT ) ) + mongo_cursor_op_query( cursor ); + + if( !cursor->reply ) + return MONGO_ERROR; + + /* no data */ + if ( cursor->reply->fields.num == 0 ) { + + /* Special case for tailable cursors. */ + if( cursor->reply->fields.cursorID ) { + if( ( mongo_cursor_get_more( cursor ) != MONGO_OK ) || + cursor->reply->fields.num == 0 ) { + return MONGO_ERROR; + } + } + + else + return MONGO_ERROR; + } + + /* first */ + if ( cursor->current.data == NULL ) { + bson_init_data( &cursor->current, &cursor->reply->objs ); + return MONGO_OK; + } + + next_object = cursor->current.data + bson_size( &cursor->current ); + message_end = ( char * )cursor->reply + cursor->reply->head.len; + + if ( next_object >= message_end ) { + if( mongo_cursor_get_more( cursor ) != MONGO_OK ) + return MONGO_ERROR; + + /* If there's still a cursor id, then the message should be pending. */ + if( cursor->reply->fields.num == 0 && cursor->reply->fields.cursorID ) { + cursor->err = MONGO_CURSOR_PENDING; + return MONGO_ERROR; + } + + bson_init_data( &cursor->current, &cursor->reply->objs ); + } else { + bson_init_data( &cursor->current, next_object ); + } + + return MONGO_OK; +} + +int mongo_cursor_destroy( mongo_cursor *cursor ) { + int result = MONGO_OK; + + if ( !cursor ) return result; + + /* Kill cursor if live. */ + if ( cursor->reply && cursor->reply->fields.cursorID ) { + mongo *conn = cursor->conn; + mongo_message *mm = mongo_message_create( 16 /*header*/ + +4 /*ZERO*/ + +4 /*numCursors*/ + +8 /*cursorID*/ + , 0, 0, MONGO_OP_KILL_CURSORS ); + char *data = &mm->data; + data = mongo_data_append32( data, &ZERO ); + data = mongo_data_append32( data, &ONE ); + data = mongo_data_append64( data, &cursor->reply->fields.cursorID ); + + result = mongo_message_send( conn, mm ); + } + + bson_free( cursor->reply ); + bson_free( ( void * )cursor->ns ); + + if( cursor->flags & MONGO_CURSOR_MUST_FREE ) + bson_free( cursor ); + + return result; +} + +/* MongoDB Helper Functions */ + +int mongo_create_index( mongo *conn, const char *ns, bson *key, int options, bson *out ) { + bson b; + bson_iterator it; + char name[255] = {'_'}; + int i = 1; + char idxns[1024]; + + bson_iterator_init( &it, key ); + while( i < 255 && bson_iterator_next( &it ) ) { + strncpy( name + i, bson_iterator_key( &it ), 255 - i ); + i += strlen( bson_iterator_key( &it ) ); + } + name[254] = '\0'; + + bson_init( &b ); + bson_append_bson( &b, "key", key ); + bson_append_string( &b, "ns", ns ); + bson_append_string( &b, "name", name ); + if ( options & MONGO_INDEX_UNIQUE ) + bson_append_bool( &b, "unique", 1 ); + if ( options & MONGO_INDEX_DROP_DUPS ) + bson_append_bool( &b, "dropDups", 1 ); + if ( options & MONGO_INDEX_BACKGROUND ) + bson_append_bool( &b, "background", 1 ); + if ( options & MONGO_INDEX_SPARSE ) + bson_append_bool( &b, "sparse", 1 ); + bson_finish( &b ); + + strncpy( idxns, ns, 1024-16 ); + strcpy( strchr( idxns, '.' ), ".system.indexes" ); + mongo_insert( conn, idxns, &b ); + bson_destroy( &b ); + + *strchr( idxns, '.' ) = '\0'; /* just db not ns */ + return mongo_cmd_get_last_error( conn, idxns, out ); +} + +bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ) { + bson b; + bson_bool_t success; + + bson_init( &b ); + bson_append_int( &b, field, 1 ); + bson_finish( &b ); + + success = mongo_create_index( conn, ns, &b, options, out ); + bson_destroy( &b ); + return success; +} + +int64_t mongo_count( mongo *conn, const char *db, const char *ns, bson *query ) { + bson cmd; + bson out = {NULL, 0}; + int64_t count = -1; + + bson_init( &cmd ); + bson_append_string( &cmd, "count", ns ); + if ( query && bson_size( query ) > 5 ) /* not empty */ + bson_append_bson( &cmd, "query", query ); + bson_finish( &cmd ); + + if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) { + bson_iterator it; + if( bson_find( &it, &out, "n" ) ) + count = bson_iterator_long( &it ); + bson_destroy( &cmd ); + bson_destroy( &out ); + return count; + } else { + bson_destroy( &out ); + bson_destroy( &cmd ); + return MONGO_ERROR; + } +} + +int mongo_run_command( mongo *conn, const char *db, bson *command, + bson *out ) { + + bson fields; + int sl = strlen( db ); + char *ns = bson_malloc( sl + 5 + 1 ); /* ".$cmd" + nul */ + int res; + + strcpy( ns, db ); + strcpy( ns+sl, ".$cmd" ); + + res = mongo_find_one( conn, ns, command, bson_empty( &fields ), out ); + bson_free( ns ); + return res; +} + +int mongo_simple_int_command( mongo *conn, const char *db, + const char *cmdstr, int arg, bson *realout ) { + + bson out = {NULL, 0}; + bson cmd; + bson_bool_t success = 0; + + bson_init( &cmd ); + bson_append_int( &cmd, cmdstr, arg ); + bson_finish( &cmd ); + + if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) { + bson_iterator it; + if( bson_find( &it, &out, "ok" ) ) + success = bson_iterator_bool( &it ); + } + + bson_destroy( &cmd ); + + if ( realout ) + *realout = out; + else + bson_destroy( &out ); + + if( success ) + return MONGO_OK; + else { + conn->err = MONGO_COMMAND_FAILED; + return MONGO_ERROR; + } +} + +int mongo_simple_str_command( mongo *conn, const char *db, + const char *cmdstr, const char *arg, bson *realout ) { + + bson out = {NULL, 0}; + int success = 0; + + bson cmd; + bson_init( &cmd ); + bson_append_string( &cmd, cmdstr, arg ); + bson_finish( &cmd ); + + if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) { + bson_iterator it; + if( bson_find( &it, &out, "ok" ) ) + success = bson_iterator_bool( &it ); + } + + bson_destroy( &cmd ); + + if ( realout ) + *realout = out; + else + bson_destroy( &out ); + + if( success ) + return MONGO_OK; + else + return MONGO_ERROR; +} + +int mongo_cmd_drop_db( mongo *conn, const char *db ) { + return mongo_simple_int_command( conn, db, "dropDatabase", 1, NULL ); +} + +int mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out ) { + return mongo_simple_str_command( conn, db, "drop", collection, out ); +} + +void mongo_cmd_reset_error( mongo *conn, const char *db ) { + mongo_simple_int_command( conn, db, "reseterror", 1, NULL ); +} + +static int mongo_cmd_get_error_helper( mongo *conn, const char *db, + bson *realout, const char *cmdtype ) { + + bson out = {NULL,0}; + bson_bool_t haserror = 0; + + /* Reset last error codes. */ + conn->lasterrcode = 0; + bson_free( conn->lasterrstr ); + conn->lasterrstr = NULL; + + /* If there's an error, store its code and string in the connection object. */ + if( mongo_simple_int_command( conn, db, cmdtype, 1, &out ) == MONGO_OK ) { + bson_iterator it; + haserror = ( bson_find( &it, &out, "err" ) != BSON_NULL ); + if( haserror ) { + conn->lasterrstr = ( char * )bson_malloc( bson_iterator_string_len( &it ) ); + if( conn->lasterrstr ) { + strcpy( conn->lasterrstr, bson_iterator_string( &it ) ); + } + + if( bson_find( &it, &out, "code" ) != BSON_NULL ) + conn->lasterrcode = bson_iterator_int( &it ); + } + } + + if( realout ) + *realout = out; /* transfer of ownership */ + else + bson_destroy( &out ); + + if( haserror ) + return MONGO_ERROR; + else + return MONGO_OK; +} + +int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out ) { + return mongo_cmd_get_error_helper( conn, db, out, "getpreverror" ); +} + +int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out ) { + return mongo_cmd_get_error_helper( conn, db, out, "getlasterror" ); +} + +bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *realout ) { + bson out = {NULL,0}; + bson_bool_t ismaster = 0; + + if ( mongo_simple_int_command( conn, "admin", "ismaster", 1, &out ) == MONGO_OK ) { + bson_iterator it; + bson_find( &it, &out, "ismaster" ); + ismaster = bson_iterator_bool( &it ); + } + + if( realout ) + *realout = out; /* transfer of ownership */ + else + bson_destroy( &out ); + + return ismaster; +} + +static void digest2hex( mongo_md5_byte_t digest[16], char hex_digest[33] ) { + static const char hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + int i; + for ( i=0; i<16; i++ ) { + hex_digest[2*i] = hex[( digest[i] & 0xf0 ) >> 4]; + hex_digest[2*i + 1] = hex[ digest[i] & 0x0f ]; + } + hex_digest[32] = '\0'; +} + +static void mongo_pass_digest( const char *user, const char *pass, char hex_digest[33] ) { + mongo_md5_state_t st; + mongo_md5_byte_t digest[16]; + + mongo_md5_init( &st ); + mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) ); + mongo_md5_append( &st, ( const mongo_md5_byte_t * )":mongo:", 7 ); + mongo_md5_append( &st, ( const mongo_md5_byte_t * )pass, strlen( pass ) ); + mongo_md5_finish( &st, digest ); + digest2hex( digest, hex_digest ); +} + +int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass ) { + bson user_obj; + bson pass_obj; + char hex_digest[33]; + char *ns = bson_malloc( strlen( db ) + strlen( ".system.users" ) + 1 ); + int res; + + strcpy( ns, db ); + strcpy( ns+strlen( db ), ".system.users" ); + + mongo_pass_digest( user, pass, hex_digest ); + + bson_init( &user_obj ); + bson_append_string( &user_obj, "user", user ); + bson_finish( &user_obj ); + + bson_init( &pass_obj ); + bson_append_start_object( &pass_obj, "$set" ); + bson_append_string( &pass_obj, "pwd", hex_digest ); + bson_append_finish_object( &pass_obj ); + bson_finish( &pass_obj ); + + res = mongo_update( conn, ns, &user_obj, &pass_obj, MONGO_UPDATE_UPSERT ); + + bson_free( ns ); + bson_destroy( &user_obj ); + bson_destroy( &pass_obj ); + + return res; +} + +bson_bool_t mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass ) { + bson from_db; + bson cmd; + bson out; + const char *nonce; + bson_bool_t success = 0; + + mongo_md5_state_t st; + mongo_md5_byte_t digest[16]; + char hex_digest[33]; + + if( mongo_simple_int_command( conn, db, "getnonce", 1, &from_db ) == MONGO_OK ) { + bson_iterator it; + bson_find( &it, &from_db, "nonce" ); + nonce = bson_iterator_string( &it ); + } else { + return MONGO_ERROR; + } + + mongo_pass_digest( user, pass, hex_digest ); + + mongo_md5_init( &st ); + mongo_md5_append( &st, ( const mongo_md5_byte_t * )nonce, strlen( nonce ) ); + mongo_md5_append( &st, ( const mongo_md5_byte_t * )user, strlen( user ) ); + mongo_md5_append( &st, ( const mongo_md5_byte_t * )hex_digest, 32 ); + mongo_md5_finish( &st, digest ); + digest2hex( digest, hex_digest ); + + bson_init( &cmd ); + bson_append_int( &cmd, "authenticate", 1 ); + bson_append_string( &cmd, "user", user ); + bson_append_string( &cmd, "nonce", nonce ); + bson_append_string( &cmd, "key", hex_digest ); + bson_finish( &cmd ); + + bson_destroy( &from_db ); + /*bson_init( &from_db ); */ + if( mongo_run_command( conn, db, &cmd, &out ) == MONGO_OK ) { + bson_iterator it; + if( bson_find( &it, &out, "ok" ) ) + success = bson_iterator_bool( &it ); + } + + bson_destroy( &from_db ); + bson_destroy( &cmd ); + + if( success ) + return MONGO_OK; + else + return MONGO_ERROR; +} diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h new file mode 100644 index 0000000000..6f86174101 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/mongo.h @@ -0,0 +1,648 @@ +/** + * @file mongo.h + * @brief Main MongoDB Declarations + */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _MONGO_H_ +#define _MONGO_H_ + +#include "bson.h" + +MONGO_EXTERN_C_START + +#define MONGO_MAJOR 0 +#define MONGO_MINOR 4 +#define MONGO_PATCH 0 + +#define MONGO_OK 0 +#define MONGO_ERROR -1 + +#define MONGO_DEFAULT_PORT 27017 + +typedef enum mongo_error_t { + MONGO_CONN_SUCCESS = 0, /**< Connection success! */ + MONGO_CONN_NO_SOCKET, /**< Could not create a socket. */ + MONGO_CONN_FAIL, /**< An error occured while calling connect(). */ + MONGO_CONN_ADDR_FAIL, /**< An error occured while calling getaddrinfo(). */ + MONGO_CONN_NOT_MASTER, /**< Warning: connected to a non-master node (read-only). */ + MONGO_CONN_BAD_SET_NAME, /**< Given rs name doesn't match this replica set. */ + MONGO_CONN_NO_PRIMARY, /**< Can't find primary in replica set. Connection closed. */ + + MONGO_IO_ERROR, /**< An error occurred while reading or writing on socket. */ + MONGO_READ_SIZE_ERROR, /**< The response is not the expected length. */ + MONGO_COMMAND_FAILED, /**< The command returned with 'ok' value of 0. */ + MONGO_CURSOR_EXHAUSTED, /**< The cursor has no more results. */ + MONGO_CURSOR_INVALID, /**< The cursor has timed out or is not recognized. */ + MONGO_CURSOR_PENDING, /**< Tailable cursor still alive but no data. */ + MONGO_BSON_INVALID, /**< BSON not valid for the specified op. */ + MONGO_BSON_NOT_FINISHED /**< BSON object has not been finished. */ +} mongo_error_t; + +enum mongo_cursor_flags { + MONGO_CURSOR_MUST_FREE = 1, /**< mongo_cursor_destroy should free cursor. */ + MONGO_CURSOR_QUERY_SENT = ( 1<<1 ) /**< Initial query has been sent. */ +}; + +enum mongo_index_opts { + MONGO_INDEX_UNIQUE = ( 1<<0 ), + MONGO_INDEX_DROP_DUPS = ( 1<<2 ), + MONGO_INDEX_BACKGROUND = ( 1<<3 ), + MONGO_INDEX_SPARSE = ( 1<<4 ) +}; + +enum mongo_update_opts { + MONGO_UPDATE_UPSERT = 0x1, + MONGO_UPDATE_MULTI = 0x2, + MONGO_UPDATE_BASIC = 0x4 +}; + +enum mongo_cursor_opts { + MONGO_TAILABLE = ( 1<<1 ), /**< Create a tailable cursor. */ + MONGO_SLAVE_OK = ( 1<<2 ), /**< Allow queries on a non-primary node. */ + MONGO_NO_CURSOR_TIMEOUT = ( 1<<4 ), /**< Disable cursor timeouts. */ + MONGO_AWAIT_DATA = ( 1<<5 ), /**< Momentarily block for more data. */ + MONGO_EXHAUST = ( 1<<6 ), /**< Stream in multiple 'more' packages. */ + MONGO_PARTIAL = ( 1<<7 ) /**< Allow reads even if a shard is down. */ +}; + +enum mongo_operations { + MONGO_OP_MSG = 1000, + MONGO_OP_UPDATE = 2001, + MONGO_OP_INSERT = 2002, + MONGO_OP_QUERY = 2004, + MONGO_OP_GET_MORE = 2005, + MONGO_OP_DELETE = 2006, + MONGO_OP_KILL_CURSORS = 2007 +}; + +#pragma pack(1) +typedef struct { + int len; + int id; + int responseTo; + int op; +} mongo_header; + +typedef struct { + mongo_header head; + char data; +} mongo_message; + +typedef struct { + int flag; /* FIX THIS COMMENT non-zero on failure */ + int64_t cursorID; + int start; + int num; +} mongo_reply_fields; + +typedef struct { + mongo_header head; + mongo_reply_fields fields; + char objs; +} mongo_reply; +#pragma pack() + +typedef struct mongo_host_port { + char host[255]; + int port; + struct mongo_host_port *next; +} mongo_host_port; + +typedef struct { + mongo_host_port *seeds; /**< List of seeds provided by the user. */ + mongo_host_port *hosts; /**< List of host/ports given by the replica set */ + char *name; /**< Name of the replica set. */ + bson_bool_t primary_connected; /**< Primary node connection status. */ +} mongo_replset; + +typedef struct mongo { + mongo_host_port *primary; /**< Primary connection info. */ + mongo_replset *replset; /**< replset object if connected to a replica set. */ + int sock; /**< Socket file descriptor. */ + int flags; /**< Flags on this connection object. */ + int conn_timeout_ms; /**< Connection timeout in milliseconds. */ + int op_timeout_ms; /**< Read and write timeout in milliseconds. */ + bson_bool_t connected; /**< Connection status. */ + + mongo_error_t err; /**< Most recent driver error code. */ + char *errstr; /**< String version of most recent driver error code. */ + int lasterrcode; /**< getlasterror given by the server on calls. */ + char *lasterrstr; /**< getlasterror string generated by server. */ +} mongo; + +typedef struct { + mongo_reply *reply; /**< reply is owned by cursor */ + mongo *conn; /**< connection is *not* owned by cursor */ + const char *ns; /**< owned by cursor */ + int flags; /**< Flags used internally by this drivers. */ + int seen; /**< Number returned so far. */ + bson current; /**< This cursor's current bson object. */ + mongo_error_t err; /**< Errors on this cursor. */ + bson *query; /**< Bitfield containing cursor options. */ + bson *fields; /**< Bitfield containing cursor options. */ + int options; /**< Bitfield containing cursor options. */ + int limit; /**< Bitfield containing cursor options. */ + int skip; /**< Bitfield containing cursor options. */ +} mongo_cursor; + +/* Connection API */ + +/** Initialize a new mongo connection object. If not created + * with mongo_new, you must initialize each mongo + * object using this function. + * + * @note When finished, you must pass this object to + * mongo_destroy( ). + * + * @param conn a mongo connection object allocated on the stack + * or heap. + */ +void mongo_init( mongo *conn ); + +/** + * Connect to a single MongoDB server. + * + * @param conn a mongo object. + * @param host a numerical network address or a network hostname. + * @param port the port to connect to. + * + * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type + * mongo_conn_return_t will be set on the conn->err field. + */ +int mongo_connect( mongo *conn , const char *host, int port ); + +/** + * Set up this connection object for connecting to a replica set. + * To connect, pass the object to mongo_replset_connect(). + * + * @param conn a mongo object. + * @param name the name of the replica set to connect to. + * */ +void mongo_replset_init( mongo *conn, const char *name ); + +/** + * Add a seed node to the replica set connection object. + * + * You must specify at least one seed node before connecting to a replica set. + * + * @param conn a mongo object. + * @param host a numerical network address or a network hostname. + * @param port the port to connect to. + */ +void mongo_replset_add_seed( mongo *conn, const char *host, int port ); + +/** + * Utility function for converting a host-port string to a mongo_host_port. + * + * @param host_string a string containing either a host or a host and port separated + * by a colon. + * @param host_port the mongo_host_port object to write the result to. + */ +void mongo_parse_host( const char *host_string, mongo_host_port *host_port ); + +/** + * Connect to a replica set. + * + * Before passing a connection object to this function, you must already have called + * mongo_set_replset and mongo_replset_add_seed. + * + * @param conn a mongo object. + * + * @return MONGO_OK or MONGO_ERROR on failure. On failure, a constant of type + * mongo_conn_return_t will be set on the conn->err field. + */ +int mongo_replset_connect( mongo *conn ); + +/** Set a timeout for operations on this connection. This + * is a platform-specific feature, and only work on *nix + * system. You must also compile for linux to support this. + * + * @param conn a mongo object. + * @param millis timeout time in milliseconds. + * + * @return MONGO_OK. On error, return MONGO_ERROR and + * set the conn->err field. + */ +int mongo_set_op_timeout( mongo *conn, int millis ); + +/** + * Ensure that this connection is healthy by performing + * a round-trip to the server. + * + * @param conn a mongo connection + * + * @return MONGO_OK if connected; otherwise, MONGO_ERROR. + */ +int mongo_check_connection( mongo *conn ); + +/** + * Try reconnecting to the server using the existing connection settings. + * + * This function will disconnect the current socket. If you've authenticated, + * you'll need to re-authenticate after calling this function. + * + * @param conn a mongo object. + * + * @return MONGO_OK or MONGO_ERROR and + * set the conn->err field. + */ +int mongo_reconnect( mongo *conn ); + +/** + * Close the current connection to the server. After calling + * this function, you may call mongo_reconnect with the same + * connection object. + * + * @param conn a mongo object. + */ +void mongo_disconnect( mongo *conn ); + +/** + * Close any existing connection to the server and free all allocated + * memory associated with the conn object. + * + * You must always call this function when finished with the connection object. + * + * @param conn a mongo object. + */ +void mongo_destroy( mongo *conn ); + +/** + * Insert a BSON document into a MongoDB server. This function + * will fail if the supplied BSON struct is not UTF-8 or if + * the keys are invalid for insert (contain '.' or start with '$'). + * + * @param conn a mongo object. + * @param ns the namespace. + * @param data the bson data. + * + * @return MONGO_OK or MONGO_ERROR. If the conn->err + * field is MONGO_BSON_INVALID, check the err field + * on the bson struct for the reason. + */ +int mongo_insert( mongo *conn, const char *ns, bson *data ); + +/** + * Insert a batch of BSON documents into a MongoDB server. This function + * will fail if any of the documents to be inserted is invalid. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param data the bson data. + * @param num the number of documents in data. + * + * @return MONGO_OK or MONGO_ERROR. + * + */ +int mongo_insert_batch( mongo *conn , const char *ns , + bson **data , int num ); + +/** + * Update a document in a MongoDB server. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param cond the bson update query. + * @param op the bson update data. + * @param flags flags for the update. + * + * @return MONGO_OK or MONGO_ERROR with error stored in conn object. + * + */ +int mongo_update( mongo *conn, const char *ns, const bson *cond, + const bson *op, int flags ); + +/** + * Remove a document from a MongoDB server. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param cond the bson query. + * + * @return MONGO_OK or MONGO_ERROR with error stored in conn object. + */ +int mongo_remove( mongo *conn, const char *ns, const bson *cond ); + +/** + * Find documents in a MongoDB server. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param query the bson query. + * @param fields a bson document of fields to be returned. + * @param limit the maximum number of documents to retrun. + * @param skip the number of documents to skip. + * @param options A bitfield containing cursor options. + * + * @return A cursor object allocated on the heap or NULL if + * an error has occurred. For finer-grained error checking, + * use the cursor builder API instead. + */ +mongo_cursor *mongo_find( mongo *conn, const char *ns, bson *query, + bson *fields, int limit, int skip, int options ); + +/** + * Initalize a new cursor object. + * + * @param cursor + * @param ns the namespace, represented as the the database + * name and collection name separated by a dot. e.g., "test.users" + */ +void mongo_cursor_init( mongo_cursor *cursor, mongo *conn, const char *ns ); + +/** + * Set the bson object specifying this cursor's query spec. If + * your query is the empty bson object "{}", then you need not + * set this value. + * + * @param cursor + * @param query a bson object representing the query spec. This may + * be either a simple query spec or a complex spec storing values for + * $query, $orderby, $hint, and/or $explain. See + * http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol for details. + */ +void mongo_cursor_set_query( mongo_cursor *cursor, bson *query ); + +/** + * Set the fields to return for this cursor. If you want to return + * all fields, you need not set this value. + * + * @param cursor + * @param fields a bson object representing the fields to return. + * See http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields. + */ +void mongo_cursor_set_fields( mongo_cursor *cursor, bson *fields ); + +/** + * Set the number of documents to skip. + * + * @param cursor + * @param skip + */ +void mongo_cursor_set_skip( mongo_cursor *cursor, int skip ); + +/** + * Set the number of documents to return. + * + * @param cursor + * @param limit + */ +void mongo_cursor_set_limit( mongo_cursor *cursor, int limit ); + +/** + * Set any of the available query options (e.g., MONGO_TAILABLE). + * + * @param cursor + * @param options a bitfield storing query options. See + * mongo_cursor_bitfield_t for available constants. + */ +void mongo_cursor_set_options( mongo_cursor *cursor, int options ); + +/** + * Return the current BSON object data as a const char*. This is useful + * for creating bson iterators with bson_iterator_init. + * + * @param cursor + */ +const char *mongo_cursor_data( mongo_cursor *cursor ); + +/** + * Return the current BSON object data as a const char*. This is useful + * for creating bson iterators with bson_iterator_init. + * + * @param cursor + */ +const bson *mongo_cursor_bson( mongo_cursor *cursor ); + +/** + * Iterate the cursor, returning the next item. When successful, + * the returned object will be stored in cursor->current; + * + * @param cursor + * + * @return MONGO_OK. On error, returns MONGO_ERROR and sets + * cursor->err with a value of mongo_error_t. + */ +int mongo_cursor_next( mongo_cursor *cursor ); + +/** + * Destroy a cursor object. When finished with a cursor, you + * must pass it to this function. + * + * @param cursor the cursor to destroy. + * + * @return MONGO_OK or an error code. On error, check cursor->conn->err + * for errors. + */ +int mongo_cursor_destroy( mongo_cursor *cursor ); + +/** + * Find a single document in a MongoDB server. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param query the bson query. + * @param fields a bson document of the fields to be returned. + * @param out a bson document in which to put the query result. + * + */ +/* out can be NULL if you don't care about results. useful for commands */ +bson_bool_t mongo_find_one( mongo *conn, const char *ns, bson *query, + bson *fields, bson *out ); + +/* MongoDB Helper Functions */ + +/** + * Count the number of documents in a collection matching a query. + * + * @param conn a mongo object. + * @param db the db name. + * @param coll the collection name. + * @param query the BSON query. + * + * @return the number of matching documents. If the command fails, + * MONGO_ERROR is returned. + */ +int64_t mongo_count( mongo *conn, const char *db, const char *coll, + bson *query ); + +/** + * Create a compouned index. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param data the bson index data. + * @param options a bitfield for setting index options. Possibilities include + * MONGO_INDEX_UNIQUE, MONGO_INDEX_DROP_DUPS, MONGO_INDEX_BACKGROUND, + * and MONGO_INDEX_SPARSE. + * @param out a bson document containing errors, if any. + * + * @return MONGO_OK if index is created successfully; otherwise, MONGO_ERROR. + */ +int mongo_create_index( mongo *conn, const char *ns, bson *key, int options, bson *out ); + +/** + * Create an index with a single key. + * + * @param conn a mongo object. + * @param ns the namespace. + * @param field the index key. + * @param options index options. + * @param out a BSON document containing errors, if any. + * + * @return true if the index was created. + */ +bson_bool_t mongo_create_simple_index( mongo *conn, const char *ns, const char *field, int options, bson *out ); + +/* ---------------------------- + COMMANDS + ------------------------------ */ + +/** + * Run a command on a MongoDB server. + * + * @param conn a mongo object. + * @param db the name of the database. + * @param command the BSON command to run. + * @param out the BSON result of the command. + * + * @return true if the command ran without error. + */ +bson_bool_t mongo_run_command( mongo *conn, const char *db, bson *command, bson *out ); + +/** + * Run a command that accepts a simple string key and integer value. + * + * @param conn a mongo object. + * @param db the name of the database. + * @param cmd the command to run. + * @param arg the integer argument to the command. + * @param out the BSON result of the command. + * + * @return MONGO_OK or an error code. + * + */ +int mongo_simple_int_command( mongo *conn, const char *db, + const char *cmd, int arg, bson *out ); + +/** + * Run a command that accepts a simple string key and value. + * + * @param conn a mongo object. + * @param db the name of the database. + * @param cmd the command to run. + * @param arg the string argument to the command. + * @param out the BSON result of the command. + * + * @return true if the command ran without error. + * + */ +bson_bool_t mongo_simple_str_command( mongo *conn, const char *db, const char *cmd, const char *arg, bson *out ); + +/** + * Drop a database. + * + * @param conn a mongo object. + * @param db the name of the database to drop. + * + * @return MONGO_OK or an error code. + */ +int mongo_cmd_drop_db( mongo *conn, const char *db ); + +/** + * Drop a collection. + * + * @param conn a mongo object. + * @param db the name of the database. + * @param collection the name of the collection to drop. + * @param out a BSON document containing the result of the command. + * + * @return true if the collection drop was successful. + */ +bson_bool_t mongo_cmd_drop_collection( mongo *conn, const char *db, const char *collection, bson *out ); + +/** + * Add a database user. + * + * @param conn a mongo object. + * @param db the database in which to add the user. + * @param user the user name + * @param pass the user password + * + * @return MONGO_OK or MONGO_ERROR. + */ +int mongo_cmd_add_user( mongo *conn, const char *db, const char *user, const char *pass ); + +/** + * Authenticate a user. + * + * @param conn a mongo object. + * @param db the database to authenticate against. + * @param user the user name to authenticate. + * @param pass the user's password. + * + * @return MONGO_OK on sucess and MONGO_ERROR on failure. + */ +int mongo_cmd_authenticate( mongo *conn, const char *db, const char *user, const char *pass ); + +/** + * Check if the current server is a master. + * + * @param conn a mongo object. + * @param out a BSON result of the command. + * + * @return true if the server is a master. + */ +/* return value is master status */ +bson_bool_t mongo_cmd_ismaster( mongo *conn, bson *out ); + +/** + * Get the error for the last command with the current connection. + * + * @param conn a mongo object. + * @param db the name of the database. + * @param out a BSON object containing the error details. + * + * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values + * of conn->lasterrcode and conn->lasterrstr for the error status. + */ +int mongo_cmd_get_last_error( mongo *conn, const char *db, bson *out ); + +/** + * Get the most recent error with the current connection. + * + * @param conn a mongo object. + * @param db the name of the database. + * @param out a BSON object containing the error details. + * + * @return MONGO_OK if no error and MONGO_ERROR on error. On error, check the values + * of conn->lasterrcode and conn->lasterrstr for the error status. + */ +int mongo_cmd_get_prev_error( mongo *conn, const char *db, bson *out ); + +/** + * Reset the error state for the connection. + * + * @param conn a mongo object. + * @param db the name of the database. + */ +void mongo_cmd_reset_error( mongo *conn, const char *db ); + +MONGO_EXTERN_C_END + +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c new file mode 100644 index 0000000000..5c8ad9ba5e --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.c @@ -0,0 +1,98 @@ +/* net.c */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Implementation for generic version of net.h */ +#include "net.h" +#include + +int mongo_write_socket( mongo *conn, const void *buf, int len ) { + const char *cbuf = buf; + while ( len ) { + int sent = send( conn->sock, cbuf, len, 0 ); + if ( sent == -1 ) { + conn->err = MONGO_IO_ERROR; + return MONGO_ERROR; + } + cbuf += sent; + len -= sent; + } + + return MONGO_OK; +} + +int mongo_read_socket( mongo *conn, void *buf, int len ) { + char *cbuf = buf; + while ( len ) { + int sent = recv( conn->sock, cbuf, len, 0 ); + if ( sent == 0 || sent == -1 ) { + conn->err = MONGO_IO_ERROR; + return MONGO_ERROR; + } + cbuf += sent; + len -= sent; + } + + return MONGO_OK; +} + +/* This is a no-op in the generic implementation. */ +int mongo_set_socket_op_timeout( mongo *conn, int millis ) { + return MONGO_OK; +} + +static int mongo_create_socket( mongo *conn ) { + int fd; + + if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { + conn->err = MONGO_CONN_NO_SOCKET; + return MONGO_ERROR; + } + conn->sock = fd; + + return MONGO_OK; +} + +int mongo_socket_connect( mongo *conn, const char *host, int port ) { + struct sockaddr_in sa; + socklen_t addressSize; + int flag = 1; + + if( mongo_create_socket( conn ) != MONGO_OK ) + return MONGO_ERROR; + + memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) ); + sa.sin_family = AF_INET; + sa.sin_port = htons( port ); + sa.sin_addr.s_addr = inet_addr( host ); + addressSize = sizeof( sa ); + + if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) { + mongo_close_socket( conn->sock ); + conn->connected = 0; + conn->sock = 0; + conn->err = MONGO_CONN_FAIL; + return MONGO_ERROR; + } + + setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) ); + if( conn->op_timeout_ms > 0 ) + mongo_set_socket_op_timeout( conn, conn->op_timeout_ms ); + + conn->connected = 1; + + return MONGO_OK; +} diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h new file mode 100644 index 0000000000..49190877c8 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/net.h @@ -0,0 +1,57 @@ +/** @file net.h */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Header for generic net.h */ +#ifndef _MONGO_NET_H_ +#define _MONGO_NET_H_ + +#include "mongo.h" + +#ifdef _WIN32 +#include +#include +#define mongo_close_socket(sock) ( closesocket(sock) ) +typedef int socklen_t; +#else +#include +#include +#include +#include +#include +#include +#include +#define mongo_close_socket(sock) ( close(sock) ) +#endif + +#ifndef _WIN32 +#include +#endif + +#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1 +#define _MONGO_USE_GETADDRINFO +#endif + +MONGO_EXTERN_C_START + +/* This is a no-op in the generic implementation. */ +int mongo_set_socket_op_timeout( mongo *conn, int millis ); +int mongo_read_socket( mongo *conn, void *buf, int len ); +int mongo_write_socket( mongo *conn, const void *buf, int len ); +int mongo_socket_connect( mongo *conn, const char *host, int port ); + +MONGO_EXTERN_C_END +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c new file mode 100644 index 0000000000..a63e3d73f9 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/numbers.c @@ -0,0 +1,127 @@ +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* all the numbers that fit in a 4 byte string */ +const char bson_numstrs[1000][4] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", + "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", + "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", + "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", + "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", + "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", + "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", + "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", + "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", + + "100", "101", "102", "103", "104", "105", "106", "107", "108", "109", + "110", "111", "112", "113", "114", "115", "116", "117", "118", "119", + "120", "121", "122", "123", "124", "125", "126", "127", "128", "129", + "130", "131", "132", "133", "134", "135", "136", "137", "138", "139", + "140", "141", "142", "143", "144", "145", "146", "147", "148", "149", + "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", + "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", + "170", "171", "172", "173", "174", "175", "176", "177", "178", "179", + "180", "181", "182", "183", "184", "185", "186", "187", "188", "189", + "190", "191", "192", "193", "194", "195", "196", "197", "198", "199", + + "200", "201", "202", "203", "204", "205", "206", "207", "208", "209", + "210", "211", "212", "213", "214", "215", "216", "217", "218", "219", + "220", "221", "222", "223", "224", "225", "226", "227", "228", "229", + "230", "231", "232", "233", "234", "235", "236", "237", "238", "239", + "240", "241", "242", "243", "244", "245", "246", "247", "248", "249", + "250", "251", "252", "253", "254", "255", "256", "257", "258", "259", + "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", + "270", "271", "272", "273", "274", "275", "276", "277", "278", "279", + "280", "281", "282", "283", "284", "285", "286", "287", "288", "289", + "290", "291", "292", "293", "294", "295", "296", "297", "298", "299", + + "300", "301", "302", "303", "304", "305", "306", "307", "308", "309", + "310", "311", "312", "313", "314", "315", "316", "317", "318", "319", + "320", "321", "322", "323", "324", "325", "326", "327", "328", "329", + "330", "331", "332", "333", "334", "335", "336", "337", "338", "339", + "340", "341", "342", "343", "344", "345", "346", "347", "348", "349", + "350", "351", "352", "353", "354", "355", "356", "357", "358", "359", + "360", "361", "362", "363", "364", "365", "366", "367", "368", "369", + "370", "371", "372", "373", "374", "375", "376", "377", "378", "379", + "380", "381", "382", "383", "384", "385", "386", "387", "388", "389", + "390", "391", "392", "393", "394", "395", "396", "397", "398", "399", + + "400", "401", "402", "403", "404", "405", "406", "407", "408", "409", + "410", "411", "412", "413", "414", "415", "416", "417", "418", "419", + "420", "421", "422", "423", "424", "425", "426", "427", "428", "429", + "430", "431", "432", "433", "434", "435", "436", "437", "438", "439", + "440", "441", "442", "443", "444", "445", "446", "447", "448", "449", + "450", "451", "452", "453", "454", "455", "456", "457", "458", "459", + "460", "461", "462", "463", "464", "465", "466", "467", "468", "469", + "470", "471", "472", "473", "474", "475", "476", "477", "478", "479", + "480", "481", "482", "483", "484", "485", "486", "487", "488", "489", + "490", "491", "492", "493", "494", "495", "496", "497", "498", "499", + + "500", "501", "502", "503", "504", "505", "506", "507", "508", "509", + "510", "511", "512", "513", "514", "515", "516", "517", "518", "519", + "520", "521", "522", "523", "524", "525", "526", "527", "528", "529", + "530", "531", "532", "533", "534", "535", "536", "537", "538", "539", + "540", "541", "542", "543", "544", "545", "546", "547", "548", "549", + "550", "551", "552", "553", "554", "555", "556", "557", "558", "559", + "560", "561", "562", "563", "564", "565", "566", "567", "568", "569", + "570", "571", "572", "573", "574", "575", "576", "577", "578", "579", + "580", "581", "582", "583", "584", "585", "586", "587", "588", "589", + "590", "591", "592", "593", "594", "595", "596", "597", "598", "599", + + "600", "601", "602", "603", "604", "605", "606", "607", "608", "609", + "610", "611", "612", "613", "614", "615", "616", "617", "618", "619", + "620", "621", "622", "623", "624", "625", "626", "627", "628", "629", + "630", "631", "632", "633", "634", "635", "636", "637", "638", "639", + "640", "641", "642", "643", "644", "645", "646", "647", "648", "649", + "650", "651", "652", "653", "654", "655", "656", "657", "658", "659", + "660", "661", "662", "663", "664", "665", "666", "667", "668", "669", + "670", "671", "672", "673", "674", "675", "676", "677", "678", "679", + "680", "681", "682", "683", "684", "685", "686", "687", "688", "689", + "690", "691", "692", "693", "694", "695", "696", "697", "698", "699", + + "700", "701", "702", "703", "704", "705", "706", "707", "708", "709", + "710", "711", "712", "713", "714", "715", "716", "717", "718", "719", + "720", "721", "722", "723", "724", "725", "726", "727", "728", "729", + "730", "731", "732", "733", "734", "735", "736", "737", "738", "739", + "740", "741", "742", "743", "744", "745", "746", "747", "748", "749", + "750", "751", "752", "753", "754", "755", "756", "757", "758", "759", + "760", "761", "762", "763", "764", "765", "766", "767", "768", "769", + "770", "771", "772", "773", "774", "775", "776", "777", "778", "779", + "780", "781", "782", "783", "784", "785", "786", "787", "788", "789", + "790", "791", "792", "793", "794", "795", "796", "797", "798", "799", + + "800", "801", "802", "803", "804", "805", "806", "807", "808", "809", + "810", "811", "812", "813", "814", "815", "816", "817", "818", "819", + "820", "821", "822", "823", "824", "825", "826", "827", "828", "829", + "830", "831", "832", "833", "834", "835", "836", "837", "838", "839", + "840", "841", "842", "843", "844", "845", "846", "847", "848", "849", + "850", "851", "852", "853", "854", "855", "856", "857", "858", "859", + "860", "861", "862", "863", "864", "865", "866", "867", "868", "869", + "870", "871", "872", "873", "874", "875", "876", "877", "878", "879", + "880", "881", "882", "883", "884", "885", "886", "887", "888", "889", + "890", "891", "892", "893", "894", "895", "896", "897", "898", "899", + + "900", "901", "902", "903", "904", "905", "906", "907", "908", "909", + "910", "911", "912", "913", "914", "915", "916", "917", "918", "919", + "920", "921", "922", "923", "924", "925", "926", "927", "928", "929", + "930", "931", "932", "933", "934", "935", "936", "937", "938", "939", + "940", "941", "942", "943", "944", "945", "946", "947", "948", "949", + "950", "951", "952", "953", "954", "955", "956", "957", "958", "959", + "960", "961", "962", "963", "964", "965", "966", "967", "968", "969", + "970", "971", "972", "973", "974", "975", "976", "977", "978", "979", + "980", "981", "982", "983", "984", "985", "986", "987", "988", "989", + "990", "991", "992", "993", "994", "995", "996", "997", "998", "999", +}; diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h new file mode 100644 index 0000000000..4a96af77e3 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform.h @@ -0,0 +1,94 @@ +/** @file platform.h */ + +/** Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* all platform-specific ifdefs should go here */ + +#ifndef _PLATFORM_HACKS_H_ +#define _PLATFORM_HACKS_H_ + +#ifdef __GNUC__ +#define MONGO_INLINE static __inline__ +#else +#define MONGO_INLINE static +#endif + +#ifdef __cplusplus +#define MONGO_EXTERN_C_START extern "C" { +#define MONGO_EXTERN_C_END } +#else +#define MONGO_EXTERN_C_START +#define MONGO_EXTERN_C_END +#endif + + +#if defined(MONGO_HAVE_STDINT) || __STDC_VERSION__ >= 199901L +#include +#elif defined(MONGO_HAVE_UNISTD) +#include +#elif defined(MONGO_USE__INT64) +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#elif defined(MONGO_USE_LONG_LONG_INT) +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#else +#error must have a 64bit int type +#endif + +/* big endian is only used for OID generation. little is used everywhere else */ +#ifdef MONGO_BIG_ENDIAN +#define bson_little_endian64(out, in) ( bson_swap_endian64(out, in) ) +#define bson_little_endian32(out, in) ( bson_swap_endian32(out, in) ) +#define bson_big_endian64(out, in) ( memcpy(out, in, 8) ) +#define bson_big_endian32(out, in) ( memcpy(out, in, 4) ) +#else +#define bson_little_endian64(out, in) ( memcpy(out, in, 8) ) +#define bson_little_endian32(out, in) ( memcpy(out, in, 4) ) +#define bson_big_endian64(out, in) ( bson_swap_endian64(out, in) ) +#define bson_big_endian32(out, in) ( bson_swap_endian32(out, in) ) +#endif + +MONGO_EXTERN_C_START + +MONGO_INLINE void bson_swap_endian64( void *outp, const void *inp ) { + const char *in = ( const char * )inp; + char *out = ( char * )outp; + + out[0] = in[7]; + out[1] = in[6]; + out[2] = in[5]; + out[3] = in[4]; + out[4] = in[3]; + out[5] = in[2]; + out[6] = in[1]; + out[7] = in[0]; + +} +MONGO_INLINE void bson_swap_endian32( void *outp, const void *inp ) { + const char *in = ( const char * )inp; + char *out = ( char * )outp; + + out[0] = in[3]; + out[1] = in[2]; + out[2] = in[1]; + out[3] = in[0]; +} + +MONGO_EXTERN_C_END + +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c new file mode 100644 index 0000000000..b2f7c22898 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.c @@ -0,0 +1,183 @@ +/* net.c */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Implementation for Linux version of net.h */ +#include "net.h" +#include + +int mongo_write_socket( mongo *conn, const void *buf, int len ) { + const char *cbuf = buf; + while ( len ) { + int sent = send( conn->sock, cbuf, len, 0 ); + if ( sent == -1 ) { + conn->err = MONGO_IO_ERROR; + return MONGO_ERROR; + } + cbuf += sent; + len -= sent; + } + + return MONGO_OK; +} + +int mongo_read_socket( mongo *conn, void *buf, int len ) { + char *cbuf = buf; + while ( len ) { + int sent = recv( conn->sock, cbuf, len, 0 ); + if ( sent == 0 || sent == -1 ) { + conn->err = MONGO_IO_ERROR; + return MONGO_ERROR; + } + cbuf += sent; + len -= sent; + } + + return MONGO_OK; +} + +static int mongo_create_socket( mongo *conn ) { + int fd; + + if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { + conn->err = MONGO_CONN_NO_SOCKET; + return MONGO_ERROR; + } + conn->sock = fd; + + return MONGO_OK; +} + +static int mongo_set_blocking_status( mongo *conn ) { + int flags; + int blocking; + + blocking = ( conn->conn_timeout_ms == 0 ); + if( blocking ) + return MONGO_OK; + else { + if( ( flags = fcntl( conn->sock, F_GETFL ) ) == -1 ) { + conn->err = MONGO_IO_ERROR; + mongo_close_socket( conn->sock ); + return MONGO_ERROR; + } + + flags |= O_NONBLOCK; + + if( ( flags = fcntl( conn->sock, F_SETFL, flags ) ) == -1 ) { + conn->err = MONGO_IO_ERROR; + mongo_close_socket( conn->sock ); + return MONGO_ERROR; + } + } + + return MONGO_OK; +} + +int mongo_set_socket_op_timeout( mongo *conn, int millis ) { + struct timeval tv; + tv.tv_sec = millis / 1000; + tv.tv_usec = ( millis % 1000 ) * 1000; + + if ( setsockopt( conn->sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof( tv ) ) == -1 ) { + conn->err = MONGO_IO_ERROR; + return MONGO_ERROR; + } + + if ( setsockopt( conn->sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof( tv ) ) == -1 ) { + conn->err = MONGO_IO_ERROR; + return MONGO_ERROR; + } + + return MONGO_OK; +} + +#ifdef _MONGO_USE_GETADDRINFO +int mongo_socket_connect( mongo *conn, const char *host, int port ) { + + struct addrinfo *addrs = NULL; + struct addrinfo hints; + int flag = 1; + char port_str[12]; + int ret; + + conn->sock = 0; + conn->connected = 0; + + memset( &hints, 0, sizeof( hints ) ); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + sprintf( port_str, "%d", port ); + + if( mongo_create_socket( conn ) != MONGO_OK ) + return MONGO_ERROR; + + if( getaddrinfo( host, port_str, &hints, &addrs ) != 0 ) { + bson_errprintf( "getaddrinfo failed: %s", gai_strerror( ret ) ); + conn->err = MONGO_CONN_ADDR_FAIL; + return MONGO_ERROR; + } + + if ( connect( conn->sock, addrs->ai_addr, addrs->ai_addrlen ) == -1 ) { + mongo_close_socket( conn->sock ); + freeaddrinfo( addrs ); + conn->err = MONGO_CONN_FAIL; + return MONGO_ERROR; + } + + setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * )&flag, sizeof( flag ) ); + if( conn->op_timeout_ms > 0 ) + mongo_set_socket_op_timeout( conn, conn->op_timeout_ms ); + + conn->connected = 1; + freeaddrinfo( addrs ); + + return MONGO_OK; +} +#else +int mongo_socket_connect( mongo *conn, const char *host, int port ) { + struct sockaddr_in sa; + socklen_t addressSize; + int flag = 1; + + if( mongo_create_socket( conn ) != MONGO_OK ) + return MONGO_ERROR; + + memset( sa.sin_zero , 0 , sizeof( sa.sin_zero ) ); + sa.sin_family = AF_INET; + sa.sin_port = htons( port ); + sa.sin_addr.s_addr = inet_addr( host ); + addressSize = sizeof( sa ); + + if ( connect( conn->sock, ( struct sockaddr * )&sa, addressSize ) == -1 ) { + mongo_close_socket( conn->sock ); + conn->connected = 0; + conn->sock = 0; + conn->err = MONGO_CONN_FAIL; + return MONGO_ERROR; + } + + setsockopt( conn->sock, IPPROTO_TCP, TCP_NODELAY, ( char * ) &flag, sizeof( flag ) ); + + if( conn->op_timeout_ms > 0 ) + mongo_set_socket_op_timeout( conn, conn->op_timeout_ms ); + + conn->connected = 1; + + return MONGO_OK; +} +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h new file mode 100644 index 0000000000..054247c1e8 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/driver/src/platform/linux/net.h @@ -0,0 +1,51 @@ +/** + * @file net.h + * @brief Networking. + */ + +/* Copyright 2009-2011 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Header for Linux net.h */ +#ifndef _MONGO_NET_H_ +#define _MONGO_NET_H_ + +#include "mongo.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define mongo_close_socket(sock) ( close(sock) ) + +#if defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE) || _POSIX_C_SOURCE >= 1 +#define _MONGO_USE_GETADDRINFO +#endif + +MONGO_EXTERN_C_START + +int mongo_set_socket_op_timeout( mongo *conn, int millis ); +int mongo_read_socket( mongo *conn, void *buf, int len ); +int mongo_write_socket( mongo *conn, const void *buf, int len ); +int mongo_socket_connect( mongo *conn, const char *host, int port ); + +MONGO_EXTERN_C_END +#endif diff --git a/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c b/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c new file mode 100644 index 0000000000..d721bed8a5 --- /dev/null +++ b/src/mod/event_handlers/mod_cdr_mongodb/mod_cdr_mongodb.c @@ -0,0 +1,413 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005-2011, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Daniel Swarbrick + * + * mod_cdr_mongodb.c -- MongoDB CDR Module + * + * Derived from: + * mod_xml_cdr.c -- XML CDR Module to files or curl + * + */ +#include +#include + +static struct { + switch_memory_pool_t *pool; + int shutdown; + char *mongo_host; + uint32_t mongo_port; + char *mongo_namespace; + mongo mongo_conn[1]; + switch_mutex_t *mongo_mutex; + switch_bool_t log_b; +} globals; + +static switch_xml_config_item_t config_settings[] = { + /* key, flags, ptr, default_value, syntax, helptext */ + SWITCH_CONFIG_ITEM_STRING_STRDUP("host", CONFIG_REQUIRED, &globals.mongo_host, "127.0.0.1", NULL, "MongoDB server host address"), + SWITCH_CONFIG_ITEM_STRING_STRDUP("namespace", CONFIG_REQUIRED, &globals.mongo_namespace, NULL, "database.collection", "MongoDB namespace"), + + /* key, type, flags, ptr, default_value, data, syntax, helptext */ + SWITCH_CONFIG_ITEM("port", SWITCH_CONFIG_INT, CONFIG_REQUIRED, &globals.mongo_port, 27017, NULL, NULL, "MongoDB server TCP port"), + SWITCH_CONFIG_ITEM("log-b-leg", SWITCH_CONFIG_BOOL, CONFIG_RELOADABLE, &globals.log_b, SWITCH_TRUE, NULL, NULL, "Log B-leg in addition to A-leg"), + + SWITCH_CONFIG_ITEM_END() +}; + + +SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown); +SWITCH_MODULE_DEFINITION(mod_cdr_mongodb, mod_cdr_mongodb_load, mod_cdr_mongodb_shutdown, NULL); + +static void set_bson_profile_data(bson *b, switch_caller_profile_t *caller_profile) +{ + bson_append_string(b, "username", caller_profile->username); + bson_append_string(b, "dialplan", caller_profile->dialplan); + bson_append_string(b, "caller_id_name", caller_profile->caller_id_name); + bson_append_string(b, "ani", caller_profile->ani); + bson_append_string(b, "aniii", caller_profile->aniii); + bson_append_string(b, "caller_id_number", caller_profile->caller_id_number); + bson_append_string(b, "network_addr", caller_profile->network_addr); + bson_append_string(b, "rdnis", caller_profile->rdnis); + bson_append_string(b, "destination_number", caller_profile->destination_number); + bson_append_string(b, "uuid", caller_profile->uuid); + bson_append_string(b, "source", caller_profile->source); + bson_append_string(b, "context", caller_profile->context); + bson_append_string(b, "chan_name", caller_profile->chan_name); +} + + +static switch_status_t my_on_reporting(switch_core_session_t *session) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_event_header_t *hi; + switch_caller_profile_t *caller_profile; + switch_app_log_t *app_log; + bson cdr; + int is_b; + char *tmp; + + if (globals.shutdown) { + return SWITCH_STATUS_SUCCESS; + } + + is_b = channel && switch_channel_get_originator_caller_profile(channel); + if (!globals.log_b && is_b) { + const char *force_cdr = switch_channel_get_variable(channel, SWITCH_FORCE_PROCESS_CDR_VARIABLE); + if (!switch_true(force_cdr)) { + return SWITCH_STATUS_SUCCESS; + } + } + + bson_init(&cdr); + + /* Channel data */ + bson_append_start_object(&cdr, "channel_data"); + bson_append_string(&cdr, "state", switch_channel_state_name(switch_channel_get_state(channel))); + bson_append_string(&cdr, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound"); + bson_append_int(&cdr, "state_number", switch_channel_get_state(channel)); + + if ((tmp = switch_channel_get_flag_string(channel))) { + bson_append_string(&cdr, "flags", tmp); + free(tmp); + } + + if ((tmp = switch_channel_get_cap_string(channel))) { + bson_append_string(&cdr, "caps", tmp); + free(tmp); + } + bson_append_finish_object(&cdr); /* channel_data */ + + + /* Channel variables */ + bson_append_start_object(&cdr, "variables"); + for (hi = switch_channel_variable_first(channel); hi; hi = hi->next) { + if (!zstr(hi->name) && !zstr(hi->value)) { + bson_append_string(&cdr, hi->name, hi->value); + } + } + bson_append_finish_object(&cdr); /* variables */ + + + /* App log */ + if ((app_log = switch_core_session_get_app_log(session))) { + switch_app_log_t *ap; + + bson_append_start_object(&cdr, "app_log"); + for (ap = app_log; ap; ap = ap->next) { + bson_append_start_object(&cdr, "application"); + bson_append_string(&cdr, "app_name", ap->app); + bson_append_string(&cdr, "app_data", ap->arg); + bson_append_long(&cdr, "app_stamp", ap->stamp); + bson_append_finish_object(&cdr); /* application */ + } + + bson_append_finish_object(&cdr); /* app_log */ + } + + + /* Callflow */ + caller_profile = switch_channel_get_caller_profile(channel); + + while (caller_profile) { + bson_append_start_object(&cdr, "callflow"); + + if (!zstr(caller_profile->dialplan)) { + bson_append_string(&cdr, "dialplan", caller_profile->dialplan); + } + + if (!zstr(caller_profile->profile_index)) { + bson_append_string(&cdr, "profile_index", caller_profile->profile_index); + } + + if (caller_profile->caller_extension) { + switch_caller_application_t *ap; + + bson_append_start_object(&cdr, "extension"); + + bson_append_string(&cdr, "name", caller_profile->caller_extension->extension_name); + bson_append_string(&cdr, "number", caller_profile->caller_extension->extension_number); + + if (caller_profile->caller_extension->current_application) { + bson_append_string(&cdr, "current_app", caller_profile->caller_extension->current_application->application_name); + } + + for (ap = caller_profile->caller_extension->applications; ap; ap = ap->next) { + bson_append_start_object(&cdr, "application"); + if (ap == caller_profile->caller_extension->current_application) { + bson_append_bool(&cdr, "last_executed", 1); + } + bson_append_string(&cdr, "app_name", ap->application_name); + bson_append_string(&cdr, "app_data", ap->application_data); + bson_append_finish_object(&cdr); + } + + if (caller_profile->caller_extension->children) { + switch_caller_profile_t *cp = NULL; + + for (cp = caller_profile->caller_extension->children; cp; cp = cp->next) { + + if (!cp->caller_extension) { + continue; + } + + bson_append_start_object(&cdr, "sub_extensions"); + bson_append_start_object(&cdr, "extension"); + + bson_append_string(&cdr, "name", cp->caller_extension->extension_name); + bson_append_string(&cdr, "number", cp->caller_extension->extension_number); + bson_append_string(&cdr, "dialplan", cp->dialplan); + if (cp->caller_extension->current_application) { + bson_append_string(&cdr, "current_app", cp->caller_extension->current_application->application_name); + } + + for (ap = cp->caller_extension->applications; ap; ap = ap->next) { + bson_append_start_object(&cdr, "application"); + if (ap == cp->caller_extension->current_application) { + bson_append_bool(&cdr, "last_executed", 1); + } + bson_append_string(&cdr, "app_name", ap->application_name); + bson_append_string(&cdr, "app_data", ap->application_data); + bson_append_finish_object(&cdr); + } + + bson_append_finish_object(&cdr); /* extension */ + bson_append_finish_object(&cdr); /* sub_extensions */ + } + } + + bson_append_finish_object(&cdr); /* extension */ + } + + bson_append_start_object(&cdr, "caller_profile"); + set_bson_profile_data(&cdr, caller_profile); + + if (caller_profile->origination_caller_profile) { + switch_caller_profile_t *cp = NULL; + + bson_append_start_object(&cdr, "origination"); + for (cp = caller_profile->origination_caller_profile; cp; cp = cp->next) { + bson_append_start_object(&cdr, "origination_caller_profile"); + set_bson_profile_data(&cdr, cp); + bson_append_finish_object(&cdr); + } + bson_append_finish_object(&cdr); /* origination */ + } + + if (caller_profile->originator_caller_profile) { + switch_caller_profile_t *cp = NULL; + + bson_append_start_object(&cdr, "originator"); + for (cp = caller_profile->originator_caller_profile; cp; cp = cp->next) { + bson_append_start_object(&cdr, "originator_caller_profile"); + set_bson_profile_data(&cdr, cp); + bson_append_finish_object(&cdr); + } + bson_append_finish_object(&cdr); /* originator */ + } + + if (caller_profile->originatee_caller_profile) { + switch_caller_profile_t *cp = NULL; + + bson_append_start_object(&cdr, "originatee"); + for (cp = caller_profile->originatee_caller_profile; cp; cp = cp->next) { + bson_append_start_object(&cdr, "originatee_caller_profile"); + set_bson_profile_data(&cdr, cp); + bson_append_finish_object(&cdr); + } + bson_append_finish_object(&cdr); /* originatee */ + } + + bson_append_finish_object(&cdr); /* caller_profile */ + + /* Timestamps */ + if (caller_profile->times) { + bson_append_start_object(&cdr, "times"); + + /* Insert timestamps as long ints (microseconds) to preserve accuracy */ + bson_append_long(&cdr, "created_time", caller_profile->times->created); + bson_append_long(&cdr, "profile_created_time", caller_profile->times->profile_created); + bson_append_long(&cdr, "progress_time", caller_profile->times->progress); + bson_append_long(&cdr, "progress_media_time", caller_profile->times->progress_media); + bson_append_long(&cdr, "answered_time", caller_profile->times->answered); + bson_append_long(&cdr, "bridged_time", caller_profile->times->bridged); + bson_append_long(&cdr, "last_hold_time", caller_profile->times->last_hold); + bson_append_long(&cdr, "hold_accum_time", caller_profile->times->hold_accum); + bson_append_long(&cdr, "hangup_time", caller_profile->times->hungup); + bson_append_long(&cdr, "resurrect_time", caller_profile->times->resurrected); + bson_append_long(&cdr, "transfer_time", caller_profile->times->transferred); + bson_append_finish_object(&cdr); /* times */ + } + + bson_append_finish_object(&cdr); /* callflow */ + caller_profile = caller_profile->next; + } + + bson_finish(&cdr); + + switch_mutex_lock(globals.mongo_mutex); + + if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) { + if (globals.mongo_conn->err == MONGO_IO_ERROR) { + mongo_error_t db_status; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MongoDB connection failed; attempting reconnect...\n"); + db_status = mongo_reconnect(globals.mongo_conn); + + if (db_status != MONGO_OK) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MongoDB reconnect failed with error code %d\n", db_status); + status = SWITCH_STATUS_FALSE; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MongoDB connection re-established.\n"); + if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr) != MONGO_OK) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err); + status = SWITCH_STATUS_FALSE; + } + } + + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: error code %d\n", globals.mongo_conn->err); + status = SWITCH_STATUS_FALSE; + } + } + + switch_mutex_unlock(globals.mongo_mutex); + bson_destroy(&cdr); + + return status; +} + + +static switch_state_handler_table_t state_handlers = { + /*.on_init */ NULL, + /*.on_routing */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ NULL, + /*.on_exchange_media */ NULL, + /*.on_soft_execute */ NULL, + /*.on_consume_media */ NULL, + /*.on_hibernate */ NULL, + /*.on_reset */ NULL, + /*.on_park */ NULL, + /*.on_reporting */ my_on_reporting +}; + + +static switch_status_t load_config(switch_memory_pool_t *pool) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + + if (switch_xml_config_parse_module_settings("cdr_mongodb.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + + return status; +} + + +SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load) +{ + switch_status_t status = SWITCH_STATUS_SUCCESS; + mongo_error_t db_status; + + memset(&globals, 0, sizeof(globals)); + globals.pool = pool; + if (load_config(pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Unable to load or parse config!\n"); + } + + db_status = mongo_connect(globals.mongo_conn, globals.mongo_host, globals.mongo_port); + + if (db_status != MONGO_OK) { + switch (globals.mongo_conn->err) { + case MONGO_CONN_NO_SOCKET: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: no socket\n"); + break; + case MONGO_CONN_FAIL: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: connection failed\n"); + break; + case MONGO_CONN_NOT_MASTER: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: not master\n"); + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: unknown error %d\n", db_status); + } + return SWITCH_STATUS_FALSE; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected to MongoDB server %s:%d\n", globals.mongo_host, globals.mongo_port); + } + + switch_mutex_init(&globals.mongo_mutex, SWITCH_MUTEX_NESTED, pool); + + switch_core_add_state_handler(&state_handlers); + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + return status; +} + + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown) +{ + globals.shutdown = 1; + switch_core_remove_state_handler(&state_handlers); + switch_mutex_destroy(globals.mongo_mutex); + + mongo_destroy(globals.mongo_conn); + + return SWITCH_STATUS_SUCCESS; +} + + + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4: + */ diff --git a/src/mod/event_handlers/mod_event_socket/mod_event_socket.c b/src/mod/event_handlers/mod_event_socket/mod_event_socket.c index 384b6db0f6..f6ea4735ce 100644 --- a/src/mod/event_handlers/mod_event_socket/mod_event_socket.c +++ b/src/mod/event_handlers/mod_event_socket/mod_event_socket.c @@ -184,7 +184,7 @@ static switch_status_t socket_logger(const switch_log_node_t *node, switch_log_l } else { switch_log_node_free(&dnode); if (++l->lost_logs > MAX_MISSED) { - kill_listener(l, "Disconnected due to log queue failure.\n"); + kill_listener(l, NULL); } } } @@ -384,7 +384,7 @@ static void event_handler(switch_event_t *event) } } else { if (++l->lost_events > MAX_MISSED) { - kill_listener(l, "Disconnected due to event queue failure.\n"); + kill_listener(l, NULL); } switch_event_destroy(&clone); } @@ -1180,7 +1180,6 @@ static switch_status_t read_packet(listener_t *listener, switch_event_t **event, } } if (var && val) { - switch_event_del_header(*event, var); switch_event_add_header_string(*event, SWITCH_STACK_BOTTOM, var, val); if (!strcasecmp(var, "content-length")) { clen = atoi(val); @@ -1884,11 +1883,30 @@ static switch_status_t parse_command(listener_t *listener, switch_event_t **even char *uuid; if ((uuid = cmd + 9)) { + char *fmt; strip_cr(uuid); - + + if ((fmt = strchr(uuid, ' '))) { + *fmt++ = '\0'; + } + if (!(listener->session = switch_core_session_locate(uuid))) { - switch_snprintf(reply, reply_len, "-ERR invalid uuid"); - goto done; + if (fmt) { + switch_snprintf(reply, reply_len, "-ERR invalid uuid"); + goto done; + } else { + fmt = uuid; + } + } + + if ((fmt = strchr(uuid, ' '))) { + if (!strcasecmp(fmt, "xml")) { + listener->format = EVENT_FORMAT_XML; + } else if (!strcasecmp(fmt, "plain")) { + listener->format = EVENT_FORMAT_PLAIN; + } else if (!strcasecmp(fmt, "json")) { + listener->format = EVENT_FORMAT_JSON; + } } switch_set_flag_locked(listener, LFLAG_SESSION); diff --git a/src/mod/event_handlers/mod_event_zmq/Makefile b/src/mod/event_handlers/mod_event_zmq/Makefile index 145055af62..6d193084ed 100644 --- a/src/mod/event_handlers/mod_event_zmq/Makefile +++ b/src/mod/event_handlers/mod_event_zmq/Makefile @@ -1,6 +1,6 @@ BASE=../../../.. -ZMQ=zeromq-2.1.7 +ZMQ=zeromq-2.1.9 ZMQ_BASEURL=http://download.zeromq.org ZMQ_BASEURL_ALT=http://download.zeromq.org/historic diff --git a/src/mod/languages/mod_java/src/org/freeswitch/swig/Event.java b/src/mod/languages/mod_java/src/org/freeswitch/swig/Event.java index c1c9ba17e7..d1adc09dc6 100644 --- a/src/mod/languages/mod_java/src/org/freeswitch/swig/Event.java +++ b/src/mod/languages/mod_java/src/org/freeswitch/swig/Event.java @@ -66,6 +66,14 @@ public class Event { this(freeswitchJNI.new_Event__SWIG_1(SWIGTYPE_p_switch_event_t.getCPtr(wrap_me), free_me), true); } + public int chat_execute(String app, String data) { + return freeswitchJNI.Event_chat_execute(swigCPtr, this, app, data); + } + + public int chat_send(String dest_proto) { + return freeswitchJNI.Event_chat_send(swigCPtr, this, dest_proto); + } + public String serialize(String format) { return freeswitchJNI.Event_serialize(swigCPtr, this, format); } diff --git a/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java b/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java index 069b7eecf2..fc74914d15 100644 --- a/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java +++ b/src/mod/languages/mod_java/src/org/freeswitch/swig/freeswitchJNI.java @@ -54,6 +54,8 @@ class freeswitchJNI { public final static native long new_Event__SWIG_0(String jarg1, String jarg2); public final static native long new_Event__SWIG_1(long jarg1, int jarg2); public final static native void delete_Event(long jarg1); + public final static native int Event_chat_execute(long jarg1, Event jarg1_, String jarg2, String jarg3); + public final static native int Event_chat_send(long jarg1, Event jarg1_, String jarg2); public final static native String Event_serialize(long jarg1, Event jarg1_, String jarg2); public final static native boolean Event_setPriority(long jarg1, Event jarg1_, long jarg2); public final static native String Event_getHeader(long jarg1, Event jarg1_, String jarg2); diff --git a/src/mod/languages/mod_java/switch_swig_wrap.cpp b/src/mod/languages/mod_java/switch_swig_wrap.cpp index 4b3c9d38d1..5061dc46f6 100644 --- a/src/mod/languages/mod_java/switch_swig_wrap.cpp +++ b/src/mod/languages/mod_java/switch_swig_wrap.cpp @@ -1054,6 +1054,57 @@ SWIGEXPORT void JNICALL Java_org_freeswitch_swig_freeswitchJNI_delete_1Event(JNI } +SWIGEXPORT jint JNICALL Java_org_freeswitch_swig_freeswitchJNI_Event_1chat_1execute(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jstring jarg2, jstring jarg3) { + jint jresult = 0 ; + Event *arg1 = (Event *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) NULL ; + int result; + + (void)jenv; + (void)jcls; + (void)jarg1_; + arg1 = *(Event **)&jarg1; + arg2 = 0; + if (jarg2) { + arg2 = (char *)jenv->GetStringUTFChars(jarg2, 0); + if (!arg2) return 0; + } + arg3 = 0; + if (jarg3) { + arg3 = (char *)jenv->GetStringUTFChars(jarg3, 0); + if (!arg3) return 0; + } + result = (int)(arg1)->chat_execute((char const *)arg2,(char const *)arg3); + jresult = (jint)result; + if (arg2) jenv->ReleaseStringUTFChars(jarg2, (const char *)arg2); + if (arg3) jenv->ReleaseStringUTFChars(jarg3, (const char *)arg3); + return jresult; +} + + +SWIGEXPORT jint JNICALL Java_org_freeswitch_swig_freeswitchJNI_Event_1chat_1send(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jstring jarg2) { + jint jresult = 0 ; + Event *arg1 = (Event *) 0 ; + char *arg2 = (char *) NULL ; + int result; + + (void)jenv; + (void)jcls; + (void)jarg1_; + arg1 = *(Event **)&jarg1; + arg2 = 0; + if (jarg2) { + arg2 = (char *)jenv->GetStringUTFChars(jarg2, 0); + if (!arg2) return 0; + } + result = (int)(arg1)->chat_send((char const *)arg2); + jresult = (jint)result; + if (arg2) jenv->ReleaseStringUTFChars(jarg2, (const char *)arg2); + return jresult; +} + + SWIGEXPORT jstring JNICALL Java_org_freeswitch_swig_freeswitchJNI_Event_1serialize(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jstring jarg2) { jstring jresult = 0 ; Event *arg1 = (Event *) 0 ; diff --git a/src/mod/languages/mod_lua/mod_lua.cpp b/src/mod/languages/mod_lua/mod_lua.cpp index dc01aa9fa9..a165795cb0 100644 --- a/src/mod/languages/mod_lua/mod_lua.cpp +++ b/src/mod/languages/mod_lua/mod_lua.cpp @@ -430,6 +430,24 @@ SWITCH_STANDARD_API(luarun_api_function) return SWITCH_STATUS_SUCCESS; } +SWITCH_STANDARD_CHAT_APP(lua_chat_function) +{ + lua_State *L = lua_init(); + char *dup = NULL; + + if (data) { + dup = strdup(data); + } + + mod_lua_conjure_event(L, message, "message", 1); + lua_parse_and_execute(L, (char *)dup); + lua_uninit(L); + + switch_safe_free(dup); + + return SWITCH_STATUS_SUCCESS; + +} SWITCH_STANDARD_API(lua_api_function) { @@ -591,6 +609,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_lua_load) switch_api_interface_t *api_interface; switch_application_interface_t *app_interface; switch_dialplan_interface_t *dp_interface; + switch_chat_application_interface_t *chat_app_interface; /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); @@ -600,6 +619,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_lua_load) SWITCH_ADD_APP(app_interface, "lua", "Launch LUA ivr", "Run a lua ivr on a channel", lua_function, "