/*! * \internal * \brief Send a NOTIFY request to the endpoint within a threaded task. */ static enum notify_result push_notify(const char *endpoint_name, void *info, task_data_create data_create) { RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); struct notify_data *data; if (!(endpoint = ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), "endpoint", endpoint_name))) { return INVALID_ENDPOINT; } if (!(data = data_create(endpoint, info))) { return ALLOC_ERROR; } if (ast_sip_push_task(NULL, notify_endpoint, data)) { ao2_cleanup(data); return TASK_PUSH_ERROR; } return SUCCESS; }
static void sub_default_handler(void *data, struct stasis_subscription *sub, struct stasis_message *message) { struct stasis_app *app = data; RAII_VAR(struct ast_json *, json, NULL, ast_json_unref); if (stasis_subscription_final_message(sub, message)) { ao2_cleanup(app); } if (stasis_message_type(message) == ast_channel_dial_type()) { call_forwarded_handler(app, message); } /* By default, send any message that has a JSON representation */ json = stasis_message_to_json(message, stasis_app_get_sanitizer()); if (!json) { return; } app_send(app, json); }
static void *notify_option_alloc(const char *category) { int category_size = strlen(category) + 1; struct notify_option *option = ao2_alloc( sizeof(*option) + category_size, notify_option_destroy); if (!option) { return NULL; } ast_copy_string(option->name, category, category_size); if (!(option->items = ao2_container_alloc_list( AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_ALLOW, NULL, NULL))) { ao2_cleanup(option); return NULL; } return option; }
static void *bridge_channel_control_thread(void *data) { struct bridge_channel_control_thread_data *thread_data = data; struct ast_channel *bridge_channel = thread_data->bridge_channel; struct stasis_app_control *control = thread_data->control; struct stasis_forward *forward = thread_data->forward; ast_callid callid = ast_channel_callid(bridge_channel); if (callid) { ast_callid_threadassoc_add(callid); } ast_free(thread_data); thread_data = NULL; stasis_app_control_execute_until_exhausted(bridge_channel, control); ast_hangup(bridge_channel); ao2_cleanup(control); stasis_forward_cancel(forward); return NULL; }
static struct mwi_subscription *mwi_subscription_alloc(struct ast_sip_endpoint *endpoint, unsigned int is_solicited, struct ast_sip_subscription *sip_sub) { struct mwi_subscription *sub; const char *endpoint_id = ast_sorcery_object_get_id(endpoint); sub = ao2_alloc(sizeof(*sub) + strlen(endpoint_id), mwi_subscription_destructor); if (!sub) { return NULL; } /* Safe strcpy */ strcpy(sub->id, endpoint_id); /* Unsolicited MWI doesn't actually result in a SIP subscription being * created. This is because a SIP subscription associates with a dialog. * Most devices expect unsolicited MWI NOTIFYs to appear out of dialog. If * they receive an in-dialog MWI NOTIFY (i.e. with a to-tag), then they * will reject the NOTIFY with a 481, thus resulting in message-waiting * state not being updated on the device */ if (is_solicited) { sub->sip_sub = ao2_bump(sip_sub); } sub->stasis_subs = ao2_container_alloc(STASIS_BUCKETS, stasis_sub_hash, stasis_sub_cmp); if (!sub->stasis_subs) { ao2_cleanup(sub); return NULL; } sub->is_solicited = is_solicited; ast_debug(3, "Created %s MWI subscription for endpoint %s\n", is_solicited ? "solicited" : "unsolicited", sub->id); return sub; }
static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags) { RAII_VAR(struct mwi_subscription *, aggregate_sub, NULL, ao2_cleanup); struct ast_sip_endpoint *endpoint = obj; struct ao2_container *mwi_subscriptions = arg; char *mailboxes; char *mailbox; if (ast_strlen_zero(endpoint->subscription.mwi.mailboxes)) { return 0; } if (endpoint->subscription.mwi.aggregate) { aggregate_sub = mwi_subscription_alloc(endpoint, 0, NULL); if (!aggregate_sub) { return 0; } } mailboxes = ast_strdupa(endpoint->subscription.mwi.mailboxes); while ((mailbox = strsep(&mailboxes, ","))) { struct mwi_subscription *sub = aggregate_sub ?: mwi_subscription_alloc(endpoint, 0, NULL); RAII_VAR(struct mwi_stasis_subscription *, mwi_stasis_sub, mwi_stasis_subscription_alloc(mailbox, sub), ao2_cleanup); if (mwi_stasis_sub) { ao2_link(sub->stasis_subs, mwi_stasis_sub); } if (!aggregate_sub) { ao2_link(mwi_subscriptions, sub); ao2_cleanup(sub); } } if (aggregate_sub) { ao2_link(mwi_subscriptions, aggregate_sub); } return 0; }
int ast_ari_add_handler(struct stasis_rest_handlers *handler) { RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup); size_t old_size, new_size; SCOPED_MUTEX(lock, &root_handler_lock); old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler); new_size = old_size + sizeof(handler); new_handler = ao2_alloc(new_size, NULL); if (!new_handler) { return -1; } memcpy(new_handler, root_handler, old_size); new_handler->children[new_handler->num_children++] = handler; ao2_cleanup(root_handler); ao2_ref(new_handler, +1); root_handler = new_handler; ast_module_ref(ast_module_info->self); return 0; }
static void stasis_message_sink_dtor(void *obj) { struct stasis_message_sink *sink = obj; { SCOPED_MUTEX(lock, &sink->lock); while (!sink->is_done) { /* Normally waiting forever is bad, but if we're not * done, we're not done. */ ast_cond_wait(&sink->cond, &sink->lock); } } ast_mutex_destroy(&sink->lock); ast_cond_destroy(&sink->cond); while (sink->num_messages > 0) { ao2_cleanup(sink->messages[--sink->num_messages]); } ast_free(sink->messages); sink->messages = NULL; sink->max_messages = 0; }
struct stasis_message_type *stasis_message_type_create(const char *name, struct stasis_message_vtable *vtable) { struct stasis_message_type *type; type = ao2_alloc(sizeof(*type), message_type_dtor); if (!type) { return NULL; } if (!vtable) { /* Null object pattern, FTW! */ vtable = &null_vtable; } type->name = ast_strdup(name); if (!type->name) { ao2_cleanup(type); return NULL; } type->vtable = vtable; return type; }
int ast_ari_remove_handler(struct stasis_rest_handlers *handler) { struct stasis_rest_handlers *new_handler; size_t size; size_t i; size_t 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) { ast_mutex_unlock(&root_handler_lock); return -1; } /* Create replacement root_handler less the handler to remove. */ 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; /* Replace the old root_handler with the new. */ ao2_cleanup(root_handler); root_handler = new_handler; ast_mutex_unlock(&root_handler_lock); return 0; }
static int format_contact_status(void *obj, void *arg, int flags) { struct ast_sip_contact_wrapper *wrapper = obj; struct ast_sip_contact *contact = wrapper->contact; struct ast_sip_ami *ami = arg; struct ast_sip_contact_status *status; struct ast_str *buf; const struct ast_sip_endpoint *endpoint = ami->arg; buf = ast_sip_create_ami_event("ContactStatusDetail", ami); if (!buf) { return -1; } status = ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id); ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri); if (status) { ast_str_append(&buf, 0, "Status: %s\r\n", status_map[status->status]); ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt); } else { ast_str_append(&buf, 0, "Status: Unknown\r\n"); ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n"); } ast_str_append(&buf, 0, "EndpointName: %s\r\n", ast_sorcery_object_get_id(endpoint)); astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); ami->count++; ast_free(buf); ao2_cleanup(status); return 0; }
/*! \brief Callback function for hanging up a Snoop channel */ static int snoop_hangup(struct ast_channel *chan) { struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan); if (snoop->spy_active) { ast_audiohook_lock(&snoop->spy); ast_audiohook_detach(&snoop->spy); ast_audiohook_unlock(&snoop->spy); } if (snoop->whisper_active) { ast_audiohook_lock(&snoop->whisper); ast_audiohook_detach(&snoop->whisper); ast_audiohook_unlock(&snoop->whisper); } publish_chanspy_message(snoop, 0); ao2_cleanup(snoop); ast_channel_tech_pvt_set(chan, NULL); return 0; }
static void websocket_server_internal_dtor(void *obj) { struct ast_websocket_server *server = obj; ao2_cleanup(server->protocols); server->protocols = NULL; }
static void session_cleanup(struct event_session *session) { session_shutdown(session); ao2_cleanup(session); }
static int rx_task(void *data) { RAII_VAR(struct rx_task_data *, task_data, data, ao2_cleanup); RAII_VAR(struct ao2_container *, contacts, NULL, ao2_cleanup); int added = 0, updated = 0, deleted = 0; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; pjsip_response_addr addr; const char *aor_name = ast_sorcery_object_get_id(task_data->aor); /* Retrieve the current contacts, we'll need to know whether to update or not */ contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); /* So we don't count static contacts against max_contacts we prune them out from the container */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); if (registrar_validate_contacts(task_data->rdata, contacts, task_data->aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 400, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(task_data->endpoint)); return PJ_TRUE; } if ((MAX(added - deleted, 0) + (!task_data->aor->remove_existing ? ao2_container_count(contacts) : 0)) > task_data->aor->max_contacts) { /* Enforce the maximum number of contacts */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(task_data->endpoint, task_data->rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %d\n", ast_sorcery_object_get_id(task_data->endpoint), ast_sorcery_object_get_id(task_data->aor), task_data->aor->max_contacts); return PJ_TRUE; } if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), task_data->rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(task_data->rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { int expiration; char contact_uri[PJSIP_MAX_URL_SIZE]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(task_data->aor, contact_hdr, task_data->rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } ast_sip_location_add_contact(task_data->aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1))); ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d", contact_uri, aor_name, expiration); } else if (expiration) { RAII_VAR(struct ast_sip_contact *, updated, ast_sorcery_copy(ast_sip_get_sorcery(), contact), ao2_cleanup); updated->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); updated->qualify_frequency = task_data->aor->qualify_frequency; updated->authenticate_qualify = task_data->aor->authenticate_qualify; ast_sip_location_update_contact(updated); ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d", contact_uri, aor_name, expiration); } else { ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s", contact_uri, aor_name); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER * do so */ if (task_data->aor->remove_existing) { ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); } /* Update the contacts as things will probably have changed */ ao2_cleanup(contacts); contacts = ast_sip_location_retrieve_aor_contacts(task_data->aor); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (pjsip_endpt_create_response(ast_sip_get_pjsip_endpoint(), task_data->rdata, 200, NULL, &tdata) != PJ_SUCCESS) { return PJ_TRUE; } /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); if (pjsip_get_response_addr(tdata->pool, task_data->rdata, &addr) == PJ_SUCCESS) { pjsip_endpt_send_response(ast_sip_get_pjsip_endpoint(), &addr, tdata, NULL, NULL); } else { pjsip_tx_data_dec_ref(tdata); } return PJ_TRUE; }
static void notify_cfg_destroy(void *obj) { struct notify_cfg *cfg = obj; ao2_cleanup(cfg->notify_options); }
static void notify_option_destroy(void *obj) { struct notify_option *option = obj; ao2_cleanup(option->items); }
static void qualify_data_destroy(struct qualify_data *qual_data) { ao2_cleanup(qual_data->endpoint); ast_free(qual_data); }
/*! * \internal * \brief ast_bridge parking push method. * \since 12.0.0 * * \param self Bridge to operate upon * \param bridge_channel Bridge channel to push * \param swap Bridge channel to swap places with if not NULL * * \note On entry, self is already locked * * \retval 0 on success * \retval -1 on failure */ static int bridge_parking_push(struct ast_bridge_parking *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap) { struct parked_user *pu; const char *blind_transfer; RAII_VAR(struct ast_channel *, parker, NULL, ao2_cleanup); /* XXX replace with ast_channel_cleanup when available */ RAII_VAR(struct park_common_datastore *, park_datastore, NULL, park_common_datastore_free); ast_bridge_base_v_table.push(&self->base, bridge_channel, swap); ast_assert(self->lot != NULL); /* Answer the channel if needed */ if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) { ast_answer(bridge_channel->chan); } if (swap) { int use_ringing = 0; ast_bridge_channel_lock(swap); pu = swap->bridge_pvt; if (!pu) { /* This should be impossible since the only way a channel can enter in the first place * is if it has a parked user associated with it */ publish_parked_call_failure(bridge_channel->chan); ast_bridge_channel_unlock(swap); return -1; } /* Give the swap channel's parked user reference to the incoming channel */ pu->chan = bridge_channel->chan; bridge_channel->bridge_pvt = pu; swap->bridge_pvt = NULL; if (ast_bridge_channel_has_role(swap, "holding_participant")) { const char *idle_mode = ast_bridge_channel_get_role_option(swap, "holding_participant", "idle_mode"); if (!ast_strlen_zero(idle_mode) && !strcmp(idle_mode, "ringing")) { use_ringing = 1; } } ast_bridge_channel_unlock(swap); parking_set_duration(bridge_channel->features, pu); if (parking_channel_set_roles(bridge_channel->chan, self->lot, use_ringing)) { ast_log(LOG_WARNING, "Failed to apply holding bridge roles to %s while joining the parking lot.\n", ast_channel_name(bridge_channel->chan)); } publish_parked_call(pu, PARKED_CALL_SWAP); return 0; } if (!(park_datastore = get_park_common_datastore_copy(bridge_channel->chan))) { /* There was either a failure to apply the datastore when performing park common setup or else we had alloc failures while cloning. Abort. */ return -1; } parker = ast_channel_get_by_name(park_datastore->parker_uuid); /* If the parker and the parkee are the same channel pointer, then the channel entered using * the park application. It's possible that the channel that transferred it is still alive (particularly * when a multichannel bridge is parked), so try to get the real parker if possible. */ ast_channel_lock(bridge_channel->chan); blind_transfer = S_OR(pbx_builtin_getvar_helper(bridge_channel->chan, "BLINDTRANSFER"), ast_channel_name(bridge_channel->chan)); if (blind_transfer) { blind_transfer = ast_strdupa(blind_transfer); } ast_channel_unlock(bridge_channel->chan); if (parker == bridge_channel->chan) { struct ast_channel *real_parker = ast_channel_get_by_name(blind_transfer); if (real_parker) { ao2_cleanup(parker); parker = real_parker; } } pu = generate_parked_user(self->lot, bridge_channel->chan, parker, park_datastore->parker_dial_string, park_datastore->randomize, park_datastore->time_limit); if (!pu) { publish_parked_call_failure(bridge_channel->chan); return -1; } /* If a comeback_override was provided, set it for the parked user's comeback string. */ if (park_datastore->comeback_override) { ast_copy_string(pu->comeback, park_datastore->comeback_override, sizeof(pu->comeback)); } /* Generate ParkedCall Stasis Message */ publish_parked_call(pu, PARKED_CALL); /* If the parkee and the parker are the same and silence_announce isn't set, play the announcement to the parkee */ if (!strcmp(blind_transfer, ast_channel_name(bridge_channel->chan)) && !park_datastore->silence_announce) { char saynum_buf[16]; snprintf(saynum_buf, sizeof(saynum_buf), "%u %u", 0, pu->parking_space); ast_bridge_channel_queue_playfile(bridge_channel, say_parking_space, saynum_buf, NULL); } /* Apply parking duration limits */ parking_set_duration(bridge_channel->features, pu); /* Set this to the bridge pvt so that we don't have to refind the parked user associated with this bridge channel again. */ bridge_channel->bridge_pvt = pu; ast_verb(3, "Parking '" COLORIZE_FMT "' in '" COLORIZE_FMT "' at space %d\n", COLORIZE(COLOR_BRMAGENTA, 0, ast_channel_name(bridge_channel->chan)), COLORIZE(COLOR_BRMAGENTA, 0, self->lot->name), pu->parking_space); parking_notify_metermaids(pu->parking_space, self->lot->cfg->parking_con, AST_DEVICE_INUSE); return 0; }
/*! * \brief Create a pjsip transport. */ static int transport_create(void *data) { struct transport_create_data *create_data = data; struct ws_transport *newtransport = NULL; pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt); pj_pool_t *pool; pj_str_t buf; pj_status_t status; newtransport = ao2_t_alloc_options(sizeof(*newtransport), transport_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK, "pjsip websocket transport"); if (!newtransport) { ast_log(LOG_ERROR, "Failed to allocate WebSocket transport.\n"); goto on_error; } newtransport->transport.endpt = endpt; if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) { ast_log(LOG_ERROR, "Failed to allocate WebSocket endpoint pool.\n"); goto on_error; } newtransport->transport.pool = pool; newtransport->ws_session = create_data->ws_session; /* Keep the session until transport dies */ ast_websocket_ref(newtransport->ws_session); status = pj_atomic_create(pool, 0, &newtransport->transport.ref_cnt); if (status != PJ_SUCCESS) { goto on_error; } status = pj_lock_create_recursive_mutex(pool, pool->obj_name, &newtransport->transport.lock); if (status != PJ_SUCCESS) { goto on_error; } pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&buf, ast_sockaddr_stringify(ast_websocket_remote_address(newtransport->ws_session))), &newtransport->transport.key.rem_addr); newtransport->transport.key.rem_addr.addr.sa_family = pj_AF_INET(); newtransport->transport.key.type = ast_websocket_is_secure(newtransport->ws_session) ? transport_type_wss : transport_type_ws; newtransport->transport.addr_len = pj_sockaddr_get_len(&newtransport->transport.key.rem_addr); pj_sockaddr_cp(&newtransport->transport.local_addr, &newtransport->transport.key.rem_addr); newtransport->transport.local_name.host.ptr = (char *)pj_pool_alloc(pool, newtransport->transport.addr_len+4); pj_sockaddr_print(&newtransport->transport.key.rem_addr, newtransport->transport.local_name.host.ptr, newtransport->transport.addr_len+4, 0); newtransport->transport.local_name.host.slen = pj_ansi_strlen(newtransport->transport.local_name.host.ptr); newtransport->transport.local_name.port = pj_sockaddr_get_port(&newtransport->transport.key.rem_addr); newtransport->transport.type_name = (char *)pjsip_transport_get_type_name(newtransport->transport.key.type); newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type); newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64); newtransport->transport.tpmgr = tpmgr; newtransport->transport.send_msg = &ws_send_msg; newtransport->transport.destroy = &ws_destroy; status = pjsip_transport_register(newtransport->transport.tpmgr, (pjsip_transport *)newtransport); if (status != PJ_SUCCESS) { goto on_error; } /* Add a reference for pjsip transport manager */ ao2_ref(newtransport, +1); newtransport->rdata.tp_info.transport = &newtransport->transport; newtransport->rdata.tp_info.pool = pjsip_endpt_create_pool(endpt, "rtd%p", PJSIP_POOL_RDATA_LEN, PJSIP_POOL_RDATA_INC); if (!newtransport->rdata.tp_info.pool) { ast_log(LOG_ERROR, "Failed to allocate WebSocket rdata.\n"); pjsip_transport_destroy((pjsip_transport *)newtransport); goto on_error; } create_data->transport = newtransport; return 0; on_error: ao2_cleanup(newtransport); return -1; }
/*! \brief Function called when the process is shutting down */ static void codec_shutdown(void) { ast_cli_unregister_multiple(codec_cli, ARRAY_LEN(codec_cli)); ao2_cleanup(codecs); codecs = NULL; }
/*! \brief Initiate new call, part of PBX interface * dest is the dial string */ static int local_call(struct ast_channel *ast, const char *dest, int timeout) { struct local_pvt *p = ast_channel_tech_pvt(ast); int pvt_locked = 0; struct ast_channel *owner = NULL; struct ast_channel *chan = NULL; int res; char *reduced_dest = ast_strdupa(dest); char *slash; const char *chan_cid; if (!p) { return -1; } /* since we are letting go of channel locks that were locked coming into * this function, then we need to give the tech pvt a ref */ ao2_ref(p, 1); ast_channel_unlock(ast); ast_unreal_lock_all(&p->base, &chan, &owner); pvt_locked = 1; if (owner != ast) { res = -1; goto return_cleanup; } if (!owner || !chan) { res = -1; goto return_cleanup; } ast_unreal_call_setup(owner, chan); /* * If the local channel has /n on the end of it, we need to lop * that off for our argument to setting up the CC_INTERFACES * variable. */ if ((slash = strrchr(reduced_dest, '/'))) { *slash = '\0'; } ast_set_cc_interfaces_chanvar(chan, reduced_dest); ao2_unlock(p); pvt_locked = 0; ast_channel_unlock(owner); chan_cid = S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL); if (chan_cid) { chan_cid = ast_strdupa(chan_cid); } ast_channel_unlock(chan); res = -1; switch (p->type) { case LOCAL_CALL_ACTION_DIALPLAN: if (!ast_exists_extension(NULL, p->context, p->exten, 1, chan_cid)) { ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->exten, p->context); } else { publish_local_bridge_message(p); /* Start switch on sub channel */ res = ast_pbx_start(chan); } break; case LOCAL_CALL_ACTION_BRIDGE: publish_local_bridge_message(p); ast_answer(chan); res = ast_bridge_impart(p->action.bridge.join, chan, p->action.bridge.swap, p->action.bridge.features, AST_BRIDGE_IMPART_CHAN_INDEPENDENT); ao2_ref(p->action.bridge.join, -1); p->action.bridge.join = NULL; ao2_cleanup(p->action.bridge.swap); p->action.bridge.swap = NULL; p->action.bridge.features = NULL; break; case LOCAL_CALL_ACTION_MASQUERADE: publish_local_bridge_message(p); ast_answer(chan); res = ast_channel_move(p->action.masq, chan); if (!res) { /* Chan is now an orphaned zombie. Destroy it. */ ast_hangup(chan); } p->action.masq = ast_channel_unref(p->action.masq); break; } if (!res) { ao2_lock(p); ast_set_flag(&p->base, AST_UNREAL_CARETAKER_THREAD); ao2_unlock(p); } /* we already unlocked them, clear them here so the cleanup label won't touch them. */ owner = ast_channel_unref(owner); chan = ast_channel_unref(chan); return_cleanup: if (p) { if (pvt_locked) { ao2_unlock(p); } ao2_ref(p, -1); } if (chan) { ast_channel_unlock(chan); ast_channel_unref(chan); } /* * owner is supposed to be == to ast, if it is, don't unlock it * because ast must exit locked */ if (owner) { if (owner != ast) { ast_channel_unlock(owner); ast_channel_lock(ast); } ast_channel_unref(owner); } else { /* we have to exit with ast locked */ ast_channel_lock(ast); } return res; }
static int register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, const char *aor_name, struct ao2_container *contacts) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0, updated = 0, deleted = 0; pjsip_contact_hdr *contact_hdr = NULL; struct registrar_contact_details details = { 0, }; pjsip_tx_data *tdata; RAII_VAR(struct ast_str *, path_str, NULL, ast_free); struct ast_sip_contact *response_contact; char *user_agent = NULL; pjsip_user_agent_hdr *user_agent_hdr; pjsip_expires_hdr *expires_hdr; pjsip_via_hdr *via_hdr; pjsip_via_hdr *via_hdr_last; char *via_addr = NULL; int via_port = 0; pjsip_cid_hdr *call_id_hdr; char *call_id = NULL; size_t alloc_size; /* So we don't count static contacts against max_contacts we prune them out from the container */ ao2_callback(contacts, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, registrar_prune_static, NULL); if (registrar_validate_contacts(rdata, contacts, aor, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 400, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_invalid_contacts_provided"); ast_log(LOG_WARNING, "Failed to validate contacts in REGISTER request from '%s'\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 420, NULL, NULL, NULL); ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); return PJ_TRUE; } if ((MAX(added - deleted, 0) + (!aor->remove_existing ? ao2_container_count(contacts) : 0)) > aor->max_contacts) { /* Enforce the maximum number of contacts */ pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 403, NULL, NULL, NULL); ast_sip_report_failed_acl(endpoint, rdata, "registrar_attempt_exceeds_maximum_configured_contacts"); ast_log(LOG_WARNING, "Registration attempt from endpoint '%s' to AOR '%s' will exceed max contacts of %u\n", ast_sorcery_object_get_id(endpoint), aor_name, aor->max_contacts); return PJ_TRUE; } if (!(details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 256, 256))) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 500, NULL, NULL, NULL); return PJ_TRUE; } user_agent_hdr = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &USER_AGENT, NULL); if (user_agent_hdr) { alloc_size = pj_strlen(&user_agent_hdr->hvalue) + 1; user_agent = ast_alloca(alloc_size); ast_copy_pj_str(user_agent, &user_agent_hdr->hvalue, alloc_size); } /* Find the first Via header */ via_hdr = via_hdr_last = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, NULL); if (via_hdr) { /* Find the last Via header */ while ( (via_hdr = (pjsip_via_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_VIA, via_hdr->next)) != NULL) { via_hdr_last = via_hdr; } alloc_size = pj_strlen(&via_hdr_last->sent_by.host) + 1; via_addr = ast_alloca(alloc_size); ast_copy_pj_str(via_addr, &via_hdr_last->sent_by.host, alloc_size); via_port=via_hdr_last->sent_by.port; } call_id_hdr = (pjsip_cid_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CALL_ID, NULL); if (call_id_hdr) { alloc_size = pj_strlen(&call_id_hdr->id) + 1; call_id = ast_alloca(alloc_size); ast_copy_pj_str(call_id, &call_id_hdr->id, alloc_size); } /* Iterate each provided Contact header and add, update, or delete */ while ((contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr ? contact_hdr->next : NULL))) { int expiration; char contact_uri[pjsip_max_url_size]; RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); if (contact_hdr->star) { /* A star means to unregister everything, so do so for the possible contacts */ ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); break; } if (!PJSIP_URI_SCHEME_IS_SIP(contact_hdr->uri) && !PJSIP_URI_SCHEME_IS_SIPS(contact_hdr->uri)) { /* This registrar only currently supports sip: and sips: URI schemes */ continue; } expiration = registrar_get_expiration(aor, contact_hdr, rdata); details.uri = pjsip_uri_get_uri(contact_hdr->uri); pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", contact_uri, aor_name); continue; } if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, user_agent, via_addr, via_port, call_id, endpoint)) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); } else if (expiration) { struct ast_sip_contact *contact_update; contact_update = ast_sorcery_copy(ast_sip_get_sorcery(), contact); if (!contact_update) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); continue; } contact_update->expiration_time = ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)); contact_update->qualify_frequency = aor->qualify_frequency; contact_update->authenticate_qualify = aor->authenticate_qualify; if (path_str) { ast_string_field_set(contact_update, path, ast_str_buffer(path_str)); } if (user_agent) { ast_string_field_set(contact_update, user_agent, user_agent); } if (!ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) { ast_string_field_set(contact_update, reg_server, ast_config_AST_SYSTEM_NAME); } if (ast_sip_location_update_contact(contact_update)) { ast_log(LOG_ERROR, "Failed to update contact '%s' expiration time to %d seconds.\n", contact->uri, expiration); ast_sip_location_delete_contact(contact); continue; } ast_debug(3, "Refreshed contact '%s' on AOR '%s' with new expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_REFRESHED", "Contact: %s\r\n" "AOR: %s\r\n" "Expiration: %d\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, contact_update->user_agent); ao2_cleanup(contact_update); } else { /* We want to report the user agent that was actually in the removed contact */ ast_sip_location_delete_contact(contact); ast_verb(3, "Removed contact '%s' from AOR '%s' due to request\n", contact_uri, aor_name); ast_test_suite_event_notify("AOR_CONTACT_REMOVED", "Contact: %s\r\n" "AOR: %s\r\n" "UserAgent: %s", contact_uri, aor_name, contact->user_agent); } } pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); /* If the AOR is configured to remove any existing contacts that have not been updated/added as a result of this REGISTER * do so */ if (aor->remove_existing) { ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, registrar_delete_contact, NULL); } /* Re-retrieve contacts. Caller will clean up the original container. */ contacts = ast_sip_location_retrieve_aor_contacts_nolock(aor); response_contact = ao2_callback(contacts, 0, NULL, NULL); /* Send a response containing all of the contacts (including static) that are present on this AOR */ if (ast_sip_create_response(rdata, 200, response_contact, &tdata) != PJ_SUCCESS) { ao2_cleanup(response_contact); ao2_cleanup(contacts); return PJ_TRUE; } ao2_cleanup(response_contact); /* Add the date header to the response, some UAs use this to set their date and time */ registrar_add_date_header(tdata); ao2_callback(contacts, 0, registrar_add_contact, tdata); ao2_cleanup(contacts); if ((expires_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL))) { expires_hdr = pjsip_expires_hdr_create(tdata->pool, registrar_get_expiration(aor, NULL, rdata)); pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires_hdr); } ast_sip_send_stateful_response(rdata, tdata, endpoint); return PJ_TRUE; }
static int send_waveform_to_channel(struct ast_channel *chan, char *waveform, int length, char *intkeys) { int res = 0; int fds[2]; int needed = 0; struct ast_format *owriteformat; struct ast_frame *f; struct myframe { struct ast_frame f; char offset[AST_FRIENDLY_OFFSET]; char frdata[2048]; } myf = { .f = { 0, }, }; if (pipe(fds)) { ast_log(LOG_WARNING, "Unable to create pipe\n"); return -1; } /* Answer if it's not already going */ if (ast_channel_state(chan) != AST_STATE_UP) ast_answer(chan); ast_stopstream(chan); ast_indicate(chan, -1); owriteformat = ao2_bump(ast_channel_writeformat(chan)); res = ast_set_write_format(chan, ast_format_slin); if (res < 0) { ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); ao2_cleanup(owriteformat); return -1; } myf.f.frametype = AST_FRAME_VOICE; myf.f.subclass.format = ast_format_slin; myf.f.offset = AST_FRIENDLY_OFFSET; myf.f.src = __PRETTY_FUNCTION__; myf.f.data.ptr = myf.frdata; res = send_waveform_to_fd(waveform, length, fds[1]); if (res >= 0) { /* Order is important -- there's almost always going to be mp3... we want to prioritize the user */ for (;;) { res = ast_waitfor(chan, 1000); if (res < 1) { res = -1; break; } f = ast_read(chan); if (!f) { ast_log(LOG_WARNING, "Null frame == hangup() detected\n"); res = -1; break; } if (f->frametype == AST_FRAME_DTMF) { ast_debug(1, "User pressed a key\n"); if (intkeys && strchr(intkeys, f->subclass.integer)) { res = f->subclass.integer; ast_frfree(f); break; } } if (f->frametype == AST_FRAME_VOICE) { /* Treat as a generator */ needed = f->samples * 2; if (needed > sizeof(myf.frdata)) { ast_log(LOG_WARNING, "Only able to deliver %d of %d requested samples\n", (int)sizeof(myf.frdata) / 2, needed/2); needed = sizeof(myf.frdata); } res = read(fds[0], myf.frdata, needed); if (res > 0) { myf.f.datalen = res; myf.f.samples = res / 2; if (ast_write(chan, &myf.f) < 0) { res = -1; ast_frfree(f); break; } if (res < needed) { /* last frame */ ast_debug(1, "Last frame\n"); res = 0; ast_frfree(f); break; } } else { ast_debug(1, "No more waveform\n"); res = 0; } } ast_frfree(f); } } close(fds[0]); close(fds[1]); if (!res && owriteformat) ast_set_write_format(chan, owriteformat); ao2_cleanup(owriteformat); return res; }
void AST_OPTIONAL_API_NAME(ast_websocket_unref)(struct ast_websocket *session) { ao2_cleanup(session); }
/*! * \internal * \brief Destroy the scheduled data and remove from scheduler. */ static void sched_data_destructor(void *obj) { struct sched_data *data = obj; ao2_cleanup(data->contact); }
static int serialize_showchan(struct ast_channel *c, char *buf, size_t size) { long elapsed_seconds = 0; int hour = 0, min = 0, sec = 0; struct ast_str *format_buf = ast_str_alloca(64); char cgrp[256]; char pgrp[256]; struct ast_str *write_transpath = ast_str_alloca(256); struct ast_str *read_transpath = ast_str_alloca(256); struct ast_bridge *bridge; memset(buf, 0, size); if (!c) return 0; elapsed_seconds = ast_channel_get_duration(c); hour = elapsed_seconds / 3600; min = (elapsed_seconds % 3600) / 60; sec = elapsed_seconds % 60; ast_channel_lock(c); bridge = ast_channel_get_bridge(c); ast_channel_unlock(c); snprintf(buf,size, "Name= %s\n" "Type= %s\n" "UniqueID= %s\n" "LinkedID= %s\n" "CallerIDNum= %s\n" "CallerIDName= %s\n" "ConnectedLineIDNum= %s\n" "ConnectedLineIDName=%s\n" "DNIDDigits= %s\n" "RDNIS= %s\n" "Parkinglot= %s\n" "Language= %s\n" "State= %s (%u)\n" "Rings= %d\n" "NativeFormat= %s\n" "WriteFormat= %s\n" "ReadFormat= %s\n" "RawWriteFormat= %s\n" "RawReadFormat= %s\n" "WriteTranscode= %s %s\n" "ReadTranscode= %s %s\n" "1stFileDescriptor= %d\n" "Framesin= %u %s\n" "Framesout= %u %s\n" "TimetoHangup= %ld\n" "ElapsedTime= %dh%dm%ds\n" "BridgeID= %s\n" "Context= %s\n" "Extension= %s\n" "Priority= %d\n" "CallGroup= %s\n" "PickupGroup= %s\n" "Application= %s\n" "Data= %s\n" "Blocking_in= %s\n", ast_channel_name(c), ast_channel_tech(c)->type, ast_channel_uniqueid(c), ast_channel_linkedid(c), S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, "(N/A)"), S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, "(N/A)"), S_COR(ast_channel_connected(c)->id.number.valid, ast_channel_connected(c)->id.number.str, "(N/A)"), S_COR(ast_channel_connected(c)->id.name.valid, ast_channel_connected(c)->id.name.str, "(N/A)"), S_OR(ast_channel_dialed(c)->number.str, "(N/A)"), S_COR(ast_channel_redirecting(c)->from.number.valid, ast_channel_redirecting(c)->from.number.str, "(N/A)"), ast_channel_parkinglot(c), ast_channel_language(c), ast_state2str(ast_channel_state(c)), ast_channel_state(c), ast_channel_rings(c), ast_format_cap_get_names(ast_channel_nativeformats(c), &format_buf), ast_format_get_name(ast_channel_writeformat(c)), ast_format_get_name(ast_channel_readformat(c)), ast_format_get_name(ast_channel_rawwriteformat(c)), ast_format_get_name(ast_channel_rawreadformat(c)), ast_channel_writetrans(c) ? "Yes" : "No", ast_translate_path_to_str(ast_channel_writetrans(c), &write_transpath), ast_channel_readtrans(c) ? "Yes" : "No", ast_translate_path_to_str(ast_channel_readtrans(c), &read_transpath), ast_channel_fd(c, 0), ast_channel_fin(c) & ~DEBUGCHAN_FLAG, (ast_channel_fin(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", ast_channel_fout(c) & ~DEBUGCHAN_FLAG, (ast_channel_fout(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "", (long)ast_channel_whentohangup(c)->tv_sec, hour, min, sec, bridge ? bridge->uniqueid : "(Not bridged)", ast_channel_context(c), ast_channel_exten(c), ast_channel_priority(c), ast_print_group(cgrp, sizeof(cgrp), ast_channel_callgroup(c)), ast_print_group(pgrp, sizeof(pgrp), ast_channel_pickupgroup(c)), ast_channel_appl(c) ? ast_channel_appl(c) : "(N/A)", ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)", (ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)")); ao2_cleanup(bridge); return 0; }
static char *cli_show_tasks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { struct ao2_iterator i; struct ast_sip_sched_task *schtd; const char *log_format = ast_logger_get_dateformat(); struct ast_tm tm; char queued[32]; char last_start[32]; char last_end[32]; int datelen; struct timeval now = ast_tvnow(); const char *separator = "======================================"; switch (cmd) { case CLI_INIT: e->command = "pjsip show scheduled_tasks"; e->usage = "Usage: pjsip show scheduled_tasks\n" " Show all scheduled tasks\n"; return NULL; case CLI_GENERATE: return NULL; } if (a->argc != 3) { return CLI_SHOWUSAGE; } ast_localtime(&now, &tm, NULL); datelen = ast_strftime(queued, sizeof(queued), log_format, &tm); ast_cli(a->fd, "PJSIP Scheduled Tasks:\n\n"); ast_cli(a->fd, " %1$-24s %2$-8s %3$-9s %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", "Task Name", "Interval", "Times Run", "State", datelen, "Queued", "Last Started", "Last Ended"); ast_cli(a->fd, " %1$-24.24s %2$-8.8s %3$-9.9s %4$-7.7s %6$-*5$.*5$s %7$-*5$.*5$s %8$-*5$.*5$s\n", separator, separator, separator, separator, datelen, separator, separator, separator); ao2_ref(tasks, +1); ao2_rdlock(tasks); i = ao2_iterator_init(tasks, 0); while ((schtd = ao2_iterator_next(&i))) { ast_localtime(&schtd->when_queued, &tm, NULL); ast_strftime(queued, sizeof(queued), log_format, &tm); if (ast_tvzero(schtd->last_start)) { strcpy(last_start, "not yet started"); } else { ast_localtime(&schtd->last_start, &tm, NULL); ast_strftime(last_start, sizeof(last_start), log_format, &tm); } if (ast_tvzero(schtd->last_end)) { if (ast_tvzero(schtd->last_start)) { strcpy(last_end, "not yet started"); } else { strcpy(last_end, "running"); } } else { ast_localtime(&schtd->last_end, &tm, NULL); ast_strftime(last_end, sizeof(last_end), log_format, &tm); } ast_cli(a->fd, " %1$-24.24s %2$-8.3f %3$-9d %4$-7s %6$-*5$s %7$-*5$s %8$-*5$s\n", schtd->name, schtd->interval / 1000.0, schtd->run_count, schtd->is_running ? "running" : "waiting", datelen, queued, last_start, last_end); ao2_cleanup(schtd); } ao2_iterator_destroy(&i); ao2_unlock(tasks); ao2_ref(tasks, -1); ast_cli(a->fd, "\n"); return CLI_SUCCESS; }
static void endpoint_blob_dtor(void *obj) { struct ast_endpoint_blob *event = obj; ao2_cleanup(event->snapshot); ast_json_unref(event->blob); }
static int msg_send(void *data) { RAII_VAR(struct msg_data *, mdata, data, ao2_cleanup); const struct ast_sip_body body = { .type = "text", .subtype = "plain", .body_text = ast_msg_get_body(mdata->msg) }; pjsip_tx_data *tdata; RAII_VAR(char *, uri, NULL, ast_free); RAII_VAR(struct ast_sip_endpoint *, endpoint, get_outbound_endpoint( mdata->to, &uri), ao2_cleanup); if (!endpoint) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not find endpoint '%s' and " "no default outbound endpoint configured\n", mdata->to); return -1; } if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, NULL, &tdata)) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n"); return -1; } update_to(tdata, mdata->to); update_from(tdata, mdata->from); if (ast_sip_add_body(tdata, &body)) { pjsip_tx_data_dec_ref(tdata); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not add body to request\n"); return -1; } vars_to_headers(mdata->msg, tdata); ast_debug(1, "Sending message to '%s' (via endpoint %s) from '%s'\n", mdata->to, ast_sorcery_object_get_id(endpoint), mdata->from); if (ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL)) { ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n"); return -1; } return PJ_SUCCESS; } static int sip_msg_send(const struct ast_msg *msg, const char *to, const char *from) { struct msg_data *mdata; if (ast_strlen_zero(to)) { ast_log(LOG_ERROR, "SIP MESSAGE - a 'To' URI must be specified\n"); return -1; } if (!(mdata = msg_data_create(msg, to, from)) || ast_sip_push_task(message_serializer, msg_send, mdata)) { ao2_ref(mdata, -1); return -1; } return 0; } static const struct ast_msg_tech msg_tech = { .name = "pjsip", .msg_send = sip_msg_send, }; static pj_status_t send_response(pjsip_rx_data *rdata, enum pjsip_status_code code, pjsip_dialog *dlg, pjsip_transaction *tsx) { pjsip_tx_data *tdata; pj_status_t status; status = ast_sip_create_response(rdata, code, NULL, &tdata); if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to create response (%d)\n", status); return status; } if (dlg && tsx) { status = pjsip_dlg_send_response(dlg, tsx, tdata); } else { struct ast_sip_endpoint *endpoint; endpoint = ast_pjsip_rdata_get_endpoint(rdata); status = ast_sip_send_stateful_response(rdata, tdata, endpoint); ao2_cleanup(endpoint); } if (status != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send response (%d)\n", status); } return status; } static pj_bool_t module_on_rx_request(pjsip_rx_data *rdata) { enum pjsip_status_code code; struct ast_msg *msg; /* if not a MESSAGE, don't handle */ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_message_method)) { return PJ_FALSE; } code = check_content_type(rdata); if (code != PJSIP_SC_OK) { send_response(rdata, code, NULL, NULL); return PJ_TRUE; } msg = ast_msg_alloc(); if (!msg) { send_response(rdata, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL); return PJ_TRUE; } code = rx_data_to_ast_msg(rdata, msg); if (code != PJSIP_SC_OK) { send_response(rdata, code, NULL, NULL); ast_msg_destroy(msg); return PJ_TRUE; } if (!ast_msg_has_destination(msg)) { ast_debug(1, "MESSAGE request received, but no handler wanted it\n"); send_response(rdata, PJSIP_SC_NOT_FOUND, NULL, NULL); ast_msg_destroy(msg); return PJ_TRUE; } /* Send it to the messaging core. * * If we are unable to send a response, the most likely reason is that we * are handling a retransmission of an incoming MESSAGE and were unable to * create a transaction due to a duplicate key. If we are unable to send * a response, we should not queue the message to the dialplan */ if (!send_response(rdata, PJSIP_SC_ACCEPTED, NULL, NULL)) { ast_msg_queue(msg); } return PJ_TRUE; } static int incoming_in_dialog_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata) { char buf[MAX_BODY_SIZE]; enum pjsip_status_code code; struct ast_frame f; pjsip_dialog *dlg = session->inv_session->dlg; pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata); if (!session->channel) { send_response(rdata, PJSIP_SC_NOT_FOUND, dlg, tsx); return 0; } if ((code = check_content_type(rdata)) != PJSIP_SC_OK) { send_response(rdata, code, dlg, tsx); return 0; } if (print_body(rdata, buf, sizeof(buf)-1) < 1) { /* invalid body size */ send_response(rdata, PJSIP_SC_REQUEST_ENTITY_TOO_LARGE, dlg, tsx); return 0; } ast_debug(3, "Received in dialog SIP message\n"); memset(&f, 0, sizeof(f)); f.frametype = AST_FRAME_TEXT; f.subclass.integer = 0; f.offset = 0; f.data.ptr = buf; f.datalen = strlen(buf) + 1; ast_queue_frame(session->channel, &f); send_response(rdata, PJSIP_SC_ACCEPTED, dlg, tsx); return 0; }