Index: res/res_pjsip/location.c =================================================================== --- res/res_pjsip/location.c (revision 411010) +++ res/res_pjsip/location.c (working copy) @@ -256,25 +256,66 @@ return 0; } +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 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; - RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup); + const char *aor_id = ast_sorcery_object_get_id(aor); + struct ast_sip_contact *contact; + char contact_id[strlen(aor_id) + strlen(var->value) + 2 + 1]; - if (ast_sip_push_task_synchronous(NULL, permanent_contact_validate, (char*)var->value)) { + if (ast_sip_push_task_synchronous(NULL, permanent_contact_validate, (char *) var->value)) { ast_log(LOG_ERROR, "Permanent URI on aor '%s' with contact '%s' failed to parse\n", - ast_sorcery_object_get_id(aor), var->value); + aor_id, var->value); return -1; } - if ((!aor->permanent_contacts && !(aor->permanent_contacts = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 1, NULL, NULL))) || - !(contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", NULL))) { + 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, var->value); + contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id); + if (!contact) { return -1; } - ast_string_field_set(contact, uri, var->value); ao2_link(aor->permanent_contacts, contact); + ao2_cleanup(contact); return 0; } Index: res/res_pjsip/pjsip_options.c =================================================================== --- res/res_pjsip/pjsip_options.c (revision 411010) +++ res/res_pjsip/pjsip_options.c (working copy) @@ -378,14 +378,15 @@ */ static void unschedule_qualify(struct ast_sip_contact *contact) { - RAII_VAR(struct sched_data *, data, ao2_find( - sched_qualifies, contact, OBJ_UNLINK), ao2_cleanup); + struct sched_data *data; + data = ao2_find(sched_qualifies, contact, OBJ_UNLINK | OBJ_SEARCH_KEY); if (!data) { return; } AST_SCHED_DEL_UNREF(sched, data->id, ao2_cleanup(data)); + ao2_cleanup(data); } /*! @@ -753,17 +754,60 @@ static int sched_qualifies_hash_fn(const void *obj, int flags) { - const struct sched_data *data = obj; + const struct sched_data *object; + const struct ast_sip_contact *key; - return ast_str_hash(ast_sorcery_object_get_id(data->contact)); + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_KEY: + key = obj; + break; + case OBJ_SEARCH_OBJECT: + object = obj; + key = object->contact; + break; + default: + /* Hash can only work on something with a full key. */ + ast_assert(0); + return 0; + } + return ast_str_hash(ast_sorcery_object_get_id(key)); } static int sched_qualifies_cmp_fn(void *obj, void *arg, int flags) { - struct sched_data *data = obj; + const struct sched_data *object_left = obj; + const struct sched_data *object_right = arg; + struct ast_sip_contact *right_key = arg; + int cmp; - return !strcmp(ast_sorcery_object_get_id(data->contact), - ast_sorcery_object_get_id(arg)); + switch (flags & OBJ_SEARCH_MASK) { + case OBJ_SEARCH_OBJECT: + right_key = object_right->contact; + /* Fall through */ + case OBJ_SEARCH_KEY: + cmp = strcmp(ast_sorcery_object_get_id(object_left->contact), + ast_sorcery_object_get_id(right_key)); + break; + case OBJ_SEARCH_PARTIAL_KEY: + /* Not supported by container. */ + ast_assert(0); + return 0; + default: + /* + * What arg points to is specific to this traversal callback + * and has no special meaning to astobj2. + */ + cmp = 0; + break; + } + if (cmp) { + return 0; + } + /* + * At this point the traversal callback is identical to a sorted + * container. + */ + return CMP_MATCH; } int ast_sip_initialize_sorcery_qualify(void) @@ -794,6 +838,8 @@ contact->qualify_frequency = aor->qualify_frequency; + ast_log(LOG_NOTICE, "BUGBUG contact %s freq=%d auth=%d\n", contact->uri, contact->qualify_frequency, contact->authenticate_qualify); + qualify_and_schedule(contact); return 0; @@ -858,7 +904,6 @@ if (!(sched_qualifies = ao2_t_container_alloc( QUALIFIED_BUCKETS, sched_qualifies_hash_fn, sched_qualifies_cmp_fn, "Create container for scheduled qualifies"))) { - return -1; } Index: res/res_pjsip_registrar.c =================================================================== --- res/res_pjsip_registrar.c (revision 411010) +++ res/res_pjsip_registrar.c (working copy) @@ -159,7 +159,7 @@ return ast_tvzero(contact->expiration_time) ? CMP_MATCH : 0; } -/*! \brief Internal function used to delete all contacts from an AOR */ +/*! \brief Internal function used to delete a contact from an AOR */ static int registrar_delete_contact(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj;