static struct ao2_container *cli_contact_get_container(const char *regex) { RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup); struct ao2_container *child_container; regex_t regexbuf; parent_container = cli_aor_get_container(""); if (!parent_container) { return NULL; } child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, cli_contact_sort, cli_contact_compare); if (!child_container) { return NULL; } ao2_callback(parent_container, OBJ_NODATA, cli_aor_gather_contacts, child_container); if (!ast_strlen_zero(regex)) { if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) { ao2_ref(child_container, -1); return NULL; } ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_contacts, ®exbuf); regfree(®exbuf); } return child_container; }
static struct ao2_container *get_container(const char *regex, ao2_sort_fn sort_fn, ao2_callback_fn compare_fn) { struct ao2_container *child_container; regex_t regexbuf; RAII_VAR(struct ao2_container *, parent_container, stasis_cache_dump(ast_channel_cache_by_name(), ast_channel_snapshot_type()), ao2_cleanup); if (!parent_container) { return NULL; } child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, sort_fn, compare_fn); if (!child_container) { return NULL; } ao2_callback(parent_container, OBJ_MULTIPLE | OBJ_NODATA, cli_message_to_snapshot, child_container); if (!ast_strlen_zero(regex)) { if (regcomp(®exbuf, regex, REG_EXTENDED | REG_NOSUB)) { ao2_ref(child_container, -1); return NULL; } ao2_callback(child_container, OBJ_UNLINK | OBJ_MULTIPLE | OBJ_NODATA, cli_filter_channels, ®exbuf); regfree(®exbuf); } return child_container; }
struct stasis_app_control *control_create(struct ast_channel *channel) { RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); int res; control = ao2_alloc(sizeof(*control), control_dtor); if (!control) { return NULL; } res = ast_cond_init(&control->wait_cond, NULL); if (res != 0) { ast_log(LOG_ERROR, "Error initializing ast_cond_t: %s\n", strerror(errno)); return NULL; } control->command_queue = ao2_container_alloc_list( AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL); if (!control->command_queue) { return NULL; } control->channel = channel; AST_LIST_HEAD_INIT(&control->add_rules); AST_LIST_HEAD_INIT(&control->remove_rules); ao2_ref(control, +1); return control; }
/*! \brief Custom handler for permanent URIs */ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_aor *aor = obj; const char *aor_id = ast_sorcery_object_get_id(aor); char *contacts; char *contact_uri; if (ast_strlen_zero(var->value)) { return 0; } contacts = ast_strdupa(var->value); while ((contact_uri = ast_strip(strsep(&contacts, ",")))) { struct ast_sip_contact *contact; struct ast_sip_contact_status *status; char hash[33]; char contact_id[strlen(aor_id) + sizeof(hash) + 2]; if (ast_strlen_zero(contact_uri)) { continue; } if (ast_sip_validate_uri_length(contact_uri)) { ast_log(LOG_ERROR, "Contact uri or hostname length exceeds pjproject limit: %s\n", contact_uri); return -1; } if (!aor->permanent_contacts) { aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL); if (!aor->permanent_contacts) { return -1; } } ast_md5_hash(hash, contact_uri); snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, hash); contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id); if (!contact) { return -1; } ast_string_field_set(contact, uri, contact_uri); status = ast_res_pjsip_find_or_create_contact_status(contact); if (!status) { ao2_ref(contact, -1); return -1; } ao2_ref(status, -1); ao2_link(aor->permanent_contacts, contact); ao2_ref(contact, -1); } return 0; }
static int ami_show_auths(struct mansession *s, const struct message *m) { struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), }; struct ao2_container *auths; auths = cli_get_auths(); if (!auths) { astman_send_error(s, m, "Could not get Auths\n"); return 0; } if (!ao2_container_count(auths)) { astman_send_error(s, m, "No Auths found\n"); ao2_ref(auths, -1); return 0; } astman_send_listack(s, m, "A listing of Auths follows, presented as AuthList events", "start"); ao2_callback(auths, OBJ_NODATA, format_ami_authlist_handler, &ami); astman_send_list_complete_start(s, m, "AuthListComplete", ami.count); astman_send_list_complete_end(s); ao2_ref(auths, -1); return 0; } static struct ao2_container *cli_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex); if (!container) { return NULL; } s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); if (!s_container) { return NULL; } if (ao2_container_dup(s_container, container, 0)) { ao2_ref(s_container, -1); return NULL; } return s_container; }
struct ao2_container *ast_sip_location_retrieve_contacts_from_aor_list(const char *aor_list) { struct ao2_container *contacts; contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL); if (!contacts) { return NULL; } ast_sip_for_each_aor(aor_list, gather_contacts_for_aor, contacts); return contacts; }
/*! \brief Custom handler for permanent URIs */ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_aor *aor = obj; const char *aor_id = ast_sorcery_object_get_id(aor); char *contacts; char *contact_uri; if (ast_strlen_zero(var->value)) { return 0; } contacts = ast_strdupa(var->value); while ((contact_uri = strsep(&contacts, ","))) { struct ast_sip_contact *contact; char contact_id[strlen(aor_id) + strlen(contact_uri) + 2 + 1]; if (ast_sip_push_task_synchronous(NULL, permanent_contact_validate, contact_uri)) { ast_log(LOG_ERROR, "Permanent URI on aor '%s' with contact '%s' failed to parse\n", ast_sorcery_object_get_id(aor), contact_uri); return -1; } if (!aor->permanent_contacts) { aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL); if (!aor->permanent_contacts) { return -1; } } snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, contact_uri); contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id); if (!contact) { return -1; } ast_string_field_set(contact, uri, contact_uri); ao2_link(aor->permanent_contacts, contact); ao2_ref(contact, -1); } return 0; }
static struct ao2_container *cli_contact_get_container(void) { RAII_VAR(struct ao2_container *, parent_container, NULL, ao2_cleanup); struct ao2_container *child_container; parent_container = cli_aor_get_container(); if (!parent_container) { return NULL; } child_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, cli_contact_sort, cli_contact_compare); if (!child_container) { return NULL; } ao2_callback(parent_container, OBJ_NODATA, cli_aor_gather_contacts, child_container); return child_container; }
int ast_local_init(void) { if (STASIS_MESSAGE_TYPE_INIT(ast_local_optimization_begin_type)) { return -1; } if (STASIS_MESSAGE_TYPE_INIT(ast_local_optimization_end_type)) { return -1; } if (STASIS_MESSAGE_TYPE_INIT(ast_local_bridge_type)) { return -1; } if (!(local_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { return -1; } ast_format_cap_append_by_type(local_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN); locals = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, locals_cmp_cb); if (!locals) { ao2_cleanup(local_tech.capabilities); local_tech.capabilities = NULL; return -1; } /* Make sure we can register our channel type */ if (ast_channel_register(&local_tech)) { ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n"); ao2_ref(locals, -1); ao2_cleanup(local_tech.capabilities); local_tech.capabilities = NULL; return -1; } ast_cli_register_multiple(cli_local, ARRAY_LEN(cli_local)); ast_manager_register_xml_core("LocalOptimizeAway", EVENT_FLAG_SYSTEM|EVENT_FLAG_CALL, manager_optimize_away); ast_register_cleanup(local_shutdown); return 0; }
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 struct ao2_container *cli_aor_get_container(const char *regex) { struct ao2_container *container; struct ao2_container *s_container; container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex); if (!container) { return NULL; } /* Create a sorted container of aors. */ s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); if (s_container && ao2_container_dup(s_container, container, 0)) { ao2_ref(s_container, -1); s_container = NULL; } ao2_ref(container, -1); return s_container; }
static struct ao2_container *cli_aor_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "aor", regex); if (!container) { return NULL; } s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); if (!s_container) { return NULL; } if (ao2_container_dup(s_container, container, 0)) { ao2_ref(s_container, -1); return NULL; } return s_container; }
static struct ao2_container *cli_get_container(void) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; container = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); if (!container) { return NULL; } s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); if (!s_container) { return NULL; } if (ao2_container_dup(s_container, container, 0)) { ao2_ref(s_container, -1); return NULL; } return s_container; }
static int distribute(void *data) { static pjsip_process_rdata_param param = { .start_mod = &distributor_mod, .idx_after_start = 1, }; pj_bool_t handled = PJ_FALSE; pjsip_rx_data *rdata = data; int is_request = rdata->msg_info.msg->type == PJSIP_REQUEST_MSG; int is_ack = is_request ? rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD : 0; struct ast_sip_endpoint *endpoint; pjsip_endpt_process_rx_data(ast_sip_get_pjsip_endpoint(), rdata, ¶m, &handled); if (!handled && is_request && !is_ack) { pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, 501, NULL, NULL, NULL); } /* The endpoint_mod stores an endpoint reference in the mod_data of rdata. This * is the only appropriate spot to actually decrement the reference. */ endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; ao2_cleanup(endpoint); pjsip_rx_data_free_cloned(rdata); return 0; } struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata) { struct ast_sip_endpoint *endpoint = rdata->endpt_info.mod_data[endpoint_mod.id]; if (endpoint) { ao2_ref(endpoint, +1); } return endpoint; } static int suspects_sort(const void *obj, const void *arg, int flags) { const struct unidentified_request *object_left = obj; const struct unidentified_request *object_right = arg; const char *right_key = arg; int cmp; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: right_key = object_right->src_name; /* Fall through */ case OBJ_SEARCH_KEY: cmp = strcmp(object_left->src_name, right_key); break; case OBJ_SEARCH_PARTIAL_KEY: cmp = strncmp(object_left->src_name, right_key, strlen(right_key)); break; default: cmp = 0; break; } return cmp; } static int suspects_compare(void *obj, void *arg, int flags) { const struct unidentified_request *object_left = obj; const struct unidentified_request *object_right = arg; const char *right_key = arg; int cmp = 0; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_OBJECT: right_key = object_right->src_name; /* Fall through */ case OBJ_SEARCH_KEY: if (strcmp(object_left->src_name, right_key) == 0) { cmp = CMP_MATCH; } break; case OBJ_SEARCH_PARTIAL_KEY: if (strncmp(object_left->src_name, right_key, strlen(right_key)) == 0) { cmp = CMP_MATCH; } break; default: cmp = 0; break; } return cmp; } static int suspects_hash(const void *obj, int flags) { const struct unidentified_request *object; const char *key; switch (flags & OBJ_SEARCH_MASK) { case OBJ_SEARCH_KEY: key = obj; break; case OBJ_SEARCH_OBJECT: object = obj; key = object->src_name; break; default: /* Hash can only work on something with a full key. */ ast_assert(0); return 0; } return ast_str_hash(key); } static struct ao2_container *cli_unid_get_container(const char *regex) { struct ao2_container *s_container; s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, suspects_sort, suspects_compare); if (!s_container) { return NULL; } if (ao2_container_dup(s_container, unidentified_requests, 0)) { ao2_ref(s_container, -1); return NULL; } return s_container; } static int cli_unid_iterate(void *container, ao2_callback_fn callback, void *args) { ao2_callback(container, 0, callback, args); return 0; }
/*! * \internal * \brief Get the requested mailboxes. * \since 12.1.0 * * \param s AMI session. * \param m AMI message. * * \retval 0 to keep AMI connection. * \retval -1 to disconnect AMI connection. */ static int mwi_mailbox_get(struct mansession *s, const struct message *m) { char id_text[256]; const char *id; const char *mailbox_id = astman_get_header(m, "Mailbox"); const struct ast_mwi_mailbox_object *mailbox; struct ao2_container *mailboxes; unsigned count; struct ao2_iterator iter; if (ast_strlen_zero(mailbox_id)) { astman_send_error(s, m, "Missing mailbox parameter in request"); return 0; } if (*mailbox_id == '/') { struct ast_str *regex_string; regex_string = ast_str_create(strlen(mailbox_id) + 1); if (!regex_string) { astman_send_error(s, m, "Memory Allocation Failure"); return 0; } /* Make "/regex/" into "regex" */ if (ast_regex_string_to_regex_pattern(mailbox_id, ®ex_string) != 0) { astman_send_error_va(s, m, "Mailbox regex format invalid in: %s", mailbox_id); ast_free(regex_string); return 0; } mailboxes = ast_mwi_mailbox_get_by_regex(ast_str_buffer(regex_string)); ast_free(regex_string); } else { mailboxes = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, NULL); if (mailboxes) { mailbox = ast_mwi_mailbox_get(mailbox_id); if (mailbox) { if (!ao2_link(mailboxes, (void *) mailbox)) { ao2_ref(mailboxes, -1); mailboxes = NULL; } ast_mwi_mailbox_unref(mailbox); } } } if (!mailboxes) { astman_send_error(s, m, "Mailbox container creation failure"); return 0; } astman_send_listack(s, m, "Mailboxes will follow", "start"); id = astman_get_header(m, "ActionID"); if (!ast_strlen_zero(id)) { snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id); } else { id_text[0] = '\0'; } /* Output mailbox list. */ count = 0; iter = ao2_iterator_init(mailboxes, AO2_ITERATOR_UNLINK); for (; (mailbox = ao2_iterator_next(&iter)); ast_mwi_mailbox_unref(mailbox)) { ++count; astman_append(s, "Event: MWIGet\r\n" "Mailbox: %s\r\n" "OldMessages: %u\r\n" "NewMessages: %u\r\n" "%s" "\r\n", ast_mwi_mailbox_get_id(mailbox), ast_mwi_mailbox_get_msgs_old(mailbox), ast_mwi_mailbox_get_msgs_new(mailbox), id_text); } ao2_iterator_destroy(&iter); ao2_ref(mailboxes, -1); astman_send_list_complete_start(s, m, "MWIGetComplete", count); astman_send_list_complete_end(s); return 0; }
static void 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, struct aor_core_response *response) { static const pj_str_t USER_AGENT = { "User-Agent", 10 }; int added = 0; int updated = 0; int deleted = 0; int permanent = 0; int contact_count; struct ao2_container *existing_contacts = NULL; pjsip_contact_hdr *contact_hdr = (pjsip_contact_hdr *)&rdata->msg_info.msg->hdr; 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; /* We create a single pool and use it throughout this function where we need one */ details.pool = pjsip_endpt_create_pool(ast_sip_get_pjsip_endpoint(), "Contact Comparison", 1024, 256); if (!details.pool) { response->code = 500; return; } /* If there are any permanent contacts configured on the AOR we need to take them * into account when counting contacts. */ if (aor->permanent_contacts) { permanent = ao2_container_count(aor->permanent_contacts); } if (registrar_validate_contacts(rdata, details.pool, contacts, aor, permanent, &added, &updated, &deleted)) { /* The provided Contact headers do not conform to the specification */ 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)); response->code = 400; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (registrar_validate_path(rdata, aor, &path_str)) { /* Ensure that intervening proxies did not make invalid modifications to the request */ ast_log(LOG_WARNING, "Invalid modifications made to REGISTER request from '%s' by intervening proxy\n", ast_sorcery_object_get_id(endpoint)); response->code = 420; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } if (aor->remove_existing) { /* Cumulative number of contacts affected by this registration */ contact_count = MAX(updated + added - deleted, 0); /* We need to keep track of only existing contacts so we can later * remove them if need be. */ existing_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL, ast_sorcery_object_id_compare); if (!existing_contacts) { response->code = 500; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); return; } ao2_callback(contacts, OBJ_NODATA, registrar_add_non_permanent, existing_contacts); } else { /* Total contacts after this registration */ contact_count = ao2_container_count(contacts) - permanent + added - deleted; } if (contact_count > aor->max_contacts) { /* Enforce the maximum number of contacts */ 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); response->code = 403; pjsip_endpt_release_pool(ast_sip_get_pjsip_endpoint(), details.pool); ao2_cleanup(existing_contacts); return; } 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 */ for (; (contact_hdr = (pjsip_contact_hdr *) pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, contact_hdr->next)); pj_pool_reset(details.pool)) { 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_UNLINK | OBJ_MULTIPLE, registrar_delete_contact, (void *)aor_name); /* If we are keeping track of existing contacts for removal then, well, there is * absolutely nothing left so no need to try to remove any. */ if (existing_contacts) { ao2_ref(existing_contacts, -1); existing_contacts = NULL; } 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)); contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details); /* If a contact was returned and we need to keep track of existing contacts then it * should be removed. */ if (contact && existing_contacts) { ao2_unlink(existing_contacts, contact); } if (!contact) { int prune_on_boot; /* 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; } prune_on_boot = !ast_sip_will_uri_survive_restart(details.uri, endpoint, rdata); contact = ast_sip_location_create_contact(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, prune_on_boot, endpoint); if (!contact) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", contact_uri, aor_name); continue; } if (prune_on_boot) { const char *contact_name; struct contact_transport_monitor *monitor; /* * Monitor the transport in case it gets disconnected because * the contact won't be valid anymore if that happens. */ contact_name = ast_sorcery_object_get_id(contact); monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); if (monitor) { strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_register(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor); ao2_ref(monitor, -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\r\n" "UserAgent: %s", contact_uri, aor_name, expiration, user_agent); ao2_link(contacts, contact); } 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_link(contacts, contact_update); ao2_cleanup(contact_update); } else { if (contact->prune_on_boot) { struct contact_transport_monitor *monitor; const char *contact_name = ast_sorcery_object_get_id(contact); monitor = ast_alloca(sizeof(*monitor) + 2 + strlen(aor_name) + strlen(contact_name)); strcpy(monitor->aor_name, aor_name);/* Safe */ monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; strcpy(monitor->contact_name, contact_name);/* Safe */ ast_sip_transport_monitor_unregister(rdata->tp_info.transport, register_contact_transport_shutdown_cb, monitor, contact_transport_monitor_matcher); } /* 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 contacts over max_contacts * that have not been updated/added/deleted as a result of this * REGISTER do so. * * The existing contacts container holds all contacts that were not * involved in this REGISTER. * The contacts container holds the current contacts of the AOR. */ if (aor->remove_existing && existing_contacts) { /* Total contacts after this registration */ contact_count = ao2_container_count(existing_contacts) + updated + added; if (contact_count > aor->max_contacts) { /* Remove excess existing contacts that expire the soonest */ remove_excess_contacts(existing_contacts, contacts, contact_count - aor->max_contacts); } ao2_ref(existing_contacts, -1); } 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); response->code = 500; return; } 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); 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); } response->tdata = tdata; }