/* * 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 #include #include #include #include #include #include #include #include #include #if !defined(CSRG_BASED) #include #endif #ifdef SunOS #include #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); }