cdesktopenv/cde/programs/dtcm/server/update.c

614 lines
16 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 libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/* $XConsortium: update.c /main/4 1995/11/09 12:53:47 rswiston $ */
/*
* (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 <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <pwd.h>
#include <time.h>
#if !defined(CSRG_BASED)
#include <values.h>
#endif
#ifdef SunOS
#include <sys/systeminfo.h>
#endif
#include "cmscalendar.h"
#include "update.h"
#include "cm.h"
#include "attr.h"
#include "updateattrs.h"
#include "cmsdata.h"
#include "cmsentry.h"
#include "access.h"
#include "repeat.h"
#include "delete.h"
#include "insert.h"
#include "log.h"
#include "v5ops.h"
#include "iso8601.h"
#include "rerule.h"
#include "reutil.h"
extern char *_DtCm_rule_buf; /* buffer to hold a rule for parser */
extern RepeatEvent *_DtCm_repeat_info; /* parsed recurrence info */
extern boolean_t RulesMatch(char *rule1, char *rule2);
/******************************************************************************
* forward declaration of static functions used within the file
******************************************************************************/
static boolean_t _SameRecurrenceRule(
cms_attribute_value *newval,
cms_attribute_value *oldval);
static CSA_return_code _SetNewStartDate(
cms_entry *olde,
RepeatEvent *oldre,
cms_entry *newe,
RepeatEvent *newre,
cms_key *key);
static CSA_return_code _AdjustStartEndTimeForUpdateInst(
cms_entry *newe,
cms_entry *olde,
cms_key *key,
uint num_attrs,
cms_attribute *attrs);
static CSA_return_code _AdjustStartEndTimeForUpdateEntry(
List_node *lnode,
cms_entry *newe,
cms_key *key,
uint num_attrs,
cms_attribute *attrs);
static void _GetStartEndIndex(
uint num_attrs,
cms_attribute *attrs,
int *starti,
int *endi);
static void _AdjustExceptionDates(cms_entry *entry, time_t delta);
static int _NumberExceptionDates(cms_entry *entry);
/*****************************************************************************
* extern functions used in the library
*****************************************************************************/
extern CSA_return_code
_DtCmsUpdateEntry(
_DtCmsCalendar *cal,
char *sender,
uint access,
cms_key *key,
uint num_attrs,
cms_attribute *attrs,
cms_entry **oldentry,
cms_entry **newentry)
{
CSA_return_code stat;
cms_entry *olde, *newe;
List_node *lnode = NULL;
int file_size;
/* get the entry from the tree */
if ((olde = (cms_entry *)rb_lookup(cal->tree, (caddr_t)key)) == NULL) {
/* find entry in the repeating entry list */
if ((lnode = hc_lookup_node(cal->list, (caddr_t)key)) == NULL)
return (CSA_X_DT_E_ENTRY_NOT_FOUND);
else
olde = (cms_entry *)lnode->data;
}
if (olde == NULL)
return (CSA_X_DT_E_ENTRY_NOT_FOUND);
/* check access rights */
if ((stat = _DtCmsCheckChangeAccess(sender, access, olde))
!= CSA_SUCCESS)
return (stat);
/* copy the entry and apply updates */
if ((stat = _DtCm_copy_cms_entry(olde, &newe)) != CSA_SUCCESS)
return (stat);
if ((stat = _DtCmUpdateAttributes(num_attrs, attrs, &newe->num_attrs,
&newe->attrs, &cal->entry_tbl, B_FALSE, &cal->types, B_FALSE))
!= CSA_SUCCESS) {
_DtCm_free_cms_entry(newe);
return (stat);
}
/* update start date */
_csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].value->\
item.date_time_value, &newe->key.time);
if (lnode != NULL && (stat = _AdjustStartEndTimeForUpdateEntry(
lnode, newe, key, num_attrs, attrs)) != CSA_SUCCESS) {
_DtCm_free_cms_entry(newe);
return (stat);
}
/* make sure end time is not earlier than start time */
if ((stat = _DtCmsCheckStartEndTime(newe)) != CSA_SUCCESS) {
_DtCm_free_cms_entry(newe);
return (stat);
}
/* set last update */
if ((stat = _DtCmsSetLastUpdate(newe)) != CSA_SUCCESS) {
_DtCm_free_cms_entry(newe);
return (stat);
}
/* save file size in case we need to roll back */
if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
!= CSA_SUCCESS) {
_DtCm_free_cms_entry(newe);
return (stat);
}
/* remove old entry */
if ((stat = _DtCmsDeleteEntryAndLog(cal, NULL, 0, key, &olde))
!= CSA_SUCCESS){
_DtCm_free_cms_entry(newe);
return (stat);
}
/* insert new entry */
if ((stat = _DtCmsInsertEntryAndLog(cal, newe)) != CSA_SUCCESS) {
_DtCmsTruncateFile(cal->calendar, file_size);
_DtCmsInsertEntry(cal, olde);
_DtCm_free_cms_entry(newe);
_DtCm_free_cms_entry(olde);
} else {
if (newentry)
*newentry = newe;
else
_DtCm_free_cms_entry(newe);
if (oldentry)
*oldentry = olde;
else
_DtCm_free_cms_entry(olde);
}
return (stat);
}
extern CSA_return_code
_DtCmsUpdateInstances(
_DtCmsCalendar *cal,
char *sender,
uint access,
cms_key *key,
int scope,
uint num_attrs,
cms_attribute *attrs,
cms_entry **oldentry,
cms_entry **newentry)
{
CSA_return_code stat;
cms_entry *olde, *newe = NULL, *updatedold;
List_node *lnode = NULL;
int file_size;
int i, remain, rulei;
cms_attribute_value *oaptr, *naptr;
/* save file size in case we need to roll back */
if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
!= CSA_SUCCESS)
return (stat);
/* remove old entry */
if ((stat = _DtCmsDeleteInstancesAndLog(cal, sender, access, key, scope,
&updatedold, &olde))
!= CSA_SUCCESS)
return (stat);
/* copy the entry and apply updates */
if ((stat = _DtCm_copy_cms_entry(olde, &newe)) != CSA_SUCCESS)
goto _cleanup;
if ((stat = _DtCmUpdateAttributes(num_attrs, attrs, &newe->num_attrs,
&newe->attrs, &cal->entry_tbl, B_FALSE, &cal->types, B_FALSE))
!= CSA_SUCCESS)
goto _cleanup;
/* check recurrence rule */
for (i = 0, rulei = -1; i < num_attrs; i++) {
if (attrs[i].name.num == CSA_ENTRY_ATTR_RECURRENCE_RULE_I) {
rulei = i;
break;
}
}
if (scope == CSA_SCOPE_ONE) {
if (rulei == -1) {
_DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
[CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].value);
_DtCmUpdateStringAttrVal(NULL, &newe->attrs\
[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value);
_DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
[CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I].value);
_DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
[CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I].value);
_DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
[CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I].value);
_DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
[CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I].\
value);
_DtCmUpdateStringAttrVal(NULL, &newe->attrs\
[CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I].value);
}
} else {
if (rulei == -1 ||
_SameRecurrenceRule(attrs[rulei].value, olde->\
attrs[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value)) {
/*
* if recurrence info is not changed, replace
* the deleted part with the new one, i.e.,
* duration of new equals to the number of
* deleted instances of the old one.
* Also, update the exception list.
*/
if (newe->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value)
{
_DtCmsCleanupExceptionDates(newe, key->time);
}
oaptr = olde->attrs\
[CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].value;
naptr = (updatedold == NULL) ? NULL :
updatedold->attrs[\
CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].\
value;
if (oaptr->item.uint32_value == 0)
remain = 0;
else
remain = oaptr->item.uint32_value -
(naptr ? naptr->item.uint32_value : 0) +
_DtCmsNumberExceptionDates(newe);
if ((stat = _DtCmsUpdateDurationInRule(newe, remain))
!= CSA_SUCCESS)
goto _cleanup;
}
}
if ((stat = _AdjustStartEndTimeForUpdateInst(newe, olde, key,
num_attrs, attrs)) != CSA_SUCCESS)
goto _cleanup;
/* set last update */
if ((stat = _DtCmsSetLastUpdate(newe)) != CSA_SUCCESS)
goto _cleanup;
/* insert new entry */
newe->key.id = 0;
if ((stat = _DtCmsInsertEntryAndLog(cal, newe)) != CSA_SUCCESS) {
goto _cleanup;
}
if (newentry)
*newentry = newe;
else
_DtCm_free_cms_entry(newe);
if (oldentry)
*oldentry = olde;
else
_DtCm_free_cms_entry(olde);
if (updatedold) _DtCm_free_cms_entry(updatedold);
return (stat);
_cleanup:
_DtCmsTruncateFile(cal->calendar, file_size);
if (updatedold == NULL)
_DtCmsInsertEntry(cal, olde);
else {
_DtCm_free_cms_entry(updatedold);
if (lnode = hc_lookup_node(cal->list, (caddr_t)key)) {
updatedold = (cms_entry *)lnode->data;
lnode->data = (caddr_t)olde;
olde = updatedold;
}
}
_DtCm_free_cms_entry(olde);
if (newe) _DtCm_free_cms_entry(newe);
return (stat);
}
/*****************************************************************************
* static functions used within the file
*****************************************************************************/
static boolean_t
_SameRecurrenceRule(cms_attribute_value *newval, cms_attribute_value *oldval)
{
if (newval == NULL || newval->item.string_value == NULL)
return (B_FALSE);
if (strcmp(newval->item.string_value, oldval->item.string_value))
return (B_FALSE);
else
return (B_TRUE);
}
static CSA_return_code
_AdjustStartEndTimeForUpdateInst(
cms_entry *newe,
cms_entry *olde,
cms_key *key,
uint num_attrs,
cms_attribute *attrs)
{
CSA_return_code stat;
time_t oldbod, newbod, endtime, delta;
int i, starti, endi;
/* update start date */
_GetStartEndIndex(num_attrs, attrs, &starti, &endi);
oldbod = _DtCmsBeginOfDay(olde->key.time);
if (starti >= 0 && endi == -1 &&
newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value) {
/* adjust end date */
_csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].\
value->item.date_time_value, &newe->key.time);
newbod = _DtCmsBeginOfDay(newe->key.time);
_csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].\
value->item.date_time_value, &endtime);
endtime += (newbod - oldbod);
_csa_tick_to_iso8601(endtime, newe->attrs\
[CSA_ENTRY_ATTR_END_DATE_I].value->item.\
date_time_value);
} else if (starti == -1 && endi >= 0) {
/* ajust start date */
if (newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value) {
_csa_iso8601_to_tick(newe->attrs\
[CSA_ENTRY_ATTR_END_DATE_I].value->\
item.date_time_value, &endtime);
newbod = _DtCmsBeginOfDay(endtime);
} else
newbod = _DtCmsBeginOfDay(key->time);
newe->key.time += (newbod - oldbod);
_csa_tick_to_iso8601(newe->key.time, newe->attrs\
[CSA_ENTRY_ATTR_START_DATE_I].value->item.\
date_time_value);
} else if (starti == -1 && endi == -1) {
/* adjust both start and end date */
newe->key.time = key->time;
_csa_tick_to_iso8601(key->time, newe->attrs[\
CSA_ENTRY_ATTR_START_DATE_I].value->item.\
date_time_value);
if (newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value) {
newbod = _DtCmsBeginOfDay(newe->key.time);
_csa_iso8601_to_tick(newe->attrs\
[CSA_ENTRY_ATTR_END_DATE_I].value->\
item.date_time_value, &endtime);
endtime += (newbod - oldbod);
_csa_tick_to_iso8601(endtime, newe->attrs\
[CSA_ENTRY_ATTR_END_DATE_I].value->\
item.date_time_value);
}
} else {
_csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].\
value->item.date_time_value, &newe->key.time);
}
if ((stat = _DtCmsCheckStartEndTime(newe)) != CSA_SUCCESS) {
return (stat);
}
if ((delta = _DtCmsTimeOfDay(newe->key.time) -
_DtCmsTimeOfDay(key->time)) != 0)
_AdjustExceptionDates(newe, delta);
return (CSA_SUCCESS);
}
static CSA_return_code
_SetNewStartDate(
cms_entry *olde,
RepeatEvent *oldre,
cms_entry *newe,
RepeatEvent *newre,
cms_key *key)
{
if ((newe->key.time = DeriveNewStartTime(olde->key.time, oldre,
key->time, newe->key.time, newre)) == 0)
return (CSA_E_FAILURE);
if (_csa_tick_to_iso8601(newe->key.time, newe->attrs\
[CSA_ENTRY_ATTR_START_DATE_I].value->item.date_time_value))
return (CSA_E_FAILURE);
return (CSA_SUCCESS);
}
/*
* The assumption is that there's only one instance per day.
* When we support more than one instance per day, this
* needs to be updated.
*/
static void
_AdjustExceptionDates(cms_entry *entry, time_t delta)
{
time_t tick;
CSA_date_time_list dt, head;
if (entry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value == NULL ||
entry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->item.\
date_time_list_value == NULL)
return;
head = entry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->item.\
date_time_list_value;
for (dt = head; dt != NULL; dt = dt->next) {
_csa_iso8601_to_tick(dt->date_time, &tick);
tick += delta;
_csa_tick_to_iso8601(tick, dt->date_time);
}
}
static void
_GetStartEndIndex(
uint num_attrs,
cms_attribute *attrs,
int *starti,
int *endi)
{
int i;
for (i = 0, *starti = -1, *endi = -1; i < num_attrs; i++) {
if (attrs[i].name.num == CSA_ENTRY_ATTR_START_DATE_I)
*starti = i;
else if (attrs[i].name.num == CSA_ENTRY_ATTR_END_DATE_I)
*endi = i;
}
}
static CSA_return_code
_AdjustStartEndTimeForUpdateEntry(
List_node *lnode,
cms_entry *newe,
cms_key *key,
uint num_attrs,
cms_attribute *attrs)
{
CSA_return_code stat;
cms_entry *olde = (cms_entry *)lnode->data;
time_t newbod, instbod, fstbod, newfstbod, endtime;
int starti, endi, enddelta = 0;
cms_attribute_value *oldaptr, *newaptr, *endaptr;
RepeatEvent *newre;
extern void _DtCm_rule_parser();
/* check to make sure repeating type is not changed */
oldaptr = olde->attrs[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value;
newaptr = newe->attrs[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value;
endaptr = newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value;
if (endaptr)
_csa_iso8601_to_tick(endaptr->item.date_time_value, &endtime);
if (key->time != olde->key.time &&
(newaptr == NULL || newaptr->item.string_value == NULL ||
RulesMatch(oldaptr->item.string_value, newaptr->item.string_value)
== B_FALSE)) {
return (CSA_E_INVALID_ATTRIBUTE_VALUE);
}
_GetStartEndIndex(num_attrs, attrs, &starti, &endi);
newbod = _DtCmsBeginOfDay(newe->key.time);
instbod = _DtCmsBeginOfDay(key->time);
fstbod = _DtCmsBeginOfDay(olde->key.time);
if (starti >= 0 && key->time != olde->key.time) {
if (newbod == instbod) {
/* keep the same start day */
newe->key.time -= (newbod - fstbod);
_csa_tick_to_iso8601(newe->key.time,
newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].\
value->item.date_time_value);
if (endi >= 0)
enddelta = newbod - fstbod;
} else {
/* parse the rule */
_DtCm_rule_buf = newaptr->item.string_value;
_DtCm_rule_parser();
if ((newre = _DtCm_repeat_info) == NULL)
return (CSA_E_INVALID_RULE);
/* get new start date */
if ((stat = _SetNewStartDate(olde, lnode->re,
newe, newre, key)) != CSA_SUCCESS) {
_DtCm_free_re(newre);
return (stat);
}
_DtCm_free_re(newre);
newfstbod = _DtCmsBeginOfDay(newe->key.time);
if (endi < 0)
enddelta = fstbod - newfstbod;
else
enddelta = newbod - newfstbod;
}
} else if (starti >= 0 && endi < 0 && newbod != fstbod) {
enddelta = fstbod - newbod;
} else if (starti < 0 && endi >= 0 && key->time != olde->key.time &&
endaptr)
{
enddelta = _DtCmsBeginOfDay(endtime) - fstbod;
}
/* fix end date */
if (enddelta && endaptr) {
endtime -= enddelta;
_csa_tick_to_iso8601(endtime, endaptr->item.date_time_value);
}
return (CSA_SUCCESS);
}