[asterisk-commits] kmoore: branch 12 r405565 - in /branches/12: ./ channels/ contrib/ast-db-mana...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Jan 15 07:14:22 CST 2014


Author: kmoore
Date: Wed Jan 15 07:14:06 2014
New Revision: 405565

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=405565
Log:
PJSIP: Add Path header support

This adds Path support to chan_pjsip in res_pjsip_path.c with minimal
additions in res_pjsip_registrar.c to store the path and additions in
res_pjsip_outbound_registration.c to enable advertisement of path
support to registrars and intervening proxies.

Path information is stored on contacts and is enabled via Address of
Record (AoRs) and Registration configuration sections.

While adding path support, it became necessary to be able to add SIP
supplements that handled messages outside of sessions, so a framework
for handling these types of hooks was added in parallel to the
already-existing session supplements and several senders of
out-of-dialog requests were refactored as a result.

(closes issue ASTERISK-21084)
Review: https://reviewboard.asterisk.org/r/3050/

Added:
    branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py   (with props)
    branches/12/res/res_pjsip_path.c   (with props)
Modified:
    branches/12/CHANGES
    branches/12/UPGRADE.txt
    branches/12/channels/chan_pjsip.c
    branches/12/include/asterisk/res_pjsip.h
    branches/12/include/asterisk/res_pjsip_session.h
    branches/12/res/res_pjsip.c
    branches/12/res/res_pjsip/location.c
    branches/12/res/res_pjsip/pjsip_distributor.c
    branches/12/res/res_pjsip/pjsip_options.c
    branches/12/res/res_pjsip_caller_id.c
    branches/12/res/res_pjsip_diversion.c
    branches/12/res/res_pjsip_header_funcs.c
    branches/12/res/res_pjsip_messaging.c
    branches/12/res/res_pjsip_mwi.c
    branches/12/res/res_pjsip_nat.c
    branches/12/res/res_pjsip_notify.c
    branches/12/res/res_pjsip_outbound_registration.c
    branches/12/res/res_pjsip_refer.c
    branches/12/res/res_pjsip_registrar.c
    branches/12/res/res_pjsip_session.c
    branches/12/res/res_pjsip_t38.c

Modified: branches/12/CHANGES
URL: http://svnview.digium.com/svn/asterisk/branches/12/CHANGES?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/CHANGES (original)
+++ branches/12/CHANGES Wed Jan 15 07:14:06 2014
@@ -7,6 +7,15 @@
 === and the other UPGRADE files for older releases.
 ===
 ==============================================================================
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 12.0.0 to Asterisk 12.1.0 ------------
+------------------------------------------------------------------------------
+
+chan_pjsip
+------------------
+ * Path support has been added with the 'support_path' option in registration
+   and aor sections.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 11 to Asterisk 12 --------------------

Modified: branches/12/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/branches/12/UPGRADE.txt?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/UPGRADE.txt (original)
+++ branches/12/UPGRADE.txt Wed Jan 15 07:14:06 2014
@@ -46,6 +46,14 @@
    As a result, using the AMI Command action with "core set verbose" could
    again set the root console verbose level and affect the verbose level
    logged.
+
+Realtime Configuration:
+ * New columns have been added to realtime tables for 'support_path' on
+   ps_registrations and ps_aors and for 'path' on ps_contacts for the new
+   SIP Path support in chan_pjsip.
+ * New columns have been added to the ps_endpoints realtime table for the
+   'redirect_method' and 'set_var' options.
+
 
 From 11 to 12:
 There are many significant architectural changes in Asterisk 12. It is

Modified: branches/12/channels/chan_pjsip.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/channels/chan_pjsip.c?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/channels/chan_pjsip.c (original)
+++ branches/12/channels/chan_pjsip.c Wed Jan 15 07:14:06 2014
@@ -129,7 +129,7 @@
 /*! \brief SIP session supplement structure */
 static struct ast_sip_session_supplement chan_pjsip_supplement = {
 	.method = "INVITE",
-	.priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL,
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
 	.session_begin = chan_pjsip_session_begin,
 	.session_end = chan_pjsip_session_end,
 	.incoming_request = chan_pjsip_incoming_request,
@@ -140,7 +140,7 @@
 
 static struct ast_sip_session_supplement chan_pjsip_ack_supplement = {
 	.method = "ACK",
-	.priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL,
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL,
 	.incoming_request = chan_pjsip_incoming_ack,
 };
 
@@ -863,7 +863,7 @@
 	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
 	struct pjsip_tx_data *tdata;
 
-	if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, &tdata)) {
+	if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
 		ast_log(LOG_ERROR, "Could not create text video update INFO request\n");
 		return -1;
 	}
