/* * $Id: aux-items.c,v 1.20 1999/06/26 11:36:28 ceder Exp $ * Copyright (C) 1994-1999 Lysator Academic Computer Association. * * This file is part of the LysKOM server. * * LysKOM is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * LysKOM is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with LysKOM; see the file COPYING. If not, write to * Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN, * or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, * MA 02139, USA. * * Please mail bug reports to bug-lyskom@lysator.liu.se. */ #ifdef HAVE_CONFIG_H # include #endif #include #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #include #include #include "kom-types.h" #include "com.h" #include "async.h" #include "connections.h" #include "manipulate.h" #include "aux-items.h" #include "kom-errno.h" #include "kom-memory.h" #include "server/smalloc.h" #include "cache.h" #include "s-string.h" #include "lyskomd.h" #include "kom-config.h" #include "regex.h" #include "log.h" #include "services.h" #include "admin.h" #include "param.h" #include "server-time.h" #include "string-malloc.h" #define AUX_ADJUST_FLAG(flg) item->flags.flg = (def->clear_flags.flg)?0:((def->set_flags.flg)?1:item->flags.flg) /* Externally accessible variables (used by the parser) */ Aux_item_definition *aux_item_definition_list = NULL; unsigned long num_aux_item_definitions = 0; /* Variables local to this file */ static Aux_item_definition compiled_aux_items[] = { }; /* Forward declaration of triggers */ void aux_item_trigger_mark_text(Aux_item_trigger_data *); void aux_item_trigger_unmark_text(Aux_item_trigger_data *); void aux_item_trigger_mirror_faq(Aux_item_trigger_data *); void aux_item_trigger_link_item(Aux_item_trigger_data *data); /* Forward declarations of validators */ Bool aux_item_validate_existing_text(Aux_item_validation_data *data); /* Symbol table for the triggers */ static Aux_item_trigger_mapping aux_item_triggers [] = { { "mark-text", aux_item_trigger_mark_text }, { "unmark-text", aux_item_trigger_unmark_text }, { "link-faq", aux_item_trigger_mirror_faq }, { "link-item", aux_item_trigger_link_item }, { NULL, NULL } }; /* Symbol table for the validators */ static Aux_item_validator_mapping aux_item_validators [] = { { "existing-readable-text", aux_item_validate_existing_text }, { NULL, NULL } }; /* Use this to initialize an empty definition */ Aux_item_definition empty_aux_item_definition = { NULL, /* Name */ 0, /* Tag */ { 0,0,0,0,0,0,0,0 }, /* Clear flags */ { 0,0,0,0,0,0,0,0 }, /* Set flags */ 0, /* Disabled */ 0, /* Author only */ 0, /* Supervisor only */ 0, /* System only */ 0, /* Unique */ 0, /* Can't delete */ 0, /* Inherit limit */ FALSE, /* Texts */ 0, FALSE, /* Confs */ 0, FALSE, /* Letterbox */ FALSE, /* System */ 0, /* Number of validators */ NULL, /* Validator list */ 0, NULL, 0, NULL, 0, NULL, NULL /* Next */ }; /* * simple_aux_item is the default definition for simple * items (non-reserved tags) */ static Aux_item_definition simple_aux_item = { "simple", /* Name */ 0, /* Tag */ { 0,0,0,0,0,0,0,0 }, /* Clear flags */ { 0,0,0,0,0,0,0,0 }, /* Set flags */ 0, /* Disabled */ 0, /* Author only */ 0, /* Supervisor only */ 0, /* System only */ 0, /* Unique */ 0, /* Can't delete */ 0, /* Inherit limit */ TRUE, /* Texts */ 0, TRUE, /* Confs */ 0, TRUE, /* Letterbox */ TRUE, /* System */ 0, NULL, /* Validators */ 0, NULL, /* Add triggers */ 0, NULL, /* Delete triggers */ 0, NULL, /* Undelete triggers */ NULL }; /* Forward declarations */ static Bool aux_item_check_unique(Aux_item *item, Aux_item_definition *xdef, Aux_item_list *add_to_list, unsigned long start_looking_at); /* * aux_item_default_definition * * Return the default definition of ITEM or NULL if ITEM cannot * be created without an explicit definition */ static Aux_item_definition * aux_item_default_definition(Aux_item *item) { /* A predefined aux_item that we didn't find a definition for is illegal. */ if (AUX_IS_ILLEGAL(item->tag)) return NULL; if (AUX_IS_PREDEFINED(item->tag)) return NULL; /* This might be an extended item. This code is very horrendous, execially if you take a closer look at the AUX_CHECK_BIT macro. I wrote it this way because I think it will work in a wrong-endian environment, and I was unsure of how well simple masking would. */ /* MMMM XFUP SSSS CCCC NNNN NNNN NNNN NNNN */ /* tmp = item->tag; extended = (tmp & 0x07000000) ? 1 : 0; def_s.author_only = (tmp & 0x04000000) ? 1 : 0; def_s.one_per_person = (tmp & 0x02000000) ? 1 : 0; def_s.may_not_delete = (tmp & 0x01000000) ? 1 : 0; def_s.set_flags.deleted = (tmp & 0x00700000) ? 1 : 0; def_s.set_flags.inherit = (tmp & 0x00400000) ? 1 : 0; def_s.set_flags.secret = (tmp & 0x00200000) ? 1 : 0; def_s.set_flags.hide_creator = (tmp & 0x00100000) ? 1 : 0; def_s.clear_flags.deleted = (tmp & 0x00070000) ? 1 : 0; def_s.clear_flags.inherit = (tmp & 0x00040000) ? 1 : 0; def_s.clear_flags.secret = (tmp & 0x00020000) ? 1 : 0; def_s.clear_flags.hide_creator = (tmp & 0x00010000) ? 1 : 0; def_s.tag = item->tag; def_s.inherit_limit = 0; def_s.name = "extended-default"; / * If it wasn't extended, then it was simple, because it sure wasn't predefined. * / if (!extended) { return &simple_aux_item; } return &def_s; */ return &simple_aux_item; } /* * aux_item_find_trigger */ Aux_item_trigger aux_item_find_trigger(char *trigger_name) { unsigned long i = 0; while (aux_item_triggers[i].name != NULL) { if (!strcmp(aux_item_triggers[i].name, trigger_name)) return aux_item_triggers[i].function; i += 1; } return NULL; } Aux_item_validation_function aux_item_find_validator(char *validator_name) { unsigned long i = 0; while (aux_item_validators[i].name != NULL) { if (!strcmp(aux_item_validators[i].name, validator_name)) return aux_item_validators[i].function; i += 1; } return NULL; } /* * find_aux_item_definition * * Return the definition of ITEM or NULL if it does not * have a valid information. */ Aux_item_definition * find_aux_item_definition(Aux_item *item) { Aux_item_definition *def; def = aux_item_definition_list; while (def != NULL) { if (def->tag == item->tag) return def; def = def->next; } return aux_item_default_definition(item); } /* * ====================================================================== * UTILITY FUNCTIONS * ====================================================================== */ /* TRIGGER THINGS */ static void aux_item_fix_trigger_data(Aux_item_trigger_data *data) { switch (data->object_type) { case TEXT_OBJECT_TYPE: data->item = &((Text_stat *)data->object)->aux_item_list.items[data->item_index]; break; case CONF_OBJECT_TYPE: data->item = &((Conference *)data->object)->aux_item_list.items[data->item_index]; break; case INFO_OBJECT_TYPE: data->item = &((Info *)data->object)->aux_item_list.items[data->item_index]; break; default: break; } } static void aux_item_call_add_triggers(Aux_item_definition *def, Object_type object_type, unsigned long item_index, unsigned long integer_argument, void * pointer_argument, Aux_item * item) { unsigned long i; Aux_item_trigger_data data; data.action = AUX_ITEM_ADD_ACTION; data.object_type = object_type; data.item_index = item_index; data.object_no = integer_argument; data.object = pointer_argument; data.item = item; for (i = 0; i < def->num_add_triggers; i++) { (*(def->add_triggers[i]))(&data); aux_item_fix_trigger_data(&data); } } static void aux_item_call_delete_triggers(Aux_item_definition *def, Object_type object_type, unsigned long item_index, unsigned long integer_argument, void * pointer_argument, Aux_item *item) { unsigned long i; Aux_item_trigger_data data; data.action = AUX_ITEM_DELETE_ACTION; data.object_type = object_type; data.item_index = item_index; data.object_no = integer_argument; data.object = pointer_argument; data.item = item; for (i = 0; i < def->num_delete_triggers; i++) { (*(def->delete_triggers[i]))(&data); } } static void aux_item_call_undelete_triggers(Aux_item_definition *def, Object_type object_type, unsigned long item_index, unsigned long integer_argument, void * pointer_argument, Aux_item * item) { unsigned long i; Aux_item_trigger_data data; data.action = AUX_ITEM_UNDELETE_ACTION; data.object_type = object_type; data.item_index = item_index; data.object_no = integer_argument; data.object = pointer_argument; data.item = item; for (i = 0; i < def->num_undelete_triggers; i++) { (*(def->undelete_triggers[i]))(&data); } } /* * initialize_aux_items * * Initialize this code and its data structures */ void aux_item_definition_add(Aux_item_definition *def) { Aux_item_definition *new_definition; new_definition = smalloc(sizeof(Aux_item_definition)); if (new_definition == NULL) { restart_kom("out of memory adding aux-item definition"); } *new_definition = *def; num_aux_item_definitions += 1; new_definition->next = aux_item_definition_list; aux_item_definition_list = new_definition; } /* * Compile the verification regexp for the definition in DEF * and put the result somewhere convenient. */ static void aux_item_definition_cache_regexp(Aux_item_definition *def, unsigned long ix) { struct re_pattern_buffer *pat_buf; const char *errmsg; re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; def->validators[ix].v.re.cached_re_buf = smalloc(sizeof(*pat_buf)); if (def->validators[ix].v.re.cached_re_buf == NULL) { kom_log("Out of memory compiling aux-item regexp %lu (%s).\n", def->tag, def->name); return; } pat_buf = def->validators[ix].v.re.cached_re_buf; pat_buf->translate = DEFAULT_COLLAT_TAB; pat_buf->translate = NULL; pat_buf->fastmap = 0; pat_buf->allocated = 0; pat_buf->buffer = 0; if ((errmsg = re_compile_pattern(def->validators[ix].v.re.regexp, strlen(def->validators[ix].v.re.regexp), pat_buf)) != NULL) { kom_log("%s in validate regexp of aux-item definition %lu (%s).\n", errmsg, def->tag, def->name); if (def->validators[ix].v.re.cached_re_buf) sfree(def->validators[ix].v.re.cached_re_buf); if (def->validators[ix].v.re.regexp) string_free(def->validators[ix].v.re.regexp); def->validators[ix].v.re.cached_re_buf = NULL; def->validators[ix].v.re.regexp = NULL; return; } } void initialize_aux_items(char *aux_def_file) { unsigned long i; /* Set up precompiled items */ for (i = 0; i < (sizeof(compiled_aux_items)/sizeof(*compiled_aux_items)); i++) { aux_item_definition_add(&compiled_aux_items[i]); } /* Read definitions from configuration file */ parse_aux_item_definitions(aux_def_file); } void free_aux_item_definitions(void) { unsigned long i; Aux_item_definition *def; Aux_item_definition *tmp; def = aux_item_definition_list; while (def != NULL) { string_free(def->name); for (i = 0; i < def->num_validators; i++) { if (def->validators[i].type == AUX_VALIDATE_REGEXP) { if (def->validators[i].v.re.regexp != NULL) string_free(def->validators[i].v.re.regexp); if (def->validators[i].v.re.cached_re_buf != NULL) sfree(def->validators[i].v.re.cached_re_buf); } } if (def->validators != NULL) { sfree(def->validators); def->validators = NULL; def->num_validators = 0; } if (def->add_triggers != NULL) { sfree(def->add_triggers); def->add_triggers = NULL; def->num_add_triggers = 0; } if (def->delete_triggers != NULL) { sfree(def->delete_triggers); def->delete_triggers = NULL; def->num_delete_triggers = 0; } if (def->undelete_triggers != NULL) { sfree(def->undelete_triggers); def->undelete_triggers = NULL; def->num_undelete_triggers = 0; } tmp = def->next; sfree(def); def = tmp; } aux_item_definition_list = NULL; } long find_aux_item_index(Aux_item_list *list, unsigned long aux_no) { unsigned long i; for (i = 0; i < list->length; i++) { if (list->items[i].aux_no == aux_no) return i; } return -1; } /* Find the aux item list containing the item that ITEM is linked to */ static Aux_item_list * find_linked_aux_item_list(Aux_item *item) { Text_stat *text_stat; Conference *conf_stat; switch (item->linked_item.target_type) { case NO_OBJECT_TYPE: return NULL; case TEXT_OBJECT_TYPE: GET_T_STAT(text_stat, item->linked_item.target_object.text, NULL); return &text_stat->aux_item_list; case CONF_OBJECT_TYPE: GET_C_STAT(conf_stat, item->linked_item.target_object.conf, NULL); return &conf_stat->aux_item_list; case INFO_OBJECT_TYPE: return &kom_info.aux_item_list; case PERS_OBJECT_TYPE: default: /* No lists in these items */ kom_log("find_linked_aux_item_list: Bad aux_item somewhere: " "link to person or other kind.\n"); return NULL; } } /* Find the aux item that ITEM is linked to */ static Aux_item * find_linked_aux_item(Aux_item *item) { Aux_item_list *target_list; target_list = find_linked_aux_item_list(item); if (target_list == NULL) return NULL; return find_aux_item(target_list, item->linked_item.target_item); } /* Mark the object linked to by an aux_item as changed */ static void mark_linked_object_as_changed(Aux_item *item) { Text_stat *text_stat; Conference *conf_stat; switch (item->linked_item.target_type) { case TEXT_OBJECT_TYPE: VOID_GET_T_STAT(text_stat, item->linked_item.target_object.text); mark_text_as_changed(item->linked_item.target_object.text); break; case CONF_OBJECT_TYPE: VOID_GET_C_STAT(conf_stat, item->linked_item.target_object.conf); mark_conference_as_changed(item->linked_item.target_object.conf); break; case INFO_OBJECT_TYPE: case PERS_OBJECT_TYPE: default: /* Need no commit for these objects */ ; } } /* * Utility function for linking aux items. This function will take * care of adding the appropriate item to the appropriate list. * * Note that this may cause aux-item lists to be realloced, so pointers * into such lists may become invalid. Take particular care with the * pointers in aux-item trigger data * * Call aux_item_fix_trigger_data if you need to fix the contents of * trigger data after calling this function. */ static void aux_item_link_items(Object_type src_type, /* Source object type */ unsigned long src_no, /* Source object pointer */ void *src_ptr, /* Source pointer */ Aux_item *src_item, /* Source item */ Object_type dst_type, /* Destination obj type */ unsigned long dst_no, /* Destination obj pointer */ void *dst_ptr, /* Destination pointer */ Aux_item *dst_item_data) { Aux_item_list item_list; /* Get a pointer to the destination object */ if (dst_ptr == NULL) { switch (dst_type) { case TEXT_OBJECT_TYPE: dst_ptr = cached_get_text_stat(dst_no); break; case CONF_OBJECT_TYPE: dst_ptr = cached_get_conf_stat(dst_no); break; case INFO_OBJECT_TYPE: dst_ptr = &kom_info; break; default: break; } } if (dst_ptr == NULL || src_ptr == NULL) return; /* Set up the linking information in the destination item */ dst_item_data->linked_item.target_type = src_type; dst_item_data->linked_item.target_item = src_item->aux_no; switch (src_type) { case TEXT_OBJECT_TYPE: dst_item_data->linked_item.target_object.text = src_no; break; case CONF_OBJECT_TYPE: dst_item_data->linked_item.target_object.conf = src_no; break; case INFO_OBJECT_TYPE: default: dst_item_data->linked_item.target_object.text = 0; } /* Set up the linking information in the source item */ src_item->linked_item.target_type = dst_type; src_item->linked_item.target_item = 0; switch (dst_type) { case TEXT_OBJECT_TYPE: src_item->linked_item.target_object.text = dst_no; src_item->linked_item.target_item = ((Text_stat *)dst_ptr)->highest_aux + 1; break; case CONF_OBJECT_TYPE: src_item->linked_item.target_object.conf = dst_no; src_item->linked_item.target_item = ((Conference *)dst_ptr)->highest_aux + 1; break; case INFO_OBJECT_TYPE: src_item->linked_item.target_item = ((Info *)dst_ptr)->highest_aux_no + 1; src_item->linked_item.target_object.text = 0; src_item->linked_item.target_object.conf = 0; break; default: src_item->linked_item.target_item = 0; src_item->linked_item.target_object.text = 0; src_item->linked_item.target_object.conf = 0; } /* Set up an item list to add */ item_list.length = 1; item_list.items = dst_item_data; prepare_aux_item_list(&item_list, src_item->creator); /* * Add the item and mark the destination as changed * * Note that these calls may cause the aux item list of the * destination to move in memory due to calls to realloc. */ switch (dst_type) { case TEXT_OBJECT_TYPE: text_stat_add_aux_item_list((Text_stat *)dst_ptr, (Text_no)dst_no, &item_list, src_item->creator); mark_text_as_changed((Text_no)dst_no); break; case CONF_OBJECT_TYPE: conf_stat_add_aux_item_list((Conference *)dst_ptr, (Conf_no)dst_no, &item_list, src_item->creator); mark_conference_as_changed((Conf_no)dst_no); break; case INFO_OBJECT_TYPE: system_add_aux_item_list((Info *)dst_ptr, &item_list, src_item->creator); break; default: break; } /* * Set src_item to NULL since there is a chance that it is * no longer valid (this happens when the source and destination * objects are the same and reallocing the aux item list causes * it to move in memory */ src_item = NULL; /* * Mark the source object as changed */ mark_linked_object_as_changed(dst_item_data); } static void aux_item_list_add_items(Aux_item_list *add_to_list, Aux_item_list *items_to_add, Object_type object_type, unsigned long object_no, void *object_ptr, unsigned long *highest_ptr, Pers_no item_creator) { unsigned long i; unsigned long highest_local; unsigned long start_index; if (items_to_add == NULL) return; /* * Make local copies of variables we need in their original state */ start_index = add_to_list->length; highest_local = *highest_ptr; /* * Set up the target list so it looks like we've added * items to it already. That way it is semi-safe to call * this function recursively. */ add_to_list->items = srealloc(add_to_list->items, (add_to_list->length + items_to_add->length) * sizeof(Aux_item)); add_to_list->length += items_to_add->length; *highest_ptr = *highest_ptr + items_to_add->length; for (i = 0; i < items_to_add->length; i++) { init_aux_item(&add_to_list->items[start_index + i]); } /* * Now add the items and call the add triggers */ for (i = 0; i < items_to_add->length; i++) { highest_local += 1; copy_aux_item(&add_to_list->items[i + start_index], &items_to_add->items[i]); add_to_list->items[i + start_index].aux_no = highest_local; add_to_list->items[i + start_index].creator = item_creator; aux_item_call_add_triggers( find_aux_item_definition(&items_to_add->items[i]), object_type, i + start_index, object_no, object_ptr, &add_to_list->items[i + start_index]); } } /* * prepare_aux_item_list * * Prepare items in LIST for checking and addition. You *have* * to call this before any of the addition or checking * function. CREATOR is the person who wants to create the * item list. This function will set various fields in the * items. UTSL. */ void prepare_aux_item_list(Aux_item_list *list, Pers_no creator) { unsigned long i; if (list == NULL) return; for (i = 0; i < list->length; i++) { prepare_aux_item(&list->items[i], creator, NULL); } } void prepare_aux_item(Aux_item *item, Pers_no creator, Aux_item_definition *def) { if (item == NULL) return; def = def ? def : find_aux_item_definition(item); if (def == NULL) return; AUX_ADJUST_FLAG(inherit); AUX_ADJUST_FLAG(secret); AUX_ADJUST_FLAG(hide_creator); AUX_ADJUST_FLAG(dont_garb); AUX_ADJUST_FLAG(reserved3); AUX_ADJUST_FLAG(reserved4); AUX_ADJUST_FLAG(reserved5); /* AUX_ADJUST_FLAG(deleted); */ item->flags.deleted = 0; if (def->inherit_limit != 0 && (item->inherit_limit > def->inherit_limit || item->inherit_limit == 0)) item->inherit_limit = def->inherit_limit; item->creator = creator; item->sent_at = current_time; } static Bool aux_item_validate(Aux_item_validation_data validation_data) { unsigned long i; Aux_item_definition *def; def = validation_data.def; for (i = 0; i < def->num_validators; i++) { switch (def->validators[i].type) { case AUX_VALIDATE_FUNCTION: /* * Call the validation function * If it returns false, then abort validation * If it returns true, then continue with the * next validator */ if ((*def->validators[i].v.fn.function)(&validation_data) == FALSE) { return FALSE; } break; case AUX_VALIDATE_REGEXP: /* * Ensure that the regexp is cached */ if (def->validators[i].v.re.cached_re_buf == NULL) { aux_item_definition_cache_regexp(def, i); } /* * If the RE is still not cached there is a problem * in this case to not validate the item at all and * log a problem */ if (def->validators[i].v.re.cached_re_buf == NULL) { kom_log("Failed to cache regexp validator for \"%s\". " "Rejecting all such items\n", def->name); sfree(def->validators[i].v.re.regexp); def->validators[i].v.re.regexp = NULL; def->validators[i].type = AUX_VALIDATE_REJECT; kom_errno = KOM_ILL_AUX; return FALSE; } /* * RE is compiled and can be used */ switch (re_search(def->validators[i].v.re.cached_re_buf, validation_data.item->data.string, s_strlen(validation_data.item->data), 0, s_strlen(validation_data.item->data), NULL)) { case -1: /* No match */ kom_errno = KOM_ILL_AUX; return FALSE; case -2: /* Internal error may (be temporary) */ kom_log("Internal error in regex matching aux-item data.\n"); kom_errno = KOM_ILL_AUX; return FALSE; break; default: /* Matched */ break; } break; case AUX_VALIDATE_REJECT: /* * Just reject the item. Used when we detect a problem * with a validator */ kom_errno = KOM_ILL_AUX; return FALSE; break; default: kom_log("BUG: unknown aux_item validator type\n"); } } /* * If we got all the way to here we passed all the validators */ return TRUE; } /* * aux_item_add_perm * * Check if it is alright to add an aux_item. * * ITEM is the item that you want to add. * DEF is the item's definition or NULL. * ITEM_CREATOR is the person who wants to create the item. * OBJECT_CREATOR is the person who created the object to which * the item might be added. For texts it is the author, for * persons it is the person and for conferences it is the * conference supervisor. * OWNER_CHECK is TRUE when this function is supposed to check author * and supervisor-brelated flags. The only case when this is to * be FALSE is when checking author or supervisor makes not sense, * such as when creating a completely new conference. * ADD_TO_LIST is the item list to add to, or NULL. * START_LOOKING_AT is the first index in add_to_list to start * looking at for possible duplicates. * CREATING is true if we are checking while creating an object */ Bool aux_item_add_perm(Aux_item *item, Aux_item_definition *def, Pers_no item_creator, Pers_no object_creator, Bool owner_check, Aux_item_list *add_to_list, unsigned long start_looking_at, Bool creating, Object_type object_type ) { short can_add_when = 0; kom_errno = KOM_NO_ERROR; def = def ? def : def = find_aux_item_definition(item); /* Can't create an item with no definition */ if (def == NULL) { kom_errno = KOM_ILL_AUX; return FALSE; } if (def->disabled) { kom_errno = KOM_ILL_AUX; return FALSE; } switch (object_type) { case TEXT_OBJECT_TYPE: can_add_when = def->text_a; break; case CONF_OBJECT_TYPE: can_add_when = def->conf_a; break; default: can_add_when = 0; } if (can_add_when) { if (!((creating && (can_add_when & AUX_ITEM_ADD_ON_CREATE)) || (!creating && (can_add_when & AUX_ITEM_ADD_ON_MODIFY)))) { kom_errno = KOM_AUX_PERM; return FALSE; } } if (def->system_only && owner_check) { kom_errno = KOM_AUX_PERM; return FALSE; } if (def->author_only && owner_check && object_creator != item_creator && !is_strictly_supervisor(object_creator, NULL, item_creator, NULL) && !(item_creator == ACTPERS && ENA(wheel, 8))) /* NOT OK! */ { kom_errno = KOM_AUX_PERM; return FALSE; } if (def->supervisor_only && owner_check && !is_strictly_supervisor(object_creator, NULL, item_creator, NULL) && !(item_creator == ACTPERS && ENA(wheel,8))) /* NOT OK! */ { kom_errno = KOM_AUX_PERM; return FALSE; } /* Even the administrator can't override this */ if (!aux_item_check_unique(item, def, add_to_list, start_looking_at)) { kom_errno = KOM_AUX_PERM; return FALSE; } /* Check the contents */ if (def->num_validators > 0) { Aux_item_validation_data validation_data; validation_data.item = item; validation_data.def = def; validation_data.item_creator = item_creator; validation_data.object_creator = object_creator; validation_data.owner_check = owner_check; validation_data.add_to_list = add_to_list; validation_data.start_looking_at = start_looking_at; validation_data.creating = creating; validation_data.object_type = object_type; if (aux_item_validate(validation_data) != TRUE) { kom_errno = KOM_ILL_AUX; return FALSE; } } return TRUE; } /* * aux_item_check_unique * * Check whether there are items that block creation of a new item. * ITEM is the item we want to add. * DEF is the item's definitioon * ADD_TO_LIST is the list we want to add to * START_LOOKING_AT is the first index in the list to look at. */ static Bool aux_item_check_unique(Aux_item *item, Aux_item_definition *def, Aux_item_list *add_to_list, unsigned long start_looking_at) { unsigned long i; if (add_to_list == NULL || item == NULL || start_looking_at >= add_to_list->length) return TRUE; def = def ? def : find_aux_item_definition(item); if (def == NULL || !def->one_per_person) return TRUE; for (i = start_looking_at; i < add_to_list->length; i++) { if (add_to_list->items[i].tag == item->tag && add_to_list->items[i].creator == item->creator && !add_to_list->items[i].flags.deleted) return FALSE; } return TRUE; } /* * aux_inherit_items * * Inherit aux items from PARENT to TARGET. COUNTER is a pointer * to the aux_no counter in the object being inherited to and CREATOR * is the author or equivalent of the target object. */ void aux_inherit_items(Aux_item_list *target, Aux_item_list *parent, unsigned long *counter, Pers_no target_creator, Bool creating, Object_type object_type, unsigned long object_no, void *object) { int i; Aux_item item; Aux_item_definition *def; /* Inheriting linked items is strange, but works. */ if (!target || !parent) return; for (i = 0; i < parent->length; i++) { if (parent->items[i].flags.inherit && !parent->items[i].flags.deleted && parent->items[i].inherit_limit != 1) { def = find_aux_item_definition(&parent->items[i]); if (def == NULL || !aux_item_add_perm(&parent->items[i], def, parent->items[i].creator, target_creator, TRUE, target, 0, creating, object_type )) continue; copy_aux_item(&item, &parent->items[i]); init_aux_item_link(&item.linked_item); prepare_aux_item(&item, parent->items[i].creator, def); if (item.inherit_limit != 0) item.inherit_limit -= 1; target->items = srealloc(target->items, (target->length + 1) * sizeof(Aux_item)); target->items[target->length] = item; *counter += 1; target->items[target->length].aux_no = *counter; target->length += 1; aux_item_call_add_triggers(def, object_type, target->length - 1, object_no, object, &target->items[target->length]); } } } /* * filter_aux_item_list * * Filter out information in ORIGINAL that VIEWER is not allowed * to see. Copies of the items are placed in RESULT. VIEWER_P is * the Person corresponding to viewer or NULL. All memory is * allocated with tmp_alloc */ void filter_aux_item_list(Aux_item_list *original, Aux_item_list *result, Connection *viewer_conn) { Aux_item *orig_aux; unsigned long from, to; /* Pers_no viewer; Person *viewer_p;*/ result->items = tmp_alloc(original->length * sizeof (Aux_item)); result->length = 0; to = 0; for (from = 0; from < original->length; from++) { orig_aux = &original->items[from]; if ( orig_aux->flags.secret && !is_supervisor(orig_aux->creator, NULL, viewer_conn->pers_no, viewer_conn->person) && !ENA_C(viewer_conn, admin, 4)) continue; result->items[to] = *orig_aux; if (orig_aux->flags.hide_creator && !is_supervisor(orig_aux->creator, NULL, viewer_conn->pers_no, viewer_conn->person) && !ENA_C(viewer_conn, admin, 4)) result->items[to].creator = 0; result->length += 1; to += 1; } } /* * check_delete_aux * * Return TRUE if ACTPERS may delete ITEM. DEF is the item's definition * or NULL. */ Success check_delete_aux_item_list(Number_list *items_to_delete, Aux_item_list *list_to_delete_from) { long i; Aux_item *item; Aux_item_definition *def; for (i = 0; i < items_to_delete->length; i++) { if (items_to_delete->data[i] == 0) continue; /* Get pointer to the item and to its definition */ if ((item = find_aux_item(list_to_delete_from, items_to_delete->data[i])) == NULL) { kom_errno = KOM_AUX_PERM; err_stat = i; return FAILURE; } /* Check all sorts of permissions */ def = find_aux_item_definition(item); if ((def == NULL) || (def->may_not_delete) || (item->creator == 0 && !ENA(wheel, 8)) || /* NOT OK! */ (item->creator != ACTPERS && /* NOT OK! */ !is_supervisor(item->creator, NULL, ACTPERS, NULL) && /* NOT OK! */ !ENA(wheel,8))) /* NOT OK! */ { kom_errno = KOM_AUX_PERM; err_stat = i; return FAILURE; } /* For already deleted items, clear them out of the list */ if (item->flags.deleted) items_to_delete->data[i] = 0; } return OK; } /* * delete_aux_item_list * * Mark ITEM as deleted. LIST is the list we're deleting * the item from. * * Call commit_aux_item_list to make the changes permanent */ void delete_aux_item_list(Number_list *items_to_delete, Aux_item_list *list_to_delete_from, Object_type object_type, unsigned long object_no, void *object) { long i; Aux_item *item; Aux_item *linked_item; long item_index; long item_to_delete; unsigned long linked_object_no; Number_list linked_delete; Aux_item_list *linked_item_list; void *linked_object; for (i = 0; i < items_to_delete->length; i++) { if (items_to_delete->data[i] == 0) continue; item_index = find_aux_item_index(list_to_delete_from, items_to_delete->data[i]); if (item_index == -1) restart_kom("Call to delete_aux_item_list without call to check_delete_aux_item_list"); item = &list_to_delete_from->items[item_index]; item->flags.deleted = 1; aux_item_call_delete_triggers(find_aux_item_definition(item), object_type, item_index, object_no, object, item); /* If we have linked items, undelete them too. It is safe to do this with a recursive call since we have marked this item as undeleted. But just to avoid pointless recursion, only do the recursive call if the corresponding item is marked as deleted */ linked_item = find_linked_aux_item(item); if (linked_item && !linked_item->flags.deleted) { /* Set up the undelete list for the target object */ item_to_delete = item->linked_item.target_item; linked_delete.length = 1; linked_delete.data = &item_to_delete; /* Find the item list to undelete from */ linked_item_list = find_linked_aux_item_list(item); /* Set up the object and object_no parameters for undelete */ switch (item->linked_item.target_type) { case CONF_OBJECT_TYPE: linked_object_no = item->linked_item.target_object.conf; linked_object = (void*)cached_get_conf_stat(linked_object_no); break; case TEXT_OBJECT_TYPE: linked_object_no = item->linked_item.target_object.text; linked_object = (void*)cached_get_text_stat(linked_object_no); break; case INFO_OBJECT_TYPE: linked_object_no = 0; linked_object = NULL; break; default: kom_log("delete_aux_item_list(): bad target type\n"); return; } /* We only get here if the link type is known */ delete_aux_item_list(&linked_delete, linked_item_list, item->linked_item.target_type, linked_object_no, linked_object); } } } /* * Reverse tentative deletions from an aux-item-list * Only deletions that have not been made permanent by a call to * commit_aux_item_list can be undeleted. */ void undelete_aux_item_list(Number_list *items_to_undelete, Aux_item_list *list_to_undelete_from, Object_type object_type, unsigned long object_no, void *object) { Kom_err saved_kom_errno; unsigned long saved_err_stat; long i; Aux_item *item; Aux_item *linked_item; Aux_item_list *linked_item_list; long item_index; long item_to_undelete; unsigned long linked_object_no; Number_list linked_undelete; void *linked_object; saved_kom_errno = kom_errno; saved_err_stat = err_stat; for (i = 0; i < items_to_undelete->length; i++) { if (items_to_undelete->data[i] == 0) continue; item_index = find_aux_item_index(list_to_undelete_from, items_to_undelete->data[i]); if (item_index == -1) restart_kom("Call to undelete_aux_item_list without call to check_delete_aux_item_list"); item = &list_to_undelete_from->items[item_index]; item->flags.deleted = 0; aux_item_call_undelete_triggers(find_aux_item_definition(item), object_type, item_index, object_no, object, item); /* If we have linked items, undelete them too. It is safe to do this with a recursive call since we have marked this item as undeleted. But just to avoid pointless recursion, only do the recursive call if the corresponding item is marked as deleted */ linked_item = find_linked_aux_item(item); if (linked_item && linked_item->flags.deleted) { /* Set up the undelete list for the target object */ item_to_undelete = item->linked_item.target_item; linked_undelete.length = 1; linked_undelete.data = &item_to_undelete; /* Find the item list to undelete from */ linked_item_list = find_linked_aux_item_list(item); /* Set up the object and object_no parameters for undelete */ switch (item->linked_item.target_type) { case CONF_OBJECT_TYPE: linked_object_no = item->linked_item.target_object.conf; linked_object = (void*)cached_get_conf_stat(linked_object_no); break; case TEXT_OBJECT_TYPE: linked_object_no = item->linked_item.target_object.text; linked_object = (void*)cached_get_text_stat(linked_object_no); break; case INFO_OBJECT_TYPE: linked_object_no = 0; linked_object = NULL; break; default: kom_log("undelete_aux_item_list(): bad link type\n"); kom_errno = saved_kom_errno; err_stat = saved_err_stat; return; } /* We only get here if the link type is known */ undelete_aux_item_list(&linked_undelete, linked_item_list, item->linked_item.target_type, linked_object_no, linked_object); } } kom_errno = saved_kom_errno; err_stat = saved_err_stat; } /* * Commit deletions in the aux-item-list. After this has been called * undelete_aux_item_list has no effect. The memory associated with * the deleted items is not released until the entire list is * released from memory or reallocated. */ static void commit_aux_item_list_internal(Aux_item_list *list_to_commit, Bool shallow) { long i; /* Loop index */ long target; /* Where to move items when compacting */ Aux_item *linked_item; Aux_item_list *aux_item_list; target = 0; for (i = 0; i < list_to_commit->length; i++) { if (list_to_commit->items[i].flags.deleted) { /* Clear the data in the aux item */ s_clear(&list_to_commit->items[i].data); /* If the item we're looking at is linked to another item, we have to commit the list of the object the link points to. To avoid infinite recursion only do this if the linked item is marked as deleted */ linked_item = find_linked_aux_item(&list_to_commit->items[i]); if (linked_item && linked_item->flags.deleted && !shallow) { aux_item_list = find_linked_aux_item_list(&list_to_commit->items[i]); commit_aux_item_list_internal(aux_item_list, TRUE); mark_linked_object_as_changed(&list_to_commit->items[i]); } continue; } else if (target != i) { /* If we have deleted an item earlier, compact the list */ list_to_commit->items[target] = list_to_commit->items[i]; list_to_commit->items[i].data = EMPTY_STRING; target += 1; } else { /* If we have not deleted an item, bump target up one */ target += 1; } } /* Zero out all the unused slots in the array. This is not strictly necessary but looks better and makes finding bugs a bit easier */ memset(&list_to_commit->items[target], 0, (list_to_commit->length - target) * sizeof(Aux_item)); /* Target points to one index more than the last non-deleted item */ list_to_commit->length = target; } void commit_aux_item_list(Aux_item_list *list_to_commit) { commit_aux_item_list_internal(list_to_commit, FALSE); } /* * find_aux_item * * Return a pointer to the item in LIST with aux_no AUX-NO */ Aux_item * find_aux_item(Aux_item_list *list, unsigned long aux_no) { long item_index = find_aux_item_index(list, aux_no); if (item_index == -1) return NULL; return &list->items[item_index]; } /* ********************************************************************** * ********************************************************************** * * The following calls are specific for various types of items. They * are here simply to collect all of them in the same place. * * The following functions are defined for each type * * X_check_add_aux_item_list(X *obj, Aux_item_list *list, * Pers_no item_creator) * * Return OK if CREATOR is allowed to add ITEM or LIST to OBJ. * DEF is a pointer to the definition of ITEM or NULL. You must call * prepare_aux_item_list before calling these. * * * X_add_aux_item_list(X *obj, Aux_item_definition *list, Pers_no creator) * * Add ITEM or all the items in LIST to OBJ. CREATOR is the person * adding the items. The creator field of the items are set to * creator. This call does not check permissions first. You need * to call prepare_aux_item_list before calling these, and you * should use X_check_add_aux_item_list or X_check_add_aux_item * before calling these. * * */ Success text_stat_check_add_aux_item_list(Text_stat *text_s, Aux_item_list *list, Pers_no item_creator, Bool creating) { Aux_item *item; unsigned long i; Aux_item_definition *def; if (list == NULL) return OK; kom_errno = KOM_NO_ERROR; for (i = 0; i < list->length; i++) { item = &list->items[i]; def = find_aux_item_definition(item); if (def == NULL) { kom_errno = KOM_ILL_AUX; err_stat = i; return FAILURE; } if (!aux_item_add_perm(item, def, item_creator, text_s?text_s->author:item_creator, TRUE, text_s?&text_s->aux_item_list:NULL, 0, creating, TEXT_OBJECT_TYPE) || !aux_item_check_unique(item, def, list, i + 1) || !def->texts) { kom_errno = kom_errno?kom_errno:KOM_AUX_PERM; err_stat = i; return FAILURE; } } return OK; } void text_stat_add_aux_item_list(Text_stat *text_s, Text_no text_no, Aux_item_list *item_list, Pers_no item_creator) { aux_item_list_add_items(&text_s->aux_item_list, item_list, TEXT_OBJECT_TYPE, text_no, text_s, &text_s->highest_aux, item_creator); } /* ================================================================== */ Success conf_stat_check_add_aux_item_list(Conference *conf, Conf_no conf_no, Aux_item_list *list, Pers_no creator, Bool creating) { unsigned long i; Aux_item *item; Aux_item_definition *def; if (list == NULL) return OK; kom_errno = KOM_NO_ERROR; for (i = 0; i < list->length; i++) { item = &list->items[i]; def = find_aux_item_definition(item); if (def == NULL) { kom_errno = KOM_ILL_AUX; err_stat = i; return FAILURE; } /* If CONF is non-NULL it already exists. We consider conf to be * its own "object creator" since this means that author_only * will allow only the supervisor of the conference to set * the aux_item (the conference creator is irrelevant here, as * everywhere else.) The semantics of supervisor_only will * mimic author_only. */ if (!aux_item_add_perm(item, def, creator, conf_no, TRUE, &conf->aux_item_list, 0, creating, CONF_OBJECT_TYPE) || !aux_item_check_unique(item, def, list, i + 1) || (!conf->type.letter_box && !def->confs) || (conf->type.letter_box && !def->letterboxes)) { kom_errno = kom_errno?kom_errno:KOM_AUX_PERM; err_stat = i; return FAILURE; } } return OK; } void conf_stat_add_aux_item_list(Conference *conf, Conf_no conf_no, Aux_item_list *item_list, Pers_no item_creator) { aux_item_list_add_items(&conf->aux_item_list, item_list, CONF_OBJECT_TYPE, conf_no, conf, &conf->highest_aux, item_creator); } Success system_check_add_aux_item_list(Info *info, Aux_item_list *list, Pers_no creator) { unsigned long i; Aux_item *item; Aux_item_definition *def; if (list == NULL) return OK; kom_errno = KOM_NO_ERROR; for (i = 0; i < list->length; i++) { item = &list->items[i]; def = find_aux_item_definition(item); if (def == NULL) { kom_errno = KOM_ILL_AUX; err_stat = i; return FAILURE; } if (!aux_item_add_perm(item, /* item */ def, /* definition */ creator, /* item_creator */ creator, /* object_creator */ TRUE, /* owner_check */ &info->aux_item_list, /* add_to_list */ 0, /* start_looking_at */ FALSE, /* creating */ INFO_OBJECT_TYPE) || !aux_item_check_unique(item, def, list, i + 1) || !def->system) { kom_errno = kom_errno?kom_errno:KOM_AUX_PERM; err_stat = i; return FAILURE; } } return OK; } void system_add_aux_item_list(Info *info, Aux_item_list *item_list, Pers_no item_creator) { aux_item_list_add_items(&info->aux_item_list, item_list, INFO_OBJECT_TYPE, 0, info, &info->highest_aux_no, item_creator); } extern Success query_predefined_aux_items(Number_list *result) { Aux_item_definition *def; result->data = tmp_alloc(sizeof(unsigned long) * num_aux_item_definitions); if (result->data == NULL) { restart_kom("Out of memory in query_predefined_aux_items.\n"); } result->length = 0; def = aux_item_definition_list; while (def != NULL) { if (def->tag != 0) { assert((unsigned long)result->length < num_aux_item_definitions); result->data[result->length] = def->tag; result->length += 1; } def = def->next; } return OK; } void aux_item_trigger_mark_text(Aux_item_trigger_data *data) { Text_no text_no; Text_stat *text_stat; String_size ill_char; text_no = s_strtol(data->item->data, &ill_char, 10); VOID_GET_T_STAT(text_stat, text_no); if (text_stat->no_of_marks < param.max_marks_text) text_stat->no_of_marks += 1; mark_text_as_changed(text_no); } void aux_item_trigger_unmark_text(Aux_item_trigger_data *data) { Text_no text_no; Text_stat *text_stat; String_size ill_char; text_no = s_strtol(data->item->data, &ill_char, 10); VOID_GET_T_STAT(text_stat, text_no); if (text_stat->no_of_marks > 0) text_stat->no_of_marks -= 1; mark_text_as_changed(text_no); } void aux_item_trigger_mirror_faq(Aux_item_trigger_data *data) { Text_no text_no; Aux_item item_data; char conf_no_string[40]; String_size ill_char, end_pos; String str; if (data->object_type != CONF_OBJECT_TYPE || data->object == NULL) return; /* Get the object number of where we want to put the new item */ text_no = s_strtol(data->item->data, &ill_char, 10); /* Create the string for the new item */ sprintf(conf_no_string, "%-40lu", (unsigned long)data->object_no); str = s_fcrea_str(conf_no_string); end_pos = s_strchr(str, ' ', 0); if (end_pos != -1) { s_strdel(&str, end_pos, s_strlen(str) - 1); } /* Fill in the new item */ init_aux_item(&item_data); item_data.tag = 28; /* Mirror of FAQ item */ item_data.data = str; /* Create the link */ aux_item_link_items(data->object_type, data->object_no, data->object, data->item, TEXT_OBJECT_TYPE, text_no, NULL, &item_data ); } /* * Generic link trigger. This is primarily for testing. * * The item must contain three fields, separated with a space character. * Field one is a character containing specifying the object type the * other end of the link is supposed to be in. If it is 'C', the link is * to a conference. If it is 'T', the link is to a text. If it is 'I', * the link is to the system info. * * The second field is the item tag to use for the other end of the link. * * The third field is the object id of the target object. For the * system info it is ignored but must be present. * * Some examples: * "C 99 123": Link to an item of type 99 in conference 123 * "I 92 9": Link to an item of type 92 in the system info * "T 22 191": Link to an item of type 22 in text 191 * * The created item will contain the same kind of information. */ void aux_item_trigger_link_item(Aux_item_trigger_data *data) { char object_no_string[100]; char *tmp_str; char type_char; Object_type target_object_type; unsigned long target_item_tag, target_object_no; int nread; Aux_item item_data; init_aux_item(&item_data); /* ---------------------------------------- * Find the data for the linked item * * The object type, object id and item tag * are in the item the trigger is called * for * * The string is given by the current item */ tmp_str = s_crea_c_str(data->item->data); nread = sscanf(tmp_str, "%c %lu %lu", &type_char, &target_item_tag, &target_object_no); string_free(tmp_str); if (nread != 3) return; switch (type_char) { case 'T': target_object_type = TEXT_OBJECT_TYPE; break; case 'C': target_object_type = CONF_OBJECT_TYPE; break; case 'I': target_object_type = INFO_OBJECT_TYPE; break; default: return; } /* Create the string for the linked item */ switch (data->object_type) { case TEXT_OBJECT_TYPE: type_char = 'T'; break; case CONF_OBJECT_TYPE: type_char = 'C'; break; case INFO_OBJECT_TYPE: type_char = 'I'; break; default: type_char = '?'; break; } sprintf(object_no_string, "%c %lu %lu", type_char, (unsigned long)data->item->tag, (unsigned long)data->object_no); /* * Create the mirror item in item_data * * Item tag is found above * Item data is found above * Item link target item is the item-no of the current item * Item link target object type is the object type in the trigger data * Item link target object no is the object no in the trigger data */ item_data.tag = target_item_tag; item_data.data = s_fcrea_str(object_no_string); aux_item_link_items(data->object_type, data->object_no, data->object, data->item, target_object_type, target_object_no, NULL, &item_data ); } /* * Aux item validators */ Bool aux_item_validate_existing_text(Aux_item_validation_data *v_data) { Text_stat *text_stat; Text_no text_no; String_size ill_char; text_no = s_strtol(v_data->item->data, &ill_char, 10); GET_T_STAT(text_stat, text_no, FALSE); return person_text_read_access(text_no, text_stat, v_data->item_creator, NULL); }