INFO/Record request configurable to use dynamic features
[asterisk/asterisk.git] / channels / chan_sip.c
index 94f9579..99fb9b0 100644 (file)
@@ -1384,6 +1384,7 @@ static void  print_group(int fd, ast_group_t group, int crlf);
 static const char *dtmfmode2str(int mode) attribute_const;
 static int str2dtmfmode(const char *str) attribute_unused;
 static const char *insecure2str(int mode) attribute_const;
+static const char *allowoverlap2str(int mode) attribute_const;
 static void cleanup_stale_contexts(char *new, char *old);
 static void print_codec_to_cli(int fd, struct ast_codec_pref *pref);
 static const char *domain_mode_to_text(const enum domain_mode mode);
@@ -1491,7 +1492,7 @@ static void check_via(struct sip_pvt *p, struct sip_request *req);
 static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
 static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
 static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id);
-static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline);
+static int get_msg_text(char *buf, int len, struct sip_request *req);
 static int transmit_state_notify(struct sip_pvt *p, int state, int full, int timeout);
 static void update_connectedline(struct sip_pvt *p, const void *data, size_t datalen);
 static void update_redirecting(struct sip_pvt *p, const void *data, size_t datalen);
@@ -2812,14 +2813,7 @@ cleanup:
 
        if (tcptls_session) {
                ast_mutex_lock(&tcptls_session->lock);
-               if (tcptls_session->f) {
-                       fclose(tcptls_session->f);
-                       tcptls_session->f = NULL;
-               }
-               if (tcptls_session->fd != -1) {
-                       close(tcptls_session->fd);
-                       tcptls_session->fd = -1;
-               }
+               ast_tcptls_close_session_file(tcptls_session);
                tcptls_session->parent = NULL;
                ast_mutex_unlock(&tcptls_session->lock);
 
@@ -2986,10 +2980,9 @@ void dialog_unlink_all(struct sip_pvt *dialog)
                }
                dialog->registry = registry_unref(dialog->registry, "delete dialog->registry");
        }
