Files
bongo/src/libs/cal/bongo-cal-instance.c
T

1142 lines
28 KiB
C

/****************************************************************************
* <Novell-copyright>
* Copyright (c) 2006 Novell, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you
* may find current contact information at www.novell.com.
* </Novell-copyright>
****************************************************************************/
#include <config.h>
#include "bongo-cal-private.h"
#include <bongocal.h>
#include <libical/ical.h>
typedef struct {
BongoCalRecurFrequency freq;
const char *name;
} FreqNames;
static FreqNames freqNames[] = {
{ BONGO_CAL_RECUR_NONE, "" },
{ BONGO_CAL_RECUR_SECONDLY, "secondly" },
{ BONGO_CAL_RECUR_MINUTELY, "minutely" },
{ BONGO_CAL_RECUR_HOURLY, "hourly" },
{ BONGO_CAL_RECUR_DAILY, "daily" },
{ BONGO_CAL_RECUR_WEEKLY, "weekly" },
{ BONGO_CAL_RECUR_MONTHLY, "monthly" },
{ BONGO_CAL_RECUR_YEARLY, "yearly" },
};
static const char *dayNames[] = {
"MO", "TU", "WE", "TH", "FR", "SA", "SU"
};
static BongoCalTimezone *
TimezoneCb(const char *tzid, void *data)
{
BongoCalInstance *inst = data;
return BongoCalObjectGetTimezone(inst->cal, tzid);
}
/* Helper object for interpreting json calendar objects. Uses some
* helpers from libical, but libical data types shouldn't be exported */
BongoCalInstance *
BongoCalInstanceNew(BongoCalObject *obj, BongoJsonObject *json)
{
BongoCalInstance *inst;
inst = MemMalloc0(sizeof(BongoCalInstance));
BongoCalInstanceInit(inst, obj, json);
return inst;
}
void
BongoCalInstanceInit(BongoCalInstance *inst, BongoCalObject *obj, BongoJsonObject *json)
{
inst->cal = obj;
if (json == NULL) {
json = BongoJsonObjectNew();
BongoJsonObjectPutString(json, "type", "event");
}
inst->json = json;
}
void
BongoCalInstanceFree(BongoCalInstance *inst, BOOL freeJson)
{
BongoCalInstanceClear(inst, freeJson);
MemFree(inst);
}
#if 0
static void
FreeInstanceArray(GArray *instances, BOOL freeJson)
{
unsigned int i;
if (!instances) {
return;
}
for (i = 0; i < instances->len; i++) {
BongoCalInstanceFree(g_array_index(instances, BongoCalInstance*, i), freeJson);
}
g_array_free(instances, TRUE);
}
#endif
static void
FreeRRule(BongoCalRule *r)
{
BongoListFree (r->bymonth);
BongoListFree (r->byweekno);
BongoListFree (r->byyearday);
BongoListFree (r->bymonthday);
BongoListFree (r->byday);
BongoListFree (r->byhour);
BongoListFree (r->byminute);
BongoListFree (r->bysecond);
BongoListFree (r->bysetpos);
MemFree (r);
}
static void
FreeRRules(BongoSList *rules)
{
BongoSList *l;
for (l = rules; l != NULL; l = l->next) {
FreeRRule(l->data);
}
BongoSListFree(rules);
}
void
BongoCalInstanceClear(BongoCalInstance *inst, BOOL freeJson)
{
if (freeJson) {
BongoJsonObjectFree(inst->json);
}
BongoSListFreeDeep(inst->rdates);
BongoSListFreeDeep(inst->exdates);
FreeRRules(inst->rrules);
FreeRRules(inst->exrules);
}
static void
ReadTime(BongoCalInstance *inst, BongoJsonObject *obj, BongoCalTime *time)
{
const char *value;
BongoJsonObject *params;
if (BongoJsonObjectGetString(obj, "value", &value) != BONGO_JSON_OK) {
return;
}
*time = BongoCalTimeParseIcal(value);
if (BongoJsonObjectGetObject(obj, "params", &params) != BONGO_JSON_OK) {
return;
}
if (BongoJsonObjectGetString(params, "tzid", &value) == BONGO_JSON_OK) {
BongoCalTimezone *tz;
tz = BongoCalObjectGetTimezone(inst->cal, value);
if (tz) {
*time = BongoCalTimeSetTimezone(*time, tz);
} else {
*time = BongoCalTimeSetTzid(*time, value);
}
} else {
*time = BongoCalTimeSetTimezone(*time, BongoCalTimezoneGetUtc());
}
if (BongoJsonObjectGetString(params, "type", &value) == BONGO_JSON_OK) {
if (!XplStrCaseCmp(value, "DATE")) {
time->isDate = TRUE;
} else if (!XplStrCaseCmp(value, "DATE-TIME")) {
time->isDate = FALSE;
}
}
}
static BongoJsonObject *
WriteTime(BongoCalTime t)
{
BongoJsonObject *obj;
char buffer[BONGO_CAL_TIME_BUFSIZE];
BongoJsonObject *params;
BOOL haveParams;
obj = BongoJsonObjectNew();
params = BongoJsonObjectNew();
haveParams = FALSE;
if (!t.isUtc && t.tzid) {
BongoJsonObjectPutString(params, "tzid", t.tzid);
haveParams = TRUE;
}
if (t.isDate) {
BongoJsonObjectPutString(params, "type", "DATE");
haveParams = TRUE;
}
if (haveParams) {
BongoJsonObjectPutObject(obj, "params", params);
} else {
BongoJsonObjectFree(params);
}
BongoCalTimeToIcal(t, buffer, sizeof(buffer));
BongoJsonObjectPutString(obj, "value", buffer);
return obj;
}
static BongoJsonObject *
WriteLocalizedTime(BongoCalTime t, BongoCalTimezone *tz)
{
BongoJsonObject *obj;
obj = WriteTime(t);
if (tz) {
char buffer[BONGO_CAL_TIME_BUFSIZE];
t = BongoCalTimezoneConvertTime(t, tz);
BongoCalTimeToIcal(t, buffer, sizeof(buffer));
BongoJsonObjectPutString(obj, "valueLocal", buffer);
}
return obj;
}
static BOOL
ReadPeriod(BongoJsonObject *obj, BongoCalPeriod *period)
{
const char *value;
BongoJsonObject *jsonValue;
if (BongoJsonObjectGetObject(obj, "value", &jsonValue) != BONGO_JSON_OK) {
return FALSE;
}
if (BongoJsonObjectGetString(jsonValue, "start", &value) != BONGO_JSON_OK) {
return FALSE;
}
period->start = BongoCalTimeParseIcal(value);
if (BongoJsonObjectGetString(jsonValue, "end", &value) == BONGO_JSON_OK) {
period->value.end = BongoCalTimeParseIcal(value);
period->type = BONGO_CAL_PERIOD_DATETIME;
} else if (BongoJsonObjectGetString(jsonValue, "duration", &value) == BONGO_JSON_OK) {
period->value.duration = BongoCalDurationParseIcal(value);
period->type = BONGO_CAL_PERIOD_DURATION;
} else {
return FALSE;
}
return TRUE;
}
static BongoCalRecurFrequency
FindFrequency(const char *name)
{
unsigned int i;
for (i = 0; i < sizeof(freqNames) / sizeof(FreqNames); i++) {
if (!XplStrCaseCmp(freqNames[i].name, name)) {
return freqNames[i].freq;
}
}
return BONGO_CAL_RECUR_NONE;
}
static int
GetWeekday(const char *name)
{
int i;
for (i = 0; i < 7; i++) {
if (!XplStrCaseCmp(dayNames[i], name)) {
return i;
}
}
return -1;
}
static BongoList *
IntArrayToList(GArray *array, int min, int max)
{
BongoList *ret = NULL;
unsigned int i;
for (i = 0; i < array->len; i++) {
int intVal;
if (BongoJsonArrayGetInt(array, i, &intVal) == BONGO_JSON_OK) {
if (intVal >= min && intVal <= max) {
ret = BongoListPrepend(ret, BongoListIntToVoid(intVal));
}
}
}
return BongoListReverse(ret);
}
static BongoList *
DayArrayToList(GArray *array)
{
BongoList *ret = NULL;
unsigned int i;
for (i = 0; i < array->len; i++) {
const char *stringVal;
if (BongoJsonArrayGetString(array, i, &stringVal) == BONGO_JSON_OK) {
int ord;
int day;
char *end;
ord = strtol(stringVal, &end, 10);
day = GetWeekday(end);
if (ord >= -53 && ord <= 53) {
ret = BongoListPrepend(ret, BongoListIntToVoid(day));
ret = BongoListPrepend(ret, BongoListIntToVoid(ord));
}
}
}
return BongoListReverse(ret);
}
static BOOL
ReadRecur(BongoJsonObject *obj, BongoCalRule *recur)
{
GArray *arrayVal;
const char *stringVal;
int intVal;
BongoJsonObject *jsonValue;
BongoJsonObject *jsonParams;
if (BongoJsonObjectGetObject(obj, "value", &jsonValue) != BONGO_JSON_OK) {
return FALSE;
}
BongoJsonObjectGetObject(obj, "params", &jsonParams);
memset(recur, 0, sizeof(BongoCalRule));
recur->json = obj;
if (BongoJsonObjectGetString(jsonValue, "freq", &stringVal) != BONGO_JSON_OK) {
return FALSE;
}
recur->freq = FindFrequency(stringVal);
if (BongoJsonObjectGetInt(jsonValue, "interval", &intVal) == BONGO_JSON_OK) {
recur->interval = intVal;
}
if (recur->interval == 0) {
recur->interval = 1;
}
if (BongoJsonObjectGetString(jsonValue, "until", &stringVal) == BONGO_JSON_OK) {
BongoCalTime t = BongoCalTimeParseIcal(stringVal);
recur->until = BongoCalTimeAsUint64(t);
} else if (BongoJsonObjectGetInt(jsonValue, "count", &intVal) == BONGO_JSON_OK) {
recur->count = intVal;
if (jsonParams && (BongoJsonObjectGetString(jsonParams, "x-bongo-until", &stringVal) == BONGO_JSON_OK)) {
BongoCalTime t = BongoCalTimeParseIcal(stringVal);
recur->until = BongoCalTimeAsUint64(t);
}
}
if (BongoJsonObjectGetString(jsonValue, "weekstart", &stringVal) == BONGO_JSON_OK) {
recur->weekStartDay = GetWeekday(stringVal);
if (recur->weekStartDay == -1) {
return FALSE;
}
}
if (BongoJsonObjectGetArray(jsonValue, "bymonth", &arrayVal) == BONGO_JSON_OK) {
BongoList *l;
recur->bymonth = IntArrayToList(arrayVal, 1, 12);
for (l = recur->bymonth; l != NULL; l = l->next) {
l->data--;
}
}
if (BongoJsonObjectGetArray(jsonValue, "byweekno", &arrayVal) == BONGO_JSON_OK) {
recur->byweekno = IntArrayToList(arrayVal, -53, 53);
}
if (BongoJsonObjectGetArray(jsonValue, "byyearday", &arrayVal) == BONGO_JSON_OK) {
recur->byyearday = IntArrayToList(arrayVal, -366, 366);
}
if (BongoJsonObjectGetArray(jsonValue, "bymonthday", &arrayVal) == BONGO_JSON_OK) {
recur->bymonthday = IntArrayToList(arrayVal, 1, 31);
}
if (BongoJsonObjectGetArray(jsonValue, "byhour", &arrayVal) == BONGO_JSON_OK) {
recur->byhour = IntArrayToList(arrayVal, 0, 23);
}
if (BongoJsonObjectGetArray(jsonValue, "byminute", &arrayVal) == BONGO_JSON_OK) {
recur->byminute = IntArrayToList(arrayVal, 0, 59);
}
if (BongoJsonObjectGetArray(jsonValue, "bysecond", &arrayVal) == BONGO_JSON_OK) {
recur->bysecond = IntArrayToList(arrayVal, 0, 60);
}
if (BongoJsonObjectGetArray(jsonValue, "bysetpos", &arrayVal) == BONGO_JSON_OK) {
recur->bysetpos = IntArrayToList(arrayVal, INT_MIN, INT_MAX);
}
if (BongoJsonObjectGetArray(jsonValue, "byday", &arrayVal) == BONGO_JSON_OK) {
recur->byday = DayArrayToList(arrayVal);
}
return TRUE;
}
static void
CacheTime(BongoCalInstance *inst, const char *id, BongoCalTime *time)
{
BongoJsonObject *child;
if (!BongoCalTimeIsEmpty(*time)) {
return;
}
if (BongoJsonObjectGetObject(inst->json, id, &child) != BONGO_JSON_OK) {
return;
}
ReadTime(inst, child, time);
}
static void
CacheDuration(BongoCalInstance *inst, const char *id, BongoCalDuration *duration)
{
BongoJsonObject *child;
const char *value;
if (!BongoCalDurationIsEmpty(*duration)) {
return;
}
if (BongoJsonObjectGetObject(inst->json, id, &child) != BONGO_JSON_OK) {
return;
}
if (BongoJsonObjectGetString(child, "value", &value) != BONGO_JSON_OK) {
return;
}
*duration = BongoCalDurationParseIcal(value);
}
static void
SetTime(BongoCalInstance *inst, const char *id, BongoCalTime *cache, BongoCalTime t)
{
BongoJsonObject *obj;
obj = WriteTime(t);
BongoJsonObjectPutObject(inst->json, id, obj);
*cache = t;
}
static void
CacheString(BongoCalInstance *inst, const char *id, const char **cache)
{
BongoJsonObject *child;
if (*cache) {
return;
}
if (BongoJsonObjectGetObject(inst->json, id, &child) != BONGO_JSON_OK) {
return;
}
BongoJsonObjectGetString(child, "value", cache);
}
static void
SetString(BongoCalInstance *inst, const char *id, const char **cache, const char *value)
{
BongoJsonObject *obj;
obj = BongoJsonObjectNew();
BongoJsonObjectPutString(obj, "value", value);
BongoJsonObjectGetString(obj, "value", cache);
BongoJsonObjectPutObject(inst->json, id, obj);
}
#if 0
static void
CacheInt(BongoCalInstance *inst, const char *id, BOOL *haveCache, int *cache)
{
BongoJsonObject *child;
if (*haveCache) {
return;
}
if (BongoJsonObjectGetObject(inst->json, id, &child) != BONGO_JSON_OK) {
return;
}
if (BongoJsonObjectGetInt(child, "value", cache) == BONGO_JSON_OK) {
*haveCache = TRUE;
} else {
*cache = 0;
}
}
#endif
static void
CacheUtcOffset(BongoCalInstance *inst, const char *id, BOOL *haveCache, int *cache)
{
BongoJsonObject *child;
const char *str;
if (*haveCache) {
return;
}
if (BongoJsonObjectGetObject(inst->json, id, &child) != BONGO_JSON_OK) {
return;
}
if (BongoJsonObjectGetString(child, "value", &str) == BONGO_JSON_OK) {
int t;
int hours;
int minutes;
int seconds;
*haveCache = TRUE;
t = strtol(str, 0, 10);
if (abs(t) < 9999) {
t *= 100;
}
hours = (t / 10000);
minutes = (t - hours * 10000) / 100;
seconds = (t - hours * 10000 - minutes * 100);
*cache = hours * 3600 + minutes * 60 + seconds;
} else {
*cache = 0;
}
}
static void
CacheRDates(BongoCalInstance *inst, const char *key, BongoSList **dates)
{
GArray *array;
unsigned int i;
if (*dates) {
return;
}
if (BongoJsonObjectGetArray(inst->json, key, &array) != BONGO_JSON_OK) {
return;
}
for (i = 0; i < array->len; i++) {
BongoJsonObject *obj;
if (BongoJsonArrayGetObject(array, i, &obj) == BONGO_JSON_OK) {
BongoCalRDate *rdate;
const char *type;
BOOL period = FALSE;
if (BongoJsonObjectGetString(obj, "type", &type) == BONGO_JSON_OK) {
if (!XplStrCaseCmp(type, "period")) {
period = TRUE;
}
}
rdate = MemNew0(BongoCalRDate, 1);
if (period) {
rdate->type = BONGO_CAL_RDATE_PERIOD;
ReadPeriod(obj, &rdate->value.period);
} else {
rdate->type = BONGO_CAL_RDATE_DATETIME;
ReadTime(inst, obj, &rdate->value.time);
}
*dates = BongoSListPrepend(*dates, rdate);
}
}
*dates = BongoSListReverse(*dates);
}
static void
CacheRRules(BongoCalInstance *inst, const char *key, BongoSList **rules)
{
GArray *array;
unsigned int i;
if (*rules) {
return;
}
if (BongoJsonObjectGetArray(inst->json, key, &array) != BONGO_JSON_OK) {
return;
}
for (i = 0; i < array->len; i++) {
BongoJsonObject *obj;
if (BongoJsonArrayGetObject(array, i, &obj) == BONGO_JSON_OK) {
BongoCalRule *r;
r = MemNew0(BongoCalRule, 1);
ReadRecur(obj, r);
*rules = BongoSListPrepend(*rules, r);
}
}
*rules = BongoSListReverse(*rules);
}
static void
EnsureUid(BongoCalInstance *inst)
{
CacheString(inst, "uid", &inst->uid);
if (!inst->uid) {
SetString(inst, "uid", &inst->uid, "NO UID DEFINED");
}
}
BongoCalType
BongoCalInstanceGetType(BongoCalInstance *instance)
{
return BongoCalTypeFromJson(instance->json);
}
BongoJsonObject *
BongoCalInstanceGetJson(BongoCalInstance *inst)
{
return inst->json;
}
const char *
BongoCalInstanceGetUid(BongoCalInstance *inst)
{
EnsureUid(inst);
return inst->uid;
}
void
BongoCalInstanceSetUid(BongoCalInstance *inst, const char *uid)
{
SetString(inst, "uid", &inst->uid, uid);
}
BongoCalTime
BongoCalInstanceGetRecurId(BongoCalInstance *inst)
{
CacheTime(inst, "recurid", &inst->recurid);
return inst->recurid;
}
void
BongoCalInstanceSetRecurId(BongoCalInstance *inst, BongoCalTime recurid)
{
SetTime(inst, "recurid", &inst->recurid, recurid);
}
BOOL
BongoCalInstanceIsRecurrence(BongoCalInstance *instance)
{
BongoJsonObject *obj;
return (BongoJsonObjectGetObject(instance->json, "recurid", &obj) == BONGO_JSON_OK);
}
const char *
BongoCalInstanceGetSummary(BongoCalInstance *inst)
{
CacheString(inst, "summary", &inst->summary);
return inst->summary;
}
void
BongoCalInstanceSetSummary(BongoCalInstance *inst, const char *summary)
{
SetString(inst, "summary", &inst->summary, summary);
}
const char *
BongoCalInstanceGetStamp(BongoCalInstance *inst)
{
CacheString(inst, "dtstamp", &inst->stamp);
return inst->stamp;
}
void
BongoCalInstanceSetStamp(BongoCalInstance *inst, const char *stamp)
{
SetString(inst, "dtstamp", &inst->stamp, stamp);
}
const char *
BongoCalInstanceGetLocation(BongoCalInstance *inst)
{
CacheString(inst, "location", &inst->location);
return inst->location;
}
const char *
BongoCalInstanceGetDescription(BongoCalInstance *inst)
{
CacheString(inst, "description", &inst->description);
return inst->description;
}
BOOL
BongoCalInstanceHasStart(BongoCalInstance *inst)
{
CacheTime(inst, "dtstart", &inst->start);
return !BongoCalTimeIsEmpty(inst->start);
}
BongoCalTime
BongoCalInstanceGetStart(BongoCalInstance *inst)
{
CacheTime(inst, "dtstart", &inst->start);
return inst->start;
}
void
BongoCalInstanceSetStart(BongoCalInstance *inst, BongoCalTime t)
{
SetTime(inst, "dtstart", &inst->start, t);
}
BOOL
BongoCalInstanceHasEnd(BongoCalInstance *inst)
{
/* FIXME: deal with Duration time */
CacheTime(inst, "dtend", &inst->end);
return BongoCalTimeIsEmpty(inst->end);
}
BongoCalTime
BongoCalInstanceGetEnd(BongoCalInstance *inst)
{
CacheTime(inst, "dtend", &inst->end);
if (BongoCalTimeIsEmpty(inst->end)) {
CacheDuration(inst, "duration", &inst->duration);
/* FIXME: deal with Duration time */
inst->end = BongoCalTimeAdjustDuration(inst->start, inst->duration);
}
return inst->end;
}
/* Returns the last recurrence end time */
BongoCalTime
BongoCalInstanceGetLastEnd(BongoCalInstance *instance)
{
BongoSList *rrules;
BongoCalTime lastRecur;
BongoCalTime end;
BOOL done;
end = BongoCalInstanceGetEnd(instance);
if (!BongoCalInstanceHasRecurrences(instance)) {
return end;
}
BongoCalRecurEnsureEndDates(instance, FALSE, TimezoneCb, instance);
done = FALSE;
lastRecur = BongoCalTimeEmpty();
if (BongoCalInstanceGetRRules(instance, &rrules)) {
BongoSList *l;
for (l = rrules; l != NULL; l = l->next) {
BongoCalRule *recur = l->data;
BongoCalTime untilt;
if (recur->count == 0 && recur->until == 0) {
/* Infinitely-recurring rule, lastend is MAX_RECUR */
lastRecur = BongoCalTimeEmpty();
lastRecur.year = MAX_YEAR;
done = TRUE;
break;
}
untilt = BongoCalTimeNewFromUint64(recur->until, FALSE, NULL);
if (BongoCalTimeCompare(untilt, lastRecur) > 0) {
lastRecur = untilt;
}
}
}
if (BongoCalTimeCompare(lastRecur, end) < 0) {
return end;
} else {
/* FIXME: we really want to use the length of the appt here */
return BongoCalTimeAdjust(lastRecur, 1, 0, 0, 0);
}
}
void
BongoCalInstanceSetEnd(BongoCalInstance *inst, BongoCalTime t)
{
SetTime(inst, "dtend", &inst->end, t);
}
BOOL
BongoCalInstanceHasTzOffsetTo(BongoCalInstance *inst)
{
CacheUtcOffset(inst, "tzoffsetto", &inst->hasTzOffsetTo, &inst->tzOffsetTo);
return inst->hasTzOffsetTo;
}
int
BongoCalInstanceGetTzOffsetTo(BongoCalInstance *inst)
{
CacheUtcOffset(inst, "tzoffsetto", &inst->hasTzOffsetTo, &inst->tzOffsetTo);
return inst->tzOffsetTo;
}
BOOL
BongoCalInstanceHasTzOffsetFrom(BongoCalInstance *inst)
{
CacheUtcOffset(inst, "tzoffsetfrom", &inst->hasTzOffsetFrom, &inst->tzOffsetFrom);
return inst->hasTzOffsetFrom;
}
int
BongoCalInstanceGetTzOffsetFrom(BongoCalInstance *inst)
{
CacheUtcOffset(inst, "tzoffsetfrom", &inst->hasTzOffsetFrom, &inst->tzOffsetFrom);
return inst->tzOffsetFrom;
}
BOOL
BongoCalInstanceHasRecurrences(BongoCalInstance *inst)
{
return (BongoCalInstanceHasRDates(inst) || BongoCalInstanceHasRRules(inst));
}
BOOL
BongoCalInstanceHasRRules(BongoCalInstance *inst)
{
CacheRRules(inst, "rrules", &inst->rrules);
return (inst->rrules != NULL);
}
BOOL
BongoCalInstanceGetRRules(BongoCalInstance *inst, BongoSList **list)
{
CacheRRules(inst, "rrules", &inst->rrules);
*list = inst->rrules;
return (inst->rrules != NULL);
}
BOOL
BongoCalInstanceHasRDates(BongoCalInstance *inst)
{
CacheRDates(inst, "rdates", &inst->rdates);
return (inst->rdates != NULL);
}
BOOL
BongoCalInstanceGetRDates(BongoCalInstance *inst, BongoSList **list)
{
CacheRDates(inst, "rdates", &inst->rdates);
*list = inst->rdates;
return (inst->rdates != NULL);
}
/* Recurrence exceptions */
BOOL
BongoCalInstanceHasExceptions(BongoCalInstance *inst)
{
return BongoCalInstanceHasExRules(inst) || BongoCalInstanceHasExDates(inst);
}
BOOL
BongoCalInstanceHasExRules(BongoCalInstance *inst)
{
CacheRRules(inst, "exrules", &inst->exrules);
return (inst->exrules != NULL);
}
BOOL
BongoCalInstanceGetExRules(BongoCalInstance *inst, BongoSList **list)
{
CacheRRules(inst, "exrules", &inst->exrules);
*list = inst->exrules;
return (inst->exrules != NULL);
}
BOOL
BongoCalInstanceHasExDates(BongoCalInstance *inst)
{
CacheRDates(inst, "exdates", &inst->exdates);
return (inst->exdates != NULL);
}
BOOL
BongoCalInstanceGetExDates(BongoCalInstance *inst, BongoSList **list)
{
CacheRDates(inst, "exdates", &inst->exdates);
*list = inst->exdates;
return (inst->exdates != NULL);
}
BongoCalOccurrence
BongoCalInstanceGetOccurrence(BongoCalInstance *instance)
{
BongoCalOccurrence ret;
ret.start = BongoCalInstanceGetStart(instance);
ret.end = BongoCalInstanceGetEnd(instance);
ret.recurid = BongoCalInstanceGetRecurId(instance);
ret.generated = FALSE;
ret.instance = instance;
return ret;
}
#if 0
BOOL
BongoCalInstanceGetInstance(BongoCalInstance *inst,
BongoCalTime recurid,
BOOL allowGenerated,
BongoCalInstance **instOut)
{
int i;
CacheInstances(inst, "generated", &inst->generated, &inst->generatedSorted);
CacheInstances(inst, "exceptions", &inst->exceptions, &inst->exceptionsSorted);
if (inst->exceptions) {
if (!inst->exceptionsSorted) {
inst->exceptionsSorted = TRUE;
}
i = GArrayFindSorted(inst->exceptions, &recurid, FindRecurId);
if (i != -1) {
*instOut = g_array_index(inst->exceptions, BongoCalInstance *, i);
return TRUE;
}
}
if (inst->generated && allowGenerated) {
if (!inst->generatedSorted) {
g_array_sort(inst->generated, CompareRecurId);
inst->generatedSorted = TRUE;
}
i = GArrayFindSorted(inst->generated, &recurid, FindRecurId);
if (i != -1) {
*instOut = g_array_index(inst->generated, BongoCalInstance *, i);
return TRUE;
}
}
*instOut = NULL;
return FALSE;
}
static void
AddGeneratedInstance(BongoCalInstance *inst,
BongoCalInstance *child,
BOOL isGenerated)
{
GArray *array;
/* Requires "generated" to be created first */
if (BongoJsonObjectGetArray(inst->json, "generated", &array) != BONGO_JSON_OK) {
array = g_array_sized_new(FALSE, FALSE, sizeof(BongoJsonNode *), 15);
BongoJsonObjectPutArray(inst->json, "generated", array);
}
if (array) {
BongoJsonArrayAppendObject(array, child->json);
if (!inst->generated) {
inst->generated = g_array_sized_new(FALSE, FALSE, sizeof(BongoCalInstance *), 16);
}
g_array_append_val(inst->generated, child);
inst->generatedSorted = FALSE;
}
}
#endif
BOOL
BongoCalInstanceCrosses(BongoCalInstance *instance,
BongoCalTime start,
BongoCalTime end)
{
BongoCalTime instStart;
BongoCalTime instEnd;
instStart = BongoCalInstanceGetStart(instance);
instEnd = BongoCalInstanceGetEnd(instance);
if (BongoCalTimeCompare(instStart, end) <= 0 &&
BongoCalTimeCompare(instEnd, start) >= 0) {
return TRUE;
}
return FALSE;
}
static int
CompareOccs(const void *voida, const void *voidb)
{
BongoCalOccurrence *occa = (BongoCalOccurrence*)voida;
BongoCalOccurrence *occb = (BongoCalOccurrence*)voidb;
int ret;
ret = BongoCalTimeCompare(occa->start, occb->start);
if (ret != 0) {
return ret;
}
ret = BongoCalTimeCompare(occa->end, occb->end);
if (ret != 0) {
return ret;
}
return strcmp(BongoCalInstanceGetUid(occa->instance), BongoCalInstanceGetUid(occb->instance));
}
void
BongoCalOccurrencesSort(GArray *occurrences)
{
g_array_sort(occurrences, CompareOccs);
}
BongoJsonObject *
BongoCalOccurrenceToJson(BongoCalOccurrence occ, BongoCalTimezone *tz)
{
BongoJsonObject *obj;
BongoJsonObject *child;
BongoJsonNode *summary;
obj = BongoJsonObjectNew();
child = WriteLocalizedTime(occ.start, tz);
if (BongoJsonObjectPutObject(obj, "dtstart", child) != BONGO_JSON_OK) {
BongoJsonObjectFree(child);
BongoJsonObjectFree(obj);
return NULL;
}
child = WriteLocalizedTime(occ.end, tz);
if (BongoJsonObjectPutObject(obj, "dtend", child) != BONGO_JSON_OK) {
BongoJsonObjectFree(child);
BongoJsonObjectFree(obj);
return NULL;
}
if (!BongoCalTimeIsEmpty(occ.recurid)) {
child = WriteTime(occ.recurid);
if (BongoJsonObjectPutObject(obj, "recurid", child) != BONGO_JSON_OK) {
BongoJsonObjectFree(child);
BongoJsonObjectFree(obj);
return NULL;
}
}
if (BongoJsonObjectPutString(obj, "uid", BongoCalInstanceGetUid(occ.instance)) != BONGO_JSON_OK) {
BongoJsonObjectFree(obj);
return NULL;
}
if (BongoJsonObjectGet(occ.instance->json, "summary", &summary) == BONGO_JSON_OK) {
BongoJsonNode *dup;
dup = BongoJsonNodeDup(summary);
if (dup) {
BongoJsonObjectPut(obj, "summary", dup);
}
}
if (BongoJsonObjectPutBool(obj, "generated", occ.generated) != BONGO_JSON_OK) {
BongoJsonObjectFree(obj);
return NULL;
}
return obj;
}
GArray *
BongoCalOccurrencesToJson(GArray *occs, BongoCalTimezone *tz)
{
GArray *jsonArray;
unsigned int i;
jsonArray = g_array_sized_new(FALSE, FALSE, sizeof(BongoJsonNode *), 16);
for (i = 0; i < occs->len; i++) {
BongoJsonObject *obj;
obj = BongoCalOccurrenceToJson(g_array_index(occs, BongoCalOccurrence, i), tz);
if (obj) {
BongoJsonArrayAppendObject(jsonArray, obj);
}
}
return jsonArray;
}