/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: v4ops.c /main/5 1996/10/02 17:21:09 drk $ */ /* * (c) Copyright 1993, 1994 Hewlett-Packard Company * (c) Copyright 1993, 1994 International Business Machines Corp. * (c) Copyright 1993, 1994 Novell, Inc. * (c) Copyright 1993, 1994 Sun Microsystems, Inc. */ #include #include #include #include #include #include #include #include #if defined(CSRG_BASED) #if defined(__NetBSD__) #include #else #include #endif #define MAXINT INT_MAX #else #include #endif #ifdef SunOS #include #endif #include "v4ops.h" #include "rtable4.h" #include "cmscalendar.h" #include "tree.h" #include "list.h" #include "log.h" #include "appt4.h" /* Internal appointment data structure */ #include "reminder.h" #include "access.h" #include "laccess.h" #include "lookup.h" #include "cmsconvert.h" #include "cmsdata.h" #include "repeat.h" #include "misc.h" #include "utility.h" extern int debug; extern char *pgname; /***************************************************************************** * forward declaration of static functions used within the file *****************************************************************************/ static Exception_4 append_exception_list(Appt_4 *p_appt, int ordinal); static void trunc_exception_list(Appt_4 *p_appt, int ordinal); static int num_exception(Appt_4 *p_appt); /***************************************************************************** * extern functions used in the library *****************************************************************************/ /* * Insert one appointment into the single appointment tree structure * or the repeat appointment list structure. * The passed in appointment is stored in the structure. */ extern CSA_return_code _DtCmsInsertAppt(_DtCmsCalendar *cal, Appt_4 *appt4) { CSA_return_code stat; Rb_Status rb_stat; time_t current_time; Attr_4 p_attr; if (cal == NULL || appt4 == NULL) return (CSA_E_INVALID_PARAMETER); /* assign key if this is a new appointment */ _DtCmsGenerateKey(cal, &(appt4->appt_id.key)); if (debug) { fprintf(stderr, "Insert appt4: (%ld)%s\n", appt4->appt_id.key, ctime(&(appt4->appt_id.tick))); } /* Add the appointment into the data structure */ if (appt4->period.period == single_4) rb_stat = rb_insert (cal->tree, (caddr_t)appt4, (caddr_t)&(appt4->appt_id)); else rb_stat = hc_insert (REPT_LIST(cal), (caddr_t)appt4, (caddr_t)&(appt4->appt_id), NULL, NULL); if (rb_stat == rb_ok) { /* Add the qualified reminder attrs to the reminder queue */ current_time = time(0); p_attr = appt4->attr; while (p_attr != NULL) { time_t tick; Rm_que *p_reminder; tick = appt4->appt_id.tick - atol(p_attr->value); p_reminder = build_reminder (current_time, appt4, p_attr, tick, 1); if (p_reminder != NULL) _DtCmsAddReminderV4(&cal->rm_queue, p_reminder); p_attr = p_attr->next; } } return (_DtCmsRbToCsaStat(rb_stat)); } /* * If p_auth is null, the initiator is the owner of the calendar. Permission * to delete any appointment is always granted. If p_auth is not null, we need * to check if it matches the author of the deleting appointment. Only the * author can delete his/her appointment. */ extern CSA_return_code _DtCmsDeleteAppt( _DtCmsCalendar *cal, char *user, uint access, Id_4 *p_key, Appt_4 **appt_r) { Appt_4 *p_appt; List_node *p_lnode; Tree_node *p_node; /* * Before we delete an event from the single appointment tree, we * need to check if the initiator is the author of the appointment. */ if ((user != NULL) && (p_appt = (Appt_4 *)rb_lookup(APPT_TREE(cal), (caddr_t)p_key)) != NULL) { if (!(access & (access_delete_4|CSA_OWNER_RIGHTS)) && !_DtCmIsSameUser(user, p_appt->author)) return (CSA_E_NO_AUTHORITY); } if ((p_node = rb_delete (APPT_TREE(cal), (caddr_t)p_key)) != NULL) { p_appt = (Appt_4*)p_node->data; _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, B_FALSE); free (p_node); if (debug) { fprintf (stderr, "Deleted (%ld)%s\n", APPT_KEY(p_appt), ctime(&(p_appt->appt_id.tick))); } if (appt_r != NULL) *appt_r = p_appt; else _DtCm_free_appt4(p_appt); return (CSA_SUCCESS); } /* Attempt to delete the event from the repeating appointment list */ p_lnode = (List_node *)hc_lookup_node(REPT_LIST(cal), (caddr_t)p_key); if (p_lnode != NULL) { p_appt = (Appt_4 *)p_lnode->data; if (user != NULL && !(access & (access_delete_4|CSA_OWNER_RIGHTS)) && !_DtCmIsSameUser(user, p_appt->author)) return (CSA_E_NO_AUTHORITY); _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, B_FALSE); (void) hc_delete_node (REPT_LIST(cal), p_lnode); free (p_lnode); if (debug) { fprintf (stderr, "Deleted (%ld)%s\n", APPT_KEY(p_appt), ctime(&(p_appt->appt_id.tick))); } if (appt_r != NULL) *appt_r = p_appt; else _DtCm_free_appt4(p_appt); return (CSA_SUCCESS); } return (CSA_E_USER_NOT_FOUND | CSA_E_INVALID_ENTRY_HANDLE); } extern CSA_return_code _DtCmsDeleteApptAndLog( _DtCmsCalendar *cal, char *user, uint access, Id_4 *key, Appt_4 **oldappt) { CSA_return_code stat; Appt_4 *appt; if ((stat = _DtCmsDeleteAppt(cal, user, access, key, &appt)) == CSA_SUCCESS) { /* Transact the log */ if ((stat = v4_transact_log(cal->calendar, appt, _DtCmsLogRemove)) != CSA_SUCCESS) { (void)_DtCmsInsertAppt(cal, appt); } else if (oldappt) { *oldappt = appt; } else { _DtCm_free_appt4(appt); } } return (stat); } extern CSA_return_code _DtCmsDeleteApptInstancesAndLog( _DtCmsCalendar *cal, char *source, uint access, Id_4 *key, Options_4 option, int *remain, Appt_4 **oldappt) { List_node *p_lnode; CSA_return_code stat = CSA_SUCCESS; Appt_4 *p_appt, *oldcopy; int ordinal; int f, file_size, ntimes, ninstance, status = 0; struct stat info; p_lnode = (List_node *)hc_lookup_node (REPT_LIST(cal), (caddr_t)key); if (p_lnode == NULL) return (CSA_X_DT_E_ENTRY_NOT_FOUND); p_appt = (Appt_4*)p_lnode->data; if (!(access & (access_delete_4 | CSA_OWNER_RIGHTS)) && !_DtCmIsSameUser(source, p_appt->author)) return (CSA_E_NO_AUTHORITY); if ((ordinal = _DtCms_in_repeater (key, p_appt, B_TRUE)) == 0) return (CSA_X_DT_E_ENTRY_NOT_FOUND); if (debug) fprintf(stderr,"Delete instance: Ordinal=%d\n",ordinal); /* * save file size in case the first log transaction * succeeds but the second log transaction fails. */ if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size)) != CSA_SUCCESS) return (stat); /* remove from log */ if ((stat = v4_transact_log(cal->calendar, p_appt, _DtCmsLogRemove)) != 0) { return (stat); } ninstance = _DtCms_get_ninstance_v4(p_appt); /* calculate the ntimes value for the part * of the sequence that start from the ordinal to the end */ if (remain != NULL) *remain = _DtCms_get_new_ntimes_v4(p_appt->period, key->tick, (ninstance - ordinal + 1)); /* * make a copy of the original appointment, * in case we need a rollback */ if ((oldcopy = _DtCm_copy_one_appt4(p_appt)) == NULL) { stat = CSA_E_INSUFFICIENT_MEMORY; goto delete_cleanup; } /* remove from memory */ if (option == do_one_4) { if (!append_exception_list (p_appt, ordinal)) { stat = CSA_E_INSUFFICIENT_MEMORY; goto delete_cleanup; } } else { ninstance = ordinal - 1; if (ninstance == 1) { /* convert to one-time event */ p_appt->period.period = single_4; p_appt->period.nth = 0; p_appt->period.enddate = 0; p_appt->ntimes = 0; if (p_appt->exception) { _DtCm_free_excpt4(p_appt->exception); p_appt->exception = NULL; } stat = _DtCmsRbToCsaStat(rb_insert(cal->tree, (caddr_t)p_appt, (caddr_t)&(p_appt->appt_id))); } else { p_appt->ntimes = _DtCms_get_new_ntimes_v4( p_appt->period, p_appt->appt_id.tick, ninstance); /* update enddate just for M-F, MWF, TTh * and weekdaycombo */ switch (p_appt->period.period) { case monThruFri_4: case monWedFri_4: case tueThur_4: case daysOfWeek_4: p_appt->period.enddate = _DtCms_prev_tick_v4( key->tick, p_appt->period); } trunc_exception_list(p_appt, ordinal); } } /* The last one from the series has been deleted, no more left. */ if (ninstance == num_exception (p_appt)) ordinal = 0; if (ordinal == 0) { /* Obsolete the reminders which match the ordinal */ _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, ordinal, (option == do_one_4 ? B_FALSE : B_TRUE)); _DtCm_free_appt4(p_appt); hc_delete_node (REPT_LIST(cal), p_lnode); free (p_lnode); } else { /* Write out the series with new exception list. */ if (stat || (stat = v4_transact_log(cal->calendar, p_appt, _DtCmsLogAdd)) != CSA_SUCCESS) { /* reverse memory update */ p_appt->ntimes = oldcopy->ntimes; p_appt->period = oldcopy->period; _DtCm_free_excpt4(p_appt->exception); p_appt->exception = oldcopy->exception; oldcopy->exception = NULL; goto delete_cleanup; } if (ninstance == 1) { time_t current_time; Attr_4 p_attr; _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, (option == do_one_4 ? B_FALSE : B_TRUE)); hc_delete_node(REPT_LIST(cal), p_lnode); free(p_lnode); /* Add the qualified reminder attrs to * the reminder queue */ current_time = time(0); p_attr = p_appt->attr; while (p_attr != NULL) { time_t tick; Rm_que *p_reminder; tick = p_appt->appt_id.tick-atol(p_attr->value); p_reminder = build_reminder(current_time, p_appt, p_attr, tick, 1); if (p_reminder != NULL) _DtCmsAddReminderV4(&cal->rm_queue, p_reminder); p_attr = p_attr->next; } } else { _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, ordinal, (option == do_one_4 ? B_FALSE : B_TRUE)); } } if (oldappt) *oldappt = oldcopy; else _DtCm_free_appt4(oldcopy); return (CSA_SUCCESS); delete_cleanup: /* rollback log file */ _DtCmsTruncateFile(cal->calendar, file_size); if (oldcopy) _DtCm_free_appt4(oldcopy); return (stat); } extern CSA_return_code _DtCmsChangeAll( _DtCmsCalendar *cal, char *source, uint access, Id_4 *p_key, Appt_4 *newa, Appt_4 **oldappt) { CSA_return_code stat = CSA_SUCCESS; Appt_4 *olda, *newcopy = NULL; int ordinal; int file_size; if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size)) != CSA_SUCCESS) return (stat); /* * first, remove the old appointment from internal data structure * and the callog file */ if ((stat = _DtCmsDeleteApptAndLog(cal, source, access, p_key, &olda)) != CSA_SUCCESS) return (stat); if (is_repeater(olda) && is_repeater(newa)) { if (_DtCmsBeginOfDay(APPT_TICK(newa)) == _DtCmsBeginOfDay(p_key->tick) && olda->period.period == newa->period.period) { /* keep the start day of the original * appointment if the date of the * key matches that of the new * appointment and the interval * is not changed */ APPT_TICK(newa) -= _DtCmsBeginOfDay(APPT_TICK(newa)) - _DtCmsBeginOfDay(APPT_TICK(olda)); } else { /* otherwise, calculate new parent */ if ((ordinal = _DtCms_in_repeater(p_key, olda, B_TRUE)) == 0) { stat = CSA_X_DT_E_ENTRY_NOT_FOUND; goto change_all_cleanup; } else { APPT_TICK(newa) = _DtCms_first_tick_v4( APPT_TICK(newa), newa->period, ordinal); /* if enddate exist, adjust it */ if (olda->period.enddate && newa->period.period == olda->period.period && newa->period.nth == olda->period.nth) { newa->period.enddate += (_DtCmsBeginOfDay(APPT_TICK(newa)) - _DtCmsBeginOfDay(APPT_TICK(olda))); } } } /* ????? */ /* We use the same exception list for the * new appt. ?? is this reasonable for * all cases ?? */ newa->exception = _DtCm_copy_excpt4(olda->exception); } /* adjust start date */ _DtCms_adjust_appt_startdate(newa); /* make copy of new appointment */ if ((newcopy = _DtCm_copy_one_appt4(newa)) == NULL) { stat = CSA_E_INSUFFICIENT_MEMORY; goto change_all_cleanup; } /* reuse the key */ newcopy->appt_id.key = olda->appt_id.key; /* keep the original author */ free(newcopy->author); if ((newcopy->author = strdup(olda->author)) == NULL) { stat = CSA_E_INSUFFICIENT_MEMORY; goto change_all_cleanup; } if ((stat = _DtCmsInsertApptAndLog(cal, newcopy)) != CSA_SUCCESS) { goto change_all_cleanup; } newa->appt_id.key = newcopy->appt_id.key; if (oldappt) *oldappt = olda; else _DtCm_free_appt4(olda); return (stat); change_all_cleanup: if (newcopy) _DtCm_free_appt4(newcopy); _DtCmsTruncateFile(cal->calendar, file_size); (void)_DtCmsInsertAppt(cal, olda); return (stat); } extern CSA_return_code _DtCmsChangeSome( _DtCmsCalendar *cal, char *source, uint access, Id_4 *p_key, Appt_4 *newa, Options_4 option, Appt_4 **oldappt) { CSA_return_code stat; Appt_4 *olda, *newcopy = NULL; List_node *lnode; Exception_4 *newexcept; int file_size; int remain; if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size)) != CSA_SUCCESS) return (stat); /* * first, remove the old appointment from internal data structure * and the callog file */ if ((stat = _DtCmsDeleteApptInstancesAndLog(cal, source, access, p_key, option, &remain, &olda)) != CSA_SUCCESS) return (stat); /* adjust start date */ _DtCms_adjust_appt_startdate(newa); /* make copy of new appointment */ newcopy = _DtCm_copy_one_appt4(newa); if (option == do_forward_4) { /* if repeating info is not changed, replace * the deleted part with the new series, i.e., * ntimes of new series _DtCmsIsEquals to the number * of deleted instances. Also get new * exception list. */ if (olda->period.period == newa->period.period && olda->period.nth == newa->period.nth && olda->ntimes == newa->ntimes) { newcopy->ntimes = remain; if (olda->exception != NULL) { _DtCm_free_excpt4(newcopy->exception); if ((stat = _DtCmsTruncateElist(olda, remain, &newcopy->exception)) != CSA_SUCCESS) { goto change_some_cleanup; } } } } /* keep the original author */ free(newcopy->author); if ((newcopy->author = strdup(olda->author)) == NULL) { stat = CSA_E_INSUFFICIENT_MEMORY; goto change_some_cleanup; } APPT_KEY(newcopy) = 0; if ((stat = _DtCmsInsertApptAndLog(cal, newcopy)) != CSA_SUCCESS) { goto change_some_cleanup; } else newa->appt_id.key = newcopy->appt_id.key; if (oldappt) *oldappt = olda; else _DtCm_free_appt4(olda); return (stat); change_some_cleanup: if (newcopy) _DtCm_free_appt4(newcopy); _DtCmsTruncateFile(cal->calendar, file_size); (void)_DtCmsInsertAppt(cal, olda); return (stat); } /* * the passed in appt is stored in the internal data structure */ extern CSA_return_code _DtCmsInsertApptAndLog(_DtCmsCalendar *cal, Appt_4 *appt) { CSA_return_code stat; if ((stat = _DtCmsInsertAppt(cal, appt)) == CSA_SUCCESS) { /* Add the new appointment to the log */ if ((stat = v4_transact_log(cal->calendar, appt, _DtCmsLogAdd)) != CSA_SUCCESS) { (void)_DtCmsDeleteAppt(cal, NULL, 0, &appt->appt_id, &appt); } } return (stat); } extern _DtCmsComparisonResult _DtCmsCompareAppt(Id_4 *key, caddr_t data) { Appt_4 *appt = (Appt_4 *)data; /* Composite key */ if (key->tick < appt->appt_id.tick) return (_DtCmsIsLess); if (key->tick > appt->appt_id.tick) return (_DtCmsIsGreater); /* tick's are _DtCmsIsEqual */ if (key->key < appt->appt_id.key) return (_DtCmsIsLess); if (key->key > appt->appt_id.key) return (_DtCmsIsGreater); return (_DtCmsIsEqual); } extern _DtCmsComparisonResult _DtCmsCompareRptAppt(Id_4 *key, caddr_t data) { Appt_4 *appt = (Appt_4 *)data; if (key->key < appt->appt_id.key) return (_DtCmsIsLess); if (key->key > appt->appt_id.key) return (_DtCmsIsGreater); return (_DtCmsIsEqual); } extern caddr_t _DtCmsGetApptKey (caddr_t data) { return ((caddr_t) &(((Appt_4 *) data)->appt_id)); } extern CSA_return_code v4_transact_log(char *calendar, Appt_4 *a, _DtCmsLogOps op) { CSA_return_code stat; char *log=NULL; if ((log = _DtCmsGetLogFN(calendar)) == NULL) return (CSA_E_INSUFFICIENT_MEMORY); else { stat = _DtCmsAppendAppt4ByFN(log, a, op); free(log); } return(stat); } extern CSA_return_code _DtCmsLookupRangeV4( _DtCmsCalendar *cal, char *user, uint access, Range_4 *p_range, boolean_t no_end_time_range, long end1, long end2, boolean_t (*match_func)(), uint num_attrs, cms_attribute *attrs, CSA_enum *ops, Appt_4 **appt_r, Abb_Appt_4 **abbr_r) { CSA_return_code stat = CSA_SUCCESS; Period_4 period; caddr_t ilp = NULL; int tmp_tick, endtick; Id_4 lo_key; int n; List_node *p_lnode; time_t hi_tick, lo_tick; int tick; int ordinal; int ntimes; Appt_4 *p_appt; cms_entry *entries; CSA_return_code (*add_list_func)(); /* do lookup on new format calendar */ if (cal->fversion > 1) { if ((stat = _DtCmsLookupEntries(cal, user, access, p_range->key1, p_range->key2, no_end_time_range, end1, end2, num_attrs, attrs, ops, &entries)) != CSA_SUCCESS) { return (stat); } if (appt_r) stat = _DtCmsCmsentriesToAppt4ForClient(entries, appt_r); else stat = _DtCmsCmsentriesToAbbrAppt4ForClient(entries, abbr_r); _DtCm_free_cms_entries(entries); return (stat); } if (appt_r) add_list_func = _AddToLinkedAppts; else add_list_func = _AddToLinkedAbbrAppts; while (p_range != NULL) { lo_key.key = MAXINT; lo_key.tick = p_range->key1; hi_tick = p_range->key2; if (debug) { fprintf(stderr,"Range lookup from %s", ctime(&lo_key.tick)); fprintf(stderr, " to %s\n", ctime(&hi_tick)); } n = 0; /* Get a range of appointments in single appointment tree */ while ((p_appt = (Appt_4 *)rb_lookup_next_larger( APPT_TREE(cal),(caddr_t)&lo_key)) && (APPT_TICK(p_appt) < hi_tick)) { n++; endtick = p_appt->appt_id.tick + p_appt->duration; if ((!no_end_time_range && (p_appt->duration == 0 || endtick <= end1 || endtick >= end2)) || (match_func && !match_func(p_appt, num_attrs, attrs, ops))) { lo_key.key = APPT_KEY(p_appt); lo_key.tick = APPT_TICK(p_appt); continue; } if ((stat = (*add_list_func)(p_appt, user, access,&ilp)) != CSA_SUCCESS) { if (ilp != NULL) { if (appt_r) _DtCm_free_appt4((Appt_4 *)ilp); else _DtCm_free_abbrev_appt4( (Abb_Appt_4 *)ilp); ilp = NULL; } break; } lo_key.key = APPT_KEY(p_appt); lo_key.tick = APPT_TICK(p_appt); } /* Get a range of events from repeating appointment list */ p_lnode = REPT_LIST(cal)->root; while (p_lnode != NULL) { lo_tick = p_range->key1; hi_tick = p_range->key2; p_appt = (Appt_4*)p_lnode->data; /* calculate the last tick */ if (p_lnode->lasttick == 0) p_lnode->lasttick = _DtCms_last_tick_v4( APPT_TICK(p_appt), p_appt->period, p_appt->ntimes); if (p_lnode->lasttick <= lo_tick || APPT_TICK(p_appt) >= hi_tick || (!no_end_time_range && p_appt->duration == 0) || (!no_end_time_range && (p_lnode->lasttick+p_appt->duration) <= end1) || (!no_end_time_range && (p_appt->appt_id.tick+p_appt->duration) >= end2) || (match_func && !match_func(p_appt, num_attrs, attrs, ops)) || _GetAccessLevel(user, access, p_appt) == private_4) { p_lnode = hc_lookup_next (p_lnode); continue; } if (!no_end_time_range && lo_tick < (end1 - p_appt->duration)) lo_tick = end1 - p_appt->duration; if (!no_end_time_range && ((end2 - p_appt->duration) < hi_tick)) hi_tick = end2 - p_appt->duration; ntimes = _DtCms_get_ninstance_v4(p_appt); period = p_appt->period; for (tick = _DtCms_closest_tick_v4(lo_tick, APPT_TICK(p_appt), period, &ordinal), ordinal--; tick < hi_tick; tick = _DtCms_next_tick_v4(tick, period)) { /* Repeater but beyond the scope */ if (++ordinal > ntimes || tick > p_lnode->lasttick) break; if (tick <= lo_tick || _DtCms_marked_4_cancellation(p_appt, ordinal)) continue; n++; /* Replace the parent key by the * current tick for the repeating event */ tmp_tick = APPT_TICK(p_appt); APPT_TICK(p_appt) = tick; /* Add to list, restore parent key */ stat = (*add_list_func)(p_appt, user, access, &ilp); APPT_TICK(p_appt) = tmp_tick; if (stat != CSA_SUCCESS) { if (ilp != NULL) { if (appt_r) _DtCm_free_appt4( (Appt_4 *)ilp); else _DtCm_free_abbrev_appt4( (Abb_Appt_4 *)ilp); ilp = NULL; } break; } } p_lnode = hc_lookup_next (p_lnode); } p_range = p_range->next; } if (debug) fprintf (stderr, "Found %d entries in range lookup\n", n); if (stat == CSA_SUCCESS) { if (appt_r) *appt_r = (Appt_4 *)ilp; else *abbr_r = (Abb_Appt_4 *)ilp; } return (stat); } extern CSA_return_code _DtCmsLookupKeyrangeV4( _DtCmsCalendar *cal, char *user, uint access, boolean_t no_start_time_range, boolean_t no_end_time_range, time_t start1, time_t start2, time_t end1, time_t end2, long id, boolean_t (*match_func)(), uint num_attrs, cms_attribute *attrs, CSA_enum *ops, Appt_4 **appt_r, Abb_Appt_4 **abbr_r) { CSA_return_code stat = CSA_SUCCESS; Period_4 period; long tmp_tick, endtick; Id_4 lo_key; int n = 0; List_node *p_lnode; int tick; int ordinal; int ntimes; Appt_4 *p_appt, *tappt; Abb_Appt_4 *tabbr; cms_entry *entries; CSA_return_code (*add_list_func)(); /* do lookup on new format calendar */ if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) { if ((stat = _DtCmsLookupEntriesById(cal, user, access, no_start_time_range, no_end_time_range, start1, start2, end1, end2, id, num_attrs, attrs, ops, &entries)) != CSA_SUCCESS) { return (stat); } if (appt_r) { stat = _DtCmsCmsentriesToAppt4ForClient(entries, &tappt); *appt_r = _AddApptInOrder(*appt_r, tappt); } else { stat = _DtCmsCmsentriesToAbbrAppt4ForClient(entries, &tabbr); *abbr_r = _AddAbbApptInOrder(*abbr_r, tabbr); } _DtCm_free_cms_entries(entries); return (stat); } if (appt_r) add_list_func = _AddToLinkedAppts; else add_list_func = _AddToLinkedAbbrAppts; lo_key.key = id; lo_key.tick = start1; /* Search from repeating appointments first for optimization */ p_lnode = hc_lookup_node (REPT_LIST(cal), (caddr_t)&lo_key); if (p_lnode != NULL) { p_appt = (Appt_4*)p_lnode->data; if ((match_func && !match_func(p_appt, num_attrs, attrs, ops)) || _GetAccessLevel(user, access, p_appt) == private_4) return (CSA_SUCCESS); /* just return the first event */ if (no_start_time_range && no_end_time_range) return ((*add_list_func)(p_appt, user, access, (appt_r ? (caddr_t)appt_r : (caddr_t)abbr_r))); /* Get the range of events from this appointment. */ ntimes = _DtCms_get_ninstance_v4(p_appt); period = p_appt->period; /* calculate the last tick */ if (p_lnode->lasttick == 0) p_lnode->lasttick = _DtCms_last_tick_v4( APPT_TICK(p_appt), p_appt->period, p_appt->ntimes); if (p_lnode->lasttick <= start1 || p_appt->appt_id.tick >= start2 || (!no_end_time_range && p_appt->duration == 0) || (!no_end_time_range && (p_lnode->lasttick+p_appt->duration) <= end1) || (!no_end_time_range && (p_appt->appt_id.tick+p_appt->duration) >= end2)) return (SUCCESS); if (!no_end_time_range && (start1 < (end1 - p_appt->duration))) start1 = end1 - p_appt->duration; if (!no_end_time_range && ((end2 - p_appt->duration) < start2)) start2 = end2 - p_appt->duration; for (tick = _DtCms_closest_tick_v4(start1, APPT_TICK(p_appt), period, &ordinal), ordinal--; stat == CSA_SUCCESS && tick < start2; tick = _DtCms_next_tick_v4(tick, period)) { /* Repeater but beyond the scope */ if (++ordinal > ntimes || tick > p_lnode->lasttick) break; if (tick <= start1) continue; /* If not cancelled, add to linked list */ if (!_DtCms_marked_4_cancellation (p_appt, ordinal)) { n++; /* Replace the parent key by * the current tick for the * repeating event */ tmp_tick = APPT_TICK(p_appt); APPT_TICK(p_appt) = tick; /* Add to list, restore parent key */ stat = (*add_list_func)(p_appt, user, access, (appt_r ? (caddr_t)appt_r : (caddr_t)abbr_r)); APPT_TICK(p_appt) = tmp_tick; } } return (stat); } else { /* Check if it is in single appointment tree */ while ((p_appt = (Appt_4 *)rb_lookup_next_larger( APPT_TREE(cal), (caddr_t) &lo_key)) && (APPT_TICK(p_appt) < start2)) { if (p_appt->appt_id.key != lo_key.key) { lo_key.tick = APPT_TICK(p_appt); lo_key.key = APPT_KEY(p_appt); } else { endtick = p_appt->appt_id.tick+p_appt->duration; if ((!no_end_time_range && (p_appt->duration == 0 || endtick <= end1 || endtick >= end2)) || (match_func && !match_func(p_appt, num_attrs, attrs, ops))) { return (CSA_SUCCESS); } else { return ((*add_list_func)(p_appt, user, access, (appt_r ? (caddr_t)appt_r : (caddr_t)abbr_r))); } } } return (CSA_SUCCESS); } } extern CSA_return_code _AddToLinkedAppts( Appt_4 *p_appt, char *user, uint access, caddr_t *ilp) { Appt_4 *p_prev; Appt_4 *copy; switch (_GetAccessLevel(user, access, p_appt)) { case public_4: copy = _DtCm_copy_one_appt4(p_appt); break; case semiprivate_4: copy = _DtCm_copy_semiprivate_appt4(p_appt); break; default: return (CSA_SUCCESS); } if (copy == NULL) return(CSA_E_INSUFFICIENT_MEMORY); else { *ilp = (caddr_t)_AddApptInOrder((Appt_4 *)*ilp, copy); return (CSA_SUCCESS); } } extern CSA_return_code _AddToLinkedAbbrAppts( Appt_4 *p_appt, char *user, uint access, caddr_t *ilp) { Abb_Appt_4 *copy; switch (_GetAccessLevel(user, access, p_appt)) { case public_4: copy = _DtCm_appt_to_abbrev4(p_appt); break; case semiprivate_4: copy = _DtCm_appt_to_semiprivate_abbrev4(p_appt); break; default: return (CSA_SUCCESS); } if (copy == NULL) return(CSA_E_INSUFFICIENT_MEMORY); else { *ilp = (caddr_t)_AddAbbApptInOrder((Abb_Appt_4 *)*ilp, copy); return (CSA_SUCCESS); } } extern Privacy_Level_4 _GetAccessLevel(char *user, uint access, Appt_4 *p_appt) { if (access & CSA_OWNER_RIGHTS || ((access & access_read_4) && p_appt->privacy == public_4) || _DtCmIsSameUser(user, p_appt->author)) { return (public_4); } else if (p_appt->privacy == private_4) { return (private_4); } else return (semiprivate_4); } extern Appt_4 * _AddApptInOrder(Appt_4 *head, Appt_4 *aptr) { Appt_4 *p_appt, *p_prev, *p_next; while (aptr) { p_next = aptr->next; aptr->next = NULL; /* Add the item to the linked list in ascending order */ p_prev = NULL; p_appt = head; while (p_appt != NULL) { if (APPT_TICK(aptr) <= APPT_TICK(p_appt)) break; p_prev = p_appt; p_appt = p_appt->next; } if (p_prev == NULL) { aptr->next = p_appt; head = aptr; } else { aptr->next = p_prev->next; p_prev->next = aptr; } aptr = p_next; } return (head); } extern Abb_Appt_4 * _AddAbbApptInOrder(Abb_Appt_4 *head, Abb_Appt_4 *aptr) { Abb_Appt_4 *p_appt, *p_prev, *p_next; while (aptr) { p_next = aptr->next; aptr->next = NULL; /* Add the item to the linked list in ascending order */ p_prev = NULL; p_appt = head; while (p_appt != NULL) { if (APPT_TICK(aptr) <= APPT_TICK(p_appt)) break; p_prev = p_appt; p_appt = p_appt->next; } if (p_prev == NULL) { aptr->next = p_appt; head = aptr; } else { aptr->next = p_prev->next; p_prev->next = aptr; } aptr = p_next; } return (head); } extern CSA_return_code _DtCmsSetV4AccessListAndLog(_DtCmsCalendar *cal, Access_Entry_4 *alist) { CSA_return_code stat; char *name, *log; int file_size; /* update access list for old format calendar */ if ((name = _DtCmsTarget2Name(cal->calendar)) == NULL) return (CSA_E_INSUFFICIENT_MEMORY); if ((log = _DtCmsGetLogFN(name)) == NULL) { free(name); return (CSA_E_INSUFFICIENT_MEMORY); } /* * save file size in case the first log transaction * succeeds but the second log transaction fails. */ if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size)) != CSA_SUCCESS) { free (name); free (log); return (stat); } /* Set to the data structure */ if ((stat = _DtCmsSetV4AccessListInCal(cal, alist)) == CSA_SUCCESS) { if (((stat = _DtCmsAppendAccessByFN(log, access_read_4, GET_R_ACCESS(cal))) != CSA_SUCCESS) || ((stat = _DtCmsAppendAccessByFN(log, access_write_4, GET_W_ACCESS(cal))) != CSA_SUCCESS) || ((stat = _DtCmsAppendAccessByFN(log, access_delete_4, GET_D_ACCESS(cal))) != CSA_SUCCESS) || ((stat = _DtCmsAppendAccessByFN(log, access_exec_4, GET_X_ACCESS(cal))) != CSA_SUCCESS)) { /* rollback log file */ _DtCmsTruncateFile(cal->calendar, file_size); } else cal->modified = B_TRUE; } free (name); free(log); return (stat); } extern CSA_return_code _DtCmsGetV4Reminders( _DtCmsCalendar *cal, long tick, Reminder_4 **rem_r, _DtCmsEntryId **ids_r) { Rm_que *p_node, *p_prev, *p_next, *p_new; Reminder_4 *rptr, *v4rem = NULL; _DtCmsEntryId *idptr, *ids = NULL; if (rem_r == NULL) return (CSA_E_INVALID_PARAMETER); /* do lookup in old format calendar */ p_prev = NULL; p_node = cal->rm_queue; while (p_node != NULL) { /* It is still a future reminder. */ if (tick < p_node->remind_at) break; /* The reminder expired. It either needs to be recalculated * (repeating appointment) or dropped (non-repeating appt.) */ p_next = _DtCmsRemoveReminderV4(&cal->rm_queue, p_prev, p_node); if (is_repeater(p_node->appt)) { /* Calculate next reminder for repeating appointment */ p_new = build_reminder(tick+1, p_node->appt, p_node->attr, p_node->remind_at, p_node->remind_ord); if (p_new != NULL) { _DtCmsAddReminderV4(&cal->rm_queue, p_new); if (p_new->next == p_next) p_prev = p_new; } } free (p_node); p_node = p_next; } /* Pick the first one from the active reminder queue because it is * always >= the given key. */ p_node = cal->rm_queue; if (p_node != NULL) { tick = p_node->remind_at; do { rptr = _DtCmsGetReminderInfoV4(p_node); rptr->next = v4rem; v4rem = rptr; if (ids_r) { idptr = (_DtCmsEntryId *)calloc(1, sizeof(_DtCmsEntryId)); idptr->id = p_node->appt->appt_id.key; idptr->next = ids; ids = idptr; } p_node = p_node->next; } while ((p_node != NULL) && (p_node->remind_at == tick)); } *rem_r = v4rem; if (ids_r) *ids_r = ids; return (CSA_SUCCESS); } extern void _DtCmsFreeEntryIds(_DtCmsEntryId *ids) { _DtCmsEntryId *ptr; while (ids != NULL) { ptr = ids->next; free(ids); ids = ptr; } } extern CSA_return_code _DtCmsTruncateElist(Appt_4 *parent_p, int remain, Except_4 **excpt) { int except_no, ntimes_diff; Except_4 *e, *p, *last_e = NULL, *head = NULL; ntimes_diff = _DtCms_get_ninstance_v4(parent_p) - remain; p = parent_p->exception; while(p != NULL) { if ((except_no = (p->ordinal - ntimes_diff)) > 0 && except_no <= remain) { if ((e = (Except_4*)calloc(1, sizeof(Except_4))) == NULL) { _DtCm_free_excpt4(head); return (CSA_E_INSUFFICIENT_MEMORY); } e->ordinal = except_no; e->next = NULL; if (last_e != NULL) last_e->next = e; else head = e; last_e = e; } p = p->next; } *excpt = head; return (CSA_SUCCESS); } extern CSA_return_code _DtCmsSetV4AccessListInCal(_DtCmsCalendar *cal, Access_Entry_4 *e) { Access_Entry_4 *q; /* Wipe out the old access lists. */ _DtCm_free_access_list4(GET_R_ACCESS (cal)); _DtCm_free_access_list4(GET_W_ACCESS (cal)); _DtCm_free_access_list4(GET_D_ACCESS (cal)); _DtCm_free_access_list4(GET_X_ACCESS (cal)); SET_R_ACCESS(cal, NULL); SET_W_ACCESS(cal, NULL); SET_D_ACCESS(cal, NULL); SET_X_ACCESS(cal, NULL); _DtCm_free_access_list4(cal->alist); /* Split the access list to 3 differnt operation lists */ while (e != NULL) { if (e->access_type & access_read_4) { q = _DtCm_make_access_entry4(e->who, e->access_type); q->next = GET_R_ACCESS(cal); SET_R_ACCESS(cal, q); } if (e->access_type & access_write_4) { q = _DtCm_make_access_entry4(e->who, e->access_type); q->next = GET_W_ACCESS(cal); SET_W_ACCESS(cal, q); } if (e->access_type & access_delete_4) { q = _DtCm_make_access_entry4(e->who, e->access_type); q->next = GET_D_ACCESS(cal); SET_D_ACCESS(cal, q); } if (e->access_type & access_exec_4) { q = _DtCm_make_access_entry4(e->who, e->access_type); q->next = GET_X_ACCESS(cal); SET_X_ACCESS(cal, q); } e = e->next; } cal->alist = _DtCmsCalendarAccessList(cal); return (CSA_SUCCESS); } /****************************************************************************** * static functions used within the file ******************************************************************************/ static Exception_4 append_exception_list(Appt_4 *p_appt, int ordinal) { Exception_4 p_excpt; Exception_4 p_prev; Exception_4 p_ex; if ((p_excpt = (Exception_4)calloc(1, sizeof(*p_excpt))) == NULL) return (NULL); p_excpt->ordinal = ordinal; p_prev = NULL; p_ex = p_appt->exception; while (p_ex != NULL) { /* Exception list is in descending order for faster access */ if (ordinal > p_ex->ordinal) break; p_prev = p_ex; p_ex = p_ex->next; } if (p_prev == NULL) { p_excpt->next = p_appt->exception; p_appt->exception = p_excpt; } else { p_excpt->next = p_prev->next; p_prev->next = p_excpt; } return (p_excpt); } /* * remove exceptions that are larger than ordinal */ static void trunc_exception_list(Appt_4 *p_appt, int ordinal) { Exception_4 p_next; Exception_4 p_ex; p_ex = p_appt->exception; /* Exception list is in descending order for faster access */ while ((p_ex != NULL) && (p_ex->ordinal > ordinal)) { p_next = p_ex->next; free(p_ex); p_ex = p_next; } p_appt->exception = p_ex; } static int num_exception(Appt_4 *p_appt) { int ntimes; Except_4 *p; ntimes = 0; p = p_appt->exception; while (p != NULL) { ntimes++; p = p->next; } return (ntimes); }