1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/programs/dtcm/server/v4ops.c
Ulrich Wilkens 2712d5f787 NetBSD port
2014-02-22 19:12:19 -07:00

1493 lines
35 KiB
C

/*
* 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 librararies 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 <EUSCompat.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <time.h>
#if defined(CSRG_BASED)
#if defined(__NetBSD__)
#include <limits.h>
#else
#include <sys/limits.h>
#endif
#define MAXINT INT_MAX
#else
#include <values.h>
#endif
#ifdef SunOS
#include <sys/systeminfo.h>
#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;
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);
}