├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── HISTORY.md ├── LICENSE ├── Makefile.am ├── README.md ├── api.c ├── api.h ├── autoload_conf └── janus.conf.xml ├── bootstrap.sh ├── cJSON.c ├── cJSON.h ├── configure.ac ├── globals.c ├── globals.h ├── hash.c ├── hash.h ├── http.c ├── http.h ├── mod_janus.c ├── servers.c └── servers.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.so 4 | *.lo 5 | *.a 6 | *.la 7 | *.loT 8 | *.orig 9 | *.rej 10 | .libs 11 | .deps 12 | *.dirstamp 13 | .\#* 14 | \#* 15 | *.user 16 | *.suo 17 | *.ncb 18 | *.pdb 19 | *.map 20 | *.lib 21 | *.obj 22 | *.idb 23 | *.res 24 | *.exp 25 | *.exe 26 | *.manifest 27 | *.dep 28 | *.dll 29 | *.sbr 30 | *.ilk 31 | *.bsc 32 | *.pch 33 | *.opensdf 34 | *.tar 35 | *.gz 36 | *.tgz 37 | *.xz 38 | *.bz2 39 | *.tbz2 40 | *.deb 41 | *.swp 42 | *.serial 43 | aclocal.m4 44 | autom4te.cache 45 | config.cache 46 | configure.lineno 47 | config.log 48 | config.status 49 | Makefile 50 | Makefile.in 51 | compile 52 | config.guess 53 | config.sub 54 | configure 55 | depcomp 56 | install-sh 57 | libtool 58 | ltmain.sh 59 | missing 60 | test-driver 61 | core.* 62 | TAGS 63 | .vscode/c_cpp_properties.json 64 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # to build module you will need 2 | # apt-get update && apt-get install -y build-essential cmake pkg-config libfreeswitch-dev libspeexdsp-dev 3 | # mkdir -p build 4 | # cd build 5 | # cmake -DCMAKE_BUILD_TYPE=Release .. 6 | # make 7 | 8 | cmake_minimum_required(VERSION 3.18) 9 | project(mod_janus) 10 | set(CMAKE_C_STANDARD 11) 11 | set(CMAKE_SHARED_LIBRARY_PREFIX "") 12 | 13 | find_package(PkgConfig REQUIRED) 14 | pkg_check_modules(FreeSWITCH REQUIRED IMPORTED_TARGET freeswitch) 15 | pkg_get_variable(FS_MOD_DIR freeswitch modulesdir) 16 | message("FreeSWITCH modules dir: ${FS_MOD_DIR}") 17 | 18 | add_library(mod_janus SHARED globals.c cJSON.c http.c api.c servers.c hash.c mod_janus.c) 19 | 20 | 21 | set_property(TARGET mod_janus PROPERTY POSITION_INDEPENDENT_CODE ON) 22 | 23 | 24 | target_link_libraries(mod_janus PRIVATE PkgConfig::FreeSWITCH pthread) 25 | 26 | 27 | install(TARGETS mod_janus DESTINATION ${FS_MOD_DIR}) 28 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at n.brandinger@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 2 | develop 3 | - #1 Implement trickle ICE 4 | - #2 Use ks JSON library 5 | 6 | v1.0.0 7 | - First version 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 2 | Copyright (C) 2005-2012, Anthony Minessale II anthm@freeswitch.org 3 | 4 | Version: MPL 1.1 5 | 6 | The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | 9 | You may obtain a copy of the License at http://www.mozilla.org/MPL/ 10 | 11 | Software distributed under the License is distributed on an "AS IS" basis, 12 | WITHOUT WARRANTY OF ANY KIND, either express or implied. 13 | See the License for the specific language governing rights and limitations under the License. 14 | 15 | The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 16 | 17 | The Initial Developer of the Original Code is Anthony Minessale II anthm@freeswitch.org 18 | Portions created by the Initial Developer are Copyright (C) the Initial Developer. 19 | All Rights Reserved. 20 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | -include $(top_srcdir)/build/modmake.rulesam 2 | AUTOMAKE_OPTIONS = foreign subdir-objects 3 | MODNAME=mod_janus 4 | 5 | LIBS := $(if $(switch_builddir),$(switch_builddir)/libfreeswitch.la,) 6 | 7 | mod_LTLIBRARIES = mod_janus.la 8 | mod_janus_la_SOURCES = globals.c cJSON.c http.c api.c servers.c hash.c mod_janus.c 9 | mod_janus_la_CFLAGS = $(AM_CFLAGS) $(FREESWITCH_CFLAGS) 10 | mod_janus_la_LDFLAGS = -avoid-version -module -no-undefined -shared $(FREESWITCH_LIBS) $(OPENSSL_LIBS) $(MOSQUITTO_LIBS) 11 | mod_janus_la_LIBADD = $(LIBS) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mod_janus 2 | 3 | The mod_janus endpoint provides an interface to the Janus [audiobridge](https://janus.conf.meetecho.com/docs/audiobridge.html). 4 | 5 | This allows legacy POTS to join the same room as the WebRTC users that are already supported by Janus. 6 | 7 | The module will only support audio calls - video calls will be rejected. The long polling HTTP interface is used in communication with Janus. No provision is given to the Websocket interface. 8 | 9 | ## Table of Contents 10 | 11 | * [Table of Contents](#table-of-contents) 12 | * [License](#license) 13 | * [Contributor(s)](#contributors) 14 | * [Features](#features) 15 | * [Build and install mod_janus](#build-and-install-mod_janus) 16 | * [Configuration](#configuration) 17 | * [Usage](#usage) 18 | * [Command Line Interface (CLI)](#command-line-interface-cli) 19 | * [Notes](#notes) 20 | * [Troubleshooting](#troubleshooting) 21 | 22 | ## License 23 | 24 | FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 25 | Copyright (C) 2005-2012, Anthony Minessale II 26 | 27 | Version: MPL 1.1 28 | 29 | 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 30 | 31 | 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. 32 | 33 | The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 34 | 35 | The Initial Developer of the Original Code is Anthony Minessale II 36 | Portions created by the Initial Developer are Copyright (C) the Initial Developer. All Rights Reserved. 37 | 38 | ## Contributor(s) 39 | 40 | * Richard Screene (rscreene@yahoo.co.uk) 41 | 42 | 43 | ## Build and install mod_janus 44 | 45 | Change to a directory where the FreeSWITCH sources will be compiled 46 | 47 | ``` 48 | cd /usr/src 49 | ``` 50 | 51 | Clone the FreeSWITCH repository into the above directory 52 | 53 | ``` 54 | git clone https://github.com/signalwire/freeswitch.git 55 | ``` 56 | 57 | Perform an initial bootstrap of FreeSWITCH so that a `modules.conf` file is created 58 | 59 | ``` 60 | ./bootstrap.sh 61 | ``` 62 | 63 | Add the mod_janus to `modules.conf` so that an out-of-source build will be performed 64 | 65 | ``` 66 | mod_janus|https://github.com/freeswitch/mod_janus.git -b master 67 | ``` 68 | 69 | Configure, build and install FreeSWITCH 70 | 71 | ``` 72 | ./configure 73 | make 74 | make install 75 | ``` 76 | 77 | The following commands will build and install *only* mod_janus 78 | 79 | ``` 80 | make mod_janus 81 | make mod_janus-install 82 | ``` 83 | 84 | To run mod_janus within FreeSWITCH, perform the following two steps 85 | 1. Add mod_janus to freeswitch/conf/autoload/modules.conf.xml 86 | 2. Add autoload_conf/janus.conf.xml to freeswitch/conf/autoload_configs 87 | 88 | ## Configuration 89 | 90 | The configuration file consists of two sections: 91 | 1. A settings section that currently only contains the debug flag, and 92 | 2. A list of Janus servers to connect to. Multiple servers may be defined the module can route calls to any of them. 93 | 94 | Each server contains the following fields: 95 | * name - is the internal name given to the server that must be specified in the dial string. 96 | * url - is the address of the server 97 | * secret - is the API secret required by Janus (if it has been enabled on the Janus end) 98 | * auth-token - is the token string string added to the Janus poll request 99 | * enabled - defines if the server should be brought into service when the module starts. This state may be modified by the console API. The default is false. 100 | * rtp-ip - [see mod_sofia](https://freeswitch.org/confluence/display/FREESWITCH/mod_sofia) 101 | * ext-rtp-ip - [see mod_sofia](https://freeswitch.org/confluence/display/FREESWITCH/mod_sofia) 102 | * apply-candidate-acl - [see mod_sofia](https://freeswitch.org/confluence/display/FREESWITCH/mod_sofia) (default is none) 103 | * local-network-acl - [see mod_sofia](https://freeswitch.org/confluence/display/FREESWITCH/mod_sofia) (default is "localnet.auto") 104 | * codec-string - the list of codecs that should be offered to Janus. Should always be Opus which is the default. 105 | 106 | ## Usage 107 | 108 | If called with the following dialstring (`{janus-use-existing-room=true}janus/demo/MyName@1234`) this configuration file should allow you to test using the Janus [audiobridge demo](https://janus.conf.meetecho.com/audiobridgetest.html). 109 | 110 | ``` 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | ``` 129 | 130 | The following channel variables are defined: 131 | * janus-use-existing-room - By default the module will create the room, if this flag is set then the caller is joined to an existing room. 132 | * janus-room-description - This is a textual description of the room specified in the *create* request to Janus (only applicable if janus-use-existing-room is false) 133 | * janus-room-record - The value is specified in the *create* request to Janus and indicates that the room mix should be recorded (only applicable if janus-use-existing-room is false). The default value is not to record. 134 | * janus-room-record-file - This value specifies the file name to which the recording should be written. It is passed in the *create* request to Janus (only applicable if janus-use-existing-room is false and janus-room-record is true). If omitted the default filename will be used. 135 | * janus-room-pin - PIN that is used to validate a user entering the room. This value is used to set the PIN for a created room. 136 | * janus-user-token - The token for the user that should be passed in the *join* request. NB. no method is provided to set the allowed tokens in the *create* room request. 137 | * janus-user-record - Janus should generate a file containing the audio from the user only. It is specified in the *configure* request. The default value is not to record. 138 | * janus-user-record-file - This specifies the base of the filename used when recording the user audio stream. If omitted the default filename will be used. 139 | * janus-start-muted - Included in the *confifigure* request to indicate that the user should enter the room muted (no mechanism exists in the module to modify the mute status later). The default value is that the user should not be muted. 140 | 141 | The dial string is composed of the following parts: 142 | ``` 143 | /janus//@ 144 | ``` 145 | 146 | ## Command Line Interface (CLI) 147 | 148 | The following commands are available on the console API: 149 | * janus debug [true|false] - enables debug on/off 150 | * janus list - lists all the servers with the following values: name, enabled, total calls, calls in progress, start timestamp (usec) and the internal server id 151 | * janus server [enable|disable] - set the server active or inactive. NB because we have to wait for the long poll to complete this may take around 30 seconds. 152 | * janus status - totalled for all servers the following are reported: total calls, calls in progress, start timestamp (usec) 153 | 154 | ## Notes 155 | 156 | TODO: Use websocket rather than long-polling for connection to Janus 157 | TODO: I am not convinced that the shutdown is always successful 158 | 159 | ## Troubleshooting 160 | 161 | For test purposes it is possible to use the Janus [audiobridge demo](https://janus.conf.meetecho.com/audiobridgetest.html) by adding something like this to the dialplan: 162 | ``` 163 | 164 | 165 | 166 | 167 | 168 | ``` 169 | -------------------------------------------------------------------------------- /api.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * api.c -- API functions for janus endpoint module 31 | * 32 | */ 33 | 34 | // need latest version of cJSON to handle size of Janus identifiers (~64-bit) 35 | #include "cJSON.h" 36 | #include "switch.h" 37 | // use switch_stun_random_string() to get a transactionId 38 | #include "switch_stun.h" 39 | #include "globals.h" 40 | #include "http.h" 41 | 42 | #define TRANSACTION_ID_LENGTH 16 43 | #define JANUS_STRING "janus" 44 | #define JANUS_PLUGIN "janus.plugin.audiobridge" 45 | #define MAX_POLL_EVENTS 10 46 | // The long-poll request has a 30 seconds timeout. If it has no event to report, a simple keep-alive message will be triggered 47 | #define HTTP_GET_TIMEOUT 60000 48 | #define HTTP_POST_TIMEOUT 3000 49 | 50 | typedef struct { 51 | const char *pType; 52 | janus_id_t serverId; 53 | const char *pTransactionId; 54 | const char *opaqueId; 55 | janus_id_t senderId; 56 | switch_bool_t isPlugin; 57 | const char *pSecret; 58 | cJSON *pJsonBody; 59 | cJSON *pJsonJsep; 60 | const char *pCandidate; 61 | } message_t; 62 | 63 | // calling process must delete the returned value 64 | static char *generateTransactionId() { 65 | char *pTransactionId; 66 | 67 | 68 | switch_malloc(pTransactionId, TRANSACTION_ID_LENGTH); 69 | 70 | switch_stun_random_string(pTransactionId, TRANSACTION_ID_LENGTH - 1, NULL); 71 | // add terminating null 72 | pTransactionId[TRANSACTION_ID_LENGTH - 1] = '\0'; 73 | 74 | return pTransactionId; 75 | } 76 | 77 | // calling process is responsible for freeing the returned JSON object 78 | static cJSON *encode(const message_t message) { 79 | cJSON *pJsonRequest = cJSON_CreateObject(); 80 | 81 | if (pJsonRequest == NULL) { 82 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 83 | goto error; 84 | } 85 | 86 | if (cJSON_AddStringToObject(pJsonRequest, JANUS_STRING, message.pType) == NULL) { 87 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (create)\n"); 88 | goto error; 89 | } 90 | 91 | if (message.pTransactionId) { 92 | if (cJSON_AddStringToObject(pJsonRequest, "transaction", message.pTransactionId) == NULL) { 93 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (transaction)\n"); 94 | goto error; 95 | } 96 | } 97 | 98 | if (message.pSecret) { 99 | if (cJSON_AddStringToObject(pJsonRequest, "apisecret", message.pSecret) == NULL) { 100 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (apisecret)\n"); 101 | goto error; 102 | } 103 | } 104 | 105 | if (message.opaqueId) { 106 | if (cJSON_AddStringToObject(pJsonRequest, "opaque_id", message.opaqueId) == NULL) { 107 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (opaque_id)\n"); 108 | goto error; 109 | } 110 | } 111 | 112 | if (message.isPlugin) { 113 | if (cJSON_AddStringToObject(pJsonRequest, "plugin", JANUS_PLUGIN) == NULL) { 114 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (plugin)\n"); 115 | goto error; 116 | } 117 | } 118 | 119 | if (message.pJsonBody) { 120 | cJSON_AddItemToObject(pJsonRequest, "body", message.pJsonBody); 121 | } 122 | 123 | if (message.pJsonJsep) { 124 | cJSON_AddItemToObject(pJsonRequest, "jsep", message.pJsonJsep); 125 | } 126 | 127 | return pJsonRequest; 128 | 129 | error: 130 | cJSON_Delete(pJsonRequest); 131 | return NULL; 132 | } 133 | 134 | // calling process should delete return value 135 | static message_t *decode(cJSON *pJsonResponse) { 136 | message_t *pMessage; 137 | cJSON *pJsonRspPluginData; 138 | cJSON *pJsonRspPlugin; 139 | cJSON *pJsonRspCandidate; 140 | cJSON *pJsonRspCandidateData; 141 | cJSON *pJsonRspCandidateCompleted; 142 | cJSON *pJsonRspJanus; 143 | cJSON *pJsonRspTransaction; 144 | cJSON *pJsonRspServerId; 145 | cJSON *pJsonRspSender; 146 | 147 | if (pJsonResponse == NULL) { 148 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No response to decode\n"); 149 | return NULL; 150 | } 151 | 152 | switch_zmalloc(pMessage, sizeof(*pMessage)); 153 | 154 | pMessage->pJsonBody = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "data"); 155 | if (pMessage->pJsonBody && !cJSON_IsObject(pMessage->pJsonBody)) { 156 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid type (data)\n"); 157 | return NULL; 158 | } else if (!pMessage->pJsonBody) { 159 | if ((pJsonRspPluginData = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "plugindata")) != NULL) { 160 | if (cJSON_IsObject(pJsonRspPluginData)) { 161 | pJsonRspPlugin = cJSON_GetObjectItemCaseSensitive(pJsonRspPluginData, "plugin"); 162 | if (!cJSON_IsString(pJsonRspPlugin) || strcmp(JANUS_PLUGIN, pJsonRspPlugin->valuestring)) { 163 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.plugin)\n"); 164 | return NULL; 165 | } 166 | 167 | pMessage->pJsonBody = cJSON_GetObjectItemCaseSensitive(pJsonRspPluginData, "data"); 168 | if (!cJSON_IsObject(pMessage->pJsonBody)) { 169 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid type (plugindata.data)\n"); 170 | return NULL; 171 | } 172 | } else { 173 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid type (plugindata)\n"); 174 | return NULL; 175 | } 176 | } else if ((pJsonRspCandidate = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "candidate")) != NULL) { 177 | if (cJSON_IsObject(pJsonRspCandidate)) { 178 | //NB. sdpMLineIndex is ignored - we're only doing audio 179 | 180 | if ((pJsonRspCandidateCompleted = cJSON_GetObjectItemCaseSensitive(pJsonRspCandidate, "completed")) != NULL) { 181 | if (!cJSON_IsBool(pJsonRspCandidateCompleted) || cJSON_IsFalse(pJsonRspCandidateCompleted)) { 182 | // assumes that completed is always true value 183 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (candidate.completed)\n"); 184 | return NULL; 185 | } 186 | pMessage->pCandidate = ""; 187 | } else if ((pJsonRspCandidateData = cJSON_GetObjectItemCaseSensitive(pJsonRspCandidate, "candidate")) != NULL) { 188 | if (!cJSON_IsString(pJsonRspCandidateData)) { 189 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (candidate.candidate)\n"); 190 | return NULL; 191 | } 192 | pMessage->pCandidate = pJsonRspCandidateData->valuestring; 193 | } 194 | } else { 195 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid type (candidate)\n"); 196 | return NULL; 197 | } 198 | } 199 | } 200 | 201 | pMessage->pJsonJsep = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "jsep"); 202 | if (pMessage->pJsonJsep && !cJSON_IsObject(pMessage->pJsonJsep)) { 203 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid type (jsep)\n"); 204 | return NULL; 205 | } 206 | 207 | pJsonRspJanus = cJSON_GetObjectItemCaseSensitive(pJsonResponse, JANUS_STRING); 208 | if (pJsonRspJanus) { 209 | if (!cJSON_IsString(pJsonRspJanus)) { 210 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (janus)\n"); 211 | return NULL; 212 | } else { 213 | pMessage->pType = pJsonRspJanus->valuestring; 214 | DEBUG(SWITCH_CHANNEL_LOG, "janus=%s\n", pMessage->pType); 215 | } 216 | } 217 | 218 | pJsonRspTransaction = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "transaction"); 219 | if (pJsonRspTransaction) { 220 | if (!cJSON_IsString(pJsonRspTransaction)) { 221 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (transaction)\n"); 222 | return NULL; 223 | } else { 224 | pMessage->pTransactionId = pJsonRspTransaction->valuestring; 225 | DEBUG(SWITCH_CHANNEL_LOG, "transaction=%s\n", pMessage->pTransactionId); 226 | } 227 | } 228 | 229 | pJsonRspServerId = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "session_id"); 230 | if (pJsonRspServerId) { 231 | if (!cJSON_IsNumber(pJsonRspServerId)) { 232 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (session_id)\n"); 233 | return NULL; 234 | } else { 235 | pMessage->serverId = (janus_id_t) pJsonRspServerId->valuedouble; 236 | DEBUG(SWITCH_CHANNEL_LOG, "serverId=%" SWITCH_UINT64_T_FMT "\n", pMessage->serverId); 237 | } 238 | } 239 | 240 | pJsonRspSender = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "sender"); 241 | if (pJsonRspSender) { 242 | if (!cJSON_IsNumber(pJsonRspSender)) { 243 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (transaction)\n"); 244 | return NULL; 245 | } else { 246 | pMessage->senderId = (janus_id_t) pJsonRspSender->valuedouble; 247 | DEBUG(SWITCH_CHANNEL_LOG, "sender=%" SWITCH_UINT64_T_FMT "\n", pMessage->senderId); 248 | } 249 | } 250 | 251 | return pMessage; 252 | } 253 | 254 | janus_id_t apiGetServerId(const char *pUrl, const char *pSecret) { 255 | janus_id_t serverId = 0; 256 | message_t request, *pResponse = NULL; 257 | 258 | cJSON *pJsonRequest = NULL; 259 | cJSON *pJsonResponse = NULL; 260 | cJSON *pJsonRspId; 261 | char *pTransactionId = generateTransactionId(); 262 | 263 | switch_assert(pUrl); 264 | 265 | //"{\"janus\":\"create\",\"transaction\":\"5Y1VuEbeNf7U\",\"apisecret\":\"API-SECRET\"}"; 266 | 267 | (void) memset((void *) &request, 0, sizeof(request)); 268 | request.pType = "create"; 269 | request.pTransactionId = pTransactionId; 270 | request.pSecret = pSecret; 271 | 272 | if (!(pJsonRequest = encode(request))) { 273 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 274 | goto done; 275 | } 276 | 277 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 278 | pJsonResponse = httpPost(pUrl, HTTP_POST_TIMEOUT, pJsonRequest); 279 | 280 | if (!(pResponse = decode(pJsonResponse))) { 281 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 282 | goto done; 283 | } 284 | 285 | if (!pResponse->pType || strcmp("success", pResponse->pType) || 286 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId) || 287 | !pResponse->pJsonBody) { 288 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 289 | goto done; 290 | } 291 | 292 | pJsonRspId = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "id"); 293 | if (!cJSON_IsNumber(pJsonRspId)) { 294 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (id)\n"); 295 | goto done; 296 | } 297 | serverId = (janus_id_t) pJsonRspId->valuedouble; 298 | 299 | done: 300 | cJSON_Delete(pJsonRequest); 301 | cJSON_Delete(pJsonResponse); 302 | switch_safe_free(pResponse); 303 | switch_safe_free(pTransactionId); 304 | 305 | return serverId; 306 | } 307 | 308 | switch_status_t apiClaimServerId(const char *pUrl, const char *pSecret, janus_id_t serverId) { 309 | message_t request, *pResponse = NULL; 310 | switch_status_t result = SWITCH_STATUS_SUCCESS; 311 | 312 | cJSON *pJsonRequest = NULL; 313 | cJSON *pJsonResponse = NULL; 314 | cJSON *pJsonRspError; 315 | cJSON *pJsonRspErrorCode; 316 | cJSON *pJsonRspErrorReason; 317 | char *pTransactionId = generateTransactionId(); 318 | char url[1024]; 319 | 320 | switch_assert(pUrl); 321 | 322 | //"{\"janus\":\"claim\",\"transaction\":\"5Y1VuEbeNf7U\",\"apisecret\":\"API-SECRET\",\"session_id\":999999}"; 323 | 324 | (void) memset((void *) &request, 0, sizeof(request)); 325 | request.pType = "claim"; 326 | request.pTransactionId = pTransactionId; 327 | request.pSecret = pSecret; 328 | 329 | if (!(pJsonRequest = encode(request))) { 330 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 331 | result = SWITCH_STATUS_FALSE; 332 | goto done; 333 | } 334 | 335 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT, pUrl, serverId) < 0) { 336 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 337 | result = SWITCH_STATUS_FALSE; 338 | goto done; 339 | } 340 | 341 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 342 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 343 | 344 | if (!(pResponse = decode(pJsonResponse))) { 345 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 346 | result = SWITCH_STATUS_SOCKERR; 347 | goto done; 348 | } 349 | 350 | if (!pResponse->pType || !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { 351 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 352 | result = SWITCH_STATUS_FALSE; 353 | goto done; 354 | } 355 | 356 | if (!strcmp(pResponse->pType, "success")) { 357 | DEBUG(SWITCH_CHANNEL_LOG, "Successful claim\n"); 358 | result = SWITCH_STATUS_SUCCESS; 359 | goto done; 360 | } else if (!strcmp(pResponse->pType, "error")) { 361 | pJsonRspError = cJSON_GetObjectItemCaseSensitive(pJsonResponse, "error"); 362 | if (cJSON_IsObject(pJsonRspError)) { 363 | pJsonRspErrorCode = cJSON_GetObjectItemCaseSensitive(pJsonRspError, "code"); 364 | if (!cJSON_IsNumber(pJsonRspErrorCode)) { 365 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No error code (error.code)\n"); 366 | result = SWITCH_STATUS_FALSE; 367 | goto done; 368 | } 369 | DEBUG(SWITCH_CHANNEL_LOG, "error.code=%d\n", pJsonRspErrorCode->valueint); 370 | 371 | pJsonRspErrorReason = cJSON_GetObjectItemCaseSensitive(pJsonRspError, "reason"); 372 | if (!cJSON_IsString(pJsonRspErrorReason)) { 373 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No reason (error.reason)\n"); 374 | result = SWITCH_STATUS_FALSE; 375 | goto done; 376 | } 377 | DEBUG(SWITCH_CHANNEL_LOG, "error.reason=%s\n", pJsonRspErrorReason->valuestring); 378 | result = SWITCH_STATUS_NOT_INITALIZED; 379 | goto done; 380 | } else { 381 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No error block\n"); 382 | result = SWITCH_STATUS_FALSE; 383 | goto done; 384 | } 385 | } else { 386 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown response=%s\n", pResponse->pType); 387 | result = SWITCH_STATUS_FALSE; 388 | goto done; 389 | } 390 | 391 | done: 392 | cJSON_Delete(pJsonRequest); 393 | cJSON_Delete(pJsonResponse); 394 | switch_safe_free(pResponse); 395 | switch_safe_free(pTransactionId); 396 | 397 | return result; 398 | } 399 | 400 | janus_id_t apiGetSenderId(const char *pUrl, const char *pSecret, const janus_id_t serverId, const char *callId) { 401 | message_t request, *pResponse = NULL; 402 | janus_id_t senderId = 0; 403 | 404 | cJSON *pJsonRequest = NULL; 405 | cJSON *pJsonResponse = NULL; 406 | cJSON *pJsonRspId; 407 | char *pTransactionId = generateTransactionId(); 408 | char url[1024]; 409 | 410 | switch_assert(pUrl); 411 | 412 | // {"janus":"attach","plugin":"janus.plugin.audiobridge","opaque_id":"audiobridgetest-QsFKsttqnbOx","transaction":"ScRdYl6r0qoX"} 413 | 414 | // If opaque_id is present in the message (attach request), add it, since we use call_id to correlate Janus events to the call 415 | // https://github.com/meetecho/janus-gateway/pull/748 416 | 417 | (void) memset((void *) &request, 0, sizeof(request)); 418 | request.pType = "attach"; 419 | request.serverId = serverId; 420 | request.pTransactionId = pTransactionId; 421 | request.opaqueId = callId; 422 | request.pSecret = pSecret; 423 | request.isPlugin = SWITCH_TRUE; 424 | 425 | if (!(pJsonRequest = encode(request))) { 426 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 427 | goto done; 428 | } 429 | 430 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT, pUrl, serverId) < 0) { 431 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 432 | goto done; 433 | } 434 | 435 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 436 | // http_get("https://curl.haxx.se/libcurl/c/CURLOPT_HEADERFUNCTION.html", session); 437 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 438 | 439 | if (!(pResponse = decode(pJsonResponse))) { 440 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 441 | goto done; 442 | } 443 | 444 | if (!pResponse->pType || strcmp("success", pResponse->pType) || 445 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { 446 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 447 | goto done; 448 | } 449 | 450 | pJsonRspId = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "id"); 451 | if (!cJSON_IsNumber(pJsonRspId)) { 452 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (id)\n"); 453 | goto done; 454 | } 455 | senderId = (janus_id_t) pJsonRspId->valuedouble; 456 | 457 | done: 458 | cJSON_Delete(pJsonRequest); 459 | cJSON_Delete(pJsonResponse); 460 | switch_safe_free(pResponse); 461 | switch_safe_free(pTransactionId); 462 | 463 | return senderId; 464 | } 465 | 466 | janus_id_t apiCreateRoom(const char *pUrl, const char *pSecret, const janus_id_t serverId, 467 | const janus_id_t senderId, const janus_id_t roomId, const char *pDescription, 468 | switch_bool_t record, const char *pRecordingFile, const char *pPin) { 469 | message_t request, *pResponse = NULL; 470 | janus_id_t result = 0; 471 | 472 | cJSON *pJsonRequest = NULL; 473 | cJSON *pJsonResponse = NULL; 474 | cJSON *pJsonRspResult; 475 | cJSON *pJsonRspErrorCode; 476 | cJSON *pJsonRspRoomId; 477 | char *pTransactionId = generateTransactionId(); 478 | char url[1024]; 479 | 480 | switch_assert(pUrl); 481 | 482 | //"{\"janus\":\"message\",\"transaction\":\"%s\",\"apisecret\":\"%s\",\"body\":{\"request\":\"create\",\"room\":%s}}", 483 | 484 | (void) memset((void *) &request, 0, sizeof(request)); 485 | request.pType = "message"; 486 | request.serverId = serverId; 487 | request.pTransactionId = pTransactionId; 488 | request.pSecret = pSecret; 489 | 490 | request.pJsonBody = cJSON_CreateObject(); 491 | if (request.pJsonBody == NULL) { 492 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create body\n"); 493 | goto done; 494 | } 495 | 496 | if (cJSON_AddStringToObject(request.pJsonBody, "request", "create") == NULL) { 497 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.request)\n"); 498 | goto done; 499 | } 500 | 501 | if (cJSON_AddNumberToObject(request.pJsonBody, "room", roomId) == NULL) { 502 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.room)\n"); 503 | goto done; 504 | } 505 | 506 | if (pDescription) { 507 | if (cJSON_AddStringToObject(request.pJsonBody, "description", pDescription) == NULL) { 508 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.description)\n"); 509 | goto done; 510 | } 511 | } 512 | 513 | if (pPin) { 514 | if (cJSON_AddStringToObject(request.pJsonBody, "pin", pPin) == NULL) { 515 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.pin)\n"); 516 | goto done; 517 | } 518 | } 519 | 520 | if (cJSON_AddBoolToObject(request.pJsonBody, "record", (cJSON_bool) record) == NULL) { 521 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create boolean (body.record)\n"); 522 | goto done; 523 | } 524 | 525 | if (pRecordingFile) { 526 | if (cJSON_AddStringToObject(request.pJsonBody, "record_file", pRecordingFile) == NULL) { 527 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.record_file)\n"); 528 | goto done; 529 | } 530 | } 531 | 532 | if (!(pJsonRequest = encode(request))) { 533 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 534 | goto done; 535 | } 536 | 537 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "/%" SWITCH_UINT64_T_FMT, pUrl, serverId, senderId) < 0) { 538 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 539 | goto done; 540 | } 541 | 542 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 543 | // http_get("https://curl.haxx.se/libcurl/c/CURLOPT_HEADERFUNCTION.html", session); 544 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 545 | 546 | if (!(pResponse = decode(pJsonResponse))) { 547 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 548 | goto done; 549 | } 550 | 551 | if (!pResponse->pType || strcmp("success", pResponse->pType) || 552 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId) || 553 | (pResponse->senderId != senderId) || !pResponse->pJsonBody) { 554 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 555 | goto done; 556 | } 557 | 558 | pJsonRspResult = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "audiobridge"); 559 | if (!cJSON_IsString(pJsonRspResult)) { 560 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No response (plugindata.data.audiobridge)\n"); 561 | goto done; 562 | } 563 | 564 | if (!strcmp("event", pJsonRspResult->valuestring)) { 565 | pJsonRspErrorCode = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "error_code"); 566 | if (!cJSON_IsNumber(pJsonRspErrorCode)) { 567 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No error code (error_code)\n"); 568 | goto done; 569 | } 570 | 571 | if (pJsonRspErrorCode->valueint == 486) { 572 | // its not a proper error if the room already exists 573 | DEBUG(SWITCH_CHANNEL_LOG, "Room already exists\n"); 574 | result = roomId; 575 | } else { 576 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (error_code) - error=%d\n", pJsonRspErrorCode->valueint); 577 | goto done; 578 | } 579 | } else if (!strcmp("created", pJsonRspResult->valuestring)) { 580 | pJsonRspRoomId = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "room"); 581 | if (!cJSON_IsNumber(pJsonRspRoomId) && (roomId != (janus_id_t) pJsonRspRoomId->valuedouble)) { 582 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.data.room)\n"); 583 | goto done; 584 | } 585 | result = roomId; 586 | } else { 587 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.data.audiobridge)\n"); 588 | goto done; 589 | } 590 | 591 | done: 592 | cJSON_Delete(pJsonRequest); 593 | cJSON_Delete(pJsonResponse); 594 | switch_safe_free(pResponse); 595 | switch_safe_free(pTransactionId); 596 | 597 | return result; 598 | } 599 | 600 | switch_status_t apiJoin(const char *pUrl, const char *pSecret, 601 | const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, 602 | const char *pDisplay, const char *pPin, const char *pToken, const char *callId) { 603 | message_t request, *pResponse = NULL; 604 | switch_status_t result = SWITCH_STATUS_SUCCESS; 605 | 606 | cJSON *pJsonRequest = NULL; 607 | cJSON *pJsonResponse = NULL; 608 | char *pTransactionId = generateTransactionId(); 609 | char url[1024]; 610 | 611 | switch_assert(pUrl); 612 | 613 | //"{\"janus\":\"message\",\"transaction\":\"%s\",\"apisecret\":\"%s\",\"body\":{\"request\":\"join\",\"room\":%lu,\"display\":\"%s\"}}", 614 | 615 | (void) memset((void *) &request, 0, sizeof(request)); 616 | request.pType = "message"; 617 | request.serverId = serverId; 618 | request.pTransactionId = pTransactionId; 619 | request.pSecret = pSecret; 620 | 621 | request.pJsonBody = cJSON_CreateObject(); 622 | if (request.pJsonBody == NULL) { 623 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create body\n"); 624 | result = SWITCH_STATUS_FALSE; 625 | goto done; 626 | } 627 | 628 | if (cJSON_AddStringToObject(request.pJsonBody, "request", "join") == NULL) { 629 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.request)\n"); 630 | result = SWITCH_STATUS_FALSE; 631 | goto done; 632 | } 633 | 634 | if (cJSON_AddNumberToObject(request.pJsonBody, "room", roomId) == NULL) { 635 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.room)\n"); 636 | result = SWITCH_STATUS_FALSE; 637 | goto done; 638 | } 639 | 640 | if (pPin) { 641 | if (cJSON_AddStringToObject(request.pJsonBody, "pin", pPin) == NULL) { 642 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.pin)\n"); 643 | result = SWITCH_STATUS_FALSE; 644 | goto done; 645 | } 646 | } 647 | 648 | if (pDisplay) { 649 | if (cJSON_AddStringToObject(request.pJsonBody, "display", pDisplay) == NULL) { 650 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.display)\n"); 651 | result = SWITCH_STATUS_FALSE; 652 | goto done; 653 | } 654 | } 655 | 656 | if (pToken) { 657 | if (cJSON_AddStringToObject(request.pJsonBody, "token", pToken) == NULL) { 658 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.token)\n"); 659 | result = SWITCH_STATUS_FALSE; 660 | goto done; 661 | } 662 | } 663 | 664 | if (callId) { 665 | if (cJSON_AddStringToObject(request.pJsonBody, "opaque_id", callId) == NULL) { 666 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.opaque_id)\n"); 667 | result = SWITCH_STATUS_FALSE; 668 | goto done; 669 | } 670 | } 671 | 672 | if (!(pJsonRequest = encode(request))) { 673 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 674 | result = SWITCH_STATUS_FALSE; 675 | goto done; 676 | } 677 | 678 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "/%" SWITCH_UINT64_T_FMT, pUrl, serverId, senderId) < 0) { 679 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 680 | result = SWITCH_STATUS_FALSE; 681 | goto done; 682 | } 683 | 684 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 685 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 686 | 687 | if (!(pResponse = decode(pJsonResponse))) { 688 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 689 | result = SWITCH_STATUS_FALSE; 690 | goto done; 691 | } 692 | 693 | if (!pResponse->pType || strcmp("ack", pResponse->pType) || 694 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { 695 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 696 | result = SWITCH_STATUS_FALSE; 697 | goto done; 698 | } 699 | 700 | done: 701 | cJSON_Delete(pJsonRequest); 702 | cJSON_Delete(pJsonResponse); 703 | switch_safe_free(pResponse); 704 | switch_safe_free(pTransactionId); 705 | 706 | return result; 707 | } 708 | 709 | switch_status_t apiConfigure(const char *pUrl, const char *pSecret, 710 | const janus_id_t serverId, const janus_id_t senderId, const switch_bool_t muted, 711 | switch_bool_t record, const char *pRecordingFile, 712 | const char *pType, const char *pSdp, const char *callId) { 713 | message_t request, *pResponse = NULL; 714 | switch_status_t result = SWITCH_STATUS_SUCCESS; 715 | 716 | cJSON *pJsonRequest = NULL; 717 | cJSON *pJsonResponse = NULL; 718 | char *pTransactionId = generateTransactionId(); 719 | char url[1024]; 720 | 721 | switch_assert(pUrl); 722 | 723 | //{"janus":"message","body":{"request":"configure","muted":false},"transaction":"QPDt2vYOQmmd","jsep":{"type":"offer","sdp":"..."}} 724 | 725 | (void) memset((void *) &request, 0, sizeof(request)); 726 | request.pType = "message"; 727 | request.serverId = serverId; 728 | request.pTransactionId = pTransactionId; 729 | request.pSecret = pSecret; 730 | 731 | request.pJsonBody = cJSON_CreateObject(); 732 | 733 | if (request.pJsonBody == NULL) { 734 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create body\n"); 735 | result = SWITCH_STATUS_FALSE; 736 | goto done; 737 | } 738 | 739 | if (cJSON_AddStringToObject(request.pJsonBody, "request", "configure") == NULL) { 740 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.request)\n"); 741 | result = SWITCH_STATUS_FALSE; 742 | goto done; 743 | } 744 | 745 | if (cJSON_AddBoolToObject(request.pJsonBody, "muted", (cJSON_bool) muted) == NULL) { 746 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create boolean (body.muted)\n"); 747 | result = SWITCH_STATUS_FALSE; 748 | goto done; 749 | } 750 | 751 | if (cJSON_AddBoolToObject(request.pJsonBody, "record", (cJSON_bool) record) == NULL) { 752 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create boolean (body.record)\n"); 753 | goto done; 754 | } 755 | 756 | if (pRecordingFile) { 757 | if (cJSON_AddStringToObject(request.pJsonBody, "filename", pRecordingFile) == NULL) { 758 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.filename)\n"); 759 | goto done; 760 | } 761 | } 762 | 763 | if (callId) { 764 | if (cJSON_AddStringToObject(request.pJsonBody, "opaque_id", callId) == NULL) { 765 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.opaque_id)\n"); 766 | result = SWITCH_STATUS_FALSE; 767 | goto done; 768 | } 769 | } 770 | 771 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "type=%s sdp=%s\n", pType, pSdp); 772 | 773 | if (pType && pSdp) { 774 | request.pJsonJsep = cJSON_CreateObject(); 775 | if (request.pJsonJsep == NULL) { 776 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create jsep\n"); 777 | result = SWITCH_STATUS_FALSE; 778 | goto done; 779 | } 780 | 781 | if (cJSON_AddStringToObject(request.pJsonJsep, "type", pType) == NULL) { 782 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (jsep.type)\n"); 783 | result = SWITCH_STATUS_FALSE; 784 | goto done; 785 | } 786 | 787 | if (cJSON_AddFalseToObject(request.pJsonJsep, "trickle") == NULL) { 788 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (jsep.trickle)\n"); 789 | result = SWITCH_STATUS_FALSE; 790 | goto done; 791 | } 792 | 793 | if (cJSON_AddStringToObject(request.pJsonJsep, "sdp", pSdp) == NULL) { 794 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (jsep.sdp)\n"); 795 | result = SWITCH_STATUS_FALSE; 796 | goto done; 797 | } 798 | } 799 | 800 | if (!(pJsonRequest = encode(request))) { 801 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 802 | result = SWITCH_STATUS_FALSE; 803 | goto done; 804 | } 805 | 806 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "/%" SWITCH_UINT64_T_FMT, pUrl, serverId, senderId) < 0) { 807 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 808 | result = SWITCH_STATUS_FALSE; 809 | goto done; 810 | } 811 | 812 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 813 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 814 | 815 | if (!(pResponse = decode(pJsonResponse))) { 816 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 817 | result = SWITCH_STATUS_FALSE; 818 | goto done; 819 | } 820 | 821 | if (!pResponse->pType || strcmp("ack", pResponse->pType) || 822 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { 823 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 824 | result = SWITCH_STATUS_FALSE; 825 | goto done; 826 | } 827 | 828 | done: 829 | cJSON_Delete(pJsonRequest); 830 | cJSON_Delete(pJsonResponse); 831 | switch_safe_free(pResponse); 832 | switch_safe_free(pTransactionId); 833 | 834 | return result; 835 | } 836 | 837 | switch_status_t apiLeave(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const char *callId) { 838 | message_t request, *pResponse = NULL; 839 | switch_status_t result = SWITCH_STATUS_SUCCESS; 840 | 841 | cJSON *pJsonRequest = NULL; 842 | cJSON *pJsonResponse = NULL; 843 | char *pTransactionId = generateTransactionId(); 844 | char url[1024]; 845 | 846 | switch_assert(pUrl); 847 | 848 | //{"janus":"message","body":{"request":"leave"},"transaction":"QPDt2vYOQmmd"} 849 | 850 | (void) memset((void *) &request, 0, sizeof(request)); 851 | request.pType = "message"; 852 | request.serverId = serverId; 853 | request.pTransactionId = pTransactionId; 854 | request.pSecret = pSecret; 855 | 856 | request.pJsonBody = cJSON_CreateObject(); 857 | if (request.pJsonBody == NULL) { 858 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create body\n"); 859 | result = SWITCH_STATUS_FALSE; 860 | goto done; 861 | } 862 | 863 | if (cJSON_AddStringToObject(request.pJsonBody, "request", "leave") == NULL) { 864 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.request)\n"); 865 | result = SWITCH_STATUS_FALSE; 866 | goto done; 867 | } 868 | 869 | if (callId) { 870 | if (cJSON_AddStringToObject(request.pJsonBody, "opaque_id", callId) == NULL) { 871 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.opaque_id)\n"); 872 | result = SWITCH_STATUS_FALSE; 873 | goto done; 874 | } 875 | } 876 | if (!(pJsonRequest = encode(request))) { 877 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 878 | result = SWITCH_STATUS_FALSE; 879 | goto done; 880 | } 881 | 882 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "/%" SWITCH_UINT64_T_FMT, pUrl, serverId, senderId) < 0) { 883 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 884 | result = SWITCH_STATUS_FALSE; 885 | goto done; 886 | } 887 | 888 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 889 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 890 | 891 | if (!(pResponse = decode(pJsonResponse))) { 892 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 893 | result = SWITCH_STATUS_FALSE; 894 | goto done; 895 | } 896 | 897 | if (!pResponse->pType || strcmp("ack", pResponse->pType) || 898 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { 899 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 900 | result = SWITCH_STATUS_FALSE; 901 | goto done; 902 | } 903 | 904 | done: 905 | cJSON_Delete(pJsonRequest); 906 | cJSON_Delete(pJsonResponse); 907 | switch_safe_free(pResponse); 908 | switch_safe_free(pTransactionId); 909 | 910 | return result; 911 | } 912 | 913 | switch_status_t apiDetach(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId) { 914 | message_t request, *pResponse = NULL; 915 | switch_status_t result = SWITCH_STATUS_SUCCESS; 916 | 917 | cJSON *pJsonRequest = NULL; 918 | cJSON *pJsonResponse = NULL; 919 | char *pTransactionId = generateTransactionId(); 920 | char url[1024]; 921 | 922 | switch_assert(pUrl); 923 | 924 | (void) memset((void *) &request, 0, sizeof(request)); 925 | request.pType = "detach"; 926 | request.serverId = serverId; 927 | request.pTransactionId = pTransactionId; 928 | request.pSecret = pSecret; 929 | 930 | if (!(pJsonRequest = encode(request))) { 931 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); 932 | result = SWITCH_STATUS_FALSE; 933 | goto done; 934 | } 935 | 936 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "/%" SWITCH_UINT64_T_FMT, pUrl, serverId, senderId) < 0) { 937 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 938 | result = SWITCH_STATUS_FALSE; 939 | goto done; 940 | } 941 | 942 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request\n"); 943 | pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); 944 | 945 | if (!(pResponse = decode(pJsonResponse))) { 946 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 947 | goto done; 948 | } 949 | 950 | if (!pResponse->pType || strcmp("success", pResponse->pType) || 951 | !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { 952 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); 953 | result = SWITCH_STATUS_FALSE; 954 | goto done; 955 | } 956 | 957 | done: 958 | cJSON_Delete(pJsonRequest); 959 | cJSON_Delete(pJsonResponse); 960 | switch_safe_free(pResponse); 961 | switch_safe_free(pTransactionId); 962 | 963 | return result; 964 | } 965 | 966 | switch_status_t apiPoll(const char *pUrl, const char *pSecret, const janus_id_t serverId, const char *pAuthToken, 967 | switch_status_t (*pJoinedFunc)(const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const janus_id_t participantId), 968 | switch_status_t (*pAcceptedFunc)(const janus_id_t serverId, const janus_id_t senderId, const char *pSdp), 969 | switch_status_t (*pTrickleFunc)(const janus_id_t serverId, const janus_id_t senderId, const char *pCandidate), 970 | switch_status_t (*pAnsweredFunc)(const janus_id_t serverId, const janus_id_t senderId), 971 | switch_status_t (*pHungupFunc)(const janus_id_t serverId, const janus_id_t senderId, const char *pReason)) { 972 | switch_status_t result = SWITCH_STATUS_SUCCESS; 973 | 974 | cJSON *pJsonResponse = NULL; 975 | cJSON *pEvent; 976 | cJSON *pJsonRspReason; 977 | cJSON *pJsonRspType; 978 | cJSON *pJsonRspRoomId; 979 | cJSON *pJsonRspParticipantId; 980 | cJSON *pJsonRspJsepType; 981 | cJSON *pJsonRspJsepSdp; 982 | cJSON *pJsonRspError; 983 | cJSON *pJsonRspErrorCode; 984 | 985 | char url[1024]; 986 | 987 | switch_assert(pUrl); 988 | 989 | if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "?maxev=%d", pUrl, serverId, MAX_POLL_EVENTS) < 0) { 990 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); 991 | result = SWITCH_STATUS_FALSE; 992 | goto done; 993 | } 994 | 995 | if (pSecret) { 996 | size_t len = strlen(url); 997 | if (snprintf(&url[len], sizeof(url) - len, "&apisecret=%s", pSecret) < 0) { 998 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate secret\n"); 999 | result = SWITCH_STATUS_FALSE; 1000 | goto done; 1001 | } 1002 | } 1003 | 1004 | if (pAuthToken) { 1005 | size_t len = strlen(url); 1006 | if (snprintf(&url[len], sizeof(url) - len, "&token=%s", pAuthToken) < 0) { 1007 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate token\n"); 1008 | result = SWITCH_STATUS_FALSE; 1009 | goto done; 1010 | } 1011 | } 1012 | 1013 | DEBUG(SWITCH_CHANNEL_LOG, "Sending HTTP request - url=%s\n", url); 1014 | // http_get("https://curl.haxx.se/libcurl/c/CURLOPT_HEADERFUNCTION.html", session); 1015 | pJsonResponse = httpGet(url, HTTP_GET_TIMEOUT); 1016 | 1017 | if (pJsonResponse == NULL) { 1018 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 1019 | result = SWITCH_STATUS_FALSE; 1020 | goto done; 1021 | } 1022 | 1023 | pEvent = pJsonResponse ? pJsonResponse->child : NULL; 1024 | while (pEvent) { 1025 | message_t *pResponse = NULL; 1026 | 1027 | if (!(pResponse = decode(pEvent))) { 1028 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); 1029 | goto endloop; 1030 | } 1031 | 1032 | if (!strcmp(pResponse->pType, "keepalive")) { 1033 | DEBUG(SWITCH_CHANNEL_LOG, "Its a keepalive - do nothing\n"); 1034 | } else if (!strcmp(pResponse->pType, "hangup")) { 1035 | DEBUG(SWITCH_CHANNEL_LOG, "Its an hangup\n"); 1036 | 1037 | pJsonRspReason = cJSON_GetObjectItemCaseSensitive(pEvent, "reason"); 1038 | if (!cJSON_IsString(pJsonRspReason)) { 1039 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (reason)\n"); 1040 | goto endloop; 1041 | } 1042 | 1043 | if ((*pHungupFunc)(pResponse->serverId, pResponse->senderId, pJsonRspReason->valuestring)) { 1044 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't hangup\n"); 1045 | } 1046 | } else if (!strcmp(pResponse->pType, "detached")) { 1047 | // treat detached the same as hangup in case we missed the first message - 1048 | // this might mean we call the hungup function when the call has been terminated 1049 | if ((*pHungupFunc)(pResponse->serverId, pResponse->senderId, NULL)) { 1050 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't detach\n"); 1051 | } 1052 | } else if (!strcmp(pResponse->pType, "webrtcup")) { 1053 | DEBUG(SWITCH_CHANNEL_LOG, "WebRTC has been setup\n"); 1054 | } else if (!strcmp(pResponse->pType, "media")) { 1055 | DEBUG(SWITCH_CHANNEL_LOG, "Media is flowing\n"); 1056 | } else if (!strcmp(pResponse->pType, "trickle")) { 1057 | DEBUG(SWITCH_CHANNEL_LOG, "Receieved a candidate\n"); 1058 | 1059 | if ((*pTrickleFunc)(pResponse->serverId, pResponse->senderId, pResponse->pCandidate)) { 1060 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't add candidate\n"); 1061 | } 1062 | } else if (!strcmp(pResponse->pType, "event")) { 1063 | pJsonRspType = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "audiobridge"); 1064 | if (!cJSON_IsString(pJsonRspType)) { 1065 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No response (plugindata.data.audiobridge)\n"); 1066 | goto endloop; 1067 | } 1068 | 1069 | if (!strcmp("joined", pJsonRspType->valuestring)) { 1070 | janus_id_t roomId; 1071 | janus_id_t participantId; 1072 | 1073 | pJsonRspRoomId = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "room"); 1074 | if (!cJSON_IsNumber(pJsonRspRoomId)) { 1075 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.data.room)\n"); 1076 | goto endloop; 1077 | } 1078 | roomId = (janus_id_t) pJsonRspRoomId->valuedouble; 1079 | DEBUG(SWITCH_CHANNEL_LOG, "roomId=%" SWITCH_UINT64_T_FMT "\n", (janus_id_t) roomId); 1080 | 1081 | pJsonRspParticipantId = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "id"); 1082 | if (pJsonRspParticipantId) { 1083 | if (!cJSON_IsNumber(pJsonRspParticipantId)) { 1084 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.data.id)\n"); 1085 | goto endloop; 1086 | } 1087 | participantId = (janus_id_t) pJsonRspParticipantId->valuedouble; 1088 | DEBUG(SWITCH_CHANNEL_LOG, "participantId=%" SWITCH_UINT64_T_FMT "\n", (janus_id_t) participantId); 1089 | 1090 | 1091 | // call the relevant handler in mod_janus 1092 | 1093 | if ((*pJoinedFunc)(pResponse->serverId, pResponse->senderId, roomId, participantId)) { 1094 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't join\n"); 1095 | } 1096 | } else { 1097 | DEBUG(SWITCH_CHANNEL_LOG, "Someone else has joined\n"); 1098 | } 1099 | } else if (!strcmp("event", pJsonRspType->valuestring)) { 1100 | pJsonRspType = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "result"); 1101 | if (pJsonRspType) { 1102 | if (!cJSON_IsString(pJsonRspType) || strcmp("ok", pJsonRspType->valuestring)) { 1103 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.data.result)\n"); 1104 | goto endloop; 1105 | } 1106 | 1107 | pJsonRspJsepType = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonJsep, "type"); 1108 | if (!cJSON_IsString(pJsonRspJsepType) || strcmp("answer", pJsonRspJsepType->valuestring)) { 1109 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (jsep.type)\n"); 1110 | goto endloop; 1111 | } 1112 | 1113 | pJsonRspJsepSdp = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonJsep, "sdp"); 1114 | if (!cJSON_IsString(pJsonRspJsepSdp)) { 1115 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (jsep.sdp)\n"); 1116 | goto endloop; 1117 | } 1118 | 1119 | if ((*pAcceptedFunc)(pResponse->serverId, pResponse->senderId, pJsonRspJsepSdp->valuestring)) { 1120 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't accept\n"); 1121 | } 1122 | 1123 | if ((*pAnsweredFunc)(pResponse->serverId, pResponse->senderId)) { 1124 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't answer\n"); 1125 | } 1126 | } else if ((pJsonRspType = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "leaving")) != NULL) { 1127 | if (!cJSON_IsNumber(pJsonRspType)) { 1128 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response (plugindata.data.leaving)\n"); 1129 | goto endloop; 1130 | } 1131 | DEBUG(SWITCH_CHANNEL_LOG, "leaving=%" SWITCH_UINT64_T_FMT "\n", (janus_id_t) pJsonRspType->valuedouble); 1132 | } else if ((pJsonRspErrorCode = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "error_code")) != NULL 1133 | && (pJsonRspError = cJSON_GetObjectItemCaseSensitive(pResponse->pJsonBody, "error")) != NULL) { 1134 | if (!cJSON_IsNumber(pJsonRspErrorCode) || !cJSON_IsString(pJsonRspError)) { 1135 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No error code or reason on join request\n"); 1136 | goto endloop; 1137 | } 1138 | DEBUG(SWITCH_CHANNEL_LOG, "error_code=%d\n", pJsonRspErrorCode->valueint); 1139 | DEBUG(SWITCH_CHANNEL_LOG, "error=%s\n", pJsonRspError->valuestring); 1140 | 1141 | if ((*pHungupFunc)(pResponse->serverId, pResponse->senderId, pJsonRspError->valuestring)) { 1142 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't hangup\n"); 1143 | } 1144 | } else { 1145 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown audiobridge event\n"); 1146 | goto endloop; 1147 | } 1148 | } else if (!strcmp("left", pJsonRspType->valuestring)) { 1149 | DEBUG(SWITCH_CHANNEL_LOG, "Caller has left the room\n"); 1150 | } else { 1151 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown result - audiobridge=%s\n", pJsonRspType->valuestring); 1152 | } 1153 | } else { 1154 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unknown event - janus=%s\n", pResponse->pType); 1155 | } 1156 | 1157 | endloop: 1158 | switch_safe_free(pResponse); 1159 | pEvent = pEvent->next; 1160 | } 1161 | 1162 | done: 1163 | cJSON_Delete(pJsonResponse); 1164 | 1165 | return result; 1166 | } 1167 | /* For Emacs: 1168 | * Local Variables: 1169 | * mode:c 1170 | * indent-tabs-mode:t 1171 | * tab-width:4 1172 | * c-basic-offset:4 1173 | * End: 1174 | * For VIM: 1175 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 1176 | */ 1177 | -------------------------------------------------------------------------------- /api.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * api.h -- API header for janus endpoint module 31 | * 32 | */ 33 | #ifndef _API_H_ 34 | #define _API_H_ 35 | 36 | #include "globals.h" 37 | 38 | janus_id_t apiGetServerId(const char *pUrl, const char *pSecret); 39 | switch_status_t apiClaimServerId(const char *pUrl, const char *pSecret, const janus_id_t serverId); 40 | janus_id_t apiGetSenderId(const char *pUrl, const char *pSecret, const janus_id_t serverId, const char *callId); 41 | janus_id_t apiCreateRoom(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pDescription, switch_bool_t record, const char *pRecordingFile, const char *pPin); 42 | switch_status_t apiJoin(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pDisplay, const char *pPin, const char *pToken, const char *callId); 43 | switch_status_t apiConfigure(const char *pUrl, const char *pSecret, 44 | const janus_id_t serverId, const janus_id_t senderId, const switch_bool_t muted, 45 | switch_bool_t record, const char *pRecordingFile, 46 | const char *pType, const char *pSdp, const char *callId); 47 | switch_status_t apiLeave(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const char *callId); 48 | switch_status_t apiDetach(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId); 49 | switch_status_t apiPoll(const char *pUrl, const char *pSecret, const janus_id_t serverId, const char *pAuthToken, 50 | switch_status_t (*pJoinedFunc)(const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const janus_id_t participantId), 51 | switch_status_t (*pAcceptedFunc)(const janus_id_t serverId, const janus_id_t senderId, const char *pSdp), 52 | switch_status_t (*pTrickleFunc)(const janus_id_t serverId, const janus_id_t senderId, const char *pCandidate), 53 | switch_status_t (*pAnsweredFunc)(const janus_id_t serverId, const janus_id_t senderId), 54 | switch_status_t (*pHungupFunc)(const janus_id_t serverId, const janus_id_t senderId, const char *pReason)); 55 | 56 | #endif //_API_H_ 57 | /* For Emacs: 58 | * Local Variables: 59 | * mode:c 60 | * indent-tabs-mode:t 61 | * tab-width:4 62 | * c-basic-offset:4 63 | * End: 64 | * For VIM: 65 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 66 | */ 67 | -------------------------------------------------------------------------------- /autoload_conf/janus.conf.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | autoreconf -i 3 | -------------------------------------------------------------------------------- /cJSON.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2009-2017 Dave Gamble and cJSON contributors 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef cJSON__h 24 | #define cJSON__h 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) 32 | #define __WINDOWS__ 33 | #endif 34 | 35 | #ifdef __WINDOWS__ 36 | 37 | /* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: 38 | 39 | CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols 40 | CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) 41 | CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol 42 | 43 | For *nix builds that support visibility attribute, you can define similar behavior by 44 | 45 | setting default visibility to hidden by adding 46 | -fvisibility=hidden (for gcc) 47 | or 48 | -xldscope=hidden (for sun cc) 49 | to CFLAGS 50 | 51 | then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does 52 | 53 | */ 54 | 55 | #define CJSON_CDECL __cdecl 56 | #define CJSON_STDCALL __stdcall 57 | 58 | /* export symbols by default, this is necessary for copy pasting the C and header file */ 59 | #if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) 60 | #define CJSON_EXPORT_SYMBOLS 61 | #endif 62 | 63 | #if defined(CJSON_HIDE_SYMBOLS) 64 | #define CJSON_PUBLIC(type) type CJSON_STDCALL 65 | #elif defined(CJSON_EXPORT_SYMBOLS) 66 | #define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL 67 | #elif defined(CJSON_IMPORT_SYMBOLS) 68 | #define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL 69 | #endif 70 | #else /* !__WINDOWS__ */ 71 | #define CJSON_CDECL 72 | #define CJSON_STDCALL 73 | 74 | #if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) 75 | #define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type 76 | #else 77 | #define CJSON_PUBLIC(type) type 78 | #endif 79 | #endif 80 | 81 | /* project version */ 82 | #define CJSON_VERSION_MAJOR 1 83 | #define CJSON_VERSION_MINOR 7 84 | #define CJSON_VERSION_PATCH 10 85 | 86 | #include 87 | 88 | /* cJSON Types: */ 89 | #define cJSON_Invalid (0) 90 | #define cJSON_False (1 << 0) 91 | #define cJSON_True (1 << 1) 92 | #define cJSON_NULL (1 << 2) 93 | #define cJSON_Number (1 << 3) 94 | #define cJSON_String (1 << 4) 95 | #define cJSON_Array (1 << 5) 96 | #define cJSON_Object (1 << 6) 97 | #define cJSON_Raw (1 << 7) /* raw json */ 98 | 99 | #define cJSON_IsReference 256 100 | #define cJSON_StringIsConst 512 101 | 102 | /* The cJSON structure: */ 103 | typedef struct cJSON 104 | { 105 | /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ 106 | struct cJSON *next; 107 | struct cJSON *prev; 108 | /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ 109 | struct cJSON *child; 110 | 111 | /* The type of the item, as above. */ 112 | int type; 113 | 114 | /* The item's string, if type==cJSON_String and type == cJSON_Raw */ 115 | char *valuestring; 116 | /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ 117 | int valueint; 118 | /* The item's number, if type==cJSON_Number */ 119 | double valuedouble; 120 | 121 | /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ 122 | char *string; 123 | } cJSON; 124 | 125 | typedef struct cJSON_Hooks 126 | { 127 | /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ 128 | void *(CJSON_CDECL *malloc_fn)(size_t sz); 129 | void (CJSON_CDECL *free_fn)(void *ptr); 130 | } cJSON_Hooks; 131 | 132 | typedef int cJSON_bool; 133 | 134 | /* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. 135 | * This is to prevent stack overflows. */ 136 | #ifndef CJSON_NESTING_LIMIT 137 | #define CJSON_NESTING_LIMIT 1000 138 | #endif 139 | 140 | /* returns the version of cJSON as a string */ 141 | CJSON_PUBLIC(const char*) cJSON_Version(void); 142 | 143 | /* Supply malloc, realloc and free functions to cJSON */ 144 | CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); 145 | 146 | /* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ 147 | /* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ 148 | CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); 149 | /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ 150 | /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ 151 | CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); 152 | 153 | /* Render a cJSON entity to text for transfer/storage. */ 154 | CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); 155 | /* Render a cJSON entity to text for transfer/storage without any formatting. */ 156 | CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); 157 | /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ 158 | CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); 159 | /* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ 160 | /* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ 161 | CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); 162 | /* Delete a cJSON entity and all subentities. */ 163 | CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); 164 | 165 | /* Returns the number of items in an array (or object). */ 166 | CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); 167 | /* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ 168 | CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); 169 | /* Get item "string" from object. Case insensitive. */ 170 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); 171 | CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); 172 | CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); 173 | /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ 174 | CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); 175 | 176 | /* Check if the item is a string and return its valuestring */ 177 | CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); 178 | 179 | /* These functions check the type of an item */ 180 | CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); 181 | CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); 182 | CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); 183 | CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); 184 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); 185 | CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); 186 | CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); 187 | CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); 188 | CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); 189 | CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); 190 | 191 | /* These calls create a cJSON item of the appropriate type. */ 192 | CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); 193 | CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); 194 | CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); 195 | CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); 196 | CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); 197 | CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); 198 | /* raw json */ 199 | CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); 200 | CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); 201 | CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); 202 | 203 | /* Create a string where valuestring references a string so 204 | * it will not be freed by cJSON_Delete */ 205 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); 206 | /* Create an object/arrray that only references it's elements so 207 | * they will not be freed by cJSON_Delete */ 208 | CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); 209 | CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); 210 | 211 | /* These utilities create an Array of count items. */ 212 | CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); 213 | CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); 214 | CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); 215 | CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); 216 | 217 | /* Append item to the specified array/object. */ 218 | CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); 219 | CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); 220 | /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. 221 | * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before 222 | * writing to `item->string` */ 223 | CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); 224 | /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ 225 | CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); 226 | CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); 227 | 228 | /* Remove/Detatch items from Arrays/Objects. */ 229 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); 230 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); 231 | CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); 232 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); 233 | CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); 234 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); 235 | CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); 236 | 237 | /* Update array items. */ 238 | CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ 239 | CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); 240 | CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); 241 | CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); 242 | CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); 243 | 244 | /* Duplicate a cJSON item */ 245 | CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); 246 | /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will 247 | need to be released. With recurse!=0, it will duplicate any children connected to the item. 248 | The item->next and ->prev pointers are always zero on return from Duplicate. */ 249 | /* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. 250 | * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ 251 | CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); 252 | 253 | 254 | CJSON_PUBLIC(void) cJSON_Minify(char *json); 255 | 256 | /* Helper functions for creating and adding items to an object at the same time. 257 | * They return the added item or NULL on failure. */ 258 | CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); 259 | CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); 260 | CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); 261 | CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); 262 | CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); 263 | CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); 264 | CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); 265 | CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); 266 | CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); 267 | 268 | /* When assigning an integer value, it needs to be propagated to valuedouble too. */ 269 | #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) 270 | /* helper for the cJSON_SetNumberValue macro */ 271 | CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); 272 | #define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) 273 | 274 | /* Macro for iterating over an array or object */ 275 | #define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) 276 | 277 | /* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ 278 | CJSON_PUBLIC(void *) cJSON_malloc(size_t size); 279 | CJSON_PUBLIC(void) cJSON_free(void *object); 280 | 281 | #ifdef __cplusplus 282 | } 283 | #endif 284 | 285 | #endif 286 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([freeswitch_standalone_module], [1.7.0], bugs@freeswitch.org) 2 | 3 | AM_INIT_AUTOMAKE 4 | 5 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) 6 | 7 | AM_PROG_CC_C_O 8 | 9 | AC_PROG_LIBTOOL 10 | 11 | # backwards compat with older pkg-config 12 | # - pull in AC_DEFUN from pkg.m4 13 | m4_ifndef([PKG_CHECK_VAR], [ 14 | # PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, 15 | # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) 16 | # ------------------------------------------- 17 | # Retrieves the value of the pkg-config variable for the given module. 18 | AC_DEFUN([PKG_CHECK_VAR], 19 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 20 | AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl 21 | 22 | _PKG_CONFIG([$1], [variable="][$3]["], [$2]) 23 | AS_VAR_COPY([$1], [pkg_cv_][$1]) 24 | 25 | AS_VAR_IF([$1], [""], [$5], [$4])dnl 26 | ])# PKG_CHECK_VAR 27 | ]) 28 | 29 | AC_DEFUN([PKG_CHECK_VER], 30 | [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl 31 | AC_ARG_VAR([$1], [version for $2, overriding pkg-config])dnl 32 | 33 | _PKG_CONFIG([$1], [modversion], [$2]) 34 | AS_VAR_COPY([$1], [pkg_cv_][$1]) 35 | 36 | AS_VAR_IF([$1], [""], [$4], [$3])dnl 37 | ])# PKG_CHECK_VAR 38 | 39 | PKG_CHECK_MODULES([FREESWITCH],[freeswitch],[],[]) 40 | PKG_CHECK_VAR([moddir],[freeswitch],[modulesdir]) 41 | PKG_CHECK_VAR([confdir],[freeswitch],[confdir]) 42 | PKG_CHECK_VER([version],[freeswitch]) 43 | 44 | SWITCH_VERSION_MAJOR=`echo $version|cut -d. -f1` 45 | SWITCH_VERSION_MINOR=`echo $version|cut -d. -f2` 46 | SWITCH_VERSION_MICRO=`echo $version|cut -d. -f3` 47 | 48 | AC_SUBST(SWITCH_VERSION_MAJOR, [$SWITCH_VERSION_MAJOR]) 49 | AC_SUBST(SWITCH_VERSION_MINOR, [$SWITCH_VERSION_MINOR]) 50 | AC_SUBST(SWITCH_VERSION_MICRO, [$SWITCH_VERSION_MICRO]) 51 | AC_SUBST(FREESWITCH_CFLAGS) 52 | AC_SUBST(FREESWITCH_LDFLAGS) 53 | AC_SUBST(moddir) 54 | AC_SUBST(confdir) 55 | 56 | AC_CONFIG_FILES([Makefile]) 57 | 58 | AC_OUTPUT 59 | -------------------------------------------------------------------------------- /globals.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * globals.h -- Global function for janus endpoint module 31 | * 32 | */ 33 | #include "globals.h" 34 | 35 | globals_t globals; 36 | /* For Emacs: 37 | * Local Variables: 38 | * mode:c 39 | * indent-tabs-mode:t 40 | * tab-width:4 41 | * c-basic-offset:4 42 | * End: 43 | * For VIM: 44 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 45 | */ 46 | -------------------------------------------------------------------------------- /globals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * globals.h -- Global headers for janus endpoint module 31 | * 32 | */ 33 | #ifndef _GLOBALS_H_ 34 | #define _GLOBALS_H_ 35 | 36 | #include "switch.h" 37 | 38 | typedef uint64_t janus_id_t; 39 | 40 | #include "hash.h" 41 | 42 | typedef struct { 43 | switch_memory_pool_t *pModulePool; 44 | int debug; 45 | hash_t serverIdLookup; 46 | switch_time_t started; 47 | switch_hash_t *pServerNameLookup; 48 | 49 | switch_mutex_t *mutex; 50 | unsigned int totalCalls; 51 | unsigned int callsInProgress; 52 | char guess_ip[80]; 53 | switch_bool_t auto_nat; 54 | } globals_t; 55 | 56 | // define as a macro so we can eliminate one nested function 57 | #define DEBUG(details, ...) do { \ 58 | if (globals.debug) { \ 59 | switch_log_printf(details, SWITCH_LOG_INFO, __VA_ARGS__); \ 60 | } else { \ 61 | switch_log_printf(details, SWITCH_LOG_DEBUG, __VA_ARGS__); \ 62 | } \ 63 | } while (0) 64 | 65 | extern globals_t globals; 66 | 67 | #endif //_GLOBALS_H_ 68 | /* For Emacs: 69 | * Local Variables: 70 | * mode:c 71 | * indent-tabs-mode:t 72 | * tab-width:4 73 | * c-basic-offset:4 74 | * End: 75 | * For VIM: 76 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 77 | */ 78 | -------------------------------------------------------------------------------- /hash.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * hash.c -- Hash functions for janus endpoint module 31 | * 32 | * Thin wrapper that takes a key as a uin64_t and translates it 33 | * to a string that is acceptable by the standard hash functions 34 | * 35 | */ 36 | #include "globals.h" 37 | #include "hash.h" 38 | 39 | // calling process must free the returned value 40 | static char *idToStr(const janus_id_t id) { 41 | char *pResult = switch_mprintf("%" SWITCH_UINT64_T_FMT, id); 42 | if (pResult == NULL) { 43 | fprintf(stderr,"ABORT! Realloc failure at: %s:%d", __FILE__, __LINE__); 44 | abort(); 45 | } 46 | return pResult; 47 | } 48 | 49 | switch_status_t hashCreate(hash_t *pHash, switch_memory_pool_t *pPool) { 50 | switch_status_t status; 51 | 52 | switch_assert(pHash); 53 | switch_assert(pPool); 54 | 55 | status = switch_mutex_init(&pHash->pMutex, SWITCH_MUTEX_NESTED, pPool); 56 | if (status == SWITCH_STATUS_SUCCESS) { 57 | status = switch_core_hash_init(&pHash->pTable); 58 | if (status != SWITCH_STATUS_SUCCESS) { 59 | (void) switch_mutex_destroy(pHash->pMutex); 60 | } 61 | } 62 | 63 | return status; 64 | } 65 | 66 | switch_status_t hashInsert(const hash_t *pHash, const janus_id_t id, const void * const pData) { 67 | switch_status_t status; 68 | char *pIdStr = idToStr(id); 69 | 70 | switch_assert(pHash); 71 | switch_assert(pData); 72 | 73 | DEBUG(SWITCH_CHANNEL_LOG, "Insert id=%" SWITCH_UINT64_T_FMT "\n", id); 74 | 75 | status = switch_core_hash_insert_locked(pHash->pTable, pIdStr, pData, pHash->pMutex); 76 | switch_safe_free(pIdStr); 77 | 78 | return status; 79 | } 80 | 81 | void *hashFind(const hash_t *pHash, const janus_id_t id) { 82 | void *pResult; 83 | char *pIdStr = idToStr(id); 84 | 85 | switch_assert(pHash); 86 | 87 | DEBUG(SWITCH_CHANNEL_LOG, "Find id=%" SWITCH_UINT64_T_FMT "\n", id); 88 | 89 | pResult = switch_core_hash_find_locked(pHash->pTable, pIdStr, pHash->pMutex); 90 | switch_safe_free(pIdStr); 91 | 92 | return pResult; 93 | } 94 | 95 | switch_status_t hashDelete(const hash_t *pHash, const janus_id_t id) { 96 | void *pResult; 97 | char *pIdStr = idToStr(id); 98 | 99 | switch_assert(pHash); 100 | 101 | DEBUG(SWITCH_CHANNEL_LOG, "Delete id=%" SWITCH_UINT64_T_FMT "\n", id); 102 | 103 | pResult = switch_core_hash_delete_locked(pHash->pTable, pIdStr, pHash->pMutex); 104 | switch_safe_free(pIdStr); 105 | 106 | return pResult ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; 107 | } 108 | 109 | void *hashIterate(hash_t *pHash, switch_hash_index_t **pIndex) { 110 | void *pVal = NULL; 111 | const void *pVar; 112 | 113 | switch_assert(pHash); 114 | 115 | if (!*pIndex) { 116 | *pIndex = switch_core_hash_first(pHash->pTable); 117 | } else { 118 | *pIndex = switch_core_hash_next(pIndex); 119 | } 120 | 121 | if (*pIndex) { 122 | switch_core_hash_this(*pIndex, &pVar, NULL, &pVal); 123 | } 124 | 125 | return pVal; 126 | } 127 | 128 | switch_status_t hashDestroy(hash_t *pHash) { 129 | switch_status_t status; 130 | 131 | status = switch_core_hash_destroy(&pHash->pTable); 132 | status = switch_mutex_destroy(pHash->pMutex) | status; 133 | pHash->pMutex = NULL; 134 | 135 | return status; 136 | } 137 | /* For Emacs: 138 | * Local Variables: 139 | * mode:c 140 | * indent-tabs-mode:t 141 | * tab-width:4 142 | * c-basic-offset:4 143 | * End: 144 | * For VIM: 145 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 146 | */ 147 | -------------------------------------------------------------------------------- /hash.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * hash.h -- Hash headers for janus endpoint module 31 | * 32 | * Thin wrapper that takes a key as a uin64_t and translates it 33 | * to a string that is acceptable by the standard hash functions 34 | * 35 | */ 36 | #ifndef _HASH_H_ 37 | #define _HASH_H_ 38 | 39 | #include "switch.h" 40 | #include "globals.h" 41 | 42 | typedef struct { 43 | switch_mutex_t *pMutex; 44 | switch_hash_t *pTable; 45 | } hash_t; 46 | 47 | switch_status_t hashCreate(hash_t *pHash, switch_memory_pool_t *pPool); 48 | switch_status_t hashInsert(const hash_t *pHash, const janus_id_t id, const void *pData); 49 | void *hashFind(const hash_t *pHash, const janus_id_t id); 50 | switch_status_t hashDelete(const hash_t *pHash, const janus_id_t id); 51 | void *hashIterate(hash_t *pHash, switch_hash_index_t **pIndex); 52 | switch_status_t hashDestroy(hash_t *pHash); 53 | 54 | #endif //_HASH_H_ 55 | /* For Emacs: 56 | * Local Variables: 57 | * mode:c 58 | * indent-tabs-mode:t 59 | * tab-width:4 60 | * c-basic-offset:4 61 | * End: 62 | * For VIM: 63 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 64 | */ 65 | -------------------------------------------------------------------------------- /http.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * http.c -- HTTP functions for janus endpoint module 31 | * 32 | */ 33 | #include "cJSON.h" 34 | #include "switch.h" 35 | #include "switch_curl.h" 36 | 37 | #include "globals.h" 38 | #include "http.h" 39 | 40 | #define INITIAL_BODY_SIZE 1000 41 | 42 | static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { 43 | switch_buffer_t *pBuffer = (switch_buffer_t *) userdata; 44 | switch_buffer_write(pBuffer, ptr, size * nmemb); 45 | return nmemb; 46 | } 47 | 48 | cJSON *httpPost(const char *pUrl, const unsigned int timeout, cJSON *pJsonRequest) 49 | { 50 | cJSON *pJsonResponse = NULL; 51 | switch_CURL *curl_handle = NULL; 52 | switch_CURLcode curl_status = CURLE_UNKNOWN_OPTION; 53 | long httpRes = 0; 54 | switch_curl_slist_t *headers = NULL; 55 | char *pJsonStr = NULL; 56 | switch_buffer_t *pBody = NULL; 57 | const char *pBodyStr; 58 | 59 | switch_assert(pUrl); 60 | 61 | switch_buffer_create_dynamic(&pBody, INITIAL_BODY_SIZE, INITIAL_BODY_SIZE, 0); 62 | 63 | curl_handle = switch_curl_easy_init(); 64 | if (!curl_handle) { 65 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't get CURL handle\n"); 66 | return NULL; 67 | } 68 | 69 | headers = switch_curl_slist_append(headers, "Content-Type: application/json"); 70 | headers = switch_curl_slist_append(headers, "Accept: application/json"); 71 | 72 | switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); 73 | switch_curl_easy_setopt(curl_handle, CURLOPT_URL, pUrl); 74 | pJsonStr = cJSON_PrintUnformatted(pJsonRequest); 75 | switch_curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, pJsonStr); 76 | switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-janus/1.0"); 77 | switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); 78 | switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) pBody); 79 | if (timeout) { 80 | switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, timeout); 81 | switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); 82 | } 83 | 84 | DEBUG(SWITCH_CHANNEL_LOG, "HTTP POST url=%s json=%s\n", pUrl, pJsonStr); 85 | 86 | curl_status = switch_curl_easy_perform(curl_handle); 87 | switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); 88 | 89 | if (curl_status == CURLE_OK) { 90 | // terminate the string 91 | (void) switch_buffer_write(pBody, "\0", 1); 92 | 93 | (void) switch_buffer_peek_zerocopy(pBody, (const void **) &pBodyStr); 94 | 95 | DEBUG(SWITCH_CHANNEL_LOG, "result=%s\n", pBodyStr); 96 | 97 | pJsonResponse = cJSON_Parse(pBodyStr); 98 | } else { 99 | // nothing downloaded or download interrupted 100 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received curl error %d HTTP error code %ld trying to fetch %s\n", curl_status, httpRes, pUrl); 101 | } 102 | 103 | switch_curl_easy_cleanup(curl_handle); 104 | switch_safe_free(pJsonStr); 105 | switch_buffer_destroy(&pBody); 106 | switch_curl_slist_free_all(headers); 107 | 108 | return pJsonResponse; 109 | } 110 | 111 | cJSON *httpGet(const char *pUrl, const unsigned int timeout) 112 | { 113 | cJSON *pJsonResponse = NULL; 114 | switch_CURL *curl_handle = NULL; 115 | switch_CURLcode curl_status = CURLE_UNKNOWN_OPTION; 116 | long httpRes = 0; 117 | switch_curl_slist_t *headers = NULL; 118 | switch_buffer_t *pBody = NULL; 119 | const char *pBodyStr; 120 | 121 | switch_assert(pUrl); 122 | 123 | switch_buffer_create_dynamic(&pBody, INITIAL_BODY_SIZE, INITIAL_BODY_SIZE, 0); 124 | 125 | curl_handle = switch_curl_easy_init(); 126 | if (!curl_handle) { 127 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't get CURL handle\n"); 128 | return NULL; 129 | } 130 | 131 | headers = switch_curl_slist_append(headers, "Accept: application/json"); 132 | 133 | switch_curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headers); 134 | switch_curl_easy_setopt(curl_handle, CURLOPT_URL, pUrl); 135 | switch_curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-janus/1.0"); 136 | switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_callback); 137 | switch_curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) pBody); 138 | if (timeout) { 139 | switch_curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT_MS, timeout); 140 | switch_curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1); 141 | } 142 | 143 | DEBUG(SWITCH_CHANNEL_LOG, "HTTP GET url=%s\n", pUrl); 144 | 145 | curl_status = switch_curl_easy_perform(curl_handle); 146 | switch_curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, &httpRes); 147 | 148 | if (curl_status == CURLE_OK) { 149 | // terminate the string 150 | (void) switch_buffer_write(pBody, "\0", 1); 151 | 152 | (void) switch_buffer_peek_zerocopy(pBody, (const void **) &pBodyStr); 153 | 154 | DEBUG(SWITCH_CHANNEL_LOG, "code=%ld result=%s\n", httpRes, pBodyStr); 155 | 156 | pJsonResponse = cJSON_Parse(pBodyStr); 157 | } else { 158 | // nothing downloaded or download interrupted 159 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received curl error %d HTTP error code %ld trying to fetch %s\n", curl_status, httpRes, pUrl); 160 | } 161 | 162 | switch_curl_easy_cleanup(curl_handle); 163 | switch_buffer_destroy(&pBody); 164 | switch_curl_slist_free_all(headers); 165 | 166 | return pJsonResponse; 167 | } 168 | /* For Emacs: 169 | * Local Variables: 170 | * mode:c 171 | * indent-tabs-mode:t 172 | * tab-width:4 173 | * c-basic-offset:4 174 | * End: 175 | * For VIM: 176 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 177 | */ 178 | -------------------------------------------------------------------------------- /http.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * http.h -- HTTP headers for janus endpoint module 31 | * 32 | */ 33 | #ifndef _HTTP_H_ 34 | #define _HTTP_H_ 35 | 36 | #include "cJSON.h" 37 | 38 | cJSON *httpPost(const char *url, const unsigned int timeout, cJSON *pJsonRequest); 39 | cJSON *httpGet(const char *url, const unsigned int timeout); 40 | 41 | #endif //_HTTP_H_ 42 | /* For Emacs: 43 | * Local Variables: 44 | * mode:c 45 | * indent-tabs-mode:t 46 | * tab-width:4 47 | * c-basic-offset:4 48 | * End: 49 | * For VIM: 50 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 51 | */ 52 | -------------------------------------------------------------------------------- /mod_janus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * mod_janus.c -- janus Endpoint Module 31 | * 32 | * Implements interface between FreeSWITCH and Janus audiobridge 33 | * (https://github.com/meetecho/janus-gateway) 34 | */ 35 | 36 | #include "switch.h" 37 | #include "switch_curl.h" 38 | 39 | #include "globals.h" 40 | #include "http.h" 41 | #include "servers.h" 42 | #include "api.h" 43 | #include "hash.h" 44 | 45 | SWITCH_MODULE_LOAD_FUNCTION(mod_janus_load); 46 | SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_janus_shutdown); 47 | //SWITCH_MODULE_RUNTIME_FUNCTION(mod_janus_runtime); 48 | SWITCH_MODULE_DEFINITION(mod_janus, mod_janus_load, mod_janus_shutdown, NULL); //mod_janus_runtime); 49 | 50 | 51 | switch_endpoint_interface_t *janus_endpoint_interface; 52 | 53 | typedef enum { 54 | TFLAG_IO = (1 << 0), 55 | TFLAG_INBOUND = (1 << 1), 56 | TFLAG_OUTBOUND = (1 << 2), 57 | TFLAG_DTMF = (1 << 3), 58 | TFLAG_VOICE = (1 << 4), 59 | TFLAG_HANGUP = (1 << 5), 60 | TFLAG_LINEAR = (1 << 6), 61 | TFLAG_CODEC = (1 << 7), 62 | TFLAG_BREAK = (1 << 8) 63 | } TFLAGS; 64 | 65 | struct private_object { 66 | unsigned int flags; 67 | switch_codec_t read_codec; 68 | switch_codec_t write_codec; 69 | switch_frame_t read_frame; 70 | unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE]; 71 | switch_caller_profile_t *caller_profile; 72 | switch_mutex_t *mutex; 73 | switch_mutex_t *flag_mutex; 74 | //switch_thread_cond_t *cond; 75 | switch_media_handle_t *smh; 76 | switch_core_media_params_t mparams; 77 | 78 | janus_id_t serverId; 79 | janus_id_t roomId; 80 | char *pDisplay; 81 | janus_id_t senderId; 82 | const char *callId; 83 | 84 | char *pSdpBody; 85 | char *pSdpCandidates; 86 | char isTrickleComplete; 87 | }; 88 | typedef struct private_object private_t; 89 | 90 | #define JANUS_SYNTAX "janus [debug|status|listgw]" 91 | #define JANUS_DEBUG_SYNTAX "janus debug [true|false]" 92 | #define JANUS_GATEWAY_SYNTAX "janus server [enable|disable]" 93 | 94 | SWITCH_STANDARD_API(janus_api_commands); 95 | 96 | 97 | static switch_status_t channel_on_init(switch_core_session_t *session); 98 | static switch_status_t channel_on_hangup(switch_core_session_t *session); 99 | static switch_status_t channel_on_destroy(switch_core_session_t *session); 100 | static switch_status_t channel_on_routing(switch_core_session_t *session); 101 | static switch_status_t channel_on_exchange_media(switch_core_session_t *session); 102 | static switch_status_t channel_on_soft_execute(switch_core_session_t *session); 103 | static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, 104 | switch_caller_profile_t *outbound_profile, 105 | switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, 106 | switch_call_cause_t *cancel_cause); 107 | static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id); 108 | static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); 109 | static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig); 110 | 111 | 112 | switch_status_t joined(janus_id_t serverId, janus_id_t senderId, janus_id_t roomId, janus_id_t participantId) { 113 | switch_core_session_t *session; 114 | switch_channel_t *channel; 115 | private_t *tech_pvt; 116 | server_t *pServer; 117 | switch_core_session_t *partner_session; 118 | 119 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, serverId))) { 120 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", serverId); 121 | return SWITCH_STATUS_NOTFOUND; 122 | } 123 | 124 | if (!(session = (switch_core_session_t *) hashFind(&pServer->senderIdLookup, senderId))) { 125 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No session for senderId=%" SWITCH_UINT64_T_FMT "\n", senderId); 126 | return SWITCH_STATUS_NOTFOUND; 127 | } 128 | 129 | channel = switch_core_session_get_channel(session); 130 | switch_assert(channel); 131 | 132 | tech_pvt = switch_core_session_get_private(session); 133 | switch_assert(tech_pvt); 134 | 135 | switch_channel_set_variable(channel, "media_webrtc", "true"); 136 | switch_channel_set_flag(channel, CF_AUDIO); 137 | 138 | if (switch_core_session_get_partner(session, &partner_session) == SWITCH_STATUS_SUCCESS) { 139 | switch_channel_t *partner_channel = switch_core_session_get_channel(partner_session); 140 | // forces the B-leg to use the same codec of A-leg and set rfc2833 flags for DTMF to work 141 | // passed by originate flag use-bridged-channel-codec 142 | if (switch_channel_var_true(channel, "janus-use-bridged-channel-codec")) { 143 | // required to force Freeswitch to add telephone-event in sdp to Janus 144 | tech_pvt->mparams.te = 101; 145 | tech_pvt->mparams.recv_te = 101; 146 | // make sure DTMF input is pass trough and with no delays 147 | switch_channel_set_flag(channel, CF_PASS_RFC2833); 148 | 149 | const char *partner_codec = switch_channel_get_variable(partner_channel, "ep_codec_string"); 150 | const char *partner_dtmf_type = switch_channel_get_variable(partner_channel, "dtmf_type"); 151 | 152 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using codec from A-Leg: %s\n", partner_codec); 153 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using DTMF Type from A-Leg: %s\n", partner_dtmf_type); 154 | 155 | switch_channel_set_variable(channel, "dtmf_type", partner_dtmf_type); 156 | switch_channel_set_variable(channel, "absolute_codec_string", switch_channel_get_variable(partner_channel, partner_codec)); 157 | } else { 158 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using codec from janus.conf.xml: %s\n", pServer->codec_string); 159 | switch_channel_set_variable(channel, "absolute_codec_string", pServer->codec_string); 160 | } 161 | switch_core_session_rwunlock(partner_session); 162 | } else if (switch_channel_var_true(channel, "janus-use-bridged-channel-codec")) { 163 | // Make sure that telephone-event is added to the SDP towards Janus 164 | tech_pvt->mparams.te = 101; 165 | tech_pvt->mparams.recv_te = 101; 166 | // make sure DTMF input is pass trough and with no delays 167 | switch_channel_set_flag(channel, CF_PASS_RFC2833); 168 | 169 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using default DTMF Type: rfc2833 \n"); 170 | switch_channel_set_variable(channel, "dtmf_type", "rfc2833"); 171 | } 172 | 173 | switch_core_media_prepare_codecs(session, SWITCH_TRUE); 174 | 175 | switch_core_session_set_ice(session); 176 | 177 | for (unsigned int i = 0; i < pServer->cand_acl_count; i ++) { 178 | switch_core_media_add_ice_acl(session, SWITCH_MEDIA_TYPE_AUDIO, pServer->cand_acl[i]); 179 | } 180 | 181 | if (switch_core_media_choose_ports(session, SWITCH_TRUE, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { 182 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot choose ports\n"); 183 | switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); 184 | return SWITCH_STATUS_FALSE; 185 | } 186 | 187 | switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0); 188 | 189 | //switch_channel_set_flag(channel, CF_REQ_MEDIA); 190 | //switch_channel_set_flag(channel, CF_MEDIA_ACK); 191 | //switch_channel_set_flag(channel, CF_MEDIA_SET); 192 | 193 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Generated SDP=%s\n", tech_pvt->mparams.local_sdp_str); 194 | 195 | if (apiConfigure(pServer->pUrl, 196 | pServer->pSecret, 197 | tech_pvt->serverId, 198 | tech_pvt->senderId, 199 | switch_channel_var_true(channel, "janus-start-muted"), 200 | switch_channel_var_true(channel, "janus-user-record"), 201 | switch_channel_get_variable(channel, "janus-user-record-file"), 202 | "offer", 203 | tech_pvt->mparams.local_sdp_str, 204 | tech_pvt->callId) != SWITCH_STATUS_SUCCESS) { 205 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to configure\n"); 206 | switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); 207 | } 208 | 209 | switch_channel_mark_ring_ready(channel); 210 | 211 | return SWITCH_STATUS_SUCCESS; 212 | } 213 | 214 | // called when we have received the body of the SDP and all of the candidates 215 | switch_status_t proceed(switch_core_session_t *session) { 216 | char sdp[4096] = ""; 217 | 218 | switch_channel_t *channel; 219 | private_t *tech_pvt; 220 | 221 | channel = switch_core_session_get_channel(session); 222 | switch_assert(channel); 223 | 224 | tech_pvt = switch_core_session_get_private(session); 225 | switch_assert(tech_pvt); 226 | 227 | if (tech_pvt->pSdpBody) { 228 | (void) strncat(sdp, tech_pvt->pSdpBody, sizeof(sdp) - 1); 229 | } else { 230 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "No SDP received\n"); 231 | switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); 232 | return SWITCH_STATUS_FALSE; 233 | } 234 | if (tech_pvt->pSdpCandidates) { 235 | (void) strncat(sdp, tech_pvt->pSdpCandidates, sizeof(sdp) - 1); 236 | } 237 | 238 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "ACCEPTED sdp=%s\n", sdp); 239 | 240 | if (switch_core_media_negotiate_sdp(session, sdp, NULL, SDP_TYPE_RESPONSE)) { 241 | if (switch_core_media_activate_rtp(session) != SWITCH_STATUS_SUCCESS) { 242 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_core_media_activate_rtp error\n"); 243 | switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); 244 | return SWITCH_STATUS_FALSE; 245 | } 246 | } else{ 247 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot negotiate SDP\n"); 248 | switch_channel_hangup(channel, SWITCH_CAUSE_MEDIA_TIMEOUT); 249 | } 250 | 251 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Doing pre-answer\n"); 252 | if (switch_channel_pre_answer(channel) != SWITCH_STATUS_SUCCESS) { 253 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel pre answer failed.\n"); 254 | return SWITCH_STATUS_FALSE; 255 | } 256 | 257 | switch_channel_mark_pre_answered(channel); 258 | 259 | switch_set_flag_locked(tech_pvt, TFLAG_VOICE); 260 | 261 | tech_pvt->read_frame.codec = switch_core_session_get_read_codec(session); 262 | 263 | 264 | return SWITCH_STATUS_SUCCESS; 265 | } 266 | 267 | switch_status_t accepted(janus_id_t serverId, janus_id_t senderId, const char *pSdp) { 268 | switch_core_session_t *session; 269 | private_t *tech_pvt; 270 | server_t *pServer; 271 | char isTrickling; 272 | 273 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, serverId))) { 274 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", serverId); 275 | return SWITCH_STATUS_NOTFOUND; 276 | } 277 | 278 | if (!(session = (switch_core_session_t *) hashFind(&pServer->senderIdLookup, senderId))) { 279 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No session for senderId=%" SWITCH_UINT64_T_FMT "\n", senderId); 280 | return SWITCH_STATUS_NOTFOUND; 281 | } 282 | 283 | tech_pvt = switch_core_session_get_private(session); 284 | switch_assert(tech_pvt); 285 | 286 | tech_pvt->pSdpBody = switch_core_session_strdup(session, pSdp); 287 | 288 | isTrickling = strstr(pSdp, "a=candidate:") == NULL; 289 | DEBUG(SWITCH_CHANNEL_LOG, "isTrickling=%d\n", isTrickling); 290 | 291 | if (!isTrickling || tech_pvt->isTrickleComplete) { 292 | return proceed(session); 293 | } else { 294 | return SWITCH_STATUS_SUCCESS; 295 | } 296 | } 297 | 298 | switch_status_t trickle(janus_id_t serverId, janus_id_t senderId, const char *pCandidate) { 299 | switch_core_session_t *session; 300 | server_t *pServer; 301 | private_t *tech_pvt; 302 | 303 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "serverId=%" SWITCH_UINT64_T_FMT " candidate=%s\n", 304 | serverId, pCandidate); 305 | 306 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, serverId))) { 307 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", serverId); 308 | return SWITCH_STATUS_NOTFOUND; 309 | } 310 | 311 | if (!(session = (switch_core_session_t *) hashFind(&pServer->senderIdLookup, senderId))) { 312 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No session for senderId=%" SWITCH_UINT64_T_FMT "\n", senderId); 313 | return SWITCH_STATUS_NOTFOUND; 314 | } 315 | 316 | tech_pvt = switch_core_session_get_private(session); 317 | switch_assert(tech_pvt); 318 | 319 | if (switch_strlen_zero(pCandidate)) { 320 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "serverId=%" SWITCH_UINT64_T_FMT " got all candidates\n", serverId); 321 | tech_pvt->isTrickleComplete = SWITCH_TRUE; 322 | tech_pvt->pSdpCandidates = switch_core_session_sprintf(session, "%sa=end-of-candidates\r\n", 323 | tech_pvt->pSdpCandidates ? tech_pvt->pSdpCandidates : ""); 324 | if (tech_pvt->pSdpBody) { 325 | // we've already for the body - proceed 326 | return proceed(session); 327 | } else { 328 | // wait for the SDP body and then continue 329 | return SWITCH_STATUS_SUCCESS; 330 | } 331 | } else { 332 | tech_pvt->pSdpCandidates = switch_core_session_sprintf(session, "%sa=%s\r\n", 333 | tech_pvt->pSdpCandidates ? tech_pvt->pSdpCandidates : "", pCandidate); 334 | 335 | DEBUG(SWITCH_CHANNEL_LOG, "candidates=%s\n", tech_pvt->pSdpCandidates); 336 | return SWITCH_STATUS_SUCCESS; 337 | } 338 | } 339 | 340 | switch_status_t answered(janus_id_t serverId, janus_id_t senderId) { 341 | // called when we get a webrtcup event from Janus 342 | switch_core_session_t *session; 343 | switch_channel_t *channel; 344 | server_t *pServer; 345 | 346 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, serverId))) { 347 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", serverId); 348 | return SWITCH_STATUS_NOTFOUND; 349 | } 350 | 351 | if (!(session = (switch_core_session_t *) hashFind(&pServer->senderIdLookup, senderId))) { 352 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No session for senderId=%" SWITCH_UINT64_T_FMT "\n", senderId); 353 | return SWITCH_STATUS_NOTFOUND; 354 | } 355 | 356 | channel = switch_core_session_get_channel(session); 357 | switch_assert(channel); 358 | 359 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Doing answer\n"); 360 | if (switch_channel_answer(channel) != SWITCH_STATUS_SUCCESS) { 361 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Channel answer failed.\n"); 362 | return SWITCH_STATUS_FALSE; 363 | } 364 | 365 | switch_channel_mark_answered(channel); 366 | 367 | return SWITCH_STATUS_SUCCESS; 368 | } 369 | 370 | switch_status_t hungup(janus_id_t serverId, janus_id_t senderId, const char *pReason) { 371 | switch_core_session_t *session; 372 | switch_channel_t *channel; 373 | server_t *pServer; 374 | 375 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, serverId))) { 376 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", serverId); 377 | return SWITCH_STATUS_NOTFOUND; 378 | } 379 | 380 | if (!(session = (switch_core_session_t *) hashFind(&pServer->senderIdLookup, senderId))) { 381 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No session for senderId=%" SWITCH_UINT64_T_FMT "\n", senderId); 382 | return SWITCH_STATUS_NOTFOUND; 383 | } 384 | 385 | channel = switch_core_session_get_channel(session); 386 | switch_assert(channel); 387 | 388 | switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); 389 | 390 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Hungup reason=%s\n", pReason); 391 | 392 | return SWITCH_STATUS_SUCCESS; 393 | } 394 | 395 | static void *SWITCH_THREAD_FUNC server_thread_run(switch_thread_t *pThread, void *pObj) { 396 | server_t *pServer = (server_t *) pObj; 397 | janus_id_t serverId = 0; 398 | 399 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Thread started - server=%s\n", pServer->name); 400 | 401 | (void) hashCreate(&pServer->senderIdLookup, globals.pModulePool); 402 | 403 | while (!switch_test_flag(pServer, SFLAG_TERMINATING)) { 404 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Server=%s invoking\n", pServer->name); 405 | 406 | // wait for a few seconds before re-connection 407 | switch_yield(5000000); 408 | 409 | if (serverId) { 410 | // the connection or Janus has restarted - try to re-use the same serverId 411 | switch_status_t status = apiClaimServerId(pServer->pUrl, pServer->pSecret, serverId); 412 | if (status == SWITCH_STATUS_SOCKERR) { 413 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Server=%s claim socket error - retry later\n", pServer->name); 414 | } else if (status == SWITCH_STATUS_SUCCESS) { 415 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Server=%s claim success - serverId=%" SWITCH_UINT64_T_FMT "\n", pServer->name, serverId); 416 | if (hashInsert(&globals.serverIdLookup, serverId, (void *) pServer) != SWITCH_STATUS_SUCCESS) { 417 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't insert server into hash table\n"); 418 | } 419 | switch_mutex_lock(pServer->mutex); 420 | pServer->serverId = serverId; 421 | switch_mutex_unlock(pServer->mutex); 422 | } else { 423 | // terminate all calls in progress on this server 424 | switch_core_session_t *session; 425 | private_t *tech_pvt; 426 | switch_channel_t *channel; 427 | switch_hash_index_t *pIndex = NULL; 428 | 429 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Server=%s claim failed - status=%d\n", pServer->name, status); 430 | 431 | while ((session = (switch_core_session_t *) hashIterate(&pServer->senderIdLookup, &pIndex)) != NULL) { 432 | tech_pvt = switch_core_session_get_private(session); 433 | switch_assert(tech_pvt); 434 | channel = switch_core_session_get_channel(session); 435 | switch_assert(channel); 436 | // the server is likely to have been removed by the time the hangup has completed 437 | switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); 438 | (void) hashDelete(&pServer->senderIdLookup, tech_pvt->senderId); 439 | } 440 | // reset serverId so we get a new one the next time around 441 | serverId = 0; 442 | } 443 | } else { 444 | // first time 445 | serverId = apiGetServerId(pServer->pUrl, pServer->pSecret); 446 | if (!serverId) { 447 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Error getting serverId\n"); 448 | } else if (hashInsert(&globals.serverIdLookup, serverId, (void *) pServer) != SWITCH_STATUS_SUCCESS) { 449 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't insert server into hash table\n"); 450 | } else { 451 | switch_mutex_lock(pServer->mutex); 452 | pServer->started = switch_time_now(); 453 | pServer->serverId = serverId; 454 | pServer->callsInProgress = 0; 455 | switch_mutex_unlock(pServer->mutex); 456 | } 457 | } 458 | 459 | while (!switch_test_flag(pServer, SFLAG_TERMINATING) && hashFind(&globals.serverIdLookup, serverId)) { 460 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Poll started, serverId: %ld\n", (long)serverId); 461 | 462 | if (apiPoll(pServer->pUrl, pServer->pSecret, serverId, pServer->pAuthToken, joined, accepted, trickle, answered, hungup) != SWITCH_STATUS_SUCCESS) { 463 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Poll failed, serverId: %ld\n", (long)serverId); 464 | if (hashDelete(&globals.serverIdLookup, serverId) != SWITCH_STATUS_SUCCESS) { 465 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't remove server %ld from hash table\n", (long)serverId); 466 | } 467 | // Reset servers serverId/session_id 468 | switch_mutex_lock(pServer->mutex); 469 | pServer->serverId = 0; 470 | switch_mutex_unlock(pServer->mutex); 471 | } else { 472 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Poll completed, serverId: %ld\n", (long)serverId); 473 | } 474 | } 475 | } 476 | (void) hashDestroy(&pServer->senderIdLookup); 477 | 478 | (void) hashDelete(&globals.serverIdLookup, serverId); 479 | 480 | return NULL; 481 | } 482 | 483 | 484 | static void startServerThread(server_t *pServer, switch_bool_t force) { 485 | switch_threadattr_t *pThreadAttr = NULL; 486 | 487 | switch_assert(pServer); 488 | 489 | switch_mutex_lock(pServer->flag_mutex); 490 | if (force || !switch_test_flag(pServer, SFLAG_ENABLED)) { 491 | switch_set_flag(pServer, SFLAG_ENABLED); 492 | switch_mutex_unlock(pServer->flag_mutex); 493 | 494 | DEBUG(SWITCH_CHANNEL_LOG, "Starting server=%s\n", pServer->name); 495 | 496 | switch_threadattr_create(&pThreadAttr, globals.pModulePool); 497 | //switch_threadattr_detach_set(pThreadAttr, 1); 498 | switch_threadattr_stacksize_set(pThreadAttr, SWITCH_THREAD_STACKSIZE); 499 | switch_thread_create(&pServer->pThread, pThreadAttr, server_thread_run, pServer, globals.pModulePool); 500 | } else { 501 | switch_mutex_unlock(pServer->flag_mutex); 502 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Server=%s is already enabled\n", pServer->name); 503 | } 504 | } 505 | 506 | static void stopServerThread(server_t *pServer) { 507 | switch_status_t status, returnValue; 508 | 509 | switch_assert(pServer); 510 | 511 | switch_mutex_lock(pServer->flag_mutex); 512 | if (switch_test_flag(pServer, SFLAG_ENABLED) && 513 | !switch_test_flag(pServer, SFLAG_TERMINATING) && pServer->pThread) { 514 | switch_set_flag(pServer, SFLAG_TERMINATING); 515 | switch_mutex_unlock(pServer->flag_mutex); 516 | 517 | DEBUG(SWITCH_CHANNEL_LOG, "Stopping server=%s\n", pServer->name); 518 | 519 | // wait for the thread to terminate. If we don't do this the thread is 520 | // forcefully terminated and valgrind complains about memory being leaked. 521 | status = switch_thread_join(&returnValue, pServer->pThread); 522 | pServer->pThread = NULL; 523 | 524 | if ((status != SWITCH_STATUS_SUCCESS) || (returnValue != SWITCH_STATUS_SUCCESS)) { 525 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Server=%s result from thread=%d %d\n", 526 | pServer->name, status, returnValue); 527 | } 528 | 529 | switch_clear_flag_locked(pServer, SFLAG_TERMINATING | SFLAG_ENABLED); 530 | } else { 531 | switch_mutex_unlock(pServer->flag_mutex); 532 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Server=%s is already disabled\n", pServer->name); 533 | } 534 | } 535 | 536 | // static switch_bool_t isVideoCall(switch_core_session_t *session) { 537 | // switch_channel_t *channel; 538 | 539 | // switch_assert(session); 540 | 541 | // channel = switch_core_session_get_channel(session); 542 | // switch_assert(channel); 543 | 544 | // return switch_channel_test_flag(channel, CF_VIDEO); 545 | // } 546 | 547 | /* 548 | State methods they get called when the state changes to the specific state 549 | returning SWITCH_STATUS_SUCCESS tells the core to execute the standard state method next 550 | so if you fully implement the state you can return SWITCH_STATUS_FALSE to skip it. 551 | */ 552 | static switch_status_t channel_on_init(switch_core_session_t *session) 553 | { 554 | switch_channel_t *channel = NULL; 555 | private_t *tech_pvt = NULL; 556 | server_t *pServer = NULL; 557 | switch_core_session_t *partner_session; 558 | 559 | switch_assert(session); 560 | 561 | tech_pvt = switch_core_session_get_private(session); 562 | switch_assert(tech_pvt); 563 | 564 | channel = switch_core_session_get_channel(session); 565 | switch_assert(channel); 566 | 567 | if (switch_core_session_get_partner(session, &partner_session) == SWITCH_STATUS_SUCCESS) { 568 | switch_channel_t *partner_channel = switch_core_session_get_channel(partner_session); 569 | tech_pvt->callId = switch_channel_get_variable(partner_channel, "sip_call_id"); 570 | switch_core_session_rwunlock(partner_session); 571 | } else { 572 | tech_pvt->callId = switch_channel_get_variable(channel, "sip_call_id"); 573 | } 574 | 575 | if (!tech_pvt->callId) { 576 | tech_pvt->callId = switch_core_session_get_uuid(session); 577 | } 578 | 579 | if (!switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { 580 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Module supports only outgoing calls\n"); 581 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 582 | return SWITCH_STATUS_FALSE; 583 | } 584 | 585 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, tech_pvt->serverId))) { 586 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", tech_pvt->serverId); 587 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 588 | return SWITCH_STATUS_NOTFOUND; 589 | } 590 | 591 | tech_pvt->senderId = apiGetSenderId(pServer->pUrl, pServer->pSecret, tech_pvt->serverId, tech_pvt->callId); 592 | if (!tech_pvt->senderId) { 593 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error getting senderId\n"); 594 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 595 | // failing to originate cause attach session_id is not found, reset it 596 | switch_mutex_lock(pServer->mutex); 597 | pServer->serverId = 0; 598 | switch_mutex_unlock(pServer->mutex); 599 | return SWITCH_STATUS_FALSE; 600 | } 601 | 602 | if (hashInsert(&pServer->senderIdLookup, tech_pvt->senderId, (void *) session) != SWITCH_STATUS_SUCCESS) { 603 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to insert senderId=%" SWITCH_UINT64_T_FMT " in hash\n", tech_pvt->senderId); 604 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 605 | return SWITCH_STATUS_FALSE; 606 | } 607 | 608 | if (switch_channel_var_false(channel, "janus-use-existing-room")) { 609 | if (!apiCreateRoom(pServer->pUrl, pServer->pSecret, tech_pvt->serverId, tech_pvt->senderId, tech_pvt->roomId, 610 | switch_channel_get_variable(channel, "janus-room-description"), 611 | switch_channel_var_true(channel, "janus-room-record"), 612 | switch_channel_get_variable(channel, "janus-room-record-file"), 613 | switch_channel_get_variable(channel, "janus-room-pin"))) { 614 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to create room\n"); 615 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 616 | return SWITCH_STATUS_FALSE; 617 | } 618 | } 619 | 620 | switch_set_flag_locked(tech_pvt, TFLAG_IO); 621 | 622 | switch_mutex_lock(pServer->mutex); 623 | pServer->callsInProgress ++; 624 | pServer->totalCalls ++; 625 | switch_mutex_unlock(pServer->mutex); 626 | 627 | switch_mutex_lock(globals.mutex); 628 | globals.callsInProgress ++; 629 | globals.totalCalls ++; 630 | switch_mutex_unlock(globals.mutex); 631 | 632 | return SWITCH_STATUS_SUCCESS; 633 | } 634 | 635 | static switch_status_t channel_on_routing(switch_core_session_t *session) 636 | { 637 | switch_channel_t *channel = NULL; 638 | private_t *tech_pvt = NULL; 639 | server_t *pServer = NULL; 640 | 641 | switch_assert(session); 642 | 643 | channel = switch_core_session_get_channel(session); 644 | switch_assert(channel); 645 | 646 | if (switch_channel_get_callstate(channel) == CCS_EARLY) { 647 | // Channel re-routing, dialplan transfer called, do not send join again 648 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "%s Channel re-routing, dialplan transfer called.\n", switch_channel_get_name(channel)); 649 | return SWITCH_STATUS_SUCCESS; 650 | } 651 | 652 | tech_pvt = switch_core_session_get_private(session); 653 | switch_assert(tech_pvt); 654 | 655 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "%s CHANNEL ROUTING\n", switch_channel_get_name(channel)); 656 | 657 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, tech_pvt->serverId))) { 658 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", tech_pvt->serverId); 659 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 660 | return SWITCH_STATUS_NOTFOUND; 661 | } 662 | 663 | if (apiJoin( 664 | pServer->pUrl, 665 | pServer->pSecret, 666 | tech_pvt->serverId, 667 | tech_pvt->senderId, 668 | tech_pvt->roomId, 669 | tech_pvt->pDisplay, 670 | switch_channel_get_variable(channel, "janus-room-pin"), 671 | switch_channel_get_variable(channel, "janus-user-token"), 672 | tech_pvt->callId) != SWITCH_STATUS_SUCCESS) { 673 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to join room\n"); 674 | switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); 675 | return SWITCH_STATUS_FALSE; 676 | } 677 | 678 | return SWITCH_STATUS_SUCCESS; 679 | } 680 | 681 | static switch_status_t channel_on_execute(switch_core_session_t *session) 682 | { 683 | switch_channel_t *channel = NULL; 684 | private_t *tech_pvt = NULL; 685 | 686 | switch_assert(session); 687 | 688 | channel = switch_core_session_get_channel(session); 689 | switch_assert(channel); 690 | 691 | tech_pvt = switch_core_session_get_private(session); 692 | switch_assert(tech_pvt); 693 | 694 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel)); 695 | 696 | return SWITCH_STATUS_SUCCESS; 697 | } 698 | 699 | static switch_status_t channel_on_destroy(switch_core_session_t *session) 700 | { 701 | switch_channel_t *channel = NULL; 702 | private_t *tech_pvt = NULL; 703 | 704 | switch_assert(session); 705 | 706 | channel = switch_core_session_get_channel(session); 707 | switch_assert(channel); 708 | 709 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "%s CHANNEL DESTROY\n", switch_channel_get_name(channel)); 710 | 711 | tech_pvt = switch_core_session_get_private(session); 712 | 713 | if (tech_pvt) { 714 | if (switch_core_codec_ready(&tech_pvt->read_codec)) { 715 | switch_core_codec_destroy(&tech_pvt->read_codec); 716 | } 717 | 718 | if (switch_core_codec_ready(&tech_pvt->write_codec)) { 719 | switch_core_codec_destroy(&tech_pvt->write_codec); 720 | } 721 | } 722 | 723 | switch_core_media_deactivate_rtp(session); 724 | 725 | switch_media_handle_destroy(session); 726 | 727 | 728 | return SWITCH_STATUS_SUCCESS; 729 | } 730 | 731 | 732 | static switch_status_t channel_on_hangup(switch_core_session_t *session) 733 | { 734 | switch_channel_t *channel = NULL; 735 | private_t *tech_pvt = NULL; 736 | server_t *pServer = NULL; 737 | switch_core_session_t *partner_session; 738 | 739 | switch_assert(session); 740 | 741 | channel = switch_core_session_get_channel(session); 742 | switch_assert(channel); 743 | 744 | tech_pvt = switch_core_session_get_private(session); 745 | switch_assert(tech_pvt); 746 | 747 | // proper cleanup of media 748 | switch_core_media_kill_socket(session, SWITCH_MEDIA_TYPE_AUDIO); 749 | switch_core_session_stop_media(session); 750 | 751 | 752 | /* Assure partner session is closed 753 | (sometimes doesn't happen automatically, for example during playback) */ 754 | if (switch_core_session_get_partner(session, &partner_session) == SWITCH_STATUS_SUCCESS) { 755 | switch_channel_t *partner_channel = switch_core_session_get_channel(partner_session); 756 | switch_channel_hangup(partner_channel, SWITCH_CAUSE_NORMAL_CLEARING); 757 | switch_core_session_rwunlock(partner_session); 758 | } 759 | 760 | switch_clear_flag_locked(tech_pvt, TFLAG_IO); 761 | switch_clear_flag_locked(tech_pvt, TFLAG_VOICE); 762 | 763 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); 764 | 765 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, tech_pvt->serverId))) { 766 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", tech_pvt->serverId); 767 | // we can get here if the calls are being terminated because the server has failed 768 | return SWITCH_STATUS_NOTFOUND; 769 | } 770 | 771 | if (apiLeave(pServer->pUrl, pServer->pSecret, tech_pvt->serverId, tech_pvt->senderId, tech_pvt->callId) != SWITCH_STATUS_SUCCESS) { 772 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to leave room\n"); 773 | // carry on regardless 774 | } 775 | 776 | if (apiDetach(pServer->pUrl, pServer->pSecret, tech_pvt->serverId, tech_pvt->senderId) != SWITCH_STATUS_SUCCESS) { 777 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to detach\n"); 778 | // carry on regardless 779 | } 780 | 781 | (void) hashDelete(&pServer->senderIdLookup, tech_pvt->senderId); 782 | 783 | switch_mutex_lock(pServer->mutex); 784 | if (pServer->callsInProgress > 0) { 785 | pServer->callsInProgress --; 786 | } 787 | switch_mutex_unlock(pServer->mutex); 788 | 789 | switch_mutex_lock(globals.mutex); 790 | if (globals.callsInProgress > 0) { 791 | globals.callsInProgress --; 792 | } 793 | switch_mutex_unlock(globals.mutex); 794 | 795 | return SWITCH_STATUS_SUCCESS; 796 | } 797 | 798 | static switch_status_t channel_kill_channel(switch_core_session_t *session, int sig) 799 | { 800 | switch_channel_t *channel = NULL; 801 | private_t *tech_pvt = NULL; 802 | 803 | switch_assert(session); 804 | 805 | channel = switch_core_session_get_channel(session); 806 | switch_assert(channel); 807 | 808 | tech_pvt = switch_core_session_get_private(session); 809 | switch_assert(tech_pvt); 810 | 811 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "%s CHANNEL KILLED sig=%d\n", switch_channel_get_name(channel), sig); 812 | 813 | switch (sig) { 814 | case SWITCH_SIG_KILL: 815 | switch_set_flag_locked(tech_pvt, TFLAG_IO); 816 | 817 | switch_core_media_kill_socket(session, SWITCH_MEDIA_TYPE_AUDIO); 818 | break; 819 | case SWITCH_SIG_BREAK: 820 | switch_set_flag_locked(tech_pvt, TFLAG_BREAK); 821 | 822 | switch_core_media_break(session, SWITCH_MEDIA_TYPE_AUDIO); 823 | break; 824 | default: 825 | break; 826 | } 827 | 828 | return SWITCH_STATUS_SUCCESS; 829 | } 830 | 831 | static switch_status_t channel_on_exchange_media(switch_core_session_t *session) 832 | { 833 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "CHANNEL LOOPBACK\n"); 834 | return SWITCH_STATUS_SUCCESS; 835 | } 836 | 837 | static switch_status_t channel_on_soft_execute(switch_core_session_t *session) 838 | { 839 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "CHANNEL TRANSMIT\n"); 840 | return SWITCH_STATUS_SUCCESS; 841 | } 842 | 843 | static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) 844 | { 845 | private_t *tech_pvt = switch_core_session_get_private(session); 846 | switch_assert(tech_pvt); 847 | 848 | return SWITCH_STATUS_SUCCESS; 849 | } 850 | 851 | static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) 852 | { 853 | // removing code that on some cases core dumps 854 | // 855 | // switch_channel_t *channel = NULL; 856 | // private_t *tech_pvt = NULL; 857 | // //switch_time_t started = switch_time_now(); 858 | // //unsigned int elapsed; 859 | // switch_byte_t *data; 860 | // 861 | // //DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "Read frame called\n"); 862 | // 863 | // channel = switch_core_session_get_channel(session); 864 | // assert(channel != NULL); 865 | // 866 | // tech_pvt = switch_core_session_get_private(session); 867 | // switch_assert(tech_pvt); 868 | // 869 | // tech_pvt->read_frame.flags = SFF_NONE; 870 | // *frame = NULL; 871 | // 872 | // switch_set_flag(tech_pvt, TFLAG_VOICE); 873 | // 874 | // while (switch_test_flag(tech_pvt, TFLAG_IO)) { 875 | // 876 | // if (switch_test_flag(tech_pvt, TFLAG_BREAK)) { 877 | // switch_clear_flag(tech_pvt, TFLAG_BREAK); 878 | // goto cng; 879 | // } 880 | // 881 | // if (!switch_test_flag(tech_pvt, TFLAG_IO)) { 882 | // return SWITCH_STATUS_FALSE; 883 | // } 884 | // 885 | // if (switch_test_flag(tech_pvt, TFLAG_IO) && switch_test_flag(tech_pvt, TFLAG_VOICE)) { 886 | // switch_clear_flag_locked(tech_pvt, TFLAG_VOICE); 887 | // if (!tech_pvt->read_frame.datalen) { 888 | // continue; 889 | // } 890 | // *frame = &tech_pvt->read_frame; 891 | // #if SWITCH_BYTE_ORDER == __BIG_ENDIAN 892 | // if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) { 893 | // switch_swap_linear((*frame)->data, (int) (*frame)->datalen / 2); 894 | // } 895 | // #endif 896 | // return switch_core_media_read_frame(session, frame, flags, stream_id, SWITCH_MEDIA_TYPE_AUDIO); 897 | // } 898 | // 899 | // switch_cond_next(); 900 | // } 901 | // 902 | // 903 | // return SWITCH_STATUS_FALSE; 904 | // 905 | // cng: 906 | // data = (switch_byte_t *) tech_pvt->read_frame.data; 907 | // data[0] = 65; 908 | // data[1] = 0; 909 | // tech_pvt->read_frame.datalen = 2; 910 | // tech_pvt->read_frame.flags = SFF_CNG; 911 | // *frame = &tech_pvt->read_frame; 912 | // return SWITCH_STATUS_SUCCESS; 913 | 914 | return switch_core_media_read_frame(session, frame, flags, stream_id, SWITCH_MEDIA_TYPE_AUDIO); 915 | } 916 | 917 | static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) 918 | { 919 | // removing code that on some cases core dumps 920 | // 921 | //switch_status_t status = SWITCH_STATUS_SUCCESS; 922 | // switch_channel_t *channel = NULL; 923 | // private_t *tech_pvt = NULL; 924 | // //switch_frame_t *pframe; 925 | // 926 | // channel = switch_core_session_get_channel(session); 927 | // switch_assert(channel); 928 | // 929 | // tech_pvt = switch_core_session_get_private(session); 930 | // switch_assert(tech_pvt); 931 | // 932 | // if (!switch_test_flag(tech_pvt, TFLAG_IO)) { 933 | // return SWITCH_STATUS_FALSE; 934 | // } 935 | // #if SWITCH_BYTE_ORDER == __BIG_ENDIAN 936 | // if (switch_test_flag(tech_pvt, TFLAG_LINEAR)) { 937 | // switch_swap_linear(frame->data, (int) frame->datalen / 2); 938 | // } 939 | // #endif 940 | 941 | return switch_core_media_write_frame(session, frame, flags, stream_id, SWITCH_MEDIA_TYPE_AUDIO); 942 | } 943 | 944 | static switch_status_t channel_answer_channel(switch_core_session_t *session) 945 | { 946 | private_t *tech_pvt; 947 | switch_channel_t *channel = NULL; 948 | 949 | channel = switch_core_session_get_channel(session); 950 | switch_assert(channel); 951 | 952 | tech_pvt = switch_core_session_get_private(session); 953 | switch_assert(tech_pvt); 954 | 955 | return SWITCH_STATUS_SUCCESS; 956 | } 957 | 958 | static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) 959 | { 960 | switch_channel_t *channel; 961 | private_t *tech_pvt; 962 | 963 | channel = switch_core_session_get_channel(session); 964 | switch_assert(channel); 965 | 966 | tech_pvt = (private_t *) switch_core_session_get_private(session); 967 | switch_assert(tech_pvt); 968 | 969 | switch (msg->message_id) { 970 | case SWITCH_MESSAGE_INDICATE_ANSWER: 971 | { 972 | channel_answer_channel(session); 973 | } 974 | break; 975 | default: 976 | break; 977 | } 978 | 979 | return SWITCH_STATUS_SUCCESS; 980 | } 981 | 982 | /* Make sure when you have 2 sessions in the same scope that you pass the appropriate one to the routines 983 | that allocate memory or you will have 1 channel with memory allocated from another channel's pool! 984 | */ 985 | static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, 986 | switch_caller_profile_t *outbound_profile, 987 | switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, 988 | switch_call_cause_t *cancel_cause) 989 | { 990 | char channelName[2048] = "janus/"; 991 | char *pServerName = NULL; 992 | char *pCurr = NULL; 993 | char *pNext = NULL; 994 | char *pDialStr = NULL; 995 | private_t *tech_pvt; 996 | switch_channel_t *channel; 997 | switch_caller_profile_t *caller_profile; 998 | switch_call_cause_t status = SWITCH_CAUSE_SUCCESS; 999 | server_t *pTmpServer, *pServer; 1000 | 1001 | // this check has been disabled due to the fact that FreeSWITCH crashes in some cases 1002 | // if (isVideoCall(session)) { 1003 | // switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Only implemented for audio calls\n"); 1004 | // return SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL; 1005 | // } 1006 | 1007 | *new_session = switch_core_session_request(janus_endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, flags, pool); 1008 | 1009 | if (!*new_session) { 1010 | return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; 1011 | } 1012 | 1013 | if (!outbound_profile) { 1014 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Doh! no caller profile\n"); 1015 | status = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; 1016 | goto error; 1017 | } 1018 | 1019 | channel = switch_core_session_get_channel(*new_session); 1020 | switch_assert(channel); 1021 | 1022 | (void) strncat(channelName, outbound_profile->destination_number, sizeof(channelName) - 7); 1023 | switch_channel_set_name(channel, channelName); 1024 | 1025 | pDialStr = switch_safe_strdup(outbound_profile->destination_number); 1026 | 1027 | if (pDialStr == NULL) { 1028 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot allocate memory for dialstring\n"); 1029 | status = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; 1030 | goto error; 1031 | } 1032 | 1033 | pCurr = pDialStr; 1034 | pNext = strchr(pCurr, '/'); 1035 | if (pNext == NULL) { 1036 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid dialstring format=%s\n", pDialStr); 1037 | status = SWITCH_CAUSE_PROTOCOL_ERROR; 1038 | goto error; 1039 | } 1040 | *pNext ++ = '\0'; 1041 | pServerName = pDialStr; 1042 | 1043 | if (!(pTmpServer = serversFind(pServerName))) { 1044 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Unknown server=%s\n", pServerName); 1045 | status = SWITCH_CAUSE_NO_ROUTE_DESTINATION; 1046 | goto error; 1047 | } 1048 | DEBUG(SWITCH_CHANNEL_SESSION_LOG(session), "Found serverId=%" SWITCH_UINT64_T_FMT "\n", pTmpServer->serverId); 1049 | 1050 | // check that the server is available via the serverIdLookup map - ie. that it is active 1051 | if (!(pServer = (server_t *) hashFind(&globals.serverIdLookup, pTmpServer->serverId))) { 1052 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "No server for serverId=%" SWITCH_UINT64_T_FMT "\n", pTmpServer->serverId); 1053 | status = SWITCH_CAUSE_NO_ROUTE_DESTINATION; 1054 | goto error; 1055 | } 1056 | 1057 | if (!switch_test_flag(pServer, SFLAG_ENABLED)) { 1058 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Server=%s has been disabled\n", pServer->name); 1059 | return SWITCH_CAUSE_OUTGOING_CALL_BARRED; 1060 | } 1061 | 1062 | switch_core_session_add_stream(*new_session, NULL); 1063 | if (!(tech_pvt = (private_t *) switch_core_session_alloc(*new_session, sizeof(private_t)))) { 1064 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); 1065 | status = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; 1066 | goto error; 1067 | } 1068 | 1069 | tech_pvt->read_frame.data = tech_pvt->databuf; 1070 | tech_pvt->read_frame.buflen = sizeof(tech_pvt->databuf); 1071 | switch_mutex_init(&tech_pvt->mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session)); 1072 | switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(*new_session)); 1073 | switch_core_session_set_private(*new_session, tech_pvt); 1074 | 1075 | switch_media_handle_create(&tech_pvt->smh, *new_session, &tech_pvt->mparams); 1076 | 1077 | //tech_pvt->mparams.codec_string = switch_core_session_strdup(*new_session, pServer->codec_string); 1078 | 1079 | if (!zstr(pServer->rtpip)) { 1080 | tech_pvt->mparams.rtpip4 = switch_core_session_strdup(*new_session, pServer->rtpip); 1081 | tech_pvt->mparams.rtpip = tech_pvt->mparams.rtpip4; 1082 | } 1083 | 1084 | if (!zstr(pServer->rtpip6)) { 1085 | tech_pvt->mparams.rtpip6 = switch_core_session_strdup(*new_session, pServer->rtpip6); 1086 | } 1087 | 1088 | tech_pvt->mparams.local_network = pServer->local_network; 1089 | tech_pvt->mparams.extrtpip = pServer->extrtpip; 1090 | 1091 | tech_pvt->serverId = pServer->serverId; 1092 | 1093 | pCurr = pNext; 1094 | pNext = strchr(pCurr, '@'); 1095 | if (pNext == NULL) { 1096 | switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid dialstring format=%s\n", pDialStr); 1097 | status = SWITCH_CAUSE_PROTOCOL_ERROR; 1098 | goto error; 1099 | } 1100 | *pNext ++ = '\0'; 1101 | 1102 | tech_pvt->pDisplay = switch_core_session_strdup(*new_session, pCurr); 1103 | tech_pvt->roomId = strtoull(pNext, NULL, 10); 1104 | 1105 | caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); 1106 | switch_channel_set_caller_profile(channel, caller_profile); 1107 | tech_pvt->caller_profile = caller_profile; 1108 | 1109 | switch_set_flag_locked(tech_pvt, TFLAG_OUTBOUND); 1110 | 1111 | switch_channel_set_state(channel, CS_INIT); 1112 | 1113 | switch_safe_free(pDialStr); 1114 | return status; 1115 | 1116 | error: 1117 | switch_safe_free(pDialStr); 1118 | switch_core_session_destroy(new_session); 1119 | return status; 1120 | } 1121 | 1122 | static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event) 1123 | { 1124 | struct private_object *tech_pvt = switch_core_session_get_private(session); 1125 | char *body = switch_event_get_body(event); 1126 | switch_assert(tech_pvt); 1127 | 1128 | if (!body) { 1129 | body = ""; 1130 | } 1131 | 1132 | return SWITCH_STATUS_SUCCESS; 1133 | } 1134 | 1135 | switch_state_handler_table_t janus_state_handlers = { 1136 | /*.on_init */ channel_on_init, 1137 | /*.on_routing */ channel_on_routing, 1138 | /*.on_execute */ channel_on_execute, 1139 | /*.on_hangup */ channel_on_hangup, 1140 | /*.on_exchange_media */ channel_on_exchange_media, 1141 | /*.on_soft_execute */ channel_on_soft_execute, 1142 | /*.on_consume_media */ NULL, 1143 | /*.on_hibernate */ NULL, 1144 | /*.on_reset */ NULL, 1145 | /*.on_park */ NULL, 1146 | /*.on_reporting */ NULL, 1147 | /*.on_destroy */ channel_on_destroy 1148 | }; 1149 | 1150 | switch_io_routines_t janus_io_routines = { 1151 | /*.outgoing_channel */ channel_outgoing_channel, 1152 | /*.read_frame */ channel_read_frame, 1153 | /*.write_frame */ channel_write_frame, 1154 | /*.kill_channel */ channel_kill_channel, 1155 | /*.send_dtmf */ channel_send_dtmf, 1156 | /*.receive_message */ channel_receive_message, 1157 | /*.receive_event */ channel_receive_event 1158 | }; 1159 | 1160 | static switch_status_t load_config(void) 1161 | { 1162 | char *cf = "janus.conf"; 1163 | switch_xml_t cfg, xml, settings, param, xmlint; 1164 | 1165 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Janus - loading config\n"); 1166 | 1167 | if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { 1168 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf); 1169 | return SWITCH_STATUS_TERM; 1170 | } 1171 | 1172 | if ((settings = switch_xml_child(cfg, "settings"))) { 1173 | for (param = switch_xml_child(settings, "param"); param; param = param->next) { 1174 | char *pVarStr = (char *) switch_xml_attr_soft(param, "name"); 1175 | char *pValStr = (char *) switch_xml_attr_soft(param, "value"); 1176 | DEBUG(SWITCH_CHANNEL_LOG, "Config %s->%s\n", pVarStr, pValStr); 1177 | 1178 | if (!strcmp(pVarStr, "debug")) { 1179 | globals.debug = switch_true(pValStr); 1180 | } 1181 | } 1182 | } 1183 | 1184 | xmlint = switch_xml_child(cfg, "server"); 1185 | if (!xmlint) { 1186 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No servers defined\n"); 1187 | return SWITCH_STATUS_TERM; 1188 | } else { 1189 | for (; xmlint; xmlint = xmlint->next) { 1190 | (void) serversAdd(xmlint); 1191 | } 1192 | } 1193 | 1194 | switch_xml_free(xml); 1195 | switch_xml_free(xmlint); 1196 | 1197 | return SWITCH_STATUS_SUCCESS; 1198 | } 1199 | 1200 | 1201 | 1202 | SWITCH_MODULE_LOAD_FUNCTION(mod_janus_load) 1203 | { 1204 | switch_api_interface_t *api_interface; 1205 | switch_hash_index_t *pIndex = NULL; 1206 | server_t *pServer; 1207 | 1208 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Janus - Loading\n"); 1209 | 1210 | switch_curl_init(); 1211 | 1212 | memset(&globals, 0, sizeof(globals)); 1213 | globals.pModulePool = pool; 1214 | globals.auto_nat = (switch_nat_get_type() ? SWITCH_TRUE : SWITCH_FALSE); 1215 | switch_core_hash_init(&globals.pServerNameLookup); 1216 | 1217 | switch_find_local_ip(globals.guess_ip, sizeof(globals.guess_ip), NULL, AF_INET); 1218 | 1219 | switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pModulePool); 1220 | // default values 1221 | globals.debug = SWITCH_FALSE; 1222 | 1223 | 1224 | load_config(); 1225 | 1226 | *module_interface = switch_loadable_module_create_module_interface(pool, modname); 1227 | janus_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE); 1228 | janus_endpoint_interface->interface_name = "janus"; 1229 | janus_endpoint_interface->io_routines = &janus_io_routines; 1230 | janus_endpoint_interface->state_handler = &janus_state_handlers; 1231 | 1232 | 1233 | SWITCH_ADD_API(api_interface, "janus", "Janus Menu", janus_api_commands, JANUS_SYNTAX); 1234 | 1235 | switch_console_set_complete("add janus debug ::[true:false"); 1236 | switch_console_set_complete("add janus status"); 1237 | switch_console_set_complete("add janus list"); 1238 | switch_console_set_complete("add janus server ::janus::listServers enable"); 1239 | switch_console_set_complete("add janus server ::janus::listServers disable"); 1240 | switch_console_add_complete_func("::janus::listServers", serversList); 1241 | 1242 | globals.started = switch_time_now(); 1243 | 1244 | if (hashCreate(&globals.serverIdLookup, globals.pModulePool) != SWITCH_STATUS_SUCCESS) { 1245 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot create serverId lookup\n"); 1246 | return SWITCH_STATUS_FALSE; 1247 | } 1248 | 1249 | while ((pServer = serversIterate(&pIndex))) { 1250 | if (switch_test_flag(pServer, SFLAG_ENABLED)) { 1251 | startServerThread(pServer, SWITCH_TRUE); 1252 | } 1253 | } 1254 | 1255 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Janus - Loaded\n"); 1256 | 1257 | /* indicate that the module should continue to be loaded */ 1258 | return SWITCH_STATUS_SUCCESS; 1259 | } 1260 | 1261 | /* 1262 | SWITCH_MODULE_RUNTIME_FUNCTION(mod_janus_runtime) 1263 | { 1264 | return SWITCH_STATUS_TERM; 1265 | } 1266 | */ 1267 | 1268 | SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_janus_shutdown) 1269 | { 1270 | switch_hash_index_t *pIndex = NULL; 1271 | server_t *pServer; 1272 | 1273 | while ((pServer = serversIterate(&pIndex)) != NULL) { 1274 | stopServerThread(pServer); 1275 | } 1276 | 1277 | (void) hashDestroy(&globals.serverIdLookup); 1278 | 1279 | (void) serversDestroy(); 1280 | 1281 | switch_curl_destroy(); 1282 | 1283 | return SWITCH_STATUS_SUCCESS; 1284 | } 1285 | 1286 | SWITCH_STANDARD_API(janus_api_commands) 1287 | { 1288 | char *argv[10] = { 0 }; 1289 | int argc = 0; 1290 | char *myarg = NULL; 1291 | switch_status_t status = SWITCH_STATUS_SUCCESS; 1292 | 1293 | if (session) { 1294 | // do nothing 1295 | } else if (zstr(cmd) || !(myarg = strdup(cmd))) { 1296 | stream->write_function(stream, "USAGE %s\n", JANUS_SYNTAX); 1297 | status = SWITCH_STATUS_FALSE; 1298 | } else if ((argc = switch_separate_string(myarg, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) < 1) { 1299 | stream->write_function(stream, "USAGE %s\n", JANUS_SYNTAX); 1300 | status = SWITCH_STATUS_FALSE; 1301 | } else if (argv[0] && !strncasecmp(argv[0], "status", 6)) { 1302 | stream->write_function(stream, "totalCalls|callsInProgress|started\n"); 1303 | stream->write_function(stream, "%u|%u|%lld\n", globals.totalCalls, globals.callsInProgress, globals.started); 1304 | } else if (argv[0] && !strncasecmp(argv[0], "debug", 5)) { 1305 | if ((argc >= 2) && argv[1]) { 1306 | globals.debug = switch_true(argv[1]); 1307 | stream->write_function(stream, "OK\n"); 1308 | } else { 1309 | stream->write_function(stream, "USAGE %s\n", JANUS_DEBUG_SYNTAX); 1310 | } 1311 | } else if (argv[0] && !strncasecmp(argv[0], "list", 6)) { 1312 | serversSummary(stream); 1313 | } else if (argv[0] && !strncasecmp(argv[0], "server", 7)) { 1314 | if (argc >= 3 && argv[1] && argv[2]) { 1315 | server_t *pServer = serversFind(argv[1]); 1316 | if (pServer == NULL) { 1317 | stream->write_function(stream, "ERR Unknown server [%s]\n", argv[1]); 1318 | } else if (!strncasecmp(argv[2], "enable", 6)) { 1319 | startServerThread(pServer, FALSE); 1320 | stream->write_function(stream, "OK\n"); 1321 | } else if (!strncasecmp(argv[2], "disable", 7)) { 1322 | stopServerThread(pServer); 1323 | stream->write_function(stream, "OK\n"); 1324 | } else { 1325 | stream->write_function(stream, "USAGE %s\n", JANUS_GATEWAY_SYNTAX); 1326 | } 1327 | } else { 1328 | stream->write_function(stream, "USAGE %s\n", JANUS_GATEWAY_SYNTAX); 1329 | } 1330 | } else { 1331 | stream->write_function(stream, "USAGE %s\n", JANUS_SYNTAX); 1332 | } 1333 | 1334 | switch_safe_free(myarg); 1335 | return status; 1336 | } 1337 | /* For Emacs: 1338 | * Local Variables: 1339 | * mode:c 1340 | * indent-tabs-mode:t 1341 | * tab-width:4 1342 | * c-basic-offset:4 1343 | * End: 1344 | * For VIM: 1345 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 1346 | */ 1347 | -------------------------------------------------------------------------------- /servers.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * servers.c -- Server list functions for janus endpoint module 31 | * 32 | */ 33 | #include "globals.h" 34 | #include "servers.h" 35 | 36 | switch_status_t serversList(const char *pLine, const char *pCursor, switch_console_callback_match_t **matches) { 37 | switch_hash_index_t *pIndex = NULL; 38 | server_t *pServer; 39 | switch_console_callback_match_t *myMatches = NULL; 40 | 41 | while ((pServer = serversIterate(&pIndex)) != NULL) { 42 | switch_console_push_match(&myMatches, pServer->name); 43 | } 44 | 45 | if (myMatches) { 46 | *matches = myMatches; 47 | } 48 | 49 | return SWITCH_STATUS_SUCCESS; 50 | } 51 | 52 | switch_status_t serversAdd(switch_xml_t xmlint) { 53 | switch_xml_t param; 54 | char *pName = (char *) switch_xml_attr_soft(xmlint, "name"); 55 | server_t *pServer; 56 | 57 | switch_assert(globals.pServerNameLookup); 58 | 59 | if (!pName) { 60 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Server has no name\n"); 61 | return SWITCH_STATUS_FALSE; 62 | } 63 | 64 | if (serversFind(pName) != NULL) { 65 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Server=%s already defined\n", pName); 66 | return SWITCH_STATUS_FALSE; 67 | } 68 | 69 | DEBUG(SWITCH_CHANNEL_LOG, "Defining server=%s\n", pName); 70 | 71 | pServer = switch_core_alloc(globals.pModulePool, sizeof(*pServer)); 72 | 73 | switch_mutex_init(&pServer->mutex, SWITCH_MUTEX_NESTED, globals.pModulePool); 74 | switch_mutex_init(&pServer->flag_mutex, SWITCH_MUTEX_NESTED, globals.pModulePool); 75 | 76 | pServer->serverId = 0; 77 | pServer->totalCalls = 0; 78 | pServer->callsInProgress = 0; 79 | pServer->pThread = NULL; 80 | 81 | // set default values 82 | pServer->name = switch_core_strdup(globals.pModulePool, pName); 83 | pServer->codec_string = "opus"; 84 | pServer->local_network = "localnet.auto"; 85 | 86 | for (param = switch_xml_child(xmlint, "param"); param; param = param->next) { 87 | char *pVarStr = (char *) switch_xml_attr_soft(param, "name"); 88 | char *pValStr = (char *) switch_xml_attr_soft(param, "value"); 89 | DEBUG(SWITCH_CHANNEL_LOG, "Server=%s %s->%s\n", pName, pVarStr, pValStr); 90 | 91 | if (!strcmp(pVarStr, "url") && !zstr(pVarStr)) { 92 | pServer->pUrl = switch_core_strdup(globals.pModulePool, pValStr); 93 | } else if (!strcmp(pVarStr, "secret") && !zstr(pValStr)) { 94 | pServer->pSecret = switch_core_strdup(globals.pModulePool, pValStr); 95 | } else if (!strcmp(pVarStr, "auth-token") && !zstr(pValStr)) { 96 | pServer->pAuthToken = switch_core_strdup(globals.pModulePool, pValStr); 97 | } else if (!strcmp(pVarStr, "local-network-acl") && !zstr(pValStr)) { 98 | if (strcasecmp(pValStr, "none")) { 99 | pServer->local_network = switch_core_strdup(globals.pModulePool, pValStr); 100 | } 101 | } else if (!strcmp(pVarStr, "ext-rtp-ip") && !zstr(pValStr)) { 102 | char *ip = globals.guess_ip; 103 | 104 | if (!strcmp(pValStr, "0.0.0.0")) { 105 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", 106 | globals.guess_ip); 107 | } else if (!strcasecmp(pValStr, "auto-nat")) { 108 | ip = NULL; 109 | } else { 110 | ip = strcasecmp(pValStr, "auto") ? pValStr : globals.guess_ip; 111 | switch_clear_flag(pServer, SFLAG_AUTO_NAT); 112 | } 113 | if (ip) { 114 | if (!strncasecmp(ip, "autonat:", 8)) { 115 | pServer->extrtpip = switch_core_strdup(globals.pModulePool, ip + 8); 116 | switch_set_flag(pServer, SFLAG_AUTO_NAT); 117 | } else { 118 | pServer->extrtpip = switch_core_strdup(globals.pModulePool, ip); 119 | } 120 | } 121 | } else if (!strcmp(pVarStr, "rtp-ip")) { 122 | char *ip = globals.guess_ip; 123 | char buf[64]; 124 | 125 | if (zstr(pValStr)) { 126 | ip = globals.guess_ip; 127 | } else if (!strcmp(pValStr, "0.0.0.0")) { 128 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid IP 0.0.0.0 replaced with %s\n", globals.guess_ip); 129 | } else if (!strncasecmp(pValStr, "interface:", 10)) { 130 | char *ifname = pValStr + 10; 131 | int family = AF_UNSPEC; 132 | if (!strncasecmp(ifname, "auto/", 5)) { ifname += 5; family = AF_UNSPEC; } 133 | if (!strncasecmp(ifname, "ipv4/", 5)) { ifname += 5; family = AF_INET; } 134 | if (!strncasecmp(ifname, "ipv6/", 5)) { ifname += 5; family = AF_INET6; } 135 | if (switch_find_interface_ip(buf, sizeof(buf), NULL, ifname, family) == SWITCH_STATUS_SUCCESS) { 136 | ip = buf; 137 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Using %s IP for interface %s for rtp-ip\n", ip, pValStr + 10); 138 | } else { 139 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown IP for interface %s for rtp-ip\n", pValStr + 10); 140 | } 141 | } else { 142 | ip = strcasecmp(pValStr, "auto") ? pValStr : globals.guess_ip; 143 | } 144 | 145 | if (strchr(ip, ':')) { 146 | pServer->rtpip6 = switch_core_strdup(globals.pModulePool, ip); 147 | } else { 148 | pServer->rtpip = switch_core_strdup(globals.pModulePool, ip); 149 | } 150 | } else if (!strcasecmp(pVarStr, "apply-candidate-acl") && !zstr(pValStr)) { 151 | if (!strcasecmp(pValStr, "none")) { 152 | pServer->cand_acl_count = 0; 153 | } else if (pServer->cand_acl_count < SWITCH_MAX_CAND_ACL) { 154 | pServer->cand_acl[pServer->cand_acl_count++] = switch_core_strdup(globals.pModulePool, pValStr); 155 | } else { 156 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Max acl records of %d reached\n", SWITCH_MAX_CAND_ACL); 157 | } 158 | } else if (!strcasecmp(pVarStr, "codec-string") && !zstr(pValStr)) { 159 | pServer->codec_string = switch_core_strdup(globals.pModulePool, pValStr); 160 | } else if (!strcmp(pVarStr, "enabled") && !zstr(pValStr)) { 161 | // set the flag to the opposite state so that we will do the right thine 162 | if (switch_true(pValStr)) { 163 | switch_set_flag(pServer, SFLAG_ENABLED); 164 | } 165 | } 166 | } 167 | 168 | if (!pServer->pUrl) { 169 | switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Server=%s Mandatory parameter not specified\n", pName); 170 | return SWITCH_STATUS_FALSE; 171 | } 172 | 173 | switch_core_hash_insert(globals.pServerNameLookup, pName, pServer); 174 | 175 | return SWITCH_STATUS_SUCCESS; 176 | } 177 | 178 | switch_status_t serversSummary(switch_stream_handle_t *pStream) { 179 | switch_hash_index_t *pIndex = NULL; 180 | server_t *pServer; 181 | char text[128]; 182 | 183 | switch_assert(globals.pServerNameLookup); 184 | 185 | pStream->write_function(pStream, "name|enabled|totalCalls|callsInProgress|started|id\n"); 186 | while ((pServer = serversIterate(&pIndex)) != NULL) { 187 | switch_mutex_lock(pServer->mutex); 188 | (void) snprintf(text, sizeof(text), "%s|%s|%u|%u|%" SWITCH_INT64_T_FMT "|%" SWITCH_UINT64_T_FMT "\n", pServer->name, 189 | switch_test_flag(pServer, SFLAG_ENABLED) ? "true" : "false", pServer->totalCalls, 190 | pServer->callsInProgress, pServer->started, pServer->serverId); 191 | switch_mutex_unlock(pServer->mutex); 192 | 193 | pStream->write_function(pStream, text); 194 | 195 | } 196 | return SWITCH_STATUS_SUCCESS; 197 | } 198 | 199 | server_t *serversFind(const char * const pName) { 200 | switch_assert(globals.pServerNameLookup); 201 | 202 | return switch_core_hash_find(globals.pServerNameLookup, pName); 203 | } 204 | 205 | server_t *serversIterate(switch_hash_index_t **pIndex) { 206 | void *val; 207 | const void *vvar; 208 | 209 | switch_assert(globals.pServerNameLookup); 210 | 211 | if (!*pIndex) { 212 | *pIndex = switch_core_hash_first(globals.pServerNameLookup); 213 | } else { 214 | *pIndex = switch_core_hash_next(pIndex); 215 | } 216 | 217 | if (*pIndex) { 218 | switch_core_hash_this(*pIndex, &vvar, NULL, &val); 219 | return (server_t *) val; 220 | } else { 221 | return NULL; 222 | } 223 | } 224 | 225 | switch_status_t serversDestroy() { 226 | return switch_core_hash_destroy(&globals.pServerNameLookup); 227 | } 228 | /* For Emacs: 229 | * Local Variables: 230 | * mode:c 231 | * indent-tabs-mode:t 232 | * tab-width:4 233 | * c-basic-offset:4 234 | * End: 235 | * For VIM: 236 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 237 | */ 238 | -------------------------------------------------------------------------------- /servers.h: -------------------------------------------------------------------------------- 1 | /* 2 | * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 3 | * Copyright (C) 2005-2014, Anthony Minessale II 4 | * 5 | * Version: MPL 1.1 6 | * 7 | * The contents of this file are subject to the Mozilla Public License Version 8 | * 1.1 (the "License"); you may not use this file except in compliance with 9 | * the License. You may obtain a copy of the License at 10 | * http://www.mozilla.org/MPL/ 11 | * 12 | * Software distributed under the License is distributed on an "AS IS" basis, 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 14 | * for the specific language governing rights and limitations under the 15 | * License. 16 | * 17 | * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application 18 | * 19 | * The Initial Developer of the Original Code is 20 | * Anthony Minessale II 21 | * Portions created by the Initial Developer are Copyright (C) 22 | * the Initial Developer. All Rights Reserved. 23 | * 24 | * Contributor(s): 25 | * 26 | * Anthony Minessale II 27 | * Richard Screene 28 | * 29 | * 30 | * servers.h -- Server list headers for janus endpoint module 31 | * 32 | */ 33 | #ifndef _SERVERS_H_ 34 | #define _SERVERS_H_ 35 | 36 | #include "switch.h" 37 | #include "hash.h" 38 | 39 | typedef enum { 40 | SFLAG_ENABLED = (1 << 0), 41 | SFLAG_TERMINATING = (1 << 1), 42 | SFLAG_AUTO_NAT = (1 << 2) 43 | } SFLAGS; 44 | 45 | typedef struct { 46 | char *name; 47 | char *pUrl; 48 | char *pSecret; 49 | char *pAuthToken; 50 | switch_thread_t *pThread; 51 | 52 | char *local_network; 53 | char *extrtpip; 54 | 55 | char *cand_acl[SWITCH_MAX_CAND_ACL]; 56 | uint32_t cand_acl_count; 57 | 58 | char *rtpip; 59 | char *rtpip6; 60 | char *codec_string; 61 | 62 | switch_mutex_t *flag_mutex; 63 | unsigned int flags; 64 | 65 | switch_mutex_t *mutex; 66 | 67 | hash_t senderIdLookup; 68 | 69 | // values that may be changed go under here - the static values 70 | // above are only modified during initialisation and so we 71 | // not mutexed 72 | 73 | janus_id_t serverId; 74 | switch_time_t started; 75 | unsigned int totalCalls; 76 | unsigned int callsInProgress; 77 | } server_t; 78 | 79 | switch_status_t serversList(const char *pLine, const char *pCursor, switch_console_callback_match_t **matches); 80 | switch_status_t serversAdd(switch_xml_t xmlint); 81 | switch_status_t serversSummary(switch_stream_handle_t *pStream); 82 | server_t *serversFind(const char * const pName); 83 | server_t *serversIterate(switch_hash_index_t **pIndex); 84 | switch_status_t serversDestroy(); 85 | 86 | #endif //_SERVERS_H_ 87 | /* For Emacs: 88 | * Local Variables: 89 | * mode:c 90 | * indent-tabs-mode:t 91 | * tab-width:4 92 | * c-basic-offset:4 93 | * End: 94 | * For VIM: 95 | * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: 96 | */ 97 | --------------------------------------------------------------------------------