@@ -1261,7 +1261,7 @@
 
 	body.body_text = ast_str_buffer(body_text);
 
-	if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, &tdata)) {
+	if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, NULL, &tdata)) {
 		ast_log(LOG_ERROR, "Could not create DTMF INFO request\n");
 		return -1;
 	}
@@ -1539,7 +1539,7 @@
 		return -1;
 	}
 
-	if (!(session = ast_sip_session_create_outgoing(endpoint, args.aor, request_user, req_data->caps))) {
+	if (!(session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user, req_data->caps))) {
 		req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
 		return -1;
 	}
@@ -1618,9 +1618,9 @@
 
 	ast_debug(3, "Sending in dialog SIP message\n");
 
-	ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, &tdata);
+	ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, NULL, &tdata);
 	ast_sip_add_body(tdata, &body);
-	ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint);
+	ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint, NULL, NULL);
 
 	return 0;
 }
@@ -1831,7 +1831,7 @@
 
 static struct ast_sip_session_supplement pbx_start_supplement = {
 	.method = "INVITE",
-	.priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_LAST,
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_LAST,
 	.incoming_request = pbx_start_incoming_request,
 };
 

Added: branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
URL: http://svnview.digium.com/svn/asterisk/branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py?view=auto&rev=405565
==============================================================================
--- branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py (added)
+++ branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py Wed Jan 15 07:14:06 2014
@@ -1,0 +1,32 @@
+"""Add pjsip endpoint options for 12.1
+
+Revision ID: 2fc7930b41b3
+Revises: 581a4264e537
+Create Date: 2014-01-14 09:23:53.923454
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '2fc7930b41b3'
+down_revision = '581a4264e537'
+
+from alembic import op
+import sqlalchemy as sa
+
+YESNO_VALUES = ['yes', 'no']
+REDIRECT_METHODS = ['user', 'uri_core', 'uri_pjsip']
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('redirect_method', sa.Enum(*REDIRECT_METHODS, name='redirect_methods')))
+    op.add_column('ps_endpoints', sa.Column('set_var', sa.Text()))
+    op.add_column('ps_contacts', sa.Column('path', sa.Text()))
+    op.add_column('ps_aors', sa.Column('support_path', sa.Enum(*YESNO_VALUES, name='yesno_values')))
+    op.add_column('ps_registrations', sa.Column('support_path', sa.Enum(*YESNO_VALUES, name='yesno_values')))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'redirect_method')
+    op.drop_column('ps_endpoints', 'set_var')
+    op.drop_column('ps_contacts', 'path')
+    op.drop_column('ps_aors', 'support_path')
+    op.drop_column('ps_registrations', 'support_path')

Propchange: branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
------------------------------------------------------------------------------
    svn:executable = *

Propchange: branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: branches/12/contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: branches/12/include/asterisk/res_pjsip.h
URL: http://svnview.digium.com/svn/asterisk/branches/12/include/asterisk/res_pjsip.h?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/include/asterisk/res_pjsip.h (original)
+++ branches/12/include/asterisk/res_pjsip.h Wed Jan 15 07:14:06 2014
@@ -149,6 +149,8 @@
 		AST_STRING_FIELD(uri);
 		/*! Outbound proxy to use for qualify */
 		AST_STRING_FIELD(outbound_proxy);
+		/*! Path information to place in Route headers */
+		AST_STRING_FIELD(path);
 	);
 	/*! Absolute time that this contact is no longer valid after */
 	struct timeval expiration_time;
@@ -212,6 +214,8 @@
 	unsigned int remove_existing;
 	/*! Any permanent configured contacts */
 	struct ao2_container *permanent_contacts;
+	/*! Determines whether SIP Path headers are supported */
+	unsigned int support_path;
 };
 
 /*!
@@ -904,11 +908,13 @@
  * \param aor Pointer to the AOR
  * \param uri Full contact URI
  * \param expiration_time Optional expiration time of the contact
+ * \param path_info Path information
  *
  * \retval -1 failure
  * \retval 0 success
  */
-int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time);
+int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri,
+	struct timeval expiration_time, const char *path_info);
 
 /*!
  * \brief Update a contact
@@ -1212,18 +1218,22 @@
  *
  * \param method The method of the SIP request to send
  * \param dlg Optional. If specified, the dialog on which to request the message.
- * \param endpoint Optional. If specified, the request will be created out-of-dialog
- * to the endpoint.
+ * \param endpoint Optional. If specified, the request will be created out-of-dialog to the endpoint.
  * \param uri Optional. If specified, the request will be sent to this URI rather
- * this value.
  * than one configured for the endpoint.
+ * \param contact The contact with which this request is associated for out-of-dialog requests.
  * \param[out] tdata The newly-created request
+ *
+ * The provided contact is attached to tdata with its reference bumped, but will
+ * not survive for the entire lifetime of tdata since the contact is cleaned up
+ * when all supplements have completed execution.
+ *
  * \retval 0 Success
  * \retval -1 Failure
  */
 int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
 		struct ast_sip_endpoint *endpoint, const char *uri,
-		pjsip_tx_data **tdata);
+		struct ast_sip_contact *contact, pjsip_tx_data **tdata);
 
 /*!
  * \brief General purpose method for sending a SIP request
@@ -1238,10 +1248,48 @@
  * \param tdata The request to send
  * \param dlg Optional. If specified, the dialog on which the request should be sent
  * \param endpoint Optional. If specified, the request is sent out-of-dialog to the endpoint.
- * \retval 0 Success
- * \retval -1 Failure
- */
-int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint);
+ * \param token Data to be passed to the callback upon receipt of response
+ * \param callback Callback to be called upon receipt of response
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
+	struct ast_sip_endpoint *endpoint, void *token,
+	void (*callback)(void *token, pjsip_event *e));
+
+/*!
+ * \brief General purpose method for creating a SIP response
+ *
+ * Its typical use would be to create responses for out of dialog
+ * requests.
+ *
+ * \param rdata The rdata from the incoming request.
+ * \param st_code The response code to transmit.
+ * \param contact The contact with which this request is associated.
+ * \param[out] tdata The newly-created response
+ *
+ * The provided contact is attached to tdata with its reference bumped, but will
+ * not survive for the entire lifetime of tdata since the contact is cleaned up
+ * when all supplements have completed execution.
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
+	struct ast_sip_contact *contact, pjsip_tx_data **p_tdata);
+
+/*!
+ * \brief Send a response to an out of dialog request
+ *
+ * \param res_addr The response address for this response
+ * \param tdata The response to send
+ * \param endpoint The ast_sip_endpoint associated with this response
+ *
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint);
 
 /*!
  * \brief Determine if an incoming request requires authentication
@@ -1748,4 +1796,97 @@
 		ao2_callback_fn on_channel_snapshot,
 				      void *arg);
 
+enum ast_sip_supplement_priority {
+	/*! Top priority. Supplements with this priority are those that need to run before any others */
+	AST_SIP_SUPPLEMENT_PRIORITY_FIRST = 0,
+	/*! Channel creation priority.
+	 * chan_pjsip creates a channel at this priority. If your supplement depends on being run before
+	 * or after channel creation, then set your priority to be lower or higher than this value.
+	 */
+	AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL = 1000000,
+	/*! Lowest priority. Supplements with this priority should be run after all other supplements */
+	AST_SIP_SUPPLEMENT_PRIORITY_LAST = INT_MAX,
+};
+
+/*!
+ * \brief A supplement to SIP message processing
+ *
+ * These can be registered by any module in order to add
+ * processing to incoming and outgoing SIP out of dialog
+ * requests and responses
+ */
+struct ast_sip_supplement {
+	/*! Method on which to call the callbacks. If NULL, call on all methods */
+	const char *method;
+	/*! Priority for this supplement. Lower numbers are visited before higher numbers */
+	enum ast_sip_supplement_priority priority;
+	/*!
+	 * \brief Called on incoming SIP request
+	 * This method can indicate a failure in processing in its return. If there
+	 * is a failure, it is required that this method sends a response to the request.
+	 * This method is always called from a SIP servant thread.
+	 *
+	 * \note
+	 * The following PJSIP methods will not work properly:
+	 * pjsip_rdata_get_dlg()
+	 * pjsip_rdata_get_tsx()
+	 * The reason is that the rdata passed into this function is a cloned rdata structure,
+	 * and its module data is not copied during the cloning operation.
+	 * If you need to get the dialog, you can get it via session->inv_session->dlg.
+	 *
+	 * \note
+	 * There is no guarantee that a channel will be present on the session when this is called.
+	 */
+	int (*incoming_request)(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+	/*!
+	 * \brief Called on an incoming SIP response
+	 * This method is always called from a SIP servant thread.
+	 *
+	 * \note
+	 * The following PJSIP methods will not work properly:
+	 * pjsip_rdata_get_dlg()
+	 * pjsip_rdata_get_tsx()
+	 * The reason is that the rdata passed into this function is a cloned rdata structure,
+	 * and its module data is not copied during the cloning operation.
+	 * If you need to get the dialog, you can get it via session->inv_session->dlg.
+	 *
+	 * \note
+	 * There is no guarantee that a channel will be present on the session when this is called.
+	 */
+	void (*incoming_response)(struct ast_sip_endpoint *endpoint, struct pjsip_rx_data *rdata);
+	/*!
+	 * \brief Called on an outgoing SIP request
+	 * This method is always called from a SIP servant thread.
+	 */
+	void (*outgoing_request)(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata);
+	/*!
+	 * \brief Called on an outgoing SIP response
+	 * This method is always called from a SIP servant thread.
+	 */
+	void (*outgoing_response)(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact, struct pjsip_tx_data *tdata);
+	/*! Next item in the list */
+	AST_LIST_ENTRY(ast_sip_supplement) next;
+};
+
+/*!
+ * \brief Register a supplement to SIP out of dialog processing
+ *
+ * This allows for someone to insert themselves in the processing of out
+ * of dialog SIP requests and responses. This, for example could allow for
+ * a module to set channel data based on headers in an incoming message.
+ * Similarly, a module could reject an incoming request if desired.
+ *
+ * \param supplement The supplement to register
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_sip_register_supplement(struct ast_sip_supplement *supplement);
+
+/*!
+ * \brief Unregister a an supplement to SIP out of dialog processing
+ *
+ * \param supplement The supplement to unregister
+ */
+void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement);
+
 #endif /* _RES_PJSIP_H */

Modified: branches/12/include/asterisk/res_pjsip_session.h
URL: http://svnview.digium.com/svn/asterisk/branches/12/include/asterisk/res_pjsip_session.h?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/include/asterisk/res_pjsip_session.h (original)
+++ branches/12/include/asterisk/res_pjsip_session.h Wed Jan 15 07:14:06 2014
@@ -98,6 +98,8 @@
 	char exten[AST_MAX_EXTENSION];
 	/* The endpoint with which Asterisk is communicating */
 	struct ast_sip_endpoint *endpoint;
+	/* The contact associated with this session */
+	struct ast_sip_contact *contact;
 	/* The PJSIP details of the session, which includes the dialog */
 	struct pjsip_inv_session *inv_session;
 	/* The Asterisk channel associated with the session */
@@ -138,18 +140,6 @@
 typedef int (*ast_sip_session_response_cb)(struct ast_sip_session *session, pjsip_rx_data *rdata);
 typedef int (*ast_sip_session_sdp_creation_cb)(struct ast_sip_session *session, pjmedia_sdp_session *sdp);
 
-enum ast_sip_session_supplement_priority {
-	/*! Top priority. Supplements with this priority are those that need to run before any others */
-	AST_SIP_SESSION_SUPPLEMENT_PRIORITY_FIRST = 0,
-	/*! Channel creation priority.
-	 * chan_pjsip creates a channel at this priority. If your supplement depends on being run before
-	 * or after channel creation, then set your priority to be lower or higher than this value.
-	 */
-	AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL = 1000000,
-	/*! Lowest priority. Supplements with this priority should be run after all other supplements */
-	AST_SIP_SESSION_SUPPLEMENT_PRIORITY_LAST = INT_MAX,
-};
-
 /*!
  * \brief A supplement to SIP message processing
  *
@@ -160,7 +150,7 @@
     /*! Method on which to call the callbacks. If NULL, call on all methods */
     const char *method;
 	/*! Priority for this supplement. Lower numbers are visited before higher numbers */
-	enum ast_sip_session_supplement_priority priority;
+	enum ast_sip_supplement_priority priority;
     /*!
 	 * \brief Notification that the session has begun
 	 * This method will always be called from a SIP servant thread.
@@ -342,9 +332,11 @@
  * this reference when the session is destroyed.
  *
  * \param endpoint The endpoint that this session communicates with
+ * \param contact The contact associated with this session
  * \param inv_session The PJSIP INVITE session data
  */
-struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, pjsip_inv_session *inv);
+struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint,
+	struct ast_sip_contact *contact, pjsip_inv_session *inv);
 
 /*!
  * \brief Create a new outgoing SIP session
@@ -354,11 +346,14 @@
  * this reference when the session is destroyed.
  *
  * \param endpoint The endpoint that this session uses for settings
- * \param location Optional name of the location to call, be it named location or explicit URI
+ * \param contact The contact that this session will communicate with
+ * \param location Name of the location to call, be it named location or explicit URI. Overrides contact if present.
  * \param request_user Optional request user to place in the request URI if permitted
  * \param req_caps The requested capabilities
  */
-struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint, const char *location, const char *request_user, struct ast_format_cap *req_caps);
+struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint,
+	struct ast_sip_contact *contact, const char *location, const char *request_user,
+	struct ast_format_cap *req_caps);
 
 /*!
  * \brief Defer local termination of a session until remote side terminates, or an amount of time passes

Modified: branches/12/res/res_pjsip.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/res_pjsip.c?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/res/res_pjsip.c (original)
+++ branches/12/res/res_pjsip.c Wed Jan 15 07:14:06 2014
@@ -883,6 +883,9 @@
 						OPTIONS request is sent to a contact for qualify purposes.
 					</para></description>
 				</configOption>
+				<configOption name="path">
+					<synopsis>Stored Path vector for use in Route headers on outgoing requests.</synopsis>
+				</configOption>
 			</configObject>
 			<configObject name="aor">
 				<synopsis>The configuration for a location of an endpoint</synopsis>
@@ -984,6 +987,15 @@
 					<description><para>
 						If set the provided URI will be used as the outbound proxy when an
 						OPTIONS request is sent to a contact for qualify purposes.
+					</para></description>
+				</configOption>
+				<configOption name="support_path">
+					<synopsis>Enables Path support for REGISTER requests and Route support for other requests.</synopsis>
+					<description><para>
+						When this option is enabled, the Path headers in register requests will be saved
+						and its contents will be used in Route headers for outbound out-of-dialog requests
+						and in Path headers for outbound 200 responses. Path support will also be indicated
+						in the Supported header.
 					</para></description>
 				</configOption>
 			</configObject>
@@ -1105,6 +1117,7 @@
 	</manager>
  ***/
 
+#define MOD_DATA_CONTACT "contact"
 
 static pjsip_endpoint *ast_pjsip_endpoint;
 
@@ -1598,10 +1611,18 @@
 	return 0;
 }
 
+static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata);
+static pjsip_module supplement_module = {
+	.name = { "Out of dialog supplement hook", 29 },
+	.id = -1,
+	.priority = PJSIP_MOD_PRIORITY_APPLICATION - 1,
+	.on_rx_request = supplement_on_rx_request,
+};
+
 static int create_out_of_dialog_request(const pjsip_method *method, struct ast_sip_endpoint *endpoint,
-		const char *uri, pjsip_tx_data **tdata)
-{
-	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
+		const char *uri, struct ast_sip_contact *provided_contact, pjsip_tx_data **tdata)
+{
+	RAII_VAR(struct ast_sip_contact *, contact, ao2_bump(provided_contact), ao2_cleanup);
 	pj_str_t remote_uri;
 	pj_str_t from;
 	pj_pool_t *pool;
@@ -1613,7 +1634,9 @@
 			return -1;
 		}
 
-		contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+		if (!contact) {
+			contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors);
+		}
 		if (!contact || ast_strlen_zero(contact->uri)) {
 			ast_log(LOG_ERROR, "Unable to retrieve contact for endpoint %s\n",
 					ast_sorcery_object_get_id(endpoint));
@@ -1665,6 +1688,8 @@
 		return -1;
 	}
 
+	ast_sip_mod_data_set((*tdata)->pool, (*tdata)->mod_data, supplement_module.id, MOD_DATA_CONTACT, ao2_bump(contact));
+
 	/* We can release this pool since request creation copied all the necessary
 	 * data into the outbound request's pool
 	 */
@@ -1674,7 +1699,7 @@
 
 int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg,
 		struct ast_sip_endpoint *endpoint, const char *uri,
-		pjsip_tx_data **tdata)
+		struct ast_sip_contact *contact, pjsip_tx_data **tdata)
 {
 	const pjsip_method *pmethod = get_pjsip_method(method);
 
@@ -1686,8 +1711,46 @@
 	if (dlg) {
 		return create_in_dialog_request(pmethod, dlg, tdata);
 	} else {
-		return create_out_of_dialog_request(pmethod, endpoint, uri, tdata);
-	}
+		return create_out_of_dialog_request(pmethod, endpoint, uri, contact, tdata);
+	}
+}
+
+AST_RWLIST_HEAD_STATIC(supplements, ast_sip_supplement);
+
+int ast_sip_register_supplement(struct ast_sip_supplement *supplement)
+{
+	struct ast_sip_supplement *iter;
+	int inserted = 0;
+	SCOPED_LOCK(lock, &supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&supplements, iter, next) {
+		if (iter->priority > supplement->priority) {
+			AST_RWLIST_INSERT_BEFORE_CURRENT(supplement, next);
+			inserted = 1;
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
+
+	if (!inserted) {
+		AST_RWLIST_INSERT_TAIL(&supplements, supplement, next);
+	}
+	ast_module_ref(ast_module_info->self);
+	return 0;
+}
+
+void ast_sip_unregister_supplement(struct ast_sip_supplement *supplement)
+{
+	struct ast_sip_supplement *iter;
+	SCOPED_LOCK(lock, &supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK);
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&supplements, iter, next) {
+		if (supplement == iter) {
+			AST_RWLIST_REMOVE_CURRENT(next);
+			ast_module_unref(ast_module_info->self);
+			break;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END;
 }
 
 static int send_in_dialog_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg)
@@ -1699,45 +1762,120 @@
 	return 0;
 }
 
+static pj_bool_t does_method_match(const pj_str_t *message_method, const char *supplement_method)
+{
+	pj_str_t method;
+
+	if (ast_strlen_zero(supplement_method)) {
+		return PJ_TRUE;
+	}
+
+	pj_cstr(&method, supplement_method);
+
+	return pj_stristr(&method, message_method) ? PJ_TRUE : PJ_FALSE;
+}
+
+/*! \brief Structure to hold information about an outbound request */
+struct send_request_data {
+	struct ast_sip_endpoint *endpoint;		/*! The endpoint associated with this request */
+	void *token;					/*! Information to be provided to the callback upon receipt of a response */
+	void (*callback)(void *token, pjsip_event *e);	/*! The callback to be called upon receipt of a response */
+};
+
+static void send_request_data_destroy(void *obj)
+{
+	struct send_request_data *req_data = obj;
+	ao2_cleanup(req_data->endpoint);
+}
+
+static struct send_request_data *send_request_data_alloc(struct ast_sip_endpoint *endpoint,
+	void *token, void (*callback)(void *token, pjsip_event *e))
+{
+	struct send_request_data *req_data = ao2_alloc(sizeof(*req_data), send_request_data_destroy);
+
+	if (!req_data) {
+		return NULL;
+	}
+
+	req_data->endpoint = ao2_bump(endpoint);
+	req_data->token = token;
+	req_data->callback = callback;
+
+	return req_data;
+}
+
 static void send_request_cb(void *token, pjsip_event *e)
 {
-	RAII_VAR(struct ast_sip_endpoint *, endpoint, token, ao2_cleanup);
+	RAII_VAR(struct send_request_data *, req_data, token, ao2_cleanup);
 	pjsip_transaction *tsx = e->body.tsx_state.tsx;
 	pjsip_rx_data *challenge = e->body.tsx_state.src.rdata;
 	pjsip_tx_data *tdata;
-
-	if (tsx->status_code != 401 && tsx->status_code != 407) {
+	struct ast_sip_supplement *supplement;
+
+	AST_RWLIST_RDLOCK(&supplements);
+	AST_LIST_TRAVERSE(&supplements, supplement, next) {
+		if (supplement->incoming_response && does_method_match(&challenge->msg_info.cseq->method.name, supplement->method)) {
+			supplement->incoming_response(req_data->endpoint, challenge);
+		}
+	}
+	AST_RWLIST_UNLOCK(&supplements);
+
+	if (tsx->status_code == 401 || tsx->status_code == 407) {
+		if (!ast_sip_create_request_with_auth(&req_data->endpoint->outbound_auths, challenge, tsx, &tdata)) {
+			pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, req_data->token, req_data->callback);
+		}
 		return;
 	}
 
-	if (!ast_sip_create_request_with_auth(&endpoint->outbound_auths, challenge, tsx, &tdata)) {
-		pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, NULL, NULL);
-	}
-}
-
-static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpoint *endpoint)
-{
-	ao2_ref(endpoint, +1);
-	if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, endpoint, send_request_cb) != PJ_SUCCESS) {
+	if (req_data->callback) {
+		req_data->callback(req_data->token, e);
+	}
+}
+
+static int send_out_of_dialog_request(pjsip_tx_data *tdata, struct ast_sip_endpoint *endpoint,
+	void *token, void (*callback)(void *token, pjsip_event *e))
+{
+	struct ast_sip_supplement *supplement;
+	struct send_request_data *req_data = send_request_data_alloc(endpoint, token, callback);
+	struct ast_sip_contact *contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+
+	if (!req_data) {
+		return -1;
+	}
+
+	AST_RWLIST_RDLOCK(&supplements);
+	AST_LIST_TRAVERSE(&supplements, supplement, next) {
+		if (supplement->outgoing_request && does_method_match(&tdata->msg->line.req.method.name, supplement->method)) {
+			supplement->outgoing_request(endpoint, contact, tdata);
+		}
+	}
+	AST_RWLIST_UNLOCK(&supplements);
+
+	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
+	ao2_cleanup(contact);
+
+	if (pjsip_endpt_send_request(ast_sip_get_pjsip_endpoint(), tdata, -1, req_data, send_request_cb) != PJ_SUCCESS) {
 		ast_log(LOG_ERROR, "Error attempting to send outbound %.*s request to endpoint %s\n",
 				(int) pj_strlen(&tdata->msg->line.req.method.name),
 				pj_strbuf(&tdata->msg->line.req.method.name),
 				ast_sorcery_object_get_id(endpoint));
-		ao2_ref(endpoint, -1);
+		ao2_cleanup(req_data);
 		return -1;
 	}
 
 	return 0;
 }
 
-int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint)
+int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg,
+	struct ast_sip_endpoint *endpoint, void *token,
+	void (*callback)(void *token, pjsip_event *e))
 {
 	ast_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
 
 	if (dlg) {
 		return send_in_dialog_request(tdata, dlg);
 	} else {
-		return send_out_of_dialog_request(tdata, endpoint);
+		return send_out_of_dialog_request(tdata, endpoint, token, callback);
 	}
 }
 
@@ -2009,6 +2147,57 @@
 	pj_hash_set(pool, ht, key, PJ_HASH_KEY_STRING, 0, val);
 
 	return ht;
+}
+
+static pj_bool_t supplement_on_rx_request(pjsip_rx_data *rdata)
+{
+	struct ast_sip_supplement *supplement;
+
+	if (pjsip_rdata_get_dlg(rdata)) {
+		return PJ_FALSE;
+	}
+
+	AST_RWLIST_RDLOCK(&supplements);
+	AST_LIST_TRAVERSE(&supplements, supplement, next) {
+		if (supplement->incoming_request && does_method_match(&rdata->msg_info.msg->line.req.method.name, supplement->method)) {
+			supplement->incoming_request(ast_pjsip_rdata_get_endpoint(rdata), rdata);
+		}
+	}
+	AST_RWLIST_UNLOCK(&supplements);
+
+	return PJ_FALSE;
+}
+
+int ast_sip_send_response(pjsip_response_addr *res_addr, pjsip_tx_data *tdata, struct ast_sip_endpoint *sip_endpoint)
+{
+	struct ast_sip_supplement *supplement;
+	pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+	struct ast_sip_contact *contact = ast_sip_mod_data_get(tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT);
+
+	AST_RWLIST_RDLOCK(&supplements);
+	AST_LIST_TRAVERSE(&supplements, supplement, next) {
+		if (supplement->outgoing_response && does_method_match(&cseq->method.name, supplement->method)) {
+			supplement->outgoing_response(sip_endpoint, contact, tdata);
+		}
+	}
+	AST_RWLIST_UNLOCK(&supplements);
+
+	ast_sip_mod_data_set(tdata->pool, tdata->mod_data, supplement_module.id, MOD_DATA_CONTACT, NULL);
+	ao2_cleanup(contact);
+
+	return pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), res_addr, tdata, NULL, NULL);
+}
+
+int ast_sip_create_response(const pjsip_rx_data *rdata, int st_code,
+	struct ast_sip_contact *contact, pjsip_tx_data **tdata)
+{
+	int res = pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, st_code, NULL, tdata);
+
+	if (!res) {
+		ast_sip_mod_data_set((*tdata)->pool, (*tdata)->mod_data, supplement_module.id, MOD_DATA_CONTACT, ao2_bump(contact));
+	}
+
+	return res;
 }
 
 static void remove_request_headers(pjsip_endpoint *endpt)
@@ -2128,8 +2317,8 @@
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
-	if (ast_sip_initialize_outbound_authentication()) {
-		ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n");
+	if (ast_sip_register_service(&supplement_module)) {
+		ast_log(LOG_ERROR, "Failed to initialize supplement hooks. Aborting load\n");
 		ast_sip_destroy_distributor();
 		ast_res_pjsip_destroy_configuration();
 		ast_sip_destroy_global_headers();
@@ -2142,6 +2331,21 @@
 		return AST_MODULE_LOAD_DECLINE;
 	}
 
+	if (ast_sip_initialize_outbound_authentication()) {
+		ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n");
+		ast_sip_unregister_service(&supplement_module);
+		ast_sip_destroy_distributor();
+		ast_res_pjsip_destroy_configuration();
+		ast_sip_destroy_global_headers();
+		stop_monitor_thread();
+		pj_pool_release(memory_pool);
+		memory_pool = NULL;
+		pjsip_endpt_destroy(ast_pjsip_endpoint);
+		ast_pjsip_endpoint = NULL;
+		pj_caching_pool_destroy(&caching_pool);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
 	ast_res_pjsip_init_options_handling(0);
 
 	ast_module_ref(ast_module_info->self);

Modified: branches/12/res/res_pjsip/location.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/res_pjsip/location.c?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/res/res_pjsip/location.c (original)
+++ branches/12/res/res_pjsip/location.c Wed Jan 15 07:14:06 2014
@@ -178,7 +178,7 @@
 	return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name);
 }
 
-int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time)
+int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time, const char *path_info)
 {
 	char name[AST_UUID_STR_LEN];
 	RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
@@ -193,6 +193,9 @@
 	contact->expiration_time = expiration_time;
 	contact->qualify_frequency = aor->qualify_frequency;
 	contact->authenticate_qualify = aor->authenticate_qualify;
+	if (path_info && aor->support_path) {
+		ast_string_field_set(contact, path, path_info);
+	}
 
 	if (!ast_strlen_zero(aor->outbound_proxy)) {
 		ast_string_field_set(contact, outbound_proxy, aor->outbound_proxy);
@@ -610,6 +613,7 @@
 
 	ast_sorcery_object_field_register(sorcery, "contact", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "contact", "uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, uri));
+	ast_sorcery_object_field_register(sorcery, "contact", "path", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, path));
 	ast_sorcery_object_field_register_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "contact", "qualify_frequency", 0, OPT_UINT_T,
 					  PARSE_IN_RANGE, FLDSET(struct ast_sip_contact, qualify_frequency), 0, 86400);
@@ -626,6 +630,7 @@
 	ast_sorcery_object_field_register_custom(sorcery, "aor", "contact", "", permanent_uri_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register(sorcery, "aor", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, mailboxes));
 	ast_sorcery_object_field_register(sorcery, "aor", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_aor, outbound_proxy));
+	ast_sorcery_object_field_register(sorcery, "aor", "support_path", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, support_path));
 
 	ast_sip_register_endpoint_formatter(&endpoint_aor_formatter);
 	ast_sip_register_cli_formatter(&cli_contact_formatter);

Modified: branches/12/res/res_pjsip/pjsip_distributor.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/res_pjsip/pjsip_distributor.c?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/res/res_pjsip/pjsip_distributor.c (original)
+++ branches/12/res/res_pjsip/pjsip_distributor.c Wed Jan 15 07:14:06 2014
@@ -321,7 +321,7 @@
 
 static pjsip_module auth_mod = {
 	.name = {"Request Authenticator", 21},
-	.priority = PJSIP_MOD_PRIORITY_APPLICATION - 1,
+	.priority = PJSIP_MOD_PRIORITY_APPLICATION - 2,
 	.on_rx_request = authenticate,
 };
 

Modified: branches/12/res/res_pjsip/pjsip_options.c
URL: http://svnview.digium.com/svn/asterisk/branches/12/res/res_pjsip/pjsip_options.c?view=diff&rev=405565&r1=405564&r2=405565
==============================================================================
--- branches/12/res/res_pjsip/pjsip_options.c (original)
+++ branches/12/res/res_pjsip/pjsip_options.c Wed Jan 15 07:14:06 2014
@@ -34,7 +34,7 @@
 #define DEFAULT_ENCODING "text/plain"
 #define QUALIFIED_BUCKETS 211
 
-static int qualify_contact(struct ast_sip_contact *contact);
+static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact);
 
 /*!
  * \internal
@@ -186,7 +186,7 @@
  * \internal
  * \brief Find endpoints associated with the given contact.
  */
-static struct ao2_container *find_endpoints(struct ast_sip_contact *contact)
+static struct ao2_iterator *find_endpoints(struct ast_sip_contact *contact)
 {
 	RAII_VAR(struct ao2_container *, endpoints,
 		 ast_sip_get_endpoints(), ao2_cleanup);
@@ -201,43 +201,15 @@
 static void qualify_contact_cb(void *token, pjsip_event *e)
 {
 	RAII_VAR(struct ast_sip_contact *, contact, token, ao2_cleanup);
-	RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
-
-	pjsip_transaction *tsx = e->body.tsx_state.tsx;
-	pjsip_rx_data *challenge = e->body.tsx_state.src.rdata;
-	pjsip_tx_data *tdata;
 
 	switch(e->body.tsx_state.type) {
 	case PJSIP_EVENT_TRANSPORT_ERROR:
 	case PJSIP_EVENT_TIMER:
 		update_contact_status(contact, UNAVAILABLE);
-		return;
+		break;
 	default:
+		update_contact_status(contact, AVAILABLE);
 		break;
-	}
-
-	if (!contact->authenticate_qualify || (tsx->status_code != 401 &&
-					       tsx->status_code != 407)) {
-		update_contact_status(contact, AVAILABLE);
-		return;
-	}
-
-	/* try to find endpoints that are associated with the contact */
-	if (!(endpoints = find_endpoints(contact))) {
-		ast_log(LOG_ERROR, "No endpoints found for contact %s, cannot authenticate",
-			contact->uri);

[... 953 lines stripped ...]



More information about the asterisk-commits mailing list