-       if (dialog->stateid > -1) {
-               ast_extension_state_del(dialog->stateid, NULL);
-               dialog_unref(dialog, "removing extension_state, should unref the associated dialog ptr that was stored there.");
-               dialog->stateid = -1; /* shouldn't we 'zero' this out? */
+       if (dialog->stateid != -1) {
+               ast_extension_state_del(dialog->stateid, cb_extensionstate);
+               dialog->stateid = -1;
        }
        /* Remove link from peer to subscription of MWI */
        if (dialog->relatedpeer && dialog->relatedpeer->mwipvt == dialog) {
@@ -6837,17 +6830,25 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
                break;
        case AST_CONTROL_INCOMPLETE:
                if (ast->_state != AST_STATE_UP) {
-                       if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                       switch (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                       case SIP_PAGE2_ALLOWOVERLAP_YES:
                                transmit_response_reliable(p, "484 Address Incomplete", &p->initreq);
-                       } else {
+                               p->invitestate = INV_COMPLETED;
+                               sip_alreadygone(p);
+                               ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+                               break;
+                       case SIP_PAGE2_ALLOWOVERLAP_DTMF:
+                               /* Just wait for inband DTMF digits */
+                               break;
+                       default:
+                               /* it actually means no support for overlap */
                                transmit_response_reliable(p, "404 Not Found", &p->initreq);
+                               p->invitestate = INV_COMPLETED;
+                               sip_alreadygone(p);
+                               ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
+                               break;
                        }
-                       p->invitestate = INV_COMPLETED;
-                       sip_alreadygone(p);
-                       ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
-                       break;
                }
-               res = 0;
                break;
        case AST_CONTROL_PROCEEDING:
                if ((ast->_state != AST_STATE_UP) &&
@@ -7370,16 +7371,23 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
        case 4:
                f = ast_rtp_instance_read(p->trtp, 0);  /* RTP Text */
                if (sipdebug_text) {
+                       struct ast_str *out = ast_str_create(f->datalen * 4 + 6);
                        int i;
                        unsigned char* arr = f->data.ptr;
-                       for (i=0; i < f->datalen; i++) {
-                               ast_verbose("%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
-                       }
-                       ast_verbose(" -> ");
-                       for (i=0; i < f->datalen; i++) {
-                               ast_verbose("%02X ", arr[i]);
-                       }
-                       ast_verbose("\n");
+                       do {
+                               if (!out) {
+                                       break;
+                               }
+                               for (i = 0; i < f->datalen; i++) {
+                                       ast_str_append(&out, 0, "%c", (arr[i] > ' ' && arr[i] < '}') ? arr[i] : '.');
+                               }
+                               ast_str_append(&out, 0, " -> ");
+                               for (i = 0; i < f->datalen; i++) {
+                                       ast_str_append(&out, 0, "%02X ", arr[i]);
+                               }
+                               ast_verb(0, "%s\n", ast_str_buffer(out));
+                               ast_free(out);
+                       } while (0);
                }
                break;
        case 5:
@@ -7462,7 +7470,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
                                S_COR(ast->caller.id.number.valid, ast->caller.id.number.str, NULL))) {
                                ast_channel_lock(ast);
                                sip_pvt_lock(p);
-                               ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to CNG detection\n", ast->name);
+                               ast_verb(2, "Redirecting '%s' to fax extension due to CNG detection\n", ast->name);
                                pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
                                if (ast_async_goto(ast, target_context, "fax", 1)) {
                                        ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
@@ -9290,15 +9298,18 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                            ast_rtp_lookup_mime_multiple2(s3, NULL, newnoncodeccapability, 0, 0));
        }
 
-       /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
-          they are acceptable */
-       ast_format_cap_copy(p->jointcaps, newjointcapability);                /* Our joint codec profile for this call */
-       ast_format_cap_copy(p->peercaps, newpeercapability);                  /* The other sides capability in latest offer */
-       p->jointnoncodeccapability = newnoncodeccapability;     /* DTMF capabilities */
-
-       if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) { /* respond with single most preferred joint codec, limiting the other side's choice */
-               ast_codec_choose(&p->prefs, p->jointcaps, 1, &tmp_fmt);
-               ast_format_cap_set(p->jointcaps, &tmp_fmt);
+       if (portno != -1 || vportno != -1 || tportno != -1) {
+               /* We are now ready to change the sip session and p->rtp and p->vrtp with the offered codecs, since
+                  they are acceptable */
+               ast_format_cap_copy(p->jointcaps, newjointcapability);                /* Our joint codec profile for this call */
+               ast_format_cap_copy(p->peercaps, newpeercapability);                  /* The other sides capability in latest offer */
+               p->jointnoncodeccapability = newnoncodeccapability;     /* DTMF capabilities */
+       
+               /* respond with single most preferred joint codec, limiting the other side's choice */
+               if (ast_test_flag(&p->flags[1], SIP_PAGE2_PREFERRED_CODEC)) {
+                       ast_codec_choose(&p->prefs, p->jointcaps, 1, &tmp_fmt);
+                       ast_format_cap_set(p->jointcaps, &tmp_fmt);
+               }
        }
 
        /* Setup audio address and port */
@@ -9414,7 +9425,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                                ast_channel_unlock(p->owner);
                                                if (ast_exists_extension(p->owner, target_context, "fax", 1,
                                                        S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL))) {
-                                                       ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", p->owner->name);
+                                                       ast_verb(2, "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", p->owner->name);
                                                        pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten);
                                                        if (ast_async_goto(p->owner, target_context, "fax", 1)) {
                                                                ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context);
@@ -10906,14 +10917,20 @@ static int add_digit(struct sip_request *req, char digit, unsigned int duration,
        int event;
        if (mode) {
                /* Application/dtmf short version used by some implementations */
-               if (digit == '*')
+               if ('0' <= digit && digit <= '9') {
+                       event = digit - '0';
+               } else if (digit == '*') {
                        event = 10;
-               else if (digit == '#')
+               } else if (digit == '#') {
                        event = 11;
-               else if ((digit >= 'A') && (digit <= 'D'))
+               } else if ('A' <= digit && digit <= 'D') {
                        event = 12 + digit - 'A';
-               else
-                       event = atoi(&digit);
+               } else if ('a' <= digit && digit <= 'd') {
+                       event = 12 + digit - 'a';
+               } else {
+                       /* Unknown digit */
+                       event = 0;
+               }
                snprintf(tmp, sizeof(tmp), "%d\r\n", event);
                add_header(req, "Content-Type", "application/dtmf");
                add_content(req, tmp);
@@ -13972,7 +13989,12 @@ static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req)
        return TRUE;            
 }
 
-/*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled */
+/*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled
+ *
+ * \note This calls parse_uri which has the unexpected property that passing more
+ *       arguments results in more splitting. Most common is to leave out the pass
+ *       argument, causing user to contain user:pass if available.
+ */
 static int parse_uri_legacy_check(char *uri, const char *scheme, char **user, char **pass, char **hostport, char **transport)
 {
        int ret = parse_uri(uri, scheme, user, pass, hostport, transport);
@@ -14248,13 +14270,13 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st
        manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: SIP\r\nPeer: SIP/%s\r\nPeerStatus: Registered\r\nAddress: %s\r\n", peer->name,  ast_sockaddr_stringify(&peer->addr));
 
        /* Is this a new IP address for us? */
-       if (VERBOSITY_ATLEAST(2) && ast_sockaddr_cmp(&peer->addr, &oldsin)) {
-               ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s\n", peer->name,
-                               ast_sockaddr_stringify(&peer->addr));
+       if (ast_sockaddr_cmp(&peer->addr, &oldsin)) {
+               ast_verb(3, "Registered SIP '%s' at %s\n", peer->name,
+                       ast_sockaddr_stringify(&peer->addr));
        }
        sip_poke_peer(peer, 0);
        register_peer_exten(peer, 1);
-       
+
        /* Save User agent */
        useragent = sip_get_header(req, "User-Agent");
        if (strcasecmp(useragent, peer->useragent)) {
@@ -14658,6 +14680,13 @@ static void network_change_event_cb(const struct ast_event *event, void *userdat
        }
 }
 
+static void cb_extensionstate_destroy(int id, void *data)
+{
+       struct sip_pvt *p = data;
+
+       dialog_unref(p, "the extensionstate containing this dialog ptr was destroyed");
+}
+
 /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
 \note  If you add an "hint" priority to the extension in the dial plan,
        you will get notifications on device state changes */
@@ -14672,7 +14701,6 @@ static int cb_extensionstate(const char *context, const char *exten, enum ast_ex
        case AST_EXTENSION_REMOVED:     /* Extension is gone */
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);     /* Delete subscription in 32 secs */
                ast_verb(2, "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
-               p->stateid = -1;
                p->subscribed = NONE;
                append_history(p, "Subscribestatus", "%s", state == AST_EXTENSION_REMOVED ? "HintRemoved" : "Deactivated");
                break;
@@ -14839,7 +14867,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
        enum check_auth_result res = AUTH_NOT_FOUND;
        struct sip_peer *peer;
        char tmp[256];
-       char *name = NULL, *c, *domain = NULL, *dummy = NULL;
+       char *c, *name, *unused_password, *domain;
        char *uri2 = ast_strdupa(uri);
 
        terminate_uri(uri2);
@@ -14849,7 +14877,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
        c = get_in_brackets(tmp);
        c = remove_uri_parameters(c);
 
-       if (parse_uri_legacy_check(c, "sip:,sips:", &name, &dummy, &domain, NULL)) {
+       if (parse_uri_legacy_check(c, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
                ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_sockaddr_stringify_addr(addr));
                return -1;
        }
@@ -14859,12 +14887,34 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 
        extract_host_from_hostport(&domain);
 
-       /*! \todo XXX here too we interpret a missing @domain as a name-only
-        * URI, whereas the RFC says this is a domain-only uri.
-        */
-       if (!ast_strlen_zero(domain) && !AST_LIST_EMPTY(&domain_list)) {
+       if (ast_strlen_zero(domain)) {
+               /* <sip:name@[EMPTY]>, never good */
+               transmit_response(p, "404 Not found", &p->initreq);
+               return AUTH_UNKNOWN_DOMAIN;
+       }
+
+       if (ast_strlen_zero(name)) {
+               /* <sip:[EMPTY][@]hostport>, unsure whether valid for
+                * registration. RFC 3261, 10.2 states:
+                * "The To header field and the Request-URI field typically
+                * differ, as the former contains a user name."
+                * But, Asterisk has always treated the domain-only uri as a
+                * username: we allow admins to create accounts described by
+                * domain name. */
+               name = domain;
+       }
+
+       /* This here differs from 1.4 and 1.6: the domain matching ACLs were
+        * skipped if it was a domain-only URI (used as username). Here we treat
+        * <sip:hostport> as <sip:host@hostport> and won't forget to test the
+        * domain ACLs against host. */
+       if (!AST_LIST_EMPTY(&domain_list)) {
                if (!check_sip_domain(domain, NULL, 0)) {
-                       transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
+                       if (sip_cfg.alwaysauthreject) {
+                               transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE);
+                       } else {
+                               transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
+                       }
                        return AUTH_UNKNOWN_DOMAIN;
                }
        }
@@ -14948,7 +14998,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
                }
                ao2_unlock(peer);
        }
-       if (!peer && sip_cfg.autocreatepeer) {
+       if (!peer && sip_cfg.autocreatepeer != AUTOPEERS_DISABLED) {
                /* Create peer if we have autocreate mode enabled */
                peer = temp_peer(name);
                if (peer) {
@@ -15400,7 +15450,7 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c
  */
 static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id)
 {
-       char tmp[256] = "", *uri, *domain, *dummy = NULL;
+       char tmp[256] = "", *uri, *unused_password, *domain;
        char tmpf[256] = "", *from = NULL;
        struct sip_request *req;
        char *decoded_uri;
@@ -15416,7 +15466,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
        
        uri = ast_strdupa(get_in_brackets(tmp));
 
-       if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &dummy, &domain, NULL)) {
+       if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &unused_password, &domain, NULL)) {
                ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri);
                return SIP_GET_DEST_INVALID_URI;
        }
@@ -15503,18 +15553,23 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
                }
        } else {
                struct ast_cc_agent *agent;
-               int which = 0;
                /* Check the dialplan for the username part of the request URI,
                   the domain will be stored in the SIPDOMAIN variable
                   Return 0 if we have a matching extension */
-               if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from)) ||
-                   (ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from)) && (which = 1)) ||
-                   !strcmp(decoded_uri, ast_pickup_ext())) {
+               if (ast_exists_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))) {
                        if (!oreq) {
-                               ast_string_field_set(p, exten, which ? decoded_uri : uri);
+                               ast_string_field_set(p, exten, uri);
+                       }
+                       return SIP_GET_DEST_EXTEN_FOUND;
+               }
+               if (ast_exists_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
+                       || !strcmp(decoded_uri, ast_pickup_ext())) {
+                       if (!oreq) {
+                               ast_string_field_set(p, exten, decoded_uri);
                        }
                        return SIP_GET_DEST_EXTEN_FOUND;
-               } else if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) {
+               }
+               if ((agent = find_sip_cc_agent_by_notify_uri(tmp))) {
                        struct sip_cc_agent_pvt *agent_pvt = agent->private_data;
                        /* This is a CC recall. We can set p's extension to the exten from
                         * the original INVITE
@@ -15533,11 +15588,12 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re
                }
        }
 
-       /* Return 1 for pickup extension or overlap dialling support (if we support it) */
-       if((ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP) &&
-           ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))) ||
-           !strncmp(decoded_uri, ast_pickup_ext(), strlen(decoded_uri))) {
-               return SIP_GET_DEST_PICKUP_EXTEN_FOUND;
+       if (ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)
+               && (ast_canmatch_extension(NULL, p->context, uri, 1, S_OR(p->cid_num, from))
+                       || ast_canmatch_extension(NULL, p->context, decoded_uri, 1, S_OR(p->cid_num, from))
+                       || !strncmp(decoded_uri, ast_pickup_ext(), strlen(decoded_uri)))) {
+               /* Overlap dialing is enabled and we need more digits to match an extension. */
+               return SIP_GET_DEST_EXTEN_MATCHMORE;
        }
 
        return SIP_GET_DEST_EXTEN_NOT_FOUND;
