[svn-commits] file: trunk r392565 - in /trunk: channels/ channels/sip/ channels/sip/include...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Jun 22 09:03:25 CDT 2013


Author: file
Date: Sat Jun 22 09:03:22 2013
New Revision: 392565

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=392565
Log:
Merge in current pimp_my_sip work, including:

1. Security events
2. Websocket support
3. Diversion header + redirecting support
4. An anonymous endpoint identifier
5. Inbound extension state subscription support
6. PIDF notify generation
7. One touch recording support (special thanks Sean Bright!)
8. Blind and attended transfer support
9. Automatic inbound registration expiration
10. SRTP support
11. Media offer control dialplan function
12. Connected line support
13. SendText() support
14. Qualify support
15. Inband DTMF detection
16. Call and pickup groups
17. Messaging support

Thanks everyone!

Side note: I'm reminded of the song "How Far We've Come" by Matchbox Twenty.

Added:
    trunk/include/asterisk/res_sip_exten_state.h
      - copied unchanged from r392563, team/group/pimp_my_sip/include/asterisk/res_sip_exten_state.h
    trunk/include/asterisk/sdp_srtp.h
      - copied unchanged from r392563, team/group/pimp_my_sip/include/asterisk/sdp_srtp.h
    trunk/main/sdp_srtp.c
      - copied unchanged from r392563, team/group/pimp_my_sip/main/sdp_srtp.c
    trunk/res/res_sip/security_events.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip/security_events.c
    trunk/res/res_sip_diversion.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_diversion.c
    trunk/res/res_sip_endpoint_identifier_anonymous.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_endpoint_identifier_anonymous.c
    trunk/res/res_sip_exten_state.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_exten_state.c
    trunk/res/res_sip_exten_state.exports.in
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_exten_state.exports.in
    trunk/res/res_sip_messaging.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_messaging.c
    trunk/res/res_sip_one_touch_record_info.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_one_touch_record_info.c
    trunk/res/res_sip_pidf.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_pidf.c
    trunk/res/res_sip_refer.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_refer.c
    trunk/res/res_sip_registrar_expire.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_registrar_expire.c
    trunk/res/res_sip_transport_websocket.c
      - copied unchanged from r392563, team/group/pimp_my_sip/res/res_sip_transport_websocket.c
Removed:
    trunk/channels/sip/include/sdp_crypto.h
    trunk/channels/sip/include/srtp.h
    trunk/channels/sip/sdp_crypto.c
    trunk/channels/sip/srtp.c
Modified:
    trunk/channels/chan_gulp.c
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h
    trunk/configs/res_sip.conf.sample
    trunk/include/asterisk/res_sip.h
    trunk/include/asterisk/res_sip_pubsub.h
    trunk/include/asterisk/res_sip_session.h
    trunk/main/pbx.c
    trunk/res/res_sip.c
    trunk/res/res_sip.exports.in
    trunk/res/res_sip/config_transport.c
    trunk/res/res_sip/include/res_sip_private.h
    trunk/res/res_sip/location.c
    trunk/res/res_sip/sip_configuration.c
    trunk/res/res_sip/sip_distributor.c
    trunk/res/res_sip/sip_options.c
    trunk/res/res_sip_caller_id.c
    trunk/res/res_sip_dtmf_info.c
    trunk/res/res_sip_outbound_registration.c
    trunk/res/res_sip_pubsub.c
    trunk/res/res_sip_pubsub.exports.in
    trunk/res/res_sip_registrar.c
    trunk/res/res_sip_sdp_rtp.c
    trunk/res/res_sip_session.c
    trunk/res/res_sip_session.exports.in

Modified: trunk/channels/chan_gulp.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_gulp.c?view=diff&rev=392565&r1=392564&r2=392565
==============================================================================
--- trunk/channels/chan_gulp.c (original)
+++ trunk/channels/chan_gulp.c Sat Jun 22 09:03:22 2013
@@ -53,6 +53,7 @@
 #include "asterisk/musiconhold.h"
 #include "asterisk/causes.h"
 #include "asterisk/taskprocessor.h"
+#include "asterisk/dsp.h"
 #include "asterisk/stasis_endpoints.h"
 #include "asterisk/stasis_channels.h"
 
@@ -79,6 +80,19 @@
 			<para>Returns a properly formatted dial string for dialing all contacts on an AOR.</para>
 		</description>
 	</function>
+	<function name="GULP_MEDIA_OFFER" language="en_US">
+		<synopsis>
+			Media and codec offerings to be set on an outbound SIP channel prior to dialing.
+		</synopsis>
+		<syntax>
+			<parameter name="media" required="true">
+				<para>types of media offered</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Returns the codecs offered based upon the media choice</para>
+		</description>
+	</function>
  ***/
 
 static const char desc[] = "Gulp SIP Channel";
@@ -128,6 +142,7 @@
 static struct ast_frame *gulp_read(struct ast_channel *ast);
 static int gulp_write(struct ast_channel *ast, struct ast_frame *f);
 static int gulp_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
+static int gulp_transfer(struct ast_channel *ast, const char *target);
 static int gulp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
 static int gulp_devicestate(const char *data);
 
@@ -147,6 +162,7 @@
 	.write_video = gulp_write,
 	.exception = gulp_read,
 	.indicate = gulp_indicate,
+	.transfer = gulp_transfer,
 	.fixup = gulp_fixup,
 	.devicestate = gulp_devicestate,
 	.properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER
@@ -255,6 +271,105 @@
 	.read = gulp_dial_contacts,
 };
 
