From 4ebc465f3a0bbd3e8a81ca508419139cbe71a6dd Mon Sep 17 00:00:00 2001 From: Chris Rienzo Date: Mon, 6 Jan 2014 21:06:31 -0500 Subject: [PATCH] mod_rayo FS-6081 --resolve added mixer presence with entity capabilities --- src/mod/event_handlers/mod_rayo/iks_helpers.c | 23 +++ src/mod/event_handlers/mod_rayo/iks_helpers.h | 5 +- src/mod/event_handlers/mod_rayo/mod_rayo.c | 138 ++++++++++++++++-- 3 files changed, 155 insertions(+), 11 deletions(-) diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.c b/src/mod/event_handlers/mod_rayo/iks_helpers.c index 30d9d215a5..93be6db3a2 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.c +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.c @@ -522,6 +522,29 @@ char *iks_server_dialback_key(const char *secret, const char *receiving_server, return NULL; } +/** + * Print base 64 encoded SHA-1 hash + * @param sha hash to print + * @param buf to store baes 64 encoded hash + */ +void iks_sha_print_base64(iksha *sha, char *buf) +{ + int i; + char hex_digit[3] = { 0 }; + char hex_buf[SHA_1_HASH_BUF_SIZE]; + unsigned char bin_buf[SHA_1_HASH_BUF_SIZE / 2]; + iks_sha_print(sha, hex_buf); + + /* convert hex string to octets */ + for (i = 0; i < SHA_1_HASH_BUF_SIZE; i += 2) { + hex_digit[0] = hex_buf[i]; + hex_digit[1] = hex_buf[i + 1]; + bin_buf[i / 2] = strtol(hex_digit, NULL, 16); + } + + switch_b64_encode(bin_buf, SHA_1_HASH_BUF_SIZE / 2, (unsigned char *)buf, SHA_1_HASH_BUF_SIZE); +} + /* For Emacs: * Local Variables: * mode:c diff --git a/src/mod/event_handlers/mod_rayo/iks_helpers.h b/src/mod/event_handlers/mod_rayo/iks_helpers.h index 3a7bae02e7..2017134713 100644 --- a/src/mod/event_handlers/mod_rayo/iks_helpers.h +++ b/src/mod/event_handlers/mod_rayo/iks_helpers.h @@ -32,6 +32,8 @@ #include #include +#define SHA_1_HASH_BUF_SIZE 40 + #define IKS_JABBER_SERVER_PORT 5269 #define IKS_NS_XMPP_DISCO "http://jabber.org/protocol/disco#info" @@ -40,6 +42,7 @@ #define IKS_NS_XMPP_STREAMS "http://etherx.jabber.org/streams" #define IKS_NS_XMPP_DIALBACK "jabber:server:dialback" #define IKS_NS_XMPP_TLS "urn:ietf:params:xml:ns:xmpp-tls" +#define IKS_NS_XMPP_ENTITY_CAPABILITIES "http://jabber.org/protocol/caps" struct xmpp_error { const char *name; @@ -53,7 +56,6 @@ struct xmpp_error { #include "xmpp_errors.def" /* See RFC-3920 XMPP core for error definitions */ - extern iks *iks_new_presence(const char *name, const char *namespace, const char *from, const char *to); extern iks *iks_new_error(iks *iq, const struct xmpp_error *err); extern iks *iks_new_error_detailed(iks *iq, const struct xmpp_error *err, const char *detail_text); @@ -70,6 +72,7 @@ extern const char *iks_net_error_to_string(int err); extern iks *iks_insert_attrib_printf(iks *xml, const char *name, const char *fmt, ...); extern char *iks_server_dialback_key(const char *secret, const char *receiving_server, const char *originating_server, const char *stream_id); +extern void iks_sha_print_base64(iksha *sha, char *buf); /** A function to validate attribute value */ typedef int (*iks_attrib_validation_function)(const char *); diff --git a/src/mod/event_handlers/mod_rayo/mod_rayo.c b/src/mod/event_handlers/mod_rayo/mod_rayo.c index 95162093c8..d88cc26af0 100644 --- a/src/mod/event_handlers/mod_rayo/mod_rayo.c +++ b/src/mod/event_handlers/mod_rayo/mod_rayo.c @@ -170,9 +170,9 @@ struct rayo_mixer_member { * A subscriber to mixer events */ struct rayo_mixer_subscriber { - /** JID of subscriber */ + /** JID of client */ const char *jid; - /** Number of controlled parties in mixer */ + /** Number of client's calls in mixer */ int ref_count; }; @@ -250,6 +250,54 @@ typedef switch_bool_t (* rayo_actor_match_fn)(struct rayo_actor *); static switch_bool_t is_call_actor(struct rayo_actor *actor); +/** + * Entity features returned by service discovery + */ +struct entity_identity { + /** identity category */ + const char *category; + /** identity type */ + const char *type; +}; + +static struct entity_identity rayo_server_identity = { "server", "im" }; +static const char *rayo_server_features[] = { IKS_NS_XMPP_ENTITY_CAPABILITIES, IKS_NS_XMPP_DISCO, RAYO_NS, RAYO_CPA_NS, RAYO_FAX_NS, 0 }; + +static struct entity_identity rayo_mixer_identity = { "client", "rayo_mixer" }; +static const char *rayo_mixer_features[] = { 0 }; + +static struct entity_identity rayo_call_identity = { "client", "rayo_call" }; +static const char *rayo_call_features[] = { 0 }; + +/** + * Calculate SHA-1 hash of entity capabilities + * @param identity of entity + * @param features of identity (NULL terminated) + * @return base64 hash (free when done) + */ +static char *calculate_entity_sha1_ver(struct entity_identity *identity, const char **features) +{ + int i; + const char *feature; + char ver[SHA_1_HASH_BUF_SIZE + 1] = { 0 }; + iksha *sha; + + sha = iks_sha_new(); + iks_sha_hash(sha, (const unsigned char *)identity->category, strlen(identity->category), 0); + iks_sha_hash(sha, (const unsigned char *)"/", 1, 0); + iks_sha_hash(sha, (const unsigned char *)identity->type, strlen(identity->type), 0); + iks_sha_hash(sha, (const unsigned char *)"//", 2, 0); + i = 0; + while ((feature = features[i++])) { + iks_sha_hash(sha, (const unsigned char *)"<", 1, 0); + iks_sha_hash(sha, (const unsigned char *)feature, strlen(feature), 0); + } + iks_sha_hash(sha, (const unsigned char *)"<", 1, 1); + iks_sha_print_base64(sha, ver); + iks_sha_delete(sha); + + return strdup(ver); +} /** * @param msg to check @@ -408,6 +456,32 @@ static void add_header(iks *node, const char *name, const char *value) } } +/** + * Send event to clients + * @param from event sender + * @param rayo_event the event to send + * @param online_only only send to online clients + */ +static void broadcast_event(struct rayo_actor *from, iks *rayo_event, int online_only) +{ + switch_hash_index_t *hi = NULL; + switch_mutex_lock(globals.clients_mutex); + for (hi = switch_hash_first(NULL, globals.clients_roster); hi; hi = switch_hash_next(hi)) { + struct rayo_client *rclient; + const void *key; + void *val; + switch_hash_this(hi, &key, NULL, &val); + rclient = (struct rayo_client *)val; + switch_assert(rclient); + + if (!online_only || rclient->availability == PS_ONLINE) { + iks_insert_attrib(rayo_event, "to", RAYO_JID(rclient)); + RAYO_SEND_MESSAGE_DUP(from, RAYO_JID(rclient), rayo_event); + } + } + switch_mutex_unlock(globals.clients_mutex); +} + /** * Add an outbound dialing gateway * @param uri_prefix to match @@ -2445,15 +2519,20 @@ static iks *on_iq_get_xmpp_disco(struct rayo_actor *server, struct rayo_message iks *response = NULL; iks *x; iks *feature; + iks *identity; + int i = 0; + const char *feature_string; response = iks_new_iq_result(node); x = iks_insert(response, "query"); iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_DISCO); - feature = iks_insert(x, "feature"); - iks_insert_attrib(feature, "var", RAYO_NS); - feature = iks_insert(x, "feature"); - iks_insert_attrib(feature, "var", RAYO_CPA_NS); - feature = iks_insert(x, "feature"); - iks_insert_attrib(feature, "var", RAYO_FAX_NS); + identity = iks_insert(x, "identity"); + iks_insert_attrib(identity, "category", rayo_server_identity.category); + iks_insert_attrib(identity, "type", rayo_server_identity.type); + i = 0; + while((feature_string = rayo_server_features[i++])) { + feature = iks_insert(x, "feature"); + iks_insert_attrib(feature, "var", feature_string); + } /* TODO The response MUST also include features for the application formats and transport methods supported by * the responding entity, as described in the relevant specifications. @@ -2669,6 +2748,15 @@ static void on_mixer_delete_member_event(struct rayo_mixer *mixer, switch_event_ static void on_mixer_destroy_event(struct rayo_mixer *mixer, switch_event_t *event) { if (mixer) { + iks *presence; + + /* notify online clients of mixer destruction */ + presence = iks_new("presence"); + iks_insert_attrib(presence, "from", RAYO_JID(mixer)); + iks_insert_attrib(presence, "type", "unavailable"); + broadcast_event(RAYO_ACTOR(mixer), presence, 1); + iks_delete(presence); + /* remove from hash and destroy */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s, destroying mixer: %s\n", RAYO_JID(mixer), rayo_mixer_get_name(mixer)); RAYO_UNLOCK(mixer); /* release original lock */ @@ -2688,10 +2776,25 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * struct rayo_call *call = RAYO_CALL_LOCATE_BY_ID(uuid); if (!mixer) { + char *ver; + iks *presence, *c; + /* new mixer */ const char *mixer_name = switch_event_get_header(event, "Conference-Name"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "creating mixer: %s\n", mixer_name); mixer = rayo_mixer_create(mixer_name); + + /* notify online clients of mixer presence */ + ver = calculate_entity_sha1_ver(&rayo_mixer_identity, rayo_mixer_features); + + presence = iks_new_presence("c", IKS_NS_XMPP_ENTITY_CAPABILITIES, RAYO_JID(mixer), ""); + c = iks_find(presence, "c"); + iks_insert_attrib(c, "hash", "sha-1"); + iks_insert_attrib(c, "node", RAYO_MIXER_NS); + iks_insert_attrib(c, "ver", ver); + free(ver); + + broadcast_event(RAYO_ACTOR(mixer), presence, 1); } if (call) { @@ -2719,6 +2822,9 @@ static void on_mixer_add_member_event(struct rayo_mixer *mixer, switch_event_t * if (call->pending_join_request) { iks *request = call->pending_join_request; iks *result = iks_new_iq_result(request); + iks *ref = iks_insert(result, "ref"); + iks_insert_attrib(ref, "xmlns", RAYO_NS); + iks_insert_attrib_printf(ref, "uri", "xmpp:%s", RAYO_JID(mixer)); call->pending_join_request = NULL; RAYO_SEND_REPLY(call, iks_find_attrib_soft(request, "from"), result); iks_delete(request); @@ -3135,12 +3241,24 @@ static iks *rayo_create_offer(struct rayo_call *call, switch_core_session_t *ses switch_channel_t *channel = switch_core_session_get_channel(session); switch_caller_profile_t *profile = switch_channel_get_caller_profile(channel); iks *presence = iks_new("presence"); + iks *c = iks_insert(presence, "c"); iks *offer = iks_insert(presence, "offer"); const char *val; + char *ver; + /* */ iks_insert_attrib(presence, "from", RAYO_JID(call)); - iks_insert_attrib(offer, "xmlns", RAYO_NS); + /* */ + ver = calculate_entity_sha1_ver(&rayo_call_identity, rayo_call_features); + iks_insert_attrib(c, "xmlns", IKS_NS_XMPP_ENTITY_CAPABILITIES); + iks_insert_attrib(c, "hash", "sha-1"); + iks_insert_attrib(c, "node", RAYO_CALL_NS); + iks_insert_attrib(c, "ver", ver); + free(ver); + + /* */ + iks_insert_attrib(offer, "xmlns", RAYO_NS); if (globals.offer_uri && (val = switch_channel_get_variable(channel, "sip_from_uri"))) { /* is a SIP call - pass the URI */ if (!strchr(val, ':')) { @@ -3287,8 +3405,8 @@ SWITCH_STANDARD_APP(rayo_app) RAYO_SEND_MESSAGE_DUP(call, RAYO_JID(rclient), offer); } } - iks_delete(offer); switch_mutex_unlock(globals.clients_mutex); + iks_delete(offer); /* nobody to offer to */ if (!ok) {