chan_motif.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2012, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \author Joshua Colp <jcolp@digium.com>
00022  *
00023  * \brief Motif Jingle Channel Driver
00024  *
00025  * Iksemel http://iksemel.jabberstudio.org/
00026  *
00027  * \ingroup channel_drivers
00028  */
00029 
00030 /*! \li \ref chan_motif.c uses the configuration file \ref motif.conf
00031  * \addtogroup configuration_file
00032  */
00033 
00034 /*! \page motif.conf motif.conf
00035  * \verbinclude motif.conf.sample
00036  */
00037 
00038 /*** MODULEINFO
00039    <depend>iksemel</depend>
00040    <depend>res_xmpp</depend>
00041    <use type="external">openssl</use>
00042    <support_level>core</support_level>
00043  ***/
00044 
00045 #include "asterisk.h"
00046 
00047 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 432834 $")
00048 
00049 #include <sys/socket.h>
00050 #include <fcntl.h>
00051 #include <netdb.h>
00052 #include <netinet/in.h>
00053 #include <arpa/inet.h>
00054 #include <sys/signal.h>
00055 #include <iksemel.h>
00056 #include <pthread.h>
00057 
00058 #include "asterisk/lock.h"
00059 #include "asterisk/channel.h"
00060 #include "asterisk/config_options.h"
00061 #include "asterisk/module.h"
00062 #include "asterisk/pbx.h"
00063 #include "asterisk/sched.h"
00064 #include "asterisk/io.h"
00065 #include "asterisk/rtp_engine.h"
00066 #include "asterisk/acl.h"
00067 #include "asterisk/callerid.h"
00068 #include "asterisk/file.h"
00069 #include "asterisk/cli.h"
00070 #include "asterisk/app.h"
00071 #include "asterisk/musiconhold.h"
00072 #include "asterisk/manager.h"
00073 #include "asterisk/stringfields.h"
00074 #include "asterisk/utils.h"
00075 #include "asterisk/causes.h"
00076 #include "asterisk/abstract_jb.h"
00077 #include "asterisk/xmpp.h"
00078 #include "asterisk/endpoints.h"
00079 #include "asterisk/stasis_channels.h"
00080 #include "asterisk/format_cache.h"
00081 
00082 /*** DOCUMENTATION
00083    <configInfo name="chan_motif" language="en_US">
00084       <synopsis>Jingle Channel Driver</synopsis>
00085       <description>
00086          <para><emphasis>Transports</emphasis></para>
00087          <para>There are three different transports and protocol derivatives
00088          supported by <literal>chan_motif</literal>. They are in order of
00089          preference: Jingle using ICE-UDP, Google Jingle, and Google-V1.</para>
00090          <para>Jingle as defined in XEP-0166 supports the widest range of
00091          features. It is referred to as <literal>ice-udp</literal>. This is
00092          the specification that Jingle clients implement.</para>
00093          <para>Google Jingle follows the Jingle specification for signaling
00094          but uses a custom transport for media. It is supported by the
00095          Google Talk Plug-in in Gmail and by some other Jingle clients. It
00096          is referred to as <literal>google</literal> in this file.</para>
00097          <para>Google-V1 is the original Google Talk signaling protocol
00098          which uses an initial preliminary version of Jingle. It also uses
00099          the same custom transport as Google Jingle for media. It is
00100          supported by Google Voice, some other Jingle clients, and the
00101          Windows Google Talk client. It is referred to as <literal>google-v1</literal>
00102          in this file.</para>
00103          <para>Incoming sessions will automatically switch to the correct
00104          transport once it has been determined.</para>
00105          <para>Outgoing sessions are capable of determining if the target
00106          is capable of Jingle or a Google transport if the target is in the
00107          roster. Unfortunately it is not possible to differentiate between
00108          a Google Jingle or Google-V1 capable resource until a session
00109          initiate attempt occurs. If a resource is determined to use a
00110          Google transport it will initially use Google Jingle but will fall
00111          back to Google-V1 if required.</para>
00112          <para>If an outgoing session attempt fails due to failure to
00113          support the given transport <literal>chan_motif</literal> will
00114          fall back in preference order listed previously until all
00115          transports have been exhausted.</para>
00116          <para><emphasis>Dialing and Resource Selection Strategy</emphasis></para>
00117          <para>Placing a call through an endpoint can be accomplished using the
00118          following dial string:</para>
00119          <para><literal>Motif/[endpoint name]/[target]</literal></para>
00120          <para>When placing an outgoing call through an endpoint the requested
00121          target is searched for in the roster list. If present the first Jingle
00122          or Google Jingle capable resource is specifically targeted. Since the
00123          capabilities of the resource are known the outgoing session initiation
00124          will disregard the configured transport and use the determined one.</para>
00125          <para>If the target is not found in the roster the target will be used
00126          as-is and a session will be initiated using the transport specified
00127          in this configuration file. If no transport has been specified the
00128          endpoint defaults to <literal>ice-udp</literal>.</para>
00129          <para><emphasis>Video Support</emphasis></para>
00130          <para>Support for video does not need to be explicitly enabled.
00131          Configuring any video codec on your endpoint will automatically enable
00132          it.</para>
00133          <para><emphasis>DTMF</emphasis></para>
00134          <para>The only supported method for DTMF is RFC2833. This is always
00135          enabled on audio streams and negotiated if possible.</para>
00136          <para><emphasis>Incoming Calls</emphasis></para>
00137          <para>Incoming calls will first look for the extension matching the
00138          name of the endpoint in the configured context. If no such extension
00139          exists the call will automatically fall back to the <literal>s</literal> extension.</para>
00140          <para><emphasis>CallerID</emphasis></para>
00141          <para>The incoming caller id number is populated with the username of
00142          the caller and the name is populated with the full identity of the
00143          caller. If you would like to perform authentication or filtering
00144          of incoming calls it is recommended that you use these fields to do so.</para>
00145          <para>Outgoing caller id can <emphasis>not</emphasis> be set.</para>
00146          <warning>
00147             <para>Multiple endpoints using the
00148             same connection is <emphasis>NOT</emphasis> supported. Doing so
00149             may result in broken calls.</para>
00150          </warning>
00151       </description>
00152       <configFile name="motif.conf">
00153          <configObject name="endpoint">
00154             <synopsis>The configuration for an endpoint.</synopsis>
00155             <configOption name="context">
00156                <synopsis>Default dialplan context that incoming sessions will be routed to</synopsis>
00157             </configOption>
00158             <configOption name="callgroup">
00159                <synopsis>A callgroup to assign to this endpoint.</synopsis>
00160             </configOption>
00161             <configOption name="pickupgroup">
00162                <synopsis>A pickup group to assign to this endpoint.</synopsis>
00163             </configOption>
00164             <configOption name="language">
00165                <synopsis>The default language for this endpoint.</synopsis>
00166             </configOption>
00167             <configOption name="musicclass">
00168                <synopsis>Default music on hold class for this endpoint.</synopsis>
00169             </configOption>
00170             <configOption name="parkinglot">
00171                <synopsis>Default parking lot for this endpoint.</synopsis>
00172             </configOption>
00173             <configOption name="accountcode">
00174                <synopsis>Accout code for CDR purposes</synopsis>
00175             </configOption>
00176             <configOption name="allow">
00177                <synopsis>Codecs to allow</synopsis>
00178             </configOption>
00179             <configOption name="disallow">
00180                <synopsis>Codecs to disallow</synopsis>
00181             </configOption>
00182             <configOption name="connection">
00183                <synopsis>Connection to accept traffic on and on which to send traffic out</synopsis>
00184             </configOption>
00185             <configOption name="transport">
00186                <synopsis>The transport to use for the endpoint.</synopsis>
00187                <description>
00188                   <para>The default outbound transport for this endpoint. Inbound
00189                   messages are inferred. Allowed transports are <literal>ice-udp</literal>,
00190                   <literal>google</literal>, or <literal>google-v1</literal>. Note
00191                   that <literal>chan_motif</literal> will fall back to transport
00192                   preference order if the transport value chosen here fails.</para>
00193                   <enumlist>
00194                      <enum name="ice-udp">
00195                         <para>The Jingle protocol, as defined in XEP 0166.</para>
00196                      </enum>
00197                      <enum name="google">
00198                         <para>The Google Jingle protocol, which follows the Jingle
00199                         specification for signaling but uses a custom transport for
00200                         media.</para>
00201                      </enum>
00202                      <enum name="google-v1">
00203                         <para>Google-V1 is the original Google Talk signaling
00204                         protocol which uses an initial preliminary version of Jingle.
00205                         It also uses the same custom transport as <literal>google</literal> for media.</para>
00206                      </enum>
00207                   </enumlist>
00208                </description>
00209             </configOption>
00210             <configOption name="maxicecandidates">
00211                <synopsis>Maximum number of ICE candidates to offer</synopsis>
00212             </configOption>
00213             <configOption name="maxpayloads">
00214                <synopsis>Maximum number of pyaloads to offer</synopsis>
00215             </configOption>
00216          </configObject>
00217       </configFile>
00218    </configInfo>
00219 ***/
00220 
00221 /*! \brief Default maximum number of ICE candidates we will offer */
00222 #define DEFAULT_MAX_ICE_CANDIDATES "10"
00223 
00224 /*! \brief Default maximum number of payloads we will offer */
00225 #define DEFAULT_MAX_PAYLOADS "30"
00226 
00227 /*! \brief Number of buckets for endpoints */
00228 #define ENDPOINT_BUCKETS 37
00229 
00230 /*! \brief Number of buckets for sessions, on a per-endpoint basis */
00231 #define SESSION_BUCKETS 37
00232 
00233 /*! \brief Namespace for Jingle itself */
00234 #define JINGLE_NS "urn:xmpp:jingle:1"
00235 
00236 /*! \brief Namespace for Jingle RTP sessions */
00237 #define JINGLE_RTP_NS "urn:xmpp:jingle:apps:rtp:1"
00238 
00239 /*! \brief Namespace for Jingle RTP info */
00240 #define JINGLE_RTP_INFO_NS "urn:xmpp:jingle:apps:rtp:info:1"
00241 
00242 /*! \brief Namespace for Jingle ICE-UDP */
00243 #define JINGLE_ICE_UDP_NS "urn:xmpp:jingle:transports:ice-udp:1"
00244 
00245 /*! \brief Namespace for Google Talk ICE-UDP */
00246 #define GOOGLE_TRANSPORT_NS "http://www.google.com/transport/p2p"
00247 
00248 /*! \brief Namespace for Google Talk Raw UDP */
00249 #define GOOGLE_TRANSPORT_RAW_NS "http://www.google.com/transport/raw-udp"
00250 
00251 /*! \brief Namespace for Google Session */
00252 #define GOOGLE_SESSION_NS "http://www.google.com/session"
00253 
00254 /*! \brief Namespace for Google Phone description */
00255 #define GOOGLE_PHONE_NS "http://www.google.com/session/phone"
00256 
00257 /*! \brief Namespace for Google Video description */
00258 #define GOOGLE_VIDEO_NS "http://www.google.com/session/video"
00259 
00260 /*! \brief Namespace for XMPP stanzas */
00261 #define XMPP_STANZAS_NS "urn:ietf:params:xml:ns:xmpp-stanzas"
00262 
00263 /*! \brief The various transport methods supported, from highest priority to lowest priority when doing fallback */
00264 enum jingle_transport {
00265    JINGLE_TRANSPORT_ICE_UDP = 3,   /*!< XEP-0176 */
00266    JINGLE_TRANSPORT_GOOGLE_V2 = 2, /*!< https://developers.google.com/talk/call_signaling */
00267    JINGLE_TRANSPORT_GOOGLE_V1 = 1, /*!< Undocumented initial Google specification */
00268    JINGLE_TRANSPORT_NONE = 0,      /*!< No transport specified */
00269 };
00270 
00271 /*! \brief Endpoint state information */
00272 struct jingle_endpoint_state {
00273    struct ao2_container *sessions; /*!< Active sessions to or from the endpoint */
00274 };
00275 
00276 /*! \brief Endpoint which contains configuration information and active sessions */
00277 struct jingle_endpoint {
00278    AST_DECLARE_STRING_FIELDS(
00279       AST_STRING_FIELD(name);              /*!< Name of the endpoint */
00280       AST_STRING_FIELD(context);           /*!< Context to place incoming calls into */
00281       AST_STRING_FIELD(accountcode);       /*!< Account code */
00282       AST_STRING_FIELD(language);          /*!< Default language for prompts */
00283       AST_STRING_FIELD(musicclass);        /*!< Configured music on hold class */
00284       AST_STRING_FIELD(parkinglot);        /*!< Configured parking lot */
00285       );
00286    struct ast_xmpp_client *connection;     /*!< Connection to use for traffic */
00287    iksrule *rule;                          /*!< Active matching rule */
00288    unsigned int maxicecandidates;          /*!< Maximum number of ICE candidates we will offer */
00289    unsigned int maxpayloads;               /*!< Maximum number of payloads we will offer */
00290    struct ast_format_cap *cap;             /*!< Formats to use */
00291    ast_group_t callgroup;                  /*!< Call group */
00292    ast_group_t pickupgroup;                /*!< Pickup group */
00293    enum jingle_transport transport;        /*!< Default transport to use on outgoing sessions */
00294    struct jingle_endpoint_state *state;    /*!< Endpoint state information */
00295 };
00296 
00297 /*! \brief Session which contains information about an active session */
00298 struct jingle_session {
00299    AST_DECLARE_STRING_FIELDS(
00300       AST_STRING_FIELD(sid);        /*!< Session identifier */
00301       AST_STRING_FIELD(audio_name); /*!< Name of the audio content */
00302       AST_STRING_FIELD(video_name); /*!< Name of the video content */
00303       );
00304    struct jingle_endpoint_state *state;  /*!< Endpoint we are associated with */
00305    struct ast_xmpp_client *connection;   /*!< Connection to use for traffic */
00306    enum jingle_transport transport;      /*!< Transport type to use for this session */
00307    unsigned int maxicecandidates;        /*!< Maximum number of ICE candidates we will offer */
00308    unsigned int maxpayloads;             /*!< Maximum number of payloads we will offer */
00309    char remote_original[XMPP_MAX_JIDLEN];/*!< Identifier of the original remote party (remote may have changed due to redirect) */
00310    char remote[XMPP_MAX_JIDLEN];         /*!< Identifier of the remote party */
00311    iksrule *rule;                        /*!< Session matching rule */
00312    struct ast_channel *owner;            /*!< Master Channel */
00313    struct ast_rtp_instance *rtp;         /*!< RTP audio session */
00314    struct ast_rtp_instance *vrtp;        /*!< RTP video session */
00315    struct ast_format_cap *cap;           /*!< Local codec capabilities */
00316    struct ast_format_cap *jointcap;      /*!< Joint codec capabilities */
00317    struct ast_format_cap *peercap;       /*!< Peer codec capabilities */
00318    unsigned int outgoing:1;              /*!< Whether this is an outgoing leg or not */
00319    unsigned int gone:1;                  /*!< In the eyes of Jingle this session is already gone */
00320    ast_callid callid;                    /*!< Bound session call-id */
00321 };
00322 
00323 static const char desc[] = "Motif Jingle Channel";
00324 static const char channel_type[] = "Motif";
00325 
00326 struct jingle_config {
00327    struct ao2_container *endpoints; /*!< Configured endpoints */
00328 };
00329 
00330 static AO2_GLOBAL_OBJ_STATIC(globals);
00331 
00332 static struct ast_sched_context *sched; /*!< Scheduling context for RTCP */
00333 
00334 /* \brief Asterisk core interaction functions */
00335 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);
00336 static int jingle_sendtext(struct ast_channel *ast, const char *text);
00337 static int jingle_digit_begin(struct ast_channel *ast, char digit);
00338 static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
00339 static int jingle_call(struct ast_channel *ast, const char *dest, int timeout);
00340 static int jingle_hangup(struct ast_channel *ast);
00341 static int jingle_answer(struct ast_channel *ast);
00342 static struct ast_frame *jingle_read(struct ast_channel *ast);
00343 static int jingle_write(struct ast_channel *ast, struct ast_frame *f);
00344 static int jingle_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
00345 static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
00346 static struct jingle_session *jingle_alloc(struct jingle_endpoint *endpoint, const char *from, const char *sid);
00347 
00348 /*! \brief Action handlers */
00349 static void jingle_action_session_initiate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
00350 static void jingle_action_transport_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
00351 static void jingle_action_session_accept(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
00352 static void jingle_action_session_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
00353 static void jingle_action_session_terminate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
00354 
00355 /*! \brief PBX interface structure for channel registration */
00356 static struct ast_channel_tech jingle_tech = {
00357    .type = "Motif",
00358    .description = "Motif Jingle Channel Driver",
00359    .requester = jingle_request,
00360    .send_text = jingle_sendtext,
00361    .send_digit_begin = jingle_digit_begin,
00362    .send_digit_end = jingle_digit_end,
00363    .call = jingle_call,
00364    .hangup = jingle_hangup,
00365    .answer = jingle_answer,
00366    .read = jingle_read,
00367    .write = jingle_write,
00368    .write_video = jingle_write,
00369    .exception = jingle_read,
00370    .indicate = jingle_indicate,
00371    .fixup = jingle_fixup,
00372    .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
00373 };
00374 
00375 /*! \brief Defined handlers for different Jingle actions */
00376 static const struct jingle_action_handler {
00377    const char *action;
00378    void (*handler)(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak);
00379 } jingle_action_handlers[] = {
00380    /* Jingle actions */
00381    { "session-initiate", jingle_action_session_initiate, },
00382    { "transport-info", jingle_action_transport_info, },
00383    { "session-accept", jingle_action_session_accept, },
00384    { "session-info", jingle_action_session_info, },
00385    { "session-terminate", jingle_action_session_terminate, },
00386    /* Google-V1 actions */
00387    { "initiate", jingle_action_session_initiate, },
00388    { "candidates", jingle_action_transport_info, },
00389    { "accept", jingle_action_session_accept, },
00390    { "terminate", jingle_action_session_terminate, },
00391    { "reject", jingle_action_session_terminate, },
00392 };
00393 
00394 /*! \brief Reason text <-> cause code mapping */
00395 static const struct jingle_reason_mapping {
00396    const char *reason;
00397    int cause;
00398 } jingle_reason_mappings[] = {
00399    { "busy", AST_CAUSE_BUSY, },
00400    { "cancel", AST_CAUSE_CALL_REJECTED, },
00401    { "connectivity-error", AST_CAUSE_INTERWORKING, },
00402    { "decline", AST_CAUSE_CALL_REJECTED, },
00403    { "expired", AST_CAUSE_NO_USER_RESPONSE, },
00404    { "failed-transport", AST_CAUSE_PROTOCOL_ERROR, },
00405    { "failed-application", AST_CAUSE_SWITCH_CONGESTION, },
00406    { "general-error", AST_CAUSE_CONGESTION, },
00407    { "gone", AST_CAUSE_NORMAL_CLEARING, },
00408    { "incompatible-parameters", AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, },
00409    { "media-error", AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, },
00410    { "security-error", AST_CAUSE_PROTOCOL_ERROR, },
00411    { "success", AST_CAUSE_NORMAL_CLEARING, },
00412    { "timeout", AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, },
00413    { "unsupported-applications", AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, },
00414    { "unsupported-transports", AST_CAUSE_FACILITY_NOT_IMPLEMENTED, },
00415 };
00416 
00417 /*! \brief Hashing function for Jingle sessions */
00418 static int jingle_session_hash(const void *obj, const int flags)
00419 {
00420    const struct jingle_session *session = obj;
00421    const char *sid = obj;
00422 
00423    return ast_str_hash(flags & OBJ_KEY ? sid : session->sid);
00424 }
00425 
00426 /*! \brief Comparator function for Jingle sessions */
00427 static int jingle_session_cmp(void *obj, void *arg, int flags)
00428 {
00429    struct jingle_session *session1 = obj, *session2 = arg;
00430    const char *sid = arg;
00431 
00432    return !strcmp(session1->sid, flags & OBJ_KEY ? sid : session2->sid) ? CMP_MATCH | CMP_STOP : 0;
00433 }
00434 
00435 /*! \brief Destructor for Jingle endpoint state */
00436 static void jingle_endpoint_state_destructor(void *obj)
00437 {
00438    struct jingle_endpoint_state *state = obj;
00439 
00440    ao2_ref(state->sessions, -1);
00441 }
00442 
00443 /*! \brief Destructor for Jingle endpoints */
00444 static void jingle_endpoint_destructor(void *obj)
00445 {
00446    struct jingle_endpoint *endpoint = obj;
00447 
00448    if (endpoint->rule) {
00449       iks_filter_remove_rule(endpoint->connection->filter, endpoint->rule);
00450    }
00451 
00452    if (endpoint->connection) {
00453       ast_xmpp_client_unref(endpoint->connection);
00454    }
00455 
00456    ao2_cleanup(endpoint->cap);
00457    ao2_ref(endpoint->state, -1);
00458 
00459    ast_string_field_free_memory(endpoint);
00460 }
00461 
00462 /*! \brief Find function for Jingle endpoints */
00463 static void *jingle_endpoint_find(struct ao2_container *tmp_container, const char *category)
00464 {
00465    return ao2_find(tmp_container, category, OBJ_KEY);
00466 }
00467 
00468 /*! \brief Allocator function for Jingle endpoint state */
00469 static struct jingle_endpoint_state *jingle_endpoint_state_create(void)
00470 {
00471    struct jingle_endpoint_state *state;
00472 
00473    if (!(state = ao2_alloc(sizeof(*state), jingle_endpoint_state_destructor))) {
00474       return NULL;
00475    }
00476 
00477    if (!(state->sessions = ao2_container_alloc(SESSION_BUCKETS, jingle_session_hash, jingle_session_cmp))) {
00478       ao2_ref(state, -1);
00479       return NULL;
00480    }
00481 
00482    return state;
00483 }
00484 
00485 /*! \brief State find/create function */
00486 static struct jingle_endpoint_state *jingle_endpoint_state_find_or_create(const char *category)
00487 {
00488    RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00489    RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);
00490 
00491    if (!cfg || !cfg->endpoints || !(endpoint = jingle_endpoint_find(cfg->endpoints, category))) {
00492       return jingle_endpoint_state_create();
00493    }
00494 
00495    ao2_ref(endpoint->state, +1);
00496    return endpoint->state;
00497 }
00498 
00499 /*! \brief Allocator function for Jingle endpoints */
00500 static void *jingle_endpoint_alloc(const char *cat)
00501 {
00502    struct jingle_endpoint *endpoint;
00503 
00504    if (!(endpoint = ao2_alloc(sizeof(*endpoint), jingle_endpoint_destructor))) {
00505       return NULL;
00506    }
00507 
00508    if (ast_string_field_init(endpoint, 512)) {
00509       ao2_ref(endpoint, -1);
00510       return NULL;
00511    }
00512 
00513    if (!(endpoint->state = jingle_endpoint_state_find_or_create(cat))) {
00514       ao2_ref(endpoint, -1);
00515       return NULL;
00516    }
00517 
00518    ast_string_field_set(endpoint, name, cat);
00519 
00520    endpoint->cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
00521    endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
00522 
00523    return endpoint;
00524 }
00525 
00526 /*! \brief Hashing function for Jingle endpoints */
00527 static int jingle_endpoint_hash(const void *obj, const int flags)
00528 {
00529    const struct jingle_endpoint *endpoint = obj;
00530    const char *name = obj;
00531 
00532    return ast_str_hash(flags & OBJ_KEY ? name : endpoint->name);
00533 }
00534 
00535 /*! \brief Comparator function for Jingle endpoints */
00536 static int jingle_endpoint_cmp(void *obj, void *arg, int flags)
00537 {
00538    struct jingle_endpoint *endpoint1 = obj, *endpoint2 = arg;
00539    const char *name = arg;
00540 
00541    return !strcmp(endpoint1->name, flags & OBJ_KEY ? name : endpoint2->name) ? CMP_MATCH | CMP_STOP : 0;
00542 }
00543 
00544 static struct aco_type endpoint_option = {
00545    .type = ACO_ITEM,
00546    .name = "endpoint",
00547    .category_match = ACO_BLACKLIST,
00548    .category = "^general$",
00549    .item_alloc = jingle_endpoint_alloc,
00550    .item_find = jingle_endpoint_find,
00551    .item_offset = offsetof(struct jingle_config, endpoints),
00552 };
00553 
00554 struct aco_type *endpoint_options[] = ACO_TYPES(&endpoint_option);
00555 
00556 struct aco_file jingle_conf = {
00557    .filename = "motif.conf",
00558    .types = ACO_TYPES(&endpoint_option),
00559 };
00560 
00561 /*! \brief Destructor for Jingle sessions */
00562 static void jingle_session_destructor(void *obj)
00563 {
00564    struct jingle_session *session = obj;
00565 
00566    if (session->rule) {
00567       iks_filter_remove_rule(session->connection->filter, session->rule);
00568    }
00569 
00570    if (session->connection) {
00571       ast_xmpp_client_unref(session->connection);
00572    }
00573 
00574    if (session->rtp) {
00575       ast_rtp_instance_stop(session->rtp);
00576       ast_rtp_instance_destroy(session->rtp);
00577    }
00578 
00579    if (session->vrtp) {
00580       ast_rtp_instance_stop(session->vrtp);
00581       ast_rtp_instance_destroy(session->vrtp);
00582    }
00583 
00584    ao2_cleanup(session->cap);
00585    ao2_cleanup(session->jointcap);
00586    ao2_cleanup(session->peercap);
00587 
00588    ast_string_field_free_memory(session);
00589 }
00590 
00591 /*! \brief Destructor called when module configuration goes away */
00592 static void jingle_config_destructor(void *obj)
00593 {
00594    struct jingle_config *cfg = obj;
00595    ao2_cleanup(cfg->endpoints);
00596 }
00597 
00598 /*! \brief Allocator called when module configuration should appear */
00599 static void *jingle_config_alloc(void)
00600 {
00601    struct jingle_config *cfg;
00602 
00603    if (!(cfg = ao2_alloc(sizeof(*cfg), jingle_config_destructor))) {
00604       return NULL;
00605    }
00606 
00607    if (!(cfg->endpoints = ao2_container_alloc(ENDPOINT_BUCKETS, jingle_endpoint_hash, jingle_endpoint_cmp))) {
00608       ao2_ref(cfg, -1);
00609       return NULL;
00610    }
00611 
00612    return cfg;
00613 }
00614 
00615 CONFIG_INFO_STANDARD(cfg_info, globals, jingle_config_alloc,
00616            .files = ACO_FILES(&jingle_conf),
00617    );
00618 
00619 /*! \brief Function called by RTP engine to get local RTP peer */
00620 static enum ast_rtp_glue_result jingle_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
00621 {
00622    struct jingle_session *session = ast_channel_tech_pvt(chan);
00623    enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_LOCAL;
00624 
00625    if (!session->rtp) {
00626       return AST_RTP_GLUE_RESULT_FORBID;
00627    }
00628 
00629    ao2_ref(session->rtp, +1);
00630    *instance = session->rtp;
00631 
00632    return res;
00633 }
00634 
00635 /*! \brief Function called by RTP engine to get peer capabilities */
00636 static void jingle_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
00637 {
00638 }
00639 
00640 /*! \brief Function called by RTP engine to change where the remote party should send media */
00641 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)
00642 {
00643    return -1;
00644 }
00645 
00646 /*! \brief Local glue for interacting with the RTP engine core */
00647 static struct ast_rtp_glue jingle_rtp_glue = {
00648    .type = "Motif",
00649    .get_rtp_info = jingle_get_rtp_peer,
00650    .get_codec = jingle_get_codec,
00651    .update_peer = jingle_set_rtp_peer,
00652 };
00653 
00654 /*! \brief Set the channel owner on the \ref jingle_session object and related objects */
00655 static void jingle_set_owner(struct jingle_session *session, struct ast_channel *chan)
00656 {
00657    session->owner = chan;
00658    if (session->rtp) {
00659       ast_rtp_instance_set_channel_id(session->rtp, session->owner ? ast_channel_uniqueid(session->owner) : "");
00660    }
00661    if (session->vrtp) {
00662       ast_rtp_instance_set_channel_id(session->vrtp, session->owner ? ast_channel_uniqueid(session->owner) : "");
00663    }
00664 }
00665 
00666 /*! \brief Internal helper function which enables video support on a sesson if possible */
00667 static void jingle_enable_video(struct jingle_session *session)
00668 {
00669    struct ast_sockaddr tmp;
00670    struct ast_rtp_engine_ice *ice;
00671 
00672    /* If video is already present don't do anything */
00673    if (session->vrtp) {
00674       return;
00675    }
00676 
00677    /* If there are no configured video codecs do not turn video support on, it just won't work */
00678    if (!ast_format_cap_has_type(session->cap, AST_MEDIA_TYPE_VIDEO)) {
00679       return;
00680    }
00681 
00682    ast_sockaddr_parse(&tmp, "0.0.0.0", 0);
00683 
00684    if (!(session->vrtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
00685       return;
00686    }
00687 
00688    ast_rtp_instance_set_prop(session->vrtp, AST_RTP_PROPERTY_RTCP, 1);
00689    ast_rtp_instance_set_channel_id(session->vrtp, ast_channel_uniqueid(session->owner));
00690    ast_channel_set_fd(session->owner, 2, ast_rtp_instance_fd(session->vrtp, 0));
00691    ast_channel_set_fd(session->owner, 3, ast_rtp_instance_fd(session->vrtp, 1));
00692    ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(session->vrtp),
00693       ast_format_cap_get_framing(session->cap));
00694    if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2 && (ice = ast_rtp_instance_get_ice(session->vrtp))) {
00695       ice->stop(session->vrtp);
00696    }
00697 }
00698 
00699 /*! \brief Internal helper function used to allocate Jingle session on an endpoint */
00700 static struct jingle_session *jingle_alloc(struct jingle_endpoint *endpoint, const char *from, const char *sid)
00701 {
00702    struct jingle_session *session;
00703    ast_callid callid;
00704    struct ast_sockaddr tmp;
00705 
00706    if (!(session = ao2_alloc(sizeof(*session), jingle_session_destructor))) {
00707       return NULL;
00708    }
00709 
00710    callid = ast_read_threadstorage_callid();
00711    session->callid = (callid ? callid : ast_create_callid());
00712 
00713    if (ast_string_field_init(session, 512)) {
00714       ao2_ref(session, -1);
00715       return NULL;
00716    }
00717 
00718    if (!ast_strlen_zero(from)) {
00719       ast_copy_string(session->remote_original, from, sizeof(session->remote_original));
00720       ast_copy_string(session->remote, from, sizeof(session->remote));
00721    }
00722 
00723    if (ast_strlen_zero(sid)) {
00724       ast_string_field_build(session, sid, "%08lx%08lx", (unsigned long)ast_random(), (unsigned long)ast_random());
00725       session->outgoing = 1;
00726       ast_string_field_set(session, audio_name, "audio");
00727       ast_string_field_set(session, video_name, "video");
00728    } else {
00729       ast_string_field_set(session, sid, sid);
00730    }
00731 
00732    ao2_ref(endpoint->state, +1);
00733    session->state = endpoint->state;
00734    ao2_ref(endpoint->connection, +1);
00735    session->connection = endpoint->connection;
00736    session->transport = endpoint->transport;
00737 
00738    if (!(session->cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
00739        !(session->jointcap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
00740        !(session->peercap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
00741        !session->callid) {
00742       ao2_ref(session, -1);
00743       return NULL;
00744    }
00745 
00746    ast_format_cap_append_from_cap(session->cap, endpoint->cap, AST_MEDIA_TYPE_UNKNOWN);
00747 
00748    /* While we rely on res_xmpp for communication we still need a temporary ast_sockaddr to tell the RTP engine
00749     * that we want IPv4 */
00750    ast_sockaddr_parse(&tmp, "0.0.0.0", 0);
00751 
00752    /* Sessions always carry audio, but video is optional so don't enable it here */
00753    if (!(session->rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
00754       ao2_ref(session, -1);
00755       return NULL;
00756    }
00757    ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_RTCP, 1);
00758    ast_rtp_instance_set_prop(session->rtp, AST_RTP_PROPERTY_DTMF, 1);
00759 
00760    session->maxicecandidates = endpoint->maxicecandidates;
00761    session->maxpayloads = endpoint->maxpayloads;
00762 
00763    return session;
00764 }
00765 
00766 /*! \brief Function called to create a new Jingle Asterisk channel */
00767 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)
00768 {
00769    struct ast_channel *chan;
00770    const char *str = S_OR(title, session->remote);
00771    struct ast_format_cap *caps;
00772    struct ast_format *tmpfmt;
00773 
00774    if (!ast_format_cap_count(session->cap)) {
00775       return NULL;
00776    }
00777 
00778    caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
00779    if (!caps) {
00780       return NULL;
00781    }
00782 
00783    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)))) {
00784       ao2_ref(caps, -1);
00785       return NULL;
00786    }
00787 
00788    ast_channel_stage_snapshot(chan);
00789 
00790    ast_channel_tech_set(chan, &jingle_tech);
00791    ast_channel_tech_pvt_set(chan, session);
00792    jingle_set_owner(session, chan);
00793 
00794    ast_channel_callid_set(chan, session->callid);
00795 
00796    ast_format_cap_append_from_cap(caps, session->cap, AST_MEDIA_TYPE_UNKNOWN);
00797    ast_channel_nativeformats_set(chan, caps);
00798    ao2_ref(caps, -1);
00799 
00800    if (session->rtp) {
00801       struct ast_rtp_engine_ice *ice;
00802 
00803       ast_channel_set_fd(chan, 0, ast_rtp_instance_fd(session->rtp, 0));
00804       ast_channel_set_fd(chan, 1, ast_rtp_instance_fd(session->rtp, 1));
00805       ast_rtp_codecs_set_framing(ast_rtp_instance_get_codecs(session->rtp),
00806          ast_format_cap_get_framing(session->cap));
00807 
00808       if (((session->transport == JINGLE_TRANSPORT_GOOGLE_V2) ||
00809            (session->transport == JINGLE_TRANSPORT_GOOGLE_V1)) &&
00810           (ice = ast_rtp_instance_get_ice(session->rtp))) {
00811          /* We stop built in ICE support because we need to fall back to old old old STUN support */
00812          ice->stop(session->rtp);
00813       }
00814    }
00815 
00816    if (state == AST_STATE_RING) {
00817       ast_channel_rings_set(chan, 1);
00818    }
00819 
00820    ast_channel_adsicpe_set(chan, AST_ADSI_UNAVAILABLE);
00821 
00822    tmpfmt = ast_format_cap_get_format(session->cap, 0);
00823    ast_channel_set_writeformat(chan, tmpfmt);
00824    ast_channel_set_rawwriteformat(chan, tmpfmt);
00825    ast_channel_set_readformat(chan, tmpfmt);
00826    ast_channel_set_rawreadformat(chan, tmpfmt);
00827    ao2_ref(tmpfmt, -1);
00828 
00829    ao2_lock(endpoint);
00830 
00831    ast_channel_callgroup_set(chan, endpoint->callgroup);
00832    ast_channel_pickupgroup_set(chan, endpoint->pickupgroup);
00833 
00834    if (!ast_strlen_zero(endpoint->accountcode)) {
00835       ast_channel_accountcode_set(chan, endpoint->accountcode);
00836    }
00837 
00838    if (!ast_strlen_zero(endpoint->language)) {
00839       ast_channel_language_set(chan, endpoint->language);
00840    }
00841 
00842    if (!ast_strlen_zero(endpoint->musicclass)) {
00843       ast_channel_musicclass_set(chan, endpoint->musicclass);
00844    }
00845 
00846    ast_channel_context_set(chan, endpoint->context);
00847    if (ast_exists_extension(NULL, endpoint->context, endpoint->name, 1, NULL)) {
00848       ast_channel_exten_set(chan, endpoint->name);
00849    } else {
00850       ast_channel_exten_set(chan, "s");
00851    }
00852    ast_channel_priority_set(chan, 1);
00853 
00854    ao2_unlock(endpoint);
00855 
00856    ast_channel_stage_snapshot_done(chan);
00857    ast_channel_unlock(chan);
00858 
00859    return chan;
00860 }
00861 
00862 /*! \brief Internal helper function which sends a response */
00863 static void jingle_send_response(struct ast_xmpp_client *connection, ikspak *pak)
00864 {
00865    iks *response;
00866 
00867    if (!(response = iks_new("iq"))) {
00868       ast_log(LOG_ERROR, "Unable to allocate an IKS response stanza\n");
00869       return;
00870    }
00871 
00872    iks_insert_attrib(response, "type", "result");
00873    iks_insert_attrib(response, "from", connection->jid->full);
00874    iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
00875    iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));
00876 
00877    ast_xmpp_client_send(connection, response);
00878 
00879    iks_delete(response);
00880 }
00881 
00882 /*! \brief Internal helper function which sends an error response */
00883 static void jingle_send_error_response(struct ast_xmpp_client *connection, ikspak *pak, const char *type, const char *reasonstr, const char *reasonstr2)
00884 {
00885    iks *response, *error = NULL, *reason = NULL, *reason2 = NULL;
00886 
00887    if (!(response = iks_new("iq")) ||
00888        !(error = iks_new("error")) ||
00889        !(reason = iks_new(reasonstr))) {
00890       ast_log(LOG_ERROR, "Unable to allocate IKS error response stanzas\n");
00891       goto end;
00892    }
00893 
00894    iks_insert_attrib(response, "type", "error");
00895    iks_insert_attrib(response, "from", connection->jid->full);
00896    iks_insert_attrib(response, "to", iks_find_attrib(pak->x, "from"));
00897    iks_insert_attrib(response, "id", iks_find_attrib(pak->x, "id"));
00898 
00899    iks_insert_attrib(error, "type", type);
00900    iks_insert_node(error, reason);
00901 
00902    if (!ast_strlen_zero(reasonstr2) && (reason2 = iks_new(reasonstr2))) {
00903       iks_insert_node(error, reason2);
00904    }
00905 
00906    iks_insert_node(response, error);
00907 
00908    ast_xmpp_client_send(connection, response);
00909 end:
00910    iks_delete(reason2);
00911    iks_delete(reason);
00912    iks_delete(error);
00913    iks_delete(response);
00914 }
00915 
00916 /*! \brief Internal helper function which adds ICE-UDP candidates to a transport node */
00917 static int jingle_add_ice_udp_candidates_to_transport(struct ast_rtp_instance *rtp, iks *transport, iks **candidates, unsigned int maximum)
00918 {
00919    struct ast_rtp_engine_ice *ice;
00920    struct ao2_container *local_candidates;
00921    struct ao2_iterator it;
00922    struct ast_rtp_engine_ice_candidate *candidate;
00923    int i = 0, res = 0;
00924 
00925    if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
00926       ast_log(LOG_ERROR, "Unable to add ICE-UDP candidates as ICE support not available or no candidates available\n");
00927       return -1;
00928    }
00929 
00930    iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
00931    iks_insert_attrib(transport, "pwd", ice->get_password(rtp));
00932    iks_insert_attrib(transport, "ufrag", ice->get_ufrag(rtp));
00933 
00934    it = ao2_iterator_init(local_candidates, 0);
00935 
00936    while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
00937       iks *local_candidate;
00938       char tmp[30];
00939 
00940       if (!(local_candidate = iks_new("candidate"))) {
00941          res = -1;
00942          ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for ICE-UDP transport\n");
00943          break;
00944       }
00945 
00946       snprintf(tmp, sizeof(tmp), "%u", candidate->id);
00947       iks_insert_attrib(local_candidate, "component", tmp);
00948       snprintf(tmp, sizeof(tmp), "%d", ast_str_hash(candidate->foundation));
00949       iks_insert_attrib(local_candidate, "foundation", tmp);
00950       iks_insert_attrib(local_candidate, "generation", "0");
00951       iks_insert_attrib(local_candidate, "network", "0");
00952       snprintf(tmp, sizeof(tmp), "%04lx", (unsigned long)(ast_random() & 0xffff));
00953       iks_insert_attrib(local_candidate, "id", tmp);
00954       iks_insert_attrib(local_candidate, "ip", ast_sockaddr_stringify_host(&candidate->address));
00955       iks_insert_attrib(local_candidate, "port", ast_sockaddr_stringify_port(&candidate->address));
00956       snprintf(tmp, sizeof(tmp), "%d", candidate->priority);
00957       iks_insert_attrib(local_candidate, "priority", tmp);
00958       iks_insert_attrib(local_candidate, "protocol", "udp");
00959 
00960       if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
00961          iks_insert_attrib(local_candidate, "type", "host");
00962       } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
00963          iks_insert_attrib(local_candidate, "type", "srflx");
00964       } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_RELAYED) {
00965          iks_insert_attrib(local_candidate, "type", "relay");
00966       }
00967 
00968       iks_insert_node(transport, local_candidate);
00969       candidates[i++] = local_candidate;
00970    }
00971 
00972    ao2_iterator_destroy(&it);
00973    ao2_ref(local_candidates, -1);
00974 
00975    return res;
00976 }
00977 
00978 /*! \brief Internal helper function which adds Google candidates to a transport node */
00979 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)
00980 {
00981    struct ast_rtp_engine_ice *ice;
00982    struct ao2_container *local_candidates;
00983    struct ao2_iterator it;
00984    struct ast_rtp_engine_ice_candidate *candidate;
00985    int i = 0, res = 0;
00986 
00987    if (!(ice = ast_rtp_instance_get_ice(rtp)) || !(local_candidates = ice->get_local_candidates(rtp))) {
00988       ast_log(LOG_ERROR, "Unable to add Google ICE candidates as ICE support not available or no candidates available\n");
00989       return -1;
00990    }
00991 
00992    if (transport_type != JINGLE_TRANSPORT_GOOGLE_V1) {
00993       iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
00994    }
00995 
00996    it = ao2_iterator_init(local_candidates, 0);
00997 
00998    while ((candidate = ao2_iterator_next(&it)) && (i < maximum)) {
00999       iks *local_candidate;
01000       /* In Google land a username is 16 bytes, explicitly */
01001       char ufrag[17] = "";
01002 
01003       if (!(local_candidate = iks_new("candidate"))) {
01004          res = -1;
01005          ast_log(LOG_ERROR, "Unable to allocate IKS candidate stanza for Google ICE transport\n");
01006          break;
01007       }
01008 
01009       if (candidate->id == 1) {
01010          iks_insert_attrib(local_candidate, "name", !video ? "rtp" : "video_rtp");
01011       } else if (candidate->id == 2) {
01012          iks_insert_attrib(local_candidate, "name", !video ? "rtcp" : "video_rtcp");
01013       } else {
01014          iks_delete(local_candidate);
01015          continue;
01016       }
01017 
01018       iks_insert_attrib(local_candidate, "address", ast_sockaddr_stringify_host(&candidate->address));
01019       iks_insert_attrib(local_candidate, "port", ast_sockaddr_stringify_port(&candidate->address));
01020 
01021       if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
01022          iks_insert_attrib(local_candidate, "preference", "0.95");
01023          iks_insert_attrib(local_candidate, "type", "local");
01024       } else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
01025          iks_insert_attrib(local_candidate, "preference", "0.9");
01026          iks_insert_attrib(local_candidate, "type", "stun");
01027       }
01028 
01029       iks_insert_attrib(local_candidate, "protocol", "udp");
01030       iks_insert_attrib(local_candidate, "network", "0");
01031       snprintf(ufrag, sizeof(ufrag), "%s", ice->get_ufrag(rtp));
01032       iks_insert_attrib(local_candidate, "username", ufrag);
01033       iks_insert_attrib(local_candidate, "generation", "0");
01034 
01035       if (transport_type == JINGLE_TRANSPORT_GOOGLE_V1) {
01036          iks_insert_attrib(local_candidate, "password", "");
01037          iks_insert_attrib(local_candidate, "foundation", "0");
01038          iks_insert_attrib(local_candidate, "component", "1");
01039       } else {
01040          iks_insert_attrib(local_candidate, "password", ice->get_password(rtp));
01041       }
01042 
01043       /* You may notice a lack of relay support up above - this is because we don't support it for use with
01044        * the Google talk transport due to their arcane support. */
01045 
01046       iks_insert_node(transport, local_candidate);
01047       candidates[i++] = local_candidate;
01048    }
01049 
01050    ao2_iterator_destroy(&it);
01051    ao2_ref(local_candidates, -1);
01052 
01053    return res;
01054 }
01055 
01056 /*! \brief Internal function which sends a session-terminate message */
01057 static void jingle_send_session_terminate(struct jingle_session *session, const char *reasontext)
01058 {
01059    iks *iq = NULL, *jingle = NULL, *reason = NULL, *text = NULL;
01060 
01061    if (!(iq = iks_new("iq")) || !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle")) ||
01062        !(reason = iks_new("reason")) || !(text = iks_new(reasontext))) {
01063       ast_log(LOG_ERROR, "Failed to allocate stanzas for session-terminate message on session '%s'\n", session->sid);
01064       goto end;
01065    }
01066 
01067    iks_insert_attrib(iq, "to", session->remote);
01068    iks_insert_attrib(iq, "type", "set");
01069    iks_insert_attrib(iq, "id", session->connection->mid);
01070    ast_xmpp_increment_mid(session->connection->mid);
01071 
01072    if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01073       iks_insert_attrib(jingle, "type", "terminate");
01074       iks_insert_attrib(jingle, "id", session->sid);
01075       iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
01076       iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : session->remote);
01077    } else {
01078       iks_insert_attrib(jingle, "action", "session-terminate");
01079       iks_insert_attrib(jingle, "sid", session->sid);
01080       iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
01081    }
01082 
01083    iks_insert_node(iq, jingle);
01084    iks_insert_node(jingle, reason);
01085    iks_insert_node(reason, text);
01086 
01087    ast_xmpp_client_send(session->connection, iq);
01088 
01089 end:
01090    iks_delete(text);
01091    iks_delete(reason);
01092    iks_delete(jingle);
01093    iks_delete(iq);
01094 }
01095 
01096 /*! \brief Internal function which sends a session-info message */
01097 static void jingle_send_session_info(struct jingle_session *session, const char *info)
01098 {
01099    iks *iq = NULL, *jingle = NULL, *text = NULL;
01100 
01101    /* Google-V1 has no way to send informational messages so don't even bother trying */
01102    if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01103       return;
01104    }
01105 
01106    if (!(iq = iks_new("iq")) || !(jingle = iks_new("jingle")) || !(text = iks_new(info))) {
01107       ast_log(LOG_ERROR, "Failed to allocate stanzas for session-info message on session '%s'\n", session->sid);
01108       goto end;
01109    }
01110 
01111    iks_insert_attrib(iq, "to", session->remote);
01112    iks_insert_attrib(iq, "type", "set");
01113    iks_insert_attrib(iq, "id", session->connection->mid);
01114    ast_xmpp_increment_mid(session->connection->mid);
01115 
01116    iks_insert_attrib(jingle, "action", "session-info");
01117    iks_insert_attrib(jingle, "sid", session->sid);
01118    iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
01119    iks_insert_node(iq, jingle);
01120    iks_insert_node(jingle, text);
01121 
01122    ast_xmpp_client_send(session->connection, iq);
01123 
01124 end:
01125    iks_delete(text);
01126    iks_delete(jingle);
01127    iks_delete(iq);
01128 }
01129 
01130 /*! \internal
01131  *
01132  * \brief Locks both pvt and pvt owner if owner is present.
01133  *
01134  * \note This function gives a ref to pvt->owner if it is present and locked.
01135  *       This reference must be decremented after pvt->owner is unlocked.
01136  *
01137  * \note This function will never give you up,
01138  * \note This function will never let you down.
01139  * \note This function will run around and desert you.
01140  *
01141  * \pre pvt is not locked
01142  * \post pvt is locked
01143  * \post pvt->owner is locked and its reference count is increased (if pvt->owner is not NULL)
01144  *
01145  * \returns a pointer to the locked and reffed pvt->owner channel if it exists.
01146  */
01147 static struct ast_channel *jingle_session_lock_full(struct jingle_session *pvt)
01148 {
01149    struct ast_channel *chan;
01150 
01151    /* Locking is simple when it is done right.  If you see a deadlock resulting
01152     * in this function, it is not this function's fault, Your problem exists elsewhere.
01153     * This function is perfect... seriously. */
01154    for (;;) {
01155       /* First, get the channel and grab a reference to it */
01156       ao2_lock(pvt);
01157       chan = pvt->owner;
01158       if (chan) {
01159          /* The channel can not go away while we hold the pvt lock.
01160           * Give the channel a ref so it will not go away after we let
01161           * the pvt lock go. */
01162          ast_channel_ref(chan);
01163       } else {
01164          /* no channel, return pvt locked */
01165          return NULL;
01166       }
01167 
01168       /* We had to hold the pvt lock while getting a ref to the owner channel
01169        * but now we have to let this lock go in order to preserve proper
01170        * locking order when grabbing the channel lock */
01171       ao2_unlock(pvt);
01172 
01173       /* Look, no deadlock avoidance, hooray! */
01174       ast_channel_lock(chan);
01175       ao2_lock(pvt);
01176       if (pvt->owner == chan) {
01177          /* done */
01178          break;
01179       }
01180 
01181       /* If the owner changed while everything was unlocked, no problem,
01182        * just start over and everthing will work.  This is rare, do not be
01183        * confused by this loop and think this it is an expensive operation.
01184        * The majority of the calls to this function will never involve multiple
01185        * executions of this loop. */
01186       ast_channel_unlock(chan);
01187       ast_channel_unref(chan);
01188       ao2_unlock(pvt);
01189    }
01190 
01191    /* If owner exists, it is locked and reffed */
01192    return pvt->owner;
01193 }
01194 
01195 /*! \brief Helper function which queues a hangup frame with cause code */
01196 static void jingle_queue_hangup_with_cause(struct jingle_session *session, int cause)
01197 {
01198    struct ast_channel *chan;
01199 
01200    if ((chan = jingle_session_lock_full(session))) {
01201       ast_debug(3, "Hanging up channel '%s' with cause '%d'\n", ast_channel_name(chan), cause);
01202       ast_queue_hangup_with_cause(chan, cause);
01203       ast_channel_unlock(chan);
01204       ast_channel_unref(chan);
01205    }
01206    ao2_unlock(session);
01207 }
01208 
01209 /*! \brief Internal function which sends a transport-info message */
01210 static void jingle_send_transport_info(struct jingle_session *session, const char *from)
01211 {
01212    iks *iq, *jingle = NULL, *audio = NULL, *audio_transport = NULL, *video = NULL, *video_transport = NULL;
01213    iks *audio_candidates[session->maxicecandidates], *video_candidates[session->maxicecandidates];
01214    int i, res = 0;
01215 
01216    if (!(iq = iks_new("iq")) ||
01217        !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
01218       iks_delete(iq);
01219       jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
01220       ast_log(LOG_ERROR, "Failed to allocate stanzas for transport-info message, hanging up session '%s'\n", session->sid);
01221       return;
01222    }
01223 
01224    memset(audio_candidates, 0, sizeof(audio_candidates));
01225    memset(video_candidates, 0, sizeof(video_candidates));
01226 
01227    iks_insert_attrib(iq, "from", session->connection->jid->full);
01228    iks_insert_attrib(iq, "to", from);
01229    iks_insert_attrib(iq, "type", "set");
01230    iks_insert_attrib(iq, "id", session->connection->mid);
01231    ast_xmpp_increment_mid(session->connection->mid);
01232 
01233    if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01234       iks_insert_attrib(jingle, "type", "candidates");
01235       iks_insert_attrib(jingle, "id", session->sid);
01236       iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
01237       iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : from);
01238    } else {
01239       iks_insert_attrib(jingle, "action", "transport-info");
01240       iks_insert_attrib(jingle, "sid", session->sid);
01241       iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
01242    }
01243    iks_insert_node(iq, jingle);
01244 
01245    if (session->rtp) {
01246       if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01247          /* V1 protocol has the candidates directly in the session */
01248          res = jingle_add_google_candidates_to_transport(session->rtp, jingle, audio_candidates, 0, session->transport, session->maxicecandidates);
01249       } else if ((audio = iks_new("content")) && (audio_transport = iks_new("transport"))) {
01250          iks_insert_attrib(audio, "creator", session->outgoing ? "initiator" : "responder");
01251          iks_insert_attrib(audio, "name", session->audio_name);
01252          iks_insert_node(jingle, audio);
01253          iks_insert_node(audio, audio_transport);
01254 
01255          if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
01256             res = jingle_add_ice_udp_candidates_to_transport(session->rtp, audio_transport, audio_candidates, session->maxicecandidates);
01257          } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
01258             res = jingle_add_google_candidates_to_transport(session->rtp, audio_transport, audio_candidates, 0, session->transport,
01259                               session->maxicecandidates);
01260          }
01261       } else {
01262          res = -1;
01263       }
01264    }
01265 
01266    if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
01267       if ((video = iks_new("content")) && (video_transport = iks_new("transport"))) {
01268          iks_insert_attrib(video, "creator", session->outgoing ? "initiator" : "responder");
01269          iks_insert_attrib(video, "name", session->video_name);
01270          iks_insert_node(jingle, video);
01271          iks_insert_node(video, video_transport);
01272 
01273          if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
01274             res = jingle_add_ice_udp_candidates_to_transport(session->vrtp, video_transport, video_candidates, session->maxicecandidates);
01275          } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
01276             res = jingle_add_google_candidates_to_transport(session->vrtp, video_transport, video_candidates, 1, session->transport,
01277                               session->maxicecandidates);
01278          }
01279       } else {
01280          res = -1;
01281       }
01282    }
01283 
01284    if (!res) {
01285       ast_xmpp_client_send(session->connection, iq);
01286    } else {
01287       jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
01288    }
01289 
01290    /* Clean up after ourselves */
01291    for (i = 0; i < session->maxicecandidates; i++) {
01292       iks_delete(video_candidates[i]);
01293       iks_delete(audio_candidates[i]);
01294    }
01295 
01296    iks_delete(video_transport);
01297    iks_delete(video);
01298    iks_delete(audio_transport);
01299    iks_delete(audio);
01300    iks_delete(jingle);
01301    iks_delete(iq);
01302 }
01303 
01304 /*! \brief Internal helper function which adds payloads to a description */
01305 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)
01306 {
01307    int x = 0, i = 0, res = 0;
01308 
01309    for (x = 0; (x < ast_format_cap_count(session->jointcap)) && (i < (session->maxpayloads - 2)); x++) {
01310       struct ast_format *format = ast_format_cap_get_format(session->jointcap, x);
01311       int rtp_code;
01312       iks *payload;
01313       char tmp[32];
01314 
01315       if (ast_format_get_type(format) != type) {
01316          ao2_ref(format, -1);
01317          continue;
01318       }
01319 
01320       if (((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1, format, 0)) == -1) ||
01321           (!(payload = iks_new("payload-type")))) {
01322          ao2_ref(format, -1);
01323          return -1;
01324       }
01325 
01326       if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01327          iks_insert_attrib(payload, "xmlns", GOOGLE_PHONE_NS);
01328       }
01329 
01330       snprintf(tmp, sizeof(tmp), "%d", rtp_code);
01331       iks_insert_attrib(payload, "id", tmp);
01332       iks_insert_attrib(payload, "name", ast_rtp_lookup_mime_subtype2(1, format, 0, 0));
01333       iks_insert_attrib(payload, "channels", "1");
01334 
01335       if ((ast_format_cmp(format, ast_format_g722) == AST_FORMAT_CMP_EQUAL) &&
01336          ((session->transport == JINGLE_TRANSPORT_GOOGLE_V1) || (session->transport == JINGLE_TRANSPORT_GOOGLE_V2))) {
01337          iks_insert_attrib(payload, "clockrate", "16000");
01338       } else {
01339          snprintf(tmp, sizeof(tmp), "%u", ast_rtp_lookup_sample_rate2(1, format, 0));
01340          iks_insert_attrib(payload, "clockrate", tmp);
01341       }
01342 
01343       if ((type == AST_MEDIA_TYPE_VIDEO) && (session->transport == JINGLE_TRANSPORT_GOOGLE_V2)) {
01344          iks *parameter;
01345 
01346          /* Google requires these parameters to be set, but alas we can not give accurate values so use some safe defaults */
01347          if ((parameter = iks_new("parameter"))) {
01348             iks_insert_attrib(parameter, "name", "width");
01349             iks_insert_attrib(parameter, "value", "640");
01350             iks_insert_node(payload, parameter);
01351          }
01352          if ((parameter = iks_new("parameter"))) {
01353             iks_insert_attrib(parameter, "name", "height");
01354             iks_insert_attrib(parameter, "value", "480");
01355             iks_insert_node(payload, parameter);
01356          }
01357          if ((parameter = iks_new("parameter"))) {
01358             iks_insert_attrib(parameter, "name", "framerate");
01359             iks_insert_attrib(parameter, "value", "30");
01360             iks_insert_node(payload, parameter);
01361          }
01362       }
01363 
01364       iks_insert_node(description, payload);
01365       payloads[i++] = payload;
01366 
01367       ao2_ref(format, -1);
01368    }
01369    /* If this is for audio and there is room for RFC2833 add it in */
01370    if ((type == AST_MEDIA_TYPE_AUDIO) && (i < session->maxpayloads)) {
01371       iks *payload;
01372 
01373       if ((payload = iks_new("payload-type"))) {
01374          if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01375             iks_insert_attrib(payload, "xmlns", GOOGLE_PHONE_NS);
01376          }
01377 
01378          iks_insert_attrib(payload, "id", "101");
01379          iks_insert_attrib(payload, "name", "telephone-event");
01380          iks_insert_attrib(payload, "channels", "1");
01381          iks_insert_attrib(payload, "clockrate", "8000");
01382          iks_insert_node(description, payload);
01383          payloads[i++] = payload;
01384       }
01385    }
01386 
01387    return res;
01388 }
01389 
01390 /*! \brief Helper function which adds content to a description */
01391 static int jingle_add_content(struct jingle_session *session, iks *jingle, iks *content, iks *description, iks *transport,
01392                const char *name, enum ast_media_type type, struct ast_rtp_instance *rtp, iks **payloads)
01393 {
01394    int res = 0;
01395 
01396    if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
01397       iks_insert_attrib(content, "creator", session->outgoing ? "initiator" : "responder");
01398       iks_insert_attrib(content, "name", name);
01399       iks_insert_node(jingle, content);
01400 
01401       iks_insert_attrib(description, "xmlns", JINGLE_RTP_NS);
01402       if (type == AST_MEDIA_TYPE_AUDIO) {
01403          iks_insert_attrib(description, "media", "audio");
01404       } else if (type == AST_MEDIA_TYPE_VIDEO) {
01405          iks_insert_attrib(description, "media", "video");
01406       } else {
01407          return -1;
01408       }
01409       iks_insert_node(content, description);
01410    } else {
01411       iks_insert_attrib(description, "xmlns", GOOGLE_PHONE_NS);
01412       iks_insert_node(jingle, description);
01413    }
01414 
01415    if (!(res = jingle_add_payloads_to_description(session, rtp, description, payloads, type))) {
01416       if (session->transport == JINGLE_TRANSPORT_ICE_UDP) {
01417          iks_insert_attrib(transport, "xmlns", JINGLE_ICE_UDP_NS);
01418          iks_insert_node(content, transport);
01419       } else if (session->transport == JINGLE_TRANSPORT_GOOGLE_V2) {
01420          iks_insert_attrib(transport, "xmlns", GOOGLE_TRANSPORT_NS);
01421          iks_insert_node(content, transport);
01422       }
01423    }
01424 
01425    return res;
01426 }
01427 
01428 /*! \brief Internal function which sends a complete session message */
01429 static void jingle_send_session_action(struct jingle_session *session, const char *action)
01430 {
01431    iks *iq, *jingle, *audio = NULL, *audio_description = NULL, *video = NULL, *video_description = NULL;
01432    iks *audio_payloads[session->maxpayloads], *video_payloads[session->maxpayloads];
01433    iks *audio_transport = NULL, *video_transport = NULL;
01434    int i, res = 0;
01435 
01436    if (!(iq = iks_new("iq")) ||
01437        !(jingle = iks_new(session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "session" : "jingle"))) {
01438       jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
01439       iks_delete(iq);
01440       return;
01441    }
01442 
01443    memset(audio_payloads, 0, sizeof(audio_payloads));
01444    memset(video_payloads, 0, sizeof(video_payloads));
01445 
01446    iks_insert_attrib(iq, "from", session->connection->jid->full);
01447    iks_insert_attrib(iq, "to", session->remote);
01448    iks_insert_attrib(iq, "type", "set");
01449    iks_insert_attrib(iq, "id", session->connection->mid);
01450    ast_xmpp_increment_mid(session->connection->mid);
01451 
01452    if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
01453       iks_insert_attrib(jingle, "type", action);
01454       iks_insert_attrib(jingle, "id", session->sid);
01455       iks_insert_attrib(jingle, "xmlns", GOOGLE_SESSION_NS);
01456    } else {
01457       iks_insert_attrib(jingle, "action", action);
01458       iks_insert_attrib(jingle, "sid", session->sid);
01459       iks_insert_attrib(jingle, "xmlns", JINGLE_NS);
01460    }
01461 
01462    if (!strcasecmp(action, "session-initiate") || !strcasecmp(action, "initiate") || !strcasecmp(action, "accept")) {
01463       iks_insert_attrib(jingle, "initiator", session->outgoing ? session->connection->jid->full : session->remote);
01464    }
01465 
01466    iks_insert_node(iq, jingle);
01467 
01468    if (session->rtp && (audio = iks_new("content")) && (audio_description = iks_new("description")) &&
01469        (audio_transport = iks_new("transport"))) {
01470       res = jingle_add_content(session, jingle, audio, audio_description, audio_transport, session->audio_name,
01471                 AST_MEDIA_TYPE_AUDIO, session->rtp, audio_payloads);
01472    } else {
01473       ast_log(LOG_ERROR, "Failed to allocate audio content stanzas for session '%s', hanging up\n", session->sid);
01474       res = -1;
01475    }
01476 
01477    if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V1) && !res && session->vrtp) {
01478       if ((video = iks_new("content")) && (video_description = iks_new("description")) &&
01479           (video_transport = iks_new("transport"))) {
01480          res = jingle_add_content(session, jingle, video, video_description, video_transport, session->video_name,
01481                    AST_MEDIA_TYPE_VIDEO, session->vrtp, video_payloads);
01482       } else {
01483          ast_log(LOG_ERROR, "Failed to allocate video content stanzas for session '%s', hanging up\n", session->sid);
01484          res = -1;
01485       }
01486    }
01487 
01488    if (!res) {
01489       ast_xmpp_client_send(session->connection, iq);
01490    } else {
01491       jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
01492    }
01493 
01494    iks_delete(video_transport);
01495    iks_delete(audio_transport);
01496 
01497    for (i = 0; i < session->maxpayloads; i++) {
01498       iks_delete(video_payloads[i]);
01499       iks_delete(audio_payloads[i]);
01500    }
01501 
01502    iks_delete(video_description);
01503    iks_delete(video);
01504    iks_delete(audio_description);
01505    iks_delete(audio);
01506    iks_delete(jingle);
01507    iks_delete(iq);
01508 }
01509 
01510 /*! \brief Internal function which sends a session-inititate message */
01511 static void jingle_send_session_initiate(struct jingle_session *session)
01512 {
01513    jingle_send_session_action(session, session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "initiate" : "session-initiate");
01514 }
01515 
01516 /*! \brief Internal function which sends a session-accept message */
01517 static void jingle_send_session_accept(struct jingle_session *session)
01518 {
01519    jingle_send_session_action(session, session->transport == JINGLE_TRANSPORT_GOOGLE_V1 ? "accept" : "session-accept");
01520 }
01521 
01522 /*! \brief Callback for when a response is received for an outgoing session-initiate message */
01523 static int jingle_outgoing_hook(void *data, ikspak *pak)
01524 {
01525    struct jingle_session *session = data;
01526    iks *error = iks_find(pak->x, "error"), *redirect;
01527 
01528    /* In all cases this hook is done with */
01529    iks_filter_remove_rule(session->connection->filter, session->rule);
01530    session->rule = NULL;
01531 
01532    ast_callid_threadassoc_add(session->callid);
01533 
01534    /* If no error occurred they accepted our session-initiate message happily */
01535    if (!error) {
01536       struct ast_channel *chan;
01537 
01538       if ((chan = jingle_session_lock_full(session))) {
01539          ast_queue_control(chan, AST_CONTROL_PROCEEDING);
01540          ast_channel_unlock(chan);
01541          ast_channel_unref(chan);
01542       }
01543       ao2_unlock(session);
01544 
01545       jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
01546 
01547       goto end;
01548    }
01549 
01550    /* Assume that because this is an error the session is gone, there is only one case where this is incorrect - a redirect */
01551    session->gone = 1;
01552 
01553    /* Map the error we received to an appropriate cause code and hang up the channel */
01554    if ((redirect = iks_find_with_attrib(error, "redirect", "xmlns", XMPP_STANZAS_NS))) {
01555       iks *to = iks_child(redirect);
01556       char *target;
01557 
01558       if (to && (target = iks_name(to)) && !ast_strlen_zero(target)) {
01559          /* Make the xmpp: go away if it is present */
01560          if (!strncmp(target, "xmpp:", 5)) {
01561             target += 5;
01562          }
01563 
01564          /* This is actually a fairly simple operation - we update the remote and send another session-initiate */
01565          ast_copy_string(session->remote, target, sizeof(session->remote));
01566 
01567          /* Add a new hook so we can get the status of redirected session */
01568          session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
01569                          IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);
01570 
01571          jingle_send_session_initiate(session);
01572 
01573          session->gone = 0;
01574       } else {
01575          jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
01576       }
01577    } else if (iks_find_with_attrib(error, "service-unavailable", "xmlns", XMPP_STANZAS_NS)) {
01578       jingle_queue_hangup_with_cause(session, AST_CAUSE_CONGESTION);
01579    } else if (iks_find_with_attrib(error, "resource-constraint", "xmlns", XMPP_STANZAS_NS)) {
01580       jingle_queue_hangup_with_cause(session, AST_CAUSE_REQUESTED_CHAN_UNAVAIL);
01581    } else if (iks_find_with_attrib(error, "bad-request", "xmlns", XMPP_STANZAS_NS)) {
01582       jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
01583    } else if (iks_find_with_attrib(error, "remote-server-not-found", "xmlns", XMPP_STANZAS_NS)) {
01584       jingle_queue_hangup_with_cause(session, AST_CAUSE_NO_ROUTE_DESTINATION);
01585    } else if (iks_find_with_attrib(error, "feature-not-implemented", "xmlns", XMPP_STANZAS_NS)) {
01586       /* Assume that this occurred because the remote side does not support our transport, so drop it down one and try again */
01587       session->transport--;
01588 
01589       /* If we still have a viable transport mechanism re-send the session-initiate */
01590       if (session->transport != JINGLE_TRANSPORT_NONE) {
01591          struct ast_rtp_engine_ice *ice;
01592 
01593          if (((session->transport == JINGLE_TRANSPORT_GOOGLE_V2) ||
01594               (session->transport == JINGLE_TRANSPORT_GOOGLE_V1)) &&
01595              (ice = ast_rtp_instance_get_ice(session->rtp))) {
01596             /* We stop built in ICE support because we need to fall back to old old old STUN support */
01597             ice->stop(session->rtp);
01598          }
01599 
01600          /* Re-send the message to the *original* target and not a redirected one */
01601          ast_copy_string(session->remote, session->remote_original, sizeof(session->remote));
01602 
01603          session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
01604                          IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);
01605 
01606          jingle_send_session_initiate(session);
01607 
01608          session->gone = 0;
01609       } else {
01610          /* Otherwise we have exhausted all transports */
01611          jingle_queue_hangup_with_cause(session, AST_CAUSE_FACILITY_NOT_IMPLEMENTED);
01612       }
01613    } else {
01614       jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
01615    }
01616 
01617 end:
01618    ast_callid_threadassoc_remove();
01619 
01620    return IKS_FILTER_EAT;
01621 }
01622 
01623 /*! \brief Function called by core when we should answer a Jingle session */
01624 static int jingle_answer(struct ast_channel *ast)
01625 {
01626    struct jingle_session *session = ast_channel_tech_pvt(ast);
01627 
01628    /* The channel has already been answered so we don't need to do anything */
01629    if (ast_channel_state(ast) == AST_STATE_UP) {
01630       return 0;
01631    }
01632 
01633    jingle_send_session_accept(session);
01634 
01635    return 0;
01636 }
01637 
01638 /*! \brief Function called by core to read any waiting frames */
01639 static struct ast_frame *jingle_read(struct ast_channel *ast)
01640 {
01641    struct jingle_session *session = ast_channel_tech_pvt(ast);
01642    struct ast_frame *frame = &ast_null_frame;
01643 
01644    switch (ast_channel_fdno(ast)) {
01645    case 0:
01646       if (session->rtp) {
01647          frame = ast_rtp_instance_read(session->rtp, 0);
01648       }
01649       break;
01650    case 1:
01651       if (session->rtp) {
01652          frame = ast_rtp_instance_read(session->rtp, 1);
01653       }
01654       break;
01655    case 2:
01656       if (session->vrtp) {
01657          frame = ast_rtp_instance_read(session->vrtp, 0);
01658       }
01659       break;
01660    case 3:
01661       if (session->vrtp) {
01662          frame = ast_rtp_instance_read(session->vrtp, 1);
01663       }
01664       break;
01665    default:
01666       break;
01667    }
01668 
01669    if (frame && frame->frametype == AST_FRAME_VOICE &&
01670        ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
01671       if (ast_format_cap_iscompatible_format(session->jointcap, frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
01672          ast_debug(1, "Bogus frame of format '%s' received from '%s'!\n",
01673               ast_format_get_name(frame->subclass.format), ast_channel_name(ast));
01674          ast_frfree(frame);
01675          frame = &ast_null_frame;
01676       } else {
01677          struct ast_format_cap *caps;
01678 
01679          ast_debug(1, "Oooh, format changed to %s\n",
01680               ast_format_get_name(frame->subclass.format));
01681 
01682          caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
01683          if (caps) {
01684             ast_format_cap_append(caps, frame->subclass.format, 0);
01685             ast_channel_nativeformats_set(ast, caps);
01686             ao2_ref(caps, -1);
01687          }
01688          ast_set_read_format(ast, ast_channel_readformat(ast));
01689          ast_set_write_format(ast, ast_channel_writeformat(ast));
01690       }
01691    }
01692 
01693    return frame;
01694 }
01695 
01696 /*! \brief Function called by core to write frames */
01697 static int jingle_write(struct ast_channel *ast, struct ast_frame *frame)
01698 {
01699    struct jingle_session *session = ast_channel_tech_pvt(ast);
01700    int res = 0;
01701 
01702    switch (frame->frametype) {
01703    case AST_FRAME_VOICE:
01704       if (ast_format_cap_iscompatible_format(ast_channel_nativeformats(ast), frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
01705          struct ast_str *codec_buf = ast_str_alloca(64);
01706 
01707          ast_log(LOG_WARNING,
01708             "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
01709             ast_format_get_name(frame->subclass.format),
01710             ast_format_cap_get_names(ast_channel_nativeformats(ast), &codec_buf),
01711             ast_format_get_name(ast_channel_readformat(ast)),
01712             ast_format_get_name(ast_channel_writeformat(ast)));
01713          return 0;
01714       }
01715       if (session && session->rtp) {
01716          res = ast_rtp_instance_write(session->rtp, frame);
01717       }
01718       break;
01719    case AST_FRAME_VIDEO:
01720       if (session && session->vrtp) {
01721          res = ast_rtp_instance_write(session->vrtp, frame);
01722       }
01723       break;
01724    default:
01725       ast_log(LOG_WARNING, "Can't send %u type frames with Jingle write\n",
01726          frame->frametype);
01727       return 0;
01728    }
01729 
01730    return res;
01731 }
01732 
01733 /*! \brief Function called by core to change the underlying owner channel */
01734 static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
01735 {
01736    struct jingle_session *session = ast_channel_tech_pvt(newchan);
01737 
01738    ao2_lock(session);
01739 
01740    jingle_set_owner(session, newchan);
01741 
01742    ao2_unlock(session);
01743 
01744    return 0;
01745 }
01746 
01747 /*! \brief Function called by core to ask the channel to indicate some sort of condition */
01748 static int jingle_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
01749 {
01750    struct jingle_session *session = ast_channel_tech_pvt(ast);
01751    int res = 0;
01752 
01753    switch (condition) {
01754    case AST_CONTROL_RINGING:
01755       if (ast_channel_state(ast) == AST_STATE_RING) {
01756          jingle_send_session_info(session, "ringing xmlns='urn:xmpp:jingle:apps:rtp:info:1'");
01757       } else {
01758          res = -1;
01759       }
01760       break;
01761    case AST_CONTROL_BUSY:
01762       if (ast_channel_state(ast) != AST_STATE_UP) {
01763          ast_channel_hangupcause_set(ast, AST_CAUSE_BUSY);
01764          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
01765       } else {
01766          res = -1;
01767       }
01768       break;
01769    case AST_CONTROL_CONGESTION:
01770       if (ast_channel_state(ast) != AST_STATE_UP) {
01771          ast_channel_hangupcause_set(ast, AST_CAUSE_CONGESTION);
01772          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
01773       } else {
01774          res = -1;
01775       }
01776       break;
01777    case AST_CONTROL_INCOMPLETE:
01778       if (ast_channel_state(ast) != AST_STATE_UP) {
01779          ast_channel_hangupcause_set(ast, AST_CAUSE_CONGESTION);
01780          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
01781       }
01782       break;
01783    case AST_CONTROL_HOLD:
01784       ast_moh_start(ast, data, NULL);
01785       break;
01786    case AST_CONTROL_UNHOLD:
01787       ast_moh_stop(ast);
01788       break;
01789    case AST_CONTROL_SRCUPDATE:
01790       if (session->rtp) {
01791          ast_rtp_instance_update_source(session->rtp);
01792       }
01793       break;
01794    case AST_CONTROL_SRCCHANGE:
01795       if (session->rtp) {
01796          ast_rtp_instance_change_source(session->rtp);
01797       }
01798       break;
01799    case AST_CONTROL_VIDUPDATE:
01800    case AST_CONTROL_UPDATE_RTP_PEER:
01801    case AST_CONTROL_CONNECTED_LINE:
01802       break;
01803    case AST_CONTROL_PVT_CAUSE_CODE:
01804    case AST_CONTROL_MASQUERADE_NOTIFY:
01805    case -1:
01806       res = -1;
01807       break;
01808    default:
01809       ast_log(LOG_NOTICE, "Don't know how to indicate condition '%d'\n", condition);
01810       res = -1;
01811    }
01812 
01813    return res;
01814 }
01815 
01816 /*! \brief Function called by core to send text to the remote party of the Jingle session */
01817 static int jingle_sendtext(struct ast_channel *chan, const char *text)
01818 {
01819    struct jingle_session *session = ast_channel_tech_pvt(chan);
01820 
01821    return ast_xmpp_client_send_message(session->connection, session->remote, text);
01822 }
01823 
01824 /*! \brief Function called by core to start a DTMF digit */
01825 static int jingle_digit_begin(struct ast_channel *chan, char digit)
01826 {
01827    struct jingle_session *session = ast_channel_tech_pvt(chan);
01828 
01829    if (session->rtp) {
01830       ast_rtp_instance_dtmf_begin(session->rtp, digit);
01831    }
01832 
01833    return 0;
01834 }
01835 
01836 /*! \brief Function called by core to stop a DTMF digit */
01837 static int jingle_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
01838 {
01839    struct jingle_session *session = ast_channel_tech_pvt(ast);
01840 
01841    if (session->rtp) {
01842       ast_rtp_instance_dtmf_end_with_duration(session->rtp, digit, duration);
01843    }
01844 
01845    return 0;
01846 }
01847 
01848 /*! \brief Function called by core to actually start calling a remote party */
01849 static int jingle_call(struct ast_channel *ast, const char *dest, int timeout)
01850 {
01851    struct jingle_session *session = ast_channel_tech_pvt(ast);
01852 
01853    ast_setstate(ast, AST_STATE_RING);
01854 
01855    /* Since we have no idea of the remote capabilities use ours for now */
01856    ast_format_cap_append_from_cap(session->jointcap, session->cap, AST_MEDIA_TYPE_UNKNOWN);
01857 
01858    /* We set up a hook so we can know when our session-initiate message was accepted or rejected */
01859    session->rule = iks_filter_add_rule(session->connection->filter, jingle_outgoing_hook, session,
01860                    IKS_RULE_ID, session->connection->mid, IKS_RULE_DONE);
01861 
01862    jingle_send_session_initiate(session);
01863 
01864    return 0;
01865 }
01866 
01867 /*! \brief Function called by core to hang up a Jingle session */
01868 static int jingle_hangup(struct ast_channel *ast)
01869 {
01870    struct jingle_session *session = ast_channel_tech_pvt(ast);
01871 
01872    ao2_lock(session);
01873 
01874    if ((ast_channel_state(ast) != AST_STATE_DOWN) && !session->gone) {
01875       int cause = (session->owner ? ast_channel_hangupcause(session->owner) : AST_CAUSE_CONGESTION);
01876       const char *reason = "success";
01877       int i;
01878 
01879       /* Get the appropriate reason and send a session-terminate */
01880       for (i = 0; i < ARRAY_LEN(jingle_reason_mappings); i++) {
01881          if (jingle_reason_mappings[i].cause == cause) {
01882             reason = jingle_reason_mappings[i].reason;
01883             break;
01884          }
01885       }
01886 
01887       jingle_send_session_terminate(session, reason);
01888    }
01889 
01890    ast_channel_tech_pvt_set(ast, NULL);
01891    jingle_set_owner(session, NULL);
01892 
01893    ao2_unlink(session->state->sessions, session);
01894    ao2_ref(session->state, -1);
01895 
01896    ao2_unlock(session);
01897    ao2_ref(session, -1);
01898 
01899    return 0;
01900 }
01901 
01902 /*! \brief Function called by core to create a new outgoing Jingle session */
01903 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)
01904 {
01905    RAII_VAR(struct jingle_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01906    RAII_VAR(struct jingle_endpoint *, endpoint, NULL, ao2_cleanup);
01907    char *dialed, target[200] = "";
01908    struct ast_xmpp_buddy *buddy;
01909    struct jingle_session *session;
01910    struct ast_channel *chan;
01911    enum jingle_transport transport = JINGLE_TRANSPORT_NONE;
01912    struct ast_rtp_engine_ice *ice;
01913    AST_DECLARE_APP_ARGS(args,
01914               AST_APP_ARG(name);
01915               AST_APP_ARG(target);
01916       );
01917 
01918    /* We require at a minimum one audio format to be requested */
01919    if (!ast_format_cap_has_type(cap, AST_MEDIA_TYPE_AUDIO)) {
01920       ast_log(LOG_ERROR, "Motif channel driver requires an audio format when dialing a destination\n");
01921       *cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
01922       return NULL;
01923    }
01924 
01925    if (ast_strlen_zero(data) || !(dialed = ast_strdupa(data))) {
01926       ast_log(LOG_ERROR, "Unable to create channel with empty destination.\n");
01927       *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
01928       return NULL;
01929    }
01930 
01931    /* Parse the given dial string and validate the results */
01932    AST_NONSTANDARD_APP_ARGS(args, dialed, '/');
01933 
01934    if (ast_strlen_zero(args.name) || ast_strlen_zero(args.target)) {
01935       ast_log(LOG_ERROR, "Unable to determine endpoint name and target.\n");
01936       *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
01937       return NULL;
01938    }
01939 
01940    if (!(endpoint = jingle_endpoint_find(cfg->endpoints, args.name))) {
01941       ast_log(LOG_ERROR, "Endpoint '%s' does not exist.\n", args.name);
01942       *cause = AST_CAUSE_CHANNEL_UNACCEPTABLE;
01943       return NULL;
01944    }
01945 
01946    ao2_lock(endpoint->state);
01947 
01948    /* If we don't have a connection for the endpoint we can't exactly start a session on it */
01949    if (!endpoint->connection) {
01950       ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s' as no valid connection exists\n", args.name);
01951       *cause = AST_CAUSE_SWITCH_CONGESTION;
01952       ao2_unlock(endpoint->state);
01953       return NULL;
01954    }
01955 
01956    /* Find the target in the roster so we can choose a resource */
01957    if ((buddy = ao2_find(endpoint->connection->buddies, args.target, OBJ_KEY))) {
01958       struct ao2_iterator res;
01959       struct ast_xmpp_resource *resource;
01960 
01961       /* Iterate through finding the first viable Jingle capable resource */
01962       res = ao2_iterator_init(buddy->resources, 0);
01963       while ((resource = ao2_iterator_next(&res))) {
01964          if (resource->caps.jingle) {
01965             snprintf(target, sizeof(target), "%s/%s", args.target, resource->resource);
01966             transport = JINGLE_TRANSPORT_ICE_UDP;
01967             break;
01968          } else if (resource->caps.google) {
01969             snprintf(target, sizeof(target), "%s/%s", args.target, resource->resource);
01970             transport = JINGLE_TRANSPORT_GOOGLE_V2;
01971             break;
01972          }
01973          ao2_ref(resource, -1);
01974       }
01975       ao2_iterator_destroy(&res);
01976 
01977       ao2_ref(buddy, -1);
01978    } else {
01979       /* If the target is NOT in the roster use the provided target as-is */
01980       ast_copy_string(target, args.target, sizeof(target));
01981    }
01982 
01983    ao2_unlock(endpoint->state);
01984 
01985    /* If no target was found we can't set up a session */
01986    if (ast_strlen_zero(target)) {
01987       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);
01988       *cause = AST_CAUSE_SWITCH_CONGESTION;
01989       return NULL;
01990    }
01991 
01992    if (!(session = jingle_alloc(endpoint, target, NULL))) {
01993       ast_log(LOG_ERROR, "Unable to create Jingle session on endpoint '%s'\n", args.name);
01994       *cause = AST_CAUSE_SWITCH_CONGESTION;
01995       return NULL;
01996    }
01997 
01998    /* Update the transport if we learned what we should actually use */
01999    if (transport != JINGLE_TRANSPORT_NONE) {
02000       session->transport = transport;
02001       /* Note that for Google-V1 and Google-V2 we don't stop built-in ICE support, this will happen in jingle_new */
02002    }
02003 
02004    if (!(chan = jingle_new(endpoint, session, AST_STATE_DOWN, target, assignedids, requestor, NULL))) {
02005       ast_log(LOG_ERROR, "Unable to create Jingle channel on endpoint '%s'\n", args.name);
02006       *cause = AST_CAUSE_SWITCH_CONGESTION;
02007       ao2_ref(session, -1);
02008       return NULL;
02009    }
02010 
02011    /* If video was requested try to enable it on the session */
02012    if (ast_format_cap_has_type(cap, AST_MEDIA_TYPE_VIDEO)) {
02013       jingle_enable_video(session);
02014    }
02015 
02016    /* As this is outgoing set ourselves as controlling */
02017    if (session->rtp && (ice = ast_rtp_instance_get_ice(session->rtp))) {
02018       ice->ice_lite(session->rtp);
02019    }
02020 
02021    if (session->vrtp && (ice = ast_rtp_instance_get_ice(session->vrtp))) {
02022       ice->ice_lite(session->vrtp);
02023    }
02024 
02025    /* We purposely don't decrement the session here as there is a reference on the channel */
02026    ao2_link(endpoint->state->sessions, session);
02027 
02028    return chan;
02029 }
02030 
02031 /*! \brief Helper function which handles content descriptions */
02032 static int jingle_interpret_description(struct jingle_session *session, iks *description, const char *name, struct ast_rtp_instance **rtp)
02033 {
02034    char *media = iks_find_attrib(description, "media");
02035    struct ast_rtp_codecs codecs;
02036    iks *codec;
02037    int othercapability = 0;
02038 
02039    /* Google-V1 is always carrying audio, but just doesn't tell us so */
02040    if (session->transport == JINGLE_TRANSPORT_GOOGLE_V1) {
02041       media = "audio";
02042    } else if (ast_strlen_zero(media)) {
02043       jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
02044       ast_log(LOG_ERROR, "Received a content description on session '%s' without a name\n", session->sid);
02045       return -1;
02046    }
02047 
02048    /* Determine the type of media that is being carried and update the RTP instance, as well as the name */
02049    if (!strcasecmp(media, "audio")) {
02050       if (!ast_strlen_zero(name)) {
02051          ast_string_field_set(session, audio_name, name);
02052       }
02053       *rtp = session->rtp;
02054       ast_format_cap_remove_by_type(session->peercap, AST_MEDIA_TYPE_AUDIO);
02055       ast_format_cap_remove_by_type(session->jointcap, AST_MEDIA_TYPE_AUDIO);
02056    } else if (!strcasecmp(media, "video")) {
02057       if (!ast_strlen_zero(name)) {
02058          ast_string_field_set(session, video_name, name);
02059       }
02060 
02061       jingle_enable_video(session);
02062       *rtp = session->vrtp;
02063 
02064       /* If video is not present cancel this session */
02065       if (!session->vrtp) {
02066          jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
02067          ast_log(LOG_ERROR, "Received a video content description on session '%s' but could not enable video\n", session->sid);
02068          return -1;
02069       }
02070 
02071       ast_format_cap_remove_by_type(session->peercap, AST_MEDIA_TYPE_VIDEO);
02072       ast_format_cap_remove_by_type(session->jointcap, AST_MEDIA_TYPE_VIDEO);
02073    } else {
02074       /* Unknown media type */
02075       jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
02076       ast_log(LOG_ERROR, "Unsupported media type '%s' received in content description on session '%s'\n", media, session->sid);
02077       return -1;
02078    }
02079 
02080    if (ast_rtp_codecs_payloads_initialize(&codecs)) {
02081       jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
02082       ast_log(LOG_ERROR, "Could not initialize codecs for negotiation on session '%s'\n", session->sid);
02083       return -1;
02084    }
02085 
02086    /* Iterate the codecs updating the relevant RTP instance as we go */
02087    for (codec = iks_child(description); codec; codec = iks_next(codec)) {
02088       char *id = iks_find_attrib(codec, "id"), *name = iks_find_attrib(codec, "name");
02089       char *clockrate = iks_find_attrib(codec, "clockrate");
02090       int rtp_id, rtp_clockrate;
02091 
02092       if (!ast_strlen_zero(id) && !ast_strlen_zero(name) && (sscanf(id, "%30d", &rtp_id) == 1)) {
02093          if (!ast_strlen_zero(clockrate) && (sscanf(clockrate, "%30d", &rtp_clockrate) == 1)) {
02094             ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, rtp_id, media, name, 0, rtp_clockrate);
02095          } else {
02096             ast_rtp_codecs_payloads_set_rtpmap_type(&codecs, NULL, rtp_id, media, name, 0);
02097          }
02098       }
02099    }
02100 
02101    ast_rtp_codecs_payload_formats(&codecs, session->peercap, &othercapability);
02102    ast_format_cap_get_compatible(session->cap, session->peercap, session->jointcap);
02103 
02104    if (!ast_format_cap_count(session->jointcap)) {
02105       /* We have no compatible codecs, so terminate the session appropriately */
02106       jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
02107       ast_rtp_codecs_payloads_destroy(&codecs);
02108       return -1;
02109    }
02110 
02111    ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(*rtp), *rtp);
02112    ast_rtp_codecs_payloads_destroy(&codecs);
02113 
02114    return 0;
02115 }
02116 
02117 /*! \brief Helper function which handles ICE-UDP transport information */
02118 static int jingle_interpret_ice_udp_transport(struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
02119 {
02120    struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
02121    char *ufrag = iks_find_attrib(transport, "ufrag"), *pwd = iks_find_attrib(transport, "pwd");
02122    iks *candidate;
02123 
02124    if (!ice) {
02125       jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
02126       ast_log(LOG_ERROR, "Received ICE-UDP transport information on session '%s' but ICE support not available\n", session->sid);
02127       return -1;
02128    }
02129 
02130    if (!ast_strlen_zero(ufrag) && !ast_strlen_zero(pwd)) {
02131       ice->set_authentication(rtp, ufrag, pwd);
02132    }
02133 
02134    for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
02135       char *component = iks_find_attrib(candidate, "component"), *foundation = iks_find_attrib(candidate, "foundation");
02136       char *generation = iks_find_attrib(candidate, "generation"), *id = iks_find_attrib(candidate, "id");
02137       char *ip = iks_find_attrib(candidate, "ip"), *port = iks_find_attrib(candidate, "port");
02138       char *priority = iks_find_attrib(candidate, "priority"), *protocol = iks_find_attrib(candidate, "protocol");
02139       char *type = iks_find_attrib(candidate, "type");
02140       struct ast_rtp_engine_ice_candidate local_candidate = { 0, };
02141       int real_port;
02142       struct ast_sockaddr remote_address = { { 0, } };
02143 
02144       /* If this candidate is incomplete skip it */
02145       if (ast_strlen_zero(component) || ast_strlen_zero(foundation) || ast_strlen_zero(generation) || ast_strlen_zero(id) ||
02146           ast_strlen_zero(ip) || ast_strlen_zero(port) || ast_strlen_zero(priority) ||
02147           ast_strlen_zero(protocol) || ast_strlen_zero(type)) {
02148          jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
02149          ast_log(LOG_ERROR, "Incomplete ICE-UDP candidate received on session '%s'\n", session->sid);
02150          return -1;
02151       }
02152 
02153       if ((sscanf(component, "%30u", &local_candidate.id) != 1) ||
02154           (sscanf(priority, "%30u", (unsigned *)&local_candidate.priority) != 1) ||
02155           (sscanf(port, "%30d", &real_port) != 1)) {
02156          jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
02157          ast_log(LOG_ERROR, "Invalid ICE-UDP candidate information received on session '%s'\n", session->sid);
02158          return -1;
02159       }
02160 
02161       local_candidate.foundation = foundation;
02162       local_candidate.transport = protocol;
02163 
02164       ast_sockaddr_parse(&local_candidate.address, ip, PARSE_PORT_FORBID);
02165 
02166       /* We only support IPv4 right now */
02167       if (!ast_sockaddr_is_ipv4(&local_candidate.address)) {
02168          continue;
02169       }
02170 
02171       ast_sockaddr_set_port(&local_candidate.address, real_port);
02172 
02173       if (!strcasecmp(type, "host")) {
02174          local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
02175       } else if (!strcasecmp(type, "srflx")) {
02176          local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
02177       } else if (!strcasecmp(type, "relay")) {
02178          local_candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
02179       } else {
02180          continue;
02181       }
02182 
02183       /* Worst case use the first viable address */
02184       ast_rtp_instance_get_remote_address(rtp, &remote_address);
02185 
02186       if (ast_sockaddr_is_ipv4(&local_candidate.address) && ast_sockaddr_isnull(&remote_address)) {
02187          ast_rtp_instance_set_remote_address(rtp, &local_candidate.address);
02188       }
02189 
02190       ice->add_remote_candidate(rtp, &local_candidate);
02191    }
02192 
02193    ice->start(rtp);
02194 
02195    return 0;
02196 }
02197 
02198 /*! \brief Helper function which handles Google transport information */
02199 static int jingle_interpret_google_transport(struct jingle_session *session, iks *transport, struct ast_rtp_instance *rtp)
02200 {
02201    struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(rtp);
02202    iks *candidate;
02203 
02204    if (!ice) {
02205       jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
02206       ast_log(LOG_ERROR, "Received Google transport information on session '%s' but ICE support not available\n", session->sid);
02207       return -1;
02208    }
02209 
02210    /* If this session has not transitioned to the Google transport do so now */
02211    if ((session->transport != JINGLE_TRANSPORT_GOOGLE_V2) &&
02212        (session->transport != JINGLE_TRANSPORT_GOOGLE_V1)) {
02213       /* Stop built-in ICE support... we need to fall back to the old old old STUN */
02214       ice->stop(rtp);
02215 
02216       session->transport = JINGLE_TRANSPORT_GOOGLE_V2;
02217    }
02218 
02219    for (candidate = iks_child(transport); candidate; candidate = iks_next(candidate)) {
02220       char *address = iks_find_attrib(candidate, "address"), *port = iks_find_attrib(candidate, "port");
02221       char *username = iks_find_attrib(candidate, "username"), *name = iks_find_attrib(candidate, "name");
02222       char *protocol = iks_find_attrib(candidate, "protocol");
02223       int real_port;
02224       struct ast_sockaddr target = { { 0, } };
02225       /* In Google land the combined value is 32 bytes */
02226       char combined[33] = "";
02227 
02228       /* If this is NOT actually a candidate just skip it */
02229       if (strcasecmp(iks_name(candidate), "candidate") &&
02230           strcasecmp(iks_name(candidate), "p:candidate") &&
02231           strcasecmp(iks_name(candidate), "ses:candidate")) {
02232          continue;
02233       }
02234 
02235       /* If this candidate is incomplete skip it */
02236       if (ast_strlen_zero(address) || ast_strlen_zero(port) || ast_strlen_zero(username) ||
02237           ast_strlen_zero(name)) {
02238          jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
02239          ast_log(LOG_ERROR, "Incomplete Google candidate received on session '%s'\n", session->sid);
02240          return -1;
02241       }
02242 
02243       /* We only support UDP so skip any other protocols */
02244       if (!ast_strlen_zero(protocol) && strcasecmp(protocol, "udp")) {
02245          continue;
02246       }
02247 
02248       /* We only permit audio and video, not RTCP */
02249       if (strcasecmp(name, "rtp") && strcasecmp(name, "video_rtp")) {
02250          continue;
02251       }
02252 
02253       /* Parse the target information so we can send a STUN request to the candidate */
02254       if (sscanf(port, "%30d", &real_port) != 1) {
02255          jingle_queue_hangup_with_cause(session, AST_CAUSE_PROTOCOL_ERROR);
02256          ast_log(LOG_ERROR, "Invalid Google candidate port '%s' received on session '%s'\n", port, session->sid);
02257          return -1;
02258       }
02259       ast_sockaddr_parse(&target, address, PARSE_PORT_FORBID);
02260       ast_sockaddr_set_port(&target, real_port);
02261 
02262       /* Per the STUN support Google talk uses combine the two usernames */
02263       snprintf(combined, sizeof(combined), "%s%s", username, ice->get_ufrag(rtp));
02264 
02265       /* This should appease the masses... we will actually change the remote address when we get their STUN packet */
02266       ast_rtp_instance_stun_request(rtp, &target, combined);
02267    }
02268 
02269    return 0;
02270 }
02271 
02272 /*!
02273  * \brief Helper function which locates content stanzas and interprets them
02274  *
02275  * \note The session *must not* be locked before calling this
02276  */
02277 static int jingle_interpret_content(struct jingle_session *session, ikspak *pak)
02278 {
02279    iks *content;
02280    unsigned int changed = 0;
02281    struct ast_channel *chan;
02282 
02283    /* Look at the content in the session initiation */
02284    for (content = iks_child(iks_child(pak->x)); content; content = iks_next(content)) {
02285       char *name;
02286       struct ast_rtp_instance *rtp = NULL;
02287       iks *description, *transport;
02288 
02289       /* Ignore specific parts if they are known not to be useful */
02290       if (!strcmp(iks_name(content), "conference-info")) {
02291          continue;
02292       }
02293 
02294       name = iks_find_attrib(content, "name");
02295 
02296       if (session->transport != JINGLE_TRANSPORT_GOOGLE_V1) {
02297          /* If this content stanza has no name consider it invalid and move on */
02298          if (ast_strlen_zero(name) && !(name = iks_find_attrib(content, "jin:name"))) {
02299             jingle_queue_hangup_with_cause(session, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
02300             ast_log(LOG_ERROR, "Received content without a name on session '%s'\n", session->sid);
02301             return -1;
02302          }
02303 
02304          /* Try to pre-populate which RTP instance this content is relevant to */
02305          if (!strcmp(session->audio_name, name)) {
02306             rtp = session->rtp;
02307          } else if (!strcmp(session->video_name, name)) {
02308             rtp = session->vrtp;
02309          }
02310       } else {
02311          /* Google-V1 has no concept of assocating things like the above does, so since we only support audio over it assume they want audio */
02312          rtp = session->rtp;
02313       }
02314 
02315       /* If description information is available use it */
02316       if ((description = iks_find_with_attrib(content, "description", "xmlns", JINGLE_RTP_NS)) ||
02317           (description = iks_find_with_attrib(content, "rtp:description", "xmlns:rtp", JINGLE_RTP_NS)) ||
02318           (description = iks_find_with_attrib(content, "pho:description", "xmlns:pho", GOOGLE_PHONE_NS)) ||
02319           (description = iks_find_with_attrib(pak->query, "description", "xmlns", GOOGLE_PHONE_NS)) ||
02320           (description = iks_find_with_attrib(pak->query, "pho:description", "xmlns:pho", GOOGLE_PHONE_NS)) ||
02321           (description = iks_find_with_attrib(pak->query, "vid:description", "xmlns", GOOGLE_VIDEO_NS))) {
02322          /* If we failed to do something with the content description abort immediately */
02323          if (jingle_interpret_description(session, description, name, &rtp)) {
02324             return -1;
02325          }
02326 
02327          /* If we successfully interpret the description then the codecs need updating */
02328          changed = 1;
02329       }
02330 
02331       /* If we get past the description handling and we still don't know what RTP instance this is for... it is unknown content */
02332       if (!rtp) {
02333          ast_log(LOG_ERROR, "Received a content stanza but have no RTP instance for it on session '%s'\n", session->sid);
02334          jingle_queue_hangup_with_cause(session, AST_CAUSE_SWITCH_CONGESTION);
02335          return -1;
02336       }
02337 
02338       /* If ICE UDP transport information is available use it */
02339       if ((transport = iks_find_with_attrib(content, "transport", "xmlns", JINGLE_ICE_UDP_NS))) {
02340          if (jingle_interpret_ice_udp_transport(session, transport, rtp)) {
02341             return -1;
02342          }
02343       } else if ((transport = iks_find_with_attrib(content, "transport", "xmlns", GOOGLE_TRANSPORT_NS)) ||
02344             (transport = iks_find_with_attrib(content, "p:transport", "xmlns:p", GOOGLE_TRANSPORT_NS)) ||
02345             (transport = iks_find_with_attrib(pak->x, "session", "xmlns", GOOGLE_SESSION_NS)) ||
02346             (transport = iks_find_with_attrib(pak->x, "ses:session", "xmlns:ses", GOOGLE_SESSION_NS))) {
02347          /* If Google transport support is available use it */
02348          if (jingle_interpret_google_transport(session, transport, rtp)) {
02349             return -1;
02350          }
02351       } else if (iks_find(content, "transport")) {
02352          /* If this is a transport we do not support terminate the session as it probably won't work out in the end */
02353          jingle_queue_hangup_with_cause(session, AST_CAUSE_FACILITY_NOT_IMPLEMENTED);
02354          ast_log(LOG_ERROR, "Unsupported transport type received on session '%s'\n", session->sid);
02355          return -1;
02356       }
02357    }
02358 
02359    if (!changed) {
02360       return 0;
02361    }
02362 
02363    if ((chan = jingle_session_lock_full(session))) {
02364       struct ast_format_cap *caps;
02365       struct ast_format *fmt;
02366 
02367       caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
02368       if (caps) {
02369          ast_format_cap_append_from_cap(caps, session->jointcap, AST_MEDIA_TYPE_UNKNOWN);
02370          ast_channel_nativeformats_set(chan, caps);
02371          ao2_ref(caps, -1);
02372       }
02373 
02374       fmt = ast_format_cap_get_format(session->jointcap, 0);
02375       ast_set_read_format(chan, fmt);
02376       ast_set_write_format(chan, fmt);
02377       ao2_ref(fmt, -1);
02378 
02379       ast_channel_unlock(chan);
02380       ast_channel_unref(chan);
02381    }
02382    ao2_unlock(session);
02383 
02384    return 0;
02385 }
02386 
02387 /*! \brief Handler function for the 'session-initiate' action */
02388 static void jingle_action_session_initiate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
02389 {
02390    char *sid;
02391    enum jingle_transport transport = JINGLE_TRANSPORT_NONE;
02392    struct ast_channel *chan;
02393    int res;
02394 
02395    if (session) {
02396       /* This is a duplicate session setup, so respond accordingly */
02397       jingle_send_error_response(endpoint->connection, pak, "result", "out-of-order", NULL);
02398       return;
02399    }
02400 
02401    /* Retrieve the session identifier from the message, note that this may alter the transport */
02402    if ((sid = iks_find_attrib(pak->query, "id"))) {
02403       /* The presence of the session identifier in the 'id' attribute tells us that this is Google-V1 as everything else uses 'sid' */
02404       transport = JINGLE_TRANSPORT_GOOGLE_V1;
02405    } else if (!(sid = iks_find_attrib(pak->query, "sid"))) {
02406       jingle_send_error_response(endpoint->connection, pak, "bad-request", NULL, NULL);
02407       return;
02408    }
02409 
02410    /* Create a new local session */
02411    if (!(session = jingle_alloc(endpoint, pak->from->full, sid))) {
02412       jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
02413       return;
02414    }
02415 
02416    /* If we determined that the transport should change as a result of how we got the SID change it */
02417    if (transport != JINGLE_TRANSPORT_NONE) {
02418       session->transport = transport;
02419    }
02420 
02421    /* Create a new Asterisk channel using the above local session */
02422    if (!(chan = jingle_new(endpoint, session, AST_STATE_DOWN, pak->from->user, NULL, NULL, pak->from->full))) {
02423       ao2_ref(session, -1);
02424       jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
02425       return;
02426    }
02427 
02428    ao2_link(endpoint->state->sessions, session);
02429 
02430    ast_channel_lock(chan);
02431    ast_setstate(chan, AST_STATE_RING);
02432    ast_channel_unlock(chan);
02433    res = ast_pbx_start(chan);
02434 
02435    switch (res) {
02436    case AST_PBX_FAILED:
02437       ast_log(LOG_WARNING, "Failed to start PBX :(\n");
02438       jingle_send_error_response(endpoint->connection, pak, "cancel", "service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
02439       session->gone = 1;
02440       ast_hangup(chan);
02441       break;
02442    case AST_PBX_CALL_LIMIT:
02443       ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n");
02444       jingle_send_error_response(endpoint->connection, pak, "wait", "resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'", NULL);
02445       ast_hangup(chan);
02446       break;
02447    case AST_PBX_SUCCESS:
02448       jingle_send_response(endpoint->connection, pak);
02449 
02450       /* Only send a transport-info message if we successfully interpreted the available content */
02451       if (!jingle_interpret_content(session, pak)) {
02452          jingle_send_transport_info(session, iks_find_attrib(pak->x, "from"));
02453       }
02454       break;
02455    }
02456 }
02457 
02458 /*! \brief Handler function for the 'transport-info' action */
02459 static void jingle_action_transport_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
02460 {
02461    if (!session) {
02462       jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
02463                   "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
02464       return;
02465    }
02466 
02467    jingle_interpret_content(session, pak);
02468    jingle_send_response(endpoint->connection, pak);
02469 }
02470 
02471 /*! \brief Handler function for the 'session-accept' action */
02472 static void jingle_action_session_accept(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
02473 {
02474    struct ast_channel *chan;
02475 
02476    if (!session) {
02477       jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
02478                   "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
02479       return;
02480    }
02481 
02482 
02483    jingle_interpret_content(session, pak);
02484 
02485    if ((chan = jingle_session_lock_full(session))) {
02486       ast_queue_control(chan, AST_CONTROL_ANSWER);
02487       ast_channel_unlock(chan);
02488       ast_channel_unref(chan);
02489    }
02490    ao2_unlock(session);
02491 
02492    jingle_send_response(endpoint->connection, pak);
02493 }
02494 
02495 /*! \brief Handler function for the 'session-info' action */
02496 static void jingle_action_session_info(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
02497 {
02498    struct ast_channel *chan;
02499 
02500    if (!session) {
02501       jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
02502                   "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
02503       return;
02504    }
02505 
02506    if (!(chan = jingle_session_lock_full(session))) {
02507       ao2_unlock(session);
02508       jingle_send_response(endpoint->connection, pak);
02509       return;
02510    }
02511 
02512    if (iks_find_with_attrib(pak->query, "ringing", "xmlns", JINGLE_RTP_INFO_NS)) {
02513       ast_queue_control(chan, AST_CONTROL_RINGING);
02514       if (ast_channel_state(chan) != AST_STATE_UP) {
02515          ast_setstate(chan, AST_STATE_RINGING);
02516       }
02517    } else if (iks_find_with_attrib(pak->query, "hold", "xmlns", JINGLE_RTP_INFO_NS)) {
02518       ast_queue_hold(chan, NULL);
02519    } else if (iks_find_with_attrib(pak->query, "unhold", "xmlns", JINGLE_RTP_INFO_NS)) {
02520       ast_queue_unhold(chan);
02521    }
02522 
02523    ast_channel_unlock(chan);
02524    ast_channel_unref(chan);
02525    ao2_unlock(session);
02526 
02527    jingle_send_response(endpoint->connection, pak);
02528 }
02529 
02530 /*! \brief Handler function for the 'session-terminate' action */
02531 static void jingle_action_session_terminate(struct jingle_endpoint *endpoint, struct jingle_session *session, ikspak *pak)
02532 {
02533    struct ast_channel *chan;
02534    iks *reason, *text;
02535    int cause = AST_CAUSE_NORMAL;
02536    struct ast_control_pvt_cause_code *cause_code;
02537    int data_size = sizeof(*cause_code);
02538 
02539    if (!session) {
02540       jingle_send_error_response(endpoint->connection, pak, "cancel", "item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'",
02541                   "unknown-session xmlns='urn:xmpp:jingle:errors:1'");
02542       return;
02543    }
02544 
02545    if (!(chan = jingle_session_lock_full(session))) {
02546       ao2_unlock(session);
02547       jingle_send_response(endpoint->connection, pak);
02548       return;
02549    }
02550 
02551    /* Pull the reason text from the session-terminate message and translate it into a cause code */
02552    if ((reason = iks_find(pak->query, "reason")) && (text = iks_child(reason))) {
02553       int i;
02554 
02555       /* Size of the string making up the cause code is "Motif " + text */
02556       data_size += 6 + strlen(iks_name(text));
02557       cause_code = ast_alloca(data_size);
02558       memset(cause_code, 0, data_size);
02559 
02560       /* Get the appropriate cause code mapping for this reason */
02561       for (i = 0; i < ARRAY_LEN(jingle_reason_mappings); i++) {
02562          if (!strcasecmp(jingle_reason_mappings[i].reason, iks_name(text))) {
02563             cause = jingle_reason_mappings[i].cause;
02564             break;
02565          }
02566       }
02567 
02568       /* Store the technology specific information */
02569       snprintf(cause_code->code, data_size - sizeof(*cause_code) + 1, "Motif %s", iks_name(text));
02570    } else {
02571       /* No technology specific information is available */
02572       cause_code = ast_alloca(data_size);
02573       memset(cause_code, 0, data_size);
02574    }
02575 
02576    ast_copy_string(cause_code->chan_name, ast_channel_name(chan), AST_CHANNEL_NAME);
02577    cause_code->ast_cause = cause;
02578    ast_queue_control_data(chan, AST_CONTROL_PVT_CAUSE_CODE, cause_code, data_size);
02579    ast_channel_hangupcause_hash_set(chan, cause_code, data_size);
02580 
02581    ast_debug(3, "Hanging up channel '%s' due to session terminate message with cause '%d'\n", ast_channel_name(chan), cause);
02582    ast_queue_hangup_with_cause(chan, cause);
02583    session->gone = 1;
02584 
02585    ast_channel_unlock(chan);
02586    ast_channel_unref(chan);
02587    ao2_unlock(session);
02588 
02589    jingle_send_response(endpoint->connection, pak);
02590 }
02591 
02592 /*! \brief Callback for when a Jingle action is received from an endpoint */
02593 static int jingle_action_hook(void *data, ikspak *pak)
02594 {
02595    char *action;
02596    const char *sid = NULL;
02597    struct jingle_session *session = NULL;
02598    struct jingle_endpoint *endpoint = data;
02599    int i, handled = 0;
02600 
02601    /* We accept both Jingle and Google-V1 */
02602    if (!(action = iks_find_attrib(pak->query, "action")) &&
02603        !(action = iks_find_attrib(pak->query, "type"))) {
02604       /* This occurs if either receive a packet masquerading as Jingle or Google-V1 that is actually not OR we receive a response
02605        * to a message that has no response hook. */
02606       return IKS_FILTER_EAT;
02607    }
02608 
02609    /* Bump the endpoint reference count up in case a reload occurs. Unfortunately the available synchronization between iksemel and us
02610     * does not permit us to make this completely safe. */
02611    ao2_ref(endpoint, +1);
02612 
02613    /* If a Jingle session identifier is present use it */
02614    if (!(sid = iks_find_attrib(pak->query, "sid"))) {
02615       /* If a Google-V1 session identifier is present use it */
02616       sid = iks_find_attrib(pak->query, "id");
02617    }
02618 
02619    /* If a session identifier was present in the message attempt to find the session, it is up to the action handler whether
02620     * this is required or not */
02621    if (!ast_strlen_zero(sid)) {
02622       session = ao2_find(endpoint->state->sessions, sid, OBJ_KEY);
02623    }
02624 
02625    /* If a session is present associate the callid with this thread */
02626    if (session) {
02627       ast_callid_threadassoc_add(session->callid);
02628    }
02629 
02630    /* Iterate through supported action handlers looking for one that is able to handle this */
02631    for (i = 0; i < ARRAY_LEN(jingle_action_handlers); i++) {
02632       if (!strcasecmp(jingle_action_handlers[i].action, action)) {
02633          jingle_action_handlers[i].handler(endpoint, session, pak);
02634          handled = 1;
02635          break;
02636       }
02637    }
02638 
02639    /* If no action handler is present for the action they sent us make it evident */
02640    if (!handled) {
02641       ast_log(LOG_NOTICE, "Received action '%s' for session '%s' that has no handler\n", action, sid);
02642    }
02643 
02644    /* If a session was successfully found for this message deref it now since the handler is done */
02645    if (session) {
02646       ast_callid_threadassoc_remove();
02647       ao2_ref(session, -1);
02648    }
02649 
02650    ao2_ref(endpoint, -1);
02651 
02652    return IKS_FILTER_EAT;
02653 }
02654 
02655 /*! \brief Custom handler for groups */
02656 static int custom_group_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
02657 {
02658    struct jingle_endpoint *endpoint = obj;
02659 
02660    if (!strcasecmp(var->name, "callgroup")) {
02661       endpoint->callgroup = ast_get_group(var->value);
02662    } else if (!strcasecmp(var->name, "pickupgroup")) {
02663       endpoint->pickupgroup = ast_get_group(var->value);
02664    } else {
02665       return -1;
02666    }
02667 
02668    return 0;
02669 }
02670 
02671 /*! \brief Custom handler for connection */
02672 static int custom_connection_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
02673 {
02674    struct jingle_endpoint *endpoint = obj;
02675 
02676    /* You might think... but Josh, shouldn't you do this in a prelink callback? Well I *could* but until the original is destroyed
02677     * this will not actually get called, so even if the config turns out to be bogus this is harmless.
02678     */
02679    if (!(endpoint->connection = ast_xmpp_client_find(var->value))) {
02680       ast_log(LOG_ERROR, "Connection '%s' configured on endpoint '%s' could not be found\n", var->value, endpoint->name);
02681       return -1;
02682    }
02683 
02684    if (!(endpoint->rule = iks_filter_add_rule(endpoint->connection->filter, jingle_action_hook, endpoint,
02685                      IKS_RULE_TYPE, IKS_PAK_IQ,
02686                      IKS_RULE_NS, JINGLE_NS,
02687                      IKS_RULE_NS, GOOGLE_SESSION_NS,
02688                      IKS_RULE_DONE))) {
02689       ast_log(LOG_ERROR, "Action hook could not be added to connection '%s' on endpoint '%s'\n", var->value, endpoint->name);
02690       return -1;
02691    }
02692 
02693    return 0;
02694 }
02695 
02696 /*! \brief Custom handler for transport */
02697 static int custom_transport_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
02698 {
02699    struct jingle_endpoint *endpoint = obj;
02700 
02701    if (!strcasecmp(var->value, "ice-udp")) {
02702       endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
02703    } else if (!strcasecmp(var->value, "google")) {
02704       endpoint->transport = JINGLE_TRANSPORT_GOOGLE_V2;
02705    } else if (!strcasecmp(var->value, "google-v1")) {
02706       endpoint->transport = JINGLE_TRANSPORT_GOOGLE_V1;
02707    } else {
02708       ast_log(LOG_WARNING, "Unknown transport type '%s' on endpoint '%s', defaulting to 'ice-udp'\n", var->value, endpoint->name);
02709       endpoint->transport = JINGLE_TRANSPORT_ICE_UDP;
02710    }
02711 
02712    return 0;
02713 }
02714 
02715 /*!
02716  * \brief Load the module
02717  *
02718  * Module loading including tests for configuration or dependencies.
02719  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
02720  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
02721  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
02722  * configuration file or other non-critical problem return 
02723  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
02724  */
02725 static int load_module(void)
02726 {
02727    if (!(jingle_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
02728       return AST_MODULE_LOAD_DECLINE;
02729    }
02730 
02731    if (aco_info_init(&cfg_info)) {
02732       ast_log(LOG_ERROR, "Unable to intialize configuration for chan_motif.\n");
02733       goto end;
02734    }
02735 
02736    aco_option_register(&cfg_info, "context", ACO_EXACT, endpoint_options, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, context));
02737    aco_option_register_custom(&cfg_info, "callgroup", ACO_EXACT, endpoint_options, NULL, custom_group_handler, 0);
02738    aco_option_register_custom(&cfg_info, "pickupgroup", ACO_EXACT, endpoint_options, NULL, custom_group_handler, 0);
02739    aco_option_register(&cfg_info, "language", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, language));
02740    aco_option_register(&cfg_info, "musicclass", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, musicclass));
02741    aco_option_register(&cfg_info, "parkinglot", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, parkinglot));
02742    aco_option_register(&cfg_info, "accountcode", ACO_EXACT, endpoint_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct jingle_endpoint, accountcode));
02743    aco_option_register(&cfg_info, "allow", ACO_EXACT, endpoint_options, "ulaw,alaw", OPT_CODEC_T, 1, FLDSET(struct jingle_endpoint, cap));
02744    aco_option_register(&cfg_info, "disallow", ACO_EXACT, endpoint_options, "all", OPT_CODEC_T, 0, FLDSET(struct jingle_endpoint, cap));
02745    aco_option_register_custom(&cfg_info, "connection", ACO_EXACT, endpoint_options, NULL, custom_connection_handler, 0);
02746    aco_option_register_custom(&cfg_info, "transport", ACO_EXACT, endpoint_options, NULL, custom_transport_handler, 0);
02747    aco_option_register(&cfg_info, "maxicecandidates", ACO_EXACT, endpoint_options, DEFAULT_MAX_ICE_CANDIDATES, OPT_UINT_T, PARSE_DEFAULT,
02748              FLDSET(struct jingle_endpoint, maxicecandidates), DEFAULT_MAX_ICE_CANDIDATES);
02749    aco_option_register(&cfg_info, "maxpayloads", ACO_EXACT, endpoint_options, DEFAULT_MAX_PAYLOADS, OPT_UINT_T, PARSE_DEFAULT,
02750              FLDSET(struct jingle_endpoint, maxpayloads), DEFAULT_MAX_PAYLOADS);
02751 
02752    ast_format_cap_append_by_type(jingle_tech.capabilities, AST_MEDIA_TYPE_AUDIO);
02753 
02754    if (aco_process_config(&cfg_info, 0)) {
02755       ast_log(LOG_ERROR, "Unable to read config file motif.conf. Module loaded but not running.\n");
02756       aco_info_destroy(&cfg_info);
02757       ao2_cleanup(jingle_tech.capabilities);
02758       jingle_tech.capabilities = NULL;
02759       return AST_MODULE_LOAD_DECLINE;
02760    }
02761 
02762    if (!(sched = ast_sched_context_create())) {
02763       ast_log(LOG_ERROR, "Unable to create scheduler context.\n");
02764       goto end;
02765    }
02766 
02767    if (ast_sched_start_thread(sched)) {
02768       ast_log(LOG_ERROR, "Unable to create scheduler context thread.\n");
02769       goto end;
02770    }
02771 
02772    ast_rtp_glue_register(&jingle_rtp_glue);
02773 
02774    if (ast_channel_register(&jingle_tech)) {
02775       ast_log(LOG_ERROR, "Unable to register channel class %s\n", channel_type);
02776       goto end;
02777    }
02778 
02779    return 0;
02780 
02781 end:
02782    ast_rtp_glue_unregister(&jingle_rtp_glue);
02783 
02784    if (sched) {
02785       ast_sched_context_destroy(sched);
02786    }
02787 
02788    aco_info_destroy(&cfg_info);
02789    ao2_global_obj_release(globals);
02790 
02791    ao2_cleanup(jingle_tech.capabilities);
02792    jingle_tech.capabilities = NULL;
02793 
02794    return AST_MODULE_LOAD_FAILURE;
02795 }
02796 
02797 /*! \brief Reload module */
02798 static int reload(void)
02799 {
02800    if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
02801       return -1;
02802    }
02803 
02804    return 0;
02805 }
02806 
02807 /*! \brief Unload the jingle channel from Asterisk */
02808 static int unload_module(void)
02809 {
02810    ast_channel_unregister(&jingle_tech);
02811    ao2_cleanup(jingle_tech.capabilities);
02812    jingle_tech.capabilities = NULL;
02813    ast_rtp_glue_unregister(&jingle_rtp_glue);
02814    ast_sched_context_destroy(sched);
02815    aco_info_destroy(&cfg_info);
02816    ao2_global_obj_release(globals);
02817 
02818    return 0;
02819 }
02820 
02821 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Motif Jingle Channel Driver",
02822       .support_level = AST_MODULE_SUPPORT_CORE,
02823       .load = load_module,
02824       .unload = unload_module,
02825       .reload = reload,
02826       .load_pri = AST_MODPRI_CHANNEL_DRIVER,
02827           );

Generated on Thu Apr 16 06:27:23 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6