diff --git a/main/cel.c b/main/cel.c index 887a9e6a56..ad75c01865 100644 --- a/main/cel.c +++ b/main/cel.c @@ -171,6 +171,13 @@ struct cel_linkedid { /*! Container of channel references to a linkedid for CEL purposes. */ static AO2_GLOBAL_OBJ_STATIC(cel_linkedids); +struct cel_dialstatus { + /*! Uniqueid of the channel */ + char uniqueid[AST_MAX_UNIQUEID]; + /*! The dial status */ + char dialstatus[0]; +}; + /*! \brief Destructor for cel_config */ static void cel_general_config_dtor(void *obj) { @@ -373,20 +380,10 @@ static int cel_backend_cmp(void *obj, void *arg, int flags) return CMP_MATCH; } -static const char *get_caller_uniqueid(struct ast_multi_channel_blob *blob) -{ - struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller"); - if (!caller) { - return NULL; - } - - return caller->uniqueid; -} - /*! \brief Hashing function for dialstatus container */ static int dialstatus_hash(const void *obj, int flags) { - struct ast_multi_channel_blob *blob; + const struct cel_dialstatus *dialstatus; const char *key; switch (flags & OBJ_SEARCH_MASK) { @@ -394,8 +391,8 @@ static int dialstatus_hash(const void *obj, int flags) key = obj; break; case OBJ_SEARCH_OBJECT: - blob = (void *) obj; - key = get_caller_uniqueid(blob); + dialstatus = obj; + key = dialstatus->uniqueid; break; default: /* Hash can only work on something with a full key. */ @@ -408,24 +405,24 @@ static int dialstatus_hash(const void *obj, int flags) /*! \brief Comparator function for dialstatus container */ static int dialstatus_cmp(void *obj, void *arg, int flags) { - struct ast_multi_channel_blob *object_left = obj; - struct ast_multi_channel_blob *object_right = arg; + struct cel_dialstatus *object_left = obj; + struct cel_dialstatus *object_right = arg; const char *right_key = arg; int cmp; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: - right_key = get_caller_uniqueid(object_right); + right_key = object_right->uniqueid; /* Fall through */ case OBJ_SEARCH_KEY: - cmp = strcmp(get_caller_uniqueid(object_left), right_key); + cmp = strcmp(object_left->uniqueid, right_key); break; case OBJ_SEARCH_PARTIAL_KEY: /* * We could also use a partial key struct containing a length * so strlen() does not get called for every comparison instead. */ - cmp = strncmp(get_caller_uniqueid(object_left), right_key, strlen(right_key)); + cmp = strncmp(object_left->uniqueid, right_key, strlen(right_key)); break; default: /* @@ -959,16 +956,16 @@ typedef void (*cel_channel_snapshot_monitor)( struct ast_channel_snapshot *old_snapshot, struct ast_channel_snapshot *new_snapshot); -static struct ast_multi_channel_blob *get_dialstatus_blob(const char *uniqueid) +static struct cel_dialstatus *get_dialstatus(const char *uniqueid) { struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store); - struct ast_multi_channel_blob *blob = NULL; + struct cel_dialstatus *dialstatus = NULL; if (dial_statuses) { - blob = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK); + dialstatus = ao2_find(dial_statuses, uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK); ao2_ref(dial_statuses, -1); } - return blob; + return dialstatus; } static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname) @@ -1011,19 +1008,15 @@ static void cel_channel_state_change( if (!was_hungup && is_hungup) { struct ast_json *extra; - struct ast_multi_channel_blob *blob = get_dialstatus_blob(new_snapshot->uniqueid); - const char *dialstatus = ""; + struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid); - if (blob && !ast_strlen_zero(get_blob_variable(blob, "dialstatus"))) { - dialstatus = get_blob_variable(blob, "dialstatus"); - } extra = ast_json_pack("{s: i, s: s, s: s}", "hangupcause", new_snapshot->hangupcause, "hangupsource", new_snapshot->hangupsource, - "dialstatus", dialstatus); + "dialstatus", dialstatus ? dialstatus->dialstatus : ""); cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL); ast_json_unref(extra); - ao2_cleanup(blob); + ao2_cleanup(dialstatus); return; } @@ -1255,16 +1248,48 @@ static void cel_parking_cb( } } -static void save_dialstatus(struct ast_multi_channel_blob *blob) +static void save_dialstatus(struct ast_multi_channel_blob *blob, struct ast_channel_snapshot *snapshot) { struct ao2_container *dial_statuses = ao2_global_obj_ref(cel_dialstatus_store); + const char *dialstatus_string = get_blob_variable(blob, "dialstatus"); + struct cel_dialstatus *dialstatus; + size_t dialstatus_string_len; - ast_assert(blob != NULL); + if (!dial_statuses || ast_strlen_zero(dialstatus_string)) { + ao2_cleanup(dial_statuses); + return; + } - if (dial_statuses) { - ao2_link(dial_statuses, blob); + dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY); + if (dialstatus) { + if (!strcasecmp(dialstatus_string, "ANSWER") && strcasecmp(dialstatus->dialstatus, "ANSWER")) { + /* In the case of an answer after we already have a dial status we give + * priority to the answer since the call was, well, answered. In the case of + * failure dial status results we simply let the first failure be the status. + */ + ao2_unlink(dial_statuses, dialstatus); + ao2_ref(dialstatus, -1); + } else { + ao2_ref(dialstatus, -1); + ao2_ref(dial_statuses, -1); + return; + } + } + + dialstatus_string_len = strlen(dialstatus_string) + 1; + dialstatus = ao2_alloc_options(sizeof(*dialstatus) + dialstatus_string_len, NULL, + AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!dialstatus) { ao2_ref(dial_statuses, -1); + return; } + + ast_copy_string(dialstatus->uniqueid, snapshot->uniqueid, sizeof(dialstatus->uniqueid)); + ast_copy_string(dialstatus->dialstatus, dialstatus_string, dialstatus_string_len); + + ao2_link(dial_statuses, dialstatus); + ao2_ref(dialstatus, -1); + ao2_ref(dial_statuses, -1); } static int is_valid_dialstatus(struct ast_multi_channel_blob *blob) @@ -1300,32 +1325,25 @@ static void cel_dial_cb(void *data, struct stasis_subscription *sub, struct stasis_message *message) { struct ast_multi_channel_blob *blob = stasis_message_data(message); + struct ast_channel_snapshot *snapshot; - if (cel_filter_channel_snapshot(ast_multi_channel_blob_get_channel(blob, "caller"))) { - return; - } - - if (!get_caller_uniqueid(blob)) { + snapshot = ast_multi_channel_blob_get_channel(blob, "caller"); + if (!snapshot || cel_filter_channel_snapshot(snapshot)) { return; } if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) { - struct ast_channel_snapshot *caller = ast_multi_channel_blob_get_channel(blob, "caller"); struct ast_json *extra; - if (!caller) { - return; - } - extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward")); if (extra) { - cel_report_event(caller, AST_CEL_FORWARD, NULL, extra, NULL); + cel_report_event(snapshot, AST_CEL_FORWARD, NULL, extra, NULL); ast_json_unref(extra); } } if (is_valid_dialstatus(blob)) { - save_dialstatus(blob); + save_dialstatus(blob, snapshot); } } diff --git a/tests/test_cel.c b/tests/test_cel.c index 0cd8c5cfab..e54fb59845 100644 --- a/tests/test_cel.c +++ b/tests/test_cel.c @@ -1610,7 +1610,7 @@ AST_TEST_DEFINE(test_cel_dial_pickup) ast_channel_publish_dial(chan_caller, chan_callee, NULL, "ANSWER"); - HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "CANCEL"); + HANGUP_CHANNEL(chan_caller, AST_CAUSE_NORMAL, "ANSWER"); HANGUP_CHANNEL(chan_callee, AST_CAUSE_NORMAL, ""); return AST_TEST_PASS;