Sat Apr 26 2014 22:02:15

Asterisk developer's documentation


chan_motif.c File Reference

Motif Jingle Channel Driver. More...

#include "asterisk.h"
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/signal.h>
#include <iksemel.h>
#include <pthread.h>
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config_options.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/sched.h"
#include "asterisk/io.h"
#include "asterisk/rtp_engine.h"
#include "asterisk/acl.h"
#include "asterisk/callerid.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/musiconhold.h"
#include "asterisk/manager.h"
#include "asterisk/stringfields.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astobj.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/xmpp.h"
Include dependency graph for chan_motif.c:

Go to the source code of this file.

Data Structures

struct  jingle_action_handler
 Defined handlers for different Jingle actions. More...
struct  jingle_config
struct  jingle_endpoint
 Endpoint which contains configuration information and active sessions. More...
struct  jingle_endpoint_state
 Endpoint state information. More...
struct  jingle_reason_mapping
 Reason text <-> cause code mapping. More...
struct  jingle_session
 Session which contains information about an active session. More...

Defines

#define DEFAULT_MAX_ICE_CANDIDATES   "10"
 Default maximum number of ICE candidates we will offer.
#define DEFAULT_MAX_PAYLOADS   "30"
 Default maximum number of payloads we will offer.
#define ENDPOINT_BUCKETS   37
 Number of buckets for endpoints.
#define GOOGLE_PHONE_NS   "http://www.google.com/session/phone"
 Namespace for Google Phone description.
#define GOOGLE_SESSION_NS   "http://www.google.com/session"
 Namespace for Google Session.
#define GOOGLE_TRANSPORT_NS   "http://www.google.com/transport/p2p"
 Namespace for Google Talk ICE-UDP.
#define GOOGLE_TRANSPORT_RAW_NS   "http://www.google.com/transport/raw-udp"
 Namespace for Google Talk Raw UDP.
#define GOOGLE_VIDEO_NS   "http://www.google.com/session/video"
 Namespace for Google Video description.
#define JINGLE_ICE_UDP_NS   "urn:xmpp:jingle:transports:ice-udp:1"
 Namespace for Jingle ICE-UDP.
#define JINGLE_NS   "urn:xmpp:jingle:1"
 Namespace for Jingle itself.
#define JINGLE_RTP_INFO_NS   "urn:xmpp:jingle:apps:rtp:info:1"
 Namespace for Jingle RTP info.
#define JINGLE_RTP_NS   "urn:xmpp:jingle:apps:rtp:1"
 Namespace for Jingle RTP sessions.
#define SESSION_BUCKETS   37
 Number of buckets for sessions, on a per-endpoint basis.
#define XMPP_STANZAS_NS   "urn:ietf:params:xml:ns:xmpp-stanzas"
 Namespace for XMPP stanzas.

Enumerations

enum  jingle_transport { JINGLE_TRANSPORT_ICE_UDP = 3, JINGLE_TRANSPORT_GOOGLE_V2 = 2, JINGLE_TRANSPORT_GOOGLE_V1 = 1, JINGLE_TRANSPORT_NONE = 0 }
 The various transport methods supported, from highest priority to lowest priority when doing fallback. More...

Functions

static void __reg_module (void)
static void __unreg_module (void)
static AO2_GLOBAL_OBJ_STATIC (globals)
 CONFIG_INFO_STANDARD (cfg_info, globals, jingle_config_alloc,.files=ACO_FILES(&jingle_conf),)
static int custom_connection_handler (const struct aco_option *opt, struct ast_variable *var, void *obj)
 Custom handler for connection.
static int custom_group_handler (const struct aco_option *opt, struct ast_variable *var, void *obj)
 Custom handler for groups.
static int custom_transport_handler (const struct aco_option *opt, struct ast_variable *var, void *obj)
 Custom handler for transport.
static int jingle_action_hook (void *data, ikspak *pak)
 Callback for when a Jingle action is received from an endpoint.
static void jingle_action_session_accept (struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
 Handler function for the 'session-accept' action.
static void jingle_action_session_info (struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
 Handler function for the 'session-info' action.
static void jingle_action_session_initiate (struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
 Action handlers.
static void jingle_action_session_terminate (struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
 Handler function for the 'session-terminate' action.
static void jingle_action_transport_info (struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
 Handler function for the 'transport-info' action.
static int jingle_add_content (struct jingle_session *session, iks *jingle, iks *content, iks *description, iks *transport, const char *name, enum ast_format_type type, struct ast_rtp_instance *rtp, iks **payloads)
 Helper function which adds content to a description.
static int jingle_add_google_candidates_to_transport (struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int video, enum jingle_transport transport_type, unsigned int maximum)
 Internal helper function which adds Google candidates to a transport node.
static int jingle_add_ice_udp_candidates_to_transport (struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int maximum)
 Internal helper function which adds ICE-UDP candidates to a transport node.
static int jingle_add_payloads_to_description (struct jingle_session *session, struct ast_rtp_instance *rtp, iks *description, iks **payloads, enum ast_format_type type)
 Internal helper function which adds payloads to a description.
static struct jingle_sessionjingle_alloc (struct jingle_endpoint *endpoint, const char *from, const char *sid)
 Internal helper function used to allocate Jingle session on an endpoint.
static int jingle_answer (struct ast_channel *ast)
 Function called by core when we should answer a Jingle session.
static int jingle_call (struct ast_channel *ast, const char *dest, int timeout)
 Function called by core to actually start calling a remote party.
static void * jingle_config_alloc (void)
 Allocator called when module configuration should appear.
static void jingle_config_destructor (void *obj)
 Destructor called when module configuration goes away.
static int jingle_digit_begin (struct ast_channel *chan, char digit)
 Function called by core to start a DTMF digit.
static int jingle_digit_end (struct ast_channel *ast, char digit, unsigned int duration)
 Function called by core to stop a DTMF digit.
static void jingle_enable_video (struct jingle_session *session)
 Internal helper function which enables video support on a sesson if possible.
static void * jingle_endpoint_alloc (const char *cat)
 Allocator function for Jingle endpoints.
static int jingle_endpoint_cmp (void *obj, void *arg, int flags)
 Comparator function for Jingle endpoints.
static void jingle_endpoint_destructor (void *obj)
 Destructor for Jingle endpoints.
static void * jingle_endpoint_find (struct ao2_container *tmp_container, const char *category)
 Find function for Jingle endpoints.
static int jingle_endpoint_hash (const void *obj, const int flags)
 Hashing function for Jingle endpoints.
static struct
jingle_endpoint_state
jingle_endpoint_state_create (void)
 Allocator function for Jingle endpoint state.
static void jingle_endpoint_state_destructor (void *obj)
 Destructor for Jingle endpoint state.
static struct
jingle_endpoint_state
jingle_endpoint_state_find_or_create (const char *category)
 State find/create function.
static int jingle_fixup (struct ast_channel *oldchan, struct ast_channel *newchan)
 Function called by core to change the underlying owner channel.
static void jingle_get_codec (struct ast_channel *chan, struct ast_format_cap *result)
 Function called by RTP engine to get peer capabilities.
static enum ast_rtp_glue_result jingle_get_rtp_peer (struct ast_channel *chan, struct ast_rtp_instance **instance)
 Function called by RTP engine to get local RTP peer.
static int jingle_hangup (struct ast_channel *ast)
 Function called by core to hang up a Jingle session.
static int jingle_indicate (struct ast_channel *ast, int condition, const void *data, size_t datalen)
 Function called by core to ask the channel to indicate some sort of condition.
static int jingle_interpret_content (struct jingle_session *session, ikspak *pak)
 Helper function which locates content stanzas and interprets them.
static int jingle_interpret_description (struct jingle_session *session, iks *description, const char *name, struct ast_rtp_instance **rtp)
 Helper function which handles content descriptions.
static int jingle_interpret_google_transport (struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
 Helper function which handles Google transport information.
static int jingle_interpret_ice_udp_transport (struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
 Helper function which handles ICE-UDP transport information.
static struct ast_channeljingle_new (struct jingle_endpoint *endpoint, struct jingle_session *session, int state, const char *title, const char *linkedid, const char *cid_name)
 Function called to create a new Jingle Asterisk channel.
static int jingle_outgoing_hook (void *data, ikspak *pak)
 Callback for when a response is received for an outgoing session-initiate message.
static void jingle_queue_hangup_with_cause (struct jingle_session *session, int cause)
 Helper function which queues a hangup frame with cause code.
static struct ast_framejingle_read (struct ast_channel *ast)
 Function called by core to read any waiting frames.
static struct ast_channeljingle_request (const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause)
 Function called by core to create a new outgoing Jingle session.
static void jingle_send_error_response (struct ast_xmpp_client *connection, ikspak *pak, const char *type, const char *reasonstr, const char *reasonstr2)
 Internal helper function which sends an error response.
static void jingle_send_response (struct ast_xmpp_client *connection, ikspak *pak)
 Internal helper function which sends a response.
static void jingle_send_session_accept (struct jingle_session *session)
 Internal function which sends a session-accept message.
static void jingle_send_session_action (struct jingle_session *session, const char *action)
 Internal function which sends a complete session message.
static void jingle_send_session_info (struct jingle_session *session, const char *info)
 Internal function which sends a session-info message.
static void jingle_send_session_initiate (struct jingle_session *session)
 Internal function which sends a session-inititate message.
static void jingle_send_session_terminate (struct jingle_session *session, const char *reasontext)
 Internal function which sends a session-terminate message.
static void jingle_send_transport_info (struct jingle_session *session, const char *from)
 Internal function which sends a transport-info message.
static int jingle_sendtext (struct ast_channel *chan, const char *text)
 Function called by core to send text to the remote party of the Jingle session.
static int jingle_session_cmp (void *obj, void *arg, int flags)
 Comparator function for Jingle sessions.
static void jingle_session_destructor (void *obj)
 Destructor for Jingle sessions.
static int jingle_session_hash (const void *obj, const int flags)
 Hashing function for Jingle sessions.
static struct ast_channeljingle_session_lock_full (struct jingle_session *pvt)
static int jingle_set_rtp_peer (struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, const struct ast_format_cap *cap, int nat_active)
 Function called by RTP engine to change where the remote party should send media.
static int jingle_write (struct ast_channel *ast, struct ast_frame *frame)
 Function called by core to write frames.
static int load_module (void)
 Load module into PBX, register channel.
static int reload (void)
 Reload module.
static int unload_module (void)
 Unload the jingle channel from Asterisk.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Motif Jingle Channel Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DRIVER, }
static struct ast_module_infoast_module_info = &__mod_info
static const char channel_type [] = "Motif"
static const char desc [] = "Motif Jingle Channel"
static struct aco_type endpoint_option
struct aco_typeendpoint_options [] = ACO_TYPES(&endpoint_option)
static struct jingle_action_handler jingle_action_handlers []
struct aco_file jingle_conf
static struct jingle_reason_mapping jingle_reason_mappings []
static struct ast_rtp_glue jingle_rtp_glue
 Local glue for interacting with the RTP engine core.
static struct ast_channel_tech jingle_tech
 PBX interface structure for channel registration.
static struct ast_sched_contextsched

Detailed Description

Motif Jingle Channel Driver.

Author:
Joshua Colp <jcolp@digium.com>
ExtRef:
Iksemel http://iksemel.jabberstudio.org/

Definition in file chan_motif.c.


Define Documentation

#define DEFAULT_MAX_ICE_CANDIDATES   "10"

Default maximum number of ICE candidates we will offer.

Definition at line 73 of file chan_motif.c.

Referenced by load_module().

#define DEFAULT_MAX_PAYLOADS   "30"

Default maximum number of payloads we will offer.

Definition at line 76 of file chan_motif.c.

Referenced by load_module().

#define ENDPOINT_BUCKETS   37

Number of buckets for endpoints.

Definition at line 79 of file chan_motif.c.

Referenced by jingle_config_alloc().

#define GOOGLE_PHONE_NS   "http://www.google.com/session/phone"

Namespace for Google Phone description.

Definition at line 106 of file chan_motif.c.

Referenced by jingle_add_content(), jingle_add_payloads_to_description(), and jingle_interpret_content().

#define GOOGLE_SESSION_NS   "http://www.google.com/session"
#define GOOGLE_TRANSPORT_NS   "http://www.google.com/transport/p2p"
#define GOOGLE_TRANSPORT_RAW_NS   "http://www.google.com/transport/raw-udp"

Namespace for Google Talk Raw UDP.

Definition at line 100 of file chan_motif.c.

#define GOOGLE_VIDEO_NS   "http://www.google.com/session/video"

Namespace for Google Video description.

Definition at line 109 of file chan_motif.c.

Referenced by jingle_interpret_content().

#define JINGLE_ICE_UDP_NS   "urn:xmpp:jingle:transports:ice-udp:1"
#define JINGLE_RTP_INFO_NS   "urn:xmpp:jingle:apps:rtp:info:1"

Namespace for Jingle RTP info.

Definition at line 91 of file chan_motif.c.

Referenced by jingle_action_session_info().

#define JINGLE_RTP_NS   "urn:xmpp:jingle:apps:rtp:1"

Namespace for Jingle RTP sessions.

Definition at line 88 of file chan_motif.c.

Referenced by jingle_add_content(), and jingle_interpret_content().

#define SESSION_BUCKETS   37

Number of buckets for sessions, on a per-endpoint basis.

Definition at line 82 of file chan_motif.c.

Referenced by jingle_endpoint_state_create().

#define XMPP_STANZAS_NS   "urn:ietf:params:xml:ns:xmpp-stanzas"

Namespace for XMPP stanzas.

Definition at line 112 of file chan_motif.c.

Referenced by jingle_outgoing_hook().


Enumeration Type Documentation

The various transport methods supported, from highest priority to lowest priority when doing fallback.

Enumerator:
JINGLE_TRANSPORT_ICE_UDP 

XEP-0176

JINGLE_TRANSPORT_GOOGLE_V2 

https://developers.google.com/talk/call_signaling

JINGLE_TRANSPORT_GOOGLE_V1 

Undocumented initial Google specification

JINGLE_TRANSPORT_NONE 

No transport specified

Definition at line 115 of file chan_motif.c.

                      {
   JINGLE_TRANSPORT_ICE_UDP = 3,   /*!< XEP-0176 */
   JINGLE_TRANSPORT_GOOGLE_V2 = 2, /*!< https://developers.google.com/talk/call_signaling */
   JINGLE_TRANSPORT_GOOGLE_V1 = 1, /*!< Undocumented initial Google specification */
   JINGLE_TRANSPORT_NONE = 0,      /*!< No transport specified */
};

Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 2626 of file chan_motif.c.

static void __unreg_module ( void  ) [static]

Definition at line 2626 of file chan_motif.c.

static AO2_GLOBAL_OBJ_STATIC ( globals  ) [static]
CONFIG_INFO_STANDARD ( cfg_info  ,
globals  ,
jingle_config_alloc  ,
files = ACO_FILES(&jingle_conf) 
)
static int custom_connection_handler ( const struct aco_option opt,
struct ast_variable var,
void *  obj 
) [static]

Custom handler for connection.

Definition at line 2491 of file chan_motif.c.

References ast_log(), ast_xmpp_client_find(), jingle_endpoint::connection, ast_xmpp_client::filter, GOOGLE_SESSION_NS, jingle_action_hook(), JINGLE_NS, LOG_ERROR, jingle_endpoint::name, jingle_endpoint::rule, and ast_variable::value.

Referenced by load_module().

{
   struct jingle_endpoint *endpoint = obj;

   /* You might think... but Josh, shouldn't you do this in a prelink callback? Well I *could* but until the original is destroyed
    * this will not actually get called, so even if the config turns out to be bogus this is harmless.
    */
   if (!(endpoint->connection = ast_xmpp_client_find(var->value))) {
      ast_log(LOG_ERROR, "Connection '%s' configured on endpoint '%s' could not be found\n", var->value, endpoint->name);
      return -1;
   }

   if (!(endpoint->rule = iks_filter_add_rule(endpoint->connection->filter, jingle_action_hook, endpoint,
                     IKS_RULE_TYPE, IKS_PAK_IQ,
                     IKS_RULE_NS, JINGLE_NS,
                     IKS_RULE_NS, GOOGLE_SESSION_NS,
                     IKS_RULE_DONE))) {
      ast_log(LOG_ERROR, "Action hook could not be added to connection '%s' on endpoint '%s'\n", var->value, endpoint->name);
      return -1;
   }

   return 0;
}
static int custom_group_handler ( const struct aco_option opt,
struct ast_variable var,
void *  obj 
) [static]

Custom handler for groups.

Definition at line 2475 of file chan_motif.c.

References ast_get_group(), jingle_endpoint::callgroup, ast_variable::name, jingle_endpoint::pickupgroup, and ast_variable::value.

Referenced by load_module().

{
   struct jingle_endpoint *endpoint = obj;

   if (!strcasecmp(var->name, "callgroup")) {
      endpoint->callgroup = ast_get_group(var->value);
   } else if (!strcasecmp(var->name, "pickupgroup")) {
      endpoint->pickupgroup = ast_get_group(var->value);
   } else {
      return -1;
   }

   return 0;
}
static int custom_transport_handler ( const struct aco_option opt,
struct ast_variable var,
void *  obj 
) [static]

Custom handler for transport.

Definition at line 2516 of file chan_motif.c.

References ast_log(), JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, JINGLE_TRANSPORT_ICE_UDP, LOG_WARNING, jingle_endpoint::name, jingle_endpoint::transport, and ast_variable::value.

Referenced by load_module().

{
   struct jingle_endpoint *endpoint = obj;

   if (!strcasecmp(var->value, "ice-udp")) {
      endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
   } else if (!strcasecmp(var->value, "google")) {
      endpoint->transport = JINGLE_TRANSPORT_GOOGLE_V2;
   } else if (!strcasecmp(var->value, "google-v1")) {
      endpoint->transport = JINGLE_TRANSPORT_GOOGLE_V1;
   } else {
      ast_log(LOG_WARNING, "Unknown transport type '%s' on endpoint '%s', defaulting to 'ice-udp'\n", var->value, endpoint->name);
      endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
   }

   return 0;
}
static int jingle_action_hook ( void *  data,
ikspak *  pak 
) [static]

Callback for when a Jingle action is received from an endpoint.

Definition at line 2412 of file chan_motif.c.

References ao2_find, ao2_ref, ARRAY_LEN, ast_callid_threadassoc_add(), ast_callid_threadassoc_remove(), ast_log(), ast_strlen_zero(), jingle_session::callid, jingle_action_handler::handler, jingle_action_handlers, LOG_NOTICE, OBJ_KEY, jingle_endpoint_state::sessions, and jingle_endpoint::state.

Referenced by custom_connection_handler().

{
   char *action;
   const char *sid = NULL;
   struct jingle_session *session = NULL;
   struct jingle_endpoint *endpoint = data;
   int i, handled = 0;

   /* We accept both Jingle and Google-V1 */
   if (!(action = iks_find_attrib(pak->query, "action")) &&
       !(action = iks_find_attrib(pak->query, "type"))) {
      /* This occurs if either receive a packet masquerading as Jingle or Google-V1 that is actually not OR we receive a response
       * to a message that has no response hook. */
      return IKS_FILTER_EAT;
   }

   /* Bump the endpoint reference count up in case a reload occurs. Unfortunately the available synchronization between iksemel and us
    * does not permit us to make this completely safe. */
   ao2_ref(endpoint, +1);

   /* If a Jingle session identifier is present use it */
   if (!(sid = iks_find_attrib(pak->query, "sid"))) {
      /* If a Google-V1 session identifier is present use it */
      sid = iks_find_attrib(pak->query, "id");
   }

   /* If a session identifier was present in the message attempt to find the session, it is up to the action handler whether
    * this is required or not */
   if (!ast_strlen_zero(sid)) {
      session = ao2_find(endpoint->state->sessions, sid, OBJ_KEY);
   }

   /* If a session is present associate the callid with this thread */
   if (session) {
      ast_callid_threadassoc_add(session->callid);
   }

   /* Iterate through supported action handlers looking for one that is able to handle this */
   for (i = 0; i < ARRAY_LEN(jingle_action_handlers); i++) {
      if (!strcasecmp(jingle_action_handlers[i].action, action)) {
         jingle_action_handlers[i].handler(endpoint, session, pak);
         handled = 1;
         break;
      }
   }

   /* If no action handler is present for the action they sent us make it evident */
   if (!handled) {
      ast_log(LOG_NOTICE, "Received action '%s' for session '%s' that has no handler\n", action, sid);
   }

   /* If a session was successfully found for this message deref it now since the handler is done */
   if (session) {
      ast_callid_threadassoc_remove();
      ao2_ref(session, -1);
   }

   ao2_ref(endpoint, -1);

   return IKS_FILTER_EAT;
}
static void jingle_action_session_accept ( struct jingle_endpoint endpoint,
struct jingle_session session,
ikspak *  pak 
) [static]

Handler function for the 'session-accept' action.

Definition at line 2291 of file chan_motif.c.

References ao2_unlock, ast_channel_unlock, ast_channel_unref, AST_CONTROL_ANSWER, ast_queue_control(), jingle_endpoint::connection, jingle_interpret_content(), jingle_send_error_response(), jingle_send_response(), and jingle_session_lock_full().

{
   struct ast_channel *chan;

   if (!session) {
      jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
                  "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
      return;
   }


   jingle_interpret_content(session, pak);

   if ((chan = jingle_session_lock_full(session))) {
      ast_queue_control(chan, AST_CONTROL_ANSWER);
      ast_channel_unlock(chan);
      ast_channel_unref(chan);
   }
   ao2_unlock(session);

   jingle_send_response(endpoint->connection, pak);
}
static void jingle_action_session_info ( struct jingle_endpoint endpoint,
struct jingle_session session,
ikspak *  pak 
) [static]

Handler function for the 'session-info' action.

Definition at line 2315 of file chan_motif.c.

References ao2_unlock, ast_channel_unlock, ast_channel_unref, AST_CONTROL_HOLD, AST_CONTROL_RINGING, AST_CONTROL_UNHOLD, ast_queue_control(), ast_setstate(), AST_STATE_RINGING, AST_STATE_UP, jingle_endpoint::connection, JINGLE_RTP_INFO_NS, jingle_send_error_response(), jingle_send_response(), and jingle_session_lock_full().

{
   struct ast_channel *chan;

   if (!session) {
      jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
                  "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
      return;
   }

   if (!(chan = jingle_session_lock_full(session))) {
      ao2_unlock(session);
      jingle_send_response(endpoint->connection, pak);
      return;
   }

   if (iks_find_with_attrib(pak->query, "ringing", "xmlns", JINGLE_RTP_INFO_NS)) {
      ast_queue_control(chan, AST_CONTROL_RINGING);
      if (ast_channel_state(chan) != AST_STATE_UP) {
         ast_setstate(chan, AST_STATE_RINGING);
      }
   } else if (iks_find_with_attrib(pak->query, "hold", "xmlns", JINGLE_RTP_INFO_NS)) {
      ast_queue_control(chan, AST_CONTROL_HOLD);
   } else if (iks_find_with_attrib(pak->query, "unhold", "xmlns", JINGLE_RTP_INFO_NS)) {
      ast_queue_control(chan, AST_CONTROL_UNHOLD);
   }

   ast_channel_unlock(chan);
   ast_channel_unref(chan);
   ao2_unlock(session);

   jingle_send_response(endpoint->connection, pak);
}
static void jingle_action_session_initiate ( struct jingle_endpoint endpoint,
struct jingle_session session,
ikspak *  pak 
) [static]

Action handlers.

Handler function for the 'session-initiate' action.

Definition at line 2209 of file chan_motif.c.

References ao2_link, ao2_ref, ast_hangup(), ast_log(), AST_PBX_CALL_LIMIT, AST_PBX_FAILED, ast_pbx_start(), AST_PBX_SUCCESS, ast_setstate(), AST_STATE_DOWN, AST_STATE_RING, jingle_endpoint::connection, jingle_session::gone, jingle_alloc(), jingle_interpret_content(), jingle_new(), jingle_send_error_response(), jingle_send_response(), jingle_send_transport_info(), JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_NONE, LOG_WARNING, jingle_endpoint_state::sessions, jingle_endpoint::state, and jingle_session::transport.

{
   char *sid;
   enum jingle_transport transport = JINGLE_TRANSPORT_NONE;
   struct ast_channel *chan;
   int res;

   if (session) {
      /* This is a duplicate session setup, so respond accordingly */
      jingle_send_error_response(endpoint->connection, pak, "result", "out-of-order", NULL);
      return;
   }

   /* Retrieve the session identifier from the message, note that this may alter the transport */
   if ((sid = iks_find_attrib(pak->query, "id"))) {
      /* The presence of the session identifier in the 'id' attribute tells us that this is Google-V1 as everything else uses 'sid' */
      transport = JINGLE_TRANSPORT_GOOGLE_V1;
   } else if (!(sid = iks_find_attrib(pak->query, "sid"))) {
      jingle_send_error_response(endpoint->connection, pak, "bad-request", NULL, NULL);
      return;
   }

   /* Create a new local session */
   if (!(session = jingle_alloc(endpoint, pak->from->full, sid))) {
      jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
      return;
   }

   /* If we determined that the transport should change as a result of how we got the SID change it */
   if (transport != JINGLE_TRANSPORT_NONE) {
      session->transport = transport;
   }

   /* Create a new Asterisk channel using the above local session */
   if (!(chan = jingle_new(endpoint, session, AST_STATE_DOWN, pak->from->user, NULL, pak->from->full))) {
      ao2_ref(session, -1);
      jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
      return;
   }

   ao2_link(endpoint->state->sessions, session);

   ast_setstate(chan, AST_STATE_RING);
   res = ast_pbx_start(chan);

   switch (res) {
   case AST_PBX_FAILED:
      ast_log(LOG_WARNING, "Failed to start PBX :(\n");
      jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
      session->gone = 1;
      ast_hangup(chan);
      break;
   case AST_PBX_CALL_LIMIT:
      ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
      jingle_send_error_response(endpoint->connection, pak, "wait", "resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
      ast_hangup(chan);
      break;
   case AST_PBX_SUCCESS:
      jingle_send_response(endpoint->connection, pak);

      /* Only send a transport-info message if we successfully interpreted the available content */
      if (!jingle_interpret_content(session, pak)) {
         jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
      }
      break;
   }
}
static void jingle_action_session_terminate ( struct jingle_endpoint endpoint,
struct jingle_session session,
ikspak *  pak 
) [static]

Handler function for the 'session-terminate' action.

Definition at line 2350 of file chan_motif.c.

References ao2_unlock, ARRAY_LEN, ast_alloca, ast_control_pvt_cause_code::ast_cause, AST_CAUSE_NORMAL, ast_channel_hangupcause_hash_set(), AST_CHANNEL_NAME, ast_channel_name(), ast_channel_unlock, ast_channel_unref, AST_CONTROL_PVT_CAUSE_CODE, ast_copy_string(), ast_debug, ast_queue_control_data(), ast_queue_hangup_with_cause(), jingle_reason_mapping::cause, ast_control_pvt_cause_code::chan_name, ast_control_pvt_cause_code::code, jingle_endpoint::connection, jingle_session::gone, jingle_reason_mappings, jingle_send_error_response(), jingle_send_response(), jingle_session_lock_full(), and text.

{
   struct ast_channel *chan;
   iks *reason, *text;
   int cause = AST_CAUSE_NORMAL;
   struct ast_control_pvt_cause_code *cause_code;
   int data_size = sizeof(*cause_code);

   if (!session) {
      jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
                  "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
      return;
   }

   if (!(chan = jingle_session_lock_full(session))) {
      ao2_unlock(session);
      jingle_send_response(endpoint->connection, pak);
      return;
   }

   /* Pull the reason text from the session-terminate message and translate it into a cause code */
   if ((reason = iks_find(pak->query, "reason")) && (text = iks_child(reason))) {
      int i;

      /* Size of the string making up the cause code is "Motif " + text */
      data_size += 6 + strlen(iks_name(text));
      cause_code = ast_alloca(data_size);
      memset(cause_code, 0, data_size);

      /* Get the appropriate cause code mapping for this reason */
      for (i = 0; i < ARRAY_LEN(jingle_reason_mappings); i++) {
         if (!strcasecmp(jingle_reason_mappings[i].reason, iks_name(text))) {
            cause = jingle_reason_mappings[i].cause;
            break;
         }
      }

      /* Store the technology specific information */
      snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "Motif %s", iks_name(text));
   } else {
      /* No technology specific information is available */
      cause_code = ast_alloca(data_size);
      memset(cause_code, 0, data_size);
   }

   ast_copy_string(cause_code->chan_name, ast_channel_name(chan), AST_CHANNEL_NAME);
   cause_code->ast_cause = cause;
   ast_queue_control_data(chan, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
   ast_channel_hangupcause_hash_set(chan, cause_code, data_size);

   ast_debug(3, "Hanging up channel '%s' due to session terminate message with cause '%d'\n", ast_channel_name(chan), cause);
   ast_queue_hangup_with_cause(chan, cause);
   session->gone = 1;

   ast_channel_unlock(chan);
   ast_channel_unref(chan);
   ao2_unlock(session);

   jingle_send_response(endpoint->connection, pak);
}
static void jingle_action_transport_info ( struct jingle_endpoint endpoint,
struct jingle_session session,
ikspak *  pak 
) [static]

Handler function for the 'transport-info' action.

Definition at line 2278 of file chan_motif.c.

References jingle_endpoint::connection, jingle_interpret_content(), jingle_send_error_response(), and jingle_send_response().

{
   if (!session) {
      jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
                  "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
      return;
   }

   jingle_interpret_content(session, pak);
   jingle_send_response(endpoint->connection, pak);
}
static int jingle_add_content ( struct jingle_session session,
iks *  jingle,
iks *  content,
iks *  description,
iks *  transport,
const char *  name,
enum ast_format_type  type,
struct ast_rtp_instance rtp,
iks **  payloads 
) [static]

Helper function which adds content to a description.

Definition at line 1227 of file chan_motif.c.

References AST_FORMAT_TYPE_AUDIO, AST_FORMAT_TYPE_VIDEO, GOOGLE_PHONE_NS, GOOGLE_TRANSPORT_NS, jingle_add_payloads_to_description(), JINGLE_ICE_UDP_NS, JINGLE_RTP_NS, JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, JINGLE_TRANSPORT_ICE_UDP, jingle_session::outgoing, and jingle_session::transport.

Referenced by jingle_send_session_action().

{
   int res = 0;

   if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
      iks_insert_attrib(content, "creator", session->outgoing ? "initiator" : "responder");
      iks_insert_attrib(content, "name", name);
      iks_insert_node(jingle, content);

      iks_insert_attrib(description, "xmlns", JINGLE_RTP_NS);
      if (type == AST_FORMAT_TYPE_AUDIO) {
         iks_insert_attrib(description, "media", "audio");
      } else if (type == AST_FORMAT_TYPE_VIDEO) {
         iks_insert_attrib(description, "media", "video");
      } else {
         return -1;
      }
      iks_insert_node(content, description);
   } else {
      iks_insert_attrib(description, "xmlns", GOOGLE_PHONE_NS);
      iks_insert_node(jingle, description);
   }

   if (!(res = jingle_add_payloads_to_description(session, rtp, description, payloads, type))) {
      if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
         iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
         iks_insert_node(content, transport);
      } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
         iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
         iks_insert_node(content, transport);
      }
   }

   return res;
}
static int jingle_add_google_candidates_to_transport ( struct ast_rtp_instance rtp,
iks *  transport,
iks **  candidates,
unsigned int  video,
enum jingle_transport  transport_type,
unsigned int  maximum 
) [static]

Internal helper function which adds Google candidates to a transport node.

Definition at line 812 of file chan_motif.c.

References ast_rtp_engine_ice_candidate::address, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_log(), AST_RTP_ICE_CANDIDATE_TYPE_HOST, AST_RTP_ICE_CANDIDATE_TYPE_SRFLX, ast_rtp_instance_get_ice(), ast_sockaddr_stringify_host(), ast_sockaddr_stringify_port(), ast_rtp_engine_ice::get_local_candidates, ast_rtp_engine_ice::get_password, ast_rtp_engine_ice::get_ufrag, GOOGLE_TRANSPORT_NS, ast_rtp_engine_ice_candidate::id, JINGLE_TRANSPORT_GOOGLE_V1, LOG_ERROR, and ast_rtp_engine_ice_candidate::type.

Referenced by jingle_send_transport_info().

{
   struct ast_rtp_engine_ice *ice;
   struct ao2_container *local_candidates;
   struct ao2_iterator it;
   struct ast_rtp_engine_ice_candidate *candidate;
   int i = 0, res = 0;

   if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
      ast_log(LOG_ERROR, "Unable to add Google ICE candidates as ICE support not available or no candidates available\n");
      return -1;
   }

   if (transport_type != JINGLE_TRANSPORT_GOOGLE_V1) {
      iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
   }

   it = ao2_iterator_init(local_candidates, 0);

   while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
      iks *local_candidate;
      /* In Google land a username is 16 bytes, explicitly */
      char ufrag[17] = "";

      if (!(local_candidate = iks_new("candidate"))) {
         res = -1;
         ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for Google ICE transport\n");
         break;
      }

      if (candidate->id == 1) {
         iks_insert_attrib(local_candidate, "name", !video ? "rtp" : "video_rtp");
      } else if (candidate->id == 2) {
         iks_insert_attrib(local_candidate, "name", !video ? "rtcp" : "video_rtcp");
      } else {
         iks_delete(local_candidate);
         continue;
      }

      iks_insert_attrib(local_candidate, "address", ast_sockaddr_stringify_host(&candidate->address));
      iks_insert_attrib(local_candidate, "port", ast_sockaddr_stringify_port(&candidate->address));

      if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
         iks_insert_attrib(local_candidate, "preference", "0.95");
         iks_insert_attrib(local_candidate, "type", "local");
      } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
         iks_insert_attrib(local_candidate, "preference", "0.9");
         iks_insert_attrib(local_candidate, "type", "stun");
      }

      iks_insert_attrib(local_candidate, "protocol", "udp");
      iks_insert_attrib(local_candidate, "network", "0");
      snprintf(ufrag, sizeof(ufrag), "%s", ice->get_ufrag(rtp));
      iks_insert_attrib(local_candidate, "username", ufrag);
      iks_insert_attrib(local_candidate, "generation", "0");

      if (transport_type == JINGLE_TRANSPORT_GOOGLE_V1) {
         iks_insert_attrib(local_candidate, "password", "");
         iks_insert_attrib(local_candidate, "foundation", "0");
         iks_insert_attrib(local_candidate, "component", "1");
      } else {
         iks_insert_attrib(local_candidate, "password", ice->get_password(rtp));
      }

      /* You may notice a lack of relay support up above - this is because we don't support it for use with
       * the Google talk transport due to their arcane support. */

      iks_insert_node(transport, local_candidate);
      candidates[i++] = local_candidate;
   }

   ao2_iterator_destroy(&it);
   ao2_ref(local_candidates, -1);

   return res;
}
static int jingle_add_ice_udp_candidates_to_transport ( struct ast_rtp_instance rtp,
iks *  transport,
iks **  candidates,
unsigned int  maximum 
) [static]

Internal helper function which adds ICE-UDP candidates to a transport node.

Definition at line 750 of file chan_motif.c.

References ast_rtp_engine_ice_candidate::address, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_log(), ast_random(), AST_RTP_ICE_CANDIDATE_TYPE_HOST, AST_RTP_ICE_CANDIDATE_TYPE_RELAYED, AST_RTP_ICE_CANDIDATE_TYPE_SRFLX, ast_rtp_instance_get_ice(), ast_sockaddr_stringify_host(), ast_sockaddr_stringify_port(), ast_str_hash(), ast_rtp_engine_ice_candidate::foundation, ast_rtp_engine_ice::get_local_candidates, ast_rtp_engine_ice::get_password, ast_rtp_engine_ice::get_ufrag, ast_rtp_engine_ice_candidate::id, JINGLE_ICE_UDP_NS, LOG_ERROR, ast_rtp_engine_ice_candidate::priority, and ast_rtp_engine_ice_candidate::type.

Referenced by jingle_send_transport_info().

{
   struct ast_rtp_engine_ice *ice;
   struct ao2_container *local_candidates;
   struct ao2_iterator it;
   struct ast_rtp_engine_ice_candidate *candidate;
   int i = 0, res = 0;

   if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
      ast_log(LOG_ERROR, "Unable to add ICE-UDP candidates as ICE support not available or no candidates available\n");
      return -1;
   }

   iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
   iks_insert_attrib(transport, "pwd", ice->get_password(rtp));
   iks_insert_attrib(transport, "ufrag", ice->get_ufrag(rtp));

   it = ao2_iterator_init(local_candidates, 0);

   while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
      iks *local_candidate;
      char tmp[30];

      if (!(local_candidate = iks_new("candidate"))) {
         res = -1;
         ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for ICE-UDP transport\n");
         break;
      }

      snprintf(tmp, sizeof(tmp), "%d", candidate->id);
      iks_insert_attrib(local_candidate, "component", tmp);
      snprintf(tmp, sizeof(tmp), "%d", ast_str_hash(candidate->foundation));
      iks_insert_attrib(local_candidate, "foundation", tmp);
      iks_insert_attrib(local_candidate, "generation", "0");
      iks_insert_attrib(local_candidate, "network", "0");
      snprintf(tmp, sizeof(tmp), "%04lx", ast_random() & 0xffff);
      iks_insert_attrib(local_candidate, "id", tmp);
      iks_insert_attrib(local_candidate, "ip", ast_sockaddr_stringify_host(&candidate->address));
      iks_insert_attrib(local_candidate, "port", ast_sockaddr_stringify_port(&candidate->address));
      snprintf(tmp, sizeof(tmp), "%d", candidate->priority);
      iks_insert_attrib(local_candidate, "priority", tmp);
      iks_insert_attrib(local_candidate, "protocol", "udp");

      if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
         iks_insert_attrib(local_candidate, "type", "host");
      } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
         iks_insert_attrib(local_candidate, "type", "srflx");
      } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_RELAYED) {
         iks_insert_attrib(local_candidate, "type", "relay");
      }

      iks_insert_node(transport, local_candidate);
      candidates[i++] = local_candidate;
   }

   ao2_iterator_destroy(&it);
   ao2_ref(local_candidates, -1);

   return res;
}
static int jingle_add_payloads_to_description ( struct jingle_session session,
struct ast_rtp_instance rtp,
iks *  description,
iks **  payloads,
enum ast_format_type  type 
) [static]

Internal helper function which adds payloads to a description.

Definition at line 1138 of file chan_motif.c.

References ast_codec_pref_index(), AST_CODEC_PREF_SIZE, ast_format_cap_iscompatible(), AST_FORMAT_G722, AST_FORMAT_GET_TYPE, AST_FORMAT_TYPE_AUDIO, AST_FORMAT_TYPE_VIDEO, ast_rtp_codecs_payload_code(), ast_rtp_instance_get_codecs(), ast_rtp_lookup_mime_subtype2(), ast_rtp_lookup_sample_rate2(), GOOGLE_PHONE_NS, ast_format::id, JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, jingle_session::jointcap, jingle_session::maxpayloads, jingle_session::prefs, jingle_session::transport, and type.

Referenced by jingle_add_content().

{
   struct ast_format format;
   int x = 0, i = 0, res = 0;

   for (x = 0; (x < AST_CODEC_PREF_SIZE) && (i < (session->maxpayloads - 2)); x++) {
      int rtp_code;
      iks *payload;
      char tmp[32];

      if (!ast_codec_pref_index(&session->prefs, x, &format)) {
         break;
      }

      if (AST_FORMAT_GET_TYPE(format.id) != type) {
         continue;
      }

      if (!ast_format_cap_iscompatible(session->jointcap, &format)) {
         continue;
      }

      if (((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1, &format, 0)) == -1) ||
          (!(payload = iks_new("payload-type")))) {
         return -1;
      }

      if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
         iks_insert_attrib(payload, "xmlns", GOOGLE_PHONE_NS);
      }

      snprintf(tmp, sizeof(tmp), "%d", rtp_code);
      iks_insert_attrib(payload, "id", tmp);
      iks_insert_attrib(payload, "name", ast_rtp_lookup_mime_subtype2(1, &format, 0, 0));
      iks_insert_attrib(payload, "channels", "1");

      if ((format.id == AST_FORMAT_G722) && ((session->transport == JINGLE_TRANSPORT_GOOGLE_V1) || (session->transport == JINGLE_TRANSPORT_GOOGLE_V2))) {
         iks_insert_attrib(payload, "clockrate", "16000");
      } else {
         snprintf(tmp, sizeof(tmp), "%d", ast_rtp_lookup_sample_rate2(1, &format, 0));
         iks_insert_attrib(payload, "clockrate", tmp);
      }

      if ((type == AST_FORMAT_TYPE_VIDEO) && (session->transport == JINGLE_TRANSPORT_GOOGLE_V2)) {
         iks *parameter;

         /* Google requires these parameters to be set, but alas we can not give accurate values so use some safe defaults */
         if ((parameter = iks_new("parameter"))) {
            iks_insert_attrib(parameter, "name", "width");
            iks_insert_attrib(parameter, "value", "640");
            iks_insert_node(payload, parameter);
         }
         if ((parameter = iks_new("parameter"))) {
            iks_insert_attrib(parameter, "name", "height");
            iks_insert_attrib(parameter, "value", "480");
            iks_insert_node(payload, parameter);
         }
         if ((parameter = iks_new("parameter"))) {
            iks_insert_attrib(parameter, "name", "framerate");
            iks_insert_attrib(parameter, "value", "30");
            iks_insert_node(payload, parameter);
         }
      }

      iks_insert_node(description, payload);
      payloads[i++] = payload;
   }
   /* If this is for audio and there is room for RFC2833 add it in */
   if ((type == AST_FORMAT_TYPE_AUDIO) && (i < session->maxpayloads)) {
      iks *payload;

      if ((payload = iks_new("payload-type"))) {
         if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
            iks_insert_attrib(payload, "xmlns", GOOGLE_PHONE_NS);
         }

         iks_insert_attrib(payload, "id", "101");
         iks_insert_attrib(payload, "name", "telephone-event");
         iks_insert_attrib(payload, "channels", "1");
         iks_insert_attrib(payload, "clockrate", "8000");
         iks_insert_node(description, payload);
         payloads[i++] = payload;
      }
   }

   return res;
}
static struct jingle_session * jingle_alloc ( struct jingle_endpoint endpoint,
const char *  from,
const char *  sid 
) [static, read]

Internal helper function used to allocate Jingle session on an endpoint.

Definition at line 546 of file chan_motif.c.

References ao2_alloc, ao2_ref, ast_copy_string(), ast_create_callid(), ast_format_cap_alloc_nolock(), ast_format_cap_copy(), ast_random(), ast_read_threadstorage_callid(), ast_rtp_instance_new(), ast_rtp_instance_set_prop(), AST_RTP_PROPERTY_DTMF, AST_RTP_PROPERTY_RTCP, ast_sockaddr_parse(), ast_string_field_build, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), jingle_session::callid, jingle_endpoint::cap, jingle_session::cap, jingle_endpoint::connection, jingle_session::connection, jingle_session_destructor(), jingle_session::jointcap, jingle_endpoint::maxicecandidates, jingle_session::maxicecandidates, jingle_endpoint::maxpayloads, jingle_session::maxpayloads, jingle_session::outgoing, jingle_session::peercap, jingle_endpoint::prefs, jingle_session::prefs, jingle_session::remote, jingle_session::remote_original, jingle_session::rtp, jingle_endpoint::state, jingle_session::state, jingle_endpoint::transport, and jingle_session::transport.

Referenced by jingle_action_session_initiate(), and jingle_request().

{
   struct jingle_session *session;
   struct ast_callid *callid;
   struct ast_sockaddr tmp;

   if (!(session = ao2_alloc(sizeof(*session), jingle_session_destructor))) {
      return NULL;
   }

   callid = ast_read_threadstorage_callid();
   session->callid = (callid ? callid : ast_create_callid());

   if (ast_string_field_init(session, 512)) {
      ao2_ref(session, -1);
      return NULL;
   }

   if (!ast_strlen_zero(from)) {
      ast_copy_string(session->remote_original, from, sizeof(session->remote_original));
      ast_copy_string(session->remote, from, sizeof(session->remote));
   }

   if (ast_strlen_zero(sid)) {
      ast_string_field_build(session, sid, "%08lx%08lx", ast_random(), ast_random());
      session->outgoing = 1;
      ast_string_field_set(session, audio_name, "audio");
      ast_string_field_set(session, video_name, "video");
   } else {
      ast_string_field_set(session, sid, sid);
   }

   ao2_ref(endpoint->state, +1);
   session->state = endpoint->state;
   ao2_ref(endpoint->connection, +1);
   session->connection = endpoint->connection;
   session->transport = endpoint->transport;

   if (!(session->cap = ast_format_cap_alloc_nolock()) ||
       !(session->jointcap = ast_format_cap_alloc_nolock()) ||
       !(session->peercap = ast_format_cap_alloc_nolock()) ||
       !session->callid) {
      ao2_ref(session, -1);
      return NULL;
   }

   ast_format_cap_copy(session->cap, endpoint->cap);

   /* While we rely on res_xmpp for communication we still need a temporary ast_sockaddr to tell the RTP engine
    * that we want IPv4 */
   ast_sockaddr_parse(&tmp, "0.0.0.0", 0);

   /* Sessions always carry audio, but video is optional so don't enable it here */
   if (!(session->rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
      ao2_ref(session, -1);
      return NULL;
   }
   ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_RTCP, 1);
   ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_DTMF, 1);

   memcpy(&session->prefs, &endpoint->prefs, sizeof(session->prefs));

   session->maxicecandidates = endpoint->maxicecandidates;
   session->maxpayloads = endpoint->maxpayloads;

   return session;
}
static int jingle_answer ( struct ast_channel ast) [static]

Function called by core when we should answer a Jingle session.

Definition at line 1460 of file chan_motif.c.

References ast_channel_tech_pvt(), AST_STATE_UP, and jingle_send_session_accept().

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);

   /* The channel has already been answered so we don't need to do anything */
   if (ast_channel_state(ast) == AST_STATE_UP) {
      return 0;
   }

   jingle_send_session_accept(session);

   return 0;
}
static int jingle_call ( struct ast_channel ast,
const char *  dest,
int  timeout 
) [static]

Function called by core to actually start calling a remote party.

Definition at line 1676 of file chan_motif.c.

References ast_channel_tech_pvt(), ast_format_cap_copy(), ast_setstate(), AST_STATE_RING, jingle_session::cap, jingle_session::connection, ast_xmpp_client::filter, jingle_outgoing_hook(), jingle_send_session_initiate(), jingle_session::jointcap, ast_xmpp_client::mid, and jingle_session::rule.

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);

   ast_setstate(ast, AST_STATE_RING);

   /* Since we have no idea of the remote capabilities use ours for now */
   ast_format_cap_copy(session->jointcap, session->cap);

   /* We set up a hook so we can know when our session-initiate message was accepted or rejected */
   session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
                   IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);

   jingle_send_session_initiate(session);

   return 0;
}
static void* jingle_config_alloc ( void  ) [static]

Allocator called when module configuration should appear.

Definition at line 457 of file chan_motif.c.

References ao2_alloc, ao2_container_alloc, ao2_ref, ENDPOINT_BUCKETS, jingle_config::endpoints, jingle_config_destructor(), jingle_endpoint_cmp(), and jingle_endpoint_hash().

{
   struct jingle_config *cfg;

   if (!(cfg = ao2_alloc(sizeof(*cfg), jingle_config_destructor))) {
      return NULL;
   }

   if (!(cfg->endpoints = ao2_container_alloc(ENDPOINT_BUCKETS, jingle_endpoint_hash, jingle_endpoint_cmp))) {
      ao2_ref(cfg, -1);
      return NULL;
   }

   return cfg;
}
static void jingle_config_destructor ( void *  obj) [static]

Destructor called when module configuration goes away.

Definition at line 450 of file chan_motif.c.

References ao2_cleanup, and jingle_config::endpoints.

Referenced by jingle_config_alloc().

{
   struct jingle_config *cfg = obj;
   ao2_cleanup(cfg->endpoints);
}
static int jingle_digit_begin ( struct ast_channel ast,
char  digit 
) [static]

Function called by core to start a DTMF digit.

Definition at line 1652 of file chan_motif.c.

References ast_channel_tech_pvt(), ast_rtp_instance_dtmf_begin(), and jingle_session::rtp.

{
   struct jingle_session *session = ast_channel_tech_pvt(chan);

   if (session->rtp) {
      ast_rtp_instance_dtmf_begin(session->rtp, digit);
   }

   return 0;
}
static int jingle_digit_end ( struct ast_channel ast,
char  digit,
unsigned int  duration 
) [static]

Function called by core to stop a DTMF digit.

Definition at line 1664 of file chan_motif.c.

References ast_channel_tech_pvt(), ast_rtp_instance_dtmf_end_with_duration(), and jingle_session::rtp.

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);

   if (session->rtp) {
      ast_rtp_instance_dtmf_end_with_duration(session->rtp, digit, duration);
   }

   return 0;
}
static void jingle_enable_video ( struct jingle_session session) [static]

Internal helper function which enables video support on a sesson if possible.

Definition at line 513 of file chan_motif.c.

References ast_channel_set_fd(), ast_format_cap_has_type(), AST_FORMAT_TYPE_VIDEO, ast_rtp_codecs_packetization_set(), ast_rtp_instance_fd(), ast_rtp_instance_get_codecs(), ast_rtp_instance_get_ice(), ast_rtp_instance_new(), ast_rtp_instance_set_prop(), AST_RTP_PROPERTY_RTCP, ast_sockaddr_parse(), jingle_session::cap, JINGLE_TRANSPORT_GOOGLE_V2, jingle_session::owner, jingle_session::prefs, ast_rtp_engine_ice::stop, jingle_session::transport, and jingle_session::vrtp.

Referenced by jingle_interpret_description(), and jingle_request().

{
   struct ast_sockaddr tmp;
   struct ast_rtp_engine_ice *ice;

   /* If video is already present don't do anything */
   if (session->vrtp) {
      return;
   }

   /* If there are no configured video codecs do not turn video support on, it just won't work */
   if (!ast_format_cap_has_type(session->cap, AST_FORMAT_TYPE_VIDEO)) {
      return;
   }

   ast_sockaddr_parse(&tmp, "0.0.0.0", 0);

   if (!(session->vrtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
      return;
   }

   ast_rtp_instance_set_prop(session->vrtp, AST_RTP_PROPERTY_RTCP, 1);

   ast_channel_set_fd(session->owner, 2, ast_rtp_instance_fd(session->vrtp, 0));
   ast_channel_set_fd(session->owner, 3, ast_rtp_instance_fd(session->vrtp, 1));
   ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session->vrtp), session->vrtp, &session->prefs);

   if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2 && (ice = ast_rtp_instance_get_ice(session->vrtp))) {
      ice->stop(session->vrtp);
   }
}
static void* jingle_endpoint_alloc ( const char *  cat) [static]

Allocator function for Jingle endpoints.

Definition at line 355 of file chan_motif.c.

References ao2_alloc, ao2_ref, ast_format_cap_alloc_nolock(), ast_string_field_init, ast_string_field_set, jingle_endpoint::cap, jingle_endpoint_destructor(), jingle_endpoint_state_find_or_create(), JINGLE_TRANSPORT_ICE_UDP, name, jingle_endpoint::state, and jingle_endpoint::transport.

{
   struct jingle_endpoint *endpoint;

   if (!(endpoint = ao2_alloc(sizeof(*endpoint), jingle_endpoint_destructor))) {
      return NULL;
   }

   if (ast_string_field_init(endpoint, 512)) {
      ao2_ref(endpoint, -1);
      return NULL;
   }

   if (!(endpoint->state = jingle_endpoint_state_find_or_create(cat))) {
      ao2_ref(endpoint, -1);
      return NULL;
   }

   ast_string_field_set(endpoint, name, cat);

   endpoint->cap = ast_format_cap_alloc_nolock();
   endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;

   return endpoint;
}
static int jingle_endpoint_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Comparator function for Jingle endpoints.

Definition at line 391 of file chan_motif.c.

References CMP_MATCH, CMP_STOP, name, jingle_endpoint::name, and OBJ_KEY.

Referenced by jingle_config_alloc().

{
   struct jingle_endpoint *endpoint1 = obj, *endpoint2 = arg;
   const char *name = arg;

   return !strcmp(endpoint1->name, flags & OBJ_KEY ? name : endpoint2->name) ? CMP_MATCH | CMP_STOP : 0;
}
static void jingle_endpoint_destructor ( void *  obj) [static]

Destructor for Jingle endpoints.

Definition at line 298 of file chan_motif.c.

References ao2_ref, ast_format_cap_destroy(), ast_string_field_free_memory, ast_xmpp_client_unref(), jingle_endpoint::cap, jingle_endpoint::connection, ast_xmpp_client::filter, jingle_endpoint::rule, and jingle_endpoint::state.

Referenced by jingle_endpoint_alloc().

{
   struct jingle_endpoint *endpoint = obj;

   if (endpoint->rule) {
      iks_filter_remove_rule(endpoint->connection->filter, endpoint->rule);
   }

   if (endpoint->connection) {
      ast_xmpp_client_unref(endpoint->connection);
   }

   ast_format_cap_destroy(endpoint->cap);

   ao2_ref(endpoint->state, -1);

   ast_string_field_free_memory(endpoint);
}
static void* jingle_endpoint_find ( struct ao2_container tmp_container,
const char *  category 
) [static]

Find function for Jingle endpoints.

Definition at line 318 of file chan_motif.c.

References ao2_find, and OBJ_KEY.

Referenced by jingle_endpoint_state_find_or_create(), and jingle_request().

{
   return ao2_find(tmp_container, category, OBJ_KEY);
}
static int jingle_endpoint_hash ( const void *  obj,
const int  flags 
) [static]

Hashing function for Jingle endpoints.

Definition at line 382 of file chan_motif.c.

References ast_str_hash(), name, jingle_endpoint::name, and OBJ_KEY.

Referenced by jingle_config_alloc().

{
   const struct jingle_endpoint *endpoint = obj;
   const char *name = obj;

   return ast_str_hash(flags & OBJ_KEY ? name : endpoint->name);
}
static struct jingle_endpoint_state* jingle_endpoint_state_create ( void  ) [static, read]

Allocator function for Jingle endpoint state.

Definition at line 324 of file chan_motif.c.

References ao2_alloc, ao2_container_alloc, ao2_ref, jingle_endpoint_state_destructor(), jingle_session_cmp(), jingle_session_hash(), SESSION_BUCKETS, jingle_endpoint_state::sessions, and state.

Referenced by jingle_endpoint_state_find_or_create().

{
   struct jingle_endpoint_state *state;

   if (!(state = ao2_alloc(sizeof(*state), jingle_endpoint_state_destructor))) {
      return NULL;
   }

   if (!(state->sessions = ao2_container_alloc(SESSION_BUCKETS, jingle_session_hash, jingle_session_cmp))) {
      ao2_ref(state, -1);
      return NULL;
   }

   return state;
}
static void jingle_endpoint_state_destructor ( void *  obj) [static]

Destructor for Jingle endpoint state.

Definition at line 290 of file chan_motif.c.

References ao2_ref, and jingle_endpoint_state::sessions.

Referenced by jingle_endpoint_state_create().

{
   struct jingle_endpoint_state *state = obj;

   ao2_ref(state->sessions, -1);
}
static struct jingle_endpoint_state* jingle_endpoint_state_find_or_create ( const char *  category) [static, read]

State find/create function.

Definition at line 341 of file chan_motif.c.

References ao2_cleanup, ao2_global_obj_ref, ao2_ref, globals, jingle_endpoint_find(), and jingle_endpoint_state_create().

Referenced by jingle_endpoint_alloc().

{
   RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
   RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);

   if (!cfg || !cfg->endpoints || !(endpoint = jingle_endpoint_find(cfg->endpoints, category))) {
      return jingle_endpoint_state_create();
   }

   ao2_ref(endpoint->state, +1);
   return endpoint->state;
}
static int jingle_fixup ( struct ast_channel oldchan,
struct ast_channel newchan 
) [static]

Function called by core to change the underlying owner channel.

Definition at line 1562 of file chan_motif.c.

References ao2_lock, ao2_unlock, ast_channel_tech_pvt(), and jingle_session::owner.

{
   struct jingle_session *session = ast_channel_tech_pvt(newchan);

   ao2_lock(session);

   session->owner = newchan;

   ao2_unlock(session);

   return 0;
}
static void jingle_get_codec ( struct ast_channel chan,
struct ast_format_cap result 
) [static]

Function called by RTP engine to get peer capabilities.

Definition at line 494 of file chan_motif.c.

{
}
static enum ast_rtp_glue_result jingle_get_rtp_peer ( struct ast_channel chan,
struct ast_rtp_instance **  instance 
) [static]

Function called by RTP engine to get local RTP peer.

Definition at line 478 of file chan_motif.c.

References ao2_ref, ast_channel_tech_pvt(), AST_RTP_GLUE_RESULT_FORBID, AST_RTP_GLUE_RESULT_LOCAL, and jingle_session::rtp.

{
   struct jingle_session *session = ast_channel_tech_pvt(chan);
   enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL;

   if (!session->rtp) {
      return AST_RTP_GLUE_RESULT_FORBID;
   }

   ao2_ref(session->rtp, +1);
   *instance = session->rtp;

   return res;
}
static int jingle_hangup ( struct ast_channel ast) [static]

Function called by core to hang up a Jingle session.

Definition at line 1695 of file chan_motif.c.

References ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ARRAY_LEN, AST_CAUSE_CONGESTION, ast_channel_hangupcause(), ast_channel_tech_pvt(), ast_channel_tech_pvt_set(), AST_STATE_DOWN, jingle_session::gone, jingle_reason_mappings, jingle_send_session_terminate(), jingle_session::owner, jingle_reason_mapping::reason, jingle_endpoint_state::sessions, and jingle_session::state.

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);

   ao2_lock(session);

   if ((ast_channel_state(ast) != AST_STATE_DOWN) && !session->gone) {
      int cause = (session->owner ? ast_channel_hangupcause(session->owner) : AST_CAUSE_CONGESTION);
      const char *reason = "success";
      int i;

      /* Get the appropriate reason and send a session-terminate */
      for (i = 0; i < ARRAY_LEN(jingle_reason_mappings); i++) {
         if (jingle_reason_mappings[i].cause == cause) {
            reason = jingle_reason_mappings[i].reason;
            break;
         }
      }

      jingle_send_session_terminate(session, reason);
   }

   ast_channel_tech_pvt_set(ast, NULL);
   session->owner = NULL;

   ao2_unlink(session->state->sessions, session);
   ao2_ref(session->state, -1);

   ao2_unlock(session);
   ao2_ref(session, -1);

   return 0;
}
static int jingle_indicate ( struct ast_channel ast,
int  condition,
const void *  data,
size_t  datalen 
) [static]

Function called by core to ask the channel to indicate some sort of condition.

Definition at line 1576 of file chan_motif.c.

References AST_CAUSE_BUSY, AST_CAUSE_CONGESTION, ast_channel_hangupcause_set(), ast_channel_tech_pvt(), AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_HOLD, AST_CONTROL_INCOMPLETE, AST_CONTROL_PVT_CAUSE_CODE, AST_CONTROL_RINGING, AST_CONTROL_SRCCHANGE, AST_CONTROL_SRCUPDATE, AST_CONTROL_UNHOLD, AST_CONTROL_UPDATE_RTP_PEER, AST_CONTROL_VIDUPDATE, ast_log(), ast_moh_start(), ast_moh_stop(), ast_rtp_instance_change_source(), ast_rtp_instance_update_source(), AST_SOFTHANGUP_DEV, ast_softhangup_nolock(), AST_STATE_RING, AST_STATE_UP, jingle_send_session_info(), LOG_NOTICE, and jingle_session::rtp.

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);
   int res = 0;

   switch (condition) {
   case AST_CONTROL_RINGING:
      if (ast_channel_state(ast) == AST_STATE_RING) {
         jingle_send_session_info(session, "ringing xmlns='urn:xmpp:jingle:apps:rtp:info:1'");
      } else {
         res = -1;
      }
      break;
   case AST_CONTROL_BUSY:
      if (ast_channel_state(ast) != AST_STATE_UP) {
         ast_channel_hangupcause_set(ast, AST_CAUSE_BUSY);
         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
      } else {
         res = -1;
      }
      break;
   case AST_CONTROL_CONGESTION:
      if (ast_channel_state(ast) != AST_STATE_UP) {
         ast_channel_hangupcause_set(ast, AST_CAUSE_CONGESTION);
         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
      } else {
         res = -1;
      }
      break;
   case AST_CONTROL_INCOMPLETE:
      if (ast_channel_state(ast) != AST_STATE_UP) {
         ast_channel_hangupcause_set(ast, AST_CAUSE_CONGESTION);
         ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
      }
      break;
   case AST_CONTROL_HOLD:
      ast_moh_start(ast, data, NULL);
      break;
   case AST_CONTROL_UNHOLD:
      ast_moh_stop(ast);
      break;
   case AST_CONTROL_SRCUPDATE:
      if (session->rtp) {
         ast_rtp_instance_update_source(session->rtp);
      }
      break;
   case AST_CONTROL_SRCCHANGE:
      if (session->rtp) {
         ast_rtp_instance_change_source(session->rtp);
      }
      break;
   case AST_CONTROL_VIDUPDATE:
   case AST_CONTROL_UPDATE_RTP_PEER:
   case AST_CONTROL_CONNECTED_LINE:
      break;
   case AST_CONTROL_PVT_CAUSE_CODE:
   case -1:
      res = -1;
      break;
   default:
      ast_log(LOG_NOTICE, "Don't know how to indicate condition '%d'\n", condition);
      res = -1;
   }

   return res;
}
static int jingle_interpret_content ( struct jingle_session session,
ikspak *  pak 
) [static]

Helper function which locates content stanzas and interprets them.

Note:
The session *must not* be locked before calling this

Definition at line 2106 of file chan_motif.c.

References ao2_unlock, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, AST_CAUSE_FACILITY_NOT_IMPLEMENTED, AST_CAUSE_SWITCH_CONGESTION, ast_channel_nativeformats(), ast_channel_unlock, ast_channel_unref, ast_codec_choose(), ast_format_cap_copy(), ast_log(), ast_set_read_format(), ast_set_write_format(), ast_strlen_zero(), jingle_session::audio_name, GOOGLE_PHONE_NS, GOOGLE_SESSION_NS, GOOGLE_TRANSPORT_NS, GOOGLE_VIDEO_NS, JINGLE_ICE_UDP_NS, jingle_interpret_description(), jingle_interpret_google_transport(), jingle_interpret_ice_udp_transport(), jingle_queue_hangup_with_cause(), JINGLE_RTP_NS, jingle_session_lock_full(), JINGLE_TRANSPORT_GOOGLE_V1, jingle_session::jointcap, LOG_ERROR, name, jingle_session::prefs, jingle_session::rtp, jingle_session::sid, jingle_session::transport, jingle_session::video_name, and jingle_session::vrtp.

Referenced by jingle_action_session_accept(), jingle_action_session_initiate(), and jingle_action_transport_info().

{
   iks *content;
   unsigned int changed = 0;
   struct ast_channel *chan;

   /* Look at the content in the session initiation */
   for (content = iks_child(iks_child(pak->x)); content; content = iks_next(content)) {
      char *name;
      struct ast_rtp_instance *rtp = NULL;
      iks *description, *transport;

      /* Ignore specific parts if they are known not to be useful */
      if (!strcmp(iks_name(content), "conference-info")) {
         continue;
      }

      name = iks_find_attrib(content, "name");

      if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
         /* If this content stanza has no name consider it invalid and move on */
         if (ast_strlen_zero(name) && !(name = iks_find_attrib(content, "jin:name"))) {
            jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
            ast_log(LOG_ERROR, "Received content without a name on session '%s'\n", session->sid);
            return -1;
         }

         /* Try to pre-populate which RTP instance this content is relevant to */
         if (!strcmp(session->audio_name, name)) {
            rtp = session->rtp;
         } else if (!strcmp(session->video_name, name)) {
            rtp = session->vrtp;
         }
      } else {
         /* Google-V1 has no concept of assocating things like the above does, so since we only support audio over it assume they want audio */
         rtp = session->rtp;
      }

      /* If description information is available use it */
      if ((description = iks_find_with_attrib(content, "description", "xmlns", JINGLE_RTP_NS)) ||
          (description = iks_find_with_attrib(content, "rtp:description", "xmlns:rtp", JINGLE_RTP_NS)) ||
          (description = iks_find_with_attrib(content, "pho:description", "xmlns:pho", GOOGLE_PHONE_NS)) ||
          (description = iks_find_with_attrib(pak->query, "description", "xmlns", GOOGLE_PHONE_NS)) ||
          (description = iks_find_with_attrib(pak->query, "pho:description", "xmlns:pho", GOOGLE_PHONE_NS)) ||
          (description = iks_find_with_attrib(pak->query, "vid:description", "xmlns", GOOGLE_VIDEO_NS))) {
         /* If we failed to do something with the content description abort immediately */
         if (jingle_interpret_description(session, description, name, &rtp)) {
            return -1;
         }

         /* If we successfully interpret the description then the codecs need updating */
         changed = 1;
      }

      /* If we get past the description handling and we still don't know what RTP instance this is for... it is unknown content */
      if (!rtp) {
         ast_log(LOG_ERROR, "Received a content stanza but have no RTP instance for it on session '%s'\n", session->sid);
         jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
         return -1;
      }

      /* If ICE UDP transport information is available use it */
      if ((transport = iks_find_with_attrib(content, "transport", "xmlns", JINGLE_ICE_UDP_NS))) {
         if (jingle_interpret_ice_udp_transport(session, transport, rtp)) {
            return -1;
         }
      } else if ((transport = iks_find_with_attrib(content, "transport", "xmlns", GOOGLE_TRANSPORT_NS)) ||
            (transport = iks_find_with_attrib(content, "p:transport", "xmlns:p", GOOGLE_TRANSPORT_NS)) ||
            (transport = iks_find_with_attrib(pak->x, "session", "xmlns", GOOGLE_SESSION_NS)) ||
            (transport = iks_find_with_attrib(pak->x, "ses:session", "xmlns:ses", GOOGLE_SESSION_NS))) {
         /* If Google transport support is available use it */
         if (jingle_interpret_google_transport(session, transport, rtp)) {
            return -1;
         }
      } else if (iks_find(content, "transport")) {
         /* If this is a transport we do not support terminate the session as it probably won't work out in the end */
         jingle_queue_hangup_with_cause(session, AST_CAUSE_FACILITY_NOT_IMPLEMENTED);
         ast_log(LOG_ERROR, "Unsupported transport type received on session '%s'\n", session->sid);
         return -1;
      }
   }

   if (!changed) {
      return 0;
   }

   if ((chan = jingle_session_lock_full(session))) {
      struct ast_format fmt;

      ast_format_cap_copy(ast_channel_nativeformats(chan), session->jointcap);
      ast_codec_choose(&session->prefs, session->jointcap, 1, &fmt);
      ast_set_read_format(chan, &fmt);
      ast_set_write_format(chan, &fmt);

      ast_channel_unlock(chan);
      ast_channel_unref(chan);
   }
   ao2_unlock(session);

   return 0;
}
static int jingle_interpret_description ( struct jingle_session session,
iks *  description,
const char *  name,
struct ast_rtp_instance **  rtp 
) [static]

Helper function which handles content descriptions.

Definition at line 1859 of file chan_motif.c.

References AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, ast_format_cap_is_empty(), ast_format_cap_joint_append(), ast_format_cap_remove_bytype(), AST_FORMAT_TYPE_AUDIO, AST_FORMAT_TYPE_VIDEO, ast_log(), ast_rtp_codecs_payload_formats(), ast_rtp_codecs_payloads_copy(), ast_rtp_codecs_payloads_destroy(), ast_rtp_codecs_payloads_initialize(), ast_rtp_codecs_payloads_set_m_type(), ast_rtp_codecs_payloads_set_rtpmap_type(), ast_rtp_codecs_payloads_set_rtpmap_type_rate(), ast_rtp_instance_get_codecs(), ast_string_field_set, ast_strlen_zero(), jingle_session::cap, jingle_enable_video(), jingle_queue_hangup_with_cause(), JINGLE_TRANSPORT_GOOGLE_V1, jingle_session::jointcap, LOG_ERROR, jingle_session::peercap, jingle_session::rtp, jingle_session::sid, jingle_session::transport, and jingle_session::vrtp.

Referenced by jingle_interpret_content().

{
   char *media = iks_find_attrib(description, "media");
   struct ast_rtp_codecs codecs;
   iks *codec;
   int othercapability = 0;

   /* Google-V1 is always carrying audio, but just doesn't tell us so */
   if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
      media = "audio";
   } else if (ast_strlen_zero(media)) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
      ast_log(LOG_ERROR, "Received a content description on session '%s' without a name\n", session->sid);
      return -1;
   }

   /* Determine the type of media that is being carried and update the RTP instance, as well as the name */
   if (!strcasecmp(media, "audio")) {
      if (!ast_strlen_zero(name)) {
         ast_string_field_set(session, audio_name, name);
      }
      *rtp = session->rtp;
      ast_format_cap_remove_bytype(session->peercap, AST_FORMAT_TYPE_AUDIO);
      ast_format_cap_remove_bytype(session->jointcap, AST_FORMAT_TYPE_AUDIO);
   } else if (!strcasecmp(media, "video")) {
      if (!ast_strlen_zero(name)) {
         ast_string_field_set(session, video_name, name);
      }

      jingle_enable_video(session);
      *rtp = session->vrtp;

      /* If video is not present cancel this session */
      if (!session->vrtp) {
         jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
         ast_log(LOG_ERROR, "Received a video content description on session '%s' but could not enable video\n", session->sid);
         return -1;
      }

      ast_format_cap_remove_bytype(session->peercap, AST_FORMAT_TYPE_VIDEO);
      ast_format_cap_remove_bytype(session->jointcap, AST_FORMAT_TYPE_VIDEO);
   } else {
      /* Unknown media type */
      jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
      ast_log(LOG_ERROR, "Unsupported media type '%s' received in content description on session '%s'\n", media, session->sid);
      return -1;
   }

   if (ast_rtp_codecs_payloads_initialize(&codecs)) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
      ast_log(LOG_ERROR, "Could not initialize codecs for negotiation on session '%s'\n", session->sid);
      return -1;
   }

   /* Iterate the codecs updating the relevant RTP instance as we go */
   for (codec = iks_child(description); codec; codec = iks_next(codec)) {
      char *id = iks_find_attrib(codec, "id"), *name = iks_find_attrib(codec, "name");
      char *clockrate = iks_find_attrib(codec, "clockrate");
      int rtp_id, rtp_clockrate;

      if (!ast_strlen_zero(id) && !ast_strlen_zero(name) && (sscanf(id, "%30d", &rtp_id) == 1)) {
         ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, rtp_id);

         if (!ast_strlen_zero(clockrate) && (sscanf(clockrate, "%30d", &rtp_clockrate) == 1)) {
            ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, rtp_id, media, name, 0, rtp_clockrate);
         } else {
            ast_rtp_codecs_payloads_set_rtpmap_type(&codecs, NULL, rtp_id, media, name, 0);
         }
      }
   }

   ast_rtp_codecs_payload_formats(&codecs, session->peercap, &othercapability);
   ast_format_cap_joint_append(session->cap, session->peercap, session->jointcap);

   if (ast_format_cap_is_empty(session->jointcap)) {
      /* We have no compatible codecs, so terminate the session appropriately */
      jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
      ast_rtp_codecs_payloads_destroy(&codecs);
      return -1;
   }

   ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(*rtp), *rtp);
   ast_rtp_codecs_payloads_destroy(&codecs);

   return 0;
}
static int jingle_interpret_google_transport ( struct jingle_session session,
iks *  transport,
struct ast_rtp_instance rtp 
) [static]

Helper function which handles Google transport information.

Definition at line 2028 of file chan_motif.c.

References AST_CAUSE_PROTOCOL_ERROR, AST_CAUSE_SWITCH_CONGESTION, ast_log(), ast_rtp_instance_get_ice(), ast_rtp_instance_stun_request(), ast_sockaddr_parse(), ast_sockaddr_set_port, ast_strlen_zero(), ast_rtp_engine_ice::get_ufrag, jingle_queue_hangup_with_cause(), JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, LOG_ERROR, name, PARSE_PORT_FORBID, jingle_session::sid, ast_rtp_engine_ice::stop, and jingle_session::transport.

Referenced by jingle_interpret_content().

{
   struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
   iks *candidate;

   if (!ice) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
      ast_log(LOG_ERROR, "Received Google transport information on session '%s' but ICE support not available\n", session->sid);
      return -1;
   }

   /* If this session has not transitioned to the Google transport do so now */
   if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V2) &&
       (session->transport != JINGLE_TRANSPORT_GOOGLE_V1)) {
      /* Stop built-in ICE support... we need to fall back to the old old old STUN */
      ice->stop(rtp);

      session->transport = JINGLE_TRANSPORT_GOOGLE_V2;
   }

   for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
      char *address = iks_find_attrib(candidate, "address"), *port = iks_find_attrib(candidate, "port");
      char *username = iks_find_attrib(candidate, "username"), *name = iks_find_attrib(candidate, "name");
      char *protocol = iks_find_attrib(candidate, "protocol");
      int real_port;
      struct ast_sockaddr target = { { 0, } };
      /* In Google land the combined value is 32 bytes */
      char combined[33] = "";

      /* If this is NOT actually a candidate just skip it */
      if (strcasecmp(iks_name(candidate), "candidate") &&
          strcasecmp(iks_name(candidate), "p:candidate") &&
          strcasecmp(iks_name(candidate), "ses:candidate")) {
         continue;
      }

      /* If this candidate is incomplete skip it */
      if (ast_strlen_zero(address) || ast_strlen_zero(port) || ast_strlen_zero(username) ||
          ast_strlen_zero(name)) {
         jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
         ast_log(LOG_ERROR, "Incomplete Google candidate received on session '%s'\n", session->sid);
         return -1;
      }

      /* We only support UDP so skip any other protocols */
      if (!ast_strlen_zero(protocol) && strcasecmp(protocol, "udp")) {
         continue;
      }

      /* We only permit audio and video, not RTCP */
      if (strcasecmp(name, "rtp") && strcasecmp(name, "video_rtp")) {
         continue;
      }

      /* Parse the target information so we can send a STUN request to the candidate */
      if (sscanf(port, "%30d", &real_port) != 1) {
         jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
         ast_log(LOG_ERROR, "Invalid Google candidate port '%s' received on session '%s'\n", port, session->sid);
         return -1;
      }
      ast_sockaddr_parse(&target, address, PARSE_PORT_FORBID);
      ast_sockaddr_set_port(&target, real_port);

      /* Per the STUN support Google talk uses combine the two usernames */
      snprintf(combined, sizeof(combined), "%s%s", username, ice->get_ufrag(rtp));

      /* This should appease the masses... we will actually change the remote address when we get their STUN packet */
      ast_rtp_instance_stun_request(rtp, &target, combined);
   }

   return 0;
}
static int jingle_interpret_ice_udp_transport ( struct jingle_session session,
iks *  transport,
struct ast_rtp_instance rtp 
) [static]

Helper function which handles ICE-UDP transport information.

Definition at line 1947 of file chan_motif.c.

References ast_rtp_engine_ice::add_remote_candidate, ast_rtp_engine_ice_candidate::address, AST_CAUSE_PROTOCOL_ERROR, AST_CAUSE_SWITCH_CONGESTION, ast_log(), AST_RTP_ICE_CANDIDATE_TYPE_HOST, AST_RTP_ICE_CANDIDATE_TYPE_RELAYED, AST_RTP_ICE_CANDIDATE_TYPE_SRFLX, ast_rtp_instance_get_ice(), ast_rtp_instance_get_remote_address(), ast_rtp_instance_set_remote_address(), ast_sockaddr_is_ipv4(), ast_sockaddr_isnull(), ast_sockaddr_parse(), ast_sockaddr_set_port, ast_strlen_zero(), ast_rtp_engine_ice_candidate::foundation, ast_rtp_engine_ice_candidate::id, jingle_queue_hangup_with_cause(), LOG_ERROR, PARSE_PORT_FORBID, ast_rtp_engine_ice_candidate::priority, ast_rtp_engine_ice::set_authentication, jingle_session::sid, ast_rtp_engine_ice::start, ast_rtp_engine_ice_candidate::transport, type, and ast_rtp_engine_ice_candidate::type.

Referenced by jingle_interpret_content().