+static int media_offer_read_av(struct ast_sip_session *session, char *buf,
+			       size_t len, enum ast_format_type media_type)
+{
+	int i, size = 0;
+	struct ast_format fmt;
+	const char *name;
+
+	for (i = 0; ast_codec_pref_index(&session->override_prefs, i, &fmt); ++i) {
+		if (AST_FORMAT_GET_TYPE(fmt.id) != media_type) {
+			continue;
+		}
+
+		name = ast_getformatname(&fmt);
+
+		if (ast_strlen_zero(name)) {
+			ast_log(LOG_WARNING, "GULP_MEDIA_OFFER unrecognized format %s\n", name);
+			continue;
+		}
+
+		/* add one since we'll include a comma */
+		size = strlen(name) + 1;
+		len -= size;
+		if ((len) < 0) {
+			break;
+		}
+
+		/* no reason to use strncat here since we have already ensured buf has
+                   enough space, so strcat can be safely used */
+		strcat(buf, name);
+		strcat(buf, ",");
+	}
+
+	if (size) {
+		/* remove the extra comma */
+		buf[strlen(buf) - 1] = '\0';
+	}
+	return 0;
+}
+
+struct media_offer_data {
+	struct ast_sip_session *session;
+	enum ast_format_type media_type;
+	const char *value;
+};
+
+static int media_offer_write_av(void *obj)
+{
+	struct media_offer_data *data = obj;
+	int i;
+	struct ast_format fmt;
+	/* remove all of the given media type first */
+	for (i = 0; ast_codec_pref_index(&data->session->override_prefs, i, &fmt); ++i) {
+		if (AST_FORMAT_GET_TYPE(fmt.id) == data->media_type) {
+			ast_codec_pref_remove(&data->session->override_prefs, &fmt);
+		}
+	}
+	ast_format_cap_remove_bytype(data->session->req_caps, data->media_type);
+	ast_parse_allow_disallow(&data->session->override_prefs, data->session->req_caps, data->value, 1);
+
+	return 0;
+}
+
+static int media_offer_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+
+	if (!strcmp(data, "audio")) {
+		return media_offer_read_av(pvt->session, buf, len, AST_FORMAT_TYPE_AUDIO);
+	} else if (!strcmp(data, "video")) {
+		return media_offer_read_av(pvt->session, buf, len, AST_FORMAT_TYPE_VIDEO);
+	}
+
+	return 0;
+}
+
+static int media_offer_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+
+	struct media_offer_data mdata = {
+		.session = pvt->session,
+		.value = value
+	};
+
+	if (!strcmp(data, "audio")) {
+		mdata.media_type = AST_FORMAT_TYPE_AUDIO;
+	} else if (!strcmp(data, "video")) {
+		mdata.media_type = AST_FORMAT_TYPE_VIDEO;
+	}
+
+	return ast_sip_push_task_synchronous(pvt->session->serializer, media_offer_write_av, &mdata);
+}
+
+static struct ast_custom_function media_offer_function = {
+	.name = "GULP_MEDIA_OFFER",
+	.read = media_offer_read,
+	.write = media_offer_write
+};
+
 /*! \brief Function called by RTP engine to get local audio RTP peer */
 static enum ast_rtp_glue_result gulp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
 {
@@ -402,7 +517,11 @@
 
 	if (changed) {
 		ao2_ref(session, +1);
-		ast_sip_push_task(session->serializer, send_direct_media_request, session);
+
+
+		if (ast_sip_push_task(session->serializer, send_direct_media_request, session)) {
+			ao2_cleanup(session);
+		}
 	}
 
 	return 0;
@@ -467,6 +586,12 @@
 	ast_channel_exten_set(chan, S_OR(exten, "s"));
 	ast_channel_priority_set(chan, 1);
 
+	ast_channel_callgroup_set(chan, session->endpoint->callgroup);
+	ast_channel_pickupgroup_set(chan, session->endpoint->pickupgroup);
+
+	ast_channel_named_callgroups_set(chan, session->endpoint->named_callgroups);
+	ast_channel_named_pickupgroups_set(chan, session->endpoint->named_pickupgroups);
+
 	ast_endpoint_add_channel(session->endpoint->persistent, chan);
 
 	return chan;
@@ -513,6 +638,7 @@
 static struct ast_frame *gulp_read(struct ast_channel *ast)
 {
 	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 	struct ast_frame *f;
 	struct ast_sip_session_media *media = NULL;
 	int rtcp = 0;
@@ -539,14 +665,27 @@
 		return &ast_null_frame;
 	}
 
-	f = ast_rtp_instance_read(media->rtp, rtcp);
-
-	if (f && f->frametype == AST_FRAME_VOICE) {
-		if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &f->subclass.format))) {
-			ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
-			ast_format_cap_set(ast_channel_nativeformats(ast), &f->subclass.format);
-			ast_set_read_format(ast, ast_channel_readformat(ast));
-			ast_set_write_format(ast, ast_channel_writeformat(ast));
+	if (!(f = ast_rtp_instance_read(media->rtp, rtcp))) {
+		return f;
+	}
+
+	if (f->frametype != AST_FRAME_VOICE) {
+		return f;
+	}
+
+	if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &f->subclass.format))) {
+		ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
+		ast_format_cap_set(ast_channel_nativeformats(ast), &f->subclass.format);
+		ast_set_read_format(ast, ast_channel_readformat(ast));
+		ast_set_write_format(ast, ast_channel_writeformat(ast));
+	}
+
+	if (session->dsp) {
+		f = ast_dsp_process(ast, session->dsp, f);
+
+		if (f && (f->frametype == AST_FRAME_DTMF)) {
+			ast_debug(3, "* Detected inband DTMF '%c' on '%s'\n", f->subclass.integer,
+				ast_channel_name(ast));
 		}
 	}
 
