From 41d9cf2989c2817eb495fb0b10fb5cc817733bfa Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Sat, 31 Mar 2007 19:01:33 +0000 Subject: [PATCH] breaking up is hard to do git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@4819 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/endpoints/mod_sofia/Makefile | 1 + src/mod/endpoints/mod_sofia/mod_sofia.c | 4922 +----------------- src/mod/endpoints/mod_sofia/mod_sofia.h | 406 ++ src/mod/endpoints/mod_sofia/mod_sofia.vcproj | 11 +- src/mod/endpoints/mod_sofia/sofia.c | 1753 +++++++ src/mod/endpoints/mod_sofia/sofia_glue.c | 1110 ++++ src/mod/endpoints/mod_sofia/sofia_presence.c | 909 ++++ src/mod/endpoints/mod_sofia/sofia_reg.c | 790 +++ 8 files changed, 5021 insertions(+), 4881 deletions(-) create mode 100644 src/mod/endpoints/mod_sofia/mod_sofia.h create mode 100644 src/mod/endpoints/mod_sofia/sofia.c create mode 100644 src/mod/endpoints/mod_sofia/sofia_glue.c create mode 100644 src/mod/endpoints/mod_sofia/sofia_presence.c create mode 100644 src/mod/endpoints/mod_sofia/sofia_reg.c diff --git a/src/mod/endpoints/mod_sofia/Makefile b/src/mod/endpoints/mod_sofia/Makefile index 233d726a4f..676522a8b3 100644 --- a/src/mod/endpoints/mod_sofia/Makefile +++ b/src/mod/endpoints/mod_sofia/Makefile @@ -12,6 +12,7 @@ LOCAL_CFLAGS += -I$(SOFIAUA_DIR)/sdp -I$(SOFIAUA_DIR)/sip LOCAL_CFLAGS += -I$(SOFIAUA_DIR)/soa -I$(SOFIAUA_DIR)/sresolv LOCAL_CFLAGS += -I$(SOFIAUA_DIR)/stun -I$(SOFIAUA_DIR)/su LOCAL_CFLAGS += -I$(SOFIAUA_DIR)/tport -I$(SOFIAUA_DIR)/url +LOCAL_OBJS=sofia.o sofia_glue.o sofia_presence.o sofia_reg.o SOFIALA=$(SOFIAUA_DIR)/libsofia-sip-ua.la diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 853f90eaa7..f707cb73e7 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -36,1199 +36,34 @@ /* Best viewed in a 160 x 60 VT100 Terminal or so the line below at least fits across your screen*/ /*************************************************************************************************************************************************************/ +#include "mod_sofia.h" -/*Defines etc..*/ -/*************************************************************************************************************************************************************/ -#define HAVE_APR -#include -static const switch_state_handler_table_t noop_state_handler = { 0 }; -struct outbound_reg; -typedef struct outbound_reg outbound_reg_t; - -struct sofia_profile; -typedef struct sofia_profile sofia_profile_t; -#define NUA_MAGIC_T sofia_profile_t - -struct sofia_private { - char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1]; - outbound_reg_t *gateway; -}; - -typedef struct sofia_private sofia_private_t; - -struct private_object; -typedef struct private_object private_object_t; -#define NUA_HMAGIC_T sofia_private_t - -#define MY_EVENT_REGISTER "sofia::register" -#define MY_EVENT_EXPIRE "sofia::expire" -#define MULTICAST_EVENT "multicast::event" -#define SOFIA_REPLACES_HEADER "_sofia_replaces_" -#define SOFIA_USER_AGENT "FreeSWITCH(mod_sofia)" -#define SOFIA_CHAT_PROTO "sip" -#define SOFIA_SIP_HEADER_PREFIX "sip_h_" -#define SOFIA_SIP_HEADER_PREFIX_T "~sip_h_" -#define SOFIA_DEFAULT_PORT "5060" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern su_log_t tport_log[]; +struct sofia_globals globals; static switch_frame_t silence_frame = { 0 }; static char silence_data[13] = ""; -static char reg_sql[] = - "CREATE TABLE sip_registrations (\n" - " user VARCHAR(255),\n" - " host VARCHAR(255),\n" - " contact VARCHAR(1024),\n" - " status VARCHAR(255),\n" - " rpid VARCHAR(255),\n" - " expires INTEGER(8)" ");\n"; - - -static char sub_sql[] = - "CREATE TABLE sip_subscriptions (\n" - " proto VARCHAR(255),\n" - " user VARCHAR(255),\n" - " host VARCHAR(255),\n" - " sub_to_user VARCHAR(255),\n" - " sub_to_host VARCHAR(255),\n" - " event VARCHAR(255),\n" - " contact VARCHAR(1024),\n" - " call_id VARCHAR(255),\n" - " full_from VARCHAR(255),\n" - " full_via VARCHAR(255),\n" - " expires INTEGER(8)" ");\n"; - - -static char auth_sql[] = - "CREATE TABLE sip_authentication (\n" - " user VARCHAR(255),\n" - " host VARCHAR(255),\n" - " passwd VARCHAR(255),\n" - " nonce VARCHAR(255),\n" - " expires INTEGER(8)" - ");\n"; - -static const char modname[] = "mod_sofia"; #define STRLEN 15 static switch_memory_pool_t *module_pool = NULL; -#define set_param(ptr,val) if (ptr) {free(ptr) ; ptr = NULL;} if (val) {ptr = strdup(val);} -#define set_anchor(t,m) if (t->Anchor) {delete t->Anchor;} t->Anchor = new SipMessage(m); - - -/* Local Structures */ -/*************************************************************************************************************************************************************/ -struct sip_alias_node { - char *url; - nua_t *nua; - struct sip_alias_node *next; -}; - -typedef struct sip_alias_node sip_alias_node_t; - -typedef enum { - PFLAG_AUTH_CALLS = (1 << 0), - PFLAG_BLIND_REG = (1 << 1), - PFLAG_AUTH_ALL = (1 << 2), - PFLAG_FULL_ID = (1 << 3), - PFLAG_PRESENCE = (1 << 4), - PFLAG_PASS_RFC2833 = (1 << 5), - PFLAG_DISABLE_TRANSCODING = (1 << 6) -} PFLAGS; - -typedef enum { - TFLAG_IO = (1 << 0), - TFLAG_CHANGE_MEDIA = (1 << 1), - TFLAG_OUTBOUND = (1 << 2), - TFLAG_READING = (1 << 3), - TFLAG_WRITING = (1 << 4), - TFLAG_HUP = (1 << 5), - TFLAG_RTP = (1 << 6), - TFLAG_BYE = (1 << 7), - TFLAG_ANS = (1 << 8), - TFLAG_EARLY_MEDIA = (1 << 9), - TFLAG_SECURE = (1 << 10), - TFLAG_VAD_IN = (1 << 11), - TFLAG_VAD_OUT = (1 << 12), - TFLAG_VAD = (1 << 13), - TFLAG_TIMER = (1 << 14), - TFLAG_READY = (1 << 15), - TFLAG_REINVITE = (1 << 16), - TFLAG_REFER = (1 << 17), - TFLAG_NOHUP = (1 << 18), - TFLAG_XFER = (1 << 19), - TFLAG_NOMEDIA = (1 << 20), - TFLAG_BUGGY_2833 = (1 << 21), - TFLAG_SIP_HOLD = (1 << 22), - TFLAG_INB_NOMEDIA = (1 << 23), - TFLAG_LATE_NEGOTIATION = (1 << 24) -} TFLAGS; - -static struct { - switch_hash_t *profile_hash; - switch_hash_t *gateway_hash; - switch_mutex_t *hash_mutex; - uint32_t callid; - int32_t running; - switch_mutex_t *mutex; - char guess_ip[80]; -} globals; - -typedef enum { - REG_FLAG_AUTHED = (1 << 0), -} reg_flags_t; - -typedef enum { - REG_STATE_UNREGED, - REG_STATE_TRYING, - REG_STATE_REGISTER, - REG_STATE_REGED, - REG_STATE_FAILED, - REG_STATE_EXPIRED -} reg_state_t; - -struct outbound_reg { - sofia_private_t *sofia_private; - nua_handle_t *nh; - sofia_profile_t *profile; - char *name; - char *register_scheme; - char *register_realm; - char *register_username; - char *register_password; - char *register_from; - char *register_contact; - char *register_to; - char *register_proxy; - char *register_context; - char *expires_str; - uint32_t freq; - time_t expires; - time_t retry; - uint32_t flags; - reg_state_t state; - switch_memory_pool_t *pool; - struct outbound_reg *next; -}; - - -struct sofia_profile { - int debug; - char *name; - char *dbname; - char *dialplan; - char *context; - char *extrtpip; - char *rtpip; - char *sipip; - char *extsipip; - char *username; - char *url; - char *bindurl; - char *sipdomain; - char *timer_name; - char *hold_music; - int sip_port; - char *codec_string; - int running; - int codec_ms; - int dtmf_duration; - unsigned int flags; - unsigned int pflags; - uint32_t max_calls; - uint32_t nonce_ttl; - nua_t *nua; - switch_memory_pool_t *pool; - su_root_t *s_root; - sip_alias_node_t *aliases; - switch_payload_t te; - switch_payload_t cng_pt; - uint32_t codec_flags; - switch_mutex_t *ireg_mutex; - switch_mutex_t *gateway_mutex; - outbound_reg_t *gateways; - su_home_t *home; - switch_hash_t *profile_hash; - switch_hash_t *chat_hash; -}; - - -struct private_object { - sofia_private_t *sofia_private; - uint32_t flags; - switch_payload_t agreed_pt; - switch_core_session_t *session; - switch_frame_t read_frame; - char *codec_order[SWITCH_MAX_CODECS]; - int codec_order_last; - const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; - int num_codecs; - switch_codec_t read_codec; - switch_codec_t write_codec; - uint32_t codec_ms; - switch_caller_profile_t *caller_profile; - uint32_t timestamp_send; - //int32_t timestamp_recv; - switch_rtp_t *rtp_session; - int ssrc; - //switch_time_t last_read; - sofia_profile_t *profile; - char *local_sdp_audio_ip; - switch_port_t local_sdp_audio_port; - char *remote_sdp_audio_ip; - switch_port_t remote_sdp_audio_port; - char *adv_sdp_audio_ip; - switch_port_t adv_sdp_audio_port; - char *proxy_sdp_audio_ip; - switch_port_t proxy_sdp_audio_port; - char *from_uri; - char *to_uri; - char *from_address; - char *to_address; - char *callid; - char *far_end_contact; - char *contact_url; - char *from_str; - char *rm_encoding; - char *rm_fmtp; - char *fmtp_out; - char *remote_sdp_str; - char *local_sdp_str; - char *dest; - char *dest_to; - char *key; - char *xferto; - char *kick; - char *origin; - char *hash_key; - char *chat_from; - char *chat_to; - char *e_dest; - char *call_id; - unsigned long rm_rate; - switch_payload_t pt; - switch_mutex_t *flag_mutex; - switch_payload_t te; - switch_payload_t bte; - switch_payload_t cng_pt; - switch_payload_t bcng_pt; - nua_handle_t *nh; - nua_handle_t *nh2; - su_home_t *home; - sip_contact_t *contact; -}; - -/* Function Prototypes */ -/*************************************************************************************************************************************************************/ static switch_status_t sofia_on_init(switch_core_session_t *session); -static switch_status_t sofia_on_hangup(switch_core_session_t *session); static switch_status_t sofia_on_loopback(switch_core_session_t *session); - static switch_status_t sofia_on_transmit(switch_core_session_t *session); - static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t **pool); - static switch_status_t sofia_read_frame(switch_core_session_t *session, switch_frame_t **frame, int timeout, switch_io_flag_t flags, int stream_id); - static switch_status_t sofia_write_frame(switch_core_session_t *session, switch_frame_t *frame, int timeout, switch_io_flag_t flags, int stream_id); - -static switch_status_t config_sofia(int reload); - static switch_status_t sofia_kill_channel(switch_core_session_t *session, int sig); -static switch_status_t activate_rtp(private_object_t * tech_pvt); - -static void deactivate_rtp(private_object_t * tech_pvt); - -static void set_local_sdp(private_object_t * tech_pvt, char *ip, uint32_t port, char *sr, int force); - -static void tech_set_codecs(private_object_t * tech_pvt); - -static void attach_private(switch_core_session_t *session, sofia_profile_t * profile, private_object_t * tech_pvt, const char *channame); - -static void terminate_session(switch_core_session_t **session, switch_call_cause_t cause, int line); - -static switch_status_t tech_choose_port(private_object_t * tech_pvt); - -static switch_status_t do_invite(switch_core_session_t *session); - -static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t * sdp); - -static char *get_auth_data(char *dbname, char *nonce, char *npassword, size_t len, switch_mutex_t * mutex); - -static void establish_presence(sofia_profile_t * profile); - -static void sip_i_state(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); - - -static void sip_i_refer(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]); - -static void sip_i_info(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]); - -static void sip_i_invite(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); - -static void sip_i_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); - -static void event_callback(nua_event_t event, - int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); - - -static void *SWITCH_THREAD_FUNC profile_thread_run(switch_thread_t * thread, void *obj); - -static void launch_profile_thread(sofia_profile_t * profile); - -static switch_status_t chat_send(char *proto, char *from, char *to, char *subject, char *body, char *hint); /* BODY OF THE MODULE */ /*************************************************************************************************************************************************************/ -typedef enum { - AUTH_OK, - AUTH_FORBIDDEN, - AUTH_STALE, -} auth_res_t; - - -static sofia_profile_t *find_profile(char *key) -{ - static sofia_profile_t *profile; - - switch_mutex_lock(globals.hash_mutex); - profile = (sofia_profile_t *) switch_core_hash_find(globals.profile_hash, key); - switch_mutex_unlock(globals.hash_mutex); - - return profile; -} - -static void add_profile(char *key, sofia_profile_t * profile) -{ - switch_mutex_lock(globals.hash_mutex); - switch_core_hash_insert(globals.profile_hash, key, profile); - switch_mutex_unlock(globals.hash_mutex); -} - -static outbound_reg_t *find_gateway(char *key) -{ - outbound_reg_t *gateway; - - switch_mutex_lock(globals.hash_mutex); - gateway = (outbound_reg_t *) switch_core_hash_find(globals.gateway_hash, key); - switch_mutex_unlock(globals.hash_mutex); - - return gateway; -} - -static void add_gateway(char *key, outbound_reg_t * gateway) -{ - switch_mutex_lock(globals.hash_mutex); - switch_core_hash_insert(globals.gateway_hash, key, gateway); - switch_mutex_unlock(globals.hash_mutex); -} - -static char *get_url_from_contact(char *buf, uint8_t to_dup) -{ - char *url = NULL, *e; - - if ((url = strchr(buf, '<')) && (e = strchr(url, '>'))) { - url++; - if (to_dup) { - url = strdup(url); - e = strchr(url, '>'); - } - - *e = '\0'; - } - - return url; -} - - -static auth_res_t parse_auth(sofia_profile_t * profile, sip_authorization_t const *authorization, const char *regstr, char *np, size_t nplen) -{ - int indexnum; - const char *cur; - su_md5_t ctx; - char uridigest[2 * SU_MD5_DIGEST_SIZE + 1]; - char bigdigest[2 * SU_MD5_DIGEST_SIZE + 1]; - char *nonce, *uri, *qop, *cnonce, *nc, *response, *input = NULL, *input2 = NULL; - auth_res_t ret = AUTH_FORBIDDEN; - char *npassword = NULL; - int cnt = 0; - nonce = uri = qop = cnonce = nc = response = NULL; - - if (authorization->au_params) { - for (indexnum = 0; (cur = authorization->au_params[indexnum]); indexnum++) { - char *var, *val, *p, *work; - var = val = work = NULL; - if ((work = strdup(cur))) { - var = work; - if ((val = strchr(var, '='))) { - *val++ = '\0'; - while (*val == '"') { - *val++ = '\0'; - } - if ((p = strchr(val, '"'))) { - *p = '\0'; - } - - if (!strcasecmp(var, "nonce")) { - nonce = strdup(val); - cnt++; - } else if (!strcasecmp(var, "uri")) { - uri = strdup(val); - cnt++; - } else if (!strcasecmp(var, "qop")) { - qop = strdup(val); - cnt++; - } else if (!strcasecmp(var, "cnonce")) { - cnonce = strdup(val); - cnt++; - } else if (!strcasecmp(var, "response")) { - response = strdup(val); - cnt++; - } else if (!strcasecmp(var, "nc")) { - nc = strdup(val); - cnt++; - } - } - - free(work); - } - } - } - - if (cnt != 6) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Authorization header!\n"); - goto end; - } - - if (switch_strlen_zero(np)) { - if (!get_auth_data(profile->dbname, nonce, np, nplen, profile->ireg_mutex)) { - ret = AUTH_STALE; - goto end; - } - } - - npassword = np; - - if ((input = switch_mprintf("%s:%q", regstr, uri))) { - su_md5_init(&ctx); - su_md5_strupdate(&ctx, input); - su_md5_hexdigest(&ctx, uridigest); - su_md5_deinit(&ctx); - } - - if ((input2 = switch_mprintf("%q:%q:%q:%q:%q:%q", npassword, nonce, nc, cnonce, qop, uridigest))) { - memset(&ctx, 0, sizeof(ctx)); - su_md5_init(&ctx); - su_md5_strupdate(&ctx, input2); - su_md5_hexdigest(&ctx, bigdigest); - su_md5_deinit(&ctx); - - if (!strcasecmp(bigdigest, response)) { - ret = AUTH_OK; - } else { - ret = AUTH_FORBIDDEN; - } - } - - end: - switch_safe_free(input); - switch_safe_free(input2); - switch_safe_free(nonce); - switch_safe_free(uri); - switch_safe_free(qop); - switch_safe_free(cnonce); - switch_safe_free(nc); - switch_safe_free(response); - - return ret; - -} - - -static void execute_sql(char *dbname, char *sql, switch_mutex_t * mutex) -{ - switch_core_db_t *db; - - if (mutex) { - switch_mutex_lock(mutex); - } - - if (!(db = switch_core_db_open_file(dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", dbname); - goto end; - } - switch_core_db_persistant_execute(db, sql, 25); - switch_core_db_close(db); - - end: - if (mutex) { - switch_mutex_unlock(mutex); - } -} - - -struct callback_t { - char *val; - switch_size_t len; - int matches; -}; - -static int find_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - struct callback_t *cbt = (struct callback_t *) pArg; - - switch_copy_string(cbt->val, argv[0], cbt->len); - cbt->matches++; - return 0; -} - -static int del_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - switch_event_t *s_event; - - if (argc >= 3) { - if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_EXPIRE) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile-name", "%s", argv[0]); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "user", "%s", argv[1]); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "host", "%s", argv[2]); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "contact", "%s", argv[3]); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%s", argv[4]); - switch_event_fire(&s_event); - } - } - return 0; -} - -static void check_expire(switch_core_db_t *db, sofia_profile_t * profile, time_t now) -{ - char sql[1024]; - char *errmsg; - - if (!db) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - return; - } - - switch_mutex_lock(profile->ireg_mutex); - snprintf(sql, sizeof(sql), "select '%s',* from sip_registrations where expires > 0 and expires < %ld", profile->name, (long) now); - switch_core_db_exec(db, sql, del_callback, NULL, &errmsg); - - if (errmsg) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s][%s]\n", sql, errmsg); - switch_safe_free(errmsg); - errmsg = NULL; - } - - snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and expires < %ld", (long) now); - switch_core_db_persistant_execute(db, sql, 1000); - snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and expires < %ld", (long) now); - switch_core_db_persistant_execute(db, sql, 1000); - snprintf(sql, sizeof(sql), "delete from sip_subscriptions where expires > 0 and expires < %ld", (long) now); - switch_core_db_persistant_execute(db, sql, 1000); - - switch_mutex_unlock(profile->ireg_mutex); - -} - -static char *find_reg_url(sofia_profile_t * profile, const char *user, const char *host, char *val, switch_size_t len) -{ - char *errmsg; - struct callback_t cbt = { 0 }; - switch_core_db_t *db; - - if (!user) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Called with null user!\n"); - return NULL; - } - - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - return NULL; - } - - cbt.val = val; - cbt.len = len; - switch_mutex_lock(profile->ireg_mutex); - if (host) { - snprintf(val, len, "select contact from sip_registrations where user='%s' and host='%s'", user, host); - } else { - snprintf(val, len, "select contact from sip_registrations where user='%s'", user); - } - - switch_core_db_exec(db, val, find_callback, &cbt, &errmsg); - - if (errmsg) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s][%s]\n", val, errmsg); - switch_safe_free(errmsg); - errmsg = NULL; - } - - switch_mutex_unlock(profile->ireg_mutex); - - switch_core_db_close(db); - if (cbt.matches) { - return val; - } else { - return NULL; - } -} - - -static void set_local_sdp(private_object_t * tech_pvt, char *ip, uint32_t port, char *sr, int force) -{ - char buf[2048]; - switch_time_t now = switch_time_now(); - int ptime = 0; - int rate = 0; - - if (!force && !ip && !sr && switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - return; - } - - if (!ip) { - if (!(ip = tech_pvt->adv_sdp_audio_ip)) { - ip = tech_pvt->proxy_sdp_audio_ip; - } - } - if (!port) { - if (!(port = tech_pvt->adv_sdp_audio_port)) { - port = tech_pvt->proxy_sdp_audio_port; - } - } - if (!sr) { - sr = "sendrecv"; - } - - snprintf(buf, sizeof(buf), - "v=0\n" - "o=FreeSWITCH %d%" SWITCH_TIME_T_FMT " %d%" SWITCH_TIME_T_FMT " IN IP4 %s\n" - "s=FreeSWITCH\n" "c=IN IP4 %s\n" "t=0 0\n" "a=%s\n" "m=audio %d RTP/AVP", port, now, port, now, ip, ip, sr, port); - - if (tech_pvt->rm_encoding) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->pt); - } else if (tech_pvt->num_codecs) { - int i; - for (i = 0; i < tech_pvt->num_codecs; i++) { - const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; - - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", imp->ianacode); - if (!ptime) { - ptime = imp->microseconds_per_frame / 1000; - } - } - } - - if (tech_pvt->te > 95) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->te); - } - - if (tech_pvt->cng_pt) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->cng_pt); - } - - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n"); - - if (tech_pvt->rm_encoding) { - rate = tech_pvt->rm_rate; - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%ld\n", tech_pvt->pt, tech_pvt->rm_encoding, tech_pvt->rm_rate); - if (tech_pvt->fmtp_out) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=fmtp:%d %s\n", tech_pvt->pt, tech_pvt->fmtp_out); - } - if (tech_pvt->read_codec.implementation && !ptime) { - ptime = tech_pvt->read_codec.implementation->microseconds_per_frame / 1000; - } - - } else if (tech_pvt->num_codecs) { - int i; - for (i = 0; i < tech_pvt->num_codecs; i++) { - const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; - uint32_t rfc_3551_sucks = imp->samples_per_second; - - if (!rate) { - rate = imp->samples_per_second; - } - if (ptime && ptime != imp->microseconds_per_frame / 1000) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "ptime %u != advertised ptime %u\n", imp->microseconds_per_frame / 1000, ptime); - } - - if (rfc_3551_sucks && imp->ianacode == 9) { - rfc_3551_sucks = 8000; - } - - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%d\n", imp->ianacode, imp->iananame, rfc_3551_sucks); - if (imp->fmtp) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=fmtp:%d %s\n", imp->ianacode, imp->fmtp); - } - } - } - - if (tech_pvt->te > 95) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d telephone-event/8000\na=fmtp:%d 0-16\n", tech_pvt->te, tech_pvt->te); - } - if (tech_pvt->cng_pt) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d CN/%d\n", tech_pvt->cng_pt, rate); - if (!tech_pvt->rm_encoding) { - tech_pvt->cng_pt = 0; - } - } - if (ptime) { - snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=ptime:%d\n", ptime); - } - - tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, buf); -} - -static void tech_set_codecs(private_object_t * tech_pvt) -{ - switch_channel_t *channel; - char *abs, *codec_string = NULL; - char *ocodec = NULL; - - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - return; - } - - if (tech_pvt->num_codecs) { - return; - } - - assert(tech_pvt->session != NULL); - - channel = switch_core_session_get_channel(tech_pvt->session); - assert(channel != NULL); - - - if ((abs = switch_channel_get_variable(channel, "absolute_codec_string"))) { - codec_string = abs; - } else { - if (!(codec_string = switch_channel_get_variable(channel, "codec_string"))) { - if (tech_pvt->profile->codec_string) { - codec_string = tech_pvt->profile->codec_string; - } - } - - if ((ocodec = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) { - if (!codec_string || (tech_pvt->profile->pflags & PFLAG_DISABLE_TRANSCODING)) { - codec_string = ocodec; - } else { - if (!(codec_string = switch_core_session_sprintf(tech_pvt->session, "%s,%s", ocodec, codec_string))) { - codec_string = ocodec; - } - } - } - } - - if (codec_string) { - char *tmp_codec_string; - if ((tmp_codec_string = switch_core_session_strdup(tech_pvt->session, codec_string))) { - tech_pvt->codec_order_last = switch_separate_string(tmp_codec_string, ',', tech_pvt->codec_order, SWITCH_MAX_CODECS); - tech_pvt->num_codecs = - switch_loadable_module_get_codecs_sorted(tech_pvt->codecs, SWITCH_MAX_CODECS, tech_pvt->codec_order, tech_pvt->codec_order_last); - } - } else { - tech_pvt->num_codecs = - switch_loadable_module_get_codecs(switch_core_session_get_pool(tech_pvt->session), tech_pvt->codecs, - sizeof(tech_pvt->codecs) / sizeof(tech_pvt->codecs[0])); - } - -} - - -static void attach_private(switch_core_session_t *session, sofia_profile_t * profile, private_object_t * tech_pvt, const char *channame) -{ - switch_channel_t *channel; - char name[256]; - - assert(session != NULL); - assert(profile != NULL); - assert(tech_pvt != NULL); - - switch_core_session_add_stream(session, NULL); - channel = switch_core_session_get_channel(session); - - //switch_channel_set_flag(channel, CF_ACCEPT_CNG); - - switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); - switch_mutex_lock(tech_pvt->flag_mutex); - tech_pvt->flags = profile->flags; - switch_mutex_unlock(tech_pvt->flag_mutex); - tech_pvt->profile = profile; - if (tech_pvt->bte) { - tech_pvt->te = tech_pvt->bte; - } else if (!tech_pvt->te) { - tech_pvt->te = profile->te; - } - - if (tech_pvt->bcng_pt) { - tech_pvt->cng_pt = tech_pvt->bcng_pt; - } else if (!tech_pvt->cng_pt) { - tech_pvt->cng_pt = profile->cng_pt; - } - - tech_pvt->session = session; - tech_pvt->home = su_home_new(sizeof(*tech_pvt->home)); - - switch_core_session_set_private(session, tech_pvt); - - - snprintf(name, sizeof(name), "sofia/%s/%s", profile->name, channame); - switch_channel_set_name(channel, name); - //tech_set_codecs(tech_pvt); - -} - -static void terminate_session(switch_core_session_t **session, switch_call_cause_t cause, int line) -{ - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Term called from line: %d\n", line); - if (*session) { - switch_channel_t *channel = switch_core_session_get_channel(*session); - struct private_object *tech_pvt = NULL; - unsigned running = switch_core_session_running(*session); - tech_pvt = switch_core_session_get_private(*session); - - if (running) { - switch_channel_hangup(channel, cause); - } else { - if (tech_pvt) { - sofia_on_hangup(*session); - } - if (session && *session) { - switch_core_session_destroy(session); - } - } - } -} - - - -static switch_status_t sofia_ext_address_lookup(char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool) -{ - char *error; - - if (!sourceip) { - return SWITCH_STATUS_FALSE; - } - - if (!strncasecmp(sourceip, "stun:", 5)) { - char *stun_ip = sourceip + 5; - if (!stun_ip) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! NO STUN SERVER\n"); - return SWITCH_STATUS_FALSE; - } - if (switch_stun_lookup(ip, port, stun_ip, SWITCH_STUN_DEFAULT_PORT, &error, pool) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! %s:%d [%s]\n", stun_ip, SWITCH_STUN_DEFAULT_PORT, error); - return SWITCH_STATUS_FALSE; - } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Stun Success [%s]:[%d]\n", *ip, *port); - } else { - *ip = sourceip; - } - return SWITCH_STATUS_SUCCESS; -} - - -static switch_status_t tech_choose_port(private_object_t * tech_pvt) -{ - char *ip = tech_pvt->profile->rtpip; - switch_channel_t *channel; - switch_port_t sdp_port; - char tmp[50]; - - channel = switch_core_session_get_channel(tech_pvt->session); - - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA) || tech_pvt->adv_sdp_audio_port) { - return SWITCH_STATUS_SUCCESS; - } - - tech_pvt->local_sdp_audio_ip = ip; - tech_pvt->local_sdp_audio_port = switch_rtp_request_port(); - sdp_port = tech_pvt->local_sdp_audio_port; - - if (tech_pvt->profile->extrtpip) { - if (sofia_ext_address_lookup(&ip, &sdp_port, tech_pvt->profile->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != - SWITCH_STATUS_SUCCESS) { - terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); - return SWITCH_STATUS_FALSE; - } - } - - tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, ip); - tech_pvt->adv_sdp_audio_port = sdp_port; - - snprintf(tmp, sizeof(tmp), "%d", sdp_port); - switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); - switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); - - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t do_invite(switch_core_session_t *session) -{ - char rpid[1024] = { 0 }; - char alert_info[1024] = { 0 }; - char max_forwards[8] = { 0 }; - char *alertbuf; - char *forwardbuf; - int forwardval; - private_object_t *tech_pvt; - switch_channel_t *channel = NULL; - switch_caller_profile_t *caller_profile; - char *cid_name, *cid_num; - char *e_dest = NULL; - const char *holdstr = ""; - switch_stream_handle_t stream = { 0 }; - switch_hash_index_t *hi; - void *vval; - char *extra_headers = NULL; - const void *vvar; - switch_status_t status = SWITCH_STATUS_FALSE; - char *rep; - - channel = switch_core_session_get_channel(session); - assert(channel != NULL); - - rep = switch_channel_get_variable(channel, SOFIA_REPLACES_HEADER); - - tech_pvt = (private_object_t *) switch_core_session_get_private(session); - assert(tech_pvt != NULL); - - caller_profile = switch_channel_get_caller_profile(channel); - - cid_name = (char *) caller_profile->caller_id_name; - cid_num = (char *) caller_profile->caller_id_number; - tech_set_codecs(tech_pvt); - - if (!tech_pvt->from_str) { - tech_pvt->from_str = switch_core_session_sprintf(tech_pvt->session, "\"%s\" ", - cid_name, - cid_num, tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip); - - } - - if (!tech_pvt->from_str) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - return SWITCH_STATUS_FALSE; - } - - - - - if ((alertbuf = switch_channel_get_variable(channel, "alert_info"))) { - snprintf(alert_info, sizeof(alert_info) - 1, "Alert-Info: %s", alertbuf); - } - - if ((forwardbuf = switch_channel_get_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE))) { - forwardval = atoi(forwardbuf) - 1; - snprintf(max_forwards, sizeof(max_forwards) - 1, "%d", forwardval); - } - - if (tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { - return status; - } - - set_local_sdp(tech_pvt, NULL, 0, NULL, 0); - - switch_set_flag_locked(tech_pvt, TFLAG_READY); - - // forge a RPID for now KHR -- Should wrap this in an if statement so it can be turned on and off - if (switch_test_flag(caller_profile, SWITCH_CPF_SCREEN)) { - const char *priv = "off"; - const char *screen = "no"; - if (switch_test_flag(caller_profile, SWITCH_CPF_HIDE_NAME)) { - priv = "name"; - if (switch_test_flag(caller_profile, SWITCH_CPF_HIDE_NUMBER)) { - priv = "yes"; - } - } else if (switch_test_flag(caller_profile, SWITCH_CPF_HIDE_NUMBER)) { - priv = "yes"; - } - if (switch_test_flag(caller_profile, SWITCH_CPF_SCREEN)) { - screen = "yes"; - } - - snprintf(rpid, sizeof(rpid) - 1, "Remote-Party-ID: %s;party=calling;screen=%s;privacy=%s", tech_pvt->from_str, screen, priv); - - } - - if (!tech_pvt->nh) { - char *url = get_url_from_contact(tech_pvt->dest, 1); - tech_pvt->nh = nua_handle(tech_pvt->profile->nua, NULL, - NUTAG_URL(url), - SIPTAG_TO_STR(tech_pvt->dest_to), SIPTAG_FROM_STR(tech_pvt->from_str), SIPTAG_CONTACT_STR(tech_pvt->profile->url), - TAG_END()); - switch_safe_free(url); - - if (!(tech_pvt->sofia_private = malloc(sizeof(*tech_pvt->sofia_private)))) { - abort(); - } - memset(tech_pvt->sofia_private, 0, sizeof(*tech_pvt->sofia_private)); - switch_copy_string(tech_pvt->sofia_private->uuid, switch_core_session_get_uuid(session), sizeof(tech_pvt->sofia_private->uuid)); - nua_handle_bind(tech_pvt->nh, tech_pvt->sofia_private); - - } - - - if (tech_pvt->e_dest && (e_dest = strdup(tech_pvt->e_dest))) { - char *user = e_dest, *host = NULL; - char hash_key[256] = ""; - - if ((host = strchr(user, '@'))) { - *host++ = '\0'; - } - snprintf(hash_key, sizeof(hash_key), "%s%s%s", user, host, cid_num); - - tech_pvt->chat_from = tech_pvt->from_str; - tech_pvt->chat_to = tech_pvt->dest; - tech_pvt->hash_key = switch_core_session_strdup(tech_pvt->session, hash_key); - switch_core_hash_insert(tech_pvt->profile->chat_hash, tech_pvt->hash_key, tech_pvt); - free(e_dest); - } - - holdstr = switch_test_flag(tech_pvt, TFLAG_SIP_HOLD) ? "*" : ""; - - - SWITCH_STANDARD_STREAM(stream); - for (hi = switch_channel_variable_first(channel, switch_core_session_get_pool(tech_pvt->session)); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, &vvar, NULL, &vval); - if (vvar && vval) { - const char *name = vvar; - char *value = (char *) vval; - - if (!strncasecmp(name, SOFIA_SIP_HEADER_PREFIX, strlen(SOFIA_SIP_HEADER_PREFIX))) { - const char *hname = name + strlen(SOFIA_SIP_HEADER_PREFIX); - stream.write_function(&stream, "%s: %s\r\n", hname, value); - } - } - } - - if (stream.data) { - extra_headers = stream.data; - } - - nua_invite(tech_pvt->nh, - TAG_IF(!switch_strlen_zero(rpid), SIPTAG_HEADER_STR(rpid)), - TAG_IF(!switch_strlen_zero(alert_info), SIPTAG_HEADER_STR(alert_info)), - TAG_IF(!switch_strlen_zero(extra_headers), SIPTAG_HEADER_STR(extra_headers)), - TAG_IF(!switch_strlen_zero(max_forwards), SIPTAG_MAX_FORWARDS_STR(max_forwards)), - //SIPTAG_CONTACT_STR(tech_pvt->profile->url), - SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), - SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), - SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), SOATAG_HOLD(holdstr), TAG_END()); - - switch_safe_free(stream.data); - - return SWITCH_STATUS_SUCCESS; - -} - - - -static void do_xfer_invite(switch_core_session_t *session) -{ - char rpid[1024]; - private_object_t *tech_pvt; - switch_channel_t *channel = NULL; - switch_caller_profile_t *caller_profile; - - channel = switch_core_session_get_channel(session); - assert(channel != NULL); - - tech_pvt = (private_object_t *) switch_core_session_get_private(session); - assert(tech_pvt != NULL); - - caller_profile = switch_channel_get_caller_profile(channel); - - - - if ((tech_pvt->from_str = switch_core_session_sprintf(session, "\"%s\" ", - (char *) caller_profile->caller_id_name, - (char *) caller_profile->caller_id_number, - tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip))) { - - char *rep = switch_channel_get_variable(channel, SOFIA_REPLACES_HEADER); - - tech_pvt->nh2 = nua_handle(tech_pvt->profile->nua, NULL, - SIPTAG_TO_STR(tech_pvt->dest), SIPTAG_FROM_STR(tech_pvt->from_str), SIPTAG_CONTACT_STR(tech_pvt->profile->url), - TAG_END()); - - - nua_handle_bind(tech_pvt->nh2, tech_pvt->sofia_private); - - nua_invite(tech_pvt->nh2, - TAG_IF(rpid, SIPTAG_HEADER_STR(rpid)), - SIPTAG_CONTACT_STR(tech_pvt->profile->url), - SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), - SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), TAG_END()); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - } - -} - -static void tech_absorb_sdp(private_object_t * tech_pvt) -{ - switch_channel_t *channel; - char *sdp_str; - - channel = switch_core_session_get_channel(tech_pvt->session); - assert(channel != NULL); - - if ((sdp_str = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE))) { - sdp_parser_t *parser; - sdp_session_t *sdp; - sdp_media_t *m; - sdp_connection_t *connection; - - if ((parser = sdp_parse(tech_pvt->home, sdp_str, (int) strlen(sdp_str), 0))) { - if ((sdp = sdp_session(parser))) { - for (m = sdp->sdp_media; m; m = m->m_next) { - if (m->m_type != sdp_media_audio) { - continue; - } - - connection = sdp->sdp_connection; - if (m->m_connections) { - connection = m->m_connections; - } - - if (connection) { - tech_pvt->proxy_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, connection->c_address); - } - tech_pvt->proxy_sdp_audio_port = (switch_port_t) m->m_port; - if (tech_pvt->proxy_sdp_audio_ip && tech_pvt->proxy_sdp_audio_port) { - break; - } - } - } - sdp_parser_free(parser); - } - tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, sdp_str); - } -} /* State methods they get called when the state changes to the specific state @@ -1251,11 +86,11 @@ static switch_status_t sofia_on_init(switch_core_session_t *session) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "SOFIA INIT\n"); if (switch_channel_test_flag(channel, CF_NOMEDIA)) { switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); - tech_absorb_sdp(tech_pvt); + sofia_glue_tech_absorb_sdp(tech_pvt); } if (switch_test_flag(tech_pvt, TFLAG_OUTBOUND)) { - if (do_invite(session) != SWITCH_STATUS_SUCCESS) { + if (sofia_glue_do_invite(session) != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } } @@ -1355,7 +190,7 @@ static int hangup_cause_to_sip(switch_call_cause_t cause) } -static switch_status_t sofia_on_hangup(switch_core_session_t *session) +switch_status_t sofia_on_hangup(switch_core_session_t *session) { switch_core_session_t *a_session; private_object_t *tech_pvt; @@ -1372,7 +207,7 @@ static switch_status_t sofia_on_hangup(switch_core_session_t *session) cause = switch_channel_get_cause(channel); sip_cause = hangup_cause_to_sip(cause); - deactivate_rtp(tech_pvt); + sofia_glue_deactivate_rtp(tech_pvt); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel %s hanging up, cause: %s\n", switch_channel_get_name(channel), switch_channel_cause2str(cause)); @@ -1431,242 +266,6 @@ static switch_status_t sofia_on_transmit(switch_core_session_t *session) return SWITCH_STATUS_SUCCESS; } -static void deactivate_rtp(private_object_t * tech_pvt) -{ - int loops = 0; //, sock = -1; - if (switch_rtp_ready(tech_pvt->rtp_session)) { - while (loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) { - switch_yield(10000); - loops++; - } - switch_rtp_destroy(&tech_pvt->rtp_session); - } -} - -static switch_status_t tech_set_codec(private_object_t * tech_pvt, int force) -{ - switch_channel_t *channel; - - if (tech_pvt->read_codec.implementation) { - if (!force) { - return SWITCH_STATUS_SUCCESS; - } - if (strcasecmp(tech_pvt->read_codec.implementation->iananame, tech_pvt->rm_encoding) || - tech_pvt->read_codec.implementation->samples_per_second != tech_pvt->rm_rate) { - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Changing Codec from %s to %s\n", - tech_pvt->read_codec.implementation->iananame, tech_pvt->rm_encoding); - switch_core_codec_destroy(&tech_pvt->read_codec); - switch_core_codec_destroy(&tech_pvt->write_codec); - switch_core_session_reset(tech_pvt->session); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Already using %s\n", tech_pvt->read_codec.implementation->iananame); - return SWITCH_STATUS_SUCCESS; - } - } - - channel = switch_core_session_get_channel(tech_pvt->session); - assert(channel != NULL); - - if (!tech_pvt->rm_encoding) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec with no name?\n"); - terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); - return SWITCH_STATUS_FALSE; - } - - if (switch_core_codec_init(&tech_pvt->read_codec, - tech_pvt->rm_encoding, - tech_pvt->rm_fmtp, - tech_pvt->rm_rate, - tech_pvt->codec_ms, - 1, - SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | tech_pvt->profile->codec_flags, - NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); - terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); - return SWITCH_STATUS_FALSE; - } else { - if (switch_core_codec_init(&tech_pvt->write_codec, - tech_pvt->rm_encoding, - tech_pvt->rm_fmtp, - tech_pvt->rm_rate, - tech_pvt->codec_ms, - 1, - SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | tech_pvt->profile->codec_flags, - NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); - terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); - return SWITCH_STATUS_FALSE; - } else { - int ms; - tech_pvt->read_frame.rate = tech_pvt->rm_rate; - ms = tech_pvt->write_codec.implementation->microseconds_per_frame / 1000; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set Codec %s %s/%ld %d ms\n", - switch_channel_get_name(channel), tech_pvt->rm_encoding, tech_pvt->rm_rate, tech_pvt->codec_ms); - tech_pvt->read_frame.codec = &tech_pvt->read_codec; - - switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec); - switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec); - tech_pvt->fmtp_out = switch_core_session_strdup(tech_pvt->session, tech_pvt->write_codec.fmtp_out); - } - } - return SWITCH_STATUS_SUCCESS; -} - - -static switch_status_t activate_rtp(private_object_t * tech_pvt) -{ - int bw, ms; - switch_channel_t *channel; - const char *err = NULL; - char *val = NULL; - switch_rtp_flag_t flags; - switch_status_t status; - char tmp[50]; - assert(tech_pvt != NULL); - - channel = switch_core_session_get_channel(tech_pvt->session); - assert(channel != NULL); - - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - return SWITCH_STATUS_SUCCESS; - } - - if (switch_rtp_ready(tech_pvt->rtp_session) && !switch_test_flag(tech_pvt, TFLAG_REINVITE)) { - return SWITCH_STATUS_SUCCESS; - } - - if ((status = tech_set_codec(tech_pvt, 0)) != SWITCH_STATUS_SUCCESS) { - return status; - } - - bw = tech_pvt->read_codec.implementation->bits_per_second; - ms = tech_pvt->read_codec.implementation->microseconds_per_frame; - - flags = (switch_rtp_flag_t) (SWITCH_RTP_FLAG_RAW_WRITE | SWITCH_RTP_FLAG_AUTOADJ | SWITCH_RTP_FLAG_DATAWAIT); - - if (switch_test_flag(tech_pvt, TFLAG_BUGGY_2833)) { - flags |= SWITCH_RTP_FLAG_BUGGY_2833; - } - - if ((tech_pvt->profile->pflags & PFLAG_PASS_RFC2833) - || ((val = switch_channel_get_variable(channel, "pass_rfc2833")) && switch_true(val))) { - flags |= SWITCH_RTP_FLAG_PASS_RFC2833; - } - - if (tech_pvt->cng_pt) { - flags |= SWITCH_RTP_FLAG_AUTO_CNG; - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n", - switch_channel_get_name(channel), - tech_pvt->local_sdp_audio_ip, - tech_pvt->local_sdp_audio_port, - tech_pvt->remote_sdp_audio_ip, - tech_pvt->remote_sdp_audio_port, tech_pvt->agreed_pt, tech_pvt->read_codec.implementation->microseconds_per_frame / 1000); - - snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); - switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); - switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); - - if (tech_pvt->rtp_session && switch_test_flag(tech_pvt, TFLAG_REINVITE)) { - switch_clear_flag_locked(tech_pvt, TFLAG_REINVITE); - - if (switch_rtp_set_remote_address(tech_pvt->rtp_session, tech_pvt->remote_sdp_audio_ip, tech_pvt->remote_sdp_audio_port, &err) != - SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP CHANGING DEST TO: [%s:%d]\n", - tech_pvt->remote_sdp_audio_ip, tech_pvt->remote_sdp_audio_port); - /* Reactivate the NAT buster flag. */ - switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_AUTOADJ); - } - return SWITCH_STATUS_SUCCESS; - } - - tech_pvt->rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip, - tech_pvt->local_sdp_audio_port, - tech_pvt->remote_sdp_audio_ip, - tech_pvt->remote_sdp_audio_port, - tech_pvt->agreed_pt, - tech_pvt->read_codec.implementation->samples_per_frame, - tech_pvt->codec_ms * 1000, - (switch_rtp_flag_t) flags, - NULL, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)); - - if (switch_rtp_ready(tech_pvt->rtp_session)) { - uint8_t vad_in = switch_test_flag(tech_pvt, TFLAG_VAD_IN) ? 1 : 0; - uint8_t vad_out = switch_test_flag(tech_pvt, TFLAG_VAD_OUT) ? 1 : 0; - uint8_t inb = switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? 0 : 1; - - tech_pvt->ssrc = switch_rtp_get_ssrc(tech_pvt->rtp_session); - switch_set_flag_locked(tech_pvt, TFLAG_RTP); - switch_set_flag_locked(tech_pvt, TFLAG_IO); - - if ((vad_in && inb) || (vad_out && !inb)) { - switch_rtp_enable_vad(tech_pvt->rtp_session, tech_pvt->session, &tech_pvt->read_codec, SWITCH_VAD_FLAG_TALKING); - switch_set_flag_locked(tech_pvt, TFLAG_VAD); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP Engage VAD for %s ( %s %s )\n", - switch_channel_get_name(switch_core_session_get_channel(tech_pvt->session)), vad_in ? "in" : "", vad_out ? "out" : ""); - } - - if (tech_pvt->te) { - switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te); - } - if (tech_pvt->cng_pt) { - switch_rtp_set_cng_pt(tech_pvt->rtp_session, tech_pvt->cng_pt); - } - - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err); - terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); - switch_clear_flag_locked(tech_pvt, TFLAG_IO); - return SWITCH_STATUS_FALSE; - } - - switch_set_flag_locked(tech_pvt, TFLAG_IO); - return SWITCH_STATUS_SUCCESS; -} - -static switch_status_t tech_media(private_object_t * tech_pvt, char *r_sdp) -{ - sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); - sdp_session_t *sdp; - uint8_t match = 0; - switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session); - - assert(tech_pvt != NULL); - - if (switch_strlen_zero(r_sdp)) { - return SWITCH_STATUS_FALSE; - } - - if (tech_pvt->num_codecs) { - if ((sdp = sdp_session(parser))) { - match = negotiate_sdp(tech_pvt->session, sdp); - } - } - - if (parser) { - sdp_parser_free(parser); - } - - if (match) { - if (tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { - return SWITCH_STATUS_FALSE; - } - activate_rtp(tech_pvt); - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "EARLY MEDIA"); - switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); - switch_channel_mark_pre_answered(channel); - return SWITCH_STATUS_SUCCESS; - } - - return SWITCH_STATUS_FALSE; -} - - - static switch_status_t sofia_answer_channel(switch_core_session_t *session) { private_object_t *tech_pvt; @@ -1693,7 +292,7 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) } else { if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) { char *r_sdp = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); - if (tech_media(tech_pvt, r_sdp) != SWITCH_STATUS_SUCCESS) { + if (sofia_glue_tech_media(tech_pvt, r_sdp) != SWITCH_STATUS_SUCCESS) { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); return SWITCH_STATUS_FALSE; @@ -1701,12 +300,12 @@ static switch_status_t sofia_answer_channel(switch_core_session_t *session) switch_clear_flag_locked(tech_pvt, TFLAG_LATE_NEGOTIATION); } - if ((status = tech_choose_port(tech_pvt)) != SWITCH_STATUS_SUCCESS) { + if ((status = sofia_glue_tech_choose_port(tech_pvt)) != SWITCH_STATUS_SUCCESS) { return status; } - set_local_sdp(tech_pvt, NULL, 0, NULL, 0); - activate_rtp(tech_pvt); + sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); + sofia_glue_activate_rtp(tech_pvt); if (tech_pvt->nh) { if (tech_pvt->local_sdp_str) { @@ -1998,26 +597,26 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi port = switch_channel_get_variable(other_channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE); switch_core_session_rwunlock(other_session); if (ip && port) { - set_local_sdp(tech_pvt, ip, atoi(port), NULL, 1); + sofia_glue_set_local_sdp(tech_pvt, ip, atoi(port), NULL, 1); } } if (!tech_pvt->local_sdp_str) { - tech_absorb_sdp(tech_pvt); + sofia_glue_tech_absorb_sdp(tech_pvt); } - do_invite(session); + sofia_glue_do_invite(session); } break; case SWITCH_MESSAGE_INDICATE_MEDIA:{ switch_clear_flag_locked(tech_pvt, TFLAG_NOMEDIA); tech_pvt->local_sdp_str = NULL; if (!switch_rtp_ready(tech_pvt->rtp_session)) { - tech_set_codecs(tech_pvt); - if ((status = tech_choose_port(tech_pvt)) != SWITCH_STATUS_SUCCESS) { + sofia_glue_sofia_glue_tech_set_codecs(tech_pvt); + if ((status = sofia_glue_tech_choose_port(tech_pvt)) != SWITCH_STATUS_SUCCESS) { return status; } } - set_local_sdp(tech_pvt, NULL, 0, NULL, 1); - do_invite(session); + sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 1); + sofia_glue_do_invite(session); while (!switch_rtp_ready(tech_pvt->rtp_session) && switch_channel_get_state(channel) < CS_HANGUP) { switch_yield(1000); } @@ -2026,13 +625,13 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi case SWITCH_MESSAGE_INDICATE_HOLD:{ switch_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD); - do_invite(session); + sofia_glue_do_invite(session); } break; case SWITCH_MESSAGE_INDICATE_UNHOLD:{ switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); - do_invite(session); + sofia_glue_do_invite(session); } break; case SWITCH_MESSAGE_INDICATE_BRIDGE: @@ -2051,7 +650,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi a_tech_pvt->remote_sdp_audio_port = b_tech_pvt->remote_sdp_audio_port; a_tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(a_session, b_tech_pvt->local_sdp_audio_ip); a_tech_pvt->local_sdp_audio_port = b_tech_pvt->local_sdp_audio_port; - activate_rtp(a_tech_pvt); + sofia_glue_activate_rtp(a_tech_pvt); b_tech_pvt->kick = switch_core_session_strdup(b_session, tech_pvt->xferto); switch_core_session_rwunlock(a_session); @@ -2097,7 +696,7 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi } else { if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) { char *r_sdp = switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); - if (tech_media(tech_pvt, r_sdp) != SWITCH_STATUS_SUCCESS) { + if (sofia_glue_tech_media(tech_pvt, r_sdp) != SWITCH_STATUS_SUCCESS) { switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); nua_respond(tech_pvt->nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); return SWITCH_STATUS_FALSE; @@ -2105,11 +704,11 @@ static switch_status_t sofia_receive_message(switch_core_session_t *session, swi switch_clear_flag_locked(tech_pvt, TFLAG_LATE_NEGOTIATION); } - if ((status = tech_choose_port(tech_pvt)) != SWITCH_STATUS_SUCCESS) { + if ((status = sofia_glue_tech_choose_port(tech_pvt)) != SWITCH_STATUS_SUCCESS) { return status; } - set_local_sdp(tech_pvt, NULL, 0, NULL, 0); - activate_rtp(tech_pvt); + sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); + sofia_glue_activate_rtp(tech_pvt); if (tech_pvt->local_sdp_str) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Ring SDP:\n%s\n", tech_pvt->local_sdp_str); } @@ -2180,7 +779,7 @@ static const switch_state_handler_table_t sofia_event_handlers = { /*.on_transmit */ sofia_on_transmit }; -static const switch_endpoint_interface_t sofia_endpoint_interface = { +const switch_endpoint_interface_t sofia_endpoint_interface = { /*.interface_name */ "sofia", /*.io_routines */ &sofia_io_routines, /*.event_handlers */ &sofia_event_handlers, @@ -2190,7 +789,7 @@ static const switch_endpoint_interface_t sofia_endpoint_interface = { static const switch_chat_interface_t sofia_chat_interface = { /*.name */ SOFIA_CHAT_PROTO, - /*.chat_send */ chat_send, + /*.sofia_presence_chat_send */ sofia_presence_chat_send, }; @@ -2222,32 +821,6 @@ static const switch_loadable_module_interface_t sofia_module_interface = { }; -static void logger(void *logarg, char const *fmt, va_list ap) -{ - char *data = NULL; - - if (fmt) { -#ifdef HAVE_VASPRINTF - int ret; - ret = vasprintf(&data, fmt, ap); - if ((ret == -1) || !data) { - return; - } -#else - data = (char *) malloc(2048); - if (data) { - vsnprintf(data, 2048, fmt, ap); - } else { - return; - } -#endif - } - if (data) { - switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, (char *) "%s", data); - free(data); - } -} - static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, @@ -2271,7 +844,7 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session if (!(tech_pvt = (struct private_object *) switch_core_session_alloc(nsession, sizeof(*tech_pvt)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Error Creating Session\n"); - terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + sofia_glue_terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); goto done; } @@ -2285,7 +858,7 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session if (!(gw = strchr(profile_name, '/'))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL\n"); - terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + sofia_glue_terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; goto done; } @@ -2294,16 +867,16 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session if (!(dest = strchr(gw, '/'))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL\n"); - terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + sofia_glue_terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; goto done; } *dest++ = '\0'; - if (!(gateway_ptr = find_gateway(gw))) { + if (!(gateway_ptr = sofia_reg_find_gateway(gw))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Gateway\n"); - terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + sofia_glue_terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; goto done; } @@ -2318,15 +891,15 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session } else { if (!(dest = strchr(profile_name, '/'))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid URL\n"); - terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + sofia_glue_terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; goto done; } *dest++ = '\0'; - if (!(profile = find_profile(profile_name))) { + if (!(profile = sofia_glue_find_profile(profile_name))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n"); - terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + sofia_glue_terminate_session(&nsession, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; goto done; } @@ -2342,25 +915,25 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session *host = '@'; tech_pvt->e_dest = switch_core_session_strdup(nsession, dest); *host++ = '\0'; - if (find_reg_url(profile, dest, host, buf, sizeof(buf))) { + if (sofia_reg_find_reg_url(profile, dest, host, buf, sizeof(buf))) { tech_pvt->dest = switch_core_session_strdup(nsession, buf); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot locate registered user %s@%s\n", dest, host); cause = SWITCH_CAUSE_NO_ROUTE_DESTINATION; - terminate_session(&nsession, cause, __LINE__); + sofia_glue_terminate_session(&nsession, cause, __LINE__); goto done; } } else if (!strchr(dest, '@')) { char buf[128]; tech_pvt->e_dest = switch_core_session_strdup(nsession, dest); - if (find_reg_url(profile, dest, profile_name, buf, sizeof(buf))) { + if (sofia_reg_find_reg_url(profile, dest, profile_name, buf, sizeof(buf))) { tech_pvt->dest = switch_core_session_strdup(nsession, buf); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot locate registered user %s@%s\n", dest, profile_name); cause = SWITCH_CAUSE_NO_ROUTE_DESTINATION; - terminate_session(&nsession, cause, __LINE__); + sofia_glue_terminate_session(&nsession, cause, __LINE__); goto done; } } else { @@ -2373,7 +946,7 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session tech_pvt->dest_to = tech_pvt->dest; } - attach_private(nsession, profile, tech_pvt, dest); + sofia_glue_attach_private(nsession, profile, tech_pvt, dest); nchannel = switch_core_session_get_channel(nsession); caller_profile = switch_caller_profile_clone(nsession, outbound_profile); @@ -2404,3408 +977,6 @@ static switch_call_cause_t sofia_outgoing_channel(switch_core_session_t *session return cause; } - -static uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t * sdp) -{ - uint8_t match = 0; - switch_payload_t te = 0, cng_pt = 0; - private_object_t *tech_pvt; - sdp_media_t *m; - sdp_attribute_t *a; - switch_channel_t *channel; - int ptime = 0, dptime = 0; - - tech_pvt = switch_core_session_get_private(session); - assert(tech_pvt != NULL); - - channel = switch_core_session_get_channel(session); - - if ((tech_pvt->origin = switch_core_session_strdup(session, (char *) sdp->sdp_origin->o_username))) { - if (strstr(tech_pvt->origin, "CiscoSystemsSIP-GW-UserAgent")) { - switch_set_flag_locked(tech_pvt, TFLAG_BUGGY_2833); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Activate Buggy RFC2833 Mode!\n"); - } - } - - for (a = sdp->sdp_attributes; a; a = a->a_next) { - if (switch_strlen_zero(a->a_name)) { - continue; - } - if (!strcasecmp(a->a_name, "sendonly")) { - if (!switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { - char *stream; - - switch_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD); - switch_channel_set_flag(channel, CF_HOLD); - if (!(stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE))) { - stream = tech_pvt->profile->hold_music; - } - if (stream) { - switch_ivr_broadcast(switch_core_session_get_uuid(tech_pvt->session), stream, SMF_ECHO_BLEG | SMF_LOOP); - } - } - } else if (!strcasecmp(a->a_name, "sendrecv")) { - if (switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { - switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); - switch_channel_clear_flag(channel, CF_HOLD); - switch_channel_clear_flag_partner(channel, CF_BROADCAST); - switch_channel_set_flag_partner(channel, CF_BREAK); - } - } else if (!strcasecmp(a->a_name, "ptime")) { - dptime = atoi(a->a_value); - } - } - - for (m = sdp->sdp_media; m; m = m->m_next) { - sdp_connection_t *connection; - - ptime = dptime; - for (a = m->m_attributes; a; a = a->a_next) { - if (!strcasecmp(a->a_name, "ptime") && a->a_value) { - ptime = atoi(a->a_value); - } - } - - if (m->m_type == sdp_media_audio) { - sdp_rtpmap_t *map; - - connection = sdp->sdp_connection; - if (m->m_connections) { - connection = m->m_connections; - } - - if (!connection) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find a c= line in the sdp at media or session level!\n"); - match = 0; - break; - } - - for (map = m->m_rtpmaps; map; map = map->rm_next) { - int32_t i; - const switch_codec_implementation_t *mimp = NULL, *near_match = NULL; - const char *rm_encoding; - - - if (!(rm_encoding = map->rm_encoding)) { - rm_encoding = ""; - } - - if (!te && !strcasecmp(rm_encoding, "telephone-event")) { - te = tech_pvt->te = (switch_payload_t) map->rm_pt; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set 2833 dtmf payload to %u\n", te); - if (tech_pvt->rtp_session) { - switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te); - } - } - - if (!cng_pt && !strcasecmp(rm_encoding, "CN")) { - cng_pt = tech_pvt->cng_pt = (switch_payload_t) map->rm_pt; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set comfort noise payload to %u\n", cng_pt); - if (tech_pvt->rtp_session) { - switch_rtp_set_cng_pt(tech_pvt->rtp_session, tech_pvt->cng_pt); - switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_AUTO_CNG); - } - } - - if (match) { - if (te && cng_pt) { - break; - } - continue; - } - - for (i = 0; i < tech_pvt->num_codecs; i++) { - const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Codec Compare [%s:%d]/[%s:%d]\n", - rm_encoding, map->rm_pt, imp->iananame, imp->ianacode); - if (map->rm_pt < 96) { - match = (map->rm_pt == imp->ianacode) ? 1 : 0; - } else { - match = strcasecmp(rm_encoding, imp->iananame) ? 0 : 1; - } - - if (match && (map->rm_rate == imp->samples_per_second)) { - if (ptime && ptime * 1000 != imp->microseconds_per_frame) { - near_match = imp; - match = 0; - continue; - } - mimp = imp; - break; - } else { - match = 0; - } - } - - if (!match && near_match) { - const switch_codec_implementation_t *search[1]; - char *prefs[1]; - char tmp[80]; - int num; - - snprintf(tmp, sizeof(tmp), "%s@%uk@%ui", near_match->iananame, near_match->samples_per_second, ptime); - - prefs[0] = tmp; - num = switch_loadable_module_get_codecs_sorted(search, 1, prefs, 1); - - if (num) { - mimp = search[0]; - } else { - mimp = near_match; - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Substituting codec %s@%ums\n", - mimp->iananame, mimp->microseconds_per_frame / 1000); - match = 1; - } - - if (mimp) { - if ((tech_pvt->rm_encoding = switch_core_session_strdup(session, (char *) rm_encoding))) { - char tmp[50]; - tech_pvt->pt = (switch_payload_t) map->rm_pt; - tech_pvt->rm_rate = map->rm_rate; - tech_pvt->codec_ms = mimp->microseconds_per_frame / 1000; - tech_pvt->remote_sdp_audio_ip = switch_core_session_strdup(session, (char *) connection->c_address); - tech_pvt->rm_fmtp = switch_core_session_strdup(session, (char *) map->rm_fmtp); - tech_pvt->remote_sdp_audio_port = (switch_port_t) m->m_port; - tech_pvt->agreed_pt = (switch_payload_t) map->rm_pt; - snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); - switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, tech_pvt->remote_sdp_audio_ip); - switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE, tmp); - } else { - match = 0; - } - } - - if (match) { - if (tech_set_codec(tech_pvt, 1) != SWITCH_STATUS_SUCCESS) { - match = 0; - } - } - } - } - } - - return match; -} - -// map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 -static switch_call_cause_t sip_cause_to_freeswitch(int status) -{ - switch (status) { - case 200: - return SWITCH_CAUSE_NORMAL_CLEARING; - case 401: - case 402: - case 403: - case 407: - case 603: - return SWITCH_CAUSE_CALL_REJECTED; - case 404: - case 485: - case 604: - return SWITCH_CAUSE_NO_ROUTE_DESTINATION; - case 408: - case 504: - return SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE; - case 410: - return SWITCH_CAUSE_NUMBER_CHANGED; - case 413: - case 414: - case 416: - case 420: - case 421: - case 423: - case 505: - case 513: - return SWITCH_CAUSE_INTERWORKING; - case 480: - return SWITCH_CAUSE_NO_USER_RESPONSE; - case 400: - case 481: - case 500: - case 503: - return SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE; - case 486: - case 600: - return SWITCH_CAUSE_USER_BUSY; - case 484: - return SWITCH_CAUSE_INVALID_NUMBER_FORMAT; - case 488: - case 606: - return SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL; - case 502: - return SWITCH_CAUSE_NETWORK_OUT_OF_ORDER; - case 405: - return SWITCH_CAUSE_SERVICE_UNAVAILABLE; - case 406: - case 415: - case 501: - return SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED; - case 482: - case 483: - return SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR; - case 487: - return SWITCH_CAUSE_ORIGINATOR_CANCEL; - - default: - return SWITCH_CAUSE_NORMAL_UNSPECIFIED; - - } -} - - -static void set_hash_key(char *hash_key, int32_t len, sip_t const *sip) -{ - - snprintf(hash_key, len, "%s%s%s", (char *) sip->sip_from->a_url->url_user, (char *) sip->sip_from->a_url->url_host, - (char *) sip->sip_to->a_url->url_user); - - -#if 0 - /* nicer one we cant use in both directions >=0 */ - snprintf(hash_key, len, "%s%s%s%s%s%s", - (char *) sip->sip_to->a_url->url_user, - (char *) sip->sip_to->a_url->url_host, - (char *) sip->sip_to->a_url->url_params, - (char *) sip->sip_from->a_url->url_user, (char *) sip->sip_from->a_url->url_host, (char *) sip->sip_from->a_url->url_params); -#endif - -} - -static void set_chat_hash(private_object_t * tech_pvt, sip_t const *sip) -{ - char hash_key[256] = ""; - char buf[512]; - - if (tech_pvt->hash_key || !sip || !sip->sip_from || !sip->sip_from->a_url || !sip->sip_from->a_url->url_user || !sip->sip_from->a_url->url_host) { - return; - } - - if (find_reg_url(tech_pvt->profile, sip->sip_from->a_url->url_user, sip->sip_from->a_url->url_host, buf, sizeof(buf))) { - tech_pvt->chat_from = sip_header_as_string(tech_pvt->home, (const sip_header_t *) sip->sip_to); - tech_pvt->chat_to = switch_core_session_strdup(tech_pvt->session, buf); - set_hash_key(hash_key, sizeof(hash_key), sip); - } else { - return; - } - - tech_pvt->hash_key = switch_core_session_strdup(tech_pvt->session, hash_key); - switch_core_hash_insert(tech_pvt->profile->chat_hash, tech_pvt->hash_key, tech_pvt); - -} - - -static void sip_i_message(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - if (sip) { - sip_from_t const *from = sip->sip_from; - char *from_user = NULL; - char *from_host = NULL; - sip_to_t const *to = sip->sip_to; - char *to_user = NULL; - char *to_host = NULL; - sip_subject_t const *sip_subject = sip->sip_subject; - sip_payload_t *payload = sip->sip_payload; - const char *subject = "n/a"; - char *msg = NULL; - - if (sip->sip_content_type) { - if (strstr((char *) sip->sip_content_type->c_subtype, "composing")) { - return; - } - } - - if (from) { - from_user = (char *) from->a_url->url_user; - from_host = (char *) from->a_url->url_host; - } - - if (to) { - to_user = (char *) to->a_url->url_user; - to_host = (char *) to->a_url->url_host; - } - - - if (!to_user) { - return; - } - - if (payload) { - msg = payload->pl_data; - } - - if (sip_subject) { - subject = sip_subject->g_value; - } - - if (nh) { - char hash_key[512]; - private_object_t *tech_pvt; - switch_channel_t *channel; - switch_event_t *event; - char *to_addr; - char *from_addr; - char *p; - char *full_from; - char proto[512] = SOFIA_CHAT_PROTO; - - full_from = sip_header_as_string(profile->home, (void *) sip->sip_from); - - if ((p = strchr(to_user, '+'))) { - switch_copy_string(proto, to_user, sizeof(proto)); - p = strchr(proto, '+'); - *p++ = '\0'; - - if ((to_addr = strdup(p))) { - if ((p = strchr(to_addr, '+'))) { - *p = '@'; - } - } - - } else { - to_addr = switch_mprintf("%s@%s", to_user, to_host); - } - - from_addr = switch_mprintf("%s@%s", from_user, from_host); - - - set_hash_key(hash_key, sizeof(hash_key), sip); - if ((tech_pvt = (private_object_t *) switch_core_hash_find(profile->chat_hash, hash_key))) { - channel = switch_core_session_get_channel(tech_pvt->session); - if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s", tech_pvt->hash_key); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "to", "%s", to_addr); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); - if (msg) { - switch_event_add_body(event, "%s", msg); - } - if (switch_core_session_queue_event(tech_pvt->session, &event) != SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); - switch_event_fire(&event); - } - } - } else { - switch_chat_interface_t *ci; - - if ((ci = switch_loadable_module_get_chat_interface(proto))) { - ci->chat_send(SOFIA_CHAT_PROTO, from_addr, to_addr, "", msg, full_from); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Chat Interface [%s]!\n", proto ? proto : "(none)"); - } - - } - switch_safe_free(to_addr); - switch_safe_free(from_addr); - if (full_from) { - su_free(profile->home, full_from); - } - } - - } -} - -static void pass_sdp(private_object_t * tech_pvt, char *sdp) -{ - char *val; - switch_channel_t *channel; - switch_core_session_t *other_session; - switch_channel_t *other_channel; - - channel = switch_core_session_get_channel(tech_pvt->session); - assert(channel != NULL); - - if ((val = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) - && (other_session = switch_core_session_locate(val))) { - other_channel = switch_core_session_get_channel(other_session); - assert(other_channel != NULL); - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, sdp); - - if (!switch_test_flag(tech_pvt, TFLAG_CHANGE_MEDIA) && (switch_channel_test_flag(other_channel, CF_OUTBOUND) && - //switch_channel_test_flag(other_channel, CF_NOMEDIA) && - switch_channel_test_flag(channel, CF_OUTBOUND) && - switch_channel_test_flag(channel, CF_NOMEDIA))) { - switch_ivr_nomedia(val, SMF_FORCE); - switch_set_flag_locked(tech_pvt, TFLAG_CHANGE_MEDIA); - } - - switch_core_session_rwunlock(other_session); - } -} - -static void sip_i_state(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - const char *l_sdp = NULL, *r_sdp = NULL; - int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; - int ss_state = nua_callstate_init; - switch_channel_t *channel = NULL; - private_object_t *tech_pvt = NULL; - switch_core_session_t *session = NULL; - const char *replaces_str = NULL; - char *uuid; - switch_core_session_t *other_session = NULL; - switch_channel_t *other_channel = NULL; - char st[80] = ""; - - - if (sofia_private) { - if (!switch_strlen_zero(sofia_private->uuid)) { - if (!(session = switch_core_session_locate(sofia_private->uuid))) { - /* too late */ - return; - } - } - } - - - tl_gets(tags, - NUTAG_CALLSTATE_REF(ss_state), - NUTAG_OFFER_RECV_REF(offer_recv), - NUTAG_ANSWER_RECV_REF(answer_recv), - NUTAG_OFFER_SENT_REF(offer_sent), - NUTAG_ANSWER_SENT_REF(answer_sent), - SIPTAG_REPLACES_STR_REF(replaces_str), SOATAG_LOCAL_SDP_STR_REF(l_sdp), SOATAG_REMOTE_SDP_STR_REF(r_sdp), TAG_END()); - - if (session) { - channel = switch_core_session_get_channel(session); - assert(channel != NULL); - - tech_pvt = switch_core_session_get_private(session); - assert(tech_pvt != NULL); - assert(tech_pvt->nh != NULL); - - if (switch_channel_test_flag(channel, CF_NOMEDIA)) { - switch_set_flag(tech_pvt, TFLAG_NOMEDIA); - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel %s entering state [%s]\n", - switch_channel_get_name(channel), nua_callstate_name(ss_state)); - - if (r_sdp) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Remote SDP:\n%s\n", r_sdp); - tech_pvt->remote_sdp_str = switch_core_session_strdup(session, r_sdp); - switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, r_sdp); - pass_sdp(tech_pvt, (char *) r_sdp); - - } - } - - if (status == 988) { - goto done; - } - - switch ((enum nua_callstate) ss_state) { - case nua_callstate_init: - break; - case nua_callstate_authenticating: - break; - case nua_callstate_calling: - break; - case nua_callstate_proceeding: - if (channel) { - if (status == 180) { - switch_channel_mark_ring_ready(channel); - if (!switch_channel_test_flag(channel, CF_GEN_RINGBACK)) { - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) - && (other_session = switch_core_session_locate(uuid))) { - switch_core_session_message_t msg; - msg.message_id = SWITCH_MESSAGE_INDICATE_RINGING; - msg.from = __FILE__; - switch_core_session_receive_message(other_session, &msg); - switch_core_session_rwunlock(other_session); - } - - } else { - switch_core_session_queue_indication(session, SWITCH_MESSAGE_INDICATE_RINGING); - } - } - } - - if (r_sdp) { - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); - switch_channel_mark_pre_answered(channel); - if (!switch_channel_test_flag(channel, CF_GEN_RINGBACK) && (uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) - && (other_session = switch_core_session_locate(uuid))) { - other_channel = switch_core_session_get_channel(other_session); - if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); - } - - switch_channel_pre_answer(other_channel); - switch_core_session_rwunlock(other_session); - } - goto done; - } else { - if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) { - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "DELAYED NEGOTIATION"); - } else { - if (tech_media(tech_pvt, (char *) r_sdp) != SWITCH_STATUS_SUCCESS) { - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); - nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); - } - } - goto done; - } - } - } - break; - case nua_callstate_completing: - nua_ack(nh, TAG_END()); - break; - case nua_callstate_received: - - if (session && switch_core_session_running(session)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Re-Entering Call State Received!\n"); - goto done; - } - - if (channel) { - if (r_sdp) { - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOMEDIA"); - switch_channel_set_state(channel, CS_INIT); - switch_set_flag_locked(tech_pvt, TFLAG_READY); - switch_core_session_thread_launch(session); - goto done; - } else { - sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); - sdp_session_t *sdp; - uint8_t match = 0; - - if (tech_pvt->num_codecs) { - if ((sdp = sdp_session(parser))) { - match = negotiate_sdp(session, sdp); - } - } - - if (parser) { - sdp_parser_free(parser); - } - - if (match) { - nua_handle_t *bnh; - sip_replaces_t *replaces; - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED"); - switch_channel_set_state(channel, CS_INIT); - switch_set_flag_locked(tech_pvt, TFLAG_READY); - - switch_core_session_thread_launch(session); - - if (replaces_str && (replaces = sip_replaces_make(tech_pvt->home, replaces_str)) - && (bnh = nua_handle_by_replaces(nua, replaces))) { - sofia_private_t *b_private; - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Processing Replaces Attended Transfer\n"); - while (switch_channel_get_state(channel) < CS_EXECUTE) { - switch_yield(10000); - } - - if ((b_private = nua_handle_magic(bnh))) { - char *br_b = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); - char *br_a = b_private->uuid; - - if (br_b) { - switch_ivr_uuid_bridge(br_a, br_b); - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); - switch_channel_hangup(channel, SWITCH_CAUSE_ATTENDED_TRANSFER); - } else { - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); - switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); - } - } else { - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); - switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); - } - nua_handle_unref(bnh); - } - goto done; - } - - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS"); - nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); - } - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Invite with no SDP activating no-media-mode\n"); - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP"); - - switch_set_flag(tech_pvt, TFLAG_LATE_NEGOTIATION); - switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); - switch_channel_set_flag(channel, CF_NOMEDIA); - switch_channel_set_state(channel, CS_INIT); - switch_set_flag_locked(tech_pvt, TFLAG_READY); - switch_core_session_thread_launch(session); - goto done; - } - } - - break; - case nua_callstate_early: - break; - case nua_callstate_completed: - if (tech_pvt && r_sdp) { - if (r_sdp) { - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - goto done; - } else { - sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); - sdp_session_t *sdp; - uint8_t match = 0; - - if (tech_pvt->num_codecs) { - if ((sdp = sdp_session(parser))) { - match = negotiate_sdp(session, sdp); - } - } - if (match) { - if (tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { - goto done; - } - set_local_sdp(tech_pvt, NULL, 0, NULL, 0); - switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); - activate_rtp(tech_pvt); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Processing Reinvite\n"); - if (parser) { - sdp_parser_free(parser); - } - } - } - } - } - break; - case nua_callstate_ready: - if (tech_pvt && nh == tech_pvt->nh2) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cheater Reinvite!\n"); - switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); - tech_pvt->nh = tech_pvt->nh2; - tech_pvt->nh2 = NULL; - if (tech_choose_port(tech_pvt) == SWITCH_STATUS_SUCCESS) { - activate_rtp(tech_pvt); - } - goto done; - } - - if (channel) { - if (switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { - switch_set_flag_locked(tech_pvt, TFLAG_ANS); - switch_channel_mark_answered(channel); - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) - && (other_session = switch_core_session_locate(uuid))) { - other_channel = switch_core_session_get_channel(other_session); - switch_channel_answer(other_channel); - switch_core_session_rwunlock(other_session); - } - goto done; - } - - if (!r_sdp) { - r_sdp = (const char *) switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); - } - if (r_sdp) { - if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { - switch_set_flag_locked(tech_pvt, TFLAG_ANS); - switch_channel_mark_answered(channel); - if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) - && (other_session = switch_core_session_locate(uuid))) { - other_channel = switch_core_session_get_channel(other_session); - if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { - switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); - } - switch_channel_answer(other_channel); - switch_core_session_rwunlock(other_session); - } - goto done; - } else { - sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); - sdp_session_t *sdp; - uint8_t match = 0; - - if (tech_pvt->num_codecs) { - if ((sdp = sdp_session(parser))) { - match = negotiate_sdp(session, sdp); - } - } - - if (parser) { - sdp_parser_free(parser); - } - - - if (match) { - switch_set_flag_locked(tech_pvt, TFLAG_ANS); - if (tech_choose_port(tech_pvt) == SWITCH_STATUS_SUCCESS) { - activate_rtp(tech_pvt); - switch_channel_mark_answered(channel); - goto done; - } - } - - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS"); - nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); - } - } - - } - - break; - case nua_callstate_terminating: - break; - case nua_callstate_terminated: - if (session) { - if (!switch_test_flag(tech_pvt, TFLAG_BYE)) { - - switch_set_flag_locked(tech_pvt, TFLAG_BYE); - if (switch_test_flag(tech_pvt, TFLAG_NOHUP)) { - switch_clear_flag_locked(tech_pvt, TFLAG_NOHUP); - } else { - snprintf(st, sizeof(st), "%d", status); - switch_channel_set_variable(channel, "sip_term_status", st); - terminate_session(&session, sip_cause_to_freeswitch(status), __LINE__); - } - } - - if (tech_pvt->sofia_private) { - free(tech_pvt->sofia_private); - tech_pvt->sofia_private = NULL; - } - tech_pvt->nh = NULL; - } else if (sofia_private) { - free(sofia_private); - } - - if (nh) { - nua_handle_bind(nh, NULL); - nua_handle_destroy(nh); - } - break; - } - - done: - - if (session) { - switch_core_session_rwunlock(session); - } -} - - -static char *get_auth_data(char *dbname, char *nonce, char *npassword, size_t len, switch_mutex_t * mutex) -{ - switch_core_db_t *db; - switch_core_db_stmt_t *stmt; - char *sql = NULL, *ret = NULL; - - if (mutex) { - switch_mutex_lock(mutex); - } - - if (!dbname) { - goto end; - } - - if (!(db = switch_core_db_open_file(dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", dbname); - goto end; - } - - sql = switch_mprintf("select passwd from sip_authentication where nonce='%q'", nonce); - if (switch_core_db_prepare(db, sql, -1, &stmt, 0)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Statement Error!\n"); - goto fail; - } else { - int running = 1; - int colcount; - - while (running < 5000) { - int result = switch_core_db_step(stmt); - - if (result == SWITCH_CORE_DB_ROW) { - if ((colcount = switch_core_db_column_count(stmt))) { - switch_copy_string(npassword, (char *) switch_core_db_column_text(stmt, 0), len); - ret = npassword; - } - break; - } else if (result == SWITCH_CORE_DB_BUSY) { - running++; - switch_yield(1000); - continue; - } - break; - } - - switch_core_db_finalize(stmt); - } - - - fail: - - switch_core_db_close(db); - - end: - if (mutex) { - switch_mutex_unlock(mutex); - } - - if (sql) { - switch_safe_free(sql); - } - - return ret; -} - - -typedef enum { - REG_REGISTER, - REG_INVITE -} sofia_regtype_t; - -static uint8_t handle_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sip_t const *sip, sofia_regtype_t regtype, char *key, - uint32_t keylen) -{ - sip_from_t const *from = NULL; - sip_expires_t const *expires = NULL; - sip_authorization_t const *authorization = NULL; - sip_contact_t const *contact = NULL; - switch_xml_t domain, xml, user, param, xparams; - char params[1024] = ""; - char *sql; - switch_event_t *s_event; - const char *from_user = NULL; - const char *from_host = NULL; - char contact_str[1024] = ""; - char buf[512]; - const char *passwd = NULL; - const char *a1_hash = NULL; - uint8_t stale = 0, ret = 0, forbidden = 0; - auth_res_t auth_res; - long exptime = 60; - switch_event_t *event; - const char *rpid = "unknown"; - const char *display = "\"user\""; - - /* all callers must confirm that sip, sip->sip_request and sip->sip_contact are not NULL */ - assert(sip != NULL && sip->sip_contact != NULL && sip->sip_request != NULL); - - expires = sip->sip_expires; - authorization = sip->sip_authorization; - contact = sip->sip_contact; - from = sip->sip_from; - - if (from) { - from_user = from->a_url->url_user; - from_host = from->a_url->url_host; - } - - if (!from_user || !from_host) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can not do authorization without a complete from header\n"); - nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); - return 1; - } - - if (contact->m_url) { - const char *port = contact->m_url->url_port; - display = contact->m_display; - - if (switch_strlen_zero(display)) { - if (from) { - display = from->a_display; - if (switch_strlen_zero(display)) { - display = "\"user\""; - } - } - } - - if (!port) { - port = SOFIA_DEFAULT_PORT; - } - - if (contact->m_url->url_params) { - snprintf(contact_str, sizeof(contact_str), "%s ", - display, contact->m_url->url_user, contact->m_url->url_host, port, contact->m_url->url_params); - } else { - snprintf(contact_str, sizeof(contact_str), "%s ", display, contact->m_url->url_user, contact->m_url->url_host, port); - } - } - - if (expires) { - exptime = expires->ex_delta; - } else if (contact->m_expires) { - exptime = atol(contact->m_expires); - } - - if (regtype == REG_REGISTER) { - authorization = sip->sip_authorization; - } else if (regtype == REG_INVITE) { - authorization = sip->sip_proxy_authorization; - } - - if ((profile->pflags & PFLAG_BLIND_REG)) { - goto reg; - } - - if (authorization) { - if ((auth_res = parse_auth(profile, authorization, sip->sip_request->rq_method_name, key, keylen)) == AUTH_STALE) { - stale = 1; - } - - if (auth_res != AUTH_OK && !stale) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "send %s for [%s@%s]\n", forbidden ? "forbidden" : "challange", from_user, from_host); - if (auth_res == AUTH_FORBIDDEN) { - nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS(nua), TAG_END()); - } else { - nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), TAG_END()); - } - return 1; - } - } - - if (!authorization || stale) { - snprintf(params, sizeof(params), "from_user=%s&from_host=%s&contact=%s", from_user, from_host, contact_str); - - - if (switch_xml_locate("directory", "domain", "name", from_host, &xml, &domain, params) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find domain for [%s@%s]\n", from_user, from_host); - nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_CONTACT(contact), TAG_END()); - return 1; - } - - if (!(user = switch_xml_find_child(domain, "user", "id", from_user))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find user [%s@%s]\n", from_user, from_host); - nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_CONTACT(contact), TAG_END()); - switch_xml_free(xml); - return 1; - } - - if (!(xparams = switch_xml_child(user, "params"))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find params for user [%s@%s]\n", from_user, from_host); - nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_CONTACT(contact), TAG_END()); - switch_xml_free(xml); - return 1; - } - - - for (param = switch_xml_child(xparams, "param"); param; param = param->next) { - const char *var = switch_xml_attr_soft(param, "name"); - const char *val = switch_xml_attr_soft(param, "value"); - - if (!strcasecmp(var, "password")) { - passwd = val; - } - - if (!strcasecmp(var, "a1-hash")) { - a1_hash = val; - } - } - - if (passwd || a1_hash) { - switch_uuid_t uuid; - char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; - char *sql, *auth_str; - - su_md5_t ctx; - char hexdigest[2 * SU_MD5_DIGEST_SIZE + 1]; - char *input; - - if (!a1_hash) { - input = switch_mprintf("%s:%s:%s", from_user, from_host, passwd); - su_md5_init(&ctx); - su_md5_strupdate(&ctx, input); - su_md5_hexdigest(&ctx, hexdigest); - su_md5_deinit(&ctx); - switch_safe_free(input); - - switch_uuid_get(&uuid); - switch_uuid_format(uuid_str, &uuid); - a1_hash = hexdigest; - } - - sql = switch_mprintf("delete from sip_authentication where user='%q' and host='%q';\n" - "insert into sip_authentication values('%q','%q','%q','%q', %ld)", - from_user, from_host, from_user, from_host, a1_hash, uuid_str, time(NULL) + profile->nonce_ttl); - auth_str = - switch_mprintf("Digest realm=\"%q\", nonce=\"%q\",%s algorithm=MD5, qop=\"auth\"", from_host, uuid_str, stale ? " stale=\"true\"," : ""); - - - if (regtype == REG_REGISTER) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Requesting Registration from: [%s@%s]\n", from_user, from_host); - nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END()); - } else if (regtype == REG_INVITE) { - nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED, NUTAG_WITH_THIS(nua), SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END()); - - } - - execute_sql(profile->dbname, sql, profile->ireg_mutex); - switch_safe_free(sql); - switch_safe_free(auth_str); - ret = 1; - } else { - ret = 0; - } - - switch_xml_free(xml); - - if (ret) { - return ret; - } - } - reg: - - if (exptime) { - if (!find_reg_url(profile, from_user, from_host, buf, sizeof(buf))) { - sql = switch_mprintf("insert into sip_registrations values ('%q','%q','%q','Registered', '%q', %ld)", - from_user, from_host, contact_str, rpid, (long) time(NULL) + (long) exptime * 2); - - } else { - sql = - switch_mprintf - ("update sip_registrations set contact='%q', expires=%ld, rpid='%q' where user='%q' and host='%q'", - contact_str, (long) time(NULL) + (long) exptime * 2, rpid, from_user, from_host); - - } - - if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_REGISTER) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile-name", "%s", profile->name); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from-user", "%s", from_user); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from-host", "%s", from_host); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "contact", "%s", contact_str); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime); - switch_event_fire(&s_event); - } - - if (sql) { - execute_sql(profile->dbname, sql, profile->ireg_mutex); - switch_safe_free(sql); - sql = NULL; - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, - "Register:\nFrom: [%s@%s]\nContact: [%s]\nExpires: [%ld]\n", from_user, from_host, contact_str, (long) exptime); - - - if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "sip"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); - - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "Registered"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); - switch_event_fire(&event); - } - } else { - if ((sql = switch_mprintf("delete from sip_subscriptions where user='%q' and host='%q'", from_user, from_host))) { - execute_sql(profile->dbname, sql, profile->ireg_mutex); - switch_safe_free(sql); - sql = NULL; - } - if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_OUT) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "sip"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s+%s@%s", SOFIA_CHAT_PROTO, from_user, from_host); - - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "unavailable"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); - switch_event_fire(&event); - } - } - - - if (switch_event_create(&event, SWITCH_EVENT_ROSTER) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "sip"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); - switch_event_fire(&event); - } - - if (regtype == REG_REGISTER) { - nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(contact), NUTAG_WITH_THIS(nua), TAG_END()); - return 1; - } - - return 0; -} - - -static int sub_reg_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - sofia_profile_t *profile = (sofia_profile_t *) pArg; - //char *proto = argv[0]; - char *user = argv[1]; - char *host = argv[2]; - switch_event_t *event; - char *status = NULL; - if (switch_strlen_zero(status)) { - status = "Available"; - } - if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", user, host); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", status); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_subtype", "probe"); - switch_event_fire(&event); - } - - return 0; -} - -static int resub_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - sofia_profile_t *profile = (sofia_profile_t *) pArg; - char *user = argv[0]; - char *host = argv[1]; - char *status = argv[2]; - char *rpid = argv[3]; - char *proto = argv[4]; - switch_event_t *event; - - if (switch_strlen_zero(proto)) { - proto = NULL; - } - - if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "%s", proto ? proto : SOFIA_CHAT_PROTO); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", user, host); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", status); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); - switch_event_fire(&event); - } - - return 0; -} - -static int sub_callback(void *pArg, int argc, char **argv, char **columnNames) -{ - sofia_profile_t *profile = (sofia_profile_t *) pArg; - char *pl; - char *id, *note; - uint32_t in = atoi(argv[0]); - char *status = argv[1]; - char *rpid = argv[2]; - char *proto = argv[3]; - char *user = argv[4]; - char *host = argv[5]; - char *sub_to_user = argv[6]; - char *sub_to_host = argv[7]; - char *event = argv[8]; - char *contact = argv[9]; - char *callid = argv[10]; - char *full_from = argv[11]; - char *full_via = argv[12]; - nua_handle_t *nh; - char *to; - char *open; - char *tmp; - - if (!rpid) { - rpid = "unknown"; - } - - if (in) { - note = switch_mprintf("%s", status); - open = "open"; - } else { - note = NULL; - open = "closed"; - } - - if (!strcasecmp(sub_to_host, host)) { - /* same host */ - id = switch_mprintf("sip:%s+%s@%s", proto, sub_to_user, sub_to_host); - } else if (strcasecmp(proto, SOFIA_CHAT_PROTO)) { - /*encapsulate */ - id = switch_mprintf("sip:%s+%s+%s@%s", proto, sub_to_user, sub_to_host, host); - } else { - id = switch_mprintf("sip:%s@%s", sub_to_user, sub_to_host); - } - - to = switch_mprintf("sip:%s@%s", user, host); - pl = switch_mprintf("\r\n" - "\r\n" - "\r\n" - "\r\n" - "%s\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" "\r\n" "%s\r\n" "", id, open, rpid, note); - - - - nh = nua_handle(profile->nua, NULL, TAG_END()); - tmp = contact; - contact = get_url_from_contact(tmp, 0); - - nua_notify(nh, - NUTAG_URL(contact), - SIPTAG_TO_STR(full_from), - SIPTAG_FROM_STR(id), - SIPTAG_CONTACT_STR(profile->url), - SIPTAG_CALL_ID_STR(callid), - SIPTAG_VIA_STR(full_via), - SIPTAG_SUBSCRIPTION_STATE_STR("active;expires=3600"), - SIPTAG_EVENT_STR(event), SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), SIPTAG_PAYLOAD_STR(pl), TAG_END()); - - switch_safe_free(id); - switch_safe_free(note); - switch_safe_free(pl); - switch_safe_free(to); - - return 0; -} - -static void sip_i_subscribe(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - if (sip) { - long exp, exp_raw; - sip_to_t const *to = sip->sip_to; - sip_from_t const *from = sip->sip_from; - sip_contact_t const *contact = sip->sip_contact; - char *from_user = NULL; - char *from_host = NULL; - char *to_user = NULL; - char *to_host = NULL; - char *sql, *event = NULL; - char *proto = "sip"; - char *d_user = NULL; - char *contact_str = ""; - char *call_id = NULL; - char *to_str = NULL; - char *full_from = NULL; - char *full_via = NULL; - switch_core_db_t *db; - char *errmsg; - char *sstr; - const char *display = "\"user\""; - switch_event_t *sevent; - - if (contact) { - char *port = (char *) contact->m_url->url_port; - - display = contact->m_display; - - if (switch_strlen_zero(display)) { - if (from) { - display = from->a_display; - if (switch_strlen_zero(display)) { - display = "\"user\""; - } - } - } else { - display = "\"user\""; - } - - if (!port) { - port = SOFIA_DEFAULT_PORT; - } - - if (contact->m_url->url_params) { - contact_str = switch_mprintf("%s ", - display, contact->m_url->url_user, contact->m_url->url_host, port, contact->m_url->url_params); - } else { - contact_str = switch_mprintf("%s ", display, contact->m_url->url_user, contact->m_url->url_host, port); - } - } - - if (to) { - to_str = switch_mprintf("sip:%s@%s", to->a_url->url_user, to->a_url->url_host); //, to->a_url->url_port); - } - - if (to) { - to_user = (char *) to->a_url->url_user; - to_host = (char *) to->a_url->url_host; - } - - - if (strstr(to_user, "ext+") || strstr(to_user, "user+") || strstr(to_user, "conf+")) { - char proto[80]; - char *p; - - switch_copy_string(proto, to_user, sizeof(proto)); - if ((p = strchr(proto, '+'))) { - *p = '\0'; - } - - if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); - switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "login", "%s", profile->name); - switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, to_host); - switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "rpid", "unknown"); - switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "status", "Click To Call"); - switch_event_fire(&sevent); - } - } - - if (strchr(to_user, '+')) { - char *h; - if ((proto = (d_user = strdup(to_user)))) { - if ((to_user = strchr(d_user, '+'))) { - *to_user++ = '\0'; - if ((h = strchr(to_user, '+')) || (h = strchr(to_user, '@'))) { - *h++ = '\0'; - to_host = h; - } - } - } - - if (!(proto && to_user && to_host)) { - nua_respond(nh, SIP_404_NOT_FOUND, NUTAG_WITH_THIS(nua), TAG_END()); - goto end; - } - } - - call_id = sip_header_as_string(profile->home, (void *) sip->sip_call_id); - event = sip_header_as_string(profile->home, (void *) sip->sip_event); - full_from = sip_header_as_string(profile->home, (void *) sip->sip_from); - full_via = sip_header_as_string(profile->home, (void *) sip->sip_via); - - exp_raw = (sip->sip_expires ? sip->sip_expires->ex_delta : 3600); - exp = (long) time(NULL) + exp_raw; - - if (sip && sip->sip_from) { - from_user = (char *) sip->sip_from->a_url->url_user; - from_host = (char *) sip->sip_from->a_url->url_host; - } else { - from_user = "n/a"; - from_host = "n/a"; - } - - if ((sql = switch_mprintf("delete from sip_subscriptions where " - "proto='%q' and user='%q' and host='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q';\n" - "insert into sip_subscriptions values ('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q',%ld)", - proto, - from_user, - from_host, - to_user, - to_host, event, proto, from_user, from_host, to_user, to_host, event, contact_str, call_id, full_from, full_via, exp))) { - execute_sql(profile->dbname, sql, profile->ireg_mutex); - switch_safe_free(sql); - } - - sstr = switch_mprintf("active;expires=%ld", exp_raw); - - nua_respond(nh, SIP_202_ACCEPTED, - NUTAG_WITH_THIS(nua), - SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_FROM(sip->sip_to), SIPTAG_TO(sip->sip_from), SIPTAG_CONTACT_STR(to_str), TAG_END()); - - - - switch_safe_free(sstr); - - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - goto end; - } - if ((sql = switch_mprintf("select * from sip_subscriptions where user='%q' and host='%q'", to_user, to_host, to_user, to_host))) { - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, sub_reg_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - switch_safe_free(sql); - } - switch_core_db_close(db); - end: - - if (event) { - su_free(profile->home, event); - } - if (call_id) { - su_free(profile->home, call_id); - } - if (full_from) { - su_free(profile->home, full_from); - } - if (full_via) { - su_free(profile->home, full_via); - } - - switch_safe_free(d_user); - switch_safe_free(to_str); - switch_safe_free(contact_str); - } -} - -static void sip_r_subscribe(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - -} - - -/*---------------------------------------*/ -static void sip_i_refer(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) -{ - /* Incoming refer */ - sip_from_t const *from; - sip_to_t const *to; - sip_refer_to_t const *refer_to; - private_object_t *tech_pvt = NULL; - char *etmp = NULL, *exten = NULL; - switch_channel_t *channel_a = NULL, *channel_b = NULL; - - tech_pvt = switch_core_session_get_private(session); - channel_a = switch_core_session_get_channel(session); - - if (!sip->sip_cseq || !(etmp = switch_mprintf("refer;id=%u", sip->sip_cseq->cs_seq))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - goto done; - } - - - if (switch_channel_test_flag(channel_a, CF_NOMEDIA)) { - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); - goto done; - } - - from = sip->sip_from; - to = sip->sip_to; - - if ((refer_to = sip->sip_refer_to)) { - if (profile->pflags & PFLAG_FULL_ID) { - exten = switch_mprintf("%s@%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host); - } else { - exten = (char *) refer_to->r_url->url_user; - } - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Process REFER to [%s@%s]\n", exten, (char *) refer_to->r_url->url_host); - - if (refer_to->r_url->url_headers) { - sip_replaces_t *replaces; - nua_handle_t *bnh; - char *rep; - - if ((rep = strchr(refer_to->r_url->url_headers, '='))) { - char *br_a = NULL, *br_b = NULL; - char *buf; - rep++; - - - - if ((buf = switch_core_session_alloc(session, strlen(rep) + 1))) { - rep = url_unescape(buf, (const char *) rep); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Replaces: [%s]\n", rep); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - goto done; - } - if ((replaces = sip_replaces_make(tech_pvt->home, rep)) - && (bnh = nua_handle_by_replaces(nua, replaces))) { - sofia_private_t *b_private = NULL; - private_object_t *b_tech_pvt = NULL; - switch_core_session_t *b_session = NULL; - - switch_channel_set_variable(channel_a, SOFIA_REPLACES_HEADER, rep); - if ((b_private = nua_handle_magic(bnh))) { - if (!(b_session = switch_core_session_locate(b_private->uuid))) { - goto done; - } - b_tech_pvt = (private_object_t *) switch_core_session_get_private(b_session); - channel_b = switch_core_session_get_channel(b_session); - - br_a = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE); - br_b = switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n", br_a, br_b); - - if (br_a && br_b) { - switch_ivr_uuid_bridge(br_a, br_b); - switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); - switch_set_flag_locked(tech_pvt, TFLAG_BYE); - switch_set_flag_locked(b_tech_pvt, TFLAG_BYE); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); - - } else { - if (!br_a && !br_b) { - switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); - switch_set_flag_locked(b_tech_pvt, TFLAG_XFER); - b_tech_pvt->xferto = switch_core_session_strdup(b_session, switch_core_session_get_uuid(session)); - } else if (!br_a && br_b) { - switch_core_session_t *br_b_session; - - if ((br_b_session = switch_core_session_locate(br_b))) { - private_object_t *br_b_tech_pvt = switch_core_session_get_private(br_b_session); - switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session); - - switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); - - switch_channel_clear_state_handler(br_b_channel, NULL); - switch_channel_set_state_flag(br_b_channel, CF_TRANSFER); - switch_channel_set_state(br_b_channel, CS_TRANSMIT); - - switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); - tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, b_tech_pvt->local_sdp_audio_ip); - tech_pvt->local_sdp_audio_port = b_tech_pvt->local_sdp_audio_port; - - tech_pvt->remote_sdp_audio_ip = switch_core_session_strdup(session, br_b_tech_pvt->remote_sdp_audio_ip); - tech_pvt->remote_sdp_audio_port = br_b_tech_pvt->remote_sdp_audio_port; - activate_rtp(tech_pvt); - - br_b_tech_pvt->kick = switch_core_session_strdup(br_b_session, switch_core_session_get_uuid(session)); - - - switch_core_session_rwunlock(br_b_session); - } - - switch_channel_hangup(channel_b, SWITCH_CAUSE_ATTENDED_TRANSFER); - } - switch_set_flag_locked(tech_pvt, TFLAG_BYE); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); - - } - if (b_session) { - switch_core_session_rwunlock(b_session); - } - } - nua_handle_unref(bnh); - } else { /* the other channel is on a different box, we have to go find them */ - if (exten && (br_a = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE))) { - switch_core_session_t *a_session; - switch_channel_t *channel = switch_core_session_get_channel(session); - - if ((a_session = switch_core_session_locate(br_a))) { - switch_core_session_t *tsession; - switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; - uint32_t timeout = 60; - char *tuuid_str; - - channel = switch_core_session_get_channel(a_session); - - exten = switch_mprintf("sofia/%s/%s@%s:%s", - profile->name, - (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host, refer_to->r_url->url_port); - - switch_channel_set_variable(channel, SOFIA_REPLACES_HEADER, rep); - - if (switch_ivr_originate(a_session, - &tsession, &cause, exten, timeout, &noop_state_handler, NULL, NULL, NULL) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel! [%s]\n", exten); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); - goto done; - } - - switch_core_session_rwunlock(a_session); - tuuid_str = switch_core_session_get_uuid(tsession); - switch_ivr_uuid_bridge(br_a, tuuid_str); - switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); - switch_set_flag_locked(tech_pvt, TFLAG_BYE); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); - } else { - goto error; - } - - } else { - error: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Transfer! [%s]\n", br_a); - switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), - TAG_END()); - } - } - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot parse Replaces!\n"); - } - goto done; - } - - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Refer-To\n"); - goto done; - } - - if (exten) { - switch_channel_t *channel = switch_core_session_get_channel(session); - char *br; - - if ((br = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) { - switch_core_session_t *b_session; - - if ((b_session = switch_core_session_locate(br))) { - switch_channel_set_variable(channel, "TRANSFER_FALLBACK", from->a_user); - switch_ivr_session_transfer(b_session, exten, profile->dialplan, profile->context); - switch_core_session_rwunlock(b_session); - } - - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "BLIND_TRANSFER"); - - /* - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), - SIPTAG_EVENT_STR(etmp), - TAG_END()); - */ - } else { - exten = switch_mprintf("sip:%s@%s:%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host, refer_to->r_url->url_port); - tech_pvt->dest = switch_core_session_strdup(session, exten); - - - switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); - - /* - nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), - NUTAG_SUBSTATE(nua_substate_terminated), - SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), - SIPTAG_EVENT_STR(etmp), - TAG_END()); - */ - do_xfer_invite(session); - - } - } - - done: - if (exten && strchr(exten, '@')) { - switch_safe_free(exten); - } - if (etmp) { - switch_safe_free(etmp); - } - - -} - - -static void sip_i_publish(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - if (sip) { - sip_from_t const *from = sip->sip_from; - char *from_user = NULL; - char *from_host = NULL; - char *rpid = "unknown"; - sip_payload_t *payload = sip->sip_payload; - char *event_type; - - if (from) { - from_user = (char *) from->a_url->url_user; - from_host = (char *) from->a_url->url_host; - } - - if (payload) { - switch_xml_t xml, note, person, tuple, status, basic, act; - switch_event_t *event; - uint8_t in = 0; - char *sql; - - if ((xml = switch_xml_parse_str(payload->pl_data, strlen(payload->pl_data)))) { - char *status_txt = "", *note_txt = ""; - - if ((tuple = switch_xml_child(xml, "tuple")) && (status = switch_xml_child(tuple, "status")) - && (basic = switch_xml_child(status, "basic"))) { - status_txt = basic->txt; - } - - if ((person = switch_xml_child(xml, "dm:person")) && (note = switch_xml_child(person, "dm:note"))) { - note_txt = note->txt; - } - - if (person && (act = switch_xml_child(person, "rpid:activities"))) { - if ((rpid = strchr(act->child->name, ':'))) { - rpid++; - } else { - rpid = act->child->name; - } - } - - if (!strcasecmp(status_txt, "open")) { - if (switch_strlen_zero(note_txt)) { - note_txt = "Available"; - } - in = 1; - } else if (!strcasecmp(status_txt, "closed")) { - if (switch_strlen_zero(note_txt)) { - note_txt = "Unavailable"; - } - } - - if ((sql = - switch_mprintf("update sip_registrations set status='%q',rpid='%q' where user='%q' and host='%q'", - note_txt, rpid, from_user, from_host))) { - execute_sql(profile->dbname, sql, profile->ireg_mutex); - switch_safe_free(sql); - } - - event_type = sip_header_as_string(profile->home, (void *) sip->sip_event); - - if (in) { - if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); - - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", note_txt); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "%s", event_type); - switch_event_fire(&event); - } - } else { - if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_OUT) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); - - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "%s", event_type); - switch_event_fire(&event); - } - } - - if (event_type) { - su_free(profile->home, event_type); - } - - switch_xml_free(xml); - } - - } - - } - - nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); - -} - -static void sip_i_info(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) -{ - - //placeholder for string searching - char *signal_ptr; - - //Try and find signal information in the payload - signal_ptr = strstr(sip->sip_payload->pl_data, "Signal="); - - //See if we found a match - if (signal_ptr) { - struct private_object *tech_pvt = NULL; - switch_channel_t *channel = NULL; - char dtmf_digit[2] = { 0, 0 }; - - //Get the channel - channel = switch_core_session_get_channel(session); - - //Barf if we didn't get it - assert(channel != NULL); - - //make sure we have our privates - tech_pvt = switch_core_session_get_private(session); - - //Barf if we didn't get it - assert(tech_pvt != NULL); - - //move signal_ptr where we need it (right past Signal=) - signal_ptr = signal_ptr + 7; - - //put the digit somewhere we can muck with - strncpy(dtmf_digit, signal_ptr, 1); - - //queue it up - switch_channel_queue_dtmf(channel, dtmf_digit); - - //print debug info - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INFO DTMF(%s)\n", dtmf_digit); - - } else { //unknown info type - sip_from_t const *from; - - from = sip->sip_from; - - //print in the logs if something comes through we don't understand - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unknown INFO Recieved: %s%s" URL_PRINT_FORMAT "[%s]\n", - from->a_display ? from->a_display : "", from->a_display ? " " : "", URL_PRINT_ARGS(from->a_url), sip->sip_payload->pl_data); - } - - return; -} - - -#define url_set_chanvars(session, url, varprefix) _url_set_chanvars(session, url, #varprefix "_user", #varprefix "_host", #varprefix "_port", #varprefix "_uri") -static const char *_url_set_chanvars(switch_core_session_t *session, url_t * url, const char *user_var, - const char *host_var, const char *port_var, const char *uri_var) -{ - const char *user = NULL, *host = NULL, *port = NULL; - char *uri = NULL; - switch_channel_t *channel = switch_core_session_get_channel(session); - - if (url) { - user = url->url_user; - host = url->url_host; - port = url->url_port; - } - - if (user) { - switch_channel_set_variable(channel, user_var, user); - } - - if (!port) { - port = SOFIA_DEFAULT_PORT; - } - - switch_channel_set_variable(channel, port_var, port); - if (host) { - if (user) { - uri = switch_core_session_sprintf(session, "%s@%s:%s", user, host, port); - } else { - uri = switch_core_session_sprintf(session, "%s:%s", host, port); - } - switch_channel_set_variable_nodup(channel, uri_var, uri); - switch_channel_set_variable(channel, host_var, host); - } - - return uri; -} - -static void process_rpid(sip_unknown_t * un, private_object_t * tech_pvt) -{ - int argc, x, screen = 1; - char *mydata, *argv[10] = { 0 }; - if (!switch_strlen_zero(un->un_value)) { - if ((mydata = strdup(un->un_value))) { - argc = switch_separate_string(mydata, ';', argv, (sizeof(argv) / sizeof(argv[0]))); - - // Do We really need this at this time - // clid_uri = argv[0]; - - for (x = 1; x < argc && argv[x]; x++) { - // we dont need to do anything with party yet we should only be seeing party=calling here anyway - // maybe thats a dangerous assumption bit oh well yell at me later - // if (!strncasecmp(argv[x], "party", 5)) { - // party = argv[x]; - // } else - if (!strncasecmp(argv[x], "privacy=", 8)) { - char *arg = argv[x] + 9; - - if (!strcasecmp(arg, "yes")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); - } else if (!strcasecmp(arg, "full")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); - } else if (!strcasecmp(arg, "name")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME); - } else if (!strcasecmp(arg, "number")) { - switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NUMBER); - } else { - switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME); - switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NUMBER); - } - - } else if (!strncasecmp(argv[x], "screen=", 7) && screen > 0) { - char *arg = argv[x] + 8; - if (!strcasecmp(arg, "no")) { - screen = 0; - switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN); - } - } - } - free(mydata); - } - } -} - -static void sip_i_invite(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - switch_core_session_t *session = NULL; - char key[128] = ""; - sip_unknown_t *un; - private_object_t *tech_pvt = NULL; - switch_channel_t *channel = NULL; - const char *channel_name = NULL; - const char *displayname = NULL; - const char *destination_number = NULL; - const char *from_user = NULL, *from_host = NULL; - const char *context = NULL; - char network_ip[80]; - - if (!sip || !sip->sip_request || !sip->sip_request->rq_method_name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received an invalid packet!\n"); - nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); - return; - } - - - if (!(sip->sip_contact && sip->sip_contact->m_url)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n"); - nua_respond(nh, 400, "Missing Contact Header", TAG_END()); - return; - } - - if ((profile->pflags & PFLAG_AUTH_CALLS)) { - if (handle_register(nua, profile, nh, sip, REG_INVITE, key, sizeof(key))) { - return; - } - } - - if (!(session = switch_core_session_request(&sofia_endpoint_interface, NULL))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Session Alloc Failed!\n"); - nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, TAG_END()); - return; - } - - if (!(tech_pvt = (private_object_t *) switch_core_session_alloc(session, sizeof(private_object_t)))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); - nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, TAG_END()); - terminate_session(&session, SWITCH_CAUSE_SWITCH_CONGESTION, __LINE__); - return; - } - - if (!switch_strlen_zero(key)) { - tech_pvt->key = switch_core_session_strdup(session, key); - } - - get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_addr); - - channel = switch_core_session_get_channel(session); - - if (sip->sip_from && sip->sip_from->a_url) { - from_user = sip->sip_from->a_url->url_user; - from_host = sip->sip_from->a_url->url_host; - channel_name = url_set_chanvars(session, sip->sip_from->a_url, sip_from); - - if (!switch_strlen_zero(from_user)) { - if (*from_user == '+') { - switch_channel_set_variable(channel, "sip_from_user_stripped", (const char *) (from_user + 1)); - } else { - switch_channel_set_variable(channel, "sip_from_user_stripped", from_user); - } - } - - if (!switch_strlen_zero(sip->sip_from->a_display)) { - char *tmp; - tmp = switch_core_session_strdup(session, sip->sip_from->a_display); - if (*tmp == '"') { - char *p; - - tmp++; - if ((p = strchr(tmp, '"'))) { - *p = '\0'; - } - } - displayname = tmp; - } else { - displayname = switch_strlen_zero(from_user) ? "unkonwn" : from_user; - } - } - - if (sip->sip_request->rq_url) { - const char *req_uri = url_set_chanvars(session, sip->sip_request->rq_url, sip_req); - if (profile->pflags & PFLAG_FULL_ID) { - destination_number = req_uri; - } else { - destination_number = sip->sip_request->rq_url->url_user; - } - } - - if (sip->sip_to && sip->sip_to->a_url) { - url_set_chanvars(session, sip->sip_to->a_url, sip_to); - } - - if (sip->sip_contact && sip->sip_contact->m_url) { - const char *contact_uri = url_set_chanvars(session, sip->sip_contact->m_url, sip_contact); - if (!channel_name) { - channel_name = contact_uri; - } - } - - attach_private(session, profile, tech_pvt, channel_name); - tech_set_codecs(tech_pvt); - - switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "INBOUND CALL"); - set_chat_hash(tech_pvt, sip); - - if (switch_test_flag(tech_pvt, TFLAG_INB_NOMEDIA)) { - switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); - switch_channel_set_flag(channel, CF_NOMEDIA); - } - - if (!tech_pvt->call_id && sip->sip_call_id && sip->sip_call_id->i_id) { - tech_pvt->call_id = switch_core_session_strdup(session, sip->sip_call_id->i_id); - switch_channel_set_variable(channel, "sip_call_id", tech_pvt->call_id); - } - - if (sip->sip_via) { - if (sip->sip_via->v_host) { - switch_channel_set_variable(channel, "sip_via_host", sip->sip_via->v_host); - } - if (sip->sip_via->v_port) { - switch_channel_set_variable(channel, "sip_via_port", sip->sip_via->v_port); - } - if (sip->sip_via->v_rport) { - switch_channel_set_variable(channel, "sip_via_rport", sip->sip_via->v_rport); - } - } - - if (sip->sip_max_forwards) { - char max_forwards[32]; - snprintf(max_forwards, sizeof(max_forwards), "%lu", sip->sip_max_forwards->mf_count); - switch_channel_set_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE, max_forwards); - } - - - if (sip->sip_request->rq_url) { - outbound_reg_t *gateway; - char *from_key = switch_core_session_sprintf(session, "sip:%s@%s", - (char *) sip->sip_request->rq_url->url_user, - (char *) sip->sip_request->rq_url->url_host); - - if ((gateway = find_gateway(from_key))) { - context = gateway->register_context; - switch_channel_set_variable(channel, "sip_gateway", gateway->name); - } - } - - - if (!context) { - if (profile->context && !strcasecmp(profile->context, "_domain_")) { - context = from_host; - } else { - context = profile->context; - } - } - - tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), - from_user, - profile->dialplan, - displayname, from_user, network_ip, NULL, NULL, NULL, modname, context, destination_number); - - if (tech_pvt->caller_profile) { - - /* Loop thru unknown Headers Here so we can do something with them */ - for (un = sip->sip_unknown; un; un = un->un_next) { - if (!strncasecmp(un->un_name, "Alert-Info", 10)) { - if (!switch_strlen_zero(un->un_value)) { - switch_channel_set_variable(channel, "alert_info", un->un_value); - } - } else if (!strncasecmp(un->un_name, "Remote-Party-ID", 15)) { - process_rpid(un, tech_pvt); - } else if (!strncasecmp(un->un_name, "X-", 2)) { - if (!switch_strlen_zero(un->un_value)) { - char *new_name; - if ((new_name = switch_mprintf("%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name))) { - switch_channel_set_variable(channel, new_name, un->un_value); - free(new_name); - } - } - } - } - - switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); - } - - if (!(tech_pvt->sofia_private = malloc(sizeof(*tech_pvt->sofia_private)))) { - abort(); - } - memset(tech_pvt->sofia_private, 0, sizeof(*tech_pvt->sofia_private)); - switch_copy_string(tech_pvt->sofia_private->uuid, switch_core_session_get_uuid(session), sizeof(tech_pvt->sofia_private->uuid)); - nua_handle_bind(nh, tech_pvt->sofia_private); - tech_pvt->nh = nh; -} - -static void sip_i_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - char key[128] = ""; - - if (!sip || !sip->sip_request || !sip->sip_request->rq_method_name) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received an invalid packet!\n"); - nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); - return; - } - - if (!(sip->sip_contact && sip->sip_contact->m_url)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n"); - nua_respond(nh, 400, "Missing Contact Header", TAG_END()); - return; - } - - handle_register(nua, profile, nh, sip, REG_REGISTER, key, sizeof(key)); -} - - -static void sip_i_options(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - nua_respond(nh, SIP_200_OK, - //SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), - //SOATAG_AUDIO_AUX("cn telephone-event"), - //NUTAG_INCLUDE_EXTRA_SDP(1), - TAG_END()); -} - - -static void sip_r_register(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - if (sofia_private && sofia_private->gateway) { - switch (status) { - case 200: - if (sip && sip->sip_contact && sip->sip_contact->m_expires) { - char *new_expires = (char *) sip->sip_contact->m_expires; - uint32_t expi = (uint32_t) atoi(new_expires); - - if (expi != sofia_private->gateway->freq) { - sofia_private->gateway->freq = expi; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, - "Changing expire time to %d by request of proxy %s\n", expi, sofia_private->gateway->register_proxy); - } - - } - sofia_private->gateway->state = REG_STATE_REGISTER; - break; - case 100: - break; - default: - sofia_private->gateway->state = REG_STATE_FAILED; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Registration Failed with status %d\n", status); - break; - } - } -} - -static void sip_r_challenge(int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) -{ - outbound_reg_t *gateway = NULL; - sip_www_authenticate_t const *authenticate = NULL; - char const *realm = NULL; - char *p = NULL, *duprealm = NULL, *qrealm = NULL; - char const *scheme = NULL; - int indexnum; - char *cur; - char authentication[256] = ""; - int ss_state; - - if (session) { - private_object_t *tech_pvt; - if ((tech_pvt = switch_core_session_get_private(session)) && switch_test_flag(tech_pvt, TFLAG_REFER)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "received reply from refer\n"); - return; - } - } - - - if (sip->sip_www_authenticate) { - authenticate = sip->sip_www_authenticate; - } else if (sip->sip_proxy_authenticate) { - authenticate = sip->sip_proxy_authenticate; - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Missing Authenticate Header!\n"); - return; - } - scheme = (char const *) authenticate->au_scheme; - if (authenticate->au_params) { - for (indexnum = 0; (cur = (char *) authenticate->au_params[indexnum]); indexnum++) { - if ((realm = strstr(cur, "realm="))) { - realm += 6; - break; - } - } - } - - if (!(scheme && realm)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No scheme and realm!\n"); - return; - } - - if (profile) { - outbound_reg_t *gateway_ptr; - - if ((duprealm = strdup(realm))) { - qrealm = duprealm; - - while (*qrealm && *qrealm == '"') { - qrealm++; - } - - if ((p = strchr(qrealm, '"'))) { - *p = '\0'; - } - - if (sip->sip_from) { - char *from_key = switch_mprintf("sip:%s@%s", - (char *) sip->sip_from->a_url->url_user, - (char *) sip->sip_from->a_url->url_host); - - if (!(gateway = find_gateway(from_key))) { - gateway = find_gateway(qrealm); - } - - switch_safe_free(from_key); - } - - if (!gateway) { - for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { - if (scheme && qrealm && !strcasecmp(gateway_ptr->register_scheme, scheme) - && !strcasecmp(gateway_ptr->register_realm, qrealm)) { - gateway = gateway_ptr; - break; - } - } - } - - if (!gateway) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Match for Scheme [%s] Realm [%s]\n", scheme, qrealm); - return; - } - switch_safe_free(duprealm); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - return; - } - } - - snprintf(authentication, sizeof(authentication), "%s:%s:%s:%s", scheme, realm, gateway->register_username, gateway->register_password); - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Authenticating '%s' with '%s'.\n", profile->username, authentication); - - - ss_state = nua_callstate_authenticating; - - tl_gets(tags, NUTAG_CALLSTATE_REF(ss_state), SIPTAG_WWW_AUTHENTICATE_REF(authenticate), TAG_END()); - - nua_authenticate(nh, SIPTAG_EXPIRES_STR(gateway->expires_str), NUTAG_AUTH(authentication), TAG_END()); - -} - -static void event_callback(nua_event_t event, - int status, - char const *phrase, - nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) -{ - struct private_object *tech_pvt = NULL; - auth_res_t auth_res = AUTH_FORBIDDEN; - switch_core_session_t *session = NULL; - switch_channel_t *channel = NULL; - - if (sofia_private) { - if (!switch_strlen_zero(sofia_private->uuid)) { - - if ((session = switch_core_session_locate(sofia_private->uuid))) { - tech_pvt = switch_core_session_get_private(session); - channel = switch_core_session_get_channel(tech_pvt->session); - if (switch_channel_test_flag(channel, CF_NOMEDIA)) { - switch_set_flag(tech_pvt, TFLAG_NOMEDIA); - } - if (!tech_pvt->call_id && sip && sip->sip_call_id && sip->sip_call_id->i_id) { - tech_pvt->call_id = switch_core_session_strdup(session, (char *) sip->sip_call_id->i_id); - switch_channel_set_variable(channel, "sip_call_id", tech_pvt->call_id); - } - } else { - /* too late */ - return; - } - } - } - - - if (status != 100 && status != 200) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "event [%s] status [%d][%s] session: %s\n", - nua_event_name(event), status, phrase, session ? switch_channel_get_name(channel) : "n/a"); - } - - if ((profile->pflags & PFLAG_AUTH_ALL) && tech_pvt && tech_pvt->key && sip) { - sip_authorization_t const *authorization = NULL; - - if (sip->sip_authorization) { - authorization = sip->sip_authorization; - } else if (sip->sip_proxy_authorization) { - authorization = sip->sip_proxy_authorization; - } - - if (authorization) { - auth_res = parse_auth(profile, authorization, (char *) sip->sip_request->rq_method_name, tech_pvt->key, strlen(tech_pvt->key)); - } - - if (auth_res != AUTH_OK) { - switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); - nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_END()); - goto done; - } - - if (channel) { - switch_channel_set_variable(channel, "sip_authorized", "true"); - } - } - - if (sip && (status == 401 || status == 407)) { - sip_r_challenge(status, phrase, nua, profile, nh, session, sip, tags); - goto done; - } - - switch (event) { - case nua_r_shutdown: - case nua_r_get_params: - case nua_r_invite: - case nua_r_unregister: - case nua_r_options: - case nua_i_fork: - case nua_r_info: - case nua_r_bye: - case nua_i_bye: - case nua_r_unsubscribe: - case nua_r_publish: - case nua_r_message: - case nua_r_notify: - case nua_i_notify: - case nua_i_cancel: - case nua_i_error: - case nua_i_active: - case nua_i_ack: - case nua_i_terminated: - case nua_r_set_params: - break; - case nua_r_register: - sip_r_register(status, phrase, nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_options: - sip_i_options(status, phrase, nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_invite: - if (!session) { - sip_i_invite(nua, profile, nh, sofia_private, sip, tags); - } - break; - case nua_i_publish: - sip_i_publish(nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_register: - sip_i_register(nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_prack: - break; - case nua_i_state: - sip_i_state(status, phrase, nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_message: - sip_i_message(status, phrase, nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_info: - sip_i_info(nua, profile, nh, session, sip, tags); - break; - case nua_r_refer: - break; - case nua_i_refer: - if (session) { - sip_i_refer(nua, profile, nh, session, sip, tags); - } - break; - case nua_r_subscribe: - sip_r_subscribe(status, phrase, nua, profile, nh, sofia_private, sip, tags); - break; - case nua_i_subscribe: - sip_i_subscribe(status, phrase, nua, profile, nh, sofia_private, sip, tags); - break; - default: - if (status > 100) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: unknown event %d: %03d %s\n", nua_event_name(event), event, status, phrase); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: unknown event %d\n", nua_event_name(event), event); - } - break; - } - - done: - - if (session) { - switch_core_session_rwunlock(session); - } -} - - -static void unreg(sofia_profile_t * profile) -{ - outbound_reg_t *gateway_ptr; - for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { - if (gateway_ptr->sofia_private) { - free(gateway_ptr->sofia_private); - nua_handle_bind(gateway_ptr->nh, NULL); - gateway_ptr->sofia_private = NULL; - } - nua_handle_destroy(gateway_ptr->nh); - } -} - -static void check_gateway(sofia_profile_t * profile, time_t now) -{ - outbound_reg_t *gateway_ptr; - for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { - int ss_state = nua_callstate_authenticating; - reg_state_t ostate = gateway_ptr->state; - - switch (ostate) { - case REG_STATE_REGISTER: - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "registered %s\n", gateway_ptr->name); - gateway_ptr->expires = now + gateway_ptr->freq; - gateway_ptr->state = REG_STATE_REGED; - break; - case REG_STATE_UNREGED: - if ((gateway_ptr->nh = nua_handle(gateway_ptr->profile->nua, NULL, - NUTAG_URL(gateway_ptr->register_proxy), - SIPTAG_TO_STR(gateway_ptr->register_to), - NUTAG_CALLSTATE_REF(ss_state), SIPTAG_FROM_STR(gateway_ptr->register_from), TAG_END()))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "registering %s\n", gateway_ptr->name); - - if (!(gateway_ptr->sofia_private = malloc(sizeof(*gateway_ptr->sofia_private)))) { - abort(); - } - memset(gateway_ptr->sofia_private, 0, sizeof(*gateway_ptr->sofia_private)); - - gateway_ptr->sofia_private->gateway = gateway_ptr; - nua_handle_bind(gateway_ptr->nh, gateway_ptr->sofia_private); - - nua_register(gateway_ptr->nh, - SIPTAG_FROM_STR(gateway_ptr->register_from), - SIPTAG_CONTACT_STR(gateway_ptr->register_contact), - SIPTAG_EXPIRES_STR(gateway_ptr->expires_str), - NUTAG_REGISTRAR(gateway_ptr->register_proxy), - NUTAG_OUTBOUND("no-options-keepalive"), NUTAG_OUTBOUND("no-validate"), NUTAG_KEEPALIVE(0), TAG_NULL()); - gateway_ptr->retry = now + 10; - gateway_ptr->state = REG_STATE_TRYING; - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error registering %s\n", gateway_ptr->name); - gateway_ptr->state = REG_STATE_FAILED; - } - break; - - case REG_STATE_TRYING: - if (gateway_ptr->retry && now >= gateway_ptr->retry) { - gateway_ptr->state = REG_STATE_UNREGED; - gateway_ptr->retry = 0; - } - break; - default: - if (now >= gateway_ptr->expires) { - gateway_ptr->state = REG_STATE_UNREGED; - } - break; - } - } - -} - -#define IREG_SECONDS 30 -#define GATEWAY_SECONDS 1 -static void *SWITCH_THREAD_FUNC profile_thread_run(switch_thread_t * thread, void *obj) -{ - sofia_profile_t *profile = (sofia_profile_t *) obj; - switch_memory_pool_t *pool; - sip_alias_node_t *node; - uint32_t ireg_loops = 0; - uint32_t gateway_loops = 0; - switch_core_db_t *db; - switch_event_t *s_event; - - profile->s_root = su_root_create(NULL); - profile->home = su_home_new(sizeof(*profile->home)); - - profile->nua = nua_create(profile->s_root, /* Event loop */ - event_callback, /* Callback for processing events */ - profile, /* Additional data to pass to callback */ - NUTAG_URL(profile->bindurl), NTATAG_UDP_MTU(65536), TAG_END()); /* Last tag should always finish the sequence */ - - nua_set_params(profile->nua, - //NUTAG_EARLY_MEDIA(1), - NUTAG_AUTOANSWER(0), - NUTAG_AUTOALERT(0), - NUTAG_ALLOW("REGISTER"), - NUTAG_ALLOW("REFER"), - NUTAG_ALLOW("INFO"), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("PUBLISH")), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("NOTIFY")), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("SUBSCRIBE")), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ENABLEMESSAGE(1)), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("presence")), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("presence.winfo")), - SIPTAG_SUPPORTED_STR("100rel, precondition"), SIPTAG_USER_AGENT_STR(SOFIA_USER_AGENT), TAG_END()); - - - for (node = profile->aliases; node; node = node->next) { - node->nua = nua_create(profile->s_root, /* Event loop */ - event_callback, /* Callback for processing events */ - profile, /* Additional data to pass to callback */ - NUTAG_URL(node->url), TAG_END()); /* Last tag should always finish the sequence */ - - nua_set_params(node->nua, - NUTAG_EARLY_MEDIA(1), - NUTAG_AUTOANSWER(0), - NUTAG_AUTOALERT(0), - NUTAG_ALLOW("REGISTER"), - NUTAG_ALLOW("REFER"), - NUTAG_ALLOW("INFO"), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("PUBLISH")), - TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ENABLEMESSAGE(1)), - SIPTAG_SUPPORTED_STR("100rel, precondition"), SIPTAG_USER_AGENT_STR(SOFIA_USER_AGENT), TAG_END()); - - } - - - if ((db = switch_core_db_open_file(profile->dbname))) { - switch_core_db_test_reactive(db, "select contact from sip_registrations", reg_sql); - switch_core_db_test_reactive(db, "select contact from sip_subscriptions", sub_sql); - switch_core_db_test_reactive(db, "select * from sip_authentication", auth_sql); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database!\n"); - return NULL; - } - - - switch_mutex_init(&profile->ireg_mutex, SWITCH_MUTEX_NESTED, profile->pool); - switch_mutex_init(&profile->gateway_mutex, SWITCH_MUTEX_NESTED, profile->pool); - - ireg_loops = IREG_SECONDS; - gateway_loops = GATEWAY_SECONDS; - - if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._udp"); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); - switch_event_fire(&s_event); - } - - if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._tcp"); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); - switch_event_fire(&s_event); - } - - if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._sctp"); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); - switch_event_fire(&s_event); - } - - add_profile(profile->name, profile); - - if (profile->pflags & PFLAG_PRESENCE) { - establish_presence(profile); - } - - while (globals.running == 1) { - if (++ireg_loops >= IREG_SECONDS) { - check_expire(db, profile, time(NULL)); - ireg_loops = 0; - } - - if (++gateway_loops >= GATEWAY_SECONDS) { - check_gateway(profile, time(NULL)); - gateway_loops = 0; - } - - su_root_step(profile->s_root, 1000); - } - - switch_core_db_close(db); - unreg(profile); - su_home_unref(profile->home); - - - if (switch_event_create(&s_event, SWITCH_EVENT_UNPUBLISH) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._udp"); - switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); - switch_event_fire(&s_event); - } - - su_root_destroy(profile->s_root); - pool = profile->pool; - switch_core_destroy_memory_pool(&pool); - switch_mutex_lock(globals.mutex); - globals.running = 0; - switch_mutex_unlock(globals.mutex); - - return NULL; -} - -static void launch_profile_thread(sofia_profile_t * profile) -{ - switch_thread_t *thread; - switch_threadattr_t *thd_attr = NULL; - - switch_threadattr_create(&thd_attr, profile->pool); - switch_threadattr_detach_set(thd_attr, 1); - switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); - switch_thread_create(&thread, thd_attr, profile_thread_run, profile, profile->pool); -} - - - -static switch_status_t config_sofia(int reload) -{ - char *cf = "sofia.conf"; - switch_xml_t cfg, xml = NULL, xprofile, param, settings, profiles, gateway_tag, gateways_tag; - switch_status_t status = SWITCH_STATUS_SUCCESS; - sofia_profile_t *profile = NULL; - char url[512] = ""; - switch_mutex_lock(globals.mutex); - globals.running = 1; - switch_mutex_unlock(globals.mutex); - - if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); - status = SWITCH_STATUS_FALSE; - goto done; - } - - if ((settings = switch_xml_child(cfg, "global_settings"))) { - for (param = switch_xml_child(settings, "param"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - char *val = (char *) switch_xml_attr_soft(param, "value"); - if (!strcasecmp(var, "log-level")) { - su_log_set_level(NULL, atoi(val)); - } else if (!strcasecmp(var, "log-level-trace")) { - su_log_set_level(tport_log, atoi(val)); - } - } - } - - if ((profiles = switch_xml_child(cfg, "profiles"))) { - for (xprofile = switch_xml_child(profiles, "profile"); xprofile; xprofile = xprofile->next) { - if (!(settings = switch_xml_child(xprofile, "settings"))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Settings, check the new config!\n"); - } else { - char *xprofilename = (char *) switch_xml_attr_soft(xprofile, "name"); - switch_memory_pool_t *pool = NULL; - - - /* Setup the pool */ - if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); - goto done; - } - - if (!(profile = (sofia_profile_t *) switch_core_alloc(pool, sizeof(*profile)))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); - goto done; - } - - if (!xprofilename) { - xprofilename = "unnamed"; - } - - profile->pool = pool; - - profile->name = switch_core_strdup(profile->pool, xprofilename); - snprintf(url, sizeof(url), "sofia_reg_%s", xprofilename); - profile->dbname = switch_core_strdup(profile->pool, url); - switch_core_hash_init(&profile->chat_hash, profile->pool); - - profile->dtmf_duration = 100; - profile->codec_ms = 20; - - for (param = switch_xml_child(settings, "param"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - char *val = (char *) switch_xml_attr_soft(param, "value"); - - if (!strcasecmp(var, "debug")) { - profile->debug = atoi(val); - } else if (!strcasecmp(var, "use-rtp-timer") && switch_true(val)) { - switch_set_flag(profile, TFLAG_TIMER); - } else if (!strcasecmp(var, "inbound-no-media") && switch_true(val)) { - switch_set_flag(profile, TFLAG_INB_NOMEDIA); - } else if (!strcasecmp(var, "inbound-late-negotiation") && switch_true(val)) { - switch_set_flag(profile, TFLAG_LATE_NEGOTIATION); - } else if (!strcasecmp(var, "rfc2833-pt")) { - profile->te = (switch_payload_t) atoi(val); - } else if (!strcasecmp(var, "cng-pt")) { - profile->cng_pt = (switch_payload_t) atoi(val); - } else if (!strcasecmp(var, "sip-port")) { - profile->sip_port = atoi(val); - } else if (!strcasecmp(var, "vad")) { - if (!strcasecmp(val, "in")) { - switch_set_flag(profile, TFLAG_VAD_IN); - } else if (!strcasecmp(val, "out")) { - switch_set_flag(profile, TFLAG_VAD_OUT); - } else if (!strcasecmp(val, "both")) { - switch_set_flag(profile, TFLAG_VAD_IN); - switch_set_flag(profile, TFLAG_VAD_OUT); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invald option %s for VAD\n", val); - } - } else if (!strcasecmp(var, "ext-rtp-ip")) { - profile->extrtpip = switch_core_strdup(profile->pool, strcasecmp(val, "auto") ? val : globals.guess_ip); - } else if (!strcasecmp(var, "rtp-ip")) { - profile->rtpip = switch_core_strdup(profile->pool, strcasecmp(val, "auto") ? val : globals.guess_ip); - } else if (!strcasecmp(var, "sip-ip")) { - profile->sipip = switch_core_strdup(profile->pool, strcasecmp(val, "auto") ? val : globals.guess_ip); - } else if (!strcasecmp(var, "ext-sip-ip")) { - if (!strcasecmp(val, "auto")) { - profile->extsipip = switch_core_strdup(profile->pool, globals.guess_ip); - } else { - char *ip = NULL; - switch_port_t port = 0; - if (sofia_ext_address_lookup(&ip, &port, val, profile->pool) == SWITCH_STATUS_SUCCESS) { - - if (ip) { - profile->extsipip = switch_core_strdup(profile->pool, ip); - } - } - } - - } else if (!strcasecmp(var, "sip-domain")) { - profile->sipdomain = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "rtp-timer-name")) { - profile->timer_name = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "hold-music")) { - profile->hold_music = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "manage-presence")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_PRESENCE; - } - } else if (!strcasecmp(var, "pass-rfc2833")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_PASS_RFC2833; - } - } else if (!strcasecmp(var, "disable-transcoding")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_DISABLE_TRANSCODING; - } - } else if (!strcasecmp(var, "auth-calls")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_AUTH_CALLS; - } - } else if (!strcasecmp(var, "nonce-ttl")) { - profile->nonce_ttl = atoi(val); - } else if (!strcasecmp(var, "accept-blind-reg")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_BLIND_REG; - } - } else if (!strcasecmp(var, "auth-all-packets")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_AUTH_ALL; - } - } else if (!strcasecmp(var, "full-id-in-dialplan")) { - if (switch_true(val)) { - profile->pflags |= PFLAG_FULL_ID; - } - } else if (!strcasecmp(var, "bitpacking")) { - if (!strcasecmp(val, "aal2")) { - profile->codec_flags = SWITCH_CODEC_FLAG_AAL2; - } - } else if (!strcasecmp(var, "username")) { - profile->username = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "context")) { - profile->context = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "alias")) { - sip_alias_node_t *node; - if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) { - if ((node->url = switch_core_strdup(profile->pool, val))) { - node->next = profile->aliases; - profile->aliases = node; - } - } - } else if (!strcasecmp(var, "dialplan")) { - profile->dialplan = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "max-calls")) { - profile->max_calls = atoi(val); - } else if (!strcasecmp(var, "codec-prefs")) { - profile->codec_string = switch_core_strdup(profile->pool, val); - } else if (!strcasecmp(var, "codec-ms")) { - profile->codec_ms = atoi(val); - } else if (!strcasecmp(var, "dtmf-duration")) { - int dur = atoi(val); - if (dur > 10 && dur < 8000) { - profile->dtmf_duration = dur; - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Duration out of bounds!\n"); - } - } - } - - if (!profile->cng_pt) { - profile->cng_pt = SWITCH_RTP_CNG_PAYLOAD; - } - - if (!profile->sipip) { - profile->sipip = switch_core_strdup(profile->pool, globals.guess_ip); - } - - if (!profile->rtpip) { - profile->rtpip = switch_core_strdup(profile->pool, globals.guess_ip); - } - - if (profile->nonce_ttl < 60) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting nonce TTL to 60 seconds\n"); - profile->nonce_ttl = 60; - } - - if (switch_test_flag(profile, TFLAG_TIMER) && !profile->timer_name) { - profile->timer_name = switch_core_strdup(profile->pool, "soft"); - } - - if (!profile->username) { - profile->username = switch_core_strdup(profile->pool, "FreeSWITCH"); - } - - if (!profile->rtpip) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Setting ip to '127.0.0.1'\n"); - profile->rtpip = switch_core_strdup(profile->pool, "127.0.0.1"); - } - - if (!profile->sip_port) { - profile->sip_port = atoi(SOFIA_DEFAULT_PORT); - } - - if (!profile->dialplan) { - profile->dialplan = switch_core_strdup(profile->pool, "XML"); - } - - if (!profile->sipdomain) { - profile->sipdomain = switch_core_strdup(profile->pool, profile->sipip); - } - if (profile->extsipip) { - profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->sip_port); - profile->bindurl = switch_core_sprintf(profile->pool, "%s;maddr=%s", profile->url, profile->sipip); - } else { - profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->sip_port); - profile->bindurl = profile->url; - } - } - if (profile) { - if ((gateways_tag = switch_xml_child(xprofile, "registrations"))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, - "The syntax has been discontinued, please see the new syntax in the default configuration examples\n"); - } - - if ((gateways_tag = switch_xml_child(xprofile, "gateways"))) { - for (gateway_tag = switch_xml_child(gateways_tag, "gateway"); gateway_tag; gateway_tag = gateway_tag->next) { - char *name = (char *) switch_xml_attr_soft(gateway_tag, "name"); - outbound_reg_t *gateway; - - if (switch_strlen_zero(name)) { - name = "anonymous"; - } - - if ((gateway = switch_core_alloc(profile->pool, sizeof(*gateway)))) { - char *scheme = "Digest", - *realm = NULL, - *username = NULL, *password = NULL, *extension = NULL, *proxy = NULL, *context = "default", *expire_seconds = "3600"; - - gateway->pool = profile->pool; - gateway->profile = profile; - gateway->name = switch_core_strdup(gateway->pool, name); - gateway->freq = 0; - - - for (param = switch_xml_child(gateway_tag, "param"); param; param = param->next) { - char *var = (char *) switch_xml_attr_soft(param, "name"); - char *val = (char *) switch_xml_attr_soft(param, "value"); - - if (!strcmp(var, "scheme")) { - scheme = val; - } else if (!strcmp(var, "realm")) { - realm = val; - } else if (!strcmp(var, "username")) { - username = val; - } else if (!strcmp(var, "password")) { - password = val; - } else if (!strcmp(var, "extension")) { - extension = val; - } else if (!strcmp(var, "proxy")) { - proxy = val; - } else if (!strcmp(var, "context")) { - context = val; - } else if (!strcmp(var, "expire-seconds")) { - expire_seconds = val; - } - } - - if (switch_strlen_zero(realm)) { - realm = name; - } - - if (switch_strlen_zero(username)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: username param is REQUIRED!\n"); - switch_xml_free(xml); - goto skip; - } - - if (switch_strlen_zero(password)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: password param is REQUIRED!\n"); - switch_xml_free(xml); - goto skip; - } - - if (switch_strlen_zero(extension)) { - extension = username; - } - - if (switch_strlen_zero(proxy)) { - proxy = realm; - } - - gateway->register_scheme = switch_core_strdup(gateway->pool, scheme); - gateway->register_context = switch_core_strdup(gateway->pool, context); - gateway->register_realm = switch_core_strdup(gateway->pool, realm); - gateway->register_username = switch_core_strdup(gateway->pool, username); - gateway->register_password = switch_core_strdup(gateway->pool, password); - gateway->register_from = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, realm); - gateway->register_contact = switch_core_sprintf(gateway->pool, "sip:%s@%s:%d", extension, profile->sipip, profile->sip_port); - - if (!strncasecmp(proxy, "sip:", 4)) { - gateway->register_proxy = switch_core_strdup(gateway->pool, proxy); - gateway->register_to = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, proxy + 4); - } else { - gateway->register_proxy = switch_core_sprintf(gateway->pool, "sip:%s", proxy); - gateway->register_to = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, proxy); - } - - gateway->expires_str = switch_core_strdup(gateway->pool, expire_seconds); - - if ((gateway->freq = atoi(gateway->expires_str)) < 5) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, - "Invalid Freq: %d. Setting Register-Frequency to 3600\n", gateway->freq); - gateway->freq = 3600; - } - gateway->freq -= 2; - - gateway->next = profile->gateways; - profile->gateways = gateway; - - if (find_gateway(gateway->name)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", gateway->name); - } else if (find_gateway(gateway->register_from)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate uri '%s'\n", gateway->register_from); - } else if (find_gateway(gateway->register_contact)) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate contact '%s'\n", gateway->register_from); - } else { - add_gateway(gateway->name, gateway); - add_gateway(gateway->register_from, gateway); - add_gateway(gateway->register_contact, gateway); - } - } - - skip: - assert(gateway_tag); - } - } - - if (profile->sipip) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Started Profile %s [%s]\n", profile->name, url); - launch_profile_thread(profile); - } else { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Unable to start Profile %s due to no configured sip-ip\n", profile->name); - } - profile = NULL; - } - } - } - done: - if (xml) { - switch_xml_free(xml); - } - - return status; - -} - -static void event_handler(switch_event_t *event) -{ - char *subclass, *sql; - - if ((subclass = switch_event_get_header(event, "orig-event-subclass")) && !strcasecmp(subclass, MY_EVENT_REGISTER)) { - char *from_user = switch_event_get_header(event, "orig-from-user"); - char *from_host = switch_event_get_header(event, "orig-from-host"); - char *contact_str = switch_event_get_header(event, "orig-contact"); - char *exp_str = switch_event_get_header(event, "orig-expires"); - char *rpid = switch_event_get_header(event, "orig-rpid"); - long expires = (long) time(NULL) + atol(exp_str); - char *profile_name = switch_event_get_header(event, "orig-profile-name"); - sofia_profile_t *profile; - char buf[512]; - - if (!rpid) { - rpid = "unknown"; - } - - if (!profile_name || !(profile = find_profile(profile_name))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n"); - return; - } - - - if (!find_reg_url(profile, from_user, from_host, buf, sizeof(buf))) { - sql = switch_mprintf("insert into sip_registrations values ('%q','%q','%q','Regestered', '%q', %ld)", - from_user, from_host, contact_str, rpid, expires); - } else { - sql = - switch_mprintf - ("update sip_registrations set contact='%q', rpid='%q', expires=%ld where user='%q' and host='%q'", - contact_str, rpid, expires, from_user, from_host); - - } - - if (sql) { - execute_sql(profile->dbname, sql, profile->ireg_mutex); - switch_safe_free(sql); - sql = NULL; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Propagating registration for %s@%s->%s\n", from_user, from_host, contact_str); - - } - - } -} - - -static switch_status_t chat_send(char *proto, char *from, char *to, char *subject, char *body, char *hint) -{ - char buf[256]; - char *user, *host; - sofia_profile_t *profile; - char *ffrom = NULL; - nua_handle_t *msg_nh; - char *contact; - - if (to && (user = strdup(to))) { - if ((host = strchr(user, '@'))) { - *host++ = '\0'; - } - - if (!host || !(profile = find_profile(host))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - "Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to, - body ? body : "[no body]", host ? host : "NULL"); - return SWITCH_STATUS_FALSE; - } - - if (!find_reg_url(profile, user, host, buf, sizeof(buf))) { - return SWITCH_STATUS_FALSE; - } - - if (!strcmp(proto, SOFIA_CHAT_PROTO)) { - from = hint; - } else { - char *fp, *p, *fu = NULL; - - if (!(fp = strdup(from))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); - return SWITCH_STATUS_FALSE; - } - - if ((p = strchr(fp, '@'))) { - *p = '\0'; - fu = strdup(fp); - *p = '+'; - } - - ffrom = switch_mprintf("\"%s\" ", fu, proto, fp, profile->name); - from = ffrom; - switch_safe_free(fu); - switch_safe_free(fp); - } - - contact = get_url_from_contact(buf, 1); - msg_nh = nua_handle(profile->nua, NULL, SIPTAG_FROM_STR(from), NUTAG_URL(contact), SIPTAG_TO_STR(buf), // if this cries, add contact here too, change the 1 to 0 and omit the safe_free - SIPTAG_CONTACT_STR(profile->url), TAG_END()); - - switch_safe_free(contact); - - - nua_message(msg_nh, SIPTAG_CONTENT_TYPE_STR("text/html"), SIPTAG_PAYLOAD_STR(body), TAG_END()); - - - switch_safe_free(ffrom); - free(user); - } - - return SWITCH_STATUS_SUCCESS; -} - -static void cancel_presence(void) -{ - char *sql, *errmsg = NULL; - switch_core_db_t *db; - sofia_profile_t *profile; - switch_hash_index_t *hi; - void *val; - - if ((sql = switch_mprintf("select 0,'unavailable','unavailable',* from sip_subscriptions where event='presence'"))) { - switch_mutex_lock(globals.hash_mutex); - for (hi = switch_hash_first(switch_hash_pool_get(globals.profile_hash), globals.profile_hash); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, NULL, NULL, &val); - profile = (sofia_profile_t *) val; - if (!(profile->pflags & PFLAG_PRESENCE)) { - continue; - } - - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - continue; - } - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, sub_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - switch_core_db_close(db); - } - switch_safe_free(sql); - } - switch_mutex_unlock(globals.hash_mutex); -} - -static void establish_presence(sofia_profile_t * profile) -{ - char *sql, *errmsg = NULL; - switch_core_db_t *db; - - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - return; - } - - if ((sql = switch_mprintf("select user,host,'Registered','unknown','' from sip_registrations"))) { - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, resub_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - switch_safe_free(sql); - } - - if ((sql = switch_mprintf("select sub_to_user,sub_to_host,'Online','unknown',proto from sip_subscriptions " - "where proto='ext' or proto='user' or proto='conf'"))) { - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, resub_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - switch_safe_free(sql); - } - - switch_core_db_close(db); - -} - - - -static char *translate_rpid(char *in, char *ext) -{ - char *r = NULL; - - if (in && (strstr(in, "null") || strstr(in, "NULL"))) { - in = NULL; - } - - if (!in) { - in = ext; - } - - if (!in) { - return NULL; - } - - if (!strcasecmp(in, "dnd")) { - r = "busy"; - } - - if (ext && !strcasecmp(ext, "away")) { - r = "idle"; - } - - return r; -} - -static void pres_event_handler(switch_event_t *event) -{ - sofia_profile_t *profile; - switch_hash_index_t *hi; - void *val; - char *from = switch_event_get_header(event, "from"); - char *proto = switch_event_get_header(event, "proto"); - char *rpid = switch_event_get_header(event, "rpid"); - char *status = switch_event_get_header(event, "status"); - char *event_type = switch_event_get_header(event, "event_type"); - //char *event_subtype = switch_event_get_header(event, "event_subtype"); - char *sql = NULL; - char *euser = NULL, *user = NULL, *host = NULL; - char *errmsg; - switch_core_db_t *db; - - - if (rpid && !strcasecmp(rpid, "n/a")) { - rpid = NULL; - } - - if (status && !strcasecmp(status, "n/a")) { - status = NULL; - } - - if (rpid) { - rpid = translate_rpid(rpid, status); - } - - if (!status) { - status = "Available"; - - if (rpid) { - if (!strcasecmp(rpid, "busy")) { - status = "Busy"; - } else if (!strcasecmp(rpid, "unavailable")) { - status = "Idle"; - } else if (!strcasecmp(rpid, "away")) { - status = "Idle"; - } - } - } - - if (!rpid) { - rpid = "unknown"; - } - - if (event->event_id == SWITCH_EVENT_ROSTER) { - - if (from) { - sql = switch_mprintf("select 1,'%q','%q',* from sip_subscriptions where event='presence' and full_from like '%%%q%%'", status, rpid, from); - } else { - sql = switch_mprintf("select 1,'%q','%q',* from sip_subscriptions where event='presence'", status, rpid); - } - - switch_mutex_lock(globals.hash_mutex); - for (hi = switch_hash_first(switch_hash_pool_get(globals.profile_hash), globals.profile_hash); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, NULL, NULL, &val); - profile = (sofia_profile_t *) val; - if (!(profile->pflags & PFLAG_PRESENCE)) { - continue; - } - - if (sql) { - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - continue; - } - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, sub_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - switch_core_db_close(db); - } - - } - switch_mutex_unlock(globals.hash_mutex); - - return; - } - - if (switch_strlen_zero(event_type)) { - event_type = "presence"; - } - - if ((user = strdup(from))) { - if ((host = strchr(user, '@'))) { - char *p; - *host++ = '\0'; - if ((p = strchr(host, '/'))) { - *p = '\0'; - } - } else { - switch_safe_free(user); - return; - } - if ((euser = strchr(user, '+'))) { - euser++; - } else { - euser = user; - } - - } else { - return; - } - - - switch (event->event_id) { - case SWITCH_EVENT_PRESENCE_PROBE: - if (proto) { - switch_core_db_t *db = NULL; - char *to = switch_event_get_header(event, "to"); - char *user, *euser, *host, *p; - - if (!to || !(user = strdup(to))) { - return; - } - - if ((host = strchr(user, '@'))) { - *host++ = '\0'; - } - euser = user; - if ((p = strchr(euser, '+'))) { - euser = (p + 1); - } - - if (euser && host && - (sql = - switch_mprintf("select user,host,status,rpid,'' from sip_registrations where user='%q' and host='%q'", - euser, host)) && (profile = find_profile(host))) { - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - switch_safe_free(user); - switch_safe_free(sql); - return; - } - - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, resub_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - switch_safe_free(sql); - } - switch_safe_free(user); - switch_core_db_close(db); - } - return; - case SWITCH_EVENT_PRESENCE_IN: - sql = - switch_mprintf - ("select 1,'%q','%q',* from sip_subscriptions where proto='%q' and event='%q' and sub_to_user='%q' and sub_to_host='%q'", - status, rpid, proto, event_type, euser, host); - break; - case SWITCH_EVENT_PRESENCE_OUT: - sql = - switch_mprintf - ("select 0,'%q','%q',* from sip_subscriptions where proto='%q' and event='%q' and sub_to_user='%q' and sub_to_host='%q'", - status, rpid, proto, event_type, euser, host); - break; - default: - break; - } - - switch_mutex_lock(globals.hash_mutex); - for (hi = switch_hash_first(switch_hash_pool_get(globals.profile_hash), globals.profile_hash); hi; hi = switch_hash_next(hi)) { - switch_hash_this(hi, NULL, NULL, &val); - profile = (sofia_profile_t *) val; - if (!(profile->pflags & PFLAG_PRESENCE)) { - continue; - } - - if (sql) { - if (!(db = switch_core_db_open_file(profile->dbname))) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); - continue; - } - switch_mutex_lock(profile->ireg_mutex); - switch_core_db_exec(db, sql, sub_callback, profile, &errmsg); - switch_mutex_unlock(profile->ireg_mutex); - - switch_core_db_close(db); - } - } - switch_mutex_unlock(globals.hash_mutex); - - switch_safe_free(sql); - switch_safe_free(user); -} - - SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) { @@ -5830,10 +1001,6 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_mod return SWITCH_STATUS_TERM; } - su_init(); - su_log_redirect(NULL, logger, NULL); - su_log_redirect(tport_log, logger, NULL); - switch_core_hash_init(&globals.profile_hash, module_pool); switch_core_hash_init(&globals.gateway_hash, module_pool); switch_mutex_init(&globals.hash_mutex, SWITCH_MUTEX_NESTED, module_pool); @@ -5841,22 +1008,22 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_mod config_sofia(0); - if (switch_event_bind((char *) modname, SWITCH_EVENT_PRESENCE_IN, SWITCH_EVENT_SUBCLASS_ANY, pres_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_event_bind((char *) modname, SWITCH_EVENT_PRESENCE_IN, SWITCH_EVENT_SUBCLASS_ANY, sofia_presence_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); return SWITCH_STATUS_GENERR; } - if (switch_event_bind((char *) modname, SWITCH_EVENT_PRESENCE_OUT, SWITCH_EVENT_SUBCLASS_ANY, pres_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_event_bind((char *) modname, SWITCH_EVENT_PRESENCE_OUT, SWITCH_EVENT_SUBCLASS_ANY, sofia_presence_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); return SWITCH_STATUS_GENERR; } - if (switch_event_bind((char *) modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, pres_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_event_bind((char *) modname, SWITCH_EVENT_PRESENCE_PROBE, SWITCH_EVENT_SUBCLASS_ANY, sofia_presence_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); return SWITCH_STATUS_GENERR; } - if (switch_event_bind((char *) modname, SWITCH_EVENT_ROSTER, SWITCH_EVENT_SUBCLASS_ANY, pres_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { + if (switch_event_bind((char *) modname, SWITCH_EVENT_ROSTER, SWITCH_EVENT_SUBCLASS_ANY, sofia_presence_event_handler, NULL) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n"); return SWITCH_STATUS_GENERR; } @@ -5872,7 +1039,7 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_mod SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void) { - cancel_presence(); + sofia_presence_cancel(); switch_mutex_lock(globals.mutex); if (globals.running == 1) { @@ -5899,3 +1066,4 @@ SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void) * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: */ + diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.h b/src/mod/endpoints/mod_sofia/mod_sofia.h new file mode 100644 index 0000000000..8c6163dc1c --- /dev/null +++ b/src/mod/endpoints/mod_sofia/mod_sofia.h @@ -0,0 +1,406 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Ken Rice, Asteria Solutions Group, Inc + * Paul D. Tinsley + * Bret McDanel + * + * + * mod_sofia.h -- SOFIA SIP Endpoint + * + */ + +/*Defines etc..*/ +/*************************************************************************************************************************************************************/ +#define IREG_SECONDS 30 +#define GATEWAY_SECONDS 1 + +#define HAVE_APR +#include + +static const char modname[] = "mod_sofia"; +static const switch_state_handler_table_t noop_state_handler = { 0 }; +struct outbound_reg; +typedef struct outbound_reg outbound_reg_t; + +struct sofia_profile; +typedef struct sofia_profile sofia_profile_t; +#define NUA_MAGIC_T sofia_profile_t + +struct sofia_private { + char uuid[SWITCH_UUID_FORMATTED_LENGTH + 1]; + outbound_reg_t *gateway; +}; + +typedef struct sofia_private sofia_private_t; + +struct private_object; +typedef struct private_object private_object_t; +#define NUA_HMAGIC_T sofia_private_t + +#define MY_EVENT_REGISTER "sofia::register" +#define MY_EVENT_EXPIRE "sofia::expire" +#define MULTICAST_EVENT "multicast::event" +#define SOFIA_REPLACES_HEADER "_sofia_replaces_" +#define SOFIA_USER_AGENT "FreeSWITCH(mod_sofia)" +#define SOFIA_CHAT_PROTO "sip" +#define SOFIA_SIP_HEADER_PREFIX "sip_h_" +#define SOFIA_SIP_HEADER_PREFIX_T "~sip_h_" +#define SOFIA_DEFAULT_PORT "5060" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define set_param(ptr,val) if (ptr) {free(ptr) ; ptr = NULL;} if (val) {ptr = strdup(val);} +#define set_anchor(t,m) if (t->Anchor) {delete t->Anchor;} t->Anchor = new SipMessage(m); + +/* Local Structures */ +/*************************************************************************************************************************************************************/ +struct sip_alias_node { + char *url; + nua_t *nua; + struct sip_alias_node *next; +}; + +typedef struct sip_alias_node sip_alias_node_t; + +typedef enum { + PFLAG_AUTH_CALLS = (1 << 0), + PFLAG_BLIND_REG = (1 << 1), + PFLAG_AUTH_ALL = (1 << 2), + PFLAG_FULL_ID = (1 << 3), + PFLAG_PRESENCE = (1 << 4), + PFLAG_PASS_RFC2833 = (1 << 5), + PFLAG_DISABLE_TRANSCODING = (1 << 6) +} PFLAGS; + +typedef enum { + TFLAG_IO = (1 << 0), + TFLAG_CHANGE_MEDIA = (1 << 1), + TFLAG_OUTBOUND = (1 << 2), + TFLAG_READING = (1 << 3), + TFLAG_WRITING = (1 << 4), + TFLAG_HUP = (1 << 5), + TFLAG_RTP = (1 << 6), + TFLAG_BYE = (1 << 7), + TFLAG_ANS = (1 << 8), + TFLAG_EARLY_MEDIA = (1 << 9), + TFLAG_SECURE = (1 << 10), + TFLAG_VAD_IN = (1 << 11), + TFLAG_VAD_OUT = (1 << 12), + TFLAG_VAD = (1 << 13), + TFLAG_TIMER = (1 << 14), + TFLAG_READY = (1 << 15), + TFLAG_REINVITE = (1 << 16), + TFLAG_REFER = (1 << 17), + TFLAG_NOHUP = (1 << 18), + TFLAG_XFER = (1 << 19), + TFLAG_NOMEDIA = (1 << 20), + TFLAG_BUGGY_2833 = (1 << 21), + TFLAG_SIP_HOLD = (1 << 22), + TFLAG_INB_NOMEDIA = (1 << 23), + TFLAG_LATE_NEGOTIATION = (1 << 24) +} TFLAGS; + +struct sofia_globals { + switch_hash_t *profile_hash; + switch_hash_t *gateway_hash; + switch_mutex_t *hash_mutex; + uint32_t callid; + int32_t running; + switch_mutex_t *mutex; + char guess_ip[80]; +}; +extern struct sofia_globals globals; + + +typedef enum { + REG_FLAG_AUTHED = (1 << 0), +} reg_flags_t; + +typedef enum { + REG_STATE_UNREGED, + REG_STATE_TRYING, + REG_STATE_REGISTER, + REG_STATE_REGED, + REG_STATE_FAILED, + REG_STATE_EXPIRED +} reg_state_t; + +struct outbound_reg { + sofia_private_t *sofia_private; + nua_handle_t *nh; + sofia_profile_t *profile; + char *name; + char *register_scheme; + char *register_realm; + char *register_username; + char *register_password; + char *register_from; + char *register_contact; + char *register_to; + char *register_proxy; + char *register_context; + char *expires_str; + uint32_t freq; + time_t expires; + time_t retry; + uint32_t flags; + reg_state_t state; + switch_memory_pool_t *pool; + struct outbound_reg *next; +}; + + +struct sofia_profile { + int debug; + char *name; + char *dbname; + char *dialplan; + char *context; + char *extrtpip; + char *rtpip; + char *sipip; + char *extsipip; + char *username; + char *url; + char *bindurl; + char *sipdomain; + char *timer_name; + char *hold_music; + int sip_port; + char *codec_string; + int running; + int codec_ms; + int dtmf_duration; + unsigned int flags; + unsigned int pflags; + uint32_t max_calls; + uint32_t nonce_ttl; + nua_t *nua; + switch_memory_pool_t *pool; + su_root_t *s_root; + sip_alias_node_t *aliases; + switch_payload_t te; + switch_payload_t cng_pt; + uint32_t codec_flags; + switch_mutex_t *ireg_mutex; + switch_mutex_t *gateway_mutex; + outbound_reg_t *gateways; + su_home_t *home; + switch_hash_t *profile_hash; + switch_hash_t *chat_hash; +}; + + +struct private_object { + sofia_private_t *sofia_private; + uint32_t flags; + switch_payload_t agreed_pt; + switch_core_session_t *session; + switch_frame_t read_frame; + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last; + const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; + int num_codecs; + switch_codec_t read_codec; + switch_codec_t write_codec; + uint32_t codec_ms; + switch_caller_profile_t *caller_profile; + uint32_t timestamp_send; + //int32_t timestamp_recv; + switch_rtp_t *rtp_session; + int ssrc; + //switch_time_t last_read; + sofia_profile_t *profile; + char *local_sdp_audio_ip; + switch_port_t local_sdp_audio_port; + char *remote_sdp_audio_ip; + switch_port_t remote_sdp_audio_port; + char *adv_sdp_audio_ip; + switch_port_t adv_sdp_audio_port; + char *proxy_sdp_audio_ip; + switch_port_t proxy_sdp_audio_port; + char *from_uri; + char *to_uri; + char *from_address; + char *to_address; + char *callid; + char *far_end_contact; + char *contact_url; + char *from_str; + char *rm_encoding; + char *rm_fmtp; + char *fmtp_out; + char *remote_sdp_str; + char *local_sdp_str; + char *dest; + char *dest_to; + char *key; + char *xferto; + char *kick; + char *origin; + char *hash_key; + char *chat_from; + char *chat_to; + char *e_dest; + char *call_id; + unsigned long rm_rate; + switch_payload_t pt; + switch_mutex_t *flag_mutex; + switch_payload_t te; + switch_payload_t bte; + switch_payload_t cng_pt; + switch_payload_t bcng_pt; + nua_handle_t *nh; + nua_handle_t *nh2; + su_home_t *home; + sip_contact_t *contact; +}; + +struct callback_t { + char *val; + switch_size_t len; + int matches; +}; + +typedef enum { + REG_REGISTER, + REG_INVITE +} sofia_regtype_t; + +typedef enum { + AUTH_OK, + AUTH_FORBIDDEN, + AUTH_STALE, +} auth_res_t; + +/* Function Prototypes */ +/*************************************************************************************************************************************************************/ + + +switch_status_t sofia_glue_activate_rtp(private_object_t * tech_pvt); + +void sofia_glue_deactivate_rtp(private_object_t * tech_pvt); + +void sofia_glue_set_local_sdp(private_object_t * tech_pvt, char *ip, uint32_t port, char *sr, int force); + +void sofia_glue_sofia_glue_tech_set_codecs(private_object_t * tech_pvt); + +void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t * profile, private_object_t * tech_pvt, const char *channame); + +void sofia_glue_terminate_session(switch_core_session_t **session, switch_call_cause_t cause, int line); + +switch_status_t sofia_glue_tech_choose_port(private_object_t * tech_pvt); + +switch_status_t sofia_glue_do_invite(switch_core_session_t *session); + +uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t * sdp); + +char *sofia_reg_get_auth_data(char *dbname, char *nonce, char *npassword, size_t len, switch_mutex_t * mutex); + +void sofia_presence_establish_presence(sofia_profile_t * profile); + +void sofia_handle_sip_i_state(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); + + +void sofia_handle_sip_i_refer(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]); + +void sofia_handle_sip_i_info(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]); + +void sofia_handle_sip_i_invite(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); + +void sofia_reg_handle_sip_i_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); + +void sofia_event_callback(nua_event_t event, + int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); + + +void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t * thread, void *obj); + +void launch_sofia_profile_thread(sofia_profile_t * profile); + +switch_status_t sofia_presence_chat_send(char *proto, char *from, char *to, char *subject, char *body, char *hint); +void sofia_glue_tech_absorb_sdp(private_object_t * tech_pvt); +switch_status_t sofia_glue_tech_media(private_object_t * tech_pvt, char *r_sdp); +char *sofia_reg_find_reg_url(sofia_profile_t * profile, const char *user, const char *host, char *val, switch_size_t len); +void event_handler(switch_event_t *event); +void sofia_presence_event_handler(switch_event_t *event); +void sofia_presence_cancel(void); +switch_status_t config_sofia(int reload); +auth_res_t parse_auth(sofia_profile_t * profile, sip_authorization_t const *authorization, const char *regstr, char *np, size_t nplen); +void sofia_reg_handle_sip_r_challenge(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]); +void sofia_reg_handle_sip_r_register(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); +void sofia_handle_sip_i_options(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); +void sofia_presence_handle_sip_i_publish(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); +void sofia_presence_handle_sip_i_message(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); +void sofia_presence_handle_sip_r_subscribe(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); +void sofia_presence_handle_sip_i_subscribe(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]); + +sofia_profile_t *sofia_glue_find_profile(char *key); +void sofia_glue_add_profile(char *key, sofia_profile_t * profile); +void sofia_glue_execute_sql(char *dbname, char *sql, switch_mutex_t * mutex); +void sofia_reg_check_expire(switch_core_db_t *db, sofia_profile_t * profile, time_t now); +void sofia_reg_check_gateway(sofia_profile_t * profile, time_t now); +void sofia_reg_unregister(sofia_profile_t * profile); +switch_status_t sofia_glue_ext_address_lookup(char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool); +outbound_reg_t *sofia_reg_find_gateway(char *key); +void sofia_reg_add_gateway(char *key, outbound_reg_t * gateway); +void sofia_glue_pass_sdp(private_object_t * tech_pvt, char *sdp); +switch_call_cause_t sofia_glue_sip_cause_to_freeswitch(int status); +void sofia_glue_do_xfer_invite(switch_core_session_t *session); +uint8_t sofia_reg_handle_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sip_t const *sip, sofia_regtype_t regtype, char *key, uint32_t keylen); +const switch_endpoint_interface_t sofia_endpoint_interface; +void sofia_presence_set_chat_hash(private_object_t * tech_pvt, sip_t const *sip); +switch_status_t sofia_on_hangup(switch_core_session_t *session); +char *sofia_glue_get_url_from_contact(char *buf, uint8_t to_dup); +int sofia_presence_resub_callback(void *pArg, int argc, char **argv, char **columnNames); +int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char **columnNames); +void sofia_presence_set_hash_key(char *hash_key, int32_t len, sip_t const *sip); diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.vcproj b/src/mod/endpoints/mod_sofia/mod_sofia.vcproj index f97fa5c96e..c2f4db1416 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.vcproj +++ b/src/mod/endpoints/mod_sofia/mod_sofia.vcproj @@ -194,16 +194,19 @@ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx" UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}" > - - + + + + + + + + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * Ken Rice, Asteria Solutions Group, Inc + * Paul D. Tinsley + * Bret McDanel + * + * + * sofia.c -- SOFIA SIP Endpoint + * + */ +#include "mod_sofia.h" +extern su_log_t tport_log[]; + + +static char reg_sql[] = + "CREATE TABLE sip_registrations (\n" + " user VARCHAR(255),\n" + " host VARCHAR(255),\n" + " contact VARCHAR(1024),\n" + " status VARCHAR(255),\n" + " rpid VARCHAR(255),\n" + " expires INTEGER(8)" ");\n"; + + +static char sub_sql[] = + "CREATE TABLE sip_subscriptions (\n" + " proto VARCHAR(255),\n" + " user VARCHAR(255),\n" + " host VARCHAR(255),\n" + " sub_to_user VARCHAR(255),\n" + " sub_to_host VARCHAR(255),\n" + " event VARCHAR(255),\n" + " contact VARCHAR(1024),\n" + " call_id VARCHAR(255),\n" + " full_from VARCHAR(255),\n" + " full_via VARCHAR(255),\n" + " expires INTEGER(8)" ");\n"; + + +static char auth_sql[] = + "CREATE TABLE sip_authentication (\n" + " user VARCHAR(255),\n" + " host VARCHAR(255),\n" + " passwd VARCHAR(255),\n" + " nonce VARCHAR(255),\n" + " expires INTEGER(8)" + ");\n"; + + +void sofia_event_callback(nua_event_t event, + int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + struct private_object *tech_pvt = NULL; + auth_res_t auth_res = AUTH_FORBIDDEN; + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + + if (sofia_private) { + if (!switch_strlen_zero(sofia_private->uuid)) { + + if ((session = switch_core_session_locate(sofia_private->uuid))) { + tech_pvt = switch_core_session_get_private(session); + channel = switch_core_session_get_channel(tech_pvt->session); + if (switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_set_flag(tech_pvt, TFLAG_NOMEDIA); + } + if (!tech_pvt->call_id && sip && sip->sip_call_id && sip->sip_call_id->i_id) { + tech_pvt->call_id = switch_core_session_strdup(session, (char *) sip->sip_call_id->i_id); + switch_channel_set_variable(channel, "sip_call_id", tech_pvt->call_id); + } + } else { + /* too late */ + return; + } + } + } + + + if (status != 100 && status != 200) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "event [%s] status [%d][%s] session: %s\n", + nua_event_name(event), status, phrase, session ? switch_channel_get_name(channel) : "n/a"); + } + + if ((profile->pflags & PFLAG_AUTH_ALL) && tech_pvt && tech_pvt->key && sip) { + sip_authorization_t const *authorization = NULL; + + if (sip->sip_authorization) { + authorization = sip->sip_authorization; + } else if (sip->sip_proxy_authorization) { + authorization = sip->sip_proxy_authorization; + } + + if (authorization) { + auth_res = parse_auth(profile, authorization, (char *) sip->sip_request->rq_method_name, tech_pvt->key, strlen(tech_pvt->key)); + } + + if (auth_res != AUTH_OK) { + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + nua_respond(nh, SIP_401_UNAUTHORIZED, TAG_END()); + goto done; + } + + if (channel) { + switch_channel_set_variable(channel, "sip_authorized", "true"); + } + } + + if (sip && (status == 401 || status == 407)) { + sofia_reg_handle_sip_r_challenge(status, phrase, nua, profile, nh, session, sip, tags); + goto done; + } + + switch (event) { + case nua_r_shutdown: + case nua_r_get_params: + case nua_r_invite: + case nua_r_unregister: + case nua_r_options: + case nua_i_fork: + case nua_r_info: + case nua_r_bye: + case nua_i_bye: + case nua_r_unsubscribe: + case nua_r_publish: + case nua_r_message: + case nua_r_notify: + case nua_i_notify: + case nua_i_cancel: + case nua_i_error: + case nua_i_active: + case nua_i_ack: + case nua_i_terminated: + case nua_r_set_params: + break; + case nua_r_register: + sofia_reg_handle_sip_r_register(status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_options: + sofia_handle_sip_i_options(status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_invite: + if (!session) { + sofia_handle_sip_i_invite(nua, profile, nh, sofia_private, sip, tags); + } + break; + case nua_i_publish: + sofia_presence_handle_sip_i_publish(nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_register: + sofia_reg_handle_sip_i_register(nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_prack: + break; + case nua_i_state: + sofia_handle_sip_i_state(status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_message: + sofia_presence_handle_sip_i_message(status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_info: + sofia_handle_sip_i_info(nua, profile, nh, session, sip, tags); + break; + case nua_r_refer: + break; + case nua_i_refer: + if (session) { + sofia_handle_sip_i_refer(nua, profile, nh, session, sip, tags); + } + break; + case nua_r_subscribe: + sofia_presence_handle_sip_r_subscribe(status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + case nua_i_subscribe: + sofia_presence_handle_sip_i_subscribe(status, phrase, nua, profile, nh, sofia_private, sip, tags); + break; + default: + if (status > 100) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: unknown event %d: %03d %s\n", nua_event_name(event), event, status, phrase); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: unknown event %d\n", nua_event_name(event), event); + } + break; + } + + done: + + if (session) { + switch_core_session_rwunlock(session); + } +} + + +void event_handler(switch_event_t *event) +{ + char *subclass, *sql; + + if ((subclass = switch_event_get_header(event, "orig-event-subclass")) && !strcasecmp(subclass, MY_EVENT_REGISTER)) { + char *from_user = switch_event_get_header(event, "orig-from-user"); + char *from_host = switch_event_get_header(event, "orig-from-host"); + char *contact_str = switch_event_get_header(event, "orig-contact"); + char *exp_str = switch_event_get_header(event, "orig-expires"); + char *rpid = switch_event_get_header(event, "orig-rpid"); + long expires = (long) time(NULL) + atol(exp_str); + char *profile_name = switch_event_get_header(event, "orig-profile-name"); + sofia_profile_t *profile; + char buf[512]; + + if (!rpid) { + rpid = "unknown"; + } + + if (!profile_name || !(profile = sofia_glue_find_profile(profile_name))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n"); + return; + } + + + if (!sofia_reg_find_reg_url(profile, from_user, from_host, buf, sizeof(buf))) { + sql = switch_mprintf("insert into sip_registrations values ('%q','%q','%q','Regestered', '%q', %ld)", + from_user, from_host, contact_str, rpid, expires); + } else { + sql = + switch_mprintf + ("update sip_registrations set contact='%q', rpid='%q', expires=%ld where user='%q' and host='%q'", + contact_str, rpid, expires, from_user, from_host); + + } + + if (sql) { + sofia_glue_execute_sql(profile->dbname, sql, profile->ireg_mutex); + switch_safe_free(sql); + sql = NULL; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Propagating registration for %s@%s->%s\n", from_user, from_host, contact_str); + + } + + } +} + + + +void *SWITCH_THREAD_FUNC sofia_profile_thread_run(switch_thread_t * thread, void *obj) +{ + sofia_profile_t *profile = (sofia_profile_t *) obj; + switch_memory_pool_t *pool; + sip_alias_node_t *node; + uint32_t ireg_loops = 0; + uint32_t gateway_loops = 0; + switch_core_db_t *db; + switch_event_t *s_event; + + profile->s_root = su_root_create(NULL); + profile->home = su_home_new(sizeof(*profile->home)); + + profile->nua = nua_create(profile->s_root, /* Event loop */ + sofia_event_callback, /* Callback for processing events */ + profile, /* Additional data to pass to callback */ + NUTAG_URL(profile->bindurl), NTATAG_UDP_MTU(65536), TAG_END()); /* Last tag should always finish the sequence */ + + nua_set_params(profile->nua, + //NUTAG_EARLY_MEDIA(1), + NUTAG_AUTOANSWER(0), + NUTAG_AUTOALERT(0), + NUTAG_ALLOW("REGISTER"), + NUTAG_ALLOW("REFER"), + NUTAG_ALLOW("INFO"), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("PUBLISH")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("NOTIFY")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("SUBSCRIBE")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ENABLEMESSAGE(1)), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("presence")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW_EVENTS("presence.winfo")), + SIPTAG_SUPPORTED_STR("100rel, precondition"), SIPTAG_USER_AGENT_STR(SOFIA_USER_AGENT), TAG_END()); + + + for (node = profile->aliases; node; node = node->next) { + node->nua = nua_create(profile->s_root, /* Event loop */ + sofia_event_callback, /* Callback for processing events */ + profile, /* Additional data to pass to callback */ + NUTAG_URL(node->url), TAG_END()); /* Last tag should always finish the sequence */ + + nua_set_params(node->nua, + NUTAG_EARLY_MEDIA(1), + NUTAG_AUTOANSWER(0), + NUTAG_AUTOALERT(0), + NUTAG_ALLOW("REGISTER"), + NUTAG_ALLOW("REFER"), + NUTAG_ALLOW("INFO"), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ALLOW("PUBLISH")), + TAG_IF((profile->pflags & PFLAG_PRESENCE), NUTAG_ENABLEMESSAGE(1)), + SIPTAG_SUPPORTED_STR("100rel, precondition"), SIPTAG_USER_AGENT_STR(SOFIA_USER_AGENT), TAG_END()); + + } + + + if ((db = switch_core_db_open_file(profile->dbname))) { + switch_core_db_test_reactive(db, "select contact from sip_registrations", reg_sql); + switch_core_db_test_reactive(db, "select contact from sip_subscriptions", sub_sql); + switch_core_db_test_reactive(db, "select * from sip_authentication", auth_sql); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database!\n"); + return NULL; + } + + + switch_mutex_init(&profile->ireg_mutex, SWITCH_MUTEX_NESTED, profile->pool); + switch_mutex_init(&profile->gateway_mutex, SWITCH_MUTEX_NESTED, profile->pool); + + ireg_loops = IREG_SECONDS; + gateway_loops = GATEWAY_SECONDS; + + if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._udp"); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); + switch_event_fire(&s_event); + } + + if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._tcp"); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); + switch_event_fire(&s_event); + } + + if (switch_event_create(&s_event, SWITCH_EVENT_PUBLISH) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._sctp"); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); + switch_event_fire(&s_event); + } + + sofia_glue_add_profile(profile->name, profile); + + if (profile->pflags & PFLAG_PRESENCE) { + sofia_presence_establish_presence(profile); + } + + while (globals.running == 1) { + if (++ireg_loops >= IREG_SECONDS) { + sofia_reg_check_expire(db, profile, time(NULL)); + ireg_loops = 0; + } + + if (++gateway_loops >= GATEWAY_SECONDS) { + sofia_reg_check_gateway(profile, time(NULL)); + gateway_loops = 0; + } + + su_root_step(profile->s_root, 1000); + } + + switch_core_db_close(db); + sofia_reg_unregister(profile); + su_home_unref(profile->home); + + + if (switch_event_create(&s_event, SWITCH_EVENT_UNPUBLISH) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "service", "_sip._udp"); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "port", "%d", profile->sip_port); + switch_event_fire(&s_event); + } + + su_root_destroy(profile->s_root); + pool = profile->pool; + switch_core_destroy_memory_pool(&pool); + switch_mutex_lock(globals.mutex); + globals.running = 0; + switch_mutex_unlock(globals.mutex); + + return NULL; +} + +void launch_sofia_profile_thread(sofia_profile_t * profile) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + + switch_threadattr_create(&thd_attr, profile->pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + switch_thread_create(&thread, thd_attr, sofia_profile_thread_run, profile, profile->pool); +} + +static void logger(void *logarg, char const *fmt, va_list ap) +{ + char *data = NULL; + + if (fmt) { +#ifdef HAVE_VASPRINTF + int ret; + ret = vasprintf(&data, fmt, ap); + if ((ret == -1) || !data) { + return; + } +#else + data = (char *) malloc(2048); + if (data) { + vsnprintf(data, 2048, fmt, ap); + } else { + return; + } +#endif + } + if (data) { + switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_CONSOLE, (char *) "%s", data); + free(data); + } +} + + +switch_status_t config_sofia(int reload) +{ + char *cf = "sofia.conf"; + switch_xml_t cfg, xml = NULL, xprofile, param, settings, profiles, gateway_tag, gateways_tag; + switch_status_t status = SWITCH_STATUS_SUCCESS; + sofia_profile_t *profile = NULL; + char url[512] = ""; + switch_mutex_lock(globals.mutex); + globals.running = 1; + switch_mutex_unlock(globals.mutex); + + if (!reload) { + su_init(); + su_log_redirect(NULL, logger, NULL); + su_log_redirect(tport_log, logger, NULL); + } + + if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); + status = SWITCH_STATUS_FALSE; + goto done; + } + + if ((settings = switch_xml_child(cfg, "global_settings"))) { + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + if (!strcasecmp(var, "log-level")) { + su_log_set_level(NULL, atoi(val)); + } else if (!strcasecmp(var, "log-level-trace")) { + su_log_set_level(tport_log, atoi(val)); + } + } + } + + if ((profiles = switch_xml_child(cfg, "profiles"))) { + for (xprofile = switch_xml_child(profiles, "profile"); xprofile; xprofile = xprofile->next) { + if (!(settings = switch_xml_child(xprofile, "settings"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Settings, check the new config!\n"); + } else { + char *xprofilename = (char *) switch_xml_attr_soft(xprofile, "name"); + switch_memory_pool_t *pool = NULL; + + + /* Setup the pool */ + if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); + goto done; + } + + if (!(profile = (sofia_profile_t *) switch_core_alloc(pool, sizeof(*profile)))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + goto done; + } + + if (!xprofilename) { + xprofilename = "unnamed"; + } + + profile->pool = pool; + + profile->name = switch_core_strdup(profile->pool, xprofilename); + snprintf(url, sizeof(url), "sofia_reg_%s", xprofilename); + profile->dbname = switch_core_strdup(profile->pool, url); + switch_core_hash_init(&profile->chat_hash, profile->pool); + + profile->dtmf_duration = 100; + profile->codec_ms = 20; + + for (param = switch_xml_child(settings, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "debug")) { + profile->debug = atoi(val); + } else if (!strcasecmp(var, "use-rtp-timer") && switch_true(val)) { + switch_set_flag(profile, TFLAG_TIMER); + } else if (!strcasecmp(var, "inbound-no-media") && switch_true(val)) { + switch_set_flag(profile, TFLAG_INB_NOMEDIA); + } else if (!strcasecmp(var, "inbound-late-negotiation") && switch_true(val)) { + switch_set_flag(profile, TFLAG_LATE_NEGOTIATION); + } else if (!strcasecmp(var, "rfc2833-pt")) { + profile->te = (switch_payload_t) atoi(val); + } else if (!strcasecmp(var, "cng-pt")) { + profile->cng_pt = (switch_payload_t) atoi(val); + } else if (!strcasecmp(var, "sip-port")) { + profile->sip_port = atoi(val); + } else if (!strcasecmp(var, "vad")) { + if (!strcasecmp(val, "in")) { + switch_set_flag(profile, TFLAG_VAD_IN); + } else if (!strcasecmp(val, "out")) { + switch_set_flag(profile, TFLAG_VAD_OUT); + } else if (!strcasecmp(val, "both")) { + switch_set_flag(profile, TFLAG_VAD_IN); + switch_set_flag(profile, TFLAG_VAD_OUT); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invald option %s for VAD\n", val); + } + } else if (!strcasecmp(var, "ext-rtp-ip")) { + profile->extrtpip = switch_core_strdup(profile->pool, strcasecmp(val, "auto") ? val : globals.guess_ip); + } else if (!strcasecmp(var, "rtp-ip")) { + profile->rtpip = switch_core_strdup(profile->pool, strcasecmp(val, "auto") ? val : globals.guess_ip); + } else if (!strcasecmp(var, "sip-ip")) { + profile->sipip = switch_core_strdup(profile->pool, strcasecmp(val, "auto") ? val : globals.guess_ip); + } else if (!strcasecmp(var, "ext-sip-ip")) { + if (!strcasecmp(val, "auto")) { + profile->extsipip = switch_core_strdup(profile->pool, globals.guess_ip); + } else { + char *ip = NULL; + switch_port_t port = 0; + if (sofia_glue_ext_address_lookup(&ip, &port, val, profile->pool) == SWITCH_STATUS_SUCCESS) { + + if (ip) { + profile->extsipip = switch_core_strdup(profile->pool, ip); + } + } + } + + } else if (!strcasecmp(var, "sip-domain")) { + profile->sipdomain = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "rtp-timer-name")) { + profile->timer_name = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "hold-music")) { + profile->hold_music = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "manage-presence")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_PRESENCE; + } + } else if (!strcasecmp(var, "pass-rfc2833")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_PASS_RFC2833; + } + } else if (!strcasecmp(var, "disable-transcoding")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_DISABLE_TRANSCODING; + } + } else if (!strcasecmp(var, "auth-calls")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_AUTH_CALLS; + } + } else if (!strcasecmp(var, "nonce-ttl")) { + profile->nonce_ttl = atoi(val); + } else if (!strcasecmp(var, "accept-blind-reg")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_BLIND_REG; + } + } else if (!strcasecmp(var, "auth-all-packets")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_AUTH_ALL; + } + } else if (!strcasecmp(var, "full-id-in-dialplan")) { + if (switch_true(val)) { + profile->pflags |= PFLAG_FULL_ID; + } + } else if (!strcasecmp(var, "bitpacking")) { + if (!strcasecmp(val, "aal2")) { + profile->codec_flags = SWITCH_CODEC_FLAG_AAL2; + } + } else if (!strcasecmp(var, "username")) { + profile->username = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "context")) { + profile->context = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "alias")) { + sip_alias_node_t *node; + if ((node = switch_core_alloc(profile->pool, sizeof(*node)))) { + if ((node->url = switch_core_strdup(profile->pool, val))) { + node->next = profile->aliases; + profile->aliases = node; + } + } + } else if (!strcasecmp(var, "dialplan")) { + profile->dialplan = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "max-calls")) { + profile->max_calls = atoi(val); + } else if (!strcasecmp(var, "codec-prefs")) { + profile->codec_string = switch_core_strdup(profile->pool, val); + } else if (!strcasecmp(var, "codec-ms")) { + profile->codec_ms = atoi(val); + } else if (!strcasecmp(var, "dtmf-duration")) { + int dur = atoi(val); + if (dur > 10 && dur < 8000) { + profile->dtmf_duration = dur; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Duration out of bounds!\n"); + } + } + } + + if (!profile->cng_pt) { + profile->cng_pt = SWITCH_RTP_CNG_PAYLOAD; + } + + if (!profile->sipip) { + profile->sipip = switch_core_strdup(profile->pool, globals.guess_ip); + } + + if (!profile->rtpip) { + profile->rtpip = switch_core_strdup(profile->pool, globals.guess_ip); + } + + if (profile->nonce_ttl < 60) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Setting nonce TTL to 60 seconds\n"); + profile->nonce_ttl = 60; + } + + if (switch_test_flag(profile, TFLAG_TIMER) && !profile->timer_name) { + profile->timer_name = switch_core_strdup(profile->pool, "soft"); + } + + if (!profile->username) { + profile->username = switch_core_strdup(profile->pool, "FreeSWITCH"); + } + + if (!profile->rtpip) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Setting ip to '127.0.0.1'\n"); + profile->rtpip = switch_core_strdup(profile->pool, "127.0.0.1"); + } + + if (!profile->sip_port) { + profile->sip_port = atoi(SOFIA_DEFAULT_PORT); + } + + if (!profile->dialplan) { + profile->dialplan = switch_core_strdup(profile->pool, "XML"); + } + + if (!profile->sipdomain) { + profile->sipdomain = switch_core_strdup(profile->pool, profile->sipip); + } + if (profile->extsipip) { + profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->extsipip, profile->sip_port); + profile->bindurl = switch_core_sprintf(profile->pool, "%s;maddr=%s", profile->url, profile->sipip); + } else { + profile->url = switch_core_sprintf(profile->pool, "sip:mod_sofia@%s:%d", profile->sipip, profile->sip_port); + profile->bindurl = profile->url; + } + } + if (profile) { + if ((gateways_tag = switch_xml_child(xprofile, "registrations"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, + "The syntax has been discontinued, please see the new syntax in the default configuration examples\n"); + } + + if ((gateways_tag = switch_xml_child(xprofile, "gateways"))) { + for (gateway_tag = switch_xml_child(gateways_tag, "gateway"); gateway_tag; gateway_tag = gateway_tag->next) { + char *name = (char *) switch_xml_attr_soft(gateway_tag, "name"); + outbound_reg_t *gateway; + + if (switch_strlen_zero(name)) { + name = "anonymous"; + } + + if ((gateway = switch_core_alloc(profile->pool, sizeof(*gateway)))) { + char *scheme = "Digest", + *realm = NULL, + *username = NULL, *password = NULL, *extension = NULL, *proxy = NULL, *context = "default", *expire_seconds = "3600"; + + gateway->pool = profile->pool; + gateway->profile = profile; + gateway->name = switch_core_strdup(gateway->pool, name); + gateway->freq = 0; + + + for (param = switch_xml_child(gateway_tag, "param"); param; param = param->next) { + char *var = (char *) switch_xml_attr_soft(param, "name"); + char *val = (char *) switch_xml_attr_soft(param, "value"); + + if (!strcmp(var, "scheme")) { + scheme = val; + } else if (!strcmp(var, "realm")) { + realm = val; + } else if (!strcmp(var, "username")) { + username = val; + } else if (!strcmp(var, "password")) { + password = val; + } else if (!strcmp(var, "extension")) { + extension = val; + } else if (!strcmp(var, "proxy")) { + proxy = val; + } else if (!strcmp(var, "context")) { + context = val; + } else if (!strcmp(var, "expire-seconds")) { + expire_seconds = val; + } + } + + if (switch_strlen_zero(realm)) { + realm = name; + } + + if (switch_strlen_zero(username)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: username param is REQUIRED!\n"); + switch_xml_free(xml); + goto skip; + } + + if (switch_strlen_zero(password)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERROR: password param is REQUIRED!\n"); + switch_xml_free(xml); + goto skip; + } + + if (switch_strlen_zero(extension)) { + extension = username; + } + + if (switch_strlen_zero(proxy)) { + proxy = realm; + } + + gateway->register_scheme = switch_core_strdup(gateway->pool, scheme); + gateway->register_context = switch_core_strdup(gateway->pool, context); + gateway->register_realm = switch_core_strdup(gateway->pool, realm); + gateway->register_username = switch_core_strdup(gateway->pool, username); + gateway->register_password = switch_core_strdup(gateway->pool, password); + gateway->register_from = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, realm); + gateway->register_contact = switch_core_sprintf(gateway->pool, "sip:%s@%s:%d", extension, profile->sipip, profile->sip_port); + + if (!strncasecmp(proxy, "sip:", 4)) { + gateway->register_proxy = switch_core_strdup(gateway->pool, proxy); + gateway->register_to = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, proxy + 4); + } else { + gateway->register_proxy = switch_core_sprintf(gateway->pool, "sip:%s", proxy); + gateway->register_to = switch_core_sprintf(gateway->pool, "sip:%s@%s", username, proxy); + } + + gateway->expires_str = switch_core_strdup(gateway->pool, expire_seconds); + + if ((gateway->freq = atoi(gateway->expires_str)) < 5) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, + "Invalid Freq: %d. Setting Register-Frequency to 3600\n", gateway->freq); + gateway->freq = 3600; + } + gateway->freq -= 2; + + gateway->next = profile->gateways; + profile->gateways = gateway; + + if (sofia_reg_find_gateway(gateway->name)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate gateway '%s'\n", gateway->name); + } else if (sofia_reg_find_gateway(gateway->register_from)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate uri '%s'\n", gateway->register_from); + } else if (sofia_reg_find_gateway(gateway->register_contact)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Ignoring duplicate contact '%s'\n", gateway->register_from); + } else { + sofia_reg_add_gateway(gateway->name, gateway); + sofia_reg_add_gateway(gateway->register_from, gateway); + sofia_reg_add_gateway(gateway->register_contact, gateway); + } + } + + skip: + assert(gateway_tag); + } + } + + if (profile->sipip) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Started Profile %s [%s]\n", profile->name, url); + launch_sofia_profile_thread(profile); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Unable to start Profile %s due to no configured sip-ip\n", profile->name); + } + profile = NULL; + } + } + } + done: + if (xml) { + switch_xml_free(xml); + } + + return status; + +} + +void sofia_handle_sip_i_state(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + const char *l_sdp = NULL, *r_sdp = NULL; + int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; + int ss_state = nua_callstate_init; + switch_channel_t *channel = NULL; + private_object_t *tech_pvt = NULL; + switch_core_session_t *session = NULL; + const char *replaces_str = NULL; + char *uuid; + switch_core_session_t *other_session = NULL; + switch_channel_t *other_channel = NULL; + char st[80] = ""; + + + if (sofia_private) { + if (!switch_strlen_zero(sofia_private->uuid)) { + if (!(session = switch_core_session_locate(sofia_private->uuid))) { + /* too late */ + return; + } + } + } + + + tl_gets(tags, + NUTAG_CALLSTATE_REF(ss_state), + NUTAG_OFFER_RECV_REF(offer_recv), + NUTAG_ANSWER_RECV_REF(answer_recv), + NUTAG_OFFER_SENT_REF(offer_sent), + NUTAG_ANSWER_SENT_REF(answer_sent), + SIPTAG_REPLACES_STR_REF(replaces_str), SOATAG_LOCAL_SDP_STR_REF(l_sdp), SOATAG_REMOTE_SDP_STR_REF(r_sdp), TAG_END()); + + if (session) { + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + assert(tech_pvt->nh != NULL); + + if (switch_channel_test_flag(channel, CF_NOMEDIA)) { + switch_set_flag(tech_pvt, TFLAG_NOMEDIA); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Channel %s entering state [%s]\n", + switch_channel_get_name(channel), nua_callstate_name(ss_state)); + + if (r_sdp) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Remote SDP:\n%s\n", r_sdp); + tech_pvt->remote_sdp_str = switch_core_session_strdup(session, r_sdp); + switch_channel_set_variable(channel, SWITCH_R_SDP_VARIABLE, r_sdp); + sofia_glue_pass_sdp(tech_pvt, (char *) r_sdp); + + } + } + + if (status == 988) { + goto done; + } + + switch ((enum nua_callstate) ss_state) { + case nua_callstate_init: + break; + case nua_callstate_authenticating: + break; + case nua_callstate_calling: + break; + case nua_callstate_proceeding: + if (channel) { + if (status == 180) { + switch_channel_mark_ring_ready(channel); + if (!switch_channel_test_flag(channel, CF_GEN_RINGBACK)) { + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + switch_core_session_message_t msg; + msg.message_id = SWITCH_MESSAGE_INDICATE_RINGING; + msg.from = __FILE__; + switch_core_session_receive_message(other_session, &msg); + switch_core_session_rwunlock(other_session); + } + + } else { + switch_core_session_queue_indication(session, SWITCH_MESSAGE_INDICATE_RINGING); + } + } + } + + if (r_sdp) { + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); + switch_channel_mark_pre_answered(channel); + if (!switch_channel_test_flag(channel, CF_GEN_RINGBACK) && (uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + other_channel = switch_core_session_get_channel(other_session); + if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); + } + + switch_channel_pre_answer(other_channel); + switch_core_session_rwunlock(other_session); + } + goto done; + } else { + if (switch_test_flag(tech_pvt, TFLAG_LATE_NEGOTIATION)) { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "DELAYED NEGOTIATION"); + } else { + if (sofia_glue_tech_media(tech_pvt, (char *) r_sdp) != SWITCH_STATUS_SUCCESS) { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "CODEC NEGOTIATION ERROR"); + nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); + } + } + goto done; + } + } + } + break; + case nua_callstate_completing: + nua_ack(nh, TAG_END()); + break; + case nua_callstate_received: + + if (session && switch_core_session_running(session)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Re-Entering Call State Received!\n"); + goto done; + } + + if (channel) { + if (r_sdp) { + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOMEDIA"); + switch_channel_set_state(channel, CS_INIT); + switch_set_flag_locked(tech_pvt, TFLAG_READY); + switch_core_session_thread_launch(session); + goto done; + } else { + sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); + sdp_session_t *sdp; + uint8_t match = 0; + + if (tech_pvt->num_codecs) { + if ((sdp = sdp_session(parser))) { + match = negotiate_sdp(session, sdp); + } + } + + if (parser) { + sdp_parser_free(parser); + } + + if (match) { + nua_handle_t *bnh; + sip_replaces_t *replaces; + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED"); + switch_channel_set_state(channel, CS_INIT); + switch_set_flag_locked(tech_pvt, TFLAG_READY); + + switch_core_session_thread_launch(session); + + if (replaces_str && (replaces = sip_replaces_make(tech_pvt->home, replaces_str)) + && (bnh = nua_handle_by_replaces(nua, replaces))) { + sofia_private_t *b_private; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Processing Replaces Attended Transfer\n"); + while (switch_channel_get_state(channel) < CS_EXECUTE) { + switch_yield(10000); + } + + if ((b_private = nua_handle_magic(bnh))) { + char *br_b = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE); + char *br_a = b_private->uuid; + + if (br_b) { + switch_ivr_uuid_bridge(br_a, br_b); + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); + switch_channel_hangup(channel, SWITCH_CAUSE_ATTENDED_TRANSFER); + } else { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + } else { + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); + switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + } + nua_handle_unref(bnh); + } + goto done; + } + + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS"); + nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Invite with no SDP activating no-media-mode\n"); + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "RECEIVED_NOSDP"); + + switch_set_flag(tech_pvt, TFLAG_LATE_NEGOTIATION); + switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); + switch_channel_set_flag(channel, CF_NOMEDIA); + switch_channel_set_state(channel, CS_INIT); + switch_set_flag_locked(tech_pvt, TFLAG_READY); + switch_core_session_thread_launch(session); + goto done; + } + } + + break; + case nua_callstate_early: + break; + case nua_callstate_completed: + if (tech_pvt && r_sdp) { + if (r_sdp) { + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + goto done; + } else { + sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); + sdp_session_t *sdp; + uint8_t match = 0; + + if (tech_pvt->num_codecs) { + if ((sdp = sdp_session(parser))) { + match = negotiate_sdp(session, sdp); + } + } + if (match) { + if (sofia_glue_tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { + goto done; + } + sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); + switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); + sofia_glue_activate_rtp(tech_pvt); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Processing Reinvite\n"); + if (parser) { + sdp_parser_free(parser); + } + } + } + } + } + break; + case nua_callstate_ready: + if (tech_pvt && nh == tech_pvt->nh2) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Cheater Reinvite!\n"); + switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); + tech_pvt->nh = tech_pvt->nh2; + tech_pvt->nh2 = NULL; + if (sofia_glue_tech_choose_port(tech_pvt) == SWITCH_STATUS_SUCCESS) { + sofia_glue_activate_rtp(tech_pvt); + } + goto done; + } + + if (channel) { + if (switch_test_flag(tech_pvt, TFLAG_EARLY_MEDIA)) { + switch_set_flag_locked(tech_pvt, TFLAG_ANS); + switch_channel_mark_answered(channel); + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + other_channel = switch_core_session_get_channel(other_session); + switch_channel_answer(other_channel); + switch_core_session_rwunlock(other_session); + } + goto done; + } + + if (!r_sdp) { + r_sdp = (const char *) switch_channel_get_variable(channel, SWITCH_R_SDP_VARIABLE); + } + if (r_sdp) { + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + switch_set_flag_locked(tech_pvt, TFLAG_ANS); + switch_channel_mark_answered(channel); + if ((uuid = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(uuid))) { + other_channel = switch_core_session_get_channel(other_session); + if (!switch_channel_get_variable(other_channel, SWITCH_B_SDP_VARIABLE)) { + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, r_sdp); + } + switch_channel_answer(other_channel); + switch_core_session_rwunlock(other_session); + } + goto done; + } else { + sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); + sdp_session_t *sdp; + uint8_t match = 0; + + if (tech_pvt->num_codecs) { + if ((sdp = sdp_session(parser))) { + match = negotiate_sdp(session, sdp); + } + } + + if (parser) { + sdp_parser_free(parser); + } + + + if (match) { + switch_set_flag_locked(tech_pvt, TFLAG_ANS); + if (sofia_glue_tech_choose_port(tech_pvt) == SWITCH_STATUS_SUCCESS) { + sofia_glue_activate_rtp(tech_pvt); + switch_channel_mark_answered(channel); + goto done; + } + } + + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "NO CODECS"); + nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END()); + } + } + + } + + break; + case nua_callstate_terminating: + break; + case nua_callstate_terminated: + if (session) { + if (!switch_test_flag(tech_pvt, TFLAG_BYE)) { + + switch_set_flag_locked(tech_pvt, TFLAG_BYE); + if (switch_test_flag(tech_pvt, TFLAG_NOHUP)) { + switch_clear_flag_locked(tech_pvt, TFLAG_NOHUP); + } else { + snprintf(st, sizeof(st), "%d", status); + switch_channel_set_variable(channel, "sip_term_status", st); + sofia_glue_terminate_session(&session, sofia_glue_sip_cause_to_freeswitch(status), __LINE__); + } + } + + if (tech_pvt->sofia_private) { + free(tech_pvt->sofia_private); + tech_pvt->sofia_private = NULL; + } + tech_pvt->nh = NULL; + } else if (sofia_private) { + free(sofia_private); + } + + if (nh) { + nua_handle_bind(nh, NULL); + nua_handle_destroy(nh); + } + break; + } + + done: + + if (session) { + switch_core_session_rwunlock(session); + } +} + + +/*---------------------------------------*/ +void sofia_handle_sip_i_refer(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) +{ + /* Incoming refer */ + sip_from_t const *from; + sip_to_t const *to; + sip_refer_to_t const *refer_to; + private_object_t *tech_pvt = NULL; + char *etmp = NULL, *exten = NULL; + switch_channel_t *channel_a = NULL, *channel_b = NULL; + + tech_pvt = switch_core_session_get_private(session); + channel_a = switch_core_session_get_channel(session); + + if (!sip->sip_cseq || !(etmp = switch_mprintf("refer;id=%u", sip->sip_cseq->cs_seq))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + goto done; + } + + + if (switch_channel_test_flag(channel_a, CF_NOMEDIA)) { + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); + goto done; + } + + from = sip->sip_from; + to = sip->sip_to; + + if ((refer_to = sip->sip_refer_to)) { + if (profile->pflags & PFLAG_FULL_ID) { + exten = switch_mprintf("%s@%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host); + } else { + exten = (char *) refer_to->r_url->url_user; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Process REFER to [%s@%s]\n", exten, (char *) refer_to->r_url->url_host); + + if (refer_to->r_url->url_headers) { + sip_replaces_t *replaces; + nua_handle_t *bnh; + char *rep; + + if ((rep = strchr(refer_to->r_url->url_headers, '='))) { + char *br_a = NULL, *br_b = NULL; + char *buf; + rep++; + + + + if ((buf = switch_core_session_alloc(session, strlen(rep) + 1))) { + rep = url_unescape(buf, (const char *) rep); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Replaces: [%s]\n", rep); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + goto done; + } + if ((replaces = sip_replaces_make(tech_pvt->home, rep)) + && (bnh = nua_handle_by_replaces(nua, replaces))) { + sofia_private_t *b_private = NULL; + private_object_t *b_tech_pvt = NULL; + switch_core_session_t *b_session = NULL; + + switch_channel_set_variable(channel_a, SOFIA_REPLACES_HEADER, rep); + if ((b_private = nua_handle_magic(bnh))) { + if (!(b_session = switch_core_session_locate(b_private->uuid))) { + goto done; + } + b_tech_pvt = (private_object_t *) switch_core_session_get_private(b_session); + channel_b = switch_core_session_get_channel(b_session); + + br_a = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE); + br_b = switch_channel_get_variable(channel_b, SWITCH_SIGNAL_BOND_VARIABLE); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Attended Transfer [%s][%s]\n", br_a, br_b); + + if (br_a && br_b) { + switch_ivr_uuid_bridge(br_a, br_b); + switch_channel_set_variable(channel_b, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); + switch_set_flag_locked(tech_pvt, TFLAG_BYE); + switch_set_flag_locked(b_tech_pvt, TFLAG_BYE); + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); + + } else { + if (!br_a && !br_b) { + switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); + switch_set_flag_locked(b_tech_pvt, TFLAG_XFER); + b_tech_pvt->xferto = switch_core_session_strdup(b_session, switch_core_session_get_uuid(session)); + } else if (!br_a && br_b) { + switch_core_session_t *br_b_session; + + if ((br_b_session = switch_core_session_locate(br_b))) { + private_object_t *br_b_tech_pvt = switch_core_session_get_private(br_b_session); + switch_channel_t *br_b_channel = switch_core_session_get_channel(br_b_session); + + switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); + + switch_channel_clear_state_handler(br_b_channel, NULL); + switch_channel_set_state_flag(br_b_channel, CF_TRANSFER); + switch_channel_set_state(br_b_channel, CS_TRANSMIT); + + switch_set_flag_locked(tech_pvt, TFLAG_REINVITE); + tech_pvt->local_sdp_audio_ip = switch_core_session_strdup(session, b_tech_pvt->local_sdp_audio_ip); + tech_pvt->local_sdp_audio_port = b_tech_pvt->local_sdp_audio_port; + + tech_pvt->remote_sdp_audio_ip = switch_core_session_strdup(session, br_b_tech_pvt->remote_sdp_audio_ip); + tech_pvt->remote_sdp_audio_port = br_b_tech_pvt->remote_sdp_audio_port; + sofia_glue_activate_rtp(tech_pvt); + + br_b_tech_pvt->kick = switch_core_session_strdup(br_b_session, switch_core_session_get_uuid(session)); + + + switch_core_session_rwunlock(br_b_session); + } + + switch_channel_hangup(channel_b, SWITCH_CAUSE_ATTENDED_TRANSFER); + } + switch_set_flag_locked(tech_pvt, TFLAG_BYE); + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); + + } + if (b_session) { + switch_core_session_rwunlock(b_session); + } + } + nua_handle_unref(bnh); + } else { /* the other channel is on a different box, we have to go find them */ + if (exten && (br_a = switch_channel_get_variable(channel_a, SWITCH_SIGNAL_BOND_VARIABLE))) { + switch_core_session_t *a_session; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if ((a_session = switch_core_session_locate(br_a))) { + switch_core_session_t *tsession; + switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING; + uint32_t timeout = 60; + char *tuuid_str; + + channel = switch_core_session_get_channel(a_session); + + exten = switch_mprintf("sofia/%s/%s@%s:%s", + profile->name, + (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host, refer_to->r_url->url_port); + + switch_channel_set_variable(channel, SOFIA_REPLACES_HEADER, rep); + + if (switch_ivr_originate(a_session, + &tsession, &cause, exten, timeout, &noop_state_handler, NULL, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Create Outgoing Channel! [%s]\n", exten); + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), TAG_END()); + goto done; + } + + switch_core_session_rwunlock(a_session); + tuuid_str = switch_core_session_get_uuid(tsession); + switch_ivr_uuid_bridge(br_a, tuuid_str); + switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER"); + switch_set_flag_locked(tech_pvt, TFLAG_BYE); + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), SIPTAG_EVENT_STR(etmp), TAG_END()); + } else { + goto error; + } + + } else { + error: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Transfer! [%s]\n", br_a); + switch_channel_set_variable(channel_a, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "ATTENDED_TRANSFER_ERROR"); + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_PAYLOAD_STR("SIP/2.0 403 Forbidden"), SIPTAG_EVENT_STR(etmp), + TAG_END()); + } + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot parse Replaces!\n"); + } + goto done; + } + + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Refer-To\n"); + goto done; + } + + if (exten) { + switch_channel_t *channel = switch_core_session_get_channel(session); + char *br; + + if ((br = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE))) { + switch_core_session_t *b_session; + + if ((b_session = switch_core_session_locate(br))) { + switch_channel_set_variable(channel, "TRANSFER_FALLBACK", from->a_user); + switch_ivr_session_transfer(b_session, exten, profile->dialplan, profile->context); + switch_core_session_rwunlock(b_session); + } + + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "BLIND_TRANSFER"); + + /* + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), + SIPTAG_EVENT_STR(etmp), + TAG_END()); + */ + } else { + exten = switch_mprintf("sip:%s@%s:%s", (char *) refer_to->r_url->url_user, (char *) refer_to->r_url->url_host, refer_to->r_url->url_port); + tech_pvt->dest = switch_core_session_strdup(session, exten); + + + switch_set_flag_locked(tech_pvt, TFLAG_NOHUP); + + /* + nua_notify(tech_pvt->nh, SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), + NUTAG_SUBSTATE(nua_substate_terminated), + SIPTAG_PAYLOAD_STR("SIP/2.0 200 OK"), + SIPTAG_EVENT_STR(etmp), + TAG_END()); + */ + sofia_glue_do_xfer_invite(session); + + } + } + + done: + if (exten && strchr(exten, '@')) { + switch_safe_free(exten); + } + if (etmp) { + switch_safe_free(etmp); + } + + +} + + +void sofia_handle_sip_i_info(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) +{ + + //placeholder for string searching + char *signal_ptr; + + //Try and find signal information in the payload + signal_ptr = strstr(sip->sip_payload->pl_data, "Signal="); + + //See if we found a match + if (signal_ptr) { + struct private_object *tech_pvt = NULL; + switch_channel_t *channel = NULL; + char dtmf_digit[2] = { 0, 0 }; + + //Get the channel + channel = switch_core_session_get_channel(session); + + //Barf if we didn't get it + assert(channel != NULL); + + //make sure we have our privates + tech_pvt = switch_core_session_get_private(session); + + //Barf if we didn't get it + assert(tech_pvt != NULL); + + //move signal_ptr where we need it (right past Signal=) + signal_ptr = signal_ptr + 7; + + //put the digit somewhere we can muck with + strncpy(dtmf_digit, signal_ptr, 1); + + //queue it up + switch_channel_queue_dtmf(channel, dtmf_digit); + + //print debug info + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "INFO DTMF(%s)\n", dtmf_digit); + + } else { //unknown info type + sip_from_t const *from; + + from = sip->sip_from; + + //print in the logs if something comes through we don't understand + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Unknown INFO Recieved: %s%s" URL_PRINT_FORMAT "[%s]\n", + from->a_display ? from->a_display : "", from->a_display ? " " : "", URL_PRINT_ARGS(from->a_url), sip->sip_payload->pl_data); + } + + return; +} + +#define url_set_chanvars(session, url, varprefix) _url_set_chanvars(session, url, #varprefix "_user", #varprefix "_host", #varprefix "_port", #varprefix "_uri") +const char *_url_set_chanvars(switch_core_session_t *session, url_t * url, const char *user_var, + const char *host_var, const char *port_var, const char *uri_var) +{ + const char *user = NULL, *host = NULL, *port = NULL; + char *uri = NULL; + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (url) { + user = url->url_user; + host = url->url_host; + port = url->url_port; + } + + if (user) { + switch_channel_set_variable(channel, user_var, user); + } + + if (!port) { + port = SOFIA_DEFAULT_PORT; + } + + switch_channel_set_variable(channel, port_var, port); + if (host) { + if (user) { + uri = switch_core_session_sprintf(session, "%s@%s:%s", user, host, port); + } else { + uri = switch_core_session_sprintf(session, "%s:%s", host, port); + } + switch_channel_set_variable_nodup(channel, uri_var, uri); + switch_channel_set_variable(channel, host_var, host); + } + + return uri; +} + +void process_rpid(sip_unknown_t * un, private_object_t * tech_pvt) +{ + int argc, x, screen = 1; + char *mydata, *argv[10] = { 0 }; + if (!switch_strlen_zero(un->un_value)) { + if ((mydata = strdup(un->un_value))) { + argc = switch_separate_string(mydata, ';', argv, (sizeof(argv) / sizeof(argv[0]))); + + // Do We really need this at this time + // clid_uri = argv[0]; + + for (x = 1; x < argc && argv[x]; x++) { + // we dont need to do anything with party yet we should only be seeing party=calling here anyway + // maybe thats a dangerous assumption bit oh well yell at me later + // if (!strncasecmp(argv[x], "party", 5)) { + // party = argv[x]; + // } else + if (!strncasecmp(argv[x], "privacy=", 8)) { + char *arg = argv[x] + 9; + + if (!strcasecmp(arg, "yes")) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); + } else if (!strcasecmp(arg, "full")) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME | SWITCH_CPF_HIDE_NUMBER); + } else if (!strcasecmp(arg, "name")) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME); + } else if (!strcasecmp(arg, "number")) { + switch_set_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NUMBER); + } else { + switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NAME); + switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_HIDE_NUMBER); + } + + } else if (!strncasecmp(argv[x], "screen=", 7) && screen > 0) { + char *arg = argv[x] + 8; + if (!strcasecmp(arg, "no")) { + screen = 0; + switch_clear_flag(tech_pvt->caller_profile, SWITCH_CPF_SCREEN); + } + } + } + free(mydata); + } + } +} + +void sofia_handle_sip_i_invite(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + switch_core_session_t *session = NULL; + char key[128] = ""; + sip_unknown_t *un; + private_object_t *tech_pvt = NULL; + switch_channel_t *channel = NULL; + const char *channel_name = NULL; + const char *displayname = NULL; + const char *destination_number = NULL; + const char *from_user = NULL, *from_host = NULL; + const char *context = NULL; + char network_ip[80]; + + if (!sip || !sip->sip_request || !sip->sip_request->rq_method_name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received an invalid packet!\n"); + nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); + return; + } + + + if (!(sip->sip_contact && sip->sip_contact->m_url)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n"); + nua_respond(nh, 400, "Missing Contact Header", TAG_END()); + return; + } + + if ((profile->pflags & PFLAG_AUTH_CALLS)) { + if (sofia_reg_handle_register(nua, profile, nh, sip, REG_INVITE, key, sizeof(key))) { + return; + } + } + + if (!(session = switch_core_session_request(&sofia_endpoint_interface, NULL))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Session Alloc Failed!\n"); + nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, TAG_END()); + return; + } + + if (!(tech_pvt = (private_object_t *) switch_core_session_alloc(session, sizeof(private_object_t)))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Hey where is my memory pool?\n"); + nua_respond(nh, SIP_503_SERVICE_UNAVAILABLE, TAG_END()); + sofia_glue_terminate_session(&session, SWITCH_CAUSE_SWITCH_CONGESTION, __LINE__); + return; + } + + if (!switch_strlen_zero(key)) { + tech_pvt->key = switch_core_session_strdup(session, key); + } + + get_addr(network_ip, sizeof(network_ip), &((struct sockaddr_in *) msg_addrinfo(nua_current_request(nua))->ai_addr)->sin_addr); + + channel = switch_core_session_get_channel(session); + + if (sip->sip_from && sip->sip_from->a_url) { + from_user = sip->sip_from->a_url->url_user; + from_host = sip->sip_from->a_url->url_host; + channel_name = url_set_chanvars(session, sip->sip_from->a_url, sip_from); + + if (!switch_strlen_zero(from_user)) { + if (*from_user == '+') { + switch_channel_set_variable(channel, "sip_from_user_stripped", (const char *) (from_user + 1)); + } else { + switch_channel_set_variable(channel, "sip_from_user_stripped", from_user); + } + } + + if (!switch_strlen_zero(sip->sip_from->a_display)) { + char *tmp; + tmp = switch_core_session_strdup(session, sip->sip_from->a_display); + if (*tmp == '"') { + char *p; + + tmp++; + if ((p = strchr(tmp, '"'))) { + *p = '\0'; + } + } + displayname = tmp; + } else { + displayname = switch_strlen_zero(from_user) ? "unkonwn" : from_user; + } + } + + if (sip->sip_request->rq_url) { + const char *req_uri = url_set_chanvars(session, sip->sip_request->rq_url, sip_req); + if (profile->pflags & PFLAG_FULL_ID) { + destination_number = req_uri; + } else { + destination_number = sip->sip_request->rq_url->url_user; + } + } + + if (sip->sip_to && sip->sip_to->a_url) { + url_set_chanvars(session, sip->sip_to->a_url, sip_to); + } + + if (sip->sip_contact && sip->sip_contact->m_url) { + const char *contact_uri = url_set_chanvars(session, sip->sip_contact->m_url, sip_contact); + if (!channel_name) { + channel_name = contact_uri; + } + } + + sofia_glue_attach_private(session, profile, tech_pvt, channel_name); + sofia_glue_sofia_glue_tech_set_codecs(tech_pvt); + + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "INBOUND CALL"); + sofia_presence_set_chat_hash(tech_pvt, sip); + + if (switch_test_flag(tech_pvt, TFLAG_INB_NOMEDIA)) { + switch_set_flag_locked(tech_pvt, TFLAG_NOMEDIA); + switch_channel_set_flag(channel, CF_NOMEDIA); + } + + if (!tech_pvt->call_id && sip->sip_call_id && sip->sip_call_id->i_id) { + tech_pvt->call_id = switch_core_session_strdup(session, sip->sip_call_id->i_id); + switch_channel_set_variable(channel, "sip_call_id", tech_pvt->call_id); + } + + if (sip->sip_via) { + if (sip->sip_via->v_host) { + switch_channel_set_variable(channel, "sip_via_host", sip->sip_via->v_host); + } + if (sip->sip_via->v_port) { + switch_channel_set_variable(channel, "sip_via_port", sip->sip_via->v_port); + } + if (sip->sip_via->v_rport) { + switch_channel_set_variable(channel, "sip_via_rport", sip->sip_via->v_rport); + } + } + + if (sip->sip_max_forwards) { + char max_forwards[32]; + snprintf(max_forwards, sizeof(max_forwards), "%lu", sip->sip_max_forwards->mf_count); + switch_channel_set_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE, max_forwards); + } + + + if (sip->sip_request->rq_url) { + outbound_reg_t *gateway; + char *from_key = switch_core_session_sprintf(session, "sip:%s@%s", + (char *) sip->sip_request->rq_url->url_user, + (char *) sip->sip_request->rq_url->url_host); + + if ((gateway = sofia_reg_find_gateway(from_key))) { + context = gateway->register_context; + switch_channel_set_variable(channel, "sip_gateway", gateway->name); + } + } + + + if (!context) { + if (profile->context && !strcasecmp(profile->context, "_domain_")) { + context = from_host; + } else { + context = profile->context; + } + } + + tech_pvt->caller_profile = switch_caller_profile_new(switch_core_session_get_pool(session), + from_user, + profile->dialplan, + displayname, from_user, network_ip, NULL, NULL, NULL, modname, context, destination_number); + + if (tech_pvt->caller_profile) { + + /* Loop thru unknown Headers Here so we can do something with them */ + for (un = sip->sip_unknown; un; un = un->un_next) { + if (!strncasecmp(un->un_name, "Alert-Info", 10)) { + if (!switch_strlen_zero(un->un_value)) { + switch_channel_set_variable(channel, "alert_info", un->un_value); + } + } else if (!strncasecmp(un->un_name, "Remote-Party-ID", 15)) { + process_rpid(un, tech_pvt); + } else if (!strncasecmp(un->un_name, "X-", 2)) { + if (!switch_strlen_zero(un->un_value)) { + char *new_name; + if ((new_name = switch_mprintf("%s%s", SOFIA_SIP_HEADER_PREFIX, un->un_name))) { + switch_channel_set_variable(channel, new_name, un->un_value); + free(new_name); + } + } + } + } + + switch_channel_set_caller_profile(channel, tech_pvt->caller_profile); + } + + if (!(tech_pvt->sofia_private = malloc(sizeof(*tech_pvt->sofia_private)))) { + abort(); + } + memset(tech_pvt->sofia_private, 0, sizeof(*tech_pvt->sofia_private)); + switch_copy_string(tech_pvt->sofia_private->uuid, switch_core_session_get_uuid(session), sizeof(tech_pvt->sofia_private->uuid)); + nua_handle_bind(nh, tech_pvt->sofia_private); + tech_pvt->nh = nh; +} + +void sofia_handle_sip_i_options(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + nua_respond(nh, SIP_200_OK, + //SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + //SOATAG_AUDIO_AUX("cn telephone-event"), + //NUTAG_INCLUDE_EXTRA_SDP(1), + TAG_END()); +} + + + + diff --git a/src/mod/endpoints/mod_sofia/sofia_glue.c b/src/mod/endpoints/mod_sofia/sofia_glue.c new file mode 100644 index 0000000000..b500a69b67 --- /dev/null +++ b/src/mod/endpoints/mod_sofia/sofia_glue.c @@ -0,0 +1,1110 @@ +#include "mod_sofia.h" + + +void sofia_glue_set_local_sdp(private_object_t *tech_pvt, char *ip, uint32_t port, char *sr, int force) +{ + char buf[2048]; + switch_time_t now = switch_time_now(); + int ptime = 0; + int rate = 0; + + if (!force && !ip && !sr && switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + return; + } + + if (!ip) { + if (!(ip = tech_pvt->adv_sdp_audio_ip)) { + ip = tech_pvt->proxy_sdp_audio_ip; + } + } + if (!port) { + if (!(port = tech_pvt->adv_sdp_audio_port)) { + port = tech_pvt->proxy_sdp_audio_port; + } + } + if (!sr) { + sr = "sendrecv"; + } + + snprintf(buf, sizeof(buf), + "v=0\n" + "o=FreeSWITCH %d%" SWITCH_TIME_T_FMT " %d%" SWITCH_TIME_T_FMT " IN IP4 %s\n" + "s=FreeSWITCH\n" "c=IN IP4 %s\n" "t=0 0\n" "a=%s\n" "m=audio %d RTP/AVP", port, now, port, now, ip, ip, sr, port); + + if (tech_pvt->rm_encoding) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->pt); + } else if (tech_pvt->num_codecs) { + int i; + for (i = 0; i < tech_pvt->num_codecs; i++) { + const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", imp->ianacode); + if (!ptime) { + ptime = imp->microseconds_per_frame / 1000; + } + } + } + + if (tech_pvt->te > 95) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->te); + } + + if (tech_pvt->cng_pt) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %d", tech_pvt->cng_pt); + } + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "\n"); + + if (tech_pvt->rm_encoding) { + rate = tech_pvt->rm_rate; + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%ld\n", tech_pvt->pt, tech_pvt->rm_encoding, tech_pvt->rm_rate); + if (tech_pvt->fmtp_out) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=fmtp:%d %s\n", tech_pvt->pt, tech_pvt->fmtp_out); + } + if (tech_pvt->read_codec.implementation && !ptime) { + ptime = tech_pvt->read_codec.implementation->microseconds_per_frame / 1000; + } + + } else if (tech_pvt->num_codecs) { + int i; + for (i = 0; i < tech_pvt->num_codecs; i++) { + const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + uint32_t rfc_3551_sucks = imp->samples_per_second; + + if (!rate) { + rate = imp->samples_per_second; + } + if (ptime && ptime != imp->microseconds_per_frame / 1000) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "ptime %u != advertised ptime %u\n", imp->microseconds_per_frame / 1000, ptime); + } + + if (rfc_3551_sucks && imp->ianacode == 9) { + rfc_3551_sucks = 8000; + } + + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d %s/%d\n", imp->ianacode, imp->iananame, rfc_3551_sucks); + if (imp->fmtp) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=fmtp:%d %s\n", imp->ianacode, imp->fmtp); + } + } + } + + if (tech_pvt->te > 95) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d telephone-event/8000\na=fmtp:%d 0-16\n", tech_pvt->te, tech_pvt->te); + } + if (tech_pvt->cng_pt) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=rtpmap:%d CN/%d\n", tech_pvt->cng_pt, rate); + if (!tech_pvt->rm_encoding) { + tech_pvt->cng_pt = 0; + } + } + if (ptime) { + snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "a=ptime:%d\n", ptime); + } + + tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, buf); +} + +void sofia_glue_sofia_glue_tech_set_codecs(private_object_t * tech_pvt) +{ + switch_channel_t *channel; + char *abs, *codec_string = NULL; + char *ocodec = NULL; + + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + return; + } + + if (tech_pvt->num_codecs) { + return; + } + + assert(tech_pvt->session != NULL); + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + + if ((abs = switch_channel_get_variable(channel, "absolute_codec_string"))) { + codec_string = abs; + } else { + if (!(codec_string = switch_channel_get_variable(channel, "codec_string"))) { + if (tech_pvt->profile->codec_string) { + codec_string = tech_pvt->profile->codec_string; + } + } + + if ((ocodec = switch_channel_get_variable(channel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) { + if (!codec_string || (tech_pvt->profile->pflags & PFLAG_DISABLE_TRANSCODING)) { + codec_string = ocodec; + } else { + if (!(codec_string = switch_core_session_sprintf(tech_pvt->session, "%s,%s", ocodec, codec_string))) { + codec_string = ocodec; + } + } + } + } + + if (codec_string) { + char *tmp_codec_string; + if ((tmp_codec_string = switch_core_session_strdup(tech_pvt->session, codec_string))) { + tech_pvt->codec_order_last = switch_separate_string(tmp_codec_string, ',', tech_pvt->codec_order, SWITCH_MAX_CODECS); + tech_pvt->num_codecs = + switch_loadable_module_get_codecs_sorted(tech_pvt->codecs, SWITCH_MAX_CODECS, tech_pvt->codec_order, tech_pvt->codec_order_last); + } + } else { + tech_pvt->num_codecs = + switch_loadable_module_get_codecs(switch_core_session_get_pool(tech_pvt->session), tech_pvt->codecs, + sizeof(tech_pvt->codecs) / sizeof(tech_pvt->codecs[0])); + } + +} + + +void sofia_glue_attach_private(switch_core_session_t *session, sofia_profile_t * profile, private_object_t * tech_pvt, const char *channame) +{ + switch_channel_t *channel; + char name[256]; + + assert(session != NULL); + assert(profile != NULL); + assert(tech_pvt != NULL); + + switch_core_session_add_stream(session, NULL); + channel = switch_core_session_get_channel(session); + + //switch_channel_set_flag(channel, CF_ACCEPT_CNG); + + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(session)); + switch_mutex_lock(tech_pvt->flag_mutex); + tech_pvt->flags = profile->flags; + switch_mutex_unlock(tech_pvt->flag_mutex); + tech_pvt->profile = profile; + if (tech_pvt->bte) { + tech_pvt->te = tech_pvt->bte; + } else if (!tech_pvt->te) { + tech_pvt->te = profile->te; + } + + if (tech_pvt->bcng_pt) { + tech_pvt->cng_pt = tech_pvt->bcng_pt; + } else if (!tech_pvt->cng_pt) { + tech_pvt->cng_pt = profile->cng_pt; + } + + tech_pvt->session = session; + tech_pvt->home = su_home_new(sizeof(*tech_pvt->home)); + + switch_core_session_set_private(session, tech_pvt); + + + snprintf(name, sizeof(name), "sofia/%s/%s", profile->name, channame); + switch_channel_set_name(channel, name); + //sofia_glue_sofia_glue_tech_set_codecs(tech_pvt); + +} + +void sofia_glue_terminate_session(switch_core_session_t **session, switch_call_cause_t cause, int line) +{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Term called from line: %d\n", line); + if (*session) { + switch_channel_t *channel = switch_core_session_get_channel(*session); + struct private_object *tech_pvt = NULL; + unsigned running = switch_core_session_running(*session); + tech_pvt = switch_core_session_get_private(*session); + + if (running) { + switch_channel_hangup(channel, cause); + } else { + if (tech_pvt) { + sofia_on_hangup(*session); + } + if (session && *session) { + switch_core_session_destroy(session); + } + } + } +} + + + +switch_status_t sofia_glue_ext_address_lookup(char **ip, switch_port_t *port, char *sourceip, switch_memory_pool_t *pool) +{ + char *error; + + if (!sourceip) { + return SWITCH_STATUS_FALSE; + } + + if (!strncasecmp(sourceip, "stun:", 5)) { + char *stun_ip = sourceip + 5; + if (!stun_ip) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! NO STUN SERVER\n"); + return SWITCH_STATUS_FALSE; + } + if (switch_stun_lookup(ip, port, stun_ip, SWITCH_STUN_DEFAULT_PORT, &error, pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Stun Failed! %s:%d [%s]\n", stun_ip, SWITCH_STUN_DEFAULT_PORT, error); + return SWITCH_STATUS_FALSE; + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Stun Success [%s]:[%d]\n", *ip, *port); + } else { + *ip = sourceip; + } + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t sofia_glue_tech_choose_port(private_object_t * tech_pvt) +{ + char *ip = tech_pvt->profile->rtpip; + switch_channel_t *channel; + switch_port_t sdp_port; + char tmp[50]; + + channel = switch_core_session_get_channel(tech_pvt->session); + + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA) || tech_pvt->adv_sdp_audio_port) { + return SWITCH_STATUS_SUCCESS; + } + + tech_pvt->local_sdp_audio_ip = ip; + tech_pvt->local_sdp_audio_port = switch_rtp_request_port(); + sdp_port = tech_pvt->local_sdp_audio_port; + + if (tech_pvt->profile->extrtpip) { + if (sofia_glue_ext_address_lookup(&ip, &sdp_port, tech_pvt->profile->extrtpip, switch_core_session_get_pool(tech_pvt->session)) != + SWITCH_STATUS_SUCCESS) { + sofia_glue_terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + return SWITCH_STATUS_FALSE; + } + } + + tech_pvt->adv_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, ip); + tech_pvt->adv_sdp_audio_port = sdp_port; + + snprintf(tmp, sizeof(tmp), "%d", sdp_port); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); + + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t sofia_glue_do_invite(switch_core_session_t *session) +{ + char rpid[1024] = { 0 }; + char alert_info[1024] = { 0 }; + char max_forwards[8] = { 0 }; + char *alertbuf; + char *forwardbuf; + int forwardval; + private_object_t *tech_pvt; + switch_channel_t *channel = NULL; + switch_caller_profile_t *caller_profile; + char *cid_name, *cid_num; + char *e_dest = NULL; + const char *holdstr = ""; + switch_stream_handle_t stream = { 0 }; + switch_hash_index_t *hi; + void *vval; + char *extra_headers = NULL; + const void *vvar; + switch_status_t status = SWITCH_STATUS_FALSE; + char *rep; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + rep = switch_channel_get_variable(channel, SOFIA_REPLACES_HEADER); + + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + caller_profile = switch_channel_get_caller_profile(channel); + + cid_name = (char *) caller_profile->caller_id_name; + cid_num = (char *) caller_profile->caller_id_number; + sofia_glue_sofia_glue_tech_set_codecs(tech_pvt); + + if (!tech_pvt->from_str) { + tech_pvt->from_str = switch_core_session_sprintf(tech_pvt->session, "\"%s\" ", + cid_name, + cid_num, tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip); + + } + + if (!tech_pvt->from_str) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + return SWITCH_STATUS_FALSE; + } + + + + + if ((alertbuf = switch_channel_get_variable(channel, "alert_info"))) { + snprintf(alert_info, sizeof(alert_info) - 1, "Alert-Info: %s", alertbuf); + } + + if ((forwardbuf = switch_channel_get_variable(channel, SWITCH_MAX_FORWARDS_VARIABLE))) { + forwardval = atoi(forwardbuf) - 1; + snprintf(max_forwards, sizeof(max_forwards) - 1, "%d", forwardval); + } + + if (sofia_glue_tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { + return status; + } + + sofia_glue_set_local_sdp(tech_pvt, NULL, 0, NULL, 0); + + switch_set_flag_locked(tech_pvt, TFLAG_READY); + + // forge a RPID for now KHR -- Should wrap this in an if statement so it can be turned on and off + if (switch_test_flag(caller_profile, SWITCH_CPF_SCREEN)) { + const char *priv = "off"; + const char *screen = "no"; + if (switch_test_flag(caller_profile, SWITCH_CPF_HIDE_NAME)) { + priv = "name"; + if (switch_test_flag(caller_profile, SWITCH_CPF_HIDE_NUMBER)) { + priv = "yes"; + } + } else if (switch_test_flag(caller_profile, SWITCH_CPF_HIDE_NUMBER)) { + priv = "yes"; + } + if (switch_test_flag(caller_profile, SWITCH_CPF_SCREEN)) { + screen = "yes"; + } + + snprintf(rpid, sizeof(rpid) - 1, "Remote-Party-ID: %s;party=calling;screen=%s;privacy=%s", tech_pvt->from_str, screen, priv); + + } + + if (!tech_pvt->nh) { + char *url = sofia_glue_get_url_from_contact(tech_pvt->dest, 1); + tech_pvt->nh = nua_handle(tech_pvt->profile->nua, NULL, + NUTAG_URL(url), + SIPTAG_TO_STR(tech_pvt->dest_to), SIPTAG_FROM_STR(tech_pvt->from_str), SIPTAG_CONTACT_STR(tech_pvt->profile->url), + TAG_END()); + switch_safe_free(url); + + if (!(tech_pvt->sofia_private = malloc(sizeof(*tech_pvt->sofia_private)))) { + abort(); + } + memset(tech_pvt->sofia_private, 0, sizeof(*tech_pvt->sofia_private)); + switch_copy_string(tech_pvt->sofia_private->uuid, switch_core_session_get_uuid(session), sizeof(tech_pvt->sofia_private->uuid)); + nua_handle_bind(tech_pvt->nh, tech_pvt->sofia_private); + + } + + + if (tech_pvt->e_dest && (e_dest = strdup(tech_pvt->e_dest))) { + char *user = e_dest, *host = NULL; + char hash_key[256] = ""; + + if ((host = strchr(user, '@'))) { + *host++ = '\0'; + } + snprintf(hash_key, sizeof(hash_key), "%s%s%s", user, host, cid_num); + + tech_pvt->chat_from = tech_pvt->from_str; + tech_pvt->chat_to = tech_pvt->dest; + tech_pvt->hash_key = switch_core_session_strdup(tech_pvt->session, hash_key); + switch_core_hash_insert(tech_pvt->profile->chat_hash, tech_pvt->hash_key, tech_pvt); + free(e_dest); + } + + holdstr = switch_test_flag(tech_pvt, TFLAG_SIP_HOLD) ? "*" : ""; + + + SWITCH_STANDARD_STREAM(stream); + for (hi = switch_channel_variable_first(channel, switch_core_session_get_pool(tech_pvt->session)); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &vvar, NULL, &vval); + if (vvar && vval) { + const char *name = vvar; + char *value = (char *) vval; + + if (!strncasecmp(name, SOFIA_SIP_HEADER_PREFIX, strlen(SOFIA_SIP_HEADER_PREFIX))) { + const char *hname = name + strlen(SOFIA_SIP_HEADER_PREFIX); + stream.write_function(&stream, "%s: %s\r\n", hname, value); + } + } + } + + if (stream.data) { + extra_headers = stream.data; + } + + nua_invite(tech_pvt->nh, + TAG_IF(!switch_strlen_zero(rpid), SIPTAG_HEADER_STR(rpid)), + TAG_IF(!switch_strlen_zero(alert_info), SIPTAG_HEADER_STR(alert_info)), + TAG_IF(!switch_strlen_zero(extra_headers), SIPTAG_HEADER_STR(extra_headers)), + TAG_IF(!switch_strlen_zero(max_forwards), SIPTAG_MAX_FORWARDS_STR(max_forwards)), + //SIPTAG_CONTACT_STR(tech_pvt->profile->url), + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), + SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), SOATAG_HOLD(holdstr), TAG_END()); + + switch_safe_free(stream.data); + + return SWITCH_STATUS_SUCCESS; + +} + + + +void sofia_glue_do_xfer_invite(switch_core_session_t *session) +{ + char rpid[1024]; + private_object_t *tech_pvt; + switch_channel_t *channel = NULL; + switch_caller_profile_t *caller_profile; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + tech_pvt = (private_object_t *) switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + caller_profile = switch_channel_get_caller_profile(channel); + + + + if ((tech_pvt->from_str = switch_core_session_sprintf(session, "\"%s\" ", + (char *) caller_profile->caller_id_name, + (char *) caller_profile->caller_id_number, + tech_pvt->profile->extsipip ? tech_pvt->profile->extsipip : tech_pvt->profile->sipip))) { + + char *rep = switch_channel_get_variable(channel, SOFIA_REPLACES_HEADER); + + tech_pvt->nh2 = nua_handle(tech_pvt->profile->nua, NULL, + SIPTAG_TO_STR(tech_pvt->dest), SIPTAG_FROM_STR(tech_pvt->from_str), SIPTAG_CONTACT_STR(tech_pvt->profile->url), + TAG_END()); + + + nua_handle_bind(tech_pvt->nh2, tech_pvt->sofia_private); + + nua_invite(tech_pvt->nh2, + TAG_IF(rpid, SIPTAG_HEADER_STR(rpid)), + SIPTAG_CONTACT_STR(tech_pvt->profile->url), + SOATAG_USER_SDP_STR(tech_pvt->local_sdp_str), + SOATAG_RTP_SORT(SOA_RTP_SORT_REMOTE), SOATAG_RTP_SELECT(SOA_RTP_SELECT_ALL), TAG_IF(rep, SIPTAG_REPLACES_STR(rep)), TAG_END()); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + } + +} + +void sofia_glue_tech_absorb_sdp(private_object_t * tech_pvt) +{ + switch_channel_t *channel; + char *sdp_str; + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + if ((sdp_str = switch_channel_get_variable(channel, SWITCH_B_SDP_VARIABLE))) { + sdp_parser_t *parser; + sdp_session_t *sdp; + sdp_media_t *m; + sdp_connection_t *connection; + + if ((parser = sdp_parse(tech_pvt->home, sdp_str, (int) strlen(sdp_str), 0))) { + if ((sdp = sdp_session(parser))) { + for (m = sdp->sdp_media; m; m = m->m_next) { + if (m->m_type != sdp_media_audio) { + continue; + } + + connection = sdp->sdp_connection; + if (m->m_connections) { + connection = m->m_connections; + } + + if (connection) { + tech_pvt->proxy_sdp_audio_ip = switch_core_session_strdup(tech_pvt->session, connection->c_address); + } + tech_pvt->proxy_sdp_audio_port = (switch_port_t) m->m_port; + if (tech_pvt->proxy_sdp_audio_ip && tech_pvt->proxy_sdp_audio_port) { + break; + } + } + } + sdp_parser_free(parser); + } + tech_pvt->local_sdp_str = switch_core_session_strdup(tech_pvt->session, sdp_str); + } +} + +void sofia_glue_deactivate_rtp(private_object_t * tech_pvt) +{ + int loops = 0; //, sock = -1; + if (switch_rtp_ready(tech_pvt->rtp_session)) { + while (loops < 10 && (switch_test_flag(tech_pvt, TFLAG_READING) || switch_test_flag(tech_pvt, TFLAG_WRITING))) { + switch_yield(10000); + loops++; + } + switch_rtp_destroy(&tech_pvt->rtp_session); + } +} + +switch_status_t sofia_glue_tech_set_codec(private_object_t * tech_pvt, int force) +{ + switch_channel_t *channel; + + if (tech_pvt->read_codec.implementation) { + if (!force) { + return SWITCH_STATUS_SUCCESS; + } + if (strcasecmp(tech_pvt->read_codec.implementation->iananame, tech_pvt->rm_encoding) || + tech_pvt->read_codec.implementation->samples_per_second != tech_pvt->rm_rate) { + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Changing Codec from %s to %s\n", + tech_pvt->read_codec.implementation->iananame, tech_pvt->rm_encoding); + switch_core_codec_destroy(&tech_pvt->read_codec); + switch_core_codec_destroy(&tech_pvt->write_codec); + switch_core_session_reset(tech_pvt->session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Already using %s\n", tech_pvt->read_codec.implementation->iananame); + return SWITCH_STATUS_SUCCESS; + } + } + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + if (!tech_pvt->rm_encoding) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec with no name?\n"); + sofia_glue_terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + return SWITCH_STATUS_FALSE; + } + + if (switch_core_codec_init(&tech_pvt->read_codec, + tech_pvt->rm_encoding, + tech_pvt->rm_fmtp, + tech_pvt->rm_rate, + tech_pvt->codec_ms, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | tech_pvt->profile->codec_flags, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + sofia_glue_terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + return SWITCH_STATUS_FALSE; + } else { + if (switch_core_codec_init(&tech_pvt->write_codec, + tech_pvt->rm_encoding, + tech_pvt->rm_fmtp, + tech_pvt->rm_rate, + tech_pvt->codec_ms, + 1, + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE | tech_pvt->profile->codec_flags, + NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); + sofia_glue_terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + return SWITCH_STATUS_FALSE; + } else { + int ms; + tech_pvt->read_frame.rate = tech_pvt->rm_rate; + ms = tech_pvt->write_codec.implementation->microseconds_per_frame / 1000; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Set Codec %s %s/%ld %d ms\n", + switch_channel_get_name(channel), tech_pvt->rm_encoding, tech_pvt->rm_rate, tech_pvt->codec_ms); + tech_pvt->read_frame.codec = &tech_pvt->read_codec; + + switch_core_session_set_read_codec(tech_pvt->session, &tech_pvt->read_codec); + switch_core_session_set_write_codec(tech_pvt->session, &tech_pvt->write_codec); + tech_pvt->fmtp_out = switch_core_session_strdup(tech_pvt->session, tech_pvt->write_codec.fmtp_out); + } + } + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t sofia_glue_activate_rtp(private_object_t * tech_pvt) +{ + int bw, ms; + switch_channel_t *channel; + const char *err = NULL; + char *val = NULL; + switch_rtp_flag_t flags; + switch_status_t status; + char tmp[50]; + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + if (switch_test_flag(tech_pvt, TFLAG_NOMEDIA)) { + return SWITCH_STATUS_SUCCESS; + } + + if (switch_rtp_ready(tech_pvt->rtp_session) && !switch_test_flag(tech_pvt, TFLAG_REINVITE)) { + return SWITCH_STATUS_SUCCESS; + } + + if ((status = sofia_glue_tech_set_codec(tech_pvt, 0)) != SWITCH_STATUS_SUCCESS) { + return status; + } + + bw = tech_pvt->read_codec.implementation->bits_per_second; + ms = tech_pvt->read_codec.implementation->microseconds_per_frame; + + flags = (switch_rtp_flag_t) (SWITCH_RTP_FLAG_RAW_WRITE | SWITCH_RTP_FLAG_AUTOADJ | SWITCH_RTP_FLAG_DATAWAIT); + + if (switch_test_flag(tech_pvt, TFLAG_BUGGY_2833)) { + flags |= SWITCH_RTP_FLAG_BUGGY_2833; + } + + if ((tech_pvt->profile->pflags & PFLAG_PASS_RFC2833) + || ((val = switch_channel_get_variable(channel, "pass_rfc2833")) && switch_true(val))) { + flags |= SWITCH_RTP_FLAG_PASS_RFC2833; + } + + if (tech_pvt->cng_pt) { + flags |= SWITCH_RTP_FLAG_AUTO_CNG; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "RTP [%s] %s:%d->%s:%d codec: %u ms: %d\n", + switch_channel_get_name(channel), + tech_pvt->local_sdp_audio_ip, + tech_pvt->local_sdp_audio_port, + tech_pvt->remote_sdp_audio_ip, + tech_pvt->remote_sdp_audio_port, tech_pvt->agreed_pt, tech_pvt->read_codec.implementation->microseconds_per_frame / 1000); + + snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, tech_pvt->adv_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, tmp); + + if (tech_pvt->rtp_session && switch_test_flag(tech_pvt, TFLAG_REINVITE)) { + switch_clear_flag_locked(tech_pvt, TFLAG_REINVITE); + + if (switch_rtp_set_remote_address(tech_pvt->rtp_session, tech_pvt->remote_sdp_audio_ip, tech_pvt->remote_sdp_audio_port, &err) != + SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP CHANGING DEST TO: [%s:%d]\n", + tech_pvt->remote_sdp_audio_ip, tech_pvt->remote_sdp_audio_port); + /* Reactivate the NAT buster flag. */ + switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_AUTOADJ); + } + return SWITCH_STATUS_SUCCESS; + } + + tech_pvt->rtp_session = switch_rtp_new(tech_pvt->local_sdp_audio_ip, + tech_pvt->local_sdp_audio_port, + tech_pvt->remote_sdp_audio_ip, + tech_pvt->remote_sdp_audio_port, + tech_pvt->agreed_pt, + tech_pvt->read_codec.implementation->samples_per_frame, + tech_pvt->codec_ms * 1000, + (switch_rtp_flag_t) flags, + NULL, tech_pvt->profile->timer_name, &err, switch_core_session_get_pool(tech_pvt->session)); + + if (switch_rtp_ready(tech_pvt->rtp_session)) { + uint8_t vad_in = switch_test_flag(tech_pvt, TFLAG_VAD_IN) ? 1 : 0; + uint8_t vad_out = switch_test_flag(tech_pvt, TFLAG_VAD_OUT) ? 1 : 0; + uint8_t inb = switch_test_flag(tech_pvt, TFLAG_OUTBOUND) ? 0 : 1; + + tech_pvt->ssrc = switch_rtp_get_ssrc(tech_pvt->rtp_session); + switch_set_flag_locked(tech_pvt, TFLAG_RTP); + switch_set_flag_locked(tech_pvt, TFLAG_IO); + + if ((vad_in && inb) || (vad_out && !inb)) { + switch_rtp_enable_vad(tech_pvt->rtp_session, tech_pvt->session, &tech_pvt->read_codec, SWITCH_VAD_FLAG_TALKING); + switch_set_flag_locked(tech_pvt, TFLAG_VAD); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "RTP Engage VAD for %s ( %s %s )\n", + switch_channel_get_name(switch_core_session_get_channel(tech_pvt->session)), vad_in ? "in" : "", vad_out ? "out" : ""); + } + + if (tech_pvt->te) { + switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te); + } + if (tech_pvt->cng_pt) { + switch_rtp_set_cng_pt(tech_pvt->rtp_session, tech_pvt->cng_pt); + } + + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTP REPORTS ERROR: [%s]\n", err); + sofia_glue_terminate_session(&tech_pvt->session, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER, __LINE__); + switch_clear_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_FALSE; + } + + switch_set_flag_locked(tech_pvt, TFLAG_IO); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t sofia_glue_tech_media(private_object_t * tech_pvt, char *r_sdp) +{ + sdp_parser_t *parser = sdp_parse(tech_pvt->home, r_sdp, (int) strlen(r_sdp), 0); + sdp_session_t *sdp; + uint8_t match = 0; + switch_channel_t *channel = switch_core_session_get_channel(tech_pvt->session); + + assert(tech_pvt != NULL); + + if (switch_strlen_zero(r_sdp)) { + return SWITCH_STATUS_FALSE; + } + + if (tech_pvt->num_codecs) { + if ((sdp = sdp_session(parser))) { + match = negotiate_sdp(tech_pvt->session, sdp); + } + } + + if (parser) { + sdp_parser_free(parser); + } + + if (match) { + if (sofia_glue_tech_choose_port(tech_pvt) != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_FALSE; + } + sofia_glue_activate_rtp(tech_pvt); + switch_channel_set_variable(channel, SWITCH_ENDPOINT_DISPOSITION_VARIABLE, "EARLY MEDIA"); + switch_set_flag_locked(tech_pvt, TFLAG_EARLY_MEDIA); + switch_channel_mark_pre_answered(channel); + return SWITCH_STATUS_SUCCESS; + } + + return SWITCH_STATUS_FALSE; +} + + + +uint8_t negotiate_sdp(switch_core_session_t *session, sdp_session_t * sdp) +{ + uint8_t match = 0; + switch_payload_t te = 0, cng_pt = 0; + private_object_t *tech_pvt; + sdp_media_t *m; + sdp_attribute_t *a; + switch_channel_t *channel; + int ptime = 0, dptime = 0; + + tech_pvt = switch_core_session_get_private(session); + assert(tech_pvt != NULL); + + channel = switch_core_session_get_channel(session); + + if ((tech_pvt->origin = switch_core_session_strdup(session, (char *) sdp->sdp_origin->o_username))) { + if (strstr(tech_pvt->origin, "CiscoSystemsSIP-GW-UserAgent")) { + switch_set_flag_locked(tech_pvt, TFLAG_BUGGY_2833); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Activate Buggy RFC2833 Mode!\n"); + } + } + + for (a = sdp->sdp_attributes; a; a = a->a_next) { + if (switch_strlen_zero(a->a_name)) { + continue; + } + if (!strcasecmp(a->a_name, "sendonly")) { + if (!switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { + char *stream; + + switch_set_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + switch_channel_set_flag(channel, CF_HOLD); + if (!(stream = switch_channel_get_variable(channel, SWITCH_HOLD_MUSIC_VARIABLE))) { + stream = tech_pvt->profile->hold_music; + } + if (stream) { + switch_ivr_broadcast(switch_core_session_get_uuid(tech_pvt->session), stream, SMF_ECHO_BLEG | SMF_LOOP); + } + } + } else if (!strcasecmp(a->a_name, "sendrecv")) { + if (switch_test_flag(tech_pvt, TFLAG_SIP_HOLD)) { + switch_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD); + switch_channel_clear_flag(channel, CF_HOLD); + switch_channel_clear_flag_partner(channel, CF_BROADCAST); + switch_channel_set_flag_partner(channel, CF_BREAK); + } + } else if (!strcasecmp(a->a_name, "ptime")) { + dptime = atoi(a->a_value); + } + } + + for (m = sdp->sdp_media; m; m = m->m_next) { + sdp_connection_t *connection; + + ptime = dptime; + for (a = m->m_attributes; a; a = a->a_next) { + if (!strcasecmp(a->a_name, "ptime") && a->a_value) { + ptime = atoi(a->a_value); + } + } + + if (m->m_type == sdp_media_audio) { + sdp_rtpmap_t *map; + + connection = sdp->sdp_connection; + if (m->m_connections) { + connection = m->m_connections; + } + + if (!connection) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot find a c= line in the sdp at media or session level!\n"); + match = 0; + break; + } + + for (map = m->m_rtpmaps; map; map = map->rm_next) { + int32_t i; + const switch_codec_implementation_t *mimp = NULL, *near_match = NULL; + const char *rm_encoding; + + + if (!(rm_encoding = map->rm_encoding)) { + rm_encoding = ""; + } + + if (!te && !strcasecmp(rm_encoding, "telephone-event")) { + te = tech_pvt->te = (switch_payload_t) map->rm_pt; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set 2833 dtmf payload to %u\n", te); + if (tech_pvt->rtp_session) { + switch_rtp_set_telephony_event(tech_pvt->rtp_session, tech_pvt->te); + } + } + + if (!cng_pt && !strcasecmp(rm_encoding, "CN")) { + cng_pt = tech_pvt->cng_pt = (switch_payload_t) map->rm_pt; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set comfort noise payload to %u\n", cng_pt); + if (tech_pvt->rtp_session) { + switch_rtp_set_cng_pt(tech_pvt->rtp_session, tech_pvt->cng_pt); + switch_rtp_set_flag(tech_pvt->rtp_session, SWITCH_RTP_FLAG_AUTO_CNG); + } + } + + if (match) { + if (te && cng_pt) { + break; + } + continue; + } + + for (i = 0; i < tech_pvt->num_codecs; i++) { + const switch_codec_implementation_t *imp = tech_pvt->codecs[i]; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Codec Compare [%s:%d]/[%s:%d]\n", + rm_encoding, map->rm_pt, imp->iananame, imp->ianacode); + if (map->rm_pt < 96) { + match = (map->rm_pt == imp->ianacode) ? 1 : 0; + } else { + match = strcasecmp(rm_encoding, imp->iananame) ? 0 : 1; + } + + if (match && (map->rm_rate == imp->samples_per_second)) { + if (ptime && ptime * 1000 != imp->microseconds_per_frame) { + near_match = imp; + match = 0; + continue; + } + mimp = imp; + break; + } else { + match = 0; + } + } + + if (!match && near_match) { + const switch_codec_implementation_t *search[1]; + char *prefs[1]; + char tmp[80]; + int num; + + snprintf(tmp, sizeof(tmp), "%s@%uk@%ui", near_match->iananame, near_match->samples_per_second, ptime); + + prefs[0] = tmp; + num = switch_loadable_module_get_codecs_sorted(search, 1, prefs, 1); + + if (num) { + mimp = search[0]; + } else { + mimp = near_match; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Substituting codec %s@%ums\n", + mimp->iananame, mimp->microseconds_per_frame / 1000); + match = 1; + } + + if (mimp) { + if ((tech_pvt->rm_encoding = switch_core_session_strdup(session, (char *) rm_encoding))) { + char tmp[50]; + tech_pvt->pt = (switch_payload_t) map->rm_pt; + tech_pvt->rm_rate = map->rm_rate; + tech_pvt->codec_ms = mimp->microseconds_per_frame / 1000; + tech_pvt->remote_sdp_audio_ip = switch_core_session_strdup(session, (char *) connection->c_address); + tech_pvt->rm_fmtp = switch_core_session_strdup(session, (char *) map->rm_fmtp); + tech_pvt->remote_sdp_audio_port = (switch_port_t) m->m_port; + tech_pvt->agreed_pt = (switch_payload_t) map->rm_pt; + snprintf(tmp, sizeof(tmp), "%d", tech_pvt->remote_sdp_audio_port); + switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_IP_VARIABLE, tech_pvt->remote_sdp_audio_ip); + switch_channel_set_variable(channel, SWITCH_REMOTE_MEDIA_PORT_VARIABLE, tmp); + } else { + match = 0; + } + } + + if (match) { + if (sofia_glue_tech_set_codec(tech_pvt, 1) != SWITCH_STATUS_SUCCESS) { + match = 0; + } + } + } + } + } + + return match; +} + +// map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 +switch_call_cause_t sofia_glue_sip_cause_to_freeswitch(int status) +{ + switch (status) { + case 200: + return SWITCH_CAUSE_NORMAL_CLEARING; + case 401: + case 402: + case 403: + case 407: + case 603: + return SWITCH_CAUSE_CALL_REJECTED; + case 404: + case 485: + case 604: + return SWITCH_CAUSE_NO_ROUTE_DESTINATION; + case 408: + case 504: + return SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE; + case 410: + return SWITCH_CAUSE_NUMBER_CHANGED; + case 413: + case 414: + case 416: + case 420: + case 421: + case 423: + case 505: + case 513: + return SWITCH_CAUSE_INTERWORKING; + case 480: + return SWITCH_CAUSE_NO_USER_RESPONSE; + case 400: + case 481: + case 500: + case 503: + return SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE; + case 486: + case 600: + return SWITCH_CAUSE_USER_BUSY; + case 484: + return SWITCH_CAUSE_INVALID_NUMBER_FORMAT; + case 488: + case 606: + return SWITCH_CAUSE_BEARERCAPABILITY_NOTIMPL; + case 502: + return SWITCH_CAUSE_NETWORK_OUT_OF_ORDER; + case 405: + return SWITCH_CAUSE_SERVICE_UNAVAILABLE; + case 406: + case 415: + case 501: + return SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED; + case 482: + case 483: + return SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR; + case 487: + return SWITCH_CAUSE_ORIGINATOR_CANCEL; + + default: + return SWITCH_CAUSE_NORMAL_UNSPECIFIED; + + } +} + + +void sofia_glue_pass_sdp(private_object_t * tech_pvt, char *sdp) +{ + char *val; + switch_channel_t *channel; + switch_core_session_t *other_session; + switch_channel_t *other_channel; + + channel = switch_core_session_get_channel(tech_pvt->session); + assert(channel != NULL); + + if ((val = switch_channel_get_variable(channel, SWITCH_SIGNAL_BOND_VARIABLE)) + && (other_session = switch_core_session_locate(val))) { + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + switch_channel_set_variable(other_channel, SWITCH_B_SDP_VARIABLE, sdp); + + if (!switch_test_flag(tech_pvt, TFLAG_CHANGE_MEDIA) && (switch_channel_test_flag(other_channel, CF_OUTBOUND) && + //switch_channel_test_flag(other_channel, CF_NOMEDIA) && + switch_channel_test_flag(channel, CF_OUTBOUND) && + switch_channel_test_flag(channel, CF_NOMEDIA))) { + switch_ivr_nomedia(val, SMF_FORCE); + switch_set_flag_locked(tech_pvt, TFLAG_CHANGE_MEDIA); + } + + switch_core_session_rwunlock(other_session); + } +} + + +char *sofia_glue_get_url_from_contact(char *buf, uint8_t to_dup) +{ + char *url = NULL, *e; + + if ((url = strchr(buf, '<')) && (e = strchr(url, '>'))) { + url++; + if (to_dup) { + url = strdup(url); + e = strchr(url, '>'); + } + + *e = '\0'; + } + + return url; +} + + +sofia_profile_t *sofia_glue_find_profile(char *key) +{ + sofia_profile_t *profile; + + switch_mutex_lock(globals.hash_mutex); + profile = (sofia_profile_t *) switch_core_hash_find(globals.profile_hash, key); + switch_mutex_unlock(globals.hash_mutex); + + return profile; +} + +void sofia_glue_add_profile(char *key, sofia_profile_t * profile) +{ + switch_mutex_lock(globals.hash_mutex); + switch_core_hash_insert(globals.profile_hash, key, profile); + switch_mutex_unlock(globals.hash_mutex); +} + +void sofia_glue_execute_sql(char *dbname, char *sql, switch_mutex_t * mutex) +{ + switch_core_db_t *db; + + if (mutex) { + switch_mutex_lock(mutex); + } + + if (!(db = switch_core_db_open_file(dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", dbname); + goto end; + } + switch_core_db_persistant_execute(db, sql, 25); + switch_core_db_close(db); + + end: + if (mutex) { + switch_mutex_unlock(mutex); + } +} + + + diff --git a/src/mod/endpoints/mod_sofia/sofia_presence.c b/src/mod/endpoints/mod_sofia/sofia_presence.c new file mode 100644 index 0000000000..e658f37824 --- /dev/null +++ b/src/mod/endpoints/mod_sofia/sofia_presence.c @@ -0,0 +1,909 @@ +#include "mod_sofia.h" + + +switch_status_t sofia_presence_chat_send(char *proto, char *from, char *to, char *subject, char *body, char *hint) +{ + char buf[256]; + char *user, *host; + sofia_profile_t *profile; + char *ffrom = NULL; + nua_handle_t *msg_nh; + char *contact; + + if (to && (user = strdup(to))) { + if ((host = strchr(user, '@'))) { + *host++ = '\0'; + } + + if (!host || !(profile = sofia_glue_find_profile(host))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Chat proto [%s]\nfrom [%s]\nto [%s]\n%s\nInvalid Profile %s\n", proto, from, to, + body ? body : "[no body]", host ? host : "NULL"); + return SWITCH_STATUS_FALSE; + } + + if (!sofia_reg_find_reg_url(profile, user, host, buf, sizeof(buf))) { + return SWITCH_STATUS_FALSE; + } + + if (!strcmp(proto, SOFIA_CHAT_PROTO)) { + from = hint; + } else { + char *fp, *p, *fu = NULL; + + if (!(fp = strdup(from))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); + return SWITCH_STATUS_FALSE; + } + + if ((p = strchr(fp, '@'))) { + *p = '\0'; + fu = strdup(fp); + *p = '+'; + } + + ffrom = switch_mprintf("\"%s\" ", fu, proto, fp, profile->name); + from = ffrom; + switch_safe_free(fu); + switch_safe_free(fp); + } + + contact = sofia_glue_get_url_from_contact(buf, 1); + msg_nh = nua_handle(profile->nua, NULL, SIPTAG_FROM_STR(from), NUTAG_URL(contact), SIPTAG_TO_STR(buf), // if this cries, add contact here too, change the 1 to 0 and omit the safe_free + SIPTAG_CONTACT_STR(profile->url), TAG_END()); + + switch_safe_free(contact); + + + nua_message(msg_nh, SIPTAG_CONTENT_TYPE_STR("text/html"), SIPTAG_PAYLOAD_STR(body), TAG_END()); + + + switch_safe_free(ffrom); + free(user); + } + + return SWITCH_STATUS_SUCCESS; +} + +void sofia_presence_cancel(void) +{ + char *sql, *errmsg = NULL; + switch_core_db_t *db; + sofia_profile_t *profile; + switch_hash_index_t *hi; + void *val; + + if ((sql = switch_mprintf("select 0,'unavailable','unavailable',* from sip_subscriptions where event='presence'"))) { + switch_mutex_lock(globals.hash_mutex); + for (hi = switch_hash_first(switch_hash_pool_get(globals.profile_hash), globals.profile_hash); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, NULL, NULL, &val); + profile = (sofia_profile_t *) val; + if (!(profile->pflags & PFLAG_PRESENCE)) { + continue; + } + + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + continue; + } + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_sub_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + switch_core_db_close(db); + } + switch_safe_free(sql); + } + switch_mutex_unlock(globals.hash_mutex); +} + +void sofia_presence_establish_presence(sofia_profile_t * profile) +{ + char *sql, *errmsg = NULL; + switch_core_db_t *db; + + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + return; + } + + if ((sql = switch_mprintf("select user,host,'Registered','unknown','' from sofia_handle_sip_registrations"))) { + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_resub_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + switch_safe_free(sql); + } + + if ((sql = switch_mprintf("select sub_to_user,sub_to_host,'Online','unknown',proto from sip_subscriptions " + "where proto='ext' or proto='user' or proto='conf'"))) { + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_resub_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + switch_safe_free(sql); + } + + switch_core_db_close(db); + +} + + + +char *sofia_presence_translate_rpid(char *in, char *ext) +{ + char *r = NULL; + + if (in && (strstr(in, "null") || strstr(in, "NULL"))) { + in = NULL; + } + + if (!in) { + in = ext; + } + + if (!in) { + return NULL; + } + + if (!strcasecmp(in, "dnd")) { + r = "busy"; + } + + if (ext && !strcasecmp(ext, "away")) { + r = "idle"; + } + + return r; +} + +void sofia_presence_event_handler(switch_event_t *event) +{ + sofia_profile_t *profile; + switch_hash_index_t *hi; + void *val; + char *from = switch_event_get_header(event, "from"); + char *proto = switch_event_get_header(event, "proto"); + char *rpid = switch_event_get_header(event, "rpid"); + char *status = switch_event_get_header(event, "status"); + char *event_type = switch_event_get_header(event, "event_type"); + //char *event_subtype = switch_event_get_header(event, "event_subtype"); + char *sql = NULL; + char *euser = NULL, *user = NULL, *host = NULL; + char *errmsg; + switch_core_db_t *db; + + + if (rpid && !strcasecmp(rpid, "n/a")) { + rpid = NULL; + } + + if (status && !strcasecmp(status, "n/a")) { + status = NULL; + } + + if (rpid) { + rpid = sofia_presence_translate_rpid(rpid, status); + } + + if (!status) { + status = "Available"; + + if (rpid) { + if (!strcasecmp(rpid, "busy")) { + status = "Busy"; + } else if (!strcasecmp(rpid, "unavailable")) { + status = "Idle"; + } else if (!strcasecmp(rpid, "away")) { + status = "Idle"; + } + } + } + + if (!rpid) { + rpid = "unknown"; + } + + if (event->event_id == SWITCH_EVENT_ROSTER) { + + if (from) { + sql = switch_mprintf("select 1,'%q','%q',* from sip_subscriptions where event='presence' and full_from like '%%%q%%'", status, rpid, from); + } else { + sql = switch_mprintf("select 1,'%q','%q',* from sip_subscriptions where event='presence'", status, rpid); + } + + switch_mutex_lock(globals.hash_mutex); + for (hi = switch_hash_first(switch_hash_pool_get(globals.profile_hash), globals.profile_hash); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, NULL, NULL, &val); + profile = (sofia_profile_t *) val; + if (!(profile->pflags & PFLAG_PRESENCE)) { + continue; + } + + if (sql) { + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + continue; + } + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_sub_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + switch_core_db_close(db); + } + + } + switch_mutex_unlock(globals.hash_mutex); + + return; + } + + if (switch_strlen_zero(event_type)) { + event_type = "presence"; + } + + if ((user = strdup(from))) { + if ((host = strchr(user, '@'))) { + char *p; + *host++ = '\0'; + if ((p = strchr(host, '/'))) { + *p = '\0'; + } + } else { + switch_safe_free(user); + return; + } + if ((euser = strchr(user, '+'))) { + euser++; + } else { + euser = user; + } + + } else { + return; + } + + + switch (event->event_id) { + case SWITCH_EVENT_PRESENCE_PROBE: + if (proto) { + switch_core_db_t *db = NULL; + char *to = switch_event_get_header(event, "to"); + char *user, *euser, *host, *p; + + if (!to || !(user = strdup(to))) { + return; + } + + if ((host = strchr(user, '@'))) { + *host++ = '\0'; + } + euser = user; + if ((p = strchr(euser, '+'))) { + euser = (p + 1); + } + + if (euser && host && + (sql = + switch_mprintf("select user,host,status,rpid,'' from sofia_handle_sip_registrations where user='%q' and host='%q'", + euser, host)) && (profile = sofia_glue_find_profile(host))) { + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + switch_safe_free(user); + switch_safe_free(sql); + return; + } + + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_resub_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + switch_safe_free(sql); + } + switch_safe_free(user); + switch_core_db_close(db); + } + return; + case SWITCH_EVENT_PRESENCE_IN: + sql = + switch_mprintf + ("select 1,'%q','%q',* from sip_subscriptions where proto='%q' and event='%q' and sub_to_user='%q' and sub_to_host='%q'", + status, rpid, proto, event_type, euser, host); + break; + case SWITCH_EVENT_PRESENCE_OUT: + sql = + switch_mprintf + ("select 0,'%q','%q',* from sip_subscriptions where proto='%q' and event='%q' and sub_to_user='%q' and sub_to_host='%q'", + status, rpid, proto, event_type, euser, host); + break; + default: + break; + } + + switch_mutex_lock(globals.hash_mutex); + for (hi = switch_hash_first(switch_hash_pool_get(globals.profile_hash), globals.profile_hash); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, NULL, NULL, &val); + profile = (sofia_profile_t *) val; + if (!(profile->pflags & PFLAG_PRESENCE)) { + continue; + } + + if (sql) { + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + continue; + } + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_sub_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + + switch_core_db_close(db); + } + } + switch_mutex_unlock(globals.hash_mutex); + + switch_safe_free(sql); + switch_safe_free(user); +} + +int sofia_presence_sub_reg_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + sofia_profile_t *profile = (sofia_profile_t *) pArg; + //char *proto = argv[0]; + char *user = argv[1]; + char *host = argv[2]; + switch_event_t *event; + char *status = NULL; + if (switch_strlen_zero(status)) { + status = "Available"; + } + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", user, host); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", status); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_subtype", "probe"); + switch_event_fire(&event); + } + + return 0; +} + +int sofia_presence_resub_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + sofia_profile_t *profile = (sofia_profile_t *) pArg; + char *user = argv[0]; + char *host = argv[1]; + char *status = argv[2]; + char *rpid = argv[3]; + char *proto = argv[4]; + switch_event_t *event; + + if (switch_strlen_zero(proto)) { + proto = NULL; + } + + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "%s", proto ? proto : SOFIA_CHAT_PROTO); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", user, host); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", status); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_fire(&event); + } + + return 0; +} + +int sofia_presence_sub_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + sofia_profile_t *profile = (sofia_profile_t *) pArg; + char *pl; + char *id, *note; + uint32_t in = atoi(argv[0]); + char *status = argv[1]; + char *rpid = argv[2]; + char *proto = argv[3]; + char *user = argv[4]; + char *host = argv[5]; + char *sub_to_user = argv[6]; + char *sub_to_host = argv[7]; + char *event = argv[8]; + char *contact = argv[9]; + char *callid = argv[10]; + char *full_from = argv[11]; + char *full_via = argv[12]; + nua_handle_t *nh; + char *to; + char *open; + char *tmp; + + if (!rpid) { + rpid = "unknown"; + } + + if (in) { + note = switch_mprintf("%s", status); + open = "open"; + } else { + note = NULL; + open = "closed"; + } + + if (!strcasecmp(sub_to_host, host)) { + /* same host */ + id = switch_mprintf("sip:%s+%s@%s", proto, sub_to_user, sub_to_host); + } else if (strcasecmp(proto, SOFIA_CHAT_PROTO)) { + /*encapsulate */ + id = switch_mprintf("sip:%s+%s+%s@%s", proto, sub_to_user, sub_to_host, host); + } else { + id = switch_mprintf("sip:%s@%s", sub_to_user, sub_to_host); + } + + to = switch_mprintf("sip:%s@%s", user, host); + pl = switch_mprintf("\r\n" + "\r\n" + "\r\n" + "\r\n" + "%s\r\n" + "\r\n" + "\r\n" + "\r\n" + "\r\n" "\r\n" "%s\r\n" "", id, open, rpid, note); + + + + nh = nua_handle(profile->nua, NULL, TAG_END()); + tmp = contact; + contact = sofia_glue_get_url_from_contact(tmp, 0); + + nua_notify(nh, + NUTAG_URL(contact), + SIPTAG_TO_STR(full_from), + SIPTAG_FROM_STR(id), + SIPTAG_CONTACT_STR(profile->url), + SIPTAG_CALL_ID_STR(callid), + SIPTAG_VIA_STR(full_via), + SIPTAG_SUBSCRIPTION_STATE_STR("active;expires=3600"), + SIPTAG_EVENT_STR(event), SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), SIPTAG_PAYLOAD_STR(pl), TAG_END()); + + switch_safe_free(id); + switch_safe_free(note); + switch_safe_free(pl); + switch_safe_free(to); + + return 0; +} + +void sofia_presence_handle_sip_i_subscribe(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + if (sip) { + long exp, exp_raw; + sip_to_t const *to = sip->sip_to; + sip_from_t const *from = sip->sip_from; + sip_contact_t const *contact = sip->sip_contact; + char *from_user = NULL; + char *from_host = NULL; + char *to_user = NULL; + char *to_host = NULL; + char *sql, *event = NULL; + char *proto = "sip"; + char *d_user = NULL; + char *contact_str = ""; + char *call_id = NULL; + char *to_str = NULL; + char *full_from = NULL; + char *full_via = NULL; + switch_core_db_t *db; + char *errmsg; + char *sstr; + const char *display = "\"user\""; + switch_event_t *sevent; + + if (contact) { + char *port = (char *) contact->m_url->url_port; + + display = contact->m_display; + + if (switch_strlen_zero(display)) { + if (from) { + display = from->a_display; + if (switch_strlen_zero(display)) { + display = "\"user\""; + } + } + } else { + display = "\"user\""; + } + + if (!port) { + port = SOFIA_DEFAULT_PORT; + } + + if (contact->m_url->url_params) { + contact_str = switch_mprintf("%s ", + display, contact->m_url->url_user, contact->m_url->url_host, port, contact->m_url->url_params); + } else { + contact_str = switch_mprintf("%s ", display, contact->m_url->url_user, contact->m_url->url_host, port); + } + } + + if (to) { + to_str = switch_mprintf("sip:%s@%s", to->a_url->url_user, to->a_url->url_host); //, to->a_url->url_port); + } + + if (to) { + to_user = (char *) to->a_url->url_user; + to_host = (char *) to->a_url->url_host; + } + + + if (strstr(to_user, "ext+") || strstr(to_user, "user+") || strstr(to_user, "conf+")) { + char proto[80]; + char *p; + + switch_copy_string(proto, to_user, sizeof(proto)); + if ((p = strchr(proto, '+'))) { + *p = '\0'; + } + + if (switch_event_create(&sevent, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); + switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "login", "%s", profile->name); + switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "from", "%s@%s", to_user, to_host); + switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "rpid", "unknown"); + switch_event_add_header(sevent, SWITCH_STACK_BOTTOM, "status", "Click To Call"); + switch_event_fire(&sevent); + } + } + + if (strchr(to_user, '+')) { + char *h; + if ((proto = (d_user = strdup(to_user)))) { + if ((to_user = strchr(d_user, '+'))) { + *to_user++ = '\0'; + if ((h = strchr(to_user, '+')) || (h = strchr(to_user, '@'))) { + *h++ = '\0'; + to_host = h; + } + } + } + + if (!(proto && to_user && to_host)) { + nua_respond(nh, SIP_404_NOT_FOUND, NUTAG_WITH_THIS(nua), TAG_END()); + goto end; + } + } + + call_id = sip_header_as_string(profile->home, (void *) sip->sip_call_id); + event = sip_header_as_string(profile->home, (void *) sip->sip_event); + full_from = sip_header_as_string(profile->home, (void *) sip->sip_from); + full_via = sip_header_as_string(profile->home, (void *) sip->sip_via); + + exp_raw = (sip->sip_expires ? sip->sip_expires->ex_delta : 3600); + exp = (long) time(NULL) + exp_raw; + + if (sip && sip->sip_from) { + from_user = (char *) sip->sip_from->a_url->url_user; + from_host = (char *) sip->sip_from->a_url->url_host; + } else { + from_user = "n/a"; + from_host = "n/a"; + } + + if ((sql = switch_mprintf("delete from sip_subscriptions where " + "proto='%q' and user='%q' and host='%q' and sub_to_user='%q' and sub_to_host='%q' and event='%q';\n" + "insert into sip_subscriptions values ('%q','%q','%q','%q','%q','%q','%q','%q','%q','%q',%ld)", + proto, + from_user, + from_host, + to_user, + to_host, event, proto, from_user, from_host, to_user, to_host, event, contact_str, call_id, full_from, full_via, exp))) { + sofia_glue_execute_sql(profile->dbname, sql, profile->ireg_mutex); + switch_safe_free(sql); + } + + sstr = switch_mprintf("active;expires=%ld", exp_raw); + + nua_respond(nh, SIP_202_ACCEPTED, + NUTAG_WITH_THIS(nua), + SIPTAG_SUBSCRIPTION_STATE_STR(sstr), SIPTAG_FROM(sip->sip_to), SIPTAG_TO(sip->sip_from), SIPTAG_CONTACT_STR(to_str), TAG_END()); + + + + switch_safe_free(sstr); + + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + goto end; + } + if ((sql = switch_mprintf("select * from sip_subscriptions where user='%q' and host='%q'", to_user, to_host, to_user, to_host))) { + switch_mutex_lock(profile->ireg_mutex); + switch_core_db_exec(db, sql, sofia_presence_sub_reg_callback, profile, &errmsg); + switch_mutex_unlock(profile->ireg_mutex); + switch_safe_free(sql); + } + switch_core_db_close(db); + end: + + if (event) { + su_free(profile->home, event); + } + if (call_id) { + su_free(profile->home, call_id); + } + if (full_from) { + su_free(profile->home, full_from); + } + if (full_via) { + su_free(profile->home, full_via); + } + + switch_safe_free(d_user); + switch_safe_free(to_str); + switch_safe_free(contact_str); + } +} + +void sofia_presence_handle_sip_r_subscribe(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + +} + +void sofia_presence_handle_sip_i_publish(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + if (sip) { + sip_from_t const *from = sip->sip_from; + char *from_user = NULL; + char *from_host = NULL; + char *rpid = "unknown"; + sip_payload_t *payload = sip->sip_payload; + char *event_type; + + if (from) { + from_user = (char *) from->a_url->url_user; + from_host = (char *) from->a_url->url_host; + } + + if (payload) { + switch_xml_t xml, note, person, tuple, status, basic, act; + switch_event_t *event; + uint8_t in = 0; + char *sql; + + if ((xml = switch_xml_parse_str(payload->pl_data, strlen(payload->pl_data)))) { + char *status_txt = "", *note_txt = ""; + + if ((tuple = switch_xml_child(xml, "tuple")) && (status = switch_xml_child(tuple, "status")) + && (basic = switch_xml_child(status, "basic"))) { + status_txt = basic->txt; + } + + if ((person = switch_xml_child(xml, "dm:person")) && (note = switch_xml_child(person, "dm:note"))) { + note_txt = note->txt; + } + + if (person && (act = switch_xml_child(person, "rpid:activities"))) { + if ((rpid = strchr(act->child->name, ':'))) { + rpid++; + } else { + rpid = act->child->name; + } + } + + if (!strcasecmp(status_txt, "open")) { + if (switch_strlen_zero(note_txt)) { + note_txt = "Available"; + } + in = 1; + } else if (!strcasecmp(status_txt, "closed")) { + if (switch_strlen_zero(note_txt)) { + note_txt = "Unavailable"; + } + } + + if ((sql = + switch_mprintf("update sofia_handle_sip_registrations set status='%q',rpid='%q' where user='%q' and host='%q'", + note_txt, rpid, from_user, from_host))) { + sofia_glue_execute_sql(profile->dbname, sql, profile->ireg_mutex); + switch_safe_free(sql); + } + + event_type = sip_header_as_string(profile->home, (void *) sip->sip_event); + + if (in) { + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "%s", note_txt); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "%s", event_type); + switch_event_fire(&event); + } + } else { + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_OUT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "%s", event_type); + switch_event_fire(&event); + } + } + + if (event_type) { + su_free(profile->home, event_type); + } + + switch_xml_free(xml); + } + + } + + } + + nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), TAG_END()); + +} + +void sofia_presence_set_hash_key(char *hash_key, int32_t len, sip_t const *sip) +{ + + snprintf(hash_key, len, "%s%s%s", (char *) sip->sip_from->a_url->url_user, (char *) sip->sip_from->a_url->url_host, + (char *) sip->sip_to->a_url->url_user); + + +#if 0 + /* nicer one we cant use in both directions >=0 */ + snprintf(hash_key, len, "%s%s%s%s%s%s", + (char *) sip->sip_to->a_url->url_user, + (char *) sip->sip_to->a_url->url_host, + (char *) sip->sip_to->a_url->url_params, + (char *) sip->sip_from->a_url->url_user, (char *) sip->sip_from->a_url->url_host, (char *) sip->sip_from->a_url->url_params); +#endif + +} + + +void sofia_presence_handle_sip_i_message(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + if (sip) { + sip_from_t const *from = sip->sip_from; + char *from_user = NULL; + char *from_host = NULL; + sip_to_t const *to = sip->sip_to; + char *to_user = NULL; + char *to_host = NULL; + sip_subject_t const *sip_subject = sip->sip_subject; + sip_payload_t *payload = sip->sip_payload; + const char *subject = "n/a"; + char *msg = NULL; + + if (sip->sip_content_type) { + if (strstr((char *) sip->sip_content_type->c_subtype, "composing")) { + return; + } + } + + if (from) { + from_user = (char *) from->a_url->url_user; + from_host = (char *) from->a_url->url_host; + } + + if (to) { + to_user = (char *) to->a_url->url_user; + to_host = (char *) to->a_url->url_host; + } + + + if (!to_user) { + return; + } + + if (payload) { + msg = payload->pl_data; + } + + if (sip_subject) { + subject = sip_subject->g_value; + } + + if (nh) { + char hash_key[512]; + private_object_t *tech_pvt; + switch_channel_t *channel; + switch_event_t *event; + char *to_addr; + char *from_addr; + char *p; + char *full_from; + char proto[512] = SOFIA_CHAT_PROTO; + + full_from = sip_header_as_string(profile->home, (void *) sip->sip_from); + + if ((p = strchr(to_user, '+'))) { + switch_copy_string(proto, to_user, sizeof(proto)); + p = strchr(proto, '+'); + *p++ = '\0'; + + if ((to_addr = strdup(p))) { + if ((p = strchr(to_addr, '+'))) { + *p = '@'; + } + } + + } else { + to_addr = switch_mprintf("%s@%s", to_user, to_host); + } + + from_addr = switch_mprintf("%s@%s", from_user, from_host); + + + sofia_presence_set_hash_key(hash_key, sizeof(hash_key), sip); + if ((tech_pvt = (private_object_t *) switch_core_hash_find(profile->chat_hash, hash_key))) { + channel = switch_core_session_get_channel(tech_pvt->session); + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", SOFIA_CHAT_PROTO); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s", tech_pvt->hash_key); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "to", "%s", to_addr); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "subject", "SIMPLE MESSAGE"); + if (msg) { + switch_event_add_body(event, "%s", msg); + } + if (switch_core_session_queue_event(tech_pvt->session, &event) != SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true"); + switch_event_fire(&event); + } + } + } else { + switch_chat_interface_t *ci; + + if ((ci = switch_loadable_module_get_chat_interface(proto))) { + ci->chat_send(SOFIA_CHAT_PROTO, from_addr, to_addr, "", msg, full_from); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Chat Interface [%s]!\n", proto ? proto : "(none)"); + } + + } + switch_safe_free(to_addr); + switch_safe_free(from_addr); + if (full_from) { + su_free(profile->home, full_from); + } + } + + } +} + +void sofia_presence_set_chat_hash(private_object_t * tech_pvt, sip_t const *sip) +{ + char hash_key[256] = ""; + char buf[512]; + + if (tech_pvt->hash_key || !sip || !sip->sip_from || !sip->sip_from->a_url || !sip->sip_from->a_url->url_user || !sip->sip_from->a_url->url_host) { + return; + } + + if (sofia_reg_find_reg_url(tech_pvt->profile, sip->sip_from->a_url->url_user, sip->sip_from->a_url->url_host, buf, sizeof(buf))) { + tech_pvt->chat_from = sip_header_as_string(tech_pvt->home, (const sip_header_t *) sip->sip_to); + tech_pvt->chat_to = switch_core_session_strdup(tech_pvt->session, buf); + sofia_presence_set_hash_key(hash_key, sizeof(hash_key), sip); + } else { + return; + } + + tech_pvt->hash_key = switch_core_session_strdup(tech_pvt->session, hash_key); + switch_core_hash_insert(tech_pvt->profile->chat_hash, tech_pvt->hash_key, tech_pvt); + +} diff --git a/src/mod/endpoints/mod_sofia/sofia_reg.c b/src/mod/endpoints/mod_sofia/sofia_reg.c new file mode 100644 index 0000000000..1aa3bc8ca4 --- /dev/null +++ b/src/mod/endpoints/mod_sofia/sofia_reg.c @@ -0,0 +1,790 @@ +#include "mod_sofia.h" + + +void sofia_reg_unregister(sofia_profile_t * profile) +{ + outbound_reg_t *gateway_ptr; + for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { + if (gateway_ptr->sofia_private) { + free(gateway_ptr->sofia_private); + nua_handle_bind(gateway_ptr->nh, NULL); + gateway_ptr->sofia_private = NULL; + } + nua_handle_destroy(gateway_ptr->nh); + } +} + +void sofia_reg_check_gateway(sofia_profile_t * profile, time_t now) +{ + outbound_reg_t *gateway_ptr; + for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { + int ss_state = nua_callstate_authenticating; + reg_state_t ostate = gateway_ptr->state; + + switch (ostate) { + case REG_STATE_REGISTER: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "registered %s\n", gateway_ptr->name); + gateway_ptr->expires = now + gateway_ptr->freq; + gateway_ptr->state = REG_STATE_REGED; + break; + case REG_STATE_UNREGED: + if ((gateway_ptr->nh = nua_handle(gateway_ptr->profile->nua, NULL, + NUTAG_URL(gateway_ptr->register_proxy), + SIPTAG_TO_STR(gateway_ptr->register_to), + NUTAG_CALLSTATE_REF(ss_state), SIPTAG_FROM_STR(gateway_ptr->register_from), TAG_END()))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "registering %s\n", gateway_ptr->name); + + if (!(gateway_ptr->sofia_private = malloc(sizeof(*gateway_ptr->sofia_private)))) { + abort(); + } + memset(gateway_ptr->sofia_private, 0, sizeof(*gateway_ptr->sofia_private)); + + gateway_ptr->sofia_private->gateway = gateway_ptr; + nua_handle_bind(gateway_ptr->nh, gateway_ptr->sofia_private); + + nua_register(gateway_ptr->nh, + SIPTAG_FROM_STR(gateway_ptr->register_from), + SIPTAG_CONTACT_STR(gateway_ptr->register_contact), + SIPTAG_EXPIRES_STR(gateway_ptr->expires_str), + NUTAG_REGISTRAR(gateway_ptr->register_proxy), + NUTAG_OUTBOUND("no-options-keepalive"), NUTAG_OUTBOUND("no-validate"), NUTAG_KEEPALIVE(0), TAG_NULL()); + gateway_ptr->retry = now + 10; + gateway_ptr->state = REG_STATE_TRYING; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error registering %s\n", gateway_ptr->name); + gateway_ptr->state = REG_STATE_FAILED; + } + break; + + case REG_STATE_TRYING: + if (gateway_ptr->retry && now >= gateway_ptr->retry) { + gateway_ptr->state = REG_STATE_UNREGED; + gateway_ptr->retry = 0; + } + break; + default: + if (now >= gateway_ptr->expires) { + gateway_ptr->state = REG_STATE_UNREGED; + } + break; + } + } + +} + + +int sofia_reg_find_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + struct callback_t *cbt = (struct callback_t *) pArg; + + switch_copy_string(cbt->val, argv[0], cbt->len); + cbt->matches++; + return 0; +} + +int sofia_reg_del_callback(void *pArg, int argc, char **argv, char **columnNames) +{ + switch_event_t *s_event; + + if (argc >= 3) { + if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_EXPIRE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile-name", "%s", argv[0]); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "user", "%s", argv[1]); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "host", "%s", argv[2]); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "contact", "%s", argv[3]); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%s", argv[4]); + switch_event_fire(&s_event); + } + } + return 0; +} + +void sofia_reg_check_expire(switch_core_db_t *db, sofia_profile_t * profile, time_t now) +{ + char sql[1024]; + char *errmsg; + + if (!db) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + return; + } + + switch_mutex_lock(profile->ireg_mutex); + snprintf(sql, sizeof(sql), "select '%s',* from sip_registrations where expires > 0 and expires < %ld", profile->name, (long) now); + switch_core_db_exec(db, sql, sofia_reg_del_callback, NULL, &errmsg); + + if (errmsg) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s][%s]\n", sql, errmsg); + switch_safe_free(errmsg); + errmsg = NULL; + } + + snprintf(sql, sizeof(sql), "delete from sip_registrations where expires > 0 and expires < %ld", (long) now); + switch_core_db_persistant_execute(db, sql, 1000); + snprintf(sql, sizeof(sql), "delete from sip_authentication where expires > 0 and expires < %ld", (long) now); + switch_core_db_persistant_execute(db, sql, 1000); + snprintf(sql, sizeof(sql), "delete from sip_subscriptions where expires > 0 and expires < %ld", (long) now); + switch_core_db_persistant_execute(db, sql, 1000); + + switch_mutex_unlock(profile->ireg_mutex); + +} + +char *sofia_reg_find_reg_url(sofia_profile_t * profile, const char *user, const char *host, char *val, switch_size_t len) +{ + char *errmsg; + struct callback_t cbt = { 0 }; + switch_core_db_t *db; + + if (!user) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Called with null user!\n"); + return NULL; + } + + if (!(db = switch_core_db_open_file(profile->dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname); + return NULL; + } + + cbt.val = val; + cbt.len = len; + switch_mutex_lock(profile->ireg_mutex); + if (host) { + snprintf(val, len, "select contact from sip_registrations where user='%s' and host='%s'", user, host); + } else { + snprintf(val, len, "select contact from sip_registrations where user='%s'", user); + } + + switch_core_db_exec(db, val, sofia_reg_find_callback, &cbt, &errmsg); + + if (errmsg) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR [%s][%s]\n", val, errmsg); + switch_safe_free(errmsg); + errmsg = NULL; + } + + switch_mutex_unlock(profile->ireg_mutex); + + switch_core_db_close(db); + if (cbt.matches) { + return val; + } else { + return NULL; + } +} + +char *sofia_reg_get_auth_data(char *dbname, char *nonce, char *npassword, size_t len, switch_mutex_t * mutex) +{ + switch_core_db_t *db; + switch_core_db_stmt_t *stmt; + char *sql = NULL, *ret = NULL; + + if (mutex) { + switch_mutex_lock(mutex); + } + + if (!dbname) { + goto end; + } + + if (!(db = switch_core_db_open_file(dbname))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", dbname); + goto end; + } + + sql = switch_mprintf("select passwd from sip_authentication where nonce='%q'", nonce); + if (switch_core_db_prepare(db, sql, -1, &stmt, 0)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Statement Error!\n"); + goto fail; + } else { + int running = 1; + int colcount; + + while (running < 5000) { + int result = switch_core_db_step(stmt); + + if (result == SWITCH_CORE_DB_ROW) { + if ((colcount = switch_core_db_column_count(stmt))) { + switch_copy_string(npassword, (char *) switch_core_db_column_text(stmt, 0), len); + ret = npassword; + } + break; + } else if (result == SWITCH_CORE_DB_BUSY) { + running++; + switch_yield(1000); + continue; + } + break; + } + + switch_core_db_finalize(stmt); + } + + + fail: + + switch_core_db_close(db); + + end: + if (mutex) { + switch_mutex_unlock(mutex); + } + + if (sql) { + switch_safe_free(sql); + } + + return ret; +} + + + +uint8_t sofia_reg_handle_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sip_t const *sip, sofia_regtype_t regtype, char *key, + uint32_t keylen) +{ + sip_from_t const *from = NULL; + sip_expires_t const *expires = NULL; + sip_authorization_t const *authorization = NULL; + sip_contact_t const *contact = NULL; + switch_xml_t domain, xml, user, param, xparams; + char params[1024] = ""; + char *sql; + switch_event_t *s_event; + const char *from_user = NULL; + const char *from_host = NULL; + char contact_str[1024] = ""; + char buf[512]; + const char *passwd = NULL; + const char *a1_hash = NULL; + uint8_t stale = 0, ret = 0, forbidden = 0; + auth_res_t auth_res; + long exptime = 60; + switch_event_t *event; + const char *rpid = "unknown"; + const char *display = "\"user\""; + + /* all callers must confirm that sip, sip->sip_request and sip->sip_contact are not NULL */ + assert(sip != NULL && sip->sip_contact != NULL && sip->sip_request != NULL); + + expires = sip->sip_expires; + authorization = sip->sip_authorization; + contact = sip->sip_contact; + from = sip->sip_from; + + if (from) { + from_user = from->a_url->url_user; + from_host = from->a_url->url_host; + } + + if (!from_user || !from_host) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can not do authorization without a complete from header\n"); + nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); + return 1; + } + + if (contact->m_url) { + const char *port = contact->m_url->url_port; + display = contact->m_display; + + if (switch_strlen_zero(display)) { + if (from) { + display = from->a_display; + if (switch_strlen_zero(display)) { + display = "\"user\""; + } + } + } + + if (!port) { + port = SOFIA_DEFAULT_PORT; + } + + if (contact->m_url->url_params) { + snprintf(contact_str, sizeof(contact_str), "%s ", + display, contact->m_url->url_user, contact->m_url->url_host, port, contact->m_url->url_params); + } else { + snprintf(contact_str, sizeof(contact_str), "%s ", display, contact->m_url->url_user, contact->m_url->url_host, port); + } + } + + if (expires) { + exptime = expires->ex_delta; + } else if (contact->m_expires) { + exptime = atol(contact->m_expires); + } + + if (regtype == REG_REGISTER) { + authorization = sip->sip_authorization; + } else if (regtype == REG_INVITE) { + authorization = sip->sip_proxy_authorization; + } + + if ((profile->pflags & PFLAG_BLIND_REG)) { + goto reg; + } + + if (authorization) { + if ((auth_res = parse_auth(profile, authorization, sip->sip_request->rq_method_name, key, keylen)) == AUTH_STALE) { + stale = 1; + } + + if (auth_res != AUTH_OK && !stale) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "send %s for [%s@%s]\n", forbidden ? "forbidden" : "challange", from_user, from_host); + if (auth_res == AUTH_FORBIDDEN) { + nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS(nua), TAG_END()); + } else { + nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), TAG_END()); + } + return 1; + } + } + + if (!authorization || stale) { + snprintf(params, sizeof(params), "from_user=%s&from_host=%s&contact=%s", from_user, from_host, contact_str); + + + if (switch_xml_locate("directory", "domain", "name", from_host, &xml, &domain, params) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find domain for [%s@%s]\n", from_user, from_host); + nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_CONTACT(contact), TAG_END()); + return 1; + } + + if (!(user = switch_xml_find_child(domain, "user", "id", from_user))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find user [%s@%s]\n", from_user, from_host); + nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_CONTACT(contact), TAG_END()); + switch_xml_free(xml); + return 1; + } + + if (!(xparams = switch_xml_child(user, "params"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find params for user [%s@%s]\n", from_user, from_host); + nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_CONTACT(contact), TAG_END()); + switch_xml_free(xml); + return 1; + } + + + for (param = switch_xml_child(xparams, "param"); param; param = param->next) { + const char *var = switch_xml_attr_soft(param, "name"); + const char *val = switch_xml_attr_soft(param, "value"); + + if (!strcasecmp(var, "password")) { + passwd = val; + } + + if (!strcasecmp(var, "a1-hash")) { + a1_hash = val; + } + } + + if (passwd || a1_hash) { + switch_uuid_t uuid; + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + char *sql, *auth_str; + + su_md5_t ctx; + char hexdigest[2 * SU_MD5_DIGEST_SIZE + 1]; + char *input; + + if (!a1_hash) { + input = switch_mprintf("%s:%s:%s", from_user, from_host, passwd); + su_md5_init(&ctx); + su_md5_strupdate(&ctx, input); + su_md5_hexdigest(&ctx, hexdigest); + su_md5_deinit(&ctx); + switch_safe_free(input); + + switch_uuid_get(&uuid); + switch_uuid_format(uuid_str, &uuid); + a1_hash = hexdigest; + } + + sql = switch_mprintf("delete from sip_authentication where user='%q' and host='%q';\n" + "insert into sip_authentication values('%q','%q','%q','%q', %ld)", + from_user, from_host, from_user, from_host, a1_hash, uuid_str, time(NULL) + profile->nonce_ttl); + auth_str = + switch_mprintf("Digest realm=\"%q\", nonce=\"%q\",%s algorithm=MD5, qop=\"auth\"", from_host, uuid_str, stale ? " stale=\"true\"," : ""); + + + if (regtype == REG_REGISTER) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Requesting Registration from: [%s@%s]\n", from_user, from_host); + nua_respond(nh, SIP_401_UNAUTHORIZED, NUTAG_WITH_THIS(nua), SIPTAG_WWW_AUTHENTICATE_STR(auth_str), TAG_END()); + } else if (regtype == REG_INVITE) { + nua_respond(nh, SIP_407_PROXY_AUTH_REQUIRED, NUTAG_WITH_THIS(nua), SIPTAG_PROXY_AUTHENTICATE_STR(auth_str), TAG_END()); + + } + + sofia_glue_execute_sql(profile->dbname, sql, profile->ireg_mutex); + switch_safe_free(sql); + switch_safe_free(auth_str); + ret = 1; + } else { + ret = 0; + } + + switch_xml_free(xml); + + if (ret) { + return ret; + } + } + reg: + + if (exptime) { + if (!sofia_reg_find_reg_url(profile, from_user, from_host, buf, sizeof(buf))) { + sql = switch_mprintf("insert into sip_registrations values ('%q','%q','%q','Registered', '%q', %ld)", + from_user, from_host, contact_str, rpid, (long) time(NULL) + (long) exptime * 2); + + } else { + sql = + switch_mprintf + ("update sip_registrations set contact='%q', expires=%ld, rpid='%q' where user='%q' and host='%q'", + contact_str, (long) time(NULL) + (long) exptime * 2, rpid, from_user, from_host); + + } + + if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_REGISTER) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "profile-name", "%s", profile->name); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from-user", "%s", from_user); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "from-host", "%s", from_host); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "contact", "%s", contact_str); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); + switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "expires", "%ld", (long) exptime); + switch_event_fire(&s_event); + } + + if (sql) { + sofia_glue_execute_sql(profile->dbname, sql, profile->ireg_mutex); + switch_safe_free(sql); + sql = NULL; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Register:\nFrom: [%s@%s]\nContact: [%s]\nExpires: [%ld]\n", from_user, from_host, contact_str, (long) exptime); + + + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_IN) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "sip"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "Registered"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_fire(&event); + } + } else { + if ((sql = switch_mprintf("delete from sip_subscriptions where user='%q' and host='%q'", from_user, from_host))) { + sofia_glue_execute_sql(profile->dbname, sql, profile->ireg_mutex); + switch_safe_free(sql); + sql = NULL; + } + if (switch_event_create(&event, SWITCH_EVENT_PRESENCE_OUT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "sip"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "login", "%s", profile->url); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s+%s@%s", SOFIA_CHAT_PROTO, from_user, from_host); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "status", "unavailable"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "rpid", "%s", rpid); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "event_type", "presence"); + switch_event_fire(&event); + } + } + + + if (switch_event_create(&event, SWITCH_EVENT_ROSTER) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "proto", "sip"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "from", "%s@%s", from_user, from_host); + switch_event_fire(&event); + } + + if (regtype == REG_REGISTER) { + nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(contact), NUTAG_WITH_THIS(nua), TAG_END()); + return 1; + } + + return 0; +} + + + +void sofia_reg_handle_sip_i_register(nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + char key[128] = ""; + + if (!sip || !sip->sip_request || !sip->sip_request->rq_method_name) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Received an invalid packet!\n"); + nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END()); + return; + } + + if (!(sip->sip_contact && sip->sip_contact->m_url)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "NO CONTACT!\n"); + nua_respond(nh, 400, "Missing Contact Header", TAG_END()); + return; + } + + sofia_reg_handle_register(nua, profile, nh, sip, REG_REGISTER, key, sizeof(key)); +} + + +void sofia_reg_handle_sip_r_register(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, sofia_private_t * sofia_private, sip_t const *sip, tagi_t tags[]) +{ + if (sofia_private && sofia_private->gateway) { + switch (status) { + case 200: + if (sip && sip->sip_contact && sip->sip_contact->m_expires) { + char *new_expires = (char *) sip->sip_contact->m_expires; + uint32_t expi = (uint32_t) atoi(new_expires); + + if (expi != sofia_private->gateway->freq) { + sofia_private->gateway->freq = expi; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, + "Changing expire time to %d by request of proxy %s\n", expi, sofia_private->gateway->register_proxy); + } + + } + sofia_private->gateway->state = REG_STATE_REGISTER; + break; + case 100: + break; + default: + sofia_private->gateway->state = REG_STATE_FAILED; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Registration Failed with status %d\n", status); + break; + } + } +} + +void sofia_reg_handle_sip_r_challenge(int status, + char const *phrase, + nua_t * nua, sofia_profile_t * profile, nua_handle_t * nh, switch_core_session_t *session, sip_t const *sip, tagi_t tags[]) +{ + outbound_reg_t *gateway = NULL; + sip_www_authenticate_t const *authenticate = NULL; + char const *realm = NULL; + char *p = NULL, *duprealm = NULL, *qrealm = NULL; + char const *scheme = NULL; + int indexnum; + char *cur; + char authentication[256] = ""; + int ss_state; + + if (session) { + private_object_t *tech_pvt; + if ((tech_pvt = switch_core_session_get_private(session)) && switch_test_flag(tech_pvt, TFLAG_REFER)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "received reply from refer\n"); + return; + } + } + + + if (sip->sip_www_authenticate) { + authenticate = sip->sip_www_authenticate; + } else if (sip->sip_proxy_authenticate) { + authenticate = sip->sip_proxy_authenticate; + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Missing Authenticate Header!\n"); + return; + } + scheme = (char const *) authenticate->au_scheme; + if (authenticate->au_params) { + for (indexnum = 0; (cur = (char *) authenticate->au_params[indexnum]); indexnum++) { + if ((realm = strstr(cur, "realm="))) { + realm += 6; + break; + } + } + } + + if (!(scheme && realm)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No scheme and realm!\n"); + return; + } + + if (profile) { + outbound_reg_t *gateway_ptr; + + if ((duprealm = strdup(realm))) { + qrealm = duprealm; + + while (*qrealm && *qrealm == '"') { + qrealm++; + } + + if ((p = strchr(qrealm, '"'))) { + *p = '\0'; + } + + if (sip->sip_from) { + char *from_key = switch_mprintf("sip:%s@%s", + (char *) sip->sip_from->a_url->url_user, + (char *) sip->sip_from->a_url->url_host); + + if (!(gateway = sofia_reg_find_gateway(from_key))) { + gateway = sofia_reg_find_gateway(qrealm); + } + + switch_safe_free(from_key); + } + + if (!gateway) { + for (gateway_ptr = profile->gateways; gateway_ptr; gateway_ptr = gateway_ptr->next) { + if (scheme && qrealm && !strcasecmp(gateway_ptr->register_scheme, scheme) + && !strcasecmp(gateway_ptr->register_realm, qrealm)) { + gateway = gateway_ptr; + break; + } + } + } + + if (!gateway) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Match for Scheme [%s] Realm [%s]\n", scheme, qrealm); + return; + } + switch_safe_free(duprealm); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); + return; + } + } + + snprintf(authentication, sizeof(authentication), "%s:%s:%s:%s", scheme, realm, gateway->register_username, gateway->register_password); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Authenticating '%s' with '%s'.\n", profile->username, authentication); + + + ss_state = nua_callstate_authenticating; + + tl_gets(tags, NUTAG_CALLSTATE_REF(ss_state), SIPTAG_WWW_AUTHENTICATE_REF(authenticate), TAG_END()); + + nua_authenticate(nh, SIPTAG_EXPIRES_STR(gateway->expires_str), NUTAG_AUTH(authentication), TAG_END()); + +} + +auth_res_t parse_auth(sofia_profile_t * profile, sip_authorization_t const *authorization, const char *regstr, char *np, size_t nplen) +{ + int indexnum; + const char *cur; + su_md5_t ctx; + char uridigest[2 * SU_MD5_DIGEST_SIZE + 1]; + char bigdigest[2 * SU_MD5_DIGEST_SIZE + 1]; + char *nonce, *uri, *qop, *cnonce, *nc, *response, *input = NULL, *input2 = NULL; + auth_res_t ret = AUTH_FORBIDDEN; + char *npassword = NULL; + int cnt = 0; + nonce = uri = qop = cnonce = nc = response = NULL; + + if (authorization->au_params) { + for (indexnum = 0; (cur = authorization->au_params[indexnum]); indexnum++) { + char *var, *val, *p, *work; + var = val = work = NULL; + if ((work = strdup(cur))) { + var = work; + if ((val = strchr(var, '='))) { + *val++ = '\0'; + while (*val == '"') { + *val++ = '\0'; + } + if ((p = strchr(val, '"'))) { + *p = '\0'; + } + + if (!strcasecmp(var, "nonce")) { + nonce = strdup(val); + cnt++; + } else if (!strcasecmp(var, "uri")) { + uri = strdup(val); + cnt++; + } else if (!strcasecmp(var, "qop")) { + qop = strdup(val); + cnt++; + } else if (!strcasecmp(var, "cnonce")) { + cnonce = strdup(val); + cnt++; + } else if (!strcasecmp(var, "response")) { + response = strdup(val); + cnt++; + } else if (!strcasecmp(var, "nc")) { + nc = strdup(val); + cnt++; + } + } + + free(work); + } + } + } + + if (cnt != 6) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Authorization header!\n"); + goto end; + } + + if (switch_strlen_zero(np)) { + if (!sofia_reg_get_auth_data(profile->dbname, nonce, np, nplen, profile->ireg_mutex)) { + ret = AUTH_STALE; + goto end; + } + } + + npassword = np; + + if ((input = switch_mprintf("%s:%q", regstr, uri))) { + su_md5_init(&ctx); + su_md5_strupdate(&ctx, input); + su_md5_hexdigest(&ctx, uridigest); + su_md5_deinit(&ctx); + } + + if ((input2 = switch_mprintf("%q:%q:%q:%q:%q:%q", npassword, nonce, nc, cnonce, qop, uridigest))) { + memset(&ctx, 0, sizeof(ctx)); + su_md5_init(&ctx); + su_md5_strupdate(&ctx, input2); + su_md5_hexdigest(&ctx, bigdigest); + su_md5_deinit(&ctx); + + if (!strcasecmp(bigdigest, response)) { + ret = AUTH_OK; + } else { + ret = AUTH_FORBIDDEN; + } + } + + end: + switch_safe_free(input); + switch_safe_free(input2); + switch_safe_free(nonce); + switch_safe_free(uri); + switch_safe_free(qop); + switch_safe_free(cnonce); + switch_safe_free(nc); + switch_safe_free(response); + + return ret; + +} + + +outbound_reg_t *sofia_reg_find_gateway(char *key) +{ + outbound_reg_t *gateway; + + switch_mutex_lock(globals.hash_mutex); + gateway = (outbound_reg_t *) switch_core_hash_find(globals.gateway_hash, key); + switch_mutex_unlock(globals.hash_mutex); + + return gateway; +} + +void sofia_reg_add_gateway(char *key, outbound_reg_t * gateway) +{ + switch_mutex_lock(globals.hash_mutex); + switch_core_hash_insert(globals.gateway_hash, key, gateway); + switch_mutex_unlock(globals.hash_mutex); +} + + +