@@ -16197,10 +16253,7 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                                              int sipmethod, const char *uri, enum xmittype reliable,
                                              struct ast_sockaddr *addr, struct sip_peer **authpeer)
 {
-       char from[256] = { 0, };
-       char *dummy = NULL;     /* dummy return value for parse_uri */
-       char *domain = NULL;    /* dummy return value for parse_uri */
-       char *of;
+       char from[256] = "", *of, *name, *unused_password, *domain;
        enum check_auth_result res = AUTH_DONT_KNOW;
        char calleridname[50];
        char *uri2 = ast_strdupa(uri);
@@ -16217,8 +16270,9 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                return res;
        }
 
-       if (calleridname[0])
+       if (calleridname[0]) {
                ast_string_field_set(p, cid_name, calleridname);
+       }
 
        if (ast_strlen_zero(p->exten)) {
                char *t = uri2;
@@ -16240,32 +16294,36 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
        /* save the URI part of the From header */
        ast_string_field_set(p, from, of);
 
-       /* ignore all fields but name */
-       if (parse_uri_legacy_check(of, "sip:,sips:", &of, &dummy, &domain, NULL)) {
+       if (parse_uri_legacy_check(of, "sip:,sips:", &name, &unused_password, &domain, NULL)) {
                ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n");
        }
 
-       SIP_PEDANTIC_DECODE(of);
+       SIP_PEDANTIC_DECODE(name);
        SIP_PEDANTIC_DECODE(domain);
 
-       if (ast_strlen_zero(of)) {
-               /* XXX note: the original code considered a missing @host
-                * as a username-only URI. The SIP RFC (19.1.1) says that
-                * this is wrong, and it should be considered as a domain-only URI.
-                * For backward compatibility, we keep this block, but it is
-                * really a mistake and should go away.
-                */
+       extract_host_from_hostport(&domain);
 
-               extract_host_from_hostport(&domain);
-               of = domain;
+       if (ast_strlen_zero(domain)) {
+               /* <sip:name@[EMPTY]>, never good */
+               ast_log(LOG_ERROR, "Empty domain name in FROM header\n");
+               return res;
+       }
+
+       if (ast_strlen_zero(name)) {
+               /* <sip:[EMPTY][@]hostport>. Asterisk 1.4 and 1.6 have always
+                * treated that as a username, so we continue the tradition:
+                * uri is now <sip:host@hostport>. */
+               name = domain;
        } else {
-               char *tmp = ast_strdupa(of);
-               /* We need to be able to handle auth-headers looking like
+               /* Non-empty name, try to get caller id from it */
+               char *tmp = ast_strdupa(name);
+               /* We need to be able to handle from-headers looking like
                        <sip:8164444422;phone-context=+1@1.2.3.4:5060;user=phone;tag=SDadkoa01-gK0c3bdb43>
                */
                tmp = strsep(&tmp, ";");
-               if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp))
+               if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp)) {
                        ast_shrink_phone_number(tmp);
+               }
                ast_string_field_set(p, cid_num, tmp);
        }
 
@@ -16279,20 +16337,22 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                 * pick one or another depending on the request ? XXX
                 */
                const char *hdr = sip_get_header(req, "Authorization");
-               if (ast_strlen_zero(hdr))
+               if (ast_strlen_zero(hdr)) {
                        hdr = sip_get_header(req, "Proxy-Authorization");
+               }
 
-               if ( !ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\"")) ) {
+               if (!ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\""))) {
                        ast_copy_string(from, hdr + strlen("username=\""), sizeof(from));
-                       of = from;
-                       of = strsep(&of, "\"");
+                       name = from;
+                       name = strsep(&name, "\"");
                }
        }
 
-       res = check_peer_ok(p, of, req, sipmethod, addr,
+       res = check_peer_ok(p, name, req, sipmethod, addr,
                        authpeer, reliable, calleridname, uri2);
-       if (res != AUTH_DONT_KNOW)
+       if (res != AUTH_DONT_KNOW) {
                return res;
+       }
 
        /* Finally, apply the guest policy */
        if (sip_cfg.allowguest) {
@@ -16305,11 +16365,11 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ
                } else {
                        res = AUTH_RTP_FAILED;
                }
-       } else if (sip_cfg.alwaysauthreject)
+       } else if (sip_cfg.alwaysauthreject) {
                res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
-       else
+       } else {
                res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
-
+       }
 
        if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT)) {
                ast_set_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT);
@@ -16326,30 +16386,37 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, int sipmethod,
        return check_user_full(p, req, sipmethod, uri, reliable, addr, NULL);
 }
 
-/*! \brief  Get text out of a SIP MESSAGE packet */
-static int get_msg_text(char *buf, int len, struct sip_request *req, int addnewline)
+/*! \brief Get message body from a SIP request
+ * \param buf Destination buffer
+ * \param len Destination buffer size
+ * \param req The SIP request
+ *
+ * When parsing the request originally, the lines are split by LF or CRLF.
+ * This function adds a single LF after every line.
+ */
+static int get_msg_text(char *buf, int len, struct sip_request *req)
 {
        int x;
-       int y;
+       int linelen;
 
        buf[0] = '\0';
-       /*XXX isn't strlen(buf) going to always be 0? */
-       y = len - strlen(buf) - 5;
-       if (y < 0)
-               y = 0;
-       for (x = 0; x < req->lines; x++) {
+       --len; /* reserve strncat null */
+       for (x = 0; len && x < req->lines; ++x) {
                const char *line = REQ_OFFSET_TO_STR(req, line[x]);
-               strncat(buf, line, y); /* safe */
-               y -= strlen(line) + 1;
-               if (y < 0)
-                       y = 0;
-               if (y != 0 && addnewline)
+               strncat(buf, line, len); /* safe */
+               linelen = strlen(buf);
+               buf += linelen;
+               len -= linelen;
+               if (len) {
                        strcat(buf, "\n"); /* safe */
+                       ++buf;
+                       --len;
+               }
        }
        return 0;
 }
 
-static int get_msg_text2(struct ast_str **buf, struct sip_request *req, int addnewline)
+static int get_msg_text2(struct ast_str **buf, struct sip_request *req)
 {
        int i, res = 0;
 
@@ -16358,7 +16425,7 @@ static int get_msg_text2(struct ast_str **buf, struct sip_request *req, int addn
        for (i = 0; res >= 0 && i < req->lines; i++) {
                const char *line = REQ_OFFSET_TO_STR(req, line[i]);
 
-               res = ast_str_append(buf, 0, "%s%s", line, addnewline ? "\n" : "");
+               res = ast_str_append(buf, 0, "%s\n", line);
        }
 
        return res < 0 ? -1 : 0;
@@ -16390,6 +16457,8 @@ AST_THREADSTORAGE(sip_msg_buf);
 static void receive_message(struct sip_pvt *p, struct sip_request *req, struct ast_sockaddr *addr, const char *e)
 {
        struct ast_str *buf;
+       char *cbuf;
+       size_t len;
        struct ast_frame f;
        const char *content_type = sip_get_header(req, "Content-Type");
        struct ast_msg *msg;
@@ -16411,11 +16480,7 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
                return;
        }
 
-       /* If this is an out of dialog msg, add back newlines, otherwise strip the new lines.
-        * In dialog msg's newlines are stripped to preserve the behavior of how Asterisk has worked
-        * in the past.  If it is found later that new lines can be added into in dialog msgs as well,
-        * then change this. */
-       if (get_msg_text2(&buf, req, p->owner ? FALSE : TRUE)) {
+       if (get_msg_text2(&buf, req)) {
                ast_log(LOG_WARNING, "Unable to retrieve text from %s\n", p->callid);
                transmit_response(p, "202 Accepted", req);
                if (!p->owner)
@@ -16423,6 +16488,18 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a
                return;
        }
 
+       /* Strip trailing line feeds from message body. (get_msg_text2 may add
+        * a trailing linefeed and we don't need any at the end) */
+       cbuf = ast_str_buffer(buf);
+       len = ast_str_strlen(buf);
+       while (len > 0) {
+               if (cbuf[--len] != '\n') {
+                       ++len;
+                       break;
+               }
+       }
+       ast_str_truncate(buf, len);
+
        if (p->owner) {
                if (sip_debug_test_pvt(p))
                        ast_verbose("SIP Text message received: '%s'\n", ast_str_buffer(buf));
@@ -16638,6 +16715,18 @@ static enum st_refresher str2strefresher(const char *s)
        return map_s_x(strefreshers, s, -1);
 }
 
+/* Autocreatepeer modes */
+static struct _map_x_s autopeermodes[] = {
+        { AUTOPEERS_DISABLED, "Off"},
+        { AUTOPEERS_VOLATILE, "Volatile"},
+        { AUTOPEERS_PERSIST,  "Persisted"},
+        { -1, NULL},
+};
+
+static const char *autocreatepeer2str(enum autocreatepeer_mode r)
+{
+       return map_x_s(autopeermodes, r, "Unknown");
+}
 
 static int peer_status(struct sip_peer *peer, char *status, int statuslen)
 {
@@ -17147,6 +17236,19 @@ static const char *insecure2str(int mode)
        return map_x_s(insecurestr, mode, "<error>");
 }
 
+static const struct _map_x_s allowoverlapstr[] = {
+       { SIP_PAGE2_ALLOWOVERLAP_YES,   "Yes" },
+       { SIP_PAGE2_ALLOWOVERLAP_DTMF,  "DTMF" },
+       { SIP_PAGE2_ALLOWOVERLAP_NO,    "No" },
+       { -1,                           NULL }, /* terminator */
+};
+
+/*! \brief Convert AllowOverlap setting to printable string */
+static const char *allowoverlap2str(int mode)
+{
+       return map_x_s(allowoverlapstr, mode, "<error>");
+}
+
 /*! \brief Destroy disused contexts between reloads
        Only used in reload_config so the code for regcontext doesn't get ugly
 */
@@ -17649,6 +17751,8 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                        ao2_t_ref(credentials, -1, "Unref peer auth for show");
                }
                ast_cli(fd, "  Context      : %s\n", peer->context);
+               ast_cli(fd, "  Record On feature : %s\n", peer->record_on_feature);
+               ast_cli(fd, "  Record Off feature : %s\n", peer->record_off_feature);
                ast_cli(fd, "  Subscr.Cont. : %s\n", S_OR(peer->subscribecontext, "<Not set>") );
                ast_cli(fd, "  Language     : %s\n", peer->language);
                ast_cli(fd, "  Tonezone     : %s\n", peer->zone[0] != '\0' ? peer->zone : "<Not set>");
@@ -17693,7 +17797,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                ast_cli(fd, "  Trust RPID   : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_TRUSTRPID)));
                ast_cli(fd, "  Send RPID    : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[0], SIP_SENDRPID)));
                ast_cli(fd, "  Subscriptions: %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
-               ast_cli(fd, "  Overlap dial : %s\n", AST_CLI_YESNO(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP)));
+               ast_cli(fd, "  Overlap dial : %s\n", allowoverlap2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_ALLOWOVERLAP)));
                if (peer->outboundproxy)
                        ast_cli(fd, "  Outb. proxy  : %s %s\n", ast_strlen_zero(peer->outboundproxy->name) ? "<not set>" : peer->outboundproxy->name,
                                                        peer->outboundproxy->force ? "(forced)" : "");
@@ -18246,11 +18350,11 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
        ast_cli(a->fd, "  Videosupport:           %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_VIDEOSUPPORT)));
        ast_cli(a->fd, "  Textsupport:            %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_TEXTSUPPORT)));
        ast_cli(a->fd, "  Ignore SDP sess. ver.:  %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_IGNORESDPVERSION)));
-       ast_cli(a->fd, "  AutoCreate Peer:        %s\n", AST_CLI_YESNO(sip_cfg.autocreatepeer));
+       ast_cli(a->fd, "  AutoCreate Peer:        %s\n", autocreatepeer2str(sip_cfg.autocreatepeer));
        ast_cli(a->fd, "  Match Auth Username:    %s\n", AST_CLI_YESNO(global_match_auth_username));
        ast_cli(a->fd, "  Allow unknown access:   %s\n", AST_CLI_YESNO(sip_cfg.allowguest));
        ast_cli(a->fd, "  Allow subscriptions:    %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)));
-       ast_cli(a->fd, "  Allow overlap dialing:  %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
+       ast_cli(a->fd, "  Allow overlap dialing:  %s\n", allowoverlap2str(ast_test_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP)));
        ast_cli(a->fd, "  Allow promisc. redir:   %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_PROMISCREDIR)));
        ast_cli(a->fd, "  Enable call counters:   %s\n", AST_CLI_YESNO(global_callcounter));
        ast_cli(a->fd, "  SIP domain support:     %s\n", AST_CLI_YESNO(!AST_LIST_EMPTY(&domain_list)));
@@ -18400,6 +18504,8 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
        ast_cli(a->fd, "  Allowed transports:     %s\n", get_transport_list(default_transports));
        ast_cli(a->fd, "  Outbound transport:     %s\n", sip_get_transport(default_primary_transport));
        ast_cli(a->fd, "  Context:                %s\n", sip_cfg.default_context);
+       ast_cli(a->fd, "  Record on feature:      %s\n", sip_cfg.default_record_on_feature);
+       ast_cli(a->fd, "  Record off feature:     %s\n", sip_cfg.default_record_off_feature);
        ast_cli(a->fd, "  Force rport:            %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[0], SIP_NAT_FORCE_RPORT)));
        ast_cli(a->fd, "  DTMF:                   %s\n", dtmfmode2str(ast_test_flag(&global_flags[0], SIP_DTMF)));
        ast_cli(a->fd, "  Qualify:                %d\n", default_qualify);
@@ -18987,16 +19093,21 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                        return;
                }
 
-               if (buf[0] == '*') {
+               if ('0' <= buf[0] && buf[0] <= '9') {
+                       event = buf[0] - '0';
+               } else if (buf[0] == '*') {
                        event = 10;
                } else if (buf[0] == '#') {
                        event = 11;
-               } else if ((buf[0] >= 'A') && (buf[0] <= 'D')) {
+               } else if ('A' <= buf[0] && buf[0] <= 'D') {
                        event = 12 + buf[0] - 'A';
+               } else if ('a' <= buf[0] && buf[0] <= 'd') {
+                       event = 12 + buf[0] - 'a';
                } else if (buf[0] == '!') {
                        event = 16;
                } else {
-                       event = atoi(buf);
+                       /* Unknown digit */
+                       event = 0;
                }
                if (event == 16) {
                        /* send a FLASH event */
@@ -19035,7 +19146,7 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                        return;
                }
 
-               get_msg_text(buf, sizeof(buf), req, TRUE);
+               get_msg_text(buf, sizeof(buf), req);
                duration = 100; /* 100 ms */
 
                if (ast_strlen_zero(buf)) {
@@ -19061,6 +19172,9 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                                f.subclass.integer = '#';
                        } else if (event < 16) {
                                f.subclass.integer = 'A' + (event - 12);
+                       } else {
+                               /* Unknown digit. */
+                               f.subclass.integer = '0';
                        }
                        f.len = duration;
                        ast_queue_frame(p->owner, &f);
@@ -19094,20 +19208,43 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
                return;
        } else if (!ast_strlen_zero(c = sip_get_header(req, "Record"))) {
                /* INFO messages generated by some phones to start/stop recording
-                       on phone calls.
-                       OEJ: I think this should be something that is enabled/disabled
-                       per device. I don't want incoming callers to record calls in my
-                       pbx.
-               */
-               /* first, get the feature string, if it exists */
-               struct ast_call_feature *feat;
+                * on phone calls.
+                */
+
+               struct ast_call_feature *feat = NULL;
                int j;
                struct ast_frame f = { AST_FRAME_DTMF, };
+               int suppress_warning = 0; /* Supress warning if the feature is blank */
+
+               if (!p->owner) {        /* not a PBX call */
+                       transmit_response(p, "481 Call leg/transaction does not exist", req);
+                       sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+                       return;
+               }
 
+               /* first, get the feature string, if it exists */
                ast_rdlock_call_features();
-               feat = ast_find_call_feature("automon");
+               if (p->relatedpeer) {
+                       if (!strcasecmp(c, "on")) {
+                               if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
+                                       suppress_warning = 1;
+                               } else {
+                                       feat = ast_find_call_feature(p->relatedpeer->record_on_feature);
+                               }
+                       } else if (!strcasecmp(c, "off")) {
+                               if (ast_strlen_zero(p->relatedpeer->record_on_feature)) {
+                                       suppress_warning = 1;
+                               } else {
+                                       feat = ast_find_call_feature(p->relatedpeer->record_off_feature);
+                               }
+                       } else {
+                               ast_log(LOG_ERROR, "Received INFO requesting to record with invalid value: %s\n", c);
+                       }
+               }
                if (!feat || ast_strlen_zero(feat->exten)) {
-                       ast_log(LOG_WARNING, "Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
+                       if (!suppress_warning) {
+                               ast_log(LOG_WARNING, "Recording requested, but no One Touch Monitor registered. (See features.conf)\n");
+                       }
                        /* 403 means that we don't support this feature, so don't request it again */
                        transmit_response(p, "403 Forbidden", req);
                        ast_unlock_call_features();
@@ -21040,8 +21177,14 @@ static void handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_req
         * -1 means did not respond, 0 means unknown,
         * 1..maxms is a valid response, >maxms means late response.
         */
-       if (pingtime < 1)       /* zero = unknown, so round up to 1 */
+       if (pingtime < 1) {     /* zero = unknown, so round up to 1 */
                pingtime = 1;
+       }
+
+       if (!peer->maxms) { /* this should never happens */
+               pvt_set_needdestroy(p, "got OPTIONS response but qualify is not enabled");
+               return;
+       }
 
        /* Now determine new state and whether it has changed.
         * Use some helper variables to simplify the writing
@@ -21315,7 +21458,7 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                return;
        }
 
-       if (p->relatedpeer && p->method == SIP_OPTIONS) {
+       if (p->relatedpeer && sipmethod == SIP_OPTIONS) {
                /* We don't really care what the response is, just that it replied back.
                   Well, as long as it's not a 100 response...  since we might
                   need to hang around for something more "definitive" */
@@ -21543,10 +21686,13 @@ static void handle_response(struct sip_pvt *p, int resp, const char *rest, struc
                                        break;
                                case 484: /* Address Incomplete */
                                        if (owner && sipmethod != SIP_BYE) {
-                                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                                               switch (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                                               case SIP_PAGE2_ALLOWOVERLAP_YES:
                                                        ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(resp));
-                                               } else {
+                                                       break;
+                                               default:
                                                        ast_queue_hangup_with_cause(p->owner, hangup_sip2cause(404));
+                                                       break;
                                                }
                                        }
                                        break;
@@ -22074,7 +22220,7 @@ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, str
                }
 
                /* Get the text of the attachment */
-               if (get_msg_text(buf, sizeof(buf), req, TRUE)) {
+               if (get_msg_text(buf, sizeof(buf), req)) {
                        ast_log(LOG_WARNING, "Unable to retrieve attachment from NOTIFY %s\n", p->callid);
                        transmit_response(p, "400 Bad request", req);
                        sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
@@ -22259,7 +22405,7 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req, st
                case SIP_GET_DEST_INVALID_URI:
                        msg = "416 Unsupported URI scheme";
                        break;
-               case SIP_GET_DEST_PICKUP_EXTEN_FOUND:
+               case SIP_GET_DEST_EXTEN_MATCHMORE:
                case SIP_GET_DEST_REFUSED:
                case SIP_GET_DEST_EXTEN_NOT_FOUND:
                        //msg = "404 Not Found";
@@ -22980,12 +23126,21 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                        case SIP_GET_DEST_INVALID_URI:
                                transmit_response_reliable(p, "416 Unsupported URI scheme", req);
                                break;
-                       case SIP_GET_DEST_PICKUP_EXTEN_FOUND:
-                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) {
+                       case SIP_GET_DEST_EXTEN_MATCHMORE:
+                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)
+                                       == SIP_PAGE2_ALLOWOVERLAP_YES) {
                                        transmit_response_reliable(p, "484 Address Incomplete", req);
                                        break;
                                }
-                       /* INTENTIONAL FALL THROUGH */
+                               /*
+                                * XXX We would have to implement collecting more digits in
+                                * chan_sip for any other schemes of overlap dialing.
+                                *
+                                * For SIP_PAGE2_ALLOWOVERLAP_DTMF it is better to do this in
+                                * the dialplan using the Incomplete application rather than
+                                * having the channel driver do it.
+                                */
+                               /* Fall through */
                        case SIP_GET_DEST_EXTEN_NOT_FOUND:
                        case SIP_GET_DEST_REFUSED:
                        default:
@@ -23934,7 +24089,7 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int
 
        /* FAKE ringing if not attended transfer */
        if (!p->refer->attendedtransfer) {
-               transmit_notify_with_sipfrag(p, seqno, "183 Ringing", FALSE);
+               transmit_notify_with_sipfrag(p, seqno, "180 Ringing", FALSE);
        }
 
        /* For blind transfer, this will lead to a new call */
@@ -24272,20 +24427,17 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
 {
        struct sip_pvt *pvt;
        int res;
-       char *peer;
+       char *to_uri, *to_host, *to_user;
        struct sip_peer *peer_ptr;
 
        if (!(pvt = sip_alloc(NULL, NULL, 0, SIP_MESSAGE, NULL))) {
                return -1;
        }
 
-       peer = ast_strdupa(to);
-       if (strchr(peer, '@')) {
-               strsep(&peer, "@");
-       } else {
-               strsep(&peer, ":");
-       }
-       if (ast_strlen_zero(peer)) {
+       to_uri = ast_strdupa(to);
+       parse_uri(to_uri, "sip:,sips:", &to_user, NULL, &to_host, NULL);
+
+       if (ast_strlen_zero(to_host)) {
                ast_log(LOG_WARNING, "MESSAGE(to) is invalid for SIP - '%s'\n", to);
                dialog_unlink_all(pvt);
                dialog_unref(pvt, "MESSAGE(to) is invalid for SIP");
@@ -24321,12 +24473,17 @@ static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *f
 
        sip_pvt_lock(pvt);
 
-       if (create_addr(pvt, peer, NULL, TRUE, NULL)) {
+       /* Look up the host to contact */
+       if (create_addr(pvt, to_host, NULL, TRUE, NULL)) {
                sip_pvt_unlock(pvt);
                dialog_unlink_all(pvt);
                dialog_unref(pvt, "create_addr failed sending a MESSAGE");
                return -1;
        }
+
+       if (!ast_strlen_zero(to_user)) {
+               ast_string_field_set(pvt, username, to_user);
+       }
        ast_sip_ouraddrfor(&pvt->sa, &pvt->ourip, pvt);
        ast_set_flag(&pvt->flags[0], SIP_OUTGOING);
 
@@ -24847,11 +25004,21 @@ static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, st
        return handler_result;
 }
 
+/*! \internal \brief Subscribe to MWI events for the specified peer
+ * \note The peer cannot be locked during this method.  sip_send_mwi_peer will
+ * attempt to lock the peer after the event subscription lock is held; if the peer is locked during
+ * this method then we will attempt to lock the event subscription lock but after the peer, creating
+ * a locking inversion.
+ */
 static void add_peer_mwi_subs(struct sip_peer *peer)
 {
        struct sip_mailbox *mailbox;
 
        AST_LIST_TRAVERSE(&peer->mailboxes, mailbox, entry) {
+               if (mailbox->event_sub) {
+                       ast_event_unsubscribe(mailbox->event_sub);
+               }
+
                mailbox->event_sub = ast_event_subscribe(AST_EVENT_MWI, mwi_event_cb, "SIP mbox event", peer,
                        AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox->mailbox,
                        AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, S_OR(mailbox->context, "default"),
@@ -24922,7 +25089,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
 {
        int gotdest = 0;
        int res = 0;
-       int firststate = AST_EXTENSION_REMOVED;
+       int firststate;
        struct sip_peer *authpeer = NULL;
        const char *eventheader = sip_get_header(req, "Event"); /* Get Event package name */
        int resubscribe = (p->subscribed != NONE) && !req->ignore;
@@ -25005,7 +25172,7 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                /* if an authentication response was sent, we are done here */
                if (res == AUTH_CHALLENGE_SENT) /* authpeer = NULL here */
                        return 0;
-               if (res < 0) {
+               if (res != AUTH_SUCCESSFUL) {
                        if (res == AUTH_FAKE_AUTH) {
                                ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From"));
                                transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE);
@@ -25019,17 +25186,17 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                }
        }
 
-       /* At this point, authpeer cannot be NULL. Remember we hold a reference,
-        * so we must release it when done.
-        * XXX must remove all the checks for authpeer == NULL.
+       /* At this point, we hold a reference to authpeer (if not NULL).  It
+        * must be released when done.
         */
 
        /* Check if this device  is allowed to subscribe at all */
        if (!ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWSUBSCRIBE)) {
                transmit_response(p, "403 Forbidden (policy)", req);
                pvt_set_needdestroy(p, "subscription not allowed");
-               if (authpeer)
+               if (authpeer) {
                        sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 1)");
+               }
                return 0;
        }
 
@@ -25049,8 +25216,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        transmit_response(p, "404 Not Found", req);
                }
                pvt_set_needdestroy(p, "subscription target not found");
-               if (authpeer)
+               if (authpeer) {
                        sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
+               }
                return 0;
        }
 
@@ -25065,9 +25233,6 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                enum subscriptiontype subscribed = NONE;
                const char *unknown_acceptheader = NULL;
 
-               if (authpeer)   /* We do not need the authpeer any more */
-                       authpeer = sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
-
                /* Header from Xten Eye-beam Accept: multipart/related, application/rlmi+xml, application/pidf+xml, application/xpidf+xml */
                accept = __get_header(req, "Accept", &start);
                while ((subscribed == NONE) && !ast_strlen_zero(accept)) {
@@ -25105,6 +25270,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                        p->subscribecontext,
                                        p->subscribeuri);
                                pvt_set_needdestroy(p, "no Accept header");
+                               if (authpeer) {
+                                       sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
+                               }
                                return 0;
                        }
                        /* if p->subscribed is non-zero, then accept is not obligatory; according to rfc 3265 section 3.1.3, at least.
@@ -25129,6 +25297,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                p->subscribecontext,
                                p->subscribeuri);
                        pvt_set_needdestroy(p, "unrecognized format");
+                       if (authpeer) {
+                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 2)");
+                       }
                        return 0;
                } else {
                        p->subscribed = subscribed;
@@ -25151,8 +25322,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        transmit_response(p, "406 Not Acceptable", req);
                        ast_debug(2, "Received SIP mailbox subscription for unknown format: %s\n", acceptheader);
                        pvt_set_needdestroy(p, "unknown format");
-                       if (authpeer)
+                       if (authpeer) {
                                sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 3)");
+                       }
                        return 0;
                }
                /* Looks like they actually want a mailbox status
@@ -25161,11 +25333,17 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                  In most devices, this is configurable to the voicemailmain extension you use
                */
                if (!authpeer || AST_LIST_EMPTY(&authpeer->mailboxes)) {
-                       transmit_response(p, "404 Not found (no mailbox)", req);
+                       if (!authpeer) {
+                               transmit_response(p, "404 Not found", req);
+                       } else {
+                               transmit_response(p, "404 Not found (no mailbox)", req);
+                               ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", S_OR(authpeer->name, ""));
+                       }
                        pvt_set_needdestroy(p, "received 404 response");
-                       ast_log(LOG_NOTICE, "Received SIP subscribe for peer without mailbox: %s\n", S_OR(authpeer->name, ""));
-                       if (authpeer)
-                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 4)");
+
+                       if (authpeer) {
+                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 3)");
+                       }
                        return 0;
                }
 
@@ -25175,18 +25353,21 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        add_peer_mwi_subs(authpeer);
                        ao2_lock(p);
                }
-               if (authpeer->mwipvt && authpeer->mwipvt != p) {        /* Destroy old PVT if this is a new one */
+               if (authpeer->mwipvt != p) {    /* Destroy old PVT if this is a new one */
                        /* We only allow one subscription per peer */
-                       dialog_unlink_all(authpeer->mwipvt);
-                       authpeer->mwipvt = dialog_unref(authpeer->mwipvt, "unref dialog authpeer->mwipvt");
-                       /* sip_destroy(authpeer->mwipvt); */
-               }
-               if (authpeer->mwipvt)
-                       dialog_unref(authpeer->mwipvt, "Unref previously stored mwipvt dialog pointer");
-               authpeer->mwipvt = dialog_ref(p, "setting peers' mwipvt to p");         /* Link from peer to pvt UH- should this be dialog_ref()? */
-               if (p->relatedpeer)
-                       sip_unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
-               p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");        /* already refcounted...Link from pvt to peer UH- should this be dialog_ref()? */
+                       if (authpeer->mwipvt) {
+                               dialog_unlink_all(authpeer->mwipvt);
+                               authpeer->mwipvt = dialog_unref(authpeer->mwipvt, "unref dialog authpeer->mwipvt");
+                       }
+                       authpeer->mwipvt = dialog_ref(p, "setting peers' mwipvt to p");
+               }
+
+               if (p->relatedpeer != authpeer) {
+                       if (p->relatedpeer) {
+                               sip_unref_peer(p->relatedpeer, "Unref previously stored relatedpeer ptr");
+                       }
+                       p->relatedpeer = sip_ref_peer(authpeer, "setting dialog's relatedpeer pointer");
+               }
                /* Do not release authpeer here */
        } else if (!strcmp(event, "call-completion")) {
                handle_cc_subscribe(p, req);
@@ -25194,24 +25375,23 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                transmit_response(p, "489 Bad Event", req);
                ast_debug(2, "Received SIP subscribe for unknown event package: %s\n", event);
                pvt_set_needdestroy(p, "unknown event package");
-               if (authpeer)
+               if (authpeer) {
                        sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 5)");
+               }
                return 0;
        }
 
-       /* At this point, if we have an authpeer we should unref it. */
-       if (authpeer) {
-               authpeer = sip_unref_peer(authpeer, "unref pointer into (*authpeer)");
-       }
-
        /* Add subscription for extension state from the PBX core */
        if (p->subscribed != MWI_NOTIFICATION  && p->subscribed != CALL_COMPLETION && !resubscribe) {
-               if (p->stateid > -1) {
+               if (p->stateid != -1) {
                        ast_extension_state_del(p->stateid, cb_extensionstate);
-                       /* we need to dec the refcount, now that the extensionstate is removed */
-                       dialog_unref(p, "the extensionstate containing this dialog ptr was deleted");
                }
-               p->stateid = ast_extension_state_add(p->context, p->exten, cb_extensionstate, dialog_ref(p,"copying dialog ptr into extension state struct"));
+               dialog_ref(p, "copying dialog ptr into extension state struct");
+               p->stateid = ast_extension_state_add_destroy(p->context, p->exten,
+                       cb_extensionstate, cb_extensionstate_destroy, p);
+               if (p->stateid == -1) {
+                       dialog_unref(p, "copying dialog ptr into extension state struct failed");
+               }
        }
 
        if (!req->ignore && p)
@@ -25228,6 +25408,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                "with Expire header less that 'minexpire' limit. Received \"Expire: %d\" min is %d\n",
                                p->exten, p->context, p->expiry, min_expiry);
                        pvt_set_needdestroy(p, "Expires is less that the min expires allowed.");
+                       if (authpeer) {
+                               sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 6)");
+                       }
                        return 0;
                }
 
@@ -25263,6 +25446,9 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                                ast_log(LOG_NOTICE, "Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension.\n", p->exten, p->context, ast_sockaddr_stringify(&p->sa));
                                transmit_response(p, "404 Not found", req);
                                pvt_set_needdestroy(p, "no extension for SUBSCRIBE");
+                               if (authpeer) {
+                                       sip_unref_peer(authpeer, "sip_unref_peer, from handle_request_subscribe (authpeer 6)");
+                               }
                                return 0;
                        }
                        ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
@@ -25278,6 +25464,10 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
                        pvt_set_needdestroy(p, "forcing expiration");
                }
        }
+
+       if (authpeer) {
+               sip_unref_peer(authpeer, "unref pointer into (*authpeer)");
+       }
        return 1;
 }
 