@@ -769,7 +908,7 @@
 		.body_text = xml
 	};
 
-	struct ast_sip_session *session = data;
+	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)) {
@@ -781,6 +920,40 @@
 		return -1;
 	}
 	ast_sip_session_send_request(session, tdata);
+
+	return 0;
+}
+
+/*! \brief Update connected line information */
+static int update_connected_line_information(void *data)
+{
+	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
+
+	if ((ast_channel_state(session->channel) != AST_STATE_UP) && (session->inv_session->role == PJSIP_UAS_ROLE)) {
+		int response_code = 0;
+
+		if (ast_channel_state(session->channel) == AST_STATE_RING) {
+			response_code = !session->endpoint->inband_progress ? 180 : 183;
+		} else if (ast_channel_state(session->channel) == AST_STATE_RINGING) {
+			response_code = 183;
+		}
+
+		if (response_code) {
+			struct pjsip_tx_data *packet = NULL;
+
+			if (pjsip_inv_answer(session->inv_session, response_code, NULL, NULL, &packet) == PJ_SUCCESS) {
+				ast_sip_session_send_response(session, packet);
+			}
+		}
+	} else {
+		enum ast_sip_session_refresh_method method = session->endpoint->connected_line_method;
+
+		if (session->inv_session->invite_tsx && (session->inv_session->options & PJSIP_INV_SUPPORT_UPDATE)) {
+			method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
+		}
+
+		ast_sip_session_refresh(session, NULL, NULL, method, 0);
+	}
 
 	return 0;
 }
@@ -797,7 +970,12 @@
 	switch (condition) {
 	case AST_CONTROL_RINGING:
 		if (ast_channel_state(ast) == AST_STATE_RING) {
-			response_code = 180;
+			if (session->endpoint->inband_progress) {
+				response_code = 183;
+				res = -1;
+			} else {
+				response_code = 180;
+			}
 		} else {
 			res = -1;
 		}
@@ -841,9 +1019,20 @@
 	case AST_CONTROL_VIDUPDATE:
 		media = pvt->media[SIP_MEDIA_VIDEO];
 		if (media && media->rtp) {
-			ast_sip_push_task(session->serializer, transmit_info_with_vidupdate, session);
-		} else
+			ao2_ref(session, +1);
+
+			if (ast_sip_push_task(session->serializer, transmit_info_with_vidupdate, session)) {
+				ao2_cleanup(session);
+			}
+		} else {
 			res = -1;
+		}
+		break;
+	case AST_CONTROL_CONNECTED_LINE:
+		ao2_ref(session, +1);
+		if (ast_sip_push_task(session->serializer, update_connected_line_information, session)) {
+			ao2_cleanup(session);
+		}
 		break;
 	case AST_CONTROL_UPDATE_RTP_PEER:
 	case AST_CONTROL_PVT_CAUSE_CODE:
@@ -857,6 +1046,13 @@
 	case AST_CONTROL_SRCUPDATE:
 		break;
 	case AST_CONTROL_SRCCHANGE:
+		break;
+	case AST_CONTROL_REDIRECTING:
+		if (ast_channel_state(ast) != AST_STATE_UP) {
+			response_code = 181;
+		} else {
+			res = -1;
+		}
 		break;
 	case -1:
 		res = -1;
@@ -867,21 +1063,141 @@
 		break;
 	}
 
-	if (!res && response_code) {
+	if (response_code) {
 		struct indicate_data *ind_data = indicate_data_alloc(session, condition, response_code, data, datalen);
-		if (ind_data) {
-			res = ast_sip_push_task(session->serializer, indicate, ind_data);
-			if (res) {
-				ast_log(LOG_NOTICE, "Cannot send response code %d to endpoint %s. Could not queue task properly\n",
-						response_code, ast_sorcery_object_get_id(session->endpoint));
-				ao2_cleanup(ind_data);
-			}
-		} else {
+		if (!ind_data || ast_sip_push_task(session->serializer, indicate, ind_data)) {
+			ast_log(LOG_NOTICE, "Cannot send response code %d to endpoint %s. Could not queue task properly\n",
+					response_code, ast_sorcery_object_get_id(session->endpoint));
+			ao2_cleanup(ind_data);
 			res = -1;
 		}
 	}
 
 	return res;
+}
+
+struct transfer_data {
+	struct ast_sip_session *session;
+	char *target;
+};
+
+static void transfer_data_destroy(void *obj)
+{
+	struct transfer_data *trnf_data = obj;
+
+	ast_free(trnf_data->target);
+	ao2_cleanup(trnf_data->session);
+}
+
+static struct transfer_data *transfer_data_alloc(struct ast_sip_session *session, const char *target)
+{
+	struct transfer_data *trnf_data = ao2_alloc(sizeof(*trnf_data), transfer_data_destroy);
+
+	if (!trnf_data) {
+		return NULL;
+	}
+
+	if (!(trnf_data->target = ast_strdup(target))) {
+		ao2_ref(trnf_data, -1);
+		return NULL;
+	}
+
+	ao2_ref(session, +1);
+	trnf_data->session = session;
+
+	return trnf_data;
+}
+
+static void transfer_redirect(struct ast_sip_session *session, const char *target)
+{
+	pjsip_tx_data *packet;
+	enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
+	pjsip_contact_hdr *contact;
+	pj_str_t tmp;
+
+	if (pjsip_inv_end_session(session->inv_session, 302, NULL, &packet) != PJ_SUCCESS) {
+		message = AST_TRANSFER_FAILED;
+		ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+
+		return;
+	}
+
+	if (!(contact = pjsip_msg_find_hdr(packet->msg, PJSIP_H_CONTACT, NULL))) {
+		contact = pjsip_contact_hdr_create(packet->pool);
+	}
+
+	pj_strdup2_with_null(packet->pool, &tmp, target);
+	if (!(contact->uri = pjsip_parse_uri(packet->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR))) {
+		message = AST_TRANSFER_FAILED;
+		ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+		pjsip_tx_data_dec_ref(packet);
+
+		return;
+	}
+	pjsip_msg_add_hdr(packet->msg, (pjsip_hdr *) contact);
+
+	ast_sip_session_send_response(session, packet);
+	ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+}
+
+static void transfer_refer(struct ast_sip_session *session, const char *target)
+{
+	pjsip_evsub *sub;
+	enum ast_control_transfer message = AST_TRANSFER_SUCCESS;
+	pj_str_t tmp;
+	pjsip_tx_data *packet;
+
+	if (pjsip_xfer_create_uac(session->inv_session->dlg, NULL, &sub) != PJ_SUCCESS) {
+		message = AST_TRANSFER_FAILED;
+		ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+
+		return;
+	}
+
+	if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, target), &packet) != PJ_SUCCESS) {
+		message = AST_TRANSFER_FAILED;
+		ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+		pjsip_evsub_terminate(sub, PJ_FALSE);
+
+		return;
+	}
+
+	pjsip_xfer_send_request(sub, packet);
+	ast_queue_control_data(session->channel, AST_CONTROL_TRANSFER, &message, sizeof(message));
+}
+
+static int transfer(void *data)
+{
+	struct transfer_data *trnf_data = data;
+
+	if (ast_channel_state(trnf_data->session->channel) == AST_STATE_RING) {
+		transfer_redirect(trnf_data->session, trnf_data->target);
+	} else {
+		transfer_refer(trnf_data->session, trnf_data->target);
+	}
+
+	ao2_ref(trnf_data, -1);
+	return 0;
+}
+
+/*! \brief Function called by core for Asterisk initiated transfer */
+static int gulp_transfer(struct ast_channel *chan, const char *target)
+{
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+	struct ast_sip_session *session = pvt->session;
+	struct transfer_data *trnf_data = transfer_data_alloc(session, target);
+
+	if (!trnf_data) {
+		return -1;
+	}
+
+	if (ast_sip_push_task(session->serializer, transfer, trnf_data)) {
+		ast_log(LOG_WARNING, "Error requesting transfer\n");
+		ao2_cleanup(trnf_data);
+		return -1;
+	}
+
+	return 0;
 }
 
 /*! \brief Function called by core to start a DTMF digit */
@@ -1014,18 +1330,18 @@
 
 static int call(void *data)
 {
-	pjsip_tx_data *packet;
 	struct ast_sip_session *session = data;
-
-	if (pjsip_inv_invite(session->inv_session, &packet) != PJ_SUCCESS) {
+	pjsip_tx_data *tdata;
+
+	int res = ast_sip_session_create_invite(session, &tdata);
+
+	if (res) {
 		ast_queue_hangup(session->channel);
 	} else {
-		ast_sip_session_send_request(session, packet);
-	}
-
+		ast_sip_session_send_request(session, tdata);
+	}
 	ao2_ref(session, -1);
-
-	return 0;
+	return res;
 }
 
 /*! \brief Function called by core to actually start calling a remote party */
@@ -1128,7 +1444,8 @@
 	struct ast_sip_session *session = pvt->session;
 	int cause = h_data->cause;
 
-	if (((status = pjsip_inv_end_session(session->inv_session, cause ? cause : 603, NULL, &packet)) == PJ_SUCCESS) && packet) {
+	if (!session->defer_terminate &&
+		((status = pjsip_inv_end_session(session->inv_session, cause ? cause : 603, NULL, &packet)) == PJ_SUCCESS) && packet) {
 		if (packet->msg->type == PJSIP_RESPONSE_MSG) {
 			ast_sip_session_send_response(session, packet);
 		} else {
@@ -1255,9 +1572,66 @@
 	return session->channel;
 }
 
+struct sendtext_data {
+	struct ast_sip_session *session;
+	char text[0];
+};
+
+static void sendtext_data_destroy(void *obj)
+{
+	struct sendtext_data *data = obj;
+	ao2_ref(data->session, -1);
+}
+
+static struct sendtext_data* sendtext_data_create(struct ast_sip_session *session, const char *text)
+{
+	int size = strlen(text) + 1;
+	struct sendtext_data *data = ao2_alloc(sizeof(*data)+size, sendtext_data_destroy);
+
+	if (!data) {
+		return NULL;
+	}
+
+	data->session = session;
+	ao2_ref(data->session, +1);
+	ast_copy_string(data->text, text, size);
+	return data;
+}
+
+static int sendtext(void *obj)
+{
+	RAII_VAR(struct sendtext_data *, data, obj, ao2_cleanup);
+	pjsip_tx_data *tdata;
+
+	const struct ast_sip_body body = {
+		.type = "text",
+		.subtype = "plain",
+		.body_text = data->text
+	};
+
+	/* NOT ast_strlen_zero, because a zero-length message is specifically
+	 * allowed by RFC 3428 (See section 10, Examples) */
+	if (!data->text) {
+		return 0;
+	}
+
+	ast_sip_create_request("MESSAGE", data->session->inv_session->dlg, data->session->endpoint, NULL, &tdata);
+	ast_sip_add_body(tdata, &body);
+	ast_sip_send_request(tdata, data->session->inv_session->dlg, data->session->endpoint);
+
+	return 0;
+}
+
 /*! \brief Function called by core to send text on Gulp session */
 static int gulp_sendtext(struct ast_channel *ast, const char *text)
 {
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct sendtext_data *data = sendtext_data_create(pvt->session, text);
+
+	if (!data || ast_sip_push_task(pvt->session->serializer, sendtext, data)) {
+		ao2_ref(data, -1);
+		return -1;
+	}
 	return 0;
 }
 
@@ -1391,7 +1765,6 @@
 static int gulp_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
 	pjsip_tx_data *packet = NULL;
-	int res = AST_PBX_FAILED;
 
 	if (session->channel) {
 		return 0;
@@ -1405,6 +1778,14 @@
 		ast_log(LOG_ERROR, "Failed to allocate new GULP channel on incoming SIP INVITE\n");
 		return -1;
 	}
+	/* channel gets created on incoming request, but we wait to call start
+           so other supplements have a chance to run */
+	return 0;
+}
+
+static int pbx_start_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)
+{
+	int res;
 
 	res = ast_pbx_start(session->channel);
 
@@ -1428,6 +1809,12 @@
 
 	return (res == AST_PBX_SUCCESS) ? 0 : -1;
 }
