|
|
@ -171,6 +171,13 @@ struct cel_linkedid {
|
|
|
|
/*! Container of channel references to a linkedid for CEL purposes. */
|
|
|
|
/*! Container of channel references to a linkedid for CEL purposes. */
|
|
|
|
static AO2_GLOBAL_OBJ_STATIC(cel_linkedids);
|
|
|
|
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 */
|
|
|
|
/*! \brief Destructor for cel_config */
|
|
|
|
static void cel_general_config_dtor(void *obj)
|
|
|
|
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;
|
|
|
|
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 */
|
|
|
|
/*! \brief Hashing function for dialstatus container */
|
|
|
|
static int dialstatus_hash(const void *obj, int flags)
|
|
|
|
static int dialstatus_hash(const void *obj, int flags)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct ast_multi_channel_blob *blob;
|
|
|
|
const struct cel_dialstatus *dialstatus;
|
|
|
|
const char *key;
|
|
|
|
const char *key;
|
|
|
|
|
|
|
|
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
@ -394,8 +391,8 @@ static int dialstatus_hash(const void *obj, int flags)
|
|
|
|
key = obj;
|
|
|
|
key = obj;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
blob = (void *) obj;
|
|
|
|
dialstatus = obj;
|
|
|
|
key = get_caller_uniqueid(blob);
|
|
|
|
key = dialstatus->uniqueid;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
/* Hash can only work on something with a full key. */
|
|
|
|
/* 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 */
|
|
|
|
/*! \brief Comparator function for dialstatus container */
|
|
|
|
static int dialstatus_cmp(void *obj, void *arg, int flags)
|
|
|
|
static int dialstatus_cmp(void *obj, void *arg, int flags)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct ast_multi_channel_blob *object_left = obj;
|
|
|
|
struct cel_dialstatus *object_left = obj;
|
|
|
|
struct ast_multi_channel_blob *object_right = arg;
|
|
|
|
struct cel_dialstatus *object_right = arg;
|
|
|
|
const char *right_key = arg;
|
|
|
|
const char *right_key = arg;
|
|
|
|
int cmp;
|
|
|
|
int cmp;
|
|
|
|
|
|
|
|
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
|
switch (flags & OBJ_SEARCH_MASK) {
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
case OBJ_SEARCH_OBJECT:
|
|
|
|
right_key = get_caller_uniqueid(object_right);
|
|
|
|
right_key = object_right->uniqueid;
|
|
|
|
/* Fall through */
|
|
|
|
/* Fall through */
|
|
|
|
case OBJ_SEARCH_KEY:
|
|
|
|
case OBJ_SEARCH_KEY:
|
|
|
|
cmp = strcmp(get_caller_uniqueid(object_left), right_key);
|
|
|
|
cmp = strcmp(object_left->uniqueid, right_key);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case OBJ_SEARCH_PARTIAL_KEY:
|
|
|
|
case OBJ_SEARCH_PARTIAL_KEY:
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* We could also use a partial key struct containing a length
|
|
|
|
* We could also use a partial key struct containing a length
|
|
|
|
* so strlen() does not get called for every comparison instead.
|
|
|
|
* 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;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
/*
|
|
|
@ -959,16 +956,16 @@ typedef void (*cel_channel_snapshot_monitor)(
|
|
|
|
struct ast_channel_snapshot *old_snapshot,
|
|
|
|
struct ast_channel_snapshot *old_snapshot,
|
|
|
|
struct ast_channel_snapshot *new_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 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) {
|
|
|
|
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);
|
|
|
|
ao2_ref(dial_statuses, -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return blob;
|
|
|
|
return dialstatus;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char *get_blob_variable(struct ast_multi_channel_blob *blob, const char *varname)
|
|
|
|
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) {
|
|
|
|
if (!was_hungup && is_hungup) {
|
|
|
|
struct ast_json *extra;
|
|
|
|
struct ast_json *extra;
|
|
|
|
struct ast_multi_channel_blob *blob = get_dialstatus_blob(new_snapshot->uniqueid);
|
|
|
|
struct cel_dialstatus *dialstatus = get_dialstatus(new_snapshot->uniqueid);
|
|
|
|
const char *dialstatus = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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}",
|
|
|
|
extra = ast_json_pack("{s: i, s: s, s: s}",
|
|
|
|
"hangupcause", new_snapshot->hangupcause,
|
|
|
|
"hangupcause", new_snapshot->hangupcause,
|
|
|
|
"hangupsource", new_snapshot->hangupsource,
|
|
|
|
"hangupsource", new_snapshot->hangupsource,
|
|
|
|
"dialstatus", dialstatus);
|
|
|
|
"dialstatus", dialstatus ? dialstatus->dialstatus : "");
|
|
|
|
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
|
|
|
|
cel_report_event(new_snapshot, AST_CEL_HANGUP, NULL, extra, NULL);
|
|
|
|
ast_json_unref(extra);
|
|
|
|
ast_json_unref(extra);
|
|
|
|
ao2_cleanup(blob);
|
|
|
|
ao2_cleanup(dialstatus);
|
|
|
|
return;
|
|
|
|
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);
|
|
|
|
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) {
|
|
|
|
dialstatus = ao2_find(dial_statuses, snapshot->uniqueid, OBJ_SEARCH_KEY);
|
|
|
|
ao2_link(dial_statuses, blob);
|
|
|
|
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);
|
|
|
|
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)
|
|
|
|
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 stasis_message *message)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct ast_multi_channel_blob *blob = stasis_message_data(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"))) {
|
|
|
|
snapshot = ast_multi_channel_blob_get_channel(blob, "caller");
|
|
|
|
return;
|
|
|
|
if (!snapshot || cel_filter_channel_snapshot(snapshot)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!get_caller_uniqueid(blob)) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!ast_strlen_zero(get_blob_variable(blob, "forward"))) {
|
|
|
|
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;
|
|
|
|
struct ast_json *extra;
|
|
|
|
|
|
|
|
|
|
|
|
if (!caller) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
|
|
|
|
extra = ast_json_pack("{s: s}", "forward", get_blob_variable(blob, "forward"));
|
|
|
|
if (extra) {
|
|
|
|
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);
|
|
|
|
ast_json_unref(extra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (is_valid_dialstatus(blob)) {
|
|
|
|
if (is_valid_dialstatus(blob)) {
|
|
|
|
save_dialstatus(blob);
|
|
|
|
save_dialstatus(blob, snapshot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|