@@ -25950,8 +26140,8 @@ create_tcptls_session_fail:
                ao2_t_ref(ca, -1, "failed to create client, getting rid of client tcptls_session arguments");
        }
        if (s->tcptls_session) {
-               close(tcptls_session->fd);
-               s->fd = tcptls_session->fd = -1;
+               ast_tcptls_close_session_file(tcptls_session);
+               s->fd = -1;
                ao2_ref(s->tcptls_session, -1);
                s->tcptls_session = NULL;
        }
@@ -25995,14 +26185,16 @@ static int get_cached_mwi(struct sip_peer *peer, int *new, int *old)
  */
 static int sip_send_mwi_to_peer(struct sip_peer *peer, int cache_only)
 {
-       /* Called with peerl lock, but releases it */
+       /* Called with peer lock, but releases it */
        struct sip_pvt *p;
        int newmsgs = 0, oldmsgs = 0;
-       const char *vmexten;
+       const char *vmexten = NULL;
 
        ao2_lock(peer);
 
-       vmexten = ast_strdupa(peer->vmexten);
+       if (peer->vmexten) {
+               vmexten = ast_strdupa(peer->vmexten);
+       }
 
        if (ast_test_flag((&peer->flags[1]), SIP_PAGE2_SUBSCRIBEMWIONLY) && !peer->mwipvt) {
                ao2_unlock(peer);
@@ -27177,12 +27369,11 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
                }
        } else if (!strcasecmp(v->name, "nat")) {
                ast_set_flag(&mask[0], SIP_NAT_FORCE_RPORT);
+               ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT); /* Default to "force_rport" */
                if (!strcasecmp(v->value, "no")) {
                        ast_clear_flag(&flags[0], SIP_NAT_FORCE_RPORT);
-               } else if (!strcasecmp(v->value, "force_rport")) {
-                       ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
                } else if (!strcasecmp(v->value, "yes")) {
-                       ast_set_flag(&flags[0], SIP_NAT_FORCE_RPORT);
+                       /* We've already defaulted to force_rport */
                        ast_set_flag(&mask[1], SIP_PAGE2_SYMMETRICRTP);
                        ast_set_flag(&flags[1], SIP_PAGE2_SYMMETRICRTP);
                } else if (!strcasecmp(v->value, "comedia")) {
@@ -27239,7 +27430,12 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
                res = 1;
        } else if (!strcasecmp(v->name, "allowoverlap")) {
                ast_set_flag(&mask[1], SIP_PAGE2_ALLOWOVERLAP);
-               ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWOVERLAP);
+               ast_clear_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP);
+               if (ast_true(v->value)) {
+                       ast_set_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP_YES);
+               } else if (!strcasecmp(v->value, "dtmf")){
+                       ast_set_flag(&flags[1], SIP_PAGE2_ALLOWOVERLAP_DTMF);
+               }
        } else if (!strcasecmp(v->name, "allowsubscribe")) {
                ast_set_flag(&mask[1], SIP_PAGE2_ALLOWSUBSCRIBE);
                ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_ALLOWSUBSCRIBE);
