Merge "res_odbc_transaction: fix some format tab"
authorzuul <zuul@gerrit.asterisk.org>
Tue, 8 Mar 2016 17:12:37 +0000 (11:12 -0600)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Tue, 8 Mar 2016 17:12:38 +0000 (11:12 -0600)
100 files changed:
CHANGES
Makefile
UPGRADE.txt
apps/app_queue.c
channels/chan_sip.c
configs/samples/pjproject.conf.sample [new file with mode: 0644]
configs/samples/pjsip.conf.sample
configs/samples/sip.conf.sample
configure
configure.ac
contrib/ast-db-manage/config/env.py
contrib/ast-db-manage/config/versions/10aedae86a32_add_outgoing_enum_va.py
contrib/ast-db-manage/config/versions/136885b81223_add_regcontext_to_pj.py
contrib/ast-db-manage/config/versions/154177371065_add_default_from_user.py
contrib/ast-db-manage/config/versions/15b1430ad6f1_add_moh_passthrough_option_to_pjsip.py
contrib/ast-db-manage/config/versions/1758e8bbf6b_increase_useragent_column_size.py
contrib/ast-db-manage/config/versions/189a235b3fd7_add_keep_alive_interval.py
contrib/ast-db-manage/config/versions/1d50859ed02e_create_accountcode.py
contrib/ast-db-manage/config/versions/21e526ad3040_add_pjsip_debug_option.py
contrib/ast-db-manage/config/versions/23530d604b96_add_rpid_immediate.py
contrib/ast-db-manage/config/versions/26d7f3bf0fa5_add_bind_rtp_to_media_address_to_pjsip.py
contrib/ast-db-manage/config/versions/26f10cadc157_add_pjsip_timeout_options.py
contrib/ast-db-manage/config/versions/28b8e71e541f_add_g726_non_standard.py
contrib/ast-db-manage/config/versions/28ce1e718f05_add_fatal_response_interval.py
contrib/ast-db-manage/config/versions/2d078ec071b7_increaes_contact_column_size.py
contrib/ast-db-manage/config/versions/2fc7930b41b3_add_pjsip_endpoint_options_for_12_1.py
contrib/ast-db-manage/config/versions/31cd4f4891ec_add_auto_dtmf_mode.py
contrib/ast-db-manage/config/versions/339a3bdf53fc_expand_accountcode_to_80.py
contrib/ast-db-manage/config/versions/371a3bf4143e_add_user_eq_phone_option_to_pjsip.py
contrib/ast-db-manage/config/versions/3855ee4e5f85_add_missing_pjsip_options.py
contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py [new file with mode: 0644]
contrib/ast-db-manage/config/versions/423f34ad36e2_fix_pjsip_qualify_ti.py
contrib/ast-db-manage/config/versions/43956d550a44_add_tables_for_pjsip.py
contrib/ast-db-manage/config/versions/45e3f47c6c44_add_pjsip_endpoint_identifier_order.py
contrib/ast-db-manage/config/versions/461d7d691209_add_pjsip_qualify_timeout.py
contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py
contrib/ast-db-manage/config/versions/4c573e7135bd_fix_tos_field_types.py
contrib/ast-db-manage/config/versions/4da0c5f79a9c_create_tables.py
contrib/ast-db-manage/config/versions/5139253c0423_make_q_member_uniqueid_autoinc.py
contrib/ast-db-manage/config/versions/51f8cb66540e_add_further_dtls_options.py
contrib/ast-db-manage/config/versions/5950038a6ead_fix_pjsip_verifiy_typo.py
contrib/ast-db-manage/config/versions/945b1098bdd_add_media_encryption_optimistic_to_pjsip.py
contrib/ast-db-manage/config/versions/a541e0b5e89_add_pjsip_max_initial_qualify_time.py
contrib/ast-db-manage/config/versions/dbc44d5a908_add_missing_columns_to_sys_and_reg.py
contrib/ast-db-manage/config/versions/e96a0b8071c_increase_pjsip_column_size.py
funcs/func_callerid.c
include/asterisk/_private.h
include/asterisk/autoconfig.h.in
include/asterisk/bridge_channel.h
include/asterisk/bridge_channel_internal.h
include/asterisk/channel.h
include/asterisk/core_local.h
include/asterisk/res_pjsip.h
include/asterisk/rtp_engine.h
main/.gitignore
main/Makefile
main/asterisk.c
main/bridge.c
main/bridge_channel.c
main/callerid.c
main/cel.c
main/channel.c
main/channel_internal_api.c
main/core_local.c
main/libasteriskpj.c [new file with mode: 0644]
main/loader.c
main/message.c
main/sorcery.c
makeopts.in
res/res_config_sqlite3.c
res/res_pjproject.c
res/res_pjsip.c
res/res_pjsip/config_transport.c
res/res_pjsip_config_wizard.c
res/res_pjsip_diversion.c
res/res_pjsip_dtmf_info.c
res/res_pjsip_messaging.c
res/res_pjsip_mwi.c
res/res_pjsip_outbound_publish.c
res/res_pjsip_outbound_registration.c
res/res_pjsip_pubsub.c
res/res_pjsip_send_to_voicemail.c
res/res_pjsip_session.c
res/res_pjsip_t38.c
res/res_sorcery_memory_cache.c
tests/test_threadpool.c
third-party/Makefile [new file with mode: 0644]
third-party/Makefile.rules [new file with mode: 0644]
third-party/pjproject/.gitignore [new file with mode: 0644]
third-party/pjproject/Makefile [new file with mode: 0644]
third-party/pjproject/Makefile.rules [new file with mode: 0644]
third-party/pjproject/apply_patches [new file with mode: 0755]
third-party/pjproject/configure.m4 [new file with mode: 0644]
third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch [new file with mode: 0644]
third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch [new file with mode: 0644]
third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch [new file with mode: 0644]
third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch [new file with mode: 0644]
third-party/pjproject/patches/config_site.h [new file with mode: 0644]
third-party/pjproject/patches/user.mak [new file with mode: 0644]
third-party/versions.mak [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 5e4c428..7c04285 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -81,6 +81,11 @@ chan_sip
  * DTLS related configuration options can now be set at a general level.
    Enabling DTLS support, though, requires enabling it at the user
    or peer level.
+ * Added the possibility to set the From: header through the the SIP dial
+   string (populating the fromuser/fromdomain fields), complementing the
+   [!dnid] option for the To: header that has existed since 1.6.0 (1d6b192).
+   NOTE: This is again separated by an exclamation mark, so the To: header may
+   not contain one of those.
 
 chan_pjsip
 ------------------
@@ -210,6 +215,30 @@ Queue
 --- Functionality changes from Asterisk 13.7.0 to Asterisk 13.8.0 ------------
 ------------------------------------------------------------------------------
 
+res_pjsip_config_wizard
+------------------
+ * A new command (pjsip export config_wizard primitives) has been added that
+   will export all the pjsip objects it created to the console or a file
+   suitable for reuse in a pjsip.conf file.
+
+Build System
+------------------
+ * To help insure that Asterisk is compiled and run with the same known
+   version of pjproject, a new option (--with-pjproject-bundled) has been
+   added to ./configure.  When specified, the version of pjproject specified
+   in third-party/versions.mak will be downloaded and configured.  When you
+   make Asterisk, the build process will also automatically build pjproject
+   and Asterisk will be statically linked to it.  Once a particular version
+   of pjproject is configured and built, it won't be configured or built
+   again unless you run a 'make distclean'.
+
+   To facilitate testing, when 'make install' is run, the pjsua and pjsystest
+   utilities and the pjproject python bindings will be installed in
+   ASTDATADIR/third-party/pjproject.
+
+   The default behavior remains building with the shared pjproject
+   installation, if any.
+
 app_confbridge
 ------------------
  * Added CONFBRIDGE_INFO(muted,) for querying the muted conference state.
@@ -221,6 +250,36 @@ app_confbridge
    conference state and made the locked column a yes/no value instead of a
    locked/unlocked value.
 
+REDIRECTING(reason)
+------------------
+ * The REDIRECTING(reason) value is now treated consistently between
+   chan_sip and chan_pjsip.
+
+   Both channel drivers match incoming reason values with values documented
+   by REDIRECTING(reason) and values documented by RFC5806 regardless of
+   whether they are quoted or not.  RFC5806 values are mapped to the
+   equivalent REDIRECTING(reason) documented value and is set in
+   REDIRECTING(reason).  e.g., an incoming RFC5806 'unconditional' value or a
+   quoted string version ('"unconditional"') is converted to
+   REDIRECTING(reason)'s 'cfu' value.  The user's dialplan only needs to deal
+   with 'cfu' instead of any of the aliases.
+
+   The incoming 480 response reason text supported by chan_sip checks for
+   known reason values and if not matched then puts quotes around the reason
+   string and assigns that to REDIRECTING(reason).
+
+   Both channel drivers send outgoing known REDIRECTING(reason) values as the
+   unquoted RFC5806 equivalent.  User custom values are either sent as is or
+   with added quotes if SIP doesn't allow a character within the value as
+   part of a RFC3261 Section 25.1 token.  Note that there are still
+   limitations on what characters can be put in a custom user value.  e.g.,
+   embedding quotes in the middle of the reason string is just going to cause
+   you grief.
+
+ * Setting a REDIRECTING(reason) value now recognizes RFC5806 aliases.
+   e.g., Setting REDIRECTING(reason) to 'unconditional' is converted to the
+   'cfu' value.
+
 res_pjproject
 ------------------
  * This module is the successor of res_pjsip_log_forwarder.  As well as
@@ -229,8 +288,23 @@ res_pjproject
    This displays the compiled-in options of the pjproject installation
    Asterisk is currently running against.
 
+ * Another feature of this module is the ability to map pjproject log levels
+   to Asterisk log levels, or to suppress the pjproject log messages
+   altogether.  Many of the messages emitted by pjproject itself are the result
+   of errors which Asterisk will ultimately handle so the messages can be
+   misleading or just noise.  A new config file (pjproject.conf) has been added
+   to configure the mapping and a new CLI command (pjproject show log mappings)
+   has been added to display the mappings currently in use.
+
 res_pjsip
 ------------------
+ * Transports are now reloadable.  In testing, no in-progress calls were
+   disrupted if the ip address or port weren't changed, but the possibility
+   still exists.  To make sure there are no unintentional drops, a new option
+   'allow_reload', which defaults to 'no' has been added to transport.  If
+   left at the default, changes to the particular transport will be ignored.
+   If set to 'yes', changes (if any) will be applied.
+
  * Added new global option (regcontext) to pjsip. When set, Asterisk will
    dynamically create and destroy a NoOp priority 1 extension
    for a given endpoint who registers or unregisters with us.
index a07a329..7528f83 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -249,7 +249,7 @@ endif
 
 _ASTCFLAGS+=$(OPTIONS)
 
-MOD_SUBDIRS:=channels pbx apps codecs formats cdr cel bridges funcs tests main res addons $(LOCAL_MOD_SUBDIRS)
+MOD_SUBDIRS:=third-party channels pbx apps codecs formats cdr cel bridges funcs tests main res addons $(LOCAL_MOD_SUBDIRS)
 OTHER_SUBDIRS:=utils agi contrib
 SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS)
 SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install)
@@ -377,8 +377,10 @@ ifeq ($(findstring $(OSARCH), mingw32 cygwin ),)
     # directories containing them must be completed before the main Asterisk
     # binary can be built.
     # If MENUSELECT_EMBED is empty, we don't need this and allow 'main' to be
-    # be built without building all dependencies first.
+    # be built with only third_party first.
 main: $(filter-out main,$(MOD_SUBDIRS))
+  else
+main: third-party
   endif
 else
     # Windows: we need to build main (i.e. the asterisk dll) first,
@@ -568,7 +570,8 @@ INSTALLDIRS="$(ASTLIBDIR)" "$(ASTMODDIR)" "$(ASTSBINDIR)" "$(ASTETCDIR)" "$(ASTV
        "$(ASTDATADIR)/documentation/thirdparty" "$(ASTDATADIR)/firmware" \
        "$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
        "$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
-       "$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)"
+       "$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)" \
+       "$(ASTDATADIR)/third-party"
 
 installdirs:
        @for i in $(INSTALLDIRS); do \
@@ -611,7 +614,7 @@ ifeq ($(HAVE_DAHDI),1)
 endif
 
 $(SUBDIRS_INSTALL):
-       +@DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" $(SUBMAKE) -C $(@:-install=) install
+       +@DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" ASTDATADIR="$(ASTDATADIR)" $(SUBMAKE) -C $(@:-install=) install
 
 NEWMODS:=$(foreach d,$(MOD_SUBDIRS),$(notdir $(wildcard $(d)/*.so)))
 OLDMODS=$(filter-out $(NEWMODS) $(notdir $(DESTDIR)$(ASTMODDIR)),$(notdir $(wildcard $(DESTDIR)$(ASTMODDIR)/*.so)))
@@ -889,7 +892,7 @@ sounds:
        @[ -f "$(DESTDIR)$(ASTDBDIR)/astdb.sqlite3" ] || [ ! -f "$(DESTDIR)$(ASTDBDIR)/astdb" ] || [ ! -f menuselect.makeopts ] || grep -q MENUSELECT_UTILS=.*astdb2sqlite3 menuselect.makeopts || (sed -i.orig -e's/MENUSELECT_UTILS=\(.*\)/MENUSELECT_UTILS=\1 astdb2sqlite3/' menuselect.makeopts && echo "Updating menuselect.makeopts to include astdb2sqlite3" && echo "Original version backed up to menuselect.makeopts.orig")
 
 $(SUBDIRS_UNINSTALL):
-       +@$(SUBMAKE) -C $(@:-uninstall=) uninstall
+       +@DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" ASTDATADIR="$(ASTDATADIR)" $(SUBMAKE) -C $(@:-uninstall=) uninstall
 
 main-binuninstall:
        +@DESTDIR="$(DESTDIR)" ASTSBINDIR="$(ASTSBINDIR)" ASTLIBDIR="$(ASTLIBDIR)" $(SUBMAKE) -C main binuninstall
index 6fb82c4..131ce6c 100644 (file)
@@ -31,6 +31,11 @@ chan_dahdi:
    ring-ring-ring pattern would exceed the pattern limits and stop
    Caller-ID detection.
 
+chan_sip:
+ - The SIP dial string has been extended past the [!dnid] option by another
+   exclamation mark: [!dnid[!fromuri].  An exclamation mark in the To-URI
+   will now mean changes to the From-URI.
+
 Core:
  - The REF_DEBUG compiler flag is now used to enable refdebug by default.
    The setting can be overridden in asterisk.conf by setting refdebug in
index 15f32fa..776ada4 100644 (file)
@@ -5441,9 +5441,13 @@ static int update_queue(struct call_queue *q, struct member *member, int callcom
        if (callcompletedinsl) {
                q->callscompletedinsl++;
        }
-       /* Calculate talktime using the same exponential average as holdtime code*/
-       oldtalktime = q->talktime;
-       q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
+       if (q->callscompletedinsl == 1) {
+               q->talktime = newtalktime;
+       } else {
+               /* Calculate talktime using the same exponential average as holdtime code */
+               oldtalktime = q->talktime;
+               q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
+       }
        ao2_unlock(q);
        return 0;
 }
index aaf0b6d..2d5843d 100644 (file)
@@ -710,7 +710,7 @@ static const struct  cfsip_methods {
  */
 static const struct sip_reasons {
        enum AST_REDIRECTING_REASON code;
-       char * const text;
+       const char *text;
 } sip_reason_table[] = {
        { AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
        { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
@@ -723,8 +723,8 @@ static const struct sip_reasons {
        { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
        { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
        { AST_REDIRECTING_REASON_AWAY, "away" },
-       { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"},
-       { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"},
+       { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" },              /* Non-standard */
+       { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" },    /* Non-standard */
 };
 
 
@@ -2375,52 +2375,54 @@ static int map_s_x(const struct _map_x_s *table, const char *s, int errorvalue)
        return errorvalue;
 }
 
-static enum AST_REDIRECTING_REASON sip_reason_str_to_code(const char *text)
+/*!
+ * \internal
+ * \brief Determine if the given string is a SIP token.
+ * \since 13.8.0
+ *
+ * \param str String to determine if is a SIP token.
+ *
+ * \note A token is defined by RFC3261 Section 25.1
+ *
+ * \return Non-zero if the string is a SIP token.
+ */
+static int sip_is_token(const char *str)
 {
-       enum AST_REDIRECTING_REASON ast = AST_REDIRECTING_REASON_UNKNOWN;
-       int i;
+       int is_token;
+
+       if (ast_strlen_zero(str)) {
+               /* An empty string is not a token. */
+               return 0;
+       }
 
-       for (i = 0; i < ARRAY_LEN(sip_reason_table); ++i) {
-               if (!strcasecmp(text, sip_reason_table[i].text)) {
-                       ast = sip_reason_table[i].code;
+       is_token = 1;
+       do {
+               if (!isalnum(*str)
+                       && !strchr("-.!%*_+`'~", *str)) {
+                       /* The character is not allowed in a token. */
+                       is_token = 0;
                        break;
                }
-       }
+       } while (*++str);
 
-       return ast;
+       return is_token;
 }
 
-static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason, int *table_lookup)
+static const char *sip_reason_code_to_str(struct ast_party_redirecting_reason *reason)
 {
-       int code = reason->code;
+       int idx;
+       int code;
 
-       /* If there's a specific string set, then we just
-        * use it.
-        */
+       /* use specific string if given */
        if (!ast_strlen_zero(reason->str)) {
-               /* If we care about whether this can be found in
-                * the table, then we need to check about that.
-                */
-               if (table_lookup) {
-                       /* If the string is literally "unknown" then don't bother with the lookup
-                        * because it can lead to a false negative.
-                        */
-                       if (!strcasecmp(reason->str, "unknown") ||
-                                       sip_reason_str_to_code(reason->str) != AST_REDIRECTING_REASON_UNKNOWN) {
-                               *table_lookup = TRUE;
-                       } else {
-                               *table_lookup = FALSE;
-                       }
-               }
                return reason->str;
        }
 
-       if (table_lookup) {
-               *table_lookup = TRUE;
-       }
-
-       if (code >= 0 && code < ARRAY_LEN(sip_reason_table)) {
-               return sip_reason_table[code].text;
+       code = reason->code;
+       for (idx = 0; idx < ARRAY_LEN(sip_reason_table); ++idx) {
+               if (code == sip_reason_table[idx].code) {
+                       return sip_reason_table[idx].text;
+               }
        }
 
        return "unknown";
@@ -7522,8 +7524,10 @@ static int interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_
                        AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
                        change_t38_state(p, T38_REJECTED);
                        transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
-               } else if (p->t38.state == T38_ENABLED)
+               } else if (p->t38.state == T38_ENABLED) {
+                       change_t38_state(p, T38_DISABLED);
                        transmit_reinvite_with_sdp(p, FALSE, FALSE);
+               }
                break;
        case AST_T38_REQUEST_PARMS: {           /* Application wants remote's parameters re-sent */
                struct ast_control_t38_parameters parameters = p->t38.their_parms;
@@ -10677,6 +10681,10 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                } else if (udptlportno > 0) {
                        if (debug)
                                ast_verbose("Got T.38 Re-invite without audio. Keeping RTP active during T.38 session.\n");
+
+                       /* Force media to go through us for T.38. */
+                       memset(&p->redirip, 0, sizeof(p->redirip));
+
                        /* Prevent audio RTCP reads */
                        if (p->owner) {
                                ast_channel_set_fd(p->owner, 1, -1);
@@ -13447,7 +13455,7 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
 
                ast_str_append(&m_modem, 0, "m=image %d udptl t38\r\n", ast_sockaddr_port(&udptldest));
 
-               if (ast_sockaddr_cmp(&udptldest, &dest)) {
+               if (ast_sockaddr_cmp_addr(&udptldest, &dest)) {
                        ast_str_append(&m_modem, 0, "c=IN %s %s\r\n",
                                        (ast_sockaddr_is_ipv6(&udptldest) && !ast_sockaddr_is_ipv4_mapped(&udptldest)) ?
                                        "IP6" : "IP4", ast_sockaddr_stringify_addr_remote(&udptldest));
@@ -13788,6 +13796,29 @@ static int transmit_reinvite_with_sdp(struct sip_pvt *p, int t38version, int old
 {
        struct sip_request req;
 
+       if (t38version) {
+               /* Force media to go through us for T.38. */
+               memset(&p->redirip, 0, sizeof(p->redirip));
+       }
+       if (p->rtp) {
+               if (t38version) {
+                       /* Silence RTCP while audio RTP is inactive */
+                       ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 0);
+                       if (p->owner) {
+                               /* Prevent audio RTCP reads */
+                               ast_channel_set_fd(p->owner, 1, -1);
+                       }
+               } else if (ast_sockaddr_isnull(&p->redirip)) {
+                       /* Enable RTCP since it will be inactive if we're coming back
+                        * with this reinvite */
+                       ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 1);
+                       if (p->owner) {
+                               /* Enable audio RTCP reads */
+                               ast_channel_set_fd(p->owner, 1, ast_rtp_instance_fd(p->rtp, 1));
+                       }
+               }
+       }
+
        reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ?  SIP_UPDATE : SIP_INVITE, 0, 1);
 
        add_header(&req, "Allow", ALLOWED_METHODS);
@@ -14190,7 +14221,7 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
 {
        struct ast_party_id diverting_from;
        const char *reason;
-       int found_in_table;
+       const char *quote_str;
        char header_text[256];
        char encoded_number[SIPBUFSIZE/2];
 
@@ -14215,17 +14246,18 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
                ast_copy_string(encoded_number, diverting_from.number.str, sizeof(encoded_number));
        }
 
-       reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason, &found_in_table);
+       reason = sip_reason_code_to_str(&ast_channel_redirecting(pvt->owner)->reason);
+
+       /* Reason is either already quoted or it is a token to not need quotes added. */
+       quote_str = *reason == '\"' || sip_is_token(reason) ? "" : "\"";
 
        /* We at least have a number to place in the Diversion header, which is enough */
        if (!diverting_from.name.valid
                || ast_strlen_zero(diverting_from.name.str)) {
                snprintf(header_text, sizeof(header_text), "<sip:%s@%s>;reason=%s%s%s",
-                               encoded_number,
-                               ast_sockaddr_stringify_host_remote(&pvt->ourip),
-                               found_in_table ? "" : "\"",
-                               reason,
-                               found_in_table ? "" : "\"");
+                       encoded_number,
+                       ast_sockaddr_stringify_host_remote(&pvt->ourip),
+                       quote_str, reason, quote_str);
        } else {
                char escaped_name[SIPBUFSIZE/2];
                if (sip_cfg.pedanticsipchecking) {
@@ -14234,12 +14266,10 @@ static void add_diversion(struct sip_request *req, struct sip_pvt *pvt)
                        ast_copy_string(escaped_name, diverting_from.name.str, sizeof(escaped_name));
                }
                snprintf(header_text, sizeof(header_text), "\"%s\" <sip:%s@%s>;reason=%s%s%s",
-                               escaped_name,
-                               encoded_number,
-                               ast_sockaddr_stringify_host_remote(&pvt->ourip),
-                               found_in_table ? "" : "\"",
-                               reason,
-                               found_in_table ? "" : "\"");
+                       escaped_name,
+                       encoded_number,
+                       ast_sockaddr_stringify_host_remote(&pvt->ourip),
+                       quote_str, reason, quote_str);
        }
 
        add_header(req, "Diversion", header_text);
@@ -17711,6 +17741,9 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
        char *params, *reason_param = NULL;
        struct sip_request *req;
 
+       ast_assert(reason_code != NULL);
+       ast_assert(reason_str != NULL);
+
        req = oreq ? oreq : &p->initreq;
 
        ast_copy_string(tmp, sip_get_header(req, "Diversion"), sizeof(tmp));
@@ -17744,16 +17777,6 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
                        if ((end = strchr(reason_param, ';'))) {
                                *end = '\0';
                        }
-                       /* Remove enclosing double-quotes */
-                       if (*reason_param == '"')
-                               reason_param = ast_strip_quoted(reason_param, "\"", "\"");
-                       if (!ast_strlen_zero(reason_param)) {
-                               sip_set_redirstr(p, reason_param);
-                               if (p->owner) {
-                                       pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
-                                       pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param);
-                               }
-                       }
                }
        }
 
@@ -17785,12 +17808,27 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
        }
 
        if (!ast_strlen_zero(reason_param)) {
-               if (reason_code) {
-                       *reason_code = sip_reason_str_to_code(reason_param);
+               *reason_str = ast_strdup(reason_param);
+
+               /* Remove any enclosing double-quotes */
+               if (*reason_param == '"') {
+                       reason_param = ast_strip_quoted(reason_param, "\"", "\"");
                }
 
-               if (reason_str) {
-                       *reason_str = ast_strdup(reason_param);
+               *reason_code = ast_redirecting_reason_parse(reason_param);
+               if (*reason_code < 0) {
+                       *reason_code = AST_REDIRECTING_REASON_UNKNOWN;
+               } else {
+                       ast_free(*reason_str);
+                       *reason_str = ast_strdup("");
+               }
+
+               if (!ast_strlen_zero(reason_param)) {
+                       sip_set_redirstr(p, reason_param);
+                       if (p->owner) {
+                               pbx_builtin_setvar_helper(p->owner, "__PRIREDIRECTREASON", p->redircause);
+                               pbx_builtin_setvar_helper(p->owner, "__SIPREDIRECTREASON", reason_param);
+                       }
                }
        }
 
@@ -22669,10 +22707,11 @@ static void change_redirecting_information(struct sip_pvt *p, struct sip_request
                redirecting->to.name.str = redirecting_to_name;
        }
        redirecting->reason.code = reason;
+       ast_free(redirecting->reason.str);
+       redirecting->reason.str = reason_str;
        if (reason_str) {
-               ast_debug(3, "Got redirecting reason %s\n", reason_str);
-               ast_free(redirecting->reason.str);
-               redirecting->reason.str = reason_str;
+               ast_debug(3, "Got redirecting reason %s\n", ast_strlen_zero(reason_str)
+                       ? sip_reason_code_to_str(&redirecting->reason) : reason_str);
        }
 }
 
@@ -23473,14 +23512,22 @@ static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest
                if (p->owner && !req->ignore) {
                        struct ast_party_redirecting redirecting;
                        struct ast_set_party_redirecting update_redirecting;
+                       char *quoted_rest = ast_alloca(strlen(rest) + 3);
+
                        ast_party_redirecting_set_init(&redirecting, ast_channel_redirecting(p->owner));
                        memset(&update_redirecting, 0, sizeof(update_redirecting));
 
-                       redirecting.reason.code = sip_reason_str_to_code(rest);
-                       redirecting.reason.str = ast_strdup(rest);
+                       redirecting.reason.code = ast_redirecting_reason_parse(rest);
+                       if (redirecting.reason.code < 0) {
+                               sprintf(quoted_rest, "\"%s\"", rest);/* Safe */
+
+                               redirecting.reason.code = AST_REDIRECTING_REASON_UNKNOWN;
+                               redirecting.reason.str = quoted_rest;
+                       } else {
+                               redirecting.reason.str = "";
+                       }
 
                        ast_channel_queue_redirecting_update(p->owner, &redirecting, &update_redirecting);
-                       ast_party_redirecting_free(&redirecting);
 
                        ast_queue_control(p->owner, AST_CONTROL_BUSY);
                }
@@ -29680,7 +29727,8 @@ static int sip_devicestate(const char *data)
  *     or      SIP/devicename/extension/IPorHost
  *     or      SIP/username@domain//IPorHost
  *     and there is an optional [!dnid] argument you can append to alter the
- *     To: header.
+ *     To: header. And after that, a [![fromuser][@fromdomain]] argument.
+ *     Leave those blank to use the defaults.
  * \endverbatim
  */
 static struct ast_channel *sip_request_call(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *dest, int *cause)
@@ -29752,11 +29800,49 @@ static struct ast_channel *sip_request_call(const char *type, struct ast_format_
        /* Save the destination, the SIP dial string */
        ast_copy_string(tmp, dest, sizeof(tmp));
 
-       /* Find DNID and take it away */
+       /* Find optional DNID (SIP to-uri) and From-CLI (SIP from-uri)
+        * and strip it from the dial string:
+        *   [!touser[@todomain][![fromuser][@fromdomain]]]
+        * For historical reasons, the touser@todomain is passed as dnid
+        * while fromuser@fromdomain are split immediately. Passing a
+        * todomain without touser will create an invalid SIP message. */
        dnid = strchr(tmp, '!');
        if (dnid != NULL) {
+               char *fromuser_and_domain;
+
                *dnid++ = '\0';
-               ast_string_field_set(p, todnid, dnid);
+               if ((fromuser_and_domain = strchr(dnid, '!'))) {
+                       char *forward_compat;
+                       char *fromdomain;
+
+                       *fromuser_and_domain++ = '\0';
+
+                       /* Cut it at a trailing NUL or trailing '!' for
+                        * forward compatibility with extra arguments
+                        * in the future. */
+                       if ((forward_compat = strchr(fromuser_and_domain, '!'))) {
+                               /* Ignore the rest.. */
+                               *forward_compat = '\0';
+                       }
+
+                       if ((fromdomain = strchr(fromuser_and_domain, '@'))) {
+                               *fromdomain++ = '\0';
+                               /* Set fromdomain. */
+                               if (!ast_strlen_zero(fromdomain)) {
+                                       ast_string_field_set(p, fromdomain, fromdomain);
+                               }
+                       }
+
+                       /* Set fromuser. */
+                       if (!ast_strlen_zero(fromuser_and_domain)) {
+                               ast_string_field_set(p, fromuser, fromuser_and_domain);
+                       }
+               }
+
+               /* Set DNID (touser/todomain). */
+               if (!ast_strlen_zero(dnid)) {
+                       ast_string_field_set(p, todnid, dnid);
+               }
        }
 
        /* Divvy up the items separated by slashes */
@@ -32741,14 +32827,6 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *i
        } else if (!ast_sockaddr_isnull(&p->redirip)) {
                memset(&p->redirip, 0, sizeof(p->redirip));
                changed = 1;
-
-               if (p->rtp) {
-                       /* Enable RTCP since it will be inactive if we're coming back
-                        * from a reinvite */
-                       ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_RTCP, 1);
-                       /* Enable audio RTCP reads */
-                       ast_channel_set_fd(chan, 1, ast_rtp_instance_fd(p->rtp, 1));
-               }
        }
 
        if (vinstance) {
diff --git a/configs/samples/pjproject.conf.sample b/configs/samples/pjproject.conf.sample
new file mode 100644 (file)
index 0000000..97af734
--- /dev/null
@@ -0,0 +1,28 @@
+; Common pjproject options
+;
+
+;========================LOG_MAPPINGS SECTION OPTIONS===============================
+;[log_mappings]
+;  SYNOPSIS: Provides pjproject to Asterisk log level mappings.
+;  NOTES: The name of this section in the pjproject.conf configuration file must
+;         remain log_mappings or the configuration will not be applied.
+;         The defaults mentioned below only apply if this file or the 'log_mappings'
+;         object can'tbe found.  If the object is found, there are no defaults. If
+;         you don't specify an entry, nothing will be logged for that level.
+;
+;asterisk_error =    ; A comma separated list of pjproject log levels to map to
+                     ; Asterisk errors.
+                     ; (default: "0,1")
+;asterisk_warning =  ; A comma separated list of pjproject log levels to map to
+                     ; Asterisk warnings.
+                     ; (default: "2")
+;asterisk_notice =   ; A comma separated list of pjproject log levels to map to
+                     ; Asterisk notices.
+                     ; (default: "")
+;asterisk_verbose =  ; A comma separated list of pjproject log levels to map to
+                     ; Asterisk verbose.
+                     ; (default: "")
+;asterisk_debug =    ; A comma separated list of pjproject log levels to map to
+                     ; Asterisk debug
+                     ; (default: "3,4,5")
+;type=               ; Must be of type log_mappings (default: "")
index 5c326f2..2d127a1 100644 (file)
                                 ; clients are slow to process the received
                                 ; information. Value is in milliseconds; default
                                 ; is 100 ms.
+;allow_reload=no    ; Although transports can now be reloaded, that may not be
+                    ; desirable because of the slight possibility of dropped
+                    ; calls. To make sure there are no unintentional drops, if
+                    ; this option is set to 'no' (the default) changes to the
+                    ; particular transport will be ignored. If set to 'yes',
+                    ; changes (if any) will be applied.
 
 ;==========================AOR SECTION OPTIONS=========================
 ;[aor]
index fe68514..d89a2a1 100644 (file)
@@ -24,6 +24,9 @@
 ;        SIP/devicename/extension
 ;        SIP/devicename/extension/IPorHost
 ;        SIP/username@domain//IPorHost
+; And to alter the To: or the From: header, you can additionally append
+; the following to any of the above strings:
+;        [![touser[@todomain]][![fromuser][@fromdomain]]]
 ;
 ;
 ; Devicename
 ;
 ;         SIP/sales@mysipproxy!sales@edvina.net
 ;
+; (Specifying only @todomain without touser will create an invalid SIP
+; request.)
+;
+; Similarly, you can specify the From header as well, after a second
+; exclamation mark:
+;
+;         SIP/customer@mysipproxy!!customersupport@wearespindle.com
+;
 ; A new feature for 1.8 allows one to specify a host or IP address to use
 ; when routing the call. This is typically used in tandem with func_srv if
 ; multiple methods of reaching the same domain exist. The host or IP address
index 2c0f99d..3967cec 100755 (executable)
--- a/configure
+++ b/configure
@@ -915,6 +915,10 @@ PBX_PORTAUDIO
 PORTAUDIO_DIR
 PORTAUDIO_INCLUDE
 PORTAUDIO_LIB
+PBX_POPT
+POPT_DIR
+POPT_INCLUDE
+POPT_LIB
 PBX_PJSIP_EXTERNAL_RESOLVER
 PJSIP_EXTERNAL_RESOLVER_DIR
 PJSIP_EXTERNAL_RESOLVER_INCLUDE
@@ -939,10 +943,7 @@ PBX_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
 PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_DIR
 PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_INCLUDE
 PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_LIB
-PBX_POPT
-POPT_DIR
-POPT_INCLUDE
-POPT_LIB
+PJPROJECT_BUNDLED
 PBX_PJPROJECT
 PJPROJECT_DIR
 PJPROJECT_INCLUDE
@@ -1362,6 +1363,7 @@ with_osptk
 with_oss
 with_postgres
 with_pjproject
+with_pjproject_bundled
 with_popt
 with_portaudio
 with_pri
@@ -2101,6 +2103,8 @@ Optional Packages:
   --with-oss=PATH         use Open Sound System files in PATH
   --with-postgres=PATH    use PostgreSQL files in PATH
   --with-pjproject=PATH   use PJPROJECT files in PATH
+  --with-pjproject-bundled
+                          Use bundled pjproject libraries
   --with-popt=PATH        use popt files in PATH
   --with-portaudio=PATH   use PortAudio files in PATH
   --with-pri=PATH         use ISDN PRI files in PATH
@@ -10415,6 +10419,7 @@ fi
 
 
 
+
     PJPROJECT_DESCRIP="PJPROJECT"
     PJPROJECT_OPTION="pjproject"
     PBX_PJPROJECT=0
 
 
 
+PJPROJECT_BUNDLED=no
 
-    POPT_DESCRIP="popt"
-    POPT_OPTION="popt"
-    PBX_POPT=0
 
-# Check whether --with-popt was given.
-if test "${with_popt+set}" = set; then :
-  withval=$with_popt;
-       case ${withval} in
-       n|no)
-       USE_POPT=no
-       # -1 is a magic value used by menuselect to know that the package
-       # was disabled, other than 'not found'
-       PBX_POPT=-1
-       ;;
-       y|ye|yes)
-       ac_mandatory_list="${ac_mandatory_list} POPT"
-       ;;
-       *)
-       POPT_DIR="${withval}"
-       ac_mandatory_list="${ac_mandatory_list} POPT"
-       ;;
-       esac
 
+# Check whether --with-pjproject-bundled was given.
+if test "${with_pjproject_bundled+set}" = set; then :
+  withval=$with_pjproject_bundled; case "${enableval}" in
+             n|no) PJPROJECT_BUNDLED=no ;;
+             *) PJPROJECT_BUNDLED=yes ;;
+       esac
 fi
 
 
 
+if test "$PJPROJECT_BUNDLED" = "yes" -a "${ac_mandatory_list#*PJPROJECT*}" != "$ac_mandatory_list" ; then
+   as_fn_error $? "--with-pjproject and --with-pjproject-bundled can't both be specified" "$LINENO" 5
+fi
 
-
-
+if test "$PJPROJECT_BUNDLED" = "yes" ; then
+   ac_mandatory_list="$ac_mandatory_list PJPROJECT"
+   PJPROJECT_DIR="${ac_top_build_prefix}third-party/pjproject"
+fi
 
 
 PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_DESCRIP="PJSIP Dialog Create UAS with Incremented Lock"
@@ -10551,6 +10547,39 @@ PBX_PJSIP_EXTERNAL_RESOLVER=0
 
 
 
+
+    POPT_DESCRIP="popt"
+    POPT_OPTION="popt"
+    PBX_POPT=0
+
+# Check whether --with-popt was given.
+if test "${with_popt+set}" = set; then :
+  withval=$with_popt;
+       case ${withval} in
+       n|no)
+       USE_POPT=no
+       # -1 is a magic value used by menuselect to know that the package
+       # was disabled, other than 'not found'
+       PBX_POPT=-1
+       ;;
+       y|ye|yes)
+       ac_mandatory_list="${ac_mandatory_list} POPT"
+       ;;
+       *)
+       POPT_DIR="${withval}"
+       ac_mandatory_list="${ac_mandatory_list} POPT"
+       ;;
+       esac
+
+fi
+
+
+
+
+
+
+
+
     PORTAUDIO_DESCRIP="PortAudio"
     PORTAUDIO_OPTION="portaudio"
     PBX_PORTAUDIO=0