+
+static struct ast_sip_session_supplement pbx_start_supplement = {
+	.method = "INVITE",
+	.priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_LAST,
+	.incoming_request = pbx_start_incoming_request,
+};
 
 /*! \brief Function called when a response is received on the session */
 static void gulp_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
@@ -1496,13 +1883,24 @@
 		goto end;
 	}
 
+	if (ast_custom_function_register(&media_offer_function)) {
+		ast_log(LOG_WARNING, "Unable to register GULP_MEDIA_OFFER dialplan function\n");
+	}
+
 	if (ast_sip_session_register_supplement(&gulp_supplement)) {
 		ast_log(LOG_ERROR, "Unable to register Gulp supplement\n");
 		goto end;
 	}
 
+	if (ast_sip_session_register_supplement(&pbx_start_supplement)) {
+		ast_log(LOG_ERROR, "Unable to register Gulp pbx start supplement\n");
+		ast_sip_session_unregister_supplement(&gulp_supplement);
+		goto end;
+	}
+
 	if (ast_sip_session_register_supplement(&gulp_ack_supplement)) {
 		ast_log(LOG_ERROR, "Unable to register Gulp ACK supplement\n");
+		ast_sip_session_unregister_supplement(&pbx_start_supplement);
 		ast_sip_session_unregister_supplement(&gulp_supplement);
 		goto end;
 	}
@@ -1510,6 +1908,7 @@
 	return 0;
 
 end:
+	ast_custom_function_unregister(&media_offer_function);
 	ast_custom_function_unregister(&gulp_dial_contacts_function);
 	ast_channel_unregister(&gulp_tech);
 	ast_rtp_glue_unregister(&gulp_rtp_glue);
@@ -1526,7 +1925,11 @@
 /*! \brief Unload the Gulp channel from Asterisk */
 static int unload_module(void)
 {
+	ast_custom_function_unregister(&media_offer_function);
+
 	ast_sip_session_unregister_supplement(&gulp_supplement);
+	ast_sip_session_unregister_supplement(&pbx_start_supplement);
+
 	ast_custom_function_unregister(&gulp_dial_contacts_function);
 	ast_channel_unregister(&gulp_tech);
 	ast_rtp_glue_unregister(&gulp_rtp_glue);

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=392565&r1=392564&r2=392565
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Sat Jun 22 09:03:22 2013
@@ -285,8 +285,7 @@
 #include "sip/include/config_parser.h"
 #include "sip/include/reqresp_parser.h"
 #include "sip/include/sip_utils.h"
-#include "sip/include/srtp.h"
-#include "sip/include/sdp_crypto.h"
+#include "asterisk/sdp_srtp.h"
 #include "asterisk/ccss.h"
 #include "asterisk/xml.h"
 #include "sip/include/dialog.h"
@@ -1490,8 +1489,7 @@
 static void handle_response(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, uint32_t seqno);
 
 /*------ SRTP Support -------- */
-static int setup_srtp(struct sip_srtp **srtp);
-static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a);
+static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp, const char *a);
 
 /*------ T38 Support --------- */
 static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
@@ -5918,7 +5916,7 @@
 }
 
 /*! \brief Initialize DTLS-SRTP support on an RTP instance */
-static int dialog_initialize_dtls_srtp(const struct sip_pvt *dialog, struct ast_rtp_instance *rtp, struct sip_srtp **srtp)
+static int dialog_initialize_dtls_srtp(const struct sip_pvt *dialog, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp)
 {
 	struct ast_rtp_engine_dtls *dtls;
 
@@ -5943,7 +5941,7 @@
 		return -1;
 	}
 