@@ -27490,6 +27686,8 @@ static void set_peer_defaults(struct sip_peer *peer)
        ast_copy_flags(&peer->flags[1], &global_flags[1], SIP_PAGE2_FLAGS_TO_COPY);
        ast_copy_flags(&peer->flags[2], &global_flags[2], SIP_PAGE3_FLAGS_TO_COPY);
        ast_string_field_set(peer, context, sip_cfg.default_context);
+       ast_string_field_set(peer, record_on_feature, sip_cfg.default_record_on_feature);
+       ast_string_field_set(peer, record_off_feature, sip_cfg.default_record_off_feature);
        ast_string_field_set(peer, messagecontext, sip_cfg.messagecontext);
        ast_string_field_set(peer, subscribecontext, sip_cfg.default_subscribecontext);
        ast_string_field_set(peer, language, default_language);
@@ -27741,12 +27939,14 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 
                                        if (!strncasecmp(trans, "udp", 3)) {
                                                peer->transports |= SIP_TRANSPORT_UDP;
-                                       } else if (!strncasecmp(trans, "tcp", 3)) {
+                                       } else if (sip_cfg.tcp_enabled && !strncasecmp(trans, "tcp", 3)) {
                                                peer->transports |= SIP_TRANSPORT_TCP;
-                                       } else if (!strncasecmp(trans, "tls", 3)) {
+                                       } else if (default_tls_cfg.enabled && !strncasecmp(trans, "tls", 3)) {
                                                peer->transports |= SIP_TRANSPORT_TLS;
+                                       } else if (!strncasecmp(trans, "tcp", 3) || !strncasecmp(trans, "tls", 3)) {
+                                               ast_log(LOG_WARNING, "'%.3s' is not a valid transport type when %.3senabled=no. If no other is specified, the defaults from general will be used.\n", trans, trans);
                                        } else {
-                                               ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, udp will be used.\n", trans);
+                                               ast_log(LOG_NOTICE, "'%s' is not a valid transport type. if no other is specified, the defaults from general will be used.\n", trans);
                                        }
 
                                        if (!peer->default_outbound_transport) { /*!< The first transport listed should be default outbound */
@@ -27797,6 +27997,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                        } else if (!strcasecmp(v->name, "context")) {
                                ast_string_field_set(peer, context, v->value);
                                ast_set_flag(&peer->flags[1], SIP_PAGE2_HAVEPEERCONTEXT);
+                       } else if (!strcasecmp(v->name, "recordonfeature")) {
+                               ast_string_field_set(peer, record_on_feature, v->value);
+                       } else if (!strcasecmp(v->name, "recordofffeature")) {
+                               ast_string_field_set(peer, record_off_feature, v->value);
                        } else if (!strcasecmp(v->name, "outofcall_message_context")) {
                                ast_string_field_set(peer, messagecontext, v->value);
                        } else if (!strcasecmp(v->name, "subscribecontext")) {
@@ -27939,7 +28143,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                                        peer->amaflags = format;
                                }
                        } else if (!strcasecmp(v->name, "maxforwards")) {
-                               if ((sscanf(v->value, "%30d", &peer->maxforwards) != 1) || (peer->maxforwards < 1)) {
+                               if (sscanf(v->value, "%30d", &peer->maxforwards) != 1
+                                       || peer->maxforwards < 1 || 255 < peer->maxforwards) {
                                        ast_log(LOG_WARNING, "'%s' is not a valid maxforwards value at line %d.  Using default.\n", v->value, v->lineno);
                                        peer->maxforwards = sip_cfg.default_max_forwards;
                                }
@@ -28310,7 +28515,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
 static int peer_markall_func(void *device, void *arg, int flags)
 {
        struct sip_peer *peer = device;
-       peer->the_mark = 1;
+       if (!peer->selfdestruct || sip_cfg.autocreatepeer != AUTOPEERS_PERSIST) {
+               peer->the_mark = 1;
+       }
        return 0;
 }
 
@@ -28330,6 +28537,18 @@ static void sip_set_default_format_capabilities(struct ast_format_cap *cap)
        ast_format_cap_add(cap, ast_format_set(&tmp_fmt, AST_FORMAT_H263, 0));
 }
 
+static void display_nat_warning(const char *cat, int reason, struct ast_flags *flags) {
+       int global_nat, specific_nat;
+
+       if (reason == CHANNEL_MODULE_LOAD && (specific_nat = ast_test_flag(&flags[0], SIP_NAT_FORCE_RPORT)) != (global_nat = ast_test_flag(&global_flags[0], SIP_NAT_FORCE_RPORT))) {
+               ast_log(LOG_WARNING, "!!! PLEASE NOTE: Setting 'nat' for a peer/user that differs from the  global setting can make\n");
+               ast_log(LOG_WARNING, "!!! the name of that peer/user discoverable by an attacker. Replies for non-existent peers/users\n");
+               ast_log(LOG_WARNING, "!!! will be sent to a different port than replies for an existing peer/user. If at all possible,\n");
+               ast_log(LOG_WARNING, "!!! use the global 'nat' setting and do not set 'nat' per peer/user.\n");
+               ast_log(LOG_WARNING, "!!! (config category='%s' global force_rport='%s' peer/user force_rport='%s')\n", cat, AST_CLI_YESNO(global_nat), AST_CLI_YESNO(specific_nat));
+       }
+}
+
 /*! \brief Re-read SIP.conf config file
 \note  This function reloads all config data, except for
        active peers (with registrations). They will only
@@ -28423,11 +28642,6 @@ static int reload_config(enum channelreloadreason reason)
                                }
                                ASTOBJ_UNLOCK(iterator);
                } while(0));
-
-               /* Then, actually destroy users and registry */
-               ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
-               ast_debug(4, "--------------- Done destroying registry list\n");
-               ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, NULL, "callback to mark all peers");
        }
 
        /* Reset certificate handling for TLS sessions */
