summaryrefslogtreecommitdiff
path: root/channels/chan_gulp.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/chan_gulp.c')
-rw-r--r--channels/chan_gulp.c461
1 files changed, 432 insertions, 29 deletions
diff --git a/channels/chan_gulp.c b/channels/chan_gulp.c
index 9e939a0f4..6a80651cf 100644
--- a/channels/chan_gulp.c
+++ b/channels/chan_gulp.c
@@ -53,6 +53,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<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 int gulp_answer(struct ast_channel *ast);
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 @@ static struct ast_channel_tech gulp_tech = {
.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 @@ static struct ast_custom_function gulp_dial_contacts_function = {
.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 @@ static int gulp_set_rtp_peer(struct ast_channel *chan,
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 @@ static struct ast_channel *gulp_new(struct ast_sip_session *session, int state,
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 int gulp_answer(struct ast_channel *ast)
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 @@ static struct ast_frame *gulp_read(struct ast_channel *ast)
return &ast_null_frame;
}
- f = ast_rtp_instance_read(media->rtp, rtcp);
+ if (!(f = ast_rtp_instance_read(media->rtp, rtcp))) {
+ return f;
+ }
+
+ if (f->frametype != AST_FRAME_VOICE) {
+ return f;
+ }
- 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 (!(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 @@ static int transmit_info_with_vidupdate(void *data)
.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)) {
@@ -785,6 +924,40 @@ static int transmit_info_with_vidupdate(void *data)
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;
+}
+
/*! \brief Function called by core to ask the channel to indicate some sort of condition */
static int gulp_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
{
@@ -797,7 +970,12 @@ static int gulp_indicate(struct ast_channel *ast, int condition, const void *dat
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 @@ static int gulp_indicate(struct ast_channel *ast, int condition, const void *dat
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:
@@ -858,6 +1047,13 @@ static int gulp_indicate(struct ast_channel *ast, int condition, const void *dat
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;
break;
@@ -867,16 +1063,12 @@ static int gulp_indicate(struct ast_channel *ast, int condition, const void *dat
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;
}
}
@@ -884,6 +1076,130 @@ static int gulp_indicate(struct ast_channel *ast, int condition, const void *dat
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 */
static int gulp_digit_begin(struct ast_channel *chan, char digit)
{
@@ -1014,18 +1330,18 @@ static int gulp_digit_end(struct ast_channel *ast, char digit, unsigned int dura
static int call(void *data)
{
- pjsip_tx_data *packet;
struct ast_sip_session *session = data;
+ pjsip_tx_data *tdata;
+
+ int res = ast_sip_session_create_invite(session, &tdata);
- if (pjsip_inv_invite(session->inv_session, &packet) != PJ_SUCCESS) {
+ 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 @@ static int hangup(void *data)
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 @@ static struct ast_channel *gulp_request(const char *type, struct ast_format_cap
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 void gulp_session_end(struct ast_sip_session *session)
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 @@ static int gulp_incoming_request(struct ast_sip_session *session, struct pjsip_r
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);
@@ -1429,6 +1810,12 @@ static int gulp_incoming_request(struct ast_sip_session *session, struct pjsip_r
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 @@ static int load_module(void)
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 @@ static int load_module(void)
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 @@ static int reload(void)
/*! \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);