/*! * \brief Finds the control object for a channel, filling the response with an * error, if appropriate. * \param[out] response Response to fill with an error if control is not found. * \param channel_id ID of the channel to lookup. * \return Channel control object. * \return \c NULL if control object does not exist. */ static struct stasis_app_control *find_channel_control( struct ast_ari_response *response, const char *channel_id) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); ast_assert(response != NULL); control = stasis_app_control_find_by_channel_id(channel_id); if (control == NULL) { /* Distinguish between 400 and 422 errors */ RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup); snapshot = ast_channel_snapshot_get_latest(channel_id); if (snapshot == NULL) { ast_log(LOG_DEBUG, "Couldn't find '%s'\n", channel_id); ast_ari_response_error(response, 400, "Bad Request", "Channel not found"); return NULL; } ast_log(LOG_DEBUG, "Found non-stasis '%s'\n", channel_id); ast_ari_response_error(response, 422, "Unprocessable Entity", "Channel not in Stasis application"); return NULL; } ao2_ref(control, +1); return control; }
struct ast_named_lock *__ast_named_lock_get(const char *filename, int lineno, const char *func, enum ast_named_lock_type lock_type, const char *keyspace, const char *key) { struct ast_named_lock *lock = NULL; int concat_key_buff_len = strlen(keyspace) + strlen(key) + 2; char *concat_key = ast_alloca(concat_key_buff_len); sprintf(concat_key, "%s-%s", keyspace, key); /* Safe */ ao2_lock(named_locks); lock = ao2_find(named_locks, concat_key, OBJ_SEARCH_KEY | OBJ_NOLOCK); if (lock) { ao2_unlock(named_locks); ast_assert((ao2_options_get(lock) & AO2_ALLOC_OPT_LOCK_MASK) == lock_type); return lock; } lock = ao2_alloc_options(sizeof(*lock) + concat_key_buff_len, NULL, lock_type); if (lock) { strcpy(lock->key, concat_key); /* Safe */ ao2_link_flags(named_locks, lock, OBJ_NOLOCK); } ao2_unlock(named_locks); return lock; }
static struct ast_manager_event_blob *call_pickup_to_ami(struct stasis_message *message) { struct ast_multi_channel_blob *contents = stasis_message_data(message); struct ast_channel_snapshot *chan; struct ast_channel_snapshot *target; struct ast_manager_event_blob *res; RAII_VAR(struct ast_str *, channel_str, NULL, ast_free); RAII_VAR(struct ast_str *, target_str, NULL, ast_free); chan = ast_multi_channel_blob_get_channel(contents, "channel"); target = ast_multi_channel_blob_get_channel(contents, "target"); ast_assert(chan != NULL && target != NULL); if (!(channel_str = ast_manager_build_channel_state_string(chan))) { return NULL; } if (!(target_str = ast_manager_build_channel_state_string_prefix(target, "Target"))) { return NULL; } res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "Pickup", "%s" "%s", ast_str_buffer(channel_str), ast_str_buffer(target_str)); return res; }
/*! * \brief Finds a bridge, filling the response with an error, if appropriate. * * \param[out] response Response to fill with an error if control is not found. * \param bridge_id ID of the bridge to lookup. * * \return Bridget. * \return \c NULL if bridge does not exist. */ static struct ast_bridge *find_bridge( struct ast_ari_response *response, const char *bridge_id) { RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup); ast_assert(response != NULL); bridge = stasis_app_bridge_find_by_id(bridge_id); if (bridge == NULL) { RAII_VAR(struct ast_bridge_snapshot *, snapshot, ast_bridge_snapshot_get_latest(bridge_id), ao2_cleanup); if (!snapshot) { ast_ari_response_error(response, 404, "Not found", "Bridge not found"); return NULL; } ast_ari_response_error(response, 409, "Conflict", "Bridge not in Stasis application"); return NULL; } ao2_ref(bridge, +1); return bridge; }
static int wait_bridge_sort_fn(const void *obj_left, const void *obj_right, const int flags) { const struct wait_bridge_wrapper *left = obj_left; const struct wait_bridge_wrapper *right = obj_right; const char *right_key = obj_right; int cmp; switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) { case OBJ_POINTER: right_key = right->name; /* Fall through */ case OBJ_KEY: cmp = strcmp(left->name, right_key); break; case OBJ_PARTIAL_KEY: cmp = strncmp(left->name, right_key, strlen(right_key)); break; default: /* Sort can only work on something with a full or partial key. */ ast_assert(0); cmp = 0; break; } return cmp; }
int ast_ari_remove_handler(struct stasis_rest_handlers *handler) { RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup); size_t size, i, j; ast_assert(root_handler != NULL); ast_mutex_lock(&root_handler_lock); size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler); new_handler = ao2_alloc(size, NULL); if (!new_handler) { return -1; } memcpy(new_handler, root_handler, sizeof(*new_handler)); for (i = 0, j = 0; i < root_handler->num_children; ++i) { if (root_handler->children[i] == handler) { ast_module_unref(ast_module_info->self); continue; } new_handler->children[j++] = root_handler->children[i]; } new_handler->num_children = j; ao2_cleanup(root_handler); ao2_ref(new_handler, +1); root_handler = new_handler; ast_mutex_unlock(&root_handler_lock); return 0; }
void ast_ari_websocket_events_event_websocket_established( struct ast_ari_websocket_session *ws_session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args) { RAII_VAR(struct event_session *, session, NULL, event_session_cleanup); struct ast_json *msg; const char *session_id; ast_debug(3, "/events WebSocket established\n"); ast_assert(ws_session != NULL); session_id = ast_ari_websocket_session_id(ws_session); /* Find the event_session and update its websocket */ session = ao2_find(event_session_registry, session_id, OBJ_SEARCH_KEY); if (session) { ao2_unlink(event_session_registry, session); event_session_update_websocket(session, ws_session); } else { ast_log(LOG_WARNING, "Failed to locate an event session for the provided websocket session\n"); } /* We don't process any input, but we'll consume it waiting for EOF */ while ((msg = ast_ari_websocket_session_read(ws_session))) { ast_json_unref(msg); } }
/*! * \internal * \brief Create a ast_sip_contact_status object. */ static void *contact_status_alloc(const char *name) { struct ast_sip_contact_status *status = ast_sorcery_generic_alloc(sizeof(*status), contact_status_destroy); char *id = ast_strdupa(name); char *aor = id; char *aor_separator = NULL; if (!status) { ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status\n"); return NULL; } if (ast_string_field_init(status, 256)) { ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status stringfields\n"); ao2_cleanup(status); return NULL; } /* Dynamic contacts are delimited with ";@" and static ones with "@@" */ if ((aor_separator = strstr(id, ";@")) || (aor_separator = strstr(id, "@@"))) { *aor_separator = '\0'; } ast_assert(aor_separator != NULL); ast_string_field_set(status, aor, aor); status->status = CREATED; return status; }
/*! \brief Publish a received device state \ref ast_event to \ref stasis */ static void publish_device_state_to_stasis(struct ast_event *event) { const char *device; enum ast_device_state state; unsigned int cachable; struct ast_eid *event_eid; ast_assert(ast_event_get_type(event) == AST_EVENT_DEVICE_STATE_CHANGE); device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); cachable = ast_event_get_ie_uint(event, AST_EVENT_IE_CACHABLE); event_eid = (struct ast_eid *)ast_event_get_ie_raw(event, AST_EVENT_IE_EID); if (ast_strlen_zero(device)) { return; } if (ast_publish_device_state_full(device, state, cachable, event_eid)) { char eid[18]; ast_eid_to_str(eid, sizeof(eid), event_eid); ast_log(LOG_WARNING, "Failed to publish device state message for %s from %s\n", device, eid); } }
/*! * \brief convert from a pointer _p to a user-defined object * * \return the pointer to the astobj2 structure */ static struct astobj2 *INTERNAL_OBJ(void *user_data) { struct astobj2 *p; if (!user_data) { ast_log(LOG_ERROR, "user_data is NULL\n"); return NULL; } p = (struct astobj2 *) ((char *) user_data - sizeof(*p)); if (AO2_MAGIC != p->priv_data.magic) { if (p->priv_data.magic) { ast_log(LOG_ERROR, "bad magic number 0x%x for object %p\n", p->priv_data.magic, user_data); } else { ast_log(LOG_ERROR, "bad magic number for object %p. Object is likely destroyed.\n", user_data); } ast_assert(0); return NULL; } return p; }
/*! \brief Load (or reload) configuration. */ static int process_config(int reload) { RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup); switch (aco_process_config(&cfg_info, reload)) { case ACO_PROCESS_ERROR: return -1; case ACO_PROCESS_OK: case ACO_PROCESS_UNCHANGED: break; } conf = ast_ari_config_get(); if (!conf) { ast_assert(0); /* We just configured; it should be there */ return -1; } if (conf->general->enabled) { if (ao2_container_count(conf->users) == 0) { ast_log(LOG_ERROR, "No configured users for ARI\n"); } else { ao2_callback(conf->users, OBJ_NODATA, validate_user_cb, NULL); } } return 0; }
void ast_ari_get_global_var(struct ast_variable *headers, struct ast_get_global_var_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); RAII_VAR(struct ast_str *, tmp, NULL, ast_free); const char *value; ast_assert(response != NULL); if (ast_strlen_zero(args->variable)) { ast_ari_response_error( response, 400, "Bad Request", "Variable name is required"); return; } tmp = ast_str_create(32); if (!tmp) { ast_ari_response_alloc_failed(response); return; } value = ast_str_retrieve_variable(&tmp, 0, NULL, NULL, args->variable); if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) { ast_ari_response_alloc_failed(response); return; } ast_ari_response_ok(response, ast_json_ref(json)); }
int __ao2_ref_debug(void *user_data, int delta, const char *tag, const char *file, int line, const char *func) { struct astobj2 *obj = INTERNAL_OBJ(user_data); int old_refcount = -1; if (obj) { old_refcount = internal_ao2_ref(user_data, delta, file, line, func); } if (ref_log && user_data) { if (!obj) { /* Invalid object: Bad magic number. */ fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**invalid**,%s\n", user_data, delta, ast_get_tid(), file, line, func, tag); fflush(ref_log); } else if (old_refcount + delta == 0) { fprintf(ref_log, "%p,%d,%d,%s,%d,%s,**destructor**,%s\n", user_data, delta, ast_get_tid(), file, line, func, tag); fflush(ref_log); } else if (delta != 0) { fprintf(ref_log, "%p,%s%d,%d,%s,%d,%s,%d,%s\n", user_data, (delta < 0 ? "" : "+"), delta, ast_get_tid(), file, line, func, old_refcount, tag); fflush(ref_log); } } if (obj == NULL) { ast_assert(0); } return old_refcount; }
static void send_subscription_subscribe(struct stasis_topic *topic, struct stasis_subscription *sub) { struct stasis_subscription_change *change; struct stasis_message *msg; /* This assumes that we have already unsubscribed */ ast_assert(stasis_subscription_is_subscribed(sub)); if (!stasis_subscription_change_type()) { return; } change = subscription_change_alloc(topic, sub->uniqueid, "Subscribe"); if (!change) { return; } msg = stasis_message_create(stasis_subscription_change_type(), change); if (!msg) { ao2_cleanup(change); return; } stasis_publish(topic, msg); ao2_cleanup(msg); ao2_cleanup(change); }
static int cli_aor_print_header(void *obj, void *arg, int flags) { struct ast_sip_cli_context *context = arg; RAII_VAR(struct ast_sip_cli_formatter_entry *, formatter_entry, NULL, ao2_cleanup); int indent = CLI_INDENT_TO_SPACES(context->indent_level); int filler = CLI_LAST_TABSTOP - indent - 7; ast_assert(context->output_buffer != NULL); ast_str_append(&context->output_buffer, 0, "%*s: <Aor%*.*s> <MaxContact>\n", indent, "Aor", filler, filler, CLI_HEADER_FILLER); if (context->recurse) { context->indent_level++; formatter_entry = ast_sip_lookup_cli_formatter("contact"); if (formatter_entry) { formatter_entry->print_header(NULL, context, 0); } context->indent_level--; } return 0; }
static int format_cmp_cb(void *obj, void *arg, int flags) { const struct ast_format *left = obj; const struct ast_format *right = arg; const char *right_key = arg; int cmp; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: right_key = ast_format_get_name(right); /* Fall through */ case OBJ_SEARCH_KEY: cmp = strcasecmp(ast_format_get_name(left), right_key); break; case OBJ_SEARCH_PARTIAL_KEY: cmp = strncasecmp(ast_format_get_name(left), right_key, strlen(right_key)); break; default: ast_assert(0); cmp = 0; break; } if (cmp) { return 0; } return CMP_MATCH; }
void ast_ari_channels_get(struct ast_variable *headers, struct ast_ari_channels_get_args *args, struct ast_ari_response *response) { RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); struct stasis_cache *cache; struct ast_channel_snapshot *snapshot; cache = ast_channel_cache(); if (!cache) { ast_ari_response_error( response, 500, "Internal Server Error", "Message bus not initialized"); return; } msg = stasis_cache_get(cache, ast_channel_snapshot_type(), args->channel_id); if (!msg) { ast_ari_response_error( response, 404, "Not Found", "Channel not found"); return; } snapshot = stasis_message_data(msg); ast_assert(snapshot != NULL); ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL)); }
static int permanent_uri_sort_fn(const void *obj_left, const void *obj_right, int flags) { const struct ast_sip_contact *object_left = obj_left; const struct ast_sip_contact *object_right = obj_right; const char *right_key = obj_right; int cmp; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: right_key = ast_sorcery_object_get_id(object_right); /* Fall through */ case OBJ_SEARCH_KEY: cmp = strcmp(ast_sorcery_object_get_id(object_left), 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(ast_sorcery_object_get_id(object_left), right_key, strlen(right_key)); break; default: /* Sort can only work on something with a full or partial key. */ ast_assert(0); cmp = 0; break; } return cmp; }
/*! * \brief Finds the control object for a channel, filling the response with an * error, if appropriate. * \param[out] response Response to fill with an error if control is not found. * \param channel_id ID of the channel to lookup. * \return Channel control object. * \return \c NULL if control object does not exist. */ static struct stasis_app_control *find_control( struct ast_ari_response *response, const char *channel_id) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); ast_assert(response != NULL); control = stasis_app_control_find_by_channel_id(channel_id); if (control == NULL) { /* Distinguish between 404 and 409 errors */ RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup); chan = ast_channel_get_by_name(channel_id); if (chan == NULL) { ast_ari_response_error(response, 404, "Not Found", "Channel not found"); return NULL; } ast_ari_response_error(response, 409, "Conflict", "Channel not in Stasis application"); return NULL; } ao2_ref(control, +1); return control; }
static int corosync_node_cmp_fn(void *obj, void *arg, int flags) { struct corosync_node *left = obj; struct corosync_node *right = arg; const int *id = arg; int cmp; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: id = &right->id; /* Fall through */ case OBJ_SEARCH_KEY: cmp = (left->id == *id); break; case OBJ_SEARCH_PARTIAL_KEY: cmp = (left->id == right->id); break; default: /* Sort can only work on something with a full or partial key. */ ast_assert(0); cmp = 1; break; } return cmp ? CMP_MATCH : 0; }
void ast_json_free(void *p) { struct json_mem *mem; struct json_mem_list *free_list; mem = to_json_mem(p); if (!mem) { return; } /* Since the unref is holding a lock in mem, we can't free it * immediately. Store it off on a thread local list to be freed by * ast_json_unref(). */ free_list = json_free_list(); if (!free_list) { ast_log(LOG_ERROR, "Error allocating free list\n"); ast_assert(0); /* It's not ideal to free the memory immediately, but that's the * best we can do if the threadlocal allocation fails */ json_mem_free(mem); return; } AST_LIST_INSERT_HEAD(free_list, mem, list); }
int ast_format_cache_set(struct ast_format *format) { SCOPED_AO2WRLOCK(lock, formats); struct ast_format *old_format; ast_assert(format != NULL); if (ast_strlen_zero(ast_format_get_name(format))) { return -1; } old_format = ao2_find(formats, ast_format_get_name(format), OBJ_SEARCH_KEY | OBJ_NOLOCK); if (old_format) { ao2_unlink_flags(formats, old_format, OBJ_NOLOCK); } ao2_link_flags(formats, format, OBJ_NOLOCK); set_cached_format(ast_format_get_name(format), format); ast_verb(2, "%s cached format with name '%s'\n", old_format ? "Updated" : "Created", ast_format_get_name(format)); ao2_cleanup(old_format); return 0; }
void ast_channel_publish_varset(struct ast_channel *chan, const char *name, const char *value) { RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref); ast_assert(name != NULL); ast_assert(value != NULL); blob = ast_json_pack("{s: s, s: s}", "variable", name, "value", value); if (!blob) { ast_log(LOG_ERROR, "Error creating message\n"); return; } ast_channel_publish_blob(chan, ast_channel_varset_type(), blob); }
static void app_dtor(void *obj) { struct stasis_app *app = obj; ast_verb(1, "Destroying Stasis app %s\n", app->name); ast_assert(app->router == NULL); ast_assert(app->bridge_router == NULL); ast_assert(app->endpoint_router == NULL); ao2_cleanup(app->topic); app->topic = NULL; ao2_cleanup(app->forwards); app->forwards = NULL; ao2_cleanup(app->data); app->data = NULL; }
static void subscription_dtor(void *obj) { struct stasis_subscription *sub = obj; /* Subscriptions need to be manually unsubscribed before destruction * b/c there's a cyclic reference between topics and subscriptions */ ast_assert(!stasis_subscription_is_subscribed(sub)); /* If there are any messages in flight to this subscription; that would * be bad. */ ast_assert(stasis_subscription_is_done(sub)); ao2_cleanup(sub->topic); sub->topic = NULL; ast_taskprocessor_unreference(sub->mailbox); sub->mailbox = NULL; ast_cond_destroy(&sub->join_cond); }
static void test_handler(void *data, const char *app_name, struct ast_json *message) { struct app_data *actual = data; int res; ++(actual->invocations); res = ast_json_array_append(actual->messages, ast_json_copy(message)); ast_assert(res == 0); }
static void jb_get_and_deliver(struct ast_channel *chan) { struct ast_jb *jb = ast_channel_jb(chan); const struct ast_jb_impl *jbimpl = jb->impl; void *jbobj = jb->jbobj; struct ast_frame *f, finterp = { .frametype = AST_FRAME_VOICE, }; long now; int interpolation_len, res; now = get_now(jb, NULL); jb->next = jbimpl->next(jbobj); if (now < jb->next) { jb_framelog("\tJB_GET {now=%ld}: now < next=%ld\n", now, jb->next); return; } while (now >= jb->next) { interpolation_len = ast_codec_interp_len(&jb->last_format); res = jbimpl->get(jbobj, &f, now, interpolation_len); switch (res) { case AST_JB_IMPL_OK: /* deliver the frame */ ast_write(chan, f); case AST_JB_IMPL_DROP: jb_framelog("\tJB_GET {now=%ld}: %s frame with ts=%ld and len=%ld\n", now, jb_get_actions[res], f->ts, f->len); ast_format_copy(&jb->last_format, &f->subclass.format); ast_frfree(f); break; case AST_JB_IMPL_INTERP: /* interpolate a frame */ f = &finterp; ast_format_copy(&f->subclass.format, &jb->last_format); f->samples = interpolation_len * 8; f->src = "JB interpolation"; f->delivery = ast_tvadd(jb->timebase, ast_samp2tv(jb->next, 1000)); f->offset = AST_FRIENDLY_OFFSET; /* deliver the interpolated frame */ ast_write(chan, f); jb_framelog("\tJB_GET {now=%ld}: Interpolated frame with len=%d\n", now, interpolation_len); break; case AST_JB_IMPL_NOFRAME: ast_log(LOG_WARNING, "AST_JB_IMPL_NOFRAME is returned from the %s jb when now=%ld >= next=%ld, jbnext=%ld!\n", jbimpl->name, now, jb->next, jbimpl->next(jbobj)); jb_framelog("\tJB_GET {now=%ld}: No frame for now!?\n", now); return; default: ast_log(LOG_ERROR, "This should never happen!\n"); ast_assert("JB type unknown" == NULL); break; } jb->next = jbimpl->next(jbobj); } }
int __ao2_trylock(void *user_data, enum ao2_lock_req lock_how, const char *file, const char *func, int line, const char *var) { struct astobj2 *obj = INTERNAL_OBJ(user_data); struct astobj2_lock *obj_mutex; struct astobj2_rwlock *obj_rwlock; int res = 0; if (obj == NULL) { ast_assert(0); return -1; } switch (obj->priv_data.options & AO2_ALLOC_OPT_LOCK_MASK) { case AO2_ALLOC_OPT_LOCK_MUTEX: obj_mutex = INTERNAL_OBJ_MUTEX(user_data); res = __ast_pthread_mutex_trylock(file, line, func, var, &obj_mutex->mutex.lock); #ifdef AO2_DEBUG if (!res) { ast_atomic_fetchadd_int(&ao2.total_locked, 1); } #endif break; case AO2_ALLOC_OPT_LOCK_RWLOCK: obj_rwlock = INTERNAL_OBJ_RWLOCK(user_data); switch (lock_how) { case AO2_LOCK_REQ_MUTEX: case AO2_LOCK_REQ_WRLOCK: res = __ast_rwlock_trywrlock(file, line, func, &obj_rwlock->rwlock.lock, var); if (!res) { ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, -1); #ifdef AO2_DEBUG ast_atomic_fetchadd_int(&ao2.total_locked, 1); #endif } break; case AO2_LOCK_REQ_RDLOCK: res = __ast_rwlock_tryrdlock(file, line, func, &obj_rwlock->rwlock.lock, var); if (!res) { ast_atomic_fetchadd_int(&obj_rwlock->rwlock.num_lockers, +1); #ifdef AO2_DEBUG ast_atomic_fetchadd_int(&ao2.total_locked, 1); #endif } break; } break; case AO2_ALLOC_OPT_LOCK_NOLOCK: /* The ao2 object has no lock. */ return 0; default: ast_log(__LOG_ERROR, file, line, func, "Invalid lock option on ao2 object %p\n", user_data); return -1; } return res; }
void stasis_message_router_publish_sync(struct stasis_message_router *router, struct stasis_message *message) { ast_assert(router != NULL); ao2_bump(router); stasis_publish_sync(router->subscription, message); ao2_cleanup(router); }
/*! Forward a endpoint's topics to an app */ static struct app_forwards *forwards_create_endpoint(struct stasis_app *app, struct ast_endpoint *endpoint) { struct app_forwards *forwards; int ret = 0; if (!app) { return NULL; } forwards = forwards_create(app, endpoint ? ast_endpoint_get_id(endpoint) : ENDPOINT_ALL); if (!forwards) { return NULL; } forwards->forward_type = FORWARD_ENDPOINT; if (endpoint) { forwards->topic_forward = stasis_forward_all(ast_endpoint_topic(endpoint), app->topic); forwards->topic_cached_forward = stasis_forward_all( ast_endpoint_topic_cached(endpoint), app->topic); if (!forwards->topic_forward || !forwards->topic_cached_forward) { /* Half-subscribed is a bad thing */ forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } } else { /* Since endpoint subscriptions also subscribe to channels, in the case * of all endpoint subscriptions, we only want messages for the endpoints. * As such, we route those particular messages and then re-publish them * on the app's topic. */ ast_assert(app->endpoint_router == NULL); app->endpoint_router = stasis_message_router_create(ast_endpoint_topic_all_cached()); if (!app->endpoint_router) { forwards_unsubscribe(forwards); ao2_ref(forwards, -1); return NULL; } ret |= stasis_message_router_add(app->endpoint_router, ast_endpoint_state_type(), endpoint_state_cb, app); ret |= stasis_message_router_add(app->endpoint_router, ast_endpoint_contact_state_type(), endpoint_state_cb, app); if (ret) { ao2_ref(app->endpoint_router, -1); app->endpoint_router = NULL; ao2_ref(forwards, -1); return NULL; } } return forwards; }