@@ -28453,6 +28667,7 @@ static int reload_config(enum channelreloadreason reason)
        sipdebug &= sip_debug_console;
        ast_clear_flag(&global_flags[0], AST_FLAGS_ALL);
        ast_clear_flag(&global_flags[1], AST_FLAGS_ALL);
+       ast_clear_flag(&global_flags[2], AST_FLAGS_ALL);
 
        /* Reset IP addresses  */
        ast_sockaddr_parse(&bindaddr, "0.0.0.0:0", 0);
@@ -28528,7 +28743,7 @@ static int reload_config(enum channelreloadreason reason)
        sip_cfg.allowtransfer = TRANSFER_OPENFORALL;    /* Merrily accept all transfers by default */
        sip_cfg.rtautoclear = 120;
        ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWSUBSCRIBE);       /* Default for all devices: TRUE */
-       ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP);         /* Default for all devices: TRUE */
+       ast_set_flag(&global_flags[1], SIP_PAGE2_ALLOWOVERLAP_YES);     /* Default for all devices: Yes */
        sip_cfg.peer_rtupdate = TRUE;
        global_dynamic_exclude_static = 0;      /* Exclude static peers */
        sip_cfg.tcp_enabled = FALSE;
@@ -28545,6 +28760,8 @@ static int reload_config(enum channelreloadreason reason)
 
        /* Initialize some reasonable defaults at SIP reload (used both for channel and as default for devices */
        ast_copy_string(sip_cfg.default_context, DEFAULT_CONTEXT, sizeof(sip_cfg.default_context));