-	if (!(*srtp = sip_srtp_alloc())) {
+	if (!(*srtp = ast_sdp_srtp_alloc())) {
 		ast_log(LOG_ERROR, "Failed to create required SRTP structure on RTP instance '%p'\n",
 			rtp);
 		return -1;
@@ -6418,17 +6416,17 @@
 			ast_clear_flag(&p->flags[0], SIP_REINVITE);
 		}
 
-		if (p->rtp && !p->srtp && setup_srtp(&p->srtp) < 0) {
+		if (p->rtp && !p->srtp && !(p->srtp = ast_sdp_srtp_alloc())) {
 			ast_log(LOG_WARNING, "SRTP audio setup failed\n");
 			return -1;
 		}
 
-		if (p->vrtp && !p->vsrtp && setup_srtp(&p->vsrtp) < 0) {
+		if (p->vrtp && !p->vsrtp && !(p->vsrtp = ast_sdp_srtp_alloc())) {
 			ast_log(LOG_WARNING, "SRTP video setup failed\n");
 			return -1;
 		}
 
-		if (p->trtp && !p->tsrtp && setup_srtp(&p->tsrtp) < 0) {
+		if (p->trtp && !p->tsrtp && !(p->tsrtp = ast_sdp_srtp_alloc())) {
 			ast_log(LOG_WARNING, "SRTP text setup failed\n");
 			return -1;
 		}
@@ -6690,17 +6688,17 @@
 	destroy_msg_headers(p);
 
 	if (p->srtp) {
-		sip_srtp_destroy(p->srtp);
+		ast_sdp_srtp_destroy(p->srtp);
 		p->srtp = NULL;
 	}
 
 	if (p->vsrtp) {
-		sip_srtp_destroy(p->vsrtp);
+		ast_sdp_srtp_destroy(p->vsrtp);
 		p->vsrtp = NULL;
 	}
 
 	if (p->tsrtp) {
-		sip_srtp_destroy(p->tsrtp);
+		ast_sdp_srtp_destroy(p->tsrtp);
 		p->tsrtp = NULL;
 	}
 
@@ -10154,7 +10152,7 @@
 					secure_audio = 1;
 
 					if (p->srtp) {
-						ast_set_flag(p->srtp, SRTP_CRYPTO_OFFER_OK);
+						ast_set_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK);
 					}
 				} else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
 					secure_audio = 1;
@@ -10235,8 +10233,8 @@
 				} else if (!strcmp(protocol, "UDP/TLS/RTP/SAVP") || !strcmp(protocol, "UDP/TLS/RTP/SAVPF")) {
 					secure_video = 1;
 
-					if (p->vsrtp || (p->vsrtp = sip_srtp_alloc())) {
-						ast_set_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK);
+					if (p->vsrtp || (p->vsrtp = ast_sdp_srtp_alloc())) {
+						ast_set_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK);
 					}
 				} else if (!strcmp(protocol, "RTP/SAVP") || !strcmp(protocol, "RTP/SAVPF")) {
 					secure_video = 1;
@@ -10516,7 +10514,7 @@
 		goto process_sdp_cleanup;
 	}
 
-	if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)))) {
+	if (secure_audio && !(p->srtp && (ast_test_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK)))) {
 		ast_log(LOG_WARNING, "Can't provide secure audio requested in SDP offer\n");
 		res = -1;
 		goto process_sdp_cleanup;
@@ -10528,7 +10526,7 @@
 		goto process_sdp_cleanup;
 	}
 
-	if (secure_video && !(p->vsrtp && (ast_test_flag(p->vsrtp, SRTP_CRYPTO_OFFER_OK)))) {
+	if (secure_video && !(p->vsrtp && (ast_test_flag(p->vsrtp, AST_SRTP_CRYPTO_OFFER_OK)))) {
 		ast_log(LOG_WARNING, "Can't provide secure video requested in SDP offer\n");
 		res = -1;
 		goto process_sdp_cleanup;
@@ -12993,52 +12991,20 @@
 	}
 }
 
-static void get_crypto_attrib(struct sip_pvt *p, struct sip_srtp *srtp, const char **a_crypto)
-{
-	int taglen = 80;
-
-	/* Set encryption properties */
-	if (srtp) {
-		if (!srtp->crypto) {
-			srtp->crypto = sdp_crypto_setup();
-		}
-
-		if (p->dtls_cfg.enabled) {
-			/* If DTLS-SRTP is enabled the key details will be pulled from TLS */
-			return;
-		}
-
-		/* set the key length based on INVITE or settings */
-		if (ast_test_flag(srtp, SRTP_CRYPTO_TAG_80)) {
-			taglen = 80;
-		} else if (ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32) ||
-		    ast_test_flag(srtp, SRTP_CRYPTO_TAG_32)) {
-			taglen = 32;
-		}
-
-		if (srtp->crypto && (sdp_crypto_offer(srtp->crypto, taglen) >= 0)) {
-			*a_crypto = sdp_crypto_attrib(srtp->crypto);
-		}
-
-		if (!*a_crypto) {
-			ast_log(LOG_WARNING, "No SRTP key management enabled\n");
-		}
-	}
-}
-
-static char *get_sdp_rtp_profile(const struct sip_pvt *p, unsigned int secure, struct ast_rtp_instance *instance)
-{
-	struct ast_rtp_engine_dtls *dtls;
-
-	if ((dtls = ast_rtp_instance_get_dtls(instance)) && dtls->active(instance)) {
-		return ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF) ? "UDP/TLS/RTP/SAVPF" : "UDP/TLS/RTP/SAVP";
-	} else {
-		if (ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)) {
-			return secure ? "RTP/SAVPF" : "RTP/AVPF";
-		} else {
-			return secure ? "RTP/SAVP" : "RTP/AVP";
-		}
-	}
+static char *crypto_get_attrib(struct ast_sdp_srtp *srtp, int dtls_enabled, int default_taglen_32)
+{
+	char *a_crypto;
+	char *orig_crypto;
+
+	if (!srtp) {
+		return NULL;
+	}
+
+	orig_crypto = ast_strdupa(ast_sdp_srtp_get_attrib(srtp, dtls_enabled, default_taglen_32));
+	if (ast_asprintf(&a_crypto, "a=crypto:%s\r\n", orig_crypto) == -1) {
+		return NULL;
+	}
+	return a_crypto;
 }
 
 /*! \brief Add Session Description Protocol message
@@ -13079,9 +13045,9 @@
 	struct ast_str *a_video = ast_str_create(256); /* Attributes for video */
 	struct ast_str *a_text = ast_str_create(256);  /* Attributes for text */
 	struct ast_str *a_modem = ast_str_alloca(1024); /* Attributes for modem */
