/* * $Id: aux-items.c,v 1.6 1998/11/09 22:31:09 ceder Exp $ * Copyright (C) 1994, 1995, 1996 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. */ #include #ifdef HAVE_STDLIB_H # include #endif #ifdef HAVE_STRING_H # include #endif #include #include #include "kom-types.h" #include "manipulate.h" #include "aux-items.h" #include "kom-errno.h" #include "kom-memory.h" #include "async.h" #include "com.h" #include "connections.h" #include "server/smalloc.h" #include "cache.h" #include "s-string.h" #include "lyskomd.h" #include "config.h" #include "regex.h" #include "log.h" #include "services.h" #define AUX_ADJUST_FLAG(flg) item->flags.flg = (def->clear_flags.flg)?0:((def->set_flags.flg)?1:item->flags.flg) Aux_item_definition *aux_item_definition_list = NULL; unsigned long num_aux_item_definitions = 0; static Aux_item_definition compiled_aux_items[] = { }; void aux_item_trigger_mark_text(Aux_item_trigger_data *); void aux_item_trigger_unmark_text(Aux_item_trigger_data *); static Aux_item_trigger_mapping aux_item_triggers [] = { { "mark-text", aux_item_trigger_mark_text }, { "unmark-text", aux_item_trigger_unmark_text }, { NULL, NULL } }; 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, /* Author only */ 0, /* Supervisor only */ 0, /* Unique */ 0, /* Can't delete */ 0, /* Inherit limit */ FALSE, /* Texts */ 0, FALSE, /* Confs */ 0, FALSE, /* Letterbox */ FALSE, /* System */ NULL, /* Validate */ NULL, /* Cached regexp */ 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, /* Author only */ 0, /* Supervisor only */ 0, /* Unique */ 0, /* Can't delete */ 0, /* Inherit limit */ TRUE, /* Texts */ 0, TRUE, /* Confs */ 0, TRUE, /* Letterbox */ TRUE, /* System */ NULL, /* Validate regexp */ NULL, /* Cached regexp */ 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) { unsigned long tmp; unsigned long extended; static Aux_item_definition def_s; /* 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; } /* * 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; } /* * 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 * ====================================================================== */ /* * 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 definitionn"); } *new_definition = *def; num_aux_item_definitions += 1; new_definition->next = aux_item_definition_list; aux_item_definition_list = new_definition; } static void aux_item_definition_cache_regexp(Aux_item_definition *def) { struct re_pattern_buffer *pat_buf; const char *errmsg; re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; def->cached_re_buf = smalloc(sizeof(*pat_buf)); if (def->cached_re_buf == NULL) { log("Out of memory compiling aux-item regexp %lu (%s).\n", def->tag, def->name); return; } pat_buf = def->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->validate_regexp, strlen(def->validate_regexp), pat_buf)) != NULL) { log("%s in validate regexp of aux-item definition %lu (%s).\n", errmsg, def->tag, def->name); if (def->cached_re_buf) sfree(def->cached_re_buf); def->cached_re_buf = NULL; return; } } void initialize_aux_items(char *aux_def_file) { unsigned long i; Aux_item_definition *def; /* 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); def = aux_item_definition_list; while (def != NULL) { if (def->validate_regexp != NULL) { aux_item_definition_cache_regexp(def); } def = def->next; } } /* TRIGGER THINGS */ static void aux_item_call_add_triggers(Aux_item_definition *def, short object_type, unsigned long item_index, unsigned long integer_argument, void * pointer_argument) { 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; for (i = 0; i < def->num_add_triggers; i++) { (*def->add_triggers[i])(&data); } } static void aux_item_call_delete_triggers(Aux_item_definition *def, short object_type, unsigned long item_index, unsigned long integer_argument, void * pointer_argument) { 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; 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, short object_type, unsigned long item_index, unsigned long integer_argument, void * pointer_argument) { 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; for (i = 0; i < def->num_undelete_triggers; i++) { (*def->undelete_triggers[i])(&data); } } /* * 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; } /* * 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, short object_type ) { short can_add_when = 0; def = def ? def : def = find_aux_item_definition(item); /* Can't create an item with no definition */ if (def == NULL) 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)))) { return FALSE; } } /* Check the author_only flag, which really is author-or-supervisor * How do we handle newly created confs, where we want this to * ALWAYS succeed? FIXME +++ */ 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))) { return FALSE; } /* Check the supervisor_only flag * Check using is_strictly_supervisor. * How do we handle newly created confs, where we want this to * ALWAYS succeed? FIXME +++ */ if (def->supervisor_only && owner_check && !is_strictly_supervisor(object_creator, NULL, item_creator, NULL) && !(item_creator == ACTPERS && ENA(wheel,8))) { return FALSE; } /* Even the administrator can't override this */ if (!aux_item_check_unique(item, def, add_to_list, start_looking_at)) return FALSE; /* Check the contents */ if (def->validate_regexp) { if (def->cached_re_buf == NULL) aux_item_definition_cache_regexp(def); if (def->cached_re_buf == NULL) return FALSE; switch ( re_search (def->cached_re_buf, item->data.string, s_strlen(item->data), 0, s_strlen(item->data), NULL) ) { case -1: return FALSE; case -2: log("Internal error in regex matching aux-item data."); break; default: /* Matched */ break; } } 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, short object_type, unsigned long object_no, void *object) { int i; Aux_item item; Aux_item_definition *def; 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]); prepare_aux_item(&item, parent->items[i].creator, def); if (item.inherit_limit != 0) item.inherit_limit -= 1; ADD_AUX_ITEM(*target, item, *counter); aux_item_call_add_triggers(def, object_type, target->length - 1, object_no, object); } } } /* * 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, Pers_no viewer, Person *viewer_p) { Aux_item *orig_aux; unsigned long from, to; 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, viewer_p) && !ENA(admin, 4) ) continue; result->items[to] = *orig_aux; if (orig_aux->flags.hide_creator && !is_supervisor(orig_aux->creator,NULL, viewer, viewer_p) && !ENA(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) { unsigned 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_ILL_AUX; 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)) || (item->creator != ACTPERS && !is_supervisor(item->creator, NULL, ACTPERS, NULL) && !ENA(wheel,8))) { 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. */ void delete_aux_item_list(Number_list *items_to_delete, Aux_item_list *list_to_delete_from, short object_type, unsigned long object_no, void *object) { unsigned long i; Aux_item *item; for (i = 0; i < items_to_delete->length; i++) { if (items_to_delete->data[i] == 0) continue; item = find_aux_item(list_to_delete_from, items_to_delete->data[i]); item->flags.deleted = 1; aux_item_call_delete_triggers( find_aux_item_definition(item), object_type, items_to_delete->data[i], object_no, object); } } void undelete_aux_item_list(Number_list *items_to_undelete, Aux_item_list *list_to_undelete_from, short object_type, unsigned long object_no, void *object) { unsigned long i; Aux_item *item; for (i = 0; i < items_to_undelete->length; i++) { if (items_to_undelete->data[i] == 0) continue; item = find_aux_item(list_to_undelete_from, items_to_undelete->data[i]); item->flags.deleted = 0; aux_item_call_delete_triggers(find_aux_item_definition(item), object_type, items_to_undelete->data[i], object_no, object); } } /* * 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) { unsigned long i; for (i = 0; i < list->length; i++) { if (list->items[i].aux_no == aux_no) return &list->items[i]; } return NULL; } /* ********************************************************************** * ********************************************************************** * * 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; 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_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) { unsigned long i; if (item_list == NULL) return; text_s->aux_item_list.items = srealloc(text_s->aux_item_list.items, (text_s->aux_item_list.length + item_list->length) * sizeof(Aux_item)); for (i = 0; i < item_list->length; i++) { text_s->highest_aux += 1; item_list->items[i].aux_no = text_s->highest_aux; item_list->items[i].creator = item_creator; item_list->items[i].sent_at = time(NULL); copy_aux_item( &text_s->aux_item_list.items[text_s->aux_item_list.length], &item_list->items[i]); text_s->aux_item_list.length += 1; aux_item_call_add_triggers( find_aux_item_definition(&item_list->items[i]), TEXT_OBJECT_TYPE, text_s->highest_aux, text_no, text_s); } } /* ================================================================== */ 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; 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) || !(def->confs || (def->letterboxes && conf->type.letter_box))) { 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) { unsigned long i; if (item_list == NULL) return; conf->aux_item_list.items = srealloc(conf->aux_item_list.items, (conf->aux_item_list.length + item_list->length) * sizeof(Aux_item)); for (i = 0; i < item_list->length; i++) { conf->highest_aux += 1; item_list->items[i].aux_no = conf->highest_aux; item_list->items[i].creator = item_creator; item_list->items[i].sent_at = time(NULL); copy_aux_item(&conf->aux_item_list.items[conf->aux_item_list.length], &item_list->items[i]); conf->aux_item_list.length += 1; aux_item_call_add_triggers( find_aux_item_definition(&item_list->items[i]), CONF_OBJECT_TYPE, conf->highest_aux, conf_no, conf); } } 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; 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, creator, creator, FALSE, &info->aux_item_list, 0, FALSE, OTHER_OBJECT_TYPE) || !aux_item_check_unique(item, def, list, i + 1) || !def->system) { 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) { unsigned long i; if (item_list == NULL) return; info->aux_item_list.items = srealloc(info->aux_item_list.items, (info->aux_item_list.length + item_list->length) * sizeof(Aux_item)); for (i = 0; i < item_list->length; i++) { info->highest_aux_no += 1; item_list->items[i].aux_no = info->highest_aux_no; item_list->items[i].creator = item_creator; item_list->items[i].sent_at = time(NULL); copy_aux_item(&info->aux_item_list.items[info->aux_item_list.length], &item_list->items[i]); info->aux_item_list.length += 1; } } 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(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) { if (data->object_type != TEXT_OBJECT_TYPE) return; } void aux_item_trigger_unmark_text(Aux_item_trigger_data *data) { if (data->object_type != TEXT_OBJECT_TYPE) return; }