2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2012, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \author Joshua Colp <jcolp@digium.com>
23 * \brief Motif Jingle Channel Driver
25 * Iksemel http://iksemel.jabberstudio.org/
27 * \ingroup channel_drivers
30 /*! \li \ref chan_motif.c uses the configuration file \ref motif.conf
31 * \addtogroup configuration_file
34 /*! \page motif.conf motif.conf
35 * \verbinclude motif.conf.sample
39 <depend>iksemel</depend>
40 <depend>res_xmpp</depend>
41 <use type="external">openssl</use>
42 <support_level>core</support_level>
47 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
49 #include <sys/socket.h>
52 #include <netinet/in.h>
53 #include <arpa/inet.h>
54 #include <sys/signal.h>
58 #include "asterisk/lock.h"
59 #include "asterisk/channel.h"
60 #include "asterisk/config_options.h"
61 #include "asterisk/module.h"
62 #include "asterisk/pbx.h"
63 #include "asterisk/sched.h"
64 #include "asterisk/io.h"
65 #include "asterisk/rtp_engine.h"
66 #include "asterisk/acl.h"
67 #include "asterisk/callerid.h"
68 #include "asterisk/file.h"
69 #include "asterisk/cli.h"
70 #include "asterisk/app.h"
71 #include "asterisk/musiconhold.h"
72 #include "asterisk/manager.h"
73 #include "asterisk/stringfields.h"
74 #include "asterisk/utils.h"
75 #include "asterisk/causes.h"
76 #include "asterisk/abstract_jb.h"
77 #include "asterisk/xmpp.h"
78 #include "asterisk/endpoints.h"
79 #include "asterisk/stasis_channels.h"
80 #include "asterisk/format_cache.h"
83 <configInfo name="chan_motif" language="en_US">
84 <synopsis>Jingle Channel Driver</synopsis>
86 <para><emphasis>Transports</emphasis></para>
87 <para>There are three different transports and protocol derivatives
88 supported by <literal>chan_motif</literal>. They are in order of
89 preference: Jingle using ICE-UDP, Google Jingle, and Google-V1.</para>
90 <para>Jingle as defined in XEP-0166 supports the widest range of
91 features. It is referred to as <literal>ice-udp</literal>. This is
92 the specification that Jingle clients implement.</para>
93 <para>Google Jingle follows the Jingle specification for signaling
94 but uses a custom transport for media. It is supported by the
95 Google Talk Plug-in in Gmail and by some other Jingle clients. It
96 is referred to as <literal>google</literal> in this file.</para>
97 <para>Google-V1 is the original Google Talk signaling protocol
98 which uses an initial preliminary version of Jingle. It also uses
99 the same custom transport as Google Jingle for media. It is
100 supported by Google Voice, some other Jingle clients, and the
101 Windows Google Talk client. It is referred to as <literal>google-v1</literal>
103 <para>Incoming sessions will automatically switch to the correct
104 transport once it has been determined.</para>
105 <para>Outgoing sessions are capable of determining if the target
106 is capable of Jingle or a Google transport if the target is in the
107 roster. Unfortunately it is not possible to differentiate between
108 a Google Jingle or Google-V1 capable resource until a session
109 initiate attempt occurs. If a resource is determined to use a
110 Google transport it will initially use Google Jingle but will fall
111 back to Google-V1 if required.</para>
112 <para>If an outgoing session attempt fails due to failure to
113 support the given transport <literal>chan_motif</literal> will
114 fall back in preference order listed previously until all
115 transports have been exhausted.</para>
116 <para><emphasis>Dialing and Resource Selection Strategy</emphasis></para>
117 <para>Placing a call through an endpoint can be accomplished using the
118 following dial string:</para>
119 <para><literal>Motif/[endpoint name]/[target]</literal></para>
120 <para>When placing an outgoing call through an endpoint the requested
121 target is searched for in the roster list. If present the first Jingle
122 or Google Jingle capable resource is specifically targeted. Since the
123 capabilities of the resource are known the outgoing session initiation
124 will disregard the configured transport and use the determined one.</para>
125 <para>If the target is not found in the roster the target will be used
126 as-is and a session will be initiated using the transport specified
127 in this configuration file. If no transport has been specified the
128 endpoint defaults to <literal>ice-udp</literal>.</para>
129 <para><emphasis>Video Support</emphasis></para>
130 <para>Support for video does not need to be explicitly enabled.
131 Configuring any video codec on your endpoint will automatically enable
133 <para><emphasis>DTMF</emphasis></para>
134 <para>The only supported method for DTMF is RFC2833. This is always
135 enabled on audio streams and negotiated if possible.</para>
136 <para><emphasis>Incoming Calls</emphasis></para>
137 <para>Incoming calls will first look for the extension matching the
138 name of the endpoint in the configured context. If no such extension
139 exists the call will automatically fall back to the <literal>s</literal> extension.</para>
140 <para><emphasis>CallerID</emphasis></para>
141 <para>The incoming caller id number is populated with the username of
142 the caller and the name is populated with the full identity of the
143 caller. If you would like to perform authentication or filtering
144 of incoming calls it is recommended that you use these fields to do so.</para>
145 <para>Outgoing caller id can <emphasis>not</emphasis> be set.</para>
147 <para>Multiple endpoints using the
148 same connection is <emphasis>NOT</emphasis> supported. Doing so
149 may result in broken calls.</para>
152 <configFile name="motif.conf">
153 <configObject name="endpoint">
154 <synopsis>The configuration for an endpoint.</synopsis>
155 <configOption name="context">
156 <synopsis>Default dialplan context that incoming sessions will be routed to</synopsis>
158 <configOption name="callgroup">
159 <synopsis>A callgroup to assign to this endpoint.</synopsis>
161 <configOption name="pickupgroup">
162 <synopsis>A pickup group to assign to this endpoint.</synopsis>
164 <configOption name="language">
165 <synopsis>The default language for this endpoint.</synopsis>
167 <configOption name="musicclass">
168 <synopsis>Default music on hold class for this endpoint.</synopsis>
170 <configOption name="parkinglot">
171 <synopsis>Default parking lot for this endpoint.</synopsis>
173 <configOption name="accountcode">
174 <synopsis>Accout code for CDR purposes</synopsis>
176 <configOption name="allow">
177 <synopsis>Codecs to allow</synopsis>
179 <configOption name="disallow">
180 <synopsis>Codecs to disallow</synopsis>
182 <configOption name="connection">
183 <synopsis>Connection to accept traffic on and on which to send traffic out</synopsis>
185 <configOption name="transport">
186 <synopsis>The transport to use for the endpoint.</synopsis>
188 <para>The default outbound transport for this endpoint. Inbound
189 messages are inferred. Allowed transports are <literal>ice-udp</literal>,
190 <literal>google</literal>, or <literal>google-v1</literal>. Note
191 that <literal>chan_motif</literal> will fall back to transport
192 preference order if the transport value chosen here fails.</para>
194 <enum name="ice-udp">
195 <para>The Jingle protocol, as defined in XEP 0166.</para>
198 <para>The Google Jingle protocol, which follows the Jingle
199 specification for signaling but uses a custom transport for
202 <enum name="google-v1">
203 <para>Google-V1 is the original Google Talk signaling
204 protocol which uses an initial preliminary version of Jingle.
205 It also uses the same custom transport as <literal>google</literal> for media.</para>
210 <configOption name="maxicecandidates">
211 <synopsis>Maximum number of ICE candidates to offer</synopsis>
213 <configOption name="maxpayloads">
214 <synopsis>Maximum number of pyaloads to offer</synopsis>
221 /*! \brief Default maximum number of ICE candidates we will offer */
222 #define DEFAULT_MAX_ICE_CANDIDATES "10"
224 /*! \brief Default maximum number of payloads we will offer */
225 #define DEFAULT_MAX_PAYLOADS "30"
227 /*! \brief Number of buckets for endpoints */
228 #define ENDPOINT_BUCKETS 37
230 /*! \brief Number of buckets for sessions, on a per-endpoint basis */
231 #define SESSION_BUCKETS 37
233 /*! \brief Namespace for Jingle itself */
234 #define JINGLE_NS "urn:xmpp:jingle:1"
236 /*! \brief Namespace for Jingle RTP sessions */
237 #define JINGLE_RTP_NS "urn:xmpp:jingle:apps:rtp:1"
239 /*! \brief Namespace for Jingle RTP info */
240 #define JINGLE_RTP_INFO_NS "urn:xmpp:jingle:apps:rtp:info:1"
242 /*! \brief Namespace for Jingle ICE-UDP */
243 #define JINGLE_ICE_UDP_NS "urn:xmpp:jingle:transports:ice-udp:1"
245 /*! \brief Namespace for Google Talk ICE-UDP */
246 #define GOOGLE_TRANSPORT_NS "http://www.google.com/transport/p2p"
248 /*! \brief Namespace for Google Talk Raw UDP */
249 #define GOOGLE_TRANSPORT_RAW_NS "http://www.google.com/transport/raw-udp"
251 /*! \brief Namespace for Google Session */
252 #define GOOGLE_SESSION_NS "http://www.google.com/session"
254 /*! \brief Namespace for Google Phone description */
255 #define GOOGLE_PHONE_NS "http://www.google.com/session/phone"
257 /*! \brief Namespace for Google Video description */
258 #define GOOGLE_VIDEO_NS "http://www.google.com/session/video"
260 /*! \brief Namespace for XMPP stanzas */
261 #define XMPP_STANZAS_NS "urn:ietf:params:xml:ns:xmpp-stanzas"
263 /*! \brief The various transport methods supported, from highest priority to lowest priority when doing fallback */
264 enum jingle_transport {
265 JINGLE_TRANSPORT_ICE_UDP = 3, /*!< XEP-0176 */
266 JINGLE_TRANSPORT_GOOGLE_V2 = 2, /*!< https://developers.google.com/talk/call_signaling */
267 JINGLE_TRANSPORT_GOOGLE_V1 = 1, /*!< Undocumented initial Google specification */
268 JINGLE_TRANSPORT_NONE = 0, /*!< No transport specified */
271 /*! \brief Endpoint state information */
272 struct jingle_endpoint_state {
273 struct ao2_container *sessions; /*!< Active sessions to or from the endpoint */
276 /*! \brief Endpoint which contains configuration information and active sessions */
277 struct jingle_endpoint {
278 AST_DECLARE_STRING_FIELDS(
279 AST_STRING_FIELD(name); /*!< Name of the endpoint */
280 AST_STRING_FIELD(context); /*!< Context to place incoming calls into */
281 AST_STRING_FIELD(accountcode); /*!< Account code */
282 AST_STRING_FIELD(language); /*!< Default language for prompts */
283 AST_STRING_FIELD(musicclass); /*!< Configured music on hold class */
284 AST_STRING_FIELD(parkinglot); /*!< Configured parking lot */
286 struct ast_xmpp_client *connection; /*!< Connection to use for traffic */
287 iksrule *rule; /*!< Active matching rule */
288 unsigned int maxicecandidates; /*!< Maximum number of ICE candidates we will offer */
289 unsigned int maxpayloads; /*!< Maximum number of payloads we will offer */
290 struct ast_format_cap *cap; /*!< Formats to use */
291 ast_group_t callgroup; /*!< Call group */
292 ast_group_t pickupgroup; /*!< Pickup group */
293 enum jingle_transport transport; /*!< Default transport to use on outgoing sessions */
294 struct jingle_endpoint_state *state; /*!< Endpoint state information */
297 /*! \brief Session which contains information about an active session */
298 struct jingle_session {
299 AST_DECLARE_STRING_FIELDS(
300 AST_STRING_FIELD(sid); /*!< Session identifier */
301 AST_STRING_FIELD(audio_name); /*!< Name of the audio content */
302 AST_STRING_FIELD(video_name); /*!< Name of the video content */
304 struct jingle_endpoint_state *state; /*!< Endpoint we are associated with */
305 struct ast_xmpp_client *connection; /*!< Connection to use for traffic */
306 enum jingle_transport transport; /*!< Transport type to use for this session */
307 unsigned int maxicecandidates; /*!< Maximum number of ICE candidates we will offer */
308 unsigned int maxpayloads; /*!< Maximum number of payloads we will offer */
309 char remote_original[XMPP_MAX_JIDLEN];/*!< Identifier of the original remote party (remote may have changed due to redirect) */
310 char remote[XMPP_MAX_JIDLEN]; /*!< Identifier of the remote party */
311 iksrule *rule; /*!< Session matching rule */
312 struct ast_channel *owner; /*!< Master Channel */
313 struct ast_rtp_instance *rtp; /*!< RTP audio session */
314 struct ast_rtp_instance *vrtp; /*!< RTP video session */
315 struct ast_format_cap *cap; /*!< Local codec capabilities */
316 struct ast_format_cap *jointcap; /*!< Joint codec capabilities */
317 struct ast_format_cap *peercap; /*!< Peer codec capabilities */
318 unsigned int outgoing:1; /*!< Whether this is an outgoing leg or not */
319 unsigned int gone:1; /*!< In the eyes of Jingle this session is already gone */
320 struct ast_callid *callid; /*!< Bound session call-id */
323 static const char desc[] = "Motif Jingle Channel";
324 static const char channel_type[] = "Motif";
326 struct jingle_config {
327 struct ao2_container *endpoints; /*!< Configured endpoints */
330 static AO2_GLOBAL_OBJ_STATIC(globals);
332 static struct ast_sched_context *sched; /*!< Scheduling context for RTCP */
334 /* \brief Asterisk core interaction functions */
335 static struct ast_channel *jingle_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
336 static int jingle_sendtext(struct ast_channel *ast, const char *text);
337 static int jingle_digit_begin(struct ast_channel *ast, char digit);
338 static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
339 static int jingle_call(struct ast_channel *ast, const char *dest, int timeout);
340 static int jingle_hangup(struct ast_channel *ast);
341 static int jingle_answer(struct ast_channel *ast);
342 static struct ast_frame *jingle_read(struct ast_channel *ast);
343 static int jingle_write(struct ast_channel *ast, struct ast_frame *f);
344 static int jingle_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
345 static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
346 static struct jingle_session *jingle_alloc(struct jingle_endpoint *endpoint, const char *from, const char *sid);
348 /*! \brief Action handlers */
349 static void jingle_action_session_initiate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
350 static void jingle_action_transport_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
351 static void jingle_action_session_accept(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
352 static void jingle_action_session_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
353 static void jingle_action_session_terminate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
355 /*! \brief PBX interface structure for channel registration */
356 static struct ast_channel_tech jingle_tech = {
358 .description = "Motif Jingle Channel Driver",
359 .requester = jingle_request,
360 .send_text = jingle_sendtext,
361 .send_digit_begin = jingle_digit_begin,
362 .send_digit_end = jingle_digit_end,
364 .hangup = jingle_hangup,
365 .answer = jingle_answer,
367 .write = jingle_write,
368 .write_video = jingle_write,
369 .exception = jingle_read,
370 .indicate = jingle_indicate,
371 .fixup = jingle_fixup,
372 .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
375 /*! \brief Defined handlers for different Jingle actions */
376 static const struct jingle_action_handler {
378 void (*handler)(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
379 } jingle_action_handlers[] = {
381 { "session-initiate", jingle_action_session_initiate, },
382 { "transport-info", jingle_action_transport_info, },
383 { "session-accept", jingle_action_session_accept, },
384 { "session-info", jingle_action_session_info, },
385 { "session-terminate", jingle_action_session_terminate, },
386 /* Google-V1 actions */
387 { "initiate", jingle_action_session_initiate, },
388 { "candidates", jingle_action_transport_info, },
389 { "accept", jingle_action_session_accept, },
390 { "terminate", jingle_action_session_terminate, },
391 { "reject", jingle_action_session_terminate, },
394 /*! \brief Reason text <-> cause code mapping */
395 static const struct jingle_reason_mapping {
398 } jingle_reason_mappings[] = {
399 { "busy", AST_CAUSE_BUSY, },
400 { "cancel", AST_CAUSE_CALL_REJECTED, },
401 { "connectivity-error", AST_CAUSE_INTERWORKING, },
402 { "decline", AST_CAUSE_CALL_REJECTED, },
403 { "expired", AST_CAUSE_NO_USER_RESPONSE, },
404 { "failed-transport", AST_CAUSE_PROTOCOL_ERROR, },
405 { "failed-application", AST_CAUSE_SWITCH_CONGESTION, },
406 { "general-error", AST_CAUSE_CONGESTION, },
407 { "gone", AST_CAUSE_NORMAL_CLEARING, },
408 { "incompatible-parameters", AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, },
409 { "media-error", AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, },
410 { "security-error", AST_CAUSE_PROTOCOL_ERROR, },
411 { "success", AST_CAUSE_NORMAL_CLEARING, },
412 { "timeout", AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, },
413 { "unsupported-applications", AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, },
414 { "unsupported-transports", AST_CAUSE_FACILITY_NOT_IMPLEMENTED, },
417 /*! \brief Hashing function for Jingle sessions */
418 static int jingle_session_hash(const void *obj, const int flags)
420 const struct jingle_session *session = obj;
421 const char *sid = obj;
423 return ast_str_hash(flags & OBJ_KEY ? sid : session->sid);
426 /*! \brief Comparator function for Jingle sessions */
427 static int jingle_session_cmp(void *obj, void *arg, int flags)
429 struct jingle_session *session1 = obj, *session2 = arg;
430 const char *sid = arg;
432 return !strcmp(session1->sid, flags & OBJ_KEY ? sid : session2->sid) ? CMP_MATCH | CMP_STOP : 0;
435 /*! \brief Destructor for Jingle endpoint state */
436 static void jingle_endpoint_state_destructor(void *obj)
438 struct jingle_endpoint_state *state = obj;
440 ao2_ref(state->sessions, -1);
443 /*! \brief Destructor for Jingle endpoints */
444 static void jingle_endpoint_destructor(void *obj)
446 struct jingle_endpoint *endpoint = obj;
448 if (endpoint->rule) {
449 iks_filter_remove_rule(endpoint->connection->filter, endpoint->rule);
452 if (endpoint->connection) {
453 ast_xmpp_client_unref(endpoint->connection);
456 ao2_cleanup(endpoint->cap);
457 ao2_ref(endpoint->state, -1);
459 ast_string_field_free_memory(endpoint);
462 /*! \brief Find function for Jingle endpoints */
463 static void *jingle_endpoint_find(struct ao2_container *tmp_container, const char *category)
465 return ao2_find(tmp_container, category, OBJ_KEY);
468 /*! \brief Allocator function for Jingle endpoint state */
469 static struct jingle_endpoint_state *jingle_endpoint_state_create(void)
471 struct jingle_endpoint_state *state;
473 if (!(state = ao2_alloc(sizeof(*state), jingle_endpoint_state_destructor))) {
477 if (!(state->sessions = ao2_container_alloc(SESSION_BUCKETS, jingle_session_hash, jingle_session_cmp))) {
485 /*! \brief State find/create function */
486 static struct jingle_endpoint_state *jingle_endpoint_state_find_or_create(const char *category)
488 RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
489 RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);
491 if (!cfg || !cfg->endpoints || !(endpoint = jingle_endpoint_find(cfg->endpoints, category))) {
492 return jingle_endpoint_state_create();
495 ao2_ref(endpoint->state, +1);
496 return endpoint->state;
499 /*! \brief Allocator function for Jingle endpoints */
500 static void *jingle_endpoint_alloc(const char *cat)
502 struct jingle_endpoint *endpoint;
504 if (!(endpoint = ao2_alloc(sizeof(*endpoint), jingle_endpoint_destructor))) {
508 if (ast_string_field_init(endpoint, 512)) {
509 ao2_ref(endpoint, -1);
513 if (!(endpoint->state = jingle_endpoint_state_find_or_create(cat))) {
514 ao2_ref(endpoint, -1);
518 ast_string_field_set(endpoint, name, cat);
520 endpoint->cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
521 endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
526 /*! \brief Hashing function for Jingle endpoints */
527 static int jingle_endpoint_hash(const void *obj, const int flags)
529 const struct jingle_endpoint *endpoint = obj;
530 const char *name = obj;
532 return ast_str_hash(flags & OBJ_KEY ? name : endpoint->name);
535 /*! \brief Comparator function for Jingle endpoints */
536 static int jingle_endpoint_cmp(void *obj, void *arg, int flags)
538 struct jingle_endpoint *endpoint1 = obj, *endpoint2 = arg;
539 const char *name = arg;
541 return !strcmp(endpoint1->name, flags & OBJ_KEY ? name : endpoint2->name) ? CMP_MATCH | CMP_STOP : 0;
544 static struct aco_type endpoint_option = {
547 .category_match = ACO_BLACKLIST,
548 .category = "^general$",
549 .item_alloc = jingle_endpoint_alloc,
550 .item_find = jingle_endpoint_find,
551 .item_offset = offsetof(struct jingle_config, endpoints),
554 struct aco_type *endpoint_options[] = ACO_TYPES(&endpoint_option);
556 struct aco_file jingle_conf = {
557 .filename = "motif.conf",
558 .types = ACO_TYPES(&endpoint_option),
561 /*! \brief Destructor for Jingle sessions */
562 static void jingle_session_destructor(void *obj)
564 struct jingle_session *session = obj;
567 iks_filter_remove_rule(session->connection->filter, session->rule);
570 if (session->connection) {
571 ast_xmpp_client_unref(session->connection);
575 ast_rtp_instance_stop(session->rtp);
576 ast_rtp_instance_destroy(session->rtp);
580 ast_rtp_instance_stop(session->vrtp);
581 ast_rtp_instance_destroy(session->vrtp);
584 ao2_cleanup(session->cap);
585 ao2_cleanup(session->jointcap);
586 ao2_cleanup(session->peercap);
588 if (session->callid) {
589 ast_callid_unref(session->callid);
592 ast_string_field_free_memory(session);
595 /*! \brief Destructor called when module configuration goes away */
596 static void jingle_config_destructor(void *obj)
598 struct jingle_config *cfg = obj;
599 ao2_cleanup(cfg->endpoints);
602 /*! \brief Allocator called when module configuration should appear */
603 static void *jingle_config_alloc(void)
605 struct jingle_config *cfg;
607 if (!(cfg = ao2_alloc(sizeof(*cfg), jingle_config_destructor))) {
611 if (!(cfg->endpoints = ao2_container_alloc(ENDPOINT_BUCKETS, jingle_endpoint_hash, jingle_endpoint_cmp))) {
619 CONFIG_INFO_STANDARD(cfg_info, globals, jingle_config_alloc,
620 .files = ACO_FILES(&jingle_conf),
623 /*! \brief Function called by RTP engine to get local RTP peer */
624 static enum ast_rtp_glue_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
626 struct jingle_session *session = ast_channel_tech_pvt(chan);
627 enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL;
630 return AST_RTP_GLUE_RESULT_FORBID;
633 ao2_ref(session->rtp, +1);
634 *instance = session->rtp;
639 /*! \brief Function called by RTP engine to get peer capabilities */
640 static void jingle_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
644 /*! \brief Function called by RTP engine to change where the remote party should send media */
645 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)
650 /*! \brief Local glue for interacting with the RTP engine core */
651 static struct ast_rtp_glue jingle_rtp_glue = {
653 .get_rtp_info = jingle_get_rtp_peer,
654 .get_codec = jingle_get_codec,
655 .update_peer = jingle_set_rtp_peer,
658 /*! \brief Set the channel owner on the \ref jingle_session object and related objects */
659 static void jingle_set_owner(struct jingle_session *session, struct ast_channel *chan)
661 session->owner = chan;
663 ast_rtp_instance_set_channel_id(session->rtp, session->owner ? ast_channel_uniqueid(session->owner) : "");
666 ast_rtp_instance_set_channel_id(session->vrtp, session->owner ? ast_channel_uniqueid(session->owner) : "");
670 /*! \brief Internal helper function which enables video support on a sesson if possible */
671 static void jingle_enable_video(struct jingle_session *session)
673 struct ast_sockaddr tmp;
674 struct ast_rtp_engine_ice *ice;
676 /* If video is already present don't do anything */
681 /* If there are no configured video codecs do not turn video support on, it just won't work */
682 if (!ast_format_cap_has_type(session->cap, AST_MEDIA_TYPE_VIDEO)) {
686 ast_sockaddr_parse(&tmp, "0.0.0.0", 0);
688 if (!(session->vrtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
692 ast_rtp_instance_set_prop(session->vrtp, AST_RTP_PROPERTY_RTCP, 1);
693 ast_rtp_instance_set_channel_id(session->vrtp, ast_channel_uniqueid(session->owner));
694 ast_channel_set_fd(session->owner, 2, ast_rtp_instance_fd(session->vrtp, 0));
695 ast_channel_set_fd(session->owner, 3, ast_rtp_instance_fd(session->vrtp, 1));
696 ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(session->vrtp),
697 ast_format_cap_get_framing(session->cap));
698 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2 && (ice = ast_rtp_instance_get_ice(session->vrtp))) {
699 ice->stop(session->vrtp);
703 /*! \brief Internal helper function used to allocate Jingle session on an endpoint */
704 static struct jingle_session *jingle_alloc(struct jingle_endpoint *endpoint, const char *from, const char *sid)
706 struct jingle_session *session;
707 struct ast_callid *callid;
708 struct ast_sockaddr tmp;
710 if (!(session = ao2_alloc(sizeof(*session), jingle_session_destructor))) {
714 callid = ast_read_threadstorage_callid();
715 session->callid = (callid ? callid : ast_create_callid());
717 if (ast_string_field_init(session, 512)) {
718 ao2_ref(session, -1);
722 if (!ast_strlen_zero(from)) {
723 ast_copy_string(session->remote_original, from, sizeof(session->remote_original));
724 ast_copy_string(session->remote, from, sizeof(session->remote));
727 if (ast_strlen_zero(sid)) {
728 ast_string_field_build(session, sid, "%08lx%08lx", (unsigned long)ast_random(), (unsigned long)ast_random());
729 session->outgoing = 1;
730 ast_string_field_set(session, audio_name, "audio");
731 ast_string_field_set(session, video_name, "video");
733 ast_string_field_set(session, sid, sid);
736 ao2_ref(endpoint->state, +1);
737 session->state = endpoint->state;
738 ao2_ref(endpoint->connection, +1);
739 session->connection = endpoint->connection;
740 session->transport = endpoint->transport;
742 if (!(session->cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
743 !(session->jointcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
744 !(session->peercap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
746 ao2_ref(session, -1);
750 ast_format_cap_append_from_cap(session->cap, endpoint->cap, AST_MEDIA_TYPE_UNKNOWN);
752 /* While we rely on res_xmpp for communication we still need a temporary ast_sockaddr to tell the RTP engine
753 * that we want IPv4 */
754 ast_sockaddr_parse(&tmp, "0.0.0.0", 0);
756 /* Sessions always carry audio, but video is optional so don't enable it here */
757 if (!(session->rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
758 ao2_ref(session, -1);
761 ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_RTCP, 1);
762 ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_DTMF, 1);
764 session->maxicecandidates = endpoint->maxicecandidates;
765 session->maxpayloads = endpoint->maxpayloads;
770 /*! \brief Function called to create a new Jingle Asterisk channel */
771 static struct ast_channel *jingle_new(struct jingle_endpoint *endpoint, struct jingle_session *session, int state, const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *cid_name)
773 struct ast_channel *chan;
774 const char *str = S_OR(title, session->remote);
775 struct ast_format_cap *caps;
776 struct ast_format *tmpfmt;
778 if (!ast_format_cap_count(session->cap)) {
782 caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
787 if (!(chan = ast_channel_alloc_with_endpoint(1, state, S_OR(title, ""), S_OR(cid_name, ""), "", "", "", assignedids, requestor, 0, endpoint->connection->endpoint, "Motif/%s-%04lx", str, (unsigned long)(ast_random() & 0xffff)))) {
792 ast_channel_stage_snapshot(chan);
794 ast_channel_tech_set(chan, &jingle_tech);
795 ast_channel_tech_pvt_set(chan, session);
796 jingle_set_owner(session, chan);
798 ast_channel_callid_set(chan, session->callid);
800 ast_format_cap_append_from_cap(caps, session->cap, AST_MEDIA_TYPE_UNKNOWN);
801 ast_channel_nativeformats_set(chan, caps);
805 struct ast_rtp_engine_ice *ice;
807 ast_channel_set_fd(chan, 0, ast_rtp_instance_fd(session->rtp, 0));
808 ast_channel_set_fd(chan, 1, ast_rtp_instance_fd(session->rtp, 1));
809 ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(session->rtp),
810 ast_format_cap_get_framing(session->cap));
812 if (((session->transport == JINGLE_TRANSPORT_GOOGLE_V2) ||
813 (session->transport == JINGLE_TRANSPORT_GOOGLE_V1)) &&
814 (ice = ast_rtp_instance_get_ice(session->rtp))) {
815 /* We stop built in ICE support because we need to fall back to old old old STUN support */
816 ice->stop(session->rtp);
820 if (state == AST_STATE_RING) {
821 ast_channel_rings_set(chan, 1);
824 ast_channel_adsicpe_set(chan, AST_ADSI_UNAVAILABLE);
826 tmpfmt = ast_format_cap_get_format(session->cap, 0);
827 ast_channel_set_writeformat(chan, tmpfmt);
828 ast_channel_set_rawwriteformat(chan, tmpfmt);
829 ast_channel_set_readformat(chan, tmpfmt);
830 ast_channel_set_rawreadformat(chan, tmpfmt);
835 ast_channel_callgroup_set(chan, endpoint->callgroup);
836 ast_channel_pickupgroup_set(chan, endpoint->pickupgroup);
838 if (!ast_strlen_zero(endpoint->accountcode)) {
839 ast_channel_accountcode_set(chan, endpoint->accountcode);
842 if (!ast_strlen_zero(endpoint->language)) {
843 ast_channel_language_set(chan, endpoint->language);
846 if (!ast_strlen_zero(endpoint->musicclass)) {
847 ast_channel_musicclass_set(chan, endpoint->musicclass);
850 ast_channel_context_set(chan, endpoint->context);
851 if (ast_exists_extension(NULL, endpoint->context, endpoint->name, 1, NULL)) {
852 ast_channel_exten_set(chan, endpoint->name);
854 ast_channel_exten_set(chan, "s");
856 ast_channel_priority_set(chan, 1);
858 ao2_unlock(endpoint);
860 ast_channel_stage_snapshot_done(chan);
861 ast_channel_unlock(chan);
866 /*! \brief Internal helper function which sends a response */
867 static void jingle_send_response(struct ast_xmpp_client *connection, ikspak *pak)
871 if (!(response = iks_new("iq"))) {
872 ast_log(LOG_ERROR, "Unable to allocate an IKS response stanza\n");
876 iks_insert_attrib(response, "type", "result");
877 iks_insert_attrib(response, "from", connection->jid->full);
878 iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
879 iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));
881 ast_xmpp_client_send(connection, response);
883 iks_delete(response);
886 /*! \brief Internal helper function which sends an error response */
887 static void jingle_send_error_response(struct ast_xmpp_client *connection, ikspak *pak, const char *type, const char *reasonstr, const char *reasonstr2)
889 iks *response, *error = NULL, *reason = NULL, *reason2 = NULL;
891 if (!(response = iks_new("iq")) ||
892 !(error = iks_new("error")) ||
893 !(reason = iks_new(reasonstr))) {
894 ast_log(LOG_ERROR, "Unable to allocate IKS error response stanzas\n");
898 iks_insert_attrib(response, "type", "error");
899 iks_insert_attrib(response, "from", connection->jid->full);
900 iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
901 iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));
903 iks_insert_attrib(error, "type", type);
904 iks_insert_node(error, reason);
906 if (!ast_strlen_zero(reasonstr2) && (reason2 = iks_new(reasonstr2))) {
907 iks_insert_node(error, reason2);
910 iks_insert_node(response, error);
912 ast_xmpp_client_send(connection, response);
917 iks_delete(response);
920 /*! \brief Internal helper function which adds ICE-UDP candidates to a transport node */
921 static int jingle_add_ice_udp_candidates_to_transport(struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int maximum)
923 struct ast_rtp_engine_ice *ice;
924 struct ao2_container *local_candidates;
925 struct ao2_iterator it;
926 struct ast_rtp_engine_ice_candidate *candidate;
929 if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
930 ast_log(LOG_ERROR, "Unable to add ICE-UDP candidates as ICE support not available or no candidates available\n");
934 iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
935 iks_insert_attrib(transport, "pwd", ice->get_password(rtp));
936 iks_insert_attrib(transport, "ufrag", ice->get_ufrag(rtp));
938 it = ao2_iterator_init(local_candidates, 0);
940 while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
941 iks *local_candidate;
944 if (!(local_candidate = iks_new("candidate"))) {
946 ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for ICE-UDP transport\n");
950 snprintf(tmp, sizeof(tmp), "%u", candidate->id);
951 iks_insert_attrib(local_candidate, "component", tmp);
952 snprintf(tmp, sizeof(tmp), "%d", ast_str_hash(candidate->foundation));
953 iks_insert_attrib(local_candidate, "foundation", tmp);
954 iks_insert_attrib(local_candidate, "generation", "0");
955 iks_insert_attrib(local_candidate, "network", "0");
956 snprintf(tmp, sizeof(tmp), "%04lx", (unsigned long)(ast_random() & 0xffff));
957 iks_insert_attrib(local_candidate, "id", tmp);
958 iks_insert_attrib(local_candidate, "ip", ast_sockaddr_stringify_host(&candidate->address));
959 iks_insert_attrib(local_candidate, "port", ast_sockaddr_stringify_port(&candidate->address));
960 snprintf(tmp, sizeof(tmp), "%d", candidate->priority);
961 iks_insert_attrib(local_candidate, "priority", tmp);
962 iks_insert_attrib(local_candidate, "protocol", "udp");
964 if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
965 iks_insert_attrib(local_candidate, "type", "host");
966 } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
967 iks_insert_attrib(local_candidate, "type", "srflx");
968 } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_RELAYED) {
969 iks_insert_attrib(local_candidate, "type", "relay");
972 iks_insert_node(transport, local_candidate);
973 candidates[i++] = local_candidate;
976 ao2_iterator_destroy(&it);
977 ao2_ref(local_candidates, -1);
982 /*! \brief Internal helper function which adds Google candidates to a transport node */
983 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)
985 struct ast_rtp_engine_ice *ice;
986 struct ao2_container *local_candidates;
987 struct ao2_iterator it;
988 struct ast_rtp_engine_ice_candidate *candidate;
991 if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
992 ast_log(LOG_ERROR, "Unable to add Google ICE candidates as ICE support not available or no candidates available\n");
996 if (transport_type != JINGLE_TRANSPORT_GOOGLE_V1) {
997 iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
1000 it = ao2_iterator_init(local_candidates, 0);
1002 while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
1003 iks *local_candidate;
1004 /* In Google land a username is 16 bytes, explicitly */
1005 char ufrag[17] = "";
1007 if (!(local_candidate = iks_new("candidate"))) {
1009 ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for Google ICE transport\n");
1013 if (candidate->id == 1) {
1014 iks_insert_attrib(local_candidate, "name", !video ? "rtp" : "video_rtp");
1015 } else if (candidate->id == 2) {
1016 iks_insert_attrib(local_candidate, "name", !video ? "rtcp" : "video_rtcp");
1018 iks_delete(local_candidate);
1022 iks_insert_attrib(local_candidate, "address", ast_sockaddr_stringify_host(&candidate->address));
1023 iks_insert_attrib(local_candidate, "port", ast_sockaddr_stringify_port(&candidate->address));
1025 if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
1026 iks_insert_attrib(local_candidate, "preference", "0.95");
1027 iks_insert_attrib(local_candidate, "type", "local");
1028 } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
1029 iks_insert_attrib(local_candidate, "preference", "0.9");
1030 iks_insert_attrib(local_candidate, "type", "stun");
1033 iks_insert_attrib(local_candidate, "protocol", "udp");
1034 iks_insert_attrib(local_candidate, "network", "0");
1035 snprintf(ufrag, sizeof(ufrag), "%s", ice->get_ufrag(rtp));
1036 iks_insert_attrib(local_candidate, "username", ufrag);
1037 iks_insert_attrib(local_candidate, "generation", "0");
1039 if (transport_type == JINGLE_TRANSPORT_GOOGLE_V1) {
1040 iks_insert_attrib(local_candidate, "password", "");
1041 iks_insert_attrib(local_candidate, "foundation", "0");
1042 iks_insert_attrib(local_candidate, "component", "1");
1044 iks_insert_attrib(local_candidate, "password", ice->get_password(rtp));
1047 /* You may notice a lack of relay support up above - this is because we don't support it for use with
1048 * the Google talk transport due to their arcane support. */
1050 iks_insert_node(transport, local_candidate);
1051 candidates[i++] = local_candidate;
1054 ao2_iterator_destroy(&it);
1055 ao2_ref(local_candidates, -1);
1060 /*! \brief Internal function which sends a session-terminate message */
1061 static void jingle_send_session_terminate(struct jingle_session *session, const char *reasontext)
1063 iks *iq = NULL, *jingle = NULL, *reason = NULL, *text = NULL;
1065 if (!(iq = iks_new("iq")) || !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle")) ||
1066 !(reason = iks_new("reason")) || !(text = iks_new(reasontext))) {
1067 ast_log(LOG_ERROR, "Failed to allocate stanzas for session-terminate message on session '%s'\n", session->sid);
1071 iks_insert_attrib(iq, "to", session->remote);
1072 iks_insert_attrib(iq, "type", "set");
1073 iks_insert_attrib(iq, "id", session->connection->mid);
1074 ast_xmpp_increment_mid(session->connection->mid);
1076 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1077 iks_insert_attrib(jingle, "type", "terminate");
1078 iks_insert_attrib(jingle, "id", session->sid);
1079 iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
1080 iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : session->remote);
1082 iks_insert_attrib(jingle, "action", "session-terminate");
1083 iks_insert_attrib(jingle, "sid", session->sid);
1084 iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
1087 iks_insert_node(iq, jingle);
1088 iks_insert_node(jingle, reason);
1089 iks_insert_node(reason, text);
1091 ast_xmpp_client_send(session->connection, iq);
1100 /*! \brief Internal function which sends a session-info message */
1101 static void jingle_send_session_info(struct jingle_session *session, const char *info)
1103 iks *iq = NULL, *jingle = NULL, *text = NULL;
1105 /* Google-V1 has no way to send informational messages so don't even bother trying */
1106 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1110 if (!(iq = iks_new("iq")) || !(jingle = iks_new("jingle")) || !(text = iks_new(info))) {
1111 ast_log(LOG_ERROR, "Failed to allocate stanzas for session-info message on session '%s'\n", session->sid);
1115 iks_insert_attrib(iq, "to", session->remote);
1116 iks_insert_attrib(iq, "type", "set");
1117 iks_insert_attrib(iq, "id", session->connection->mid);
1118 ast_xmpp_increment_mid(session->connection->mid);
1120 iks_insert_attrib(jingle, "action", "session-info");
1121 iks_insert_attrib(jingle, "sid", session->sid);
1122 iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
1123 iks_insert_node(iq, jingle);
1124 iks_insert_node(jingle, text);
1126 ast_xmpp_client_send(session->connection, iq);
1136 * \brief Locks both pvt and pvt owner if owner is present.
1138 * \note This function gives a ref to pvt->owner if it is present and locked.
1139 * This reference must be decremented after pvt->owner is unlocked.
1141 * \note This function will never give you up,
1142 * \note This function will never let you down.
1143 * \note This function will run around and desert you.
1145 * \pre pvt is not locked
1146 * \post pvt is locked
1147 * \post pvt->owner is locked and its reference count is increased (if pvt->owner is not NULL)
1149 * \returns a pointer to the locked and reffed pvt->owner channel if it exists.
1151 static struct ast_channel *jingle_session_lock_full(struct jingle_session *pvt)
1153 struct ast_channel *chan;
1155 /* Locking is simple when it is done right. If you see a deadlock resulting
1156 * in this function, it is not this function's fault, Your problem exists elsewhere.
1157 * This function is perfect... seriously. */
1159 /* First, get the channel and grab a reference to it */
1163 /* The channel can not go away while we hold the pvt lock.
1164 * Give the channel a ref so it will not go away after we let
1165 * the pvt lock go. */
1166 ast_channel_ref(chan);
1168 /* no channel, return pvt locked */
1172 /* We had to hold the pvt lock while getting a ref to the owner channel
1173 * but now we have to let this lock go in order to preserve proper
1174 * locking order when grabbing the channel lock */
1177 /* Look, no deadlock avoidance, hooray! */
1178 ast_channel_lock(chan);
1180 if (pvt->owner == chan) {
1185 /* If the owner changed while everything was unlocked, no problem,
1186 * just start over and everthing will work. This is rare, do not be
1187 * confused by this loop and think this it is an expensive operation.
1188 * The majority of the calls to this function will never involve multiple
1189 * executions of this loop. */
1190 ast_channel_unlock(chan);
1191 ast_channel_unref(chan);
1195 /* If owner exists, it is locked and reffed */
1199 /*! \brief Helper function which queues a hangup frame with cause code */
1200 static void jingle_queue_hangup_with_cause(struct jingle_session *session, int cause)
1202 struct ast_channel *chan;
1204 if ((chan = jingle_session_lock_full(session))) {
1205 ast_debug(3, "Hanging up channel '%s' with cause '%d'\n", ast_channel_name(chan), cause);
1206 ast_queue_hangup_with_cause(chan, cause);
1207 ast_channel_unlock(chan);
1208 ast_channel_unref(chan);
1210 ao2_unlock(session);
1213 /*! \brief Internal function which sends a transport-info message */
1214 static void jingle_send_transport_info(struct jingle_session *session, const char *from)
1216 iks *iq, *jingle = NULL, *audio = NULL, *audio_transport = NULL, *video = NULL, *video_transport = NULL;
1217 iks *audio_candidates[session->maxicecandidates], *video_candidates[session->maxicecandidates];
1220 if (!(iq = iks_new("iq")) ||
1221 !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
1223 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
1224 ast_log(LOG_ERROR, "Failed to allocate stanzas for transport-info message, hanging up session '%s'\n", session->sid);
1228 memset(audio_candidates, 0, sizeof(audio_candidates));
1229 memset(video_candidates, 0, sizeof(video_candidates));
1231 iks_insert_attrib(iq, "from", session->connection->jid->full);
1232 iks_insert_attrib(iq, "to", from);
1233 iks_insert_attrib(iq, "type", "set");
1234 iks_insert_attrib(iq, "id", session->connection->mid);
1235 ast_xmpp_increment_mid(session->connection->mid);
1237 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1238 iks_insert_attrib(jingle, "type", "candidates");
1239 iks_insert_attrib(jingle, "id", session->sid);
1240 iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
1241 iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : from);
1243 iks_insert_attrib(jingle, "action", "transport-info");
1244 iks_insert_attrib(jingle, "sid", session->sid);
1245 iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
1247 iks_insert_node(iq, jingle);
1250 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1251 /* V1 protocol has the candidates directly in the session */
1252 res = jingle_add_google_candidates_to_transport(session->rtp, jingle, audio_candidates, 0, session->transport, session->maxicecandidates);
1253 } else if ((audio = iks_new("content")) && (audio_transport = iks_new("transport"))) {
1254 iks_insert_attrib(audio, "creator", session->outgoing ? "initiator" : "responder");
1255 iks_insert_attrib(audio, "name", session->audio_name);
1256 iks_insert_node(jingle, audio);
1257 iks_insert_node(audio, audio_transport);
1259 if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
1260 res = jingle_add_ice_udp_candidates_to_transport(session->rtp, audio_transport, audio_candidates, session->maxicecandidates);
1261 } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
1262 res = jingle_add_google_candidates_to_transport(session->rtp, audio_transport, audio_candidates, 0, session->transport,
1263 session->maxicecandidates);
1270 if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
1271 if ((video = iks_new("content")) && (video_transport = iks_new("transport"))) {
1272 iks_insert_attrib(video, "creator", session->outgoing ? "initiator" : "responder");
1273 iks_insert_attrib(video, "name", session->video_name);
1274 iks_insert_node(jingle, video);
1275 iks_insert_node(video, video_transport);
1277 if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
1278 res = jingle_add_ice_udp_candidates_to_transport(session->vrtp, video_transport, video_candidates, session->maxicecandidates);
1279 } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
1280 res = jingle_add_google_candidates_to_transport(session->vrtp, video_transport, video_candidates, 1, session->transport,
1281 session->maxicecandidates);
1289 ast_xmpp_client_send(session->connection, iq);
1291 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
1294 /* Clean up after ourselves */
1295 for (i = 0; i < session->maxicecandidates; i++) {
1296 iks_delete(video_candidates[i]);
1297 iks_delete(audio_candidates[i]);
1300 iks_delete(video_transport);
1302 iks_delete(audio_transport);
1308 /*! \brief Internal helper function which adds payloads to a description */
1309 static int jingle_add_payloads_to_description(struct jingle_session *session, struct ast_rtp_instance *rtp, iks *description, iks **payloads, enum ast_media_type type)
1311 int x = 0, i = 0, res = 0;
1313 for (x = 0; (x < ast_format_cap_count(session->jointcap)) && (i < (session->maxpayloads - 2)); x++) {
1314 struct ast_format *format = ast_format_cap_get_format(session->jointcap, x);
1319 if (ast_format_get_type(format) != type) {
1320 ao2_ref(format, -1);
1324 if (((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1, format, 0)) == -1) ||
1325 (!(payload = iks_new("payload-type")))) {
1326 ao2_ref(format, -1);
1330 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1331 iks_insert_attrib(payload, "xmlns", GOOGLE_PHONE_NS);
1334 snprintf(tmp, sizeof(tmp), "%d", rtp_code);
1335 iks_insert_attrib(payload, "id", tmp);
1336 iks_insert_attrib(payload, "name", ast_rtp_lookup_mime_subtype2(1, format, 0, 0));
1337 iks_insert_attrib(payload, "channels", "1");
1339 if ((ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) &&
1340 ((session->transport == JINGLE_TRANSPORT_GOOGLE_V1) || (session->transport == JINGLE_TRANSPORT_GOOGLE_V2))) {
1341 iks_insert_attrib(payload, "clockrate", "16000");
1343 snprintf(tmp, sizeof(tmp), "%u", ast_rtp_lookup_sample_rate2(1, format, 0));
1344 iks_insert_attrib(payload, "clockrate", tmp);
1347 if ((type == AST_MEDIA_TYPE_VIDEO) && (session->transport == JINGLE_TRANSPORT_GOOGLE_V2)) {
1350 /* Google requires these parameters to be set, but alas we can not give accurate values so use some safe defaults */
1351 if ((parameter = iks_new("parameter"))) {
1352 iks_insert_attrib(parameter, "name", "width");
1353 iks_insert_attrib(parameter, "value", "640");
1354 iks_insert_node(payload, parameter);
1356 if ((parameter = iks_new("parameter"))) {
1357 iks_insert_attrib(parameter, "name", "height");
1358 iks_insert_attrib(parameter, "value", "480");
1359 iks_insert_node(payload, parameter);
1361 if ((parameter = iks_new("parameter"))) {
1362 iks_insert_attrib(parameter, "name", "framerate");
1363 iks_insert_attrib(parameter, "value", "30");
1364 iks_insert_node(payload, parameter);
1368 iks_insert_node(description, payload);
1369 payloads[i++] = payload;
1371 ao2_ref(format, -1);
1373 /* If this is for audio and there is room for RFC2833 add it in */
1374 if ((type == AST_MEDIA_TYPE_AUDIO) && (i < session->maxpayloads)) {
1377 if ((payload = iks_new("payload-type"))) {
1378 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1379 iks_insert_attrib(payload, "xmlns", GOOGLE_PHONE_NS);
1382 iks_insert_attrib(payload, "id", "101");
1383 iks_insert_attrib(payload, "name", "telephone-event");
1384 iks_insert_attrib(payload, "channels", "1");
1385 iks_insert_attrib(payload, "clockrate", "8000");
1386 iks_insert_node(description, payload);
1387 payloads[i++] = payload;
1394 /*! \brief Helper function which adds content to a description */
1395 static int jingle_add_content(struct jingle_session *session, iks *jingle, iks *content, iks *description, iks *transport,
1396 const char *name, enum ast_media_type type, struct ast_rtp_instance *rtp, iks **payloads)
1400 if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
1401 iks_insert_attrib(content, "creator", session->outgoing ? "initiator" : "responder");
1402 iks_insert_attrib(content, "name", name);
1403 iks_insert_node(jingle, content);
1405 iks_insert_attrib(description, "xmlns", JINGLE_RTP_NS);
1406 if (type == AST_MEDIA_TYPE_AUDIO) {
1407 iks_insert_attrib(description, "media", "audio");
1408 } else if (type == AST_MEDIA_TYPE_VIDEO) {
1409 iks_insert_attrib(description, "media", "video");
1413 iks_insert_node(content, description);
1415 iks_insert_attrib(description, "xmlns", GOOGLE_PHONE_NS);
1416 iks_insert_node(jingle, description);
1419 if (!(res = jingle_add_payloads_to_description(session, rtp, description, payloads, type))) {
1420 if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
1421 iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
1422 iks_insert_node(content, transport);
1423 } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
1424 iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
1425 iks_insert_node(content, transport);
1432 /*! \brief Internal function which sends a complete session message */
1433 static void jingle_send_session_action(struct jingle_session *session, const char *action)
1435 iks *iq, *jingle, *audio = NULL, *audio_description = NULL, *video = NULL, *video_description = NULL;
1436 iks *audio_payloads[session->maxpayloads], *video_payloads[session->maxpayloads];
1437 iks *audio_transport = NULL, *video_transport = NULL;
1440 if (!(iq = iks_new("iq")) ||
1441 !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
1442 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
1447 memset(audio_payloads, 0, sizeof(audio_payloads));
1448 memset(video_payloads, 0, sizeof(video_payloads));
1450 iks_insert_attrib(iq, "from", session->connection->jid->full);
1451 iks_insert_attrib(iq, "to", session->remote);
1452 iks_insert_attrib(iq, "type", "set");
1453 iks_insert_attrib(iq, "id", session->connection->mid);
1454 ast_xmpp_increment_mid(session->connection->mid);
1456 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
1457 iks_insert_attrib(jingle, "type", action);
1458 iks_insert_attrib(jingle, "id", session->sid);
1459 iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
1461 iks_insert_attrib(jingle, "action", action);
1462 iks_insert_attrib(jingle, "sid", session->sid);
1463 iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
1466 if (!strcasecmp(action, "session-initiate") || !strcasecmp(action, "initiate") || !strcasecmp(action, "accept")) {
1467 iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : session->remote);
1470 iks_insert_node(iq, jingle);
1472 if (session->rtp && (audio = iks_new("content")) && (audio_description = iks_new("description")) &&
1473 (audio_transport = iks_new("transport"))) {
1474 res = jingle_add_content(session, jingle, audio, audio_description, audio_transport, session->audio_name,
1475 AST_MEDIA_TYPE_AUDIO, session->rtp, audio_payloads);
1477 ast_log(LOG_ERROR, "Failed to allocate audio content stanzas for session '%s', hanging up\n", session->sid);
1481 if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
1482 if ((video = iks_new("content")) && (video_description = iks_new("description")) &&
1483 (video_transport = iks_new("transport"))) {
1484 res = jingle_add_content(session, jingle, video, video_description, video_transport, session->video_name,
1485 AST_MEDIA_TYPE_VIDEO, session->vrtp, video_payloads);
1487 ast_log(LOG_ERROR, "Failed to allocate video content stanzas for session '%s', hanging up\n", session->sid);
1493 ast_xmpp_client_send(session->connection, iq);
1495 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
1498 iks_delete(video_transport);
1499 iks_delete(audio_transport);
1501 for (i = 0; i < session->maxpayloads; i++) {
1502 iks_delete(video_payloads[i]);
1503 iks_delete(audio_payloads[i]);
1506 iks_delete(video_description);
1508 iks_delete(audio_description);
1514 /*! \brief Internal function which sends a session-inititate message */
1515 static void jingle_send_session_initiate(struct jingle_session *session)
1517 jingle_send_session_action(session, session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "initiate" : "session-initiate");
1520 /*! \brief Internal function which sends a session-accept message */
1521 static void jingle_send_session_accept(struct jingle_session *session)
1523 jingle_send_session_action(session, session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "accept" : "session-accept");
1526 /*! \brief Callback for when a response is received for an outgoing session-initiate message */
1527 static int jingle_outgoing_hook(void *data, ikspak *pak)
1529 struct jingle_session *session = data;
1530 iks *error = iks_find(pak->x, "error"), *redirect;
1532 /* In all cases this hook is done with */
1533 iks_filter_remove_rule(session->connection->filter, session->rule);
1534 session->rule = NULL;
1536 ast_callid_threadassoc_add(session->callid);
1538 /* If no error occurred they accepted our session-initiate message happily */
1540 struct ast_channel *chan;
1542 if ((chan = jingle_session_lock_full(session))) {
1543 ast_queue_control(chan, AST_CONTROL_PROCEEDING);
1544 ast_channel_unlock(chan);
1545 ast_channel_unref(chan);
1547 ao2_unlock(session);
1549 jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
1554 /* Assume that because this is an error the session is gone, there is only one case where this is incorrect - a redirect */
1557 /* Map the error we received to an appropriate cause code and hang up the channel */
1558 if ((redirect = iks_find_with_attrib(error, "redirect", "xmlns", XMPP_STANZAS_NS))) {
1559 iks *to = iks_child(redirect);
1562 if (to && (target = iks_name(to)) && !ast_strlen_zero(target)) {
1563 /* Make the xmpp: go away if it is present */
1564 if (!strncmp(target, "xmpp:", 5)) {
1568 /* This is actually a fairly simple operation - we update the remote and send another session-initiate */
1569 ast_copy_string(session->remote, target, sizeof(session->remote));
1571 /* Add a new hook so we can get the status of redirected session */
1572 session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
1573 IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);
1575 jingle_send_session_initiate(session);
1579 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
1581 } else if (iks_find_with_attrib(error, "service-unavailable", "xmlns", XMPP_STANZAS_NS)) {
1582 jingle_queue_hangup_with_cause(session, AST_CAUSE_CONGESTION);
1583 } else if (iks_find_with_attrib(error, "resource-constraint", "xmlns", XMPP_STANZAS_NS)) {
1584 jingle_queue_hangup_with_cause(session, AST_CAUSE_REQUESTED_CHAN_UNAVAIL);
1585 } else if (iks_find_with_attrib(error, "bad-request", "xmlns", XMPP_STANZAS_NS)) {
1586 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
1587 } else if (iks_find_with_attrib(error, "remote-server-not-found", "xmlns", XMPP_STANZAS_NS)) {
1588 jingle_queue_hangup_with_cause(session, AST_CAUSE_NO_ROUTE_DESTINATION);
1589 } else if (iks_find_with_attrib(error, "feature-not-implemented", "xmlns", XMPP_STANZAS_NS)) {
1590 /* Assume that this occurred because the remote side does not support our transport, so drop it down one and try again */
1591 session->transport--;
1593 /* If we still have a viable transport mechanism re-send the session-initiate */
1594 if (session->transport != JINGLE_TRANSPORT_NONE) {
1595 struct ast_rtp_engine_ice *ice;
1597 if (((session->transport == JINGLE_TRANSPORT_GOOGLE_V2) ||
1598 (session->transport == JINGLE_TRANSPORT_GOOGLE_V1)) &&
1599 (ice = ast_rtp_instance_get_ice(session->rtp))) {
1600 /* We stop built in ICE support because we need to fall back to old old old STUN support */
1601 ice->stop(session->rtp);
1604 /* Re-send the message to the *original* target and not a redirected one */
1605 ast_copy_string(session->remote, session->remote_original, sizeof(session->remote));
1607 session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
1608 IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);
1610 jingle_send_session_initiate(session);
1614 /* Otherwise we have exhausted all transports */
1615 jingle_queue_hangup_with_cause(session, AST_CAUSE_FACILITY_NOT_IMPLEMENTED);
1618 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
1622 ast_callid_threadassoc_remove();
1624 return IKS_FILTER_EAT;
1627 /*! \brief Function called by core when we should answer a Jingle session */
1628 static int jingle_answer(struct ast_channel *ast)
1630 struct jingle_session *session = ast_channel_tech_pvt(ast);
1632 /* The channel has already been answered so we don't need to do anything */
1633 if (ast_channel_state(ast) == AST_STATE_UP) {
1637 jingle_send_session_accept(session);
1642 /*! \brief Function called by core to read any waiting frames */
1643 static struct ast_frame *jingle_read(struct ast_channel *ast)
1645 struct jingle_session *session = ast_channel_tech_pvt(ast);
1646 struct ast_frame *frame = &ast_null_frame;
1648 switch (ast_channel_fdno(ast)) {
1651 frame = ast_rtp_instance_read(session->rtp, 0);
1656 frame = ast_rtp_instance_read(session->rtp, 1);
1660 if (session->vrtp) {
1661 frame = ast_rtp_instance_read(session->vrtp, 0);
1665 if (session->vrtp) {
1666 frame = ast_rtp_instance_read(session->vrtp, 1);
1673 if (frame && frame->frametype == AST_FRAME_VOICE &&
1674 ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
1675 if (ast_format_cap_iscompatible_format(session->jointcap, frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
1676 ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
1677 ast_format_get_name(frame->subclass.format), ast_channel_name(ast));
1679 frame = &ast_null_frame;
1681 struct ast_format_cap *caps;
1683 ast_debug(1, "Oooh, format changed to %s\n",
1684 ast_format_get_name(frame->subclass.format));
1686 caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
1688 ast_format_cap_append(caps, frame->subclass.format, 0);
1689 ast_channel_nativeformats_set(ast, caps);
1692 ast_set_read_format(ast, ast_channel_readformat(ast));
1693 ast_set_write_format(ast, ast_channel_writeformat(ast));
1700 /*! \brief Function called by core to write frames */
1701 static int jingle_write(struct ast_channel *ast, struct ast_frame *frame)
1703 struct jingle_session *session = ast_channel_tech_pvt(ast);
1706 switch (frame->frametype) {
1707 case AST_FRAME_VOICE:
1708 if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
1709 struct ast_str *codec_buf = ast_str_alloca(64);
1711 ast_log(LOG_WARNING,
1712 "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
1713 ast_format_get_name(frame->subclass.format),
1714 ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
1715 ast_format_get_name(ast_channel_readformat(ast)),
1716 ast_format_get_name(ast_channel_writeformat(ast)));
1719 if (session && session->rtp) {
1720 res = ast_rtp_instance_write(session->rtp, frame);
1723 case AST_FRAME_VIDEO:
1724 if (session && session->vrtp) {
1725 res = ast_rtp_instance_write(session->vrtp, frame);
1729 ast_log(LOG_WARNING, "Can't send %u type frames with Jingle write\n",
1737 /*! \brief Function called by core to change the underlying owner channel */
1738 static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
1740 struct jingle_session *session = ast_channel_tech_pvt(newchan);
1744 jingle_set_owner(session, newchan);
1746 ao2_unlock(session);
1751 /*! \brief Function called by core to ask the channel to indicate some sort of condition */
1752 static int jingle_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
1754 struct jingle_session *session = ast_channel_tech_pvt(ast);
1757 switch (condition) {
1758 case AST_CONTROL_RINGING:
1759 if (ast_channel_state(ast) == AST_STATE_RING) {
1760 jingle_send_session_info(session, "ringing xmlns='urn:xmpp:jingle:apps:rtp:info:1'");
1765 case AST_CONTROL_BUSY:
1766 if (ast_channel_state(ast) != AST_STATE_UP) {
1767 ast_channel_hangupcause_set(ast, AST_CAUSE_BUSY);
1768 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
1773 case AST_CONTROL_CONGESTION:
1774 if (ast_channel_state(ast) != AST_STATE_UP) {
1775 ast_channel_hangupcause_set(ast, AST_CAUSE_CONGESTION);
1776 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
1781 case AST_CONTROL_INCOMPLETE:
1782 if (ast_channel_state(ast) != AST_STATE_UP) {
1783 ast_channel_hangupcause_set(ast, AST_CAUSE_CONGESTION);
1784 ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
1787 case AST_CONTROL_HOLD:
1788 ast_moh_start(ast, data, NULL);
1790 case AST_CONTROL_UNHOLD:
1793 case AST_CONTROL_SRCUPDATE:
1795 ast_rtp_instance_update_source(session->rtp);
1798 case AST_CONTROL_SRCCHANGE:
1800 ast_rtp_instance_change_source(session->rtp);
1803 case AST_CONTROL_VIDUPDATE:
1804 case AST_CONTROL_UPDATE_RTP_PEER:
1805 case AST_CONTROL_CONNECTED_LINE:
1807 case AST_CONTROL_PVT_CAUSE_CODE:
1812 ast_log(LOG_NOTICE, "Don't know how to indicate condition '%d'\n", condition);
1819 /*! \brief Function called by core to send text to the remote party of the Jingle session */
1820 static int jingle_sendtext(struct ast_channel *chan, const char *text)
1822 struct jingle_session *session = ast_channel_tech_pvt(chan);
1824 return ast_xmpp_client_send_message(session->connection, session->remote, text);
1827 /*! \brief Function called by core to start a DTMF digit */
1828 static int jingle_digit_begin(struct ast_channel *chan, char digit)
1830 struct jingle_session *session = ast_channel_tech_pvt(chan);
1833 ast_rtp_instance_dtmf_begin(session->rtp, digit);
1839 /*! \brief Function called by core to stop a DTMF digit */
1840 static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
1842 struct jingle_session *session = ast_channel_tech_pvt(ast);
1845 ast_rtp_instance_dtmf_end_with_duration(session->rtp, digit, duration);
1851 /*! \brief Function called by core to actually start calling a remote party */
1852 static int jingle_call(struct ast_channel *ast, const char *dest, int timeout)
1854 struct jingle_session *session = ast_channel_tech_pvt(ast);
1856 ast_setstate(ast, AST_STATE_RING);
1858 /* Since we have no idea of the remote capabilities use ours for now */
1859 ast_format_cap_append_from_cap(session->jointcap, session->cap, AST_MEDIA_TYPE_UNKNOWN);
1861 /* We set up a hook so we can know when our session-initiate message was accepted or rejected */
1862 session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
1863 IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);
1865 jingle_send_session_initiate(session);
1870 /*! \brief Function called by core to hang up a Jingle session */
1871 static int jingle_hangup(struct ast_channel *ast)
1873 struct jingle_session *session = ast_channel_tech_pvt(ast);
1877 if ((ast_channel_state(ast) != AST_STATE_DOWN) && !session->gone) {
1878 int cause = (session->owner ? ast_channel_hangupcause(session->owner) : AST_CAUSE_CONGESTION);
1879 const char *reason = "success";
1882 /* Get the appropriate reason and send a session-terminate */
1883 for (i = 0; i < ARRAY_LEN(jingle_reason_mappings); i++) {
1884 if (jingle_reason_mappings[i].cause == cause) {
1885 reason = jingle_reason_mappings[i].reason;
1890 jingle_send_session_terminate(session, reason);
1893 ast_channel_tech_pvt_set(ast, NULL);
1894 jingle_set_owner(session, NULL);
1896 ao2_unlink(session->state->sessions, session);
1897 ao2_ref(session->state, -1);
1899 ao2_unlock(session);
1900 ao2_ref(session, -1);
1905 /*! \brief Function called by core to create a new outgoing Jingle session */
1906 static struct ast_channel *jingle_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
1908 RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1909 RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);
1910 char *dialed, target[200] = "";
1911 struct ast_xmpp_buddy *buddy;
1912 struct jingle_session *session;
1913 struct ast_channel *chan;
1914 enum jingle_transport transport = JINGLE_TRANSPORT_NONE;
1915 struct ast_rtp_engine_ice *ice;
1916 AST_DECLARE_APP_ARGS(args,
1918 AST_APP_ARG(target);
1921 /* We require at a minimum one audio format to be requested */
1922 if (!ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO)) {
1923 ast_log(LOG_ERROR, "Motif channel driver requires an audio format when dialing a destination\n");
1924 *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
1928 if (ast_strlen_zero(data) || !(dialed = ast_strdupa(data))) {
1929 ast_log(LOG_ERROR, "Unable to create channel with empty destination.\n");
1930 *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
1934 /* Parse the given dial string and validate the results */
1935 AST_NONSTANDARD_APP_ARGS(args, dialed, '/');
1937 if (ast_strlen_zero(args.name) || ast_strlen_zero(args.target)) {
1938 ast_log(LOG_ERROR, "Unable to determine endpoint name and target.\n");
1939 *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
1943 if (!(endpoint = jingle_endpoint_find(cfg->endpoints, args.name))) {
1944 ast_log(LOG_ERROR, "Endpoint '%s' does not exist.\n", args.name);
1945 *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
1949 ao2_lock(endpoint->state);
1951 /* If we don't have a connection for the endpoint we can't exactly start a session on it */
1952 if (!endpoint->connection) {
1953 ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s' as no valid connection exists\n", args.name);
1954 *cause = AST_CAUSE_SWITCH_CONGESTION;
1955 ao2_unlock(endpoint->state);
1959 /* Find the target in the roster so we can choose a resource */
1960 if ((buddy = ao2_find(endpoint->connection->buddies, args.target, OBJ_KEY))) {
1961 struct ao2_iterator res;
1962 struct ast_xmpp_resource *resource;
1964 /* Iterate through finding the first viable Jingle capable resource */
1965 res = ao2_iterator_init(buddy->resources, 0);
1966 while ((resource = ao2_iterator_next(&res))) {
1967 if (resource->caps.jingle) {
1968 snprintf(target, sizeof(target), "%s/%s", args.target, resource->resource);
1969 transport = JINGLE_TRANSPORT_ICE_UDP;
1971 } else if (resource->caps.google) {
1972 snprintf(target, sizeof(target), "%s/%s", args.target, resource->resource);
1973 transport = JINGLE_TRANSPORT_GOOGLE_V2;
1976 ao2_ref(resource, -1);
1978 ao2_iterator_destroy(&res);
1982 /* If the target is NOT in the roster use the provided target as-is */
1983 ast_copy_string(target, args.target, sizeof(target));
1986 ao2_unlock(endpoint->state);
1988 /* If no target was found we can't set up a session */
1989 if (ast_strlen_zero(target)) {
1990 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);
1991 *cause = AST_CAUSE_SWITCH_CONGESTION;
1995 if (!(session = jingle_alloc(endpoint, target, NULL))) {
1996 ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s'\n", args.name);
1997 *cause = AST_CAUSE_SWITCH_CONGESTION;
2001 /* Update the transport if we learned what we should actually use */
2002 if (transport != JINGLE_TRANSPORT_NONE) {
2003 session->transport = transport;
2004 /* Note that for Google-V1 and Google-V2 we don't stop built-in ICE support, this will happen in jingle_new */
2007 if (!(chan = jingle_new(endpoint, session, AST_STATE_DOWN, target, assignedids, requestor, NULL))) {
2008 ast_log(LOG_ERROR, "Unable to create Jingle channel on endpoint '%s'\n", args.name);
2009 *cause = AST_CAUSE_SWITCH_CONGESTION;
2010 ao2_ref(session, -1);
2014 /* If video was requested try to enable it on the session */
2015 if (ast_format_cap_has_type(cap, AST_MEDIA_TYPE_VIDEO)) {
2016 jingle_enable_video(session);
2019 /* As this is outgoing set ourselves as controlling */
2020 if (session->rtp && (ice = ast_rtp_instance_get_ice(session->rtp))) {
2021 ice->ice_lite(session->rtp);
2024 if (session->vrtp && (ice = ast_rtp_instance_get_ice(session->vrtp))) {
2025 ice->ice_lite(session->vrtp);
2028 /* We purposely don't decrement the session here as there is a reference on the channel */
2029 ao2_link(endpoint->state->sessions, session);
2034 /*! \brief Helper function which handles content descriptions */
2035 static int jingle_interpret_description(struct jingle_session *session, iks *description, const char *name, struct ast_rtp_instance **rtp)
2037 char *media = iks_find_attrib(description, "media");
2038 struct ast_rtp_codecs codecs;
2040 int othercapability = 0;
2042 /* Google-V1 is always carrying audio, but just doesn't tell us so */
2043 if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
2045 } else if (ast_strlen_zero(media)) {
2046 jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
2047 ast_log(LOG_ERROR, "Received a content description on session '%s' without a name\n", session->sid);
2051 /* Determine the type of media that is being carried and update the RTP instance, as well as the name */
2052 if (!strcasecmp(media, "audio")) {
2053 if (!ast_strlen_zero(name)) {
2054 ast_string_field_set(session, audio_name, name);
2056 *rtp = session->rtp;
2057 ast_format_cap_remove_by_type(session->peercap, AST_MEDIA_TYPE_AUDIO);
2058 ast_format_cap_remove_by_type(session->jointcap, AST_MEDIA_TYPE_AUDIO);
2059 } else if (!strcasecmp(media, "video")) {
2060 if (!ast_strlen_zero(name)) {
2061 ast_string_field_set(session, video_name, name);
2064 jingle_enable_video(session);
2065 *rtp = session->vrtp;
2067 /* If video is not present cancel this session */
2068 if (!session->vrtp) {
2069 jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
2070 ast_log(LOG_ERROR, "Received a video content description on session '%s' but could not enable video\n", session->sid);
2074 ast_format_cap_remove_by_type(session->peercap, AST_MEDIA_TYPE_VIDEO);
2075 ast_format_cap_remove_by_type(session->jointcap, AST_MEDIA_TYPE_VIDEO);
2077 /* Unknown media type */
2078 jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
2079 ast_log(LOG_ERROR, "Unsupported media type '%s' received in content description on session '%s'\n", media, session->sid);
2083 if (ast_rtp_codecs_payloads_initialize(&codecs)) {
2084 jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
2085 ast_log(LOG_ERROR, "Could not initialize codecs for negotiation on session '%s'\n", session->sid);
2089 /* Iterate the codecs updating the relevant RTP instance as we go */
2090 for (codec = iks_child(description); codec; codec = iks_next(codec)) {
2091 char *id = iks_find_attrib(codec, "id"), *name = iks_find_attrib(codec, "name");
2092 char *clockrate = iks_find_attrib(codec, "clockrate");
2093 int rtp_id, rtp_clockrate;
2095 if (!ast_strlen_zero(id) && !ast_strlen_zero(name) && (sscanf(id, "%30d", &rtp_id) == 1)) {
2096 if (!ast_strlen_zero(clockrate) && (sscanf(clockrate, "%30d", &rtp_clockrate) == 1)) {
2097 ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, rtp_id, media, name, 0, rtp_clockrate);
2099 ast_rtp_codecs_payloads_set_rtpmap_type(&codecs, NULL, rtp_id, media, name, 0);
2104 ast_rtp_codecs_payload_formats(&codecs, session->peercap, &othercapability);
2105 ast_format_cap_get_compatible(session->cap, session->peercap, session->jointcap);
2107 if (!ast_format_cap_count(session->jointcap)) {
2108 /* We have no compatible codecs, so terminate the session appropriately */
2109 jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
2110 ast_rtp_codecs_payloads_destroy(&codecs);
2114 ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(*rtp), *rtp);
2115 ast_rtp_codecs_payloads_destroy(&codecs);
2120 /*! \brief Helper function which handles ICE-UDP transport information */
2121 static int jingle_interpret_ice_udp_transport(struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
2123 struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
2124 char *ufrag = iks_find_attrib(transport, "ufrag"), *pwd = iks_find_attrib(transport, "pwd");
2128 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
2129 ast_log(LOG_ERROR, "Received ICE-UDP transport information on session '%s' but ICE support not available\n", session->sid);
2133 if (!ast_strlen_zero(ufrag) && !ast_strlen_zero(pwd)) {
2134 ice->set_authentication(rtp, ufrag, pwd);
2137 for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
2138 char *component = iks_find_attrib(candidate, "component"), *foundation = iks_find_attrib(candidate, "foundation");
2139 char *generation = iks_find_attrib(candidate, "generation"), *id = iks_find_attrib(candidate, "id");
2140 char *ip = iks_find_attrib(candidate, "ip"), *port = iks_find_attrib(candidate, "port");
2141 char *priority = iks_find_attrib(candidate, "priority"), *protocol = iks_find_attrib(candidate, "protocol");
2142 char *type = iks_find_attrib(candidate, "type");
2143 struct ast_rtp_engine_ice_candidate local_candidate = { 0, };
2145 struct ast_sockaddr remote_address = { { 0, } };
2147 /* If this candidate is incomplete skip it */
2148 if (ast_strlen_zero(component) || ast_strlen_zero(foundation) || ast_strlen_zero(generation) || ast_strlen_zero(id) ||
2149 ast_strlen_zero(ip) || ast_strlen_zero(port) || ast_strlen_zero(priority) ||
2150 ast_strlen_zero(protocol) || ast_strlen_zero(type)) {
2151 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
2152 ast_log(LOG_ERROR, "Incomplete ICE-UDP candidate received on session '%s'\n", session->sid);
2156 if ((sscanf(component, "%30u", &local_candidate.id) != 1) ||
2157 (sscanf(priority, "%30u", (unsigned *)&local_candidate.priority) != 1) ||
2158 (sscanf(port, "%30d", &real_port) != 1)) {
2159 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
2160 ast_log(LOG_ERROR, "Invalid ICE-UDP candidate information received on session '%s'\n", session->sid);
2164 local_candidate.foundation = foundation;
2165 local_candidate.transport = protocol;
2167 ast_sockaddr_parse(&local_candidate.address, ip, PARSE_PORT_FORBID);
2169 /* We only support IPv4 right now */
2170 if (!ast_sockaddr_is_ipv4(&local_candidate.address)) {
2174 ast_sockaddr_set_port(&local_candidate.address, real_port);
2176 if (!strcasecmp(type, "host")) {
2177 local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
2178 } else if (!strcasecmp(type, "srflx")) {
2179 local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
2180 } else if (!strcasecmp(type, "relay")) {
2181 local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
2186 /* Worst case use the first viable address */
2187 ast_rtp_instance_get_remote_address(rtp, &remote_address);
2189 if (ast_sockaddr_is_ipv4(&local_candidate.address) && ast_sockaddr_isnull(&remote_address)) {
2190 ast_rtp_instance_set_remote_address(rtp, &local_candidate.address);
2193 ice->add_remote_candidate(rtp, &local_candidate);
2201 /*! \brief Helper function which handles Google transport information */
2202 static int jingle_interpret_google_transport(struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
2204 struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
2208 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
2209 ast_log(LOG_ERROR, "Received Google transport information on session '%s' but ICE support not available\n", session->sid);
2213 /* If this session has not transitioned to the Google transport do so now */
2214 if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V2) &&
2215 (session->transport != JINGLE_TRANSPORT_GOOGLE_V1)) {
2216 /* Stop built-in ICE support... we need to fall back to the old old old STUN */
2219 session->transport = JINGLE_TRANSPORT_GOOGLE_V2;
2222 for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
2223 char *address = iks_find_attrib(candidate, "address"), *port = iks_find_attrib(candidate, "port");
2224 char *username = iks_find_attrib(candidate, "username"), *name = iks_find_attrib(candidate, "name");
2225 char *protocol = iks_find_attrib(candidate, "protocol");
2227 struct ast_sockaddr target = { { 0, } };
2228 /* In Google land the combined value is 32 bytes */
2229 char combined[33] = "";
2231 /* If this is NOT actually a candidate just skip it */
2232 if (strcasecmp(iks_name(candidate), "candidate") &&
2233 strcasecmp(iks_name(candidate), "p:candidate") &&
2234 strcasecmp(iks_name(candidate), "ses:candidate")) {
2238 /* If this candidate is incomplete skip it */
2239 if (ast_strlen_zero(address) || ast_strlen_zero(port) || ast_strlen_zero(username) ||
2240 ast_strlen_zero(name)) {
2241 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
2242 ast_log(LOG_ERROR, "Incomplete Google candidate received on session '%s'\n", session->sid);
2246 /* We only support UDP so skip any other protocols */
2247 if (!ast_strlen_zero(protocol) && strcasecmp(protocol, "udp")) {
2251 /* We only permit audio and video, not RTCP */
2252 if (strcasecmp(name, "rtp") && strcasecmp(name, "video_rtp")) {
2256 /* Parse the target information so we can send a STUN request to the candidate */
2257 if (sscanf(port, "%30d", &real_port) != 1) {
2258 jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
2259 ast_log(LOG_ERROR, "Invalid Google candidate port '%s' received on session '%s'\n", port, session->sid);
2262 ast_sockaddr_parse(&target, address, PARSE_PORT_FORBID);
2263 ast_sockaddr_set_port(&target, real_port);
2265 /* Per the STUN support Google talk uses combine the two usernames */
2266 snprintf(combined, sizeof(combined), "%s%s", username, ice->get_ufrag(rtp));
2268 /* This should appease the masses... we will actually change the remote address when we get their STUN packet */
2269 ast_rtp_instance_stun_request(rtp, &target, combined);
2276 * \brief Helper function which locates content stanzas and interprets them
2278 * \note The session *must not* be locked before calling this
2280 static int jingle_interpret_content(struct jingle_session *session, ikspak *pak)
2283 unsigned int changed = 0;
2284 struct ast_channel *chan;
2286 /* Look at the content in the session initiation */
2287 for (content = iks_child(iks_child(pak->x)); content; content = iks_next(content)) {
2289 struct ast_rtp_instance *rtp = NULL;
2290 iks *description, *transport;
2292 /* Ignore specific parts if they are known not to be useful */
2293 if (!strcmp(iks_name(content), "conference-info")) {
2297 name = iks_find_attrib(content, "name");
2299 if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
2300 /* If this content stanza has no name consider it invalid and move on */
2301 if (ast_strlen_zero(name) && !(name = iks_find_attrib(content, "jin:name"))) {
2302 jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
2303 ast_log(LOG_ERROR, "Received content without a name on session '%s'\n", session->sid);
2307 /* Try to pre-populate which RTP instance this content is relevant to */
2308 if (!strcmp(session->audio_name, name)) {
2310 } else if (!strcmp(session->video_name, name)) {
2311 rtp = session->vrtp;
2314 /* Google-V1 has no concept of assocating things like the above does, so since we only support audio over it assume they want audio */
2318 /* If description information is available use it */
2319 if ((description = iks_find_with_attrib(content, "description", "xmlns", JINGLE_RTP_NS)) ||
2320 (description = iks_find_with_attrib(content, "rtp:description", "xmlns:rtp", JINGLE_RTP_NS)) ||
2321 (description = iks_find_with_attrib(content, "pho:description", "xmlns:pho", GOOGLE_PHONE_NS)) ||
2322 (description = iks_find_with_attrib(pak->query, "description", "xmlns", GOOGLE_PHONE_NS)) ||
2323 (description = iks_find_with_attrib(pak->query, "pho:description", "xmlns:pho", GOOGLE_PHONE_NS)) ||
2324 (description = iks_find_with_attrib(pak->query, "vid:description", "xmlns", GOOGLE_VIDEO_NS))) {
2325 /* If we failed to do something with the content description abort immediately */
2326 if (jingle_interpret_description(session, description, name, &rtp)) {
2330 /* If we successfully interpret the description then the codecs need updating */
2334 /* If we get past the description handling and we still don't know what RTP instance this is for... it is unknown content */
2336 ast_log(LOG_ERROR, "Received a content stanza but have no RTP instance for it on session '%s'\n", session->sid);
2337 jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
2341 /* If ICE UDP transport information is available use it */
2342 if ((transport = iks_find_with_attrib(content, "transport", "xmlns", JINGLE_ICE_UDP_NS))) {
2343 if (jingle_interpret_ice_udp_transport(session, transport, rtp)) {
2346 } else if ((transport = iks_find_with_attrib(content, "transport", "xmlns", GOOGLE_TRANSPORT_NS)) ||
2347 (transport = iks_find_with_attrib(content, "p:transport", "xmlns:p", GOOGLE_TRANSPORT_NS)) ||
2348 (transport = iks_find_with_attrib(pak->x, "session", "xmlns", GOOGLE_SESSION_NS)) ||
2349 (transport = iks_find_with_attrib(pak->x, "ses:session", "xmlns:ses", GOOGLE_SESSION_NS))) {
2350 /* If Google transport support is available use it */
2351 if (jingle_interpret_google_transport(session, transport, rtp)) {
2354 } else if (iks_find(content, "transport")) {
2355 /* If this is a transport we do not support terminate the session as it probably won't work out in the end */
2356 jingle_queue_hangup_with_cause(session, AST_CAUSE_FACILITY_NOT_IMPLEMENTED);
2357 ast_log(LOG_ERROR, "Unsupported transport type received on session '%s'\n", session->sid);
2366 if ((chan = jingle_session_lock_full(session))) {
2367 struct ast_format_cap *caps;
2368 struct ast_format *fmt;
2370 caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
2372 ast_format_cap_append_from_cap(caps, session->jointcap, AST_MEDIA_TYPE_UNKNOWN);
2373 ast_channel_nativeformats_set(chan, caps);
2377 fmt = ast_format_cap_get_format(session->jointcap, 0);
2378 ast_set_read_format(chan, fmt);
2379 ast_set_write_format(chan, fmt);
2382 ast_channel_unlock(chan);
2383 ast_channel_unref(chan);
2385 ao2_unlock(session);
2390 /*! \brief Handler function for the 'session-initiate' action */
2391 static void jingle_action_session_initiate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
2394 enum jingle_transport transport = JINGLE_TRANSPORT_NONE;
2395 struct ast_channel *chan;
2399 /* This is a duplicate session setup, so respond accordingly */
2400 jingle_send_error_response(endpoint->connection, pak, "result", "out-of-order", NULL);
2404 /* Retrieve the session identifier from the message, note that this may alter the transport */
2405 if ((sid = iks_find_attrib(pak->query, "id"))) {
2406 /* The presence of the session identifier in the 'id' attribute tells us that this is Google-V1 as everything else uses 'sid' */
2407 transport = JINGLE_TRANSPORT_GOOGLE_V1;
2408 } else if (!(sid = iks_find_attrib(pak->query, "sid"))) {
2409 jingle_send_error_response(endpoint->connection, pak, "bad-request", NULL, NULL);
2413 /* Create a new local session */
2414 if (!(session = jingle_alloc(endpoint, pak->from->full, sid))) {
2415 jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
2419 /* If we determined that the transport should change as a result of how we got the SID change it */
2420 if (transport != JINGLE_TRANSPORT_NONE) {
2421 session->transport = transport;
2424 /* Create a new Asterisk channel using the above local session */
2425 if (!(chan = jingle_new(endpoint, session, AST_STATE_DOWN, pak->from->user, NULL, NULL, pak->from->full))) {
2426 ao2_ref(session, -1);
2427 jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
2431 ao2_link(endpoint->state->sessions, session);
2433 ast_channel_lock(chan);
2434 ast_setstate(chan, AST_STATE_RING);
2435 ast_channel_unlock(chan);
2436 res = ast_pbx_start(chan);
2439 case AST_PBX_FAILED:
2440 ast_log(LOG_WARNING, "Failed to start PBX :(\n");
2441 jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
2445 case AST_PBX_CALL_LIMIT:
2446 ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
2447 jingle_send_error_response(endpoint->connection, pak, "wait", "resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
2450 case AST_PBX_SUCCESS:
2451 jingle_send_response(endpoint->connection, pak);
2453 /* Only send a transport-info message if we successfully interpreted the available content */
2454 if (!jingle_interpret_content(session, pak)) {
2455 jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
2461 /*! \brief Handler function for the 'transport-info' action */
2462 static void jingle_action_transport_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
2465 jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
2466 "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
2470 jingle_interpret_content(session, pak);
2471 jingle_send_response(endpoint->connection, pak);
2474 /*! \brief Handler function for the 'session-accept' action */
2475 static void jingle_action_session_accept(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
2477 struct ast_channel *chan;
2480 jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
2481 "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
2486 jingle_interpret_content(session, pak);
2488 if ((chan = jingle_session_lock_full(session))) {
2489 ast_queue_control(chan, AST_CONTROL_ANSWER);
2490 ast_channel_unlock(chan);
2491 ast_channel_unref(chan);
2493 ao2_unlock(session);
2495 jingle_send_response(endpoint->connection, pak);
2498 /*! \brief Handler function for the 'session-info' action */
2499 static void jingle_action_session_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
2501 struct ast_channel *chan;
2504 jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
2505 "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
2509 if (!(chan = jingle_session_lock_full(session))) {
2510 ao2_unlock(session);
2511 jingle_send_response(endpoint->connection, pak);
2515 if (iks_find_with_attrib(pak->query, "ringing", "xmlns", JINGLE_RTP_INFO_NS)) {
2516 ast_queue_control(chan, AST_CONTROL_RINGING);
2517 if (ast_channel_state(chan) != AST_STATE_UP) {
2518 ast_setstate(chan, AST_STATE_RINGING);
2520 } else if (iks_find_with_attrib(pak->query, "hold", "xmlns", JINGLE_RTP_INFO_NS)) {
2521 ast_queue_hold(chan, NULL);
2522 } else if (iks_find_with_attrib(pak->query, "unhold", "xmlns", JINGLE_RTP_INFO_NS)) {
2523 ast_queue_unhold(chan);
2526 ast_channel_unlock(chan);
2527 ast_channel_unref(chan);
2528 ao2_unlock(session);
2530 jingle_send_response(endpoint->connection, pak);
2533 /*! \brief Handler function for the 'session-terminate' action */
2534 static void jingle_action_session_terminate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
2536 struct ast_channel *chan;
2538 int cause = AST_CAUSE_NORMAL;
2539 struct ast_control_pvt_cause_code *cause_code;
2540 int data_size = sizeof(*cause_code);
2543 jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
2544 "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
2548 if (!(chan = jingle_session_lock_full(session))) {
2549 ao2_unlock(session);
2550 jingle_send_response(endpoint->connection, pak);
2554 /* Pull the reason text from the session-terminate message and translate it into a cause code */
2555 if ((reason = iks_find(pak->query, "reason")) && (text = iks_child(reason))) {
2558 /* Size of the string making up the cause code is "Motif " + text */
2559 data_size += 6 + strlen(iks_name(text));
2560 cause_code = ast_alloca(data_size);
2561 memset(cause_code, 0, data_size);
2563 /* Get the appropriate cause code mapping for this reason */
2564 for (i = 0; i < ARRAY_LEN(jingle_reason_mappings); i++) {
2565 if (!strcasecmp(jingle_reason_mappings[i].reason, iks_name(text))) {
2566 cause = jingle_reason_mappings[i].cause;
2571 /* Store the technology specific information */
2572 snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "Motif %s", iks_name(text));
2574 /* No technology specific information is available */
2575 cause_code = ast_alloca(data_size);
2576 memset(cause_code, 0, data_size);
2579 ast_copy_string(cause_code->chan_name, ast_channel_name(chan), AST_CHANNEL_NAME);
2580 cause_code->ast_cause = cause;
2581 ast_queue_control_data(chan, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
2582 ast_channel_hangupcause_hash_set(chan, cause_code, data_size);
2584 ast_debug(3, "Hanging up channel '%s' due to session terminate message with cause '%d'\n", ast_channel_name(chan), cause);
2585 ast_queue_hangup_with_cause(chan, cause);
2588 ast_channel_unlock(chan);
2589 ast_channel_unref(chan);
2590 ao2_unlock(session);
2592 jingle_send_response(endpoint->connection, pak);
2595 /*! \brief Callback for when a Jingle action is received from an endpoint */
2596 static int jingle_action_hook(void *data, ikspak *pak)
2599 const char *sid = NULL;
2600 struct jingle_session *session = NULL;
2601 struct jingle_endpoint *endpoint = data;
2604 /* We accept both Jingle and Google-V1 */
2605 if (!(action = iks_find_attrib(pak->query, "action")) &&
2606 !(action = iks_find_attrib(pak->query, "type"))) {
2607 /* This occurs if either receive a packet masquerading as Jingle or Google-V1 that is actually not OR we receive a response
2608 * to a message that has no response hook. */
2609 return IKS_FILTER_EAT;
2612 /* Bump the endpoint reference count up in case a reload occurs. Unfortunately the available synchronization between iksemel and us
2613 * does not permit us to make this completely safe. */
2614 ao2_ref(endpoint, +1);
2616 /* If a Jingle session identifier is present use it */
2617 if (!(sid = iks_find_attrib(pak->query, "sid"))) {
2618 /* If a Google-V1 session identifier is present use it */
2619 sid = iks_find_attrib(pak->query, "id");
2622 /* If a session identifier was present in the message attempt to find the session, it is up to the action handler whether
2623 * this is required or not */
2624 if (!ast_strlen_zero(sid)) {
2625 session = ao2_find(endpoint->state->sessions, sid, OBJ_KEY);
2628 /* If a session is present associate the callid with this thread */
2630 ast_callid_threadassoc_add(session->callid);
2633 /* Iterate through supported action handlers looking for one that is able to handle this */
2634 for (i = 0; i < ARRAY_LEN(jingle_action_handlers); i++) {
2635 if (!strcasecmp(jingle_action_handlers[i].action, action)) {
2636 jingle_action_handlers[i].handler(endpoint, session, pak);
2642 /* If no action handler is present for the action they sent us make it evident */
2644 ast_log(LOG_NOTICE, "Received action '%s' for session '%s' that has no handler\n", action, sid);
2647 /* If a session was successfully found for this message deref it now since the handler is done */
2649 ast_callid_threadassoc_remove();
2650 ao2_ref(session, -1);
2653 ao2_ref(endpoint, -1);
2655 return IKS_FILTER_EAT;
2658 /*! \brief Custom handler for groups */
2659 static int custom_group_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2661 struct jingle_endpoint *endpoint = obj;
2663 if (!strcasecmp(var->name, "callgroup")) {
2664 endpoint->callgroup = ast_get_group(var->value);
2665 } else if (!strcasecmp(var->name, "pickupgroup")) {
2666 endpoint->pickupgroup = ast_get_group(var->value);
2674 /*! \brief Custom handler for connection */
2675 static int custom_connection_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2677 struct jingle_endpoint *endpoint = obj;
2679 /* You might think... but Josh, shouldn't you do this in a prelink callback? Well I *could* but until the original is destroyed
2680 * this will not actually get called, so even if the config turns out to be bogus this is harmless.
2682 if (!(endpoint->connection = ast_xmpp_client_find(var->value))) {
2683 ast_log(LOG_ERROR, "Connection '%s' configured on endpoint '%s' could not be found\n", var->value, endpoint->name);
2687 if (!(endpoint->rule = iks_filter_add_rule(endpoint->connection->filter, jingle_action_hook, endpoint,
2688 IKS_RULE_TYPE, IKS_PAK_IQ,
2689 IKS_RULE_NS, JINGLE_NS,
2690 IKS_RULE_NS, GOOGLE_SESSION_NS,
2692 ast_log(LOG_ERROR, "Action hook could not be added to connection '%s' on endpoint '%s'\n", var->value, endpoint->name);
2699 /*! \brief Custom handler for transport */
2700 static int custom_transport_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
2702 struct jingle_endpoint *endpoint = obj;
2704 if (!strcasecmp(var->value, "ice-udp")) {
2705 endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
2706 } else if (!strcasecmp(var->value, "google")) {
2707 endpoint->transport = JINGLE_TRANSPORT_GOOGLE_V2;
2708 } else if (!strcasecmp(var->value, "google-v1")) {
2709 endpoint->transport = JINGLE_TRANSPORT_GOOGLE_V1;
2711 ast_log(LOG_WARNING, "Unknown transport type '%s' on endpoint '%s', defaulting to 'ice-udp'\n", var->value, endpoint->name);
2712 endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
2719 * \brief Load the module
2721 * Module loading including tests for configuration or dependencies.
2722 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
2723 * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
2724 * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
2725 * configuration file or other non-critical problem return
2726 * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
2728 static int load_module(void)
2730 if (!(jingle_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
2731 return AST_MODULE_LOAD_DECLINE;
2734 if (aco_info_init(&cfg_info)) {
2735 ast_log(LOG_ERROR, "Unable to intialize configuration for chan_motif.\n");
2739 aco_option_register(&cfg_info, "context", ACO_EXACT, endpoint_options, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, context));
2740 aco_option_register_custom(&cfg_info, "callgroup", ACO_EXACT, endpoint_options, NULL, custom_group_handler, 0);
2741 aco_option_register_custom(&cfg_info, "pickupgroup", ACO_EXACT, endpoint_options, NULL, custom_group_handler, 0);
2742 aco_option_register(&cfg_info, "language", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, language));
2743 aco_option_register(&cfg_info, "musicclass", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, musicclass));
2744 aco_option_register(&cfg_info, "parkinglot", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, parkinglot));
2745 aco_option_register(&cfg_info, "accountcode", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, accountcode));
2746 aco_option_register(&cfg_info, "allow", ACO_EXACT, endpoint_options, "ulaw,alaw", OPT_CODEC_T, 1, FLDSET(struct jingle_endpoint, cap));
2747 aco_option_register(&cfg_info, "disallow", ACO_EXACT, endpoint_options, "all", OPT_CODEC_T, 0, FLDSET(struct jingle_endpoint, cap));
2748 aco_option_register_custom(&cfg_info, "connection", ACO_EXACT, endpoint_options, NULL, custom_connection_handler, 0);
2749 aco_option_register_custom(&cfg_info, "transport", ACO_EXACT, endpoint_options, NULL, custom_transport_handler, 0);
2750 aco_option_register(&cfg_info, "maxicecandidates", ACO_EXACT, endpoint_options, DEFAULT_MAX_ICE_CANDIDATES, OPT_UINT_T, PARSE_DEFAULT,
2751 FLDSET(struct jingle_endpoint, maxicecandidates), DEFAULT_MAX_ICE_CANDIDATES);
2752 aco_option_register(&cfg_info, "maxpayloads", ACO_EXACT, endpoint_options, DEFAULT_MAX_PAYLOADS, OPT_UINT_T, PARSE_DEFAULT,
2753 FLDSET(struct jingle_endpoint, maxpayloads), DEFAULT_MAX_PAYLOADS);
2755 ast_format_cap_append_by_type(jingle_tech.capabilities, AST_MEDIA_TYPE_AUDIO);
2757 if (aco_process_config(&cfg_info, 0)) {
2758 ao2_ref(jingle_tech.capabilities, -1);
2759 ast_log(LOG_ERROR, "Unable to read config file motif.conf. Module loaded but not running.\n");
2760 aco_info_destroy(&cfg_info);
2761 return AST_MODULE_LOAD_DECLINE;
2764 if (!(sched = ast_sched_context_create())) {
2765 ast_log(LOG_ERROR, "Unable to create scheduler context.\n");
2769 if (ast_sched_start_thread(sched)) {
2770 ast_log(LOG_ERROR, "Unable to create scheduler context thread.\n");
2774 ast_rtp_glue_register(&jingle_rtp_glue);
2776 if (ast_channel_register(&jingle_tech)) {
2777 ast_log(LOG_ERROR, "Unable to register channel class %s\n", channel_type);
2784 ao2_cleanup(jingle_tech.capabilities);
2785 ast_rtp_glue_unregister(&jingle_rtp_glue);
2788 ast_sched_context_destroy(sched);
2791 aco_info_destroy(&cfg_info);
2793 return AST_MODULE_LOAD_FAILURE;
2796 /*! \brief Reload module */
2797 static int reload(void)
2799 return aco_process_config(&cfg_info, 1);
2802 /*! \brief Unload the jingle channel from Asterisk */
2803 static int unload_module(void)
2805 ast_channel_unregister(&jingle_tech);
2806 ao2_cleanup(jingle_tech.capabilities);
2807 jingle_tech.capabilities = NULL;
2808 ast_rtp_glue_unregister(&jingle_rtp_glue);
2809 ast_sched_context_destroy(sched);
2810 aco_info_destroy(&cfg_info);
2811 ao2_global_obj_release(globals);
2816 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Motif Jingle Channel Driver",
2817 .support_level = AST_MODULE_SUPPORT_CORE,
2818 .load = load_module,
2819 .unload = unload_module,
2821 .load_pri = AST_MODPRI_CHANNEL_DRIVER,