diff options
48 files changed, 15180 insertions, 1211 deletions
diff --git a/channels/Makefile b/channels/Makefile index ae5a0645a..10d487cfb 100644 --- a/channels/Makefile +++ b/channels/Makefile @@ -113,3 +113,4 @@ h323/Makefile.ast: h323/libchanh323.a: h323/Makefile.ast $(CMD_PREFIX) $(MAKE) -C h323 libchanh323.a + diff --git a/channels/chan_gulp.c b/channels/chan_gulp.c new file mode 100644 index 000000000..39a69e886 --- /dev/null +++ b/channels/chan_gulp.c @@ -0,0 +1,1445 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Joshua Colp <jcolp@digium.com> + * + * \brief Gulp SIP Channel Driver + * + * \ingroup channel_drivers + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sip</depend> + <depend>res_sip_session</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> +#include <pjlib.h> + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/lock.h" +#include "asterisk/channel.h" +#include "asterisk/module.h" +#include "asterisk/pbx.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/acl.h" +#include "asterisk/callerid.h" +#include "asterisk/file.h" +#include "asterisk/cli.h" +#include "asterisk/app.h" +#include "asterisk/musiconhold.h" +#include "asterisk/causes.h" +#include "asterisk/taskprocessor.h" + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_session.h" + +/*** DOCUMENTATION + <function name="GULP_DIAL_CONTACTS" language="en_US"> + <synopsis> + Return a dial string for dialing all contacts on an AOR. + </synopsis> + <syntax> + <parameter name="endpoint" required="true"> + <para>Name of the endpoint</para> + </parameter> + <parameter name="aor" required="false"> + <para>Name of an AOR to use, if not specified the configured AORs on the endpoint are used</para> + </parameter> + <parameter name="request_user" required="false"> + <para>Optional request user to use in the request URI</para> + </parameter> + </syntax> + <description> + <para>Returns a properly formatted dial string for dialing all contacts on an AOR.</para> + </description> + </function> + ***/ + +static const char desc[] = "Gulp SIP Channel"; +static const char channel_type[] = "Gulp"; + +/*! + * \brief Positions of various media + */ +enum sip_session_media_position { + /*! \brief First is audio */ + SIP_MEDIA_AUDIO = 0, + /*! \brief Second is video */ + SIP_MEDIA_VIDEO, + /*! \brief Last is the size for media details */ + SIP_MEDIA_SIZE, +}; + +struct gulp_pvt { + struct ast_sip_session *session; + struct ast_sip_session_media *media[SIP_MEDIA_SIZE]; +}; + +static void gulp_pvt_dtor(void *obj) +{ + struct gulp_pvt *pvt = obj; + int i; + ao2_cleanup(pvt->session); + pvt->session = NULL; + for (i = 0; i < SIP_MEDIA_SIZE; ++i) { + ao2_cleanup(pvt->media[i]); + pvt->media[i] = NULL; + } +} + +/* \brief Asterisk core interaction functions */ +static struct ast_channel *gulp_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause); +static int gulp_sendtext(struct ast_channel *ast, const char *text); +static int gulp_digit_begin(struct ast_channel *ast, char digit); +static int gulp_digit_end(struct ast_channel *ast, char digit, unsigned int duration); +static int gulp_call(struct ast_channel *ast, const char *dest, int timeout); +static int gulp_hangup(struct ast_channel *ast); +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_fixup(struct ast_channel *oldchan, struct ast_channel *newchan); + +/*! \brief PBX interface structure for channel registration */ +static struct ast_channel_tech gulp_tech = { + .type = channel_type, + .description = "Gulp SIP Channel Driver", + .requester = gulp_request, + .send_text = gulp_sendtext, + .send_digit_begin = gulp_digit_begin, + .send_digit_end = gulp_digit_end, + .bridge = ast_rtp_instance_bridge, + .call = gulp_call, + .hangup = gulp_hangup, + .answer = gulp_answer, + .read = gulp_read, + .write = gulp_write, + .write_video = gulp_write, + .exception = gulp_read, + .indicate = gulp_indicate, + .fixup = gulp_fixup, + .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER +}; + +/*! \brief SIP session interaction functions */ +static void gulp_session_begin(struct ast_sip_session *session); +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); +static void gulp_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata); + +/*! \brief SIP session supplement structure */ +static struct ast_sip_session_supplement gulp_supplement = { + .method = "INVITE", + .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL, + .session_begin = gulp_session_begin, + .session_end = gulp_session_end, + .incoming_request = gulp_incoming_request, + .incoming_response = gulp_incoming_response, +}; + +static int gulp_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata); + +static struct ast_sip_session_supplement gulp_ack_supplement = { + .method = "ACK", + .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL, + .incoming_request = gulp_incoming_ack, +}; + +/*! \brief Dialplan function for constructing a dial string for calling all contacts */ +static int gulp_dial_contacts(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) +{ + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(endpoint_name); + AST_APP_ARG(aor_name); + AST_APP_ARG(request_user); + ); + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + const char *aor_name; + char *rest; + RAII_VAR(struct ast_str *, dial, NULL, ast_free_ptr); + + AST_STANDARD_APP_ARGS(args, data); + + if (ast_strlen_zero(args.endpoint_name)) { + ast_log(LOG_WARNING, "An endpoint name must be specified when using the '%s' dialplan function\n", cmd); + return -1; + } else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", args.endpoint_name))) { + ast_log(LOG_WARNING, "Specified endpoint '%s' was not found\n", args.endpoint_name); + return -1; + } + + aor_name = S_OR(args.aor_name, endpoint->aors); + + if (ast_strlen_zero(aor_name)) { + ast_log(LOG_WARNING, "No AOR has been provided and no AORs are configured on endpoint '%s'\n", args.endpoint_name); + return -1; + } else if (!(dial = ast_str_create(len))) { + ast_log(LOG_WARNING, "Could not get enough buffer space for dialing contacts\n"); + return -1; + } else if (!(rest = ast_strdupa(aor_name))) { + ast_log(LOG_WARNING, "Could not duplicate provided AORs\n"); + return -1; + } + + while ((aor_name = strsep(&rest, ","))) { + RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup); + RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); + struct ao2_iterator it_contacts; + struct ast_sip_contact *contact; + + if (!aor) { + /* If the AOR provided is not found skip it, there may be more */ + continue; + } else if (!(contacts = ast_sip_location_retrieve_aor_contacts(aor))) { + /* No contacts are available, skip it as well */ + continue; + } else if (!ao2_container_count(contacts)) { + /* We were given a container but no contacts are in it... */ + continue; + } + + it_contacts = ao2_iterator_init(contacts, 0); + for (; (contact = ao2_iterator_next(&it_contacts)); ao2_ref(contact, -1)) { + ast_str_append(&dial, -1, "Gulp/"); + + if (!ast_strlen_zero(args.request_user)) { + ast_str_append(&dial, -1, "%s@", args.request_user); + } + ast_str_append(&dial, -1, "%s/%s&", args.endpoint_name, contact->uri); + } + ao2_iterator_destroy(&it_contacts); + } + + /* Trim the '&' at the end off */ + ast_str_truncate(dial, ast_str_strlen(dial) - 1); + + ast_copy_string(buf, ast_str_buffer(dial), len); + + return 0; +} + +static struct ast_custom_function gulp_dial_contacts_function = { + .name = "GULP_DIAL_CONTACTS", + .read = gulp_dial_contacts, +}; + +/*! \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) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(chan); + struct ast_sip_endpoint *endpoint; + + if (!pvt || !pvt->session || !pvt->media[SIP_MEDIA_AUDIO]->rtp) { + return AST_RTP_GLUE_RESULT_FORBID; + } + + endpoint = pvt->session->endpoint; + + *instance = pvt->media[SIP_MEDIA_AUDIO]->rtp; + ao2_ref(*instance, +1); + + ast_assert(endpoint != NULL); + if (endpoint->direct_media) { + return AST_RTP_GLUE_RESULT_REMOTE; + } + + return AST_RTP_GLUE_RESULT_LOCAL; +} + +/*! \brief Function called by RTP engine to get local video RTP peer */ +static enum ast_rtp_glue_result gulp_get_vrtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(chan); + + if (!pvt || !pvt->session || !pvt->media[SIP_MEDIA_VIDEO]->rtp) { + return AST_RTP_GLUE_RESULT_FORBID; + } + + *instance = pvt->media[SIP_MEDIA_VIDEO]->rtp; + ao2_ref(*instance, +1); + + return AST_RTP_GLUE_RESULT_LOCAL; +} + +/*! \brief Function called by RTP engine to get peer capabilities */ +static void gulp_get_codec(struct ast_channel *chan, struct ast_format_cap *result) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(chan); + ast_format_cap_copy(result, pvt->session->endpoint->codecs); +} + +static int send_direct_media_request(void *data) +{ + RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup); + return ast_sip_session_refresh(session, NULL, NULL, session->endpoint->direct_media_method, 1); +} + +static struct ast_datastore_info direct_media_mitigation_info = { }; + +static int direct_media_mitigate_glare(struct ast_sip_session *session) +{ + RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup); + + if (session->endpoint->direct_media_glare_mitigation == + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE) { + return 0; + } + + datastore = ast_sip_session_get_datastore(session, "direct_media_glare_mitigation"); + if (!datastore) { + return 0; + } + + /* Removing the datastore ensures we won't try to mitigate glare on subsequent reinvites */ + ast_sip_session_remove_datastore(session, "direct_media_glare_mitigation"); + + if ((session->endpoint->direct_media_glare_mitigation == + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING && + session->inv_session->role == PJSIP_ROLE_UAC) || + (session->endpoint->direct_media_glare_mitigation == + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING && + session->inv_session->role == PJSIP_ROLE_UAS)) { + return 1; + } + + return 0; +} + +static int check_for_rtp_changes(struct ast_channel *chan, struct ast_rtp_instance *rtp, + struct ast_sip_session_media *media, int rtcp_fd) +{ + int changed = 0; + + if (rtp) { + changed = ast_rtp_instance_get_and_cmp_remote_address(rtp, &media->direct_media_addr); + if (media->rtp) { + ast_channel_set_fd(chan, rtcp_fd, -1); + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_RTCP, 0); + } + } else if (!ast_sockaddr_isnull(&media->direct_media_addr)){ + ast_sockaddr_setnull(&media->direct_media_addr); + changed = 1; + if (media->rtp) { + ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_channel_set_fd(chan, rtcp_fd, ast_rtp_instance_fd(media->rtp, 1)); + } + } + + return changed; +} + +/*! \brief Function called by RTP engine to change where the remote party should send media */ +static int gulp_set_rtp_peer(struct ast_channel *chan, + struct ast_rtp_instance *rtp, + struct ast_rtp_instance *vrtp, + struct ast_rtp_instance *tpeer, + const struct ast_format_cap *cap, + int nat_active) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(chan); + struct ast_sip_session *session = pvt->session; + int changed = 0; + + /* Don't try to do any direct media shenanigans on early bridges */ + if ((rtp || vrtp || tpeer) && !ast_bridged_channel(chan)) { + return 0; + } + + if (nat_active && session->endpoint->disable_direct_media_on_nat) { + return 0; + } + + if (pvt->media[SIP_MEDIA_AUDIO]) { + changed |= check_for_rtp_changes(chan, rtp, pvt->media[SIP_MEDIA_AUDIO], 1); + } + if (pvt->media[SIP_MEDIA_VIDEO]) { + changed |= check_for_rtp_changes(chan, vrtp, pvt->media[SIP_MEDIA_VIDEO], 3); + } + + if (direct_media_mitigate_glare(session)) { + return 0; + } + + if (cap && !ast_format_cap_is_empty(cap) && !ast_format_cap_identical(session->direct_media_cap, cap)) { + ast_format_cap_copy(session->direct_media_cap, cap); + changed = 1; + } + + if (changed) { + ao2_ref(session, +1); + ast_sip_push_task(session->serializer, send_direct_media_request, session); + } + + return 0; +} + +/*! \brief Local glue for interacting with the RTP engine core */ +static struct ast_rtp_glue gulp_rtp_glue = { + .type = "Gulp", + .get_rtp_info = gulp_get_rtp_peer, + .get_vrtp_info = gulp_get_vrtp_peer, + .get_codec = gulp_get_codec, + .update_peer = gulp_set_rtp_peer, +}; + +/*! \brief Function called to create a new Gulp Asterisk channel */ +static struct ast_channel *gulp_new(struct ast_sip_session *session, int state, const char *exten, const char *title, const char *linkedid, const char *cid_name) +{ + struct ast_channel *chan; + struct ast_format fmt; + struct gulp_pvt *pvt; + + if (!(pvt = ao2_alloc(sizeof(*pvt), gulp_pvt_dtor))) { + return NULL; + } + + if (!(chan = ast_channel_alloc(1, state, S_OR(session->id.number.str, ""), S_OR(session->id.name.str, ""), "", "", "", linkedid, 0, "Gulp/%s-%.*s", ast_sorcery_object_get_id(session->endpoint), + (int)session->inv_session->dlg->call_id->id.slen, session->inv_session->dlg->call_id->id.ptr))) { + ao2_cleanup(pvt); + return NULL; + } + + ast_channel_tech_set(chan, &gulp_tech); + + ao2_ref(session, +1); + pvt->session = session; + /* If res_sip_session is ever updated to create/destroy ast_sip_session_media + * during a call such as if multiple same-type stream support is introduced, + * these will need to be recaptured as well */ + pvt->media[SIP_MEDIA_AUDIO] = ao2_find(session->media, "audio", OBJ_KEY); + pvt->media[SIP_MEDIA_VIDEO] = ao2_find(session->media, "video", OBJ_KEY); + ast_channel_tech_pvt_set(chan, pvt); + + if (ast_format_cap_is_empty(session->req_caps)) { + ast_format_cap_copy(ast_channel_nativeformats(chan), session->endpoint->codecs); + } else { + ast_format_cap_copy(ast_channel_nativeformats(chan), session->req_caps); + } + + ast_codec_choose(&session->endpoint->prefs, ast_channel_nativeformats(chan), 1, &fmt); + ast_format_copy(ast_channel_writeformat(chan), &fmt); + ast_format_copy(ast_channel_rawwriteformat(chan), &fmt); + ast_format_copy(ast_channel_readformat(chan), &fmt); + ast_format_copy(ast_channel_rawreadformat(chan), &fmt); + + if (state == AST_STATE_RING) { + ast_channel_rings_set(chan, 1); + } + + ast_channel_adsicpe_set(chan, AST_ADSI_UNAVAILABLE); + + ast_channel_context_set(chan, session->endpoint->context); + ast_channel_exten_set(chan, S_OR(exten, "s")); + ast_channel_priority_set(chan, 1); + + return chan; +} + +static int answer(void *data) +{ + pj_status_t status; + pjsip_tx_data *packet; + struct ast_sip_session *session = data; + + if ((status = pjsip_inv_answer(session->inv_session, 200, NULL, NULL, &packet)) == PJ_SUCCESS) { + ast_sip_session_send_response(session, packet); + } + + ao2_ref(session, -1); + return (status == PJ_SUCCESS) ? 0 : -1; +} + +/*! \brief Function called by core when we should answer a Gulp session */ +static int gulp_answer(struct ast_channel *ast) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + struct ast_sip_session *session = pvt->session; + + if (ast_channel_state(ast) == AST_STATE_UP) { + return 0; + } + + ast_setstate(ast, AST_STATE_UP); + + ao2_ref(session, +1); + if (ast_sip_push_task(session->serializer, answer, session)) { + ast_log(LOG_WARNING, "Unable to push answer task to the threadpool. Cannot answer call\n"); + ao2_cleanup(session); + return -1; + } + return 0; +} + +/*! \brief Function called by core to read any waiting frames */ +static struct ast_frame *gulp_read(struct ast_channel *ast) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + struct ast_frame *f; + struct ast_sip_session_media *media = NULL; + int rtcp = 0; + int fdno = ast_channel_fdno(ast); + + switch (fdno) { + case 0: + media = pvt->media[SIP_MEDIA_AUDIO]; + break; + case 1: + media = pvt->media[SIP_MEDIA_AUDIO]; + rtcp = 1; + break; + case 2: + media = pvt->media[SIP_MEDIA_VIDEO]; + break; + case 3: + media = pvt->media[SIP_MEDIA_VIDEO]; + rtcp = 1; + break; + } + + if (!media || !media->rtp) { + 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)); + } + } + + return f; +} + +/*! \brief Function called by core to write frames */ +static int gulp_write(struct ast_channel *ast, struct ast_frame *frame) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + int res = 0; + struct ast_sip_session_media *media; + + switch (frame->frametype) { + case AST_FRAME_VOICE: + media = pvt->media[SIP_MEDIA_AUDIO]; + + if (!media) { + return 0; + } + if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) { + char buf[256]; + + ast_log(LOG_WARNING, + "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n", + ast_getformatname(&frame->subclass.format), + ast_getformatname_multiple(buf, sizeof(buf), ast_channel_nativeformats(ast)), + ast_getformatname(ast_channel_readformat(ast)), + ast_getformatname(ast_channel_writeformat(ast))); + return 0; + } + if (media->rtp) { + res = ast_rtp_instance_write(media->rtp, frame); + } + break; + case AST_FRAME_VIDEO: + if ((media = pvt->media[SIP_MEDIA_VIDEO]) && media->rtp) { + res = ast_rtp_instance_write(media->rtp, frame); + } + break; + default: + ast_log(LOG_WARNING, "Can't send %d type frames with Gulp\n", frame->frametype); + break; + } + + return res; +} + +struct fixup_data { + struct ast_sip_session *session; + struct ast_channel *chan; +}; + +static int fixup(void *data) +{ + struct fixup_data *fix_data = data; + fix_data->session->channel = fix_data->chan; + return 0; +} + +/*! \brief Function called by core to change the underlying owner channel */ +static int gulp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(newchan); + struct ast_sip_session *session = pvt->session; + struct fixup_data fix_data; + fix_data.session = session; + fix_data.chan = newchan; + + if (session->channel != oldchan) { + return -1; + } + + if (ast_sip_push_task_synchronous(session->serializer, fixup, &fix_data)) { + ast_log(LOG_WARNING, "Unable to perform channel fixup\n"); + return -1; + } + + return 0; +} + +struct indicate_data { + struct ast_sip_session *session; + int condition; + int response_code; + void *frame_data; + size_t datalen; +}; + +static void indicate_data_destroy(void *obj) +{ + struct indicate_data *ind_data = obj; + ast_free(ind_data->frame_data); + ao2_ref(ind_data->session, -1); +} + +static struct indicate_data *indicate_data_alloc(struct ast_sip_session *session, + int condition, int response_code, const void *frame_data, size_t datalen) +{ + struct indicate_data *ind_data = ao2_alloc(sizeof(*ind_data), indicate_data_destroy); + if (!ind_data) { + return NULL; + } + ind_data->frame_data = ast_malloc(datalen); + if (!ind_data->frame_data) { + ao2_ref(ind_data, -1); + return NULL; + } + memcpy(ind_data->frame_data, frame_data, datalen); + ind_data->datalen = datalen; + ind_data->condition = condition; + ind_data->response_code = response_code; + ao2_ref(session, +1); + ind_data->session = session; + return ind_data; +} + +static int indicate(void *data) +{ + struct indicate_data *ind_data = data; + struct ast_sip_session *session = ind_data->session; + int response_code = ind_data->response_code; + 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); + } + + ao2_ref(ind_data, -1); + return 0; +} + +/*! \brief Send SIP INFO with video update request */ +static int transmit_info_with_vidupdate(void *data) +{ + const char * xml = + "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\r\n" + " <media_control>\r\n" + " <vc_primitive>\r\n" + " <to_encoder>\r\n" + " <picture_fast_update/>\r\n" + " </to_encoder>\r\n" + " </vc_primitive>\r\n" + " </media_control>\r\n"; + + const struct ast_sip_body body = { + .type = "application", + .subtype = "media_control+xml", + .body_text = xml + }; + + struct ast_sip_session *session = data; + struct pjsip_tx_data *tdata; + + if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, &tdata)) { + ast_log(LOG_ERROR, "Could not create text video update INFO request\n"); + return -1; + } + if (ast_sip_add_body(tdata, &body)) { + ast_log(LOG_ERROR, "Could not add body to text video update INFO request\n"); + return -1; + } + ast_sip_session_send_request(session, tdata); + + 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) +{ + int res = 0; + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + struct ast_sip_session *session = pvt->session; + struct ast_sip_session_media *media; + int response_code = 0; + + switch (condition) { + case AST_CONTROL_RINGING: + if (ast_channel_state(ast) == AST_STATE_RING) { + response_code = 180; + } else { + res = -1; + } + break; + case AST_CONTROL_BUSY: + if (ast_channel_state(ast) != AST_STATE_UP) { + response_code = 486; + } else { + res = -1; + } + break; + case AST_CONTROL_CONGESTION: + if (ast_channel_state(ast) != AST_STATE_UP) { + response_code = 503; + } else { + res = -1; + } + break; + case AST_CONTROL_INCOMPLETE: + if (ast_channel_state(ast) != AST_STATE_UP) { + response_code = 484; + } else { + res = -1; + } + break; + case AST_CONTROL_PROCEEDING: + if (ast_channel_state(ast) != AST_STATE_UP) { + response_code = 100; + } else { + res = -1; + } + break; + case AST_CONTROL_PROGRESS: + if (ast_channel_state(ast) != AST_STATE_UP) { + response_code = 183; + } else { + res = -1; + } + break; + 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 + res = -1; + break; + case AST_CONTROL_UPDATE_RTP_PEER: + case AST_CONTROL_PVT_CAUSE_CODE: + break; + case AST_CONTROL_HOLD: + ast_moh_start(ast, data, NULL); + break; + case AST_CONTROL_UNHOLD: + ast_moh_stop(ast); + break; + case AST_CONTROL_SRCUPDATE: + break; + case AST_CONTROL_SRCCHANGE: + break; + case -1: + res = -1; + break; + default: + ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition); + res = -1; + break; + } + + if (!res && 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 queue task properly\n", + response_code, ast_sorcery_object_get_id(session->endpoint)); + ao2_cleanup(ind_data); + } + } else { + res = -1; + } + } + + return res; +} + +/*! \brief Function called by core to start a DTMF digit */ +static int gulp_digit_begin(struct ast_channel *chan, char digit) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(chan); + struct ast_sip_session *session = pvt->session; + int res = 0; + struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO]; + + switch (session->endpoint->dtmf) { + case AST_SIP_DTMF_RFC_4733: + if (!media || !media->rtp) { + return -1; + } + + ast_rtp_instance_dtmf_begin(media->rtp, digit); + case AST_SIP_DTMF_NONE: + break; + case AST_SIP_DTMF_INBAND: + res = -1; + break; + default: + break; + } + + return res; +} + +struct info_dtmf_data { + struct ast_sip_session *session; + char digit; + unsigned int duration; +}; + +static void info_dtmf_data_destroy(void *obj) +{ + struct info_dtmf_data *dtmf_data = obj; + ao2_ref(dtmf_data->session, -1); +} + +static struct info_dtmf_data *info_dtmf_data_alloc(struct ast_sip_session *session, char digit, unsigned int duration) +{ + struct info_dtmf_data *dtmf_data = ao2_alloc(sizeof(*dtmf_data), info_dtmf_data_destroy); + if (!dtmf_data) { + return NULL; + } + ao2_ref(session, +1); + dtmf_data->session = session; + dtmf_data->digit = digit; + dtmf_data->duration = duration; + return dtmf_data; +} + +static int transmit_info_dtmf(void *data) +{ + RAII_VAR(struct info_dtmf_data *, dtmf_data, data, ao2_cleanup); + + struct ast_sip_session *session = dtmf_data->session; + struct pjsip_tx_data *tdata; + + RAII_VAR(struct ast_str *, body_text, NULL, ast_free_ptr); + + struct ast_sip_body body = { + .type = "application", + .subtype = "dtmf-relay", + }; + + if (!(body_text = ast_str_create(32))) { + ast_log(LOG_ERROR, "Could not allocate buffer for INFO DTMF.\n"); + return -1; + } + ast_str_set(&body_text, 0, "Signal=%c\r\nDuration=%u\r\n", dtmf_data->digit, dtmf_data->duration); + + body.body_text = ast_str_buffer(body_text); + + if (ast_sip_create_request("INFO", session->inv_session->dlg, session->endpoint, NULL, &tdata)) { + ast_log(LOG_ERROR, "Could not create DTMF INFO request\n"); + return -1; + } + if (ast_sip_add_body(tdata, &body)) { + ast_log(LOG_ERROR, "Could not add body to DTMF INFO request\n"); + pjsip_tx_data_dec_ref(tdata); + return -1; + } + ast_sip_session_send_request(session, tdata); + + return 0; +} + +/*! \brief Function called by core to stop a DTMF digit */ +static int gulp_digit_end(struct ast_channel *ast, char digit, unsigned int duration) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + struct ast_sip_session *session = pvt->session; + int res = 0; + struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO]; + + switch (session->endpoint->dtmf) { + case AST_SIP_DTMF_INFO: + { + struct info_dtmf_data *dtmf_data = info_dtmf_data_alloc(session, digit, duration); + + if (!dtmf_data) { + return -1; + } + + if (ast_sip_push_task(session->serializer, transmit_info_dtmf, dtmf_data)) { + ast_log(LOG_WARNING, "Error sending DTMF via INFO.\n"); + ao2_cleanup(dtmf_data); + return -1; + } + break; + } + case AST_SIP_DTMF_RFC_4733: + if (!media || !media->rtp) { + return -1; + } + + ast_rtp_instance_dtmf_end_with_duration(media->rtp, digit, duration); + case AST_SIP_DTMF_NONE: + break; + case AST_SIP_DTMF_INBAND: + res = -1; + break; + } + + return res; +} + +static int call(void *data) +{ + struct ast_sip_session *session = data; + pjsip_tx_data *packet; + + if (pjsip_inv_invite(session->inv_session, &packet) != PJ_SUCCESS) { + ast_queue_hangup(session->channel); + } else { + ast_sip_session_send_request(session, packet); + } + + ao2_ref(session, -1); + return 0; +} + +/*! \brief Function called by core to actually start calling a remote party */ +static int gulp_call(struct ast_channel *ast, const char *dest, int timeout) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + struct ast_sip_session *session = pvt->session; + + ao2_ref(session, +1); + if (ast_sip_push_task(session->serializer, call, session)) { + ast_log(LOG_WARNING, "Error attempting to place outbound call to call '%s'\n", dest); + ao2_cleanup(session); + return -1; + } + return 0; +} + +/*! \brief Internal function which translates from Asterisk cause codes to SIP response codes */ +static int hangup_cause2sip(int cause) +{ + switch (cause) { + case AST_CAUSE_UNALLOCATED: /* 1 */ + case AST_CAUSE_NO_ROUTE_DESTINATION: /* 3 IAX2: Can't find extension in context */ + case AST_CAUSE_NO_ROUTE_TRANSIT_NET: /* 2 */ + return 404; + case AST_CAUSE_CONGESTION: /* 34 */ + case AST_CAUSE_SWITCH_CONGESTION: /* 42 */ + return 503; + case AST_CAUSE_NO_USER_RESPONSE: /* 18 */ + return 408; + case AST_CAUSE_NO_ANSWER: /* 19 */ + case AST_CAUSE_UNREGISTERED: /* 20 */ + return 480; + case AST_CAUSE_CALL_REJECTED: /* 21 */ + return 403; + case AST_CAUSE_NUMBER_CHANGED: /* 22 */ + return 410; + case AST_CAUSE_NORMAL_UNSPECIFIED: /* 31 */ + return 480; + case AST_CAUSE_INVALID_NUMBER_FORMAT: + return 484; + case AST_CAUSE_USER_BUSY: + return 486; + case AST_CAUSE_FAILURE: + return 500; + case AST_CAUSE_FACILITY_REJECTED: /* 29 */ + return 501; + case AST_CAUSE_CHAN_NOT_IMPLEMENTED: + return 503; + case AST_CAUSE_DESTINATION_OUT_OF_ORDER: + return 502; + case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: /* Can't find codec to connect to host */ + return 488; + case AST_CAUSE_INTERWORKING: /* Unspecified Interworking issues */ + return 500; + case AST_CAUSE_NOTDEFINED: + default: + ast_debug(1, "AST hangup cause %d (no match found in PJSIP)\n", cause); + return 0; + } + + /* Never reached */ + return 0; +} + +struct hangup_data { + int cause; + struct ast_channel *chan; +}; + +static void hangup_data_destroy(void *obj) +{ + struct hangup_data *h_data = obj; + h_data->chan = ast_channel_unref(h_data->chan); +} + +static struct hangup_data *hangup_data_alloc(int cause, struct ast_channel *chan) +{ + struct hangup_data *h_data = ao2_alloc(sizeof(*h_data), hangup_data_destroy); + if (!h_data) { + return NULL; + } + h_data->cause = cause; + h_data->chan = ast_channel_ref(chan); + return h_data; +} + +static int hangup(void *data) +{ + pj_status_t status; + pjsip_tx_data *packet = NULL; + struct hangup_data *h_data = data; + struct ast_channel *ast = h_data->chan; + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + 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 (packet->msg->type == PJSIP_RESPONSE_MSG) { + ast_sip_session_send_response(session, packet); + } else { + ast_sip_session_send_request(session, packet); + } + } + + session->channel = NULL; + ast_channel_tech_pvt_set(ast, NULL); + + ao2_cleanup(pvt); + ao2_cleanup(h_data); + return 0; +} + +/*! \brief Function called by core to hang up a Gulp session */ +static int gulp_hangup(struct ast_channel *ast) +{ + struct gulp_pvt *pvt = ast_channel_tech_pvt(ast); + struct ast_sip_session *session = pvt->session; + int cause = hangup_cause2sip(ast_channel_hangupcause(session->channel)); + struct hangup_data *h_data = hangup_data_alloc(cause, ast); + if (!h_data) { + goto failure; + } + + if (ast_sip_push_task(session->serializer, hangup, h_data)) { + ast_log(LOG_WARNING, "Unable to push hangup task to the threadpool. Expect bad things\n"); + goto failure; + } + return 0; + +failure: + /* Go ahead and do our cleanup of the session and channel even if we're not going + * to be able to send our SIP request/response + */ + ao2_cleanup(h_data); + session->channel = NULL; + ast_channel_tech_pvt_set(ast, NULL); + + ao2_cleanup(pvt); + return -1; +} + +struct request_data { + struct ast_sip_session *session; + struct ast_format_cap *caps; + const char *dest; + int cause; +}; + +static int request(void *obj) +{ + struct request_data *req_data = obj; + char *tmp = ast_strdupa(req_data->dest), *endpoint_name = NULL, *request_user = NULL; + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + struct ast_sip_session *session = NULL; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(endpoint); + AST_APP_ARG(aor); + ); + + if (ast_strlen_zero(tmp)) { + ast_log(LOG_ERROR, "Unable to create Gulp channel with empty destination\n"); + req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE; + return -1; + } + + AST_NONSTANDARD_APP_ARGS(args, tmp, '/'); + + /* If a request user has been specified extract it from the endpoint name portion */ + if ((endpoint_name = strchr(args.endpoint, '@'))) { + request_user = args.endpoint; + *endpoint_name++ = '\0'; + } else { + endpoint_name = args.endpoint; + } + + if (ast_strlen_zero(endpoint_name)) { + ast_log(LOG_ERROR, "Unable to create Gulp channel with empty endpoint name\n"); + req_data->cause = AST_CAUSE_CHANNEL_UNACCEPTABLE; + } else if (!(endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name))) { + ast_log(LOG_ERROR, "Unable to create Gulp channel - endpoint '%s' was not found\n", endpoint_name); + req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION; + return -1; + } + + if (!(session = ast_sip_session_create_outgoing(endpoint, args.aor, request_user, req_data->caps))) { + req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION; + return -1; + } + + req_data->session = session; + + return 0; +} + +/*! \brief Function called by core to create a new outgoing Gulp session */ +static struct ast_channel *gulp_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause) +{ + struct request_data req_data; + struct ast_sip_session *session; + + req_data.caps = cap; + req_data.dest = data; + + if (ast_sip_push_task_synchronous(NULL, request, &req_data)) { + *cause = req_data.cause; + return NULL; + } + + session = req_data.session; + + if (!(session->channel = gulp_new(session, AST_STATE_DOWN, NULL, NULL, requestor ? ast_channel_linkedid(requestor) : NULL, NULL))) { + /* Session needs to be terminated prematurely */ + return NULL; + } + + return session->channel; +} + +/*! \brief Function called by core to send text on Gulp session */ +static int gulp_sendtext(struct ast_channel *ast, const char *text) +{ + return 0; +} + +/*! \brief Convert SIP hangup causes to Asterisk hangup causes */ +static int hangup_sip2cause(int cause) +{ + /* Possible values taken from causes.h */ + + switch(cause) { + case 401: /* Unauthorized */ + return AST_CAUSE_CALL_REJECTED; + case 403: /* Not found */ + return AST_CAUSE_CALL_REJECTED; + case 404: /* Not found */ + return AST_CAUSE_UNALLOCATED; + case 405: /* Method not allowed */ + return AST_CAUSE_INTERWORKING; + case 407: /* Proxy authentication required */ + return AST_CAUSE_CALL_REJECTED; + case 408: /* No reaction */ + return AST_CAUSE_NO_USER_RESPONSE; + case 409: /* Conflict */ + return AST_CAUSE_NORMAL_TEMPORARY_FAILURE; + case 410: /* Gone */ + return AST_CAUSE_NUMBER_CHANGED; + case 411: /* Length required */ + return AST_CAUSE_INTERWORKING; + case 413: /* Request entity too large */ + return AST_CAUSE_INTERWORKING; + case 414: /* Request URI too large */ + return AST_CAUSE_INTERWORKING; + case 415: /* Unsupported media type */ + return AST_CAUSE_INTERWORKING; + case 420: /* Bad extension */ + return AST_CAUSE_NO_ROUTE_DESTINATION; + case 480: /* No answer */ + return AST_CAUSE_NO_ANSWER; + case 481: /* No answer */ + return AST_CAUSE_INTERWORKING; + case 482: /* Loop detected */ + return AST_CAUSE_INTERWORKING; + case 483: /* Too many hops */ + return AST_CAUSE_NO_ANSWER; + case 484: /* Address incomplete */ + return AST_CAUSE_INVALID_NUMBER_FORMAT; + case 485: /* Ambiguous */ + return AST_CAUSE_UNALLOCATED; + case 486: /* Busy everywhere */ + return AST_CAUSE_BUSY; + case 487: /* Request terminated */ + return AST_CAUSE_INTERWORKING; + case 488: /* No codecs approved */ + return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; + case 491: /* Request pending */ + return AST_CAUSE_INTERWORKING; + case 493: /* Undecipherable */ + return AST_CAUSE_INTERWORKING; + case 500: /* Server internal failure */ + return AST_CAUSE_FAILURE; + case 501: /* Call rejected */ + return AST_CAUSE_FACILITY_REJECTED; + case 502: + return AST_CAUSE_DESTINATION_OUT_OF_ORDER; + case 503: /* Service unavailable */ + return AST_CAUSE_CONGESTION; + case 504: /* Gateway timeout */ + return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE; + case 505: /* SIP version not supported */ + return AST_CAUSE_INTERWORKING; + case 600: /* Busy everywhere */ + return AST_CAUSE_USER_BUSY; + case 603: /* Decline */ + return AST_CAUSE_CALL_REJECTED; + case 604: /* Does not exist anywhere */ + return AST_CAUSE_UNALLOCATED; + case 606: /* Not acceptable */ + return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL; + default: + if (cause < 500 && cause >= 400) { + /* 4xx class error that is unknown - someting wrong with our request */ + return AST_CAUSE_INTERWORKING; + } else if (cause < 600 && cause >= 500) { + /* 5xx class error - problem in the remote end */ + return AST_CAUSE_CONGESTION; + } else if (cause < 700 && cause >= 600) { + /* 6xx - global errors in the 4xx class */ + return AST_CAUSE_INTERWORKING; + } + return AST_CAUSE_NORMAL; + } + /* Never reached */ + return 0; +} + +static void gulp_session_begin(struct ast_sip_session *session) +{ + RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup); + + if (session->endpoint->direct_media_glare_mitigation == + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE) { + return; + } + + datastore = ast_sip_session_alloc_datastore(&direct_media_mitigation_info, + "direct_media_glare_mitigation"); + + if (!datastore) { + return; + } + + ast_sip_session_add_datastore(session, datastore); +} + +/*! \brief Function called when the session ends */ +static void gulp_session_end(struct ast_sip_session *session) +{ + if (!session->channel) { + return; + } + + if (!ast_channel_hangupcause(session->channel) && session->inv_session) { + int cause = hangup_sip2cause(session->inv_session->cause); + + ast_queue_hangup_with_cause(session->channel, cause); + } else { + ast_queue_hangup(session->channel); + } +} + +/*! \brief Function called when a request is received on the 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; + } + + if (!(session->channel = gulp_new(session, AST_STATE_DOWN, session->exten, NULL, NULL, NULL))) { + if (pjsip_inv_end_session(session->inv_session, 503, NULL, &packet) == PJ_SUCCESS) { + ast_sip_session_send_response(session, packet); + } + + ast_log(LOG_ERROR, "Failed to allocate new GULP channel on incoming SIP INVITE\n"); + return -1; + } + + ast_setstate(session->channel, AST_STATE_RING); + res = ast_pbx_start(session->channel); + + switch (res) { + case AST_PBX_FAILED: + ast_log(LOG_WARNING, "Failed to start PBX ;(\n"); + ast_channel_hangupcause_set(session->channel, AST_CAUSE_SWITCH_CONGESTION); + ast_hangup(session->channel); + break; + case AST_PBX_CALL_LIMIT: + ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n"); + ast_channel_hangupcause_set(session->channel, AST_CAUSE_SWITCH_CONGESTION); + ast_hangup(session->channel); + break; + case AST_PBX_SUCCESS: + default: + break; + } + + ast_debug(3, "Started PBX on new GULP channel %s\n", ast_channel_name(session->channel)); + + return (res == AST_PBX_SUCCESS) ? 0 : -1; +} + +/*! \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) +{ + struct pjsip_status_line status = rdata->msg_info.msg->line.status; + + if (!session->channel) { + return; + } + + switch (status.code) { + case 180: + ast_queue_control(session->channel, AST_CONTROL_RINGING); + if (ast_channel_state(session->channel) != AST_STATE_UP) { + ast_setstate(session->channel, AST_STATE_RINGING); + } + break; + case 183: + ast_queue_control(session->channel, AST_CONTROL_PROGRESS); + break; + case 200: + ast_queue_control(session->channel, AST_CONTROL_ANSWER); + break; + default: + break; + } +} + +static int gulp_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) { + if (session->endpoint->direct_media) { + ast_queue_control(session->channel, AST_CONTROL_SRCCHANGE); + } + } + return 0; +} + +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ +static int load_module(void) +{ + if (!(gulp_tech.capabilities = ast_format_cap_alloc())) { + return AST_MODULE_LOAD_DECLINE; + } + + ast_format_cap_add_all_by_type(gulp_tech.capabilities, AST_FORMAT_TYPE_AUDIO); + + ast_rtp_glue_register(&gulp_rtp_glue); + + if (ast_channel_register(&gulp_tech)) { + ast_log(LOG_ERROR, "Unable to register channel class %s\n", channel_type); + goto end; + } + + if (ast_custom_function_register(&gulp_dial_contacts_function)) { + ast_log(LOG_ERROR, "Unable to register GULP_DIAL_CONTACTS dialplan function\n"); + goto end; + } + + 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(&gulp_ack_supplement)) { + ast_log(LOG_ERROR, "Unable to register Gulp ACK supplement\n"); + ast_sip_session_unregister_supplement(&gulp_supplement); + goto end; + } + + return 0; + +end: + ast_custom_function_unregister(&gulp_dial_contacts_function); + ast_channel_unregister(&gulp_tech); + ast_rtp_glue_unregister(&gulp_rtp_glue); + + return AST_MODULE_LOAD_FAILURE; +} + +/*! \brief Reload module */ +static int reload(void) +{ + return -1; +} + +/*! \brief Unload the Gulp channel from Asterisk */ +static int unload_module(void) +{ + ast_sip_session_unregister_supplement(&gulp_supplement); + ast_custom_function_unregister(&gulp_dial_contacts_function); + ast_channel_unregister(&gulp_tech); + ast_rtp_glue_unregister(&gulp_rtp_glue); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Gulp SIP Channel Driver", + .load = load_module, + .unload = unload_module, + .reload = reload, + .load_pri = AST_MODPRI_CHANNEL_DRIVER, + ); diff --git a/configs/res_sip.conf.sample b/configs/res_sip.conf.sample new file mode 100644 index 000000000..7fd93a72c --- /dev/null +++ b/configs/res_sip.conf.sample @@ -0,0 +1,24 @@ +; This is an in-flux configuration file for the res_sip module, it will change as things progress + +;;; Transports +[local] +type=transport +protocol=udp ; Supported protocols are udp, tcp, and tls +bind=0.0.0.0 ; This supports both IPv4 and IPv6, port is optional + +;;; Endpoints +[endpoint] +type=endpoint +context=default +disallow=all +allow=ulaw +dtmfmode=rfc4733 ; Supported DTMF modes are rfc4733, inband, info, and none +;transport=local ; Name of a specific transport to use when placing calls +;100rel=yes ; Enable or disable 100rel support - valid options are: yes, no, required +;timers=yes ; Enable or disable session timers support - valid options are: yes, no, required, always +;timers_min_se=90 ; Minimum session timers expiration period, in seconds +;timers_sess_expires=1800 ; Session timers expiration period, in seconds +;mohsuggest=example ; What musiconhold class to suggest that the peer channel use when this endpoint places them on hold +;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 @@ -1,12 +1,14 @@ #! /bin/sh -# From configure.ac Revision: 382812 . +# From configure.ac Revision: 383581 . # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for asterisk trunk. +# Generated by GNU Autoconf 2.65 for asterisk trunk. # # Report bugs to <https://issues.asterisk.org>. # # -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. # # # This configure script is free software; the Free Software Foundation @@ -92,7 +94,6 @@ fi IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. -as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -137,31 +138,6 @@ export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh @@ -195,8 +171,7 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" +test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && @@ -241,25 +216,14 @@ IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : @@ -358,18 +322,10 @@ $as_echo X"$as_dir" | test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" } # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take @@ -406,19 +362,19 @@ else fi # as_fn_arith -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. +# script with status $?, using 1 if that was 0. as_fn_error () { - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 fi - $as_echo "$as_me: error: $2" >&2 + $as_echo "$as_me: error: $1" >&2 as_fn_exit $as_status } # as_fn_error @@ -491,10 +447,6 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). @@ -529,16 +481,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' + as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -550,8 +502,28 @@ else as_mkdir_p=false fi -as_test_x='test -x' -as_executable_p=as_fn_executable_p +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -564,7 +536,7 @@ test -n "$DJDIR" || exec 7<&0 </dev/null exec 6>&1 # Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` @@ -679,8 +651,6 @@ ILBC_CFLAGS ILBC_INTERNAL GSM_INTERNAL PBX_DAHDI_HALF_FULL -PKG_CONFIG_LIBDIR -PKG_CONFIG_PATH PKG_CONFIG PBX_DLADDR PBX_IP_MTU_DISCOVER @@ -1326,8 +1296,6 @@ CXXFLAGS CCC CXXCPP PKG_CONFIG -PKG_CONFIG_PATH -PKG_CONFIG_LIBDIR ILBC_CFLAGS ILBC_LIBS LIBEDIT_CFLAGS @@ -1400,9 +1368,8 @@ do fi case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. @@ -1447,7 +1414,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1473,7 +1440,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" + as_fn_error "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1677,7 +1644,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1693,7 +1660,7 @@ do ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" + as_fn_error "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in @@ -1723,8 +1690,8 @@ do | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" + -*) as_fn_error "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information." ;; *=*) @@ -1732,7 +1699,7 @@ Try \`$0 --help' for more information" # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + as_fn_error "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; @@ -1742,7 +1709,7 @@ Try \`$0 --help' for more information" $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; esac @@ -1750,13 +1717,13 @@ done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" + as_fn_error "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi @@ -1779,7 +1746,7 @@ do [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" + as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1793,6 +1760,8 @@ target=$target_alias if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi @@ -1807,9 +1776,9 @@ test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" + as_fn_error "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" + as_fn_error "pwd does not report name of working directory" # Find the source files, if location was not specified. @@ -1848,11 +1817,11 @@ else fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" + as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1892,7 +1861,7 @@ Configuration: --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages + -q, --quiet, --silent do not print \`checking...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files @@ -2056,10 +2025,6 @@ Some influential environment variables: CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor PKG_CONFIG path to pkg-config utility - PKG_CONFIG_PATH - directories to add to pkg-config's search path - PKG_CONFIG_LIBDIR - path overriding pkg-config's built-in search path ILBC_CFLAGS C compiler flags for ILBC, overriding pkg-config ILBC_LIBS linker flags for ILBC, overriding pkg-config LIBEDIT_CFLAGS @@ -2143,9 +2108,9 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF asterisk configure trunk -generated by GNU Autoconf 2.69 +generated by GNU Autoconf 2.65 -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. @@ -2191,7 +2156,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_compile @@ -2217,7 +2182,7 @@ $as_echo "$ac_try_echo"; } >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { + test $ac_status = 0; } >/dev/null && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : @@ -2228,7 +2193,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_cpp @@ -2241,10 +2206,10 @@ fi ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 @@ -2280,7 +2245,7 @@ if ac_fn_c_try_cpp "$LINENO"; then : else ac_header_preproc=no fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } @@ -2303,15 +2268,17 @@ $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ------------------------------------------ ## +( cat <<\_ASBOX +## ------------------------------------------ ## ## Report this to https://issues.asterisk.org ## -## ------------------------------------------ ##" +## ------------------------------------------ ## +_ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" @@ -2320,7 +2287,7 @@ eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_header_mongrel @@ -2361,7 +2328,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_run @@ -2375,7 +2342,7 @@ ac_fn_c_check_header_compile () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2393,7 +2360,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_header_compile @@ -2430,7 +2397,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile @@ -2456,7 +2423,7 @@ $as_echo "$ac_try_echo"; } >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { + test $ac_status = 0; } >/dev/null && { test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || test ! -s conftest.err }; then : @@ -2467,7 +2434,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_cxx_try_cpp @@ -2499,7 +2466,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - test -x conftest$ac_exeext + $as_test_x conftest$ac_exeext }; then : ac_retval=0 else @@ -2513,7 +2480,7 @@ fi # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_try_link @@ -2526,7 +2493,7 @@ ac_fn_c_check_func () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2581,7 +2548,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_func @@ -2594,7 +2561,7 @@ ac_fn_c_check_type () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=no" @@ -2635,7 +2602,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_type @@ -2648,7 +2615,7 @@ ac_fn_c_check_member () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } -if eval \${$4+:} false; then : +if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -2692,7 +2659,7 @@ fi eval ac_res=\$$4 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_c_check_member @@ -2713,8 +2680,7 @@ int main () { static int test_array [1 - 2 * !(($2) >= 0)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2730,8 +2696,7 @@ int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2757,8 +2722,7 @@ int main () { static int test_array [1 - 2 * !(($2) < 0)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2774,8 +2738,7 @@ int main () { static int test_array [1 - 2 * !(($2) >= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2809,8 +2772,7 @@ int main () { static int test_array [1 - 2 * !(($2) <= $ac_mid)]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -2874,7 +2836,7 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ rm -f conftest.val fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_c_compute_int @@ -2887,10 +2849,10 @@ rm -f conftest.val ac_fn_cxx_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : + if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 @@ -2926,7 +2888,7 @@ if ac_fn_cxx_try_cpp "$LINENO"; then : else ac_header_preproc=no fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } @@ -2949,15 +2911,17 @@ $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## ------------------------------------------ ## +( cat <<\_ASBOX +## ------------------------------------------ ## ## Report this to https://issues.asterisk.org ## -## ------------------------------------------ ##" +## ------------------------------------------ ## +_ASBOX ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" @@ -2966,7 +2930,7 @@ eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_cxx_check_header_mongrel @@ -2997,7 +2961,7 @@ $as_echo "$ac_try_echo"; } >&5 test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || - test -x conftest$ac_exeext + $as_test_x conftest$ac_exeext }; then : ac_retval=0 else @@ -3011,7 +2975,7 @@ fi # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} as_fn_set_status $ac_retval } # ac_fn_cxx_try_link @@ -3025,7 +2989,7 @@ ac_fn_cxx_check_header_compile () as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3043,7 +3007,7 @@ fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} } # ac_fn_cxx_check_header_compile cat >config.log <<_ACEOF @@ -3051,7 +3015,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by asterisk $as_me trunk, which was -generated by GNU Autoconf 2.69. Invocation command line was +generated by GNU Autoconf 2.65. Invocation command line was $ $0 $@ @@ -3161,9 +3125,11 @@ trap 'exit_status=$? { echo - $as_echo "## ---------------- ## + cat <<\_ASBOX +## ---------------- ## ## Cache variables. ## -## ---------------- ##" +## ---------------- ## +_ASBOX echo # The following way of writing the cache mishandles newlines in values, ( @@ -3197,9 +3163,11 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; ) echo - $as_echo "## ----------------- ## + cat <<\_ASBOX +## ----------------- ## ## Output variables. ## -## ----------------- ##" +## ----------------- ## +_ASBOX echo for ac_var in $ac_subst_vars do @@ -3212,9 +3180,11 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; echo if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## + cat <<\_ASBOX +## ------------------- ## ## File substitutions. ## -## ------------------- ##" +## ------------------- ## +_ASBOX echo for ac_var in $ac_subst_files do @@ -3228,9 +3198,11 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; fi if test -s confdefs.h; then - $as_echo "## ----------- ## + cat <<\_ASBOX +## ----------- ## ## confdefs.h. ## -## ----------- ##" +## ----------- ## +_ASBOX echo cat confdefs.h echo @@ -3285,12 +3257,7 @@ _ACEOF ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac + ac_site_file1=$CONFIG_SITE elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site @@ -3305,11 +3272,7 @@ do { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } + . "$ac_site_file" fi done @@ -3389,7 +3352,7 @@ if $ac_cache_corrupted; then $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 + as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## @@ -3406,22 +3369,16 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu # cross-compile macros ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done done if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 + as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -3435,27 +3392,27 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + as_fn_error "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 $as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : +if test "${ac_cv_build+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 + as_fn_error "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + as_fn_error "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 $as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +*) as_fn_error "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' @@ -3473,14 +3430,14 @@ case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 $as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : +if test "${ac_cv_host+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 + as_fn_error "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi @@ -3488,7 +3445,7 @@ fi $as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +*) as_fn_error "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' @@ -3510,22 +3467,16 @@ case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac ac_aux_dir= for ac_dir in `pwd` "$srcdir"/`pwd`; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done done if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \`pwd\` \"$srcdir\"/\`pwd\`" "$LINENO" 5 + as_fn_error "cannot find install-sh, install.sh, or shtool in \`pwd\` \"$srcdir\"/\`pwd\`" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -3571,7 +3522,7 @@ if test -n "$ac_tool_prefix"; then set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : +if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -3583,7 +3534,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3615,7 +3566,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then @@ -3627,7 +3578,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -3667,8 +3618,8 @@ fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 @@ -3782,8 +3733,9 @@ sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "C compiler cannot create executables +See \`config.log' for more details." "$LINENO" 5; }; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } @@ -3825,8 +3777,8 @@ done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 @@ -3883,9 +3835,9 @@ $as_echo "$ac_try_echo"; } >&5 else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. +as_fn_error "cannot run C compiled programs. If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } +See \`config.log' for more details." "$LINENO" 5; } fi fi fi @@ -3896,7 +3848,7 @@ rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : +if test "${ac_cv_objext+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3936,8 +3888,8 @@ sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "cannot compute suffix of object files: cannot compile +See \`config.log' for more details." "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi @@ -3947,7 +3899,7 @@ OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : +if test "${ac_cv_c_compiler_gnu+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -3984,7 +3936,7 @@ ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : +if test "${ac_cv_prog_cc_g+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag @@ -4062,7 +4014,7 @@ else fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : +if test "${ac_cv_prog_cc_c89+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no @@ -4071,7 +4023,8 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <stdarg.h> #include <stdio.h> -struct stat; +#include <sys/types.h> +#include <sys/stat.h> /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); @@ -4170,7 +4123,7 @@ if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : + if test "${ac_cv_prog_CPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded @@ -4200,7 +4153,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -4216,11 +4169,11 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi @@ -4259,7 +4212,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -4275,18 +4228,18 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } fi ac_ext=c @@ -4298,7 +4251,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : +if test "${ac_cv_path_GREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then @@ -4312,7 +4265,7 @@ do for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in @@ -4347,7 +4300,7 @@ esac done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP @@ -4361,7 +4314,7 @@ $as_echo "$ac_cv_path_GREP" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : +if test "${ac_cv_path_EGREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 @@ -4378,7 +4331,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -4413,7 +4366,7 @@ esac done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP @@ -4428,7 +4381,7 @@ $as_echo "$ac_cv_path_EGREP" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : +if test "${ac_cv_header_stdc+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -4545,7 +4498,8 @@ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -4557,7 +4511,7 @@ done ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default" -if test "x$ac_cv_header_minix_config_h" = xyes; then : +if test "x$ac_cv_header_minix_config_h" = x""yes; then : MINIX=yes else MINIX= @@ -4579,14 +4533,14 @@ $as_echo "#define _MINIX 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5 $as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; } -if ${ac_cv_safe_to_define___extensions__+:} false; then : +if test "${ac_cv_safe_to_define___extensions__+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -# define __EXTENSIONS__ 1 - $ac_includes_default +# define __EXTENSIONS__ 1 + $ac_includes_default int main () { @@ -4775,7 +4729,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}uname; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_UNAME+:} false; then : +if test "${ac_cv_path_UNAME+set}" = set; then : $as_echo_n "(cached) " >&6 else case $UNAME in @@ -4789,7 +4743,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_UNAME="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4818,7 +4772,7 @@ if test -z "$ac_cv_path_UNAME"; then set dummy uname; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_UNAME+:} false; then : +if test "${ac_cv_path_ac_pt_UNAME+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_UNAME in @@ -4832,7 +4786,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_UNAME="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4885,7 +4839,7 @@ then set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : +if test "${ac_cv_prog_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then @@ -4897,7 +4851,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4925,7 +4879,7 @@ if test -z "$ac_cv_prog_CC"; then set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then @@ -4937,7 +4891,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -4977,7 +4931,7 @@ fi set dummy ${ac_tool_prefix}g++; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : +if test "${ac_cv_prog_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then @@ -4989,7 +4943,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="${ac_tool_prefix}g++" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5017,7 +4971,7 @@ if test -z "$ac_cv_prog_CXX"; then set dummy g++; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then @@ -5029,7 +4983,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="g++" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5069,7 +5023,7 @@ fi set dummy ${ac_tool_prefix}ld; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_LD+:} false; then : +if test "${ac_cv_prog_LD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$LD"; then @@ -5081,7 +5035,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_LD="${ac_tool_prefix}ld" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5109,7 +5063,7 @@ if test -z "$ac_cv_prog_LD"; then set dummy ld; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_LD+:} false; then : +if test "${ac_cv_prog_ac_ct_LD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_LD"; then @@ -5121,7 +5075,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_LD="ld" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5161,7 +5115,7 @@ fi set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : +if test "${ac_cv_prog_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then @@ -5173,7 +5127,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5201,7 +5155,7 @@ if test -z "$ac_cv_prog_RANLIB"; then set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then @@ -5213,7 +5167,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5267,7 +5221,7 @@ if test -z "$CXX"; then set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : +if test "${ac_cv_prog_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then @@ -5279,7 +5233,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5311,7 +5265,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then @@ -5323,7 +5277,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -5389,7 +5343,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } -if ${ac_cv_cxx_compiler_gnu+:} false; then : +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -5426,7 +5380,7 @@ ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } -if ${ac_cv_prog_cxx_g+:} false; then : +if test "${ac_cv_prog_cxx_g+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag @@ -5520,7 +5474,7 @@ if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : + if test "${ac_cv_prog_CPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded @@ -5550,7 +5504,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -5566,11 +5520,11 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi @@ -5609,7 +5563,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -5625,18 +5579,18 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } fi ac_ext=c @@ -5653,7 +5607,7 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 $as_echo_n "checking how to run the C++ preprocessor... " >&6; } if test -z "$CXXCPP"; then - if ${ac_cv_prog_CXXCPP+:} false; then : + if test "${ac_cv_prog_CXXCPP+set}" = set; then : $as_echo_n "(cached) " >&6 else # Double quotes because CXXCPP needs to be expanded @@ -5683,7 +5637,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -5699,11 +5653,11 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi @@ -5742,7 +5696,7 @@ else # Broken: fails on valid input. continue fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. @@ -5758,18 +5712,18 @@ else ac_preproc_ok=: break fi -rm -f conftest.err conftest.i conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext +rm -f conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "C++ preprocessor \"$CXXCPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } fi ac_ext=c @@ -5782,7 +5736,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu # the developers regenerating the configure script don't have to install libtool. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } -if ${ac_cv_path_SED+:} false; then : +if test "${ac_cv_path_SED+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ @@ -5802,7 +5756,7 @@ do for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_SED" || continue + { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in @@ -5837,7 +5791,7 @@ esac done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then - as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 + as_fn_error "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED @@ -5851,7 +5805,7 @@ $as_echo "$ac_cv_path_SED" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_prog_egrep+:} false; then : +if test "${ac_cv_prog_egrep+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo a | (grep -E '(a|b)') >/dev/null 2>&1 @@ -5911,7 +5865,7 @@ else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 $as_echo_n "checking for non-GNU ld... " >&6; } fi -if ${lt_cv_path_LD+:} false; then : +if test "${lt_cv_path_LD+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -z "$LD"; then @@ -5948,10 +5902,10 @@ else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi -test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5 +test -z "$LD" && as_fn_error "no acceptable ld found in \$PATH" "$LINENO" 5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 $as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } -if ${lt_cv_prog_gnu_ld+:} false; then : +if test "${lt_cv_prog_gnu_ld+set}" = set; then : $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. @@ -5975,7 +5929,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : +if test "${ac_cv_prog_AWK+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then @@ -5987,7 +5941,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6028,7 +5982,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : +if test "${ac_cv_path_install+set}" = set; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -6048,7 +6002,7 @@ case $as_dir/ in #(( # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. @@ -6120,7 +6074,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : +if test "${ac_cv_prog_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then @@ -6132,7 +6086,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6160,7 +6114,7 @@ if test -z "$ac_cv_prog_RANLIB"; then set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then @@ -6172,7 +6126,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6209,7 +6163,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU make" >&5 $as_echo_n "checking for GNU make... " >&6; } -if ${ac_cv_GNU_MAKE+:} false; then : +if test "${ac_cv_GNU_MAKE+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_GNU_MAKE='Not Found' ; @@ -6229,7 +6183,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_GNU_MAKE" >&5 $as_echo "$ac_cv_GNU_MAKE" >&6; } ; if test "x$ac_cv_GNU_MAKE" = "xNot Found" ; then - as_fn_error $? "*** Please install GNU make. It is required to build Asterisk!" "$LINENO" 5 + as_fn_error "*** Please install GNU make. It is required to build Asterisk!" "$LINENO" 5 exit 1 fi GNU_MAKE=$ac_cv_GNU_MAKE @@ -6237,7 +6191,7 @@ GNU_MAKE=$ac_cv_GNU_MAKE { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : +if test "${ac_cv_path_EGREP+set}" = set; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 @@ -6254,7 +6208,7 @@ do for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in @@ -6289,7 +6243,7 @@ esac done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP @@ -6310,7 +6264,7 @@ if test -n "$ac_tool_prefix"; then set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_STRIP+:} false; then : +if test "${ac_cv_prog_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then @@ -6322,7 +6276,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_STRIP="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6354,7 +6308,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_STRIP+:} false; then : +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then @@ -6366,7 +6320,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_STRIP="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6410,7 +6364,7 @@ if test -n "$ac_tool_prefix"; then set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AR+:} false; then : +if test "${ac_cv_prog_AR+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then @@ -6422,7 +6376,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6454,7 +6408,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_AR+:} false; then : +if test "${ac_cv_prog_ac_ct_AR+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then @@ -6466,7 +6420,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6514,7 +6468,7 @@ fi set dummy bison; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_BISON+:} false; then : +if test "${ac_cv_path_BISON+set}" = set; then : $as_echo_n "(cached) " >&6 else case $BISON in @@ -6528,7 +6482,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_BISON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6555,7 +6509,7 @@ fi set dummy cmp; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CMP+:} false; then : +if test "${ac_cv_path_CMP+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CMP in @@ -6569,7 +6523,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CMP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6596,7 +6550,7 @@ fi set dummy flex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_FLEX+:} false; then : +if test "${ac_cv_path_FLEX+set}" = set; then : $as_echo_n "(cached) " >&6 else case $FLEX in @@ -6610,7 +6564,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_FLEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6637,7 +6591,7 @@ fi set dummy grep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_GREP+:} false; then : +if test "${ac_cv_path_GREP+set}" = set; then : $as_echo_n "(cached) " >&6 else case $GREP in @@ -6651,7 +6605,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_GREP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6678,7 +6632,7 @@ fi set dummy python; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PYTHON+:} false; then : +if test "${ac_cv_path_PYTHON+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PYTHON in @@ -6692,7 +6646,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PYTHON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6719,7 +6673,7 @@ fi set dummy find; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_FIND+:} false; then : +if test "${ac_cv_path_FIND+set}" = set; then : $as_echo_n "(cached) " >&6 else case $FIND in @@ -6733,7 +6687,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_FIND="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6760,7 +6714,7 @@ fi set dummy compress; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_COMPRESS+:} false; then : +if test "${ac_cv_path_COMPRESS+set}" = set; then : $as_echo_n "(cached) " >&6 else case $COMPRESS in @@ -6774,7 +6728,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_COMPRESS="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6801,7 +6755,7 @@ fi set dummy basename; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_BASENAME+:} false; then : +if test "${ac_cv_path_BASENAME+set}" = set; then : $as_echo_n "(cached) " >&6 else case $BASENAME in @@ -6815,7 +6769,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_BASENAME="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6842,7 +6796,7 @@ fi set dummy dirname; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_DIRNAME+:} false; then : +if test "${ac_cv_path_DIRNAME+set}" = set; then : $as_echo_n "(cached) " >&6 else case $DIRNAME in @@ -6856,7 +6810,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_DIRNAME="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6883,7 +6837,7 @@ fi set dummy sh; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_SHELL+:} false; then : +if test "${ac_cv_path_SHELL+set}" = set; then : $as_echo_n "(cached) " >&6 else case $SHELL in @@ -6897,7 +6851,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_SHELL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6924,7 +6878,7 @@ fi set dummy ln; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_LN+:} false; then : +if test "${ac_cv_path_LN+set}" = set; then : $as_echo_n "(cached) " >&6 else case $LN in @@ -6938,7 +6892,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_LN="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -6965,7 +6919,7 @@ fi set dummy doxygen; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_DOXYGEN+:} false; then : +if test "${ac_cv_path_DOXYGEN+set}" = set; then : $as_echo_n "(cached) " >&6 else case $DOXYGEN in @@ -6979,7 +6933,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_DOXYGEN="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7006,7 +6960,7 @@ fi set dummy dot; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_DOT+:} false; then : +if test "${ac_cv_path_DOT+set}" = set; then : $as_echo_n "(cached) " >&6 else case $DOT in @@ -7020,7 +6974,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_DOT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7047,7 +7001,7 @@ fi set dummy wget; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_WGET+:} false; then : +if test "${ac_cv_path_WGET+set}" = set; then : $as_echo_n "(cached) " >&6 else case $WGET in @@ -7061,7 +7015,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_WGET="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7088,7 +7042,7 @@ fi set dummy curl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CURL+:} false; then : +if test "${ac_cv_path_CURL+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CURL in @@ -7102,7 +7056,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CURL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7129,7 +7083,7 @@ fi set dummy rubber; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_RUBBER+:} false; then : +if test "${ac_cv_path_RUBBER+set}" = set; then : $as_echo_n "(cached) " >&6 else case $RUBBER in @@ -7143,7 +7097,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_RUBBER="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7170,7 +7124,7 @@ fi set dummy catdvi; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CATDVI+:} false; then : +if test "${ac_cv_path_CATDVI+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CATDVI in @@ -7184,7 +7138,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CATDVI="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7211,7 +7165,7 @@ fi set dummy kpsewhich; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_KPATHSEA+:} false; then : +if test "${ac_cv_path_KPATHSEA+set}" = set; then : $as_echo_n "(cached) " >&6 else case $KPATHSEA in @@ -7225,7 +7179,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_KPATHSEA="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7252,7 +7206,7 @@ fi set dummy xmllint; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_XMLLINT+:} false; then : +if test "${ac_cv_path_XMLLINT+set}" = set; then : $as_echo_n "(cached) " >&6 else case $XMLLINT in @@ -7266,7 +7220,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_XMLLINT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7293,7 +7247,7 @@ fi set dummy xmlstarlet; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_XMLSTARLET+:} false; then : +if test "${ac_cv_path_XMLSTARLET+set}" = set; then : $as_echo_n "(cached) " >&6 else case $XMLSTARLET in @@ -7307,7 +7261,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_XMLSTARLET="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7334,7 +7288,7 @@ fi set dummy git; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_GIT+:} false; then : +if test "${ac_cv_path_GIT+set}" = set; then : $as_echo_n "(cached) " >&6 else case $GIT in @@ -7348,7 +7302,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_GIT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7380,7 +7334,7 @@ else set dummy fetch; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_FETCH+:} false; then : +if test "${ac_cv_path_FETCH+set}" = set; then : $as_echo_n "(cached) " >&6 else case $FETCH in @@ -7394,7 +7348,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_FETCH="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7425,7 +7379,7 @@ fi set dummy ldconfig; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_LDCONFIG+:} false; then : +if test "${ac_cv_path_LDCONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $LDCONFIG in @@ -7439,7 +7393,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_LDCONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7466,7 +7420,7 @@ fi set dummy sha1sum; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_SHA1SUM+:} false; then : +if test "${ac_cv_path_SHA1SUM+set}" = set; then : $as_echo_n "(cached) " >&6 else case $SHA1SUM in @@ -7480,7 +7434,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_SHA1SUM="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7507,7 +7461,7 @@ fi set dummy openssl; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_OPENSSL+:} false; then : +if test "${ac_cv_path_OPENSSL+set}" = set; then : $as_echo_n "(cached) " >&6 else case $OPENSSL in @@ -7521,7 +7475,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_OPENSSL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7547,7 +7501,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for bison that supports parse-param" >&5 $as_echo_n "checking for bison that supports parse-param... " >&6; } -if ${ac_cv_path_BISON2+:} false; then : +if test "${ac_cv_path_BISON2+set}" = set; then : $as_echo_n "(cached) " >&6 else @@ -7601,7 +7555,7 @@ if test -n "$ac_tool_prefix"; then set dummy ${ac_tool_prefix}soxmix; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_SOXMIX+:} false; then : +if test "${ac_cv_prog_SOXMIX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$SOXMIX"; then @@ -7613,7 +7567,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_SOXMIX="${ac_tool_prefix}soxmix" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7641,7 +7595,7 @@ if test -z "$ac_cv_prog_SOXMIX"; then set dummy soxmix; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_SOXMIX+:} false; then : +if test "${ac_cv_prog_ac_ct_SOXMIX+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_SOXMIX"; then @@ -7653,7 +7607,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_SOXMIX="soxmix" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7700,7 +7654,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_MD5+:} false; then : +if test "${ac_cv_prog_MD5+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$MD5"; then @@ -7712,7 +7666,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_MD5="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -7866,7 +7820,7 @@ $as_echo_n "checking whether pthreads work with $flag... " >&6; } set dummy pthread-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_acx_pthread_config+:} false; then : +if test "${ac_cv_prog_acx_pthread_config+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$acx_pthread_config"; then @@ -7878,7 +7832,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_acx_pthread_config="yes" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -8022,7 +7976,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_PTHREAD_CC+:} false; then : +if test "${ac_cv_prog_PTHREAD_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$PTHREAD_CC"; then @@ -8034,7 +7988,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_PTHREAD_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -8108,7 +8062,7 @@ if test "${enable_dev_mode+set}" = set; then : AST_DEVMODE=yes AST_DEVMODE_STRICT=yes ;; - *) as_fn_error $? "bad value ${enableval} for --enable-dev-mode" "$LINENO" 5 ;; + *) as_fn_error "bad value ${enableval} for --enable-dev-mode" "$LINENO" 5 ;; esac fi @@ -8122,7 +8076,7 @@ if test "${enable_coverage+set}" = set; then : enableval=$enable_coverage; case "${enableval}" in y|ye|yes) AST_CODE_COVERAGE=yes ;; n|no) AST_CODE_COVERAGE=no ;; - *) as_fn_error $? "bad value ${enableval} for --enable-coverage" "$LINENO" 5 ;; + *) as_fn_error "bad value ${enableval} for --enable-coverage" "$LINENO" 5 ;; esac fi @@ -9067,7 +9021,7 @@ do set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AWK+:} false; then : +if test "${ac_cv_prog_AWK+set}" = set; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then @@ -9079,7 +9033,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -9115,7 +9069,7 @@ done set dummy curl-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path__libcurl_config+:} false; then : +if test "${ac_cv_path__libcurl_config+set}" = set; then : $as_echo_n "(cached) " >&6 else case $_libcurl_config in @@ -9129,7 +9083,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path__libcurl_config="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -9157,7 +9111,7 @@ fi set dummy curl-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path__libcurl_config+:} false; then : +if test "${ac_cv_path__libcurl_config+set}" = set; then : $as_echo_n "(cached) " >&6 else case $_libcurl_config in @@ -9171,7 +9125,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path__libcurl_config="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -9198,7 +9152,7 @@ fi if test x$_libcurl_config != "x" ; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the version of libcurl" >&5 $as_echo_n "checking for the version of libcurl... " >&6; } -if ${libcurl_cv_lib_curl_version+:} false; then : +if test "${libcurl_cv_lib_curl_version+set}" = set; then : $as_echo_n "(cached) " >&6 else libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $2}'` @@ -9212,7 +9166,7 @@ $as_echo "$libcurl_cv_lib_curl_version" >&6; } if test $_libcurl_wanted -gt 0 ; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl >= version 7.10.1" >&5 $as_echo_n "checking for libcurl >= version 7.10.1... " >&6; } -if ${libcurl_cv_lib_version_ok+:} false; then : +if test "${libcurl_cv_lib_version_ok+set}" = set; then : $as_echo_n "(cached) " >&6 else @@ -9266,7 +9220,7 @@ $as_echo "$libcurl_cv_lib_version_ok" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libcurl is usable" >&5 $as_echo_n "checking whether libcurl is usable... " >&6; } -if ${libcurl_cv_lib_curl_usable+:} false; then : +if test "${libcurl_cv_lib_curl_usable+set}" = set; then : $as_echo_n "(cached) " >&6 else @@ -9325,7 +9279,7 @@ $as_echo "$libcurl_cv_lib_curl_usable" >&6; } LIBS="$LIBS $CURL_LIB" ac_fn_c_check_func "$LINENO" "curl_free" "ac_cv_func_curl_free" -if test "x$ac_cv_func_curl_free" = xyes; then : +if test "x$ac_cv_func_curl_free" = x""yes; then : else @@ -11149,22 +11103,11 @@ fi # check for basic system features and functionality before # checking for package libraries -ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF - -fi - # The Ultrix 4.2 mips builtin alloca declared by alloca.h only works # for constant arguments. Useless! { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5 $as_echo_n "checking for working alloca.h... " >&6; } -if ${ac_cv_working_alloca_h+:} false; then : +if test "${ac_cv_working_alloca_h+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -11197,7 +11140,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for alloca" >&5 $as_echo_n "checking for alloca... " >&6; } -if ${ac_cv_func_alloca_works+:} false; then : +if test "${ac_cv_func_alloca_works+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -11216,7 +11159,7 @@ else #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ -void *alloca (size_t); +char *alloca (); # endif # endif # endif @@ -11260,7 +11203,7 @@ $as_echo "#define C_ALLOCA 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether \`alloca.c' needs Cray hooks" >&5 $as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; } -if ${ac_cv_os_cray+:} false; then : +if test "${ac_cv_os_cray+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -11287,7 +11230,8 @@ if test $ac_cv_os_cray = yes; then for ac_func in _getb67 GETB67 getb67; do as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define CRAY_STACKSEG_END $ac_func @@ -11301,7 +11245,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking stack direction for C alloca" >&5 $as_echo_n "checking stack direction for C alloca... " >&6; } -if ${ac_cv_c_stack_direction+:} false; then : +if test "${ac_cv_c_stack_direction+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -11311,20 +11255,23 @@ else /* end confdefs.h. */ $ac_includes_default int -find_stack_direction (int *addr, int depth) +find_stack_direction () { - int dir, dummy = 0; - if (! addr) - addr = &dummy; - *addr = addr < &dummy ? 1 : addr == &dummy ? 0 : -1; - dir = depth ? find_stack_direction (addr, depth - 1) : 0; - return dir + dummy; + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; } int -main (int argc, char **argv) +main () { - return find_stack_direction (0, argc + !argv + 20) < 0; + return find_stack_direction () < 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : @@ -11351,7 +11298,7 @@ for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5 $as_echo_n "checking for $ac_hdr that defines DIR... " >&6; } -if eval \${$as_ac_Header+:} false; then : +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -11378,7 +11325,8 @@ fi eval ac_res=\$$as_ac_Header { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1 _ACEOF @@ -11391,7 +11339,7 @@ done if test $ac_header_dirent = dirent.h; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 $as_echo_n "checking for library containing opendir... " >&6; } -if ${ac_cv_search_opendir+:} false; then : +if test "${ac_cv_search_opendir+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -11425,11 +11373,11 @@ for ac_lib in '' dir; do fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_opendir+:} false; then : + if test "${ac_cv_search_opendir+set}" = set; then : break fi done -if ${ac_cv_search_opendir+:} false; then : +if test "${ac_cv_search_opendir+set}" = set; then : else ac_cv_search_opendir=no @@ -11448,7 +11396,7 @@ fi else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5 $as_echo_n "checking for library containing opendir... " >&6; } -if ${ac_cv_search_opendir+:} false; then : +if test "${ac_cv_search_opendir+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -11482,11 +11430,11 @@ for ac_lib in '' x; do fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_opendir+:} false; then : + if test "${ac_cv_search_opendir+set}" = set; then : break fi done -if ${ac_cv_search_opendir+:} false; then : +if test "${ac_cv_search_opendir+set}" = set; then : else ac_cv_search_opendir=no @@ -11506,7 +11454,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : +if test "${ac_cv_header_stdc+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -11618,7 +11566,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5 $as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; } -if ${ac_cv_header_sys_wait_h+:} false; then : +if test "${ac_cv_header_sys_wait_h+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -11661,7 +11609,8 @@ for ac_header in arpa/inet.h fcntl.h inttypes.h libintl.h limits.h locale.h mall do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -11692,7 +11641,7 @@ if test "x${PBX_TERMCAP}" != "x1" -a "${USE_TERMCAP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_termcap_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -ltermcap" >&5 $as_echo_n "checking for ${pbxfuncname} in -ltermcap... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -11727,7 +11676,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_TERMCAP_FOUND=yes else AST_TERMCAP_FOUND=no @@ -11750,7 +11700,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${TERMCAP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" -if test "x$ac_cv_header_" = xyes; then : +if test "x$ac_cv_header_" = x""yes; then : TERMCAP_HEADER_FOUND=1 else TERMCAP_HEADER_FOUND=0 @@ -11796,7 +11746,7 @@ if test "x${PBX_TINFO}" != "x1" -a "${USE_TINFO}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_tinfo_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -ltinfo" >&5 $as_echo_n "checking for ${pbxfuncname} in -ltinfo... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -11831,7 +11781,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_TINFO_FOUND=yes else AST_TINFO_FOUND=no @@ -11854,7 +11805,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${TINFO_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" -if test "x$ac_cv_header_" = xyes; then : +if test "x$ac_cv_header_" = x""yes; then : TINFO_HEADER_FOUND=1 else TINFO_HEADER_FOUND=0 @@ -11900,7 +11851,7 @@ if test "x${PBX_CURSES}" != "x1" -a "${USE_CURSES}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_curses_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcurses" >&5 $as_echo_n "checking for ${pbxfuncname} in -lcurses... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -11935,7 +11886,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_CURSES_FOUND=yes else AST_CURSES_FOUND=no @@ -11958,7 +11910,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${CURSES_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" -if test "x$ac_cv_header_curses_h" = xyes; then : +if test "x$ac_cv_header_curses_h" = x""yes; then : CURSES_HEADER_FOUND=1 else CURSES_HEADER_FOUND=0 @@ -12004,7 +11956,7 @@ if test "x${PBX_NCURSES}" != "x1" -a "${USE_NCURSES}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ncurses_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lncurses" >&5 $as_echo_n "checking for ${pbxfuncname} in -lncurses... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -12039,7 +11991,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_NCURSES_FOUND=yes else AST_NCURSES_FOUND=no @@ -12062,7 +12015,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${NCURSES_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "curses.h" "ac_cv_header_curses_h" "$ac_includes_default" -if test "x$ac_cv_header_curses_h" = xyes; then : +if test "x$ac_cv_header_curses_h" = x""yes; then : NCURSES_HEADER_FOUND=1 else NCURSES_HEADER_FOUND=0 @@ -12108,7 +12061,7 @@ if test "x${PBX_UUID}" != "x1" -a "${USE_UUID}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_uuid_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -luuid" >&5 $as_echo_n "checking for ${pbxfuncname} in -luuid... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -12143,7 +12096,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_UUID_FOUND=yes else AST_UUID_FOUND=no @@ -12166,7 +12120,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${UUID_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "uuid/uuid.h" "ac_cv_header_uuid_uuid_h" "$ac_includes_default" -if test "x$ac_cv_header_uuid_uuid_h" = xyes; then : +if test "x$ac_cv_header_uuid_uuid_h" = x""yes; then : UUID_HEADER_FOUND=1 else UUID_HEADER_FOUND=0 @@ -12212,7 +12166,7 @@ if test "x${PBX_JANSSON}" != "x1" -a "${USE_JANSSON}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_jansson_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -ljansson" >&5 $as_echo_n "checking for ${pbxfuncname} in -ljansson... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -12247,7 +12201,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_JANSSON_FOUND=yes else AST_JANSSON_FOUND=no @@ -12270,7 +12225,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${JANSSON_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "jansson.h" "ac_cv_header_jansson_h" "$ac_includes_default" -if test "x$ac_cv_header_jansson_h" = xyes; then : +if test "x$ac_cv_header_jansson_h" = x""yes; then : JANSSON_HEADER_FOUND=1 else JANSSON_HEADER_FOUND=0 @@ -12307,16 +12262,16 @@ elif test "x$CURSES_LIB" != "x" ; then elif test "x$NCURSES_LIB" != "x" ; then EDITLINE_LIB="$NCURSES_LIB" else - as_fn_error $? "*** termcap support not found (on modern systems, this typically means the ncurses development package is missing)" "$LINENO" 5 + as_fn_error "*** termcap support not found (on modern systems, this typically means the ncurses development package is missing)" "$LINENO" 5 fi if test "x$UUID_LIB" == "x"; then - as_fn_error $? "*** uuid support not found (this typically means the uuid development package is missing)" "$LINENO" 5 + as_fn_error "*** uuid support not found (this typically means the uuid development package is missing)" "$LINENO" 5 fi if test "x$JANSSON_LIB" == "x"; then - as_fn_error $? "*** JSON support not found (this typically means the libjansson development package is missing)" "$LINENO" 5 + as_fn_error "*** JSON support not found (this typically means the libjansson development package is missing)" "$LINENO" 5 fi # Another mandatory item (unless it's explicitly disabled) @@ -12325,7 +12280,7 @@ if test "${enable_xmldoc+set}" = set; then : enableval=$enable_xmldoc; case "${enableval}" in y|ye|yes) disable_xmldoc=no ;; n|no) disable_xmldoc=yes ;; - *) as_fn_error $? "bad value ${enableval} for --disable-xmldoc" "$LINENO" 5 ;; + *) as_fn_error "bad value ${enableval} for --disable-xmldoc" "$LINENO" 5 ;; esac else disable_xmldoc=no @@ -12341,7 +12296,7 @@ if test "${disable_xmldoc}" != "yes"; then set dummy ${ac_tool_prefix}xml2-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CONFIG_LIBXML2+:} false; then : +if test "${ac_cv_path_CONFIG_LIBXML2+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CONFIG_LIBXML2 in @@ -12356,7 +12311,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CONFIG_LIBXML2="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -12385,7 +12340,7 @@ if test -z "$ac_cv_path_CONFIG_LIBXML2"; then set dummy xml2-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_CONFIG_LIBXML2+:} false; then : +if test "${ac_cv_path_ac_pt_CONFIG_LIBXML2+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_CONFIG_LIBXML2 in @@ -12400,7 +12355,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_CONFIG_LIBXML2="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -12500,7 +12455,7 @@ fi for ac_header in xlocale.h do : ac_fn_c_check_header_mongrel "$LINENO" "xlocale.h" "ac_cv_header_xlocale_h" "$ac_includes_default" -if test "x$ac_cv_header_xlocale_h" = xyes; then : +if test "x$ac_cv_header_xlocale_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_XLOCALE_H 1 _ACEOF @@ -12514,7 +12469,8 @@ for ac_header in winsock.h winsock2.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -12525,7 +12481,7 @@ done ac_fn_c_check_header_mongrel "$LINENO" "sys/poll.h" "ac_cv_header_sys_poll_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_poll_h" = xyes; then : +if test "x$ac_cv_header_sys_poll_h" = x""yes; then : else @@ -12544,7 +12500,7 @@ if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } -if ${ac_cv_sys_largefile_CC+:} false; then : +if test "${ac_cv_sys_largefile_CC+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no @@ -12595,7 +12551,7 @@ $as_echo "$ac_cv_sys_largefile_CC" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } -if ${ac_cv_sys_file_offset_bits+:} false; then : +if test "${ac_cv_sys_file_offset_bits+set}" = set; then : $as_echo_n "(cached) " >&6 else while :; do @@ -12664,7 +12620,7 @@ rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } -if ${ac_cv_sys_large_files+:} false; then : +if test "${ac_cv_sys_large_files+set}" = set; then : $as_echo_n "(cached) " >&6 else while :; do @@ -12731,74 +12687,90 @@ _ACEOF esac rm -rf conftest* fi - - fi # Checks for typedefs, structures, and compiler characteristics. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 $as_echo_n "checking for stdbool.h that conforms to C99... " >&6; } -if ${ac_cv_header_stdbool_h+:} false; then : +if test "${ac_cv_header_stdbool_h+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - #include <stdbool.h> - #ifndef bool - "error: bool is not defined" - #endif - #ifndef false - "error: false is not defined" - #endif - #if false - "error: false is not 0" - #endif - #ifndef true - "error: true is not defined" - #endif - #if true != 1 - "error: true is not 1" - #endif - #ifndef __bool_true_false_are_defined - "error: __bool_true_false_are_defined is not defined" - #endif - - struct s { _Bool s: 1; _Bool t; } s; - - char a[true == 1 ? 1 : -1]; - char b[false == 0 ? 1 : -1]; - char c[__bool_true_false_are_defined == 1 ? 1 : -1]; - char d[(bool) 0.5 == true ? 1 : -1]; - /* See body of main program for 'e'. */ - char f[(_Bool) 0.0 == false ? 1 : -1]; - char g[true]; - char h[sizeof (_Bool)]; - char i[sizeof s.t]; - enum { j = false, k = true, l = false * true, m = true * 256 }; - /* The following fails for - HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ - _Bool n[m]; - char o[sizeof n == m * sizeof n[0] ? 1 : -1]; - char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; - /* Catch a bug in an HP-UX C compiler. See - http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html - http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html - */ - _Bool q = true; - _Bool *pq = &q; - -int -main () -{ - - bool e = &s; - *pq |= q; - *pq |= ! q; - /* Refer to every declared value, to avoid compiler optimizations. */ - return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l - + !m + !n + !o + !p + !q + !pq); +#include <stdbool.h> +#ifndef bool + "error: bool is not defined" +#endif +#ifndef false + "error: false is not defined" +#endif +#if false + "error: false is not 0" +#endif +#ifndef true + "error: true is not defined" +#endif +#if true != 1 + "error: true is not 1" +#endif +#ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" +#endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + bool e = &s; + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; +# if defined __xlc__ || defined __GNUC__ + /* Catch a bug in IBM AIX xlc compiler version 6.0.0.0 + reported by James Lemley on 2005-10-05; see + http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html + This test is not quite right, since xlc is allowed to + reject this program, as the initializer for xlcbug is + not one of the forms that C requires support for. + However, doing the test right would require a runtime + test, and that would make cross-compilation harder. + Let us hope that IBM fixes the xlc bug, and also adds + support for this kind of constant expression. In the + meantime, this test will reject xlc, which is OK, since + our stdbool.h substitute should suffice. We also test + this with GCC, where it should work, to detect more + quickly whether someone messes up the test in the + future. */ + char digs[] = "0123456789"; + int xlcbug = 1 / (&(digs + 5)[-2 + (bool) 1] == &digs[4] ? 1 : -1); +# endif + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; + +int +main () +{ + + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); ; return 0; @@ -12813,8 +12785,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 $as_echo "$ac_cv_header_stdbool_h" >&6; } - ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" -if test "x$ac_cv_type__Bool" = xyes; then : +ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" +if test "x$ac_cv_type__Bool" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE__BOOL 1 @@ -12823,7 +12795,6 @@ _ACEOF fi - if test $ac_cv_header_stdbool_h = yes; then $as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h @@ -12832,7 +12803,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 $as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : +if test "${ac_cv_c_const+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -12841,11 +12812,11 @@ else int main () { - +/* FIXME: Include the comments suggested by Paul. */ #ifndef __cplusplus - /* Ultrix mips cc rejects this sort of thing. */ + /* Ultrix mips cc rejects this. */ typedef int charset[2]; - const charset cs = { 0, 0 }; + const charset cs; /* SunOS 4.1.1 cc rejects this. */ char const *const *pcpcc; char **ppc; @@ -12862,9 +12833,8 @@ main () ++pcpcc; ppc = (char**) pcpcc; pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this sort of thing. */ - char tx; - char *t = &tx; + { /* SCO 3.2v4 cc rejects this. */ + char *t; char const *s = 0 ? (char *) 0 : (char const *) 0; *t++ = 0; @@ -12880,10 +12850,10 @@ main () iptr p = 0; ++p; } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying + { /* AIX XL C 1.02.0.0 rejects this saying "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; } bx; - struct s *b = &bx; b->j = 5; + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; @@ -12913,7 +12883,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 $as_echo_n "checking for uid_t in sys/types.h... " >&6; } -if ${ac_cv_type_uid_t+:} false; then : +if test "${ac_cv_type_uid_t+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -12943,7 +12913,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : +if test "${ac_cv_c_inline+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no @@ -12986,7 +12956,7 @@ esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long double with more range or precision than double" >&5 $as_echo_n "checking for long double with more range or precision than double... " >&6; } -if ${ac_cv_type_long_double_wider+:} false; then : +if test "${ac_cv_type_long_double_wider+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13013,8 +12983,7 @@ static int test_array [1 - 2 * !((0 < ((DBL_MAX_EXP < LDBL_MAX_EXP) - (LDBL_MANT_DIG < DBL_MANT_DIG))) && (int) LDBL_EPSILON == 0 )]; -test_array [0] = 0; -return test_array [0]; +test_array [0] = 0 ; return 0; @@ -13036,7 +13005,7 @@ $as_echo "#define HAVE_LONG_DOUBLE_WIDER 1" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" -if test "x$ac_cv_type_mode_t" = xyes; then : +if test "x$ac_cv_type_mode_t" = x""yes; then : else @@ -13047,7 +13016,7 @@ _ACEOF fi ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" -if test "x$ac_cv_type_off_t" = xyes; then : +if test "x$ac_cv_type_off_t" = x""yes; then : else @@ -13058,7 +13027,7 @@ _ACEOF fi ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" -if test "x$ac_cv_type_pid_t" = xyes; then : +if test "x$ac_cv_type_pid_t" = x""yes; then : else @@ -13069,7 +13038,7 @@ _ACEOF fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : +if test "x$ac_cv_type_size_t" = x""yes; then : else @@ -13080,7 +13049,7 @@ _ACEOF fi ac_fn_c_check_member "$LINENO" "struct stat" "st_blksize" "ac_cv_member_struct_stat_st_blksize" "$ac_includes_default" -if test "x$ac_cv_member_struct_stat_st_blksize" = xyes; then : +if test "x$ac_cv_member_struct_stat_st_blksize" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 @@ -13092,7 +13061,7 @@ fi ac_fn_c_check_member "$LINENO" "struct ucred" "uid" "ac_cv_member_struct_ucred_uid" "#include <sys/types.h> #include <sys/socket.h> " -if test "x$ac_cv_member_struct_ucred_uid" = xyes; then : +if test "x$ac_cv_member_struct_ucred_uid" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_UCRED_UID 1 @@ -13103,7 +13072,7 @@ fi ac_fn_c_check_member "$LINENO" "struct ucred" "cr_uid" "ac_cv_member_struct_ucred_cr_uid" "#include <sys/types.h> #include <sys/socket.h> " -if test "x$ac_cv_member_struct_ucred_cr_uid" = xyes; then : +if test "x$ac_cv_member_struct_ucred_cr_uid" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_UCRED_CR_UID 1 @@ -13114,7 +13083,7 @@ fi ac_fn_c_check_member "$LINENO" "struct sockpeercred" "uid" "ac_cv_member_struct_sockpeercred_uid" "#include <sys/types.h> #include <sys/socket.h> " -if test "x$ac_cv_member_struct_sockpeercred_uid" = xyes; then : +if test "x$ac_cv_member_struct_sockpeercred_uid" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_SOCKPEERCRED_UID 1 @@ -13125,7 +13094,7 @@ fi ac_fn_c_check_member "$LINENO" "struct ifreq" "ifr_ifru.ifru_hwaddr" "ac_cv_member_struct_ifreq_ifr_ifru_ifru_hwaddr" "#include <net/if.h> " -if test "x$ac_cv_member_struct_ifreq_ifr_ifru_ifru_hwaddr" = xyes; then : +if test "x$ac_cv_member_struct_ifreq_ifr_ifru_ifru_hwaddr" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_IFREQ_IFR_IFRU_IFRU_HWADDR 1 @@ -13136,7 +13105,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether time.h and sys/time.h may both be included" >&5 $as_echo_n "checking whether time.h and sys/time.h may both be included... " >&6; } -if ${ac_cv_header_time+:} false; then : +if test "${ac_cv_header_time+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13171,7 +13140,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 $as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } -if ${ac_cv_struct_tm+:} false; then : +if test "${ac_cv_struct_tm+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13206,7 +13175,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working volatile" >&5 $as_echo_n "checking for working volatile... " >&6; } -if ${ac_cv_c_volatile+:} false; then : +if test "${ac_cv_c_volatile+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13239,7 +13208,7 @@ $as_echo "#define volatile /**/" >>confdefs.h fi ac_fn_c_check_type "$LINENO" "ptrdiff_t" "ac_cv_type_ptrdiff_t" "$ac_includes_default" -if test "x$ac_cv_type_ptrdiff_t" = xyes; then : +if test "x$ac_cv_type_ptrdiff_t" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTRDIFF_T 1 @@ -13253,7 +13222,7 @@ fi for ac_header in unistd.h do : ac_fn_c_check_header_mongrel "$LINENO" "unistd.h" "ac_cv_header_unistd_h" "$ac_includes_default" -if test "x$ac_cv_header_unistd_h" = xyes; then : +if test "x$ac_cv_header_unistd_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UNISTD_H 1 _ACEOF @@ -13264,7 +13233,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working chown" >&5 $as_echo_n "checking for working chown... " >&6; } -if ${ac_cv_func_chown_works+:} false; then : +if test "${ac_cv_func_chown_works+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -13317,7 +13286,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether closedir returns void" >&5 $as_echo_n "checking whether closedir returns void... " >&6; } -if ${ac_cv_func_closedir_void+:} false; then : +if test "${ac_cv_func_closedir_void+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -13359,7 +13328,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for error_at_line" >&5 $as_echo_n "checking for error_at_line... " >&6; } -if ${ac_cv_lib_error_at_line+:} false; then : +if test "${ac_cv_lib_error_at_line+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -13395,7 +13364,7 @@ fi for ac_header in vfork.h do : ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default" -if test "x$ac_cv_header_vfork_h" = xyes; then : +if test "x$ac_cv_header_vfork_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VFORK_H 1 _ACEOF @@ -13408,7 +13377,8 @@ for ac_func in fork vfork do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -13419,7 +13389,7 @@ done if test "x$ac_cv_func_fork" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5 $as_echo_n "checking for working fork... " >&6; } -if ${ac_cv_func_fork_works+:} false; then : +if test "${ac_cv_func_fork_works+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -13472,7 +13442,7 @@ ac_cv_func_vfork_works=$ac_cv_func_vfork if test "x$ac_cv_func_vfork" = xyes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5 $as_echo_n "checking for working vfork... " >&6; } -if ${ac_cv_func_vfork_works+:} false; then : +if test "${ac_cv_func_vfork_works+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -13609,7 +13579,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5 $as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; } -if ${ac_cv_sys_largefile_source+:} false; then : +if test "${ac_cv_sys_largefile_source+set}" = set; then : $as_echo_n "(cached) " >&6 else while :; do @@ -13678,7 +13648,7 @@ fi if test $ac_cv_c_compiler_gnu = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5 $as_echo_n "checking whether $CC needs -traditional... " >&6; } -if ${ac_cv_prog_gcc_traditional+:} false; then : +if test "${ac_cv_prog_gcc_traditional+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_pattern="Autoconf.*'x'" @@ -13723,7 +13693,7 @@ fi # AC_FUNC_REALLOC { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working memcmp" >&5 $as_echo_n "checking for working memcmp... " >&6; } -if ${ac_cv_func_memcmp_working+:} false; then : +if test "${ac_cv_func_memcmp_working+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -13791,7 +13761,8 @@ do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -13810,7 +13781,7 @@ done for ac_func in getpagesize do : ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" -if test "x$ac_cv_func_getpagesize" = xyes; then : +if test "x$ac_cv_func_getpagesize" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_GETPAGESIZE 1 _ACEOF @@ -13820,7 +13791,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working mmap" >&5 $as_echo_n "checking for working mmap... " >&6; } -if ${ac_cv_func_mmap_fixed_mapped+:} false; then : +if test "${ac_cv_func_mmap_fixed_mapped+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -13988,7 +13959,8 @@ for ac_header in sys/select.h sys/socket.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF @@ -13999,7 +13971,7 @@ done { $as_echo "$as_me:${as_lineno-$LINENO}: checking types of arguments for select" >&5 $as_echo_n "checking types of arguments for select... " >&6; } -if ${ac_cv_func_select_args+:} false; then : +if test "${ac_cv_func_select_args+set}" = set; then : $as_echo_n "(cached) " >&6 else for ac_arg234 in 'fd_set *' 'int *' 'void *'; do @@ -14033,7 +14005,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext done done # Provide a safe default value. -: "${ac_cv_func_select_args=int,int *,struct timeval *}" +: ${ac_cv_func_select_args='int,int *,struct timeval *'} fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_select_args" >&5 @@ -14059,7 +14031,7 @@ _ACEOF rm -f conftest* -if ${ac_cv_func_setvbuf_reversed+:} false; then : +if test "${ac_cv_func_setvbuf_reversed+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_cv_func_setvbuf_reversed=no @@ -14068,7 +14040,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 $as_echo_n "checking return type of signal handlers... " >&6; } -if ${ac_cv_type_signal+:} false; then : +if test "${ac_cv_type_signal+set}" = set; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext @@ -14101,7 +14073,7 @@ _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5 $as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; } -if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then : +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then : $as_echo_n "(cached) " >&6 else rm -f conftest.sym conftest.file @@ -14163,7 +14135,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat accepts an empty string" >&5 $as_echo_n "checking whether stat accepts an empty string... " >&6; } -if ${ac_cv_func_stat_empty_string_bug+:} false; then : +if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -14209,7 +14181,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strcoll" >&5 $as_echo_n "checking for working strcoll... " >&6; } -if ${ac_cv_func_strcoll_works+:} false; then : +if test "${ac_cv_func_strcoll_works+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -14249,7 +14221,7 @@ fi for ac_func in strftime do : ac_fn_c_check_func "$LINENO" "strftime" "ac_cv_func_strftime" -if test "x$ac_cv_func_strftime" = xyes; then : +if test "x$ac_cv_func_strftime" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRFTIME 1 _ACEOF @@ -14258,7 +14230,7 @@ else # strftime is in -lintl on SCO UNIX. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for strftime in -lintl" >&5 $as_echo_n "checking for strftime in -lintl... " >&6; } -if ${ac_cv_lib_intl_strftime+:} false; then : +if test "${ac_cv_lib_intl_strftime+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -14292,7 +14264,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_intl_strftime" >&5 $as_echo "$ac_cv_lib_intl_strftime" >&6; } -if test "x$ac_cv_lib_intl_strftime" = xyes; then : +if test "x$ac_cv_lib_intl_strftime" = x""yes; then : $as_echo "#define HAVE_STRFTIME 1" >>confdefs.h LIBS="-lintl $LIBS" @@ -14301,17 +14273,13 @@ fi fi done - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strnlen" >&5 $as_echo_n "checking for working strnlen... " >&6; } -if ${ac_cv_func_strnlen_working+:} false; then : +if test "${ac_cv_func_strnlen_working+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : - # Guess no on AIX systems, yes otherwise. - case "$host_os" in - aix*) ac_cv_func_strnlen_working=no;; - *) ac_cv_func_strnlen_working=yes;; - esac + ac_cv_func_strnlen_working=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -14360,7 +14328,7 @@ esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for working strtod" >&5 $as_echo_n "checking for working strtod... " >&6; } -if ${ac_cv_func_strtod+:} false; then : +if test "${ac_cv_func_strtod+set}" = set; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : @@ -14419,14 +14387,14 @@ if test $ac_cv_func_strtod = no; then esac ac_fn_c_check_func "$LINENO" "pow" "ac_cv_func_pow" -if test "x$ac_cv_func_pow" = xyes; then : +if test "x$ac_cv_func_pow" = x""yes; then : fi if test $ac_cv_func_pow = no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pow in -lm" >&5 $as_echo_n "checking for pow in -lm... " >&6; } -if ${ac_cv_lib_m_pow+:} false; then : +if test "${ac_cv_lib_m_pow+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -14460,7 +14428,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_pow" >&5 $as_echo "$ac_cv_lib_m_pow" >&6; } -if test "x$ac_cv_lib_m_pow" = xyes; then : +if test "x$ac_cv_lib_m_pow" = x""yes; then : POW_LIB=-lm else { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot find library containing definition of pow" >&5 @@ -14476,7 +14444,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether utime accepts a null argument" >&5 $as_echo_n "checking whether utime accepts a null argument... " >&6; } -if ${ac_cv_func_utime_null+:} false; then : +if test "${ac_cv_func_utime_null+set}" = set; then : $as_echo_n "(cached) " >&6 else rm -f conftest.data; >conftest.data @@ -14526,13 +14494,13 @@ rm -f conftest.data for ac_func in vprintf do : ac_fn_c_check_func "$LINENO" "vprintf" "ac_cv_func_vprintf" -if test "x$ac_cv_func_vprintf" = xyes; then : +if test "x$ac_cv_func_vprintf" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VPRINTF 1 _ACEOF ac_fn_c_check_func "$LINENO" "_doprnt" "ac_cv_func__doprnt" -if test "x$ac_cv_func__doprnt" = xyes; then : +if test "x$ac_cv_func__doprnt" = x""yes; then : $as_echo "#define HAVE_DOPRNT 1" >>confdefs.h @@ -14546,7 +14514,8 @@ for ac_func in asprintf atexit closefrom dup2 eaccess endpwent euidaccess ffsll do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -14559,7 +14528,7 @@ done # so that AC_CHECK_FUNCS can detect functions in that library. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqrt in -lm" >&5 $as_echo_n "checking for sqrt in -lm... " >&6; } -if ${ac_cv_lib_m_sqrt+:} false; then : +if test "${ac_cv_lib_m_sqrt+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -14593,7 +14562,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_m_sqrt" >&5 $as_echo "$ac_cv_lib_m_sqrt" >&6; } -if test "x$ac_cv_lib_m_sqrt" = xyes; then : +if test "x$ac_cv_lib_m_sqrt" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBM 1 _ACEOF @@ -14607,7 +14576,8 @@ for ac_func in exp2 log2 exp10 log10 sin cos tan asin acos atan atan2 pow rint e do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -14623,7 +14593,8 @@ if test "x${ac_cv_type_long_double_wider}" = "xyes" ; then do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -14724,7 +14695,7 @@ LDFLAGS=${old_LDFLAGS} rm -f conftest.dynamics ac_fn_c_check_header_mongrel "$LINENO" "sys/poll.h" "ac_cv_header_sys_poll_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_poll_h" = xyes; then : +if test "x$ac_cv_header_sys_poll_h" = x""yes; then : HAS_POLL=1 $as_echo "#define HAVE_SYS_POLL_H 1" >>confdefs.h @@ -14738,7 +14709,7 @@ if test "${enable_internal_poll+set}" = set; then : enableval=$enable_internal_poll; case "${enableval}" in y|ye|yes) HAS_POLL="";; n|no) HAS_POLL="${HAS_POLL}" ;; - *) as_fn_error $? "bad value ${enableval} for --enable-internal-poll" "$LINENO" 5 ;; + *) as_fn_error "bad value ${enableval} for --enable-internal-poll" "$LINENO" 5 ;; esac fi @@ -14749,7 +14720,7 @@ if test "${enable_asteriskssl+set}" = set; then : enableval=$enable_asteriskssl; case "${enableval}" in y|ye|yes) AST_ASTERISKSSL=yes ;; n|no) AST_ASTERISKSSL=no ;; - *) as_fn_error $? "bad value ${enableval} for --disable-asteriskssl" "$LINENO" 5 ;; + *) as_fn_error "bad value ${enableval} for --disable-asteriskssl" "$LINENO" 5 ;; esac else AST_ASTERISKSSL=yes @@ -14763,7 +14734,8 @@ for ac_func in funopen fopencookie do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : +eval as_val=\$$as_ac_var + if test "x$as_val" = x""yes; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF @@ -14775,7 +14747,7 @@ done for ac_func in inet_aton do : ac_fn_c_check_func "$LINENO" "inet_aton" "ac_cv_func_inet_aton" -if test "x$ac_cv_func_inet_aton" = xyes; then : +if test "x$ac_cv_func_inet_aton" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_INET_ATON 1 _ACEOF @@ -14815,7 +14787,7 @@ rm -f core conftest.err conftest.$ac_objext \ # some systems already have gethostbyname_r so we don't need to build ours in main/utils.c { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname_r" >&5 $as_echo_n "checking for library containing gethostbyname_r... " >&6; } -if ${ac_cv_search_gethostbyname_r+:} false; then : +if test "${ac_cv_search_gethostbyname_r+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -14849,11 +14821,11 @@ for ac_lib in '' socket nsl; do fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_gethostbyname_r+:} false; then : + if test "${ac_cv_search_gethostbyname_r+set}" = set; then : break fi done -if ${ac_cv_search_gethostbyname_r+:} false; then : +if test "${ac_cv_search_gethostbyname_r+set}" = set; then : else ac_cv_search_gethostbyname_r=no @@ -14927,7 +14899,7 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext ac_fn_c_check_header_mongrel "$LINENO" "byteswap.h" "ac_cv_header_byteswap_h" "$ac_includes_default" -if test "x$ac_cv_header_byteswap_h" = xyes; then : +if test "x$ac_cv_header_byteswap_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_BYTESWAP_H 1 @@ -15380,7 +15352,7 @@ rm -f core conftest.err conftest.$ac_objext \ for ac_header in sys/thr.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/thr.h" "ac_cv_header_sys_thr_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_thr_h" = xyes; then : +if test "x$ac_cv_header_sys_thr_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_THR_H 1 _ACEOF @@ -15512,8 +15484,8 @@ if test "${ac_cv_have_variable_fdset}x" = "0x"; then if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run test program while cross compiling -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "cannot run test program while cross compiling +See \`config.log' for more details." "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -16556,7 +16528,7 @@ if test "${enable_rpath+set}" = set; then : enableval=$enable_rpath; case "${enableval}" in y|ye|yes) check_rpath=yes ;; n|no) check_rpath=no ;; - *) as_fn_error $? "bad value ${enableval} for --disable-rpath" "$LINENO" 5 ;; + *) as_fn_error "bad value ${enableval} for --disable-rpath" "$LINENO" 5 ;; esac else check_rpath=yes @@ -16606,7 +16578,7 @@ rm -f core conftest.err conftest.$ac_objext \ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing res_9_ninit" >&5 $as_echo_n "checking for library containing res_9_ninit... " >&6; } -if ${ac_cv_search_res_9_ninit+:} false; then : +if test "${ac_cv_search_res_9_ninit+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -16640,11 +16612,11 @@ for ac_lib in '' resolv; do fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_res_9_ninit+:} false; then : + if test "${ac_cv_search_res_9_ninit+set}" = set; then : break fi done -if ${ac_cv_search_res_9_ninit+:} false; then : +if test "${ac_cv_search_res_9_ninit+set}" = set; then : else ac_cv_search_res_9_ninit=no @@ -16691,7 +16663,7 @@ $as_echo "#define HAVE_RES_NINIT 1" >>confdefs.h { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing res_9_ndestroy" >&5 $as_echo_n "checking for library containing res_9_ndestroy... " >&6; } -if ${ac_cv_search_res_9_ndestroy+:} false; then : +if test "${ac_cv_search_res_9_ndestroy+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -16725,11 +16697,11 @@ for ac_lib in '' resolv; do fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_res_9_ndestroy+:} false; then : + if test "${ac_cv_search_res_9_ndestroy+set}" = set; then : break fi done -if ${ac_cv_search_res_9_ndestroy+:} false; then : +if test "${ac_cv_search_res_9_ndestroy+set}" = set; then : else ac_cv_search_res_9_ndestroy=no @@ -16783,7 +16755,7 @@ rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing res_9_close" >&5 $as_echo_n "checking for library containing res_9_close... " >&6; } -if ${ac_cv_search_res_9_close+:} false; then : +if test "${ac_cv_search_res_9_close+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS @@ -16817,11 +16789,11 @@ for ac_lib in '' resolv; do fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext - if ${ac_cv_search_res_9_close+:} false; then : + if test "${ac_cv_search_res_9_close+set}" = set; then : break fi done -if ${ac_cv_search_res_9_close+:} false; then : +if test "${ac_cv_search_res_9_close+set}" = set; then : else ac_cv_search_res_9_close=no @@ -17023,7 +16995,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_fn_c_check_header_mongrel "$LINENO" "libkern/OSAtomic.h" "ac_cv_header_libkern_OSAtomic_h" "$ac_includes_default" -if test "x$ac_cv_header_libkern_OSAtomic_h" = xyes; then : +if test "x$ac_cv_header_libkern_OSAtomic_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_OSX_ATOMICS 1 @@ -17039,7 +17011,7 @@ fi # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5 $as_echo_n "checking size of int... " >&6; } -if ${ac_cv_sizeof_int+:} false; then : +if test "${ac_cv_sizeof_int+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then : @@ -17048,8 +17020,9 @@ else if test "$ac_cv_type_int" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (int) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (int) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_int=0 fi @@ -17072,7 +17045,7 @@ _ACEOF # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 $as_echo_n "checking size of long... " >&6; } -if ${ac_cv_sizeof_long+:} false; then : +if test "${ac_cv_sizeof_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : @@ -17081,8 +17054,9 @@ else if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long=0 fi @@ -17105,7 +17079,7 @@ _ACEOF # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5 $as_echo_n "checking size of long long... " >&6; } -if ${ac_cv_sizeof_long_long+:} false; then : +if test "${ac_cv_sizeof_long_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then : @@ -17114,8 +17088,9 @@ else if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long long) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long_long=0 fi @@ -17138,7 +17113,7 @@ _ACEOF # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of char *" >&5 $as_echo_n "checking size of char *... " >&6; } -if ${ac_cv_sizeof_char_p+:} false; then : +if test "${ac_cv_sizeof_char_p+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (char *))" "ac_cv_sizeof_char_p" "$ac_includes_default"; then : @@ -17147,8 +17122,9 @@ else if test "$ac_cv_type_char_p" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (char *) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (char *) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_char_p=0 fi @@ -17171,7 +17147,7 @@ _ACEOF # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5 $as_echo_n "checking size of long... " >&6; } -if ${ac_cv_sizeof_long+:} false; then : +if test "${ac_cv_sizeof_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then : @@ -17180,8 +17156,9 @@ else if test "$ac_cv_type_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long=0 fi @@ -17204,7 +17181,7 @@ _ACEOF # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5 $as_echo_n "checking size of long long... " >&6; } -if ${ac_cv_sizeof_long_long+:} false; then : +if test "${ac_cv_sizeof_long_long+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then : @@ -17213,8 +17190,9 @@ else if test "$ac_cv_type_long_long" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (long long) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (long long) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_long_long=0 fi @@ -17244,7 +17222,7 @@ fi # This bug is HP SR number 8606223364. { $as_echo "$as_me:${as_lineno-$LINENO}: checking size of fd_set.fds_bits" >&5 $as_echo_n "checking size of fd_set.fds_bits... " >&6; } -if ${ac_cv_sizeof_fd_set_fds_bits+:} false; then : +if test "${ac_cv_sizeof_fd_set_fds_bits+set}" = set; then : $as_echo_n "(cached) " >&6 else if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (fd_set.fds_bits))" "ac_cv_sizeof_fd_set_fds_bits" "$ac_includes_default"; then : @@ -17253,8 +17231,9 @@ else if test "$ac_cv_type_fd_set_fds_bits" = yes; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot compute sizeof (fd_set.fds_bits) -See \`config.log' for more details" "$LINENO" 5; } +{ as_fn_set_status 77 +as_fn_error "cannot compute sizeof (fd_set.fds_bits) +See \`config.log' for more details." "$LINENO" 5; }; } else ac_cv_sizeof_fd_set_fds_bits=0 fi @@ -17327,18 +17306,13 @@ LIBS=${old_LIBS} # PKGCONFIG is used in later tests - - - - - if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PKG_CONFIG+:} false; then : +if test "${ac_cv_path_PKG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in @@ -17352,7 +17326,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -17381,7 +17355,7 @@ if test -z "$ac_cv_path_PKG_CONFIG"; then set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : +if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in @@ -17395,7 +17369,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -17444,6 +17418,7 @@ $as_echo "yes" >&6; } $as_echo "no" >&6; } PKG_CONFIG="" fi + fi @@ -17469,7 +17444,7 @@ if test "x${PBX_ALSA}" != "x1" -a "${USE_ALSA}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_asound_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lasound" >&5 $as_echo_n "checking for ${pbxfuncname} in -lasound... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -17504,7 +17479,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ALSA_FOUND=yes else AST_ALSA_FOUND=no @@ -17527,7 +17503,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ALSA_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "alsa/asoundlib.h" "ac_cv_header_alsa_asoundlib_h" "$ac_includes_default" -if test "x$ac_cv_header_alsa_asoundlib_h" = xyes; then : +if test "x$ac_cv_header_alsa_asoundlib_h" = x""yes; then : ALSA_HEADER_FOUND=1 else ALSA_HEADER_FOUND=0 @@ -17574,7 +17550,7 @@ if test "x${PBX_BFD}" != "x1" -a "${USE_BFD}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_bfd_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lbfd" >&5 $as_echo_n "checking for ${pbxfuncname} in -lbfd... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -17609,7 +17585,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_BFD_FOUND=yes else AST_BFD_FOUND=no @@ -17632,7 +17609,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${BFD_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "bfd.h" "ac_cv_header_bfd_h" "$ac_includes_default" -if test "x$ac_cv_header_bfd_h" = xyes; then : +if test "x$ac_cv_header_bfd_h" = x""yes; then : BFD_HEADER_FOUND=1 else BFD_HEADER_FOUND=0 @@ -17681,7 +17658,7 @@ if test "x${PBX_BFD}" != "x1" -a "${USE_BFD}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_bfd_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lbfd" >&5 $as_echo_n "checking for ${pbxfuncname} in -lbfd... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -17716,7 +17693,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_BFD_FOUND=yes else AST_BFD_FOUND=no @@ -17739,7 +17717,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${BFD_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "bfd.h" "ac_cv_header_bfd_h" "$ac_includes_default" -if test "x$ac_cv_header_bfd_h" = xyes; then : +if test "x$ac_cv_header_bfd_h" = x""yes; then : BFD_HEADER_FOUND=1 else BFD_HEADER_FOUND=0 @@ -17788,7 +17766,7 @@ if test "x${PBX_CAP}" != "x1" -a "${USE_CAP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_cap_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcap" >&5 $as_echo_n "checking for ${pbxfuncname} in -lcap... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -17823,7 +17801,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_CAP_FOUND=yes else AST_CAP_FOUND=no @@ -17846,7 +17825,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${CAP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sys/capability.h" "ac_cv_header_sys_capability_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_capability_h" = xyes; then : +if test "x$ac_cv_header_sys_capability_h" = x""yes; then : CAP_HEADER_FOUND=1 else CAP_HEADER_FOUND=0 @@ -18269,7 +18248,7 @@ if test "${USE_GSM}" != "no"; then fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for gsm_create in -lgsm" >&5 $as_echo_n "checking for gsm_create in -lgsm... " >&6; } -if ${ac_cv_lib_gsm_gsm_create+:} false; then : +if test "${ac_cv_lib_gsm_gsm_create+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -18303,7 +18282,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gsm_gsm_create" >&5 $as_echo "$ac_cv_lib_gsm_gsm_create" >&6; } -if test "x$ac_cv_lib_gsm_gsm_create" = xyes; then : +if test "x$ac_cv_lib_gsm_gsm_create" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_GSM 1 @@ -18315,7 +18294,8 @@ fi if test "x${GSM_DIR}" != "x" ; then as_ac_Header=`$as_echo "ac_cv_header_${GSM_DIR}/include/gsm.h" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "${GSM_DIR}/include/gsm.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : GSM_HEADER_FOUND=1 else GSM_HEADER_FOUND=0 @@ -18324,7 +18304,8 @@ fi as_ac_Header=`$as_echo "ac_cv_header_${GSM_DIR}/include/gsm/gsm.h" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "${GSM_DIR}/include/gsm/gsm.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : GSM_GSM_HEADER_FOUND=1 else GSM_GSM_HEADER_FOUND=0 @@ -18333,7 +18314,7 @@ fi else ac_fn_c_check_header_mongrel "$LINENO" "gsm.h" "ac_cv_header_gsm_h" "$ac_includes_default" -if test "x$ac_cv_header_gsm_h" = xyes; then : +if test "x$ac_cv_header_gsm_h" = x""yes; then : GSM_HEADER_FOUND=1 else GSM_HEADER_FOUND=0 @@ -18341,7 +18322,7 @@ fi ac_fn_c_check_header_mongrel "$LINENO" "gsm/gsm.h" "ac_cv_header_gsm_gsm_h" "$ac_includes_default" -if test "x$ac_cv_header_gsm_gsm_h" = xyes; then : +if test "x$ac_cv_header_gsm_gsm_h" = x""yes; then : GSM_GSM_HEADER_FOUND=1 else GSM_GSM_HEADER_FOUND=0 @@ -18420,46 +18401,46 @@ pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ILBC" >&5 $as_echo_n "checking for ILBC... " >&6; } -if test -n "$ILBC_CFLAGS"; then - pkg_cv_ILBC_CFLAGS="$ILBC_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$ILBC_CFLAGS"; then + pkg_cv_ILBC_CFLAGS="$ILBC_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libilbc\""; } >&5 ($PKG_CONFIG --exists --print-errors "libilbc") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ILBC_CFLAGS=`$PKG_CONFIG --cflags "libilbc" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi -if test -n "$ILBC_LIBS"; then - pkg_cv_ILBC_LIBS="$ILBC_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$ILBC_LIBS"; then + pkg_cv_ILBC_LIBS="$ILBC_LIBS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libilbc\""; } >&5 ($PKG_CONFIG --exists --print-errors "libilbc") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_ILBC_LIBS=`$PKG_CONFIG --libs "libilbc" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes @@ -18467,20 +18448,20 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - ILBC_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libilbc" 2>&1` + ILBC_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libilbc"` else - ILBC_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libilbc" 2>&1` + ILBC_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libilbc"` fi # Put the nasty error message in config.log where it belongs echo "$ILBC_PKG_ERRORS" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } PBX_ILBC=0 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } PBX_ILBC=0 @@ -18527,46 +18508,46 @@ pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBEDIT" >&5 $as_echo_n "checking for LIBEDIT... " >&6; } -if test -n "$LIBEDIT_CFLAGS"; then - pkg_cv_LIBEDIT_CFLAGS="$LIBEDIT_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$LIBEDIT_CFLAGS"; then + pkg_cv_LIBEDIT_CFLAGS="$LIBEDIT_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5 ($PKG_CONFIG --exists --print-errors "libedit") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEDIT_CFLAGS=`$PKG_CONFIG --cflags "libedit" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi -if test -n "$LIBEDIT_LIBS"; then - pkg_cv_LIBEDIT_LIBS="$LIBEDIT_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$LIBEDIT_LIBS"; then + pkg_cv_LIBEDIT_LIBS="$LIBEDIT_LIBS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libedit\""; } >&5 ($PKG_CONFIG --exists --print-errors "libedit") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_LIBEDIT_LIBS=`$PKG_CONFIG --libs "libedit" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes @@ -18574,20 +18555,20 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libedit" 2>&1` + LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libedit"` else - LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libedit" 2>&1` + LIBEDIT_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libedit"` fi # Put the nasty error message in config.log where it belongs echo "$LIBEDIT_PKG_ERRORS" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } PBX_LIBEDIT=0 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } PBX_LIBEDIT=0 @@ -18637,7 +18618,7 @@ if test "x${PBX_ICONV}" != "x1" -a "${USE_ICONV}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_iconv_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -liconv" >&5 $as_echo_n "checking for ${pbxfuncname} in -liconv... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -18672,7 +18653,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ICONV_FOUND=yes else AST_ICONV_FOUND=no @@ -18695,7 +18677,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ICONV_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "iconv.h" "ac_cv_header_iconv_h" "$ac_includes_default" -if test "x$ac_cv_header_iconv_h" = xyes; then : +if test "x$ac_cv_header_iconv_h" = x""yes; then : ICONV_HEADER_FOUND=1 else ICONV_HEADER_FOUND=0 @@ -18742,7 +18724,7 @@ if test "x${PBX_ICONV}" != "x1" -a "${USE_ICONV}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_iconv_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -liconv" >&5 $as_echo_n "checking for ${pbxfuncname} in -liconv... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -18777,7 +18759,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ICONV_FOUND=yes else AST_ICONV_FOUND=no @@ -18800,7 +18783,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ICONV_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "iconv.h" "ac_cv_header_iconv_h" "$ac_includes_default" -if test "x$ac_cv_header_iconv_h" = xyes; then : +if test "x$ac_cv_header_iconv_h" = x""yes; then : ICONV_HEADER_FOUND=1 else ICONV_HEADER_FOUND=0 @@ -18847,7 +18830,7 @@ if test "x${PBX_ICONV}" != "x1" -a "${USE_ICONV}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_c_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lc" >&5 $as_echo_n "checking for ${pbxfuncname} in -lc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -18882,7 +18865,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ICONV_FOUND=yes else AST_ICONV_FOUND=no @@ -18905,7 +18889,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ICONV_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "iconv.h" "ac_cv_header_iconv_h" "$ac_includes_default" -if test "x$ac_cv_header_iconv_h" = xyes; then : +if test "x$ac_cv_header_iconv_h" = x""yes; then : ICONV_HEADER_FOUND=1 else ICONV_HEADER_FOUND=0 @@ -18953,7 +18937,7 @@ if test "x${PBX_ICAL}" != "x1" -a "${USE_ICAL}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ical_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lical" >&5 $as_echo_n "checking for ${pbxfuncname} in -lical... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -18988,7 +18972,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ICAL_FOUND=yes else AST_ICAL_FOUND=no @@ -19011,7 +18996,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ICAL_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libical/ical.h" "ac_cv_header_libical_ical_h" "$ac_includes_default" -if test "x$ac_cv_header_libical_ical_h" = xyes; then : +if test "x$ac_cv_header_libical_ical_h" = x""yes; then : ICAL_HEADER_FOUND=1 else ICAL_HEADER_FOUND=0 @@ -19058,7 +19043,7 @@ if test "x${PBX_IKSEMEL}" != "x1" -a "${USE_IKSEMEL}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_iksemel_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -liksemel" >&5 $as_echo_n "checking for ${pbxfuncname} in -liksemel... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -19093,7 +19078,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_IKSEMEL_FOUND=yes else AST_IKSEMEL_FOUND=no @@ -19116,7 +19102,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${IKSEMEL_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "iksemel.h" "ac_cv_header_iksemel_h" "$ac_includes_default" -if test "x$ac_cv_header_iksemel_h" = xyes; then : +if test "x$ac_cv_header_iksemel_h" = x""yes; then : IKSEMEL_HEADER_FOUND=1 else IKSEMEL_HEADER_FOUND=0 @@ -19791,7 +19777,7 @@ if test "x${PBX_IODBC}" != "x1" -a "${USE_IODBC}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_iodbc_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -liodbc" >&5 $as_echo_n "checking for ${pbxfuncname} in -liodbc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -19826,7 +19812,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_IODBC_FOUND=yes else AST_IODBC_FOUND=no @@ -19849,7 +19836,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${IODBC_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sql.h" "ac_cv_header_sql_h" "$ac_includes_default" -if test "x$ac_cv_header_sql_h" = xyes; then : +if test "x$ac_cv_header_sql_h" = x""yes; then : IODBC_HEADER_FOUND=1 else IODBC_HEADER_FOUND=0 @@ -19896,7 +19883,7 @@ if test "x${PBX_INOTIFY}" != "x1" -a "${USE_INOTIFY}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_c_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lc" >&5 $as_echo_n "checking for ${pbxfuncname} in -lc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -19931,7 +19918,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_INOTIFY_FOUND=yes else AST_INOTIFY_FOUND=no @@ -19954,7 +19942,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${INOTIFY_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sys/inotify.h" "ac_cv_header_sys_inotify_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_inotify_h" = xyes; then : +if test "x$ac_cv_header_sys_inotify_h" = x""yes; then : INOTIFY_HEADER_FOUND=1 else INOTIFY_HEADER_FOUND=0 @@ -20001,7 +19989,7 @@ if test "x${PBX_JACK}" != "x1" -a "${USE_JACK}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_jack_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -ljack" >&5 $as_echo_n "checking for ${pbxfuncname} in -ljack... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20036,7 +20024,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_JACK_FOUND=yes else AST_JACK_FOUND=no @@ -20059,7 +20048,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${JACK_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "jack/jack.h" "ac_cv_header_jack_jack_h" "$ac_includes_default" -if test "x$ac_cv_header_jack_jack_h" = xyes; then : +if test "x$ac_cv_header_jack_jack_h" = x""yes; then : JACK_HEADER_FOUND=1 else JACK_HEADER_FOUND=0 @@ -20107,7 +20096,7 @@ if test "x${PBX_KQUEUE}" != "x1" -a "${USE_KQUEUE}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_c_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lc" >&5 $as_echo_n "checking for ${pbxfuncname} in -lc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20142,7 +20131,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_KQUEUE_FOUND=yes else AST_KQUEUE_FOUND=no @@ -20165,7 +20155,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${KQUEUE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sys/event.h" "ac_cv_header_sys_event_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_event_h" = xyes; then : +if test "x$ac_cv_header_sys_event_h" = x""yes; then : KQUEUE_HEADER_FOUND=1 else KQUEUE_HEADER_FOUND=0 @@ -20196,7 +20186,7 @@ fi for ac_func in kevent64 do : ac_fn_c_check_func "$LINENO" "kevent64" "ac_cv_func_kevent64" -if test "x$ac_cv_func_kevent64" = xyes; then : +if test "x$ac_cv_func_kevent64" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_KEVENT64 1 _ACEOF @@ -20226,7 +20216,7 @@ if test "x${PBX_LTDL}" != "x1" -a "${USE_LTDL}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ltdl_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lltdl" >&5 $as_echo_n "checking for ${pbxfuncname} in -lltdl... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20261,7 +20251,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_LTDL_FOUND=yes else AST_LTDL_FOUND=no @@ -20284,7 +20275,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${LTDL_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "ltdl.h" "ac_cv_header_ltdl_h" "$ac_includes_default" -if test "x$ac_cv_header_ltdl_h" = xyes; then : +if test "x$ac_cv_header_ltdl_h" = x""yes; then : LTDL_HEADER_FOUND=1 else LTDL_HEADER_FOUND=0 @@ -20331,7 +20322,7 @@ if test "x${PBX_LDAP}" != "x1" -a "${USE_LDAP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ldap_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lldap" >&5 $as_echo_n "checking for ${pbxfuncname} in -lldap... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20366,7 +20357,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_LDAP_FOUND=yes else AST_LDAP_FOUND=no @@ -20389,7 +20381,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${LDAP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "ldap.h" "ac_cv_header_ldap_h" "$ac_includes_default" -if test "x$ac_cv_header_ldap_h" = xyes; then : +if test "x$ac_cv_header_ldap_h" = x""yes; then : LDAP_HEADER_FOUND=1 else LDAP_HEADER_FOUND=0 @@ -20436,7 +20428,7 @@ if test "x${PBX_MISDN}" != "x1" -a "${USE_MISDN}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_mISDN_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lmISDN" >&5 $as_echo_n "checking for ${pbxfuncname} in -lmISDN... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20471,7 +20463,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_MISDN_FOUND=yes else AST_MISDN_FOUND=no @@ -20494,7 +20487,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${MISDN_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "mISDNuser/mISDNlib.h" "ac_cv_header_mISDNuser_mISDNlib_h" "$ac_includes_default" -if test "x$ac_cv_header_mISDNuser_mISDNlib_h" = xyes; then : +if test "x$ac_cv_header_mISDNuser_mISDNlib_h" = x""yes; then : MISDN_HEADER_FOUND=1 else MISDN_HEADER_FOUND=0 @@ -20542,7 +20535,7 @@ if test "x${PBX_ISDNNET}" != "x1" -a "${USE_ISDNNET}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_isdnnet_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lisdnnet" >&5 $as_echo_n "checking for ${pbxfuncname} in -lisdnnet... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20577,7 +20570,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ISDNNET_FOUND=yes else AST_ISDNNET_FOUND=no @@ -20600,7 +20594,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ISDNNET_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "mISDNuser/isdn_net.h" "ac_cv_header_mISDNuser_isdn_net_h" "$ac_includes_default" -if test "x$ac_cv_header_mISDNuser_isdn_net_h" = xyes; then : +if test "x$ac_cv_header_mISDNuser_isdn_net_h" = x""yes; then : ISDNNET_HEADER_FOUND=1 else ISDNNET_HEADER_FOUND=0 @@ -20646,7 +20640,7 @@ if test "x${PBX_SUPPSERV}" != "x1" -a "${USE_SUPPSERV}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_suppserv_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsuppserv" >&5 $as_echo_n "checking for ${pbxfuncname} in -lsuppserv... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -20681,7 +20675,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SUPPSERV_FOUND=yes else AST_SUPPSERV_FOUND=no @@ -20704,7 +20699,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SUPPSERV_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "mISDNuser/suppserv.h" "ac_cv_header_mISDNuser_suppserv_h" "$ac_includes_default" -if test "x$ac_cv_header_mISDNuser_suppserv_h" = xyes; then : +if test "x$ac_cv_header_mISDNuser_suppserv_h" = x""yes; then : SUPPSERV_HEADER_FOUND=1 else SUPPSERV_HEADER_FOUND=0 @@ -20823,7 +20818,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_fn_c_check_header_mongrel "$LINENO" "linux/mISDNdsp.h" "ac_cv_header_linux_mISDNdsp_h" "$ac_includes_default" -if test "x$ac_cv_header_linux_mISDNdsp_h" = xyes; then : +if test "x$ac_cv_header_linux_mISDNdsp_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define MISDN_1_2 1 @@ -20834,7 +20829,7 @@ fi ac_fn_c_check_member "$LINENO" "Q931_info_t" "redirect_dn" "ac_cv_member_Q931_info_t_redirect_dn" "#include <mISDNuser/mISDNlib.h> " -if test "x$ac_cv_member_Q931_info_t_redirect_dn" = xyes; then : +if test "x$ac_cv_member_Q931_info_t_redirect_dn" = x""yes; then : else PBX_MISDN=0 @@ -20850,7 +20845,7 @@ fi set dummy ${ac_tool_prefix}mysql_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CONFIG_MYSQLCLIENT+:} false; then : +if test "${ac_cv_path_CONFIG_MYSQLCLIENT+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CONFIG_MYSQLCLIENT in @@ -20865,7 +20860,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CONFIG_MYSQLCLIENT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -20894,7 +20889,7 @@ if test -z "$ac_cv_path_CONFIG_MYSQLCLIENT"; then set dummy mysql_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_CONFIG_MYSQLCLIENT+:} false; then : +if test "${ac_cv_path_ac_pt_CONFIG_MYSQLCLIENT+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_CONFIG_MYSQLCLIENT in @@ -20909,7 +20904,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_CONFIG_MYSQLCLIENT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21013,7 +21008,7 @@ if test "x${PBX_NBS}" != "x1" -a "${USE_NBS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_nbs_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lnbs" >&5 $as_echo_n "checking for ${pbxfuncname} in -lnbs... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -21048,7 +21043,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_NBS_FOUND=yes else AST_NBS_FOUND=no @@ -21071,7 +21067,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${NBS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "nbs.h" "ac_cv_header_nbs_h" "$ac_includes_default" -if test "x$ac_cv_header_nbs_h" = xyes; then : +if test "x$ac_cv_header_nbs_h" = x""yes; then : NBS_HEADER_FOUND=1 else NBS_HEADER_FOUND=0 @@ -21106,7 +21102,7 @@ fi set dummy ${ac_tool_prefix}neon-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CONFIG_NEON+:} false; then : +if test "${ac_cv_path_CONFIG_NEON+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CONFIG_NEON in @@ -21121,7 +21117,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CONFIG_NEON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21150,7 +21146,7 @@ if test -z "$ac_cv_path_CONFIG_NEON"; then set dummy neon-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_CONFIG_NEON+:} false; then : +if test "${ac_cv_path_ac_pt_CONFIG_NEON+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_CONFIG_NEON in @@ -21165,7 +21161,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_CONFIG_NEON="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21257,7 +21253,7 @@ $as_echo "#define HAVE_NEON 1" >>confdefs.h set dummy ${ac_tool_prefix}neon-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CONFIG_NEON29+:} false; then : +if test "${ac_cv_path_CONFIG_NEON29+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CONFIG_NEON29 in @@ -21272,7 +21268,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CONFIG_NEON29="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21301,7 +21297,7 @@ if test -z "$ac_cv_path_CONFIG_NEON29"; then set dummy neon-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_CONFIG_NEON29+:} false; then : +if test "${ac_cv_path_ac_pt_CONFIG_NEON29+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_CONFIG_NEON29 in @@ -21316,7 +21312,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_CONFIG_NEON29="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21410,7 +21406,7 @@ $as_echo "#define HAVE_NEON29 1" >>confdefs.h set dummy ${ac_tool_prefix}net-snmp-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CONFIG_NETSNMP+:} false; then : +if test "${ac_cv_path_CONFIG_NETSNMP+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CONFIG_NETSNMP in @@ -21425,7 +21421,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CONFIG_NETSNMP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21454,7 +21450,7 @@ if test -z "$ac_cv_path_CONFIG_NETSNMP"; then set dummy net-snmp-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_CONFIG_NETSNMP+:} false; then : +if test "${ac_cv_path_ac_pt_CONFIG_NETSNMP+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_CONFIG_NETSNMP in @@ -21469,7 +21465,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_CONFIG_NETSNMP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -21577,7 +21573,7 @@ if test "x${PBX_NEWT}" != "x1" -a "${USE_NEWT}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_newt_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lnewt" >&5 $as_echo_n "checking for ${pbxfuncname} in -lnewt... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -21612,7 +21608,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_NEWT_FOUND=yes else AST_NEWT_FOUND=no @@ -21635,7 +21632,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${NEWT_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "newt.h" "ac_cv_header_newt_h" "$ac_includes_default" -if test "x$ac_cv_header_newt_h" = xyes; then : +if test "x$ac_cv_header_newt_h" = x""yes; then : NEWT_HEADER_FOUND=1 else NEWT_HEADER_FOUND=0 @@ -21682,7 +21679,7 @@ if test "x${PBX_UNIXODBC}" != "x1" -a "${USE_UNIXODBC}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_odbc_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lodbc" >&5 $as_echo_n "checking for ${pbxfuncname} in -lodbc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -21717,7 +21714,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_UNIXODBC_FOUND=yes else AST_UNIXODBC_FOUND=no @@ -21740,7 +21738,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${UNIXODBC_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sql.h" "ac_cv_header_sql_h" "$ac_includes_default" -if test "x$ac_cv_header_sql_h" = xyes; then : +if test "x$ac_cv_header_sql_h" = x""yes; then : UNIXODBC_HEADER_FOUND=1 else UNIXODBC_HEADER_FOUND=0 @@ -21787,7 +21785,7 @@ if test "x${PBX_OGG}" != "x1" -a "${USE_OGG}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ogg_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -logg" >&5 $as_echo_n "checking for ${pbxfuncname} in -logg... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -21822,7 +21820,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OGG_FOUND=yes else AST_OGG_FOUND=no @@ -21845,7 +21844,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OGG_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" -if test "x$ac_cv_header_" = xyes; then : +if test "x$ac_cv_header_" = x""yes; then : OGG_HEADER_FOUND=1 else OGG_HEADER_FOUND=0 @@ -21893,7 +21892,7 @@ if test "x${PBX_BKTR}" != "x1" -a "${USE_BKTR}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_execinfo_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lexecinfo" >&5 $as_echo_n "checking for ${pbxfuncname} in -lexecinfo... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -21928,7 +21927,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_BKTR_FOUND=yes else AST_BKTR_FOUND=no @@ -21951,7 +21951,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${BKTR_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "execinfo.h" "ac_cv_header_execinfo_h" "$ac_includes_default" -if test "x$ac_cv_header_execinfo_h" = xyes; then : +if test "x$ac_cv_header_execinfo_h" = x""yes; then : BKTR_HEADER_FOUND=1 else BKTR_HEADER_FOUND=0 @@ -21998,7 +21998,7 @@ if test "x${PBX_BKTR}" != "x1" -a "${USE_BKTR}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_c_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lc" >&5 $as_echo_n "checking for ${pbxfuncname} in -lc... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22033,7 +22033,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_BKTR_FOUND=yes else AST_BKTR_FOUND=no @@ -22056,7 +22057,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${BKTR_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "execinfo.h" "ac_cv_header_execinfo_h" "$ac_includes_default" -if test "x$ac_cv_header_execinfo_h" = xyes; then : +if test "x$ac_cv_header_execinfo_h" = x""yes; then : BKTR_HEADER_FOUND=1 else BKTR_HEADER_FOUND=0 @@ -22103,7 +22104,7 @@ if test "x${PBX_BLUETOOTH}" != "x1" -a "${USE_BLUETOOTH}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_bluetooth_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lbluetooth" >&5 $as_echo_n "checking for ${pbxfuncname} in -lbluetooth... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22138,7 +22139,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_BLUETOOTH_FOUND=yes else AST_BLUETOOTH_FOUND=no @@ -22161,7 +22163,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${BLUETOOTH_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "bluetooth/bluetooth.h" "ac_cv_header_bluetooth_bluetooth_h" "$ac_includes_default" -if test "x$ac_cv_header_bluetooth_bluetooth_h" = xyes; then : +if test "x$ac_cv_header_bluetooth_bluetooth_h" = x""yes; then : BLUETOOTH_HEADER_FOUND=1 else BLUETOOTH_HEADER_FOUND=0 @@ -22209,7 +22211,7 @@ if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ossaudio_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lossaudio" >&5 $as_echo_n "checking for ${pbxfuncname} in -lossaudio... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22244,7 +22246,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OSS_FOUND=yes else AST_OSS_FOUND=no @@ -22267,7 +22270,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OSS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "linux/soundcard.h" "ac_cv_header_linux_soundcard_h" "$ac_includes_default" -if test "x$ac_cv_header_linux_soundcard_h" = xyes; then : +if test "x$ac_cv_header_linux_soundcard_h" = x""yes; then : OSS_HEADER_FOUND=1 else OSS_HEADER_FOUND=0 @@ -22313,7 +22316,7 @@ if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ossaudio_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lossaudio" >&5 $as_echo_n "checking for ${pbxfuncname} in -lossaudio... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22348,7 +22351,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OSS_FOUND=yes else AST_OSS_FOUND=no @@ -22371,7 +22375,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OSS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sys/soundcard.h" "ac_cv_header_sys_soundcard_h" "$ac_includes_default" -if test "x$ac_cv_header_sys_soundcard_h" = xyes; then : +if test "x$ac_cv_header_sys_soundcard_h" = x""yes; then : OSS_HEADER_FOUND=1 else OSS_HEADER_FOUND=0 @@ -22417,7 +22421,7 @@ if test "x${PBX_OSS}" != "x1" -a "${USE_OSS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ossaudio_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lossaudio" >&5 $as_echo_n "checking for ${pbxfuncname} in -lossaudio... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22452,7 +22456,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OSS_FOUND=yes else AST_OSS_FOUND=no @@ -22475,7 +22480,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OSS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "soundcard.h" "ac_cv_header_soundcard_h" "$ac_includes_default" -if test "x$ac_cv_header_soundcard_h" = xyes; then : +if test "x$ac_cv_header_soundcard_h" = x""yes; then : OSS_HEADER_FOUND=1 else OSS_HEADER_FOUND=0 @@ -22510,7 +22515,7 @@ if test "${USE_PGSQL}" != "no"; then set dummy ${ac_tool_prefix}pg_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PG_CONFIG+:} false; then : +if test "${ac_cv_path_PG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PG_CONFIG in @@ -22524,7 +22529,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -22553,7 +22558,7 @@ if test -z "$ac_cv_path_PG_CONFIG"; then set dummy pg_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_PG_CONFIG+:} false; then : +if test "${ac_cv_path_ac_pt_PG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PG_CONFIG in @@ -22567,7 +22572,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -22622,7 +22627,7 @@ $as_echo "$as_me: *** including --without-postgres" >&6;} set dummy ${ac_tool_prefix}pg_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PG_CONFIG+:} false; then : +if test "${ac_cv_path_PG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PG_CONFIG in @@ -22636,7 +22641,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -22665,7 +22670,7 @@ if test -z "$ac_cv_path_PG_CONFIG"; then set dummy pg_config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_PG_CONFIG+:} false; then : +if test "${ac_cv_path_ac_pt_PG_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PG_CONFIG in @@ -22679,7 +22684,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -22735,7 +22740,7 @@ $as_echo "$as_me: *** including --without-postgres" >&6;} else { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PQescapeStringConn in -lpq" >&5 $as_echo_n "checking for PQescapeStringConn in -lpq... " >&6; } -if ${ac_cv_lib_pq_PQescapeStringConn+:} false; then : +if test "${ac_cv_lib_pq_PQescapeStringConn+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22769,7 +22774,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pq_PQescapeStringConn" >&5 $as_echo "$ac_cv_lib_pq_PQescapeStringConn" >&6; } -if test "x$ac_cv_lib_pq_PQescapeStringConn" = xyes; then : +if test "x$ac_cv_lib_pq_PQescapeStringConn" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PGSQL 1 @@ -22838,46 +22843,46 @@ pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PJPROJECT" >&5 $as_echo_n "checking for PJPROJECT... " >&6; } -if test -n "$PJPROJECT_CFLAGS"; then - pkg_cv_PJPROJECT_CFLAGS="$PJPROJECT_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$PJPROJECT_CFLAGS"; then + pkg_cv_PJPROJECT_CFLAGS="$PJPROJECT_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpjproject\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpjproject") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PJPROJECT_CFLAGS=`$PKG_CONFIG --cflags "libpjproject" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi -if test -n "$PJPROJECT_LIBS"; then - pkg_cv_PJPROJECT_LIBS="$PJPROJECT_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$PJPROJECT_LIBS"; then + pkg_cv_PJPROJECT_LIBS="$PJPROJECT_LIBS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libpjproject\""; } >&5 ($PKG_CONFIG --exists --print-errors "libpjproject") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_PJPROJECT_LIBS=`$PKG_CONFIG --libs "libpjproject" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes @@ -22885,20 +22890,20 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - PJPROJECT_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libpjproject" 2>&1` + PJPROJECT_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "libpjproject"` else - PJPROJECT_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libpjproject" 2>&1` + PJPROJECT_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "libpjproject"` fi # Put the nasty error message in config.log where it belongs echo "$PJPROJECT_PKG_ERRORS" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } PBX_PJPROJECT=0 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } PBX_PJPROJECT=0 @@ -22940,7 +22945,7 @@ if test "x${PBX_POPT}" != "x1" -a "${USE_POPT}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_popt_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpopt" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpopt... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -22975,7 +22980,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_POPT_FOUND=yes else AST_POPT_FOUND=no @@ -22998,7 +23004,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${POPT_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "popt.h" "ac_cv_header_popt_h" "$ac_includes_default" -if test "x$ac_cv_header_popt_h" = xyes; then : +if test "x$ac_cv_header_popt_h" = x""yes; then : POPT_HEADER_FOUND=1 else POPT_HEADER_FOUND=0 @@ -23045,7 +23051,7 @@ if test "x${PBX_PORTAUDIO}" != "x1" -a "${USE_PORTAUDIO}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_portaudio_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lportaudio" >&5 $as_echo_n "checking for ${pbxfuncname} in -lportaudio... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23080,7 +23086,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PORTAUDIO_FOUND=yes else AST_PORTAUDIO_FOUND=no @@ -23103,7 +23110,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PORTAUDIO_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "portaudio.h" "ac_cv_header_portaudio_h" "$ac_includes_default" -if test "x$ac_cv_header_portaudio_h" = xyes; then : +if test "x$ac_cv_header_portaudio_h" = x""yes; then : PORTAUDIO_HEADER_FOUND=1 else PORTAUDIO_HEADER_FOUND=0 @@ -23150,7 +23157,7 @@ if test "x${PBX_PRI}" != "x1" -a "${USE_PRI}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23185,7 +23192,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_FOUND=yes else AST_PRI_FOUND=no @@ -23208,7 +23216,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_HEADER_FOUND=1 else PRI_HEADER_FOUND=0 @@ -23254,7 +23262,7 @@ if test "x${PBX_PRI_L2_PERSISTENCE}" != "x1" -a "${USE_PRI_L2_PERSISTENCE}" != " as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23289,7 +23297,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_L2_PERSISTENCE_FOUND=yes else AST_PRI_L2_PERSISTENCE_FOUND=no @@ -23312,7 +23321,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_L2_PERSISTENCE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_L2_PERSISTENCE_HEADER_FOUND=1 else PRI_L2_PERSISTENCE_HEADER_FOUND=0 @@ -23358,7 +23367,7 @@ if test "x${PBX_PRI_DATETIME_SEND}" != "x1" -a "${USE_PRI_DATETIME_SEND}" != "no as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23393,7 +23402,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_DATETIME_SEND_FOUND=yes else AST_PRI_DATETIME_SEND_FOUND=no @@ -23416,7 +23426,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_DATETIME_SEND_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_DATETIME_SEND_HEADER_FOUND=1 else PRI_DATETIME_SEND_HEADER_FOUND=0 @@ -23462,7 +23472,7 @@ if test "x${PBX_PRI_MWI_V2}" != "x1" -a "${USE_PRI_MWI_V2}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23497,7 +23507,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_MWI_V2_FOUND=yes else AST_PRI_MWI_V2_FOUND=no @@ -23520,7 +23531,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_MWI_V2_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_MWI_V2_HEADER_FOUND=1 else PRI_MWI_V2_HEADER_FOUND=0 @@ -23566,7 +23577,7 @@ if test "x${PBX_PRI_DISPLAY_TEXT}" != "x1" -a "${USE_PRI_DISPLAY_TEXT}" != "no"; as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23601,7 +23612,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_DISPLAY_TEXT_FOUND=yes else AST_PRI_DISPLAY_TEXT_FOUND=no @@ -23624,7 +23636,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_DISPLAY_TEXT_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_DISPLAY_TEXT_HEADER_FOUND=1 else PRI_DISPLAY_TEXT_HEADER_FOUND=0 @@ -23670,7 +23682,7 @@ if test "x${PBX_PRI_MWI}" != "x1" -a "${USE_PRI_MWI}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23705,7 +23717,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_MWI_FOUND=yes else AST_PRI_MWI_FOUND=no @@ -23728,7 +23741,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_MWI_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_MWI_HEADER_FOUND=1 else PRI_MWI_HEADER_FOUND=0 @@ -23774,7 +23787,7 @@ if test "x${PBX_PRI_MCID}" != "x1" -a "${USE_PRI_MCID}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23809,7 +23822,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_MCID_FOUND=yes else AST_PRI_MCID_FOUND=no @@ -23832,7 +23846,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_MCID_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_MCID_HEADER_FOUND=1 else PRI_MCID_HEADER_FOUND=0 @@ -23878,7 +23892,7 @@ if test "x${PBX_PRI_CALL_WAITING}" != "x1" -a "${USE_PRI_CALL_WAITING}" != "no"; as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -23913,7 +23927,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_CALL_WAITING_FOUND=yes else AST_PRI_CALL_WAITING_FOUND=no @@ -23936,7 +23951,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_CALL_WAITING_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_CALL_WAITING_HEADER_FOUND=1 else PRI_CALL_WAITING_HEADER_FOUND=0 @@ -23982,7 +23997,7 @@ if test "x${PBX_PRI_AOC_EVENTS}" != "x1" -a "${USE_PRI_AOC_EVENTS}" != "no"; the as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24017,7 +24032,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_AOC_EVENTS_FOUND=yes else AST_PRI_AOC_EVENTS_FOUND=no @@ -24040,7 +24056,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_AOC_EVENTS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_AOC_EVENTS_HEADER_FOUND=1 else PRI_AOC_EVENTS_HEADER_FOUND=0 @@ -24086,7 +24102,7 @@ if test "x${PBX_PRI_TRANSFER}" != "x1" -a "${USE_PRI_TRANSFER}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24121,7 +24137,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_TRANSFER_FOUND=yes else AST_PRI_TRANSFER_FOUND=no @@ -24144,7 +24161,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_TRANSFER_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_TRANSFER_HEADER_FOUND=1 else PRI_TRANSFER_HEADER_FOUND=0 @@ -24190,7 +24207,7 @@ if test "x${PBX_PRI_CCSS}" != "x1" -a "${USE_PRI_CCSS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24225,7 +24242,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_CCSS_FOUND=yes else AST_PRI_CCSS_FOUND=no @@ -24248,7 +24266,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_CCSS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_CCSS_HEADER_FOUND=1 else PRI_CCSS_HEADER_FOUND=0 @@ -24294,7 +24312,7 @@ if test "x${PBX_PRI_HANGUP_FIX}" != "x1" -a "${USE_PRI_HANGUP_FIX}" != "no"; the as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24329,7 +24347,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_HANGUP_FIX_FOUND=yes else AST_PRI_HANGUP_FIX_FOUND=no @@ -24352,7 +24371,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_HANGUP_FIX_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_HANGUP_FIX_HEADER_FOUND=1 else PRI_HANGUP_FIX_HEADER_FOUND=0 @@ -24398,7 +24417,7 @@ if test "x${PBX_PRI_SUBADDR}" != "x1" -a "${USE_PRI_SUBADDR}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24433,7 +24452,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_SUBADDR_FOUND=yes else AST_PRI_SUBADDR_FOUND=no @@ -24456,7 +24476,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_SUBADDR_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_SUBADDR_HEADER_FOUND=1 else PRI_SUBADDR_HEADER_FOUND=0 @@ -24502,7 +24522,7 @@ if test "x${PBX_PRI_CALL_HOLD}" != "x1" -a "${USE_PRI_CALL_HOLD}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24537,7 +24557,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_CALL_HOLD_FOUND=yes else AST_PRI_CALL_HOLD_FOUND=no @@ -24560,7 +24581,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_CALL_HOLD_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_CALL_HOLD_HEADER_FOUND=1 else PRI_CALL_HOLD_HEADER_FOUND=0 @@ -24606,7 +24627,7 @@ if test "x${PBX_PRI_CALL_REROUTING}" != "x1" -a "${USE_PRI_CALL_REROUTING}" != " as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24641,7 +24662,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_CALL_REROUTING_FOUND=yes else AST_PRI_CALL_REROUTING_FOUND=no @@ -24664,7 +24686,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_CALL_REROUTING_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_CALL_REROUTING_HEADER_FOUND=1 else PRI_CALL_REROUTING_HEADER_FOUND=0 @@ -24710,7 +24732,7 @@ if test "x${PBX_PRI_SETUP_KEYPAD}" != "x1" -a "${USE_PRI_SETUP_KEYPAD}" != "no"; as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24745,7 +24767,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_SETUP_KEYPAD_FOUND=yes else AST_PRI_SETUP_KEYPAD_FOUND=no @@ -24768,7 +24791,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_SETUP_KEYPAD_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_SETUP_KEYPAD_HEADER_FOUND=1 else PRI_SETUP_KEYPAD_HEADER_FOUND=0 @@ -24818,7 +24841,7 @@ if test "x${PBX_PRI_PROG_W_CAUSE}" != "x1" -a "${USE_PRI_PROG_W_CAUSE}" != "no"; as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24853,7 +24876,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_PROG_W_CAUSE_FOUND=yes else AST_PRI_PROG_W_CAUSE_FOUND=no @@ -24876,7 +24900,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_PROG_W_CAUSE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_PROG_W_CAUSE_HEADER_FOUND=1 else PRI_PROG_W_CAUSE_HEADER_FOUND=0 @@ -24922,7 +24946,7 @@ if test "x${PBX_PRI_INBANDDISCONNECT}" != "x1" -a "${USE_PRI_INBANDDISCONNECT}" as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -24957,7 +24981,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_INBANDDISCONNECT_FOUND=yes else AST_PRI_INBANDDISCONNECT_FOUND=no @@ -24980,7 +25005,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_INBANDDISCONNECT_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_INBANDDISCONNECT_HEADER_FOUND=1 else PRI_INBANDDISCONNECT_HEADER_FOUND=0 @@ -25026,7 +25051,7 @@ if test "x${PBX_PRI_SERVICE_MESSAGES}" != "x1" -a "${USE_PRI_SERVICE_MESSAGES}" as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25061,7 +25086,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_SERVICE_MESSAGES_FOUND=yes else AST_PRI_SERVICE_MESSAGES_FOUND=no @@ -25084,7 +25110,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_SERVICE_MESSAGES_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_SERVICE_MESSAGES_HEADER_FOUND=1 else PRI_SERVICE_MESSAGES_HEADER_FOUND=0 @@ -25130,7 +25156,7 @@ if test "x${PBX_PRI_REVERSE_CHARGE}" != "x1" -a "${USE_PRI_REVERSE_CHARGE}" != " as_ac_Lib=`$as_echo "ac_cv_lib_pri_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpri" >&5 $as_echo_n "checking for ${pbxfuncname} in -lpri... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25165,7 +25191,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_PRI_REVERSE_CHARGE_FOUND=yes else AST_PRI_REVERSE_CHARGE_FOUND=no @@ -25188,7 +25215,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${PRI_REVERSE_CHARGE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libpri.h" "ac_cv_header_libpri_h" "$ac_includes_default" -if test "x$ac_cv_header_libpri_h" = xyes; then : +if test "x$ac_cv_header_libpri_h" = x""yes; then : PRI_REVERSE_CHARGE_HEADER_FOUND=1 else PRI_REVERSE_CHARGE_HEADER_FOUND=0 @@ -25236,7 +25263,7 @@ if test "x${PBX_RESAMPLE}" != "x1" -a "${USE_RESAMPLE}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_resample_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lresample" >&5 $as_echo_n "checking for ${pbxfuncname} in -lresample... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25271,7 +25298,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_RESAMPLE_FOUND=yes else AST_RESAMPLE_FOUND=no @@ -25294,7 +25322,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${RESAMPLE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libresample.h" "ac_cv_header_libresample_h" "$ac_includes_default" -if test "x$ac_cv_header_libresample_h" = xyes; then : +if test "x$ac_cv_header_libresample_h" = x""yes; then : RESAMPLE_HEADER_FOUND=1 else RESAMPLE_HEADER_FOUND=0 @@ -25403,7 +25431,7 @@ if test "x${PBX_SPANDSP}" != "x1" -a "${USE_SPANDSP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_spandsp_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lspandsp" >&5 $as_echo_n "checking for ${pbxfuncname} in -lspandsp... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25438,7 +25466,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SPANDSP_FOUND=yes else AST_SPANDSP_FOUND=no @@ -25461,7 +25490,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SPANDSP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "spandsp.h" "ac_cv_header_spandsp_h" "$ac_includes_default" -if test "x$ac_cv_header_spandsp_h" = xyes; then : +if test "x$ac_cv_header_spandsp_h" = x""yes; then : SPANDSP_HEADER_FOUND=1 else SPANDSP_HEADER_FOUND=0 @@ -25512,7 +25541,7 @@ if test "x${PBX_SPANDSP}" != "x1" -a "${USE_SPANDSP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_spandsp_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lspandsp" >&5 $as_echo_n "checking for ${pbxfuncname} in -lspandsp... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25547,7 +25576,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SPANDSP_FOUND=yes else AST_SPANDSP_FOUND=no @@ -25570,7 +25600,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SPANDSP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "spandsp.h" "ac_cv_header_spandsp_h" "$ac_includes_default" -if test "x$ac_cv_header_spandsp_h" = xyes; then : +if test "x$ac_cv_header_spandsp_h" = x""yes; then : SPANDSP_HEADER_FOUND=1 else SPANDSP_HEADER_FOUND=0 @@ -25619,7 +25649,7 @@ if test "x${PBX_SS7}" != "x1" -a "${USE_SS7}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ss7_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lss7" >&5 $as_echo_n "checking for ${pbxfuncname} in -lss7... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25654,7 +25684,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SS7_FOUND=yes else AST_SS7_FOUND=no @@ -25677,7 +25708,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SS7_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "libss7.h" "ac_cv_header_libss7_h" "$ac_includes_default" -if test "x$ac_cv_header_libss7_h" = xyes; then : +if test "x$ac_cv_header_libss7_h" = x""yes; then : SS7_HEADER_FOUND=1 else SS7_HEADER_FOUND=0 @@ -25724,7 +25755,7 @@ if test "x${PBX_OPENR2}" != "x1" -a "${USE_OPENR2}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_openr2_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lopenr2" >&5 $as_echo_n "checking for ${pbxfuncname} in -lopenr2... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -25759,7 +25790,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OPENR2_FOUND=yes else AST_OPENR2_FOUND=no @@ -25782,7 +25814,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OPENR2_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "openr2.h" "ac_cv_header_openr2_h" "$ac_includes_default" -if test "x$ac_cv_header_openr2_h" = xyes; then : +if test "x$ac_cv_header_openr2_h" = x""yes; then : OPENR2_HEADER_FOUND=1 else OPENR2_HEADER_FOUND=0 @@ -25827,7 +25859,8 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test "${PWLIBDIR:-unset}" != "unset" ; then as_ac_Header=`$as_echo "ac_cv_header_${PWLIBDIR}/version.h" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "${PWLIBDIR}/version.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : HAS_PWLIB=1 fi @@ -25837,7 +25870,8 @@ if test "${HAS_PWLIB:-unset}" = "unset" ; then if test "${OPENH323DIR:-unset}" != "unset"; then as_ac_Header=`$as_echo "ac_cv_header_${OPENH323DIR}/../pwlib/version.h" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "${OPENH323DIR}/../pwlib/version.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : HAS_PWLIB=1 fi @@ -25848,7 +25882,8 @@ fi else as_ac_Header=`$as_echo "ac_cv_header_${HOME}/pwlib/include/ptlib.h" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "${HOME}/pwlib/include/ptlib.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : HAS_PWLIB=1 fi @@ -25857,7 +25892,7 @@ fi PWLIBDIR="${HOME}/pwlib" else ac_fn_cxx_check_header_mongrel "$LINENO" "/usr/local/include/ptlib.h" "ac_cv_header__usr_local_include_ptlib_h" "$ac_includes_default" -if test "x$ac_cv_header__usr_local_include_ptlib_h" = xyes; then : +if test "x$ac_cv_header__usr_local_include_ptlib_h" = x""yes; then : HAS_PWLIB=1 fi @@ -25867,7 +25902,7 @@ fi set dummy ptlib-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PTLIB_CONFIG+:} false; then : +if test "${ac_cv_path_PTLIB_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PTLIB_CONFIG in @@ -25881,7 +25916,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PTLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -25919,7 +25954,7 @@ fi PWLIB_LIB="-L${PWLIB_LIBDIR} `echo ${PWLIB_LIB}`" else ac_fn_cxx_check_header_mongrel "$LINENO" "/usr/include/ptlib.h" "ac_cv_header__usr_include_ptlib_h" "$ac_includes_default" -if test "x$ac_cv_header__usr_include_ptlib_h" = xyes; then : +if test "x$ac_cv_header__usr_include_ptlib_h" = x""yes; then : HAS_PWLIB=1 fi @@ -25929,7 +25964,7 @@ fi set dummy ptlib-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_PTLIB_CONFIG+:} false; then : +if test "${ac_cv_path_PTLIB_CONFIG+set}" = set; then : $as_echo_n "(cached) " >&6 else case $PTLIB_CONFIG in @@ -25943,7 +25978,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_PTLIB_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -26260,7 +26295,8 @@ ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test "${OPENH323DIR:-unset}" != "unset" ; then as_ac_Header=`$as_echo "ac_cv_header_${OPENH323DIR}/version.h" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "${OPENH323DIR}/version.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : HAS_OPENH323=1 fi @@ -26269,7 +26305,8 @@ fi if test "${HAS_OPENH323:-unset}" = "unset" ; then as_ac_Header=`$as_echo "ac_cv_header_${PWLIBDIR}/../openh323/version.h" | $as_tr_sh` ac_fn_cxx_check_header_mongrel "$LINENO" "${PWLIBDIR}/../openh323/version.h" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : OPENH323DIR="${PWLIBDIR}/../openh323"; HAS_OPENH323=1 fi @@ -26281,7 +26318,8 @@ fi as_ac_Header=`$as_echo "ac_cv_header_${OPENH323DIR}/include/h323.h" | $as_tr_sh` ac_fn_cxx_check_header_compile "$LINENO" "${OPENH323DIR}/include/h323.h" "$as_ac_Header" "#include <ptlib.h> " -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : else OPENH323_INCDIR="${PWLIB_INCDIR}/openh323"; OPENH323_LIBDIR="${PWLIB_LIBDIR}" @@ -26295,7 +26333,8 @@ fi as_ac_Header=`$as_echo "ac_cv_header_${HOME}/openh323/include/h323.h" | $as_tr_sh` ac_fn_cxx_check_header_compile "$LINENO" "${HOME}/openh323/include/h323.h" "$as_ac_Header" "#include <ptlib.h> " -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : HAS_OPENH323=1 fi @@ -26308,7 +26347,7 @@ fi CPPFLAGS="${CPPFLAGS} -I/usr/local/include/openh323 -I${PWLIB_INCDIR}" ac_fn_cxx_check_header_compile "$LINENO" "/usr/local/include/openh323/h323.h" "ac_cv_header__usr_local_include_openh323_h323_h" "#include <ptlib.h> " -if test "x$ac_cv_header__usr_local_include_openh323_h323_h" = xyes; then : +if test "x$ac_cv_header__usr_local_include_openh323_h323_h" = x""yes; then : HAS_OPENH323=1 fi @@ -26327,7 +26366,7 @@ fi CPPFLAGS="${CPPFLAGS} -I/usr/include/openh323 -I${PWLIB_INCDIR}" ac_fn_cxx_check_header_compile "$LINENO" "/usr/include/openh323/h323.h" "ac_cv_header__usr_include_openh323_h323_h" "#include <ptlib.h> " -if test "x$ac_cv_header__usr_include_openh323_h323_h" = xyes; then : +if test "x$ac_cv_header__usr_include_openh323_h323_h" = x""yes; then : HAS_OPENH323=1 fi @@ -26569,7 +26608,7 @@ if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_lua5.1_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -llua5.1" >&5 $as_echo_n "checking for ${pbxfuncname} in -llua5.1... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -26604,7 +26643,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_LUA_FOUND=yes else AST_LUA_FOUND=no @@ -26627,7 +26667,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "lua5.1/lua.h" "ac_cv_header_lua5_1_lua_h" "$ac_includes_default" -if test "x$ac_cv_header_lua5_1_lua_h" = xyes; then : +if test "x$ac_cv_header_lua5_1_lua_h" = x""yes; then : LUA_HEADER_FOUND=1 else LUA_HEADER_FOUND=0 @@ -26682,7 +26722,7 @@ if test "x${PBX_LUA}" != "x1" -a "${USE_LUA}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_lua_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -llua" >&5 $as_echo_n "checking for ${pbxfuncname} in -llua... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -26717,7 +26757,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_LUA_FOUND=yes else AST_LUA_FOUND=no @@ -26740,7 +26781,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${LUA_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "lua.h" "ac_cv_header_lua_h" "$ac_includes_default" -if test "x$ac_cv_header_lua_h" = xyes; then : +if test "x$ac_cv_header_lua_h" = x""yes; then : LUA_HEADER_FOUND=1 else LUA_HEADER_FOUND=0 @@ -26787,7 +26828,7 @@ if test "x${PBX_RADIUS}" != "x1" -a "${USE_RADIUS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_radiusclient-ng_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lradiusclient-ng" >&5 $as_echo_n "checking for ${pbxfuncname} in -lradiusclient-ng... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -26822,7 +26863,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_RADIUS_FOUND=yes else AST_RADIUS_FOUND=no @@ -26845,7 +26887,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${RADIUS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "radiusclient-ng.h" "ac_cv_header_radiusclient_ng_h" "$ac_includes_default" -if test "x$ac_cv_header_radiusclient_ng_h" = xyes; then : +if test "x$ac_cv_header_radiusclient_ng_h" = x""yes; then : RADIUS_HEADER_FOUND=1 else RADIUS_HEADER_FOUND=0 @@ -26892,7 +26934,7 @@ if test "x${PBX_COROSYNC}" != "x1" -a "${USE_COROSYNC}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_cpg_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcpg" >&5 $as_echo_n "checking for ${pbxfuncname} in -lcpg... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -26927,7 +26969,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_COROSYNC_FOUND=yes else AST_COROSYNC_FOUND=no @@ -26950,7 +26993,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${COROSYNC_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "corosync/cpg.h" "ac_cv_header_corosync_cpg_h" "$ac_includes_default" -if test "x$ac_cv_header_corosync_cpg_h" = xyes; then : +if test "x$ac_cv_header_corosync_cpg_h" = x""yes; then : COROSYNC_HEADER_FOUND=1 else COROSYNC_HEADER_FOUND=0 @@ -26996,7 +27039,7 @@ if test "x${PBX_COROSYNC_CFG_STATE_TRACK}" != "x1" -a "${USE_COROSYNC_CFG_STATE_ as_ac_Lib=`$as_echo "ac_cv_lib_cfg_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcfg" >&5 $as_echo_n "checking for ${pbxfuncname} in -lcfg... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27031,7 +27074,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_COROSYNC_CFG_STATE_TRACK_FOUND=yes else AST_COROSYNC_CFG_STATE_TRACK_FOUND=no @@ -27054,7 +27098,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${COROSYNC_CFG_STATE_TRACK_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "corosync/cfg.h" "ac_cv_header_corosync_cfg_h" "$ac_includes_default" -if test "x$ac_cv_header_corosync_cfg_h" = xyes; then : +if test "x$ac_cv_header_corosync_cfg_h" = x""yes; then : COROSYNC_CFG_STATE_TRACK_HEADER_FOUND=1 else COROSYNC_CFG_STATE_TRACK_HEADER_FOUND=0 @@ -27101,7 +27145,7 @@ if test "x${PBX_SPEEX}" != "x1" -a "${USE_SPEEX}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_speex_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lspeex" >&5 $as_echo_n "checking for ${pbxfuncname} in -lspeex... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27136,7 +27180,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SPEEX_FOUND=yes else AST_SPEEX_FOUND=no @@ -27159,7 +27204,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SPEEX_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "speex/speex.h" "ac_cv_header_speex_speex_h" "$ac_includes_default" -if test "x$ac_cv_header_speex_speex_h" = xyes; then : +if test "x$ac_cv_header_speex_speex_h" = x""yes; then : SPEEX_HEADER_FOUND=1 else SPEEX_HEADER_FOUND=0 @@ -27207,7 +27252,7 @@ if test "x${PBX_SPEEX_PREPROCESS}" != "x1" -a "${USE_SPEEX_PREPROCESS}" != "no"; as_ac_Lib=`$as_echo "ac_cv_lib_speex_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lspeex" >&5 $as_echo_n "checking for ${pbxfuncname} in -lspeex... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27242,7 +27287,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SPEEX_PREPROCESS_FOUND=yes else AST_SPEEX_PREPROCESS_FOUND=no @@ -27265,7 +27311,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SPEEX_PREPROCESS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "speex/speex.h" "ac_cv_header_speex_speex_h" "$ac_includes_default" -if test "x$ac_cv_header_speex_speex_h" = xyes; then : +if test "x$ac_cv_header_speex_speex_h" = x""yes; then : SPEEX_PREPROCESS_HEADER_FOUND=1 else SPEEX_PREPROCESS_HEADER_FOUND=0 @@ -27315,7 +27361,7 @@ if test "x${PBX_SPEEXDSP}" != "x1" -a "${USE_SPEEXDSP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_speexdsp_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lspeexdsp" >&5 $as_echo_n "checking for ${pbxfuncname} in -lspeexdsp... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27350,7 +27396,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SPEEXDSP_FOUND=yes else AST_SPEEXDSP_FOUND=no @@ -27373,7 +27420,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SPEEXDSP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "speex/speex.h" "ac_cv_header_speex_speex_h" "$ac_includes_default" -if test "x$ac_cv_header_speex_speex_h" = xyes; then : +if test "x$ac_cv_header_speex_speex_h" = x""yes; then : SPEEXDSP_HEADER_FOUND=1 else SPEEXDSP_HEADER_FOUND=0 @@ -27425,7 +27472,7 @@ if test "x${PBX_SQLITE}" != "x1" -a "${USE_SQLITE}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_sqlite_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsqlite" >&5 $as_echo_n "checking for ${pbxfuncname} in -lsqlite... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27460,7 +27507,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SQLITE_FOUND=yes else AST_SQLITE_FOUND=no @@ -27483,7 +27531,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SQLITE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sqlite.h" "ac_cv_header_sqlite_h" "$ac_includes_default" -if test "x$ac_cv_header_sqlite_h" = xyes; then : +if test "x$ac_cv_header_sqlite_h" = x""yes; then : SQLITE_HEADER_FOUND=1 else SQLITE_HEADER_FOUND=0 @@ -27530,7 +27578,7 @@ if test "x${PBX_SQLITE3}" != "x1" -a "${USE_SQLITE3}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_sqlite3_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsqlite3" >&5 $as_echo_n "checking for ${pbxfuncname} in -lsqlite3... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27565,7 +27613,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SQLITE3_FOUND=yes else AST_SQLITE3_FOUND=no @@ -27588,7 +27637,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SQLITE3_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sqlite3.h" "ac_cv_header_sqlite3_h" "$ac_includes_default" -if test "x$ac_cv_header_sqlite3_h" = xyes; then : +if test "x$ac_cv_header_sqlite3_h" = x""yes; then : SQLITE3_HEADER_FOUND=1 else SQLITE3_HEADER_FOUND=0 @@ -27643,7 +27692,7 @@ if test "x${PBX_CRYPTO}" != "x1" -a "${USE_CRYPTO}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_crypto_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lcrypto" >&5 $as_echo_n "checking for ${pbxfuncname} in -lcrypto... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27678,7 +27727,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_CRYPTO_FOUND=yes else AST_CRYPTO_FOUND=no @@ -27701,7 +27751,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${CRYPTO_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "openssl/aes.h" "ac_cv_header_openssl_aes_h" "$ac_includes_default" -if test "x$ac_cv_header_openssl_aes_h" = xyes; then : +if test "x$ac_cv_header_openssl_aes_h" = x""yes; then : CRYPTO_HEADER_FOUND=1 else CRYPTO_HEADER_FOUND=0 @@ -27750,7 +27800,7 @@ if test "x${PBX_OPENSSL}" != "x1" -a "${USE_OPENSSL}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ssl_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lssl" >&5 $as_echo_n "checking for ${pbxfuncname} in -lssl... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27785,7 +27835,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OPENSSL_FOUND=yes else AST_OPENSSL_FOUND=no @@ -27808,7 +27859,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OPENSSL_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" -if test "x$ac_cv_header_openssl_ssl_h" = xyes; then : +if test "x$ac_cv_header_openssl_ssl_h" = x""yes; then : OPENSSL_HEADER_FOUND=1 else OPENSSL_HEADER_FOUND=0 @@ -27854,7 +27905,7 @@ then osptk_saved_cppflags="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${osptk_cflags}" ac_fn_c_check_header_mongrel "$LINENO" "osp/osp.h" "ac_cv_header_osp_osp_h" "$ac_includes_default" -if test "x$ac_cv_header_osp_osp_h" = xyes; then : +if test "x$ac_cv_header_osp_osp_h" = x""yes; then : osptk_header_found=yes else osptk_header_found=no @@ -27869,7 +27920,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OSPPInit in -losptk" >&5 $as_echo_n "checking for OSPPInit in -losptk... " >&6; } -if ${ac_cv_lib_osptk_OSPPInit+:} false; then : +if test "${ac_cv_lib_osptk_OSPPInit+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -27903,7 +27954,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_osptk_OSPPInit" >&5 $as_echo "$ac_cv_lib_osptk_OSPPInit" >&6; } -if test "x$ac_cv_lib_osptk_OSPPInit" = xyes; then : +if test "x$ac_cv_lib_osptk_OSPPInit" = x""yes; then : osptk_library_found=yes else osptk_library_found=no @@ -27920,8 +27971,8 @@ $as_echo_n "checking if OSP Toolkit version is compatible with app_osplookup... if test "$cross_compiling" = yes; then : { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run test program while cross compiling -See \`config.log' for more details" "$LINENO" 5; } +as_fn_error "cannot run test program while cross compiling +See \`config.log' for more details." "$LINENO" 5; } else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ @@ -27985,7 +28036,7 @@ if test "x${PBX_OPENSSL_SRTP}" != "x1" -a "${USE_OPENSSL_SRTP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_ssl_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lssl" >&5 $as_echo_n "checking for ${pbxfuncname} in -lssl... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28020,7 +28071,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_OPENSSL_SRTP_FOUND=yes else AST_OPENSSL_SRTP_FOUND=no @@ -28043,7 +28095,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${OPENSSL_SRTP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "openssl/ssl.h" "ac_cv_header_openssl_ssl_h" "$ac_includes_default" -if test "x$ac_cv_header_openssl_ssl_h" = xyes; then : +if test "x$ac_cv_header_openssl_ssl_h" = x""yes; then : OPENSSL_SRTP_HEADER_FOUND=1 else OPENSSL_SRTP_HEADER_FOUND=0 @@ -28091,7 +28143,7 @@ if test "x${PBX_SRTP}" != "x1" -a "${USE_SRTP}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_srtp_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsrtp" >&5 $as_echo_n "checking for ${pbxfuncname} in -lsrtp... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28126,7 +28178,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SRTP_FOUND=yes else AST_SRTP_FOUND=no @@ -28149,7 +28202,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SRTP_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "srtp/srtp.h" "ac_cv_header_srtp_srtp_h" "$ac_includes_default" -if test "x$ac_cv_header_srtp_srtp_h" = xyes; then : +if test "x$ac_cv_header_srtp_srtp_h" = x""yes; then : SRTP_HEADER_FOUND=1 else SRTP_HEADER_FOUND=0 @@ -28247,46 +28300,46 @@ pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GMIME" >&5 $as_echo_n "checking for GMIME... " >&6; } -if test -n "$GMIME_CFLAGS"; then - pkg_cv_GMIME_CFLAGS="$GMIME_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$GMIME_CFLAGS"; then + pkg_cv_GMIME_CFLAGS="$GMIME_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gmime-\$ver\""; } >&5 ($PKG_CONFIG --exists --print-errors "gmime-$ver") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GMIME_CFLAGS=`$PKG_CONFIG --cflags "gmime-$ver" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi -if test -n "$GMIME_LIBS"; then - pkg_cv_GMIME_LIBS="$GMIME_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$GMIME_LIBS"; then + pkg_cv_GMIME_LIBS="$GMIME_LIBS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gmime-\$ver\""; } >&5 ($PKG_CONFIG --exists --print-errors "gmime-$ver") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GMIME_LIBS=`$PKG_CONFIG --libs "gmime-$ver" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes @@ -28294,20 +28347,20 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - GMIME_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gmime-$ver" 2>&1` + GMIME_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "gmime-$ver"` else - GMIME_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gmime-$ver" 2>&1` + GMIME_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "gmime-$ver"` fi # Put the nasty error message in config.log where it belongs echo "$GMIME_PKG_ERRORS" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } PBX_GMIME=0 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } PBX_GMIME=0 @@ -28353,7 +28406,7 @@ if test "x${PBX_HOARD}" != "x1" -a "${USE_HOARD}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_hoard_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lhoard" >&5 $as_echo_n "checking for ${pbxfuncname} in -lhoard... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28388,7 +28441,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_HOARD_FOUND=yes else AST_HOARD_FOUND=no @@ -28411,7 +28465,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${HOARD_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "" "ac_cv_header_" "$ac_includes_default" -if test "x$ac_cv_header_" = xyes; then : +if test "x$ac_cv_header_" = x""yes; then : HOARD_HEADER_FOUND=1 else HOARD_HEADER_FOUND=0 @@ -28458,7 +28512,7 @@ if test "x${PBX_FREETDS}" != "x1" -a "${USE_FREETDS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_sybdb_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lsybdb" >&5 $as_echo_n "checking for ${pbxfuncname} in -lsybdb... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28493,7 +28547,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_FREETDS_FOUND=yes else AST_FREETDS_FOUND=no @@ -28516,7 +28571,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${FREETDS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "sybdb.h" "ac_cv_header_sybdb_h" "$ac_includes_default" -if test "x$ac_cv_header_sybdb_h" = xyes; then : +if test "x$ac_cv_header_sybdb_h" = x""yes; then : FREETDS_HEADER_FOUND=1 else FREETDS_HEADER_FOUND=0 @@ -28545,7 +28600,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tone_zone_find_by_num in -ltonezone" >&5 $as_echo_n "checking for tone_zone_find_by_num in -ltonezone... " >&6; } -if ${ac_cv_lib_tonezone_tone_zone_find_by_num+:} false; then : +if test "${ac_cv_lib_tonezone_tone_zone_find_by_num+set}" = set; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28579,7 +28634,7 @@ LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_tonezone_tone_zone_find_by_num" >&5 $as_echo "$ac_cv_lib_tonezone_tone_zone_find_by_num" >&6; } -if test "x$ac_cv_lib_tonezone_tone_zone_find_by_num" = xyes; then : +if test "x$ac_cv_lib_tonezone_tone_zone_find_by_num" = x""yes; then : tonezone_does_not_need_lm=yes else tonezone_does_not_need_lm=no @@ -28610,7 +28665,7 @@ if test "x${PBX_TONEZONE}" != "x1" -a "${USE_TONEZONE}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_tonezone_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -ltonezone" >&5 $as_echo_n "checking for ${pbxfuncname} in -ltonezone... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28645,7 +28700,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_TONEZONE_FOUND=yes else AST_TONEZONE_FOUND=no @@ -28668,7 +28724,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${TONEZONE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "dahdi/tonezone.h" "ac_cv_header_dahdi_tonezone_h" "$ac_includes_default" -if test "x$ac_cv_header_dahdi_tonezone_h" = xyes; then : +if test "x$ac_cv_header_dahdi_tonezone_h" = x""yes; then : TONEZONE_HEADER_FOUND=1 else TONEZONE_HEADER_FOUND=0 @@ -28717,7 +28773,7 @@ if test "x${PBX_VORBIS}" != "x1" -a "${USE_VORBIS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_vorbis_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lvorbis" >&5 $as_echo_n "checking for ${pbxfuncname} in -lvorbis... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28752,7 +28808,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_VORBIS_FOUND=yes else AST_VORBIS_FOUND=no @@ -28775,7 +28832,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${VORBIS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "vorbis/codec.h" "ac_cv_header_vorbis_codec_h" "$ac_includes_default" -if test "x$ac_cv_header_vorbis_codec_h" = xyes; then : +if test "x$ac_cv_header_vorbis_codec_h" = x""yes; then : VORBIS_HEADER_FOUND=1 else VORBIS_HEADER_FOUND=0 @@ -28822,7 +28879,7 @@ if test "x${PBX_VORBIS}" != "x1" -a "${USE_VORBIS}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_vorbis_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lvorbis" >&5 $as_echo_n "checking for ${pbxfuncname} in -lvorbis... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -28857,7 +28914,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_VORBIS_FOUND=yes else AST_VORBIS_FOUND=no @@ -28880,7 +28938,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${VORBIS_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "vorbis/codec.h" "ac_cv_header_vorbis_codec_h" "$ac_includes_default" -if test "x$ac_cv_header_vorbis_codec_h" = xyes; then : +if test "x$ac_cv_header_vorbis_codec_h" = x""yes; then : VORBIS_HEADER_FOUND=1 else VORBIS_HEADER_FOUND=0 @@ -29043,7 +29101,7 @@ if test "x${PBX_ZLIB}" != "x1" -a "${USE_ZLIB}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_z_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lz" >&5 $as_echo_n "checking for ${pbxfuncname} in -lz... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -29078,7 +29136,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_ZLIB_FOUND=yes else AST_ZLIB_FOUND=no @@ -29101,7 +29160,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${ZLIB_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" -if test "x$ac_cv_header_zlib_h" = xyes; then : +if test "x$ac_cv_header_zlib_h" = x""yes; then : ZLIB_HEADER_FOUND=1 else ZLIB_HEADER_FOUND=0 @@ -29160,7 +29219,7 @@ rm -f core conftest.err conftest.$ac_objext \ fi ac_fn_c_check_header_mongrel "$LINENO" "h323.h" "ac_cv_header_h323_h" "$ac_includes_default" -if test "x$ac_cv_header_h323_h" = xyes; then : +if test "x$ac_cv_header_h323_h" = x""yes; then : PBX_H323=1 else PBX_H323=0 @@ -29170,7 +29229,7 @@ fi ac_fn_c_check_header_mongrel "$LINENO" "linux/compiler.h" "ac_cv_header_linux_compiler_h" "$ac_includes_default" -if test "x$ac_cv_header_linux_compiler_h" = xyes; then : +if test "x$ac_cv_header_linux_compiler_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LINUX_COMPILER_H 1 @@ -29187,7 +29246,7 @@ ac_fn_c_check_header_compile "$LINENO" "linux/ixjuser.h" "ac_cv_header_linux_ixj #endif " -if test "x$ac_cv_header_linux_ixjuser_h" = xyes; then : +if test "x$ac_cv_header_linux_ixjuser_h" = x""yes; then : PBX_IXJUSER=1 else PBX_IXJUSER=0 @@ -29298,7 +29357,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext set dummy ${ac_tool_prefix}sdl-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_CONFIG_SDL+:} false; then : +if test "${ac_cv_path_CONFIG_SDL+set}" = set; then : $as_echo_n "(cached) " >&6 else case $CONFIG_SDL in @@ -29313,7 +29372,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_CONFIG_SDL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -29342,7 +29401,7 @@ if test -z "$ac_cv_path_CONFIG_SDL"; then set dummy sdl-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_path_ac_pt_CONFIG_SDL+:} false; then : +if test "${ac_cv_path_ac_pt_CONFIG_SDL+set}" = set; then : $as_echo_n "(cached) " >&6 else case $ac_pt_CONFIG_SDL in @@ -29357,7 +29416,7 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_ac_pt_CONFIG_SDL="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 @@ -29460,7 +29519,7 @@ if test "x${PBX_SDL_IMAGE}" != "x1" -a "${USE_SDL_IMAGE}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_SDL_image_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lSDL_image" >&5 $as_echo_n "checking for ${pbxfuncname} in -lSDL_image... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -29495,7 +29554,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_SDL_IMAGE_FOUND=yes else AST_SDL_IMAGE_FOUND=no @@ -29518,7 +29578,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${SDL_IMAGE_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "SDL_image.h" "ac_cv_header_SDL_image_h" "$ac_includes_default" -if test "x$ac_cv_header_SDL_image_h" = xyes; then : +if test "x$ac_cv_header_SDL_image_h" = x""yes; then : SDL_IMAGE_HEADER_FOUND=1 else SDL_IMAGE_HEADER_FOUND=0 @@ -29564,7 +29624,7 @@ if test "x${PBX_FFMPEG}" != "x1" -a "${USE_FFMPEG}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_avcodec_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lavcodec" >&5 $as_echo_n "checking for ${pbxfuncname} in -lavcodec... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -29599,7 +29659,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_FFMPEG_FOUND=yes else AST_FFMPEG_FOUND=no @@ -29622,7 +29683,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${FFMPEG_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "ffmpeg/avcodec.h" "ac_cv_header_ffmpeg_avcodec_h" "$ac_includes_default" -if test "x$ac_cv_header_ffmpeg_avcodec_h" = xyes; then : +if test "x$ac_cv_header_ffmpeg_avcodec_h" = x""yes; then : FFMPEG_HEADER_FOUND=1 else FFMPEG_HEADER_FOUND=0 @@ -29651,7 +29712,7 @@ fi # possible places for video4linux version 1 ac_fn_c_check_header_mongrel "$LINENO" "linux/videodev.h" "ac_cv_header_linux_videodev_h" "$ac_includes_default" -if test "x$ac_cv_header_linux_videodev_h" = xyes; then : +if test "x$ac_cv_header_linux_videodev_h" = x""yes; then : cat >>confdefs.h <<_ACEOF #define HAVE_VIDEODEV_H 1 @@ -29682,7 +29743,7 @@ if test "x${PBX_X11}" != "x1" -a "${USE_X11}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_X11_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lX11" >&5 $as_echo_n "checking for ${pbxfuncname} in -lX11... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -29717,7 +29778,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_X11_FOUND=yes else AST_X11_FOUND=no @@ -29740,7 +29802,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${X11_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "X11/Xlib.h" "ac_cv_header_X11_Xlib_h" "$ac_includes_default" -if test "x$ac_cv_header_X11_Xlib_h" = xyes; then : +if test "x$ac_cv_header_X11_Xlib_h" = x""yes; then : X11_HEADER_FOUND=1 else X11_HEADER_FOUND=0 @@ -29790,7 +29852,7 @@ if test "x${PBX_X11}" != "x1" -a "${USE_X11}" != "no"; then as_ac_Lib=`$as_echo "ac_cv_lib_X11_${pbxfuncname}" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lX11" >&5 $as_echo_n "checking for ${pbxfuncname} in -lX11... " >&6; } -if eval \${$as_ac_Lib+:} false; then : +if { as_var=$as_ac_Lib; eval "test \"\${$as_var+set}\" = set"; }; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS @@ -29825,7 +29887,8 @@ fi eval ac_res=\$$as_ac_Lib { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } -if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : +eval as_val=\$$as_ac_Lib + if test "x$as_val" = x""yes; then : AST_X11_FOUND=yes else AST_X11_FOUND=no @@ -29848,7 +29911,7 @@ fi ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" CPPFLAGS="${CPPFLAGS} ${X11_INCLUDE}" ac_fn_c_check_header_mongrel "$LINENO" "X11/Xlib.h" "ac_cv_header_X11_Xlib_h" "$ac_includes_default" -if test "x$ac_cv_header_X11_Xlib_h" = xyes; then : +if test "x$ac_cv_header_X11_Xlib_h" = x""yes; then : X11_HEADER_FOUND=1 else X11_HEADER_FOUND=0 @@ -29884,11 +29947,11 @@ if test "${cross_compiling}" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /sbin/launchd" >&5 $as_echo_n "checking for /sbin/launchd... " >&6; } -if ${ac_cv_file__sbin_launchd+:} false; then : +if test "${ac_cv_file__sbin_launchd+set}" = set; then : $as_echo_n "(cached) " >&6 else test "$cross_compiling" = yes && - as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 + as_fn_error "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/sbin/launchd"; then ac_cv_file__sbin_launchd=yes else @@ -29897,7 +29960,7 @@ fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__sbin_launchd" >&5 $as_echo "$ac_cv_file__sbin_launchd" >&6; } -if test "x$ac_cv_file__sbin_launchd" = xyes; then : +if test "x$ac_cv_file__sbin_launchd" = x""yes; then : $as_echo "#define HAVE_SBIN_LAUNCHD 1" >>confdefs.h @@ -29916,46 +29979,46 @@ pkg_failed=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK2" >&5 $as_echo_n "checking for GTK2... " >&6; } -if test -n "$GTK2_CFLAGS"; then - pkg_cv_GTK2_CFLAGS="$GTK2_CFLAGS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$GTK2_CFLAGS"; then + pkg_cv_GTK2_CFLAGS="$GTK2_CFLAGS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtk+-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "gtk+-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GTK2_CFLAGS=`$PKG_CONFIG --cflags "gtk+-2.0" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi -if test -n "$GTK2_LIBS"; then - pkg_cv_GTK2_LIBS="$GTK2_LIBS" - elif test -n "$PKG_CONFIG"; then - if test -n "$PKG_CONFIG" && \ +if test -n "$PKG_CONFIG"; then + if test -n "$GTK2_LIBS"; then + pkg_cv_GTK2_LIBS="$GTK2_LIBS" + else + if test -n "$PKG_CONFIG" && \ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"gtk+-2.0\""; } >&5 ($PKG_CONFIG --exists --print-errors "gtk+-2.0") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then pkg_cv_GTK2_LIBS=`$PKG_CONFIG --libs "gtk+-2.0" 2>/dev/null` - test "x$?" != "x0" && pkg_failed=yes else pkg_failed=yes fi - else - pkg_failed=untried + fi +else + pkg_failed=untried fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes @@ -29963,20 +30026,20 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - GTK2_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "gtk+-2.0" 2>&1` + GTK2_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "gtk+-2.0"` else - GTK2_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "gtk+-2.0" 2>&1` + GTK2_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "gtk+-2.0"` fi # Put the nasty error message in config.log where it belongs echo "$GTK2_PKG_ERRORS" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } PBX_GTK2=0 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } PBX_GTK2=0 @@ -30641,21 +30704,10 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then + test "x$cache_file" != "x/dev/null" && { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi + cat confcache >$cache_file else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} @@ -30671,7 +30723,6 @@ DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= -U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' @@ -30687,7 +30738,7 @@ LTLIBOBJS=$ac_ltlibobjs -: "${CONFIG_STATUS=./config.status}" +: ${CONFIG_STATUS=./config.status} ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" @@ -30788,7 +30839,6 @@ fi IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. -as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR @@ -30834,19 +30884,19 @@ export LANGUAGE (unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. +# script with status $?, using 1 if that was 0. as_fn_error () { - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 fi - $as_echo "$as_me: error: $2" >&2 + $as_echo "$as_me: error: $1" >&2 as_fn_exit $as_status } # as_fn_error @@ -30984,16 +31034,16 @@ if (echo >conf$$.file) 2>/dev/null; then # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. + # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' + as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi else - as_ln_s='cp -pR' + as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null @@ -31042,7 +31092,7 @@ $as_echo X"$as_dir" | test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" } # as_fn_mkdir_p @@ -31053,16 +31103,28 @@ else as_mkdir_p=false fi - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" @@ -31084,7 +31146,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # values after options handling. ac_log=" This file was extended by asterisk $as_me trunk, which was -generated by GNU Autoconf 2.69. Invocation command line was +generated by GNU Autoconf 2.65. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS @@ -31146,10 +31208,10 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ asterisk config.status trunk -configured by $0, generated by GNU Autoconf 2.69, +configured by $0, generated by GNU Autoconf 2.65, with options \\"\$ac_cs_config\\" -Copyright (C) 2012 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." @@ -31166,16 +31228,11 @@ ac_need_defaults=: while test $# != 0 do case $1 in - --*=?*) + --*=*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; *) ac_option=$1 ac_optarg=$2 @@ -31197,7 +31254,6 @@ do $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; @@ -31210,7 +31266,7 @@ do ac_need_defaults=false;; --he | --h) # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' + as_fn_error "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; @@ -31219,7 +31275,7 @@ Try \`$0 --help' for more information.";; ac_cs_silent=: ;; # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' + -*) as_fn_error "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" @@ -31239,7 +31295,7 @@ fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' @@ -31273,7 +31329,7 @@ do "makeopts") CONFIG_FILES="$CONFIG_FILES makeopts" ;; "channels/h323/Makefile") CONFIG_FILES="$CONFIG_FILES channels/h323/Makefile" ;; - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done @@ -31295,10 +31351,9 @@ fi # after its creation but before its name has been assigned to `$tmp'. $debug || { - tmp= ac_tmp= + tmp= trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } @@ -31306,13 +31361,12 @@ $debug || { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" + test -n "$tmp" && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp +} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. @@ -31329,12 +31383,12 @@ if test "x$ac_cr" = x; then fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' + ac_cs_awk_cr='\r' else ac_cs_awk_cr=$ac_cr fi -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +echo 'BEGIN {' >"$tmp/subs1.awk" && _ACEOF @@ -31343,18 +31397,18 @@ _ACEOF echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -31362,7 +31416,7 @@ done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h @@ -31410,7 +31464,7 @@ t delim rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && +cat >>"\$tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" @@ -31442,29 +31496,21 @@ if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || as_fn_error "could not setup config files machinery" "$LINENO" 5 _ACEOF -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// s/^[^=]*=[ ]*$// }' fi @@ -31476,7 +31522,7 @@ fi # test -n "$CONFIG_FILES" # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || +cat >"$tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF @@ -31488,11 +31534,11 @@ _ACEOF # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then break elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi @@ -31577,7 +31623,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 + as_fn_error "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" @@ -31590,7 +31636,7 @@ do esac case $ac_mode$ac_tag in :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac @@ -31609,7 +31655,7 @@ do for ac_f do case $ac_f in - -) ac_f="$ac_tmp/stdin";; + -) ac_f="$tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. @@ -31618,7 +31664,7 @@ do [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" @@ -31644,8 +31690,8 @@ $as_echo "$as_me: creating $ac_file" >&6;} esac case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + *:-:* | *:-) cat >"$tmp/stdin" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac @@ -31775,24 +31821,23 @@ s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 +which seems to be undefined. Please make sure it is defined." >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} +which seems to be undefined. Please make sure it is defined." >&2;} - rm -f "$ac_tmp/stdin" + rm -f "$tmp/stdin" case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; :H) # @@ -31801,21 +31846,21 @@ which seems to be undefined. Please make sure it is defined" >&2;} if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + mv "$tmp/config.h" "$ac_file" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error "could not create -" "$LINENO" 5 fi ;; @@ -31830,7 +31875,7 @@ _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. @@ -31851,7 +31896,7 @@ if test "$no_create" != yes; then exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 + $ac_cs_success || as_fn_exit $? fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index f7294b36e..60f2068e5 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -294,7 +294,7 @@ /* Define if your system has the GLOB_NOMAGIC headers. */ #undef HAVE_GLOB_NOMAGIC -/* Define if your system has the GMIME libraries. */ +/* Define to 1 if you have the GMime library. */ #undef HAVE_GMIME /* Define to indicate the GSM library */ @@ -306,7 +306,7 @@ /* Define to indicate that gsm.h has no prefix for its location */ #undef HAVE_GSM_HEADER -/* Define if your system has the GTK2 libraries. */ +/* Define to 1 if you have the gtk2 library. */ #undef HAVE_GTK2 /* Define to 1 if you have the Hoard Memory Allocator library. */ @@ -324,7 +324,7 @@ /* Define to 1 if you have the Iksemel Jabber library. */ #undef HAVE_IKSEMEL -/* Define if your system has the ILBC libraries. */ +/* Define to 1 if you have the System iLBC library. */ #undef HAVE_ILBC /* Define if your system has the UW IMAP Toolkit c-client library. */ @@ -376,7 +376,7 @@ /* Define to 1 if you have the OpenLDAP library. */ #undef HAVE_LDAP -/* Define if your system has the LIBEDIT libraries. */ +/* Define to 1 if you have the NetBSD Editline library library. */ #undef HAVE_LIBEDIT /* Define to 1 if you have the <libintl.h> header file. */ @@ -551,7 +551,7 @@ /* Define to indicate presence of the pg_encoding_to_char API. */ #undef HAVE_PGSQL_pg_encoding_to_char -/* Define if your system has the PJPROJECT libraries. */ +/* Define to 1 if you have the PJPROJECT library. */ #undef HAVE_PJPROJECT /* Define to 1 if your system defines IP_PKTINFO. */ @@ -854,19 +854,19 @@ /* Define to 1 if you have the `strtoq' function. */ #undef HAVE_STRTOQ -/* Define to 1 if `ifr_ifru.ifru_hwaddr' is a member of `struct ifreq'. */ +/* Define to 1 if `ifr_ifru.ifru_hwaddr' is member of `struct ifreq'. */ #undef HAVE_STRUCT_IFREQ_IFR_IFRU_IFRU_HWADDR -/* Define to 1 if `uid' is a member of `struct sockpeercred'. */ +/* Define to 1 if `uid' is member of `struct sockpeercred'. */ #undef HAVE_STRUCT_SOCKPEERCRED_UID -/* Define to 1 if `st_blksize' is a member of `struct stat'. */ +/* Define to 1 if `st_blksize' is member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BLKSIZE -/* Define to 1 if `cr_uid' is a member of `struct ucred'. */ +/* Define to 1 if `cr_uid' is member of `struct ucred'. */ #undef HAVE_STRUCT_UCRED_CR_UID -/* Define to 1 if `uid' is a member of `struct ucred'. */ +/* Define to 1 if `uid' is member of `struct ucred'. */ #undef HAVE_STRUCT_UCRED_UID /* Define to 1 if you have the mISDN Supplemental Services library. */ @@ -1144,12 +1144,12 @@ /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME -/* Define to the home page for this package. */ -#undef PACKAGE_URL - /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to 1 if the C compiler supports function prototypes. */ +#undef PROTOTYPES + /* Define to necessary symbol if this constant uses a non-standard name on your system. */ #undef PTHREAD_CREATE_JOINABLE @@ -1169,6 +1169,11 @@ /* Define to the type of arg 5 for `select'. */ #undef SELECT_TYPE_ARG5 +/* Define to 1 if the `setvbuf' function takes the buffering type as its + second argument and the buffer pointer as the third, as on System V before + release 3. */ +#undef SETVBUF_REVERSED + /* The size of `char *', as computed by sizeof. */ #undef SIZEOF_CHAR_P @@ -1204,39 +1209,24 @@ /* Define to a type of the same size as fd_set.fds_bits[[0]] */ #undef TYPEOF_FD_SET_FDS_BITS -/* Enable extensions on AIX 3, Interix. */ +/* Define to 1 if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ #ifndef _ALL_SOURCE # undef _ALL_SOURCE #endif -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# undef _GNU_SOURCE -#endif -/* Enable threading extensions on Solaris. */ -#ifndef _POSIX_PTHREAD_SEMANTICS -# undef _POSIX_PTHREAD_SEMANTICS -#endif -/* Enable extensions on HP NonStop. */ -#ifndef _TANDEM_SOURCE -# undef _TANDEM_SOURCE -#endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# undef __EXTENSIONS__ -#endif - /* Define to 1 if running on Darwin. */ #undef _DARWIN_UNLIMITED_SELECT -/* Enable large inode numbers on Mac OS X 10.5. */ -#ifndef _DARWIN_USE_64_BIT_INODE -# define _DARWIN_USE_64_BIT_INODE 1 -#endif - /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif + /* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ #undef _LARGEFILE_SOURCE @@ -1253,6 +1243,20 @@ /* Define to 1 if you need to in order for `stat' and other things to work. */ #undef _POSIX_SOURCE +/* Enable extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif + +/* Define like PROTOTYPES; this can be used by system headers. */ +#undef __PROTOTYPES + /* Define to empty if `const' does not conform to ANSI C. */ #undef const diff --git a/include/asterisk/res_sip.h b/include/asterisk/res_sip.h new file mode 100644 index 000000000..7cfc38260 --- /dev/null +++ b/include/asterisk/res_sip.h @@ -0,0 +1,1092 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _RES_SIP_H +#define _RES_SIP_H + +#include "asterisk/stringfields.h" +/* Needed for struct ast_sockaddr */ +#include "asterisk/netsock2.h" +/* Needed for linked list macros */ +#include "asterisk/linkedlists.h" +/* Needed for ast_party_id */ +#include "asterisk/channel.h" +/* Needed for ast_sorcery */ +#include "asterisk/sorcery.h" +/* Needed for ast_dnsmgr */ +#include "asterisk/dnsmgr.h" +/* Needed for pj_sockaddr */ +#include <pjlib.h> + +/* Forward declarations of PJSIP stuff */ +struct pjsip_rx_data; +struct pjsip_module; +struct pjsip_tx_data; +struct pjsip_dialog; +struct pjsip_transport; +struct pjsip_tpfactory; +struct pjsip_tls_setting; +struct pjsip_tpselector; + +/*! + * \brief Structure for SIP transport information + */ +struct ast_sip_transport_state { + /*! \brief Transport itself */ + struct pjsip_transport *transport; + + /*! \brief Transport factory */ + struct pjsip_tpfactory *factory; +}; + +#define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias" + +/*! + * Details about a SIP domain alias + */ +struct ast_sip_domain_alias { + /*! Sorcery object details */ + SORCERY_OBJECT(details); + AST_DECLARE_STRING_FIELDS( + /*! Domain to be aliased to */ + AST_STRING_FIELD(domain); + ); +}; + +/*! + * \brief Types of supported transports + */ +enum ast_sip_transport_type { + AST_SIP_TRANSPORT_UDP, + AST_SIP_TRANSPORT_TCP, + AST_SIP_TRANSPORT_TLS, + /* XXX Websocket ? */ +}; + +/*! \brief Maximum number of ciphers supported for a TLS transport */ +#define SIP_TLS_MAX_CIPHERS 64 + +/* + * \brief Transport to bind to + */ +struct ast_sip_transport { + /*! Sorcery object details */ + SORCERY_OBJECT(details); + AST_DECLARE_STRING_FIELDS( + /*! Certificate of authority list file */ + AST_STRING_FIELD(ca_list_file); + /*! Public certificate file */ + AST_STRING_FIELD(cert_file); + /*! Optional private key of the certificate file */ + AST_STRING_FIELD(privkey_file); + /*! Password to open the private key */ + AST_STRING_FIELD(password); + /*! External signaling address */ + AST_STRING_FIELD(external_signaling_address); + /*! External media address */ + AST_STRING_FIELD(external_media_address); + /*! Optional domain to use for messages if provided could not be found */ + AST_STRING_FIELD(domain); + ); + /*! Type of transport */ + enum ast_sip_transport_type type; + /*! Address and port to bind to */ + pj_sockaddr host; + /*! Number of simultaneous asynchronous operations */ + unsigned int async_operations; + /*! Optional external port for signaling */ + unsigned int external_signaling_port; + /*! TLS settings */ + pjsip_tls_setting tls; + /*! Configured TLS ciphers */ + pj_ssl_cipher ciphers[SIP_TLS_MAX_CIPHERS]; + /*! Optional local network information, used for NAT purposes */ + struct ast_ha *localnet; + /*! DNS manager for refreshing the external address */ + struct ast_dnsmgr_entry *external_address_refresher; + /*! Optional external address information */ + struct ast_sockaddr external_address; + /*! Transport state information */ + struct ast_sip_transport_state *state; +}; + +/*! + * \brief Structure for SIP nat hook information + */ +struct ast_sip_nat_hook { + /*! Sorcery object details */ + SORCERY_OBJECT(details); + /*! Callback for when a message is going outside of our local network */ + void (*outgoing_external_message)(struct pjsip_tx_data *tdata, struct ast_sip_transport *transport); +}; + +/*! + * \brief Contact associated with an address of record + */ +struct ast_sip_contact { + /*! Sorcery object details, the id is the aor name plus a random string */ + SORCERY_OBJECT(details); + AST_DECLARE_STRING_FIELDS( + /*! Full URI of the contact */ + AST_STRING_FIELD(uri); + ); + /*! Absolute time that this contact is no longer valid after */ + struct timeval expiration_time; +}; + +/*! + * \brief A SIP address of record + */ +struct ast_sip_aor { + /*! Sorcery object details, the id is the AOR name */ + SORCERY_OBJECT(details); + AST_DECLARE_STRING_FIELDS( + /*! Voicemail boxes for this AOR */ + AST_STRING_FIELD(mailboxes); + ); + /*! Minimum expiration time */ + unsigned int minimum_expiration; + /*! Maximum expiration time */ + unsigned int maximum_expiration; + /*! Default contact expiration if one is not provided in the contact */ + unsigned int default_expiration; + /*! 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 */ + unsigned int remove_existing; + /*! Any permanent configured contacts */ + struct ao2_container *permanent_contacts; +}; + +/*! + * \brief DTMF modes for SIP endpoints + */ +enum ast_sip_dtmf_mode { + /*! No DTMF to be used */ + AST_SIP_DTMF_NONE, + /* XXX Should this be 2833 instead? */ + /*! Use RFC 4733 events for DTMF */ + AST_SIP_DTMF_RFC_4733, + /*! Use DTMF in the audio stream */ + AST_SIP_DTMF_INBAND, + /*! Use SIP INFO DTMF (blech) */ + AST_SIP_DTMF_INFO, +}; + +/*! + * \brief Methods of storing SIP digest authentication credentials. + * + * Note that both methods result in MD5 digest authentication being + * used. The two methods simply alter how Asterisk determines the + * credentials for a SIP authentication + */ +enum ast_sip_auth_type { + /*! Credentials stored as a username and password combination */ + AST_SIP_AUTH_TYPE_USER_PASS, + /*! Credentials stored as an MD5 sum */ + AST_SIP_AUTH_TYPE_MD5, +}; + +#define SIP_SORCERY_AUTH_TYPE "auth" + +struct ast_sip_auth { + /* Sorcery ID of the auth is its name */ + SORCERY_OBJECT(details); + AST_DECLARE_STRING_FIELDS( + /* Identification for these credentials */ + AST_STRING_FIELD(realm); + /* Authentication username */ + AST_STRING_FIELD(auth_user); + /* Authentication password */ + AST_STRING_FIELD(auth_pass); + /* Authentication credentials in MD5 format (hash of user:realm:pass) */ + AST_STRING_FIELD(md5_creds); + ); + /* The time period (in seconds) that a nonce may be reused */ + unsigned int nonce_lifetime; + /* Used to determine what to use when authenticating */ + enum ast_sip_auth_type type; +}; + +/*! + * \brief Different methods by which incoming requests can be matched to endpoints + */ +enum ast_sip_endpoint_identifier_type { + /*! Identify based on user name in From header */ + AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME = (1 << 0), + /*! Identify based on source location of the SIP message */ + AST_SIP_ENDPOINT_IDENTIFY_BY_LOCATION = (1 << 1), +}; + +enum ast_sip_session_refresh_method { + /*! Use reinvite to negotiate direct media */ + AST_SIP_SESSION_REFRESH_METHOD_INVITE, + /*! Use UPDATE to negotiate direct media */ + AST_SIP_SESSION_REFRESH_METHOD_UPDATE, +}; + +enum ast_sip_direct_media_glare_mitigation { + /*! Take no special action to mitigate reinvite glare */ + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE, + /*! Do not send an initial direct media session refresh on outgoing call legs + * Subsequent session refreshes will be sent no matter the session direction + */ + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING, + /*! Do not send an initial direct media session refresh on incoming call legs + * Subsequent session refreshes will be sent no matter the session direction + */ + AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING, +}; + +/*! + * \brief An entity with which Asterisk communicates + */ +struct ast_sip_endpoint { + SORCERY_OBJECT(details); + AST_DECLARE_STRING_FIELDS( + /*! Context to send incoming calls to */ + AST_STRING_FIELD(context); + /*! Name of an explicit transport to use */ + AST_STRING_FIELD(transport); + /*! Outbound proxy to use */ + AST_STRING_FIELD(outbound_proxy); + /*! Explicit AORs to dial if none are specified */ + AST_STRING_FIELD(aors); + /*! Musiconhold class to suggest that the other side use when placing on hold */ + AST_STRING_FIELD(mohsuggest); + /*! Optional external media address to use in SDP */ + AST_STRING_FIELD(external_media_address); + /*! Configured voicemail boxes for this endpoint. Used for MWI */ + AST_STRING_FIELD(mailboxes); + ); + /*! Identification information for this endpoint */ + struct ast_party_id id; + /*! Domain to which this endpoint belongs */ + struct ast_sip_domain *domain; + /*! Address of record for incoming registrations */ + struct ast_sip_aor *aor; + /*! Codec preferences */ + struct ast_codec_pref prefs; + /*! Configured codecs */ + struct ast_format_cap *codecs; + /*! Names of inbound authentication credentials */ + const char **sip_inbound_auths; + /*! Number of configured auths */ + size_t num_inbound_auths; + /*! Names of outbound authentication credentials */ + const char **sip_outbound_auths; + /*! Number of configured outbound auths */ + size_t num_outbound_auths; + /*! DTMF mode to use with this endpoint */ + enum ast_sip_dtmf_mode dtmf; + /*! Whether IPv6 RTP is enabled or not */ + unsigned int rtp_ipv6; + /*! Whether symmetric RTP is enabled or not */ + unsigned int rtp_symmetric; + /*! Whether ICE support is enabled or not */ + unsigned int ice_support; + /*! Whether to use the "ptime" attribute received from the endpoint or not */ + unsigned int use_ptime; + /*! Whether to force using the source IP address/port for sending responses */ + unsigned int force_rport; + /*! Whether to rewrite the Contact header with the source IP address/port or not */ + unsigned int rewrite_contact; + /*! Enabled SIP extensions */ + unsigned int extensions; + /*! Minimum session expiration period, in seconds */ + unsigned int min_se; + /*! Session expiration period, in seconds */ + 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; + /*! Take steps to mitigate glare for direct media */ + enum ast_sip_direct_media_glare_mitigation direct_media_glare_mitigation; + /*! Do not attempt direct media session refreshes if a media NAT is detected */ + unsigned int disable_direct_media_on_nat; + /*! Do we trust the endpoint with our outbound identity? */ + unsigned int trust_id_outbound; + /*! Do we trust identity information that originates externally (e.g. P-Asserted-Identity header)? */ + unsigned int trust_id_inbound; + /*! Do we send P-Asserted-Identity headers to this endpoint? */ + unsigned int send_pai; + /*! Do we send Remote-Party-ID headers to this endpoint? */ + unsigned int send_rpid; + /*! Should unsolicited MWI be aggregated into a single NOTIFY? */ + unsigned int aggregate_mwi; +}; + +/*! + * \brief Possible returns from ast_sip_check_authentication + */ +enum ast_sip_check_auth_result { + /*! Authentication needs to be challenged */ + AST_SIP_AUTHENTICATION_CHALLENGE, + /*! Authentication succeeded */ + AST_SIP_AUTHENTICATION_SUCCESS, + /*! Authentication failed */ + AST_SIP_AUTHENTICATION_FAILED, + /*! Authentication encountered some internal error */ + AST_SIP_AUTHENTICATION_ERROR, +}; + +/*! + * \brief An interchangeable way of handling digest authentication for SIP. + * + * An authenticator is responsible for filling in the callbacks provided below. Each is called from a publicly available + * function in res_sip. The authenticator can use configuration or other local policy to determine whether authentication + * should take place and what credentials should be used when challenging and authenticating a request. + */ +struct ast_sip_authenticator { + /*! + * \brief Check if a request requires authentication + * See ast_sip_requires_authentication for more details + */ + int (*requires_authentication)(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata); + /*! + * \brief Check that an incoming request passes authentication. + * + * The tdata parameter is useful for adding information such as digest challenges. + * + * \param endpoint The endpoint sending the incoming request + * \param rdata The incoming request + * \param tdata Tentative outgoing request. + */ + enum ast_sip_check_auth_result (*check_authentication)(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata, pjsip_tx_data *tdata); +}; + +/*! + * \brief an interchangeable way of responding to authentication challenges + * + * An outbound authenticator takes incoming challenges and formulates a new SIP request with + * credentials. + */ +struct ast_sip_outbound_authenticator { + /*! + * \brief Create a new request with authentication credentials + * + * \param auths An array of IDs of auth sorcery objects + * \param num_auths The number of IDs in the array + * \param challenge The SIP response with authentication challenge(s) + * \param tsx The transaction in which the challenge was received + * \param new_request The new SIP request with challenge response(s) + * \retval 0 Successfully created new request + * \retval -1 Failed to create a new request + */ + int (*create_request_with_auth)(const char **auths, size_t num_auths, struct pjsip_rx_data *challenge, + struct pjsip_transaction *tsx, struct pjsip_tx_data **new_request); +}; + +/*! + * \brief An entity responsible for identifying the source of a SIP message + */ +struct ast_sip_endpoint_identifier { + /*! + * \brief Callback used to identify the source of a message. + * See ast_sip_identify_endpoint for more details + */ + struct ast_sip_endpoint *(*identify_endpoint)(pjsip_rx_data *rdata); +}; + +/*! + * \brief Register a SIP service in Asterisk. + * + * This is more-or-less a wrapper around pjsip_endpt_register_module(). + * Registering a service makes it so that PJSIP will call into the + * service at appropriate times. For more information about PJSIP module + * callbacks, see the PJSIP documentation. Asterisk modules that call + * this function will likely do so at module load time. + * + * \param module The module that is to be registered with PJSIP + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_register_service(pjsip_module *module); + +/*! + * This is the opposite of ast_sip_register_service(). Unregistering a + * service means that PJSIP will no longer call into the module any more. + * This will likely occur when an Asterisk module is unloaded. + * + * \param module The PJSIP module to unregister + */ +void ast_sip_unregister_service(pjsip_module *module); + +/*! + * \brief Register a SIP authenticator + * + * An authenticator has three main purposes: + * 1) Determining if authentication should be performed on an incoming request + * 2) Gathering credentials necessary for issuing an authentication challenge + * 3) Authenticating a request that has credentials + * + * Asterisk provides a default authenticator, but it may be replaced by a + * custom one if desired. + * + * \param auth The authenticator to register + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_register_authenticator(struct ast_sip_authenticator *auth); + +/*! + * \brief Unregister a SIP authenticator + * + * When there is no authenticator registered, requests cannot be challenged + * or authenticated. + * + * \param auth The authenticator to unregister + */ +void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth); + + /*! + * \brief Register an outbound SIP authenticator + * + * An outbound authenticator is responsible for creating responses to + * authentication challenges by remote endpoints. + * + * \param auth The authenticator to register + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticator *outbound_auth); + +/*! + * \brief Unregister an outbound SIP authenticator + * + * When there is no outbound authenticator registered, authentication challenges + * will be handled as any other final response would be. + * + * \param auth The authenticator to unregister + */ +void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth); + +/*! + * \brief Register a SIP endpoint identifier + * + * An endpoint identifier's purpose is to determine which endpoint a given SIP + * message has come from. + * + * Multiple endpoint identifiers may be registered so that if an endpoint + * cannot be identified by one identifier, it may be identified by another. + * + * Asterisk provides two endpoint identifiers. One identifies endpoints based + * on the user part of the From header URI. The other identifies endpoints based + * on the source IP address. + * + * If the order in which endpoint identifiers is run is important to you, then + * be sure to load individual endpoint identifier modules in the order you wish + * for them to be run in modules.conf + * + * \param identifier The SIP endpoint identifier to register + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier); + +/*! + * \brief Unregister a SIP endpoint identifier + * + * This stops an endpoint identifier from being used. + * + * \param identifier The SIP endoint identifier to unregister + */ +void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier); + +/*! + * \brief Allocate a new SIP endpoint + * + * This will return an endpoint with its refcount increased by one. This reference + * can be released using ao2_ref(). + * + * \param name The name of the endpoint. + * \retval NULL Endpoint allocation failed + * \retval non-NULL The newly allocated endpoint + */ +void *ast_sip_endpoint_alloc(const char *name); + +/*! + * \brief Get a pointer to the PJSIP endpoint. + * + * This is useful when modules have specific information they need + * to register with the PJSIP core. + * \retval NULL endpoint has not been created yet. + * \retval non-NULL PJSIP endpoint. + */ +pjsip_endpoint *ast_sip_get_pjsip_endpoint(void); + +/*! + * \brief Get a pointer to the SIP sorcery structure. + * + * \retval NULL sorcery has not been initialized + * \retval non-NULL sorcery structure + */ +struct ast_sorcery *ast_sip_get_sorcery(void); + +/*! + * \brief Initialize transport support on a sorcery instance + * + * \param sorcery The sorcery instance + * + * \retval -1 failure + * \retval 0 success + */ +int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery); + +/*! + * \brief Initialize location support on a sorcery instance + * + * \param sorcery The sorcery instance + * + * \retval -1 failure + * \retval 0 success + */ +int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery); + +/*! + * \brief Retrieve a named AOR + * + * \param aor_name Name of the AOR + * + * \retval NULL if not found + * \retval non-NULL if found + */ +struct ast_sip_aor *ast_sip_location_retrieve_aor(const char *aor_name); + +/*! + * \brief Retrieve the first bound contact for an AOR + * + * \param aor Pointer to the AOR + * \retval NULL if no contacts available + * \retval non-NULL if contacts available + */ +struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor); + +/*! + * \brief Retrieve all contacts currently available for an AOR + * + * \param aor Pointer to the AOR + * + * \retval NULL if no contacts available + * \retval non-NULL if contacts available + */ +struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor); + +/*! + * \brief Retrieve the first bound contact from a list of AORs + * + * \param aor_list A comma-separated list of AOR names + * \retval NULL if no contacts available + * \retval non-NULL if contacts available + */ +struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list); + +/*! + * \brief Retrieve a named contact + * + * \param contact_name Name of the contact + * + * \retval NULL if not found + * \retval non-NULL if found + */ +struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name); + +/*! + * \brief Add a new contact to an AOR + * + * \param aor Pointer to the AOR + * \param uri Full contact URI + * \param expiration_time Optional expiration time of the contact + * + * \retval -1 failure + * \retval 0 success + */ +int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, struct timeval expiration_time); + +/*! + * \brief Update a contact + * + * \param contact New contact object with details + * + * \retval -1 failure + * \retval 0 success + */ +int ast_sip_location_update_contact(struct ast_sip_contact *contact); + +/*! +* \brief Delete a contact +* +* \param contact Contact object to delete +* +* \retval -1 failure +* \retval 0 success +*/ +int ast_sip_location_delete_contact(struct ast_sip_contact *contact); + +/*! + * \brief Initialize domain aliases support on a sorcery instance + * + * \param sorcery The sorcery instance + * + * \retval -1 failure + * \retval 0 success + */ +int ast_sip_initialize_sorcery_domain_alias(struct ast_sorcery *sorcery); + +/*! + * \brief Initialize authentication support on a sorcery instance + * + * \param sorcery The sorcery instance + * + * \retval -1 failure + * \retval 0 success + */ +int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery); + +/*! + * \brief Callback called when an outbound request with authentication credentials is to be sent in dialog + * + * This callback will have the created request on it. The callback's purpose is to do any extra + * housekeeping that needs to be done as well as to send the request out. + * + * This callback is only necessary if working with a PJSIP API that sits between the application + * and the dialog layer. + * + * \param dlg The dialog to which the request belongs + * \param tdata The created request to be sent out + * \param user_data Data supplied with the callback + * + * \retval 0 Success + * \retval -1 Failure + */ +typedef int (*ast_sip_dialog_outbound_auth_cb)(pjsip_dialog *dlg, pjsip_tx_data *tdata, void *user_data); + +/*! + * \brief Set up outbound authentication on a SIP dialog + * + * This sets up the infrastructure so that all requests associated with a created dialog + * can be re-sent with authentication credentials if the original request is challenged. + * + * \param dlg The dialog on which requests will be authenticated + * \param endpoint The endpoint whom this dialog pertains to + * \param cb Callback to call to send requests with authentication + * \param user_data Data to be provided to the callback when it is called + * + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_dialog_setup_outbound_authentication(pjsip_dialog *dlg, const struct ast_sip_endpoint *endpoint, + ast_sip_dialog_outbound_auth_cb cb, void *user_data); + +/*! + * \brief Initialize the distributor module + * + * The distributor module is responsible for taking an incoming + * SIP message and placing it into the threadpool. Once in the threadpool, + * the distributor will perform endpoint lookups and authentication, and + * then distribute the message up the stack to any further modules. + * + * \retval -1 Failure + * \retval 0 Success + */ +int ast_sip_initialize_distributor(void); + +/*! + * \page Threading model for SIP + * + * There are three major types of threads that SIP will have to deal with: + * \li Asterisk threads + * \li PJSIP threads + * \li SIP threadpool threads (a.k.a. "servants") + * + * \par Asterisk Threads + * + * Asterisk threads are those that originate from outside of SIP but within + * Asterisk. The most common of these threads are PBX (channel) threads and + * the autoservice thread. Most interaction with these threads will be through + * channel technology callbacks. Within these threads, it is fine to handle + * Asterisk data from outside of SIP, but any handling of SIP data should be + * left to servants, \b especially if you wish to call into PJSIP for anything. + * Asterisk threads are not registered with PJLIB, so attempting to call into + * PJSIP will cause an assertion to be triggered, thus causing the program to + * crash. + * + * \par PJSIP Threads + * + * PJSIP threads are those that originate from handling of PJSIP events, such + * as an incoming SIP request or response, or a transaction timeout. The role + * of these threads is to process information as quickly as possible so that + * the next item on the SIP socket(s) can be serviced. On incoming messages, + * Asterisk automatically will push the request to a servant thread. When your + * module callback is called, processing will already be in a servant. However, + * for other PSJIP events, such as transaction state changes due to timer + * expirations, your module will be called into from a PJSIP thread. If you + * are called into from a PJSIP thread, then you should push whatever processing + * is needed to a servant as soon as possible. You can discern if you are currently + * in a SIP servant thread using the \ref ast_sip_thread_is_servant function. + * + * \par Servants + * + * Servants are where the bulk of SIP work should be performed. These threads + * exist in order to do the work that Asterisk threads and PJSIP threads hand + * off to them. Servant threads register themselves with PJLIB, meaning that + * they are capable of calling PJSIP and PJLIB functions if they wish. + * + * \par Serializer + * + * Tasks are handed off to servant threads using the API call \ref ast_sip_push_task. + * The first parameter of this call is a serializer. If this pointer + * is NULL, then the work will be handed off to whatever servant can currently handle + * the task. If this pointer is non-NULL, then the task will not be executed until + * previous tasks pushed with the same serializer have completed. For more information + * on serializers and the benefits they provide, see \ref ast_threadpool_serializer + * + * \note + * + * Do not make assumptions about individual threads based on a corresponding serializer. + * In other words, just because several tasks use the same serializer when being pushed + * to servants, it does not mean that the same thread is necessarily going to execute those + * tasks, even though they are all guaranteed to be executed in sequence. + */ + +/*! + * \brief Create a new serializer for SIP tasks + * + * See \ref ast_threadpool_serializer for more information on serializers. + * SIP creates serializers so that tasks operating on similar data will run + * in sequence. + * + * \retval NULL Failure + * \retval non-NULL Newly-created serializer + */ +struct ast_taskprocessor *ast_sip_create_serializer(void); + +/*! + * \brief Set a serializer on a SIP dialog so requests and responses are automatically serialized + * + * Passing a NULL serializer is a way to remove a serializer from a dialog. + * + * \param dlg The SIP dialog itself + * \param serializer The serializer to use + */ +void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer); + +/*! + * \brief Set an endpoint on a SIP dialog so in-dialog requests do not undergo endpoint lookup. + * + * \param dlg The SIP dialog itself + * \param endpoint The endpoint that this dialog is communicating with + */ +void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint); + +/*! + * \brief Get the endpoint associated with this dialog + * + * This function increases the refcount of the endpoint by one. Release + * the reference once you are finished with the endpoint. + * + * \param dlg The SIP dialog from which to retrieve the endpoint + * \retval NULL No endpoint associated with this dialog + * \retval non-NULL The endpoint. + */ +struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg); + +/*! + * \brief Pushes a task to SIP servants + * + * This uses the serializer provided to determine how to push the task. + * If the serializer is NULL, then the task will be pushed to the + * servants directly. If the serializer is non-NULL, then the task will be + * queued behind other tasks associated with the same serializer. + * + * \param serializer The serializer to which the task belongs. Can be NULL + * \param sip_task The task to execute + * \param task_data The parameter to pass to the task when it executes + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data); + +/*! + * \brief Push a task to SIP servants and wait for it to complete + * + * Like \ref ast_sip_push_task except that it blocks until the task completes. + * + * \warning \b Never use this function in a SIP servant thread. This can potentially + * cause a deadlock. If you are in a SIP servant thread, just call your function + * in-line. + * + * \param serializer The SIP serializer to which the task belongs. May be NULL. + * \param sip_task The task to execute + * \param task_data The parameter to pass to the task when it executes + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data); + +/*! + * \brief Determine if the current thread is a SIP servant thread + * + * \retval 0 This is not a SIP servant thread + * \retval 1 This is a SIP servant thread + */ +int ast_sip_thread_is_servant(void); + +/*! + * \brief SIP body description + * + * This contains a type and subtype that will be added as + * the "Content-Type" for the message as well as the body + * text. + */ +struct ast_sip_body { + /*! Type of the body, such as "application" */ + const char *type; + /*! Subtype of the body, such as "sdp" */ + const char *subtype; + /*! The text to go in the body */ + const char *body_text; +}; + +/*! + * \brief General purpose method for creating a dialog with an endpoint + * + * \param endpoint A pointer to the endpoint + * \param aor_name Optional name of the AOR to target, may even be an explicit SIP URI + * \param request_user Optional user to place into the target URI + * + * \retval non-NULL success + * \retval NULL failure + */ + pjsip_dialog *ast_sip_create_dialog(const struct ast_sip_endpoint *endpoint, const char *aor_name, const char *request_user); + +/*! + * \brief General purpose method for creating a SIP request + * + * Its typical use would be to create one-off requests such as an out of dialog + * SIP MESSAGE. + * + * The request can either be in- or out-of-dialog. If in-dialog, the + * dlg parameter MUST be present. If out-of-dialog the endpoint parameter + * MUST be present. If both are present, then we will assume that the message + * is to be sent in-dialog. + * + * The uri parameter can be specified if the request should be sent to an explicit + * URI rather than one configured on the endpoint. + * + * \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 uri Optional. If specified, the request will be sent to this URI rather + * than one configured for the endpoint. + * \param[out] tdata The newly-created request + * \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); + +/*! + * \brief General purpose method for sending a SIP request + * + * This is a companion function for \ref ast_sip_create_request. The request + * created there can be passed to this function, though any request may be + * passed in. + * + * This will automatically set up handling outbound authentication challenges if + * they arrive. + * + * \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); + +/*! + * \brief Determine if an incoming request requires authentication + * + * This calls into the registered authenticator's requires_authentication callback + * in order to determine if the request requires authentication. + * + * If there is no registered authenticator, then authentication will be assumed + * not to be required. + * + * \param endpoint The endpoint from which the request originates + * \param rdata The incoming SIP request + * \retval non-zero The request requires authentication + * \retval 0 The request does not require authentication + */ +int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata); + +/*! + * \brief Method to determine authentication status of an incoming request + * + * This will call into a registered authenticator. The registered authenticator will + * do what is necessary to determine whether the incoming request passes authentication. + * A tentative response is passed into this function so that if, say, a digest authentication + * challenge should be sent in the ensuing response, it can be added to the response. + * + * \param endpoint The endpoint from the request was sent + * \param rdata The request to potentially authenticate + * \param tdata Tentative response to the request + * \return The result of checking authentication. + */ +enum ast_sip_check_auth_result ast_sip_check_authentication(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata, pjsip_tx_data *tdata); + +/*! + * \brief Create a response to an authentication challenge + * + * This will call into an outbound authenticator's create_request_with_auth callback + * to create a new request with authentication credentials. See the create_request_with_auth + * callback in the \ref ast_sip_outbound_authenticator structure for details about + * the parameters and return values. + */ +int ast_sip_create_request_with_auth(const char **auths, size_t num_auths, pjsip_rx_data *challenge, + pjsip_transaction *tsx, pjsip_tx_data **new_request); + +/*! + * \brief Determine the endpoint that has sent a SIP message + * + * This will call into each of the registered endpoint identifiers' + * identify_endpoint() callbacks until one returns a non-NULL endpoint. + * This will return an ao2 object. Its reference count will need to be + * decremented when completed using the endpoint. + * + * \param rdata The inbound SIP message to use when identifying the endpoint. + * \retval NULL No matching endpoint + * \retval non-NULL The matching endpoint + */ +struct ast_sip_endpoint *ast_sip_identify_endpoint(pjsip_rx_data *rdata); + +/*! + * \brief Add a header to an outbound SIP message + * + * \param tdata The message to add the header to + * \param name The header name + * \param value The header value + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value); + +/*! + * \brief Add a body to an outbound SIP message + * + * If this is called multiple times, the latest body will replace the current + * body. + * + * \param tdata The message to add the body to + * \param body The message body to add + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_add_body(pjsip_tx_data *tdata, const struct ast_sip_body *body); + +/*! + * \brief Add a multipart body to an outbound SIP message + * + * This will treat each part of the input array as part of a multipart body and + * add each part to the SIP message. + * + * \param tdata The message to add the body to + * \param bodies The parts of the body to add + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_add_body_multipart(pjsip_tx_data *tdata, const struct ast_sip_body *bodies[], int num_bodies); + +/*! + * \brief Append body data to a SIP message + * + * This acts mostly the same as ast_sip_add_body, except that rather than replacing + * a body if it currently exists, it appends data to an existing body. + * + * \param tdata The message to append the body to + * \param body The string to append to the end of the current body + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_append_body(pjsip_tx_data *tdata, const char *body_text); + +/*! + * \brief Copy a pj_str_t into a standard character buffer. + * + * pj_str_t is not NULL-terminated. Any place that expects a NULL- + * terminated string needs to have the pj_str_t copied into a separate + * buffer. + * + * This method copies the pj_str_t contents into the destination buffer + * and NULL-terminates the buffer. + * + * \param dest The destination buffer + * \param src The pj_str_t to copy + * \param size The size of the destination buffer. + */ +void ast_copy_pj_str(char *dest, pj_str_t *src, size_t size); + +/*! + * \brief Get the looked-up endpoint on an out-of dialog request or response + * + * The function may ONLY be called on out-of-dialog requests or responses. For + * in-dialog requests and responses, it is required that the user of the dialog + * has the looked-up endpoint stored locally. + * + * This function should never return NULL if the message is out-of-dialog. It will + * always return NULL if the message is in-dialog. + * + * This function will increase the reference count of the returned endpoint by one. + * Release your reference using the ao2_ref function when finished. + * + * \param rdata Out-of-dialog request or response + * \return The looked up endpoint + */ +struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata); + +/*! + * \brief Retrieve relevant SIP auth structures from sorcery + * + * \param auth_names The sorcery IDs of auths to retrieve + * \param num_auths The number of auths to retrieve + * \param[out] out The retrieved auths are stored here + */ +int ast_sip_retrieve_auths(const char *auth_names[], size_t num_auths, struct ast_sip_auth **out); + +/*! + * \brief Clean up retrieved auth structures from memory + * + * Call this function once you have completed operating on auths + * retrieved from \ref ast_sip_retrieve_auths + * + * \param auths An array of auth structures to clean up + * \param num_auths The number of auths in the array + */ +void ast_sip_cleanup_auths(struct ast_sip_auth *auths[], size_t num_auths); + +#endif /* _RES_SIP_H */ diff --git a/include/asterisk/res_sip_pubsub.h b/include/asterisk/res_sip_pubsub.h new file mode 100644 index 000000000..33614b285 --- /dev/null +++ b/include/asterisk/res_sip_pubsub.h @@ -0,0 +1,346 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _RES_SIP_PUBSUB_H +#define _RES_SIP_PUBSUB_H + +#include "asterisk/linkedlists.h" + +/* Forward declarations */ +struct pjsip_rx_data; +struct pjsip_tx_data; +struct pjsip_evsub; +struct ast_sip_endpoint; +struct ast_datastore; +struct ast_datastore_info; + +/*! + * \brief Opaque structure representing an RFC 3265 SIP subscription + */ +struct ast_sip_subscription; + +/*! + * \brief Role for the subscription that is being created + */ +enum ast_sip_subscription_role { + /* Sending SUBSCRIBEs, receiving NOTIFYs */ + AST_SIP_SUBSCRIBER, + /* Sending NOTIFYs, receiving SUBSCRIBEs */ + AST_SIP_NOTIFIER, +}; + +/*! + * \brief Data for responses to SUBSCRIBEs and NOTIFIEs + * + * Some of PJSIP's evsub callbacks expect us to provide them + * with data so that they can craft a response rather than have + * us create our own response. + * + * Filling in the structure is optional, since the framework + * will automatically respond with a 200 OK response if we do + * not provide it with any additional data. + */ +struct ast_sip_subscription_response_data { + /*! Status code of the response */ + int status_code; + /*! Optional status text */ + const char *status_text; + /*! Optional additional headers to add to the response */ + struct ast_variable *headers; + /*! Optional body to add to the response */ + struct ast_sip_body *body; +}; + +#define AST_SIP_MAX_ACCEPT 32 + +struct ast_sip_subscription_handler { + /*! The name of the event this handler deals with */ + const char *event_name; + /*! The types of body this handler accepts */ + const char *accept[AST_SIP_MAX_ACCEPT]; + + /*! + * \brief Called when a subscription is to be destroyed + * + * This is a subscriber and notifier callback. + * + * The handler is not expected to send any sort of requests or responses + * during this callback. The handler MUST, however, begin the destruction + * process for the subscription during this callback. + */ + void (*subscription_shutdown)(struct ast_sip_subscription *subscription); + + /*! + * \brief Called when a SUBSCRIBE arrives in order to create a new subscription + * + * This is a notifier callback. + * + * If the notifier wishes to accept the subscription, then it can create + * a new ast_sip_subscription to do so. + * + * If the notifier chooses to create a new subscription, then it must accept + * the incoming subscription using pjsip_evsub_accept() and it must also + * send an initial NOTIFY with the current subscription state. + * + * \param endpoint The endpoint from which we received the SUBSCRIBE + * \param rdata The SUBSCRIBE request + * \retval NULL The SUBSCRIBE has not been accepted + * \retval non-NULL The newly-created subscription + */ + struct ast_sip_subscription *(*new_subscribe)(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata); + + /*! + * \brief Called when an endpoint renews a subscription. + * + * This is a notifier callback. + * + * Because of the way that the PJSIP evsub framework works, it will automatically + * send a response to the SUBSCRIBE. However, the subscription handler must send + * a NOTIFY with the current subscription state when this callback is called. + * + * The response_data that is passed into this callback is used to craft what should + * be in the response to the incoming SUBSCRIBE. It is initialized with a 200 status + * code and all other parameters are empty. + * + * \param sub The subscription that is being renewed + * \param rdata The SUBSCRIBE request in question + * \param[out] response_data Data pertaining to the SIP response that should be + * sent to the SUBSCRIBE + */ + void (*resubscribe)(struct ast_sip_subscription *sub, + pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data); + + /*! + * \brief Called when a subscription times out. + * + * This is a notifier callback + * + * This indicates that the subscription has timed out. The subscription handler is + * expected to send a NOTIFY that terminates the subscription. + * + * \param sub The subscription that has timed out + */ + void (*subscription_timeout)(struct ast_sip_subscription *sub); + + /*! + * \brief Called when a subscription is terminated via a SUBSCRIBE or NOTIFY request + * + * This is a notifier and subscriber callback. + * + * The PJSIP subscription framework will automatically send the response to the + * request. If a notifier receives this callback, then the subscription handler + * is expected to send a final NOTIFY to terminate the subscription. + * + * \param sub The subscription being terminated + * \param rdata The request that terminated the subscription + */ + void (*subscription_terminated)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); + + /*! + * \brief Called when a subscription handler's outbound NOTIFY receives a response + * + * This is a notifier callback. + * + * \param sub The subscription + * \param rdata The NOTIFY response + */ + void (*notify_response)(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); + + /*! + * \brief Called when a subscription handler receives an inbound NOTIFY + * + * This is a subscriber callback. + * + * Because of the way that the PJSIP evsub framework works, it will automatically + * send a response to the NOTIFY. By default this will be a 200 OK response, but + * this callback can change details of the response by returning response data + * to use. + * + * The response_data that is passed into this callback is used to craft what should + * be in the response to the incoming SUBSCRIBE. It is initialized with a 200 status + * code and all other parameters are empty. + * + * \param sub The subscription + * \param rdata The NOTIFY request + * \param[out] response_data Data pertaining to the SIP response that should be + * sent to the SUBSCRIBE + */ + void (*notify_request)(struct ast_sip_subscription *sub, + pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data); + + /*! + * \brief Called when it is time for a subscriber to resubscribe + * + * This is a subscriber callback. + * + * The subscriber can reresh the subscription using the pjsip_evsub_initiate() + * function. + * + * \param sub The subscription to refresh + * \retval 0 Success + * \retval non-zero Failure + */ + int (*refresh_subscription)(struct ast_sip_subscription *sub); + AST_LIST_ENTRY(ast_sip_subscription_handler) next; +}; + +/*! + * \brief Create a new ast_sip_subscription structure + * + * In most cases the pubsub core will create a general purpose subscription + * within PJSIP. However, PJSIP provides enhanced support for the following + * event packages: + * + * presence + * message-summary + * + * If either of these events are handled by the subscription handler, then + * the special-purpose event subscriptions will be created within PJSIP, + * and it will be expected that your subscription handler make use of the + * special PJSIP APIs. + * + * \param handler The subsription handler for this subscription + * \param role Whether we are acting as subscriber or notifier for this subscription + * \param endpoint The endpoint involved in this subscription + * \param rdata If acting as a notifier, the SUBSCRIBE request that triggered subscription creation + */ +struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler, + enum ast_sip_subscription_role role, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata); + + +/*! + * \brief Get the endpoint that is associated with this subscription + * + * This function will increase the reference count of the endpoint. Be sure to + * release the reference to it when you are finished with the endpoint. + * + * \retval NULL Could not get endpoint + * \retval non-NULL The endpoint + */ +struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub); + +/*! + * \brief Get the serializer for the subscription + * + * Tasks that originate outside of a SIP servant thread should get the serializer + * and push the task to the serializer. + * + * \param sub The subscription + * \retval NULL Failure + * \retval non-NULL The subscription's serializer + */ +struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub); + +/*! + * \brief Get the underlying PJSIP evsub structure + * + * This is useful when wishing to call PJSIP's API calls in order to + * create SUBSCRIBEs, NOTIFIES, etc. as well as get subscription state + * + * This function, as well as all methods called on the pjsip_evsub should + * be done in a SIP servant thread. + * + * \param sub The subscription + * \retval NULL Failure + * \retval non-NULL The underlying pjsip_evsub + */ +pjsip_evsub *ast_sip_subscription_get_evsub(struct ast_sip_subscription *sub); + +/*! + * \brief Send a request created via a PJSIP evsub method + * + * Callers of this function should take care to do so within a SIP servant + * thread. + * + * \param sub The subscription on which to send the request + * \param tdata The request to send + * \retval 0 Success + * \retval non-zero Failure + */ +int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx_data *tdata); + +/*! + * \brief Alternative for ast_datastore_alloc() + * + * There are two major differences between this and ast_datastore_alloc() + * 1) This allocates a refcounted object + * 2) This will fill in a uid if one is not provided + * + * DO NOT call ast_datastore_free() on a datastore allocated in this + * way since that function will attempt to free the datastore rather + * than play nicely with its refcount. + * + * \param info Callbacks for datastore + * \param uid Identifier for datastore + * \retval NULL Failed to allocate datastore + * \retval non-NULL Newly allocated datastore + */ +struct ast_datastore *ast_sip_subscription_alloc_datastore(const struct ast_datastore_info *info, const char *uid); + +/*! + * \brief Add a datastore to a SIP subscription + * + * Note that SIP uses reference counted datastores. The datastore passed into this function + * must have been allocated using ao2_alloc() or there will be serious problems. + * + * \param subscription The ssubscription to add the datastore to + * \param datastore The datastore to be added to the subscription + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore); + +/*! + * \brief Retrieve a subscription datastore + * + * The datastore retrieved will have its reference count incremented. When the caller is done + * with the datastore, the reference counted needs to be decremented using ao2_ref(). + * + * \param subscription The subscription from which to retrieve the datastore + * \param name The name of the datastore to retrieve + * \retval NULL Failed to find the specified datastore + * \retval non-NULL The specified datastore + */ +struct ast_datastore *ast_sip_subscription_get_datastore(struct ast_sip_subscription *subscription, const char *name); + +/*! + * \brief Remove a subscription datastore from the subscription + * + * This operation may cause the datastore's free() callback to be called if the reference + * count reaches zero. + * + * \param subscription The subscription to remove the datastore from + * \param name The name of the datastore to remove + */ +void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscription, const char *name); + +/*! + * \brief Register a subscription handler + * + * \retval 0 Handler was registered successfully + * \retval non-zero Handler was not registered successfully + */ +int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler); + +/*! + * \brief Unregister a subscription handler + */ +void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler *handler); + +#endif /* RES_SIP_PUBSUB_H */ diff --git a/include/asterisk/res_sip_session.h b/include/asterisk/res_sip_session.h new file mode 100644 index 000000000..cbed52621 --- /dev/null +++ b/include/asterisk/res_sip_session.h @@ -0,0 +1,468 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _RES_SIP_SESSION_H +#define _RES_SIP_SESSION_H + +/* Needed for pj_timer_entry definition */ +#include "pjlib.h" +#include "asterisk/linkedlists.h" +/* Needed for AST_MAX_EXTENSION constant */ +#include "asterisk/channel.h" +/* Needed for ast_sockaddr struct */ +#include "asterisk/netsock.h" + +/* Forward declarations */ +struct ast_sip_endpoint; +struct ast_sip_transport; +struct pjsip_inv_session; +struct ast_channel; +struct ast_datastore; +struct ast_datastore_info; +struct ao2_container; +struct pjsip_tx_data; +struct pjsip_rx_data; +struct ast_party_id; +struct pjmedia_sdp_media; +struct pjmedia_sdp_session; +struct ast_rtp_instance; + +struct ast_sip_session_sdp_handler; + +/*! + * \brief A structure containing SIP session media information + */ +struct ast_sip_session_media { + /*! \brief RTP instance itself */ + struct ast_rtp_instance *rtp; + /*! \brief Direct media address */ + struct ast_sockaddr direct_media_addr; + /*! \brief SDP handler that setup the RTP */ + struct ast_sip_session_sdp_handler *handler; + /*! \brief Stream is on hold */ + unsigned int held:1; + /*! \brief Stream type this session media handles */ + char stream_type[1]; +}; + +/*! + * \brief Opaque structure representing a request that could not be sent + * due to an outstanding INVITE transaction + */ +struct ast_sip_session_delayed_request; + +/*! + * \brief A structure describing a SIP session + * + * For the sake of brevity, a "SIP session" in Asterisk is referring to + * a dialog initiated by an INVITE. While "session" is typically interpreted + * to refer to the negotiated media within a SIP dialog, we have opted + * to use the term "SIP session" to refer to the INVITE dialog itself. + */ +struct ast_sip_session { + /* Dialplan extension where incoming call is destined */ + char exten[AST_MAX_EXTENSION]; + /* The endpoint with which Asterisk is communicating */ + struct ast_sip_endpoint *endpoint; + /* The PJSIP details of the session, which includes the dialog */ + struct pjsip_inv_session *inv_session; + /* The Asterisk channel associated with the session */ + struct ast_channel *channel; + /* Registered session supplements */ + AST_LIST_HEAD(, ast_sip_session_supplement) supplements; + /* Datastores added to the session by supplements to the session */ + struct ao2_container *datastores; + /* Media streams */ + struct ao2_container *media; + /* Serializer for tasks relating to this SIP session */ + struct ast_taskprocessor *serializer; + /* Requests that could not be sent due to current inv_session state */ + AST_LIST_HEAD_NOLOCK(, ast_sip_session_delayed_request) delayed_requests; + /* When we need to reschedule a reinvite, we use this structure to do it */ + pj_timer_entry rescheduled_reinvite; + /* Format capabilities pertaining to direct media */ + struct ast_format_cap *direct_media_cap; + /* Identity of endpoint this session deals with */ + struct ast_party_id id; + /* Requested capabilities */ + struct ast_format_cap *req_caps; +}; + +typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata); +typedef int (*ast_sip_session_response_cb)(struct ast_sip_session *session, pjsip_rx_data *rdata); + +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_gulp 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 + * + * These can be registered by any module in order to add + * processing to incoming and outgoing SIP requests and responses + */ +struct ast_sip_session_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_session_supplement_priority priority; + /*! + * \brief Notification that the session has begun + * This method will always be called from a SIP servant thread. + */ + void (*session_begin)(struct ast_sip_session *session); + /*! + * \brief Notification that the session has ended + * + * This method may or may not be called from a SIP servant thread. Do + * not make assumptions about being able to call PJSIP methods from within + * this method. + */ + void (*session_end)(struct ast_sip_session *session); + /*! + * \brief Notification that the session is being destroyed + */ + void (*session_destroy)(struct ast_sip_session *session); + /*! + * \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. + */ + int (*incoming_request)(struct ast_sip_session *session, 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. + */ + void (*incoming_response)(struct ast_sip_session *session, 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_session *session, 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_session *session, struct pjsip_tx_data *tdata); + /*! Next item in the list */ + AST_LIST_ENTRY(ast_sip_session_supplement) next; +}; + +/*! + * \brief A handler for SDPs in SIP sessions + * + * An SDP handler is registered by a module that is interested in being the + * responsible party for specific types of SDP streams. + */ +struct ast_sip_session_sdp_handler { + /*! An identifier for this handler */ + const char *id; + /*! + * \brief Set session details based on a stream in an incoming SDP offer or answer + * \param session The session for which the media is being negotiated + * \param session_media The media to be setup for this session + * \param sdp The entire SDP. Useful for getting "global" information, such as connections or attributes + * \param stream The stream on which to operate + * \retval 0 The stream was not handled by this handler. If there are other registered handlers for this stream type, they will be called. + * \retval <0 There was an error encountered. No further operation will take place and the current negotiation will be abandoned. + * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called. + */ + int (*negotiate_incoming_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream); + /*! + * \brief Create an SDP media stream and add it to the outgoing SDP offer or answer + * \param session The session for which media is being added + * \param session_media The media to be setup for this session + * \param stream The stream on which to operate + * \retval 0 The stream was not handled by this handler. If there are other registered handlers for this stream type, they will be called. + * \retval <0 There was an error encountered. No further operation will take place and the current negotiation will be abandoned. + * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called. + */ + int (*handle_incoming_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, struct pjmedia_sdp_media *stream); + /*! + * \brief Create an SDP media stream and add it to the outgoing SDP offer or answer + * \param session The session for which media is being added + * \param session_media The media to be setup for this session + * \param sdp The entire SDP as currently built + * \retval 0 This handler has no stream to add. If there are other registered handlers for this stream type, they will be called. + * \retval <0 There was an error encountered. No further operation will take place and the current SDP negotiation will be abandoned. + * \retval >0 The handler has a stream to be added to the SDP. No further handler of this stream type will be called. + */ + int (*create_outgoing_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp); + /*! + * \brief Update media stream with external address if applicable + * \param tdata The outgoing message itself + * \param stream The stream on which to operate + * \param transport The transport the SDP is going out on + */ + void (*change_outgoing_sdp_stream_media_address)(struct pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport); + /*! + * \brief Apply a negotiated SDP media stream + * \param session The session for which media is being applied + * \param session_media The media to be setup for this session + * \param local The entire local negotiated SDP + * \param local_stream The local stream which to apply + * \param remote The entire remote negotiated SDP + * \param remote_stream The remote stream which to apply + * \retval 0 The stream was not applied by this handler. If there are other registered handlers for this stream type, they will be called. + * \retval <0 There was an error encountered. No further operation will take place and the current application will be abandoned. + * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called. + */ + int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream, + const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream); + /*! + * \brief Destroy a session_media created by this handler + * \param session The session for which media is being destroyed + * \param session_media The media to destroy + */ + void (*stream_destroy)(struct ast_sip_session_media *session_media); + /*! Next item in the list. */ + AST_LIST_ENTRY(ast_sip_session_sdp_handler) next; +}; + +/*! + * \brief Allocate a new SIP session + * + * This will take care of allocating the datastores container on the session as well + * as placing all registered supplements onto the session. + * + * The endpoint that is passed in will have its reference count increased by one since + * the session will be keeping a reference to the endpoint. The session will relinquish + * this reference when the session is destroyed. + * + * \param endpoint The endpoint that this session communicates with + * \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); + +/*! + * \brief Create a new outgoing SIP session + * + * The endpoint that is passed in will have its reference count increased by one since + * the session will be keeping a reference to the endpoint. The session will relinquish + * 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 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); + +/*! + * \brief Register an SDP handler + * + * An SDP handler is responsible for parsing incoming SDP streams and ensuring that + * Asterisk can cope with the contents. Similarly, the SDP handler will be + * responsible for constructing outgoing SDP streams. + * + * Multiple handlers for the same stream type may be registered. They will be + * visited in the order they were registered. Handlers will be visited for each + * stream type until one claims to have handled the stream. + * + * \param handler The SDP handler to register + * \param stream_type The type of media stream for which to call the handler + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_session_register_sdp_handler(struct ast_sip_session_sdp_handler *handler, const char *stream_type); + +/*! + * \brief Unregister an SDP handler + * + * \param handler The SDP handler to unregister + * \param stream_type Stream type for which the SDP handler was registered + */ +void ast_sip_session_unregister_sdp_handler(struct ast_sip_session_sdp_handler *handler, const char *stream_type); + +/*! + * \brief Register a supplement to SIP session processing + * + * This allows for someone to insert themselves in the processing of 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_session_register_supplement(struct ast_sip_session_supplement *supplement); + +/*! + * \brief Unregister a an supplement to SIP session processing + * + * \param supplement The supplement to unregister + */ +void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement); + +/*! + * \brief Alternative for ast_datastore_alloc() + * + * There are two major differences between this and ast_datastore_alloc() + * 1) This allocates a refcounted object + * 2) This will fill in a uid if one is not provided + * + * DO NOT call ast_datastore_free() on a datastore allocated in this + * way since that function will attempt to free the datastore rather + * than play nicely with its refcount. + * + * \param info Callbacks for datastore + * \param uid Identifier for datastore + * \retval NULL Failed to allocate datastore + * \retval non-NULL Newly allocated datastore + */ +struct ast_datastore *ast_sip_session_alloc_datastore(const struct ast_datastore_info *info, const char *uid); + +/*! + * \brief Add a datastore to a SIP session + * + * Note that SIP uses reference counted datastores. The datastore passed into this function + * must have been allocated using ao2_alloc() or there will be serious problems. + * + * \param session The session to add the datastore to + * \param datastore The datastore to be added to the session + * \retval 0 Success + * \retval -1 Failure + */ +int ast_sip_session_add_datastore(struct ast_sip_session *session, struct ast_datastore *datastore); + +/*! + * \brief Retrieve a session datastore + * + * The datastore retrieved will have its reference count incremented. When the caller is done + * with the datastore, the reference counted needs to be decremented using ao2_ref(). + * + * \param session The session from which to retrieve the datastore + * \param name The name of the datastore to retrieve + * \retval NULL Failed to find the specified datastore + * \retval non-NULL The specified datastore + */ +struct ast_datastore *ast_sip_session_get_datastore(struct ast_sip_session *session, const char *name); + +/*! + * \brief Remove a session datastore from the session + * + * This operation may cause the datastore's free() callback to be called if the reference + * count reaches zero. + * + * \param session The session to remove the datastore from + * \param name The name of the datastore to remove + */ +void ast_sip_session_remove_datastore(struct ast_sip_session *session, const char *name); + +/*! + * \brief Retrieve identifying information from an incoming request + * + * This will retrieve identifying information and place it in the + * id parameter. The caller of the function can then apply this to + * caller ID, connected line, or whatever else may be proper. + * + * \param rdata The incoming request or response + * \param[out] id The collected identity information + * \retval 0 Successfully found identifying information + * \retval -1 Identifying information could not be found + */ +int ast_sip_session_get_identity(struct pjsip_rx_data *rdata, struct ast_party_id *id); + +/*! + * \brief Send a reinvite or UPDATE on a session + * + * This method will inspect the session in order to construct an appropriate + * session refresh request. As with any outgoing request in res_sip_session, + * this will call into registered supplements in case they wish to add anything. + * + * Note: The on_request_creation callback may or may not be called in the same + * thread where this function is called. Request creation may need to be delayed + * due to the current INVITE transaction state. + * + * \param session The session on which the reinvite will be sent + * \param on_request_creation Callback called when request is created + * \param on_response Callback called when response for request is received + * \param method The method that should be used when constructing the session refresh + * \param generate_new_sdp Boolean to indicate if a new SDP should be created + * \retval 0 Successfully sent refresh + * \retval -1 Failure to send refresh + */ +int ast_sip_session_refresh(struct ast_sip_session *session, + ast_sip_session_request_creation_cb on_request_creation, + ast_sip_session_response_cb on_response, + enum ast_sip_session_refresh_method method, + int generate_new_sdp); + +/*! + * \brief Send a SIP response + * + * This will send the SIP response specified in tdata and + * call into any registered supplements' outgoing_response callback. + * + * \param session The session on which to send the response. + * \param tdata The response to send + */ +void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata); + +/*! + * \brief Send a SIP request + * + * This will send the SIP request specified in tdata and + * call into any registered supplements' outgoing_request callback. + * + * \param session The session to which to send the request + * \param tdata The request to send + */ +void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata); + +/*! + * \brief Send a SIP request and get called back when a response is received + * + * This will send the request out exactly the same as ast_sip_send_request() does. + * The difference is that when a response arrives, the specified callback will be + * called into + * + * \param session The session on which to send the request + * \param tdata The request to send + * \param on_response Callback to be called when a response is received + */ +void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip_tx_data *tdata, + ast_sip_session_response_cb on_response); + +#endif /* _RES_SIP_SESSION_H */ diff --git a/include/asterisk/sorcery.h b/include/asterisk/sorcery.h index e390b43cf..434f5595a 100644 --- a/include/asterisk/sorcery.h +++ b/include/asterisk/sorcery.h @@ -157,10 +157,15 @@ typedef struct ast_variable *(*sorcery_transform_handler)(struct ast_variable *s /*! * \brief A callback function for when an object set is successfully applied to an object * + * \note On a failure return, the state of the object is left undefined. It is a bad + * idea to try to use this object. + * * \param sorcery Sorcery structure in use * \param obj The object itself + * \retval 0 Success + * \retval non-zero Failure */ -typedef void (*sorcery_apply_handler)(const struct ast_sorcery *sorcery, void *obj); +typedef int (*sorcery_apply_handler)(const struct ast_sorcery *sorcery, void *obj); /*! * \brief A callback function for copying the contents of one object to another diff --git a/include/asterisk/threadpool.h b/include/asterisk/threadpool.h index 89076265e..e1e7727f5 100644 --- a/include/asterisk/threadpool.h +++ b/include/asterisk/threadpool.h @@ -108,6 +108,20 @@ struct ast_threadpool_options { * maximum size. */ int max_size; + /*! + * \brief Function to call when a thread starts + * + * This is useful if there is something common that all threads + * in a threadpool need to do when they start. + */ + void (*thread_start)(void); + /*! + * \brief Function to call when a thread ends + * + * This is useful if there is common cleanup to execute when + * a thread completes + */ + void (*thread_end)(void); }; /*! diff --git a/main/astobj2.c b/main/astobj2.c index 72a171de9..a980ec379 100644 --- a/main/astobj2.c +++ b/main/astobj2.c @@ -525,6 +525,7 @@ int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *fil struct astobj2 *obj = INTERNAL_OBJ(user_data); if (obj == NULL) { + ast_backtrace(); ast_assert(0); return -1; } diff --git a/main/loader.c b/main/loader.c index 5befafb55..10d5485da 100644 --- a/main/loader.c +++ b/main/loader.c @@ -842,6 +842,7 @@ static enum ast_module_load_result start_resource(struct ast_module *mod) return AST_MODULE_LOAD_FAILURE; } + printf ("!!! Going to load %s\n", mod->resource); res = mod->info->load(); switch (res) { diff --git a/main/sorcery.c b/main/sorcery.c index 8b555222d..44e247a38 100644 --- a/main/sorcery.c +++ b/main/sorcery.c @@ -740,7 +740,7 @@ int ast_sorcery_objectset_apply(const struct ast_sorcery *sorcery, void *object, } if (!res && object_type->apply) { - object_type->apply(sorcery, object); + res = object_type->apply(sorcery, object); } return res; @@ -940,6 +940,7 @@ void *ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const ch unsigned int cached = 0; if (!object_type) { + ast_log(LOG_NOTICE, "Can't find object type '%s'\n", type); return NULL; } diff --git a/main/taskprocessor.c b/main/taskprocessor.c index 35076b06e..a8d1c80f9 100644 --- a/main/taskprocessor.c +++ b/main/taskprocessor.c @@ -602,7 +602,6 @@ struct ast_taskprocessor *ast_taskprocessor_get(const char *name, enum ast_tps_o /* Unref listener here since the taskprocessor has gained a reference to the listener */ ao2_ref(listener, -1); return p; - } struct ast_taskprocessor *ast_taskprocessor_create_with_listener(const char *name, struct ast_taskprocessor_listener *listener) diff --git a/main/threadpool.c b/main/threadpool.c index e2fdecc57..1ff76014a 100644 --- a/main/threadpool.c +++ b/main/threadpool.c @@ -983,7 +983,13 @@ static void *worker_start(void *arg) { struct worker_thread *worker = arg; + if (worker->options.thread_start) { + worker->options.thread_start(); + } worker_active(worker); + if (worker->options.thread_end) { + worker->options.thread_end(); + } return NULL; } diff --git a/res/Makefile b/res/Makefile index fec20a2e0..35d09275c 100644 --- a/res/Makefile +++ b/res/Makefile @@ -43,6 +43,9 @@ snmp/agent.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_snmp) $(if $(filter res_ael_share,$(EMBEDDED_MODS)),modules.link,res_ael_share.so): ael/ael_lex.o ael/ael.tab.o ael/pval.o ael/ael_lex.o ael/ael.tab.o ael/pval.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_ael_share) +$(if $(filter res_sip,$(EMBEDDED_MODS)),modules.link,res_sip.so): $(subst .c,.o,$(wildcard res_sip/*.c)) +$(subst .c,.o,$(wildcard res_sip/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,res_sip) + ifneq ($(findstring REBUILD_PARSERS,$(MENUSELECT_CFLAGS)),) ael/ael_lex.c: ael/ael.flex else @@ -67,7 +70,7 @@ endif ael/pval.o: ael/pval.c clean:: - rm -f snmp/*.[oi] ael/*.[oi] ais/*.[oi] stasis_http/*.[oi] + rm -f snmp/*.[oi] ael/*.[oi] ais/*.[oi] stasis_http/*.[oi] res_sip/*.[oi] # Dependencies for res_stasis_http_*.so are generated, so they're in this file include stasis_http.make diff --git a/res/res_sip.c b/res/res_sip.c new file mode 100644 index 000000000..18eead992 --- /dev/null +++ b/res/res_sip.c @@ -0,0 +1,906 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> +/* Needed for SUBSCRIBE, NOTIFY, and PUBLISH method definitions */ +#include <pjsip_simple.h> +#include <pjlib.h> + +#include "asterisk/res_sip.h" +#include "res_sip/include/res_sip_private.h" +#include "asterisk/linkedlists.h" +#include "asterisk/logger.h" +#include "asterisk/lock.h" +#include "asterisk/utils.h" +#include "asterisk/astobj2.h" +#include "asterisk/module.h" +#include "asterisk/threadpool.h" +#include "asterisk/taskprocessor.h" +#include "asterisk/uuid.h" +#include "asterisk/sorcery.h" + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sorcery_config</depend> + <support_level>core</support_level> + ***/ + +static pjsip_endpoint *ast_pjsip_endpoint; + +static struct ast_threadpool *sip_threadpool; + +static int register_service(void *data) +{ + pjsip_module **module = data; + if (!ast_pjsip_endpoint) { + ast_log(LOG_ERROR, "There is no PJSIP endpoint. Unable to register services\n"); + return -1; + } + if (pjsip_endpt_register_module(ast_pjsip_endpoint, *module) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Unable to register module %.*s\n", (int) pj_strlen(&(*module)->name), pj_strbuf(&(*module)->name)); + return -1; + } + ast_debug(1, "Registered SIP service %.*s (%p)\n", (int) pj_strlen(&(*module)->name), pj_strbuf(&(*module)->name), *module); + ast_module_ref(ast_module_info->self); + return 0; +} + +int ast_sip_register_service(pjsip_module *module) +{ + return ast_sip_push_task_synchronous(NULL, register_service, &module); +} + +static int unregister_service(void *data) +{ + pjsip_module **module = data; + ast_module_unref(ast_module_info->self); + if (!ast_pjsip_endpoint) { + return -1; + } + pjsip_endpt_unregister_module(ast_pjsip_endpoint, *module); + ast_debug(1, "Unregistered SIP service %.*s\n", (int) pj_strlen(&(*module)->name), pj_strbuf(&(*module)->name)); + return 0; +} + +void ast_sip_unregister_service(pjsip_module *module) +{ + ast_sip_push_task_synchronous(NULL, unregister_service, &module); +} + +static struct ast_sip_authenticator *registered_authenticator; + +int ast_sip_register_authenticator(struct ast_sip_authenticator *auth) +{ + if (registered_authenticator) { + ast_log(LOG_WARNING, "Authenticator %p is already registered. Cannot register a new one\n", registered_authenticator); + return -1; + } + registered_authenticator = auth; + ast_debug(1, "Registered SIP authenticator module %p\n", auth); + ast_module_ref(ast_module_info->self); + return 0; +} + +void ast_sip_unregister_authenticator(struct ast_sip_authenticator *auth) +{ + if (registered_authenticator != auth) { + ast_log(LOG_WARNING, "Trying to unregister authenticator %p but authenticator %p registered\n", + auth, registered_authenticator); + return; + } + registered_authenticator = NULL; + ast_debug(1, "Unregistered SIP authenticator %p\n", auth); + ast_module_unref(ast_module_info->self); +} + +int ast_sip_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) +{ + if (!registered_authenticator) { + ast_log(LOG_WARNING, "No SIP authenticator registered. Assuming authentication is not required\n"); + return 0; + } + + return registered_authenticator->requires_authentication(endpoint, rdata); +} + +enum ast_sip_check_auth_result ast_sip_check_authentication(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata, pjsip_tx_data *tdata) +{ + if (!registered_authenticator) { + ast_log(LOG_WARNING, "No SIP authenticator registered. Assuming authentication is successful\n"); + return 0; + } + return registered_authenticator->check_authentication(endpoint, rdata, tdata); +} + +static struct ast_sip_outbound_authenticator *registered_outbound_authenticator; + +int ast_sip_register_outbound_authenticator(struct ast_sip_outbound_authenticator *auth) +{ + if (registered_outbound_authenticator) { + ast_log(LOG_WARNING, "Outbound authenticator %p is already registered. Cannot register a new one\n", registered_outbound_authenticator); + return -1; + } + registered_outbound_authenticator = auth; + ast_debug(1, "Registered SIP outbound authenticator module %p\n", auth); + ast_module_ref(ast_module_info->self); + return 0; +} + +void ast_sip_unregister_outbound_authenticator(struct ast_sip_outbound_authenticator *auth) +{ + if (registered_outbound_authenticator != auth) { + ast_log(LOG_WARNING, "Trying to unregister outbound authenticator %p but outbound authenticator %p registered\n", + auth, registered_outbound_authenticator); + return; + } + registered_outbound_authenticator = NULL; + ast_debug(1, "Unregistered SIP outbound authenticator %p\n", auth); + ast_module_unref(ast_module_info->self); +} + +int ast_sip_create_request_with_auth(const char **auths, size_t num_auths, pjsip_rx_data *challenge, + pjsip_transaction *tsx, pjsip_tx_data **new_request) +{ + if (!registered_outbound_authenticator) { + ast_log(LOG_WARNING, "No SIP outbound authenticator registered. Cannot respond to authentication challenge\n"); + return -1; + } + return registered_outbound_authenticator->create_request_with_auth(auths, num_auths, challenge, tsx, new_request); +} + +struct endpoint_identifier_list { + struct ast_sip_endpoint_identifier *identifier; + AST_RWLIST_ENTRY(endpoint_identifier_list) list; +}; + +static AST_RWLIST_HEAD_STATIC(endpoint_identifiers, endpoint_identifier_list); + +int ast_sip_register_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier) +{ + struct endpoint_identifier_list *id_list_item; + SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + + id_list_item = ast_calloc(1, sizeof(*id_list_item)); + if (!id_list_item) { + ast_log(LOG_ERROR, "Unabled to add endpoint identifier. Out of memory.\n"); + return -1; + } + id_list_item->identifier = identifier; + + AST_RWLIST_INSERT_TAIL(&endpoint_identifiers, id_list_item, list); + ast_debug(1, "Registered endpoint identifier %p\n", identifier); + + ast_module_ref(ast_module_info->self); + return 0; +} + +void ast_sip_unregister_endpoint_identifier(struct ast_sip_endpoint_identifier *identifier) +{ + struct endpoint_identifier_list *iter; + SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&endpoint_identifiers, iter, list) { + if (iter->identifier == identifier) { + AST_RWLIST_REMOVE_CURRENT(list); + ast_free(iter); + ast_debug(1, "Unregistered endpoint identifier %p\n", identifier); + ast_module_unref(ast_module_info->self); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; +} + +struct ast_sip_endpoint *ast_sip_identify_endpoint(pjsip_rx_data *rdata) +{ + struct endpoint_identifier_list *iter; + struct ast_sip_endpoint *endpoint = NULL; + SCOPED_LOCK(lock, &endpoint_identifiers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE(&endpoint_identifiers, iter, list) { + ast_assert(iter->identifier->identify_endpoint != NULL); + endpoint = iter->identifier->identify_endpoint(rdata); + if (endpoint) { + break; + } + } + return endpoint; +} + +pjsip_endpoint *ast_sip_get_pjsip_endpoint(void) +{ + return ast_pjsip_endpoint; +} + +static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *user, const pj_str_t *target, pjsip_tpselector *selector) +{ + pj_str_t tmp, local_addr; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED; + int local_port; + char uuid_str[AST_UUID_STR_LEN]; + + if (!user) { + RAII_VAR(struct ast_uuid *, uuid, ast_uuid_generate(), ast_free_ptr); + if (!uuid) { + return -1; + } + user = ast_uuid_to_str(uuid, uuid_str, sizeof(uuid_str)); + } + + /* Parse the provided target URI so we can determine what transport it will end up using */ + pj_strdup_with_null(pool, &tmp, target); + + if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) || + (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) { + return -1; + } + + sip_uri = pjsip_uri_get_uri(uri); + + /* Determine the transport type to use */ + if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) { + type = PJSIP_TRANSPORT_TLS; + } else if (!sip_uri->transport_param.slen) { + type = PJSIP_TRANSPORT_UDP; + } else { + type = pjsip_transport_get_type_from_name(&sip_uri->transport_param); + } + + if (type == PJSIP_TRANSPORT_UNSPECIFIED) { + return -1; + } + + /* If the host is IPv6 turn the transport into an IPv6 version */ + if (pj_strchr(&sip_uri->host, ':')) { + type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6); + } + + /* Get the local bound address for the transport that will be used when communicating with the provided URI */ + if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector, + &local_addr, &local_port) != PJ_SUCCESS) { + return -1; + } + + /* If IPv6 was not specified in the host but is in the transport, set the proper type */ + if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) { + type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6); + } + + from->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + from->slen = pj_ansi_snprintf(from->ptr, PJSIP_MAX_URL_SIZE, + "<%s:%s@%s%.*s%s:%d%s%s>", + (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip", + user, + (type & PJSIP_TRANSPORT_IPV6) ? "[" : "", + (int)local_addr.slen, + local_addr.ptr, + (type & PJSIP_TRANSPORT_IPV6) ? "]" : "", + local_port, + (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "", + (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : ""); + + return 0; +} + +static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector) +{ + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + const char *transport_name = endpoint->transport; + + if (ast_strlen_zero(transport_name)) { + return 0; + } + + transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_name); + + if (!transport || !transport->state) { + return -1; + } + + if (transport->type == AST_SIP_TRANSPORT_UDP) { + selector->type = PJSIP_TPSELECTOR_TRANSPORT; + selector->u.transport = transport->state->transport; + } else if (transport->type == AST_SIP_TRANSPORT_TCP || transport->type == AST_SIP_TRANSPORT_TLS) { + selector->type = PJSIP_TPSELECTOR_LISTENER; + selector->u.listener = transport->state->factory; + } else { + return -1; + } + + return 0; +} + +pjsip_dialog *ast_sip_create_dialog(const struct ast_sip_endpoint *endpoint, const char *uri, const char *request_user) +{ + pj_str_t local_uri = { "sip:temp@temp", 13 }, remote_uri; + pjsip_dialog *dlg = NULL; + const char *outbound_proxy = endpoint->outbound_proxy; + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + static const pj_str_t HCONTACT = { "Contact", 7 }; + + pj_cstr(&remote_uri, uri); + + if (pjsip_dlg_create_uac(pjsip_ua_instance(), &local_uri, NULL, &remote_uri, NULL, &dlg) != PJ_SUCCESS) { + return NULL; + } + + if (sip_get_tpselector_from_endpoint(endpoint, &selector)) { + pjsip_dlg_terminate(dlg); + return NULL; + } + + if (sip_dialog_create_from(dlg->pool, &local_uri, NULL, &remote_uri, &selector)) { + pjsip_dlg_terminate(dlg); + return NULL; + } + + /* Update the dialog with the new local URI, we do it afterwards so we can use the dialog pool for construction */ + pj_strdup_with_null(dlg->pool, &dlg->local.info_str, &local_uri); + dlg->local.info->uri = pjsip_parse_uri(dlg->pool, dlg->local.info_str.ptr, dlg->local.info_str.slen, 0); + dlg->local.contact = pjsip_parse_hdr(dlg->pool, &HCONTACT, local_uri.ptr, local_uri.slen, NULL); + + /* If a request user has been specified and we are permitted to change it, do so */ + if (!ast_strlen_zero(request_user) && (PJSIP_URI_SCHEME_IS_SIP(dlg->target) || PJSIP_URI_SCHEME_IS_SIPS(dlg->target))) { + pjsip_sip_uri *target = pjsip_uri_get_uri(dlg->target); + pj_strdup2(dlg->pool, &target->user, request_user); + } + + /* We have to temporarily bump up the sess_count here so the dialog is not prematurely destroyed */ + dlg->sess_count++; + + pjsip_dlg_set_transport(dlg, &selector); + + if (!ast_strlen_zero(outbound_proxy)) { + pjsip_route_hdr route_set, *route; + static const pj_str_t ROUTE_HNAME = { "Route", 5 }; + pj_str_t tmp; + + pj_list_init(&route_set); + + pj_strdup2_with_null(dlg->pool, &tmp, outbound_proxy); + if (!(route = pjsip_parse_hdr(dlg->pool, &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) { + pjsip_dlg_terminate(dlg); + return NULL; + } + pj_list_push_back(&route_set, route); + + pjsip_dlg_set_route_set(dlg, &route_set); + } + + dlg->sess_count--; + + return dlg; +} + +/* PJSIP doesn't know about the INFO method, so we have to define it ourselves */ +const pjsip_method pjsip_info_method = {PJSIP_OTHER_METHOD, {"INFO", 4} }; + +static struct { + const char *method; + const pjsip_method *pmethod; +} methods [] = { + { "INVITE", &pjsip_invite_method }, + { "CANCEL", &pjsip_cancel_method }, + { "ACK", &pjsip_ack_method }, + { "BYE", &pjsip_bye_method }, + { "REGISTER", &pjsip_register_method }, + { "OPTIONS", &pjsip_options_method }, + { "SUBSCRIBE", &pjsip_subscribe_method }, + { "NOTIFY", &pjsip_notify_method }, + { "PUBLISH", &pjsip_publish_method }, + { "INFO", &pjsip_info_method }, +}; + +static const pjsip_method *get_pjsip_method(const char *method) +{ + int i; + for (i = 0; i < ARRAY_LEN(methods); ++i) { + if (!strcmp(method, methods[i].method)) { + return methods[i].pmethod; + } + } + return NULL; +} + +static int create_in_dialog_request(const pjsip_method *method, struct pjsip_dialog *dlg, pjsip_tx_data **tdata) +{ + if (pjsip_dlg_create_request(dlg, method, -1, tdata) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Unable to create in-dialog request.\n"); + return -1; + } + + return 0; +} + +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); + pj_str_t remote_uri; + pj_str_t from; + pj_pool_t *pool; + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + + if (ast_strlen_zero(uri)) { + 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)); + return -1; + } + + pj_cstr(&remote_uri, contact->uri); + } else { + pj_cstr(&remote_uri, uri); + } + + if (sip_get_tpselector_from_endpoint(endpoint, &selector)) { + ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport selector for endpoint %s\n", + ast_sorcery_object_get_id(endpoint)); + return -1; + } + + pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Outbound request", 256, 256); + + if (!pool) { + ast_log(LOG_ERROR, "Unable to create PJLIB memory pool\n"); + return -1; + } + + if (sip_dialog_create_from(pool, &from, NULL, &remote_uri, &selector)) { + ast_log(LOG_ERROR, "Unable to create From header for %.*s request to endpoint %s\n", + (int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint)); + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + return -1; + } + + if (pjsip_endpt_create_request(ast_sip_get_pjsip_endpoint(), method, &remote_uri, + &from, &remote_uri, &from, NULL, -1, NULL, tdata) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Unable to create outbound %.*s request to endpoint %s\n", + (int) pj_strlen(&method->name), pj_strbuf(&method->name), ast_sorcery_object_get_id(endpoint)); + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + return -1; + } + + /* We can release this pool since request creation copied all the necessary + * data into the outbound request's pool + */ + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool); + return 0; +} + +int ast_sip_create_request(const char *method, struct pjsip_dialog *dlg, + struct ast_sip_endpoint *endpoint, const char *uri, pjsip_tx_data **tdata) +{ + const pjsip_method *pmethod = get_pjsip_method(method); + + if (!pmethod) { + ast_log(LOG_WARNING, "Unknown method '%s'. Cannot send request\n", method); + return -1; + } + + if (dlg) { + return create_in_dialog_request(pmethod, dlg, tdata); + } else { + return create_out_of_dialog_request(pmethod, endpoint, uri, tdata); + } +} + +static int send_in_dialog_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg) +{ + if (pjsip_dlg_send_request(dlg, tdata, -1, NULL) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Unable to send in-dialog request.\n"); + return -1; + } + return 0; +} + +static void send_request_cb(void *token, pjsip_event *e) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, 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) { + return; + } + + if (!ast_sip_create_request_with_auth(endpoint->sip_outbound_auths, endpoint->num_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) { + 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); + return -1; + } + + return 0; +} + +int ast_sip_send_request(pjsip_tx_data *tdata, struct pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint) +{ + 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); + } +} + +int ast_sip_add_header(pjsip_tx_data *tdata, const char *name, const char *value) +{ + pj_str_t hdr_name; + pj_str_t hdr_value; + pjsip_generic_string_hdr *hdr; + + pj_cstr(&hdr_name, name); + pj_cstr(&hdr_value, value); + + hdr = pjsip_generic_string_hdr_create(tdata->pool, &hdr_name, &hdr_value); + + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) hdr); + return 0; +} + +static pjsip_msg_body *ast_body_to_pjsip_body(pj_pool_t *pool, const struct ast_sip_body *body) +{ + pj_str_t type; + pj_str_t subtype; + pj_str_t body_text; + + pj_cstr(&type, body->type); + pj_cstr(&subtype, body->subtype); + pj_cstr(&body_text, body->body_text); + + return pjsip_msg_body_create(pool, &type, &subtype, &body_text); +} + +int ast_sip_add_body(pjsip_tx_data *tdata, const struct ast_sip_body *body) +{ + pjsip_msg_body *pjsip_body = ast_body_to_pjsip_body(tdata->pool, body); + tdata->msg->body = pjsip_body; + return 0; +} + +int ast_sip_add_body_multipart(pjsip_tx_data *tdata, const struct ast_sip_body *bodies[], int num_bodies) +{ + int i; + /* NULL for type and subtype automatically creates "multipart/mixed" */ + pjsip_msg_body *body = pjsip_multipart_create(tdata->pool, NULL, NULL); + + for (i = 0; i < num_bodies; ++i) { + pjsip_multipart_part *part = pjsip_multipart_create_part(tdata->pool); + part->body = ast_body_to_pjsip_body(tdata->pool, bodies[i]); + pjsip_multipart_add_part(tdata->pool, body, part); + } + + tdata->msg->body = body; + return 0; +} + +int ast_sip_append_body(pjsip_tx_data *tdata, const char *body_text) +{ + size_t combined_size = strlen(body_text) + tdata->msg->body->len; + struct ast_str *body_buffer = ast_str_alloca(combined_size); + + ast_str_set(&body_buffer, 0, "%.*s%s", (int) tdata->msg->body->len, (char *) tdata->msg->body->data, body_text); + + tdata->msg->body->data = pj_pool_alloc(tdata->pool, combined_size); + pj_memcpy(tdata->msg->body->data, ast_str_buffer(body_buffer), combined_size); + tdata->msg->body->len = combined_size; + + return 0; +} + +struct ast_taskprocessor *ast_sip_create_serializer(void) +{ + struct ast_taskprocessor *serializer; + RAII_VAR(struct ast_uuid *, uuid, ast_uuid_generate(), ast_free_ptr); + char name[AST_UUID_STR_LEN]; + + if (!uuid) { + return NULL; + } + + ast_uuid_to_str(uuid, name, sizeof(name)); + + serializer = ast_threadpool_serializer(name, sip_threadpool); + if (!serializer) { + return NULL; + } + return serializer; +} + +int ast_sip_push_task(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data) +{ + if (serializer) { + return ast_taskprocessor_push(serializer, sip_task, task_data); + } else { + return ast_threadpool_push(sip_threadpool, sip_task, task_data); + } +} + +struct sync_task_data { + ast_mutex_t lock; + ast_cond_t cond; + int complete; + int fail; + int (*task)(void *); + void *task_data; +}; + +static int sync_task(void *data) +{ + struct sync_task_data *std = data; + std->fail = std->task(std->task_data); + + ast_mutex_lock(&std->lock); + std->complete = 1; + ast_cond_signal(&std->cond); + ast_mutex_unlock(&std->lock); + return std->fail; +} + +int ast_sip_push_task_synchronous(struct ast_taskprocessor *serializer, int (*sip_task)(void *), void *task_data) +{ + /* This method is an onion */ + struct sync_task_data std; + ast_mutex_init(&std.lock); + ast_cond_init(&std.cond, NULL); + std.fail = std.complete = 0; + std.task = sip_task; + std.task_data = task_data; + + if (serializer) { + if (ast_taskprocessor_push(serializer, sync_task, &std)) { + return -1; + } + } else { + if (ast_threadpool_push(sip_threadpool, sync_task, &std)) { + return -1; + } + } + + ast_mutex_lock(&std.lock); + while (!std.complete) { + ast_cond_wait(&std.cond, &std.lock); + } + ast_mutex_unlock(&std.lock); + + ast_mutex_destroy(&std.lock); + ast_cond_destroy(&std.cond); + return std.fail; +} + +void ast_copy_pj_str(char *dest, pj_str_t *src, size_t size) +{ + size_t chars_to_copy = MIN(size - 1, pj_strlen(src)); + memcpy(dest, pj_strbuf(src), chars_to_copy); + dest[chars_to_copy] = '\0'; +} + +pj_caching_pool caching_pool; +pj_pool_t *memory_pool; +pj_thread_t *monitor_thread; +static int monitor_continue; + +static void *monitor_thread_exec(void *endpt) +{ + while (monitor_continue) { + const pj_time_val delay = {0, 10}; + pjsip_endpt_handle_events(ast_pjsip_endpoint, &delay); + } + return NULL; +} + +static void stop_monitor_thread(void) +{ + monitor_continue = 0; + pj_thread_join(monitor_thread); +} + +AST_THREADSTORAGE(pj_thread_storage); +AST_THREADSTORAGE(servant_id_storage); +#define SIP_SERVANT_ID 0xDEFECA7E + +static void sip_thread_start(void) +{ + pj_thread_desc *desc; + pj_thread_t *thread; + uint32_t *servant_id; + + servant_id = ast_threadstorage_get(&servant_id_storage, sizeof(*servant_id)); + if (!servant_id) { + ast_log(LOG_ERROR, "Could not set SIP servant ID in thread-local storage.\n"); + return; + } + *servant_id = SIP_SERVANT_ID; + + desc = ast_threadstorage_get(&pj_thread_storage, sizeof(pj_thread_desc)); + if (!desc) { + ast_log(LOG_ERROR, "Could not get thread desc from thread-local storage. Expect awful things to occur\n"); + return; + } + pj_bzero(*desc, sizeof(*desc)); + + if (pj_thread_register("Asterisk Thread", *desc, &thread) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Couldn't register thread with PJLIB.\n"); + } +} + +int ast_sip_thread_is_servant(void) +{ + uint32_t *servant_id; + + servant_id = ast_threadstorage_get(&servant_id_storage, sizeof(*servant_id)); + if (!servant_id) { + return 0; + } + + return *servant_id == SIP_SERVANT_ID; +} + +static int load_module(void) +{ + /* The third parameter is just copied from + * example code from PJLIB. This can be adjusted + * if necessary. + */ + pj_status_t status; + + /* XXX For the time being, create hard-coded threadpool + * options. Just bump up by five threads every time we + * don't have any available threads. Idle threads time + * out after a minute. No maximum size + */ + struct ast_threadpool_options options = { + .version = AST_THREADPOOL_OPTIONS_VERSION, + .auto_increment = 5, + .max_size = 0, + .idle_timeout = 60, + .initial_size = 0, + .thread_start = sip_thread_start, + }; + sip_threadpool = ast_threadpool_create("SIP", NULL, &options); + + if (pj_init() != PJ_SUCCESS) { + return AST_MODULE_LOAD_DECLINE; + } + + if (pjlib_util_init() != PJ_SUCCESS) { + pj_shutdown(); + return AST_MODULE_LOAD_DECLINE; + } + + pj_caching_pool_init(&caching_pool, NULL, 1024 * 1024); + if (pjsip_endpt_create(&caching_pool.factory, "SIP", &ast_pjsip_endpoint) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Failed to create PJSIP endpoint structure. Aborting load\n"); + goto error; + } + memory_pool = pj_pool_create(&caching_pool.factory, "SIP", 1024, 1024, NULL); + if (!memory_pool) { + ast_log(LOG_ERROR, "Failed to create memory pool for SIP. Aborting load\n"); + goto error; + } + + pjsip_tsx_layer_init_module(ast_pjsip_endpoint); + pjsip_ua_init_module(ast_pjsip_endpoint, NULL); + + monitor_continue = 1; + status = pj_thread_create(memory_pool, "SIP", (pj_thread_proc *) &monitor_thread_exec, + NULL, PJ_THREAD_DEFAULT_STACK_SIZE * 2, 0, &monitor_thread); + if (status != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Failed to start SIP monitor thread. Aborting load\n"); + goto error; + } + + if (ast_res_sip_initialize_configuration()) { + ast_log(LOG_ERROR, "Failed to initialize SIP configuration. Aborting load\n"); + goto error; + } + + if (ast_sip_initialize_distributor()) { + ast_log(LOG_ERROR, "Failed to register distributor module. Aborting load\n"); + goto error; + } + + if (ast_sip_initialize_outbound_authentication()) { + ast_log(LOG_ERROR, "Failed to initialize outbound authentication. Aborting load\n"); + goto error; + } + + ast_res_sip_init_options_handling(0); + +return AST_MODULE_LOAD_SUCCESS; + +error: + ast_res_sip_destroy_configuration(); + if (monitor_thread) { + stop_monitor_thread(); + } + if (memory_pool) { + pj_pool_release(memory_pool); + memory_pool = NULL; + } + if (ast_pjsip_endpoint) { + pjsip_endpt_destroy(ast_pjsip_endpoint); + ast_pjsip_endpoint = NULL; + } + pj_caching_pool_destroy(&caching_pool); + /* XXX Should have a way of stopping monitor thread */ + return AST_MODULE_LOAD_DECLINE; +} + +static int reload_module(void) +{ + if (ast_res_sip_reload_configuration()) { + return AST_MODULE_LOAD_DECLINE; + } + ast_res_sip_init_options_handling(1); + return 0; +} + +static int unload_pjsip(void *data) +{ + if (memory_pool) { + pj_pool_release(memory_pool); + memory_pool = NULL; + } + if (ast_pjsip_endpoint) { + pjsip_endpt_destroy(ast_pjsip_endpoint); + ast_pjsip_endpoint = NULL; + } + pj_caching_pool_destroy(&caching_pool); + return 0; +} + +static int unload_module(void) +{ + ast_res_sip_destroy_configuration(); + if (monitor_thread) { + stop_monitor_thread(); + } + /* The thread this is called from cannot call PJSIP/PJLIB functions, + * so we have to push the work to the threadpool to handle + */ + ast_sip_push_task_synchronous(NULL, unload_pjsip, NULL); + + ast_threadpool_shutdown(sip_threadpool); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Basic SIP resource", + .load = load_module, + .unload = unload_module, + .reload = reload_module, + .load_pri = AST_MODPRI_CHANNEL_DEPEND, +); diff --git a/res/res_sip.exports.in b/res/res_sip.exports.in new file mode 100644 index 000000000..010f90cb1 --- /dev/null +++ b/res/res_sip.exports.in @@ -0,0 +1,52 @@ +{ + global: + LINKER_SYMBOL_PREFIXast_sip_register_service; + LINKER_SYMBOL_PREFIXast_sip_unregister_service; + LINKER_SYMBOL_PREFIXast_sip_register_authenticator; + LINKER_SYMBOL_PREFIXast_sip_unregister_authenticator; + LINKER_SYMBOL_PREFIXast_sip_register_outbound_authenticator; + LINKER_SYMBOL_PREFIXast_sip_unregister_outbound_authenticator; + LINKER_SYMBOL_PREFIXast_sip_register_endpoint_identifier; + LINKER_SYMBOL_PREFIXast_sip_unregister_endpoint_identifier; + LINKER_SYMBOL_PREFIXast_sip_create_serializer; + LINKER_SYMBOL_PREFIXast_sip_push_task; + LINKER_SYMBOL_PREFIXast_sip_push_task_synchronous; + LINKER_SYMBOL_PREFIXast_sip_create_request; + LINKER_SYMBOL_PREFIXast_sip_create_request_with_auth; + LINKER_SYMBOL_PREFIXast_sip_send_request; + LINKER_SYMBOL_PREFIXast_sip_requires_authentication; + LINKER_SYMBOL_PREFIXast_sip_authenticate_request; + LINKER_SYMBOL_PREFIXast_sip_get_authentication_credentials; + LINKER_SYMBOL_PREFIXast_sip_check_authentication; + LINKER_SYMBOL_PREFIXast_sip_create_auth_challenge_response; + LINKER_SYMBOL_PREFIXast_sip_set_outbound_authentication_credentials; + LINKER_SYMBOL_PREFIXast_sip_dialog_setup_outbound_authentication; + LINKER_SYMBOL_PREFIXast_sip_add_digest_to_challenge; + LINKER_SYMBOL_PREFIXast_sip_identify_endpoint; + LINKER_SYMBOL_PREFIXast_sip_add_header; + LINKER_SYMBOL_PREFIXast_sip_add_body; + LINKER_SYMBOL_PREFIXast_sip_add_body_multipart; + LINKER_SYMBOL_PREFIXast_sip_append_body; + LINKER_SYMBOL_PREFIXast_sip_get_pjsip_endpoint; + LINKER_SYMBOL_PREFIXast_sip_endpoint_alloc; + LINKER_SYMBOL_PREFIXast_copy_pj_str; + LINKER_SYMBOL_PREFIXast_sip_get_sorcery; + LINKER_SYMBOL_PREFIXast_sip_create_dialog; + LINKER_SYMBOL_PREFIXast_sip_location_retrieve_aor; + LINKER_SYMBOL_PREFIXast_sip_location_retrieve_first_aor_contact; + LINKER_SYMBOL_PREFIXast_sip_location_retrieve_contact_from_aor_list; + LINKER_SYMBOL_PREFIXast_sip_location_retrieve_aor_contacts; + LINKER_SYMBOL_PREFIXast_sip_location_retrieve_contact; + LINKER_SYMBOL_PREFIXast_sip_location_add_contact; + LINKER_SYMBOL_PREFIXast_sip_location_update_contact; + LINKER_SYMBOL_PREFIXast_sip_location_delete_contact; + LINKER_SYMBOL_PREFIXast_pjsip_rdata_get_endpoint; + LINKER_SYMBOL_PREFIXast_sip_thread_is_servant; + LINKER_SYMBOL_PREFIXast_sip_dialog_set_serializer; + LINKER_SYMBOL_PREFIXast_sip_dialog_set_endpoint; + LINKER_SYMBOL_PREFIXast_sip_dialog_get_endpoint; + LINKER_SYMBOL_PREFIXast_sip_retrieve_auths; + LINKER_SYMBOL_PREFIXast_sip_cleanup_auths; + local: + *; +}; diff --git a/res/res_sip/config_auth.c b/res/res_sip/config_auth.c new file mode 100644 index 000000000..9881dd8c6 --- /dev/null +++ b/res/res_sip/config_auth.c @@ -0,0 +1,120 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjlib.h> +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/sorcery.h" + +static void auth_destroy(void *obj) +{ + struct ast_sip_auth *auth = obj; + ast_string_field_free_memory(auth); +} + +static void *auth_alloc(const char *name) +{ + struct ast_sip_auth *auth = ao2_alloc(sizeof(*auth), auth_destroy); + + if (!auth) { + return NULL; + } + + if (ast_string_field_init(auth, 64)) { + ao2_cleanup(auth); + return NULL; + } + + return auth; +} + +static int auth_type_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_auth *auth = obj; + if (!strcasecmp(var->value, "userpass")) { + auth->type = AST_SIP_AUTH_TYPE_USER_PASS; + } else if (!strcasecmp(var->value, "md5")) { + auth->type = AST_SIP_AUTH_TYPE_MD5; + } else { + ast_log(LOG_WARNING, "Unknown authentication storage type '%s' specified for %s\n", + var->value, var->name); + return -1; + } + return 0; +} + +static int auth_apply(const struct ast_sorcery *sorcery, void *obj) +{ + struct ast_sip_auth *auth = obj; + int res = 0; + + if (ast_strlen_zero(auth->auth_user)) { + ast_log(LOG_ERROR, "No authentication username for auth '%s'\n", + ast_sorcery_object_get_id(auth)); + return -1; + } + + switch (auth->type) { + case AST_SIP_AUTH_TYPE_USER_PASS: + if (ast_strlen_zero(auth->auth_pass)) { + ast_log(LOG_ERROR, "'userpass' authentication specified but no" + "password specified for auth '%s'\n", ast_sorcery_object_get_id(auth)); + res = -1; + } + break; + case AST_SIP_AUTH_TYPE_MD5: + if (ast_strlen_zero(auth->md5_creds)) { + ast_log(LOG_ERROR, "'md5' authentication specified but no md5_cred" + "specified for auth '%s'\n", ast_sorcery_object_get_id(auth)); + res = -1; + } + break; + } + + return res; +} + +/*! \brief Initialize sorcery with auth support */ +int ast_sip_initialize_sorcery_auth(struct ast_sorcery *sorcery) +{ + ast_sorcery_apply_default(sorcery, SIP_SORCERY_AUTH_TYPE, "config", "res_sip.conf,criteria=type=auth"); + + if (ast_sorcery_object_register(sorcery, SIP_SORCERY_AUTH_TYPE, auth_alloc, NULL, auth_apply)) { + return -1; + } + + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "type", "", + OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "username", + "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_user)); + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "password", + "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, auth_pass)); + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "md5_cred", + "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, md5_creds)); + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "realm", + "asterisk", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_auth, realm)); + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_AUTH_TYPE, "nonce_lifetime", + "32", OPT_UINT_T, 0, FLDSET(struct ast_sip_auth, nonce_lifetime)); + ast_sorcery_object_field_register_custom(sorcery, SIP_SORCERY_AUTH_TYPE, "auth_type", + "userpass", auth_type_handler, NULL, 0, 0); + + return 0; +} diff --git a/res/res_sip/config_domain_aliases.c b/res/res_sip/config_domain_aliases.c new file mode 100644 index 000000000..86b4636ea --- /dev/null +++ b/res/res_sip/config_domain_aliases.c @@ -0,0 +1,65 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include "pjsip.h" +#include "pjlib.h" +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/sorcery.h" + +static void domain_alias_destroy(void *obj) +{ + struct ast_sip_domain_alias *alias = obj; + + ast_string_field_free_memory(alias); +} + +static void *domain_alias_alloc(const char *name) +{ + struct ast_sip_domain_alias *alias = ao2_alloc(sizeof(*alias), domain_alias_destroy); + + if (!alias) { + return NULL; + } + + if (ast_string_field_init(alias, 256)) { + ao2_cleanup(alias); + return NULL; + } + + return alias; +} + +/*! \brief Initialize sorcery with domain alias support */ +int ast_sip_initialize_sorcery_domain_alias(struct ast_sorcery *sorcery) +{ + ast_sorcery_apply_default(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, "config", "res_sip.conf,criteria=type=domain_alias"); + + if (ast_sorcery_object_register(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, domain_alias_alloc, NULL, NULL)) { + return -1; + } + + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, "type", "", + OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register(sorcery, SIP_SORCERY_DOMAIN_ALIAS_TYPE, "domain", + "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_domain_alias, domain)); + + return 0; +} diff --git a/res/res_sip/config_transport.c b/res/res_sip/config_transport.c new file mode 100644 index 000000000..eb89ee44e --- /dev/null +++ b/res/res_sip/config_transport.c @@ -0,0 +1,299 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjlib.h> + +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/astobj2.h" +#include "asterisk/sorcery.h" +#include "asterisk/acl.h" + +static int destroy_transport_state(void *data) +{ + pjsip_transport *transport = data; + pjsip_transport_shutdown(transport); + return 0; +} + +/*! \brief Destructor for transport state information */ +static void transport_state_destroy(void *obj) +{ + struct ast_sip_transport_state *state = obj; + + if (state->transport) { + ast_sip_push_task_synchronous(NULL, destroy_transport_state, state->transport); + } +} + +/*! \brief Destructor for transport */ +static void transport_destroy(void *obj) +{ + struct ast_sip_transport *transport = obj; + + ast_string_field_free_memory(transport); + ast_free_ha(transport->localnet); + + if (transport->external_address_refresher) { + ast_dnsmgr_release(transport->external_address_refresher); + } + + ao2_cleanup(transport->state); +} + +/*! \brief Allocator for transport */ +static void *transport_alloc(const char *name) +{ + struct ast_sip_transport *transport = ao2_alloc(sizeof(*transport), transport_destroy); + + if (!transport) { + return NULL; + } + + if (ast_string_field_init(transport, 256)) { + ao2_cleanup(transport); + return NULL; + } + + pjsip_tls_setting_default(&transport->tls); + transport->tls.ciphers = transport->ciphers; + + return transport; +} + +/*! \brief Apply handler for transports */ +static int transport_apply(const struct ast_sorcery *sorcery, void *obj) +{ + struct ast_sip_transport *transport = obj; + RAII_VAR(struct ast_sip_transport *, existing, ast_sorcery_retrieve_by_id(sorcery, "transport", ast_sorcery_object_get_id(obj)), ao2_cleanup); + pj_status_t res = -1; + + if (!existing || !existing->state) { + if (!(transport->state = ao2_alloc(sizeof(*transport->state), transport_state_destroy))) { + ast_log(LOG_ERROR, "Transport state for '%s' could not be allocated\n", ast_sorcery_object_get_id(obj)); + return -1; + } + } else { + transport->state = existing->state; + ao2_ref(transport->state, +1); + } + + /* Once active a transport can not be reconfigured */ + if (transport->state->transport || transport->state->factory) { + return -1; + } + + /* Set default port if not present */ + if (!pj_sockaddr_get_port(&transport->host)) { + pj_sockaddr_set_port(&transport->host, (transport->type == AST_SIP_TRANSPORT_TLS) ? 5061 : 5060); + } + + /* Now that we know what address family we can set up a dnsmgr refresh for the external media address if present */ + if (!ast_strlen_zero(transport->external_signaling_address)) { + if (transport->host.addr.sa_family == pj_AF_INET()) { + transport->external_address.ss.ss_family = AF_INET; + } else if (transport->host.addr.sa_family == pj_AF_INET6()) { + transport->external_address.ss.ss_family = AF_INET6; + } else { + ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n", + ast_sorcery_object_get_id(obj)); + return -1; + } + + if (ast_dnsmgr_lookup(transport->external_signaling_address, &transport->external_address, &transport->external_address_refresher, NULL) < 0) { + ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", ast_sorcery_object_get_id(obj)); + return -1; + } + } + + if (transport->type == AST_SIP_TRANSPORT_UDP) { + if (transport->host.addr.sa_family == pj_AF_INET()) { + res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &transport->host.ipv4, NULL, transport->async_operations, &transport->state->transport); + } else if (transport->host.addr.sa_family == pj_AF_INET6()) { + res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &transport->host.ipv6, NULL, transport->async_operations, &transport->state->transport); + } + } else if (transport->type == AST_SIP_TRANSPORT_TCP) { + pjsip_tcp_transport_cfg cfg; + + pjsip_tcp_transport_cfg_default(&cfg, transport->host.addr.sa_family); + cfg.bind_addr = transport->host; + cfg.async_cnt = transport->async_operations; + + res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &transport->state->factory); + } else if (transport->type == AST_SIP_TRANSPORT_TLS) { + transport->tls.ca_list_file = pj_str((char*)transport->ca_list_file); + transport->tls.cert_file = pj_str((char*)transport->cert_file); + transport->tls.privkey_file = pj_str((char*)transport->privkey_file); + transport->tls.password = pj_str((char*)transport->password); + + res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &transport->tls, &transport->host, NULL, transport->async_operations, &transport->state->factory); + } + + if (res != PJ_SUCCESS) { + char msg[PJ_ERR_MSG_SIZE]; + + pjsip_strerror(res, msg, sizeof(msg)); + ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg); + return -1; + } + return 0; +} + +/*! \brief Custom handler for turning a string protocol into an enum */ +static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_transport *transport = obj; + + if (!strcasecmp(var->value, "udp")) { + transport->type = AST_SIP_TRANSPORT_UDP; + } else if (!strcasecmp(var->value, "tcp")) { + transport->type = AST_SIP_TRANSPORT_TCP; + } else if (!strcasecmp(var->value, "tls")) { + transport->type = AST_SIP_TRANSPORT_TLS; + } else { + /* TODO: Implement websockets */ + return -1; + } + + return 0; +} + +/*! \brief Custom handler for turning a string bind into a pj_sockaddr */ +static int transport_bind_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_transport *transport = obj; + pj_str_t buf; + + return (pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, var->value), &transport->host) != PJ_SUCCESS) ? -1 : 0; +} + +/*! \brief Custom handler for TLS boolean settings */ +static int transport_tls_bool_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_transport *transport = obj; + + if (!strcasecmp(var->name, "verify_server")) { + transport->tls.verify_server = ast_true(var->value) ? PJ_TRUE : PJ_FALSE; + } else if (!strcasecmp(var->name, "verify_client")) { + transport->tls.verify_client = ast_true(var->value) ? PJ_TRUE : PJ_FALSE; + } else if (!strcasecmp(var->name, "require_client_cert")) { + transport->tls.require_client_cert = ast_true(var->value) ? PJ_TRUE : PJ_FALSE; + } else { + return -1; + } + + return 0; +} + +/*! \brief Custom handler for TLS method setting */ +static int transport_tls_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_transport *transport = obj; + + if (!strcasecmp(var->value, "default")) { + transport->tls.method = PJSIP_SSL_DEFAULT_METHOD; + } else if (!strcasecmp(var->value, "unspecified")) { + transport->tls.method = PJSIP_SSL_UNSPECIFIED_METHOD; + } else if (!strcasecmp(var->value, "tlsv1")) { + transport->tls.method = PJSIP_TLSV1_METHOD; + } else if (!strcasecmp(var->value, "sslv2")) { + transport->tls.method = PJSIP_SSLV2_METHOD; + } else if (!strcasecmp(var->value, "sslv3")) { + transport->tls.method = PJSIP_SSLV3_METHOD; + } else if (!strcasecmp(var->value, "sslv23")) { + transport->tls.method = PJSIP_SSLV23_METHOD; + } else { + return -1; + } + + return 0; +} + +/*! \brief Custom handler for TLS cipher setting */ +static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_transport *transport = obj; + pj_ssl_cipher cipher; + + if (transport->tls.ciphers_num == (SIP_TLS_MAX_CIPHERS - 1)) { + return -1; + } + + /* TODO: Check this over/tweak - it's taken from pjsua for now */ + if (!strnicmp(var->value, "0x", 2)) { + pj_str_t cipher_st = pj_str((char*)var->value + 2); + cipher = pj_strtoul2(&cipher_st, NULL, 16); + } else { + cipher = atoi(var->value); + } + + if (pj_ssl_cipher_is_supported(cipher)) { + transport->ciphers[transport->tls.ciphers_num++] = cipher; + return 0; + } else { + ast_log(LOG_ERROR, "Cipher '%s' is unsupported\n", var->value); + return -1; + } +} + +/*! \brief Custom handler for localnet setting */ +static int transport_localnet_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_transport *transport = obj; + int error = 0; + + if (!(transport->localnet = ast_append_ha("d", var->value, transport->localnet, &error))) { + return -1; + } + + return error; +} + +/*! \brief Initialize sorcery with transport support */ +int ast_sip_initialize_sorcery_transport(struct ast_sorcery *sorcery) +{ + ast_sorcery_apply_default(sorcery, "transport", "config", "res_sip.conf,criteria=type=transport"); + + if (ast_sorcery_object_register(sorcery, "transport", transport_alloc, NULL, transport_apply)) { + return -1; + } + + ast_sorcery_object_field_register(sorcery, "transport", "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "protocol", "udp", transport_protocol_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "bind", "", transport_bind_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sorcery, "transport", "async_operations", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, async_operations)); + ast_sorcery_object_field_register(sorcery, "transport", "ca_list_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, ca_list_file)); + ast_sorcery_object_field_register(sorcery, "transport", "cert_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, cert_file)); + ast_sorcery_object_field_register(sorcery, "transport", "privkey_file", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, privkey_file)); + ast_sorcery_object_field_register(sorcery, "transport", "password", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, password)); + ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_signaling_address)); + ast_sorcery_object_field_register(sorcery, "transport", "external_signaling_port", "0", OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, external_signaling_port), 0, 65535); + ast_sorcery_object_field_register(sorcery, "transport", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, external_media_address)); + ast_sorcery_object_field_register(sorcery, "transport", "domain", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_transport, domain)); + ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_server", "", transport_tls_bool_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "verify_client", "", transport_tls_bool_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "require_client_cert", "", transport_tls_bool_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "method", "", transport_tls_method_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "cipher", "", transport_tls_cipher_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sorcery, "transport", "localnet", "", transport_localnet_handler, NULL, 0, 0); + + return 0; +} diff --git a/res/res_sip/include/res_sip_private.h b/res/res_sip/include/res_sip_private.h new file mode 100644 index 000000000..318510aae --- /dev/null +++ b/res/res_sip/include/res_sip_private.h @@ -0,0 +1,57 @@ +/* + * res_sip.h + * + * Created on: Jan 25, 2013 + * Author: mjordan + */ + +#ifndef RES_SIP_PRIVATE_H_ +#define RES_SIP_PRIVATE_H_ + +struct ao2_container; + +/*! + * \brief Initialize the configuration for res_sip + */ +int ast_res_sip_initialize_configuration(void); + +/*! + * \brief Annihilate the configuration objects + */ +void ast_res_sip_destroy_configuration(void); + +/*! + * \brief Reload the configuration + */ +int ast_res_sip_reload_configuration(void); + +/*! + * \brief Initialize OPTIONS request handling. + * + * XXX This currently includes qualifying peers. It shouldn't. + * That should go into a registrar. When that occurs, we won't + * need the reload stuff. + * + * \param reload Reload options handling + * + * \retval 0 on success + * \retval other on failure + */ +int ast_res_sip_init_options_handling(int reload); + +/*! + * \brief Initialize outbound authentication support + * + * \retval 0 Success + * \retval non-zero Failure + */ +int ast_sip_initialize_outbound_authentication(void); + +/*! + * \brief Get the current defined endpoints + * + * \retval The current endpoints loaded by res_sip + */ +struct ao2_container *ast_res_sip_get_endpoints(void); + +#endif /* RES_SIP_PRIVATE_H_ */ diff --git a/res/res_sip/location.c b/res/res_sip/location.c new file mode 100644 index 000000000..91521c813 --- /dev/null +++ b/res/res_sip/location.c @@ -0,0 +1,262 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" +#include "pjsip.h" +#include "pjlib.h" + +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/astobj2.h" +#include "asterisk/sorcery.h" + +/*! \brief Destructor for AOR */ +static void aor_destroy(void *obj) +{ + struct ast_sip_aor *aor = obj; + + ao2_cleanup(aor->permanent_contacts); + ast_string_field_free_memory(aor); +} + +/*! \brief Allocator for AOR */ +static void *aor_alloc(const char *name) +{ + struct ast_sip_aor *aor = ao2_alloc_options(sizeof(struct ast_sip_aor), aor_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!aor) { + return NULL; + } + ast_string_field_init(aor, 128); + return aor; +} + +/*! \brief Destructor for contact */ +static void contact_destroy(void *obj) +{ + struct ast_sip_contact *contact = obj; + + ast_string_field_free_memory(contact); +} + +/*! \brief Allocator for contact */ +static void *contact_alloc(const char *name) +{ + struct ast_sip_contact *contact = ao2_alloc_options(sizeof(*contact), contact_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK); + + if (!contact) { + return NULL; + } + + if (ast_string_field_init(contact, 256)) { + ao2_cleanup(contact); + return NULL; + } + + return contact; +} + +struct ast_sip_aor *ast_sip_location_retrieve_aor(const char *aor_name) +{ + return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "aor", aor_name); +} + +/*! \brief Internal callback function which deletes and unlinks any expired contacts */ +static int contact_expire(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + + /* If the contact has not yet expired it is valid */ + if (ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) > 0) { + return 0; + } + + ast_sip_location_delete_contact(contact); + + return CMP_MATCH; +} + +/*! \brief Internal callback function which links static contacts into another container */ +static int contact_link_static(void *obj, void *arg, int flags) +{ + struct ao2_container *dest = arg; + + ao2_link_flags(dest, obj, OBJ_NOLOCK); + return 0; +} + +/*! \brief Simple callback function which returns immediately, used to grab the first contact of an AOR */ +static int contact_find_first(void *obj, void *arg, int flags) +{ + return CMP_MATCH | CMP_STOP; +} + +struct ast_sip_contact *ast_sip_location_retrieve_first_aor_contact(const struct ast_sip_aor *aor) +{ + RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); + struct ast_sip_contact *contact; + + contacts = ast_sip_location_retrieve_aor_contacts(aor); + if (!contacts || (ao2_container_count(contacts) == 0)) { + return NULL; + } + + contact = ao2_callback(contacts, OBJ_NOLOCK, contact_find_first, NULL); + return contact; +} + +struct ao2_container *ast_sip_location_retrieve_aor_contacts(const struct ast_sip_aor *aor) +{ + /* Give enough space for ^ at the beginning and ;@ at the end, since that is our object naming scheme */ + char regex[strlen(ast_sorcery_object_get_id(aor)) + 4]; + struct ao2_container *contacts; + + snprintf(regex, sizeof(regex), "^%s;@", ast_sorcery_object_get_id(aor)); + + if (!(contacts = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "contact", regex))) { + return NULL; + } + + /* Prune any expired contacts and delete them, we do this first because static contacts can never expire */ + ao2_callback(contacts, OBJ_NOLOCK | OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, contact_expire, NULL); + + /* Add any permanent contacts from the AOR */ + if (aor->permanent_contacts) { + ao2_callback(aor->permanent_contacts, OBJ_NOLOCK | OBJ_NODATA, contact_link_static, contacts); + } + + return contacts; +} + +struct ast_sip_contact *ast_sip_location_retrieve_contact_from_aor_list(const char *aor_list) +{ + char *aor_name; + char *rest; + struct ast_sip_contact *contact = NULL; + + /* If the location is still empty we have nowhere to go */ + if (ast_strlen_zero(aor_list) || !(rest = ast_strdupa(aor_list))) { + ast_log(LOG_WARNING, "Unable to determine contacts from empty aor list\n"); + return NULL; + } + + while ((aor_name = strsep(&rest, ","))) { + RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup); + RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); + + if (!aor) { + continue; + } + contact = ast_sip_location_retrieve_first_aor_contact(aor); + /* If a valid contact is available use its URI for dialing */ + if (contact) { + break; + } + } + + return contact; +} + +struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_name) +{ + 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) +{ + char name[AST_UUID_STR_LEN]; + RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + + snprintf(name, sizeof(name), "%s;@%s", ast_sorcery_object_get_id(aor), uri); + + if (!(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name))) { + return -1; + } + + ast_string_field_set(contact, uri, uri); + contact->expiration_time = expiration_time; + + return ast_sorcery_create(ast_sip_get_sorcery(), contact); +} + +int ast_sip_location_update_contact(struct ast_sip_contact *contact) +{ + return ast_sorcery_update(ast_sip_get_sorcery(), contact); +} + +int ast_sip_location_delete_contact(struct ast_sip_contact *contact) +{ + return ast_sorcery_delete(ast_sip_get_sorcery(), contact); +} + +/*! \brief Custom handler for translating from a string timeval to actual structure */ +static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_contact *contact = obj; + return ast_get_timeval(var->value, &contact->expiration_time, ast_tv(0, 0), NULL); +} + +/*! \brief Custom handler for translating from an actual structure timeval to string */ +static int expiration_struct2str(const void *obj, const intptr_t *args, char **buf) +{ + const struct ast_sip_contact *contact = obj; + return (ast_asprintf(buf, "%lu", contact->expiration_time.tv_sec) < 0) ? -1 : 0; +} + +/*! \brief Custom handler for permanent URIs */ +static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_aor *aor = obj; + RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + + if ((!aor->permanent_contacts && !(aor->permanent_contacts = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) || + !(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", NULL))) { + return -1; + } + + ast_string_field_set(contact, uri, var->value); + ao2_link_flags(aor->permanent_contacts, contact, OBJ_NOLOCK); + + return 0; +} + +/*! \brief Initialize sorcery with location support */ +int ast_sip_initialize_sorcery_location(struct ast_sorcery *sorcery) +{ + ast_sorcery_apply_default(sorcery, "contact", "astdb", "registrar"); + ast_sorcery_apply_default(sorcery, "aor", "config", "res_sip.conf,criteria=type=aor"); + + if (ast_sorcery_object_register(sorcery, "contact", contact_alloc, NULL, NULL) || + ast_sorcery_object_register(sorcery, "aor", aor_alloc, NULL, NULL)) { + return -1; + } + + 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_custom(sorcery, "contact", "expiration_time", "", expiration_str2struct, expiration_struct2str, 0, 0); + + ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration)); + ast_sorcery_object_field_register(sorcery, "aor", "maximum_expiration", "7200", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, maximum_expiration)); + ast_sorcery_object_field_register(sorcery, "aor", "default_expiration", "3600", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, default_expiration)); + ast_sorcery_object_field_register(sorcery, "aor", "max_contacts", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, max_contacts)); + ast_sorcery_object_field_register(sorcery, "aor", "remove_existing", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_aor, remove_existing)); + 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)); + + return 0; +} diff --git a/res/res_sip/sip_configuration.c b/res/res_sip/sip_configuration.c new file mode 100644 index 000000000..489ba6aec --- /dev/null +++ b/res/res_sip/sip_configuration.c @@ -0,0 +1,463 @@ +/* + * sip_cli_commands.c + * + * Created on: Jan 25, 2013 + * Author: mjordan + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "include/res_sip_private.h" +#include "asterisk/cli.h" +#include "asterisk/astobj2.h" +#include "asterisk/utils.h" +#include "asterisk/sorcery.h" +#include "asterisk/callerid.h" + +static struct ast_sorcery *sip_sorcery; + +static char *handle_cli_show_endpoints(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup); + struct ao2_iterator it_endpoints; + struct ast_sip_endpoint *endpoint; + + switch (cmd) { + case CLI_INIT: + e->command = "sip show endpoints"; + e->usage = + "Usage: sip show endpoints\n" + " Show the registered SIP endpoints\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + endpoints = ast_res_sip_get_endpoints(); + if (!endpoints) { + return CLI_FAILURE; + } + + if (!ao2_container_count(endpoints)) { + ast_cli(a->fd, "No endpoints found\n"); + return CLI_SUCCESS; + } + + ast_cli(a->fd, "Endpoints:\n"); + it_endpoints = ao2_iterator_init(endpoints, 0); + while ((endpoint = ao2_iterator_next(&it_endpoints))) { + ast_cli(a->fd, "%s\n", ast_sorcery_object_get_id(endpoint)); + ao2_ref(endpoint, -1); + } + ao2_iterator_destroy(&it_endpoints); + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_commands[] = { + AST_CLI_DEFINE(handle_cli_show_endpoints, "Show SIP Endpoints"), +}; + +static int dtmf_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + if (!strcasecmp(var->value, "rfc4733")) { + endpoint->dtmf = AST_SIP_DTMF_RFC_4733; + } else if (!strcasecmp(var->value, "inband")) { + endpoint->dtmf = AST_SIP_DTMF_INBAND; + } else if (!strcasecmp(var->value, "info")) { + endpoint->dtmf = AST_SIP_DTMF_INFO; + } else if (!strcasecmp(var->value, "none")) { + endpoint->dtmf = AST_SIP_DTMF_NONE; + } else { + return -1; + } + + return 0; +} + +static int prack_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + if (ast_true(var->value)) { + endpoint->extensions |= PJSIP_INV_SUPPORT_100REL; + } else if (ast_false(var->value)) { + endpoint->extensions &= PJSIP_INV_SUPPORT_100REL; + } else if (!strcasecmp(var->value, "required")) { + endpoint->extensions |= PJSIP_INV_REQUIRE_100REL; + } else { + return -1; + } + + return 0; +} + +static int timers_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + if (ast_true(var->value)) { + endpoint->extensions |= PJSIP_INV_SUPPORT_TIMER; + } else if (ast_false(var->value)) { + endpoint->extensions &= PJSIP_INV_SUPPORT_TIMER; + } else if (!strcasecmp(var->value, "required")) { + endpoint->extensions |= PJSIP_INV_REQUIRE_TIMER; + } else if (!strcasecmp(var->value, "always")) { + endpoint->extensions |= PJSIP_INV_ALWAYS_USE_TIMER; + } else { + return -1; + } + + return 0; +} + +static void destroy_auths(const char **auths, size_t num_auths) +{ + int i; + for (i = 0; i < num_auths; ++i) { + ast_free((char *) auths[i]); + } + ast_free(auths); +} + +#define AUTH_INCREMENT 4 + +static const char **auth_alloc(const char *value, size_t *num_auths) +{ + char *auths = ast_strdupa(value); + char *val; + int num_alloced = 0; + const char **alloced_auths = NULL; + + while ((val = strsep(&auths, ","))) { + if (*num_auths >= num_alloced) { + size_t size; + num_alloced += AUTH_INCREMENT; + size = num_alloced * sizeof(char *); + alloced_auths = ast_realloc(alloced_auths, size); + if (!alloced_auths) { + goto failure; + } + } + alloced_auths[*num_auths] = ast_strdup(val); + if (!alloced_auths[*num_auths]) { + goto failure; + } + ++(*num_auths); + } + return alloced_auths; + +failure: + destroy_auths(alloced_auths, *num_auths); + return NULL; +} + +static int inbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + endpoint->sip_inbound_auths = auth_alloc(var->value, &endpoint->num_inbound_auths); + if (!endpoint->sip_inbound_auths) { + return -1; + } + return 0; +} +static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + endpoint->sip_outbound_auths = auth_alloc(var->value, &endpoint->num_outbound_auths); + if (!endpoint->sip_outbound_auths) { + return -1; + } + return 0; +} + +static int ident_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + char *idents = ast_strdupa(var->value); + char *val; + + while ((val = strsep(&idents, ","))) { + if (!strcasecmp(val, "username")) { + endpoint->ident_method |= AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME; + } else if (!strcasecmp(val, "location")) { + endpoint->ident_method |= AST_SIP_ENDPOINT_IDENTIFY_BY_LOCATION; + } else { + ast_log(LOG_ERROR, "Unrecognized identification method %s specified for endpoint %s\n", + val, ast_sorcery_object_get_id(endpoint)); + return -1; + } + } + return 0; +} + +static int direct_media_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + if (!strcasecmp(var->value, "invite") || !strcasecmp(var->value, "reinvite")) { + endpoint->direct_media_method = AST_SIP_SESSION_REFRESH_METHOD_INVITE; + } else if (!strcasecmp(var->value, "update")) { + endpoint->direct_media_method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE; + } else { + ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n", + var->value, var->name, ast_sorcery_object_get_id(endpoint)); + return -1; + } + return 0; +} + +static int direct_media_glare_mitigation_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + if (!strcasecmp(var->value, "none")) { + endpoint->direct_media_glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE; + } else if (!strcasecmp(var->value, "outgoing")) { + endpoint->direct_media_glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING; + } else if (!strcasecmp(var->value, "incoming")) { + endpoint->direct_media_glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING; + } else { + ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n", + var->value, var->name, ast_sorcery_object_get_id(endpoint)); + return -1; + } + + return 0; +} + +static int caller_id_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + char cid_name[80] = { '\0' }; + char cid_num[80] = { '\0' }; + + ast_callerid_split(var->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num)); + if (!ast_strlen_zero(cid_name)) { + endpoint->id.name.str = ast_strdup(cid_name); + if (!endpoint->id.name.str) { + return -1; + } + endpoint->id.name.valid = 1; + } + if (!ast_strlen_zero(cid_num)) { + endpoint->id.number.str = ast_strdup(cid_num); + if (!endpoint->id.number.str) { + return -1; + } + endpoint->id.number.valid = 1; + } + return 0; +} + +static int caller_id_privacy_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + int callingpres = ast_parse_caller_presentation(var->value); + if (callingpres == -1 && sscanf(var->value, "%d", &callingpres) != 1) { + return -1; + } + endpoint->id.number.presentation = callingpres; + endpoint->id.name.presentation = callingpres; + return 0; +} + +static int caller_id_tag_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ast_sip_endpoint *endpoint = obj; + endpoint->id.tag = ast_strdup(var->value); + return endpoint->id.tag ? 0 : -1; +} + +static void *sip_nat_hook_alloc(const char *name) +{ + return ao2_alloc(sizeof(struct ast_sip_nat_hook), NULL); +} + +int ast_res_sip_initialize_configuration(void) +{ + if (ast_cli_register_multiple(cli_commands, ARRAY_LEN(cli_commands))) { + return -1; + } + + if (!(sip_sorcery = ast_sorcery_open())) { + ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n"); + return -1; + } + + ast_sorcery_apply_config(sip_sorcery, "res_sip"); + + if (ast_sip_initialize_sorcery_auth(sip_sorcery)) { + ast_log(LOG_ERROR, "Failed to register SIP authentication support\n"); + ast_sorcery_unref(sip_sorcery); + sip_sorcery = NULL; + return -1; + } + + ast_sorcery_apply_default(sip_sorcery, "endpoint", "config", "res_sip.conf,criteria=type=endpoint"); + + ast_sorcery_apply_default(sip_sorcery, "nat_hook", "memory", NULL); + + if (ast_sorcery_object_register(sip_sorcery, "endpoint", ast_sip_endpoint_alloc, NULL, NULL)) { + ast_log(LOG_ERROR, "Failed to register SIP endpoint object with sorcery\n"); + ast_sorcery_unref(sip_sorcery); + sip_sorcery = NULL; + return -1; + } + + ast_sorcery_object_register(sip_sorcery, "nat_hook", sip_nat_hook_alloc, NULL, NULL); + + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disallow", "", OPT_CODEC_T, 0, FLDSET(struct ast_sip_endpoint, prefs, codecs)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow", "", OPT_CODEC_T, 1, FLDSET(struct ast_sip_endpoint, prefs, codecs)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_endpoint, qualify_frequency), 0, 86400); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmfmode", "rfc4733", dtmf_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_ipv6)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_symmetric)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ice_support", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, ice_support)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_ptime", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, use_ptime)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_rport", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, force_rport)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rewrite_contact", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rewrite_contact)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, outbound_proxy)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mohsuggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, min_se)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_sess_expires", "1800", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, sess_expires)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, external_media_address)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username,location", ident_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, direct_media)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disable_direct_media_on_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, disable_direct_media_on_nat)); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid", "", caller_id_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_privacy", "", caller_id_privacy_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "callerid_tag", "", caller_id_tag_handler, NULL, 0, 0); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_inbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, trust_id_inbound)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "trust_id_outbound", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, trust_id_outbound)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_pai", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_pai)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "send_rpid", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, send_rpid)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mailboxes", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mailboxes)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aggregate_mwi", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, aggregate_mwi)); + + if (ast_sip_initialize_sorcery_transport(sip_sorcery)) { + ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n"); + ast_sorcery_unref(sip_sorcery); + sip_sorcery = NULL; + return -1; + } + + if (ast_sip_initialize_sorcery_location(sip_sorcery)) { + ast_log(LOG_ERROR, "Failed to register SIP location support with sorcery\n"); + ast_sorcery_unref(sip_sorcery); + sip_sorcery = NULL; + return -1; + } + + if (ast_sip_initialize_sorcery_domain_alias(sip_sorcery)) { + ast_log(LOG_ERROR, "Failed to register SIP domain aliases support with sorcery\n"); + ast_sorcery_unref(sip_sorcery); + sip_sorcery = NULL; + return -1; + } + + ast_sorcery_load(sip_sorcery); + + return 0; +} + +void ast_res_sip_destroy_configuration(void) +{ + ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); + ast_sorcery_unref(sip_sorcery); +} + +int ast_res_sip_reload_configuration(void) +{ + if (sip_sorcery) { + ast_sorcery_reload(sip_sorcery); + } + return 0; +} + +static void endpoint_destructor(void* obj) +{ + struct ast_sip_endpoint *endpoint = obj; + + ast_string_field_free_memory(endpoint); + + if (endpoint->codecs) { + ast_format_cap_destroy(endpoint->codecs); + } + destroy_auths(endpoint->sip_inbound_auths, endpoint->num_inbound_auths); + destroy_auths(endpoint->sip_outbound_auths, endpoint->num_outbound_auths); + ast_party_id_free(&endpoint->id); +} + +void *ast_sip_endpoint_alloc(const char *name) +{ + struct ast_sip_endpoint *endpoint = ao2_alloc(sizeof(*endpoint), endpoint_destructor); + if (!endpoint) { + return NULL; + } + if (ast_string_field_init(endpoint, 64)) { + ao2_cleanup(endpoint); + return NULL; + } + if (!(endpoint->codecs = ast_format_cap_alloc_nolock())) { + ao2_cleanup(endpoint); + return NULL; + } + ast_party_id_init(&endpoint->id); + return endpoint; +} + +struct ao2_container *ast_res_sip_get_endpoints(void) +{ + struct ao2_container *endpoints; + + endpoints = ast_sorcery_retrieve_by_fields(sip_sorcery, "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + + return endpoints; +} + +int ast_sip_retrieve_auths(const char *auth_names[], size_t num_auths, struct ast_sip_auth **out) +{ + int i; + + for (i = 0; i < num_auths; ++i) { + out[i] = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, auth_names[i]); + if (!out[i]) { + ast_log(LOG_NOTICE, "Couldn't find auth '%s'. Cannot authenticate\n", auth_names[i]); + return -1; + } + } + + return 0; +} + +void ast_sip_cleanup_auths(struct ast_sip_auth *auths[], size_t num_auths) +{ + int i; + for (i = 0; i < num_auths; ++i) { + ao2_cleanup(auths[i]); + } +} + +struct ast_sorcery *ast_sip_get_sorcery(void) +{ + return sip_sorcery; +} + diff --git a/res/res_sip/sip_distributor.c b/res/res_sip/sip_distributor.c new file mode 100644 index 000000000..766261089 --- /dev/null +++ b/res/res_sip/sip_distributor.c @@ -0,0 +1,239 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" + +static int distribute(void *data); +static pj_bool_t distributor(pjsip_rx_data *rdata); + +static pjsip_module distributor_mod = { + .name = {"Request Distributor", 19}, + .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 6, + .on_rx_request = distributor, + .on_rx_response = distributor, +}; + +/*! Dialog-specific information the distributor uses */ +struct distributor_dialog_data { + /* Serializer to distribute tasks to for this dialog */ + struct ast_taskprocessor *serializer; + /* Endpoint associated with this dialog */ + struct ast_sip_endpoint *endpoint; +}; + +/*! + * \internal + * + * \note Call this with the dialog locked + */ +static struct distributor_dialog_data *distributor_dialog_data_alloc(pjsip_dialog *dlg) +{ + struct distributor_dialog_data *dist; + + dist = PJ_POOL_ZALLOC_T(dlg->pool, struct distributor_dialog_data); + pjsip_dlg_set_mod_data(dlg, distributor_mod.id, dist); + + return dist; +} + +void ast_sip_dialog_set_serializer(pjsip_dialog *dlg, struct ast_taskprocessor *serializer) +{ + struct distributor_dialog_data *dist; + SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); + + dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + if (!dist) { + dist = distributor_dialog_data_alloc(dlg); + } + dist->serializer = serializer; +} + +void ast_sip_dialog_set_endpoint(pjsip_dialog *dlg, struct ast_sip_endpoint *endpoint) +{ + struct distributor_dialog_data *dist; + SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); + + dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + if (!dist) { + dist = distributor_dialog_data_alloc(dlg); + } + dist->endpoint = endpoint; +} + +struct ast_sip_endpoint *ast_sip_dialog_get_endpoint(pjsip_dialog *dlg) +{ + struct distributor_dialog_data *dist; + SCOPED_LOCK(lock, dlg, pjsip_dlg_inc_lock, pjsip_dlg_dec_lock); + + dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + if (!dist || !dist->endpoint) { + return NULL; + } + ao2_ref(dist->endpoint, +1); + return dist->endpoint; +} + +static pj_bool_t distributor(pjsip_rx_data *rdata) +{ + pjsip_dialog *dlg = pjsip_ua_find_dialog(&rdata->msg_info.cid->id, &rdata->msg_info.to->tag, &rdata->msg_info.from->tag, PJ_TRUE); + struct distributor_dialog_data *dist = NULL; + struct ast_taskprocessor *serializer = NULL; + pjsip_rx_data *clone; + + pjsip_rx_data_clone(rdata, 0, &clone); + if (dlg) { + dist = pjsip_dlg_get_mod_data(dlg, distributor_mod.id); + if (dist) { + serializer = dist->serializer; + clone->endpt_info.mod_data[distributor_mod.id] = dist->endpoint; + } + } + + ast_sip_push_task(serializer, distribute, clone); + + if (dlg) { + pjsip_dlg_dec_lock(dlg); + } + + return PJ_TRUE; +} + +static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata); + +static pjsip_module endpoint_mod = { + .name = {"Endpoint Identifier", 19}, + .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 3, + .on_rx_request = endpoint_lookup, +}; + +static pj_bool_t endpoint_lookup(pjsip_rx_data *rdata) +{ + struct ast_sip_endpoint *endpoint; + int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; + + endpoint = rdata->endpt_info.mod_data[distributor_mod.id]; + if (endpoint) { + /* Bumping the refcount makes refcounting consistent whether an endpoint + * is looked up or not */ + ao2_ref(endpoint, +1); + } else { + endpoint = ast_sip_identify_endpoint(rdata); + } + + if (!endpoint && !is_ack) { + /* XXX When we do an alwaysauthreject-like option, we'll need to take that into account + * for this response. Either that, or have a pseudo-endpoint to pass along so that authentication + * will fail + */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + return PJ_TRUE; + } + rdata->endpt_info.mod_data[endpoint_mod.id] = endpoint; + return PJ_FALSE; +} + +static pj_bool_t authenticate(pjsip_rx_data *rdata) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); + int is_ack = rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD; + + ast_assert(endpoint != NULL); + + if (!is_ack && ast_sip_requires_authentication(endpoint, rdata)) { + pjsip_tx_data *tdata; + pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 401, NULL, &tdata); + switch (ast_sip_check_authentication(endpoint, rdata, tdata)) { + case AST_SIP_AUTHENTICATION_CHALLENGE: + /* Send the 401 we created for them */ + pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL); + return PJ_TRUE; + case AST_SIP_AUTHENTICATION_SUCCESS: + pjsip_tx_data_dec_ref(tdata); + return PJ_FALSE; + case AST_SIP_AUTHENTICATION_FAILED: + pjsip_tx_data_dec_ref(tdata); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + return PJ_TRUE; + case AST_SIP_AUTHENTICATION_ERROR: + pjsip_tx_data_dec_ref(tdata); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); + return PJ_TRUE; + } + } + + return PJ_FALSE; +} + +static pjsip_module auth_mod = { + .name = {"Request Authenticator", 21}, + .priority = PJSIP_MOD_PRIORITY_APPLICATION - 1, + .on_rx_request = authenticate, +}; + +static int distribute(void *data) +{ + static pjsip_process_rdata_param param = { + .start_mod = &distributor_mod, + .idx_after_start = 1, + }; + pj_bool_t handled; + pjsip_rx_data *rdata = data; + int is_request = rdata->msg_info.msg->type == PJSIP_REQUEST_MSG; + int is_ack = is_request ? rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD : 0; + struct ast_sip_endpoint *endpoint; + + pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), rdata, ¶m, &handled); + if (!handled && is_request && !is_ack) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 501, NULL, NULL, NULL); + } + + /* The endpoint_mod stores an endpoint reference in the mod_data of rdata. This + * is the only appropriate spot to actually decrement the reference. + */ + endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; + ao2_cleanup(endpoint); + pjsip_rx_data_free_cloned(rdata); + return 0; +} + +struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata) +{ + struct ast_sip_endpoint *endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; + if (endpoint) { + ao2_ref(endpoint, +1); + } + return endpoint; +} + +int ast_sip_initialize_distributor(void) +{ + if (ast_sip_register_service(&distributor_mod)) { + return -1; + } + if (ast_sip_register_service(&endpoint_mod)) { + return -1; + } + if (ast_sip_register_service(&auth_mod)) { + return -1; + } + return 0; +} diff --git a/res/res_sip/sip_options.c b/res/res_sip/sip_options.c new file mode 100644 index 000000000..5e3f8edca --- /dev/null +++ b/res/res_sip/sip_options.c @@ -0,0 +1,378 @@ +/* + * sip_options.c + * + * Created on: Jan 25, 2013 + * Author: mjordan + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> +#include <pjlib.h> + +#include "asterisk/res_sip.h" +#include "asterisk/channel.h" +#include "asterisk/pbx.h" +#include "asterisk/astobj2.h" +#include "asterisk/cli.h" +#include "include/res_sip_private.h" + +#define DEFAULT_LANGUAGE "en" +#define DEFAULT_ENCODING "text/plain" +#define QUALIFIED_BUCKETS 211 + +/*! \brief Scheduling context for qualifies */ +static struct ast_sched_context *sched; /* XXX move this to registrar */ + +struct ao2_container *scheduled_qualifies; + +struct qualify_info { + AST_DECLARE_STRING_FIELDS( + AST_STRING_FIELD(endpoint_id); + ); + char *scheduler_data; + int scheduler_id; +}; + +static pj_bool_t options_module_start(void); +static pj_bool_t options_module_stop(void); +static pj_bool_t options_module_on_rx_request(pjsip_rx_data *rdata); +static pj_bool_t options_module_on_rx_response(pjsip_rx_data *rdata); + +static pjsip_module options_module = { + .name = {"Options Module", 14}, + .id = -1, + .priority = PJSIP_MOD_PRIORITY_APPLICATION, + .start = options_module_start, + .stop = options_module_stop, + .on_rx_request = options_module_on_rx_request, + .on_rx_response = options_module_on_rx_response, +}; + +static pj_bool_t options_module_start(void) +{ + if (!(sched = ast_sched_context_create()) || + ast_sched_start_thread(sched)) { + return -1; + } + + return PJ_SUCCESS; +} + +static pj_bool_t options_module_stop(void) +{ + ao2_t_ref(scheduled_qualifies, -1, "Remove scheduled qualifies on module stop"); + + if (sched) { + ast_sched_context_destroy(sched); + } + + return PJ_SUCCESS; +} + +static pj_status_t send_options_response(pjsip_rx_data *rdata, pjsip_dialog *pj_dlg, int code) +{ + pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); + pjsip_transaction *pj_trans = pjsip_rdata_get_tsx(rdata); + pjsip_tx_data *tdata; + const pjsip_hdr *hdr; + pjsip_response_addr res_addr; + pj_status_t status; + + /* Make the response object */ + status = pjsip_endpt_create_response(endpt, rdata, code, NULL, &tdata); + if (status != PJ_SUCCESS) { + return status; + } + + /* Add appropriate headers */ + if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ACCEPT, NULL))) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); + } + if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_ALLOW, NULL))) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); + } + if ((hdr = pjsip_endpt_get_capability(endpt, PJSIP_H_SUPPORTED, NULL))) { + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, hdr)); + } + + /* + * XXX TODO: pjsip doesn't care a lot about either of these headers - + * while it provides specific methods to create them, they are defined + * to be the standard string header creation. We never did add them + * in chan_sip, although RFC 3261 says they SHOULD. Hard coded here. + */ + ast_sip_add_header(tdata, "Accept-Encoding", DEFAULT_ENCODING); + ast_sip_add_header(tdata, "Accept-Language", DEFAULT_LANGUAGE); + + if (pj_dlg && pj_trans) { + status = pjsip_dlg_send_response(pj_dlg, pj_trans, tdata); + } else { + /* Get where to send request. */ + status = pjsip_get_response_addr(tdata->pool, rdata, &res_addr); + if (status != PJ_SUCCESS) { + pjsip_tx_data_dec_ref(tdata); + return status; + } + status = pjsip_endpt_send_response(endpt, &res_addr, tdata, NULL, NULL); + } + + return status; +} + +static pj_bool_t options_module_on_rx_request(pjsip_rx_data *rdata) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); + pjsip_uri *ruri; + pjsip_sip_uri *sip_ruri; + char exten[AST_MAX_EXTENSION]; + + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_options_method)) { + return PJ_FALSE; + } + endpoint = ast_pjsip_rdata_get_endpoint(rdata); + ast_assert(endpoint != NULL); + + ruri = rdata->msg_info.msg->line.req.uri; + if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { + send_options_response(rdata, dlg, 416); + return -1; + } + + sip_ruri = pjsip_uri_get_uri(ruri); + ast_copy_pj_str(exten, &sip_ruri->user, sizeof(exten)); + + if (ast_shutting_down()) { + send_options_response(rdata, dlg, 503); + } else if (!ast_exists_extension(NULL, endpoint->context, exten, 1, NULL)) { + send_options_response(rdata, dlg, 404); + } else { + send_options_response(rdata, dlg, 200); + } + return PJ_TRUE; +} + +static pj_bool_t options_module_on_rx_response(pjsip_rx_data *rdata) +{ + + return PJ_SUCCESS; +} + +static int qualify_info_hash_fn(const void *obj, int flags) +{ + const struct qualify_info *info = obj; + const char *endpoint_id = flags & OBJ_KEY ? obj : info->endpoint_id; + + return ast_str_hash(endpoint_id); +} + +static int qualify_info_cmp_fn(void *obj, void *arg, int flags) +{ + struct qualify_info *left = obj; + struct qualify_info *right = arg; + const char *right_endpoint_id = flags & OBJ_KEY ? arg : right->endpoint_id; + + return strcmp(left->endpoint_id, right_endpoint_id) ? 0 : CMP_MATCH | CMP_STOP; +} + + +static void qualify_info_destructor(void *obj) +{ + struct qualify_info *info = obj; + if (!info) { + return; + } + ast_string_field_free_memory(info); + /* Cancel the qualify */ + if (!AST_SCHED_DEL(sched, info->scheduler_id)) { + /* If we successfully deleted the qualify, we got it before it + * fired. We can safely delete the data that was passed to it. + * Otherwise, we're getting deleted while this is firing - don't + * touch that memory! + */ + ast_free(info->scheduler_data); + } +} + +static struct qualify_info *create_qualify_info(struct ast_sip_endpoint *endpoint) +{ + struct qualify_info *info; + + info = ao2_alloc(sizeof(*info), qualify_info_destructor); + if (!info) { + return NULL; + } + + if (ast_string_field_init(info, 64)) { + ao2_ref(info, -1); + return NULL; + } + ast_string_field_set(info, endpoint_id, ast_sorcery_object_get_id(endpoint)); + + return info; +} + +static int send_qualify_request(void *data) +{ + struct ast_sip_endpoint *endpoint = data; + pjsip_tx_data *tdata; + /* YAY! Send an OPTIONS request. */ + + ast_sip_create_request("OPTIONS", NULL, endpoint, NULL, &tdata); + ast_sip_send_request(tdata, NULL, endpoint); + + ao2_cleanup(endpoint); + return 0; +} + +static int qualify_endpoint_scheduler_cb(const void *data) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + struct ast_sorcery *sorcery; + char *endpoint_id = (char *)data; + + sorcery = ast_sip_get_sorcery(); + if (!sorcery) { + ast_free(endpoint_id); + return 0; + } + + endpoint = ast_sorcery_retrieve_by_id(sorcery, "endpoint", endpoint_id); + if (!endpoint) { + /* Whoops, endpoint went away */ + ast_free(endpoint_id); + return 0; + } + + ast_sip_push_task(NULL, send_qualify_request, endpoint); + + return 1; +} + +static void schedule_qualifies(void) +{ + RAII_VAR(struct ao2_container *, endpoints, NULL, ao2_cleanup); + struct ao2_iterator it_endpoints; + struct ast_sip_endpoint *endpoint; + struct qualify_info *info; + char *endpoint_id; + + endpoints = ast_res_sip_get_endpoints(); + if (!endpoints) { + return; + } + + it_endpoints = ao2_iterator_init(endpoints, 0); + while ((endpoint = ao2_iterator_next(&it_endpoints))) { + if (endpoint->qualify_frequency) { + /* XXX TODO: This really should only qualify registered peers, + * which means we need a registrar. We should check the + * registrar to see if this endpoint has registered and, if + * not, pass on it. + * + * Actually, all of this should just get moved into the registrar. + * Otherwise, the registar will have to kick this off when a + * new endpoint registers, so it just makes sense to have it + * all live there. + */ + info = create_qualify_info(endpoint); + if (!info) { + ao2_ref(endpoint, -1); + break; + } + endpoint_id = ast_strdup(info->endpoint_id); + if (!endpoint_id) { + ao2_t_ref(info, -1, "Dispose of info on off nominal"); + ao2_ref(endpoint, -1); + break; + } + info->scheduler_data = endpoint_id; + info->scheduler_id = ast_sched_add_variable(sched, endpoint->qualify_frequency * 1000, qualify_endpoint_scheduler_cb, endpoint_id, 1); + ao2_t_link(scheduled_qualifies, info, "Link scheduled qualify information into container"); + ao2_t_ref(info, -1, "Dispose of creation ref"); + } + ao2_t_ref(endpoint, -1, "Dispose of iterator ref"); + } + ao2_iterator_destroy(&it_endpoints); +} + +static char *send_options(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + const char *endpoint_name; + pjsip_tx_data *tdata; + + switch (cmd) { + case CLI_INIT: + e->command = "sip send options"; + e->usage = + "Usage: sip send options <endpoint>\n" + " Send a SIP OPTIONS request to the specified endpoint.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != 4) { + return CLI_SHOWUSAGE; + } + + endpoint_name = a->argv[3]; + + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name); + if (!endpoint) { + ast_log(LOG_ERROR, "Unable to retrieve endpoint %s\n", endpoint_name); + return CLI_FAILURE; + } + + if (ast_sip_create_request("OPTIONS", NULL, endpoint, NULL, &tdata)) { + ast_log(LOG_ERROR, "Unable to create OPTIONS request to endpoint %s\n", endpoint_name); + return CLI_FAILURE; + } + + if (ast_sip_send_request(tdata, NULL, endpoint)) { + ast_log(LOG_ERROR, "Unable to send OPTIONS request to endpoint %s\n", endpoint_name); + return CLI_FAILURE; + } + + return CLI_SUCCESS; +} + +static struct ast_cli_entry cli_options[] = { + AST_CLI_DEFINE(send_options, "Send an OPTIONS requst to an arbitrary SIP URI"), +}; + +int ast_res_sip_init_options_handling(int reload) +{ + const pj_str_t STR_OPTIONS = { "OPTIONS", 7 }; + + if (scheduled_qualifies) { + ao2_t_ref(scheduled_qualifies, -1, "Remove old scheduled qualifies"); + } + scheduled_qualifies = ao2_t_container_alloc(QUALIFIED_BUCKETS, qualify_info_hash_fn, qualify_info_cmp_fn, "Create container for scheduled qualifies"); + if (!scheduled_qualifies) { + return -1; + } + + if (reload) { + return 0; + } + + if (pjsip_endpt_register_module(ast_sip_get_pjsip_endpoint(), &options_module) != PJ_SUCCESS) { + options_module_stop(); + return -1; + } + + if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_OPTIONS) != PJ_SUCCESS) { + pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module); + return -1; + } + + ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options)); + + schedule_qualifies(); + + return 0; +} diff --git a/res/res_sip/sip_outbound_auth.c b/res/res_sip/sip_outbound_auth.c new file mode 100644 index 000000000..94352a521 --- /dev/null +++ b/res/res_sip/sip_outbound_auth.c @@ -0,0 +1,94 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" +#undef bzero +#define bzero bzero +#include "pjsip.h" + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" +#include "include/res_sip_private.h" + +static pj_bool_t outbound_auth(pjsip_rx_data *rdata); + +static pjsip_module outbound_auth_mod = { + .name = {"Outbound Authentication", 19}, + .priority = PJSIP_MOD_PRIORITY_DIALOG_USAGE, + .on_rx_response = outbound_auth, +}; + +struct outbound_auth_cb_data { + ast_sip_dialog_outbound_auth_cb cb; + void *user_data; +}; + +static pj_bool_t outbound_auth(pjsip_rx_data *rdata) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + pjsip_transaction *tsx; + pjsip_dialog *dlg; + struct outbound_auth_cb_data *cb_data; + pjsip_tx_data *tdata; + + if (rdata->msg_info.msg->line.status.code != 401 && + rdata->msg_info.msg->line.status.code != 407) { + /* Doesn't pertain to us. Move on */ + return PJ_FALSE; + } + + tsx = pjsip_rdata_get_tsx(rdata); + dlg = pjsip_rdata_get_dlg(rdata); + ast_assert(dlg != NULL && tsx != NULL); + endpoint = ast_sip_dialog_get_endpoint(dlg); + + if (!endpoint) { + return PJ_FALSE; + } + + if (ast_sip_create_request_with_auth(endpoint->sip_outbound_auths, endpoint->num_outbound_auths, rdata, tsx, &tdata)) { + return PJ_FALSE; + } + + cb_data = dlg->mod_data[outbound_auth_mod.id]; + if (cb_data) { + cb_data->cb(dlg, tdata, cb_data->user_data); + return PJ_TRUE; + } + + pjsip_dlg_send_request(dlg, tdata, -1, NULL); + return PJ_TRUE; +} + +int ast_sip_dialog_setup_outbound_authentication(pjsip_dialog *dlg, const struct ast_sip_endpoint *endpoint, + ast_sip_dialog_outbound_auth_cb cb, void *user_data) +{ + struct outbound_auth_cb_data *cb_data = PJ_POOL_ZALLOC_T(dlg->pool, struct outbound_auth_cb_data); + cb_data->cb = cb; + cb_data->user_data = user_data; + + dlg->sess_count++; + pjsip_dlg_add_usage(dlg, &outbound_auth_mod, cb_data); + dlg->sess_count--; + + return 0; +} + +int ast_sip_initialize_outbound_authentication(void) { + return ast_sip_register_service(&outbound_auth_mod); +} diff --git a/res/res_sip_acl.c b/res/res_sip_acl.c new file mode 100644 index 000000000..405c3c1bc --- /dev/null +++ b/res/res_sip_acl.c @@ -0,0 +1,222 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/sorcery.h" +#include "asterisk/acl.h" + +struct sip_acl { + SORCERY_OBJECT(details); + struct ast_acl_list *acl; + struct ast_acl_list *contact_acl; +}; + +static int apply_acl(pjsip_rx_data *rdata, struct ast_acl_list *acl) +{ + struct ast_sockaddr addr; + + if (ast_acl_list_is_empty(acl)) { + return 0; + } + + memset(&addr, 0, sizeof(addr)); + ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID); + ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port); + + if (ast_apply_acl(acl, &addr, "SIP ACL: ") != AST_SENSE_ALLOW) { + ast_log(LOG_WARNING, "Incoming SIP message from %s did not pass ACL test\n", ast_sockaddr_stringify(&addr)); + return 1; + } + return 0; +} + +static int extract_contact_addr(pjsip_contact_hdr *contact, struct ast_sockaddr **addrs) +{ + pjsip_sip_uri *sip_uri; + char host[256]; + + if (!contact) { + return 0; + } + if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { + return 0; + } + sip_uri = pjsip_uri_get_uri(contact->uri); + ast_copy_pj_str(host, &sip_uri->host, sizeof(host)); + return ast_sockaddr_resolve(addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC); +} + +static int apply_contact_acl(pjsip_rx_data *rdata, struct ast_acl_list *contact_acl) +{ + int num_contact_addrs; + int forbidden = 0; + struct ast_sockaddr *contact_addrs; + int i; + pjsip_contact_hdr *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; + + if (ast_acl_list_is_empty(contact_acl)) { + return 0; + } + + while ((contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { + num_contact_addrs = extract_contact_addr(contact, &contact_addrs); + if (num_contact_addrs <= 0) { + continue; + } + for (i = 0; i < num_contact_addrs; ++i) { + if (ast_apply_acl(contact_acl, &contact_addrs[i], "SIP Contact ACL: ") != AST_SENSE_ALLOW) { + ast_log(LOG_WARNING, "Incoming SIP message from %s did not pass ACL test\n", ast_sockaddr_stringify(&contact_addrs[i])); + forbidden = 1; + break; + } + } + ast_free(contact_addrs); + if (forbidden) { + /* No use checking other contacts if we already have failed ACL check */ + break; + } + } + + return forbidden; +} + +static int check_acls(void *obj, void *arg, int flags) +{ + struct sip_acl *acl = obj; + pjsip_rx_data *rdata = arg; + + if (apply_acl(rdata, acl->acl) || apply_contact_acl(rdata, acl->contact_acl)) { + return CMP_MATCH | CMP_STOP; + } + return 0; +} + +static pj_bool_t acl_on_rx_msg(pjsip_rx_data *rdata) +{ + int forbidden = 0; + struct ao2_container *acls = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "acl", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + struct sip_acl *matched_acl; + if (!acls) { + ast_log(LOG_ERROR, "Unable to retrieve ACL sorcery data\n"); + return PJ_FALSE; + } + + matched_acl = ao2_callback(acls, 0, check_acls, rdata); + if (matched_acl) { + forbidden = 1; + ao2_ref(matched_acl, -1); + } + ao2_ref(acls, -1); + + if (forbidden) { + if (rdata->msg_info.msg->line.req.method.id != PJSIP_ACK_METHOD) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + } + return PJ_TRUE; + } + + return PJ_FALSE; +} + +static pjsip_module acl_module = { + .name = { "ACL Module", 14 }, + /* This should run after a logger but before anything else */ + .priority = 1, + .on_rx_request = acl_on_rx_msg, +}; + +static int acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct sip_acl *acl = obj; + int error; + int ignore; + if (!strncmp(var->name, "contact", 7)) { + ast_append_acl(var->name + 7, var->value, &acl->contact_acl, &error, &ignore); + } else { + ast_append_acl(var->name, var->value, &acl->acl, &error, &ignore); + } + return error; +} + +static void sip_acl_destructor(void *obj) +{ + struct sip_acl *acl = obj; + acl->acl = ast_free_acl_list(acl->acl); + acl->contact_acl = ast_free_acl_list(acl->contact_acl); +} + +static void *sip_acl_alloc(const char *name) +{ + struct sip_acl *acl = ao2_alloc(sizeof(*acl), sip_acl_destructor); + if (!acl) { + return NULL; + } + return acl; +} + +static int load_acls(void) +{ + ast_sorcery_apply_default(ast_sip_get_sorcery(), "acl", "config", "res_sip.conf,criteria=type=acl"); + if (ast_sorcery_object_register(ast_sip_get_sorcery(), "acl", sip_acl_alloc, NULL, NULL)) { + ast_log(LOG_ERROR, "Failed to register SIP ACL object with sorcery\n"); + return -1; + } + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "acl", "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "permit", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "deny", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "acl", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "contactpermit", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "contactdeny", "", acl_handler, NULL, 0, 0); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "acl", "contactacl", "", acl_handler, NULL, 0, 0); + + /* XXX Is there a more selective way to do this? (i.e. Just reload a specific object type?) */ + ast_sorcery_reload(ast_sip_get_sorcery()); + return 0; +} + +static int load_module(void) +{ + if (load_acls()) { + return AST_MODULE_LOAD_DECLINE; + } + ast_sip_register_service(&acl_module); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_service(&acl_module); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP ACL Resource", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_authenticator_digest.c b/res/res_sip_authenticator_digest.c new file mode 100644 index 000000000..499342309 --- /dev/null +++ b/res/res_sip_authenticator_digest.c @@ -0,0 +1,455 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/module.h" +#include "asterisk/strings.h" + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sip</depend> + <support_level>core</support_level> + ***/ + +AO2_GLOBAL_OBJ_STATIC(entity_id); + +/*! + * \brief Determine if authentication is required + * + * Authentication is required if the endpoint has at least one auth + * section specified + */ +static int digest_requires_authentication(struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) +{ + return endpoint->num_inbound_auths > 0; +} + +static void auth_store_cleanup(void *data) +{ + struct ast_sip_auth **auth = data; + + ao2_cleanup(*auth); + ast_free(data); +} + +/*! + * \brief Thread-local storage for \ref ast_sip_auth + * + * The PJSIP authentication API is a bit annoying. When you set + * up an authentication server, you specify a lookup callback to + * call into when verifying incoming credentials. The problem + * with this callback is that it only gives you the realm and + * authentication username. In 2.0.5, there is a new version of + * the callback you can use that gives the pjsip_rx_data in + * addition. + * + * Unfortunately, the data we actually \b need is the + * \ref ast_sip_auth we are currently observing. So we have two + * choices: + * 1) Use the current PJSIP API and use thread-local storage + * to temporarily store our SIP authentication information. Then + * in the callback, we can retrieve the authentication info and + * use as needed. Given our threading model, this is safe. + * 2) Use the 2.0.5 API and temporarily store the authentication + * information in the rdata's endpoint_info. Then in the callback, + * we can retrieve the authentication info from the rdata. + * + * I've chosen option 1 since it does not require backporting + * any APIs from future versions of PJSIP, plus I feel the + * thread-local option is a bit cleaner. + */ +AST_THREADSTORAGE_CUSTOM(auth_store, NULL, auth_store_cleanup); + +/*! + * \brief Store authentication information in thread-local storage + */ +static int store_auth(struct ast_sip_auth *auth) +{ + struct ast_sip_auth **pointing; + pointing = ast_threadstorage_get(&auth_store, sizeof(pointing)); + if (!pointing || *pointing) { + return -1; + } + + ao2_ref(auth, +1); + *pointing = auth; + return 0; +} + +/*! + * \brief Remove authentication information from thread-local storage + */ +static int remove_auth(void) +{ + struct ast_sip_auth **pointing; + pointing = ast_threadstorage_get(&auth_store, sizeof(pointing)); + if (!pointing) { + return -1; + } + + ao2_cleanup(*pointing); + *pointing = NULL; + return 0; +} + +/*! + * \brief Retrieve authentication information from thread-local storage + */ +static struct ast_sip_auth *get_auth(void) +{ + struct ast_sip_auth **auth; + auth = ast_threadstorage_get(&auth_store, sizeof(auth)); + if (auth && *auth) { + ao2_ref(*auth, +1); + return *auth; + } + return NULL; +} + +/*! + * \brief Lookup callback for authentication verification + * + * This function is called when we call pjsip_auth_srv_verify(). It + * expects us to verify that the realm and account name from the + * Authorization header is correct. We are then supposed to supply + * a password or MD5 sum of credentials. + * + * \param pool A memory pool we can use for allocations + * \param realm The realm from the Authorization header + * \param acc_name the user from the Authorization header + * \param[out] info The credentials we need to fill in + * \retval PJ_SUCCESS Successful authentication + * \retval other Unsuccessful + */ +static pj_status_t digest_lookup(pj_pool_t *pool, const pj_str_t *realm, + const pj_str_t *acc_name, pjsip_cred_info *info) +{ + RAII_VAR(struct ast_sip_auth *, auth, get_auth(), ao2_cleanup); + if (!auth) { + return PJSIP_SC_FORBIDDEN; + } + + if (pj_strcmp2(realm, auth->realm)) { + return PJSIP_SC_FORBIDDEN; + } + if (pj_strcmp2(acc_name, auth->auth_user)) { + return PJSIP_SC_FORBIDDEN; + } + + pj_strdup2(pool, &info->realm, auth->realm); + pj_strdup2(pool, &info->username, auth->auth_user); + + switch (auth->type) { + case AST_SIP_AUTH_TYPE_USER_PASS: + pj_strdup2(pool, &info->data, auth->auth_pass); + info->data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + break; + case AST_SIP_AUTH_TYPE_MD5: + pj_strdup2(pool, &info->data, auth->md5_creds); + info->data_type = PJSIP_CRED_DATA_DIGEST; + break; + default: + return PJSIP_SC_FORBIDDEN; + } + return PJ_SUCCESS; +} + +/*! + * \brief Calculate a nonce + * + * We use this in order to create authentication challenges. We also use this in order + * to verify that an incoming request with credentials could be in response to one + * of our challenges. + * + * The nonce is calculated from a timestamp, the source IP address, the source port, a + * unique ID for us, and the realm. This helps to ensure that the incoming request + * is from the same source that the nonce was calculated for. Including the realm + * ensures that multiple challenges to the same request have different nonces. + * + * \param A UNIX timestamp expressed as a string + * \param rdata The incoming request + * \param realm The realm for which authentication should occur + */ +static int build_nonce(struct ast_str **nonce, const char *timestamp, const pjsip_rx_data *rdata, const char *realm) +{ + struct ast_str *str = ast_str_alloca(256); + RAII_VAR(char *, eid, ao2_global_obj_ref(entity_id), ao2_cleanup); + char hash[32]; + + ast_str_append(&str, 0, "%s", timestamp); + ast_str_append(&str, 0, ":%s", rdata->pkt_info.src_name); + ast_str_append(&str, 0, ":%d", rdata->pkt_info.src_port); + ast_str_append(&str, 0, ":%s", eid); + ast_str_append(&str, 0, ":%s", realm); + ast_md5_hash(hash, ast_str_buffer(str)); + + ast_str_append(nonce, 0, "%s/%s", timestamp, hash); + return 0; +} + +/*! + * \brief Ensure that a nonce on an incoming request is sane. + * + * The nonce in an incoming Authorization header needs to pass some scrutiny in order + * for us to consider accepting it. What we do is re-build a nonce based on request + * data and a realm and see if it matches the nonce they sent us. + * \param candidate The nonce on an incoming request + * \param rdata The incoming request + * \param auth The auth credentials we are trying to match against. + * \retval 0 Nonce does not pass validity checks + * \retval 1 Nonce passes validity check + */ +static int check_nonce(const char *candidate, const pjsip_rx_data *rdata, const struct ast_sip_auth *auth) +{ + char *copy = ast_strdupa(candidate); + char *timestamp = strsep(©, "/"); + int timestamp_int; + time_t now = time(NULL); + struct ast_str *calculated = ast_str_alloca(64); + + if (!copy) { + /* Clearly a bad nonce! */ + return 0; + } + + if (sscanf(timestamp, "%30d", ×tamp_int) != 1) { + return 0; + } + + if ((int) now - timestamp_int > auth->nonce_lifetime) { + return 0; + } + + build_nonce(&calculated, timestamp, rdata, auth->realm); + ast_debug(3, "Calculated nonce %s. Actual nonce is %s\n", ast_str_buffer(calculated), candidate); + if (strcmp(ast_str_buffer(calculated), candidate)) { + return 0; + } + return 1; +} + +static int find_challenge(const pjsip_rx_data *rdata, const struct ast_sip_auth *auth) +{ + struct pjsip_authorization_hdr *auth_hdr = (pjsip_authorization_hdr *) &rdata->msg_info.msg->hdr; + int challenge_found = 0; + char nonce[64]; + + while ((auth_hdr = (pjsip_authorization_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_AUTHORIZATION, auth_hdr->next))) { + ast_copy_pj_str(nonce, &auth_hdr->credential.digest.nonce, sizeof(nonce)); + if (check_nonce(nonce, rdata, auth) && !pj_strcmp2(&auth_hdr->credential.digest.realm, auth->realm)) { + challenge_found = 1; + break; + } + } + + return challenge_found; +} + +/*! + * \brief Common code for initializing a pjsip_auth_srv + */ +static void setup_auth_srv(pj_pool_t *pool, pjsip_auth_srv *auth_server, const struct ast_sip_auth *auth) +{ + pj_str_t realm; + pj_cstr(&realm, auth->realm); + + pjsip_auth_srv_init(pool, auth_server, &realm, digest_lookup, 0); +} + +/*! + * \brief Result of digest verification + */ +enum digest_verify_result { + /*! Authentication credentials incorrect */ + AUTH_FAIL, + /*! Authentication credentials correct */ + AUTH_SUCCESS, + /*! Authentication credentials correct but nonce mismatch */ + AUTH_STALE, +}; + +/*! + * \brief astobj2 callback for verifying incoming credentials + * + * \param auth The ast_sip_auth to check against + * \param rdata The incoming request + * \param pool A pool to use for the auth server + * \return CMP_MATCH on successful authentication + * \return 0 on failed authentication + */ +static int verify(struct ast_sip_auth *auth, pjsip_rx_data *rdata, pj_pool_t *pool) +{ + pj_status_t authed; + int response_code; + pjsip_auth_srv auth_server; + int stale = 0; + + if (!find_challenge(rdata, auth)) { + /* Couldn't find a challenge with a sane nonce. + * Nonce mismatch may just be due to staleness. + */ + stale = 1; + } + + setup_auth_srv(pool, &auth_server, auth); + + store_auth(auth); + + authed = pjsip_auth_srv_verify(&auth_server, rdata, &response_code); + + remove_auth(); + + if (authed == PJ_SUCCESS) { + if (stale) { + return AUTH_STALE; + } else { + return AUTH_SUCCESS; + } + } + return AUTH_FAIL; +} + +/*! + * \brief astobj2 callback for adding digest challenges to responses + * + * \param auth The ast_aip_auth to build a challenge from + * \param tdata The response to add the challenge to + * \param rdata The request the challenge is in response to + * \param is_stale Indicates whether nonce on incoming request was stale + */ +static void challenge(const struct ast_sip_auth *auth, pjsip_tx_data *tdata, const pjsip_rx_data *rdata, int is_stale) +{ + pj_str_t qop; + pj_str_t pj_nonce; + pjsip_auth_srv auth_server; + struct ast_str *nonce = ast_str_alloca(256); + char time_buf[32]; + time_t timestamp = time(NULL); + snprintf(time_buf, sizeof(time_buf), "%d", (int) timestamp); + + build_nonce(&nonce, time_buf, rdata, auth->realm); + + setup_auth_srv(tdata->pool, &auth_server, auth); + + pj_cstr(&pj_nonce, ast_str_buffer(nonce)); + pj_cstr(&qop, "auth"); + pjsip_auth_srv_challenge(&auth_server, &qop, &pj_nonce, NULL, is_stale ? PJ_TRUE : PJ_FALSE, tdata); +} + +/*! + * \brief Check authentication using Digest scheme + * + * This function will check an incoming message against configured authentication + * options. If \b any of the incoming Authorization headers result in successful + * authentication, then authentication is considered successful. + * + * \see ast_sip_check_authentication + */ +static enum ast_sip_check_auth_result digest_check_auth(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata, pjsip_tx_data *tdata) +{ + struct ast_sip_auth **auths = ast_alloca(endpoint->num_inbound_auths * sizeof(*auths)); + enum digest_verify_result *verify_res = ast_alloca(endpoint->num_inbound_auths * sizeof(*verify_res)); + enum ast_sip_check_auth_result res; + int i; + + if (!auths) { + return AST_SIP_AUTHENTICATION_ERROR; + } + + if (ast_sip_retrieve_auths(endpoint->sip_inbound_auths, endpoint->num_inbound_auths, auths)) { + res = AST_SIP_AUTHENTICATION_ERROR; + goto cleanup; + } + + for (i = 0; i < endpoint->num_inbound_auths; ++i) { + verify_res[i] = verify(auths[i], rdata, tdata->pool); + if (verify_res[i] == AUTH_SUCCESS) { + res = AST_SIP_AUTHENTICATION_SUCCESS; + goto cleanup; + } + } + + for (i = 0; i < endpoint->num_inbound_auths; ++i) { + challenge(auths[i], tdata, rdata, verify_res[i] == AUTH_STALE); + } + + res = AST_SIP_AUTHENTICATION_CHALLENGE; + +cleanup: + ast_sip_cleanup_auths(auths, endpoint->num_inbound_auths); + return res; +} + +static struct ast_sip_authenticator digest_authenticator = { + .requires_authentication = digest_requires_authentication, + .check_authentication = digest_check_auth, +}; + +static int build_entity_id(void) +{ + RAII_VAR(struct ast_uuid *, uu, ast_uuid_generate(), ast_free_ptr); + RAII_VAR(char *, eid, ao2_alloc(AST_UUID_STR_LEN, NULL), ao2_cleanup); + + if (!uu || !eid) { + return -1; + } + + ast_uuid_to_str(uu, eid, AST_UUID_STR_LEN); + ao2_global_obj_replace_unref(entity_id, eid); + return 0; +} + +static int reload_module(void) +{ + if (build_entity_id()) { + return -1; + } + return 0; +} + +static int load_module(void) +{ + if (build_entity_id()) { + return AST_MODULE_LOAD_DECLINE; + } + if (ast_sip_register_authenticator(&digest_authenticator)) { + ao2_global_obj_release(entity_id); + return AST_MODULE_LOAD_DECLINE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_authenticator(&digest_authenticator); + ao2_global_obj_release(entity_id); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP authentication resource", + .load = load_module, + .unload = unload_module, + .reload = reload_module, + .load_pri = AST_MODPRI_CHANNEL_DEPEND, +); diff --git a/res/res_sip_caller_id.c b/res/res_sip_caller_id.c new file mode 100644 index 000000000..22ece0436 --- /dev/null +++ b/res/res_sip_caller_id.c @@ -0,0 +1,715 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sip</depend> + <depend>res_sip_session</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_session.h" +#include "asterisk/channel.h" +#include "asterisk/module.h" +#include "asterisk/callerid.h" + +/*! + * \internal + * \brief Set an ast_party_id name and number based on an identity header. + * \param hdr From, P-Asserted-Identity, or Remote-Party-ID header on incoming message + * \param[out] id The ID to set data on + */ +static void set_id_from_hdr(pjsip_fromto_hdr *hdr, struct ast_party_id *id) +{ + char cid_name[AST_CHANNEL_NAME]; + char cid_num[AST_CHANNEL_NAME]; + pjsip_sip_uri *uri; + pjsip_name_addr *id_name_addr = (pjsip_name_addr *) hdr->uri; + + uri = pjsip_uri_get_uri(id_name_addr); + ast_copy_pj_str(cid_name, &id_name_addr->display, sizeof(cid_name)); + ast_copy_pj_str(cid_num, &uri->user, sizeof(cid_num)); + + ast_free(id->name.str); + id->name.str = ast_strdup(cid_name); + if (!ast_strlen_zero(cid_name)) { + id->name.valid = 1; + } + ast_free(id->number.str); + id->number.str = ast_strdup(cid_num); + if (!ast_strlen_zero(cid_num)) { + id->number.valid = 1; + } +} + +/*! + * \internal + * \brief Get a P-Asserted-Identity or Remote-Party-ID header from an incoming message + * + * This function will parse the header as if it were a From header. This allows for us + * to easily manipulate the URI, as well as add, modify, or remove parameters from the + * header + * + * \param rdata The incoming message + * \param header_name The name of the ID header to find + * \retval NULL No ID header present or unable to parse ID header + * \retval non-NULL The parsed ID header + */ +static pjsip_fromto_hdr *get_id_header(pjsip_rx_data *rdata, const pj_str_t *header_name) +{ + static const pj_str_t from = { "From", 4 }; + pj_str_t header_content; + pjsip_fromto_hdr *parsed_hdr; + pjsip_generic_string_hdr *ident = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, + header_name, NULL); + int parsed_len; + + if (!ident) { + return NULL; + } + + pj_strdup_with_null(rdata->tp_info.pool, &header_content, &ident->hvalue); + + parsed_hdr = pjsip_parse_hdr(rdata->tp_info.pool, &from, header_content.ptr, + pj_strlen(&header_content), &parsed_len); + + if (!parsed_hdr) { + return NULL; + } + + return parsed_hdr; +} + +/*! + * \internal + * \brief Set an ast_party_id structure based on data in a P-Asserted-Identity header + * + * This makes use of \ref set_id_from_hdr for setting name and number. It uses + * the contents of a Privacy header in order to set presentation information. + * + * \param rdata The incoming message + * \param[out] id The ID to set + * \retval 0 Successfully set the party ID + * \retval non-zero Could not set the party ID + */ +static int set_id_from_pai(pjsip_rx_data *rdata, struct ast_party_id *id) +{ + static const pj_str_t pai_str = { "P-Asserted-Identity", 19 }; + static const pj_str_t privacy_str = { "Privacy", 7 }; + pjsip_fromto_hdr *pai_hdr = get_id_header(rdata, &pai_str); + pjsip_generic_string_hdr *privacy; + + if (!pai_hdr) { + return -1; + } + + set_id_from_hdr(pai_hdr, id); + + if (!id->number.valid) { + return -1; + } + + privacy = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &privacy_str, NULL); + if (!privacy) { + return 0; + } + if (!pj_stricmp2(&privacy->hvalue, "id")) { + id->number.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + id->name.presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED; + } + + return 0; +} + +/*! + * \internal + * \brief Set an ast_party_id structure based on data in a Remote-Party-ID header + * + * This makes use of \ref set_id_from_hdr for setting name and number. It uses + * the privacy and screen parameters in order to set presentation information. + * + * \param rdata The incoming message + * \param[out] id The ID to set + * \retval 0 Succesfully set the party ID + * \retval non-zero Could not set the party ID + */ +static int set_id_from_rpid(pjsip_rx_data *rdata, struct ast_party_id *id) +{ + static const pj_str_t rpid_str = { "Remote-Party-ID", 15 }; + static const pj_str_t privacy_str = { "privacy", 7 }; + static const pj_str_t screen_str = { "screen", 6 }; + pjsip_fromto_hdr *rpid_hdr = get_id_header(rdata, &rpid_str); + pjsip_param *screen; + pjsip_param *privacy; + + if (!rpid_hdr) { + return -1; + } + + set_id_from_hdr(rpid_hdr, id); + + if (!id->number.valid) { + return -1; + } + + privacy = pjsip_param_find(&rpid_hdr->other_param, &privacy_str); + screen = pjsip_param_find(&rpid_hdr->other_param, &screen_str); + if (privacy && !pj_stricmp2(&privacy->value, "full")) { + id->number.presentation |= AST_PRES_RESTRICTED; + id->name.presentation |= AST_PRES_RESTRICTED; + } + if (screen && !pj_stricmp2(&screen->value, "yes")) { + id->number.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN; + id->name.presentation |= AST_PRES_USER_NUMBER_PASSED_SCREEN; + } + + return 0; +} + +/*! + * \internal + * \brief Set an ast_party_id structure based on data in a From + * + * This makes use of \ref set_id_from_hdr for setting name and number. It uses + * no information from the message in order to set privacy. It relies on endpoint + * configuration for privacy information. + * + * \param rdata The incoming message + * \param[out] id The ID to set + * \retval 0 Succesfully set the party ID + * \retval non-zero Could not set the party ID + */ +static int set_id_from_from(struct pjsip_rx_data *rdata, struct ast_party_id *id) +{ + pjsip_fromto_hdr *from = pjsip_msg_find_hdr(rdata->msg_info.msg, + PJSIP_H_FROM, rdata->msg_info.msg->hdr.next); + + if (!from) { + /* This had better not happen */ + return -1; + } + + set_id_from_hdr(from, id); + + if (!id->number.valid) { + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Determine if a connected line update should be queued + * + * This uses information about the session and the ID that would be queued + * in the connected line update in order to determine if we should queue + * a connected line update. + * + * \param session The session whose channel we wish to queue the connected line update on + * \param id The identification information that would be queued on the connected line update + * \retval 0 We should not queue a connected line update + * \retval non-zero We should queue a connected line update + */ +static int should_queue_connected_line_update(const struct ast_sip_session *session, const struct ast_party_id *id) +{ + /* Invalid number means no update */ + if (!id->number.valid) { + return 0; + } + + /* If the session has never communicated an update or if the + * new ID has a different number than the session, then we + * should queue an update + */ + if (ast_strlen_zero(session->id.number.str) || + strcmp(session->id.number.str, id->number.str)) { + return 1; + } + + /* By making it to this point, it means the number is not enough + * to determine if an update should be sent. Now we look at + * the name + */ + + /* If the number couldn't warrant an update and the name is + * invalid, then no update + */ + if (!id->name.valid) { + return 0; + } + + /* If the name has changed or we don't have a name set for the + * session, then we should send an update + */ + if (ast_strlen_zero(session->id.name.str) || + strcmp(session->id.name.str, id->name.str)) { + return 1; + } + + /* Neither the name nor the number have changed. No update */ + return 0; +} + +/*! + * \internal + * \brief Queue a connected line update on a session's channel. + * \param session The session whose channel should have the connected line update queued upon. + * \param id The identification information to place in the connected line update + */ +static void queue_connected_line_update(struct ast_sip_session *session, const struct ast_party_id *id) +{ + struct ast_party_connected_line connected; + struct ast_set_party_connected_line update_connected; + + ast_party_connected_line_init(&connected); + ast_party_id_copy(&connected.id, id); + + memset(&update_connected, 0, sizeof(update_connected)); + update_connected.id.number = 1; + update_connected.id.name = 1; + + ast_set_party_id_all(&update_connected.priv); + connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; + ast_party_id_copy(&session->id, &connected.id); + ast_channel_queue_connected_line_update(session->channel, &connected, &update_connected); + + ast_party_connected_line_free(&connected); +} + +/*! + * \internal + * \brief Make updates to connected line information based on an incoming request. + * + * This will get identity information from an incoming request. Once the identification is + * retrieved, we will check if the new information warrants a connected line update and queue + * a connected line update if so. + * + * \param session The session on which we received an incoming request + * \param rdata The incoming request + */ +static void update_incoming_connected_line(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + struct ast_party_id id; + + if (!session->endpoint->trust_id_inbound) { + return; + } + + ast_party_id_init(&id); + if (set_id_from_pai(rdata, &id) && set_id_from_rpid(rdata, &id)) { + return; + } + if (should_queue_connected_line_update(session, &id)) { + queue_connected_line_update(session, &id); + } + + ast_party_id_free(&id); +} + +/*! + * \internal + * \brief Session supplement callback on an incoming INVITE request + * + * If we are receiving an initial INVITE, then we will set the session's identity + * based on the INVITE or configured endpoint values. If we are receiving a reinvite, + * then we will potentially queue a connected line update via the \ref update_incoming_connected_line + * function + * + * \param session The session that has received an INVITE + * \param rdata The incoming INVITE + */ +static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED) { + /* Initial inbound INVITE. Set the session ID directly */ + if (session->endpoint->trust_id_inbound && + (!set_id_from_pai(rdata, &session->id) || !set_id_from_rpid(rdata, &session->id))) { + return 0; + } + ast_party_id_copy(&session->id, &session->endpoint->id); + if (!session->endpoint->id.number.valid) { + set_id_from_from(rdata, &session->id); + } + } else { + /* Reinvite. Check for changes to the ID and queue a connected line + * update if necessary + */ + update_incoming_connected_line(session, rdata); + } + return 0; +} + +/*! + * \internal + * \brief Session supplement callback on INVITE response + * + * INVITE responses could result in queuing connected line updates. + * + * \param session The session on which communication is happening + * \param rdata The incoming INVITE response + */ +static void caller_id_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + if (!session->channel) { + return; + } + + update_incoming_connected_line(session, rdata); +} + +/*! + * \internal + * \brief Set name and number information on an identity header. + * \param pool Memory pool to use for string duplication + * \param id_hdr A From, P-Asserted-Identity, or Remote-Party-ID header to modify + * \param id The identity information to apply to the header + */ +static void modify_id_header(pj_pool_t *pool, pjsip_fromto_hdr *id_hdr, const struct ast_party_id *id) +{ + pjsip_name_addr *id_name_addr; + pjsip_sip_uri *id_uri; + + id_name_addr = (pjsip_name_addr *) id_hdr->uri; + id_uri = pjsip_uri_get_uri(id_name_addr->uri); + + if (id->name.valid) { + pj_strdup2(pool, &id_name_addr->display, id->name.str); + } + + pj_strdup2(pool, &id_uri->user, id->number.str); +} + +/*! + * \internal + * \brief Create an identity header for an outgoing message + * \param hdr_name The name of the header to create + * \param tdata The message to place the header on + * \param id The identification information for the new header + * \return newly-created header + */ +static pjsip_fromto_hdr *create_new_id_hdr(const pj_str_t *hdr_name, pjsip_tx_data *tdata, const struct ast_party_id *id) +{ + pjsip_fromto_hdr *id_hdr; + pjsip_fromto_hdr *base; + pjsip_name_addr *id_name_addr; + pjsip_sip_uri *id_uri; + + base = tdata->msg->type == PJSIP_REQUEST_MSG ? PJSIP_MSG_FROM_HDR(tdata->msg) : + PJSIP_MSG_TO_HDR(tdata->msg); + id_hdr = pjsip_from_hdr_create(tdata->pool); + id_hdr->type = PJSIP_H_OTHER; + pj_strdup(tdata->pool, &id_hdr->name, hdr_name); + id_hdr->sname.slen = 0; + + id_name_addr = pjsip_uri_clone(tdata->pool, base->uri); + id_uri = pjsip_uri_get_uri(id_name_addr->uri); + + if (id->name.valid) { + pj_strdup2(tdata->pool, &id_name_addr->display, id->name.str); + } + + pj_strdup2(tdata->pool, &id_uri->user, id->number.str); + + id_hdr->uri = (pjsip_uri *) id_name_addr; + return id_hdr; +} + +/*! + * \internal + * \brief Add a Privacy header to an outbound message + * + * When sending a P-Asserted-Identity header, if privacy is requested, then we + * will need to indicate such by adding a Privacy header. Similarly, if no + * privacy is requested, and a Privacy header already exists on the message, + * then the old Privacy header should be removed. + * + * \param tdata The outbound message to add the Privacy header to + * \param id The id information used to determine privacy + */ +static void add_privacy_header(pjsip_tx_data *tdata, const struct ast_party_id *id) +{ + static const pj_str_t pj_privacy_name = { "Privacy", 7 }; + static const pj_str_t pj_privacy_value = { "id", 2 }; + pjsip_hdr *old_privacy; + + old_privacy = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_privacy_name, NULL); + + if ((id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED || + (id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED) { + if (!old_privacy) { + pjsip_generic_string_hdr *privacy_hdr = pjsip_generic_string_hdr_create( + tdata->pool, &pj_privacy_name, &pj_privacy_value); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)privacy_hdr); + } + } else { + if (old_privacy) { + pj_list_erase(old_privacy); + } + } +} + +/*! + * \internal + * \brief Add a P-Asserted-Identity header to an outbound message + * \param tdata The message to add the header to + * \param id The identification information used to populate the header + */ +static void add_pai_header(pjsip_tx_data *tdata, const struct ast_party_id *id) +{ + static const pj_str_t pj_pai_name = { "P-Asserted-Identity", 19 }; + pjsip_fromto_hdr *pai_hdr; + pjsip_fromto_hdr *old_pai; + + if (!id->number.valid) { + return; + } + + /* Since inv_session reuses responses, we have to make sure there's not already + * a P-Asserted-Identity present. If there is, we just modify the old one. + */ + old_pai = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_pai_name, NULL); + if (old_pai) { + modify_id_header(tdata->pool, old_pai, id); + add_privacy_header(tdata, id); + return; + } + + pai_hdr = create_new_id_hdr(&pj_pai_name, tdata, id); + if (!pai_hdr) { + return; + } + add_privacy_header(tdata, id); + + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)pai_hdr); +} + +/*! + * \internal + * \brief Add privacy and screen parameters to a Remote-Party-ID header. + * + * If privacy is requested, then the privacy and screen parameters need to + * reflect this. Similarly, if no privacy or screening is to be communicated, + * we need to make sure that any previously set values are updated. + * + * \param tdata The message where the Remote-Party-ID header is + * \param hdr The header on which the parameters are being added + * \param id The identification information used to determine privacy + */ +static void add_privacy_params(pjsip_tx_data *tdata, pjsip_fromto_hdr *hdr, const struct ast_party_id *id) +{ + static const pj_str_t privacy_str = { "privacy", 7 }; + static const pj_str_t screen_str = { "screen", 6 }; + static const pj_str_t privacy_full_str = { "full", 4 }; + static const pj_str_t privacy_off_str = { "off", 3 }; + static const pj_str_t screen_yes_str = { "yes", 3 }; + static const pj_str_t screen_no_str = { "no", 2 }; + pjsip_param *old_privacy; + pjsip_param *old_screen; + pjsip_param *privacy; + pjsip_param *screen; + + old_privacy = pjsip_param_find(&hdr->other_param, &privacy_str); + old_screen = pjsip_param_find(&hdr->other_param, &screen_str); + + if (!old_privacy) { + privacy = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); + privacy->name = privacy_str; + pj_list_insert_before(&hdr->other_param, privacy); + } else { + privacy = old_privacy; + } + + if (!old_screen) { + screen = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param); + screen->name = screen_str; + pj_list_insert_before(&hdr->other_param, screen); + } else { + screen = old_screen; + } + + if ((id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED && + (id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + privacy->value = privacy_off_str; + } else { + privacy->value = privacy_full_str; + } + + if ((id->name.presentation & AST_PRES_NUMBER_TYPE) == AST_PRES_USER_NUMBER_PASSED_SCREEN && + (id->number.presentation & AST_PRES_NUMBER_TYPE) == AST_PRES_USER_NUMBER_PASSED_SCREEN) { + screen->value = screen_yes_str; + } else { + screen->value = screen_no_str; + } +} + +/*! + * \internal + * \brief Add a Remote-Party-ID header to an outbound message + * \param tdata The message to add the header to + * \param id The identification information used to populate the header + */ +static void add_rpid_header(pjsip_tx_data *tdata, const struct ast_party_id *id) +{ + static const pj_str_t pj_rpid_name = { "Remote-Party-ID", 15 }; + pjsip_fromto_hdr *rpid_hdr; + pjsip_fromto_hdr *old_rpid; + + if (!id->number.valid) { + return; + } + + /* Since inv_session reuses responses, we have to make sure there's not already + * a P-Asserted-Identity present. If there is, we just modify the old one. + */ + old_rpid = pjsip_msg_find_hdr_by_name(tdata->msg, &pj_rpid_name, NULL); + if (old_rpid) { + modify_id_header(tdata->pool, old_rpid, id); + add_privacy_params(tdata, old_rpid, id); + return; + } + + rpid_hdr = create_new_id_hdr(&pj_rpid_name, tdata, id); + if (!rpid_hdr) { + return; + } + add_privacy_params(tdata, rpid_hdr, id); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *)rpid_hdr); +} + +/*! + * \internal + * \brief Add any appropriate identification headers to an outbound SIP message + * + * This will determine if an outbound message should have identification headers and + * will add the appropriately configured headers + * + * \param session The session on which we will be sending the message + * \param tdata The outbound message + * \param The identity information to place on the message + */ +static void add_id_headers(const struct ast_sip_session *session, pjsip_tx_data *tdata, const struct ast_party_id *id) +{ + if (((id->name.presentation & AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED || + (id->number.presentation & AST_PRES_RESTRICTION) == AST_PRES_RESTRICTED) && + !session->endpoint->trust_id_outbound) { + return; + } + if (session->endpoint->send_pai) { + add_pai_header(tdata, id); + } + if (session->endpoint->send_rpid) { + add_rpid_header(tdata, id); + } +} + +/*! + * \internal + * \brief Session supplement callback for outgoing INVITE requests + * + * For an initial INVITE request, we may change the From header to appropriately + * reflect the identity information. On all INVITEs (initial and reinvite) we may + * add other identity headers such as P-Asserted-Identity and Remote-Party-ID based + * on configuration and privacy settings + * + * \param session The session on which the INVITE will be sent + * \param tdata The outbound INVITE request + */ +static void caller_id_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + struct ast_party_id connected_id; + + if (!session->channel) { + return; + } + + connected_id = ast_channel_connected_effective_id(session->channel); + if (session->inv_session->state < PJSIP_INV_STATE_CONFIRMED && + session->endpoint->trust_id_outbound && + (connected_id.name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED && + (connected_id.name.presentation & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED) { + /* Only change the From header on the initial outbound INVITE. Switching it + * mid-call might confuse some UAs. + */ + pjsip_fromto_hdr *from; + pjsip_dialog *dlg; + + from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, tdata->msg->hdr.next); + dlg = session->inv_session->dlg; + + modify_id_header(tdata->pool, from, &connected_id); + modify_id_header(dlg->pool, dlg->local.info, &connected_id); + if (should_queue_connected_line_update(session, &session->endpoint->id)) { + queue_connected_line_update(session, &session->endpoint->id); + } + } + add_id_headers(session, tdata, &connected_id); +} + +/*! + * \internal + * \brief Session supplement for outgoing INVITE response + * + * This will add P-Asserted-Identity and Remote-Party-ID headers if necessary + * + * \param session The session on which the INVITE response is to be sent + * \param tdata The outbound INVITE response + */ +static void caller_id_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + struct ast_party_id connected_id; + + if (!session->channel) { + return; + } + connected_id = ast_channel_connected_effective_id(session->channel); + add_id_headers(session, tdata, &connected_id); +} + +static struct ast_sip_session_supplement caller_id_supplement = { + .method = "INVITE", + .priority = AST_SIP_SESSION_SUPPLEMENT_PRIORITY_CHANNEL - 1000, + .incoming_request = caller_id_incoming_request, + .incoming_response = caller_id_incoming_response, + .outgoing_request = caller_id_outgoing_request, + .outgoing_response = caller_id_outgoing_response, +}; + +static int load_module(void) +{ + ast_sip_session_register_supplement(&caller_id_supplement); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_session_unregister_supplement(&caller_id_supplement); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Caller ID Support", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_dtmf_info.c b/res/res_sip_dtmf_info.c new file mode 100644 index 000000000..453e57d06 --- /dev/null +++ b/res/res_sip_dtmf_info.c @@ -0,0 +1,128 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Jason Parker <jparker@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_session.h" +#include "asterisk/module.h" + +static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + int res = 0; + pjsip_msg_body *body = rdata->msg_info.msg->body; + + pjsip_tx_data *tdata; + + char buf[body->len]; + char *cur = buf; + char *line; + + char event = '\0'; + unsigned int duration = 0; + + if (pj_strcmp2(&body->content_type.type, "application") || + pj_strcmp2(&body->content_type.subtype, "dtmf-relay")) { + return 0; + } + + body->print_body(body, buf, body->len); + + while ((line = strsep(&cur, "\r\n"))) { + char *c; + + if (!(c = strchr(line, '='))) { + continue; + } + *c++ = '\0'; + + c = ast_skip_blanks(c); + + if (!strcasecmp(line, "signal")) { + if (c[0] == '!' || c[0] == '*' || c[0] == '#' || + ('0' <= c[0] && c[0] <= '9') || + ('A' <= c[0] && c[0] <= 'D') || + ('a' <= c[0] && c[0] <= 'd')) { + event = c[0]; + } else { + ast_log(LOG_ERROR, "Invalid DTMF event signal in INFO message.\n"); + res = -1; + break; + } + } else if (!strcasecmp(line, "duration")) { + sscanf(c, "%30u", &duration); + } + } + + if (!duration) { + duration = 100; + } + + if (event == '!') { + struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_FLASH, } }; + + ast_queue_frame(session->channel, &f); + } else if (event != '\0') { + struct ast_frame f = { AST_FRAME_DTMF, }; + f.len = duration; + f.subclass.integer = event; + + ast_queue_frame(session->channel, &f); + } else { + res = -1; + } + + if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, !res ? 200 : 500, NULL, &tdata) == PJ_SUCCESS) { + struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + + pjsip_dlg_send_response(session->inv_session->dlg, tsx, tdata); + } + + return res; +} + +static struct ast_sip_session_supplement dtmf_info_supplement = { + .method = "INFO", + .incoming_request = dtmf_info_incoming_request, +}; + +static int load_module(void) +{ + ast_sip_session_register_supplement(&dtmf_info_supplement); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_session_unregister_supplement(&dtmf_info_supplement); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP DTMF INFO Support", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_endpoint_identifier_constant.c b/res/res_sip_endpoint_identifier_constant.c new file mode 100644 index 000000000..e519a9ee8 --- /dev/null +++ b/res/res_sip_endpoint_identifier_constant.c @@ -0,0 +1,67 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * Includes code and algorithms from the Zapata library. + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <defaultenabled>no</defaultenabled> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" + +static struct ast_sip_endpoint *constant_identify(pjsip_rx_data *rdata) +{ + /* This endpoint identifier always returns the same endpoint. It's used + * simply for testing. It allocates an endpoint from sorcery so default values + * do get applied. + */ + struct ast_sip_endpoint *endpoint = ast_sorcery_alloc(ast_sip_get_sorcery(), "endpoint", NULL); + if (!endpoint) { + return NULL; + } + ast_parse_allow_disallow(&endpoint->prefs, endpoint->codecs, "ulaw", 1); + return endpoint; +} + +static struct ast_sip_endpoint_identifier constant_identifier = { + .identify_endpoint = constant_identify, +}; + +static int load_module(void) +{ + ast_sip_register_endpoint_identifier(&constant_identifier); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Constant Endpoint Identifier", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_endpoint_identifier_ip.c b/res/res_sip_endpoint_identifier_ip.c new file mode 100644 index 000000000..49c70b59d --- /dev/null +++ b/res/res_sip_endpoint_identifier_ip.c @@ -0,0 +1,151 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" +#include "asterisk/acl.h" + +/*! \brief Structure for an IP identification matching object */ +struct ip_identify_match { + /*! \brief Sorcery object details */ + SORCERY_OBJECT(details); + /*! \brief Stringfields */ + AST_DECLARE_STRING_FIELDS( + /*! The name of the endpoint */ + AST_STRING_FIELD(endpoint_name); + ); + /*! \brief Networks or addresses that should match this */ + struct ast_ha *matches; +}; + +/*! \brief Destructor function for a matching object */ +static void ip_identify_destroy(void *obj) +{ + struct ip_identify_match *identify = obj; + + ast_string_field_free_memory(identify); + ast_free_ha(identify->matches); +} + +/*! \brief Allocator function for a matching object */ +static void *ip_identify_alloc(const char *name) +{ + struct ip_identify_match *identify = ao2_alloc(sizeof(*identify), ip_identify_destroy); + + if (!identify || ast_string_field_init(identify, 256)) { + ao2_cleanup(identify); + return NULL; + } + + return identify; +} + +/*! \brief Comparator function for a matching object */ +static int ip_identify_match_check(void *obj, void *arg, int flags) +{ + struct ip_identify_match *identify = obj; + struct ast_sockaddr *addr = arg; + + return (ast_apply_ha(identify->matches, addr) != AST_SENSE_ALLOW) ? CMP_MATCH | CMP_STOP : 0; +} + +static struct ast_sip_endpoint *ip_identify(pjsip_rx_data *rdata) +{ + struct ast_sockaddr addr = { { 0, } }; + RAII_VAR(struct ao2_container *, candidates, NULL, ao2_cleanup); + RAII_VAR(struct ip_identify_match *, match, NULL, ao2_cleanup); + + /* If no possibilities exist return early to save some time */ + if (!(candidates = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "identify", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) || + !ao2_container_count(candidates)) { + return NULL; + } + + ast_sockaddr_parse(&addr, rdata->pkt_info.src_name, PARSE_PORT_FORBID); + ast_sockaddr_set_port(&addr, rdata->pkt_info.src_port); + + if (!(match = ao2_callback(candidates, 0, ip_identify_match_check, &addr))) { + return NULL; + } + + return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", match->endpoint_name); +} + +static struct ast_sip_endpoint_identifier ip_identifier = { + .identify_endpoint = ip_identify, +}; + +/*! \brief Custom handler for match field */ +static int ip_identify_match_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct ip_identify_match *identify = obj; + int error = 0; + + /* We deny what we actually want to match because there is an implicit permit all rule for ACLs */ + if (!(identify->matches = ast_append_ha("d", var->value, identify->matches, &error))) { + return -1; + } + + return error; +} + +static int load_module(void) +{ + ast_sorcery_apply_default(ast_sip_get_sorcery(), "identify", "config", "res_sip.conf,criteria=type=identify"); + + if (ast_sorcery_object_register(ast_sip_get_sorcery(), "identify", ip_identify_alloc, NULL, NULL)) { + return AST_MODULE_LOAD_DECLINE; + } + + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "identify", "endpoint", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ip_identify_match, endpoint_name)); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "identify", "match", "", ip_identify_match_handler, NULL, 0, 0); + ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify"); + + ast_sip_register_endpoint_identifier(&ip_identifier); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload_module(void) +{ + ast_sorcery_reload_object(ast_sip_get_sorcery(), "identify"); + return 0; +} + +static int unload_module(void) +{ + ast_sip_unregister_endpoint_identifier(&ip_identifier); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP IP endpoint identifier", + .load = load_module, + .reload = reload_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_endpoint_identifier_user.c b/res/res_sip_endpoint_identifier_user.c new file mode 100644 index 000000000..cd1f76bb1 --- /dev/null +++ b/res/res_sip_endpoint_identifier_user.c @@ -0,0 +1,128 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" + +static int get_endpoint_details(pjsip_rx_data *rdata, char *endpoint, size_t endpoint_size, char *domain, size_t domain_size) +{ + pjsip_uri *from = rdata->msg_info.from->uri; + pjsip_sip_uri *sip_from; + if (!PJSIP_URI_SCHEME_IS_SIP(from) && !PJSIP_URI_SCHEME_IS_SIPS(from)) { + return -1; + } + sip_from = (pjsip_sip_uri *) pjsip_uri_get_uri(from); + ast_copy_pj_str(endpoint, &sip_from->user, endpoint_size); + ast_copy_pj_str(domain, &sip_from->host, domain_size); + return 0; +} + +static int find_transport_in_use(void *obj, void *arg, int flags) +{ + struct ast_sip_transport *transport = obj; + pjsip_rx_data *rdata = arg; + + if ((transport->state->transport == rdata->tp_info.transport) || + (transport->state->factory && !pj_strcmp(&transport->state->factory->addr_name.host, &rdata->tp_info.transport->local_name.host) && + transport->state->factory->addr_name.port == rdata->tp_info.transport->local_name.port)) { + return CMP_MATCH | CMP_STOP; + } + + return 0; +} + +static struct ast_sip_endpoint *username_identify(pjsip_rx_data *rdata) +{ + char endpoint_name[64], domain_name[64], id[AST_UUID_STR_LEN]; + struct ast_sip_endpoint *endpoint; + RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup); + RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup); + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + + if (get_endpoint_details(rdata, endpoint_name, sizeof(endpoint_name), domain_name, sizeof(domain_name))) { + return NULL; + } + + /* Attempt to find the endpoint given the name and domain provided */ + snprintf(id, sizeof(id), "%s@%s", endpoint_name, domain_name); + if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) { + goto done; + } + + /* See if an alias exists for the domain provided */ + if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { + snprintf(id, sizeof(id), "%s@%s", endpoint_name, alias->domain); + if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) { + goto done; + } + } + + /* See if the transport this came in on has a provided domain */ + if ((transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) && + (transport = ao2_callback(transports, 0, find_transport_in_use, rdata)) && + !ast_strlen_zero(transport->domain)) { + snprintf(id, sizeof(id), "%s@%s", endpoint_name, transport->domain); + if ((endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id))) { + goto done; + } + } + + /* Fall back to no domain */ + endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", endpoint_name); + +done: + if (endpoint) { + if (!(endpoint->ident_method & AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME)) { + ao2_ref(endpoint, -1); + return NULL; + } + ast_debug(3, "Retrieved endpoint %s\n", ast_sorcery_object_get_id(endpoint)); + } + return endpoint; +} + +static struct ast_sip_endpoint_identifier username_identifier = { + .identify_endpoint = username_identify, +}; + +static int load_module(void) +{ + ast_sip_register_endpoint_identifier(&username_identifier); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_endpoint_identifier(&username_identifier); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP username endpoint identifier", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_logger.c b/res/res_sip_logger.c new file mode 100644 index 000000000..da1719810 --- /dev/null +++ b/res/res_sip_logger.c @@ -0,0 +1,81 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" + +static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) +{ + ast_verbose("<--- Transmitting SIP %s (%d bytes) to %s:%s:%d --->\n%.*s\n", + tdata->msg->type == PJSIP_REQUEST_MSG ? "request" : "response", + (int) (tdata->buf.cur - tdata->buf.start), + tdata->tp_info.transport->type_name, + tdata->tp_info.dst_name, + tdata->tp_info.dst_port, + (int) (tdata->buf.end - tdata->buf.start), tdata->buf.start); + return PJ_SUCCESS; +} + +static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) +{ + ast_verbose("<--- Received SIP %s (%d bytes) from %s:%s:%d --->\n%s\n", + rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ? "request" : "response", + rdata->msg_info.len, + rdata->tp_info.transport->type_name, + rdata->pkt_info.src_name, + rdata->pkt_info.src_port, + rdata->pkt_info.packet); + return PJ_FALSE; +} + +static pjsip_module logging_module = { + .name = { "Logging Module", 14 }, + .priority = 0, + .on_rx_request = logging_on_rx_msg, + .on_rx_response = logging_on_rx_msg, + .on_tx_request = logging_on_tx_msg, + .on_tx_response = logging_on_tx_msg, +}; + +static int load_module(void) +{ + ast_sip_register_service(&logging_module); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_service(&logging_module); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Packet Logger", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_mwi.c b/res/res_sip_mwi.c new file mode 100644 index 000000000..7d62816d0 --- /dev/null +++ b/res/res_sip_mwi.c @@ -0,0 +1,709 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_simple.h> +#include <pjlib.h> + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_pubsub.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/astobj2.h" +#include "asterisk/sorcery.h" +#include "asterisk/stasis.h" +#include "asterisk/app.h" + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sip</depend> + <depend>res_sip_pubsub</depend> + <support_level>core</support_level> + ***/ + +struct mwi_subscription; +AO2_GLOBAL_OBJ_STATIC(unsolicited_mwi); + +#define STASIS_BUCKETS 13 +#define MWI_BUCKETS 53 +static void mwi_subscription_shutdown(struct ast_sip_subscription *sub); +static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata); +static void mwi_resubscribe(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, + struct ast_sip_subscription_response_data *response_data); +static void mwi_subscription_timeout(struct ast_sip_subscription *sub); +static void mwi_subscription_terminated(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); +static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata); +static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, + struct ast_sip_subscription_response_data *response_data); +static int mwi_refresh_subscription(struct ast_sip_subscription *sub); + +static struct ast_sip_subscription_handler mwi_handler = { + .event_name = "message-summary", + .accept = { "application/simple-message-summary", }, + .subscription_shutdown = mwi_subscription_shutdown, + .new_subscribe = mwi_new_subscribe, + .resubscribe = mwi_resubscribe, + .subscription_timeout = mwi_subscription_timeout, + .subscription_terminated = mwi_subscription_terminated, + .notify_response = mwi_notify_response, + .notify_request = mwi_notify_request, + .refresh_subscription = mwi_refresh_subscription, +}; + +/*! + * \brief Wrapper for stasis subscription + * + * An MWI subscription has a container of these. This + * represents a stasis subscription for MWI state. + */ +struct mwi_stasis_subscription { + /*! The MWI stasis subscription */ + struct stasis_subscription *stasis_sub; + /*! The mailbox corresponding with the MWI subscription. Used as a hash key */ + char mailbox[1]; +}; + +/*! + * \brief A subscription for MWI + * + * This subscription is the basis for MWI for an endpoint. Each + * endpoint that uses MWI will have a corresponding mwi_subscription. + * + * This structure acts as the owner for the underlying SIP subscription. + * When the mwi_subscription is destroyed, the SIP subscription dies, too. + * The mwi_subscription's lifetime is governed by its underlying stasis + * subscriptions. When all stasis subscriptions are destroyed, the + * mwi_subscription is destroyed as well. + */ +struct mwi_subscription { + /*! Container of \ref mwi_stasis_subscription structures. + * A single MWI subscription may be fore multiple mailboxes, thus + * requiring multiple stasis subscriptions + */ + struct ao2_container *stasis_subs; + /*! The SIP subscription. Unsolicited MWI does not use this */ + struct ast_sip_subscription *sip_sub; + /*! Is the MWI solicited (i.e. Initiated with an external SUBSCRIBE) ? */ + unsigned int is_solicited; + /*! Identifier for the subscription. + * The identifier is the same as the corresponding endpoint's stasis ID. + * Used as a hash key + */ + char id[1]; +}; + +static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *msg); + +static struct mwi_stasis_subscription *mwi_stasis_subscription_alloc(const char *mailbox, struct mwi_subscription *mwi_sub) +{ + struct mwi_stasis_subscription *mwi_stasis_sub; + struct stasis_topic *topic; + + if (!mwi_sub) { + return NULL; + } + + mwi_stasis_sub = ao2_alloc(sizeof(*mwi_stasis_sub) + strlen(mailbox), NULL); + if (!mwi_stasis_sub) { + return NULL; + } + + topic = stasis_mwi_topic(mailbox); + + /* Safe strcpy */ + strcpy(mwi_stasis_sub->mailbox, mailbox); + ao2_ref(mwi_sub, +1); + ast_debug(3, "Creating stasis MWI subscription to mailbox %s for endpoint %s\n", mailbox, mwi_sub->id); + mwi_stasis_sub->stasis_sub = stasis_subscribe(topic, mwi_stasis_cb, mwi_sub); + return mwi_stasis_sub; +} + +static int stasis_sub_hash(const void *obj, int flags) +{ + const struct mwi_stasis_subscription *mwi_stasis = obj; + + return ast_str_hash(mwi_stasis->mailbox); +} + +static int stasis_sub_cmp(void *obj, void *arg, int flags) +{ + struct mwi_stasis_subscription *mwi_stasis1 = obj; + struct mwi_stasis_subscription *mwi_stasis2 = arg; + + return strcmp(mwi_stasis1->mailbox, mwi_stasis2->mailbox) ? 0 : CMP_MATCH; +} + +static void mwi_subscription_destructor(void *obj) +{ + struct mwi_subscription *sub = obj; + + ast_debug(3, "Destroying MWI subscription for endpoint %s\n", sub->id); + ao2_cleanup(sub->sip_sub); + ao2_cleanup(sub->stasis_subs); +} + +static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *endpoint, + enum ast_sip_subscription_role role, unsigned int is_solicited, pjsip_rx_data *rdata) +{ + struct mwi_subscription *sub; + const char *endpoint_id = ast_sorcery_object_get_id(endpoint); + + sub = ao2_alloc(sizeof(*sub) + strlen(endpoint_id), + mwi_subscription_destructor); + + if (!sub) { + return NULL; + } + + /* Safe strcpy */ + strcpy(sub->id, endpoint_id); + /* Unsolicited MWI doesn't actually result in a SIP subscription being + * created. This is because a SIP subscription associates with a dialog. + * Most devices expect unsolicited MWI NOTIFYs to appear out of dialog. If + * they receive an in-dialog MWI NOTIFY (i.e. with a to-tag), then they + * will reject the NOTIFY with a 481, thus resulting in message-waiting + * state not being updated on the device + */ + if (is_solicited) { + sub->sip_sub = ast_sip_create_subscription(&mwi_handler, + role, endpoint, rdata); + if (!sub->sip_sub) { + ast_log(LOG_WARNING, "Unable to create MWI SIP subscription for endpoint %s\n", sub->id); + ao2_cleanup(sub); + return NULL; + } + } + + sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp); + if (!sub->stasis_subs) { + ao2_cleanup(sub); + return NULL; + } + sub->is_solicited = is_solicited; + + ast_debug(3, "Created %s MWI subscription for endpoint %s\n", is_solicited ? "solicited" : "unsolicited", sub->id); + + return sub; +} + +static int mwi_sub_hash(const void *obj, int flags) +{ + const struct mwi_subscription *mwi_sub = obj; + + return ast_str_hash(mwi_sub->id); +} + +static int mwi_sub_cmp(void *obj, void *arg, int flags) +{ + struct mwi_subscription *mwi_sub1 = obj; + struct mwi_subscription *mwi_sub2 = arg; + + return strcmp(mwi_sub1->id, mwi_sub2->id) ? 0 : CMP_MATCH; +} + +struct message_accumulator { + int old_msgs; + int new_msgs; + const char *reason; +}; + +static int get_message_count(void *obj, void *arg, int flags) +{ + RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + struct mwi_stasis_subscription *mwi_stasis = obj; + struct message_accumulator *counter = arg; + struct stasis_mwi_state *mwi_state; + + msg = stasis_cache_get(stasis_mwi_topic_cached(), stasis_mwi_state_type(), mwi_stasis->mailbox); + if (!msg) { + return 0; + } + + mwi_state = stasis_message_data(msg); + counter->old_msgs += mwi_state->old_msgs; + counter->new_msgs += mwi_state->new_msgs; + return 0; +} + +struct unsolicited_mwi_data { + struct mwi_subscription *sub; + struct ast_sip_endpoint *endpoint; + pjsip_evsub_state state; + const char *reason; + const pjsip_media_type *mwi_type; + const pj_str_t *body_text; +}; + +static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flags) +{ + struct unsolicited_mwi_data *mwi_data = arg; + struct mwi_subscription *sub = mwi_data->sub; + struct ast_sip_endpoint *endpoint = mwi_data->endpoint; + pjsip_evsub_state state = mwi_data->state; + const char *reason = mwi_data->reason; + const pjsip_media_type *mwi_type = mwi_data->mwi_type; + const pj_str_t *body_text = mwi_data->body_text; + struct ast_sip_contact *contact = obj; + const char *state_name; + pjsip_tx_data *tdata; + pjsip_msg_body *msg_body; + pjsip_sub_state_hdr *sub_state; + pjsip_event_hdr *event; + const pjsip_hdr *allow_events = pjsip_evsub_get_allow_events_hdr(NULL); + + if (ast_sip_create_request("NOTIFY", NULL, endpoint, contact->uri, &tdata)) { + ast_log(LOG_WARNING, "Unable to create unsolicited NOTIFY request to endpoint %s URI %s\n", sub->id, contact->uri); + return 0; + } + + switch (state) { + case PJSIP_EVSUB_STATE_ACTIVE: + state_name = "active"; + break; + case PJSIP_EVSUB_STATE_TERMINATED: + default: + state_name = "terminated"; + break; + } + + sub_state = pjsip_sub_state_hdr_create(tdata->pool); + pj_cstr(&sub_state->sub_state, state_name); + if (reason) { + pj_cstr(&sub_state->reason_param, reason); + } + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) sub_state); + + event = pjsip_event_hdr_create(tdata->pool); + pj_cstr(&event->event_type, "message-summary"); + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) event); + + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, allow_events)); + msg_body = pjsip_msg_body_create(tdata->pool, &mwi_type->type, &mwi_type->subtype, body_text); + tdata->msg->body = msg_body; + ast_sip_send_request(tdata, NULL, endpoint); + + return 0; +} + +static void send_unsolicited_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state state, const char *reason, + const pjsip_media_type *mwi_type, const pj_str_t *body_text) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), + "endpoint", sub->id), ao2_cleanup); + char *endpoint_aors; + char *aor_name; + + if (!endpoint) { + ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because endpoint does not exist\n", + sub->id); + return; + } + if (ast_strlen_zero(endpoint->aors)) { + ast_log(LOG_WARNING, "Unable to send unsolicited MWI to %s because the endpoint has no" + " configured AORs\n", sub->id); + return; + } + + endpoint_aors = ast_strdupa(endpoint->aors); + + while ((aor_name = strsep(&endpoint_aors, ","))) { + RAII_VAR(struct ast_sip_aor *, aor, ast_sip_location_retrieve_aor(aor_name), ao2_cleanup); + RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); + struct unsolicited_mwi_data mwi_data = { + .sub = sub, + .endpoint = endpoint, + .state = state, + .reason = reason, + .mwi_type = mwi_type, + .body_text = body_text, + }; + + if (!aor) { + ast_log(LOG_WARNING, "Unable to locate AOR %s for unsolicited MWI\n", aor_name); + continue; + } + + contacts = ast_sip_location_retrieve_aor_contacts(aor); + if (!contacts || (ao2_container_count(contacts) == 0)) { + ast_log(LOG_WARNING, "No contacts bound to AOR %s. Cannot send unsolicited MWI.\n", aor_name); + continue; + } + + ao2_callback(contacts, OBJ_NODATA, send_unsolicited_mwi_notify_to_contact, &mwi_data); + } +} + +static void send_mwi_notify(struct mwi_subscription *sub, pjsip_evsub_state state, const char *reason) +{ + const pj_str_t *reason_str_ptr = NULL; + static pjsip_media_type mwi_type = { + .type = { "application", 11 }, + .subtype = { "simple-message-summary", 22 }, + }; + struct message_accumulator counter = { + .old_msgs = 0, + .new_msgs = 0, + }; + RAII_VAR(struct ast_str *, body, ast_str_create(64), ast_free_ptr); + pjsip_tx_data *tdata; + pj_str_t reason_str; + pj_str_t pj_body; + + ao2_callback(sub->stasis_subs, OBJ_NODATA, get_message_count, &counter); + + if (reason) { + pj_cstr(&reason_str, reason); + reason_str_ptr = &reason_str; + } + ast_str_append(&body, 0, "Messages-Waiting: %s\r\n", counter.new_msgs ? "yes" : "no"); + ast_str_append(&body, 0, "Voice-Message: %d/%d (0/0)\r\n", counter.new_msgs, counter.old_msgs); + pj_cstr(&pj_body, ast_str_buffer(body)); + + ast_debug(5, "Sending %s MWI NOTIFY to endpoint %s, new messages: %d, old messages: %d\n", + sub->is_solicited ? "solicited" : "unsolicited", sub->id, counter.new_msgs, + counter.old_msgs); + + if (sub->is_solicited) { + if (pjsip_mwi_notify(ast_sip_subscription_get_evsub(sub->sip_sub), + state, + NULL, + reason_str_ptr, + &mwi_type, + &pj_body, + &tdata) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Unable to create MWI NOTIFY request to %s.\n", sub->id); + return; + } + if (ast_sip_subscription_send_request(sub->sip_sub, tdata) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Unable to send MWI NOTIFY request to %s\n", sub->id); + return; + } + } else { + send_unsolicited_mwi_notify(sub, state, reason, &mwi_type, &pj_body); + } +} + +static int unsubscribe_stasis(void *obj, void *arg, int flags) +{ + struct mwi_stasis_subscription *mwi_stasis = obj; + if (mwi_stasis->stasis_sub) { + ast_debug(3, "Removing stasis subscription to mailbox %s\n", mwi_stasis->mailbox); + mwi_stasis->stasis_sub = stasis_unsubscribe(mwi_stasis->stasis_sub); + } + return CMP_MATCH; +} + +static void mwi_subscription_shutdown(struct ast_sip_subscription *sub) +{ + struct mwi_subscription *mwi_sub; + RAII_VAR(struct ast_datastore *, mwi_datastore, + ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); + + if (!mwi_datastore) { + return; + } + + mwi_sub = mwi_datastore->data; + ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL); +} + +static struct ast_datastore_info mwi_ds_info = { }; + +static int add_mwi_datastore(struct mwi_subscription *sub) +{ + RAII_VAR(struct ast_datastore *, mwi_datastore, NULL, ao2_cleanup); + + mwi_datastore = ast_sip_subscription_alloc_datastore(&mwi_ds_info, "MWI datastore"); + if (!mwi_datastore) { + return -1; + } + mwi_datastore->data = sub; + + ast_sip_subscription_add_datastore(sub->sip_sub, mwi_datastore); + return 0; +} + +static struct ast_sip_subscription *mwi_new_subscribe(struct ast_sip_endpoint *endpoint, + pjsip_rx_data *rdata) +{ + RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); + /* It's not obvious here, but the reference(s) to this subscription, + * once this function exits, is held by the stasis subscription(s) + * created in mwi_stasis_subscription_alloc() + */ + RAII_VAR(struct mwi_subscription *, sub, NULL, ao2_cleanup); + pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri; + pjsip_sip_uri *sip_ruri; + pjsip_evsub *evsub; + char aor_name[80]; + char *mailboxes; + char *mailbox; + + if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { + ast_log(LOG_WARNING, "Attempt to SUBSCRIBE to a non-SIP URI\n"); + return NULL; + } + sip_ruri = pjsip_uri_get_uri(ruri); + ast_copy_pj_str(aor_name, &sip_ruri->user, sizeof(aor_name)); + + aor = ast_sip_location_retrieve_aor(aor_name); + if (!aor) { + ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n", aor_name); + return NULL; + } + + if (ast_strlen_zero(aor->mailboxes)) { + ast_log(LOG_WARNING, "AOR %s has no configured mailboxes. MWI subscription failed\n", aor_name); + return NULL; + } + + sub = mwi_subscription_alloc(endpoint, AST_SIP_NOTIFIER, 1, rdata); + if (!sub) { + return NULL; + } + + if (add_mwi_datastore(sub)) { + ast_log(LOG_WARNING, "Unable to allocate datastore on MWI subscription from %s\n", sub->id); + return NULL; + } + + mailboxes = ast_strdupa(aor->mailboxes); + while ((mailbox = strsep(&mailboxes, ","))) { + RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub, + mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup); + if (mwi_stasis_sub) { + ao2_link(sub->stasis_subs, mwi_stasis_sub); + } + } + + evsub = ast_sip_subscription_get_evsub(sub->sip_sub); + pjsip_evsub_accept(evsub, rdata, 200, NULL); + send_mwi_notify(sub, PJSIP_EVSUB_STATE_ACTIVE, NULL); + + return sub->sip_sub; +} + +static void mwi_resubscribe(struct ast_sip_subscription *sub, + pjsip_rx_data *rdata, struct ast_sip_subscription_response_data *response_data) +{ + pjsip_tx_data *tdata; + + pjsip_mwi_current_notify(ast_sip_subscription_get_evsub(sub), &tdata); + ast_sip_subscription_send_request(sub, tdata); +} + +static void mwi_subscription_timeout(struct ast_sip_subscription *sub) +{ + struct mwi_subscription *mwi_sub; + RAII_VAR(struct ast_datastore *, mwi_datastore, + ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); + + if (!mwi_datastore) { + return; + } + + + mwi_sub = mwi_datastore->data; + + ast_log(LOG_NOTICE, "MWI subscription for %s has timed out.\n", mwi_sub->id); + + send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_TERMINATED, "timeout"); +} + +static void mwi_subscription_terminated(struct ast_sip_subscription *sub, pjsip_rx_data *rdata) +{ + struct mwi_subscription *mwi_sub; + RAII_VAR(struct ast_datastore *, mwi_datastore, + ast_sip_subscription_get_datastore(sub, "MWI datastore"), ao2_cleanup); + + if (!mwi_datastore) { + return; + } + + mwi_sub = mwi_datastore->data; + + ast_log(LOG_NOTICE, "MWI subscription for %s has been terminated\n", mwi_sub->id); + + send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_TERMINATED, NULL); +} + +static void mwi_notify_response(struct ast_sip_subscription *sub, pjsip_rx_data *rdata) +{ + /* We don't really care about NOTIFY responses for the moment */ +} + +static void mwi_notify_request(struct ast_sip_subscription *sub, pjsip_rx_data *rdata, + struct ast_sip_subscription_response_data *response_data) +{ + ast_log(LOG_WARNING, "Received an MWI NOTIFY request? This should not happen\n"); +} + +static int mwi_refresh_subscription(struct ast_sip_subscription *sub) +{ + ast_log(LOG_WARNING, "Being told to refresh an MWI subscription? This should not happen\n"); + return 0; +} + +static int serialized_notify(void *userdata) +{ + struct mwi_subscription *mwi_sub = userdata; + + send_mwi_notify(mwi_sub, PJSIP_EVSUB_STATE_ACTIVE, NULL); + ao2_ref(mwi_sub, -1); + return 0; +} + +static int serialized_cleanup(void *userdata) +{ + struct mwi_subscription *mwi_sub = userdata; + + /* This is getting rid of the reference that was added + * just before this serialized task was pushed. + */ + ao2_cleanup(mwi_sub); + /* This is getting rid of the reference held by the + * stasis subscription + */ + ao2_cleanup(mwi_sub); + return 0; +} + +static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub, + struct stasis_topic *topic, struct stasis_message *msg) +{ + struct mwi_subscription *mwi_sub = userdata; + + if (stasis_subscription_final_message(sub, msg)) { + ao2_ref(mwi_sub, +1); + ast_sip_push_task(NULL, serialized_cleanup, mwi_sub); + return; + } + + if (stasis_mwi_state_type() == stasis_message_type(msg)) { + struct ast_taskprocessor *serializer = mwi_sub->is_solicited ? ast_sip_subscription_get_serializer(mwi_sub->sip_sub) : NULL; + ao2_ref(mwi_sub, +1); + ast_sip_push_task(serializer, serialized_notify, mwi_sub); + } +} + +static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags) +{ + RAII_VAR(struct mwi_subscription *, aggregate_sub, NULL, ao2_cleanup); + struct ast_sip_endpoint *endpoint = obj; + struct ao2_container *mwi_subscriptions = arg; + char *mailboxes; + char *mailbox; + + if (ast_strlen_zero(endpoint->mailboxes)) { + return 0; + } + + if (endpoint->aggregate_mwi) { + aggregate_sub = mwi_subscription_alloc(endpoint, AST_SIP_NOTIFIER, 0, NULL); + if (!aggregate_sub) { + return 0; + } + } + + mailboxes = ast_strdupa(endpoint->mailboxes); + while ((mailbox = strsep(&mailboxes, ","))) { + struct mwi_subscription *sub = aggregate_sub ?: + mwi_subscription_alloc(endpoint, AST_SIP_SUBSCRIBER, 0, NULL); + RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub, + mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup); + if (mwi_stasis_sub) { + ao2_link(sub->stasis_subs, mwi_stasis_sub); + } + if (!aggregate_sub) { + ao2_link(mwi_subscriptions, sub); + ao2_cleanup(sub); + } + } + ao2_link(mwi_subscriptions, aggregate_sub); + return 0; +} + +static int unsubscribe(void *obj, void *arg, int flags) +{ + struct mwi_subscription *mwi_sub = obj; + + ao2_callback(mwi_sub->stasis_subs, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe_stasis, NULL); + return CMP_MATCH; +} + +static void create_mwi_subscriptions(void) +{ + struct ao2_container *mwi_subscriptions = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp); + RAII_VAR(struct ao2_container *, old_mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup); + RAII_VAR(struct ao2_container *, endpoints, ast_sorcery_retrieve_by_fields( + ast_sip_get_sorcery(), "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), + ao2_cleanup); + + if (!mwi_subscriptions) { + return; + } + + /* We remove all the old stasis subscriptions first before applying the new configuration. This + * prevents a situation where there might be multiple overlapping stasis subscriptions for an + * endpoint for mailboxes. Though there may be mailbox changes during the gap between unsubscribing + * and resubscribing, up-to-date mailbox state will be sent out to the endpoint when the + * new stasis subscription is established + */ + if (old_mwi_subscriptions) { + ao2_callback(old_mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); + } + ao2_callback(endpoints, OBJ_NODATA, create_mwi_subscriptions_for_endpoint, mwi_subscriptions); + ao2_global_obj_replace_unref(unsolicited_mwi, mwi_subscriptions); +} + +static int reload(void) +{ + create_mwi_subscriptions(); + return 0; +} + +static int load_module(void) +{ + if (ast_sip_register_subscription_handler(&mwi_handler)) { + return AST_MODULE_LOAD_DECLINE; + } + create_mwi_subscriptions(); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + RAII_VAR(struct ao2_container *, mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup); + if (mwi_subscriptions) { + ao2_callback(mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); + ao2_global_obj_release(unsolicited_mwi); + } + ast_sip_unregister_subscription_handler(&mwi_handler); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP MWI resource", + .load = load_module, + .unload = unload_module, + .reload = reload, + .load_pri = AST_MODPRI_CHANNEL_DEPEND, +); diff --git a/res/res_sip_nat.c b/res/res_sip_nat.c new file mode 100644 index 000000000..6c924af68 --- /dev/null +++ b/res/res_sip_nat.c @@ -0,0 +1,235 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" +#include "asterisk/acl.h" + +static pj_bool_t nat_on_rx_request(pjsip_rx_data *rdata) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); + pjsip_contact_hdr *contact; + + if (!endpoint) { + return PJ_FALSE; + } + + if (endpoint->rewrite_contact && (contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL)) && + (PJSIP_URI_SCHEME_IS_SIP(contact->uri) || PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { + pjsip_sip_uri *uri = pjsip_uri_get_uri(contact->uri); + + pj_cstr(&uri->host, rdata->pkt_info.src_name); + uri->port = rdata->pkt_info.src_port; + } + + if (endpoint->force_rport) { + rdata->msg_info.via->rport_param = 0; + } + + return PJ_FALSE; +} + +/*! \brief Structure which contains information about a transport */ +struct request_transport_details { + /*! \brief Type of transport */ + enum ast_sip_transport_type type; + /*! \brief Potential pointer to the transport itself, if UDP */ + pjsip_transport *transport; + /*! \brief Potential pointer to the transport factory itself, if TCP/TLS */ + pjsip_tpfactory *factory; + /*! \brief Local address for transport */ + pj_str_t local_address; + /*! \brief Local port for transport */ + int local_port; +}; + +/*! \brief Callback function for finding the transport the request is going out on */ +static int find_transport_in_use(void *obj, void *arg, int flags) +{ + struct ast_sip_transport *transport = obj; + struct request_transport_details *details = arg; + + /* If an explicit transport or factory matches then this is what is in use, if we are unavailable + * to compare based on that we make sure that the type is the same and the source IP address/port are the same + */ + if ((details->transport && details->transport == transport->state->transport) || + (details->factory && details->factory == transport->state->factory) || + ((details->type == transport->type) && (transport->state->factory) && + !pj_strcmp(&transport->state->factory->addr_name.host, &details->local_address) && + transport->state->factory->addr_name.port == details->local_port)) { + return CMP_MATCH | CMP_STOP; + } + + return 0; +} + +/*! \brief Helper function which returns the SIP URI of a Contact header */ +static pjsip_sip_uri *nat_get_contact_sip_uri(pjsip_tx_data *tdata) +{ + pjsip_contact_hdr *contact = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CONTACT, NULL); + + if (!contact || (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri))) { + return NULL; + } + + return pjsip_uri_get_uri(contact->uri); +} + +/*! \brief Structure which contains hook details */ +struct nat_hook_details { + /*! \brief Outgoing message itself */ + pjsip_tx_data *tdata; + /*! \brief Chosen transport */ + struct ast_sip_transport *transport; +}; + +/*! \brief Callback function for invoking hooks */ +static int nat_invoke_hook(void *obj, void *arg, int flags) +{ + struct ast_sip_nat_hook *hook = obj; + struct nat_hook_details *details = arg; + + if (hook->outgoing_external_message) { + hook->outgoing_external_message(details->tdata, details->transport); + } + + return 0; +} + +static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata) +{ + RAII_VAR(struct ao2_container *, transports, NULL, ao2_cleanup); + RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup); + struct request_transport_details details = { 0, }; + pjsip_via_hdr *via = NULL; + struct ast_sockaddr addr = { { 0, } }; + pjsip_sip_uri *uri = NULL; + RAII_VAR(struct ao2_container *, hooks, NULL, ao2_cleanup); + + /* If a transport selector is in use we know the transport or factory, so explicitly find it */ + if (tdata->tp_sel.type == PJSIP_TPSELECTOR_TRANSPORT) { + details.transport = tdata->tp_sel.u.transport; + } else if (tdata->tp_sel.type == PJSIP_TPSELECTOR_LISTENER) { + details.factory = tdata->tp_sel.u.listener; + } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP || tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_UDP6) { + /* Connectionless uses the same transport for all requests */ + details.type = AST_SIP_TRANSPORT_UDP; + details.transport = tdata->tp_info.transport; + } else { + if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TCP) { + details.type = AST_SIP_TRANSPORT_TCP; + } else if (tdata->tp_info.transport->key.type == PJSIP_TRANSPORT_TLS) { + details.type = AST_SIP_TRANSPORT_TLS; + } else { + /* Unknown transport type, we can't map and thus can't apply NAT changes */ + return PJ_SUCCESS; + } + + if ((uri = nat_get_contact_sip_uri(tdata))) { + details.local_address = uri->host; + details.local_port = uri->port; + } else if ((tdata->msg->type == PJSIP_REQUEST_MSG) && + (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL))) { + details.local_address = via->sent_by.host; + details.local_port = via->sent_by.port; + } else { + return PJ_SUCCESS; + } + + if (!details.local_port) { + details.local_port = (details.type == AST_SIP_TRANSPORT_TLS) ? 5061 : 5060; + } + } + + if (!(transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL)) || + !(transport = ao2_callback(transports, 0, find_transport_in_use, &details)) || !transport->localnet || + ast_sockaddr_isnull(&transport->external_address)) { + return PJ_SUCCESS; + } + + ast_sockaddr_parse(&addr, tdata->tp_info.dst_name, PARSE_PORT_FORBID); + ast_sockaddr_set_port(&addr, tdata->tp_info.dst_port); + + /* See if where we are sending this request is local or not, and if not that we can get a Contact URI to modify */ + if (ast_apply_ha(transport->localnet, &addr) != AST_SENSE_ALLOW) { + return PJ_SUCCESS; + } + + /* Update the contact header with the external address */ + if (uri || (uri = nat_get_contact_sip_uri(tdata))) { + pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport->external_address)); + if (transport->external_signaling_port) { + uri->port = transport->external_signaling_port; + } + } + + /* Update the via header if relevant */ + if ((tdata->msg->type == PJSIP_REQUEST_MSG) && (via || (via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL)))) { + pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport->external_address)); + if (transport->external_signaling_port) { + via->sent_by.port = transport->external_signaling_port; + } + } + + /* Invoke any additional hooks that may be registered */ + if ((hooks = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "nat_hook", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL))) { + struct nat_hook_details hook_details = { + .tdata = tdata, + .transport = transport, + }; + ao2_callback(hooks, 0, nat_invoke_hook, &hook_details); + } + + return PJ_SUCCESS; +} + +static pjsip_module nat_module = { + .name = { "NAT", 3 }, + .id = -1, + .priority = PJSIP_MOD_PRIORITY_TSX_LAYER - 2, + .on_rx_request = nat_on_rx_request, + .on_tx_request = nat_on_tx_message, + .on_tx_response = nat_on_tx_message, +}; + +static int load_module(void) +{ + ast_sip_register_service(&nat_module); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_service(&nat_module); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP NAT Support", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_outbound_authenticator_digest.c b/res/res_sip_outbound_authenticator_digest.c new file mode 100644 index 000000000..180c05e27 --- /dev/null +++ b/res/res_sip_outbound_authenticator_digest.c @@ -0,0 +1,110 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#include "asterisk.h" + +#include <pjsip.h> + +#include "asterisk/res_sip.h" +#include "asterisk/logger.h" +#include "asterisk/module.h" +#include "asterisk/strings.h" + +static int set_outbound_authentication_credentials(pjsip_auth_clt_sess *auth_sess, const char **auth_strs, size_t num_auths) +{ + struct ast_sip_auth **auths = ast_alloca(num_auths * sizeof(*auths)); + pjsip_cred_info *auth_creds = ast_alloca(num_auths * sizeof(*auth_creds)); + int res = 0; + int i; + + if (ast_sip_retrieve_auths(auth_strs, num_auths, auths)) { + res = -1; + goto cleanup; + } + + for (i = 0; i < num_auths; ++i) { + pj_cstr(&auth_creds[i].realm, auths[i]->realm); + pj_cstr(&auth_creds[i].username, auths[i]->auth_user); + pj_cstr(&auth_creds[i].scheme, "digest"); + switch (auths[i]->type) { + case AST_SIP_AUTH_TYPE_USER_PASS: + pj_cstr(&auth_creds[i].data, auths[i]->auth_pass); + auth_creds[i].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; + break; + case AST_SIP_AUTH_TYPE_MD5: + pj_cstr(&auth_creds[i].data, auths[i]->md5_creds); + auth_creds[i].data_type = PJSIP_CRED_DATA_DIGEST; + break; + } + } + + pjsip_auth_clt_set_credentials(auth_sess, num_auths, auth_creds); + +cleanup: + ast_sip_cleanup_auths(auths, num_auths); + return res; +} + +static int digest_create_request_with_auth(const char **auths, size_t num_auths, pjsip_rx_data *challenge, + pjsip_transaction *tsx, pjsip_tx_data **new_request) +{ + pjsip_auth_clt_sess auth_sess; + + if (pjsip_auth_clt_init(&auth_sess, ast_sip_get_pjsip_endpoint(), + tsx->pool, 0) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Failed to initialize client authentication session\n"); + return -1; + } + + if (set_outbound_authentication_credentials(&auth_sess, auths, num_auths)) { + ast_log(LOG_WARNING, "Failed to set authentication credentials\n"); + return -1; + } + + if (pjsip_auth_clt_reinit_req(&auth_sess, challenge, + tsx->last_tx, new_request) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Failed to create new request with authentication credentials\n"); + return -1; + } + + return 0; +} + +static struct ast_sip_outbound_authenticator digest_authenticator = { + .create_request_with_auth = digest_create_request_with_auth, +}; + +static int load_module(void) +{ + if (ast_sip_register_outbound_authenticator(&digest_authenticator)) { + return AST_MODULE_LOAD_DECLINE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_outbound_authenticator(&digest_authenticator); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP authentication resource", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DEPEND, +); diff --git a/res/res_sip_outbound_registration.c b/res/res_sip_outbound_registration.c new file mode 100644 index 000000000..8f1108df5 --- /dev/null +++ b/res/res_sip_outbound_registration.c @@ -0,0 +1,708 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" +#include "asterisk/taskprocessor.h" + +/*! \brief Amount of buffer time (in seconds) before expiration that we re-register at */ +#define REREGISTER_BUFFER_TIME 10 + +/*! \brief Various states that an outbound registration may be in */ +enum sip_outbound_registration_status { + /*! \brief Currently unregistered */ + SIP_REGISTRATION_UNREGISTERED = 0, + /*! \brief Registered, yay! */ + SIP_REGISTRATION_REGISTERED, + /*! \brief Registration was rejected, but response was temporal */ + SIP_REGISTRATION_REJECTED_TEMPORARY, + /*! \brief Registration was rejected, permanently */ + SIP_REGISTRATION_REJECTED_PERMANENT, + /*! \brief Registration has been stopped */ + SIP_REGISTRATION_STOPPED, +}; + +/*! \brief Outbound registration client state information (persists for lifetime of regc) */ +struct sip_outbound_registration_client_state { + /*! \brief Current status of this registration */ + enum sip_outbound_registration_status status; + /*! \brief Outbound registration client */ + pjsip_regc *client; + /*! \brief Timer entry for retrying on temporal responses */ + pj_timer_entry timer; + /*! \brief Current number of retries */ + unsigned int retries; + /*! \brief Maximum number of retries permitted */ + unsigned int max_retries; + /*! \brief Interval at which retries should occur for temporal responses */ + unsigned int retry_interval; + /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ + unsigned int auth_rejection_permanent; + /*! \brief Serializer for stuff and things */ + struct ast_taskprocessor *serializer; + /*! \brief Configured authentication credentials */ + const char **sip_outbound_auths; + /*! \brief Number of configured auths */ + size_t num_outbound_auths; + /*! \brief Registration should be destroyed after completion of transaction */ + unsigned int destroy:1; +}; + +/*! \brief Outbound registration state information (persists for lifetime that registration should exist) */ +struct sip_outbound_registration_state { + /*! \brief Client state information */ + struct sip_outbound_registration_client_state *client_state; +}; + +/*! \brief Outbound registration information */ +struct sip_outbound_registration { + /*! \brief Sorcery object details */ + SORCERY_OBJECT(details); + /*! \brief Stringfields */ + AST_DECLARE_STRING_FIELDS( + /*! \brief URI for the registrar */ + AST_STRING_FIELD(server_uri); + /*! \brief URI for the AOR */ + AST_STRING_FIELD(client_uri); + /*! \brief Optional user for contact header */ + AST_STRING_FIELD(contact_user); + /*! \brief Explicit transport to use for registration */ + AST_STRING_FIELD(transport); + /*! \brief Outbound proxy to use */ + AST_STRING_FIELD(outbound_proxy); + ); + /*! \brief Requested expiration time */ + unsigned int expiration; + /*! \brief Interval at which retries should occur for temporal responses */ + unsigned int retry_interval; + /*! \brief Treat authentication challenges that we cannot handle as permanent failures */ + unsigned int auth_rejection_permanent; + /*! \brief Maximum number of retries permitted */ + unsigned int max_retries; + /*! \brief Outbound registration state */ + struct sip_outbound_registration_state *state; + /*! \brief Configured authentication credentials */ + const char **sip_outbound_auths; + /*! \brief Number of configured auths */ + size_t num_outbound_auths; +}; + +static void destroy_auths(const char **auths, size_t num_auths) +{ + int i; + for (i = 0; i < num_auths; ++i) { + ast_free((char *) auths[i]); + } + ast_free(auths); +} + +/*! \brief Helper function which cancels the timer on a client */ +static void cancel_registration(struct sip_outbound_registration_client_state *client_state) +{ + if (pj_timer_heap_cancel(pjsip_endpt_get_timer_heap(ast_sip_get_pjsip_endpoint()), &client_state->timer)) { + /* The timer was successfully cancelled, drop the refcount of client_state */ + ao2_ref(client_state, -1); + } +} + +/*! \brief Callback function for registering */ +static int handle_client_registration(void *data) +{ + RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup); + pjsip_tx_data *tdata; + + cancel_registration(client_state); + + if ((client_state->status == SIP_REGISTRATION_STOPPED) || + (pjsip_regc_register(client_state->client, PJ_FALSE, &tdata) != PJ_SUCCESS)) { + return 0; + } + + /* Due to the registration the callback may now get called, so bump the ref count */ + ao2_ref(client_state, +1); + if (pjsip_regc_send(client_state->client, tdata) != PJ_SUCCESS) { + ao2_ref(client_state, -1); + pjsip_tx_data_dec_ref(tdata); + } + + return 0; +} + +/*! \brief Timer callback function, used just for registrations */ +static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry) +{ + RAII_VAR(struct sip_outbound_registration_client_state *, client_state, entry->user_data, ao2_cleanup); + + ao2_ref(client_state, +1); + if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) { + ast_log(LOG_WARNING, "Failed to pass outbound registration to threadpool\n"); + ao2_ref(client_state, -1); + } + + entry->id = 0; +} + +/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */ +static void schedule_registration(struct sip_outbound_registration_client_state *client_state, unsigned int seconds) +{ + pj_time_val delay = { .sec = seconds, }; + + cancel_registration(client_state); + + ao2_ref(client_state, +1); + if (pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &client_state->timer, &delay) != PJ_SUCCESS) { + ast_log(LOG_WARNING, "Failed to pass timed registration to scheduler\n"); + ao2_ref(client_state, -1); + } +} + +/*! \brief Callback function for unregistering (potentially) and destroying state */ +static int handle_client_state_destruction(void *data) +{ + RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup); + pjsip_regc_info info; + + cancel_registration(client_state); + + pjsip_regc_get_info(client_state->client, &info); + + if (info.is_busy == PJ_TRUE) { + /* If a client transaction is in progress we defer until it is complete */ + client_state->destroy = 1; + return 0; + } + + if (client_state->status != SIP_REGISTRATION_UNREGISTERED && client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) { + pjsip_tx_data *tdata; + + if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) { + pjsip_regc_send(client_state->client, tdata); + } + } + + pjsip_regc_destroy(client_state->client); + + client_state->status = SIP_REGISTRATION_STOPPED; + destroy_auths(client_state->sip_outbound_auths, client_state->num_outbound_auths); + + return 0; +} + +/*! \brief Structure for registration response */ +struct registration_response { + /*! \brief Response code for the registration attempt */ + int code; + /*! \brief Expiration time for registration */ + int expiration; + /*! \brief Retry-After value */ + int retry_after; + /*! \brief Outbound registration client state */ + struct sip_outbound_registration_client_state *client_state; + /*! \brief The response message */ + pjsip_rx_data *rdata; + /*! \brief The response transaction */ + pjsip_transaction *tsx; +}; + +/*! \brief Registration response structure destructor */ +static void registration_response_destroy(void *obj) +{ + struct registration_response *response = obj; + + pjsip_rx_data_free_cloned(response->rdata); + ao2_cleanup(response->client_state); +} + +/* \brief Helper funtion which determines if a response code is temporal or not */ +static int sip_outbound_registration_is_temporal(unsigned int code, + struct sip_outbound_registration_client_state *client_state) +{ + /* Shamelessly taken from pjsua */ + if (code == PJSIP_SC_REQUEST_TIMEOUT || + code == PJSIP_SC_INTERNAL_SERVER_ERROR || + code == PJSIP_SC_BAD_GATEWAY || + code == PJSIP_SC_SERVICE_UNAVAILABLE || + code == PJSIP_SC_SERVER_TIMEOUT || + ((code == PJSIP_SC_UNAUTHORIZED || + code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED) && + !client_state->auth_rejection_permanent) || + PJSIP_IS_STATUS_IN_CLASS(code, 600)) { + return 1; + } else { + return 0; + } +} + +/*! \brief Callback function for handling a response to a registration attempt */ +static int handle_registration_response(void *data) +{ + RAII_VAR(struct registration_response *, response, data, ao2_cleanup); + pjsip_regc_info info; + char server_uri[PJSIP_MAX_URL_SIZE], client_uri[PJSIP_MAX_URL_SIZE]; + + if (response->client_state->status == SIP_REGISTRATION_STOPPED) { + return 0; + } + + pjsip_regc_get_info(response->client_state->client, &info); + ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri)); + ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri)); + + if (response->code == 401 || response->code == 407) { + pjsip_tx_data *tdata; + if (!ast_sip_create_request_with_auth(response->client_state->sip_outbound_auths, response->client_state->num_outbound_auths, + response->rdata, response->tsx, &tdata)) { + pjsip_regc_send(response->client_state->client, tdata); + return 0; + } + /* Otherwise, fall through so the failure is processed appropriately */ + } + + if (PJSIP_IS_STATUS_IN_CLASS(response->code, 200)) { + /* If the registration went fine simply reschedule registration for the future */ + response->client_state->status = SIP_REGISTRATION_REGISTERED; + response->client_state->retries = 0; + schedule_registration(response->client_state, response->expiration - REREGISTER_BUFFER_TIME); + } else if (response->retry_after) { + /* If we have been instructed to retry after a period of time, schedule it as such */ + response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY; + schedule_registration(response->client_state, response->retry_after); + ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', instructed to retry in '%d'\n", + response->code, server_uri, client_uri, response->retry_after); + } else if (response->client_state->retry_interval && sip_outbound_registration_is_temporal(response->code, response->client_state)) { + if (response->client_state->retries == response->client_state->max_retries) { + /* If we received enough temporal responses to exceed our maximum give up permanently */ + response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT; + ast_log(LOG_WARNING, "Maximum retries reached when attempting outbound registration to '%s' with client '%s', stopping registration attempt\n", + server_uri, client_uri); + } else { + /* On the other hand if we can still try some more do so */ + response->client_state->status = SIP_REGISTRATION_REJECTED_TEMPORARY; + response->client_state->retries++; + schedule_registration(response->client_state, response->client_state->retry_interval); + ast_log(LOG_WARNING, "Temporal response '%d' received from '%s' on registration attempt to '%s', retrying in '%d' seconds\n", + response->code, server_uri, client_uri, response->client_state->retry_interval); + } + } else { + /* Finally if there's no hope of registering give up */ + response->client_state->status = SIP_REGISTRATION_REJECTED_PERMANENT; + ast_log(LOG_WARNING, "Fatal response '%d' received from '%s' on registration attempt to '%s', stopping outbound registration\n", + response->code, server_uri, client_uri); + } + + /* If deferred destruction is in use see if we need to destroy now */ + if (response->client_state->destroy) { + handle_client_state_destruction(response->client_state); + } + + return 0; +} + +/*! \brief Callback function for outbound registration client */ +static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param) +{ + RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup); + struct registration_response *response = ao2_alloc(sizeof(*response), registration_response_destroy); + struct pjsip_retry_after_hdr *retry_after = pjsip_msg_find_hdr(param->rdata->msg_info.msg, PJSIP_H_RETRY_AFTER, NULL); + + response->code = param->code; + response->expiration = param->expiration; + response->retry_after = retry_after ? retry_after->ivalue : 0; + response->client_state = client_state; + response->tsx = pjsip_rdata_get_tsx(param->rdata); + pjsip_rx_data_clone(param->rdata, 0, &response->rdata); + ao2_ref(response->client_state, +1); + + if (ast_sip_push_task(client_state->serializer, handle_registration_response, response)) { + ast_log(LOG_WARNING, "Failed to pass incoming registration response to threadpool\n"); + ao2_cleanup(response); + } +} + +/*! \brief Destructor function for registration state */ +static void sip_outbound_registration_state_destroy(void *obj) +{ + struct sip_outbound_registration_state *state = obj; + + if (!state->client_state) { + return; + } + + if (state->client_state->serializer && ast_sip_push_task(state->client_state->serializer, handle_client_state_destruction, state->client_state)) { + ast_log(LOG_WARNING, "Failed to pass outbound registration client destruction to threadpool\n"); + ao2_ref(state->client_state, -1); + } +} + +/*! \brief Destructor function for client registration state */ +static void sip_outbound_registration_client_state_destroy(void *obj) +{ + struct sip_outbound_registration_client_state *client_state = obj; + + ast_taskprocessor_unreference(client_state->serializer); +} + +/*! \brief Allocator function for registration state */ +static struct sip_outbound_registration_state *sip_outbound_registration_state_alloc(void) +{ + struct sip_outbound_registration_state *state = ao2_alloc(sizeof(*state), sip_outbound_registration_state_destroy); + + if (!state || !(state->client_state = ao2_alloc(sizeof(*state->client_state), sip_outbound_registration_client_state_destroy))) { + ao2_cleanup(state); + return NULL; + } + + if ((pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state, sip_outbound_registration_response_cb, &state->client_state->client) != PJ_SUCCESS) || + !(state->client_state->serializer = ast_sip_create_serializer())) { + /* This is on purpose, normal operation will have it be deallocated within the serializer */ + pjsip_regc_destroy(state->client_state->client); + ao2_cleanup(state->client_state); + ao2_cleanup(state); + return NULL; + } + + state->client_state->status = SIP_REGISTRATION_UNREGISTERED; + state->client_state->timer.user_data = state->client_state; + state->client_state->timer.cb = sip_outbound_registration_timer_cb; + + return state; +} + +/*! \brief Destructor function for registration information */ +static void sip_outbound_registration_destroy(void *obj) +{ + struct sip_outbound_registration *registration = obj; + + ao2_cleanup(registration->state); + destroy_auths(registration->sip_outbound_auths, registration->num_outbound_auths); + + ast_string_field_free_memory(registration); +} + +/*! \brief Allocator function for registration information */ +static void *sip_outbound_registration_alloc(const char *name) +{ + struct sip_outbound_registration *registration = ao2_alloc(sizeof(*registration), sip_outbound_registration_destroy); + + if (!registration || ast_string_field_init(registration, 256)) { + ao2_cleanup(registration); + return NULL; + } + + return registration; +} + +/*! \brief Helper function which populates a pj_str_t with a contact header */ +static int sip_dialog_create_contact(pj_pool_t *pool, pj_str_t *contact, const char *user, const pj_str_t *target, pjsip_tpselector *selector) +{ + pj_str_t tmp, local_addr; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + pjsip_transport_type_e type = PJSIP_TRANSPORT_UNSPECIFIED; + int local_port; + + pj_strdup_with_null(pool, &tmp, target); + + if (!(uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, 0)) || + (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri))) { + return -1; + } + + sip_uri = pjsip_uri_get_uri(uri); + + if (PJSIP_URI_SCHEME_IS_SIPS(sip_uri)) { + type = PJSIP_TRANSPORT_TLS; + } else if (!sip_uri->transport_param.slen) { + type = PJSIP_TRANSPORT_UDP; + } else { + type = pjsip_transport_get_type_from_name(&sip_uri->transport_param); + } + + if (type == PJSIP_TRANSPORT_UNSPECIFIED) { + return -1; + } + + if (pj_strchr(&sip_uri->host, ':')) { + type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6); + } + + if (pjsip_tpmgr_find_local_addr(pjsip_endpt_get_tpmgr(ast_sip_get_pjsip_endpoint()), pool, type, selector, + &local_addr, &local_port) != PJ_SUCCESS) { + return -1; + } + + if (!pj_strchr(&sip_uri->host, ':') && pj_strchr(&local_addr, ':')) { + type = (pjsip_transport_type_e)(((int)type) + PJSIP_TRANSPORT_IPV6); + } + + contact->ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); + contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, + "<%s:%s@%s%.*s%s:%d%s%s>", + (pjsip_transport_get_flag_from_type(type) & PJSIP_TRANSPORT_SECURE) ? "sips" : "sip", + user, + (type & PJSIP_TRANSPORT_IPV6) ? "[" : "", + (int)local_addr.slen, + local_addr.ptr, + (type & PJSIP_TRANSPORT_IPV6) ? "]" : "", + local_port, + (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "", + (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : ""); + + return 0; +} + +/*! + * \internal + * \brief Check if a registration can be reused + * + * This checks if the existing outbound registration's configuration differs from a newly-applied + * outbound registration to see if the applied one. + * + * \param existing The pre-existing outbound registration + * \param applied The newly-created registration + */ +static int can_reuse_registration(struct sip_outbound_registration *existing, struct sip_outbound_registration *applied) +{ + int i; + + if (strcmp(existing->server_uri, applied->server_uri) || strcmp(existing->client_uri, applied->client_uri) || + strcmp(existing->transport, applied->transport) || strcmp(existing->contact_user, applied->contact_user) || + strcmp(existing->outbound_proxy, applied->outbound_proxy) || existing->num_outbound_auths != applied->num_outbound_auths || + existing->auth_rejection_permanent != applied->auth_rejection_permanent) { + return 0; + } + + for (i = 0; i < existing->num_outbound_auths; ++i) { + if (strcmp(existing->sip_outbound_auths[i], applied->sip_outbound_auths[i])) { + return 0; + } + } + + return 1; +} + +/*! \brief Apply function which finds or allocates a state structure */ +static int sip_outbound_registration_apply(const struct ast_sorcery *sorcery, void *obj) +{ + RAII_VAR(struct sip_outbound_registration *, existing, ast_sorcery_retrieve_by_id(sorcery, "registration", ast_sorcery_object_get_id(obj)), ao2_cleanup); + struct sip_outbound_registration *applied = obj; + pj_str_t server_uri, client_uri, contact_uri; + pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, }; + + if (!existing) { + /* If no existing registration exists we can just start fresh easily */ + applied->state = sip_outbound_registration_state_alloc(); + } else { + /* If there is an existing registration things are more complicated, we can immediately reuse this state if most stuff remains unchanged */ + if (can_reuse_registration(existing, applied)) { + applied->state = existing->state; + ao2_ref(applied->state, +1); + return 0; + } + applied->state = sip_outbound_registration_state_alloc(); + } + + if (!applied->state) { + return -1; + } + + if (!ast_strlen_zero(applied->transport)) { + RAII_VAR(struct ast_sip_transport *, transport, ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", applied->transport), ao2_cleanup); + + if (!transport || !transport->state) { + return -1; + } + + if (transport->type == AST_SIP_TRANSPORT_UDP) { + selector.type = PJSIP_TPSELECTOR_TRANSPORT; + selector.u.transport = transport->state->transport; + } else if (transport->type == AST_SIP_TRANSPORT_TCP || transport->type == AST_SIP_TRANSPORT_TLS) { + selector.type = PJSIP_TPSELECTOR_LISTENER; + selector.u.listener = transport->state->factory; + } else { + return -1; + } + } + + pjsip_regc_set_transport(applied->state->client_state->client, &selector); + + if (!ast_strlen_zero(applied->outbound_proxy)) { + pjsip_route_hdr route_set, *route; + static const pj_str_t ROUTE_HNAME = { "Route", 5 }; + pj_str_t tmp; + + pj_list_init(&route_set); + + pj_strdup2_with_null(pjsip_regc_get_pool(applied->state->client_state->client), &tmp, applied->outbound_proxy); + if (!(route = pjsip_parse_hdr(pjsip_regc_get_pool(applied->state->client_state->client), &ROUTE_HNAME, tmp.ptr, tmp.slen, NULL))) { + return -1; + } + pj_list_push_back(&route_set, route); + + pjsip_regc_set_route_set(applied->state->client_state->client, &route_set); + } + + pj_cstr(&server_uri, applied->server_uri); + + if (sip_dialog_create_contact(pjsip_regc_get_pool(applied->state->client_state->client), &contact_uri, S_OR(applied->contact_user, "s"), &server_uri, &selector)) { + return -1; + } + + pj_cstr(&client_uri, applied->client_uri); + + if (pjsip_regc_init(applied->state->client_state->client, &server_uri, &client_uri, &client_uri, 1, &contact_uri, applied->expiration) != PJ_SUCCESS) { + return -1; + } + + return 0; +} + +/*! \brief Helper function which performs a single registration */ +static int sip_outbound_registration_perform(void *obj, void *arg, int flags) +{ + struct sip_outbound_registration *registration = obj; + size_t i; + + /* Just in case the client state is being reused for this registration, free the auth information */ + destroy_auths(registration->state->client_state->sip_outbound_auths, + registration->state->client_state->num_outbound_auths); + registration->state->client_state->num_outbound_auths = 0; + + registration->state->client_state->sip_outbound_auths = ast_calloc(registration->num_outbound_auths, sizeof(char *)); + for (i = 0; i < registration->num_outbound_auths; ++i) { + registration->state->client_state->sip_outbound_auths[i] = ast_strdup(registration->sip_outbound_auths[i]); + } + registration->state->client_state->num_outbound_auths = registration->num_outbound_auths; + registration->state->client_state->retry_interval = registration->retry_interval; + registration->state->client_state->max_retries = registration->max_retries; + registration->state->client_state->retries = 0; + + pjsip_regc_update_expires(registration->state->client_state->client, registration->expiration); + + schedule_registration(registration->state->client_state, (ast_random() % 10) + 1); + + return 0; +} + +/*! \brief Helper function which performs all registrations */ +static void sip_outbound_registration_perform_all(void) +{ + RAII_VAR(struct ao2_container *, registrations, ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "registration", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup); + + if (!registrations) { + return; + } + + ao2_callback(registrations, OBJ_NODATA, sip_outbound_registration_perform, NULL); +} + +#define AUTH_INCREMENT 4 + +static const char **auth_alloc(const char *value, size_t *num_auths) +{ + char *auths = ast_strdupa(value); + char *val; + int num_alloced = 0; + const char **alloced_auths = NULL; + + while ((val = strsep(&auths, ","))) { + if (*num_auths >= num_alloced) { + size_t size; + num_alloced += AUTH_INCREMENT; + size = num_alloced * sizeof(char *); + alloced_auths = ast_realloc(alloced_auths, size); + if (!alloced_auths) { + goto failure; + } + } + alloced_auths[*num_auths] = ast_strdup(val); + if (!alloced_auths[*num_auths]) { + goto failure; + } + ++(*num_auths); + } + return alloced_auths; + +failure: + destroy_auths(alloced_auths, *num_auths); + return NULL; +} + +static int outbound_auth_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + struct sip_outbound_registration *registration = obj; + + registration->sip_outbound_auths = auth_alloc(var->value, ®istration->num_outbound_auths); + if (!registration->sip_outbound_auths) { + return -1; + } + return 0; +} + +static int load_module(void) +{ + ast_sorcery_apply_default(ast_sip_get_sorcery(), "registration", "config", "res_sip.conf,criteria=type=registration"); + + if (ast_sorcery_object_register(ast_sip_get_sorcery(), "registration", sip_outbound_registration_alloc, NULL, sip_outbound_registration_apply)) { + return AST_MODULE_LOAD_DECLINE; + } + + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "type", "", OPT_NOOP_T, 0, 0); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "server_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, server_uri)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "client_uri", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, client_uri)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "contact_user", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, contact_user)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, transport)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "outbound_proxy", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct sip_outbound_registration, outbound_proxy)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "expiration", "3600", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, expiration)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "retry_interval", "60", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, retry_interval)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "max_retries", "10", OPT_UINT_T, 0, FLDSET(struct sip_outbound_registration, max_retries)); + ast_sorcery_object_field_register(ast_sip_get_sorcery(), "registration", "auth_rejection_permanent", "yes", OPT_BOOL_T, 1, FLDSET(struct sip_outbound_registration, auth_rejection_permanent)); + ast_sorcery_object_field_register_custom(ast_sip_get_sorcery(), "registration", "outbound_auth", "", outbound_auth_handler, NULL, 0, 0); + ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); + sip_outbound_registration_perform_all(); + + return AST_MODULE_LOAD_SUCCESS; +} + +static int reload_module(void) +{ + ast_sorcery_reload_object(ast_sip_get_sorcery(), "registration"); + sip_outbound_registration_perform_all(); + return 0; +} + +static int unload_module(void) +{ + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Outbound Registration Support", + .load = load_module, + .reload = reload_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_pubsub.c b/res/res_sip_pubsub.c new file mode 100644 index 000000000..2983d563e --- /dev/null +++ b/res/res_sip_pubsub.c @@ -0,0 +1,662 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Mark Michelson <mmichelson@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ +/*! + * \brief Opaque structure representing an RFC 3265 SIP subscription + */ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_simple.h> +#include <pjlib.h> + +#include "asterisk/res_sip_pubsub.h" +#include "asterisk/module.h" +#include "asterisk/linkedlists.h" +#include "asterisk/astobj2.h" +#include "asterisk/datastore.h" +#include "asterisk/uuid.h" +#include "asterisk/taskprocessor.h" +#include "asterisk/res_sip.h" + +static pj_bool_t sub_on_rx_request(pjsip_rx_data *rdata); + +static struct pjsip_module sub_module = { + .name = { "PubSub Module", 13 }, + .priority = PJSIP_MOD_PRIORITY_APPLICATION, + .on_rx_request = sub_on_rx_request, +}; + +/*! + * \brief Structure representing a SIP subscription + */ +struct ast_sip_subscription { + /*! Subscription datastores set up by handlers */ + struct ao2_container *datastores; + /*! The endpoint with which the subscription is communicating */ + struct ast_sip_endpoint *endpoint; + /*! Serializer on which to place operations for this subscription */ + struct ast_taskprocessor *serializer; + /*! The handler for this subscription */ + const struct ast_sip_subscription_handler *handler; + /*! The role for this subscription */ + enum ast_sip_subscription_role role; + /*! The underlying PJSIP event subscription structure */ + pjsip_evsub *evsub; + /*! The underlying PJSIP dialog */ + pjsip_dialog *dlg; +}; + +#define DATASTORE_BUCKETS 53 + +#define DEFAULT_EXPIRES 3600 + +static int datastore_hash(const void *obj, int flags) +{ + const struct ast_datastore *datastore = obj; + const char *uid = flags & OBJ_KEY ? obj : datastore->uid; + + ast_assert(uid != NULL); + + return ast_str_hash(uid); +} + +static int datastore_cmp(void *obj, void *arg, int flags) +{ + const struct ast_datastore *datastore1 = obj; + const struct ast_datastore *datastore2 = arg; + const char *uid2 = flags & OBJ_KEY ? arg : datastore2->uid; + + ast_assert(datastore1->uid != NULL); + ast_assert(uid2 != NULL); + + return strcmp(datastore1->uid, uid2) ? 0 : CMP_MATCH | CMP_STOP; +} + +static void subscription_destructor(void *obj) +{ + struct ast_sip_subscription *sub = obj; + + ast_debug(3, "Destroying SIP subscription\n"); + + ao2_cleanup(sub->datastores); + ao2_cleanup(sub->endpoint); + + if (sub->dlg) { + /* This is why we keep the dialog on the subscription. When the subscription + * is destroyed, there is no guarantee that the underlying dialog is ready + * to be destroyed. Furthermore, there's no guarantee in the opposite direction + * either. The dialog could be destroyed before our subscription is. We fix + * this problem by keeping a reference to the dialog until it is time to + * destroy the subscription. We need to have the dialog available when the + * subscription is destroyed so that we can guarantee that our attempt to + * remove the serializer will be successful. + */ + ast_sip_dialog_set_serializer(sub->dlg, NULL); + pjsip_dlg_dec_session(sub->dlg, &sub_module); + } + ast_taskprocessor_unreference(sub->serializer); +} + +static void pubsub_on_evsub_state(pjsip_evsub *sub, pjsip_event *event); +static void pubsub_on_tsx_state(pjsip_evsub *sub, pjsip_transaction *tsx, pjsip_event *event); +static void pubsub_on_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, + int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); +static void pubsub_on_rx_notify(pjsip_evsub *sub, pjsip_rx_data *rdata, int *p_st_code, + pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body); +static void pubsub_on_client_refresh(pjsip_evsub *sub); +static void pubsub_on_server_timeout(pjsip_evsub *sub); + + +static pjsip_evsub_user pubsub_cb = { + .on_evsub_state = pubsub_on_evsub_state, + .on_tsx_state = pubsub_on_tsx_state, + .on_rx_refresh = pubsub_on_rx_refresh, + .on_rx_notify = pubsub_on_rx_notify, + .on_client_refresh = pubsub_on_client_refresh, + .on_server_timeout = pubsub_on_server_timeout, +}; + +static pjsip_evsub *allocate_evsub(const char *event, enum ast_sip_subscription_role role, + struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata, pjsip_dialog *dlg) +{ + pjsip_evsub *evsub; + /* PJSIP is kind enough to have some built-in support for certain + * events. We need to use the correct initialization function for the + * built-in events + */ + if (role == AST_SIP_NOTIFIER) { + if (!strcmp(event, "message-summary")) { + pjsip_mwi_create_uas(dlg, &pubsub_cb, rdata, &evsub); + } else if (!strcmp(event, "presence")) { + pjsip_pres_create_uas(dlg, &pubsub_cb, rdata, &evsub); + } else { + pjsip_evsub_create_uas(dlg, &pubsub_cb, rdata, 0, &evsub); + } + } else { + if (!strcmp(event, "message-summary")) { + pjsip_mwi_create_uac(dlg, &pubsub_cb, 0, &evsub); + } else if (!strcmp(event, "presence")) { + pjsip_pres_create_uac(dlg, &pubsub_cb, 0, &evsub); + } else { + pj_str_t pj_event; + pj_cstr(&pj_event, event); + pjsip_evsub_create_uac(dlg, &pubsub_cb, &pj_event, 0, &evsub); + } + } + return evsub; +} + +struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler, + enum ast_sip_subscription_role role, struct ast_sip_endpoint *endpoint, pjsip_rx_data *rdata) +{ + struct ast_sip_subscription *sub = ao2_alloc(sizeof(*sub), subscription_destructor); + pjsip_dialog *dlg; + + if (!sub) { + return NULL; + } + sub->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp); + if (!sub->datastores) { + ao2_ref(sub, -1); + return NULL; + } + sub->serializer = ast_sip_create_serializer(); + if (!sub->serializer) { + ao2_ref(sub, -1); + return NULL; + } + sub->role = role; + if (role == AST_SIP_NOTIFIER) { + pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dlg); + } else { + RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + + contact = ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors); + if (!contact || ast_strlen_zero(contact->uri)) { + ast_log(LOG_WARNING, "No contacts configured for endpoint %s. Unable to create SIP subsription\n", + ast_sorcery_object_get_id(endpoint)); + ao2_ref(sub, -1); + return NULL; + } + dlg = ast_sip_create_dialog(endpoint, contact->uri, NULL); + } + if (!dlg) { + ast_log(LOG_WARNING, "Unable to create dialog for SIP subscription\n"); + ao2_ref(sub, -1); + return NULL; + } + sub->evsub = allocate_evsub(handler->event_name, role, endpoint, rdata, dlg); + /* We keep a reference to the dialog until our subscription is destroyed. See + * the subscription_destructor for more details + */ + pjsip_dlg_inc_session(dlg, &sub_module); + sub->dlg = dlg; + ast_sip_dialog_set_serializer(dlg, sub->serializer); + pjsip_evsub_set_mod_data(sub->evsub, sub_module.id, sub); + ao2_ref(endpoint, +1); + sub->endpoint = endpoint; + sub->handler = handler; + return sub; +} + +struct ast_sip_endpoint *ast_sip_subscription_get_endpoint(struct ast_sip_subscription *sub) +{ + ast_assert(sub->endpoint != NULL); + ao2_ref(sub->endpoint, +1); + return sub->endpoint; +} + +struct ast_taskprocessor *ast_sip_subscription_get_serializer(struct ast_sip_subscription *sub) +{ + ast_assert(sub->serializer != NULL); + return sub->serializer; +} + +pjsip_evsub *ast_sip_subscription_get_evsub(struct ast_sip_subscription *sub) +{ + return sub->evsub; +} + +int ast_sip_subscription_send_request(struct ast_sip_subscription *sub, pjsip_tx_data *tdata) +{ + return pjsip_evsub_send_request(ast_sip_subscription_get_evsub(sub), + tdata) == PJ_SUCCESS ? 0 : -1; +} + +static void subscription_datastore_destroy(void *obj) +{ + struct ast_datastore *datastore = obj; + + /* Using the destroy function (if present) destroy the data */ + if (datastore->info->destroy != NULL && datastore->data != NULL) { + datastore->info->destroy(datastore->data); + datastore->data = NULL; + } + + ast_free((void *) datastore->uid); + datastore->uid = NULL; +} + +struct ast_datastore *ast_sip_subscription_alloc_datastore(const struct ast_datastore_info *info, const char *uid) +{ + RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup); + const char *uid_ptr = uid; + + if (!info) { + return NULL; + } + + datastore = ao2_alloc(sizeof(*datastore), subscription_datastore_destroy); + if (!datastore) { + return NULL; + } + + datastore->info = info; + if (ast_strlen_zero(uid)) { + /* They didn't provide an ID so we'll provide one ourself */ + struct ast_uuid *uuid = ast_uuid_generate(); + char uuid_buf[AST_UUID_STR_LEN]; + if (!uuid) { + return NULL; + } + uid_ptr = ast_uuid_to_str(uuid, uuid_buf, sizeof(uuid_buf)); + ast_free(uuid); + } + + datastore->uid = ast_strdup(uid_ptr); + if (!datastore->uid) { + return NULL; + } + + ao2_ref(datastore, +1); + return datastore; +} + +int ast_sip_subscription_add_datastore(struct ast_sip_subscription *subscription, struct ast_datastore *datastore) +{ + ast_assert(datastore != NULL); + ast_assert(datastore->info != NULL); + ast_assert(ast_strlen_zero(datastore->uid) == 0); + + if (!ao2_link(subscription->datastores, datastore)) { + return -1; + } + return 0; +} + +struct ast_datastore *ast_sip_subscription_get_datastore(struct ast_sip_subscription *subscription, const char *name) +{ + return ao2_find(subscription->datastores, name, OBJ_KEY); +} + +void ast_sip_subscription_remove_datastore(struct ast_sip_subscription *subscription, const char *name) +{ + ao2_callback(subscription->datastores, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, NULL, (void *) name); +} + +AST_RWLIST_HEAD_STATIC(subscription_handlers, ast_sip_subscription_handler); + +static void add_handler(struct ast_sip_subscription_handler *handler) +{ + SCOPED_LOCK(lock, &subscription_handlers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_INSERT_TAIL(&subscription_handlers, handler, next); + ast_module_ref(ast_module_info->self); +} + +static int handler_exists_for_event_name(const char *event_name) +{ + struct ast_sip_subscription_handler *iter; + SCOPED_LOCK(lock, &subscription_handlers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + + AST_RWLIST_TRAVERSE(&subscription_handlers, iter, next) { + if (!strcmp(iter->event_name, event_name)) { + return 1; + } + } + return 0; +} + +int ast_sip_register_subscription_handler(struct ast_sip_subscription_handler *handler) +{ + pj_str_t event; + pj_str_t accept[AST_SIP_MAX_ACCEPT]; + int i; + + if (ast_strlen_zero(handler->event_name)) { + ast_log(LOG_ERROR, "No event package specifief for subscription handler. Cannot register\n"); + return -1; + } + + if (ast_strlen_zero(handler->accept[0])) { + ast_log(LOG_ERROR, "Subscription handler must supply at least one 'Accept' format\n"); + return -1; + } + + if (handler_exists_for_event_name(handler->event_name)) { + ast_log(LOG_ERROR, "A subscription handler for event %s already exists. Not registering " + "new subscription handler\n", handler->event_name); + return -1; + } + + pj_cstr(&event, handler->event_name); + for (i = 0; i < AST_SIP_MAX_ACCEPT && !ast_strlen_zero(handler->accept[i]); ++i) { + pj_cstr(&accept[i], handler->accept[i]); + } + + if (!strcmp(handler->event_name, "message-summary")) { + pjsip_mwi_init_module(ast_sip_get_pjsip_endpoint(), pjsip_evsub_instance()); + } else if (!strcmp(handler->event_name, "presence")) { + pjsip_pres_init_module(ast_sip_get_pjsip_endpoint(), pjsip_evsub_instance()); + } else { + pjsip_evsub_register_pkg(&sub_module, &event, DEFAULT_EXPIRES, i, accept); + } + + add_handler(handler); + return 0; +} + +void ast_sip_unregister_subscription_handler(struct ast_sip_subscription_handler *handler) +{ + struct ast_sip_subscription_handler *iter; + SCOPED_LOCK(lock, &subscription_handlers, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&subscription_handlers, iter, next) { + if (handler == iter) { + AST_RWLIST_REMOVE_CURRENT(next); + ast_module_unref(ast_module_info->self); + break; + } + } + AST_RWLIST_TRAVERSE_SAFE_END; +} + +static struct ast_sip_subscription_handler *find_handler(const char *event, char accept[AST_SIP_MAX_ACCEPT][64], size_t num_accept) +{ + struct ast_sip_subscription_handler *iter; + int match = 0; + SCOPED_LOCK(lock, &subscription_handlers, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE(&subscription_handlers, iter, next) { + int i; + int j; + if (strcmp(event, iter->event_name)) { + ast_debug(3, "Event %s does not match %s\n", event, iter->event_name); + continue; + } + ast_debug(3, "Event name match: %s = %s\n", event, iter->event_name); + for (i = 0; i < num_accept; ++i) { + for (j = 0; j < num_accept; ++j) { + if (ast_strlen_zero(iter->accept[i])) { + ast_debug(3, "Breaking because subscription handler has run out of 'accept' types\n"); + break; + } + if (!strcmp(accept[j], iter->accept[i])) { + ast_debug(3, "Accept headers match: %s = %s\n", accept[j], iter->accept[i]); + match = 1; + break; + } + ast_debug(3, "Accept %s does not match %s\n", accept[j], iter->accept[i]); + } + if (match) { + break; + } + } + if (match) { + break; + } + } + + return iter; +} + +static pj_bool_t sub_on_rx_request(pjsip_rx_data *rdata) +{ + static const pj_str_t event_name = { "Event", 5 }; + char event[32]; + char accept[AST_SIP_MAX_ACCEPT][64]; + pjsip_accept_hdr *accept_header; + pjsip_event_hdr *event_header; + struct ast_sip_subscription_handler *handler; + RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); + struct ast_sip_subscription *sub; + int i; + + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) { + return PJ_FALSE; + } + + endpoint = ast_pjsip_rdata_get_endpoint(rdata); + ast_assert(endpoint != NULL); + + event_header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &event_name, rdata->msg_info.msg->hdr.next); + if (!event_header) { + ast_log(LOG_WARNING, "Incoming SUBSCRIBE request with no Event header\n"); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL); + return PJ_TRUE; + } + + accept_header = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, rdata->msg_info.msg->hdr.next); + if (!accept_header) { + ast_log(LOG_WARNING, "Incoming SUBSCRIBE request with no Accept header\n"); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL); + return PJ_TRUE; + } + + ast_copy_pj_str(event, &event_header->event_type, sizeof(event)); + for (i = 0; i < accept_header->count; ++i) { + ast_copy_pj_str(accept[i], &accept_header->values[i], sizeof(accept[i])); + } + + handler = find_handler(event, accept, accept_header->count); + if (!handler) { + ast_log(LOG_WARNING, "No registered handler for event %s\n", event); + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 489, NULL, NULL, NULL); + return PJ_TRUE; + } + sub = handler->new_subscribe(endpoint, rdata); + if (!sub) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); + } + return PJ_TRUE; +} + +static void pubsub_on_evsub_state(pjsip_evsub *evsub, pjsip_event *event) +{ + struct ast_sip_subscription *sub; + if (pjsip_evsub_get_state(evsub) != PJSIP_EVSUB_STATE_TERMINATED) { + return; + } + + sub = pjsip_evsub_get_mod_data(evsub, sub_module.id); + if (!sub) { + return; + } + + if (event->type == PJSIP_EVENT_RX_MSG) { + sub->handler->subscription_terminated(sub, event->body.rx_msg.rdata); + } + + if (event->type == PJSIP_EVENT_TSX_STATE && + event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { + sub->handler->subscription_terminated(sub, event->body.tsx_state.src.rdata); + } + + if (sub->handler->subscription_shutdown) { + sub->handler->subscription_shutdown(sub); + } + pjsip_evsub_set_mod_data(evsub, sub_module.id, NULL); +} + +static void pubsub_on_tsx_state(pjsip_evsub *evsub, pjsip_transaction *tsx, pjsip_event *event) +{ + struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, sub_module.id); + + if (!sub) { + return; + } + + if (tsx->role == PJSIP_ROLE_UAC && event->body.tsx_state.type == PJSIP_EVENT_RX_MSG) { + sub->handler->notify_response(sub, event->body.tsx_state.src.rdata); + } +} + +static void set_parameters_from_response_data(pj_pool_t *pool, int *p_st_code, + pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body, + struct ast_sip_subscription_response_data *response_data) +{ + ast_assert(response_data->status_code >= 200 && response_data->status_code <= 699); + *p_st_code = response_data->status_code; + + if (!ast_strlen_zero(response_data->status_text)) { + pj_strdup2(pool, *p_st_text, response_data->status_text); + } + + if (response_data->headers) { + struct ast_variable *iter; + for (iter = response_data->headers; iter; iter = iter->next) { + pj_str_t header_name; + pj_str_t header_value; + pjsip_generic_string_hdr *hdr; + + pj_cstr(&header_name, iter->name); + pj_cstr(&header_value, iter->value); + hdr = pjsip_generic_string_hdr_create(pool, &header_name, &header_value); + pj_list_insert_before(res_hdr, hdr); + } + } + + if (response_data->body) { + pj_str_t type; + pj_str_t subtype; + pj_str_t body_text; + + pj_cstr(&type, response_data->body->type); + pj_cstr(&subtype, response_data->body->subtype); + pj_cstr(&body_text, response_data->body->body_text); + + *p_body = pjsip_msg_body_create(pool, &type, &subtype, &body_text); + } +} + +static int response_data_changed(struct ast_sip_subscription_response_data *response_data) +{ + if (response_data->status_code != 200 || + !ast_strlen_zero(response_data->status_text) || + response_data->headers || + response_data->body) { + return 1; + } + return 0; +} + +static void pubsub_on_rx_refresh(pjsip_evsub *evsub, pjsip_rx_data *rdata, + int *p_st_code, pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) +{ + struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, sub_module.id); + struct ast_sip_subscription_response_data response_data = { + .status_code = 200, + }; + + if (!sub) { + return; + } + + sub->handler->resubscribe(sub, rdata, &response_data); + + if (!response_data_changed(&response_data)) { + return; + } + + set_parameters_from_response_data(rdata->tp_info.pool, p_st_code, p_st_text, + res_hdr, p_body, &response_data); +} + +static void pubsub_on_rx_notify(pjsip_evsub *evsub, pjsip_rx_data *rdata, int *p_st_code, + pj_str_t **p_st_text, pjsip_hdr *res_hdr, pjsip_msg_body **p_body) +{ + struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, sub_module.id); + struct ast_sip_subscription_response_data response_data = { + .status_code = 200, + }; + + if (!sub|| !sub->handler->notify_request) { + return; + } + + sub->handler->notify_request(sub, rdata, &response_data); + + if (!response_data_changed(&response_data)) { + return; + } + + set_parameters_from_response_data(rdata->tp_info.pool, p_st_code, p_st_text, + res_hdr, p_body, &response_data); +} + +static int serialized_pubsub_on_client_refresh(void *userdata) +{ + struct ast_sip_subscription *sub = userdata; + + sub->handler->refresh_subscription(sub); + ao2_cleanup(sub); + return 0; +} + +static void pubsub_on_client_refresh(pjsip_evsub *evsub) +{ + struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, sub_module.id); + + ao2_ref(sub, +1); + ast_sip_push_task(sub->serializer, serialized_pubsub_on_client_refresh, sub); +} + +static int serialized_pubsub_on_server_timeout(void *userdata) +{ + struct ast_sip_subscription *sub = userdata; + + sub->handler->subscription_timeout(sub); + ao2_cleanup(sub); + return 0; +} + +static void pubsub_on_server_timeout(pjsip_evsub *evsub) +{ + struct ast_sip_subscription *sub = pjsip_evsub_get_mod_data(evsub, sub_module.id); + + ao2_ref(sub, +1); + ast_sip_push_task(sub->serializer, serialized_pubsub_on_server_timeout, sub); +} + +static int load_module(void) +{ + pjsip_evsub_init_module(ast_sip_get_pjsip_endpoint()); + if (ast_sip_register_service(&sub_module)) { + return AST_MODULE_LOAD_DECLINE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "SIP event resource", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DEPEND, +); diff --git a/res/res_sip_pubsub.exports.in b/res/res_sip_pubsub.exports.in new file mode 100644 index 000000000..55308746a --- /dev/null +++ b/res/res_sip_pubsub.exports.in @@ -0,0 +1,16 @@ +{ + global: + LINKER_SYMBOL_PREFIXast_sip_create_subscription; + LINKER_SYMBOL_PREFIXast_sip_subsription_get_endpoint; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_serializer; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_evsub; + LINKER_SYMBOL_PREFIXast_sip_subscription_send_request; + LINKER_SYMBOL_PREFIXast_sip_subscription_alloc_datastore; + LINKER_SYMBOL_PREFIXast_sip_subscription_add_datastore; + LINKER_SYMBOL_PREFIXast_sip_subscription_get_datastore; + LINKER_SYMBOL_PREFIXast_sip_subscription_remove_datastore; + LINKER_SYMBOL_PREFIXast_sip_register_subscription_handler; + LINKER_SYMBOL_PREFIXast_sip_unregister_subscription_handler; + local: + *; +}; diff --git a/res/res_sip_registrar.c b/res/res_sip_registrar.c new file mode 100644 index 000000000..e5a2e888b --- /dev/null +++ b/res/res_sip_registrar.c @@ -0,0 +1,382 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "asterisk/module.h" + +/*! \brief Internal function which returns the expiration time for a contact */ +static int registrar_get_expiration(const struct ast_sip_aor *aor, const pjsip_contact_hdr *contact, const pjsip_rx_data *rdata) +{ + pjsip_expires_hdr *expires; + int expiration = aor->default_expiration; + + if (contact->expires != -1) { + /* Expiration was provided with the contact itself */ + expiration = contact->expires; + } else if ((expires = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { + /* Expiration was provided using the Expires header */ + expiration = expires->ivalue; + } + + /* If the value has explicitly been set to 0, do not enforce */ + if (!expiration) { + return expiration; + } + + /* Enforce the range that we will allow for expiration */ + if (expiration < aor->minimum_expiration) { + expiration = aor->minimum_expiration; + } else if (expiration > aor->maximum_expiration) { + expiration = aor->maximum_expiration; + } + + return expiration; +} + +/*! \brief Structure used for finding contact */ +struct registrar_contact_details { + /*! \brief Pool used for parsing URI */ + pj_pool_t *pool; + /*! \brief URI being looked for */ + pjsip_uri *uri; +}; + +/*! \brief Callback function for finding a contact */ +static int registrar_find_contact(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + const struct registrar_contact_details *details = arg; + pjsip_uri *contact_uri = pjsip_parse_uri(details->pool, (char*)contact->uri, strlen(contact->uri), 0); + + return (pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, details->uri, contact_uri) == PJ_SUCCESS) ? CMP_MATCH | CMP_STOP : 0; +} + +/*! \brief Internal function which validates provided Contact headers to confirm that they are acceptable, and returns number of contacts */ +static int registrar_validate_contacts(const pjsip_rx_data *rdata, struct ao2_container *contacts, struct ast_sip_aor *aor, int *added, int *updated, int *deleted) +{ + pjsip_contact_hdr *previous = NULL, *contact = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; + struct registrar_contact_details details = { + .pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256), + }; + + if (!details.pool) { + return -1; + } + + while ((contact = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact->next))) { + int expiration; + RAII_VAR(struct ast_sip_contact *, existing, NULL, ao2_cleanup); + + if (contact->star) { + /* The expiration MUST be 0 when a '*' contact is used and there must be no other contact */ + if ((contact->expires != 0) || previous) { + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); + return -1; + } + } else if (previous && previous->star) { + /* If there is a previous contact and it is a '*' this is a deal breaker */ + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); + return -1; + } + previous = contact; + + if (!PJSIP_URI_SCHEME_IS_SIP(contact->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact->uri)) { + continue; + } + + details.uri = pjsip_uri_get_uri(contact->uri); + expiration = registrar_get_expiration(aor, contact, rdata); + + /* Determine if this is an add, update, or delete for policy enforcement purposes */ + if (!(existing = ao2_callback(contacts, 0, registrar_find_contact, &details))) { + if (expiration) { + (*added)++; + } + } else if (expiration) { + (*updated)++; + } else { + (*deleted)++; + } + } + + /* The provided contacts are acceptable, huzzah! */ + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); + return 0; +} + +/*! \brief Callback function which prunes static contacts */ +static int registrar_prune_static(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + + return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0; +} + +/*! \brief Internal function used to delete all contacts from an AOR */ +static int registrar_delete_contact(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + + ast_sip_location_delete_contact(contact); + + return 0; +} + +/*! \brief Internal function which adds a contact to a response */ +static int registrar_add_contact(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + pjsip_tx_data *tdata = arg; + pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(tdata->pool); + pj_str_t uri; + + pj_strdup2_with_null(tdata->pool, &uri, contact->uri); + hdr->uri = pjsip_parse_uri(tdata->pool, uri.ptr, uri.slen, PJSIP_PARSE_URI_AS_NAMEADDR); + hdr->expires = ast_tvdiff_ms(contact->expiration_time, ast_tvnow()) / 1000; + + pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); + + return 0; +} + +/*! \brief Helper function which adds a Date header to a response */ +static void registrar_add_date_header(pjsip_tx_data *tdata) +{ + char date[256]; + struct tm tm; + time_t t = time(NULL); + + gmtime_r(&t, &tm); + strftime(date, sizeof(date), "%a, %d %b %Y %T GMT", &tm); + + ast_sip_add_header(tdata, "Date", date); +} + +static pj_bool_t registrar_on_rx_request(struct pjsip_rx_data *rdata) +{ + struct ast_sip_endpoint *endpoint = ast_pjsip_rdata_get_endpoint(rdata); + pjsip_sip_uri *uri; + char user_name[64], domain_name[64]; + char *configured_aors, *aor_name; + RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup); + RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); + int added = 0, updated = 0, deleted = 0; + pjsip_contact_hdr *contact_hdr = NULL; + struct registrar_contact_details details = { 0, }; + pjsip_tx_data *tdata; + pjsip_response_addr addr; + + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_register_method) || !endpoint) { + return PJ_FALSE; + } + + if (ast_strlen_zero(endpoint->aors)) { + /* Short circuit early if the endpoint has no AORs configured on it, which means no registration possible */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + return PJ_TRUE; + } + + if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 416, NULL, NULL, NULL); + return PJ_TRUE; + } + + uri = pjsip_uri_get_uri(rdata->msg_info.to->uri); + ast_copy_pj_str(user_name, &uri->user, sizeof(user_name)); + ast_copy_pj_str(domain_name, &uri->host, sizeof(domain_name)); + + configured_aors = ast_strdupa(endpoint->aors); + + /* Iterate the configured AORs to see if the user or the user+domain match */ + while ((aor_name = strsep(&configured_aors, ","))) { + char id[AST_UUID_STR_LEN]; + RAII_VAR(struct ast_sip_domain_alias *, alias, NULL, ao2_cleanup); + + snprintf(id, sizeof(id), "%s@%s", user_name, domain_name); + if (!strcmp(aor_name, id)) { + break; + } + + if ((alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name))) { + snprintf(id, sizeof(id), "%s@%s", user_name, alias->domain); + if (!strcmp(aor_name, id)) { + break; + } + } + + if (!strcmp(aor_name, user_name)) { + break; + } + } + + if (ast_strlen_zero(aor_name) || !(aor = ast_sip_location_retrieve_aor(aor_name))) { + /* The provided AOR name was not found (be it within the configuration or sorcery itself) */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 404, NULL, NULL, NULL); + return PJ_TRUE; + } + + if (!aor->max_contacts) { + /* Registration is not permitted for this AOR */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + return PJ_TRUE; + } + + /* Retrieve the current contacts, we'll need to know whether to update or not */ + contacts = ast_sip_location_retrieve_aor_contacts(aor); + + /* So we don't count static contacts against max_contacts we prune them out from the container */ + ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); + + if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) { + /* The provided Contact headers do not conform to the specification */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL); + return PJ_TRUE; + } + + if (((added - deleted) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) { + /* Enforce the maximum number of contacts */ + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); + return PJ_TRUE; + } + + if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); + return PJ_TRUE; + } + + /* Iterate each provided Contact header and add, update, or delete */ + while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { + int expiration; + char contact_uri[PJSIP_MAX_URL_SIZE]; + RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + + if (contact_hdr->star) { + /* A star means to unregister everything, so do so for the possible contacts */ + ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); + break; + } + + if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { + /* This registrar only currently supports sip: and sips: URI schemes */ + continue; + } + + expiration = registrar_get_expiration(aor, contact_hdr, rdata); + details.uri = pjsip_uri_get_uri(contact_hdr->uri); + pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); + + if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { + /* If they are actually trying to delete a contact that does not exist... be forgiving */ + if (!expiration) { + ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", + contact_uri, aor_name); + continue; + } + + ast_sip_location_add_contact(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1))); + ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", + contact_uri, aor_name, expiration); + } else if (expiration) { + RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup); + + updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); + + ast_sip_location_update_contact(updated); + ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", + contact_uri, aor_name, expiration); + } else { + ast_sip_location_delete_contact(contact); + ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); + } + } + + pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); + + /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER + * do so + */ + if (aor->remove_existing) { + ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); + } + + /* Update the contacts as things will probably have changed */ + ao2_cleanup(contacts); + contacts = ast_sip_location_retrieve_aor_contacts(aor); + + /* Send a response containing all of the contacts (including static) that are present on this AOR */ + if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), rdata, 200, NULL, &tdata) != PJ_SUCCESS) { + return PJ_TRUE; + } + + /* Add the date header to the response, some UAs use this to set their date and time */ + registrar_add_date_header(tdata); + + ao2_callback(contacts, 0, registrar_add_contact, tdata); + + if (pjsip_get_response_addr(tdata->pool, rdata, &addr) == PJ_SUCCESS) { + pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL); + } else { + pjsip_tx_data_dec_ref(tdata); + } + + return PJ_TRUE; +} + +static pjsip_module registrar_module = { + .name = { "Registrar", 9 }, + .id = -1, + .priority = PJSIP_MOD_PRIORITY_APPLICATION, + .on_rx_request = registrar_on_rx_request, +}; + +static int load_module(void) +{ + const pj_str_t STR_REGISTER = { "REGISTER", 8 }; + + if (ast_sip_register_service(®istrar_module)) { + return AST_MODULE_LOAD_DECLINE; + } + + if (pjsip_endpt_add_capability(ast_sip_get_pjsip_endpoint(), NULL, PJSIP_H_ALLOW, NULL, 1, &STR_REGISTER) != PJ_SUCCESS) { + ast_sip_unregister_service(®istrar_module); + return AST_MODULE_LOAD_DECLINE; + } + + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_service(®istrar_module); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP Registrar Support", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_rfc3326.c b/res/res_sip_rfc3326.c new file mode 100644 index 000000000..1c9ec6154 --- /dev/null +++ b/res/res_sip_rfc3326.c @@ -0,0 +1,145 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_session.h" +#include "asterisk/module.h" +#include "asterisk/causes.h" + +static void rfc3326_use_reason_header(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + const pj_str_t str_reason = { "Reason", 6 }; + pjsip_generic_string_hdr *header = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &str_reason, NULL); + char buf[20], *cause, *text; + int code; + + if (!header) { + return; + } + + ast_copy_pj_str(buf, &header->hvalue, sizeof(buf)); + cause = ast_skip_blanks(buf); + + if (strncasecmp(cause, "Q.850", 5) || !(cause = strstr(cause, "cause="))) { + return; + } + + /* If text is present get rid of it */ + if ((text = strstr(cause, ";"))) { + *text = '\0'; + } + + if (sscanf(cause, "cause=%30d", &code) != 1) { + return; + } + + ast_channel_hangupcause_set(session->channel, code & 0x7f); +} + +static int rfc3326_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + if ((pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_bye_method) && + pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_cancel_method)) || + !session->channel) { + return 0; + } + + rfc3326_use_reason_header(session, rdata); + + return 0; +} + +static void rfc3326_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + struct pjsip_status_line status = rdata->msg_info.msg->line.status; + + if ((status.code < 300) || !session->channel) { + return; + } + + rfc3326_use_reason_header(session, rdata); +} + +static void rfc3326_add_reason_header(struct ast_sip_session *session, struct pjsip_tx_data *tdata) +{ + char buf[20]; + + snprintf(buf, sizeof(buf), "Q.850;cause=%i", ast_channel_hangupcause(session->channel) & 0x7f); + ast_sip_add_header(tdata, "Reason", buf); + + if (ast_channel_hangupcause(session->channel) == AST_CAUSE_ANSWERED_ELSEWHERE) { + ast_sip_add_header(tdata, "Reason", "SIP;cause=200;text=\"Call completed elsewhere\""); + } +} + +static void rfc3326_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata) +{ + if ((pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_bye_method) && + pjsip_method_cmp(&tdata->msg->line.req.method, &pjsip_cancel_method)) || + !session->channel) { + return; + } + + rfc3326_add_reason_header(session, tdata); +} + +static void rfc3326_outgoing_response(struct ast_sip_session *session, struct pjsip_tx_data *tdata) +{ + struct pjsip_status_line status = tdata->msg->line.status; + + if ((status.code < 300) || !session->channel) { + return; + } + + rfc3326_add_reason_header(session, tdata); +} + +static struct ast_sip_session_supplement rfc3326_supplement = { + .incoming_request = rfc3326_incoming_request, + .incoming_response = rfc3326_incoming_response, + .outgoing_request = rfc3326_outgoing_request, + .outgoing_response = rfc3326_outgoing_response, +}; + +static int load_module(void) +{ + ast_sip_session_register_supplement(&rfc3326_supplement); + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_session_unregister_supplement(&rfc3326_supplement); + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP RFC3326 Support", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_sdp_rtp.c b/res/res_sip_sdp_rtp.c new file mode 100644 index 000000000..13e6aa1aa --- /dev/null +++ b/res/res_sip_sdp_rtp.c @@ -0,0 +1,848 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013, Digium, Inc. + * + * Joshua Colp <jcolp@digium.com> + * Kevin Harwell <kharwell@digium.com> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Joshua Colp <jcolp@digium.com> + * + * \brief SIP SDP media stream handling + */ + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sip</depend> + <depend>res_sip_session</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> +#include <pjmedia.h> +#include <pjlib.h> + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "asterisk/module.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/netsock2.h" +#include "asterisk/channel.h" +#include "asterisk/causes.h" +#include "asterisk/sched.h" +#include "asterisk/acl.h" + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_session.h" + +/*! \brief Scheduler for RTCP purposes */ +static struct ast_sched_context *sched; + +/*! \brief Address for IPv4 RTP */ +static struct ast_sockaddr address_ipv4; + +/*! \brief Address for IPv6 RTP */ +static struct ast_sockaddr address_ipv6; + +static const char STR_AUDIO[] = "audio"; +static const int FD_AUDIO = 0; + +static const char STR_VIDEO[] = "video"; +static const int FD_VIDEO = 2; + +/*! \brief Retrieves an ast_format_type based on the given stream_type */ +static enum ast_format_type stream_to_media_type(const char *stream_type) +{ + if (!strcasecmp(stream_type, STR_AUDIO)) { + return AST_FORMAT_TYPE_AUDIO; + } else if (!strcasecmp(stream_type, STR_VIDEO)) { + return AST_FORMAT_TYPE_VIDEO; + } + + return 0; +} + +/*! \brief Get the starting descriptor for a media type */ +static int media_type_to_fdno(enum ast_format_type media_type) +{ + switch (media_type) { + case AST_FORMAT_TYPE_AUDIO: return FD_AUDIO; + case AST_FORMAT_TYPE_VIDEO: return FD_VIDEO; + case AST_FORMAT_TYPE_TEXT: + case AST_FORMAT_TYPE_IMAGE: break; + } + return -1; +} + +/*! \brief Remove all other cap types but the one given */ +static void format_cap_only_type(struct ast_format_cap *caps, enum ast_format_type media_type) +{ + int i = AST_FORMAT_INC; + while (i <= AST_FORMAT_TYPE_TEXT) { + if (i != media_type) { + ast_format_cap_remove_bytype(caps, i); + } + i += AST_FORMAT_INC; + } +} + +/*! \brief Internal function which creates an RTP instance */ +static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6) +{ + struct ast_rtp_engine_ice *ice; + + if (!(session_media->rtp = ast_rtp_instance_new("asterisk", sched, ipv6 ? &address_ipv6 : &address_ipv4, NULL))) { + return -1; + } + + ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->rtp_symmetric); + + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session_media->rtp), + session_media->rtp, &session->endpoint->prefs); + + if (!session->endpoint->ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) { + ice->stop(session_media->rtp); + } + + return 0; +} + +static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp_media *stream, struct ast_rtp_codecs *codecs) +{ + pjmedia_sdp_attr *attr; + pjmedia_sdp_rtpmap *rtpmap; + pjmedia_sdp_fmtp fmtp; + struct ast_format *format; + int i, num = 0; + char name[256]; + char media[20]; + char fmt_param[256]; + + ast_rtp_codecs_payloads_initialize(codecs); + + /* Iterate through provided formats */ + for (i = 0; i < stream->desc.fmt_count; ++i) { + /* The payload is kept as a string for things like t38 but for video it is always numerical */ + ast_rtp_codecs_payloads_set_m_type(codecs, NULL, pj_strtoul(&stream->desc.fmt[i])); + /* Look for the optional rtpmap attribute */ + if (!(attr = pjmedia_sdp_media_find_attr2(stream, "rtpmap", &stream->desc.fmt[i]))) { + continue; + } + + /* Interpret the attribute as an rtpmap */ + if ((pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_prov, attr, &rtpmap)) != PJ_SUCCESS) { + continue; + } + + ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name)); + ast_copy_pj_str(media, (pj_str_t*)&stream->desc.media, sizeof(media)); + ast_rtp_codecs_payloads_set_rtpmap_type_rate(codecs, NULL, pj_strtoul(&stream->desc.fmt[i]), + media, name, 0, rtpmap->clock_rate); + /* Look for an optional associated fmtp attribute */ + if (!(attr = pjmedia_sdp_media_find_attr2(stream, "fmtp", &rtpmap->pt))) { + continue; + } + + if ((pjmedia_sdp_attr_get_fmtp(attr, &fmtp)) == PJ_SUCCESS) { + sscanf(pj_strbuf(&fmtp.fmt), "%d", &num); + if ((format = ast_rtp_codecs_get_payload_format(codecs, num))) { + ast_copy_pj_str(fmt_param, &fmtp.fmt_param, sizeof(fmt_param)); + ast_format_sdp_parse(format, fmt_param); + } + } + } +} + +static int set_caps(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_media *stream) +{ + RAII_VAR(struct ast_format_cap *, caps, NULL, ast_format_cap_destroy); + RAII_VAR(struct ast_format_cap *, peer, NULL, ast_format_cap_destroy); + RAII_VAR(struct ast_format_cap *, joint, NULL, ast_format_cap_destroy); + enum ast_format_type media_type = stream_to_media_type(session_media->stream_type); + struct ast_rtp_codecs codecs; + struct ast_format fmt; + int fmts = 0; + int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && + !ast_format_cap_is_empty(session->direct_media_cap); + + if (!(caps = ast_format_cap_alloc_nolock()) || + !(peer = ast_format_cap_alloc_nolock())) { + ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); + return -1; + } + + /* get the endpoint capabilities */ + if (direct_media_enabled) { + ast_format_cap_joint_copy(session->endpoint->codecs, session->direct_media_cap, caps); + } else { + ast_format_cap_copy(caps, session->endpoint->codecs); + } + format_cap_only_type(caps, media_type); + + /* get the capabilities on the peer */ + get_codecs(session, stream, &codecs); + ast_rtp_codecs_payload_formats(&codecs, peer, &fmts); + + /* get the joint capabilities between peer and endpoint */ + if (!(joint = ast_format_cap_joint(caps, peer))) { + char usbuf[64], thembuf[64]; + + ast_rtp_codecs_payloads_destroy(&codecs); + + ast_getformatname_multiple(usbuf, sizeof(usbuf), caps); + ast_getformatname_multiple(thembuf, sizeof(thembuf), peer); + ast_log(LOG_WARNING, "No joint capabilities between our configuration(%s) and incoming SDP(%s)\n", usbuf, thembuf); + return -1; + } + + ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp), + session_media->rtp); + + ast_format_cap_copy(caps, session->req_caps); + ast_format_cap_remove_bytype(caps, media_type); + ast_format_cap_append(caps, joint); + ast_format_cap_append(session->req_caps, caps); + + if (session->channel) { + ast_format_cap_copy(caps, ast_channel_nativeformats(session->channel)); + ast_format_cap_remove_bytype(caps, media_type); + ast_format_cap_append(caps, joint); + + /* Apply the new formats to the channel, potentially changing read/write formats while doing so */ + ast_format_cap_append(ast_channel_nativeformats(session->channel), caps); + ast_codec_choose(&session->endpoint->prefs, caps, 0, &fmt); + ast_format_copy(ast_channel_rawwriteformat(session->channel), &fmt); + ast_format_copy(ast_channel_rawreadformat(session->channel), &fmt); + ast_set_read_format(session->channel, ast_channel_readformat(session->channel)); + ast_set_write_format(session->channel, ast_channel_writeformat(session->channel)); + } + + ast_rtp_codecs_payloads_destroy(&codecs); + return 1; +} + +static pjmedia_sdp_attr* generate_rtpmap_attr(pjmedia_sdp_media *media, pj_pool_t *pool, int rtp_code, + int asterisk_format, struct ast_format *format, int code) +{ + pjmedia_sdp_rtpmap rtpmap; + pjmedia_sdp_attr *attr = NULL; + char tmp[64]; + + snprintf(tmp, sizeof(tmp), "%d", rtp_code); + pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); + rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; + rtpmap.clock_rate = ast_rtp_lookup_sample_rate2(asterisk_format, format, code); + pj_strdup2(pool, &rtpmap.enc_name, ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, 0)); + rtpmap.param.slen = 0; + + pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); + + return attr; +} + +static pjmedia_sdp_attr* generate_fmtp_attr(pj_pool_t *pool, struct ast_format *format, int rtp_code) +{ + struct ast_str *fmtp0 = ast_str_alloca(256); + pj_str_t fmtp1; + pjmedia_sdp_attr *attr = NULL; + char *tmp; + + ast_format_sdp_generate(format, rtp_code, &fmtp0); + if (ast_str_strlen(fmtp0)) { + tmp = ast_str_buffer(fmtp0) + ast_str_strlen(fmtp0) - 1; + /* remove any carriage return line feeds */ + while (*tmp == '\r' || *tmp == '\n') --tmp; + *++tmp = '\0'; + /* ast...generate gives us everything, just need value */ + tmp = strchr(ast_str_buffer(fmtp0), ':'); + if (tmp && tmp + 1) { + fmtp1 = pj_str(tmp + 1); + } else { + fmtp1 = pj_str(ast_str_buffer(fmtp0)); + } + attr = pjmedia_sdp_attr_create(pool, "fmtp", &fmtp1); + } + return attr; +} + +/*! \brief Function which adds ICE attributes to a media stream */ +static void add_ice_to_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, pj_pool_t *pool, pjmedia_sdp_media *media) +{ + struct ast_rtp_engine_ice *ice; + struct ao2_container *candidates; + const char *username, *password; + pj_str_t stmp; + pjmedia_sdp_attr *attr; + struct ao2_iterator it_candidates; + struct ast_rtp_engine_ice_candidate *candidate; + + if (!session->endpoint->ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp)) || + !(candidates = ice->get_local_candidates(session_media->rtp))) { + return; + } + + if ((username = ice->get_ufrag(session_media->rtp))) { + attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", pj_cstr(&stmp, username)); + media->attr[media->attr_count++] = attr; + } + + if ((password = ice->get_password(session_media->rtp))) { + attr = pjmedia_sdp_attr_create(pool, "ice-pwd", pj_cstr(&stmp, password)); + media->attr[media->attr_count++] = attr; + } + + it_candidates = ao2_iterator_init(candidates, 0); + for (; (candidate = ao2_iterator_next(&it_candidates)); ao2_ref(candidate, -1)) { + struct ast_str *attr_candidate = ast_str_create(128); + + ast_str_set(&attr_candidate, -1, "%s %d %s %d %s ", candidate->foundation, candidate->id, candidate->transport, + candidate->priority, ast_sockaddr_stringify_host(&candidate->address)); + ast_str_append(&attr_candidate, -1, "%s typ ", ast_sockaddr_stringify_port(&candidate->address)); + + switch (candidate->type) { + case AST_RTP_ICE_CANDIDATE_TYPE_HOST: + ast_str_append(&attr_candidate, -1, "host"); + break; + case AST_RTP_ICE_CANDIDATE_TYPE_SRFLX: + ast_str_append(&attr_candidate, -1, "srflx"); + break; + case AST_RTP_ICE_CANDIDATE_TYPE_RELAYED: + ast_str_append(&attr_candidate, -1, "relay"); + break; + } + + if (!ast_sockaddr_isnull(&candidate->relay_address)) { + ast_str_append(&attr_candidate, -1, " raddr %s rport ", ast_sockaddr_stringify_host(&candidate->relay_address)); + ast_str_append(&attr_candidate, -1, " %s", ast_sockaddr_stringify_port(&candidate->relay_address)); + } + + attr = pjmedia_sdp_attr_create(pool, "candidate", pj_cstr(&stmp, ast_str_buffer(attr_candidate))); + media->attr[media->attr_count++] = attr; + + ast_free(attr_candidate); + } + + ao2_iterator_destroy(&it_candidates); +} + +/*! \brief Function which processes ICE attributes in an audio stream */ +static void process_ice_attributes(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream) +{ + struct ast_rtp_engine_ice *ice; + const pjmedia_sdp_attr *attr; + char attr_value[256]; + unsigned int attr_i; + + /* If ICE support is not enabled or available exit early */ + if (!session->endpoint->ice_support || !(ice = ast_rtp_instance_get_ice(session_media->rtp))) { + return; + } + + if ((attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-ufrag", NULL))) { + ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value)); + ice->set_authentication(session_media->rtp, attr_value, NULL); + } + + if ((attr = pjmedia_sdp_media_find_attr2(remote_stream, "ice-pwd", NULL))) { + ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value)); + ice->set_authentication(session_media->rtp, NULL, attr_value); + } + + if (pjmedia_sdp_media_find_attr2(remote_stream, "ice-lite", NULL)) { + ice->ice_lite(session_media->rtp); + } + + /* Find all of the candidates */ + for (attr_i = 0; attr_i < remote_stream->attr_count; ++attr_i) { + char foundation[32], transport[32], address[PJ_INET6_ADDRSTRLEN + 1], cand_type[6], relay_address[PJ_INET6_ADDRSTRLEN + 1] = ""; + int port, relay_port = 0; + struct ast_rtp_engine_ice_candidate candidate = { 0, }; + + attr = remote_stream->attr[attr_i]; + + /* If this is not a candidate line skip it */ + if (pj_strcmp2(&attr->name, "candidate")) { + continue; + } + + ast_copy_pj_str(attr_value, (pj_str_t*)&attr->value, sizeof(attr_value)); + + if (sscanf(attr_value, "%31s %30u %31s %30u %46s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport, + &candidate.priority, address, &port, cand_type, relay_address, &relay_port) < 7) { + /* Candidate did not parse properly */ + continue; + } + + candidate.foundation = foundation; + candidate.transport = transport; + + ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID); + ast_sockaddr_set_port(&candidate.address, port); + + if (!strcasecmp(cand_type, "host")) { + candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST; + } else if (!strcasecmp(cand_type, "srflx")) { + candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX; + } else if (!strcasecmp(cand_type, "relay")) { + candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED; + } else { + continue; + } + + if (!ast_strlen_zero(relay_address)) { + ast_sockaddr_parse(&candidate.relay_address, relay_address, PARSE_PORT_FORBID); + } + + if (relay_port) { + ast_sockaddr_set_port(&candidate.relay_address, relay_port); + } + + ice->add_remote_candidate(session_media->rtp, &candidate); + } + + ice->start(session_media->rtp); +} + +static void apply_packetization(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_media *remote_stream) +{ + pjmedia_sdp_attr *attr; + pj_str_t value; + unsigned long framing; + int codec; + struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session_media->rtp)->pref; + + /* Apply packetization if available and configured to do so */ + if (!session->endpoint->use_ptime || !(attr = pjmedia_sdp_media_find_attr2(remote_stream, "ptime", NULL))) { + return; + } + + value = attr->value; + framing = pj_strtoul(pj_strltrim(&value)); + + for (codec = 0; codec < AST_RTP_MAX_PT; codec++) { + struct ast_rtp_payload_type format = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs( + session_media->rtp), codec); + + if (!format.asterisk_format) { + continue; + } + + ast_codec_pref_setsize(pref, &format.format, framing); + } + + ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session_media->rtp), + session_media->rtp, pref); +} + +/*! \brief Function which negotiates an incoming media stream */ +static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream) +{ + char host[NI_MAXHOST]; + RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr); + enum ast_format_type media_type = stream_to_media_type(session_media->stream_type); + + /* If no type formats have been configured reject this stream */ + if (!ast_format_cap_has_type(session->endpoint->codecs, media_type)) { + return 0; + } + + ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); + + /* Ensure that the address provided is valid */ + if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { + /* The provided host was actually invalid so we error out this negotiation */ + return -1; + } + + /* Using the connection information create an appropriate RTP instance */ + if (!session_media->rtp && create_rtp(session, session_media, ast_sockaddr_is_ipv6(addrs))) { + return -1; + } + + return set_caps(session, session_media, stream); +} + +/*! \brief Function which creates an outgoing stream */ +static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + struct pjmedia_sdp_session *sdp) +{ + pj_pool_t *pool = session->inv_session->pool_prov; + static const pj_str_t STR_IN = { "IN", 2 }; + static const pj_str_t STR_IP4 = { "IP4", 3}; + static const pj_str_t STR_IP6 = { "IP6", 3}; + static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; + static const pj_str_t STR_SENDRECV = { "sendrecv", 8 }; + pjmedia_sdp_media *media; + char hostip[PJ_INET6_ADDRSTRLEN+2]; + struct ast_sockaddr addr; + char tmp[512]; + pj_str_t stmp; + pjmedia_sdp_attr *attr; + int index = 0, min_packet_size = 0, noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733) ? AST_RTP_DTMF : 0; + int rtp_code; + struct ast_format format; + struct ast_format compat_format; + RAII_VAR(struct ast_format_cap *, caps, NULL, ast_format_cap_destroy); + enum ast_format_type media_type = stream_to_media_type(session_media->stream_type); + + int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && + !ast_format_cap_is_empty(session->direct_media_cap); + + if (!ast_format_cap_has_type(session->endpoint->codecs, media_type)) { + /* If no type formats are configured don't add a stream */ + return 0; + } else if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->rtp_ipv6)) { + return -1; + } + + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || + !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { + return -1; + } + + /* TODO: This should eventually support SRTP */ + media->desc.media = pj_str(session_media->stream_type); + media->desc.transport = STR_RTP_AVP; + + /* Add connection level details */ + if (direct_media_enabled) { + ast_copy_string(hostip, ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR), sizeof(hostip)); + } else if (ast_strlen_zero(session->endpoint->external_media_address)) { + pj_sockaddr localaddr; + + if (pj_gethostip(session->endpoint->rtp_ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) { + return -1; + } + pj_sockaddr_print(&localaddr, hostip, sizeof(hostip), 2); + } else { + ast_copy_string(hostip, session->endpoint->external_media_address, sizeof(hostip)); + } + + media->conn->net_type = STR_IN; + media->conn->addr_type = session->endpoint->rtp_ipv6 ? STR_IP6 : STR_IP4; + pj_strdup2(pool, &media->conn->addr, hostip); + ast_rtp_instance_get_local_address(session_media->rtp, &addr); + media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr); + media->desc.port_count = 1; + + /* Add ICE attributes and candidates */ + add_ice_to_stream(session, session_media, pool, media); + + if (!(caps = ast_format_cap_alloc_nolock())) { + ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); + return -1; + } + + if (direct_media_enabled) { + ast_format_cap_joint_copy(session->endpoint->codecs, session->direct_media_cap, caps); + } else if (ast_format_cap_is_empty(session->req_caps)) { + ast_format_cap_copy(caps, session->endpoint->codecs); + } else { + ast_format_cap_joint_copy(session->endpoint->codecs, session->req_caps, caps); + } + + for (index = 0; ast_codec_pref_index(&session->endpoint->prefs, index, &format); ++index) { + struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session_media->rtp)->pref; + + if (AST_FORMAT_GET_TYPE(format.id) != media_type) { + continue; + } + + if (!ast_format_cap_get_compatible_format(caps, &format, &compat_format)) { + continue; + } + + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), 1, &compat_format, 0)) == -1) { + return -1; + } + + if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 1, &compat_format, 0))) { + continue; + } + + media->attr[media->attr_count++] = attr; + + if ((attr = generate_fmtp_attr(pool, &compat_format, rtp_code))) { + media->attr[media->attr_count++] = attr; + } + + if (pref && media_type != AST_FORMAT_TYPE_VIDEO) { + struct ast_format_list fmt = ast_codec_pref_getsize(pref, &compat_format); + if (fmt.cur_ms && ((fmt.cur_ms < min_packet_size) || !min_packet_size)) { + min_packet_size = fmt.cur_ms; + } + } + } + + /* Add non-codec formats */ + if (media_type != AST_FORMAT_TYPE_VIDEO) { + for (index = 1LL; index <= AST_RTP_MAX; index <<= 1) { + if (!(noncodec & index) || (rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp), + 0, NULL, index)) == -1) { + continue; + } + + if (!(attr = generate_rtpmap_attr(media, pool, rtp_code, 0, NULL, index))) { + continue; + } + + media->attr[media->attr_count++] = attr; + + if (index == AST_RTP_DTMF) { + snprintf(tmp, sizeof(tmp), "%d 0-16", rtp_code); + attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp)); + media->attr[media->attr_count++] = attr; + } + } + } + + /* If ptime is set add it as an attribute */ + if (min_packet_size) { + snprintf(tmp, sizeof(tmp), "%d", min_packet_size); + attr = pjmedia_sdp_attr_create(pool, "ptime", pj_cstr(&stmp, tmp)); + media->attr[media->attr_count++] = attr; + } + + /* Add the sendrecv attribute - we purposely don't keep track because pjmedia-sdp will automatically change our offer for us */ + attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr); + attr->name = STR_SENDRECV; + media->attr[media->attr_count++] = attr; + + /* Add the media stream to the SDP */ + sdp->media[sdp->media_count++] = media; + + return 1; +} + +static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream, + const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream) +{ + RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr); + enum ast_format_type media_type = stream_to_media_type(session_media->stream_type); + char host[NI_MAXHOST]; + int fdno; + + if (!session->channel) { + return 1; + } + + /* Create an RTP instance if need be */ + if (!session_media->rtp && create_rtp(session, session_media, session->endpoint->rtp_ipv6)) { + return -1; + } + + ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); + + /* Ensure that the address provided is valid */ + if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { + /* The provided host was actually invalid so we error out this negotiation */ + return -1; + } + + /* Apply connection information to the RTP instance */ + ast_sockaddr_set_port(addrs, remote_stream->desc.port); + ast_rtp_instance_set_remote_address(session_media->rtp, addrs); + + if (set_caps(session, session_media, local_stream) < 1) { + return -1; + } + + if (media_type == AST_FORMAT_TYPE_AUDIO) { + apply_packetization(session, session_media, remote_stream); + } + + if ((fdno = media_type_to_fdno(media_type)) < 0) { + return -1; + } + ast_channel_set_fd(session->channel, fdno, ast_rtp_instance_fd(session_media->rtp, 0)); + ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1)); + + /* If ICE support is enabled find all the needed attributes */ + process_ice_attributes(session, session_media, remote, remote_stream); + + /* audio stream handles music on hold */ + if (media_type != AST_FORMAT_TYPE_AUDIO) { + return 1; + } + + /* Music on hold for audio streams only */ + if (session_media->held && + (!ast_sockaddr_isnull(addrs) || + !pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL))) { + /* The remote side has taken us off hold */ + ast_queue_control(session->channel, AST_CONTROL_UNHOLD); + ast_queue_frame(session->channel, &ast_null_frame); + session_media->held = 0; + } else if (ast_sockaddr_isnull(addrs) || + ast_sockaddr_is_any(addrs) || + pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL)) { + /* The remote side has put us on hold */ + ast_queue_control_data(session->channel, AST_CONTROL_HOLD, S_OR(session->endpoint->mohsuggest, NULL), + !ast_strlen_zero(session->endpoint->mohsuggest) ? strlen(session->endpoint->mohsuggest) + 1 : 0); + ast_rtp_instance_stop(session_media->rtp); + ast_queue_frame(session->channel, &ast_null_frame); + session_media->held = 1; + } else { + /* The remote side has not changed state, but make sure the instance is active */ + ast_rtp_instance_activate(session_media->rtp); + } + + return 1; +} + +/*! \brief Function which updates the media stream with external media address, if applicable */ +static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport) +{ + char host[NI_MAXHOST]; + struct ast_sockaddr addr = { { 0, } }; + + ast_copy_pj_str(host, &stream->conn->addr, sizeof(host)); + ast_sockaddr_parse(&addr, host, PARSE_PORT_FORBID); + + /* Is the address within the SDP inside the same network? */ + if (ast_apply_ha(transport->localnet, &addr) == AST_SENSE_ALLOW) { + return; + } + + pj_strdup2(tdata->pool, &stream->conn->addr, transport->external_media_address); +} + +/*! \brief Function which destroys the RTP instance when session ends */ +static void stream_destroy(struct ast_sip_session_media *session_media) +{ + if (session_media->rtp) { + ast_rtp_instance_stop(session_media->rtp); + ast_rtp_instance_destroy(session_media->rtp); + } +} + +/*! \brief SDP handler for 'audio' media stream */ +static struct ast_sip_session_sdp_handler audio_sdp_handler = { + .id = STR_AUDIO, + .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, + .create_outgoing_sdp_stream = create_outgoing_sdp_stream, + .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, + .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address, + .stream_destroy = stream_destroy, +}; + +/*! \brief SDP handler for 'video' media stream */ +static struct ast_sip_session_sdp_handler video_sdp_handler = { + .id = STR_VIDEO, + .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, + .create_outgoing_sdp_stream = create_outgoing_sdp_stream, + .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, + .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address, + .stream_destroy = stream_destroy, +}; + +static int video_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) +{ + struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); + pjsip_tx_data *tdata; + + if (pj_strcmp2(&rdata->msg_info.msg->body->content_type.type, "application") || + pj_strcmp2(&rdata->msg_info.msg->body->content_type.subtype, "media_control+xml")) { + + return 0; + } + + ast_queue_control(session->channel, AST_CONTROL_VIDUPDATE); + + if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, 200, NULL, &tdata) == PJ_SUCCESS) { + pjsip_dlg_send_response(session->inv_session->dlg, tsx, tdata); + } + + return 0; +} + +static struct ast_sip_session_supplement video_info_supplement = { + .method = "INFO", + .incoming_request = video_info_incoming_request, +}; + +/*! \brief Unloads the sdp RTP/AVP module from Asterisk */ +static int unload_module(void) +{ + ast_sip_session_unregister_supplement(&video_info_supplement); + ast_sip_session_unregister_sdp_handler(&video_sdp_handler, STR_VIDEO); + ast_sip_session_unregister_sdp_handler(&audio_sdp_handler, STR_AUDIO); + + if (sched) { + ast_sched_context_destroy(sched); + } + + return 0; +} + +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ +static int load_module(void) +{ + ast_sockaddr_parse(&address_ipv4, "0.0.0.0", 0); + ast_sockaddr_parse(&address_ipv6, "::", 0); + + if (!(sched = ast_sched_context_create())) { + ast_log(LOG_ERROR, "Unable to create scheduler context.\n"); + goto end; + } + + if (ast_sched_start_thread(sched)) { + ast_log(LOG_ERROR, "Unable to create scheduler context thread.\n"); + goto end; + } + + if (ast_sip_session_register_sdp_handler(&audio_sdp_handler, STR_AUDIO)) { + ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_AUDIO); + goto end; + } + + if (ast_sip_session_register_sdp_handler(&video_sdp_handler, STR_VIDEO)) { + ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_VIDEO); + goto end; + } + + ast_sip_session_register_supplement(&video_info_supplement); + + return AST_MODULE_LOAD_SUCCESS; +end: + unload_module(); + + return AST_MODULE_LOAD_FAILURE; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "SIP SDP RTP/AVP stream handler", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DRIVER, + ); diff --git a/res/res_sip_session.c b/res/res_sip_session.c new file mode 100644 index 000000000..6a31d9448 --- /dev/null +++ b/res/res_sip_session.c @@ -0,0 +1,1799 @@ +/* +* Asterisk -- An open source telephony toolkit. +* +* Copyright (C) 2013, Digium, Inc. +* +* Mark Michelson <mmichelson@digium.com> +* +* See http://www.asterisk.org for more information about +* the Asterisk project. Please do not directly contact +* any of the maintainers of this project for assistance; +* the project provides a web site, mailing lists and IRC +* channels for your use. +* +* This program is free software, distributed under the terms of +* the GNU General Public License Version 2. See the LICENSE file +* at the top of the source tree. +*/ + +/*** MODULEINFO + <depend>pjproject</depend> + <depend>res_sip</depend> + <support_level>core</support_level> + ***/ + +#include "asterisk.h" + +#include <pjsip.h> +#include <pjsip_ua.h> +#include <pjlib.h> + +#include "asterisk/res_sip.h" +#include "asterisk/res_sip_session.h" +#include "asterisk/datastore.h" +#include "asterisk/module.h" +#include "asterisk/logger.h" +#include "asterisk/res_sip.h" +#include "asterisk/astobj2.h" +#include "asterisk/lock.h" +#include "asterisk/uuid.h" +#include "asterisk/pbx.h" +#include "asterisk/taskprocessor.h" +#include "asterisk/causes.h" + +#define SDP_HANDLER_BUCKETS 11 + +/* Some forward declarations */ +static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata); +static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata); +static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata); +static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata); +static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata); +static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdata); + +/*! \brief NAT hook for modifying outgoing messages with SDP */ +static struct ast_sip_nat_hook *nat_hook; + +/*! + * \brief Registered SDP stream handlers + * + * This container is keyed on stream types. Each + * object in the container is a linked list of + * handlers for the stream type. + */ +static struct ao2_container *sdp_handlers; + +/*! + * These are the objects in the sdp_handlers container + */ +struct sdp_handler_list { + /* The list of handlers to visit */ + AST_LIST_HEAD_NOLOCK(, ast_sip_session_sdp_handler) list; + /* The handlers in this list handle streams of this type */ + char stream_type[1]; +}; + +static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer); + +static int sdp_handler_list_hash(const void *obj, int flags) +{ + const struct sdp_handler_list *handler_list = obj; + const char *stream_type = flags & OBJ_KEY ? obj : handler_list->stream_type; + + return ast_str_hash(stream_type); +} + +static int sdp_handler_list_cmp(void *obj, void *arg, int flags) +{ + struct sdp_handler_list *handler_list1 = obj; + struct sdp_handler_list *handler_list2 = arg; + const char *stream_type2 = flags & OBJ_KEY ? arg : handler_list2->stream_type; + + return strcmp(handler_list1->stream_type, stream_type2) ? 0 : CMP_MATCH | CMP_STOP; +} + +static int session_media_hash(const void *obj, int flags) +{ + const struct ast_sip_session_media *session_media = obj; + const char *stream_type = flags & OBJ_KEY ? obj : session_media->stream_type; + + return ast_str_hash(stream_type); +} + +static int session_media_cmp(void *obj, void *arg, int flags) +{ + struct ast_sip_session_media *session_media1 = obj; + struct ast_sip_session_media *session_media2 = arg; + const char *stream_type2 = flags & OBJ_KEY ? arg : session_media2->stream_type; + + return strcmp(session_media1->stream_type, stream_type2) ? 0 : CMP_MATCH | CMP_STOP; +} + +int ast_sip_session_register_sdp_handler(struct ast_sip_session_sdp_handler *handler, const char *stream_type) +{ + RAII_VAR(struct sdp_handler_list *, handler_list, + ao2_find(sdp_handlers, stream_type, OBJ_KEY), ao2_cleanup); + SCOPED_AO2LOCK(lock, sdp_handlers); + + if (handler_list) { + struct ast_sip_session_sdp_handler *iter; + /* Check if this handler is already registered for this stream type */ + AST_LIST_TRAVERSE(&handler_list->list, iter, next) { + if (!strcmp(iter->id, handler->id)) { + ast_log(LOG_WARNING, "Handler '%s' already registered for stream type '%s'.\n", handler->id, stream_type); + return -1; + } + } + AST_LIST_INSERT_TAIL(&handler_list->list, handler, next); + ast_debug(1, "Registered SDP stream handler '%s' for stream type '%s'\n", handler->id, stream_type); + ast_module_ref(ast_module_info->self); + return 0; + } + + /* No stream of this type has been registered yet, so we need to create a new list */ + handler_list = ao2_alloc(sizeof(*handler_list) + strlen(stream_type), NULL); + if (!handler_list) { + return -1; + } + /* Safe use of strcpy */ + strcpy(handler_list->stream_type, stream_type); + AST_LIST_HEAD_INIT_NOLOCK(&handler_list->list); + AST_LIST_INSERT_TAIL(&handler_list->list, handler, next); + if (!ao2_link(sdp_handlers, handler_list)) { + return -1; + } + ast_debug(1, "Registered SDP stream handler '%s' for stream type '%s'\n", handler->id, stream_type); + ast_module_ref(ast_module_info->self); + return 0; +} + +static int remove_handler(void *obj, void *arg, void *data, int flags) +{ + struct sdp_handler_list *handler_list = obj; + struct ast_sip_session_sdp_handler *handler = data; + struct ast_sip_session_sdp_handler *iter; + const char *stream_type = arg; + + AST_LIST_TRAVERSE_SAFE_BEGIN(&handler_list->list, iter, next) { + if (!strcmp(iter->id, handler->id)) { + AST_LIST_REMOVE_CURRENT(next); + ast_debug(1, "Unregistered SDP stream handler '%s' for stream type '%s'\n", handler->id, stream_type); + ast_module_unref(ast_module_info->self); + } + } + AST_LIST_TRAVERSE_SAFE_END; + + if (AST_LIST_EMPTY(&handler_list->list)) { + ast_debug(3, "No more handlers exist for stream type '%s'\n", stream_type); + return CMP_MATCH; + } else { + return CMP_STOP; + } +} + +void ast_sip_session_unregister_sdp_handler(struct ast_sip_session_sdp_handler *handler, const char *stream_type) +{ + ao2_callback_data(sdp_handlers, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, remove_handler, (void *)stream_type, handler); +} + +static int validate_port_hash(const void *obj, int flags) +{ + const int *port = obj; + return *port; +} + +static int validate_port_cmp(void *obj, void *arg, int flags) +{ + int *port1 = obj; + int *port2 = arg; + + return *port1 == *port2 ? CMP_MATCH | CMP_STOP : 0; +} + +struct bundle_assoc { + int port; + char tag[1]; +}; + +static int bundle_assoc_hash(const void *obj, int flags) +{ + const struct bundle_assoc *assoc = obj; + const char *tag = flags & OBJ_KEY ? obj : assoc->tag; + + return ast_str_hash(tag); +} + +static int bundle_assoc_cmp(void *obj, void *arg, int flags) +{ + struct bundle_assoc *assoc1 = obj; + struct bundle_assoc *assoc2 = arg; + const char *tag2 = flags & OBJ_KEY ? arg : assoc2->tag; + + return strcmp(assoc1->tag, tag2) ? 0 : CMP_MATCH | CMP_STOP; +} + +/* return must be ast_freed */ +static pjmedia_sdp_attr *media_get_mid(pjmedia_sdp_media *media) +{ + pjmedia_sdp_attr *attr = pjmedia_sdp_media_find_attr2(media, "mid", NULL); + if (!attr) { + return NULL; + } + + return attr; +} + +static int get_bundle_port(const pjmedia_sdp_session *sdp, const char *mid) +{ + int i; + for (i = 0; i < sdp->media_count; ++i) { + pjmedia_sdp_attr *mid_attr = media_get_mid(sdp->media[i]); + if (mid_attr && !pj_strcmp2(&mid_attr->value, mid)) { + return sdp->media[i]->desc.port; + } + } + + return -1; +} + +static int validate_incoming_sdp(const pjmedia_sdp_session *sdp) +{ + int i; + RAII_VAR(struct ao2_container *, portlist, ao2_container_alloc(5, validate_port_hash, validate_port_cmp), ao2_cleanup); + RAII_VAR(struct ao2_container *, bundle_assoc_list, ao2_container_alloc(5, bundle_assoc_hash, bundle_assoc_cmp), ao2_cleanup); + + /* check for bundles (for websocket RTP multiplexing, there can be more than one) */ + for (i = 0; i < sdp->attr_count; ++i) { + char *bundle_list; + int bundle_port = 0; + if (pj_stricmp2(&sdp->attr[i]->name, "group")) { + continue; + } + + /* check to see if this group is a bundle */ + if (7 >= sdp->attr[i]->value.slen || pj_strnicmp2(&sdp->attr[i]->value, "bundle ", 7)) { + continue; + } + + bundle_list = ast_alloca(sdp->attr[i]->value.slen - 6); + strncpy(bundle_list, sdp->attr[i]->value.ptr + 7, sdp->attr[i]->value.slen - 7); + bundle_list[sdp->attr[i]->value.slen - 7] = '\0'; + while (bundle_list) { + char *item; + RAII_VAR(struct bundle_assoc *, assoc, NULL, ao2_cleanup); + item = strsep(&bundle_list, " ,"); + if (!bundle_port) { + RAII_VAR(int *, port, ao2_alloc(sizeof(int), NULL), ao2_cleanup); + RAII_VAR(int *, port_match, NULL, ao2_cleanup); + bundle_port = get_bundle_port(sdp, item); + if (bundle_port < 0) { + return -1; + } + port_match = ao2_find(portlist, &bundle_port, OBJ_KEY); + if (port_match) { + /* bundle port aready consumed by a different bundle */ + return -1; + } + *port = bundle_port; + ao2_link(portlist, port); + } + assoc = ao2_alloc(sizeof(*assoc) + strlen(item), NULL); + if (!assoc) { + return -1; + } + + /* safe use of strcpy */ + strcpy(assoc->tag, item); + assoc->port = bundle_port; + ao2_link(bundle_assoc_list, assoc); + } + } + + /* validate all streams */ + for (i = 0; i < sdp->media_count; ++i) { + RAII_VAR(int *, port, ao2_alloc(sizeof(int), NULL), ao2_cleanup); + RAII_VAR(int *, port_match, NULL, ao2_cleanup); + RAII_VAR(int *, bundle_match, NULL, ao2_cleanup); + *port = sdp->media[i]->desc.port; + port_match = ao2_find(portlist, port, OBJ_KEY); + if (port_match) { + RAII_VAR(struct bundle_assoc *, assoc, NULL, ao2_cleanup); + pjmedia_sdp_attr *mid = media_get_mid(sdp->media[i]); + char *mid_val; + + if (!mid) { + /* not part of a bundle */ + return -1; + } + + mid_val = ast_alloca(mid->value.slen + 1); + strncpy(mid_val, mid->value.ptr, mid->value.slen); + mid_val[mid->value.slen] = '\0'; + + assoc = ao2_find(bundle_assoc_list, mid_val, OBJ_KEY); + if (!assoc || assoc->port != *port) { + /* This port already exists elsewhere in the SDP + * and is not an appropriate bundle port, fail + * catastrophically */ + return -1; + } + } + ao2_link(portlist, port); + } + return 0; +} + +static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *sdp) +{ + int i; + if (validate_incoming_sdp(sdp)) { + return -1; + } + + for (i = 0; i < sdp->media_count; ++i) { + /* See if there are registered handlers for this media stream type */ + char media[20]; + struct ast_sip_session_sdp_handler *handler; + RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup); + + /* We need a null-terminated version of the media string */ + ast_copy_pj_str(media, &sdp->media[i]->desc.media, sizeof(media)); + + handler_list = ao2_find(sdp_handlers, media, OBJ_KEY); + if (!handler_list) { + ast_debug(1, "No registered SDP handlers for media type '%s'\n", media); + continue; + } + AST_LIST_TRAVERSE(&handler_list->list, handler, next) { + int res; + RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup); + session_media = ao2_find(session->media, handler_list->stream_type, OBJ_KEY); + if (!session_media || session_media->handler) { + /* There is only one slot for this stream type and it has already been claimed + * so it will go unhandled */ + break; + } + res = handler->negotiate_incoming_sdp_stream(session, session_media, sdp, sdp->media[i]); + if (res < 0) { + /* Catastrophic failure. Abort! */ + return -1; + } + if (res > 0) { + /* Handled by this handler. Move to the next stream */ + session_media->handler = handler; + break; + } + } + } + return 0; +} + +struct handle_negotiated_sdp_cb { + struct ast_sip_session *session; + const pjmedia_sdp_session *local; + const pjmedia_sdp_session *remote; +}; + +static int handle_negotiated_sdp_session_media(void *obj, void *arg, int flags) +{ + struct ast_sip_session_media *session_media = obj; + struct handle_negotiated_sdp_cb *callback_data = arg; + struct ast_sip_session *session = callback_data->session; + const pjmedia_sdp_session *local = callback_data->local; + const pjmedia_sdp_session *remote = callback_data->remote; + int i; + + for (i = 0; i < local->media_count; ++i) { + /* See if there are registered handlers for this media stream type */ + char media[20]; + struct ast_sip_session_sdp_handler *handler; + RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup); + + /* We need a null-terminated version of the media string */ + ast_copy_pj_str(media, &local->media[i]->desc.media, sizeof(media)); + + /* stream type doesn't match the one we're looking to fill */ + if (strcasecmp(session_media->stream_type, media)) { + continue; + } + + handler = session_media->handler; + if (handler) { + int res = handler->apply_negotiated_sdp_stream(session, session_media, local, local->media[i], remote, remote->media[i]); + if (res >= 0) { + return CMP_MATCH; + } + return 0; + } + + handler_list = ao2_find(sdp_handlers, media, OBJ_KEY); + if (!handler_list) { + ast_debug(1, "No registered SDP handlers for media type '%s'\n", media); + continue; + } + AST_LIST_TRAVERSE(&handler_list->list, handler, next) { + int res = handler->apply_negotiated_sdp_stream(session, session_media, local, local->media[i], remote, remote->media[i]); + if (res < 0) { + /* Catastrophic failure. Abort! */ + return 0; + } + if (res > 0) { + /* Handled by this handler. Move to the next stream */ + session_media->handler = handler; + return CMP_MATCH; + } + } + } + return CMP_MATCH; +} + +static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote) +{ + RAII_VAR(struct ao2_iterator *, successful, NULL, ao2_iterator_cleanup); + struct handle_negotiated_sdp_cb callback_data = { + .session = session, + .local = local, + .remote = remote, + }; + + successful = ao2_callback(session->media, OBJ_MULTIPLE, handle_negotiated_sdp_session_media, &callback_data); + if (successful && ao2_container_count(successful->c) == ao2_container_count(session->media)) { + /* Nothing experienced a catastrophic failure */ + return 0; + } + return -1; +} + +AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement); + +int ast_sip_session_register_supplement(struct ast_sip_session_supplement *supplement) +{ + struct ast_sip_session_supplement *iter; + int inserted = 0; + SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_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(&session_supplements, supplement, next); + } + ast_module_ref(ast_module_info->self); + return 0; +} + +void ast_sip_session_unregister_supplement(struct ast_sip_session_supplement *supplement) +{ + struct ast_sip_session_supplement *iter; + SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_WRLOCK, AST_RWLIST_UNLOCK); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&session_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 struct ast_sip_session_supplement *supplement_dup(const struct ast_sip_session_supplement *src) +{ + struct ast_sip_session_supplement *dst = ast_calloc(1, sizeof(*dst)); + if (!dst) { + return NULL; + } + /* Will need to revisit if shallow copy becomes an issue */ + *dst = *src; + return dst; +} + +#define DATASTORE_BUCKETS 53 +#define MEDIA_BUCKETS 7 + +static void session_datastore_destroy(void *obj) +{ + struct ast_datastore *datastore = obj; + + /* Using the destroy function (if present) destroy the data */ + if (datastore->info->destroy != NULL && datastore->data != NULL) { + datastore->info->destroy(datastore->data); + datastore->data = NULL; + } + + ast_free((void *) datastore->uid); + datastore->uid = NULL; +} + +struct ast_datastore *ast_sip_session_alloc_datastore(const struct ast_datastore_info *info, const char *uid) +{ + RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup); + const char *uid_ptr = uid; + + if (!info) { + return NULL; + } + + datastore = ao2_alloc(sizeof(*datastore), session_datastore_destroy); + if (!datastore) { + return NULL; + } + + datastore->info = info; + if (ast_strlen_zero(uid)) { + /* They didn't provide an ID so we'll provide one ourself */ + struct ast_uuid *uuid = ast_uuid_generate(); + char uuid_buf[AST_UUID_STR_LEN]; + if (!uuid) { + return NULL; + } + uid_ptr = ast_uuid_to_str(uuid, uuid_buf, sizeof(uuid_buf)); + ast_free(uuid); + } + + datastore->uid = ast_strdup(uid_ptr); + if (!datastore->uid) { + return NULL; + } + + ao2_ref(datastore, +1); + return datastore; +} + +int ast_sip_session_add_datastore(struct ast_sip_session *session, struct ast_datastore *datastore) +{ + ast_assert(datastore != NULL); + ast_assert(datastore->info != NULL); + ast_assert(ast_strlen_zero(datastore->uid) == 0); + + if (!ao2_link(session->datastores, datastore)) { + return -1; + } + return 0; +} + +struct ast_datastore *ast_sip_session_get_datastore(struct ast_sip_session *session, const char *name) +{ + return ao2_find(session->datastores, name, OBJ_KEY); +} + +void ast_sip_session_remove_datastore(struct ast_sip_session *session, const char *name) +{ + ao2_callback(session->datastores, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, NULL, (void *) name); +} + +int ast_sip_session_get_identity(struct pjsip_rx_data *rdata, struct ast_party_id *id) +{ + /* XXX STUB + * This is low-priority as far as getting SIP working is concerned, so this + * will be addressed later. + * + * The idea here will be that the rdata will be examined for headers such as + * P-Asserted-Identity, Remote-Party-ID, and From in order to determine Identity + * information. + * + * For reference, Asterisk SCF code does something very similar to this, except in + * C++ and using its version of the ast_party_id struct, so using it as a basis + * would be a smart idea here. + */ + return 0; +} + +/*! + * \brief Structure used for sending delayed requests + * + * Requests are typically delayed because the current transaction + * state of an INVITE. Once the pending INVITE transaction terminates, + * the delayed request will be sent + */ +struct ast_sip_session_delayed_request { + /*! Method of the request */ + char method[15]; + /*! Callback to call when the delayed request is created. */ + ast_sip_session_request_creation_cb on_request_creation; + /*! Callback to call when the delayed request receives a response */ + ast_sip_session_response_cb on_response; + /*! Request to send */ + pjsip_tx_data *tdata; + AST_LIST_ENTRY(ast_sip_session_delayed_request) next; +}; + +static struct ast_sip_session_delayed_request *delayed_request_alloc(const char *method, + ast_sip_session_request_creation_cb on_request_creation, + ast_sip_session_response_cb on_response, + pjsip_tx_data *tdata) +{ + struct ast_sip_session_delayed_request *delay = ast_calloc(1, sizeof(*delay)); + if (!delay) { + return NULL; + } + ast_copy_string(delay->method, method, sizeof(delay->method)); + delay->on_request_creation = on_request_creation; + delay->on_response = on_response; + delay->tdata = tdata; + return delay; +} + +static int send_delayed_request(struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay) +{ + ast_debug(3, "Sending delayed %s request to %s\n", delay->method, ast_sorcery_object_get_id(session->endpoint)); + + if (delay->tdata) { + ast_sip_session_send_request_with_cb(session, delay->tdata, delay->on_response); + return 0; + } + + if (!strcmp(delay->method, "INVITE")) { + ast_sip_session_refresh(session, delay->on_request_creation, + delay->on_response, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1); + } else if (!strcmp(delay->method, "UPDATE")) { + ast_sip_session_refresh(session, delay->on_request_creation, + delay->on_response, AST_SIP_SESSION_REFRESH_METHOD_UPDATE, 1); + } else { + ast_log(LOG_WARNING, "Unexpected delayed %s request with no existing request structure\n", delay->method); + return -1; + } + return 0; +} + +static int queued_delayed_request_send(void *data) +{ + RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup); + RAII_VAR(struct ast_sip_session_delayed_request *, delay, NULL, ast_free_ptr); + + delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next); + if (!delay) { + return 0; + } + + return send_delayed_request(session, delay); +} + +static void queue_delayed_request(struct ast_sip_session *session) +{ + if (AST_LIST_EMPTY(&session->delayed_requests)) { + /* No delayed request to send, so just return */ + return; + } + + ast_debug(3, "Queuing delayed request to run for %s\n", + ast_sorcery_object_get_id(session->endpoint)); + + ao2_ref(session, +1); + ast_sip_push_task(session->serializer, queued_delayed_request_send, session); +} + +static int delay_request(struct ast_sip_session *session, ast_sip_session_request_creation_cb on_request, + ast_sip_session_response_cb on_response, const char *method, pjsip_tx_data *tdata) +{ + struct ast_sip_session_delayed_request *delay = delayed_request_alloc(method, + on_request, on_response, tdata); + + if (!delay) { + return -1; + } + + AST_LIST_INSERT_TAIL(&session->delayed_requests, delay, next); + return 0; +} + +static pjmedia_sdp_session *generate_session_refresh_sdp(struct ast_sip_session *session) +{ + pjsip_inv_session *inv_session = session->inv_session; + const pjmedia_sdp_session *previous_sdp; + + if (pjmedia_sdp_neg_was_answer_remote(inv_session->neg)) { + pjmedia_sdp_neg_get_active_remote(inv_session->neg, &previous_sdp); + } else { + pjmedia_sdp_neg_get_active_local(inv_session->neg, &previous_sdp); + } + return create_local_sdp(inv_session, session, previous_sdp); +} + +int ast_sip_session_refresh(struct ast_sip_session *session, + ast_sip_session_request_creation_cb on_request_creation, ast_sip_session_response_cb on_response, + enum ast_sip_session_refresh_method method, int generate_new_sdp) +{ + pjsip_inv_session *inv_session = session->inv_session; + pjmedia_sdp_session *new_sdp = NULL; + pjsip_tx_data *tdata; + + if (inv_session->state == PJSIP_INV_STATE_DISCONNECTED) { + /* Don't try to do anything with a hung-up call */ + ast_debug(3, "Not sending reinvite to %s because of disconnected state...\n", + ast_sorcery_object_get_id(session->endpoint)); + return 0; + } + + if (inv_session->invite_tsx) { + /* We can't send a reinvite yet, so delay it */ + ast_debug(3, "Delaying sending reinvite to %s because of outstanding transaction...\n", + ast_sorcery_object_get_id(session->endpoint)); + return delay_request(session, on_request_creation, on_response, "INVITE", NULL); + } + + if (generate_new_sdp) { + new_sdp = generate_session_refresh_sdp(session); + if (!new_sdp) { + ast_log(LOG_ERROR, "Failed to generate session refresh SDP. Not sending session refresh\n"); + return -1; + } + } + + if (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE) { + if (pjsip_inv_reinvite(inv_session, NULL, new_sdp, &tdata)) { + ast_log(LOG_WARNING, "Failed to create reinvite properly.\n"); + return -1; + } + } else if (pjsip_inv_update(inv_session, NULL, new_sdp, &tdata)) { + ast_log(LOG_WARNING, "Failed to create UPDATE properly.\n"); + return -1; + } + if (on_request_creation) { + if (on_request_creation(session, tdata)) { + return -1; + } + } + ast_sip_session_send_request_with_cb(session, tdata, on_response); + return 0; +} + +void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + handle_outgoing_response(session, tdata); + pjsip_inv_send_msg(session->inv_session, tdata); + return; +} + +static pj_status_t session_load(pjsip_endpoint *endpt); +static pj_status_t session_start(void); +static pj_status_t session_stop(void); +static pj_status_t session_unload(void); +static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata); + +static pjsip_module session_module = { + .name = {"Session Module", 14}, + .priority = PJSIP_MOD_PRIORITY_APPLICATION, + .load = session_load, + .unload = session_unload, + .start = session_start, + .stop = session_stop, + .on_rx_request = session_on_rx_request, +}; + +void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip_tx_data *tdata, + ast_sip_session_response_cb on_response) +{ + pjsip_inv_session *inv_session = session->inv_session; + + if (inv_session->state == PJSIP_INV_STATE_DISCONNECTED) { + /* Don't try to do anything with a hung-up call */ + return; + } + + tdata->mod_data[session_module.id] = on_response; + handle_outgoing_request(session, tdata); + pjsip_inv_send_msg(session->inv_session, tdata); + return; +} + +void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + ast_sip_session_send_request_with_cb(session, tdata, NULL); +} + +/*! + * \brief Called when the PJSIP core loads us + * + * Since we already have Asterisk's fine module load/unload framework + * in use, we don't need to do anything special here. + */ +static pj_status_t session_load(pjsip_endpoint *endpt) +{ + return PJ_SUCCESS; +} + +/*! + * \brief Called when the PJSIP core starts us + * + * Since we already have Asterisk's fine module load/unload framework + * in use, we don't need to do anything special here. + */ +static pj_status_t session_start(void) +{ + return PJ_SUCCESS; +} + +/*! + * \brief Called when the PJSIP core stops us + * + * Since we already have Asterisk's fine module load/unload framework + * in use, we don't need to do anything special here. + */ +static pj_status_t session_stop(void) +{ + return PJ_SUCCESS; +} + +/*! + * \brief Called when the PJSIP core unloads us + * + * Since we already have Asterisk's fine module load/unload framework + * in use, we don't need to do anything special here. + */ +static pj_status_t session_unload(void) +{ + return PJ_SUCCESS; +} + +static int datastore_hash(const void *obj, int flags) +{ + const struct ast_datastore *datastore = obj; + const char *uid = flags & OBJ_KEY ? obj : datastore->uid; + + ast_assert(uid != NULL); + + return ast_str_hash(uid); +} + +static int datastore_cmp(void *obj, void *arg, int flags) +{ + const struct ast_datastore *datastore1 = obj; + const struct ast_datastore *datastore2 = arg; + const char *uid2 = flags & OBJ_KEY ? arg : datastore2->uid; + + ast_assert(datastore1->uid != NULL); + ast_assert(uid2 != NULL); + + return strcmp(datastore1->uid, uid2) ? 0 : CMP_MATCH | CMP_STOP; +} + +static void session_media_dtor(void *obj) +{ + struct ast_sip_session_media *session_media = obj; + if (session_media->handler) { + session_media->handler->stream_destroy(session_media); + } +} + +static void session_destructor(void *obj) +{ + struct ast_sip_session *session = obj; + struct ast_sip_session_supplement *supplement; + struct ast_sip_session_delayed_request *delay; + + ast_debug(3, "Destroying SIP session with endpoint %s\n", + ast_sorcery_object_get_id(session->endpoint)); + + while ((supplement = AST_LIST_REMOVE_HEAD(&session->supplements, next))) { + if (supplement->session_destroy) { + supplement->session_destroy(session); + } + ast_free(supplement); + } + + ast_taskprocessor_unreference(session->serializer); + ao2_cleanup(session->datastores); + ao2_cleanup(session->media); + AST_LIST_HEAD_DESTROY(&session->supplements); + while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) { + ast_free(delay); + } + ast_party_id_free(&session->id); + ao2_cleanup(session->endpoint); + ast_format_cap_destroy(session->req_caps); +} + +static int add_supplements(struct ast_sip_session *session) +{ + struct ast_sip_session_supplement *iter; + SCOPED_LOCK(lock, &session_supplements, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + + AST_RWLIST_TRAVERSE(&session_supplements, iter, next) { + struct ast_sip_session_supplement *copy = supplement_dup(iter); + if (!copy) { + return -1; + } + AST_LIST_INSERT_TAIL(&session->supplements, copy, next); + } + return 0; +} + +static int add_session_media(void *obj, void *arg, int flags) +{ + struct sdp_handler_list *handler_list = obj; + struct ast_sip_session * session = arg; + RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup); + session_media = ao2_alloc(sizeof(*session_media) + strlen(handler_list->stream_type), session_media_dtor); + if (!session_media) { + return CMP_STOP; + } + /* Safe use of strcpy */ + strcpy(session_media->stream_type, handler_list->stream_type); + ao2_link(session->media, session_media); + return 0; +} + +struct ast_sip_session *ast_sip_session_alloc(struct ast_sip_endpoint *endpoint, pjsip_inv_session *inv_session) +{ + RAII_VAR(struct ast_sip_session *, session, ao2_alloc(sizeof(*session), session_destructor), ao2_cleanup); + struct ast_sip_session_supplement *iter; + if (!session) { + return NULL; + } + AST_LIST_HEAD_INIT(&session->supplements); + session->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp); + if (!session->datastores) { + return NULL; + } + + session->media = ao2_container_alloc(MEDIA_BUCKETS, session_media_hash, session_media_cmp); + if (!session->media) { + return NULL; + } + /* fill session->media with available types */ + ao2_callback(sdp_handlers, OBJ_NODATA, add_session_media, session); + + session->serializer = ast_sip_create_serializer(); + if (!session->serializer) { + return NULL; + } + ast_sip_dialog_set_serializer(inv_session->dlg, session->serializer); + ast_sip_dialog_set_endpoint(inv_session->dlg, endpoint); + ao2_ref(endpoint, +1); + inv_session->mod_data[session_module.id] = session; + session->endpoint = endpoint; + session->inv_session = inv_session; + session->req_caps = ast_format_cap_alloc_nolock(); + + if (add_supplements(session)) { + return NULL; + } + AST_LIST_TRAVERSE(&session->supplements, iter, next) { + if (iter->session_begin) { + iter->session_begin(session); + } + } + session->direct_media_cap = ast_format_cap_alloc_nolock(); + AST_LIST_HEAD_INIT_NOLOCK(&session->delayed_requests); + ast_party_id_init(&session->id); + ao2_ref(session, +1); + return session; +} + +static int session_outbound_auth(pjsip_dialog *dlg, pjsip_tx_data *tdata, void *user_data) +{ + pjsip_inv_session *inv = pjsip_dlg_get_inv_session(dlg); + struct ast_sip_session *session = inv->mod_data[session_module.id]; + + if (inv->state < PJSIP_INV_STATE_CONFIRMED && tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) { + pjsip_inv_uac_restart(inv, PJ_TRUE); + } + ast_sip_session_send_request(session, tdata); + return 0; +} + +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) +{ + const char *uri = NULL; + RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + pjsip_timer_setting timer; + pjsip_dialog *dlg; + struct pjsip_inv_session *inv_session; + RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup); + pjmedia_sdp_session *offer; + + /* If no location has been provided use the AOR list from the endpoint itself */ + location = S_OR(location, endpoint->aors); + + contact = ast_sip_location_retrieve_contact_from_aor_list(location); + if (!contact || ast_strlen_zero(contact->uri)) { + uri = location; + } else { + uri = contact->uri; + } + + /* If we still have no URI to dial fail to create the session */ + if (ast_strlen_zero(uri)) { + return NULL; + } + + if (!(dlg = ast_sip_create_dialog(endpoint, uri, request_user))) { + return NULL; + } + + if (ast_sip_dialog_setup_outbound_authentication(dlg, endpoint, session_outbound_auth, NULL)) { + pjsip_dlg_terminate(dlg); + return NULL; + } + + if (pjsip_inv_create_uac(dlg, NULL, endpoint->extensions, &inv_session) != PJ_SUCCESS) { + pjsip_dlg_terminate(dlg); + return NULL; + } + + pjsip_timer_setting_default(&timer); + timer.min_se = endpoint->min_se; + timer.sess_expires = endpoint->sess_expires; + pjsip_timer_init_session(inv_session, &timer); + + if (!(session = ast_sip_session_alloc(endpoint, inv_session))) { + pjsip_inv_terminate(inv_session, 500, PJ_FALSE); + return NULL; + } + + ast_format_cap_copy(session->req_caps, req_caps); + if ((pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) || + !(offer = create_local_sdp(inv_session, session, NULL))) { + pjsip_inv_terminate(inv_session, 500, PJ_FALSE); + return NULL; + } + + pjsip_inv_set_local_sdp(inv_session, offer); + + ao2_ref(session, +1); + return session; +} + +enum sip_get_destination_result { + /*! The extension was successfully found */ + SIP_GET_DEST_EXTEN_FOUND, + /*! The extension specified in the RURI was not found */ + SIP_GET_DEST_EXTEN_NOT_FOUND, + /*! The extension specified in the RURI was a partial match */ + SIP_GET_DEST_EXTEN_PARTIAL, + /*! The RURI is of an unsupported scheme */ + SIP_GET_DEST_UNSUPPORTED_URI, +}; + +/*! + * \brief Determine where in the dialplan a call should go + * + * This uses the username in the request URI to try to match + * an extension in the endpoint's configured context in order + * to route the call. + * + * \param session The inbound SIP session + * \param rdata The SIP INVITE + */ +static enum sip_get_destination_result get_destination(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + pjsip_uri *ruri = rdata->msg_info.msg->line.req.uri; + pjsip_sip_uri *sip_ruri; + if (!PJSIP_URI_SCHEME_IS_SIP(ruri) && !PJSIP_URI_SCHEME_IS_SIPS(ruri)) { + return SIP_GET_DEST_UNSUPPORTED_URI; + } + sip_ruri = pjsip_uri_get_uri(ruri); + ast_copy_pj_str(session->exten, &sip_ruri->user, sizeof(session->exten)); + if (ast_exists_extension(NULL, session->endpoint->context, session->exten, 1, NULL)) { + return SIP_GET_DEST_EXTEN_FOUND; + } + /* XXX In reality, we'll likely have further options so that partial matches + * can be indicated here, but for getting something up and running, we're going + * to return a "not exists" error here. + */ + return SIP_GET_DEST_EXTEN_NOT_FOUND; +} + +static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct ast_sip_endpoint *endpoint) +{ + pjsip_tx_data *tdata; + pjsip_dialog *dlg; + pjsip_inv_session *inv_session; + unsigned int options = endpoint->extensions; + + if (pjsip_inv_verify_request(rdata, &options, NULL, NULL, ast_sip_get_pjsip_endpoint(), &tdata) != PJ_SUCCESS) { + if (tdata) { + pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL); + } else { + pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); + } + return NULL; + } + if (pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, NULL, &dlg) != PJ_SUCCESS) { + pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL); + return NULL; + } + if (pjsip_inv_create_uas(dlg, rdata, NULL, 0, &inv_session) != PJ_SUCCESS) { + pjsip_endpt_send_response2(ast_sip_get_pjsip_endpoint(), rdata, tdata, NULL, NULL); + pjsip_dlg_terminate(dlg); + return NULL; + } + if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) { + if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) != PJ_SUCCESS) { + pjsip_inv_terminate(inv_session, 500, PJ_FALSE); + } + pjsip_inv_send_msg(inv_session, tdata); + return NULL; + } + return inv_session; +} + +static void handle_new_invite_request(pjsip_rx_data *rdata) +{ + RAII_VAR(struct ast_sip_endpoint *, endpoint, + ast_pjsip_rdata_get_endpoint(rdata), ao2_cleanup); + pjsip_tx_data *tdata = NULL; + pjsip_inv_session *inv_session = NULL; + struct ast_sip_session *session = NULL; + pjsip_timer_setting timer; + pjsip_rdata_sdp_info *sdp_info; + pjmedia_sdp_session *local = NULL; + + ast_assert(endpoint != NULL); + + inv_session = pre_session_setup(rdata, endpoint); + if (!inv_session) { + /* pre_session_setup() returns a response on failure */ + return; + } + + session = ast_sip_session_alloc(endpoint, inv_session); + if (!session) { + if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) { + pjsip_inv_terminate(inv_session, 500, PJ_FALSE); + } else { + pjsip_inv_send_msg(inv_session, tdata); + } + return; + } + + /* From this point on, any calls to pjsip_inv_terminate have the last argument as PJ_TRUE + * so that we will be notified so we can destroy the session properly + */ + + switch (get_destination(session, rdata)) { + case SIP_GET_DEST_EXTEN_FOUND: + /* Things worked. Keep going */ + break; + case SIP_GET_DEST_UNSUPPORTED_URI: + if (pjsip_inv_initial_answer(inv_session, rdata, 416, NULL, NULL, &tdata) == PJ_SUCCESS) { + ast_sip_session_send_response(session, tdata); + } else { + pjsip_inv_terminate(inv_session, 416, PJ_TRUE); + } + return; + case SIP_GET_DEST_EXTEN_NOT_FOUND: + case SIP_GET_DEST_EXTEN_PARTIAL: + default: + if (pjsip_inv_initial_answer(inv_session, rdata, 404, NULL, NULL, &tdata) == PJ_SUCCESS) { + ast_sip_session_send_response(session, tdata); + } else { + pjsip_inv_terminate(inv_session, 404, PJ_TRUE); + } + return; + }; + + if ((sdp_info = pjsip_rdata_get_sdp_info(rdata)) && (sdp_info->sdp_err == PJ_SUCCESS) && sdp_info->sdp) { + if (handle_incoming_sdp(session, sdp_info->sdp)) { + if (pjsip_inv_initial_answer(inv_session, rdata, 488, NULL, NULL, &tdata) == PJ_SUCCESS) { + ast_sip_session_send_response(session, tdata); + } else { + pjsip_inv_terminate(inv_session, 488, PJ_TRUE); + } + return; + } + /* We are creating a local SDP which is an answer to their offer */ + local = create_local_sdp(inv_session, session, sdp_info->sdp); + } else { + /* We are creating a local SDP which is an offer */ + local = create_local_sdp(inv_session, session, NULL); + } + + /* If we were unable to create a local SDP terminate the session early, it won't go anywhere */ + if (!local) { + if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) { + ast_sip_session_send_response(session, tdata); + } else { + pjsip_inv_terminate(inv_session, 500, PJ_TRUE); + } + return; + } else { + pjsip_inv_set_local_sdp(inv_session, local); + } + + pjsip_timer_setting_default(&timer); + timer.min_se = endpoint->min_se; + timer.sess_expires = endpoint->sess_expires; + pjsip_timer_init_session(inv_session, &timer); + + /* At this point, we've verified what we can, so let's go ahead and send a 100 Trying out */ + if (pjsip_inv_initial_answer(inv_session, rdata, 100, NULL, NULL, &tdata) != PJ_SUCCESS) { + pjsip_inv_terminate(inv_session, 500, PJ_TRUE); + return; + } + ast_sip_session_send_response(session, tdata); + + handle_incoming_request(session, rdata); +} + +static int has_supplement(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + struct ast_sip_session_supplement *supplement; + struct pjsip_method *method = &rdata->msg_info.msg->line.req.method; + + if (!session) { + return PJ_FALSE; + } + + AST_LIST_TRAVERSE(&session->supplements, supplement, next) { + if (!supplement->method || !pj_strcmp2(&method->name, supplement->method)) { + return PJ_TRUE; + } + } + return PJ_FALSE; +} +/*! + * \brief Called when a new SIP request comes into PJSIP + * + * This function is called under two circumstances + * 1) An out-of-dialog request is received by PJSIP + * 2) An in-dialog request that the inv_session layer does not + * handle is received (such as an in-dialog INFO) + * + * In all cases, there is very little we actually do in this function + * 1) For requests we don't handle, we return PJ_FALSE + * 2) For new INVITEs, throw the work into the SIP threadpool to be done + * there to free up the thread(s) handling incoming requests + * 3) For in-dialog requests we handle, we defer handling them until the + * on_inv_state_change() callback instead (where we will end up putting + * them into the threadpool). + */ +static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata) +{ + pj_status_t handled = PJ_FALSE; + pjsip_dialog *dlg = pjsip_rdata_get_dlg(rdata); + pjsip_inv_session *inv_session; + + switch (rdata->msg_info.msg->line.req.method.id) { + case PJSIP_INVITE_METHOD: + if (dlg) { + ast_log(LOG_WARNING, "on_rx_request called for INVITE in mid-dialog?\n"); + break; + } + handled = PJ_TRUE; + handle_new_invite_request(rdata); + break; + default: + /* Handle other in-dialog methods if their supplements have been registered */ + handled = dlg && (inv_session = pjsip_dlg_get_inv_session(dlg)) && + has_supplement(inv_session->mod_data[session_module.id], rdata); + break; + } + + return handled; +} + +struct reschedule_reinvite_data { + struct ast_sip_session *session; + struct ast_sip_session_delayed_request *delay; +}; + +static struct reschedule_reinvite_data *reschedule_reinvite_data_alloc( + struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay) +{ + struct reschedule_reinvite_data *rrd = ast_malloc(sizeof(*rrd)); + if (!rrd) { + return NULL; + } + ao2_ref(session, +1); + rrd->session = session; + rrd->delay = delay; + return rrd; +} + +static void reschedule_reinvite_data_destroy(struct reschedule_reinvite_data *rrd) +{ + ao2_cleanup(rrd->session); + ast_free(rrd->delay); + ast_free(rrd); +} + +static int really_resend_reinvite(void *data) +{ + RAII_VAR(struct reschedule_reinvite_data *, rrd, data, reschedule_reinvite_data_destroy); + + return send_delayed_request(rrd->session, rrd->delay); +} + +static void resend_reinvite(pj_timer_heap_t *timer, pj_timer_entry *entry) +{ + struct reschedule_reinvite_data *rrd = entry->user_data; + + ast_sip_push_task(rrd->session->serializer, really_resend_reinvite, entry->user_data); +} + +static void reschedule_reinvite(struct ast_sip_session *session, ast_sip_session_response_cb on_response, pjsip_tx_data *tdata) +{ + struct ast_sip_session_delayed_request *delay = delayed_request_alloc("INVITE", + NULL, on_response, tdata); + pjsip_inv_session *inv = session->inv_session; + struct reschedule_reinvite_data *rrd = reschedule_reinvite_data_alloc(session, delay); + pj_time_val tv; + + if (!rrd || !delay) { + return; + } + + tv.sec = 0; + if (inv->role == PJSIP_ROLE_UAC) { + tv.msec = 2100 + ast_random() % 2000; + } else { + tv.msec = ast_random() % 2000; + } + + pj_timer_entry_init(&session->rescheduled_reinvite, 0, rrd, resend_reinvite); + + pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &session->rescheduled_reinvite, &tv); +} + +static void __print_debug_details(const char *function, pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) +{ + struct ast_sip_session *session; + ast_debug(5, "Function %s called on event %s\n", function, pjsip_event_str(e->type)); + if (!inv) { + ast_debug(5, "Transaction %p does not belong to an inv_session?\n", tsx); + ast_debug(5, "The transaction state is %s\n", pjsip_tsx_state_str(tsx->state)); + return; + } + session = inv->mod_data[session_module.id]; + if (!session) { + ast_debug(5, "inv_session %p has no ast session\n", inv); + } else { + ast_debug(5, "The state change pertains to the session with %s\n", + ast_sorcery_object_get_id(session->endpoint)); + } + if (inv->invite_tsx) { + ast_debug(5, "The inv session still has an invite_tsx (%p)\n", inv->invite_tsx); + } else { + ast_debug(5, "The inv session does NOT have an invite_tsx\n"); + } + if (tsx) { + ast_debug(5, "The transaction involved in this state change is %p\n", tsx); + ast_debug(5, "The current transaction state is %s\n", pjsip_tsx_state_str(tsx->state)); + ast_debug(5, "The transaction state change event is %s\n", pjsip_event_str(e->body.tsx_state.type)); + } else { + ast_debug(5, "There is no transaction involved in this state change\n"); + } + ast_debug(5, "The current inv state is %s\n", pjsip_inv_state_name(inv->state)); +} + +#define print_debug_details(inv, tsx, e) __print_debug_details(__PRETTY_FUNCTION__, (inv), (tsx), (e)) + +static void handle_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + struct ast_sip_session_supplement *supplement; + struct pjsip_request_line req = rdata->msg_info.msg->line.req; + + ast_debug(3, "Method is %.*s\n", (int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name)); + AST_LIST_TRAVERSE(&session->supplements, supplement, next) { + if (supplement->incoming_request && ( + !supplement->method || !pj_strcmp2(&req.method.name, supplement->method))) { + supplement->incoming_request(session, rdata); + } + } +} + +static void handle_incoming_response(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + struct ast_sip_session_supplement *supplement; + struct pjsip_status_line status = rdata->msg_info.msg->line.status; + + ast_debug(3, "Response is %d %.*s\n", status.code, (int) pj_strlen(&status.reason), + pj_strbuf(&status.reason)); + + AST_LIST_TRAVERSE(&session->supplements, supplement, next) { + if (supplement->incoming_response && ( + !supplement->method || !pj_strcmp2(&rdata->msg_info.cseq->method.name, supplement->method))) { + supplement->incoming_response(session, rdata); + } + } +} + +static int handle_incoming(struct ast_sip_session *session, pjsip_rx_data *rdata) +{ + ast_debug(3, "Received %s\n", rdata->msg_info.msg->type == PJSIP_REQUEST_MSG ? + "request" : "response"); + + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) { + handle_incoming_request(session, rdata); + } else { + handle_incoming_response(session, rdata); + } + + return 0; +} + +static void handle_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + struct ast_sip_session_supplement *supplement; + struct pjsip_request_line req = tdata->msg->line.req; + + ast_debug(3, "Method is %.*s\n", (int) pj_strlen(&req.method.name), pj_strbuf(&req.method.name)); + AST_LIST_TRAVERSE(&session->supplements, supplement, next) { + if (supplement->outgoing_request && + (!supplement->method || !pj_strcmp2(&req.method.name, supplement->method))) { + supplement->outgoing_request(session, tdata); + } + } +} + +static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + struct ast_sip_session_supplement *supplement; + struct pjsip_status_line status = tdata->msg->line.status; + ast_debug(3, "Response is %d %.*s\n", status.code, (int) pj_strlen(&status.reason), + pj_strbuf(&status.reason)); + + AST_LIST_TRAVERSE(&session->supplements, supplement, next) { + /* XXX Not sure how to get the method from a response. + * For now, just call supplements on all responses, no + * matter the method. This is less than ideal + */ + if (supplement->outgoing_response) { + supplement->outgoing_response(session, tdata); + } + } +} + +static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdata) +{ + ast_debug(3, "Sending %s\n", tdata->msg->type == PJSIP_REQUEST_MSG ? + "request" : "response"); + if (tdata->msg->type == PJSIP_REQUEST_MSG) { + handle_outgoing_request(session, tdata); + } else { + handle_outgoing_response(session, tdata); + } +} + +static int session_end(struct ast_sip_session *session) +{ + struct ast_sip_session_supplement *iter; + + /* Session is dead. Let's get rid of the reference to the session */ + AST_LIST_TRAVERSE(&session->supplements, iter, next) { + if (iter->session_end) { + iter->session_end(session); + } + } + + session->inv_session->mod_data[session_module.id] = NULL; + ast_sip_dialog_set_serializer(session->inv_session->dlg, NULL); + ast_sip_dialog_set_endpoint(session->inv_session->dlg, NULL); + ao2_cleanup(session); + return 0; +} + +static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) +{ + struct ast_sip_session *session = inv->mod_data[session_module.id]; + + print_debug_details(inv, NULL, e); + + switch(e->type) { + case PJSIP_EVENT_TX_MSG: + handle_outgoing(session, e->body.tx_msg.tdata); + break; + case PJSIP_EVENT_RX_MSG: + handle_incoming(session, e->body.rx_msg.rdata); + break; + case PJSIP_EVENT_TSX_STATE: + ast_debug(3, "Source of transaction state change is %s\n", pjsip_event_str(e->body.tsx_state.type)); + /* Transaction state changes are prompted by some other underlying event. */ + switch(e->body.tsx_state.type) { + case PJSIP_EVENT_TX_MSG: + handle_outgoing(session, e->body.tsx_state.src.tdata); + break; + case PJSIP_EVENT_RX_MSG: + handle_incoming(session, e->body.tsx_state.src.rdata); + break; + case PJSIP_EVENT_TRANSPORT_ERROR: + case PJSIP_EVENT_TIMER: + case PJSIP_EVENT_USER: + case PJSIP_EVENT_UNKNOWN: + case PJSIP_EVENT_TSX_STATE: + /* Inception? */ + break; + } + break; + case PJSIP_EVENT_TRANSPORT_ERROR: + case PJSIP_EVENT_TIMER: + case PJSIP_EVENT_UNKNOWN: + case PJSIP_EVENT_USER: + default: + break; + } + + if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { + session_end(session); + } +} + +static void session_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e) +{ + /* XXX STUB */ +} + +static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e) +{ + struct ast_sip_session *session = inv->mod_data[session_module.id]; + print_debug_details(inv, tsx, e); + if (!session) { + /* Transaction likely timed out after the call was hung up. Just + * ignore such transaction changes + */ + return; + } + switch (e->body.tsx_state.type) { + case PJSIP_EVENT_TX_MSG: + /* When we create an outgoing request, we do not have access to the transaction that + * is created. Instead, We have to place transaction-specific data in the tdata. Here, + * we transfer the data into the transaction. This way, when we receive a response, we + * can dig this data out again + */ + tsx->mod_data[session_module.id] = e->body.tsx_state.src.tdata->mod_data[session_module.id]; + break; + case PJSIP_EVENT_RX_MSG: + if (tsx->method.id == PJSIP_INVITE_METHOD) { + if (tsx->role == PJSIP_ROLE_UAC && tsx->state == PJSIP_TSX_STATE_COMPLETED) { + /* This means we got a non 2XX final response to our outgoing INVITE */ + if (tsx->status_code == PJSIP_SC_REQUEST_PENDING) { + reschedule_reinvite(session, tsx->mod_data[session_module.id], tsx->last_tx); + return; + } else { + /* Other failures result in destroying the session. */ + pjsip_tx_data *tdata; + pjsip_inv_end_session(inv, 500, NULL, &tdata); + ast_sip_session_send_request(session, tdata); + } + } + } else { + if (tsx->role == PJSIP_ROLE_UAS && tsx->state == PJSIP_TSX_STATE_TRYING) { + handle_incoming_request(session, e->body.tsx_state.src.rdata); + } + } + if (tsx->mod_data[session_module.id]) { + ast_sip_session_response_cb cb = tsx->mod_data[session_module.id]; + cb(session, e->body.tsx_state.src.rdata); + } + case PJSIP_EVENT_TRANSPORT_ERROR: + case PJSIP_EVENT_TIMER: + case PJSIP_EVENT_USER: + case PJSIP_EVENT_UNKNOWN: + case PJSIP_EVENT_TSX_STATE: + /* Inception? */ + break; + } + + /* Terminated INVITE transactions always should result in queuing delayed requests, + * no matter what event caused the transaction to terminate + */ + if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->state == PJSIP_TSX_STATE_TERMINATED) { + queue_delayed_request(session); + } +} + +static int add_sdp_streams(void *obj, void *arg, void *data, int flags) +{ + struct ast_sip_session_media *session_media = obj; + pjmedia_sdp_session *answer = arg; + struct ast_sip_session *session = data; + struct ast_sip_session_sdp_handler *handler = session_media->handler; + RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup); + + if (handler) { + /* if an already assigned handler does not handle the session_media or reports a catastrophic error, fail */ + if (handler->create_outgoing_sdp_stream(session, session_media, answer) <= 0) { + return 0; + } + return CMP_MATCH; + } + + handler_list = ao2_find(sdp_handlers, session_media->stream_type, OBJ_KEY); + if (!handler_list) { + return CMP_MATCH; + } + + /* no handler for this stream type and we have a list to search */ + AST_LIST_TRAVERSE(&handler_list->list, handler, next) { + int res = handler->create_outgoing_sdp_stream(session, session_media, answer); + if (res < 0) { + /* catastrophic error */ + return 0; + } + if (res > 0) { + /* handled */ + return CMP_MATCH; + } + } + + /* streams that weren't handled won't be included in generated outbound SDP */ + return CMP_MATCH; +} + +static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer) +{ + RAII_VAR(struct ao2_iterator *, successful, NULL, ao2_iterator_cleanup); + static const pj_str_t STR_ASTERISK = { "Asterisk", 8 }; + static const pj_str_t STR_IN = { "IN", 2 }; + static const pj_str_t STR_IP4 = { "IP4", 3 }; + static const pj_str_t STR_IP6 = { "IP6", 3 }; + pjmedia_sdp_session *local; + + if (!(local = PJ_POOL_ZALLOC_T(inv->pool_prov, pjmedia_sdp_session))) { + return NULL; + } + + if (!offer) { + local->origin.version = local->origin.id = (pj_uint32_t)(ast_random()); + } else { + local->origin.version = offer->origin.version + 1; + local->origin.id = offer->origin.id; + } + + local->origin.user = STR_ASTERISK; + local->origin.net_type = STR_IN; + local->origin.addr_type = session->endpoint->rtp_ipv6 ? STR_IP6 : STR_IP4; + local->origin.addr = *pj_gethostname(); + local->name = local->origin.user; + + /* Now let the handlers add streams of various types, pjmedia will automatically reorder the media streams for us */ + successful = ao2_callback_data(session->media, OBJ_MULTIPLE, add_sdp_streams, local, session); + if (!successful || ao2_container_count(successful->c) != ao2_container_count(session->media)) { + /* Something experienced a catastrophic failure */ + return NULL; + } + + /* Use the connection details of the first media stream if possible for SDP level */ + if (local->media_count) { + local->conn = local->media[0]->conn; + } + + return local; +} + +static void session_inv_on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer) +{ + struct ast_sip_session *session = inv->mod_data[session_module.id]; + pjmedia_sdp_session *answer; + + if (handle_incoming_sdp(session, offer)) { + return; + } + + if ((answer = create_local_sdp(inv, session, offer))) { + pjsip_inv_set_sdp_answer(inv, answer); + } +} + +#if 0 +static void session_inv_on_create_offer(pjsip_inv_session *inv, pjmedia_sdp_session **p_offer) +{ + /* XXX STUB */ +} +#endif + +static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status) +{ + struct ast_sip_session *session = inv->mod_data[session_module.id]; + const pjmedia_sdp_session *local, *remote; + + if (!session->channel) { + /* If we don't have a channel. We really don't care about media updates. + * Just ignore + */ + return; + } + + if ((status != PJ_SUCCESS) || (pjmedia_sdp_neg_get_active_local(inv->neg, &local) != PJ_SUCCESS) || + (pjmedia_sdp_neg_get_active_remote(inv->neg, &remote) != PJ_SUCCESS)) { + ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL); + ast_queue_hangup(session->channel); + return; + } + + handle_negotiated_sdp(session, local, remote); +} + +static pjsip_redirect_op session_inv_on_redirected(pjsip_inv_session *inv, const pjsip_uri *target, const pjsip_event *e) +{ + /* XXX STUB */ + return PJSIP_REDIRECT_REJECT; +} + +static pjsip_inv_callback inv_callback = { + .on_state_changed = session_inv_on_state_changed, + .on_new_session = session_inv_on_new_session, + .on_tsx_state_changed = session_inv_on_tsx_state_changed, + .on_rx_offer = session_inv_on_rx_offer, + .on_media_update = session_inv_on_media_update, + .on_redirected = session_inv_on_redirected, +}; + +/*! \brief Hook for modifying outgoing messages with SDP to contain the proper address information */ +static void session_outgoing_nat_hook(pjsip_tx_data *tdata, struct ast_sip_transport *transport) +{ + struct ast_sip_nat_hook *hook = tdata->mod_data[session_module.id]; + struct pjmedia_sdp_session *sdp; + int stream; + + /* SDP produced by us directly will never be multipart */ + if (hook || !tdata->msg->body || pj_stricmp2(&tdata->msg->body->content_type.type, "application") || + pj_stricmp2(&tdata->msg->body->content_type.subtype, "sdp") || ast_strlen_zero(transport->external_media_address)) { + return; + } + + sdp = tdata->msg->body->data; + + for (stream = 0; stream < sdp->media_count; ++stream) { + /* See if there are registered handlers for this media stream type */ + char media[20]; + struct ast_sip_session_sdp_handler *handler; + RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup); + + /* We need a null-terminated version of the media string */ + ast_copy_pj_str(media, &sdp->media[stream]->desc.media, sizeof(media)); + + handler_list = ao2_find(sdp_handlers, media, OBJ_KEY); + if (!handler_list) { + ast_debug(1, "No registered SDP handlers for media type '%s'\n", media); + continue; + } + AST_LIST_TRAVERSE(&handler_list->list, handler, next) { + if (handler->change_outgoing_sdp_stream_media_address) { + handler->change_outgoing_sdp_stream_media_address(tdata, sdp->media[stream], transport); + } + } + } + + /* We purposely do this so that the hook will not be invoked multiple times, ie: if a retransmit occurs */ + tdata->mod_data[session_module.id] = nat_hook; +} + +static int load_module(void) +{ + pjsip_endpoint *endpt; + if (!ast_sip_get_sorcery() || !ast_sip_get_pjsip_endpoint()) { + return AST_MODULE_LOAD_DECLINE; + } + if (!(nat_hook = ast_sorcery_alloc(ast_sip_get_sorcery(), "nat_hook", NULL))) { + return AST_MODULE_LOAD_DECLINE; + } + nat_hook->outgoing_external_message = session_outgoing_nat_hook; + ast_sorcery_create(ast_sip_get_sorcery(), nat_hook); + sdp_handlers = ao2_container_alloc(SDP_HANDLER_BUCKETS, + sdp_handler_list_hash, sdp_handler_list_cmp); + if (!sdp_handlers) { + return AST_MODULE_LOAD_DECLINE; + } + endpt = ast_sip_get_pjsip_endpoint(); + pjsip_inv_usage_init(endpt, &inv_callback); + pjsip_100rel_init_module(endpt); + pjsip_timer_init_module(endpt); + if (ast_sip_register_service(&session_module)) { + return AST_MODULE_LOAD_DECLINE; + } + return AST_MODULE_LOAD_SUCCESS; +} + +static int unload_module(void) +{ + ast_sip_unregister_service(&session_module); + if (nat_hook) { + ast_sorcery_delete(ast_sip_get_sorcery(), nat_hook); + nat_hook = NULL; + } + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "SIP Session resource", + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_APP_DEPEND, + ); diff --git a/res/res_sip_session.exports.in b/res/res_sip_session.exports.in new file mode 100644 index 000000000..08c6f3937 --- /dev/null +++ b/res/res_sip_session.exports.in @@ -0,0 +1,18 @@ +{ + global: + LINKER_SYMBOL_PREFIXast_sip_session_register_sdp_handler; + LINKER_SYMBOL_PREFIXast_sip_session_unregister_sdp_handler; + LINKER_SYMBOL_PREFIXast_sip_session_register_supplement; + LINKER_SYMBOL_PREFIXast_sip_session_unregister_supplement; + LINKER_SYMBOL_PREFIXast_sip_session_alloc_datastore; + LINKER_SYMBOL_PREFIXast_sip_session_add_datastore; + LINKER_SYMBOL_PREFIXast_sip_session_get_datastore; + LINKER_SYMBOL_PREFIXast_sip_session_remove_datastore; + LINKER_SYMBOL_PREFIXast_sip_session_get_identity; + LINKER_SYMBOL_PREFIXast_sip_session_refresh; + LINKER_SYMBOL_PREFIXast_sip_session_send_response; + LINKER_SYMBOL_PREFIXast_sip_session_send_request; + LINKER_SYMBOL_PREFIXast_sip_session_create_outgoing; + local: + *; +}; diff --git a/res/res_sorcery_config.c b/res/res_sorcery_config.c index 509538f5a..c6ec62ed6 100644 --- a/res/res_sorcery_config.c +++ b/res/res_sorcery_config.c @@ -199,7 +199,6 @@ static void sorcery_config_retrieve_multiple(const struct ast_sorcery *sorcery, if (!config_objects) { return; } - ao2_callback(config_objects, 0, sorcery_config_fields_cmp, ¶ms); } diff --git a/tests/test_sorcery.c b/tests/test_sorcery.c index 3877edc62..6cfd2de4f 100644 --- a/tests/test_sorcery.c +++ b/tests/test_sorcery.c @@ -130,9 +130,10 @@ struct sorcery_test_caching { static int apply_handler_called; /*! \brief Simple apply handler which sets global scope integer to 1 if called */ -static void test_apply_handler(const struct ast_sorcery *sorcery, void *obj) +static int test_apply_handler(const struct ast_sorcery *sorcery, void *obj) { apply_handler_called = 1; + return 0; } /*! \brief Global scope caching structure for testing */ |