@@ -24441,6 +24470,244 @@ $as_echo "$as_me: *** including --without-postgres" >&6;}
    fi
 fi
 
+if test "$USE_PJPROJECT" != "no" ; then
+   if test "$PJPROJECT_BUNDLED" = "yes" ; then
+
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for embedded pjproject (may have to download)" >&5
+$as_echo_n "checking for embedded pjproject (may have to download)... " >&6; }
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: configuring" >&5
+$as_echo "configuring" >&6; }
+       make --quiet --no-print-directory -C $PJPROJECT_DIR configure
+       if test $? -ne 0 ; then
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5
+$as_echo "failed" >&6; }
+               { $as_echo "$as_me:${as_lineno-$LINENO}: Unable to configure $PJPROJECT_DIR" >&5
+$as_echo "$as_me: Unable to configure $PJPROJECT_DIR" >&6;}
+               as_fn_error $? "Run \"make -C $PJPROJECT_DIR NOISY_BUILD=yes configure\" to see error details." "$LINENO" 5
+       fi
+
+       PJPROJECT_INCLUDE=$(make --quiet --no-print-directory -C $PJPROJECT_DIR echo_cflags)
+       PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
+       PBX_PJPROJECT=1
+       PJPROJECT_BUNDLED=yes
+
+$as_echo "#define HAVE_PJPROJECT 1" >>confdefs.h
+
+
+$as_echo "#define HAVE_PJPROJECT_BUNDLED 1" >>confdefs.h
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for embedded pjproject" >&5
+$as_echo_n "checking for embedded pjproject... " >&6; }
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+
+       PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_INCLUDE="$PJPROJECT_INCLUDE"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_dlg_create_uas_and_inc_lock declared in pjsip.h" >&5
+$as_echo_n "checking for pjsip_dlg_create_uas_and_inc_lock declared in pjsip.h... " >&6; }
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <pjsip.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pjsip_dlg_create_uas_and_inc_lock" >/dev/null 2>&1; then :
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK=1
+
+$as_echo "#define HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK 1" >>confdefs.h
+
+
+else
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f conftest*
+
+
+       CPPGLAGS="$saved_cpp"
+       PJSIP_DLG_CREATE_UAS_AND_INC_LOCK_INCLUDE="$PJPROJECT_INCLUDE"
+
+
+       PJ_TRANSACTION_GRP_LOCK_INCLUDE="$PJPROJECT_INCLUDE"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_tsx_create_uac2 declared in pjsip.h" >&5
+$as_echo_n "checking for pjsip_tsx_create_uac2 declared in pjsip.h... " >&6; }
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <pjsip.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pjsip_tsx_create_uac2" >/dev/null 2>&1; then :
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJ_TRANSACTION_GRP_LOCK=1
+
+$as_echo "#define HAVE_PJ_TRANSACTION_GRP_LOCK 1" >>confdefs.h
+
+
+else
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f conftest*
+
+
+       CPPGLAGS="$saved_cpp"
+       PJ_TRANSACTION_GRP_LOCK_INCLUDE="$PJPROJECT_INCLUDE"
+
+
+       PJSIP_REPLACE_MEDIA_STREAM_INCLUDE="$PJPROJECT_INCLUDE"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE declared in pjmedia.h" >&5
+$as_echo_n "checking for PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE declared in pjmedia.h... " >&6; }
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <pjmedia.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE" >/dev/null 2>&1; then :
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJSIP_REPLACE_MEDIA_STREAM=1
+
+$as_echo "#define HAVE_PJSIP_REPLACE_MEDIA_STREAM 1" >>confdefs.h
+
+
+else
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f conftest*
+
+
+       CPPGLAGS="$saved_cpp"
+       PJSIP_REPLACE_MEDIA_STREAM_INCLUDE="$PJPROJECT_INCLUDE"
+
+
+       PJSIP_GET_DEST_INFO_INCLUDE="$PJPROJECT_INCLUDE"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_get_dest_info declared in pjsip.h" >&5
+$as_echo_n "checking for pjsip_get_dest_info declared in pjsip.h... " >&6; }
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <pjsip.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pjsip_get_dest_info" >/dev/null 2>&1; then :
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJSIP_GET_DEST_INFO=1
+
+$as_echo "#define HAVE_PJSIP_GET_DEST_INFO 1" >>confdefs.h
+
+
+else
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f conftest*
+
+
+       CPPGLAGS="$saved_cpp"
+       PJSIP_GET_DEST_INFO_INCLUDE="$PJPROJECT_INCLUDE"
+
+
+       PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE="$PJPROJECT_INCLUDE"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pj_ssl_cert_load_from_files2 declared in pjlib.h" >&5
+$as_echo_n "checking for pj_ssl_cert_load_from_files2 declared in pjlib.h... " >&6; }
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <pjlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pj_ssl_cert_load_from_files2" >/dev/null 2>&1; then :
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJ_SSL_CERT_LOAD_FROM_FILES2=1
+
+$as_echo "#define HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2 1" >>confdefs.h
+
+
+else
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f conftest*
+
+
+       CPPGLAGS="$saved_cpp"
+       PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE="$PJPROJECT_INCLUDE"
+
+
+       PJSIP_EXTERNAL_RESOLVER_INCLUDE="$PJPROJECT_INCLUDE"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pjsip_endpt_set_ext_resolver declared in pjsip.h" >&5
+$as_echo_n "checking for pjsip_endpt_set_ext_resolver declared in pjsip.h... " >&6; }
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <pjsip.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "pjsip_endpt_set_ext_resolver" >/dev/null 2>&1; then :
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+               PBX_PJSIP_EXTERNAL_RESOLVER=1
+
+$as_echo "#define HAVE_PJSIP_EXTERNAL_RESOLVER 1" >>confdefs.h
+
+
+else
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f conftest*
+
+
+       CPPGLAGS="$saved_cpp"
+       PJSIP_EXTERNAL_RESOLVER_INCLUDE="$PJPROJECT_INCLUDE"
+
+
+   else
 
    if test "x${PBX_PJPROJECT}" != "x1" -a "${USE_PJPROJECT}" != "no"; then
 
@@ -24658,7 +24925,7 @@ if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIBS $LIBS"
+LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -24700,7 +24967,7 @@ fi
 
    # now check for the header.
    if test "${AST_PJ_TRANSACTION_GRP_LOCK_FOUND}" = "yes"; then
-      PJ_TRANSACTION_GRP_LOCK_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIBS"
+      PJ_TRANSACTION_GRP_LOCK_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
       # if --with-PJ_TRANSACTION_GRP_LOCK=DIR has been specified, use it.
       if test "x${PJ_TRANSACTION_GRP_LOCK_DIR}" != "x"; then
          PJ_TRANSACTION_GRP_LOCK_INCLUDE="-I${PJ_TRANSACTION_GRP_LOCK_DIR}/include"
 
 
 
-saved_cppflags="${CPPFLAGS}"
-saved_libs="${LIBS}"
-CPPFLAGS="${CPPFLAGS} ${PJPROJECT_CFLAGS}"
-LIBS="${LIBS} ${PJPROJECT_LIBS}"
+      saved_cppflags="${CPPFLAGS}"
+      saved_libs="${LIBS}"
+      CPPFLAGS="${CPPFLAGS} ${PJPROJECT_CFLAGS}"
+      LIBS="${LIBS} ${PJPROJECT_LIB}"
 
     if test "x${PBX_PJSIP_REPLACE_MEDIA_STREAM}" != "x1" -a "${USE_PJSIP_REPLACE_MEDIA_STREAM}" != "no"; then
         if test "x" != "x"; then
@@ -24788,8 +25055,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
        CPPFLAGS="${saved_cppflags}"
     fi
 
-LIBS="${saved_libs}"
-CPPFLAGS="${saved_cppflags}"
+      LIBS="${saved_libs}"
+      CPPFLAGS="${saved_cppflags}"
 
 
 if test "x${PBX_PJSIP_GET_DEST_INFO}" != "x1" -a "${USE_PJSIP_GET_DEST_INFO}" != "no"; then
@@ -24815,7 +25082,7 @@ if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIBS $LIBS"
+LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -24857,7 +25124,7 @@ fi
 
    # now check for the header.
    if test "${AST_PJSIP_GET_DEST_INFO_FOUND}" = "yes"; then
-      PJSIP_GET_DEST_INFO_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIBS"
+      PJSIP_GET_DEST_INFO_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB"
       # if --with-PJSIP_GET_DEST_INFO=DIR has been specified, use it.
       if test "x${PJSIP_GET_DEST_INFO_DIR}" != "x"; then
          PJSIP_GET_DEST_INFO_INCLUDE="-I${PJSIP_GET_DEST_INFO_DIR}/include"
@@ -24906,7 +25173,7 @@ if test "x${PBX_PJ_SSL_CERT_LOAD_FROM_FILES2}" != "x1" -a "${USE_PJ_SSL_CERT_LOA
          pbxlibdir="-L${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}"
       fi
    fi
-   pbxfuncname="pj_ssl_cert_load_from_files2"
+   pbxfuncname="pjsip/include/pjsip/sip_util.h"
    if test "x${pbxfuncname}" = "x" ; then   # empty lib, assume only headers
       AST_PJ_SSL_CERT_LOAD_FROM_FILES2_FOUND=yes
    else
@@ -24919,7 +25186,7 @@ if eval \${$as_ac_Lib+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpj ${pbxlibdir} $PJPROJECT_LIBS $LIBS"
+LIBS="-lpj ${pbxlibdir} $PJPROJECT_LIB $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -24961,7 +25228,7 @@ fi
 
    # now check for the header.
    if test "${AST_PJ_SSL_CERT_LOAD_FROM_FILES2_FOUND}" = "yes"; then
-      PJ_SSL_CERT_LOAD_FROM_FILES2_LIB="${pbxlibdir} -lpj $PJPROJECT_LIBS"
+      PJ_SSL_CERT_LOAD_FROM_FILES2_LIB="${pbxlibdir} -lpj $PJPROJECT_LIB"
       # if --with-PJ_SSL_CERT_LOAD_FROM_FILES2=DIR has been specified, use it.
       if test "x${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}" != "x"; then
          PJ_SSL_CERT_LOAD_FROM_FILES2_INCLUDE="-I${PJ_SSL_CERT_LOAD_FROM_FILES2_DIR}/include"
@@ -25103,6 +25370,8 @@ _ACEOF
 fi
 
 
+   fi
+fi
 
 
 if test "x${PBX_POPT}" != "x1" -a "${USE_POPT}" != "no"; then
index 1ca5eb3..12cc8d2 100644 (file)
@@ -453,14 +453,37 @@ AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
 AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk])
 AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss])
 AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
+
 AST_EXT_LIB_SETUP([PJPROJECT], [PJPROJECT], [pjproject])
-AST_EXT_LIB_SETUP([POPT], [popt], [popt])
+PJPROJECT_BUNDLED=no
+AH_TEMPLATE(m4_bpatsubst([[HAVE_PJPROJECT_BUNDLED]], [(.*)]), [Define to 1 when using the bundled pjproject.])
+
+AC_ARG_WITH([pjproject-bundled],
+       [AS_HELP_STRING([--with-pjproject-bundled],
+               [Use bundled pjproject libraries])],
+       [case "${enableval}" in
+             n|no) PJPROJECT_BUNDLED=no ;;
+             *) PJPROJECT_BUNDLED=yes ;;
+       esac])
+AC_SUBST(PJPROJECT_BUNDLED)
+
+if test "$PJPROJECT_BUNDLED" = "yes" -a "${ac_mandatory_list#*PJPROJECT*}" != "$ac_mandatory_list" ; then
+   AC_MSG_ERROR(--with-pjproject and --with-pjproject-bundled can't both be specified)
+fi
+
+if test "$PJPROJECT_BUNDLED" = "yes" ; then
+   ac_mandatory_list="$ac_mandatory_list PJPROJECT"
+   PJPROJECT_DIR="${ac_top_build_prefix}third-party/pjproject"
+fi
+
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_DLG_CREATE_UAS_AND_INC_LOCK], [PJSIP Dialog Create UAS with Incremented Lock], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJ_TRANSACTION_GRP_LOCK], [PJSIP Transaction Group Lock Support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_REPLACE_MEDIA_STREAM], [PJSIP Media Stream Replacement Support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_GET_DEST_INFO], [pjsip_get_dest_info support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2 support], [PJPROJECT], [pjsip])
 AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EXTERNAL_RESOLVER], [PJSIP External Resolver Support], [PJPROJECT], [pjsip])
+
+AST_EXT_LIB_SETUP([POPT], [popt], [popt])
 AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio])
 AST_EXT_LIB_SETUP([PRI], [ISDN PRI], [pri])
 AST_EXT_LIB_SETUP_OPTIONAL([PRI_SETUP_ACK_INBAND], [ISDN PRI progress inband ie in SETUP ACK], [PRI], [pri])
@@ -2152,22 +2175,29 @@ if test "${PG_CONFIG}" != No; then
    fi
 fi
 