+       ast_copy_string(sip_cfg.default_record_on_feature, DEFAULT_RECORD_FEATURE, sizeof(sip_cfg.default_record_on_feature));
+       ast_copy_string(sip_cfg.default_record_off_feature, DEFAULT_RECORD_FEATURE, sizeof(sip_cfg.default_record_off_feature));
        sip_cfg.default_subscribecontext[0] = '\0';
        sip_cfg.default_max_forwards = DEFAULT_MAX_FORWARDS;
        default_language[0] = '\0';
@@ -28556,8 +28773,9 @@ static int reload_config(enum channelreloadreason reason)
        ast_copy_string(default_mohinterpret, DEFAULT_MOHINTERPRET, sizeof(default_mohinterpret));
        ast_copy_string(default_mohsuggest, DEFAULT_MOHSUGGEST, sizeof(default_mohsuggest));
        ast_copy_string(default_vmexten, DEFAULT_VMEXTEN, sizeof(default_vmexten));
-       ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);                       /*!< Default DTMF setting: RFC2833 */
-       ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA);                       /*!< Allow re-invites */
+       ast_set_flag(&global_flags[0], SIP_DTMF_RFC2833);    /*!< Default DTMF setting: RFC2833 */
+       ast_set_flag(&global_flags[0], SIP_DIRECT_MEDIA);    /*!< Allow re-invites */
+       ast_set_flag(&global_flags[0], SIP_NAT_FORCE_RPORT); /*!< Default to nat=force_rport */
        ast_copy_string(default_engine, DEFAULT_ENGINE, sizeof(default_engine));
        ast_copy_string(default_parkinglot, DEFAULT_PARKINGLOT, sizeof(default_parkinglot));
 
@@ -28611,6 +28829,10 @@ static int reload_config(enum channelreloadreason reason)
 
                if (!strcasecmp(v->name, "context")) {
                        ast_copy_string(sip_cfg.default_context, v->value, sizeof(sip_cfg.default_context));
+               } else if (!strcasecmp(v->name, "recordonfeature")) {
+                       ast_copy_string(sip_cfg.default_record_on_feature, v->value, sizeof(sip_cfg.default_record_on_feature));
+               } else if (!strcasecmp(v->name, "recordofffeature")) {
+                       ast_copy_string(sip_cfg.default_record_off_feature, v->value, sizeof(sip_cfg.default_record_off_feature));
                } else if (!strcasecmp(v->name, "subscribecontext")) {
                        ast_copy_string(sip_cfg.default_subscribecontext, v->value, sizeof(sip_cfg.default_subscribecontext));
                } else if (!strcasecmp(v->name, "callcounter")) {
@@ -28831,7 +29053,11 @@ static int reload_config(enum channelreloadreason reason)
 
                        proxy_update(&sip_cfg.outboundproxy);
                } else if (!strcasecmp(v->name, "autocreatepeer")) {
-                       sip_cfg.autocreatepeer = ast_true(v->value);
+                       if (!strcasecmp(v->value, "persist")) {
+                               sip_cfg.autocreatepeer = AUTOPEERS_PERSIST;
+                       } else {
+                               sip_cfg.autocreatepeer = ast_true(v->value) ? AUTOPEERS_VOLATILE : AUTOPEERS_DISABLED;
+                       }
                } else if (!strcasecmp(v->name, "match_auth_username")) {
                        global_match_auth_username = ast_true(v->value);
                } else if (!strcasecmp(v->name, "srvlookup")) {
@@ -29109,6 +29335,13 @@ static int reload_config(enum channelreloadreason reason)
                }
        }
 
+       if (reason != CHANNEL_MODULE_LOAD) {
+               /* Then, actually destroy users and registry */
+               ASTOBJ_CONTAINER_DESTROYALL(&regl, sip_registry_destroy);
+               ast_debug(4, "--------------- Done destroying registry list\n");
+               ao2_t_callback(peers, OBJ_NODATA, peer_markall_func, NULL, "callback to mark all peers");
+       }
+
        if (subscribe_network_change) {
                network_change_event_subscribe();
        } else {
@@ -29137,9 +29370,23 @@ static int reload_config(enum channelreloadreason reason)
                ast_log(LOG_WARNING, "To disallow external domains, you need to configure local SIP domains.\n");
                sip_cfg.allow_external_domains = 1;
        }
-       /* If not configured, set default transports */
-       if (default_transports == 0) {
+       /* If not or badly configured, set default transports */
+       if (!sip_cfg.tcp_enabled && (default_transports & SIP_TRANSPORT_TCP)) {
+               ast_log(LOG_WARNING, "Cannot use 'tcp' transport with tcpenable=no. Removing from available transports.\n");
+               default_primary_transport &= ~SIP_TRANSPORT_TCP;
+               default_transports &= ~SIP_TRANSPORT_TCP;
+       }
+       if (!default_tls_cfg.enabled && (default_transports & SIP_TRANSPORT_TLS)) {
+               ast_log(LOG_WARNING, "Cannot use 'tls' transport with tlsenable=no. Removing from available transports.\n");
+               default_primary_transport &= ~SIP_TRANSPORT_TLS;
+               default_transports &= ~SIP_TRANSPORT_TLS;
+       }
+       if (!default_transports) {
+               ast_log(LOG_WARNING, "No valid transports available, falling back to 'udp'.\n");
                default_transports = default_primary_transport = SIP_TRANSPORT_UDP;
+       } else if (!default_primary_transport) {
+               ast_log(LOG_WARNING, "No valid default transport. Selecting 'udp' as default.\n");
+               default_primary_transport = SIP_TRANSPORT_UDP;
        }
 
        /* Build list of authentication to various SIP realms, i.e. service providers */
@@ -29342,6 +29589,7 @@ static int reload_config(enum channelreloadreason reason)
                        }
                        peer = build_peer(cat, ast_variable_browse(cfg, cat), NULL, 0, 0);
                        if (peer) {
+                               display_nat_warning(cat, reason, &peer->flags[0]);
                                ao2_t_link(peers, peer, "link peer into peers table");
                                if ((peer->type & SIP_TYPE_PEER) && !ast_sockaddr_isnull(&peer->addr)) {
                                        ao2_t_link(peers_by_ip, peer, "link peer into peers_by_ip table");