INFO/Record request configurable to use dynamic features
[asterisk/asterisk.git] / channels / chan_sip.c
index 8f3b411..99fb9b0 100644 (file)
@@ -2980,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) {
@@ -14681,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 */
@@ -14695,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;
@@ -14993,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) {
@@ -16710,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)
 {
@@ -17734,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>");
@@ -18331,7 +18350,7 @@ 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)));
@@ -18485,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);
@@ -19187,15 +19208,13 @@ 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.
-               */
-               
-               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);
@@ -19205,9 +19224,27 @@ static void handle_request_info(struct sip_pvt *p, struct sip_request *req)
 
                /* 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();
@@ -21140,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
@@ -21415,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" */
@@ -25046,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;
@@ -25340,12 +25383,15 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
 
        /* 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)
@@ -27640,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);
@@ -27949,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")) {
@@ -28463,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;
 }
 
@@ -28588,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 */
@@ -28711,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';
@@ -28778,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")) {
@@ -28998,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")) {
@@ -29276,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 {