{
   struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
   char *ufrag = iks_find_attrib(transport, "ufrag"), *pwd = iks_find_attrib(transport, "pwd");
   iks *candidate;

   if (!ice) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
      ast_log(LOG_ERROR, "Received ICE-UDP transport information on session '%s' but ICE support not available\n", session->sid);
      return -1;
   }

   if (!ast_strlen_zero(ufrag) && !ast_strlen_zero(pwd)) {
      ice->set_authentication(rtp, ufrag, pwd);
   }

   for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
      char *component = iks_find_attrib(candidate, "component"), *foundation = iks_find_attrib(candidate, "foundation");
      char *generation = iks_find_attrib(candidate, "generation"), *id = iks_find_attrib(candidate, "id");
      char *ip = iks_find_attrib(candidate, "ip"), *port = iks_find_attrib(candidate, "port");
      char *priority = iks_find_attrib(candidate, "priority"), *protocol = iks_find_attrib(candidate, "protocol");
      char *type = iks_find_attrib(candidate, "type");
      struct ast_rtp_engine_ice_candidate local_candidate = { 0, };
      int real_port;
      struct ast_sockaddr remote_address = { { 0, } };

      /* If this candidate is incomplete skip it */
      if (ast_strlen_zero(component) || ast_strlen_zero(foundation) || ast_strlen_zero(generation) || ast_strlen_zero(id) ||
          ast_strlen_zero(ip) || ast_strlen_zero(port) || ast_strlen_zero(priority) ||
          ast_strlen_zero(protocol) || ast_strlen_zero(type)) {
         jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
         ast_log(LOG_ERROR, "Incomplete ICE-UDP candidate received on session '%s'\n", session->sid);
         return -1;
      }

      if ((sscanf(component, "%30u", &local_candidate.id) != 1) ||
          (sscanf(priority, "%30u", &local_candidate.priority) != 1) ||
          (sscanf(port, "%30d", &real_port) != 1)) {
         jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
         ast_log(LOG_ERROR, "Invalid ICE-UDP candidate information received on session '%s'\n", session->sid);
         return -1;
      }

      local_candidate.foundation = foundation;
      local_candidate.transport = protocol;

      ast_sockaddr_parse(&local_candidate.address, ip, PARSE_PORT_FORBID);

      /* We only support IPv4 right now */
      if (!ast_sockaddr_is_ipv4(&local_candidate.address)) {
         continue;
      }

      ast_sockaddr_set_port(&local_candidate.address, real_port);

      if (!strcasecmp(type, "host")) {
         local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
      } else if (!strcasecmp(type, "srflx")) {
         local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
      } else if (!strcasecmp(type, "relay")) {
         local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
      } else {
         continue;
      }

      /* Worst case use the first viable address */
      ast_rtp_instance_get_remote_address(rtp, &remote_address);

      if (ast_sockaddr_is_ipv4(&local_candidate.address) && ast_sockaddr_isnull(&remote_address)) {
         ast_rtp_instance_set_remote_address(rtp, &local_candidate.address);
      }

      ice->add_remote_candidate(rtp, &local_candidate);
   }

   ice->start(rtp);

   return 0;
}
static struct ast_channel* jingle_new ( struct jingle_endpoint endpoint,
struct jingle_session session,
int  state,
const char *  title,
const char *  linkedid,
const char *  cid_name 
) [static, read]

Function called to create a new Jingle Asterisk channel.

Definition at line 615 of file chan_motif.c.

References jingle_endpoint::accountcode, ao2_lock, ao2_unlock, AST_ADSI_UNAVAILABLE, ast_best_codec(), ast_channel_adsicpe_set(), ast_channel_alloc(), ast_channel_callgroup_set(), ast_channel_callid_set(), ast_channel_context_set(), ast_channel_exten_set(), ast_channel_nativeformats(), ast_channel_pickupgroup_set(), ast_channel_priority_set(), ast_channel_rawreadformat(), ast_channel_rawwriteformat(), ast_channel_readformat(), ast_channel_rings_set(), ast_channel_set_fd(), ast_channel_tech_pvt_set(), ast_channel_tech_set(), ast_channel_writeformat(), ast_codec_choose(), ast_exists_extension(), ast_format_cap_copy(), ast_format_cap_is_empty(), ast_format_copy(), ast_random(), ast_rtp_codecs_packetization_set(), ast_rtp_instance_fd(), ast_rtp_instance_get_codecs(), ast_rtp_instance_get_ice(), AST_STATE_RING, ast_strlen_zero(), jingle_endpoint::callgroup, jingle_session::callid, jingle_session::cap, jingle_endpoint::context, JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, jingle_endpoint::language, jingle_endpoint::musicclass, jingle_endpoint::name, jingle_session::owner, jingle_endpoint::pickupgroup, jingle_session::prefs, jingle_session::remote, jingle_session::rtp, S_OR, ast_rtp_engine_ice::stop, str, and jingle_session::transport.

Referenced by jingle_action_session_initiate(), and jingle_request().

{
   struct ast_channel *chan;
   const char *str = S_OR(title, session->remote);
   struct ast_format tmpfmt;

   if (ast_format_cap_is_empty(session->cap)) {
      return NULL;
   }

   if (!(chan = ast_channel_alloc(1, state, S_OR(title, ""), S_OR(cid_name, ""), "", "", "", linkedid, 0, "Motif/%s-%04lx", str, ast_random() & 0xffff))) {
      return NULL;
   }

   ast_channel_tech_set(chan, &jingle_tech);
   ast_channel_tech_pvt_set(chan, session);
   session->owner = chan;

   ast_channel_callid_set(chan, session->callid);

   ast_format_cap_copy(ast_channel_nativeformats(chan), session->cap);
   ast_codec_choose(&session->prefs, session->cap, 1, &tmpfmt);

   if (session->rtp) {
      struct ast_rtp_engine_ice *ice;

      ast_channel_set_fd(chan, 0, ast_rtp_instance_fd(session->rtp, 0));
      ast_channel_set_fd(chan, 1, ast_rtp_instance_fd(session->rtp, 1));
      ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session->rtp), session->rtp, &session->prefs);

      if (((session->transport == JINGLE_TRANSPORT_GOOGLE_V2) ||
           (session->transport == JINGLE_TRANSPORT_GOOGLE_V1)) &&
          (ice = ast_rtp_instance_get_ice(session->rtp))) {
         /* We stop built in ICE support because we need to fall back to old old old STUN support */
         ice->stop(session->rtp);
      }
   }

   if (state == AST_STATE_RING) {
      ast_channel_rings_set(chan, 1);
   }

   ast_channel_adsicpe_set(chan, AST_ADSI_UNAVAILABLE);

   ast_best_codec(ast_channel_nativeformats(chan), &tmpfmt);
   ast_format_copy(ast_channel_writeformat(chan), &tmpfmt);
   ast_format_copy(ast_channel_rawwriteformat(chan), &tmpfmt);
   ast_format_copy(ast_channel_readformat(chan), &tmpfmt);
   ast_format_copy(ast_channel_rawreadformat(chan), &tmpfmt);

   ao2_lock(endpoint);

   ast_channel_callgroup_set(chan, endpoint->callgroup);
   ast_channel_pickupgroup_set(chan, endpoint->pickupgroup);

   if (!ast_strlen_zero(endpoint->accountcode)) {
      ast_channel_accountcode_set(chan, endpoint->accountcode);
   }

   if (!ast_strlen_zero(endpoint->language)) {
      ast_channel_language_set(chan, endpoint->language);
   }

   if (!ast_strlen_zero(endpoint->musicclass)) {
      ast_channel_musicclass_set(chan, endpoint->musicclass);
   }

   ast_channel_context_set(chan, endpoint->context);
   if (ast_exists_extension(NULL, endpoint->context, endpoint->name, 1, NULL)) {
      ast_channel_exten_set(chan, endpoint->name);
   } else {
      ast_channel_exten_set(chan, "s");
   }
   ast_channel_priority_set(chan, 1);

   ao2_unlock(endpoint);

   return chan;
}
static int jingle_outgoing_hook ( void *  data,
ikspak *  pak 
) [static]

Callback for when a response is received for an outgoing session-initiate message.

Definition at line 1359 of file chan_motif.c.

References ao2_unlock, ast_callid_threadassoc_add(), ast_callid_threadassoc_remove(), AST_CAUSE_CONGESTION, AST_CAUSE_FACILITY_NOT_IMPLEMENTED, AST_CAUSE_NO_ROUTE_DESTINATION, AST_CAUSE_PROTOCOL_ERROR, AST_CAUSE_REQUESTED_CHAN_UNAVAIL, ast_channel_unlock, ast_channel_unref, AST_CONTROL_PROCEEDING, ast_copy_string(), ast_queue_control(), ast_rtp_instance_get_ice(), ast_strlen_zero(), jingle_session::callid, jingle_session::connection, ast_xmpp_client::filter, jingle_session::gone, jingle_queue_hangup_with_cause(), jingle_send_session_initiate(), jingle_send_transport_info(), jingle_session_lock_full(), JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, JINGLE_TRANSPORT_NONE, ast_xmpp_client::mid, jingle_session::remote, jingle_session::remote_original, jingle_session::rtp, jingle_session::rule, ast_rtp_engine_ice::stop, jingle_session::transport, and XMPP_STANZAS_NS.

Referenced by jingle_call().

{
   struct jingle_session *session = data;
   iks *error = iks_find(pak->x, "error"), *redirect;

   /* In all cases this hook is done with */
   iks_filter_remove_rule(session->connection->filter, session->rule);
   session->rule = NULL;

   ast_callid_threadassoc_add(session->callid);

   /* If no error occurred they accepted our session-initiate message happily */
   if (!error) {
      struct ast_channel *chan;

      if ((chan = jingle_session_lock_full(session))) {
         ast_queue_control(chan, AST_CONTROL_PROCEEDING);
         ast_channel_unlock(chan);
         ast_channel_unref(chan);
      }
      ao2_unlock(session);

      jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));

      goto end;
   }

   /* Assume that because this is an error the session is gone, there is only one case where this is incorrect - a redirect */
   session->gone = 1;

   /* Map the error we received to an appropriate cause code and hang up the channel */
   if ((redirect = iks_find_with_attrib(error, "redirect", "xmlns", XMPP_STANZAS_NS))) {
      iks *to = iks_child(redirect);
      char *target;

      if (to && (target = iks_name(to)) && !ast_strlen_zero(target)) {
         /* Make the xmpp: go away if it is present */
         if (!strncmp(target, "xmpp:", 5)) {
            target += 5;
         }

         /* This is actually a fairly simple operation - we update the remote and send another session-initiate */
         ast_copy_string(session->remote, target, sizeof(session->remote));

         /* Add a new hook so we can get the status of redirected session */
         session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
                         IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);

         jingle_send_session_initiate(session);

         session->gone = 0;
      } else {
         jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
      }
   } else if (iks_find_with_attrib(error, "service-unavailable", "xmlns", XMPP_STANZAS_NS)) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_CONGESTION);
   } else if (iks_find_with_attrib(error, "resource-constraint", "xmlns", XMPP_STANZAS_NS)) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_REQUESTED_CHAN_UNAVAIL);
   } else if (iks_find_with_attrib(error, "bad-request", "xmlns", XMPP_STANZAS_NS)) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
   } else if (iks_find_with_attrib(error, "remote-server-not-found", "xmlns", XMPP_STANZAS_NS)) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_NO_ROUTE_DESTINATION);
   } else if (iks_find_with_attrib(error, "feature-not-implemented", "xmlns", XMPP_STANZAS_NS)) {
      /* Assume that this occurred because the remote side does not support our transport, so drop it down one and try again */
      session->transport--;

      /* If we still have a viable transport mechanism re-send the session-initiate */
      if (session->transport != JINGLE_TRANSPORT_NONE) {
         struct ast_rtp_engine_ice *ice;

         if (((session->transport == JINGLE_TRANSPORT_GOOGLE_V2) ||
              (session->transport == JINGLE_TRANSPORT_GOOGLE_V1)) &&
             (ice = ast_rtp_instance_get_ice(session->rtp))) {
            /* We stop built in ICE support because we need to fall back to old old old STUN support */
            ice->stop(session->rtp);
         }

         /* Re-send the message to the *original* target and not a redirected one */
         ast_copy_string(session->remote, session->remote_original, sizeof(session->remote));

         session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
                         IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);

         jingle_send_session_initiate(session);

         session->gone = 0;
      } else {
         /* Otherwise we have exhausted all transports */
         jingle_queue_hangup_with_cause(session, AST_CAUSE_FACILITY_NOT_IMPLEMENTED);
      }
   } else {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
   }

end:
   ast_callid_threadassoc_remove();

   return IKS_FILTER_EAT;
}
static void jingle_queue_hangup_with_cause ( struct jingle_session session,
int  cause 
) [static]
static struct ast_frame * jingle_read ( struct ast_channel ast) [static, read]

Function called by core to read any waiting frames.

Definition at line 1475 of file chan_motif.c.

References ast_channel_fdno(), ast_channel_name(), ast_channel_nativeformats(), ast_channel_readformat(), ast_channel_tech_pvt(), ast_channel_writeformat(), ast_debug, ast_format_cap_add(), ast_format_cap_iscompatible(), ast_format_cap_remove_bytype(), AST_FORMAT_TYPE_AUDIO, AST_FRAME_VOICE, ast_frfree, ast_getformatname(), ast_null_frame, ast_rtp_instance_read(), ast_set_read_format(), ast_set_write_format(), ast_frame_subclass::format, ast_frame::frametype, jingle_session::jointcap, jingle_session::rtp, ast_frame::subclass, and jingle_session::vrtp.

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);
   struct ast_frame *frame = &ast_null_frame;

   switch (ast_channel_fdno(ast)) {
   case 0:
      if (session->rtp) {
         frame = ast_rtp_instance_read(session->rtp, 0);
      }
      break;
   case 1:
      if (session->rtp) {
         frame = ast_rtp_instance_read(session->rtp, 1);
      }
      break;
   case 2:
      if (session->vrtp) {
         frame = ast_rtp_instance_read(session->vrtp, 0);
      }
      break;
   case 3:
      if (session->vrtp) {
         frame = ast_rtp_instance_read(session->vrtp, 1);
      }
      break;
   default:
      break;
   }

   if (frame && frame->frametype == AST_FRAME_VOICE &&
       !ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format)) {
      if (!ast_format_cap_iscompatible(session->jointcap, &frame->subclass.format)) {
         ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
              ast_getformatname(&frame->subclass.format), ast_channel_name(ast));
         ast_frfree(frame);
         frame = &ast_null_frame;
      } else {
         ast_debug(1, "Oooh, format changed to %s\n",
              ast_getformatname(&frame->subclass.format));
         ast_format_cap_remove_bytype(ast_channel_nativeformats(ast), AST_FORMAT_TYPE_AUDIO);
         ast_format_cap_add(ast_channel_nativeformats(ast), &frame->subclass.format);
         ast_set_read_format(ast, ast_channel_readformat(ast));
         ast_set_write_format(ast, ast_channel_writeformat(ast));
      }
   }

   return frame;
}
static struct ast_channel * jingle_request ( const char *  type,
struct ast_format_cap cap,
const struct ast_channel requestor,
const char *  data,
int *  cause 
) [static, read]

Function called by core to create a new outgoing Jingle session.

Definition at line 1730 of file chan_motif.c.