-AST_PKG_CONFIG_CHECK([PJPROJECT], [libpjproject])
-
-AST_EXT_LIB_CHECK([PJSIP_DLG_CREATE_UAS_AND_INC_LOCK], [pjsip], [pjsip_dlg_create_uas_and_inc_lock], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
-AST_EXT_LIB_CHECK([PJ_TRANSACTION_GRP_LOCK], [pjsip], [pjsip_tsx_create_uac2], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
-
-saved_cppflags="${CPPFLAGS}"
-saved_libs="${LIBS}"
-CPPFLAGS="${CPPFLAGS} ${PJPROJECT_CFLAGS}"
-LIBS="${LIBS} ${PJPROJECT_LIBS}"
-AST_C_COMPILE_CHECK([PJSIP_REPLACE_MEDIA_STREAM], [pjmedia_mod_offer_flag flag = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE], [pjmedia.h])
-LIBS="${saved_libs}"
-CPPFLAGS="${saved_cppflags}"
-
-AST_EXT_LIB_CHECK([PJSIP_GET_DEST_INFO], [pjsip], [pjsip_get_dest_info], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
-AST_EXT_LIB_CHECK([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj], [pj_ssl_cert_load_from_files2], [pjlib.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
-AST_EXT_LIB_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip], [pjsip_endpt_set_ext_resolver], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
+if test "$USE_PJPROJECT" != "no" ; then
+   if test "$PJPROJECT_BUNDLED" = "yes" ; then
+       AC_CONFIG_MACRO_DIR(third-party/pjproject)
+       PJPROJECT_CONFIGURE([$PJPROJECT_DIR])
+   else
+      AST_PKG_CONFIG_CHECK([PJPROJECT], [libpjproject])
+
+      AST_EXT_LIB_CHECK([PJSIP_DLG_CREATE_UAS_AND_INC_LOCK], [pjsip], [pjsip_dlg_create_uas_and_inc_lock], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
+      AST_EXT_LIB_CHECK([PJ_TRANSACTION_GRP_LOCK], [pjsip], [pjsip_tsx_create_uac2], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
+
+      saved_cppflags="${CPPFLAGS}"
+      saved_libs="${LIBS}"
+      CPPFLAGS="${CPPFLAGS} ${PJPROJECT_CFLAGS}"
+      LIBS="${LIBS} ${PJPROJECT_LIB}"
+      AST_C_COMPILE_CHECK([PJSIP_REPLACE_MEDIA_STREAM], [pjmedia_mod_offer_flag flag = PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE], [pjmedia.h])
+      LIBS="${saved_libs}"
+      CPPFLAGS="${saved_cppflags}"
+
+      AST_EXT_LIB_CHECK([PJSIP_GET_DEST_INFO], [pjsip], [pjsip_get_dest_info], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
+      AST_EXT_LIB_CHECK([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj], [pjsip/include/pjsip/sip_util.h], [pjlib.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS])
+      AST_EXT_LIB_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip], [pjsip_endpt_set_ext_resolver], [pjsip.h], [$PJPROJECT_LIBS], [$PJPROJECT_CFLAGS])
+   fi
+fi
 
 AST_EXT_LIB_CHECK([POPT], [popt], [poptStrerror], [popt.h])
 
@@ -2331,11 +2361,11 @@ AC_CHECK_FUNC([crypt], [SYSCRYPT=true], [SYSCRYPT=""])
 if test "x$LIBCRYPT_LIB" != "x" ; then
     CRYPT_LIB="$LIBCRYPT_LIB"
     CRYPT_INCLUDE="$LIBCRYPT_INCLUDE"
-    AC_DEFINE([HAVE_CRYPT], [1], [Define to 1 if you have the `crypt' function.])
+    AC_DEFINE([HAVE_CRYPT], [1], [Define to 1 if you have the 'crypt' function.])
 elif test "x$SYSCRYPT" != "x" ; then
     CRYPT_LIB=""
     CRYPT_INCLUDE=""
-    AC_DEFINE([HAVE_CRYPT], [1], [Define to 1 if you have the `crypt' function.])
+    AC_DEFINE([HAVE_CRYPT], [1], [Define to 1 if you have the 'crypt' function.])
 fi
 
 AC_SUBST(CRYPT_LIB)
@@ -2343,7 +2373,7 @@ AC_SUBST(CRYPT_INCLUDE)
 
 # Find crypt_r support
 AC_CHECK_LIB([crypt], [crypt_r],
-    [AC_DEFINE([HAVE_CRYPT_R], [1], [Define to 1 if you have the `crypt_r' function.])])
+    [AC_DEFINE([HAVE_CRYPT_R], [1], [Define to 1 if you have the 'crypt_r' function.])])
 
 AST_EXT_LIB_CHECK([CRYPTO], [crypto], [AES_encrypt], [openssl/aes.h])
 
index 6740d59..4118da0 100755 (executable)
@@ -58,7 +58,8 @@ def run_migrations_online():
     connection = engine.connect()
     context.configure(
                 connection=connection,
-                target_metadata=target_metadata
+                target_metadata=target_metadata,
+                render_as_batch=True
                 )
 
     try:
index b4ea71c..cc9f0e0 100755 (executable)
@@ -45,7 +45,10 @@ def upgrade():
     context = op.get_context()
 
     # Upgrading to this revision WILL clear your directmedia values.
-    if context.bind.dialect.name != 'postgresql':
+    if context.bind.dialect.name == 'sqlite':
+        with op.batch_alter_table('sippeers') as batch_op:
+            batch_op.alter_column('directmedia', type_=new_type)
+    elif context.bind.dialect.name != 'postgresql':
         op.alter_column('sippeers', 'directmedia',
                         type_=new_type,
                         existing_type=old_type)
@@ -66,7 +69,10 @@ def downgrade():
     op.execute(tcr.update().where(tcr.c.directmedia==u'outgoing')
                .values(directmedia=None))
 
-    if context.bind.dialect.name != 'postgresql':
+    if context.bind.dialect.name == 'sqlite':
+        with op.batch_alter_table('sippeers') as batch_op:
+            batch_op.alter_column('directmedia', type_=old_type)
+    elif context.bind.dialect.name != 'postgresql':
         op.alter_column('sippeers', 'directmedia',
                         type_=old_type,
                         existing_type=new_type)
index 724a5e5..22fd6c7 100644 (file)
@@ -17,4 +17,5 @@ def upgrade():
     op.add_column('ps_globals', sa.Column('regcontext', sa.String(80)))
 
 def downgrade():
-    op.drop_column('ps_globals', 'regcontext')
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('regcontext')
index 7e6cf99..6c5f808 100644 (file)
@@ -19,4 +19,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_globals', 'default_from_user')
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('default_from_user')
index 54f08ff..725238c 100644 (file)
@@ -27,4 +27,5 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('moh_passthrough', yesno_values))
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'moh_passthrough')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('moh_passthrough')
index 215726f..c16cff9 100755 (executable)
@@ -33,9 +33,11 @@ import sqlalchemy as sa
 
 
 def upgrade():
-    op.alter_column('sippeers', 'useragent', type_=sa.String(255))
+    with op.batch_alter_table('sippeers') as batch_op:
+        batch_op.alter_column('useragent', type_=sa.String(255))
 
 
 def downgrade():
-    op.alter_column('sippeers', 'useragent', type_=sa.String(20))
+    with op.batch_alter_table('sippeers') as batch_op:
+        batch_op.alter_column('useragent', type_=sa.String(20))
 
index ba972b6..dee384d 100644 (file)
@@ -19,4 +19,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_globals', 'keep_alive_interval')
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('keep_alive_interval')
index eb20001..4d520fc 100644 (file)
@@ -17,4 +17,5 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('accountcode', sa.String(20)))
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'accountcode')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('accountcode')
index 2adca62..8b77eb7 100755 (executable)
@@ -18,4 +18,5 @@ def upgrade():
     op.add_column('ps_globals', sa.Column('debug', sa.String(40)))
 
 def downgrade():
-    op.drop_column('ps_globals', 'debug')
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('debug')
index dc0c01c..8ca63f1 100755 (executable)
@@ -45,4 +45,5 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('rpid_immediate', yesno_values))
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'rpid_immediate')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('rpid_immediate')
index e7c11da..1199d0c 100644 (file)
@@ -28,4 +28,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'bind_rtp_to_media_address')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('bind_rtp_to_media_address')
index 8972d80..2a792d3 100644 (file)
@@ -20,5 +20,6 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'rtp_timeout')
-    op.drop_column('ps_endpoints', 'rtp_timeout_hold')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('rtp_timeout')
+        batch_op.drop_column('rtp_timeout_hold')
index ad36bd9..09056d6 100644 (file)
@@ -27,4 +27,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'g726_non_standard')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('g726_non_standard')
index 8c499ae..8e05a62 100644 (file)
@@ -19,4 +19,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_registrations', 'fatal_retry_interval')
+    with op.batch_alter_table('ps_registrations') as batch_op:
+        batch_op.drop_column('fatal_retry_interval')
index 2ade86f..9f98750 100644 (file)
@@ -15,8 +15,10 @@ import sqlalchemy as sa
 
 
 def upgrade():
-    op.alter_column('ps_aors', 'contact', type_=sa.String(255))
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.alter_column('contact', type_=sa.String(255))
 
 
 def downgrade():
-    op.alter_column('ps_aors', 'contact', type_=sa.String(40))
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.alter_column('contact', type_=sa.String(40))
index 564897e..102265e 100755 (executable)
@@ -120,15 +120,15 @@ def upgrade():
     op.create_index('ps_registrations_id', 'ps_registrations', ['id'])
 
     ########################## add columns ###########################
-
+    with op.batch_alter_table('ps_endpoints') as batch_op:
     # new columns for endpoints
-    op.add_column('ps_endpoints', sa.Column('media_address', sa.String(40)))
-    op.add_column('ps_endpoints', sa.Column('redirect_method',
+        batch_op.add_column(sa.Column('media_address', sa.String(40)))
+        batch_op.add_column(sa.Column('redirect_method',
                                             pjsip_redirect_method_values))
-    op.add_column('ps_endpoints', sa.Column('set_var', sa.Text()))
+        batch_op.add_column(sa.Column('set_var', sa.Text()))
 
     # rename mwi_fromuser to mwi_from_user
-    op.alter_column('ps_endpoints', 'mwi_fromuser',
+        batch_op.alter_column('mwi_fromuser',
                     new_column_name='mwi_from_user',
                     existing_type=sa.String(40))
 
@@ -144,20 +144,23 @@ def upgrade():
 def downgrade():
     ########################## drop columns ##########################
 
-    op.drop_column('ps_aors', 'support_path')
-    op.drop_column('ps_aors', 'outbound_proxy')
-    op.drop_column('ps_aors', 'maximum_expiration')
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.drop_column('support_path')
+        batch_op.drop_column('outbound_proxy')
+        batch_op.drop_column('maximum_expiration')
 
-    op.drop_column('ps_contacts', 'path')
-    op.drop_column('ps_contacts', 'outbound_proxy')
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.drop_column('path')
+        batch_op.drop_column('outbound_proxy')
 
-    op.alter_column('ps_endpoints', 'mwi_from_user',
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.alter_column('mwi_from_user',
                     new_column_name='mwi_fromuser',
                     existing_type=sa.String(40))
 
-    op.drop_column('ps_endpoints', 'set_var')
-    op.drop_column('ps_endpoints', 'redirect_method')
-    op.drop_column('ps_endpoints', 'media_address')
+        batch_op.drop_column('set_var')
+        batch_op.drop_column('redirect_method')
+        batch_op.drop_column('media_address')
 
     ########################## drop tables ###########################
 
index 0d84390..b1a9f8b 100644 (file)
@@ -20,14 +20,14 @@ NEW_ENUM = ['rfc4733', 'inband', 'info', 'auto']
 old_type = sa.Enum(*OLD_ENUM, name='pjsip_dtmf_mode_values')
 new_type = sa.Enum(*NEW_ENUM, name='pjsip_dtmf_mode_values_v2')
 
-tcr = sa.sql.table('ps_endpoints', sa.Column('dtmf_mode', new_type,
-                   nullable=True))
-
 def upgrade():
     context = op.get_context()
 
     # Upgrading to this revision WILL clear your directmedia values.
-    if context.bind.dialect.name != 'postgresql':
+    if context.bind.dialect.name == 'sqlite':
+        with op.batch_alter_table('ps_endpoints') as batch_op:
+            batch_op.alter_column('dtmf_mode', type_=new_type)
+    elif context.bind.dialect.name != 'postgresql':
         op.alter_column('ps_endpoints', 'dtmf_mode',
                         type_=new_type,
                         existing_type=old_type)
@@ -45,10 +45,10 @@ def upgrade():
 def downgrade():
     context = op.get_context()
 
-    op.execute(tcr.update().where(tcr.c.directmedia==u'outgoing')
-               .values(directmedia=None))
-
-    if context.bind.dialect.name != 'postgresql':
+    if context.bind.dialect.name == 'sqlite':
+        with op.batch_alter_table('ps_endpoints') as batch_op:
+            batch_op.alter_column('dtmf_mode', type_=old_type)
+    elif context.bind.dialect.name != 'postgresql':
         op.alter_column('ps_endpoints', 'dtmf_mode',
                         type_=old_type,
                         existing_type=new_type)
index 31889c6..4891152 100644 (file)
@@ -15,14 +15,20 @@ import sqlalchemy as sa
 
 
 def upgrade():
-    op.alter_column('ps_endpoints', 'accountcode', type_=sa.String(80))
-    op.alter_column('sippeers', 'accountcode', type_=sa.String(80))
-    op.alter_column('iaxfriends', 'accountcode', type_=sa.String(80))
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.alter_column('accountcode', type_=sa.String(80))
+    with op.batch_alter_table('sippeers') as batch_op:
+        batch_op.alter_column('accountcode', type_=sa.String(80))
+    with op.batch_alter_table('iaxfriends') as batch_op:
+        batch_op.alter_column('accountcode', type_=sa.String(80))
     pass
 
 
 def downgrade():
-    op.alter_column('ps_endpoints', 'accountcode', type_=sa.String(20))
-    op.alter_column('sippeers', 'accountcode', type_=sa.String(40))
-    op.alter_column('iaxfriends', 'accountcode', type_=sa.String(20))
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.alter_column('accountcode', type_=sa.String(20))
+    with op.batch_alter_table('sippeers') as batch_op:
+        batch_op.alter_column('accountcode', type_=sa.String(40))
+    with op.batch_alter_table('iaxfriends') as batch_op:
+        batch_op.alter_column('accountcode', type_=sa.String(20))
     pass
index 145d6be..5de7aa8 100644 (file)
@@ -27,4 +27,5 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('user_eq_phone', yesno_values))
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'user_eq_phone')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('user_eq_phone')
index afc1beb..08457a9 100644 (file)
@@ -20,5 +20,7 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_contacts', 'user_agent')
-    op.drop_column('ps_endpoints', 'message_context')
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.drop_column('user_agent')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('message_context')
diff --git a/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py b/contrib/ast-db-manage/config/versions/3bcc0b5bc2c9_add_allow_reload_to_ps_transports.py
new file mode 100644 (file)
index 0000000..7f2c579
--- /dev/null
@@ -0,0 +1,26 @@
+"""Add allow_reload to ps_transports
+
+Revision ID: 3bcc0b5bc2c9
+Revises: dbc44d5a908
+Create Date: 2016-02-05 17:43:39.183785
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '3bcc0b5bc2c9'
+down_revision = 'dbc44d5a908'
+
+from alembic import op
+import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
+
+YESNO_NAME = 'yesno_values'
+YESNO_VALUES = ['yes', 'no']
+
+def upgrade():
+    yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
+    op.add_column('ps_transports', sa.Column('allow_reload', yesno_values))
+
+def downgrade():
+    with op.batch_alter_table('ps_transports') as batch_op:
+        batch_op.drop_column('allow_reload')
index cda0b9a..563b98d 100644 (file)
@@ -15,12 +15,13 @@ import sqlalchemy as sa
 
 
 def upgrade():
-    op.alter_column('ps_aors', 'qualify_timeout', type_=sa.Float)
-    op.alter_column('ps_contacts', 'qualify_timeout', type_=sa.Float)
-    pass
-
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.alter_column('qualify_timeout', type_=sa.Float)
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.alter_column('qualify_timeout', type_=sa.Float)
 
 def downgrade():
-    op.alter_column('ps_aors', 'qualify_timeout', type_=sa.Integer)
-    op.alter_column('ps_contacts', 'qualify_timeout', type_=sa.Integer)
-    pass
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.alter_column('qualify_timeout', type_=sa.Integer)
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.alter_column('qualify_timeout', type_=sa.Integer)
index 0c4d9c8..140fe5b 100755 (executable)
@@ -12,6 +12,7 @@ down_revision = '4da0c5f79a9c'
 
 from alembic import op
 import sqlalchemy as sa
+from sqlalchemy.dialects.postgresql import ENUM
 
 
 YESNO_VALUES = ['yes', 'no']
@@ -181,9 +182,21 @@ def upgrade():
 
 
 def downgrade():
+    context = op.get_context()
+
     op.drop_table('ps_endpoints')
     op.drop_table('ps_auths')
     op.drop_table('ps_aors')
     op.drop_table('ps_contacts')
     op.drop_table('ps_domain_aliases')
     op.drop_table('ps_endpoint_id_ips')
+
+    enums = ['yesno_values',
+             'pjsip_100rel_values','pjsip_auth_type_values','pjsip_cid_privacy_values',
+             'pjsip_connected_line_method_values','pjsip_direct_media_glare_mitigation_values',
+             'pjsip_dtls_setup_values','pjsip_dtmf_mode_values','pjsip_identify_by_values',
+             'pjsip_media_encryption_values','pjsip_t38udptl_ec_values','pjsip_timer_values']
+
+    if context.bind.dialect.name == 'postgresql':
+        for e in enums:
+            ENUM(name=e).drop(op.get_bind(), checkfirst=False)
index b7d9924..b09acf7 100644 (file)
@@ -18,4 +18,5 @@ def upgrade():
     op.add_column('ps_globals', sa.Column('endpoint_identifier_order', sa.String(40)))
 
 def downgrade():
-    op.drop_column('ps_globals', 'endpoint_identifier_order')
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('endpoint_identifier_order')
index 9600c04..ec8a904 100644 (file)
@@ -16,10 +16,9 @@ import sqlalchemy as sa
 def upgrade():
     op.add_column('ps_aors', sa.Column('qualify_timeout', sa.Integer))
     op.add_column('ps_contacts', sa.Column('qualify_timeout', sa.Integer))
-    pass
-
 
 def downgrade():
-    op.drop_column('ps_aors', 'qualify_timeout')
-    op.drop_column('ps_contacts', 'qualify_timeout')
-    pass
+    with op.batch_alter_table('ps_aors') as batch_op:
+        batch_op.drop_column('qualify_timeout')
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.drop_column('qualify_timeout')
index 5a4f470..3ad2650 100644 (file)
@@ -19,4 +19,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'rtp_keepalive')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('rtp_keepalive')
index aefddd1..d9bbf89 100755 (executable)
@@ -19,43 +19,43 @@ YESNO_NAME = 'yesno_values'
 YESNO_VALUES = ['yes', 'no']
 
 def upgrade():
-    op.alter_column('ps_endpoints', 'tos_audio',
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.alter_column('tos_audio',
                     type_=sa.String(10))
-    op.alter_column('ps_endpoints', 'tos_video',
+        batch_op.alter_column('tos_video',
                     type_=sa.String(10))
-    op.alter_column('ps_transports', 'tos',
+        batch_op.drop_column('cos_audio')
+        batch_op.drop_column('cos_video')
+        batch_op.add_column(sa.Column('cos_audio', sa.Integer))
+        batch_op.add_column(sa.Column('cos_video', sa.Integer))
+
+    with op.batch_alter_table('ps_transports') as batch_op:
+        batch_op.alter_column('tos',
                     type_=sa.String(10))
 
     # Can't cast YENO_VALUES to Integers, so dropping and adding is required
-    op.drop_column('ps_endpoints', 'cos_audio')
-    op.drop_column('ps_endpoints', 'cos_video')
-    op.drop_column('ps_transports', 'cos')
-
-    op.add_column('ps_endpoints', sa.Column('cos_audio', sa.Integer))
-    op.add_column('ps_endpoints', sa.Column('cos_video', sa.Integer))
-    op.add_column('ps_transports', sa.Column('cos', sa.Integer))
-    pass
+        batch_op.drop_column('cos')
 
+        batch_op.add_column(sa.Column('cos', sa.Integer))
 
 def downgrade():
 
     yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
 
     # Can't cast string to YESNO_VALUES, so dropping and adding is required
-    op.drop_column('ps_endpoints', 'tos_audio')
-    op.drop_column('ps_endpoints', 'tos_video')
-    op.drop_column('ps_transports', 'tos')
-
-    op.add_column('ps_endpoints', sa.Column('tos_audio', yesno_values))
-    op.add_column('ps_endpoints', sa.Column('tos_video', yesno_values))
-    op.add_column('ps_transports', sa.Column('tos', yesno_values))
-
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('tos_audio')
+        batch_op.drop_column('tos_video')
+        batch_op.add_column(sa.Column('tos_audio', yesno_values))
+        batch_op.add_column(sa.Column('tos_video', yesno_values))
+        batch_op.drop_column('cos_audio')
+        batch_op.drop_column('cos_video')
+        batch_op.add_column(sa.Column('cos_audio', yesno_values))
+        batch_op.add_column(sa.Column('cos_video', yesno_values))
+
+    with op.batch_alter_table('ps_transports') as batch_op:
+        batch_op.drop_column('tos')
+        batch_op.add_column(sa.Column('tos', yesno_values))
     # Can't cast integers to YESNO_VALUES, so dropping and adding is required
-    op.drop_column('ps_endpoints', 'cos_audio')
-    op.drop_column('ps_endpoints', 'cos_video')
-    op.drop_column('ps_transports', 'cos')
-
-    op.add_column('ps_endpoints', sa.Column('cos_audio', yesno_values))
-    op.add_column('ps_endpoints', sa.Column('cos_video', yesno_values))
-    op.add_column('ps_transports', sa.Column('cos', yesno_values))
-    pass
+        batch_op.drop_column('cos')
+        batch_op.add_column(sa.Column('cos', yesno_values))
index ffaff92..01c40ac 100755 (executable)
@@ -30,7 +30,7 @@ down_revision = None
 
 from alembic import op
 import sqlalchemy as sa
-
+from sqlalchemy.dialects.postgresql import ENUM
 
 YESNO_VALUES = ['yes', 'no']
 TYPE_VALUES = ['friend', 'user', 'peer']
@@ -323,8 +323,20 @@ def upgrade():
 
 
 def downgrade():
+    context = op.get_context()
+
     op.drop_table('sippeers')
     op.drop_table('iaxfriends')
     op.drop_table('voicemail')
     op.drop_table('meetme')
     op.drop_table('musiconhold')
+
+    enums = ['type_values', 'yes_no_values',
+             'sip_transport_values','sip_dtmfmode_values','sip_directmedia_values',
+             'sip_progressinband_values','sip_session_timers_values','sip_session_refresher_values',
+             'sip_callingpres_values','iax_requirecalltoken_values','iax_encryption_values',
+             'iax_transfer_values','moh_mode_values']
+
+    if context.bind.dialect.name == 'postgresql':
+        for e in enums:
+            ENUM(name=e).drop(op.get_bind(), checkfirst=False)
index 6bcaa9a..632f4c4 100755 (executable)
@@ -33,28 +33,31 @@ import sqlalchemy as sa
 
 
 def upgrade():
+    context = op.get_context()
     # Was unable to find a way to use op.alter_column() to add the unique
     # index property.
-    op.drop_column('queue_members', 'uniqueid')
-    op.add_column(
-        'queue_members',
-        sa.Column(
-            name='uniqueid', type_=sa.Integer, nullable=False,
-            unique=True))
+    if context.bind.dialect.name == 'sqlite':
+        with op.batch_alter_table('queue_members') as batch_op:
+            batch_op.create_primary_key('queue_members_pj', columns='uniqueid')
+    else:
+        op.drop_column('queue_members', 'uniqueid')
+        op.add_column(
+                      'queue_members',
+                      sa.Column(
+                                name='uniqueid', type_=sa.Integer, nullable=False,
+                                unique=True))
     # The postgres backend does not like the autoincrement needed for
     # mysql here.  It is just the backend that is giving a warning and
     # not the database itself.
-    op.alter_column(
-        table_name='queue_members', column_name='uniqueid',
-        existing_type=sa.Integer, existing_nullable=False,
-        autoincrement=True)
+        op.alter_column(
+                        table_name='queue_members', column_name='uniqueid',
+                        existing_type=sa.Integer, existing_nullable=False,
+                        autoincrement=True)
 
 
 def downgrade():
     # Was unable to find a way to use op.alter_column() to remove the
     # unique index property.
-    op.drop_column('queue_members', 'uniqueid')
-    op.add_column(
-        'queue_members',
-        sa.Column(name='uniqueid', type_=sa.String(80), nullable=False))
-
+    with op.batch_alter_table('queue_members') as batch_op:
+        batch_op.drop_column('uniqueid')
+        batch_op.add_column(sa.Column(name='uniqueid', type_=sa.String(80), nullable=False))
index c2dacda..8d0f68f 100644 (file)
@@ -28,5 +28,6 @@ def upgrade():
     op.add_column('ps_endpoints', sa.Column('media_use_received_transport', yesno_values))
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'force_avp')
-    op.drop_column('ps_endpoints', 'media_use_received_transport')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('force_avp')
+        batch_op.drop_column('media_use_received_transport')
index 28ebc8b..ace5444 100644 (file)
@@ -19,11 +19,13 @@ YESNO_VALUES = ['yes', 'no']
 
 def upgrade():
     yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
-    op.alter_column('ps_transports', 'verifiy_server', type_=yesno_values,
+    with op.batch_alter_table('ps_transports') as batch_op:
+        batch_op.alter_column('verifiy_server', type_=yesno_values,
                     new_column_name='verify_server')
 
 
 def downgrade():
     yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
-    op.alter_column('ps_transports', 'verify_server', type_=yesno_values,
+    with op.batch_alter_table('ps_transports') as batch_op:
+        batch_op.alter_column('verify_server', type_=yesno_values,
                     new_column_name='verifiy_server')
index 7a463f0..fff2504 100644 (file)
@@ -28,4 +28,5 @@ def upgrade():
 
 
 def downgrade():
-    op.drop_column('ps_endpoints', 'media_encryption_optimistic')
+    with op.batch_alter_table('ps_endpoints') as batch_op:
+        batch_op.drop_column('media_encryption_optimistic')
index 0ffd784..dc06d84 100644 (file)
@@ -17,4 +17,5 @@ def upgrade():
     op.add_column('ps_globals', sa.Column('max_initial_qualify_time', sa.Integer))
 
 def downgrade():
-    op.drop_column('ps_globals', 'max_initial_qualify_time')
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.drop_column('max_initial_qualify_time')
index 8f46216..b4502eb 100644 (file)
@@ -27,11 +27,10 @@ def upgrade():
     op.add_column('ps_systems', sa.Column('disable_tcp_switch', yesno_values))
     op.add_column('ps_registrations', sa.Column('line', yesno_values))
     op.add_column('ps_registrations', sa.Column('endpoint', sa.String(40)))
-    pass
-
 
 def downgrade():
-    op.drop_column('ps_systems', 'disable_tcp_switch')
-    op.drop_column('ps_registrations', 'line')
-    op.drop_column('ps_registrations', 'endpoint')
-    pass
+    with op.batch_alter_table('ps_systems') as batch_op:
+        batch_op.drop_column('disable_tcp_switch')
+    with op.batch_alter_table('ps_registrations') as batch_op:
+        batch_op.drop_column('line')
+        batch_op.drop_column('endpoint')
index d386ded..f25c298 100644 (file)
@@ -15,25 +15,28 @@ import sqlalchemy as sa
 
 
 def upgrade():
-    op.alter_column('ps_globals', 'user_agent', type_=sa.String(255))
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.alter_column('user_agent', type_=sa.String(255))
 
-    op.alter_column('ps_contacts', 'id', type_=sa.String(255))
-    op.alter_column('ps_contacts', 'uri', type_=sa.String(255))
-    op.alter_column('ps_contacts', 'user_agent', type_=sa.String(255))
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.alter_column('id', type_=sa.String(255))
+        batch_op.alter_column('uri', type_=sa.String(255))
+        batch_op.alter_column('user_agent', type_=sa.String(255))
 
-    op.alter_column('ps_registrations', 'client_uri', type_=sa.String(255))
-    op.alter_column('ps_registrations', 'server_uri', type_=sa.String(255))
+    with op.batch_alter_table('ps_registrations') as batch_op:
+        batch_op.alter_column('client_uri', type_=sa.String(255))
+        batch_op.alter_column('server_uri', type_=sa.String(255))
 
 
 def downgrade():
-    op.alter_column('ps_registrations', 'server_uri', type_=sa.String(40))
-    op.alter_column('ps_registrations', 'client_uri', type_=sa.String(40))
-
-    op.alter_column('ps_contacts', 'user_agent', type_=sa.String(40))
-    op.alter_column('ps_contacts', 'uri', type_=sa.String(40))
-    op.alter_column('ps_contacts', 'id', type_=sa.String(40))
-
-    op.alter_column('ps_globals', 'user_agent', type_=sa.String(40))
-
+    with op.batch_alter_table('ps_globals') as batch_op:
+        batch_op.alter_column('user_agent', type_=sa.String(40))
 
+    with op.batch_alter_table('ps_contacts') as batch_op:
+        batch_op.alter_column('id', type_=sa.String(40))
+        batch_op.alter_column('uri', type_=sa.String(40))
+        batch_op.alter_column('user_agent', type_=sa.String(40))
 
+    with op.batch_alter_table('ps_registrations') as batch_op:
+        batch_op.alter_column('client_uri', type_=sa.String(40))
+        batch_op.alter_column('server_uri', type_=sa.String(40))
index dee1977..133e259 100644 (file)
@@ -373,22 +373,27 @@ ASTERISK_REGISTER_FILE()
                        fields get/set a combined value for the corresponding
                        <replaceable>...-name-pres</replaceable> and <replaceable>...-num-pres</replaceable>
                        fields.</para>
-                       <para>The allowable values for the <replaceable>reason</replaceable>
+                       <para>The recognized values for the <replaceable>reason</replaceable>
                        and <replaceable>orig-reason</replaceable> fields are the following:</para>
                        <enumlist>
-                               <enum name = "unknown"><para>Unknown</para></enum>
+                               <enum name = "away"><para>Callee is Away</para></enum>
+                               <enum name = "cf_dte"><para>Call Forwarding By The Called DTE</para></enum>
                                <enum name = "cfb"><para>Call Forwarding Busy</para></enum>
                                <enum name = "cfnr"><para>Call Forwarding No Reply</para></enum>
-                               <enum name = "unavailable"><para>Callee is Unavailable</para></enum>
-                               <enum name = "time_of_day"><para>Time of Day</para></enum>
-                               <enum name = "dnd"><para>Do Not Disturb</para></enum>
+                               <enum name = "cfu"><para>Call Forwarding Unconditional</para></enum>
                                <enum name = "deflection"><para>Call Deflection</para></enum>
+                               <enum name = "dnd"><para>Do Not Disturb</para></enum>
                                <enum name = "follow_me"><para>Follow Me</para></enum>
                                <enum name = "out_of_order"><para>Called DTE Out-Of-Order</para></enum>
-                               <enum name = "away"><para>Callee is Away</para></enum>
-                               <enum name = "cf_dte"><para>Call Forwarding By The Called DTE</para></enum>
-                               <enum name = "cfu"><para>Call Forwarding Unconditional</para></enum>
+                               <enum name = "send_to_vm"><para>Send the call to voicemail</para></enum>
+                               <enum name = "time_of_day"><para>Time of Day</para></enum>
+                               <enum name = "unavailable"><para>Callee is Unavailable</para></enum>
+                               <enum name = "unknown"><para>Unknown</para></enum>
                        </enumlist>
+                       <note><para>You can set a user defined reason string that SIP can
+                       send/receive instead.  The user defined reason string my need to be
+                       quoted depending upon SIP or the peer's requirements.  These strings
+                       are treated as unknown by the non-SIP channel drivers.</para></note>
                        <para>The allowable values for the <replaceable>xxx-name-charset</replaceable>
                        field are the following:</para>
                        <enumlist>
index 7963f31..a4a4f1b 100644 (file)
@@ -53,6 +53,7 @@ void ast_stun_init(void);               /*!< Provided by stun.c */
 int ast_cel_engine_init(void);         /*!< Provided by cel.c */
 int ast_cel_engine_reload(void);       /*!< Provided by cel.c */
 int ast_ssl_init(void);                 /*!< Provided by ssl.c */
+int ast_pj_init(void);                 /*!< Provided by libasteriskpj.c */
 int ast_test_init(void);            /*!< Provided by test.c */
 int ast_msg_init(void);             /*!< Provided by message.c */
 void ast_msg_shutdown(void);        /*!< Provided by message.c */
index 6ed96cc..43f86b0 100644 (file)
 /* Define to 1 if you have the `cosl' function. */
 #undef HAVE_COSL
 
-/* Define to 1 if you have the `crypt' function. */
+/* Define to 1 if you have the 'crypt' function. */
 #undef HAVE_CRYPT
 
 /* Define to 1 if you have the OpenSSL Cryptography library. */
 #undef HAVE_CRYPTO
 
-/* Define to 1 if you have the `crypt_r' function. */
+/* Define to 1 if you have the 'crypt_r' function. */
 #undef HAVE_CRYPT_R
 
 /* Define to 1 if you have a functional curl library. */
 /* Define if your system has the PJPROJECT libraries. */
 #undef HAVE_PJPROJECT
 
-/* Define to 1 if PJPROJECT has the PJSIP Dialog Create UAS with Incremented
-   Lock feature. */
+/* Define if your system has PJPROJECT_BUNDLED */
+#undef HAVE_PJPROJECT_BUNDLED
+
+/* Define if your system has pjsip_dlg_create_uas_and_inc_lock declared. */
 #undef HAVE_PJSIP_DLG_CREATE_UAS_AND_INC_LOCK
 
-/* Define to 1 if PJPROJECT has the PJSIP External Resolver Support feature.
-   */
+/* Define if your system has pjsip_endpt_set_ext_resolver declared. */
 #undef HAVE_PJSIP_EXTERNAL_RESOLVER
 
-/* Define to 1 if PJPROJECT has the pjsip_get_dest_info support feature. */
+/* Define if your system has pjsip_get_dest_info declared. */
 #undef HAVE_PJSIP_GET_DEST_INFO
 
 /* Define if your system has the PJSIP_REPLACE_MEDIA_STREAM headers. */
 #undef HAVE_PJSIP_REPLACE_MEDIA_STREAM
 
-/* Define to 1 if PJPROJECT has the pj_ssl_cert_load_from_files2 support
-   feature. */
+/* Define if your system has pj_ssl_cert_load_from_files2 declared. */
 #undef HAVE_PJ_SSL_CERT_LOAD_FROM_FILES2
 
-/* Define to 1 if PJPROJECT has the PJSIP Transaction Group Lock Support
-   feature. */
+/* Define if your system has pjsip_tsx_create_uac2 declared. */
 #undef HAVE_PJ_TRANSACTION_GRP_LOCK
 
 /* Define to 1 if your system defines IP_PKTINFO. */
index 66f9be6..797be4e 100644 (file)
@@ -162,6 +162,8 @@ struct ast_bridge_channel {
                struct timeval dtmf_tv;
                /*! Digit currently sending into the bridge. (zero if not sending) */
                char dtmf_digit;
+               /*! Non-zero if a T.38 session terminate is owed to the bridge. */
+               char t38_terminate;
        } owed;
        /*! DTMF hook sequence state */
        struct {
index e3fb73d..7f7d5a8 100644 (file)
@@ -84,7 +84,7 @@ struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *brid
 
 /*!
  * \internal
- * \brief Clear owed events by the channel to the original bridge.
+ * \brief Settle owed events by the channel to the original bridge.
  * \since 12.0.0
  *
  * \param orig_bridge Original bridge the channel was in before leaving.
@@ -118,6 +118,27 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel);
 
 /*!
  * \internal
+ * \brief Push the bridge channel into its specified bridge.
+ * \since 13.8.0
+ *
+ * \param bridge_channel Channel to push.
+ * \param optimized non-zero if the push with swap is for an optimization.
+ *
+ * \note A ref is not held by bridge_channel->swap when calling because the
+ * push with swap happens immediately.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.  The channel did not get pushed.
+ *
+ * \note On failure the caller must call
+ * ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+ */
+int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel, int optimized);
+
+/*!
+ * \internal
  * \brief Pull the bridge channel out of its current bridge.
  * \since 12.0.0
  *
index 384c22d..14bd32c 100644 (file)
@@ -1596,6 +1596,42 @@ void ast_channel_set_unbridged(struct ast_channel *chan, int value);
 void ast_channel_set_unbridged_nolock(struct ast_channel *chan, int value);
 
 /*!
+ * \brief This function will check if T.38 is active on the channel.
+ *
+ * \param chan Channel on which to check the unbridge_eval flag
+ *
+ * \return Returns 0 if the flag is down or 1 if the flag is up.
+ */
+int ast_channel_is_t38_active(struct ast_channel *chan);
+
+/*!
+ * \brief ast_channel_is_t38_active variant. Use this if the channel
+ *         is already locked prior to calling.
+ *
+ * \param chan Channel on which to check the is_t38_active flag
+ *
+ * \return Returns 0 if the flag is down or 1 if the flag is up.
+ */
+int ast_channel_is_t38_active_nolock(struct ast_channel *chan);
+
+/*!
+ * \brief Sets the is_t38_active flag
+ *
+ * \param chan Which channel is having its is_t38_active value set
+ * \param is_t38_active Non-zero if T.38 is active
+ */
+void ast_channel_set_is_t38_active(struct ast_channel *chan, int is_t38_active);
+
+/*!
+ * \brief Variant of ast_channel_set_is_t38_active. Use this if the channel
+ *         is already locked prior to calling.
+ *
+ * \param chan Which channel is having its is_t38_active value set
+ * \param is_t38_active Non-zero if T.38 is active
+ */
+void ast_channel_set_is_t38_active_nolock(struct ast_channel *chan, int is_t38_active);
+
+/*!
  * \brief Lock the given channel, then request softhangup on the channel with the given causecode
  * \param chan channel on which to hang up
  * \param causecode cause code to use (Zero if don't use cause code)
index 491112d..8557072 100644 (file)
@@ -42,6 +42,38 @@ struct stasis_message_type;
 /* ------------------------------------------------------------------- */
 
 /*!
+ * \brief Lock the "chan" and "owner" channels (and return them) on the base
+ *        private structure as well as the base private structure itself.
+ *
+ * \note This also adds references to each of the above mentioned elements and
+ *       also the underlying private local structure.
+ * \note None of these locks should be held prior to calling this function.
+ * \note To undo this process call ast_local_unlock_all.
+ *
+ * \since 13.8.0
+ *
+ * \param chan Must be a local channel
+ * \param outchan The local channel's "chan" channel
+ * \param outowner The local channel's "owner" channel
+ */
+void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan,
+                       struct ast_channel **outowner);
+
+/*!
+ * \brief Unlock the "chan" and "owner" channels on the base private structure
+ *        as well as the base private structure itself.
+ *
+ * \note This also removes references to each of the above mentioned elements and
+ *       also the underlying private local structure.
+ * \note This function should be used in conjunction with ast_local_lock_all.
+ *
+ * \since 13.8.0
+ *
+ * \param chan Must be a local channel
+ */
+void ast_local_unlock_all(struct ast_channel *chan);
+
+/*!
  * \brief Get the other local channel in the pair.
  * \since 12.0.0
  *
index ad34c8f..3008475 100644 (file)
@@ -185,6 +185,8 @@ struct ast_sip_transport {
        unsigned int cos;
        /*! Write timeout */
        int write_timeout;
+       /*! Allow reload */
+       int allow_reload;
 };
 
 #define SIP_SORCERY_DOMAIN_ALIAS_TYPE "domain_alias"
@@ -2223,4 +2225,26 @@ struct ast_sip_transport_state *ast_sip_get_transport_state(const char *transpor
  */
 struct ao2_container *ast_sip_get_transport_states(void);
 
+/*!
+ * \brief Sets pjsip_tpselector from ast_sip_transport
+ * \since 13.8.0
+ *
+ * \param transport The transport to be used
+ * \param selector The selector to be populated
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector);
+
+/*!
+ * \brief Sets pjsip_tpselector from ast_sip_transport
+ * \since 13.8.0
+ *
+ * \param transport_name The name of the transport to be used
+ * \param selector The selector to be populated
+ * \retval 0 success
+ * \retval -1 failure
+ */
+int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector);
+
 #endif /* _RES_PJSIP_H */
index 0f6ec7a..6734033 100644 (file)
@@ -935,7 +935,7 @@ int ast_rtp_instance_set_requested_target_address(struct ast_rtp_instance *insta
  * \since 1.8
  */
 #define ast_rtp_instance_set_remote_address(instance, address) \
-       ast_rtp_instance_set_requested_target_address((instance), (address));
+       ast_rtp_instance_set_requested_target_address((instance), (address))
 
 /*!
  * \brief Set the address that we are expecting to receive RTP on
@@ -1047,7 +1047,7 @@ void ast_rtp_instance_get_requested_target_address(struct ast_rtp_instance *inst
  * \since 1.8
  */
 #define ast_rtp_instance_get_remote_address(instance, address) \
-       ast_rtp_instance_get_incoming_source_address((instance), (address));
+       ast_rtp_instance_get_incoming_source_address((instance), (address))
 
 /*!
  * \brief Get the requested target address of the remote endpoint and
@@ -1083,7 +1083,7 @@ int ast_rtp_instance_get_and_cmp_requested_target_address(struct ast_rtp_instanc
  * \since 1.8
  */
 #define ast_rtp_instance_get_and_cmp_remote_address(instance, address) \
-       ast_rtp_instance_get_and_cmp_requested_target_address((instance), (address));
+       ast_rtp_instance_get_and_cmp_requested_target_address((instance), (address))
 
 /*!
  * \brief Set the value of an RTP instance extended property
index 3ff4656..cb90a5e 100644 (file)
@@ -1,4 +1,6 @@
 asterisk
 libasteriskssl.so.1
 libasteriskssl.dylib
+libasteriskpj.so.2
+libasteriskpj.dylib
 version.c
index b25fb45..50fdc57 100644 (file)
@@ -22,6 +22,9 @@ SRC:=$(wildcard *.c) ast_expr2.c ast_expr2f.c
 ifeq ($(AST_ASTERISKSSL),yes)
 SRC:=$(filter-out libasteriskssl.c,$(SRC))
 endif
+ifeq ($(PJPROJECT_BUNDLED),yes)
+SRC:=$(filter-out libasteriskpj.c,$(SRC))
+endif
 OBJSFILTER=fskmodem_int.o fskmodem_float.o cygload.o buildinfo.o
 OBJS=$(filter-out $(OBJSFILTER),$(SRC:.c=.o))
 
@@ -201,7 +204,7 @@ ASTSSL_LDLIBS=-L. -lasteriskssl
 ifeq ($(findstring darwin,$(OSARCH)),) # not Darwin
 ASTSSL_LIB:=libasteriskssl.so
 
-$(ASTSSL_LIB).$(ASTSSL_SO_VERSION): _ASTLDFLAGS+=-Wl,-soname=$(ASTSSL_LIB).$(ASTSSL_SO_VERSION)
+$(ASTSSL_LIB).$(ASTSSL_SO_VERSION): _ASTLDFLAGS+=-Wl,-soname=$(ASTSSL_LIB)
 $(ASTSSL_LIB).$(ASTSSL_SO_VERSION): _ASTCFLAGS+=-fPIC -DAST_MODULE=\"asteriskssl\" -DAST_NOT_MODULE
 $(ASTSSL_LIB).$(ASTSSL_SO_VERSION): LIBS+=$(ASTSSL_LIBS)
 ifeq ($(GNU_LD),1)
@@ -219,12 +222,14 @@ ifeq ($(GNU_LD),1)
 endif
        $(ECHO_PREFIX) echo "   [LD] $^ -> $@"
        $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(CC_LDFLAGS_SO) $^ $(CC_LIBS)
-ifneq ($(LDCONFIG),)
-       $(LDCONFIG) $(LDCONFIG_FLAGS) .
-endif
 
 $(ASTSSL_LIB): $(ASTSSL_LIB).$(ASTSSL_SO_VERSION)
-       $(LN) -sf $< $@
+       $(ECHO_PREFIX) echo "   [LN] $< -> $@"
+ifneq ($(LDCONFIG),)
+       $(CMD_PREFIX) $(LDCONFIG) $(LDCONFIG_FLAGS) . 2>/dev/null
+else
+       $(CMD_PREFIX) $(LN) -sf $< $@
+endif
 
 else # Darwin
 ASTSSL_LIB:=libasteriskssl.dylib
@@ -244,12 +249,92 @@ endif
 
 endif
 
+libasteriskpj.o: _ASTCFLAGS+=$(PJPROJECT_INCLUDE)
+
+ifeq ($(PJPROJECT_BUNDLED),yes)
+
+ASTPJ_SO_VERSION=2
+ASTPJ_LDLIBS=-L. -lasteriskpj
+
+-include $(ASTTOPDIR)/$(PJPROJECT_DIR)/build.mak
+
+PJPROJECT_LDLIBS := \
+-Wl,--whole-archive \
+$(PJSUA_LIB_LDLIB) \
+$(PJSIP_UA_LDLIB) \
+$(PJSIP_SIMPLE_LDLIB) \
+$(PJSIP_LDLIB) \
+$(PJNATH_LDLIB) \
+$(PJMEDIA_CODEC_LDLIB) \
+$(PJMEDIA_VIDEODEV_LDLIB) \
+$(PJMEDIA_AUDIODEV_LDLIB) \
+$(PJMEDIA_LDLIB) \
+$(PJLIB_UTIL_LDLIB) \
+$(PJLIB_LDLIB) \
+-Wl,--no-whole-archive \
+$(APP_THIRD_PARTY_LIBS) \
+$(APP_THIRD_PARTY_EXT)
+
+ifeq ($(findstring darwin,$(OSARCH)),) # not Darwin
+ASTPJ_LIB:=libasteriskpj.so
+
+libasteriskpj.exports: $(ASTTOPDIR)/$(PJPROJECT_DIR)/pjproject.symbols
+       $(ECHO_PREFIX) echo "   [GENERATE] libasteriskpj.exports"
+ifeq ($(GNU_LD),1)
+       $(CMD_PREFIX) echo -e "{\n\tglobal:" > libasteriskpj.exports
+       $(CMD_PREFIX) sed -r -e "s/.*/\t\t$(LINKER_SYMBOL_PREFIX)&;/" $(ASTTOPDIR)/$(PJPROJECT_DIR)/pjproject.symbols >> libasteriskpj.exports
+       $(CMD_PREFIX) echo -e "\t\t$(LINKER_SYMBOL_PREFIX)ast_pj_init;\n" >> libasteriskpj.exports
+       $(CMD_PREFIX) echo -e "\tlocal:\n\t\t*;\n};" >> libasteriskpj.exports
+endif
+
+$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): _ASTLDFLAGS+=-Wl,-soname=$(ASTPJ_LIB) $(PJ_LDFLAGS)
+$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): _ASTCFLAGS+=-fPIC -DAST_MODULE=\"asteriskpj\" $(PJ_CFLAGS)
+$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): LIBS+=$(PJPROJECT_LDLIBS) -lssl -lcrypto -luuid -lm -lrt -lpthread
+ifeq ($(GNU_LD),1)
+    $(ASTPJ_LIB).$(ASTPJ_SO_VERSION): SO_SUPPRESS_SYMBOLS=-Wl,--version-script,libasteriskpj.exports,--warn-common
+endif
+$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): SOLINK=$(DYLINK)
+
+# These rules are duplicated from $(ASTTOPDIR)/Makefile.rules because the library name
+# being built does not match the "%.so" pattern; there are also additional steps
+# required to build a proper shared library (as opposed to the 'loadable module'
+# type that are built by the standard rules)
+$(ASTPJ_LIB).$(ASTPJ_SO_VERSION): libasteriskpj.o libasteriskpj.exports
+       $(ECHO_PREFIX) echo "   [LD] $< -> $@"
+       $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(CC_LDFLAGS_SO) $< $(CC_LIBS)
+
+$(ASTPJ_LIB): $(ASTPJ_LIB).$(ASTPJ_SO_VERSION)
+       $(ECHO_PREFIX) echo "   [LN] $< -> $@"
+ifneq ($(LDCONFIG),)
+       $(CMD_PREFIX) $(LDCONFIG) $(LDCONFIG_FLAGS) . 2>/dev/null
+else
+       $(CMD_PREFIX) $(LN) -sf $< $@
+endif
+
+else # Darwin
+ASTPJ_LIB:=libasteriskpj.dylib
+
+# -install_name allows library to be found if installed somewhere other than
+# /lib or /usr/lib
+$(ASTPJ_LIB): _ASTLDFLAGS+=-dynamiclib -install_name $(ASTLIBDIR)/$(ASTPJ_LIB) $(PJ_LDFLAGS)
+$(ASTPJ_LIB): _ASTCFLAGS+=-fPIC -DAST_MODULE=\"asteriskpj\" $(PJ_CFLAGS)
+$(ASTPJ_LIB): LIBS+=$(PJPROJECT_LIBS)  -lssl -lcrypto -luuid -lm -lrt -lpthread
+$(ASTPJ_LIB): SOLINK=$(DYLINK)
+
+# Special rules for building a shared library (not a dynamically loadable module)
+$(ASTPJ_LIB): libasteriskpj.o
+       $(ECHO_PREFIX) echo "   [LD] $^ -> $@"
+       $(CMD_PREFIX) $(CC) $(STATIC_BUILD) -o $@ $(CC_LDFLAGS_SO) $^ $(CC_LIBS)
+endif
+
+endif
+
 tcptls.o: _ASTCFLAGS+=$(OPENSSL_INCLUDE)
 
-$(MAIN_TGT): $(OBJS) $(ASTSSL_LIB) $(LIBEDIT_OBJ) $(AST_EMBED_LDSCRIPTS)
+$(MAIN_TGT): $(OBJS) $(ASTSSL_LIB) $(ASTPJ_LIB) $(LIBEDIT_OBJ) $(AST_EMBED_LDSCRIPTS)
        @$(CC) -c -o buildinfo.o $(_ASTCFLAGS) buildinfo.c $(ASTCFLAGS)
        $(ECHO_PREFIX) echo "   [LD] $(OBJS) $(LIBEDIT_OBJ) $(AST_EMBED_LDSCRIPTS) -> $@"
-       $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(_ASTLDFLAGS) $(ASTLDFLAGS) $(OBJS) $(ASTSSL_LDLIBS) $(LIBEDIT_OBJ) $(AST_EMBED_LDSCRIPTS) buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(GMIMELDFLAGS) $(LIBEDIT_LIB)
+       $(CMD_PREFIX) $(CXX) $(STATIC_BUILD) -o $@ $(ASTLINK) $(AST_EMBED_LDFLAGS) $(_ASTLDFLAGS) $(ASTLDFLAGS) $(OBJS) $(ASTSSL_LDLIBS) $(ASTPJ_LDLIBS) $(LIBEDIT_OBJ) $(AST_EMBED_LDSCRIPTS) buildinfo.o $(AST_LIBS) $(AST_EMBED_LIBS) $(GMIMELDFLAGS) $(LIBEDIT_LIB)
 
 ifeq ($(GNU_LD),1)
 $(MAIN_TGT): asterisk.exports
@@ -266,16 +351,29 @@ ifeq ($(findstring darwin,$(OSARCH)),) # not Darwin
 else # Darwin
        $(INSTALL) -m 755 $(ASTSSL_LIB) "$(DESTDIR)$(ASTLIBDIR)/"
 endif
+endif
+ifeq ($(PJPROJECT_BUNDLED),yes)
+ifeq ($(findstring darwin,$(OSARCH)),) # not Darwin
+       $(INSTALL) -m 755 $(ASTPJ_LIB).$(ASTPJ_SO_VERSION) "$(DESTDIR)$(ASTLIBDIR)/"
+       $(LN) -sf $(ASTPJ_LIB).$(ASTPJ_SO_VERSION) "$(DESTDIR)$(ASTLIBDIR)/$(ASTPJ_LIB)"
+else # Darwin
+       $(INSTALL) -m 755 $(ASTPJ_LIB) "$(DESTDIR)$(ASTLIBDIR)/"
+endif
+endif
 ifneq ($(LDCONFIG),)
        $(LDCONFIG) $(LDCONFIG_FLAGS) "$(DESTDIR)$(ASTLIBDIR)/"
 endif
-endif
        $(LN) -sf asterisk "$(DESTDIR)$(ASTSBINDIR)/rasterisk"
 
 binuninstall:
        rm -f "$(DESTDIR)$(ASTSBINDIR)/$(MAIN_TGT)"
        rm -f "$(DESTDIR)$(ASTSBINDIR)/rasterisk"
+ifneq ($(ASTSSL_LIB).$(ASTSSL_SO_VERSION),.)
        rm -f "$(DESTDIR)$(ASTLIBDIR)/$(ASTSSL_LIB).$(ASTSSL_SO_VERSION)"
+endif
+ifneq ($(ASTPJ_LIB).$(ASTPJ_SO_VERSION),.)
+       rm -f "$(DESTDIR)$(ASTLIBDIR)/$(ASTPJ_LIB).$(ASTPJ_SO_VERSION)"
+endif
 ifneq ($(LDCONFIG),)
        $(LDCONFIG) $(LDCONFIG_FLAGS) "$(DESTDIR)$(ASTLIBDIR)/"
 endif
@@ -285,7 +383,12 @@ clean::
 ifeq ($(AST_ASTERISKSSL),yes)
        rm -f $(ASTSSL_LIB) $(ASTSSL_LIB).*
 endif
-       rm -f asterisk.exports libasteriskssl.exports
+       rm -f libasteriskpj.o
+       rm -f libasteriskpj.so* libasteriskpj.dynlib
+       rm -f .libasteriskpj*
+
+       rm -f asterisk.exports libasteriskssl.exports libasteriskpj.exports
        @if [ -f editline/Makefile ]; then $(MAKE) -C editline distclean ; fi
        @$(MAKE) -C stdtime clean
        rm -f libresample/src/*.o
+       rm -f *.tmp
index ca560cd..da804e1 100644 (file)
@@ -4478,6 +4478,11 @@ static void asterisk_daemon(int isroot, const char *runuser, const char *rungrou
                exit(1);
        }
 
+       if (ast_pj_init()) {
+               printf("Failed: ast_pj_init\n%s", term_quit());
+               exit(1);
+       }
+
        if (app_init()) {
                printf("App core initialization failed.\n%s", term_quit());
                exit(1);
index 7451a16..b9a436e 100644 (file)
@@ -2185,7 +2185,7 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
 
        bridge_channel_moving(bridge_channel, orig_bridge, dst_bridge);
 
-       if (bridge_channel_internal_push(bridge_channel)) {
+       if (bridge_channel_internal_push_full(bridge_channel, optimized)) {
                /* Try to put the channel back into the original bridge. */
                ast_bridge_features_remove(bridge_channel->features,
                        AST_BRIDGE_HOOK_REMOVE_ON_PULL);
@@ -2198,7 +2198,6 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
                                        AST_BRIDGE_HOOK_REMOVE_ON_PULL);
                                ast_bridge_channel_leave_bridge(bridge_channel,
                                        BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause);
-                               bridge_channel_settle_owed_events(orig_bridge, bridge_channel);
                        }
                } else {
                        ast_bridge_channel_leave_bridge(bridge_channel,
@@ -2206,7 +2205,7 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
                        bridge_channel_settle_owed_events(orig_bridge, bridge_channel);
                }
                res = -1;
-       } else {
+       } else if (!optimized) {
                bridge_channel_settle_owed_events(orig_bridge, bridge_channel);
        }
 
@@ -4035,19 +4034,25 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha
        BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
 
        if (bridge2) {
-               RAII_VAR(struct ast_channel *, local_chan2, NULL, ao2_cleanup);
                struct ast_channel *locals[2];
 
-               ast_channel_lock(local_chan);
-               local_chan2 = ast_local_get_peer(local_chan);
-               ast_channel_unlock(local_chan);
-
-               ast_assert(local_chan2 != NULL);
+               /* Have to lock everything just in case a hangup comes in early */
+               ast_local_lock_all(local_chan, &locals[0], &locals[1]);
+               if (!locals[0] || !locals[1]) {
+                       ast_log(LOG_ERROR, "Transfer failed probably due to an early hangup - "
+                               "missing other half of '%s'\n", ast_channel_name(local_chan));
+                       ast_local_unlock_all(local_chan);
+                       ao2_cleanup(local_chan);
+                       return AST_BRIDGE_TRANSFER_FAIL;
+               }
 
-               locals[0] = local_chan;
-               locals[1] = local_chan2;
+               /* Make sure the peer is properly set */
+               if (local_chan != locals[0]) {
+                       SWAP(locals[0], locals[1]);
+               }
 
                ast_attended_transfer_message_add_link(transfer_msg, locals);
+               ast_local_unlock_all(local_chan);
        } else {
                ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
        }
index f3483e4..8172660 100644 (file)
@@ -637,6 +637,8 @@ void ast_bridge_channel_kick(struct ast_bridge_channel *bridge_channel, int caus
  */
 static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
 {
+       const struct ast_control_t38_parameters *t38_parameters;
+
        ast_assert(frame->frametype != AST_FRAME_BRIDGE_ACTION_SYNC);
 
        ast_bridge_channel_lock_bridge(bridge_channel);
@@ -663,6 +665,27 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
                 * We explicitly will not remember HOLD/UNHOLD frames because
                 * things like attended transfers will handle them.
                 */
+               switch (frame->subclass.integer) {
+               case AST_CONTROL_T38_PARAMETERS:
+                       t38_parameters = frame->data.ptr;
+                       switch (t38_parameters->request_response) {
+                       case AST_T38_REQUEST_NEGOTIATE:
+                       case AST_T38_NEGOTIATED:
+                               bridge_channel->owed.t38_terminate = 1;
+                               break;
+                       case AST_T38_REQUEST_TERMINATE:
+                       case AST_T38_TERMINATED:
+                       case AST_T38_REFUSED:
+                               bridge_channel->owed.t38_terminate = 0;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               default:
+                       break;
+               }
+               break;
        default:
                break;
        }
@@ -675,6 +698,23 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
        return 0;
 }
 
+/*!
+ * \internal
+ * \brief Cancel owed events by the channel to the bridge.
+ * \since 13.8.0
+ *
+ * \param bridge_channel Channel that owes events to the bridge.
+ *
+ * \note On entry, the bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_cancel_owed_events(struct ast_bridge_channel *bridge_channel)
+{
+       bridge_channel->owed.dtmf_digit = '\0';
+       bridge_channel->owed.t38_terminate = 0;
+}
+
 void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct ast_bridge_channel *bridge_channel)
 {
        if (bridge_channel->owed.dtmf_digit) {
@@ -694,6 +734,23 @@ void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct as
                bridge_channel->owed.dtmf_digit = '\0';
                orig_bridge->technology->write(orig_bridge, NULL, &frame);
        }
+       if (bridge_channel->owed.t38_terminate) {
+               struct ast_control_t38_parameters t38_parameters = {
+                       .request_response = AST_T38_TERMINATED,
+               };
+               struct ast_frame frame = {
+                       .frametype = AST_FRAME_CONTROL,
+                       .subclass.integer = AST_CONTROL_T38_PARAMETERS,
+                       .data.ptr = &t38_parameters,
+                       .datalen = sizeof(t38_parameters),
+                       .src = "Bridge channel owed T.38 terminate",
+               };
+
+               ast_debug(1, "T.38 terminate simulated to bridge %s because %s left.\n",
+                       orig_bridge->uniqueid, ast_channel_name(bridge_channel->chan));
+               bridge_channel->owed.t38_terminate = 0;
+               orig_bridge->technology->write(orig_bridge, NULL, &frame);
+       }
 }
 
 /*!
@@ -2037,7 +2094,7 @@ void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
        ast_bridge_publish_leave(bridge, bridge_channel->chan);
 }
 
-int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
+int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel, int optimized)
 {
        struct ast_bridge *bridge = bridge_channel->bridge;
        struct ast_bridge_channel *swap;
@@ -2073,6 +2130,9 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
                /* This flag is cleared so the act of this channel leaving does not cause it to dissolve if need be */
                ast_clear_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
 
+               if (optimized) {
+                       bridge_channel_cancel_owed_events(swap);
+               }
                ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
                bridge_channel_internal_pull(swap);
 
@@ -2112,6 +2172,11 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
        return 0;
 }
 
+int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
+{
+       return bridge_channel_internal_push_full(bridge_channel, 0);
+}
+
 /*!
  * \internal
  * \brief Handle bridge channel control frame action.
@@ -2712,6 +2777,18 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel,
                        ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end");
        }
 
+       /* Complete any T.38 session before exiting the bridge. */
+       if (ast_channel_is_t38_active(bridge_channel->chan)) {
+               struct ast_control_t38_parameters t38_parameters = {
+                       .request_response = AST_T38_TERMINATED,
+               };
+
+               ast_debug(1, "Channel %s simulating T.38 terminate for bridge end.\n",
+                       ast_channel_name(bridge_channel->chan));
+               ast_indicate_data(bridge_channel->chan, AST_CONTROL_T38_PARAMETERS,
+                       &t38_parameters, sizeof(t38_parameters));
+       }
+
        /* Indicate a source change since this channel is leaving the bridge system. */
        ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
 
index 69fe6ff..d2b3218 100644 (file)
@@ -1209,7 +1209,16 @@ static const struct ast_value_translation redirecting_reason_types[] = {
        { AST_REDIRECTING_REASON_OUT_OF_ORDER,   "out_of_order", "Called DTE Out-Of-Order" },
        { AST_REDIRECTING_REASON_AWAY,           "away",         "Callee is Away" },
        { AST_REDIRECTING_REASON_CALL_FWD_DTE,   "cf_dte",       "Call Forwarding By The Called DTE" },
-       { AST_REDIRECTING_REASON_SEND_TO_VM,     "send_to_vm",   "Call is being redirected to user's voicemail"},
+       { AST_REDIRECTING_REASON_SEND_TO_VM,     "send_to_vm",   "Call is being redirected to user's voicemail" },
+
+       /* Convenience SIP aliases.  Alias descriptions are not used. */
+       { AST_REDIRECTING_REASON_USER_BUSY,      "user-busy" },
+       { AST_REDIRECTING_REASON_NO_ANSWER,      "no-answer" },
+       { AST_REDIRECTING_REASON_UNCONDITIONAL,  "unconditional" },
+       { AST_REDIRECTING_REASON_TIME_OF_DAY,    "time-of-day" },
+       { AST_REDIRECTING_REASON_DO_NOT_DISTURB, "do-not-disturb" },
+       { AST_REDIRECTING_REASON_FOLLOW_ME,      "follow-me" },
+       { AST_REDIRECTING_REASON_OUT_OF_ORDER,   "out-of-service" },
 /* *INDENT-ON* */
 };
 
@@ -1232,7 +1241,7 @@ const char *ast_redirecting_reason_describe(int data)
 
        for (index = 0; index < ARRAY_LEN(redirecting_reason_types); ++index) {
                if (redirecting_reason_types[index].value == data) {
-                       return redirecting_reason_types[index].description;
+                       return redirecting_reason_types[index].description ?: "Redirecting reason alias-bug";
                }
        }
 
index f2d5168..a0d0ad7 100644 (file)
@@ -541,7 +541,7 @@ static int ast_cel_track_event(enum ast_cel_event_type et)
                return 0;
        }
 
-       return (cfg->general->events & ((int64_t) 1 << et));
+       return (cfg->general->events & ((int64_t) 1 << et)) ? 1 : 0;
 }
 
 static int events_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
index f5a9afa..852de1a 100644 (file)
@@ -4473,67 +4473,47 @@ static int indicate_redirecting(struct ast_channel *chan, const void *data, size
        return res ? -1 : 0;
 }
 
-int ast_indicate_data(struct ast_channel *chan, int _condition,
-               const void *data, size_t datalen)
+static int indicate_data_internal(struct ast_channel *chan, int _condition, const void *data, size_t datalen)
 {
        /* By using an enum, we'll get compiler warnings for values not handled
         * in switch statements. */
        enum ast_control_frame_type condition = _condition;
        struct ast_tone_zone_sound *ts = NULL;
+       const struct ast_control_t38_parameters *t38_parameters;
        int res;
-       /* this frame is used by framehooks. if it is set, we must free it at the end of this function */
-       struct ast_frame *awesome_frame = NULL;
-
-       ast_channel_lock(chan);
-
-       /* Don't bother if the channel is about to go away, anyway. */
-       if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
-                       || (ast_check_hangup(chan) && !ast_channel_is_leaving_bridge(chan)))
-               && condition != AST_CONTROL_MASQUERADE_NOTIFY) {
-               res = -1;
-               goto indicate_cleanup;
-       }
-
-       if (!ast_framehook_list_is_empty(ast_channel_framehooks(chan))) {
-               /* Do framehooks now, do it, go, go now */
-               struct ast_frame frame = {
-                       .frametype = AST_FRAME_CONTROL,
-                       .subclass.integer = condition,
-                       .data.ptr = (void *) data, /* this cast from const is only okay because we do the ast_frdup below */
-                       .datalen = datalen
-               };
-
-               /* we have now committed to freeing this frame */
-               awesome_frame = ast_frdup(&frame);
-
-               /* who knows what we will get back! the anticipation is killing me. */
-               if (!(awesome_frame = ast_framehook_list_write_event(ast_channel_framehooks(chan), awesome_frame))
-                       || awesome_frame->frametype != AST_FRAME_CONTROL) {
-                       res = 0;
-                       goto indicate_cleanup;
-               }
-
-               condition = awesome_frame->subclass.integer;
-               data = awesome_frame->data.ptr;
-               datalen = awesome_frame->datalen;
-       }
 
        switch (condition) {
        case AST_CONTROL_CONNECTED_LINE:
                if (indicate_connected_line(chan, data, datalen)) {
                        res = 0;
-                       goto indicate_cleanup;
+                       return res;
                }
                break;
        case AST_CONTROL_REDIRECTING:
                if (indicate_redirecting(chan, data, datalen)) {
                        res = 0;
-                       goto indicate_cleanup;
+                       return res;
                }
                break;
        case AST_CONTROL_HOLD:
        case AST_CONTROL_UNHOLD:
-               ast_channel_hold_state_set(chan, condition);
+               ast_channel_hold_state_set(chan, _condition);
+               break;
+       case AST_CONTROL_T38_PARAMETERS:
+               t38_parameters = data;
+               switch (t38_parameters->request_response) {
+               case AST_T38_REQUEST_NEGOTIATE:
+               case AST_T38_NEGOTIATED:
+                       ast_channel_set_is_t38_active_nolock(chan, 1);
+                       break;
+               case AST_T38_REQUEST_TERMINATE:
+               case AST_T38_TERMINATED:
+               case AST_T38_REFUSED:
+                       ast_channel_set_is_t38_active_nolock(chan, 0);
+                       break;
+               default:
+                       break;
+               }
                break;
        default:
                break;
@@ -4541,7 +4521,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
        if (is_visible_indication(condition)) {
                /* A new visible indication is requested. */
-               ast_channel_visible_indication_set(chan, condition);
+               ast_channel_visible_indication_set(chan, _condition);
        } else if (condition == AST_CONTROL_UNHOLD || _condition < 0) {
                /* Visible indication is cleared/stopped. */
                ast_channel_visible_indication_set(chan, 0);
@@ -4549,7 +4529,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
        if (ast_channel_tech(chan)->indicate) {
                /* See if the channel driver can handle this condition. */
-               res = ast_channel_tech(chan)->indicate(chan, condition, data, datalen);
+               res = ast_channel_tech(chan)->indicate(chan, _condition, data, datalen);
        } else {
                res = -1;
        }
@@ -4557,7 +4537,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        if (!res) {
                /* The channel driver successfully handled this indication */
                res = 0;
-               goto indicate_cleanup;
+               return res;
        }
 
        /* The channel driver does not support this indication, let's fake
@@ -4570,7 +4550,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                /* Stop any tones that are playing */
                ast_playtones_stop(chan);
                res = 0;
-               goto indicate_cleanup;
+               return res;
        }
 
        /* Handle conditions that we have tones for. */
@@ -4578,7 +4558,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        case _XXX_AST_CONTROL_T38:
                /* deprecated T.38 control frame */
                res = -1;
-               goto indicate_cleanup;
+               return res;
        case AST_CONTROL_T38_PARAMETERS:
                /* there is no way to provide 'default' behavior for these
                 * control frames, so we need to return failure, but there
@@ -4587,7 +4567,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                 * so just return right now. in addition, we want to return
                 * whatever value the channel driver returned, in case it
                 * has some meaning.*/
-               goto indicate_cleanup;
+               return res;
        case AST_CONTROL_RINGING:
                ts = ast_get_indication_tone(ast_channel_zone(chan), "ring");
                /* It is common practice for channel drivers to return -1 if trying
@@ -4670,6 +4650,53 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                ast_log(LOG_WARNING, "Unable to handle indication %u for '%s'\n", condition, ast_channel_name(chan));
        }
 
+       return res;
+}
+
+int ast_indicate_data(struct ast_channel *chan, int _condition, const void *data, size_t datalen)
+{
+       int res;
+       /* this frame is used by framehooks. if it is set, we must free it at the end of this function */
+       struct ast_frame *awesome_frame = NULL;
+
+       ast_channel_lock(chan);
+
+       /* Don't bother if the channel is about to go away, anyway. */
+       if ((ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
+                       || (ast_check_hangup(chan) && !ast_channel_is_leaving_bridge(chan)))
+               && _condition != AST_CONTROL_MASQUERADE_NOTIFY) {
+               res = -1;
+               goto indicate_cleanup;
+       }
+
+       if (!ast_framehook_list_is_empty(ast_channel_framehooks(chan))) {
+               /* Do framehooks now, do it, go, go now */
+               struct ast_frame frame = {
+                       .frametype = AST_FRAME_CONTROL,
+                       .subclass.integer = _condition,
+                       .data.ptr = (void *) data, /* this cast from const is only okay because we do the ast_frdup below */
+                       .datalen = datalen
+               };
+
+               /* we have now committed to freeing this frame */
+               awesome_frame = ast_frdup(&frame);
+
+               /* who knows what we will get back! the anticipation is killing me. */
+               awesome_frame = ast_framehook_list_write_event(ast_channel_framehooks(chan),
+                       awesome_frame);
+               if (!awesome_frame
+                       || awesome_frame->frametype != AST_FRAME_CONTROL) {
+                       res = 0;
+                       goto indicate_cleanup;
+               }
+
+               _condition = awesome_frame->subclass.integer;
+               data = awesome_frame->data.ptr;
+               datalen = awesome_frame->datalen;
+       }
+
+       res = indicate_data_internal(chan, _condition, data, datalen);
+
 indicate_cleanup:
        ast_channel_unlock(chan);
        if (awesome_frame) {
@@ -5012,10 +5039,15 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                                res = ast_senddigit_end(chan, fr->subclass.integer, fr->len);
                                ast_channel_lock(chan);
                                CHECK_BLOCKING(chan);
-                       } else if (fr->frametype == AST_FRAME_CONTROL && fr->subclass.integer == AST_CONTROL_UNHOLD) {
-                               /* This is a side case where Echo is basically being called and the person put themselves on hold and took themselves off hold */
-                               res = (ast_channel_tech(chan)->indicate == NULL) ? 0 :
-                                       ast_channel_tech(chan)->indicate(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+                       } else if (fr->frametype == AST_FRAME_CONTROL
+                               && fr->subclass.integer == AST_CONTROL_UNHOLD) {
+                               /*
+                                * This is a side case where Echo is basically being called
+                                * and the person put themselves on hold and took themselves
+                                * off hold.
+                                */
+                               indicate_data_internal(chan, fr->subclass.integer, fr->data.ptr,
+                                       fr->datalen);
                        }
                        res = 0;        /* XXX explain, why 0 ? */
                        goto done;
@@ -5027,8 +5059,8 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
        CHECK_BLOCKING(chan);
        switch (fr->frametype) {
        case AST_FRAME_CONTROL:
-               res = (ast_channel_tech(chan)->indicate == NULL) ? 0 :
-                       ast_channel_tech(chan)->indicate(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+               indicate_data_internal(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+               res = 0;
                break;
        case AST_FRAME_DTMF_BEGIN:
                if (ast_channel_audiohooks(chan)) {
index 987602d..d94b267 100644 (file)
@@ -173,8 +173,6 @@ struct ast_channel {
                                                         *   See \arg \ref AstFileDesc */
        int softhangup;                         /*!< Whether or not we have been hung up...  Do not set this value
                                                         *   directly, use ast_softhangup() */
-       int unbridged;              /*!< If non-zero, the bridge core needs to re-evaluate the current
-                                        bridging technology which is in use by this channel's bridge. */
        int fdno;                                       /*!< Which fd had an event detected on */
        int streamid;                                   /*!< For streaming playback, the schedule ID */
        int vstreamid;                                  /*!< For streaming video playback, the schedule ID */
@@ -216,6 +214,9 @@ struct ast_channel {
        char exten[AST_MAX_EXTENSION];                  /*!< Dialplan: Current extension number */
        char macrocontext[AST_MAX_CONTEXT];             /*!< Macro: Current non-macro context. See app_macro.c */
        char macroexten[AST_MAX_EXTENSION];             /*!< Macro: Current non-macro extension. See app_macro.c */
+       char unbridged;                                                 /*!< non-zero if the bridge core needs to re-evaluate the current
+                                                                                        bridging technology which is in use by this channel's bridge. */
+       char is_t38_active;                                             /*!< non-zero if T.38 is active on this channel. */
        char dtmf_digit_to_emulate;                     /*!< Digit being emulated */
        char sending_dtmf_digit;                        /*!< Digit this channel is currently sending out. (zero if not sending) */
        struct timeval sending_dtmf_tv;         /*!< The time this channel started sending the current digit. (Invalid if sending_dtmf_digit is zero.) */
@@ -1146,7 +1147,7 @@ int ast_channel_unbridged(struct ast_channel *chan)
 
 void ast_channel_set_unbridged_nolock(struct ast_channel *chan, int value)
 {
-       chan->unbridged = value;
+       chan->unbridged = !!value;
        ast_queue_frame(chan, &ast_null_frame);
 }
 
@@ -1157,6 +1158,33 @@ void ast_channel_set_unbridged(struct ast_channel *chan, int value)
        ast_channel_unlock(chan);
 }
 
+int ast_channel_is_t38_active_nolock(struct ast_channel *chan)
+{
+       return chan->is_t38_active;
+}
+
+int ast_channel_is_t38_active(struct ast_channel *chan)
+{
+       int res;
+
+       ast_channel_lock(chan);
+       res = ast_channel_is_t38_active_nolock(chan);
+       ast_channel_unlock(chan);
+       return res;
+}
+
+void ast_channel_set_is_t38_active_nolock(struct ast_channel *chan, int is_t38_active)
+{
+       chan->is_t38_active = !!is_t38_active;
+}
+
+void ast_channel_set_is_t38_active(struct ast_channel *chan, int is_t38_active)
+{
+       ast_channel_lock(chan);
+       ast_channel_set_is_t38_active_nolock(chan, is_t38_active);
+       ast_channel_unlock(chan);
+}
+
 void ast_channel_callid_cleanup(struct ast_channel *chan)
 {
        chan->callid = 0;
index f81c71c..6644aaf 100644 (file)
@@ -235,6 +235,45 @@ struct local_pvt {
        char exten[AST_MAX_EXTENSION];
 };
 
+void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan,
+                       struct ast_channel **outowner)
+{
+       struct local_pvt *p = ast_channel_tech_pvt(chan);
+
+       *outchan = NULL;
+       *outowner = NULL;
+
+       if (p) {
+               ao2_ref(p, 1);
+               ast_unreal_lock_all(&p->base, outchan, outowner);
+       }
+}
+
+void ast_local_unlock_all(struct ast_channel *chan)
+{
+       struct local_pvt *p = ast_channel_tech_pvt(chan);
+       struct ast_unreal_pvt *base;
+
+       if (!p) {
+               return;
+       }
+
+       base = &p->base;
+
+       if (base->owner) {
+               ast_channel_unlock(base->owner);
+               ast_channel_unref(base->owner);
+       }
+
+       if (base->chan) {
+               ast_channel_unlock(base->chan);
+               ast_channel_unref(base->chan);
+       }
+
+       ao2_unlock(base);
+       ao2_ref(p, -1);
+}
+
 struct ast_channel *ast_local_get_peer(struct ast_channel *ast)
 {
        struct local_pvt *p = ast_channel_tech_pvt(ast);
diff --git a/main/libasteriskpj.c b/main/libasteriskpj.c
new file mode 100644 (file)
index 0000000..aed0ec8
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009-2012, Digium, Inc.
+ * Copyright (C) 2015, Fairview 5 Engineering, LLC
+ *
+ * Russell Bryant <russell@digium.com>
+ * George Joseph <george.joseph@fairview5.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
+ * \brief Loader stub for static pjproject libraries
+ *
+ * \author George Joseph <george.joseph@fairview5.com>
+ */
+
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#ifdef HAVE_PJPROJECT
+#include <pjlib.h>
+#endif
+
+#include "asterisk/_private.h" /* ast_pj_init() */
+
+/*!
+ * \internal
+ * \brief Initialize static pjproject implementation
+ */
+int ast_pj_init(void)
+{
+#ifdef HAVE_PJPROJECT_BUNDLED
+       pj_init();
+#endif
+       return 0;
+}
index 954b288..f959221 100644 (file)
@@ -515,9 +515,11 @@ static void unload_dynamic_module(struct ast_module *mod)
 #endif
 }
 
-static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, struct ast_heap *resource_heap, int required);
+static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap, int required);
 
-static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, struct ast_heap *resource_heap)
+#define MODULE_LOCAL_ONLY (void *)-1
+
+static struct ast_module *load_dynamic_module(const char *resource_in, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap)
 {
        char fn[PATH_MAX] = "";
        void *lib = NULL;
@@ -545,8 +547,10 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
        if (missing_so)
                strcat(resource_being_loaded->resource, ".so");
 
-       if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_LOCAL))) {
-               ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+       if (!(lib = dlopen(fn, RTLD_LAZY | RTLD_GLOBAL))) {
+               if (!suppress_logging) {
+                       ast_log(LOG_WARNING, "Error loading module '%s': %s\n", resource_in, dlerror());
+               }
                ast_free(resource_being_loaded);
                return NULL;
        }
@@ -573,7 +577,7 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
           and this one does not, then close it and return */
        if (global_symbols_only && !wants_global) {
                logged_dlclose(resource_in, lib);
-               return NULL;
+               return MODULE_LOCAL_ONLY;
        }
 
        logged_dlclose(resource_in, lib);
@@ -1053,7 +1057,7 @@ static enum ast_module_load_result start_resource(struct ast_module *mod)
  *
  *  If the ast_heap is not provided, the module's load function will be executed
  *  immediately */
-static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, struct ast_heap *resource_heap, int required)
+static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, unsigned int suppress_logging, struct ast_heap *resource_heap, int required)
 {
        struct ast_module *mod;
        enum ast_module_load_result res = AST_MODULE_LOAD_SUCCESS;
@@ -1067,14 +1071,15 @@ static enum ast_module_load_result load_resource(const char *resource_name, unsi
                        return AST_MODULE_LOAD_SKIP;
        } else {
 #ifdef LOADABLE_MODULES
-               if (!(mod = load_dynamic_module(resource_name, global_symbols_only, resource_heap))) {
-                       /* don't generate a warning message during load_modules() */
+               mod = load_dynamic_module(resource_name, global_symbols_only, suppress_logging, resource_heap);
+               if (mod == MODULE_LOCAL_ONLY) {
+                               return AST_MODULE_LOAD_SKIP;
+               }
+               if (!mod) {
                        if (!global_symbols_only) {
                                ast_log(LOG_WARNING, "Module '%s' could not be loaded.\n", resource_name);
-                               return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
-                       } else {
-                               return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SKIP;
                        }
+                       return required ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_DECLINE;
                }
 #else
                ast_log(LOG_WARNING, "Module support is not available. Module '%s' could not be loaded.\n", resource_name);
@@ -1111,7 +1116,7 @@ int ast_load_resource(const char *resource_name)
 {
        int res;
        AST_DLLIST_LOCK(&module_list);
-       res = load_resource(resource_name, 0, NULL, 0);
+       res = load_resource(resource_name, 0, 0, NULL, 0);
        if (!res) {
                ast_test_suite_event_notify("MODULE_LOAD", "Message: %s", resource_name);
        }
@@ -1168,6 +1173,8 @@ static int mod_load_cmp(void *a, void *b)
        return b_pri - a_pri;
 }
 
+AST_LIST_HEAD_NOLOCK(load_retries, load_order_entry);
+
 /*! loads modules in order by load_pri, updates mod_count
        \return -1 on failure to load module, -2 on failure to load required module, otherwise 0
 */
@@ -1176,8 +1183,13 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
        struct ast_heap *resource_heap;
        struct load_order_entry *order;
        struct ast_module *mod;
+       struct load_retries load_retries;
        int count = 0;
        int res = 0;
+       int i = 0;
+#define LOAD_RETRIES 4
+
+       AST_LIST_HEAD_INIT_NOLOCK(&load_retries);
 
        if(!(resource_heap = ast_heap_create(8, mod_load_cmp, -1))) {
                return -1;
@@ -1185,21 +1197,34 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
 
        /* first, add find and add modules to heap */
        AST_LIST_TRAVERSE_SAFE_BEGIN(load_order, order, entry) {
-               switch (load_resource(order->resource, global_symbols, resource_heap, order->required)) {
+               enum ast_module_load_result lres;
+
+               /* Suppress log messages unless this is the last pass */
+               lres = load_resource(order->resource, global_symbols, 1, resource_heap, order->required);
+               ast_debug(3, "PASS 0: %-46s %d %d\n", order->resource, lres, global_symbols);
+               switch (lres) {
                case AST_MODULE_LOAD_SUCCESS:
+                       /* We're supplying a heap so SUCCESS isn't possible but we still have to test for it. */
+                       break;
+               case AST_MODULE_LOAD_FAILURE:
                case AST_MODULE_LOAD_DECLINE:
+                       /*
+                        * DECLINE or FAILURE means there was an issue with dlopen or module_register
+                        * which might be retryable.  LOAD_FAILURE only happens for required modules
+                        * but we're still going to retry.  We need to remove the entry from the
+                        * load_order list and add it to the load_retries list.
+                        */
                        AST_LIST_REMOVE_CURRENT(entry);
-                       ast_free(order->resource);
-                       ast_free(order);
+                       AST_LIST_INSERT_TAIL(&load_retries, order, entry);
                        break;
-               case AST_MODULE_LOAD_FAILURE:
-                       ast_log(LOG_ERROR, "*** Failed to load module %s - %s\n", order->resource, order->required ? "Required" : "Not required");
-                       fprintf(stderr, "*** Failed to load module %s - %s\n", order->resource, order->required ? "Required" : "Not required");
-                       res = order->required ? -2 : -1;
-                       goto done;
                case AST_MODULE_LOAD_SKIP:
+                       /*
+                        * SKIP means that dlopen worked but global_symbols was set and this module doesn't qualify.
+                        * Leave it in load_order for the next call of load_resource_list.
+                        */
                        break;
                case AST_MODULE_LOAD_PRIORITY:
+                       /* load_resource worked and the module was added to the priority heap */
                        AST_LIST_REMOVE_CURRENT(entry);
                        ast_free(order->resource);
                        ast_free(order);
@@ -1208,9 +1233,56 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
        }
        AST_LIST_TRAVERSE_SAFE_END;
 
+       /* Retry the failures until the list is empty or we reach LOAD_RETRIES */
+       for (i = 0; !AST_LIST_EMPTY(&load_retries) && i < LOAD_RETRIES; i++) {
+               AST_LIST_TRAVERSE_SAFE_BEGIN(&load_retries, order, entry) {
+                       enum ast_module_load_result lres;
+
+                       /* Suppress log messages unless this is the last pass */
+                       lres = load_resource(order->resource, global_symbols, (i < LOAD_RETRIES - 1), resource_heap, order->required);
+                       ast_debug(3, "PASS %d %-46s %d %d\n", i + 1, order->resource, lres, global_symbols);
+                       switch (lres) {
+                       /* These are all retryable. */
+                       case AST_MODULE_LOAD_SUCCESS:
+                       case AST_MODULE_LOAD_DECLINE:
+                               break;
+                       case AST_MODULE_LOAD_FAILURE:
+                               /* LOAD_FAILURE only happens for required modules */
+                               if (i == LOAD_RETRIES - 1) {
+                                       /* This was the last chance to load a required module*/
+                                       ast_log(LOG_ERROR, "*** Failed to load module %s - Required\n", order->resource);
+                                       fprintf(stderr, "*** Failed to load module %s - Required\n", order->resource);
+                                       res =  -2;
+                                       goto done;
+                               }
+                               break;;
+                       case AST_MODULE_LOAD_SKIP:
+                               /*
+                                * SKIP means that dlopen worked but global_symbols was set and this module
+                                * doesn't qualify.  Put it back in load_order for the next call of
+                                * load_resource_list.
+                                */
+                               AST_LIST_REMOVE_CURRENT(entry);
+                               AST_LIST_INSERT_TAIL(load_order, order, entry);
+                               break;
+                       case AST_MODULE_LOAD_PRIORITY:
+                               /* load_resource worked and the module was added to the priority heap */
+                               AST_LIST_REMOVE_CURRENT(entry);
+                               ast_free(order->resource);
+                               ast_free(order);
+                               break;
+                       }
+               }
+               AST_LIST_TRAVERSE_SAFE_END;
+       }
+
        /* second remove modules from heap sorted by priority */
        while ((mod = ast_heap_pop(resource_heap))) {
-               switch (start_resource(mod)) {
+               enum ast_module_load_result lres;
+
+               lres = start_resource(mod);
+               ast_debug(3, "START: %-46s %d %d\n", mod->resource, lres, global_symbols);
+               switch (lres) {
                case AST_MODULE_LOAD_SUCCESS:
                        count++;
                case AST_MODULE_LOAD_DECLINE:
@@ -1225,6 +1297,12 @@ static int load_resource_list(struct load_order *load_order, unsigned int global
        }
 
 done:
+
+       while ((order = AST_LIST_REMOVE_HEAD(&load_retries, entry))) {
+               ast_free(order->resource);
+               ast_free(order);
+       }
+
        if (mod_count) {
                *mod_count += count;
        }
index 7098f69..54c604c 100644 (file)
@@ -398,7 +398,7 @@ static void msg_destructor(void *obj)
        struct ast_msg *msg = obj;
 
        ast_string_field_free_memory(msg);
-       ao2_ref(msg->vars, -1);
+       ao2_cleanup(msg->vars);
 }
 
 struct ast_msg *ast_msg_alloc(void)
index a445a6e..ec340e8 100644 (file)
@@ -1957,7 +1957,12 @@ static int sorcery_wizard_create(void *obj, void *arg, int flags)
                ast_debug(5, "Sorcery wizard '%s' does not support creation\n", object_wizard->wizard->callbacks.name);
                return 0;
        }
-       return (!object_wizard->caching && !object_wizard->wizard->callbacks.create(details->sorcery, object_wizard->data, details->obj)) ? CMP_MATCH | CMP_STOP : 0;
+
+       if (object_wizard->wizard->callbacks.create(details->sorcery, object_wizard->data, details->obj)) {
+               return 0;
+       }
+
+       return CMP_MATCH;
 }
 
 /*! \brief Internal callback function which notifies an individual observer that an object has been created */
@@ -2002,17 +2007,31 @@ int ast_sorcery_create(const struct ast_sorcery *sorcery, void *object)
        AST_VECTOR_RW_RDLOCK(&object_type->wizards);
        for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
                found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
-               if (sorcery_wizard_create(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+               if (!found_wizard->caching && sorcery_wizard_create(found_wizard, &sdetails, 0) == CMP_MATCH) {
                        object_wizard = found_wizard;
-                       if(ao2_container_count(object_type->observers)) {
-                               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+               }
+       }
 
-                               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create, invocation)) {
-                                       ao2_cleanup(invocation);
-                               }
+       if (object_wizard) {
+               for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+                       found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+                       if (found_wizard->caching) {
+                               sorcery_wizard_create(found_wizard, &sdetails, 0);
+                       }
+               }
+
+               if (ao2_container_count(object_type->observers)) {
+                       struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(
+                               object_type, object);
+
+                       if (invocation
+                               && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_create,
+                                       invocation)) {
+                               ao2_cleanup(invocation);
                        }
                }
        }
+
        AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object_wizard ? 0 : -1;
@@ -2052,8 +2071,11 @@ static int sorcery_wizard_update(void *obj, void *arg, int flags)
                return 0;
        }
 
-       return (!object_wizard->wizard->callbacks.update(details->sorcery, object_wizard->data, details->obj) &&
-               !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
+       if (object_wizard->wizard->callbacks.update(details->sorcery, object_wizard->data, details->obj)) {
+               return 0;
+       }
+
+       return CMP_MATCH;
 }
 
 int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
@@ -2075,17 +2097,31 @@ int ast_sorcery_update(const struct ast_sorcery *sorcery, void *object)
        AST_VECTOR_RW_RDLOCK(&object_type->wizards);
        for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
                found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
-               if (sorcery_wizard_update(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+               if (!found_wizard->caching && sorcery_wizard_update(found_wizard, &sdetails, 0) == CMP_MATCH) {
                        object_wizard = found_wizard;
-                       if (ao2_container_count(object_type->observers)) {
-                               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+               }
+       }
 
-                               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update, invocation)) {
-                                       ao2_cleanup(invocation);
-                               }
+       if (object_wizard) {
+               for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+                       found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+                       if (found_wizard->caching) {
+                               sorcery_wizard_update(found_wizard, &sdetails, 0);
+                       }
+               }
+
+               if (ao2_container_count(object_type->observers)) {
+                       struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(
+                               object_type, object);
+
+                       if (invocation
+                               && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_update,
+                                       invocation)) {
+                               ao2_cleanup(invocation);
                        }
                }
        }
+
        AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object_wizard ? 0 : -1;
@@ -2125,8 +2161,11 @@ static int sorcery_wizard_delete(void *obj, void *arg, int flags)
                return 0;
        }
 
-       return (!object_wizard->wizard->callbacks.delete(details->sorcery, object_wizard->data, details->obj) &&
-               !object_wizard->caching) ? CMP_MATCH | CMP_STOP : 0;
+       if (object_wizard->wizard->callbacks.delete(details->sorcery, object_wizard->data, details->obj)) {
+               return 0;
+       }
+
+       return CMP_MATCH;
 }
 
 int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
