ldap/servers/plugins/dna/dna.c | 467 ++++++++++++++++++++++++++++++----------- 1 file changed, 352 insertions(+), 115 deletions(-)
New commits: commit a0b60b0b87c034f7f888b3ec0a86c4bca74f471d Author: Nathan Kinder nkinder@redhat.com Date: Wed Apr 28 17:12:29 2010 -0700
Bug 584497 - Allow DNA plugin to set same value on multiple attributes
This patch allows one to set multiple dnaType attributes for a single DNA range. This allows the same value to be used for each dnaType (such as ensuring the uidNumber and gidNumber are the same value from the range when ading a posixAccount).
There are some differences with the way DNA works for multi-type ranges. For a value to be generated from a multi-type range, the magic value must be specified to indicate which attributes should use the newly generated value. This allows a range to be shared across different entry types (such as having a uidNumber/gidNumber range that is used or posixAccount and posixGroup entries). A multi-type range will not generate a value for missing attributes as DNA does for single-type ranges.
Since a range can have multiple types, the internal ordering of config structs had to be changed to order by scope only. It was previously ordered by type and then scope, which allowed us to easily detect if we had already generated a value for a given type. This change required a number of helper functions to be added for tracking and checking which types we have already generated values for.
diff --git a/ldap/servers/plugins/dna/dna.c b/ldap/servers/plugins/dna/dna.c index 94c4ab3..dfa86c4 100644 --- a/ldap/servers/plugins/dna/dna.c +++ b/ldap/servers/plugins/dna/dna.c @@ -139,7 +139,7 @@ static Slapi_PluginDesc exop_pdesc = { DNA_EXOP_FEATURE_DESC, struct configEntry { PRCList list; char *dn; - char *type; + char **types; char *prefix; char *filter; Slapi_Filter *slapi_filter; @@ -252,6 +252,11 @@ static int dna_is_replica_bind_dn(char *range_dn, char *bind_dn); static int dna_get_replica_bind_creds(char *range_dn, struct dnaServer *server, char **bind_dn, char **bind_passwd, char **bind_method, int *is_ssl, int *port); +static int dna_list_contains_type(char **list, char *type); +static int dna_list_contains_types(char **list, char **types); +static void dna_list_remove_type(char **list, char *type); +static int dna_is_multitype_range(struct configEntry *config_entry); +static void dna_create_valcheck_filter(struct configEntry *config_entry, PRUint64 value, char **filter);
/** * @@ -667,6 +672,7 @@ dna_parse_config_entry(Slapi_Entry * e, int apply) struct configEntry *config_entry; PRCList *list; int entry_added = 0; + int i = 0; int ret = DNA_SUCCESS;
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, @@ -694,10 +700,8 @@ dna_parse_config_entry(Slapi_Entry * e, int apply) slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, "----------> dn [%s]\n", entry->dn);
- value = slapi_entry_attr_get_charptr(e, DNA_TYPE); - if (value) { - entry->type = value; - } else { + entry->types = slapi_entry_attr_get_charray(e, DNA_TYPE); + if (entry->types == NULL) { slapi_log_error(SLAPI_LOG_FATAL, DNA_PLUGIN_SUBSYSTEM, "dna_parse_config_entry: The %s config " "setting is required for range %s.\n", @@ -706,8 +710,10 @@ dna_parse_config_entry(Slapi_Entry * e, int apply) goto bail; }
- slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, - "----------> %s [%s]\n", DNA_TYPE, entry->type); + for (i = 0; entry->types && entry->types[i]; i++) { + slapi_log_error(SLAPI_LOG_CONFIG, DNA_PLUGIN_SUBSYSTEM, + "----------> %s [%s]\n", DNA_TYPE, entry->types[i]); + }
value = slapi_entry_attr_get_charptr(e, DNA_NEXTVAL); if (value) { @@ -976,25 +982,17 @@ dna_parse_config_entry(Slapi_Entry * e, int apply) }
/** - * Finally add the entry to the list - * we group by type then by filter - * and finally sort by dn length with longer dn's - * first - this allows the scope checking - * code to be simple and quick and - * cunningly linear + * Finally add the entry to the list. + * We sort by scope dn length with longer + * dn's first - this allows the scope + * checking code to be simple and quick and + * cunningly linear. */ if (!PR_CLIST_IS_EMPTY(dna_global_config)) { list = PR_LIST_HEAD(dna_global_config); while (list != dna_global_config) { config_entry = (struct configEntry *) list;
- if (slapi_attr_type_cmp(config_entry->type, entry->type, 1)) - goto next; - - if (slapi_filter_compare(config_entry->slapi_filter, - entry->slapi_filter)) - goto next; - if (slapi_dn_issuffix(entry->scope, config_entry->scope)) { PR_INSERT_BEFORE(&(entry->list), list); slapi_log_error(SLAPI_LOG_CONFIG, @@ -1058,8 +1056,8 @@ dna_free_config_entry(struct configEntry ** entry) slapi_ch_free_string(&e->dn); }
- if (e->type) - slapi_ch_free_string(&e->type); + if (e->types) + slapi_ch_array_free(e->types);
if (e->prefix) slapi_ch_free_string(&e->prefix); @@ -1770,10 +1768,9 @@ dna_first_free_value(struct configEntry *config_entry, Slapi_Entry **entries = NULL; Slapi_PBlock *pb = NULL; LDAPControl **ctrls = NULL; - char *attrs[2]; - char *filter; + char *filter = NULL; char *prefix; - char *type; + int multitype; int result, status, filterlen; PRUint64 tmpval, sval, i; char *strval = NULL; @@ -1785,31 +1782,26 @@ dna_first_free_value(struct configEntry *config_entry, }
prefix = config_entry->prefix; - type = config_entry->type; tmpval = config_entry->nextval;
- attrs[0] = type; - attrs[1] = NULL; - - /* We don't sort if we're using a prefix (non integer type). Instead, - * we just search to see if the next value is free, and keep incrementing - * until we find the next free value. */ - if (prefix) { - /* The 7 below is for all of the filter characters "(&(=))" - * plus the trailing \0. The 20 is for the maximum string - * representation of a " NSPRIu64 ". */ - filterlen = strlen(config_entry->filter) + - strlen(prefix) + strlen(type) - + 7 + 20; - filter = slapi_ch_malloc(filterlen); - snprintf(filter, filterlen, "(&%s(%s=%s%" PRIu64 "))", - config_entry->filter, type, prefix, tmpval); + if (dna_is_multitype_range(config_entry)) { + multitype = 1; + } + + /* We don't sort if we're using a prefix (non integer type) or if this + * is a multi-type range. Instead, we just search to see if the next + * value is free, and keep incrementing until we find the next free value. */ + if (prefix || multitype) { + /* This will allocate the filter string for us. */ + dna_create_valcheck_filter(config_entry, tmpval, &filter); } else { + /* This is a single-type range, so just use the first (only) + * type from the list. */ ctrls = (LDAPControl **)slapi_ch_calloc(2, sizeof(LDAPControl)); if (NULL == ctrls) return LDAP_OPERATIONS_ERROR;
- ctrls[0] = dna_build_sort_control(config_entry->type); + ctrls[0] = dna_build_sort_control(config_entry->types[0]); if (NULL == ctrls[0]) { slapi_ch_free((void **)&ctrls); return LDAP_OPERATIONS_ERROR; @@ -1817,8 +1809,8 @@ dna_first_free_value(struct configEntry *config_entry,
filter = slapi_ch_smprintf("(&%s(&(%s>=%" NSPRIu64 ")(%s<=%" NSPRIu64 ")))", config_entry->filter, - type, tmpval, - type, config_entry->maxval); + config_entry->types[0], tmpval, + config_entry->types[0], config_entry->maxval); }
if (NULL == filter) { @@ -1837,7 +1829,7 @@ dna_first_free_value(struct configEntry *config_entry,
slapi_search_internal_set_pb(pb, config_entry->scope, LDAP_SCOPE_SUBTREE, filter, - attrs, 0, ctrls, + config_entry->types, 0, ctrls, NULL, getPluginID(), 0); slapi_search_internal_pb(pb);
@@ -1857,17 +1849,17 @@ dna_first_free_value(struct configEntry *config_entry, goto cleanup; }
- if (prefix) { + if (prefix || multitype) { /* The next value identified in the config entry has already * been taken. We just iterate through the values until we * (hopefully) find a free one. */ for (tmpval += config_entry->interval; tmpval <= config_entry->maxval; tmpval += config_entry->interval) { - /* filter is guaranteed to be big enough since we allocated - * enough space to fit a string representation of any unsigned - * 64-bit integer */ - snprintf(filter, filterlen, "(&%s(%s=%s%" PRIu64 "))", - config_entry->filter, type, prefix, tmpval); + /* This will reuse the old memory for the previous filter. It is + * guaranteed to have enough space since the filter is the same + * aside from the assertion value (we allocated enough for the + * largest supported integer). */ + dna_create_valcheck_filter(config_entry, tmpval, &filter);
/* clear out the pblock so we can re-use it */ slapi_free_search_results_internal(pb); @@ -1875,7 +1867,7 @@ dna_first_free_value(struct configEntry *config_entry,
slapi_search_internal_set_pb(pb, config_entry->scope, LDAP_SCOPE_SUBTREE, filter, - attrs, 0, 0, + config_entry->types, 0, 0, NULL, getPluginID(), 0);
slapi_search_internal_pb(pb); @@ -1897,13 +1889,15 @@ dna_first_free_value(struct configEntry *config_entry, } } } else { - /* entries are sorted and filtered for value >= tval therefore if the + /* Entries are sorted and filtered for value >= tval therefore if the * first one does not match tval it means that the value is free, * otherwise we need to cycle through values until we find a mismatch, - * the first mismatch is the first free pit */ + * the first mismatch is the first free pit. This is guaranteed to + * be a single-type range, so we can just use the first (only) + * type from the list of types directly. */ sval = 0; for (i = 0; NULL != entries[i]; i++) { - strval = slapi_entry_attr_get_charptr(entries[i], type); + strval = slapi_entry_attr_get_charptr(entries[i], config_entry->types[0]); errno = 0; sval = strtoull(strval, 0, 0); if (errno) { @@ -2524,10 +2518,203 @@ bail: return ret; }
+/* + * dna_list_contains_type() + * + * Checks if a type is contained in a list of types. + * Returns 1 if the type is found, 0 otherwise. + */ +static int +dna_list_contains_type(char **list, char *type) +{ + int ret = 0; + int i = 0; + + if (list && type) { + for (i = 0; list[i]; i++) { + if (slapi_attr_types_equivalent(type, list[i])) { + ret = 1; + break; + } + } + } + + return ret; +} + +/* + * dna_list_contains_types() + * + * Checks if all types in one list (types) are contained + * in another list of types (list). Returns 1 if all + * types are found, 0 otherwise. + */ +static int +dna_list_contains_types(char **list, char **types) +{ + int ret = 1; + int i = 0; + int j = 0; + + if (list && types) { + for (i = 0; types[i]; i++) { + int found = 0; + + for (j = 0; list[j]; j++) { + if (slapi_attr_types_equivalent(types[i], list[i])) { + found = 1; + break; + } + } + + if (!found) { + ret = 0; + break; + } + } + } else { + ret = 0; + } + + return ret; +} + +/* + * dna_list_remove_type() + * + * Removes a type from a list of types. + */ +static void +dna_list_remove_type(char **list, char *type) +{ + int i = 0; + int found_type = 0; + + if (list && type) { + /* Go through the list until we find the type that + * we want to remove. We simply free the type we + * want to remove and shift the remaining array + * elements down by one index. This will leave us + * with two NULL elements at the end of the list, + * but this should not cause any problems. */ + for (i = 0; list[i]; i++) { + if (found_type) { + list[i] = list[i + 1]; + } else if (slapi_attr_types_equivalent(type, list[i])) { + slapi_ch_free_string(&list[i]); + list[i] = list[i + 1]; + found_type = 1; + } + } + } +} + +/* + * dna_is_multitype_range() + * + * Returns 1 if the range has multiple types configured. + * Returns 0 otherwise. + */ +static int dna_is_multitype_range(struct configEntry *config_entry) +{ + int ret = 0; + + if (config_entry && config_entry->types && config_entry->types[1]) { + ret = 1; + } + + return ret; +} + +/* + * dna_create_valcheck_filter() + * + * Creates a filter string used to check if a value is free. If + * filter already holds a valid pointer, it is assumed to have enough + * space to hold the filter. This allows the same memory to be used + * over and over when you only want to change the assertion value. + * If filter contains a NULL pointer, a new string of the appropriate + * size will be allocated. The caller must free this when finished. + */ +static void +dna_create_valcheck_filter(struct configEntry *config_entry, PRUint64 value, char **filter) +{ + int filterlen = 0; + int typeslen = 0; + int i = 0; + int bytes_out = 0; + int multitype = 0; + + /* Just return if we didn't get an address for the filter. */ + if (filter == NULL) { + return; + } + + /* To determine the filter length, we add together the following: + * + * - the string length of the filter in the config + * - the string length sum of all configured types + * - 23 bytes for each type (20 for the max string + * representation of a NSPRIu64, 3 for "(=)" + * - 3 bytes for the beginning and end of the filter - "(&" and ")" + * - 3 bytes to OR together multiple types (if present) - "(|" and ")" + * - the string length of the prefix (if one is configured) for each type + * - 1 byte for the trailing \0 + * + * The filter length should be the same every time if the config struct + * has not been changed. We need to calculate the filter length even + * if we are reusing memory since we have no other way of knowing the + * length used when it was originally allocated. We trust the caller + * to only reuse the pointer with the same config struct. + */ + for (i = 0; config_entry->types && config_entry->types[i]; i++) { + typeslen += strlen(config_entry->types[i]); + } + + if (i > 1) { + multitype = 1; + } + + filterlen = strlen(config_entry->filter) + typeslen + + (i * 23) + 3 + 1 + + (config_entry->prefix ? (i * strlen(config_entry->prefix)) : 0) + + (multitype ? 3 : 0); + + /* Allocate space for the filter if it hasn't been allocated yet. */ + if (*filter == NULL) { + *filter = slapi_ch_malloc(filterlen); + } + + /* Write out the beginning of the filter. If multiple types + * are configured, we need to OR together the search clauses + * for the types. */ + if (multitype) { + bytes_out = snprintf(*filter, filterlen, "(&%s(|", config_entry->filter); + } else { + bytes_out = snprintf(*filter, filterlen, "(&%s", config_entry->filter); + } + + /* Loop through the types and append each filter clause. */ + for (i = 0; config_entry->types && config_entry->types[i]; i++) { + bytes_out += snprintf(*filter + bytes_out, filterlen - bytes_out, + "(%s=%s%" PRIu64 ")", config_entry->types[i], + config_entry->prefix ? config_entry->prefix : "", + value); + } + + /* Append the end of the filter. We need an extra paren + * to close out the OR if we have multiple types. */ + if (multitype) { + strncat(*filter, "))", filterlen - bytes_out); + } else { + strncat(*filter, ")", filterlen - bytes_out); + } +} + /* for mods and adds: where dn's are supplied, the closest in scope - is used as long as the type and filter - are identical - otherwise all matches count + is used as long as the type filter matches + and the type has not been generated yet. */
static int dna_pre_op(Slapi_PBlock * pb, int modtype) @@ -2537,14 +2724,15 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) struct configEntry *config_entry = 0; struct slapi_entry *e = 0; Slapi_Entry *resulting_e = 0; - char *last_type = 0; char *value = 0; - int generate = 0; + char **types_to_generate = NULL; + char **generated_types = NULL; Slapi_Mods *smods = 0; Slapi_Mod *smod = 0; LDAPMod **mods; int free_entry = 0; char *errstr = NULL; + int i = 0; int ret = 0;
slapi_log_error(SLAPI_LOG_TRACE, DNA_PLUGIN_SUBSYSTEM, @@ -2637,9 +2825,8 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) while (list != dna_global_config && LDAP_SUCCESS == ret) { config_entry = (struct configEntry *) list;
- /* did we already service this type? */ - if (last_type) { - if (!slapi_attr_type_cmp(config_entry->type, last_type, 1)) + /* Did we already service all of these configured types? */ + if (dna_list_contains_types(generated_types, config_entry->types)) { goto next; }
@@ -2670,18 +2857,33 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype)
if (LDAP_CHANGETYPE_ADD == modtype) { - /* does attribute contain the magic value - or is the type not there? - */ - value = - slapi_entry_attr_get_charptr(e, config_entry->type); - if ((value - && !slapi_UTF8CASECMP(config_entry->generate, value)) - || 0 == value) { - generate = 1; - } + if (config_entry->types && dna_is_multitype_range(config_entry)) { + /* For a multi-type range, we only generate a value + * for types where the magic value is set. We do not + * generate a value for missing types. */ + for (i = 0; config_entry->types && config_entry->types[i]; i++) { + value = slapi_entry_attr_get_charptr(e, config_entry->types[i]); + + if (value && !slapi_UTF8CASECMP(config_entry->generate, value)) { + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(config_entry->types[i])); + }
- slapi_ch_free_string(&value); + slapi_ch_free_string(&value); + } + } else { + /* For a single type range, we generate the value if + * the magic value is set or if the type is missing. */ + value = slapi_entry_attr_get_charptr(e, config_entry->types[0]); + + if ((value && !slapi_UTF8CASECMP(config_entry->generate, value)) + || 0 == value) { + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(config_entry->types[0])); + } + + slapi_ch_free_string(&value); + } } else { /* check mods for magic value */ Slapi_Mod *next_mod = slapi_mod_new(); @@ -2690,15 +2892,19 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) char *type = (char *) slapi_mod_get_type(smod);
- if (slapi_attr_types_equivalent(type, - config_entry->type)) { + /* See if the type matches any configured type. */ + if (dna_list_contains_type(config_entry->types, type)) { /* If all values are being deleted, we need to - * generate a new value. */ - if (SLAPI_IS_MOD_DELETE(slapi_mod_get_operation(smod))) { + * generate a new value. We don't do this for + * multi-type ranges since they require the magic + * value to be specified to trigger generation. */ + if (SLAPI_IS_MOD_DELETE(slapi_mod_get_operation(smod)) && + !dna_is_multitype_range(config_entry)) { int numvals = slapi_mod_get_num_values(smod);
if (numvals == 0) { - generate = 1; + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(type)); } else { Slapi_Attr *attr = NULL; int e_numvals = 0; @@ -2707,7 +2913,8 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) if (attr) { slapi_attr_get_numvalues(attr, &e_numvals); if (numvals >= e_numvals) { - generate = 1; + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(type)); } } } @@ -2715,14 +2922,17 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) /* This is either adding or replacing a value */ struct berval *bv = slapi_mod_get_first_value(smod);
- /* If generate is already set, a previous mod in - * this same modify operation either removed all - * values or set the magic value. It's possible - * that this mod is adding a valid value, which - * means we would not want to generate a new value. - * It is safe to unset generate since it will be - * reset here if necessary. */ - generate = 0; + /* If this type is already in the to be generated + * list, a previous mod in this same modify operation + * either removed all values or set the magic value. + * It's possible that this mod is adding a valid value, + * which means we would not want to generate a new value. + * It is safe to remove this type from the to be + * generated list since it will be re-added here if + * necessary. */ + if (dna_list_contains_type(types_to_generate, type)) { + dna_list_remove_type(types_to_generate, type); + }
/* If we have a value, see if it's the magic value. */ if (bv) { @@ -2731,13 +2941,16 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) if (!slapi_UTF8NCASECMP(bv->bv_val, config_entry->generate, len)) { - generate = 1; + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(type)); } } - } else { + } else if (!dna_is_multitype_range(config_entry)) { /* This is a replace with no new values, so we need - * to generate a new value. */ - generate = 1; + * to generate a new value if this is not a multi-type + * range. */ + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(type)); } } } @@ -2754,25 +2967,27 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) * to see if a value exists for the managed type in the resulting * entry. This will catch a modify operation that brings an entry * into scope for a managed range, but doesn't supply a value for - * the managed type. - */ - if ((LDAP_CHANGETYPE_MODIFY == modtype) && !generate) { + * the managed type. We don't do this for multi-type ranges. */ + if ((LDAP_CHANGETYPE_MODIFY == modtype) && (!types_to_generate || + (types_to_generate && !types_to_generate[0])) && + !dna_is_multitype_range(config_entry)) { Slapi_Attr *attr = NULL; - if (slapi_entry_attr_find(resulting_e, config_entry->type, &attr) != 0) { - generate = 1; + if (slapi_entry_attr_find(resulting_e, config_entry->types[0], &attr) != 0) { + slapi_ch_array_add(&types_to_generate, + slapi_ch_strdup(config_entry->types[0])); } }
- if (generate) { + if (types_to_generate && types_to_generate[0]) { char *new_value; int len;
/* create the value to add */ ret = dna_get_next_value(config_entry, &value); if (DNA_SUCCESS != ret) { - errstr = slapi_ch_smprintf("Allocation of a new value for" + errstr = slapi_ch_smprintf("Allocation of a new value for range" " %s failed! Unable to proceed.", - config_entry->type); + config_entry->dn); break; }
@@ -2792,28 +3007,45 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) /* do the mod */ if (LDAP_CHANGETYPE_ADD == modtype) { /* add - add to entry */ - slapi_entry_attr_set_charptr(e, - config_entry->type, - new_value); + for (i = 0; types_to_generate && types_to_generate[i]; i++) { + slapi_entry_attr_set_charptr(e, + types_to_generate[i], + new_value); + } } else { /* mod - add to mods */ - slapi_mods_add_string(smods, - LDAP_MOD_REPLACE, - config_entry->type, new_value); + for (i = 0; types_to_generate && types_to_generate[i]; i++) { + slapi_mods_add_string(smods, + LDAP_MOD_REPLACE, + types_to_generate[i], new_value); + } }
- /* free up */ - slapi_ch_free_string(&value); - slapi_ch_free_string(&new_value); - - /* make sure we don't generate for this - * type again + /* Make sure we don't generate for this + * type again by keeping a list of types + * we have generated for already. */ if (LDAP_SUCCESS == ret) { - last_type = config_entry->type; + if (generated_types == NULL) { + /* If we don't have a list of generated types yet, + * we can just use the types_to_generate list so + * we don't have to allocate anything. */ + generated_types = types_to_generate; + types_to_generate = NULL; + } else { + /* Just reuse the elements out of types_to_generate for the + * generated types list to avoid allocating them again. */ + for (i = 0; types_to_generate && types_to_generate[i]; ++i) { + slapi_ch_array_add(&generated_types, types_to_generate[i]); + types_to_generate[i] = NULL; + } + } }
- generate = 0; + /* free up */ + slapi_ch_free_string(&value); + slapi_ch_free_string(&new_value); + slapi_ch_array_free(types_to_generate); } next: list = PR_NEXT_LINK(list); @@ -2831,6 +3063,7 @@ static int dna_pre_op(Slapi_PBlock * pb, int modtype) }
bail: + slapi_ch_array_free(generated_types);
if (free_entry && e) slapi_entry_free(e); @@ -3229,7 +3462,11 @@ void dna_dump_config()
void dna_dump_config_entry(struct configEntry * entry) { - printf("<---- type -----------> %s\n", entry->type); + int i = 0; + + for (i = 0; entry->types && entry->types[i]; i++) { + printf("<---- type -----------> %s\n", entry->types[i]); + } printf("<---- filter ---------> %s\n", entry->filter); printf("<---- prefix ---------> %s\n", entry->prefix); printf("<---- scope ----------> %s\n", entry->scope);
389-commits@lists.fedoraproject.org