References ao2_cleanup, ao2_find, ao2_global_obj_ref, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_lock, ao2_ref, ao2_unlock, args, AST_APP_ARG, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, AST_CAUSE_CHANNEL_UNACCEPTABLE, AST_CAUSE_SWITCH_CONGESTION, ast_channel_linkedid(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_format_cap_has_type(), AST_FORMAT_TYPE_AUDIO, AST_FORMAT_TYPE_VIDEO, ast_log(), AST_NONSTANDARD_APP_ARGS, ast_rtp_instance_get_ice(), AST_STATE_DOWN, ast_strlen_zero(), ast_xmpp_resource::caps, ast_channel::dialed, globals, ast_xmpp_capabilities::google, ast_rtp_engine_ice::ice_lite, ast_xmpp_capabilities::jingle, jingle_alloc(), jingle_enable_video(), jingle_endpoint_find(), jingle_new(), JINGLE_TRANSPORT_GOOGLE_V2, JINGLE_TRANSPORT_ICE_UDP, JINGLE_TRANSPORT_NONE, LOG_ERROR, name, OBJ_KEY, ast_xmpp_resource::resource, ast_xmpp_buddy::resources, jingle_session::rtp, jingle_session::transport, and jingle_session::vrtp.

{
   RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
   RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);
   char *dialed, target[200] = "";
   struct ast_xmpp_buddy *buddy;
   struct jingle_session *session;
   struct ast_channel *chan;
   enum jingle_transport transport = JINGLE_TRANSPORT_NONE;
   struct ast_rtp_engine_ice *ice;
   AST_DECLARE_APP_ARGS(args,
              AST_APP_ARG(name);
              AST_APP_ARG(target);
      );

   /* We require at a minimum one audio format to be requested */
   if (!ast_format_cap_has_type(cap, AST_FORMAT_TYPE_AUDIO)) {
      ast_log(LOG_ERROR, "Motif channel driver requires an audio format when dialing a destination\n");
      *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
      return NULL;
   }

   if (ast_strlen_zero(data) || !(dialed = ast_strdupa(data))) {
      ast_log(LOG_ERROR, "Unable to create channel with empty destination.\n");
      *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
      return NULL;
   }

   /* Parse the given dial string and validate the results */
   AST_NONSTANDARD_APP_ARGS(args, dialed, '/');

   if (ast_strlen_zero(args.name) || ast_strlen_zero(args.target)) {
      ast_log(LOG_ERROR, "Unable to determine endpoint name and target.\n");
      *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
      return NULL;
   }

   if (!(endpoint = jingle_endpoint_find(cfg->endpoints, args.name))) {
      ast_log(LOG_ERROR, "Endpoint '%s' does not exist.\n", args.name);
      *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
      return NULL;
   }

   ao2_lock(endpoint->state);

   /* If we don't have a connection for the endpoint we can't exactly start a session on it */
   if (!endpoint->connection) {
      ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s' as no valid connection exists\n", args.name);
      *cause = AST_CAUSE_SWITCH_CONGESTION;
      ao2_unlock(endpoint->state);
      return NULL;
   }

   /* Find the target in the roster so we can choose a resource */
   if ((buddy = ao2_find(endpoint->connection->buddies, args.target, OBJ_KEY))) {
      struct ao2_iterator res;
      struct ast_xmpp_resource *resource;

      /* Iterate through finding the first viable Jingle capable resource */
      res = ao2_iterator_init(buddy->resources, 0);
      while ((resource = ao2_iterator_next(&res))) {
         if (resource->caps.jingle) {
            snprintf(target, sizeof(target), "%s/%s", args.target, resource->resource);
            transport = JINGLE_TRANSPORT_ICE_UDP;
            break;
         } else if (resource->caps.google) {
            snprintf(target, sizeof(target), "%s/%s", args.target, resource->resource);
            transport = JINGLE_TRANSPORT_GOOGLE_V2;
            break;
         }
         ao2_ref(resource, -1);
      }
      ao2_iterator_destroy(&res);

      ao2_ref(buddy, -1);
   } else {
      /* If the target is NOT in the roster use the provided target as-is */
      ast_copy_string(target, args.target, sizeof(target));
   }

   ao2_unlock(endpoint->state);

   /* If no target was found we can't set up a session */
   if (ast_strlen_zero(target)) {
      ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s' as no capable resource for target '%s' was found\n", args.name, args.target);
      *cause = AST_CAUSE_SWITCH_CONGESTION;
      return NULL;
   }

   if (!(session = jingle_alloc(endpoint, target, NULL))) {
      ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s'\n", args.name);
      *cause = AST_CAUSE_SWITCH_CONGESTION;
      return NULL;
   }

   /* Update the transport if we learned what we should actually use */
   if (transport != JINGLE_TRANSPORT_NONE) {
      session->transport = transport;
      /* Note that for Google-V1 and Google-V2 we don't stop built-in ICE support, this will happen in jingle_new */
   }

   if (!(chan = jingle_new(endpoint, session, AST_STATE_DOWN, target, requestor ? ast_channel_linkedid(requestor) : NULL, NULL))) {
      ast_log(LOG_ERROR, "Unable to create Jingle channel on endpoint '%s'\n", args.name);
      *cause = AST_CAUSE_SWITCH_CONGESTION;
      ao2_ref(session, -1);
      return NULL;
   }

   /* If video was requested try to enable it on the session */
   if (ast_format_cap_has_type(cap, AST_FORMAT_TYPE_VIDEO)) {
      jingle_enable_video(session);
   }

   /* As this is outgoing set ourselves as controlling */
   if (session->rtp && (ice = ast_rtp_instance_get_ice(session->rtp))) {
      ice->ice_lite(session->rtp);
   }

   if (session->vrtp && (ice = ast_rtp_instance_get_ice(session->vrtp))) {
      ice->ice_lite(session->vrtp);
   }

   /* We purposely don't decrement the session here as there is a reference on the channel */
   ao2_link(endpoint->state->sessions, session);

   return chan;
}
static void jingle_send_error_response ( struct ast_xmpp_client connection,
ikspak *  pak,
const char *  type,
const char *  reasonstr,
const char *  reasonstr2 
) [static]

Internal helper function which sends an error response.

Definition at line 716 of file chan_motif.c.

References ast_log(), ast_strlen_zero(), ast_xmpp_client_send(), ast_xmpp_client::jid, and LOG_ERROR.

Referenced by jingle_action_session_accept(), jingle_action_session_info(), jingle_action_session_initiate(), jingle_action_session_terminate(), and jingle_action_transport_info().

{
   iks *response, *error = NULL, *reason = NULL, *reason2 = NULL;

   if (!(response = iks_new("iq")) ||
       !(error = iks_new("error")) ||
       !(reason = iks_new(reasonstr))) {
      ast_log(LOG_ERROR, "Unable to allocate IKS error response stanzas\n");
      goto end;
   }

   iks_insert_attrib(response, "type", "error");
   iks_insert_attrib(response, "from", connection->jid->full);
   iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
   iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));

   iks_insert_attrib(error, "type", type);
   iks_insert_node(error, reason);

   if (!ast_strlen_zero(reasonstr2) && (reason2 = iks_new(reasonstr2))) {
      iks_insert_node(error, reason2);
   }

   iks_insert_node(response, error);

   ast_xmpp_client_send(connection, response);
end:
   iks_delete(reason2);
   iks_delete(reason);
   iks_delete(error);
   iks_delete(response);
}
static void jingle_send_response ( struct ast_xmpp_client connection,
ikspak *  pak 
) [static]

Internal helper function which sends a response.

Definition at line 696 of file chan_motif.c.

References ast_log(), ast_xmpp_client_send(), ast_xmpp_client::jid, and LOG_ERROR.

Referenced by jingle_action_session_accept(), jingle_action_session_info(), jingle_action_session_initiate(), jingle_action_session_terminate(), and jingle_action_transport_info().

{
   iks *response;

   if (!(response = iks_new("iq"))) {
      ast_log(LOG_ERROR, "Unable to allocate an IKS response stanza\n");
      return;
   }

   iks_insert_attrib(response, "type", "result");
   iks_insert_attrib(response, "from", connection->jid->full);
   iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
   iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));

   ast_xmpp_client_send(connection, response);

   iks_delete(response);
}
static void jingle_send_session_accept ( struct jingle_session session) [static]

Internal function which sends a session-accept message.

Definition at line 1353 of file chan_motif.c.

References jingle_send_session_action(), JINGLE_TRANSPORT_GOOGLE_V1, and jingle_session::transport.

Referenced by jingle_answer().

{
   jingle_send_session_action(session, session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "accept" : "session-accept");
}
static void jingle_send_session_action ( struct jingle_session session,
const char *  action 
) [static]

Internal function which sends a complete session message.

Definition at line 1265 of file chan_motif.c.

References AST_CAUSE_SWITCH_CONGESTION, AST_FORMAT_TYPE_AUDIO, AST_FORMAT_TYPE_VIDEO, ast_log(), ast_xmpp_client_send(), ast_xmpp_increment_mid(), jingle_session::audio_name, jingle_session::connection, GOOGLE_SESSION_NS, ast_xmpp_client::jid, jingle_add_content(), JINGLE_NS, jingle_queue_hangup_with_cause(), JINGLE_TRANSPORT_GOOGLE_V1, LOG_ERROR, jingle_session::maxpayloads, ast_xmpp_client::mid, jingle_session::outgoing, jingle_session::remote, jingle_session::rtp, jingle_session::sid, jingle_session::transport, jingle_session::video_name, and jingle_session::vrtp.

Referenced by jingle_send_session_accept(), and jingle_send_session_initiate().

{
   iks *iq, *jingle, *audio = NULL, *audio_description = NULL, *video = NULL, *video_description = NULL;
   iks *audio_payloads[session->maxpayloads], *video_payloads[session->maxpayloads];
   iks *audio_transport = NULL, *video_transport = NULL;
   int i, res = 0;

   if (!(iq = iks_new("iq")) ||
       !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
      iks_delete(iq);
      return;
   }

   memset(audio_payloads, 0, sizeof(audio_payloads));
   memset(video_payloads, 0, sizeof(video_payloads));

   iks_insert_attrib(iq, "from", session->connection->jid->full);
   iks_insert_attrib(iq, "to", session->remote);
   iks_insert_attrib(iq, "type", "set");
   iks_insert_attrib(iq, "id", session->connection->mid);
   ast_xmpp_increment_mid(session->connection->mid);

   if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
      iks_insert_attrib(jingle, "type", action);
      iks_insert_attrib(jingle, "id", session->sid);
      iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
   } else {
      iks_insert_attrib(jingle, "action", action);
      iks_insert_attrib(jingle, "sid", session->sid);
      iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
   }

   if (!strcasecmp(action, "session-initiate") || !strcasecmp(action, "initiate") || !strcasecmp(action, "accept")) {
      iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : session->remote);
   }

   iks_insert_node(iq, jingle);

   if (session->rtp && (audio = iks_new("content")) && (audio_description = iks_new("description")) &&
       (audio_transport = iks_new("transport"))) {
      res = jingle_add_content(session, jingle, audio, audio_description, audio_transport, session->audio_name,
                AST_FORMAT_TYPE_AUDIO, session->rtp, audio_payloads);
   } else {
      ast_log(LOG_ERROR, "Failed to allocate audio content stanzas for session '%s', hanging up\n", session->sid);
      res = -1;
   }

   if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
      if ((video = iks_new("content")) && (video_description = iks_new("description")) &&
          (video_transport = iks_new("transport"))) {
         res = jingle_add_content(session, jingle, video, video_description, video_transport, session->video_name,
                   AST_FORMAT_TYPE_VIDEO, session->vrtp, video_payloads);
      } else {
         ast_log(LOG_ERROR, "Failed to allocate video content stanzas for session '%s', hanging up\n", session->sid);
         res = -1;
      }
   }

   if (!res) {
      ast_xmpp_client_send(session->connection, iq);
   } else {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
   }

   iks_delete(video_transport);
   iks_delete(audio_transport);

   for (i = 0; i < session->maxpayloads; i++) {
      iks_delete(video_payloads[i]);
      iks_delete(audio_payloads[i]);
   }

   iks_delete(video_description);
   iks_delete(video);
   iks_delete(audio_description);
   iks_delete(audio);
   iks_delete(jingle);
   iks_delete(iq);
}
static void jingle_send_session_info ( struct jingle_session session,
const char *  info 
) [static]

Internal function which sends a session-info message.

Definition at line 930 of file chan_motif.c.

References ast_log(), ast_xmpp_client_send(), ast_xmpp_increment_mid(), jingle_session::connection, JINGLE_NS, JINGLE_TRANSPORT_GOOGLE_V1, LOG_ERROR, ast_xmpp_client::mid, jingle_session::remote, jingle_session::sid, text, and jingle_session::transport.

Referenced by jingle_indicate().

{
   iks *iq = NULL, *jingle = NULL, *text = NULL;

   /* Google-V1 has no way to send informational messages so don't even bother trying */
   if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
      return;
   }

   if (!(iq = iks_new("iq")) || !(jingle = iks_new("jingle")) || !(text = iks_new(info))) {
      ast_log(LOG_ERROR, "Failed to allocate stanzas for session-info message on session '%s'\n", session->sid);
      goto end;
   }

   iks_insert_attrib(iq, "to", session->remote);
   iks_insert_attrib(iq, "type", "set");
   iks_insert_attrib(iq, "id", session->connection->mid);
   ast_xmpp_increment_mid(session->connection->mid);

   iks_insert_attrib(jingle, "action", "session-info");
   iks_insert_attrib(jingle, "sid", session->sid);
   iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
   iks_insert_node(iq, jingle);
   iks_insert_node(jingle, text);

   ast_xmpp_client_send(session->connection, iq);

end:
   iks_delete(text);
   iks_delete(jingle);
   iks_delete(iq);
}
static void jingle_send_session_initiate ( struct jingle_session session) [static]

Internal function which sends a session-inititate message.

Definition at line 1347 of file chan_motif.c.

References jingle_send_session_action(), JINGLE_TRANSPORT_GOOGLE_V1, and jingle_session::transport.

Referenced by jingle_call(), and jingle_outgoing_hook().

{
   jingle_send_session_action(session, session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "initiate" : "session-initiate");
}
static void jingle_send_session_terminate ( struct jingle_session session,
const char *  reasontext 
) [static]

Internal function which sends a session-terminate message.

Definition at line 890 of file chan_motif.c.

References ast_log(), ast_xmpp_client_send(), ast_xmpp_increment_mid(), jingle_session::connection, GOOGLE_SESSION_NS, ast_xmpp_client::jid, JINGLE_NS, JINGLE_TRANSPORT_GOOGLE_V1, LOG_ERROR, ast_xmpp_client::mid, jingle_session::outgoing, jingle_session::remote, jingle_session::sid, text, and jingle_session::transport.

Referenced by jingle_hangup().

{
   iks *iq = NULL, *jingle = NULL, *reason = NULL, *text = NULL;

   if (!(iq = iks_new("iq")) || !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle")) ||
       !(reason = iks_new("reason")) || !(text = iks_new(reasontext))) {
      ast_log(LOG_ERROR, "Failed to allocate stanzas for session-terminate message on session '%s'\n", session->sid);
      goto end;
   }

   iks_insert_attrib(iq, "to", session->remote);
   iks_insert_attrib(iq, "type", "set");
   iks_insert_attrib(iq, "id", session->connection->mid);
   ast_xmpp_increment_mid(session->connection->mid);

   if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
      iks_insert_attrib(jingle, "type", "terminate");
      iks_insert_attrib(jingle, "id", session->sid);
      iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
      iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : session->remote);
   } else {
      iks_insert_attrib(jingle, "action", "session-terminate");
      iks_insert_attrib(jingle, "sid", session->sid);
      iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
   }

   iks_insert_node(iq, jingle);
   iks_insert_node(jingle, reason);
   iks_insert_node(reason, text);

   ast_xmpp_client_send(session->connection, iq);