@@ -2148,17 +2187,31 @@ int ast_sorcery_delete(const struct ast_sorcery *sorcery, void *object)
        AST_VECTOR_RW_RDLOCK(&object_type->wizards);
        for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
                found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
-               if (sorcery_wizard_delete(found_wizard, &sdetails, 0) == (CMP_MATCH | CMP_STOP)) {
+               if (!found_wizard->caching && sorcery_wizard_delete(found_wizard, &sdetails, 0) == CMP_MATCH) {
                        object_wizard = found_wizard;
-                       if (ao2_container_count(object_type->observers)) {
-                               struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(object_type, object);
+               }
+       }
 
-                               if (invocation && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete, invocation)) {
-                                       ao2_cleanup(invocation);
-                               }
+       if (object_wizard) {
+               for (i = 0; i < AST_VECTOR_SIZE(&object_type->wizards); i++) {
+                       found_wizard = AST_VECTOR_GET(&object_type->wizards, i);
+                       if (found_wizard->caching) {
+                               sorcery_wizard_delete(found_wizard, &sdetails, 0);
+                       }
+               }
+
+               if (ao2_container_count(object_type->observers)) {
+                       struct sorcery_observer_invocation *invocation = sorcery_observer_invocation_alloc(
+                               object_type, object);
+
+                       if (invocation
+                               && ast_taskprocessor_push(object_type->serializer, sorcery_observers_notify_delete,
+                                       invocation)) {
+                               ao2_cleanup(invocation);
                        }
                }
        }