-	const char *a_crypto = NULL;
-	const char *v_a_crypto = NULL;
-	const char *t_a_crypto = NULL;
+	RAII_VAR(char *, a_crypto, NULL, ast_free);
+	RAII_VAR(char *, v_a_crypto, NULL, ast_free);
+	RAII_VAR(char *, t_a_crypto, NULL, ast_free);
 
 	int x;
 	struct ast_format tmp_fmt;
@@ -13199,9 +13165,11 @@
 		/* Ok, we need video. Let's add what we need for video and set codecs.
 		   Video is handled differently than audio since we can not transcode. */
 		if (needvideo) {
-			get_crypto_attrib(p, p->vsrtp, &v_a_crypto);
+			v_a_crypto = crypto_get_attrib(p->vsrtp, p->dtls_cfg.enabled,
+				ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32));
 			ast_str_append(&m_video, 0, "m=video %d %s", ast_sockaddr_port(&vdest),
-				       get_sdp_rtp_profile(p, v_a_crypto ? 1 : 0, p->vrtp));
+				ast_sdp_get_rtp_profile(v_a_crypto ? 1 : 0, p->vrtp,
+					ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)));
 
 			/* Build max bitrate string */
 			if (p->maxcallbitrate)
@@ -13224,9 +13192,11 @@
 		if (needtext) {
 			if (sipdebug_text)
 				ast_verbose("Lets set up the text sdp\n");
-			get_crypto_attrib(p, p->tsrtp, &t_a_crypto);
+			t_a_crypto = crypto_get_attrib(p->tsrtp, p->dtls_cfg.enabled,
+				ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32));
 			ast_str_append(&m_text, 0, "m=text %d %s", ast_sockaddr_port(&tdest),
-				       get_sdp_rtp_profile(p, t_a_crypto ? 1 : 0, p->trtp));
+				ast_sdp_get_rtp_profile(t_a_crypto ? 1 : 0, p->trtp,
+					ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)));
 			if (debug) {  /* XXX should I use tdest below ? */
 				ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
 			}
@@ -13245,9 +13215,11 @@
 		/* We break with the "recommendation" and send our IP, in order that our
 		   peer doesn't have to ast_gethostbyname() us */
 
-		get_crypto_attrib(p, p->srtp, &a_crypto);
+		a_crypto = crypto_get_attrib(p->srtp, p->dtls_cfg.enabled,
+			ast_test_flag(&p->flags[2], SIP_PAGE3_SRTP_TAG_32));
 		ast_str_append(&m_audio, 0, "m=audio %d %s", ast_sockaddr_port(&dest),
-			       get_sdp_rtp_profile(p, a_crypto ? 1 : 0, p->rtp));
+			ast_sdp_get_rtp_profile(a_crypto ? 1 : 0, p->rtp,
+				ast_test_flag(&p->flags[2], SIP_PAGE3_USE_AVPF)));
 
 		/* Now, start adding audio codecs. These are added in this order:
 		   - First what was requested by the calling channel
@@ -25767,7 +25739,7 @@
 				transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ?  XMIT_UNRELIABLE : XMIT_CRITICAL)));
 			} else if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
 				/* If this is not a re-invite or something to ignore - it's critical */
-				if (p->srtp && !ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)) {
+				if (p->srtp && !ast_test_flag(p->srtp, AST_SRTP_CRYPTO_OFFER_OK)) {
 					ast_log(LOG_WARNING, "Target does not support required crypto\n");
 					transmit_response_reliable(p, "488 Not Acceptable Here (crypto)", req);
 				} else {
@@ -32791,22 +32763,7 @@
 	} while (0));
 }
 
-/* SRTP */
-static int setup_srtp(struct sip_srtp **srtp)
-{
-	if (!ast_rtp_engine_srtp_is_registered()) {
-		ast_debug(1, "No SRTP module loaded, can't setup SRTP session.\n");
-		return -1;
-	}
-
-	if (!(*srtp = sip_srtp_alloc())) { /* Allocate SRTP data structure */
-		return -1;
-	}
-
-	return 0;
-}
-
-static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct sip_srtp **srtp, const char *a)
+static int process_crypto(struct sip_pvt *p, struct ast_rtp_instance *rtp, struct ast_sdp_srtp **srtp, const char *a)
 {
 	struct ast_rtp_engine_dtls *dtls;
 
@@ -32819,26 +32776,27 @@
 	if (strncasecmp(a, "crypto:", 7)) {
 		return FALSE;
 	}
+	/* skip "crypto:" */
+	a += strlen("crypto:");
+
 	if (!*srtp) {
 		if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
 			ast_log(LOG_WARNING, "Ignoring unexpected crypto attribute in SDP answer\n");
 			return FALSE;
 		}
 
-		if (setup_srtp(srtp) < 0) {
+		if (!(*srtp = ast_sdp_srtp_alloc())) {
 			return FALSE;
 		}
 	}
 
-	if (!(*srtp)->crypto && !((*srtp)->crypto = sdp_crypto_setup())) {
+	if (!(*srtp)->crypto && !((*srtp)->crypto = ast_sdp_crypto_alloc())) {
 		return FALSE;
 	}
 
-	if (sdp_crypto_process((*srtp)->crypto, a, rtp, *srtp) < 0) {
+	if (ast_sdp_crypto_process(rtp, *srtp, a) < 0) {
 		return FALSE;
 	}
-
-	ast_set_flag(*srtp, SRTP_CRYPTO_OFFER_OK);
 
 	if ((dtls = ast_rtp_instance_get_dtls(rtp))) {
 		dtls->stop(rtp);

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=392565&r1=392564&r2=392565
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Sat Jun 22 09:03:22 2013
@@ -1165,9 +1165,9 @@
 	AST_LIST_HEAD_NOLOCK(request_queue, sip_request) request_queue; /*!< Requests that arrived but could not be processed immediately */
 	struct sip_invite_param *options;   /*!< Options for INVITE */
 	struct sip_st_dlg *stimer;          /*!< SIP Session-Timers */
-	struct sip_srtp *srtp;              /*!< Structure to hold Secure RTP session data for audio */
-	struct sip_srtp *vsrtp;             /*!< Structure to hold Secure RTP session data for video */
-	struct sip_srtp *tsrtp;             /*!< Structure to hold Secure RTP session data for text */
+	struct ast_sdp_srtp *srtp;              /*!< Structure to hold Secure RTP session data for audio */
+	struct ast_sdp_srtp *vsrtp;             /*!< Structure to hold Secure RTP session data for video */
+	struct ast_sdp_srtp *tsrtp;             /*!< Structure to hold Secure RTP session data for text */
 
 	int red;                            /*!< T.140 RTP Redundancy */
 	int hangupcause;                    /*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */

Modified: trunk/configs/res_sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/res_sip.conf.sample?view=diff&rev=392565&r1=392564&r2=392565
==============================================================================
--- trunk/configs/res_sip.conf.sample (original)
+++ trunk/configs/res_sip.conf.sample Sat Jun 22 09:03:22 2013
@@ -22,3 +22,5 @@
 ;rtp_ipv6=yes             ; Force IPv6 for RTP transport
 ;rtp_symmetric=yes        ; Enable symmetric RTP support
 ;use_ptime=yes            ; Whether to use the ptime value received from the endpoint or not
+;media_encryption=no      ; Options for media encryption are no, and sdes
+;use_avpf=no              ; Whether to force usage of AVPF transport for this endpoint

Modified: trunk/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/res_sip.h?view=diff&rev=392565&r1=392564&r2=392565
==============================================================================
--- trunk/include/asterisk/res_sip.h (original)
+++ trunk/include/asterisk/res_sip.h Sat Jun 22 09:03:22 2013
@@ -139,6 +139,47 @@
 	);
 	/*! Absolute time that this contact is no longer valid after */
 	struct timeval expiration_time;
+	/*! Frequency to send OPTIONS requests to contact. 0 is disabled. */
+	unsigned int qualify_frequency;
+	/*! If true authenticate the qualify if needed */
+	int authenticate_qualify;
+};
+
+#define CONTACT_STATUS "contact_status"
+
+/*!
+ * \brief Status type for a contact.
+ */
+enum ast_sip_contact_status_type {
+	UNAVAILABLE,
+	AVAILABLE
+};
+
+/*!
+ * \brief A contact's status.
+ *
+ * \detail Maintains a contact's current status and round trip time
+ *         if available.
+ */
+struct ast_sip_contact_status {
+	SORCERY_OBJECT(details);
+	/*! Current status for a contact (default - unavailable) */
+	enum ast_sip_contact_status_type status;
+	/*! The round trip start time set before sending a qualify request */
+	struct timeval rtt_start;
+	/*! The round trip time in microseconds */
+	int64_t rtt;
+};
+
+/*!
+ * \brief A transport to be used for messages to a contact
+ */
+struct ast_sip_contact_transport {
+	AST_DECLARE_STRING_FIELDS(
+		/*! Full URI of the contact */
+		AST_STRING_FIELD(uri);
+	);
+	pjsip_transport *transport;
 };
 
 /*!
@@ -157,6 +198,10 @@
 	unsigned int maximum_expiration;
 	/*! Default contact expiration if one is not provided in the contact */
 	unsigned int default_expiration;
+	/*! Frequency to send OPTIONS requests to AOR contacts. 0 is disabled. */
+	unsigned int qualify_frequency;
+	/*! If true authenticate the qualify if needed */
+	int authenticate_qualify;
 	/*! Maximum number of external contacts, 0 to disable */
 	unsigned int max_contacts;
 	/*! Whether to remove any existing contacts not related to an incoming REGISTER when it comes in */
@@ -243,6 +288,17 @@
 	 * Subsequent session refreshes will be sent no matter the session direction
 	 */
 	AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING,
+};
+
+enum ast_sip_session_media_encryption {
+	/*! Invalid media encryption configuration */
+	AST_SIP_MEDIA_TRANSPORT_INVALID = 0,
+	/*! Do not allow any encryption of session media */
+	AST_SIP_MEDIA_ENCRYPT_NONE,
+	/*! Offer SDES-encrypted session media */
+	AST_SIP_MEDIA_ENCRYPT_SDES,
+	/*! Offer encrypted session media with datagram TLS key exchange */
+	AST_SIP_MEDIA_ENCRYPT_DTLS,
 };
 
 /*!
@@ -306,14 +362,14 @@
 	unsigned int sess_expires;
 	/*! List of outbound registrations */
 	AST_LIST_HEAD_NOLOCK(, ast_sip_registration) registrations;
-	/*! Frequency to send OPTIONS requests to endpoint. 0 is disabled. */
-	unsigned int qualify_frequency;
 	/*! Method(s) by which the endpoint should be identified. */
 	enum ast_sip_endpoint_identifier_type ident_method;
 	/*! Boolean indicating if direct_media is permissible */
 	unsigned int direct_media;
 	/*! When using direct media, which method should be used */
 	enum ast_sip_session_refresh_method direct_media_method;

[... 3084 lines stripped ...]



More information about the svn-commits mailing list