end:
   iks_delete(text);
   iks_delete(reason);
   iks_delete(jingle);
   iks_delete(iq);
}
static void jingle_send_transport_info ( struct jingle_session session,
const char *  from 
) [static]

Internal function which sends a transport-info message.

Definition at line 1043 of file chan_motif.c.

References AST_CAUSE_SWITCH_CONGESTION, ast_log(), ast_xmpp_client_send(), ast_xmpp_increment_mid(), jingle_session::audio_name, jingle_session::connection, GOOGLE_SESSION_NS, ast_xmpp_client::jid, jingle_add_google_candidates_to_transport(), jingle_add_ice_udp_candidates_to_transport(), JINGLE_NS, jingle_queue_hangup_with_cause(), JINGLE_TRANSPORT_GOOGLE_V1, JINGLE_TRANSPORT_GOOGLE_V2, JINGLE_TRANSPORT_ICE_UDP, LOG_ERROR, jingle_session::maxicecandidates, ast_xmpp_client::mid, jingle_session::outgoing, jingle_session::rtp, jingle_session::sid, jingle_session::transport, jingle_session::video_name, and jingle_session::vrtp.

Referenced by jingle_action_session_initiate(), and jingle_outgoing_hook().

{
   iks *iq, *jingle = NULL, *audio = NULL, *audio_transport = NULL, *video = NULL, *video_transport = NULL;
   iks *audio_candidates[session->maxicecandidates], *video_candidates[session->maxicecandidates];
   int i, res = 0;

   if (!(iq = iks_new("iq")) ||
       !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
      iks_delete(iq);
      jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
      ast_log(LOG_ERROR, "Failed to allocate stanzas for transport-info message, hanging up session '%s'\n", session->sid);
      return;
   }

   memset(audio_candidates, 0, sizeof(audio_candidates));
   memset(video_candidates, 0, sizeof(video_candidates));

   iks_insert_attrib(iq, "from", session->connection->jid->full);
   iks_insert_attrib(iq, "to", from);
   iks_insert_attrib(iq, "type", "set");
   iks_insert_attrib(iq, "id", session->connection->mid);
   ast_xmpp_increment_mid(session->connection->mid);

   if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
      iks_insert_attrib(jingle, "type", "candidates");
      iks_insert_attrib(jingle, "id", session->sid);
      iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
      iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : from);
   } else {
      iks_insert_attrib(jingle, "action", "transport-info");
      iks_insert_attrib(jingle, "sid", session->sid);
      iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
   }
   iks_insert_node(iq, jingle);

   if (session->rtp) {
      if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
         /* V1 protocol has the candidates directly in the session */
         res = jingle_add_google_candidates_to_transport(session->rtp, jingle, audio_candidates, 0, session->transport, session->maxicecandidates);
      } else if ((audio = iks_new("content")) && (audio_transport = iks_new("transport"))) {
         iks_insert_attrib(audio, "creator", session->outgoing ? "initiator" : "responder");
         iks_insert_attrib(audio, "name", session->audio_name);
         iks_insert_node(jingle, audio);
         iks_insert_node(audio, audio_transport);

         if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
            res = jingle_add_ice_udp_candidates_to_transport(session->rtp, audio_transport, audio_candidates, session->maxicecandidates);
         } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
            res = jingle_add_google_candidates_to_transport(session->rtp, audio_transport, audio_candidates, 0, session->transport,
                              session->maxicecandidates);
         }
      } else {
         res = -1;
      }
   }

   if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
      if ((video = iks_new("content")) && (video_transport = iks_new("transport"))) {
         iks_insert_attrib(video, "creator", session->outgoing ? "initiator" : "responder");
         iks_insert_attrib(video, "name", session->video_name);
         iks_insert_node(jingle, video);
         iks_insert_node(video, video_transport);

         if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
            res = jingle_add_ice_udp_candidates_to_transport(session->vrtp, video_transport, video_candidates, session->maxicecandidates);
         } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
            res = jingle_add_google_candidates_to_transport(session->vrtp, video_transport, video_candidates, 1, session->transport,
                              session->maxicecandidates);
         }
      } else {
         res = -1;
      }
   }

   if (!res) {
      ast_xmpp_client_send(session->connection, iq);
   } else {
      jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
   }

   /* Clean up after ourselves */
   for (i = 0; i < session->maxicecandidates; i++) {
      iks_delete(video_candidates[i]);
      iks_delete(audio_candidates[i]);
   }

   iks_delete(video_transport);
   iks_delete(video);
   iks_delete(audio_transport);
   iks_delete(audio);
   iks_delete(jingle);
   iks_delete(iq);
}
static int jingle_sendtext ( struct ast_channel ast,
const char *  text 
) [static]

Function called by core to send text to the remote party of the Jingle session.

Definition at line 1644 of file chan_motif.c.

References ast_channel_tech_pvt(), ast_xmpp_client_send_message(), jingle_session::connection, and jingle_session::remote.

{
   struct jingle_session *session = ast_channel_tech_pvt(chan);

   return ast_xmpp_client_send_message(session->connection, session->remote, text);
}
static int jingle_session_cmp ( void *  obj,
void *  arg,
int  flags 
) [static]

Comparator function for Jingle sessions.

Definition at line 281 of file chan_motif.c.

References CMP_MATCH, CMP_STOP, OBJ_KEY, and jingle_session::sid.

Referenced by jingle_endpoint_state_create().

{
   struct jingle_session *session1 = obj, *session2 = arg;
   const char *sid = arg;

   return !strcmp(session1->sid, flags & OBJ_KEY ? sid : session2->sid) ? CMP_MATCH | CMP_STOP : 0;
}
static int jingle_session_hash ( const void *  obj,
const int  flags 
) [static]

Hashing function for Jingle sessions.

Definition at line 272 of file chan_motif.c.

References ast_str_hash(), OBJ_KEY, and jingle_session::sid.

Referenced by jingle_endpoint_state_create().

{
   const struct jingle_session *session = obj;
   const char *sid = obj;

   return ast_str_hash(flags & OBJ_KEY ? sid : session->sid);
}
static struct ast_channel* jingle_session_lock_full ( struct jingle_session pvt) [static, read]

Definition at line 980 of file chan_motif.c.

References ao2_lock, ao2_unlock, ast_channel_lock, ast_channel_ref, ast_channel_unlock, ast_channel_unref, and jingle_session::owner.

Referenced by jingle_action_session_accept(), jingle_action_session_info(), jingle_action_session_terminate(), jingle_interpret_content(), jingle_outgoing_hook(), and jingle_queue_hangup_with_cause().

{
   struct ast_channel *chan;

   /* Locking is simple when it is done right.  If you see a deadlock resulting
    * in this function, it is not this function's fault, Your problem exists elsewhere.
    * This function is perfect... seriously. */
   for (;;) {
      /* First, get the channel and grab a reference to it */
      ao2_lock(pvt);
      chan = pvt->owner;
      if (chan) {
         /* The channel can not go away while we hold the pvt lock.
          * Give the channel a ref so it will not go away after we let
          * the pvt lock go. */
         ast_channel_ref(chan);
      } else {
         /* no channel, return pvt locked */
         return NULL;
      }

      /* We had to hold the pvt lock while getting a ref to the owner channel
       * but now we have to let this lock go in order to preserve proper
       * locking order when grabbing the channel lock */
      ao2_unlock(pvt);

      /* Look, no deadlock avoidance, hooray! */
      ast_channel_lock(chan);
      ao2_lock(pvt);
      if (pvt->owner == chan) {
         /* done */
         break;
      }

      /* If the owner changed while everything was unlocked, no problem,
       * just start over and everthing will work.  This is rare, do not be
       * confused by this loop and think this it is an expensive operation.
       * The majority of the calls to this function will never involve multiple
       * executions of this loop. */
      ast_channel_unlock(chan);
      ast_channel_unref(chan);
      ao2_unlock(pvt);
   }

   /* If owner exists, it is locked and reffed */
   return pvt->owner;
}
static int jingle_set_rtp_peer ( struct ast_channel chan,
struct ast_rtp_instance rtp,
struct ast_rtp_instance vrtp,
struct ast_rtp_instance tpeer,
const struct ast_format_cap cap,
int  nat_active 
) [static]

Function called by RTP engine to change where the remote party should send media.

Definition at line 499 of file chan_motif.c.

{
   return -1;
}
static int jingle_write ( struct ast_channel ast,
struct ast_frame f 
) [static]

Function called by core to write frames.

Definition at line 1526 of file chan_motif.c.

References ast_channel_nativeformats(), ast_channel_readformat(), ast_channel_tech_pvt(), ast_channel_writeformat(), ast_format_cap_iscompatible(), AST_FRAME_VIDEO, AST_FRAME_VOICE, ast_getformatname(), ast_getformatname_multiple(), ast_log(), ast_rtp_instance_write(), ast_frame_subclass::format, ast_frame::frametype, LOG_WARNING, jingle_session::rtp, ast_frame::subclass, and jingle_session::vrtp.

{
   struct jingle_session *session = ast_channel_tech_pvt(ast);
   int res = 0;
   char buf[256];

   switch (frame->frametype) {
   case AST_FRAME_VOICE:
      if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) {
         ast_log(LOG_WARNING,
            "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
            ast_getformatname(&frame->subclass.format),
            ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)),
            ast_getformatname(ast_channel_readformat(ast)),
            ast_getformatname(ast_channel_writeformat(ast)));
         return 0;
      }
      if (session && session->rtp) {
         res = ast_rtp_instance_write(session->rtp, frame);
      }
      break;
   case AST_FRAME_VIDEO:
      if (session && session->vrtp) {
         res = ast_rtp_instance_write(session->vrtp, frame);
      }
      break;
   default:
      ast_log(LOG_WARNING, "Can't send %d type frames with Jingle write\n",
         frame->frametype);
      return 0;
   }

   return res;
}
static int load_module ( void  ) [static]

Load module into PBX, register channel.

Definition at line 2535 of file chan_motif.c.

References accountcode, ACO_EXACT, aco_info_destroy(), aco_info_init(), aco_option_register, aco_option_register_custom, aco_process_config(), ast_channel_register(), ast_format_cap_add_all_by_type(), ast_format_cap_alloc(), AST_FORMAT_TYPE_AUDIO, ast_log(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_FAILURE, ast_rtp_glue_register, ast_rtp_glue_unregister(), ast_sched_context_create(), ast_sched_context_destroy(), ast_sched_start_thread(), jingle_endpoint::cap, ast_channel_tech::capabilities, context, custom_connection_handler(), custom_group_handler(), custom_transport_handler(), DEFAULT_MAX_ICE_CANDIDATES, DEFAULT_MAX_PAYLOADS, FLDSET, language, LOG_ERROR, jingle_endpoint::maxicecandidates, jingle_endpoint::maxpayloads, musicclass, OPT_CODEC_T, OPT_STRINGFIELD_T, OPT_UINT_T, parkinglot, PARSE_DEFAULT, prefs, and STRFLDSET.

{
   if (!(jingle_tech.capabilities = ast_format_cap_alloc())) {
      return AST_MODULE_LOAD_DECLINE;
   }

   if (aco_info_init(&cfg_info)) {
      ast_log(LOG_ERROR, "Unable to intialize configuration for chan_motif.\n");
      goto end;
   }

   aco_option_register(&cfg_info, "context", ACO_EXACT, endpoint_options, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, context));
   aco_option_register_custom(&cfg_info, "callgroup", ACO_EXACT, endpoint_options, NULL, custom_group_handler, 0);
   aco_option_register_custom(&cfg_info, "pickupgroup", ACO_EXACT, endpoint_options, NULL, custom_group_handler, 0);
   aco_option_register(&cfg_info, "language", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, language));
   aco_option_register(&cfg_info, "musicclass", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, musicclass));
   aco_option_register(&cfg_info, "parkinglot", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, parkinglot));
   aco_option_register(&cfg_info, "accountcode", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, accountcode));
   aco_option_register(&cfg_info, "allow", ACO_EXACT, endpoint_options, "ulaw,alaw", OPT_CODEC_T, 1, FLDSET(struct jingle_endpoint, prefs, cap));
   aco_option_register(&cfg_info, "disallow", ACO_EXACT, endpoint_options, "all", OPT_CODEC_T, 0, FLDSET(struct jingle_endpoint, prefs, cap));
   aco_option_register_custom(&cfg_info, "connection", ACO_EXACT, endpoint_options, NULL, custom_connection_handler, 0);
   aco_option_register_custom(&cfg_info, "transport", ACO_EXACT, endpoint_options, NULL, custom_transport_handler, 0);
   aco_option_register(&cfg_info, "maxicecandidates", ACO_EXACT, endpoint_options, DEFAULT_MAX_ICE_CANDIDATES, OPT_UINT_T, PARSE_DEFAULT,
             FLDSET(struct jingle_endpoint, maxicecandidates), DEFAULT_MAX_ICE_CANDIDATES);
   aco_option_register(&cfg_info, "maxpayloads", ACO_EXACT, endpoint_options, DEFAULT_MAX_PAYLOADS, OPT_UINT_T, PARSE_DEFAULT,
             FLDSET(struct jingle_endpoint, maxpayloads), DEFAULT_MAX_PAYLOADS);

   ast_format_cap_add_all_by_type(jingle_tech.capabilities, AST_FORMAT_TYPE_AUDIO);

   if (aco_process_config(&cfg_info, 0)) {
      ast_log(LOG_ERROR, "Unable to read config file motif.conf. Not loading module.\n");
      aco_info_destroy(&cfg_info);
      return AST_MODULE_LOAD_DECLINE;
   }

   if (!(sched = ast_sched_context_create())) {
      ast_log(LOG_ERROR, "Unable to create scheduler context.\n");
      goto end;
   }

   if (ast_sched_start_thread(sched)) {
      ast_log(LOG_ERROR, "Unable to create scheduler context thread.\n");
      goto end;
   }

   ast_rtp_glue_register(&jingle_rtp_glue);

   if (ast_channel_register(&jingle_tech)) {
      ast_log(LOG_ERROR, "Unable to register channel class %s\n", channel_type);
      goto end;
   }

   return 0;

end:
   ast_rtp_glue_unregister(&jingle_rtp_glue);

   if (sched) {
      ast_sched_context_destroy(sched);
   }

   aco_info_destroy(&cfg_info);

   return AST_MODULE_LOAD_FAILURE;
}
static int reload ( void  ) [static]

Reload module.

Definition at line 2602 of file chan_motif.c.

References aco_process_config().

{
   return aco_process_config(&cfg_info, 1);
}

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Motif Jingle Channel Driver" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DRIVER, } [static]

Definition at line 2626 of file chan_motif.c.

Definition at line 2626 of file chan_motif.c.

const char channel_type[] = "Motif" [static]

Definition at line 177 of file chan_motif.c.

const char desc[] = "Motif Jingle Channel" [static]

Definition at line 176 of file chan_motif.c.

struct aco_type endpoint_option [static]

Definition at line 399 of file chan_motif.c.

Definition at line 408 of file chan_motif.c.

Initial value:
 {
   .filename = "motif.conf",
   .types = ACO_TYPES(&endpoint_option),
}

Definition at line 410 of file chan_motif.c.

struct ast_rtp_glue jingle_rtp_glue [static]

Local glue for interacting with the RTP engine core.

Definition at line 505 of file chan_motif.c.

struct ast_channel_tech jingle_tech [static]

PBX interface structure for channel registration.

Definition at line 209 of file chan_motif.c.

struct ast_sched_context* sched [static]

Scheduling context for RTCP

Definition at line 185 of file chan_motif.c.