+
        AST_VECTOR_RW_UNLOCK(&object_type->wizards);
 
        return object_wizard ? 0 : -1;
index f606ace..6baef1b 100644 (file)
@@ -229,8 +229,10 @@ OSS_LIB=@OSS_LIB@ @FFMPEG_LIB@ @SDL_LIB@ @SDL_IMAGE_LIB@ @X11_LIB@
 PGSQL_INCLUDE=@PGSQL_INCLUDE@
 PGSQL_LIB=@PGSQL_LIB@
 
+PJPROJECT_BUNDLED=@PJPROJECT_BUNDLED@
 PJPROJECT_INCLUDE=@PJPROJECT_INCLUDE@
 PJPROJECT_LIB=@PJPROJECT_LIB@
+PJPROJECT_DIR=@PJPROJECT_DIR@
 
 POPT_INCLUDE=@POPT_INCLUDE@
 POPT_LIB=@POPT_LIB@
index 0b0a78c..a306123 100644 (file)
@@ -127,8 +127,14 @@ static inline const char *sqlite3_escape_string_helper(struct ast_threadstorage
         * add two quotes, and convert NULL pointers to the word "NULL", but we
         * don't allow those anyway. Just going to use %q for now. */
        struct ast_str *buf = ast_str_thread_get(ts, maxlen);
-       char *tmp = ast_str_buffer(buf);
        char q = ts == &escape_value_buf ? '\'' : '"';
+       char *tmp;
+
+       if (ast_str_size(buf) < maxlen) {
+               /* realloc if buf is too small */
+               ast_str_make_space(&buf, maxlen);
+       }
+       tmp = ast_str_buffer(buf);
 
        ast_str_reset(buf);
        *tmp++ = q; /* Initial quote */
@@ -160,9 +166,15 @@ static const char *sqlite3_escape_column_op(const char *param)
 {
        size_t maxlen = strlen(param) * 2 + sizeof("\"\" =");
        struct ast_str *buf = ast_str_thread_get(&escape_column_buf, maxlen);
-       char *tmp = ast_str_buffer(buf);
+       char *tmp;
        int space = 0;
 
+       if (ast_str_size(buf) < maxlen) {
+               /* realloc if buf is too small */
+               ast_str_make_space(&buf, maxlen);
+       }
+       tmp = ast_str_buffer(buf);
+
        ast_str_reset(buf);
        *tmp++ = '"';
        while ((*tmp++ = *param++)) {
index 9e08bf3..9ed3d57 100644 (file)
        <support_level>core</support_level>
  ***/
 
+/*** DOCUMENTATION
+       <configInfo name="res_pjproject" language="en_US">
+               <synopsis>pjproject common configuration</synopsis>
+               <configFile name="pjproject.conf">
+                       <configObject name="log_mappings">
+                               <synopsis>PJPROJECT to Asterisk Log Level Mapping</synopsis>
+                               <description><para>Warnings and errors in the pjproject libraries are generally handled
+                                       by Asterisk.  In many cases, Asterisk wouldn't even consider them to
+                                       be warnings or errors so the messages emitted by pjproject directly
+                                       are either superfluous or misleading.  The 'log_mappings'
+                                       object allows mapping the pjproject levels to Asterisk levels, or nothing.
+                                       </para>
+                                       <note><para>The id of this object, as well as its type, must be
+                                       'log_mappings' or it won't be found.</para></note>
+                               </description>
+                               <configOption name="type">
+                                       <synopsis>Must be of type 'log_mappings'.</synopsis>
+                               </configOption>
+                               <configOption name="asterisk_error" default="0,1">
+                                       <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_ERROR.</synopsis>
+                               </configOption>
+                               <configOption name="asterisk_warning" default="2">
+                                       <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_WARNING.</synopsis>
+                               </configOption>
+                               <configOption name="asterisk_notice" default="">
+                                       <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_NOTICE.</synopsis>
+                               </configOption>
+                               <configOption name="asterisk_debug" default="3,4,5">
+                                       <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_DEBUG.</synopsis>
+                               </configOption>
+                               <configOption name="asterisk_verbose" default="">
+                                       <synopsis>A comma separated list of pjproject log levels to map to Asterisk LOG_VERBOSE.</synopsis>
+                               </configOption>
+                       </configObject>
+               </configFile>
+       </configInfo>
+ ***/
+
 #include "asterisk.h"
 
 ASTERISK_REGISTER_FILE()
@@ -51,7 +89,9 @@ ASTERISK_REGISTER_FILE()
 #include "asterisk/cli.h"
 #include "asterisk/res_pjproject.h"
 #include "asterisk/vector.h"
+#include "asterisk/sorcery.h"
 
+static struct ast_sorcery *pjproject_sorcery;
 static pj_log_func *log_cb_orig;
 static unsigned decor_orig;
 
@@ -70,6 +110,66 @@ static struct pjproject_log_intercept_data pjproject_log_intercept = {
        .fd = -1,
 };
 
+struct log_mappings {
+       /*! Sorcery object details */
+       SORCERY_OBJECT(details);
+       /*! These are all comma-separated lists of pjproject log levels */
+       AST_DECLARE_STRING_FIELDS(
+               /*! pjproject log levels mapped to Asterisk ERROR */
+               AST_STRING_FIELD(asterisk_error);
+               /*! pjproject log levels mapped to Asterisk WARNING */
+               AST_STRING_FIELD(asterisk_warning);
+               /*! pjproject log levels mapped to Asterisk NOTICE */
+               AST_STRING_FIELD(asterisk_notice);
+               /*! pjproject log levels mapped to Asterisk VERBOSE */
+               AST_STRING_FIELD(asterisk_verbose);
+               /*! pjproject log levels mapped to Asterisk DEBUG */
+               AST_STRING_FIELD(asterisk_debug);
+       );
+};
+
+static struct log_mappings *default_log_mappings;
+
+static struct log_mappings *get_log_mappings(void)
+{
+       struct log_mappings *mappings;
+
+       mappings = ast_sorcery_retrieve_by_id(pjproject_sorcery, "log_mappings", "log_mappings");
+       if (!mappings) {
+               return ao2_bump(default_log_mappings);
+       }
+
+       return mappings;
+}
+
+#define __LOG_SUPPRESS -1
+
+static int get_log_level(int pj_level)
+{
+       RAII_VAR(struct log_mappings *, mappings, get_log_mappings(), ao2_cleanup);
+       unsigned char l;
+
+       if (!mappings) {
+               return __LOG_ERROR;
+       }
+
+       l = '0' + fmin(pj_level, 9);
+
+       if (strchr(mappings->asterisk_error, l)) {
+               return __LOG_ERROR;
+       } else if (strchr(mappings->asterisk_warning, l)) {
+               return __LOG_WARNING;
+       } else if (strchr(mappings->asterisk_notice, l)) {
+               return __LOG_NOTICE;
+       } else if (strchr(mappings->asterisk_verbose, l)) {
+               return __LOG_VERBOSE;
+       } else if (strchr(mappings->asterisk_debug, l)) {
+               return __LOG_DEBUG;
+       }
+
+       return __LOG_SUPPRESS;
+}
+
 static void log_forwarder(int level, const char *data, int len)
 {
        int ast_level;
@@ -89,25 +189,19 @@ static void log_forwarder(int level, const char *data, int len)
                return;
        }
 
-       /* Lower number indicates higher importance */
-       switch (level) {
-       case 0: /* level zero indicates fatal error, according to docs */
-       case 1: /* 1 seems to be used for errors */
-               ast_level = __LOG_ERROR;
-               break;
-       case 2: /* 2 seems to be used for warnings and errors */
-               ast_level = __LOG_WARNING;
-               break;
-       default:
-               ast_level = __LOG_DEBUG;
+       ast_level = get_log_level(level);
+
+       if (ast_level == __LOG_SUPPRESS) {
+               return;
+       }
 
+       if (ast_level == __LOG_DEBUG) {
                /* For levels 3 and up, obey the debug level for res_pjproject */
                mod_level = ast_opt_dbg_module ?
                        ast_debug_get_by_module("res_pjproject") : 0;
                if (option_debug < level && mod_level < level) {
                        return;
                }
-               break;
        }
 
        /* PJPROJECT uses indention to indicate function call depth. We'll prepend
@@ -201,14 +295,105 @@ static char *handle_pjproject_show_buildopts(struct ast_cli_entry *e, int cmd, s
        return CLI_SUCCESS;
 }
 
+static void mapping_destroy(void *object)
+{
+       struct log_mappings *mappings = object;
+
+       ast_string_field_free_memory(mappings);
+}
+
+static void *mapping_alloc(const char *name)
+{
+       struct log_mappings *mappings = ast_sorcery_generic_alloc(sizeof(*mappings), mapping_destroy);
+       if (!mappings) {
+               return NULL;
+       }
+       ast_string_field_init(mappings, 128);
+
+       return mappings;
+}
+
+static char *handle_pjproject_show_log_mappings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_variable *objset;
+       struct ast_variable *i;
+       struct log_mappings *mappings;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "pjproject show log mappings";
+               e->usage =
+                       "Usage: pjproject show log mappings\n"
+                       "       Show pjproject to Asterisk log mappings\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       ast_cli(a->fd, "PJPROJECT to Asterisk log mappings:\n");
+       ast_cli(a->fd, "Asterisk Level   : PJPROJECT log levels\n");
+
+       mappings = get_log_mappings();
+       if (!mappings) {
+               ast_log(LOG_ERROR, "Unable to retrieve pjproject log_mappings\n");
+               return CLI_SUCCESS;
+       }
+
+       objset = ast_sorcery_objectset_create(pjproject_sorcery, mappings);
+       if (!objset) {
+               ao2_ref(mappings, -1);
+               return CLI_SUCCESS;
+       }
+
+       for (i = objset; i; i = i->next) {
+               ast_cli(a->fd, "%-16s : %s\n", i->name, i->value);
+       }
+       ast_variables_destroy(objset);
+
+       ao2_ref(mappings, -1);
+       return CLI_SUCCESS;
+}
+
 static struct ast_cli_entry pjproject_cli[] = {
        AST_CLI_DEFINE(handle_pjproject_show_buildopts, "Show the compiled config of the pjproject in use"),
+       AST_CLI_DEFINE(handle_pjproject_show_log_mappings, "Show pjproject to Asterisk log mappings"),
 };
 
 static int load_module(void)
 {
        ast_debug(3, "Starting PJPROJECT logging to Asterisk logger\n");
 
+       if (!(pjproject_sorcery = ast_sorcery_open())) {
+               ast_log(LOG_ERROR, "Failed to open SIP sorcery failed to open\n");
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       ast_sorcery_apply_default(pjproject_sorcery, "log_mappings", "config", "pjproject.conf,criteria=type=log_mappings");
+       if (ast_sorcery_object_register(pjproject_sorcery, "log_mappings", mapping_alloc, NULL, NULL)) {
+               ast_log(LOG_WARNING, "Failed to register pjproject log_mappings object with sorcery\n");
+               ast_sorcery_unref(pjproject_sorcery);
+               pjproject_sorcery = NULL;
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "type", "", OPT_NOOP_T, 0, 0);
+       ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_debug", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_debug));
+       ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_error", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_error));
+       ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_warning", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_warning));
+       ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_notice", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_notice));
+       ast_sorcery_object_field_register(pjproject_sorcery, "log_mappings", "asterisk_verbose", "",  OPT_STRINGFIELD_T, 0, STRFLDSET(struct log_mappings, asterisk_verbose));
+
+       default_log_mappings = ast_sorcery_alloc(pjproject_sorcery, "log_mappings", "log_mappings");
+       if (!default_log_mappings) {
+               ast_log(LOG_ERROR, "Unable to allocate memory for pjproject log_mappings\n");
+               return AST_MODULE_LOAD_DECLINE;
+       }
+       ast_string_field_set(default_log_mappings, asterisk_error, "0,1");
+       ast_string_field_set(default_log_mappings, asterisk_warning, "2");
+       ast_string_field_set(default_log_mappings, asterisk_debug, "3,4,5");
+
+       ast_sorcery_load(pjproject_sorcery);
+
        pj_init();
 
        decor_orig = pj_log_get_decor();
@@ -247,12 +432,27 @@ static int unload_module(void)
 
        pj_shutdown();
 
+       ao2_cleanup(default_log_mappings);
+       default_log_mappings = NULL;
+
+       ast_sorcery_unref(pjproject_sorcery);
+
        return 0;
 }
 
+static int reload_module(void)
+{
+       if (pjproject_sorcery) {
+               ast_sorcery_reload(pjproject_sorcery);
+       }
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "PJPROJECT Log and Utility Support",
        .support_level = AST_MODULE_SUPPORT_CORE,
        .load = load_module,
        .unload = unload_module,
+       .reload = reload_module,
        .load_pri = AST_MODPRI_CHANNEL_DEPEND - 6,
 );
index e355292..713d94e 100644 (file)
                                                Value is in milliseconds; default is 100 ms.</para>
                                        </description>
                                </configOption>
+                               <configOption name="allow_reload" default="no">
+                                       <synopsis>Allow this transport to be reloaded.</synopsis>
+                                       <description>
+                                               <para>Allow this transport to be reloaded when res_pjsip is reloaded.
+                                               This option defaults to "no" because reloading a transport may disrupt
+                                               in-progress calls.</para>
+                                       </description>
+                               </configOption>
                        </configObject>
                        <configObject name="contact">
                                <synopsis>A way of creating an aliased name to a SIP URI</synopsis>
@@ -2479,22 +2487,14 @@ static int sip_dialog_create_from(pj_pool_t *pool, pj_str_t *from, const char *u
        return 0;
 }
 
-static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector)
+int ast_sip_set_tpselector_from_transport(const struct ast_sip_transport *transport, pjsip_tpselector *selector)
 {
-       RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
        RAII_VAR(struct ast_sip_transport_state *, transport_state, 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);
-       transport_state = ast_sip_get_transport_state(transport_name);
 
-       if (!transport || !transport_state) {
-               ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' for endpoint '%s'\n",
-                       transport_name, ast_sorcery_object_get_id(endpoint));
+       transport_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport));
+       if (!transport_state) {
+               ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport state for '%s'\n",
+                       ast_sorcery_object_get_id(transport));
                return -1;
        }
 
@@ -2517,6 +2517,35 @@ static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpo
        return 0;
 }
 
+int ast_sip_set_tpselector_from_transport_name(const char *transport_name, pjsip_tpselector *selector)
+{
+       RAII_VAR(struct ast_sip_transport *, transport, NULL, ao2_cleanup);
+
+       if (ast_strlen_zero(transport_name)) {
+               return 0;
+       }
+
+       transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_name);
+       if (!transport) {
+               ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s'\n",
+                       transport_name);
+               return -1;
+       }
+
+       return ast_sip_set_tpselector_from_transport(transport, selector);
+}
+
+static int sip_get_tpselector_from_endpoint(const struct ast_sip_endpoint *endpoint, pjsip_tpselector *selector)
+{
+       const char *transport_name = endpoint->transport;
+
+       if (ast_strlen_zero(transport_name)) {
+               return 0;
+       }
+
+       return ast_sip_set_tpselector_from_transport_name(endpoint->transport, selector);
+}
+
 void ast_sip_add_usereqphone(const struct ast_sip_endpoint *endpoint, pj_pool_t *pool, pjsip_uri *uri)
 {
        pjsip_sip_uri *sip_uri;
index 0fcd7d9..61a979c 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "asterisk.h"
 
+#include <math.h>
 #include <pjsip.h>
 #include <pjlib.h>
 
@@ -107,6 +108,56 @@ static int internal_state_cmp(void *obj, void *arg, int flags)
        return CMP_MATCH;
 }
 
+/*! \brief hashing function for state objects */
+static int transport_state_hash(const void *obj, const int flags)
+{
+       const struct ast_sip_transport_state *object;
+       const char *key;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_KEY:
+               key = obj;
+               break;
+       case OBJ_SEARCH_OBJECT:
+               object = obj;
+               key = object->id;
+               break;
+       default:
+               ast_assert(0);
+               return 0;
+       }
+       return ast_str_hash(key);
+}
+
+/*! \brief comparator function for state objects */
+static int transport_state_cmp(void *obj, void *arg, int flags)
+{
+       const struct ast_sip_transport_state *object_left = obj;
+       const struct ast_sip_transport_state *object_right = arg;
+       const char *right_key = arg;
+       int cmp;
+
+       switch (flags & OBJ_SEARCH_MASK) {
+       case OBJ_SEARCH_OBJECT:
+               right_key = object_right->id;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(object_left->id, right_key);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               /* Not supported by container. */
+               ast_assert(0);
+               return 0;
+       default:
+               cmp = 0;
+               break;
+       }
+       if (cmp) {
+               return 0;
+       }
+       return CMP_MATCH;
+}
+
 static int sip_transport_to_ami(const struct ast_sip_transport *transport,
                                struct ast_str **buf)
 {
@@ -347,6 +398,44 @@ static void copy_state_to_transport(struct ast_sip_transport *transport)
        memcpy(&transport->external_address, &transport->state->external_address, sizeof(transport->external_address));
 }
 
+static int has_state_changed(struct ast_sip_transport_state *a, struct ast_sip_transport_state *b)
+{
+       if (a->type != b->type) {
+               return -1;
+       }
+
+       if (pj_sockaddr_cmp(&a->host, &b->host)) {
+               return -1;
+       }
+
+       if ((a->localnet || b->localnet)
+               && ((!a->localnet != !b->localnet)
+               || ast_sockaddr_cmp(&a->localnet->addr, &b->localnet->addr)
+               || ast_sockaddr_cmp(&a->localnet->netmask, &b->localnet->netmask)))
+       {
+               return -1;
+       }
+
+       if (ast_sockaddr_cmp(&a->external_address, &b->external_address)) {
+               return -1;
+       }
+
+       if (a->tls.method != b->tls.method
+               || a->tls.ciphers_num != b->tls.ciphers_num
+               || a->tls.proto != b->tls.proto
+               || a->tls.verify_client != b->tls.verify_client
+               || a->tls.verify_server != b->tls.verify_server
+               || a->tls.require_client_cert != b->tls.require_client_cert) {
+               return -1;
+       }
+
+       if (memcmp(a->ciphers, b->ciphers, sizeof(pj_ssl_cipher) * fmax(a->tls.ciphers_num, b->tls.ciphers_num))) {
+               return -1;
+       }
+
+       return 0;
+}
+
 static void states_cleanup(void *states)
 {
        if (states) {
@@ -364,6 +453,9 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
        RAII_VAR(struct internal_state *, perm_state, NULL, ao2_cleanup);
        RAII_VAR(struct ast_variable *, changes, NULL, ast_variables_destroy);
        pj_status_t res = -1;
+       int i;
+#define BIND_TRIES 3
+#define BIND_DELAY_US 100000
 
        if (!states) {
                return -1;
@@ -376,32 +468,39 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
         */
        ao2_wrlock(states);
 
+       temp_state = internal_state_alloc(transport);
+       if (!temp_state) {
+               ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
+               return -1;
+       }
+
        perm_state = find_internal_state_by_transport(transport);
        if (perm_state) {
                ast_sorcery_diff(sorcery, perm_state->transport, transport, &changes);
-               if (changes) {
+               if (!changes && !has_state_changed(perm_state->state, temp_state->state)) {
+                       /* In case someone is using the deprecated fields, reset them */
+                       transport->state = perm_state->state;
+                       copy_state_to_transport(transport);
+                       ao2_replace(perm_state->transport, transport);
+                       return 0;
+               }
+
+               if (!transport->allow_reload) {
                        if (!perm_state->change_detected) {
                                perm_state->change_detected = 1;
                                ast_log(LOG_WARNING, "Transport '%s' is not reloadable, maintaining previous values\n", transport_id);
                        }
+                       /* In case someone is using the deprecated fields, reset them */
+                       transport->state = perm_state->state;
+                       copy_state_to_transport(transport);
+                       ao2_replace(perm_state->transport, transport);
+                       return 0;
                }
-
-               /* In case someone is using the deprecated fields, reset them */
-               transport->state = perm_state->state;
-               copy_state_to_transport(transport);
-               ao2_replace(perm_state->transport, transport);
-               return 0;
-       }
-
-       temp_state = internal_state_alloc(transport);
-       if (!temp_state) {
-               ast_log(LOG_ERROR, "Transport '%s' failed to allocate memory\n", transport_id);
-               goto error;
        }
 
        if (temp_state->state->host.addr.sa_family != PJ_AF_INET && temp_state->state->host.addr.sa_family != PJ_AF_INET6) {
                ast_log(LOG_ERROR, "Transport '%s' could not be started as binding not specified\n", transport_id);
-               goto error;
+               return -1;
        }
 
        /* Set default port if not present */
@@ -418,26 +517,38 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
                } else {
                        ast_log(LOG_ERROR, "Unknown address family for transport '%s', could not get external signaling address\n",
                                        transport_id);
-                       goto error;
+                       return -1;
                }
 
                if (ast_dnsmgr_lookup(transport->external_signaling_address, &temp_state->state->external_address, &temp_state->state->external_address_refresher, NULL) < 0) {
                        ast_log(LOG_ERROR, "Could not create dnsmgr for external signaling address on '%s'\n", transport_id);
-                       goto error;
+                       return -1;
                }
        }
 
        if (transport->type == AST_TRANSPORT_UDP) {
-               if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
-                       res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv4, NULL, transport->async_operations, &temp_state->state->transport);
-               } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
-                       res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(), &temp_state->state->host.ipv6, NULL, transport->async_operations, &temp_state->state->transport);
+
+               for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+                       if (perm_state && perm_state->state && perm_state->state->transport) {
+                               pjsip_udp_transport_pause(perm_state->state->transport,
+                                       PJSIP_UDP_TRANSPORT_DESTROY_SOCKET);
+                               usleep(BIND_DELAY_US);
+                       }
+
+                       if (temp_state->state->host.addr.sa_family == pj_AF_INET()) {
+                               res = pjsip_udp_transport_start(ast_sip_get_pjsip_endpoint(),
+                                       &temp_state->state->host.ipv4, NULL, transport->async_operations,
+                                       &temp_state->state->transport);
+                       } else if (temp_state->state->host.addr.sa_family == pj_AF_INET6()) {
+                               res = pjsip_udp_transport_start6(ast_sip_get_pjsip_endpoint(),
+                                       &temp_state->state->host.ipv6, NULL, transport->async_operations,
+                                       &temp_state->state->transport);
+                       }
                }
 
                if (res == PJ_SUCCESS && (transport->tos || transport->cos)) {
                        pj_sock_t sock;
                        pj_qos_params qos_params;
-
                        sock = pjsip_udp_transport_get_socket(temp_state->state->transport);
                        pj_sock_get_qos_params(sock, &qos_params);
                        set_qos(transport, &qos_params);
@@ -451,18 +562,37 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
                cfg.async_cnt = transport->async_operations;
                set_qos(transport, &cfg.qos_params);
 
-               res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg, &temp_state->state->factory);
+               for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+                       if (perm_state && perm_state->state && perm_state->state->factory
+                               && perm_state->state->factory->destroy) {
+                               perm_state->state->factory->destroy(perm_state->state->factory);
+                               usleep(BIND_DELAY_US);
+                       }
+
+                       res = pjsip_tcp_transport_start3(ast_sip_get_pjsip_endpoint(), &cfg,
+                               &temp_state->state->factory);
+               }
        } else if (transport->type == AST_TRANSPORT_TLS) {
                if (transport->async_operations > 1 && ast_compare_versions(pj_get_version(), "2.5.0") < 0) {
                        ast_log(LOG_ERROR, "Transport: %s: When protocol=tls and pjproject version < 2.5.0, async_operations can't be > 1\n",
                                        ast_sorcery_object_get_id(obj));
-                       goto error;
+                       return -1;
                }
 
                temp_state->state->tls.password = pj_str((char*)transport->password);
                set_qos(transport, &temp_state->state->tls.qos_params);
 
-               res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls, &temp_state->state->host, NULL, transport->async_operations, &temp_state->state->factory);
+               for (i = 0; i < BIND_TRIES && res != PJ_SUCCESS; i++) {
+                       if (perm_state && perm_state->state && perm_state->state->factory
+                               && perm_state->state->factory->destroy) {
+                               perm_state->state->factory->destroy(perm_state->state->factory);
+                               usleep(BIND_DELAY_US);
+                       }
+
+                       res = pjsip_tls_transport_start2(ast_sip_get_pjsip_endpoint(), &temp_state->state->tls,
+                               &temp_state->state->host, NULL, transport->async_operations,
+                               &temp_state->state->factory);
+               }
        } else if ((transport->type == AST_TRANSPORT_WS) || (transport->type == AST_TRANSPORT_WSS)) {
                if (transport->cos || transport->tos) {
                        ast_log(LOG_WARNING, "TOS and COS values ignored for websocket transport\n");
@@ -475,17 +605,16 @@ static int transport_apply(const struct ast_sorcery *sorcery, void *obj)
 
                pj_strerror(res, msg, sizeof(msg));
                ast_log(LOG_ERROR, "Transport '%s' could not be started: %s\n", ast_sorcery_object_get_id(obj), msg);
-               goto error;
+               return -1;
        }
 
        copy_state_to_transport(transport);
-       ao2_link(states, temp_state);
+       if (perm_state) {
+               ao2_unlink_flags(states, perm_state, OBJ_NOLOCK);
+       }
+       ao2_link_flags(states, temp_state, OBJ_NOLOCK);
 
        return 0;
-
-error:
-       ao2_unlink(states, temp_state);
-       return -1;
 }
 
 /*! \brief Custom handler for type just makes sure the state is created */
@@ -577,6 +706,11 @@ static int privkey_file_to_str(const void *obj, const intptr_t *args, char **buf
 static int transport_protocol_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
        struct ast_sip_transport *transport = obj;
+       RAII_VAR(struct ast_sip_transport_state *, state, find_or_create_temporary_state(transport), ao2_cleanup);
+
+       if (!state) {
+               return -1;
+       }
 
        if (!strcasecmp(var->value, "udp")) {
                transport->type = AST_TRANSPORT_UDP;
@@ -592,6 +726,8 @@ static int transport_protocol_handler(const struct aco_option *opt, struct ast_v
                return -1;
        }
 
+       state->type = transport->type;
+
        return 0;
 }
 
@@ -1160,9 +1296,26 @@ struct ast_sip_transport_state *ast_sip_get_transport_state(const char *transpor
        return state->state;
 }
 
+static int populate_transport_states(void *obj, void *arg, int flags)
+{
+       struct internal_state *state = obj;
+       struct ao2_container *container = arg;
+
+       ao2_link(container, state->state);
+
+       return CMP_MATCH;
+}
+
 struct ao2_container *ast_sip_get_transport_states(void)
 {
-       return ao2_container_clone(transport_states, 0);
+       struct ao2_container *states = ao2_container_alloc(DEFAULT_STATE_BUCKETS, transport_state_hash, transport_state_cmp);
+
+       if (!states) {
+               return NULL;
+       }
+
+       ao2_callback(transport_states, OBJ_NODATA | OBJ_MULTIPLE, populate_transport_states, states);
+       return states;
 }
 
 /*! \brief Initialize sorcery with transport support */
@@ -1209,6 +1362,7 @@ int ast_sip_initialize_sorcery_transport(void)
        ast_sorcery_object_field_register_custom(sorcery, "transport", "tos", "0", transport_tos_handler, tos_to_str, NULL, 0, 0);
        ast_sorcery_object_field_register(sorcery, "transport", "cos", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_transport, cos));
        ast_sorcery_object_field_register(sorcery, "transport", "websocket_write_timeout", AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_transport, write_timeout), 1, INT_MAX);
+       ast_sorcery_object_field_register(sorcery, "transport", "allow_reload", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_transport, allow_reload));
 
        internal_sip_register_endpoint_formatter(&endpoint_transport_formatter);
 
index 9d85a46..cf09a54 100644 (file)
@@ -45,6 +45,7 @@ ASTERISK_REGISTER_FILE()
 #include <pjsip.h>
 
 #include "asterisk/astobj2.h"
+#include "asterisk/cli.h"
 #include "asterisk/res_pjsip.h"
 #include "asterisk/module.h"
 #include "asterisk/pbx.h"
@@ -276,7 +277,7 @@ struct object_type_wizard {
        struct ast_config *last_config;
        char object_type[];
 };
-static AST_VECTOR(object_type_wizards, struct object_type_wizard *) object_type_wizards;
+static AST_VECTOR_RW(object_type_wizards, struct object_type_wizard *) object_type_wizards;
 
 /*! \brief Callbacks for vector deletes */
 #define NOT_EQUALS(a, b) (a != b)
@@ -304,12 +305,15 @@ static struct object_type_wizard *find_wizard(const char *object_type)
 {
        int idx;
 
+       AST_VECTOR_RW_RDLOCK(&object_type_wizards);
        for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
                struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
                if (!strcmp(otw->object_type, object_type)) {
+                       AST_VECTOR_RW_UNLOCK(&object_type_wizards);
                        return otw;
                }
        }
+       AST_VECTOR_RW_UNLOCK(&object_type_wizards);
 
        return NULL;
 }
@@ -1137,7 +1141,9 @@ static void wizard_mapped_observer(const char *name, struct ast_sorcery *sorcery
                otw->wizard_data = wizard_data;
                otw->last_config = NULL;
                strcpy(otw->object_type, object_type); /* Safe */
+               AST_VECTOR_RW_WRLOCK(&object_type_wizards);
                AST_VECTOR_APPEND(&object_type_wizards, otw);
+               AST_VECTOR_RW_UNLOCK(&object_type_wizards);
                ast_debug(1, "Wizard mapped for object_type '%s'\n", object_type);
        }
 }
@@ -1177,19 +1183,118 @@ static void instance_destroying_observer(const char *name, struct ast_sorcery *s
        ast_module_unref(ast_module_info->self);
 }
 
+static char *handle_export_primitives(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       struct ast_sorcery *sorcery;
+       int idx;
+       FILE *f = NULL;
+       const char *fn = NULL;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "pjsip export config_wizard primitives [to]";
+               e->usage =
+                       "Usage: pjsip export config_wizard primitives [ to <filename ]\n"
+                       "       Export the config_wizard objects as pjsip primitives to\n"
+                       "       the console or to <filename>\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc > 5) {
+               char date[256]="";
+               time_t t;
+               fn = a->argv[5];
+
+               time(&t);
+               ast_copy_string(date, ctime(&t), sizeof(date));
+               f = fopen(fn, "w");
+               if (!f) {
+                       ast_log(LOG_ERROR, "Unable to write %s (%s)\n", fn, strerror(errno));
+                       return CLI_FAILURE;
+               }
+
+               fprintf(f, ";!\n");
+               fprintf(f, ";! Automatically generated configuration file\n");
+               fprintf(f, ";! Filename: %s\n", fn);
+               fprintf(f, ";! Generator: %s\n", "'pjsip export config_wizard primitives'");
+               fprintf(f, ";! Creation Date: %s", date);
+               fprintf(f, ";!\n");
+       }
+
+       sorcery = ast_sip_get_sorcery();
+
+       AST_VECTOR_RW_RDLOCK(&object_type_wizards);
+       for(idx = 0; idx < AST_VECTOR_SIZE(&object_type_wizards); idx++) {
+               struct object_type_wizard *otw = AST_VECTOR_GET(&object_type_wizards, idx);
+               struct ao2_container *container;
+               struct ao2_iterator i;
+               void *o;
+
+               container = ast_sorcery_retrieve_by_fields(sorcery, otw->object_type, AST_RETRIEVE_FLAG_MULTIPLE, NULL);
+               if (!container) {
+                       continue;
+               }
+
+               i = ao2_iterator_init(container, 0);
+               while ((o = ao2_iterator_next(&i))) {
+                       struct ast_variable *vars;
+                       struct ast_variable *v;
+
+                       vars = ast_sorcery_objectset_create(sorcery, o);
+                       if (vars && ast_variable_find_in_list(vars, "@pjsip_wizard")) {
+                               if (f) {
+                                       fprintf(f, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
+                               } else {
+                                       ast_cli(a->fd, "\n[%s]\ntype = %s\n", ast_sorcery_object_get_id(o), otw->object_type);
+                               }
+                               for (v = vars; v; v = v->next) {
+                                       if (!ast_strlen_zero(v->value)) {
+                                               if (f) {
+                                                       fprintf(f, "%s = %s\n", v->name, v->value);
+                                               } else {
+                                                       ast_cli(a->fd, "%s = %s\n", v->name, v->value);
+                                               }
+                                       }
+                               }
+                       }
+                       ast_variables_destroy(vars);
+                       ao2_ref(o, -1);
+               }
+               ao2_iterator_destroy(&i);
+               ao2_cleanup(container);
+       }
+       AST_VECTOR_RW_UNLOCK(&object_type_wizards);
+
+       if (f) {
+               fclose(f);
+               ast_cli(a->fd, "Wrote configuration to %s\n", fn);
+       }
+
+
+       return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry config_wizard_cli[] = {
+       AST_CLI_DEFINE(handle_export_primitives, "Export config wizard primitives"),
+};
+
 static int load_module(void)
 {
-       AST_VECTOR_INIT(&object_type_wizards, 12);
+       AST_VECTOR_RW_INIT(&object_type_wizards, 12);
        ast_sorcery_global_observer_add(&global_observer);
+       ast_cli_register_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
 
        return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
 {
+       ast_cli_unregister_multiple(config_wizard_cli, ARRAY_LEN(config_wizard_cli));
        ast_sorcery_global_observer_remove(&global_observer);
        AST_VECTOR_REMOVE_CMP_UNORDERED(&object_type_wizards, NULL, NOT_EQUALS, OTW_DELETE_CB);
-       AST_VECTOR_FREE(&object_type_wizards);
+       AST_VECTOR_RW_FREE(&object_type_wizards);
 
        return 0;
 }
index ea2c7cd..f1a6ddf 100644 (file)
 
 static const pj_str_t diversion_name = { "Diversion", 9 };
 
+/*!
+ * \internal
+ * \brief Determine if the given string is a SIP token.
+ * \since 13.8.0
+ *
+ * \param str String to determine if is a SIP token.
+ *
+ * \note A token is defined by RFC3261 Section 25.1
+ *
+ * \return Non-zero if the string is a SIP token.
+ */
+static int sip_is_token(const char *str)
+{
+       int is_token;
+
+       if (ast_strlen_zero(str)) {
+               /* An empty string is not a token. */
+               return 0;
+       }
+
+       is_token = 1;
+       do {
+               if (!isalnum(*str)
+                       && !strchr("-.!%*_+`'~", *str)) {
+                       /* The character is not allowed in a token. */
+                       is_token = 0;
+                       break;
+               }
+       } while (*++str);
+
+       return is_token;
+}
+
 /*! \brief Diversion header reasons
  *
  * The core defines a bunch of constants used to define
@@ -46,7 +79,7 @@ static const pj_str_t diversion_name = { "Diversion", 9 };
  */
 static const struct reasons {
        enum AST_REDIRECTING_REASON code;
-       char *const text;
+       const char *text;
 } reason_table[] = {
        { AST_REDIRECTING_REASON_UNKNOWN, "unknown" },
        { AST_REDIRECTING_REASON_USER_BUSY, "user-busy" },
@@ -59,39 +92,28 @@ static const struct reasons {
        { AST_REDIRECTING_REASON_FOLLOW_ME, "follow-me" },
        { AST_REDIRECTING_REASON_OUT_OF_ORDER, "out-of-service" },
        { AST_REDIRECTING_REASON_AWAY, "away" },
-       { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"},
-       { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm"},
+       { AST_REDIRECTING_REASON_CALL_FWD_DTE, "cf_dte" },              /* Non-standard */
+       { AST_REDIRECTING_REASON_SEND_TO_VM, "send_to_vm" },    /* Non-standard */
 };
 
 static const char *reason_code_to_str(const struct ast_party_redirecting_reason *reason)
 {
-       int code = reason->code;
+       int idx;
+       int code;
 
        /* use specific string if given */
        if (!ast_strlen_zero(reason->str)) {
                return reason->str;
        }
 
-       if (code >= 0 && code < ARRAY_LEN(reason_table)) {
-               return reason_table[code].text;
-       }
-
-       return "unknown";
-}
-
-static enum AST_REDIRECTING_REASON reason_str_to_code(const char *text)
-{
-       enum AST_REDIRECTING_REASON code = AST_REDIRECTING_REASON_UNKNOWN;
-       int i;
-
-       for (i = 0; i < ARRAY_LEN(reason_table); ++i) {
-               if (!strcasecmp(text, reason_table[i].text)) {
-                       code = reason_table[i].code;
-                       break;
+       code = reason->code;
+       for (idx = 0; idx < ARRAY_LEN(reason_table); ++idx) {
+               if (code == reason_table[idx].code) {
+                       return reason_table[idx].text;
                }
        }
 
-       return code;
+       return "unknown";
 }
 
 static pjsip_fromto_hdr *get_diversion_header(pjsip_rx_data *rdata)
@@ -159,13 +181,31 @@ static void set_redirecting_reason(pjsip_fromto_hdr *hdr,
 {
        static const pj_str_t reason_name = { "reason", 6 };
        pjsip_param *reason = pjsip_param_find(&hdr->other_param, &reason_name);
+       char *reason_str;
 
        if (!reason) {
                return;
        }
 
        set_redirecting_value(&data->str, &reason->value);
-       data->code = reason_str_to_code(data->str);
+       if (!data->str) {
+               /* Oops, allocation failure */
+               return;
+       }
+       reason_str = ast_strdupa(data->str);
+
+       /* Remove any enclosing double-quotes */
+       if (*reason_str == '"') {
+               reason_str = ast_strip_quoted(reason_str, "\"", "\"");
+       }
+
+       data->code = ast_redirecting_reason_parse(reason_str);
+       if (data->code < 0) {
+               data->code = AST_REDIRECTING_REASON_UNKNOWN;
+       } else {
+               ast_free(data->str);
+               data->str = ast_strdup("");
+       }
 }
 
 static void set_redirecting(struct ast_sip_session *session,
@@ -251,6 +291,9 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
        pjsip_sip_uri *uri;
        pjsip_param *param;
        pjsip_fromto_hdr *old_hdr;
+       const char *reason_str;
+       const char *quote_str;
+       char *reason_buf;
 
        struct ast_party_id *id = &data->from;
        pjsip_uri *base = PJSIP_MSG_FROM_HDR(tdata->msg)->uri;
@@ -272,7 +315,17 @@ static void add_diversion_header(pjsip_tx_data *tdata, struct ast_party_redirect
 
        param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
        param->name = pj_str("reason");
-       param->value = pj_str((char*)reason_code_to_str(&data->reason));
+
+       reason_str = reason_code_to_str(&data->reason);
+
+       /* Reason is either already quoted or it is a token to not need quotes added. */
+       quote_str = *reason_str == '\"' || sip_is_token(reason_str) ? "" : "\"";
+
+       reason_buf = pj_pool_alloc(tdata->pool, strlen(reason_str) + 3);
+       sprintf(reason_buf, "%s%s%s", quote_str, reason_str, quote_str);/* Safe */
+
+       param->value = pj_str(reason_buf);
+
        pj_list_insert_before(&hdr->other_param, param);
 
        hdr->uri = (pjsip_uri *) name_addr;
index 78d529c..47ccd1a 100644 (file)
@@ -82,14 +82,13 @@ static char get_event(const char *c)
 static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
 {
        pjsip_msg_body *body = rdata->msg_info.msg->body;
-       char buf[body ? body->len : 0];
+       char buf[body ? body->len + 1 : 1];
        char *cur = buf;
        char *line;
-
        char event = '\0';
        unsigned int duration = 100;
-
        char is_dtmf;
+       int res;
 
        if (!session->channel) {
                return 0;
@@ -107,7 +106,12 @@ static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pj
                return 0;
        }
 
-       body->print_body(body, buf, body->len);
+       res = body->print_body(body, buf, body->len);
+       if (res < 0) {
+               send_response(session, rdata, 500);
+               return 0;
+       }
+       buf[res] = '\0';
 
        if (is_dtmf) {
                /* directly use what is in the message body */
index 7532e39..f72f3f0 100644 (file)
@@ -530,6 +530,10 @@ static struct msg_data* msg_data_create(const struct ast_msg *msg, const char *t
        /* Make sure we start with sip: */
        mdata->to = ast_begins_with(to, "sip:") ? ast_strdup(++to) : ast_strdup(to - 3);
        mdata->from = ast_strdup(from);
+       if (!mdata->to || !mdata->from) {
+               ao2_ref(mdata, -1);
+               return NULL;
+       }
 
        /* sometimes from can still contain the tag at this point, so remove it */
        if ((tag = strchr(mdata->from, ';'))) {
@@ -597,7 +601,7 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
 
        if (!(mdata = msg_data_create(msg, to, from)) ||
            ast_sip_push_task(message_serializer, msg_send, mdata)) {
-               ao2_ref(mdata, -1);
+               ao2_cleanup(mdata);
                return -1;
        }
        return 0;
index f6600dd..e1eea6f 100644 (file)
@@ -448,7 +448,7 @@ static void send_unsolicited_mwi_notify(struct mwi_subscription *sub,
 
                contacts = ast_sip_location_retrieve_aor_contacts(aor);
                if (!contacts || (ao2_container_count(contacts) == 0)) {
-                       ast_log(LOG_NOTICE, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
+                       ast_debug(1, "No contacts bound to AOR %s. Cannot send unsolicited MWI until a contact registers.\n", aor_name);
                        continue;
                }
 
@@ -600,7 +600,7 @@ static int mwi_validate_for_aor(void *obj, void *arg, int flags)
        mailboxes = ast_strdupa(aor->mailboxes);
        while ((mailbox = strsep(&mailboxes, ","))) {
                if (endpoint_receives_unsolicited_mwi_for_mailbox(endpoint, mailbox)) {
-                       ast_log(LOG_NOTICE, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. "
+                       ast_debug(1, "Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. "
                                        "Denying MWI subscription to %s\n", ast_sorcery_object_get_id(endpoint), mailbox,
                                        ast_sorcery_object_get_id(aor));
                        return -1;
@@ -710,13 +710,13 @@ static int mwi_new_subscribe(struct ast_sip_endpoint *endpoint,
 
        aor = ast_sip_location_retrieve_aor(resource);
        if (!aor) {
-               ast_log(LOG_WARNING, "Unable to locate aor %s. MWI subscription failed.\n",
+               ast_debug(1, "Unable to locate aor %s. MWI subscription failed.\n",
                        resource);
                return 404;
        }
 
        if (ast_strlen_zero(aor->mailboxes)) {
-               ast_log(LOG_NOTICE, "AOR %s has no configured mailboxes. MWI subscription failed.\n",
+               ast_debug(1, "AOR %s has no configured mailboxes. MWI subscription failed.\n",
                        resource);
                return 404;
        }
index a58bcbb..856d84a 100644 (file)
@@ -893,7 +893,7 @@ static void sip_outbound_publish_callback(struct pjsip_publishc_cbparam *param)
                pjsip_publishc_destroy(client->client);
                client->client = NULL;
 
-               if (sip_outbound_publish_client_alloc(publish)) {
+               if (sip_outbound_publish_client_alloc(client)) {
                        ast_log(LOG_ERROR, "Failed to create a new outbound publish client for '%s' on 412 response\n",
                                ast_sorcery_object_get_id(publish));
                        goto end;
index 0ff609a..dd69ff2 100644 (file)
@@ -346,6 +346,8 @@ struct sip_outbound_registration_client_state {
        unsigned int destroy:1;
        /*! \brief Non-zero if we have attempted sending a REGISTER with authentication */
        unsigned int auth_attempted:1;
+       /*! \brief The name of the transport to be used for the registration */
+       char *transport_name;
 };
 
 /*! \brief Outbound registration state information (persists for lifetime that registration should exist) */
@@ -508,6 +510,7 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
 {
        pj_status_t status;
        int *callback_invoked;
+       pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
 
        callback_invoked = ast_threadstorage_get(&register_callback_invoked, sizeof(int));
        if (!callback_invoked) {
@@ -517,6 +520,13 @@ static pj_status_t registration_client_send(struct sip_outbound_registration_cli
 
        /* Due to the message going out the callback may now be invoked, so bump the count */
        ao2_ref(client_state, +1);
+       /*
+        * Set the transport in case transports were reloaded.
+        * When pjproject removes the extraneous error messages produced,
+        * we can check status and only set the transport and resend if there was an error
+        */
+       ast_sip_set_tpselector_from_transport_name(client_state->transport_name, &selector);
+       pjsip_regc_set_transport(client_state->client, &selector);
        status = pjsip_regc_send(client_state->client, tdata);
 
        /* If the attempt to send the message failed and the callback was not invoked we need to
@@ -966,6 +976,7 @@ static void sip_outbound_registration_client_state_destroy(void *obj)
 {
        struct sip_outbound_registration_client_state *client_state = obj;
 
+       ast_free(client_state->transport_name);
        ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "-1", 1.0);
        ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "-1", 1.0,
                sip_outbound_registration_status_str(client_state->status));
@@ -1003,6 +1014,7 @@ static struct sip_outbound_registration_state *sip_outbound_registration_state_a
        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;
+       state->client_state->transport_name = ast_strdup(registration->transport);
 
        ast_statsd_log_string("PJSIP.registrations.count", AST_STATSD_GAUGE, "+1", 1.0);
        ast_statsd_log_string_va("PJSIP.registrations.state.%s", AST_STATSD_GAUGE, "+1", 1.0,
@@ -1171,25 +1183,6 @@ static int sip_outbound_registration_regc_alloc(void *data)
 
        pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), pool);
 
-       if (!ast_strlen_zero(registration->transport)) {
-               RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(registration->transport), ao2_cleanup);
-
-               if (!transport_state) {
-                       ast_log(LOG_ERROR, "Unable to retrieve PJSIP transport '%s' "
-                               " for outbound registration", registration->transport);
-                       return -1;
-               }
-
-               if (transport_state->transport) {
-                       selector.type = PJSIP_TPSELECTOR_TRANSPORT;
-                       selector.u.transport = transport_state->transport;
-               } else if (transport_state->factory) {
-                       selector.type = PJSIP_TPSELECTOR_LISTENER;
-                       selector.u.listener = transport_state->factory;
-               } else {
-                       return -1;
-               }
-       }
 
        ast_assert(state->client_state->client == NULL);
        if (pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state,
@@ -1198,6 +1191,7 @@ static int sip_outbound_registration_regc_alloc(void *data)
                return -1;
        }
 
+       ast_sip_set_tpselector_from_transport_name(registration->transport, &selector);
        pjsip_regc_set_transport(state->client_state->client, &selector);
 
        if (!ast_strlen_zero(registration->outbound_proxy)) {
index bde7075..0da4319 100644 (file)
@@ -1559,6 +1559,28 @@ void *ast_sip_subscription_get_header(const struct ast_sip_subscription *sub, co
        return pjsip_msg_find_hdr_by_name(msg, &name, NULL);
 }
 
+/*!
+ * \internal
+ * \brief Wrapper for pjsip_evsub_send_request
+ *
+ * This function (re)sets the transport before sending to catch cases
+ * where the transport might have changed.
+ *
+ * If pjproject gives us the ability to resend, we'll only reset the transport
+ * if PJSIP_ETPNOTAVAIL is returned from send.
+ *
+ * \returns pj_status_t
+ */
+static pj_status_t internal_pjsip_evsub_send_request(struct sip_subscription_tree *sub_tree, pjsip_tx_data *tdata)
+{
+       pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+       ast_sip_set_tpselector_from_transport_name(sub_tree->endpoint->transport, &selector);
+       pjsip_dlg_set_transport(sub_tree->dlg, &selector);
+
+       return pjsip_evsub_send_request(sub_tree->evsub, tdata);
+}
+
 /* XXX This function is not used. */
 struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_subscription_handler *handler,
                struct ast_sip_endpoint *endpoint, const char *resource)
@@ -1606,7 +1628,7 @@ struct ast_sip_subscription *ast_sip_create_subscription(const struct ast_sip_su
        evsub = sub_tree->evsub;
 
        if (pjsip_evsub_initiate(evsub, NULL, -1, &tdata) == PJ_SUCCESS) {
-               pjsip_evsub_send_request(evsub, tdata);
+               internal_pjsip_evsub_send_request(sub_tree, tdata);
        } else {
                /* pjsip_evsub_terminate will result in pubsub_on_evsub_state,
                 * being called and terminating the subscription. Therefore, we don't
@@ -1687,8 +1709,8 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
 {
 #ifdef TEST_FRAMEWORK
        struct ast_sip_endpoint *endpoint = sub_tree->endpoint;
-#endif
        pjsip_evsub *evsub = sub_tree->evsub;
+#endif
        int res;
 
        if (allocate_tdata_buffer(tdata)) {
@@ -1696,7 +1718,8 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
                return -1;
        }
 
-       res = pjsip_evsub_send_request(evsub, tdata) == PJ_SUCCESS ? 0 : -1;
+       res = internal_pjsip_evsub_send_request(sub_tree, tdata);
+
        subscription_persistence_update(sub_tree, NULL);
 
        ast_test_suite_event_notify("SUBSCRIPTION_STATE_SET",
@@ -1705,7 +1728,7 @@ static int sip_subscription_send_request(struct sip_subscription_tree *sub_tree,
                pjsip_evsub_get_state_name(evsub),
                ast_sorcery_object_get_id(endpoint));
 
-       return res;
+       return (res == PJ_SUCCESS ? 0 : -1);
 }
 
 /*!
index 9d2b5b1..bd70bcf 100644 (file)
@@ -47,7 +47,8 @@
 #define SEND_TO_VM_HEADER_VALUE "feature_send_to_vm"
 
 #define SEND_TO_VM_REDIRECT "REDIRECTING(reason)"
-#define SEND_TO_VM_REDIRECT_VALUE "\"send_to_vm\""
+#define SEND_TO_VM_REDIRECT_VALUE "send_to_vm"
+#define SEND_TO_VM_REDIRECT_QUOTED_VALUE "\"" SEND_TO_VM_REDIRECT_VALUE "\""
 
 static void send_response(struct ast_sip_session *session, int code, struct pjsip_rx_data *rdata)
 {
@@ -102,9 +103,13 @@ static int has_diversion_reason(pjsip_rx_data *rdata)
        pjsip_param *reason;
        pjsip_fromto_hdr *hdr = get_diversion_header(rdata);
 
-       return hdr &&
-               (reason = get_diversion_reason(hdr)) &&
-               !pj_stricmp2(&reason->value, SEND_TO_VM_REDIRECT_VALUE);
+       if (!hdr) {
+               return 0;
+       }
+       reason = get_diversion_reason(hdr);
+       return reason
+               && (!pj_stricmp2(&reason->value, SEND_TO_VM_REDIRECT_QUOTED_VALUE)
+                       || !pj_stricmp2(&reason->value, SEND_TO_VM_REDIRECT_VALUE));
 }
 
 static int has_call_feature(pjsip_rx_data *rdata)
@@ -160,12 +165,10 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
        sip_session_datastore->data = other_party;
 
        if (ast_sip_session_add_datastore(session, sip_session_datastore)) {
-               ast_channel_unref(other_party);
                ao2_ref(sip_session_datastore, -1);
                send_response(session, 500, rdata);
                return -1;
        }
-       ao2_ref(sip_session_datastore, -1);
 
        if (has_feature) {
                pbx_builtin_setvar_helper(other_party, SEND_TO_VM_HEADER,
@@ -177,6 +180,7 @@ static int handle_incoming_request(struct ast_sip_session *session, struct pjsip
                                          SEND_TO_VM_REDIRECT_VALUE);
        }
 
+       ao2_ref(sip_session_datastore, -1);
        return 0;
 }
 
index e7dd5b9..9836871 100644 (file)
@@ -888,10 +888,32 @@ int ast_sip_session_refresh(struct ast_sip_session *session,
        return 0;
 }
 
+/*!
+ * \internal
+ * \brief Wrapper for pjsip_inv_send_msg
+ *
+ * This function (re)sets the transport before sending to catch cases
+ * where the transport might have changed.
+ *
+ * If pjproject gives us the ability to resend, we'll only reset the transport
+ * if PJSIP_ETPNOTAVAIL is returned from send.
+ *
+ * \returns pj_status_t
+ */
+static pj_status_t internal_pjsip_inv_send_msg(pjsip_inv_session *inv, const char *transport_name, pjsip_tx_data *tdata)
+{
+       pjsip_tpselector selector = { .type = PJSIP_TPSELECTOR_NONE, };
+
+       ast_sip_set_tpselector_from_transport_name(transport_name, &selector);
+       pjsip_dlg_set_transport(inv->dlg, &selector);
+
+       return pjsip_inv_send_msg(inv, tdata);
+}
+
 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);
+       internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata);
        return;
 }
 
@@ -1087,7 +1109,8 @@ void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip
        }
 
        handle_outgoing_request(session, tdata);
-       pjsip_inv_send_msg(session->inv_session, tdata);
+       internal_pjsip_inv_send_msg(session->inv_session, session->endpoint->transport, tdata);
+
        return;
 }
 
@@ -1852,7 +1875,7 @@ static pjsip_inv_session *pre_session_setup(pjsip_rx_data *rdata, const struct a
                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);
+               internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
                return NULL;
        }
        return inv_session;
@@ -2005,7 +2028,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
                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);
+                       internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
                }
                return;
        }
@@ -2015,7 +2038,7 @@ static void handle_new_invite_request(pjsip_rx_data *rdata)
                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);
+                       internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata);
                }
                ao2_cleanup(invite);
        }
index 2bb6f03..c025171 100644 (file)
@@ -570,41 +570,6 @@ static struct ast_sip_session_supplement t38_supplement = {
        .outgoing_request = t38_outgoing_invite_request,
 };
 
-static int t38_incoming_bye_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
-{
-       struct ast_datastore *datastore;
-       struct ast_sip_session_media *session_media;
-
-       if (!session->channel) {
-               return 0;
-       }
-
-       datastore = ast_sip_session_get_datastore(session, "t38");
-       if (!datastore) {
-               return 0;
-       }
-
-       session_media = ao2_find(session->media, "image", OBJ_KEY);
-       if (!session_media) {
-               ao2_ref(datastore, -1);
-               return 0;
-       }
-
-       t38_change_state(session, session_media, datastore->data, T38_REJECTED);
-
-       ao2_ref(datastore, -1);
-       ao2_ref(session_media, -1);
-
-       return 0;
-}
-
-/*! \brief Supplement for handling a remote termination of T.38 state */
-static struct ast_sip_session_supplement t38_bye_supplement = {
-       .method = "BYE",
-       .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1,
-       .incoming_request = t38_incoming_bye_request,
-};
-
 /*! \brief Parse a T.38 image stream and store the attribute information */
 static void t38_interpret_sdp(struct t38_state *state, struct ast_sip_session *session, struct ast_sip_session_media *session_media,
        const struct pjmedia_sdp_media *stream)
@@ -935,7 +900,6 @@ static int unload_module(void)
 {
        ast_sip_session_unregister_sdp_handler(&image_sdp_handler, "image");
        ast_sip_session_unregister_supplement(&t38_supplement);
-       ast_sip_session_unregister_supplement(&t38_bye_supplement);
 
        return 0;
 }
@@ -962,11 +926,6 @@ static int load_module(void)
                goto end;
        }
 
-       if (ast_sip_session_register_supplement(&t38_bye_supplement)) {
-               ast_log(LOG_ERROR, "Unable to register T.38 BYE session supplement\n");
-               goto end;
-       }
-
        if (ast_sip_session_register_sdp_handler(&image_sdp_handler, "image")) {
                ast_log(LOG_ERROR, "Unable to register SDP handler for image stream type\n");
                goto end;
index 0421d81..704372e 100644 (file)
@@ -1832,7 +1832,7 @@ static char *sorcery_memory_cache_expire(struct ast_cli_entry *e, int cmd, struc
                }
        }
 
-       if (a->argc > 6) {
+       if (a->argc < 5 || a->argc > 6) {
                return CLI_SHOWUSAGE;
        }
 
@@ -1886,7 +1886,7 @@ static char *sorcery_memory_cache_stale(struct ast_cli_entry *e, int cmd, struct
                }
        }
 
-       if (a->argc > 6) {
+       if (a->argc < 5 || a->argc > 6) {
                return CLI_SHOWUSAGE;
        }
 
@@ -1945,7 +1945,7 @@ static char *sorcery_memory_cache_populate(struct ast_cli_entry *e, int cmd, str
                }
        }
 
-       if (a->argc > 5) {
+       if (a->argc != 5) {
                return CLI_SHOWUSAGE;
        }
 
index afb981b..d8acf26 100644 (file)
@@ -921,6 +921,41 @@ end:
        return res;
 }
 
+static enum ast_test_result_state wait_until_thread_state_task_pushed(struct ast_test *test,
+               struct test_listener_data *tld, int num_active, int num_idle, int num_tasks)
+{
+       enum ast_test_result_state res = AST_TEST_PASS;
+       struct timeval start;
+       struct timespec end;
+
+       res = wait_until_thread_state(test, tld, num_active, num_idle);
+       if (res == AST_TEST_FAIL) {
+               return res;
+       }
+
+       start = ast_tvnow();
+       end.tv_sec = start.tv_sec + 5;
+       end.tv_nsec = start.tv_usec * 1000;
+
+       ast_mutex_lock(&tld->lock);
+
+       while (tld->num_tasks != num_tasks) {
+               if (ast_cond_timedwait(&tld->cond, &tld->lock, &end) == ETIMEDOUT) {
+                       break;
+               }
+       }
+
+       if (tld->num_tasks != num_tasks) {
+               ast_test_status_update(test, "Number of tasks pushed %d does not match expected %d\n",
+                               tld->num_tasks, num_tasks);
+               res = AST_TEST_FAIL;
+       }
+
+       ast_mutex_unlock(&tld->lock);
+
+       return res;
+}
+
 AST_TEST_DEFINE(threadpool_auto_increment)
 {
        struct ast_threadpool *pool = NULL;
@@ -1021,11 +1056,10 @@ AST_TEST_DEFINE(threadpool_auto_increment)
                goto end;
        }
 
-       res = wait_until_thread_state(test, tld, 0, 3);
+       res = wait_until_thread_state_task_pushed(test, tld, 0, 3, 4);
        if (res == AST_TEST_FAIL) {
                goto end;
        }
-       res = listener_check(test, listener, 1, 0, 4, 0, 3, 1);
 
 end:
        ast_threadpool_shutdown(pool);
diff --git a/third-party/Makefile b/third-party/Makefile
new file mode 100644 (file)
index 0000000..0aca21e
--- /dev/null
@@ -0,0 +1,21 @@
+
+include Makefile.rules
+
+TP_SUBDIRS := pjproject
+# Sub directories that contain special install/uninstall targets must be explicitly listed
+# to prevent accidentally running the package's default install target.
+TP_INSTALL_SUBDIRS := pjproject
+
+.PHONY: all dist-clean distclean install clean moduleinfo makeopts uninstall __embed_libs __embed_ldscript __embed_ldflags $(TP_SUBDIRS)
+
+override MAKECMDGOALS?=all
+
+MAKECMDGOALS:=$(subst dist-clean,distclean,$(MAKECMDGOALS))
+MAKECMDGOALS:=$(subst tpclean,clean,$(MAKECMDGOALS))
+
+all distclean dist-clean install tpclean : $(TP_SUBDIRS)
+install uninstall: $(TP_INSTALL_SUBDIRS)
+
+$(TP_SUBDIRS):
+       +$(CMD_PREFIX) $(SUBMAKE) -C $@ $(MAKECMDGOALS)
+
diff --git a/third-party/Makefile.rules b/third-party/Makefile.rules
new file mode 100644 (file)
index 0000000..e633e0e
--- /dev/null
@@ -0,0 +1,36 @@
+
+ifeq ($(NOISY_BUILD),)
+SUBMAKE?=$(MAKE) --quiet --no-print-directory
+ECHO_PREFIX?=@
+CMD_PREFIX?=@
+QUIET_CONFIGURE=-q
+REALLY_QUIET=&>/dev/null
+else
+SUBMAKE?=$(MAKE)
+ECHO_PREFIX?=@\#
+CMD_PREFIX?=
+QUIET_CONFIGURE=
+REALLY_QUIET=
+endif
+
+DOWNLOAD := $(shell which wget 2>/dev/null)
+DOWNLOAD := $(if $(DOWNLOAD),$(DOWNLOAD) -O- ,)
+
+ifeq ($(DOWNLOAD),)
+DOWNLOAD := $(shell which curl 2>/dev/null)
+DOWNLOAD := $(if $(DOWNLOAD), $(DOWNLOAD) -L ,)
+endif
+
+ifeq ($(DOWNLOAD),)
+DOWNLOAD := echo "No download program available" ; exit 1;
+endif
+
+export SUBMAKE
+export ECHO_PREFIX
+export CMD_PREFIX
+export QUIET_CONFIGURE
+export REALLY_QUIET
+export ASTTOPDIR
+export ASTSBINDIR
+export DESTDIR
+export ASTDATADIR
diff --git a/third-party/pjproject/.gitignore b/third-party/pjproject/.gitignore
new file mode 100644 (file)
index 0000000..5079dee
--- /dev/null
@@ -0,0 +1,4 @@
+source/
+**.bz2
+build.mak
+pjproject.symbols
diff --git a/third-party/pjproject/Makefile b/third-party/pjproject/Makefile
new file mode 100644 (file)
index 0000000..3100951
--- /dev/null
@@ -0,0 +1,110 @@
+.SUFFIXES:
+.PHONY: _all all _install install clean distclean echo_cflags configure
+
+ifeq ($(MAKECMDGOALS),install)
+include ../../makeopts
+else
+-include ../../makeopts
+endif
+
+include ../versions.mak
+include ../Makefile.rules
+include Makefile.rules
+
+ECHO_PREFIX := $(ECHO_PREFIX) echo '[pjproject] '
+
+ifeq ($(MAKECMDGOALS),echo_cflags)
+-include build.mak
+ECHO_PREFIX=@\#
+endif
+
+ifneq ($(PJPROJECT_BUNDLED),yes)
+all install:
+       @echo '[pjproject] Not enabled'
+else
+
+ifneq ($(findstring clean,$(MAKECMDGOALS)),clean)
+include build.mak
+endif
+
+all: _all
+install: _install
+endif
+
+ifndef $(TMPDIR)
+ifneq ($(wildcard /tmp),)
+TMPDIR=/tmp
+else
+TMPDIR=.
+endif
+endif
+
+$(TMPDIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2 : ../versions.mak
+       $(ECHO_PREFIX) Downloading $@ with $(DOWNLOAD)
+       $(CMD_PREFIX) $(DOWNLOAD) $(PJPROJECT_URL)/$(@F) > $@
+
+source/user.mak source/pjlib/include/pj/config_site.h: $(TMPDIR)/pjproject-$(PJPROJECT_VERSION).tar.bz2 patches/config_site.h patches/user.mak 
+       $(ECHO_PREFIX) Unpacking $<
+       -@rm -rf source &>/dev/null
+       -@mkdir source &>/dev/null
+       $(CMD_PREFIX) tar --strip-components=1 -C source -xjf $<
+       $(ECHO_PREFIX) Applying patches and custom files
+       $(CMD_PREFIX) ./apply_patches $(QUIET_CONFIGURE) ./patches ./source
+       $(CMD_PREFIX) cp -f ./patches/config_site.h ./source/pjlib/include/pj/
+       $(CMD_PREFIX) cp -f ./patches/user.mak ./source/
+
+build.mak: source/pjlib/include/pj/config_site.h source/user.mak Makefile.rules
+       $(ECHO_PREFIX) Configuring with $(PJPROJECT_CONFIG_OPTS)
+       $(CMD_PREFIX) (cd source ; autoconf aconfigure.ac > aconfigure && ./aconfigure $(QUIET_CONFIGURE) $(PJPROJECT_CONFIG_OPTS))
+       @sed -r -e "/prefix|export PJ_SHARED_LIBRARIES|MACHINE_NAME|OS_NAME|HOST_NAME|CC_NAME|CROSS_COMPILE|LINUX_POLL/d" source/build.mak > build.mak
+
+configure: build.mak
+
+echo_cflags: build.mak
+       @echo $(PJ_CFLAGS)
+
+source/pjlib/build/.pjlib-$(TARGET_NAME).depend: build.mak
+       $(ECHO_PREFIX) "Making dependencies"
+       +$(CMD_PREFIX) $(SUBMAKE) -C source dep
+
+source/pjlib/lib/libpj-$(TARGET_NAME).a: source/pjlib/build/.pjlib-$(TARGET_NAME).depend
+       $(ECHO_PREFIX) Compiling libs
+       +$(CMD_PREFIX) $(SUBMAKE) -C source lib $(REALLY_QUIET)
+
+pjproject.symbols: source/pjlib/lib/libpj-$(TARGET_NAME).a
+       $(ECHO_PREFIX) Generating symbols
+       $(CMD_PREFIX) nm -Pog $(PJ_LIB_FILES) | sed -n -r -e "s/.+: ([pP][jJ][^ ]+) .+/\1/gp" | sort -u > pjproject.symbols
+
+source/pjsip-apps/bin/pjsua-$(TARGET_NAME): source/pjlib/lib/libpj-$(TARGET_NAME).a
+       $(ECHO_PREFIX) Compiling apps
+       $(CMD_PREFIX) $(SUBMAKE) -C source/pjsip-apps/build pjsua pjsystest $(REALLY_QUIET)
+
+source/pjsip-apps/src/python/build/_pjsua.so: source/pjlib/lib/libpj-$(TARGET_NAME).a
+       $(ECHO_PREFIX) Compiling python bindings
+       $(CMD_PREFIX) (cd source/pjsip-apps/src/python ; python setup.py build --build-platlib=./build $(REALLY_QUIET))
+
+_all: pjproject.symbols source/pjsip-apps/bin/pjsua-$(TARGET_NAME) source/pjsip-apps/src/python/build/_pjsua.so
+
+_install: _all
+       $(ECHO_PREFIX) Installing apps and python bindings
+       @if [ ! -d "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject" ]; then \
+               $(INSTALL) -d "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject"; \
+       fi;
+       $(CMD_PREFIX) $(INSTALL) -m 755 source/pjsip-apps/bin/pjsua-$(TARGET_NAME) "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject/pjsua"
+       $(CMD_PREFIX) $(INSTALL) -m 755 source/pjsip-apps/bin/pjsystest-$(TARGET_NAME) "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject/pjsystest"
+       $(CMD_PREFIX) $(INSTALL) -m 755 source/pjsip-apps/src/python/build/_pjsua.so "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject/"
+       $(CMD_PREFIX) $(INSTALL) -m 644 source/pjsip-apps/src/python/build/pjsua.py "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject/"
+
+uninstall:
+       $(ECHO_PREFIX) Uninstalling apps and python bindings
+       $(CMD_PREFIX) rm -rf "$(DESTDIR)$(ASTDATADIR)/third-party/pjproject"
+
+clean:
+       $(ECHO_PREFIX) Cleaning
+       -$(CMD_PREFIX) test -d source && ($(SUBMAKE) -C source clean ; find source -name *.a -delete ; rm -rf source/pjsip-apps/src/python/build) || :
+       -$(CMD_PREFIX) rm -rf pjproject.symbols
+
+distclean:
+       $(ECHO_PREFIX) Distcleaning
+       -$(CMD_PREFIX) rm -rf source pjproject.symbols pjproject-*.tar.bz2 build.mak
+
diff --git a/third-party/pjproject/Makefile.rules b/third-party/pjproject/Makefile.rules
new file mode 100644 (file)
index 0000000..d6e4be0
--- /dev/null
@@ -0,0 +1,7 @@
+PJPROJECT_URL = http://www.pjsip.org/release/$(PJPROJECT_VERSION)
+
+# Even though we're not installing pjproject, we're setting prefix to /opt/pjproject to be safe
+PJPROJECT_CONFIG_OPTS = --prefix=/opt/pjproject --with-external-speex --with-external-gsm --with-external-srtp \
+       --with-external-pa --disable-video --disable-v4l2 --disable-sound --disable-resample \
+       --disable-opencore-amr --disable-ilbc-codec --without-libyuv --disable-g7221-codec \
+       --enable-epoll
diff --git a/third-party/pjproject/apply_patches b/third-party/pjproject/apply_patches
new file mode 100755 (executable)
index 0000000..1b72d14
--- /dev/null
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+if [ "$1" = "-q" ] ; then
+       quiet=1
+       shift
+fi
+
+patchdir=${1:?You must supply a patches directory}
+sourcedir=${2?:You must supply a source directory}
+
+patchdir=`readlink -f $patchdir`
+sourcedir=`readlink -f $sourcedir`
+
+if [ ! -d "$patchdir" ] ; then
+       echo "$patchdir is not a directory" >&2
+       exit 1
+fi
+
+if [ ! -d "$sourcedir" ] ; then
+       echo "$sourcedir is not a directory"  >&2
+       exit 1
+fi
+
+if [ ! "$(ls -A $patchdir/*.patch 2>/dev/null)" ] ; then
+       echo "No patches in $patchdir"  >&2
+       exit 0
+fi
+
+for patchfile in $patchdir/*.patch ; do
+       patch -d $sourcedir -p1 -s -r- -f -N --dry-run -i "$patchfile" || (echo "Patchfile $(basename $patchfile) failed to apply >&2" ; exit 1) || exit 1
+done
+
+for patchfile in "$patchdir"/*.patch ; do
+       [ -z $quiet ] && echo "Applying patch $(basename $patchfile)"
+       patch -d "$sourcedir" -p1 -s -i "$patchfile" || exit 1
+done
+
+exit 0
+
diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4
new file mode 100644 (file)
index 0000000..3351527
--- /dev/null
@@ -0,0 +1,46 @@
+AC_DEFUN([PJPROJECT_SYMBOL_CHECK],
+[
+       $1_INCLUDE="$PJPROJECT_INCLUDE"
+    AC_MSG_CHECKING([for $2 declared in $3])
+
+       saved_cpp="$CPPFLAGS"
+       CPPFLAGS="$PJPROJECT_INCLUDE"
+       AC_EGREP_HEADER($2, $3, [
+               AC_MSG_RESULT(yes)
+               PBX_$1=1
+               AC_DEFINE([HAVE_$1], 1, [Define if your system has $2 declared.])
+       ], [
+               AC_MSG_RESULT(no)
+       ])
+
+       CPPGLAGS="$saved_cpp"
+       $1_INCLUDE="$PJPROJECT_INCLUDE"
+])
+
+AC_DEFUN([PJPROJECT_CONFIGURE],
+[
+       AC_MSG_CHECKING(for embedded pjproject (may have to download))
+       AC_MSG_RESULT(configuring)
+       make --quiet --no-print-directory -C $1 configure
+       if test $? -ne 0 ; then
+               AC_MSG_RESULT(failed)
+               AC_MSG_NOTICE(Unable to configure $1)
+               AC_MSG_ERROR(Run "make -C $1 NOISY_BUILD=yes configure" to see error details.)
+       fi
+
+       PJPROJECT_INCLUDE=$(make --quiet --no-print-directory -C $1 echo_cflags)
+       PJPROJECT_CFLAGS="$PJPROJECT_INCLUDE"
+       PBX_PJPROJECT=1
+       PJPROJECT_BUNDLED=yes
+       AC_DEFINE([HAVE_PJPROJECT], 1, [Define if your system has PJPROJECT])
+       AC_DEFINE([HAVE_PJPROJECT_BUNDLED], 1, [Define if your system has PJPROJECT_BUNDLED])
+       AC_MSG_CHECKING(for embedded pjproject)
+       AC_MSG_RESULT(yes)
+
+       PJPROJECT_SYMBOL_CHECK([PJSIP_DLG_CREATE_UAS_AND_INC_LOCK], [pjsip_dlg_create_uas_and_inc_lock], [pjsip.h])
+       PJPROJECT_SYMBOL_CHECK([PJ_TRANSACTION_GRP_LOCK], [pjsip_tsx_create_uac2], [pjsip.h])
+       PJPROJECT_SYMBOL_CHECK([PJSIP_REPLACE_MEDIA_STREAM], [PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE], [pjmedia.h])
+       PJPROJECT_SYMBOL_CHECK([PJSIP_GET_DEST_INFO], [pjsip_get_dest_info], [pjsip.h])
+       PJPROJECT_SYMBOL_CHECK([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_from_files2], [pjlib.h])
+       PJPROJECT_SYMBOL_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip_endpt_set_ext_resolver], [pjsip.h])
+])
diff --git a/third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch b/third-party/pjproject/patches/0001-2.4.5-fix-for-tls-async-ops.patch
new file mode 100644 (file)
index 0000000..33fc8ea
--- /dev/null
@@ -0,0 +1,224 @@
+diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h
+index 1682bda..a69af32 100644
+--- a/pjlib/include/pj/ssl_sock.h
++++ b/pjlib/include/pj/ssl_sock.h
+@@ -864,6 +864,18 @@ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param);
+ /**
++ * Duplicate pj_ssl_sock_param.
++ *
++ * @param pool        Pool to allocate memory.
++ * @param dst Destination parameter.
++ * @param src Source parameter.
++ */
++PJ_DECL(void) pj_ssl_sock_param_copy(pj_pool_t *pool, 
++                                   pj_ssl_sock_param *dst,
++                                   const pj_ssl_sock_param *src);
++
++
++/**
+  * Create secure socket instance.
+  *
+  * @param pool                The pool for allocating secure socket instance.
+@@ -1115,6 +1127,30 @@ PJ_DECL(pj_status_t) pj_ssl_sock_start_accept(pj_ssl_sock_t *ssock,
+ /**
++ * Same as #pj_ssl_sock_start_accept(), but application can provide
++ * a secure socket parameter, which will be used to create a new secure
++ * socket reported in \a on_accept_complete() callback when there is
++ * an incoming connection.
++ *
++ * @param ssock               The secure socket.
++ * @param pool                Pool used to allocate some internal data for the
++ *                    operation.
++ * @param localaddr   Local address to bind on.
++ * @param addr_len    Length of buffer containing local address.
++ * @param newsock_param       Secure socket parameter for new accepted sockets.
++ *
++ * @return            PJ_SUCCESS if the operation has been successful,
++ *                    or the appropriate error code on failure.
++ */
++PJ_DECL(pj_status_t)
++pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
++                        pj_pool_t *pool,
++                        const pj_sockaddr_t *local_addr,
++                        int addr_len,
++                        const pj_ssl_sock_param *newsock_param);
++
++
++/**
+  * Starts asynchronous socket connect() operation and SSL/TLS handshaking 
+  * for this socket. Once the connection is done (either successfully or not),
+  * the \a on_connect_complete() callback will be called.
+diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
+index 913efee..717ab1d 100644
+--- a/pjlib/src/pj/ssl_sock_common.c
++++ b/pjlib/src/pj/ssl_sock_common.c
+@@ -19,6 +19,7 @@
+ #include <pj/ssl_sock.h>
+ #include <pj/assert.h>
+ #include <pj/errno.h>
++#include <pj/pool.h>
+ #include <pj/string.h>
+ /*
+@@ -48,6 +49,31 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param)
+ }
++/*
++ * Duplicate SSL socket parameter.
++ */
++PJ_DEF(void) pj_ssl_sock_param_copy( pj_pool_t *pool, 
++                                   pj_ssl_sock_param *dst,
++                                   const pj_ssl_sock_param *src)
++{
++    /* Init secure socket param */
++    pj_memcpy(dst, src, sizeof(*dst));
++    if (src->ciphers_num > 0) {
++      unsigned i;
++      dst->ciphers = (pj_ssl_cipher*)
++                      pj_pool_calloc(pool, src->ciphers_num, 
++                                     sizeof(pj_ssl_cipher));
++      for (i = 0; i < src->ciphers_num; ++i)
++          dst->ciphers[i] = src->ciphers[i];
++    }
++
++    if (src->server_name.slen) {
++        /* Server name must be null-terminated */
++        pj_strdup_with_null(pool, &dst->server_name, &src->server_name);
++    }
++}
++
++
+ PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings(
+                                               pj_uint32_t verify_status, 
+                                               const char *error_strings[],
+diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
+index 40a5a1e..6a701b7 100644
+--- a/pjlib/src/pj/ssl_sock_ossl.c
++++ b/pjlib/src/pj/ssl_sock_ossl.c
+@@ -141,6 +141,7 @@ struct pj_ssl_sock_t
+     pj_pool_t          *pool;
+     pj_ssl_sock_t      *parent;
+     pj_ssl_sock_param   param;
++    pj_ssl_sock_param   newsock_param;
+     pj_ssl_cert_t      *cert;
+     
+     pj_ssl_cert_info    local_cert_info;
+@@ -1757,11 +1758,9 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
+     unsigned i;
+     pj_status_t status;
+-    PJ_UNUSED_ARG(src_addr_len);
+-
+     /* Create new SSL socket instance */
+-    status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param,
+-                              &ssock);
++    status = pj_ssl_sock_create(ssock_parent->pool,
++                              &ssock_parent->newsock_param, &ssock);
+     if (status != PJ_SUCCESS)
+       goto on_return;
+@@ -2183,20 +2182,8 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
+       return status;
+     /* Init secure socket param */
+-    ssock->param = *param;
++    pj_ssl_sock_param_copy(pool, &ssock->param, param);
+     ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3;
+-    if (param->ciphers_num > 0) {
+-      unsigned i;
+-      ssock->param.ciphers = (pj_ssl_cipher*)
+-                             pj_pool_calloc(pool, param->ciphers_num, 
+-                                            sizeof(pj_ssl_cipher));
+-      for (i = 0; i < param->ciphers_num; ++i)
+-          ssock->param.ciphers[i] = param->ciphers[i];
+-    }
+-
+-    /* Server name must be null-terminated */
+-    pj_strdup_with_null(pool, &ssock->param.server_name, 
+-                      &param->server_name);
+     /* Finally */
+     *p_ssock = ssock;
+@@ -2617,12 +2604,36 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+                                             const pj_sockaddr_t *localaddr,
+                                             int addr_len)
+ {
++    return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len,
++                                   &ssock->param);
++}
++
++
++/**
++ * Same as #pj_ssl_sock_start_accept(), but application provides parameter
++ * for new accepted secure sockets.
++ */
++PJ_DEF(pj_status_t)
++pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
++                        pj_pool_t *pool,
++                        const pj_sockaddr_t *localaddr,
++                        int addr_len,
++                        const pj_ssl_sock_param *newsock_param)
++{
+     pj_activesock_cb asock_cb;
+     pj_activesock_cfg asock_cfg;
+     pj_status_t status;
+     PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
++    /* Verify new socket parameters */
++    if (newsock_param->grp_lock != ssock->param.grp_lock ||
++        newsock_param->sock_af != ssock->param.sock_af ||
++        newsock_param->sock_type != ssock->param.sock_type)
++    {
++        return PJ_EINVAL;
++    }
++
+     /* Create socket */
+     status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
+                           &ssock->sock);
+@@ -2691,6 +2702,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+       goto on_error;
+     /* Start accepting */
++    pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
+     status = pj_activesock_start_accept(ssock->asock, pool);
+     if (status != PJ_SUCCESS)
+       goto on_error;
+diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
+index a9e95fb..91d99a7 100644
+--- a/pjsip/src/pjsip/sip_transport_tls.c
++++ b/pjsip/src/pjsip/sip_transport_tls.c
+@@ -314,7 +314,7 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
+     int af, sip_ssl_method;
+     pj_uint32_t sip_ssl_proto;
+     struct tls_listener *listener;
+-    pj_ssl_sock_param ssock_param;
++    pj_ssl_sock_param ssock_param, newsock_param;
+     pj_sockaddr *listener_addr;
+     pj_bool_t has_listener;
+     pj_status_t status;
+@@ -473,9 +473,14 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
+      */
+     has_listener = PJ_FALSE;
+-    status = pj_ssl_sock_start_accept(listener->ssock, pool, 
++    pj_memcpy(&newsock_param, &ssock_param, sizeof(newsock_param));
++    newsock_param.async_cnt = 1;
++    newsock_param.cb.on_data_read = &on_data_read;
++    newsock_param.cb.on_data_sent = &on_data_sent;
++    status = pj_ssl_sock_start_accept2(listener->ssock, pool, 
+                         (pj_sockaddr_t*)listener_addr, 
+-                        pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr));
++                        pj_sockaddr_get_len((pj_sockaddr_t*)listener_addr),
++                        &newsock_param);
+     if (status == PJ_SUCCESS || status == PJ_EPENDING) {
+       pj_ssl_sock_info info;
+       has_listener = PJ_TRUE;
+-- 
+cgit v0.11.2
+
diff --git a/third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch b/third-party/pjproject/patches/0001-Bump-tcp-tls-and-transaction-log-levels-from-1-to-3.patch
new file mode 100644 (file)
index 0000000..9873abf
--- /dev/null
@@ -0,0 +1,70 @@
+From a147b72df1ec150c1d733e882225db86142fb339 Mon Sep 17 00:00:00 2001
+From: George Joseph <george.joseph@fairview5.com>
+Date: Sun, 21 Feb 2016 10:01:53 -0700
+Subject: [PATCH] Bump tcp/tls and transaction log levels from 1 to 3
+
+sip_transport_tcp, sip_transport_tls and sip_transaction are printing messages
+at log level 1 or 2 for things that are transient, recoverable, possibly
+expected, or are handled with return codes. A good example of this is if we're
+trying to send an OPTIONS message to a TCP client that has disappeared.  Both
+sip_transport_tcp and sip_transaction are printing "connection refused"
+messages because the remote client isn't listening.  This is generally expected
+behavior and it should be up to the app caller to determine if an error message
+is warranted.
+---
+ pjsip/src/pjsip/sip_transaction.c   | 4 ++--
+ pjsip/src/pjsip/sip_transport_tcp.c | 2 +-
+ pjsip/src/pjsip/sip_transport_tls.c | 2 +-
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
+index 46bd971..1b4fdb7 100644
+--- a/pjsip/src/pjsip/sip_transaction.c
++++ b/pjsip/src/pjsip/sip_transaction.c
+@@ -1898,7 +1898,7 @@ static void send_msg_callback( pjsip_send_state *send_state,
+           err =pj_strerror((pj_status_t)-sent, errmsg, sizeof(errmsg));
+-          PJ_LOG(2,(tsx->obj_name,
++          PJ_LOG(3,(tsx->obj_name,
+                     "Failed to send %s! err=%d (%s)",
+                     pjsip_tx_data_get_info(send_state->tdata), -sent,
+                     errmsg));
+@@ -1938,7 +1938,7 @@ static void send_msg_callback( pjsip_send_state *send_state,
+           }
+       } else {
+-          PJ_PERROR(2,(tsx->obj_name, (pj_status_t)-sent,
++          PJ_PERROR(3,(tsx->obj_name, (pj_status_t)-sent,
+                        "Temporary failure in sending %s, "
+                        "will try next server",
+                        pjsip_tx_data_get_info(send_state->tdata)));
+diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c
+index 222cb13..1bbb324 100644
+--- a/pjsip/src/pjsip/sip_transport_tcp.c
++++ b/pjsip/src/pjsip/sip_transport_tcp.c
+@@ -164,7 +164,7 @@ static void tcp_perror(const char *sender, const char *title,
+     pj_strerror(status, errmsg, sizeof(errmsg));
+-    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
++    PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status));
+ }
+diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c
+index 617d7f5..a83ac32 100644
+--- a/pjsip/src/pjsip/sip_transport_tls.c
++++ b/pjsip/src/pjsip/sip_transport_tls.c
+@@ -170,7 +170,7 @@ static void tls_perror(const char *sender, const char *title,
+     pj_strerror(status, errmsg, sizeof(errmsg));
+-    PJ_LOG(1,(sender, "%s: %s [code=%d]", title, errmsg, status));
++    PJ_LOG(3,(sender, "%s: %s [code=%d]", title, errmsg, status));
+ }
+-- 
+2.5.0
+
diff --git a/third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch b/third-party/pjproject/patches/0001-ioqueue-Enable-epoll-in-aconfigure.ac.patch
new file mode 100644 (file)
index 0000000..36b6c65
--- /dev/null
@@ -0,0 +1,80 @@
+From b5c0bc905911f75e08987e6833075481fe16dab2 Mon Sep 17 00:00:00 2001
+From: George Joseph <george.joseph@fairview5.com>
+Date: Mon, 22 Feb 2016 13:05:59 -0700
+Subject: [PATCH] ioqueue:  Enable epoll in aconfigure.ac
+
+Although the --enable-epoll option was being accepted, the result
+was always forced to select.  This patch updates aconfigure.ac
+to properly set the value of ac_linux_poll if --enable-epoll is
+specified.
+---
+ README.txt                           |  1 +
+ aconfigure                           | 11 +++++++----
+ aconfigure.ac                        |  7 +++++--
+ pjlib/include/pj/compat/os_auto.h.in |  3 +++
+ 4 files changed, 16 insertions(+), 6 deletions(-)
+
+diff --git a/README.txt b/README.txt
+index bc45da8..48415fd 100644
+--- a/README.txt
++++ b/README.txt
+@@ -463,6 +463,7 @@ Using Default Settings
+    $ ./configure --help
+    ...
+    Optional Features:
++   --enable-epoll           Use epoll on Linux instead of select
+    --disable-floating-point   Disable floating point where possible
+    --disable-sound            Exclude sound (i.e. use null sound)
+    --disable-small-filter     Exclude small filter in resampling
+diff --git a/aconfigure.ac b/aconfigure.ac
+index 2f71abb..3e88124 100644
+--- a/aconfigure.ac
++++ b/aconfigure.ac
+@@ -410,6 +410,7 @@ dnl ######################
+ dnl # ioqueue selection
+ dnl # 
+ AC_SUBST(ac_os_objs)
++AC_SUBST(ac_linux_poll)
+ AC_MSG_CHECKING([ioqueue backend])
+ AC_ARG_ENABLE(epoll,
+             AC_HELP_STRING([--enable-epoll],
+@@ -417,10 +418,13 @@ AC_ARG_ENABLE(epoll,
+             [
+               ac_os_objs=ioqueue_epoll.o
+               AC_MSG_RESULT([/dev/epoll])
++              AC_DEFINE(PJ_HAS_LINUX_EPOLL,1)
++              ac_linux_poll=epoll
+             ],
+             [
+               ac_os_objs=ioqueue_select.o
+-              AC_MSG_RESULT([select()]) 
++              AC_MSG_RESULT([select()])
++              ac_linux_poll=select
+             ])
+ AC_SUBST(ac_shared_libraries)
+@@ -1879,7 +1883,6 @@ esac
+ AC_SUBST(target)
+-AC_SUBST(ac_linux_poll,select)
+ AC_SUBST(ac_host,unix)
+ AC_SUBST(ac_main_obj)
+ case $target in
+diff --git a/pjlib/include/pj/compat/os_auto.h.in b/pjlib/include/pj/compat/os_auto.h.in
+index 77980d3..c8e73b2 100644
+--- a/pjlib/include/pj/compat/os_auto.h.in
++++ b/pjlib/include/pj/compat/os_auto.h.in
+@@ -128,6 +128,9 @@
+  */
+ #undef PJ_SELECT_NEEDS_NFDS
++/* Was Linux epoll support enabled */
++#undef PJ_HAS_LINUX_EPOLL
++
+ /* Is errno a good way to retrieve OS errors?
+  */
+ #undef PJ_HAS_ERRNO_VAR
+-- 
+2.5.0
+
diff --git a/third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch b/third-party/pjproject/patches/0001-sip_transport-Search-for-transport-even-if-listener-.patch
new file mode 100644 (file)
index 0000000..001912c
--- /dev/null
@@ -0,0 +1,114 @@
+From 552194179eb6deae8326eb0fef446e69240ea41b Mon Sep 17 00:00:00 2001
+From: George Joseph <george.joseph@fairview5.com>
+Date: Fri, 19 Feb 2016 17:05:53 -0700
+Subject: [PATCH] sip_transport:  Search for transport even if listener was
+ specified.
+
+If a listener was specified when calling pjsip_tpmgr_acquire_transport2,
+a new transport was always created instead of using an existing one.  This
+caused several issues mostly related to the remote end not expecting a new
+connection.  I.E.  A TCP client who registered to a server is not going to
+be listening for connections coming back from the server and refuses the
+connection.
+
+Now when pjsip_tpmgr_acquire_transport2 is called with a listener, the
+registry is still searched for an existing transport and the listener
+is used as a factory only if no existing transport can be found.
+---
+ pjsip/src/pjsip/sip_transport.c | 68 ++++++++++++++++++++---------------------
+ 1 file changed, 34 insertions(+), 34 deletions(-)
+
+diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c
+index 0410324..620b9c0 100644
+--- a/pjsip/src/pjsip/sip_transport.c
++++ b/pjsip/src/pjsip/sip_transport.c
+@@ -1999,29 +1999,11 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
+       TRACE_((THIS_FILE, "Transport %s acquired", seltp->obj_name));
+       return PJ_SUCCESS;
+-
+-
+-    } else if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
+-             sel->u.listener)
+-    {
+-      /* Application has requested that a specific listener is to
+-       * be used. In this case, skip transport hash table lookup.
+-       */
+-
+-      /* Verify that the listener type matches the destination type */
+-      if (sel->u.listener->type != type) {
+-          pj_lock_release(mgr->lock);
+-          return PJSIP_ETPNOTSUITABLE;
+-      }
+-
+-      /* We'll use this listener to create transport */
+-      factory = sel->u.listener;
+-
+     } else {
+       /*
+        * This is the "normal" flow, where application doesn't specify
+-       * specific transport/listener to be used to send message to.
++       * specific transport to be used to send message to.
+        * In this case, lookup the transport from the hash table.
+        */
+       pjsip_transport_key key;
+@@ -2081,22 +2063,40 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport2(pjsip_tpmgr *mgr,
+           return PJ_SUCCESS;
+       }
+-      /*
+-       * Transport not found!
+-       * Find factory that can create such transport.
+-       */
+-      factory = mgr->factory_list.next;
+-      while (factory != &mgr->factory_list) {
+-          if (factory->type == type)
+-              break;
+-          factory = factory->next;
+-      }
++      if (sel && sel->type == PJSIP_TPSELECTOR_LISTENER &&
++              sel->u.listener)
++              {
++              /* Application has requested that a specific listener is to
++               * be used.
++               */
++
++              /* Verify that the listener type matches the destination type */
++              if (sel->u.listener->type != type) {
++                      pj_lock_release(mgr->lock);
++                      return PJSIP_ETPNOTSUITABLE;
++              }
+-      if (factory == &mgr->factory_list) {
+-          /* No factory can create the transport! */
+-          pj_lock_release(mgr->lock);
+-          TRACE_((THIS_FILE, "No suitable factory was found either"));
+-          return PJSIP_EUNSUPTRANSPORT;
++              /* We'll use this listener to create transport */
++              factory = sel->u.listener;
++
++      } else {
++              /*
++               * Transport not found!
++               * Find factory that can create such transport.
++               */
++              factory = mgr->factory_list.next;
++              while (factory != &mgr->factory_list) {
++                      if (factory->type == type)
++                              break;
++                      factory = factory->next;
++              }
++
++              if (factory == &mgr->factory_list) {
++                      /* No factory can create the transport! */
++                      pj_lock_release(mgr->lock);
++                      TRACE_((THIS_FILE, "No suitable factory was found either"));
++                      return PJSIP_EUNSUPTRANSPORT;
++              }
+       }
+     }
+-- 
+2.5.0
+
diff --git a/third-party/pjproject/patches/config_site.h b/third-party/pjproject/patches/config_site.h
new file mode 100644 (file)
index 0000000..544c1e8
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Asterisk config_site.h
+ */
+
+#include <sys/select.h>
+
+#define PJ_HAS_IPV6 1
+#define NDEBUG 1
+#define PJ_MAX_HOSTNAME (256)
+#define PJSIP_MAX_URL_SIZE (512)
+#ifdef PJ_HAS_LINUX_EPOLL
+#define PJ_IOQUEUE_MAX_HANDLES (5000)
+#else
+#define PJ_IOQUEUE_MAX_HANDLES (FD_SETSIZE)
+#endif
+#define PJ_IOQUEUE_HAS_SAFE_UNREG 1
+#define PJ_IOQUEUE_MAX_EVENTS_IN_SINGLE_POLL (16)
+
+#define PJ_SCANNER_USE_BITWISE 0
+#define PJ_OS_HAS_CHECK_STACK  0
+#define PJ_LOG_MAX_LEVEL               3
+#define PJ_ENABLE_EXTRA_CHECK  0
+#define PJSIP_MAX_TSX_COUNT            ((64*1024)-1)
+#define PJSIP_MAX_DIALOG_COUNT ((64*1024)-1)
+#define PJSIP_UDP_SO_SNDBUF_SIZE       (512*1024)
+#define PJSIP_UDP_SO_RCVBUF_SIZE       (512*1024)
+#define PJ_DEBUG                       0
+#define PJSIP_SAFE_MODULE              0
+#define PJ_HAS_STRICMP_ALNUM           0
+#define PJ_HASH_USE_OWN_TOLOWER                1
+#define PJSIP_UNESCAPE_IN_PLACE                1
+
+#undef PJ_TODO
+#define PJ_TODO(x)
diff --git a/third-party/pjproject/patches/user.mak b/third-party/pjproject/patches/user.mak
new file mode 100644 (file)
index 0000000..31579d1
--- /dev/null
@@ -0,0 +1,2 @@
+
+CFLAGS += -fPIC -Wno-unu