2000-07-23 Damon Chaplin <damon@helixcode.com> * cal-util/calobj.c (ical_object_create_from_vobject): fixed it so it doesn't crash when a URL is loaded. * cal-util/calobj.h: added a few more fields. svn path=/trunk/; revision=4280
2009 lines
42 KiB
C
2009 lines
42 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
||
/*
|
||
* Calendar objects implementations.
|
||
* Copyright (C) 1998 the Free Software Foundation
|
||
*
|
||
* Authors:
|
||
* Miguel de Icaza (miguel@gnu.org)
|
||
* Federico Mena (quartic@gimp.org)
|
||
*/
|
||
#include <config.h>
|
||
#include <string.h>
|
||
#include <glib.h>
|
||
#include <ctype.h>
|
||
#include <unistd.h>
|
||
#include <time.h>
|
||
#include "calobj.h"
|
||
#include "timeutil.h"
|
||
#include "libversit/vcc.h"
|
||
#include "icalendar-save.h"
|
||
#include "icalendar.h"
|
||
|
||
|
||
|
||
/* VCalendar product ID */
|
||
#define PRODID "-//Helix Code//NONSGML Evolution Calendar//EN"
|
||
|
||
static gint compare_exdates (gconstpointer a, gconstpointer b);
|
||
static void ical_object_normalize_summary (iCalObject *ico);
|
||
static void list_free (GList *list);
|
||
|
||
|
||
|
||
char *
|
||
ical_gen_uid (void)
|
||
{
|
||
static char *hostname;
|
||
time_t t = time (NULL);
|
||
static int serial;
|
||
|
||
if (!hostname){
|
||
char buffer [128];
|
||
|
||
if ((gethostname (buffer, sizeof (buffer)-1) == 0) &&
|
||
(buffer [0] != 0))
|
||
hostname = g_strdup (buffer);
|
||
else
|
||
hostname = g_strdup ("localhost");
|
||
}
|
||
|
||
return g_strdup_printf (
|
||
"%s-%d-%d-%d-%d@%s",
|
||
isodate_from_time_t (t),
|
||
getpid (),
|
||
getgid (),
|
||
getppid (),
|
||
serial++,
|
||
hostname);
|
||
}
|
||
|
||
iCalObject *
|
||
ical_object_new (void)
|
||
{
|
||
iCalObject *ico;
|
||
|
||
ico = g_new0 (iCalObject, 1);
|
||
|
||
ico->seq = -1;
|
||
ico->dtstamp = time (NULL);
|
||
ico->uid = ical_gen_uid ();
|
||
|
||
ico->pilot_id = 0;
|
||
ico->pilot_status = ICAL_PILOT_SYNC_MOD;
|
||
|
||
ico->ref_count = 1;
|
||
|
||
return ico;
|
||
}
|
||
|
||
iCalObject *
|
||
ical_new (char *comment, char *organizer, char *summary)
|
||
{
|
||
iCalObject *ico;
|
||
|
||
ico = ical_object_new ();
|
||
|
||
ico->comment = g_strdup (comment);
|
||
ico->organizer = g_new0 (iCalPerson, 1);
|
||
ico->organizer->addr = g_strdup (organizer);
|
||
ico->summary = g_strdup (summary);
|
||
ico->class = g_strdup ("PUBLIC");
|
||
ico->status = g_strdup ("NEEDS ACTION");
|
||
|
||
ico->dalarm.type = ALARM_DISPLAY;
|
||
ico->palarm.type = ALARM_PROGRAM;
|
||
ico->malarm.type = ALARM_MAIL;
|
||
ico->aalarm.type = ALARM_AUDIO;
|
||
|
||
ical_object_normalize_summary (ico);
|
||
|
||
return ico;
|
||
}
|
||
|
||
|
||
void
|
||
ical_object_ref (iCalObject *ico)
|
||
{
|
||
ico->ref_count++;
|
||
}
|
||
|
||
|
||
#define free_if_defined(x) if (x){ g_free (x); x = 0; }
|
||
#define lfree_if_defined(x) if (x){ list_free (x); x = 0; }
|
||
static void
|
||
ical_object_destroy (iCalObject *ico)
|
||
{
|
||
/* Regular strings */
|
||
free_if_defined (ico->comment);
|
||
free_if_defined (ico->organizer);
|
||
free_if_defined (ico->summary);
|
||
free_if_defined (ico->uid);
|
||
free_if_defined (ico->status);
|
||
free_if_defined (ico->class);
|
||
free_if_defined (ico->url);
|
||
free_if_defined (ico->recur);
|
||
|
||
/* Lists */
|
||
lfree_if_defined (ico->exdate);
|
||
lfree_if_defined (ico->categories);
|
||
lfree_if_defined (ico->resources);
|
||
lfree_if_defined (ico->related);
|
||
lfree_if_defined (ico->attach);
|
||
|
||
/* Alarms */
|
||
g_free (ico->dalarm.data);
|
||
g_free (ico->palarm.data);
|
||
g_free (ico->malarm.data);
|
||
g_free (ico->aalarm.data);
|
||
|
||
g_free (ico);
|
||
}
|
||
|
||
void
|
||
ical_object_unref (iCalObject *ico)
|
||
{
|
||
ico->ref_count--;
|
||
if (ico->ref_count == 0)
|
||
ical_object_destroy (ico);
|
||
}
|
||
|
||
|
||
static void
|
||
my_free (gpointer data, gpointer user_dat_ignored)
|
||
{
|
||
g_free (data);
|
||
}
|
||
|
||
static void
|
||
list_free (GList *list)
|
||
{
|
||
g_list_foreach (list, my_free, 0);
|
||
g_list_free (list);
|
||
}
|
||
|
||
/* This resets any recurrence rules of the iCalObject. */
|
||
void
|
||
ical_object_reset_recurrence (iCalObject *ico)
|
||
{
|
||
free_if_defined (ico->recur);
|
||
lfree_if_defined (ico->exdate);
|
||
}
|
||
|
||
static GList *
|
||
set_list (char *str)
|
||
{
|
||
GList *list = 0;
|
||
char *s;
|
||
|
||
for (s = strtok (str, ";"); s; s = strtok (NULL, ";"))
|
||
list = g_list_prepend (list, g_strdup (s));
|
||
|
||
return list;
|
||
}
|
||
|
||
static GList *
|
||
set_date_list (char *str)
|
||
{
|
||
GList *list = 0;
|
||
char *s;
|
||
|
||
for (s = strtok (str, ";,"); s; s = strtok (NULL, ";,")){
|
||
time_t *t = g_new (time_t, 1);
|
||
|
||
while (*s && isspace (*s))
|
||
s++;
|
||
*t = time_from_isodate (s);
|
||
list = g_list_prepend (list, t);
|
||
}
|
||
return list;
|
||
}
|
||
|
||
void
|
||
ical_object_add_exdate (iCalObject *o, time_t t)
|
||
{
|
||
time_t *pt = g_new (time_t, 1);
|
||
|
||
*pt = t;
|
||
o->exdate = g_list_prepend (o->exdate, pt);
|
||
}
|
||
|
||
static void
|
||
ignore_space(char **str)
|
||
{
|
||
while (**str && isspace (**str))
|
||
(*str)++;
|
||
}
|
||
|
||
static void
|
||
skip_numbers (char **str)
|
||
{
|
||
while (**str){
|
||
ignore_space (str);
|
||
if (!isdigit (**str))
|
||
return;
|
||
while (**str && isdigit (**str))
|
||
(*str)++;
|
||
}
|
||
}
|
||
|
||
static void
|
||
weekdaylist (iCalObject *o, char **str)
|
||
{
|
||
int i;
|
||
struct {
|
||
char first_letter, second_letter;
|
||
int index;
|
||
} days [] = {
|
||
{ 'S', 'U', 0 },
|
||
{ 'M', 'O', 1 },
|
||
{ 'T', 'U', 2 },
|
||
{ 'W', 'E', 3 },
|
||
{ 'T', 'H', 4 },
|
||
{ 'F', 'R', 5 },
|
||
{ 'S', 'A', 6 }
|
||
};
|
||
|
||
ignore_space (str);
|
||
do {
|
||
for (i = 0; i < 7; i++){
|
||
if (**str == days [i].first_letter && *(*str+1) == days [i].second_letter){
|
||
o->recur->weekday |= 1 << i;
|
||
*str += 2;
|
||
if (**str == ' ')
|
||
(*str)++;
|
||
}
|
||
}
|
||
} while (isalpha (**str));
|
||
|
||
if (o->recur->weekday == 0){
|
||
struct tm tm = *localtime (&o->dtstart);
|
||
|
||
o->recur->weekday = 1 << tm.tm_wday;
|
||
}
|
||
}
|
||
|
||
static void
|
||
weekdaynum (iCalObject *o, char **str)
|
||
{
|
||
int i;
|
||
struct {
|
||
char first_letter, second_letter;
|
||
int index;
|
||
} days [] = {
|
||
{ 'S', 'U', 0 },
|
||
{ 'M', 'O', 1 },
|
||
{ 'T', 'U', 2 },
|
||
{ 'W', 'E', 3 },
|
||
{ 'T', 'H', 4 },
|
||
{ 'F', 'R', 5 },
|
||
{ 'S', 'A', 6 }
|
||
};
|
||
|
||
ignore_space (str);
|
||
do {
|
||
for (i = 0; i < 7; i++){
|
||
if (**str == days [i].first_letter && *(*str+1) == days [i].second_letter){
|
||
o->recur->weekday = i;
|
||
*str += 2;
|
||
if (**str == ' ')
|
||
(*str)++;
|
||
}
|
||
}
|
||
} while (isalpha (**str));
|
||
}
|
||
|
||
static void
|
||
ocurrencelist (iCalObject *o, char **str)
|
||
{
|
||
char *p;
|
||
|
||
ignore_space (str);
|
||
p = *str;
|
||
if (!isdigit (*p))
|
||
return;
|
||
|
||
if (!(*p >= '1' && *p <= '5'))
|
||
return;
|
||
|
||
if (!(*(p+1) == '+' || *(p+1) == '-'))
|
||
return;
|
||
|
||
o->recur->u.month_pos = (*p-'0') * (*(p+1) == '+' ? 1 : -1);
|
||
*str += 2;
|
||
}
|
||
|
||
#if 0
|
||
|
||
static void
|
||
daynumber (iCalObject *o, char **str)
|
||
{
|
||
int val = 0;
|
||
char *p = *str;
|
||
|
||
ignore_space (str);
|
||
if (strcmp (p, "LD")){
|
||
o->recur->u.month_day = DAY_LASTDAY;
|
||
*str += 2;
|
||
return;
|
||
}
|
||
|
||
if (!(isdigit (*p)))
|
||
return;
|
||
|
||
while (**str && isdigit (**str)){
|
||
val = val * 10 + (**str - '0');
|
||
(*str)++;
|
||
}
|
||
|
||
if (**str == '+')
|
||
(*str)++;
|
||
|
||
if (**str == '-')
|
||
val *= -1;
|
||
o->recur->u.month_day = val;
|
||
}
|
||
|
||
#endif
|
||
|
||
static void
|
||
daynumberlist (iCalObject *o, char **str)
|
||
{
|
||
int first = 0;
|
||
int val = 0;
|
||
|
||
ignore_space (str);
|
||
|
||
while (**str){
|
||
if (!isdigit (**str))
|
||
return;
|
||
while (**str && isdigit (**str)){
|
||
val = 10 * val + (**str - '0');
|
||
(*str)++;
|
||
}
|
||
if (!first){
|
||
/*
|
||
* Some broken applications set this to zero
|
||
*/
|
||
if (val == 0){
|
||
struct tm day = *localtime (&o->dtstart);
|
||
|
||
val = day.tm_mday;
|
||
}
|
||
o->recur->u.month_day = val;
|
||
first = 1;
|
||
val = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
load_recur_weekly (iCalObject *o, char **str)
|
||
{
|
||
weekdaylist (o, str);
|
||
}
|
||
|
||
static void
|
||
load_recur_monthly_pos (iCalObject *o, char **str)
|
||
{
|
||
ocurrencelist (o, str);
|
||
weekdaynum (o, str);
|
||
}
|
||
|
||
static void
|
||
load_recur_monthly_day (iCalObject *o, char **str)
|
||
{
|
||
daynumberlist (o, str);
|
||
}
|
||
|
||
static void
|
||
load_recur_yearly_month (iCalObject *o, char **str)
|
||
{
|
||
/* Skip as we do not support multiple months and we do expect
|
||
* the dtstart to agree with the value on this field
|
||
*/
|
||
skip_numbers (str);
|
||
}
|
||
|
||
static void
|
||
load_recur_yearly_day (iCalObject *o, char **str)
|
||
{
|
||
/* Skip as we do not support multiple days and we do expect
|
||
* the dtstart to agree with the value on this field
|
||
*
|
||
* FIXME: we should support every-n-years
|
||
*/
|
||
skip_numbers (str);
|
||
}
|
||
|
||
static void
|
||
duration (iCalObject *o, char **str)
|
||
{
|
||
unsigned int duration = 0;
|
||
|
||
ignore_space (str);
|
||
if (**str != '#')
|
||
return;
|
||
(*str)++;
|
||
while (**str && isdigit (**str)){
|
||
duration = duration * 10 + (**str - '0');
|
||
(*str)++;
|
||
}
|
||
o->recur->duration = duration;
|
||
}
|
||
|
||
static void
|
||
enddate (iCalObject *o, char **str)
|
||
{
|
||
ignore_space (str);
|
||
if (isdigit (**str)){
|
||
o->recur->_enddate = time_from_isodate (*str);
|
||
*str += 16;
|
||
}
|
||
}
|
||
|
||
static int
|
||
load_recurrence (iCalObject *o, char *str)
|
||
{
|
||
enum RecurType type;
|
||
int interval = 0;
|
||
|
||
type = -1;
|
||
switch (*str++){
|
||
case 'D':
|
||
type = RECUR_DAILY;
|
||
break;
|
||
|
||
case 'W':
|
||
type = RECUR_WEEKLY;
|
||
break;
|
||
|
||
case 'M':
|
||
if (*str == 'P')
|
||
type = RECUR_MONTHLY_BY_POS;
|
||
else if (*str == 'D')
|
||
type = RECUR_MONTHLY_BY_DAY;
|
||
str++;
|
||
break;
|
||
|
||
case 'Y':
|
||
if (*str == 'M')
|
||
type = RECUR_YEARLY_BY_MONTH;
|
||
else if (*str == 'D')
|
||
type = RECUR_YEARLY_BY_DAY;
|
||
str++;
|
||
break;
|
||
}
|
||
if (type == -1)
|
||
return 0;
|
||
|
||
o->recur = g_new0 (Recurrence, 1);
|
||
o->recur->type = type;
|
||
ignore_space (&str);
|
||
|
||
/* Get the interval */
|
||
for (;*str && isdigit (*str);str++)
|
||
interval = interval * 10 + (*str-'0');
|
||
|
||
if (interval == 0)
|
||
interval = 1;
|
||
|
||
o->recur->interval = interval;
|
||
|
||
/* this is the default per the spec */
|
||
o->recur->duration = 2;
|
||
|
||
ignore_space (&str);
|
||
|
||
switch (type){
|
||
case RECUR_DAILY:
|
||
break;
|
||
case RECUR_WEEKLY:
|
||
load_recur_weekly (o, &str);
|
||
break;
|
||
case RECUR_MONTHLY_BY_POS:
|
||
load_recur_monthly_pos (o, &str);
|
||
break;
|
||
case RECUR_MONTHLY_BY_DAY:
|
||
load_recur_monthly_day (o, &str);
|
||
break;
|
||
case RECUR_YEARLY_BY_MONTH:
|
||
load_recur_yearly_month (o, &str);
|
||
break;
|
||
case RECUR_YEARLY_BY_DAY:
|
||
load_recur_yearly_day (o, &str);
|
||
break;
|
||
default:
|
||
g_warning ("Unimplemented recurrence type %d", (int) type);
|
||
break;
|
||
}
|
||
duration (o, &str);
|
||
enddate (o, &str);
|
||
|
||
/* Compute the enddate */
|
||
if (o->recur->_enddate == 0){
|
||
if (o->recur->duration != 0){
|
||
ical_object_compute_end (o);
|
||
} else
|
||
o->recur->enddate = 0;
|
||
} else {
|
||
o->recur->enddate = o->recur->_enddate;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
#define is_a_prop_of(obj,prop) isAPropertyOf (obj,prop)
|
||
#define str_val(obj) the_str = fakeCString (vObjectUStringZValue (obj))
|
||
#define has(obj,prop) (vo = isAPropertyOf (obj, prop))
|
||
|
||
/*
|
||
* FIXME: This is loosing precission. Enhanec the thresholds
|
||
*/
|
||
#define HOURS(n) (n*(60*60))
|
||
|
||
static void
|
||
setup_alarm_at (iCalObject *ico, CalendarAlarm *alarm, char *iso_time, VObject *vo)
|
||
{
|
||
time_t alarm_time = time_from_isodate (iso_time);
|
||
time_t base = ico->dtstart;
|
||
int d = difftime (base, alarm_time);
|
||
VObject *a;
|
||
char *the_str;
|
||
|
||
alarm->enabled = 1;
|
||
if (d > HOURS (2)){
|
||
if (d > HOURS (48)){
|
||
alarm->count = d / HOURS (24);
|
||
alarm->units = ALARM_DAYS;
|
||
} else {
|
||
alarm->count = d / (60*60);
|
||
alarm->units = ALARM_HOURS;
|
||
}
|
||
} else {
|
||
alarm->count = d / 60;
|
||
alarm->units = ALARM_MINUTES;
|
||
}
|
||
|
||
if ((a = is_a_prop_of (vo, VCSnoozeTimeProp))){
|
||
alarm->snooze_secs = isodiff_to_secs (str_val (a));
|
||
free (the_str);
|
||
}
|
||
|
||
if ((a = is_a_prop_of (vo, VCRepeatCountProp))){
|
||
alarm->snooze_repeat = atoi (str_val (a));
|
||
free (the_str);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Duplicates an iCalObject. Implementation is a grand hack.
|
||
* If you need the new ICalObject to have a new uid, free the current one,
|
||
* and call ical_gen_uid() to generate a new one.
|
||
*/
|
||
iCalObject *
|
||
ical_object_duplicate (iCalObject *o)
|
||
{
|
||
VObject *vo;
|
||
iCalObject *new;
|
||
|
||
vo = ical_object_to_vobject (o);
|
||
switch (o->type){
|
||
case ICAL_EVENT:
|
||
new = ical_object_create_from_vobject (vo, VCEventProp);
|
||
break;
|
||
case ICAL_TODO:
|
||
new = ical_object_create_from_vobject (vo, VCTodoProp);
|
||
break;
|
||
default:
|
||
new = NULL;
|
||
}
|
||
|
||
cleanVObject (vo);
|
||
return new;
|
||
}
|
||
|
||
/* FIXME: we need to load the recurrence properties */
|
||
iCalObject *
|
||
ical_object_create_from_vobject (VObject *o, const char *object_name)
|
||
{
|
||
time_t now = time (NULL);
|
||
iCalObject *ical;
|
||
VObject *vo, *a;
|
||
VObjectIterator i;
|
||
char *the_str;
|
||
|
||
ical = g_new0 (iCalObject, 1);
|
||
|
||
if (strcmp (object_name, VCEventProp) == 0)
|
||
ical->type = ICAL_EVENT;
|
||
else if (strcmp (object_name, VCTodoProp) == 0)
|
||
ical->type = ICAL_TODO;
|
||
else {
|
||
g_free (ical);
|
||
return 0;
|
||
}
|
||
|
||
ical->ref_count = 1;
|
||
|
||
/* uid */
|
||
if (has (o, VCUniqueStringProp)){
|
||
ical->uid = g_strdup (str_val (vo));
|
||
free (the_str);
|
||
} else {
|
||
ical->uid = ical_gen_uid ();
|
||
}
|
||
|
||
/* seq */
|
||
if (has (o, VCSequenceProp)){
|
||
ical->seq = atoi (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->seq = 0;
|
||
|
||
/* dtstart */
|
||
if (has (o, VCDTstartProp)){
|
||
ical->dtstart = time_from_isodate (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->dtstart = 0;
|
||
|
||
/* dtend */
|
||
ical->dtend = 0; /* default value */
|
||
if (ical->type == ICAL_EVENT){
|
||
if (has (o, VCDTendProp)){
|
||
ical->dtend = time_from_isodate (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
} else if (ical->type == ICAL_TODO){
|
||
if (has (o, VCDueProp)){
|
||
ical->dtend = time_from_isodate (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
}
|
||
|
||
/* dcreated */
|
||
if (has (o, VCDCreatedProp)){
|
||
ical->created = time_from_isodate (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* completed */
|
||
if (has (o, VCCompletedProp)){
|
||
ical->completed = time_from_isodate (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* last_mod */
|
||
if (has (o, VCLastModifiedProp)){
|
||
ical->last_mod = time_from_isodate (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->last_mod = now;
|
||
|
||
/* exdate */
|
||
if (has (o, VCExpDateProp)){
|
||
ical->exdate = set_date_list (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* description/comment */
|
||
if (has (o, VCDescriptionProp)){
|
||
ical->comment = g_strdup (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* summary */
|
||
if (has (o, VCSummaryProp)){
|
||
ical->summary = g_strdup (str_val (vo));
|
||
free (the_str);
|
||
|
||
/* Convert any CR/LF/CRLF sequences in the summary field to
|
||
spaces so we just have a one-line field. */
|
||
ical_object_normalize_summary (ical);
|
||
} else
|
||
ical->summary = g_strdup ("");
|
||
|
||
/* status */
|
||
if (has (o, VCStatusProp)){
|
||
ical->status = g_strdup (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->status = g_strdup ("NEEDS ACTION");
|
||
|
||
if (has (o, VCClassProp)){
|
||
ical->class = g_strdup (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->class = g_strdup ("PUBLIC");
|
||
|
||
/* categories */
|
||
if (has (o, VCCategoriesProp)){
|
||
ical->categories = set_list (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* resources */
|
||
if (has (o, VCResourcesProp)){
|
||
ical->resources = set_list (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* priority */
|
||
if (has (o, VCPriorityProp)){
|
||
ical->priority = atoi (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* tranparency */
|
||
if (has (o, VCTranspProp)){
|
||
ical->transp = atoi (str_val (vo)) ? ICAL_TRANSPARENT : ICAL_OPAQUE;
|
||
free (the_str);
|
||
}
|
||
|
||
/* Organizer */
|
||
if (has (o, VCOrgNameProp)){
|
||
ical->organizer = g_new0 (iCalPerson, 1);
|
||
ical->organizer->addr = g_strdup (str_val (vo));
|
||
free (the_str);
|
||
}
|
||
|
||
/* related */
|
||
if (has (o, VCRelatedToProp)){
|
||
char *str;
|
||
char *s;
|
||
iCalRelation *rel;
|
||
str = str_val (vo);
|
||
for (s = strtok (str, ";"); s; s = strtok (NULL, ";")) {
|
||
rel = g_new0 (iCalRelation, 1);
|
||
rel->uid = g_strdup (s);
|
||
rel->reltype = g_strdup ("PARENT");
|
||
ical->related = g_list_prepend (ical->related, rel);
|
||
}
|
||
free (the_str);
|
||
}
|
||
|
||
/* attach */
|
||
initPropIterator (&i, o);
|
||
while (moreIteration (&i)){
|
||
vo = nextVObject (&i);
|
||
if (strcmp (vObjectName (vo), VCAttachProp) == 0){
|
||
ical->attach = g_list_prepend (ical->attach, g_strdup (str_val (vo)));
|
||
free (the_str);
|
||
}
|
||
}
|
||
|
||
/* url */
|
||
if (has (o, VCURLProp)){
|
||
/* There seems to be a problem with the URL property. For some
|
||
reason an empty property gets saved, vObjectUStringZValue
|
||
returns NULL and fakeCString crashes. So we check for NULL.
|
||
*/
|
||
const wchar_t *zval;
|
||
|
||
zval = vObjectUStringZValue (o);
|
||
if (zval) {
|
||
the_str = fakeCString (zval);
|
||
ical->url = g_strdup (the_str);
|
||
free (the_str);
|
||
}
|
||
}
|
||
|
||
/* dalarm */
|
||
ical->dalarm.type = ALARM_DISPLAY;
|
||
ical->dalarm.enabled = 0;
|
||
if (has (o, VCDAlarmProp)){
|
||
if ((a = is_a_prop_of (vo, VCRunTimeProp))){
|
||
setup_alarm_at (ical, &ical->dalarm, str_val (a), vo);
|
||
free (the_str);
|
||
}
|
||
}
|
||
|
||
/* aalarm */
|
||
ical->aalarm.type = ALARM_AUDIO;
|
||
ical->aalarm.enabled = 0;
|
||
if (has (o, VCAAlarmProp)){
|
||
if ((a = is_a_prop_of (vo, VCRunTimeProp))){
|
||
setup_alarm_at (ical, &ical->aalarm, str_val (a), vo);
|
||
free (the_str);
|
||
}
|
||
}
|
||
|
||
/* palarm */
|
||
ical->palarm.type = ALARM_PROGRAM;
|
||
ical->palarm.enabled = 0;
|
||
if (has (o, VCPAlarmProp)){
|
||
ical->palarm.type = ALARM_PROGRAM;
|
||
if ((a = is_a_prop_of (vo, VCRunTimeProp))){
|
||
setup_alarm_at (ical, &ical->palarm, str_val (a), vo);
|
||
free (the_str);
|
||
|
||
if ((a = is_a_prop_of (vo, VCProcedureNameProp))){
|
||
ical->palarm.data = g_strdup (str_val (a));
|
||
free (the_str);
|
||
} else
|
||
ical->palarm.data = g_strdup ("");
|
||
}
|
||
}
|
||
|
||
/* malarm */
|
||
ical->malarm.type = ALARM_MAIL;
|
||
ical->malarm.enabled = 0;
|
||
if (has (o, VCMAlarmProp)){
|
||
ical->malarm.type = ALARM_MAIL;
|
||
if ((a = is_a_prop_of (vo, VCRunTimeProp))){
|
||
setup_alarm_at (ical, &ical->malarm, str_val (a), vo);
|
||
free (the_str);
|
||
|
||
if ((a = is_a_prop_of (vo, VCEmailAddressProp))){
|
||
ical->malarm.data = g_strdup (str_val (a));
|
||
free (the_str);
|
||
} else
|
||
ical->malarm.data = g_strdup ("");
|
||
}
|
||
}
|
||
|
||
/* rrule */
|
||
if (has (o, VCRRuleProp)){
|
||
if (!load_recurrence (ical, str_val (vo))) {
|
||
ical_object_unref (ical);
|
||
return NULL;
|
||
}
|
||
free (the_str);
|
||
}
|
||
|
||
/*
|
||
* Pilot
|
||
*/
|
||
if (has (o, XPilotIdProp)){
|
||
ical->pilot_id = atoi (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->pilot_id = 0;
|
||
|
||
if (has (o, XPilotStatusProp)){
|
||
ical->pilot_status = atoi (str_val (vo));
|
||
free (the_str);
|
||
} else
|
||
ical->pilot_status = ICAL_PILOT_SYNC_MOD;
|
||
|
||
return ical;
|
||
}
|
||
|
||
static char *
|
||
to_str (int num)
|
||
{
|
||
static char buf [40];
|
||
|
||
sprintf (buf, "%d", num);
|
||
return buf;
|
||
}
|
||
|
||
/*
|
||
* stores a GList in the property.
|
||
*/
|
||
static void
|
||
store_list (VObject *o, char *prop, GList *values)
|
||
{
|
||
GList *l;
|
||
int len;
|
||
char *result, *p;
|
||
|
||
for (len = 0, l = values; l; l = l->next)
|
||
len += strlen (l->data) + 1;
|
||
|
||
result = g_malloc (len);
|
||
|
||
for (p = result, l = values; l; l = l->next) {
|
||
int len = strlen (l->data);
|
||
|
||
strcpy (p, l->data);
|
||
|
||
if (l->next) {
|
||
p [len] = ';';
|
||
p += len+1;
|
||
} else
|
||
p += len;
|
||
}
|
||
|
||
*p = 0;
|
||
|
||
addPropValue (o, prop, result);
|
||
g_free (result);
|
||
}
|
||
|
||
static void
|
||
store_rel_list (VObject *o, char *prop, GList *values)
|
||
{
|
||
GList *l;
|
||
int len;
|
||
char *result, *p;
|
||
|
||
for (len = 0, l = values; l; l = l->next)
|
||
len += strlen (((iCalRelation*)(l->data))->uid) + 1;
|
||
|
||
result = g_malloc (len);
|
||
|
||
for (p = result, l = values; l; l = l->next) {
|
||
int len = strlen (((iCalRelation*)(l->data))->uid);
|
||
|
||
strcpy (p, ((iCalRelation*)(l->data))->uid);
|
||
|
||
if (l->next) {
|
||
p [len] = ';';
|
||
p += len+1;
|
||
} else
|
||
p += len;
|
||
}
|
||
|
||
*p = 0;
|
||
|
||
addPropValue (o, prop, result);
|
||
g_free (result);
|
||
}
|
||
|
||
static void
|
||
store_date_list (VObject *o, char *prop, GList *values)
|
||
{
|
||
GList *l;
|
||
int size, len;
|
||
char *s, *p;
|
||
|
||
size = g_list_length (values);
|
||
s = p = g_malloc ((size * 17 + 1) * sizeof (char));
|
||
|
||
for (l = values; l; l = l->next){
|
||
strcpy (s, isodate_from_time_t (*(time_t *)l->data));
|
||
len = strlen (s);
|
||
s [len] = ',';
|
||
s += len + 1;
|
||
}
|
||
s--;
|
||
*s = 0;
|
||
addPropValue (o, prop, p);
|
||
g_free (p);
|
||
}
|
||
|
||
static char *recur_type_name [] = { "D", "W", "MP", "MD", "YM", "YD" };
|
||
static char *recur_day_list [] = { "SU", "MO", "TU","WE", "TH", "FR", "SA" };
|
||
static char *alarm_names [] = { VCMAlarmProp, VCPAlarmProp, VCDAlarmProp, VCAAlarmProp };
|
||
|
||
static VObject *
|
||
save_alarm (VObject *o, CalendarAlarm *alarm, iCalObject *ical)
|
||
{
|
||
VObject *alarm_object;
|
||
struct tm tm;
|
||
time_t alarm_time;
|
||
|
||
if (!alarm->enabled)
|
||
return NULL;
|
||
tm = *localtime (&ical->dtstart);
|
||
switch (alarm->units){
|
||
case ALARM_MINUTES:
|
||
tm.tm_min -= alarm->count;
|
||
break;
|
||
|
||
case ALARM_HOURS:
|
||
tm.tm_hour -= alarm->count;
|
||
break;
|
||
|
||
case ALARM_DAYS:
|
||
tm.tm_mday -= alarm->count;
|
||
break;
|
||
}
|
||
|
||
alarm_time = mktime (&tm);
|
||
alarm_object = addProp (o, alarm_names [alarm->type]);
|
||
addPropValue (alarm_object, VCRunTimeProp, isodate_from_time_t (alarm_time));
|
||
|
||
if (alarm->snooze_secs)
|
||
addPropValue (alarm_object, VCSnoozeTimeProp, isodiff_from_secs (alarm->snooze_secs));
|
||
else
|
||
addPropValue (alarm_object, VCSnoozeTimeProp, "");
|
||
|
||
if (alarm->snooze_repeat){
|
||
char buf [20];
|
||
|
||
sprintf (buf, "%d", alarm->snooze_repeat);
|
||
addPropValue (alarm_object, VCRepeatCountProp, buf);
|
||
} else
|
||
addPropValue (alarm_object, VCRepeatCountProp, "");
|
||
return alarm_object;
|
||
}
|
||
|
||
VObject *
|
||
ical_object_to_vobject (iCalObject *ical)
|
||
{
|
||
VObject *o, *alarm, *s;
|
||
GList *l;
|
||
|
||
if (ical->type == ICAL_EVENT)
|
||
o = newVObject (VCEventProp);
|
||
else
|
||
o = newVObject (VCTodoProp);
|
||
|
||
/* uid */
|
||
if (ical->uid)
|
||
addPropValue (o, VCUniqueStringProp, ical->uid);
|
||
|
||
/* seq */
|
||
addPropValue (o, VCSequenceProp, to_str (ical->seq));
|
||
|
||
/* dtstart */
|
||
addPropValue (o, VCDTstartProp, isodate_from_time_t (ical->dtstart));
|
||
|
||
/* dtend */
|
||
if (ical->type == ICAL_EVENT){
|
||
addPropValue (o, VCDTendProp, isodate_from_time_t (ical->dtend));
|
||
} else if (ical->type == ICAL_TODO){
|
||
addPropValue (o, VCDueProp, isodate_from_time_t (ical->dtend));
|
||
}
|
||
|
||
/* dcreated */
|
||
addPropValue (o, VCDCreatedProp, isodate_from_time_t (ical->created));
|
||
|
||
/* completed */
|
||
if (ical->completed)
|
||
addPropValue (o, VCDTendProp, isodate_from_time_t (ical->completed));
|
||
|
||
/* last_mod */
|
||
addPropValue (o, VCLastModifiedProp, isodate_from_time_t (ical->last_mod));
|
||
|
||
/* exdate */
|
||
if (ical->exdate)
|
||
store_date_list (o, VCExpDateProp, ical->exdate);
|
||
|
||
/* description/comment */
|
||
if (ical->comment && strlen (ical->comment)){
|
||
s = addPropValue (o, VCDescriptionProp, ical->comment);
|
||
if (strchr (ical->comment, '\n'))
|
||
addProp (s, VCQuotedPrintableProp);
|
||
}
|
||
|
||
/* summary */
|
||
if (ical->summary && strlen (ical->summary)) {
|
||
s = addPropValue (o, VCSummaryProp, ical->summary);
|
||
if (strchr (ical->summary, '\n'))
|
||
addProp (s, VCQuotedPrintableProp);
|
||
}
|
||
|
||
/* status */
|
||
addPropValue (o, VCStatusProp, ical->status);
|
||
|
||
/* class */
|
||
addPropValue (o, VCClassProp, ical->class);
|
||
|
||
/* categories */
|
||
if (ical->categories)
|
||
store_list (o, VCCategoriesProp, ical->categories);
|
||
|
||
/* resources */
|
||
if (ical->resources)
|
||
store_list (o, VCCategoriesProp, ical->resources);
|
||
|
||
/* priority */
|
||
addPropValue (o, VCPriorityProp, to_str (ical->priority));
|
||
|
||
/* transparency */
|
||
addPropValue (o, VCTranspProp, to_str (ical->transp));
|
||
|
||
/* Owner/organizer */
|
||
if (ical->organizer && ical->organizer->addr)
|
||
addPropValue (o, VCOrgNameProp, ical->organizer->addr);
|
||
|
||
/* related */
|
||
if (ical->related)
|
||
store_rel_list (o, VCRelatedToProp, ical->related);
|
||
|
||
/* attach */
|
||
for (l = ical->attach; l; l = l->next)
|
||
addPropValue (o, VCAttachProp, l->data);
|
||
|
||
/* url */
|
||
if (ical->url)
|
||
addPropValue (o, VCURLProp, ical->url);
|
||
|
||
if (ical->recur){
|
||
char result [256];
|
||
char buffer [80];
|
||
int i;
|
||
|
||
sprintf (result, "%s%d ", recur_type_name [ical->recur->type], ical->recur->interval);
|
||
switch (ical->recur->type){
|
||
case RECUR_DAILY:
|
||
break;
|
||
|
||
case RECUR_WEEKLY:
|
||
for (i = 0; i < 7; i++){
|
||
if (ical->recur->weekday & (1 << i)){
|
||
sprintf (buffer, "%s ", recur_day_list [i]);
|
||
strcat (result, buffer);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case RECUR_MONTHLY_BY_POS: {
|
||
int nega = ical->recur->u.month_pos < 0;
|
||
|
||
sprintf (buffer, "%d%s ", nega ? -ical->recur->u.month_pos : ical->recur->u.month_pos,
|
||
nega ? "-" : "+");
|
||
strcat (result, buffer);
|
||
/* the gui is set up for a single day, not a set here in this case */
|
||
sprintf (buffer, "%s ", recur_day_list [ical->recur->weekday]);
|
||
strcat (result, buffer);
|
||
}
|
||
break;
|
||
|
||
case RECUR_MONTHLY_BY_DAY:
|
||
sprintf (buffer, "%d ", ical->recur->u.month_pos);
|
||
strcat (result, buffer);
|
||
break;
|
||
|
||
case RECUR_YEARLY_BY_MONTH:
|
||
break;
|
||
|
||
case RECUR_YEARLY_BY_DAY:
|
||
break;
|
||
}
|
||
if (ical->recur->_enddate == 0)
|
||
sprintf (buffer, "#%d ",ical->recur->duration);
|
||
else
|
||
sprintf (buffer, "%s ", isodate_from_time_t (ical->recur->_enddate));
|
||
strcat (result, buffer);
|
||
addPropValue (o, VCRRuleProp, result);
|
||
}
|
||
|
||
save_alarm (o, &ical->aalarm, ical);
|
||
save_alarm (o, &ical->dalarm, ical);
|
||
|
||
if ((alarm = save_alarm (o, &ical->palarm, ical)))
|
||
addPropValue (alarm, VCProcedureNameProp, ical->palarm.data);
|
||
if ((alarm = save_alarm (o, &ical->malarm, ical)))
|
||
addPropValue (alarm, VCEmailAddressProp, ical->malarm.data);
|
||
|
||
/* Pilot */
|
||
{
|
||
char buffer [20];
|
||
|
||
sprintf (buffer, "%d", ical->pilot_id);
|
||
addPropValue (o, XPilotIdProp, buffer);
|
||
sprintf (buffer, "%d", ical->pilot_status);
|
||
addPropValue (o, XPilotStatusProp, buffer);
|
||
}
|
||
|
||
return o;
|
||
}
|
||
|
||
void
|
||
ical_foreach (GList *events, calendarfn fn, void *closure)
|
||
{
|
||
for (; events; events = events->next){
|
||
iCalObject *ical = events->data;
|
||
|
||
(*fn) (ical, ical->dtstart, ical->dtend, closure);
|
||
}
|
||
}
|
||
|
||
static int
|
||
is_date_in_list (GList *list, struct tm *date)
|
||
{
|
||
struct tm tm;
|
||
|
||
for (; list; list = list->next){
|
||
time_t *timep = list->data;
|
||
|
||
tm = *localtime (timep);
|
||
if (date->tm_mday == tm.tm_mday &&
|
||
date->tm_mon == tm.tm_mon &&
|
||
date->tm_year == tm.tm_year){
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* Generates an event instance based on the reference time */
|
||
static gboolean
|
||
generate (iCalObject *ico, time_t reference, calendarfn cb, void *closure)
|
||
{
|
||
time_t offset;
|
||
struct tm tm_start, ref;
|
||
time_t start, end;
|
||
|
||
offset = ico->dtend - ico->dtstart;
|
||
|
||
tm_start = *localtime (&ico->dtstart);
|
||
ref = *localtime (&reference);
|
||
|
||
tm_start.tm_mday = ref.tm_mday;
|
||
tm_start.tm_mon = ref.tm_mon;
|
||
tm_start.tm_year = ref.tm_year;
|
||
|
||
start = mktime (&tm_start);
|
||
if (start == -1) {
|
||
g_message ("generate(): Produced invalid start date!");
|
||
return FALSE;
|
||
}
|
||
|
||
end = start + offset;
|
||
|
||
#if 0
|
||
/* FIXME: I think this is not needed, since we are offsetting by full day values,
|
||
* and the times should remain the same --- if you have a daily appointment
|
||
* at 18:00, it is always at 18:00 even during daylight savings.
|
||
*
|
||
* However, what should happen on the exact change-of-savings day with
|
||
* appointments in the early morning hours?
|
||
*/
|
||
|
||
if (ref.tm_isdst > tm_start.tm_isdst) {
|
||
tm_start.tm_hour--;
|
||
tm_end.tm_hour--;
|
||
} else if (ref.tm_isdst < tm_start.tm_isdst) {
|
||
tm_start.tm_hour++;
|
||
tm_end.tm_hour++;
|
||
}
|
||
#endif
|
||
|
||
if (ico->exdate && is_date_in_list (ico->exdate, &tm_start))
|
||
return TRUE;
|
||
|
||
return (*cb) (ico, start, end, closure);
|
||
}
|
||
|
||
int
|
||
ical_object_get_first_weekday (int weekday_mask)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < 7; i++)
|
||
if (weekday_mask & (1 << i))
|
||
return i;
|
||
|
||
return -1;
|
||
}
|
||
|
||
#define time_in_range(t, a, b) ((t >= a) && (b ? (t < b) : 1))
|
||
#define recur_in_range(t, r) (r->enddate ? (t < r->enddate) : 1)
|
||
|
||
/*
|
||
* Generate every possible event. Invokes the callback routine for
|
||
* every occurrence of the event in the [START, END] time interval.
|
||
*
|
||
* If END is zero, the event is generated forever.
|
||
* The callback routine is expected to return 0 when no further event
|
||
* generation is requested.
|
||
*/
|
||
void
|
||
ical_object_generate_events (iCalObject *ico, time_t start, time_t end, calendarfn cb, void *closure)
|
||
{
|
||
time_t current;
|
||
int first_week_day;
|
||
|
||
/* If there is no recurrence, just check ranges */
|
||
|
||
if (!ico->recur) {
|
||
if ((end && (ico->dtstart < end) && (ico->dtend > start))
|
||
|| ((end == 0) && (ico->dtend > start))) {
|
||
/* The new calendar views expect the times to not be
|
||
clipped, so they can show that it continues past
|
||
the end of the viewable area. */
|
||
#if 0
|
||
time_t ev_s, ev_e;
|
||
|
||
/* Clip range */
|
||
|
||
ev_s = MAX (ico->dtstart, start);
|
||
ev_e = MIN (ico->dtend, end);
|
||
|
||
(* cb) (ico, ev_s, ev_e, closure);
|
||
#else
|
||
(* cb) (ico, ico->dtstart, ico->dtend, closure);
|
||
#endif
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* The event has a recurrence rule -- check that we will generate at least one instance */
|
||
|
||
if (end != 0) {
|
||
if (ico->dtstart > end)
|
||
return;
|
||
|
||
if (!IS_INFINITE (ico->recur) && (ico->recur->enddate < start))
|
||
return;
|
||
}
|
||
|
||
/* Generate the instances */
|
||
|
||
current = ico->dtstart;
|
||
|
||
switch (ico->recur->type) {
|
||
case RECUR_DAILY:
|
||
do {
|
||
if (time_in_range (current, start, end) && recur_in_range (current, ico->recur))
|
||
if (!generate (ico, current, cb, closure))
|
||
return;
|
||
|
||
/* Advance */
|
||
|
||
current = time_add_day (current, ico->recur->interval);
|
||
|
||
if (current == -1) {
|
||
g_warning ("RECUR_DAILY: time_add_day() returned invalid time");
|
||
return;
|
||
}
|
||
} while ((current < end) || (end == 0));
|
||
|
||
break;
|
||
|
||
case RECUR_WEEKLY:
|
||
do {
|
||
struct tm tm;
|
||
|
||
tm = *localtime (¤t);
|
||
|
||
if (time_in_range (current, start, end) && recur_in_range (current, ico->recur)) {
|
||
/* Weekdays to recur on are specified as a bitmask */
|
||
if (ico->recur->weekday & (1 << tm.tm_wday)) {
|
||
if (!generate (ico, current, cb, closure))
|
||
return;
|
||
}
|
||
}
|
||
|
||
/* Advance by day for scanning the week or by interval at week end */
|
||
|
||
if (tm.tm_wday == 6)
|
||
current = time_add_day (current, (ico->recur->interval - 1) * 7 + 1);
|
||
else
|
||
current = time_add_day (current, 1);
|
||
|
||
if (current == -1) {
|
||
g_warning ("RECUR_WEEKLY: time_add_day() returned invalid time\n");
|
||
return;
|
||
}
|
||
} while (current < end || (end == 0));
|
||
|
||
break;
|
||
|
||
case RECUR_MONTHLY_BY_POS:
|
||
/* FIXME: We only deal with positives now */
|
||
if (ico->recur->u.month_pos < 0) {
|
||
g_warning ("RECUR_MONTHLY_BY_POS does not support negative positions yet");
|
||
return;
|
||
}
|
||
|
||
if (ico->recur->u.month_pos == 0)
|
||
return;
|
||
|
||
first_week_day = /* ical_object_get_first_weekday (ico->recur->weekday); */
|
||
ico->recur->weekday; /* the i/f only lets you choose a single day of the week! */
|
||
|
||
/* This should not happen, but take it into account */
|
||
if (first_week_day == -1) {
|
||
g_warning ("ical_object_get_first_weekday() returned -1");
|
||
return;
|
||
}
|
||
|
||
do {
|
||
struct tm tm;
|
||
time_t t;
|
||
int week_day_start;
|
||
|
||
tm = *localtime (¤t);
|
||
tm.tm_mday = 1;
|
||
t = mktime (&tm);
|
||
tm = *localtime (&t);
|
||
week_day_start = tm.tm_wday;
|
||
|
||
tm.tm_mday = (7 * (ico->recur->u.month_pos - ((week_day_start <= first_week_day ) ? 1 : 0))
|
||
- (week_day_start - first_week_day) + 1);
|
||
if( tm.tm_mday > 31 )
|
||
{
|
||
tm.tm_mday = 1;
|
||
tm.tm_mon += ico->recur->interval;
|
||
current = mktime (&tm);
|
||
continue;
|
||
}
|
||
|
||
switch( tm.tm_mon )
|
||
{
|
||
case 3:
|
||
case 5:
|
||
case 8:
|
||
case 10:
|
||
if( tm.tm_mday > 30 )
|
||
{
|
||
tm.tm_mday = 1;
|
||
tm.tm_mon += ico->recur->interval;
|
||
current = mktime (&tm);
|
||
continue;
|
||
}
|
||
break;
|
||
case 1:
|
||
if( ((tm.tm_year+1900)%4) == 0
|
||
&& ((tm.tm_year+1900)%400) != 100
|
||
&& ((tm.tm_year+1900)%400) != 200
|
||
&& ((tm.tm_year+1900)%400) != 300 )
|
||
{
|
||
|
||
if( tm.tm_mday > 29 )
|
||
{
|
||
tm.tm_mday = 1;
|
||
tm.tm_mon += ico->recur->interval;
|
||
current = mktime (&tm);
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if( tm.tm_mday > 28 )
|
||
{
|
||
tm.tm_mday = 1;
|
||
tm.tm_mon += ico->recur->interval;
|
||
current = mktime (&tm);
|
||
continue;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
t = mktime (&tm);
|
||
|
||
if (time_in_range (t, start, end) && recur_in_range (current, ico->recur))
|
||
if (!generate (ico, t, cb, closure))
|
||
return;
|
||
|
||
/* Advance by the appropriate number of months */
|
||
|
||
current = mktime (&tm);
|
||
|
||
tm.tm_mday = 1;
|
||
tm.tm_mon += ico->recur->interval;
|
||
current = mktime (&tm);
|
||
|
||
if (current == -1) {
|
||
g_warning ("RECUR_MONTHLY_BY_DAY: mktime error\n");
|
||
return;
|
||
}
|
||
} while ((current < end) || (end == 0));
|
||
|
||
break;
|
||
|
||
case RECUR_MONTHLY_BY_DAY:
|
||
do {
|
||
struct tm tm;
|
||
time_t t;
|
||
int p;
|
||
|
||
tm = *localtime (¤t);
|
||
|
||
p = tm.tm_mday;
|
||
tm.tm_mday = ico->recur->u.month_day;
|
||
t = mktime (&tm);
|
||
if (time_in_range (t, start, end) && recur_in_range (current, ico->recur))
|
||
if (!generate (ico, t, cb, closure))
|
||
return;
|
||
|
||
/* Advance by the appropriate number of months */
|
||
|
||
tm.tm_mday = p;
|
||
tm.tm_mon += ico->recur->interval;
|
||
current = mktime (&tm);
|
||
|
||
if (current == -1) {
|
||
g_warning ("RECUR_MONTHLY_BY_DAY: mktime error\n");
|
||
return;
|
||
}
|
||
} while (current < end || (end == 0));
|
||
|
||
break;
|
||
|
||
case RECUR_YEARLY_BY_MONTH:
|
||
case RECUR_YEARLY_BY_DAY:
|
||
do {
|
||
if (time_in_range (current, start, end) && recur_in_range (current, ico->recur))
|
||
if (!generate (ico, current, cb, closure))
|
||
return;
|
||
|
||
/* Advance */
|
||
|
||
current = time_add_year (current, ico->recur->interval);
|
||
} while (current < end || (end == 0));
|
||
|
||
break;
|
||
|
||
default:
|
||
g_assert_not_reached ();
|
||
}
|
||
}
|
||
|
||
static int
|
||
duration_callback (iCalObject *ico, time_t start, time_t end, void *closure)
|
||
{
|
||
int *count = closure;
|
||
struct tm tm;
|
||
|
||
tm = *localtime (&start);
|
||
|
||
(*count)++;
|
||
if (ico->recur->duration == *count) {
|
||
ico->recur->enddate = time_day_end (end);
|
||
return 0;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
/* Computes ico->recur->enddate from ico->recur->duration */
|
||
void
|
||
ical_object_compute_end (iCalObject *ico)
|
||
{
|
||
int count = 0;
|
||
|
||
g_return_if_fail (ico->recur != NULL);
|
||
|
||
ico->recur->_enddate = 0;
|
||
ico->recur->enddate = 0;
|
||
ical_object_generate_events (ico, ico->dtstart, 0, duration_callback, &count);
|
||
}
|
||
|
||
int
|
||
alarm_compute_offset (CalendarAlarm *a)
|
||
{
|
||
if (!a->enabled)
|
||
return -1;
|
||
switch (a->units){
|
||
case ALARM_MINUTES:
|
||
a->offset = a->count * 60;
|
||
break;
|
||
case ALARM_HOURS:
|
||
a->offset = a->count * 3600;
|
||
break;
|
||
case ALARM_DAYS:
|
||
a->offset = a->count * 24 * 3600;
|
||
}
|
||
return a->offset;
|
||
}
|
||
|
||
|
||
/**
|
||
* ical_object_find_in_string:
|
||
* @uid: Unique identifier of the sought object.
|
||
* @vcalobj: String representation of a complete calendar object.
|
||
* @ico: The resulting #iCalObject is stored here.
|
||
*
|
||
* Parses a complete vCalendar object string and tries to find the calendar
|
||
* object that matches the specified @uid. If found, it stores the resulting
|
||
* #iCalObject in the @ico parameter.
|
||
*
|
||
* Return value: A result code depending on whether the parse and search were
|
||
* successful.
|
||
**/
|
||
CalObjFindStatus
|
||
ical_object_find_in_string (const char *uid, const char *vcalobj, iCalObject **ico)
|
||
{
|
||
#if 0
|
||
icalcomponent* comp = NULL;
|
||
icalcomponent *subcomp;
|
||
iCalObject *ical;
|
||
|
||
g_return_val_if_fail (vcalobj != NULL, CAL_OBJ_FIND_NOT_FOUND);
|
||
|
||
comp = icalparser_parse_string (vcalobj);
|
||
|
||
if (!comp) {
|
||
printf ("CAL_OBJ_FIND_SYNTAX_ERROR #1\n");
|
||
return CAL_OBJ_FIND_SYNTAX_ERROR;
|
||
}
|
||
|
||
subcomp = icalcomponent_get_first_component (comp,
|
||
ICAL_ANY_COMPONENT);
|
||
if (!subcomp) {
|
||
printf ("CAL_OBJ_FIND_SYNTAX_ERROR #2\n");
|
||
return CAL_OBJ_FIND_SYNTAX_ERROR;
|
||
}
|
||
|
||
while (subcomp) {
|
||
ical = ical_object_create_from_icalcomponent (subcomp);
|
||
if (ical->type != ICAL_EVENT &&
|
||
ical->type != ICAL_TODO &&
|
||
ical->type != ICAL_JOURNAL) {
|
||
g_warning ("Skipping unsupported iCalendar component");
|
||
} else {
|
||
if (strcasecmp (ical->uid, uid) == 0) {
|
||
(*ico) = ical;
|
||
(*ico)->ref_count = 1;
|
||
printf ("CAL_OBJ_FIND_SUCCESS\n");
|
||
|
||
printf ("ical_object_find_in_string:\n");
|
||
printf ("-----------------------------------------------------\n");
|
||
dump_icalobject (*ico);
|
||
printf ("-----------------------------------------------------\n");
|
||
|
||
|
||
return CAL_OBJ_FIND_SUCCESS;
|
||
}
|
||
}
|
||
subcomp = icalcomponent_get_next_component (comp,
|
||
ICAL_ANY_COMPONENT);
|
||
}
|
||
|
||
printf ("CAL_OBJ_FIND_NOT_FOUND\n");
|
||
return CAL_OBJ_FIND_NOT_FOUND;
|
||
|
||
#else /* 1 */
|
||
VObject *vcal;
|
||
VObjectIterator i;
|
||
CalObjFindStatus status;
|
||
|
||
g_return_val_if_fail (uid != NULL, CAL_OBJ_FIND_SYNTAX_ERROR);
|
||
g_return_val_if_fail (vcalobj != NULL, CAL_OBJ_FIND_SYNTAX_ERROR);
|
||
g_return_val_if_fail (ico != NULL, CAL_OBJ_FIND_SYNTAX_ERROR);
|
||
|
||
*ico = NULL;
|
||
status = CAL_OBJ_FIND_NOT_FOUND;
|
||
|
||
vcal = Parse_MIME (vcalobj, strlen (vcalobj));
|
||
|
||
if (!vcal)
|
||
return CAL_OBJ_FIND_SYNTAX_ERROR;
|
||
|
||
initPropIterator (&i, vcal);
|
||
|
||
while (moreIteration (&i)) {
|
||
VObject *vobj;
|
||
VObject *uid_prop;
|
||
char *the_str;
|
||
|
||
vobj = nextVObject (&i);
|
||
|
||
uid_prop = isAPropertyOf (vobj, VCUniqueStringProp);
|
||
if (!uid_prop)
|
||
continue;
|
||
|
||
/* str_val() sets the_str to the string representation of the
|
||
* property.
|
||
*/
|
||
str_val (uid_prop);
|
||
|
||
if (strcmp (the_str, uid) == 0) {
|
||
const char *object_name;
|
||
|
||
object_name = vObjectName (vobj);
|
||
*ico = ical_object_create_from_vobject (vobj, object_name);
|
||
|
||
if (*ico)
|
||
status = CAL_OBJ_FIND_SUCCESS;
|
||
}
|
||
|
||
free (the_str);
|
||
|
||
if (status == CAL_OBJ_FIND_SUCCESS)
|
||
break;
|
||
}
|
||
|
||
cleanVObject (vcal);
|
||
cleanStrTbl ();
|
||
|
||
return status;
|
||
#endif /* 1 */
|
||
}
|
||
|
||
|
||
#if 1
|
||
/* Creates a VObject with the base information of a calendar */
|
||
static VObject *
|
||
get_calendar_base_vobject (void)
|
||
{
|
||
VObject *vobj;
|
||
time_t now;
|
||
struct tm tm;
|
||
|
||
/* We call localtime for the side effect of setting tzname */
|
||
|
||
now = time (NULL);
|
||
tm = *localtime (&now);
|
||
|
||
vobj = newVObject (VCCalProp);
|
||
|
||
addPropValue (vobj, VCProdIdProp, PRODID);
|
||
|
||
#if defined (HAVE_TM_ZONE)
|
||
addPropValue (vobj, VCTimeZoneProp, tm.tm_zone);
|
||
#elif defined (HAVE_TZNAME)
|
||
addPropValue (vobj, VCTimeZoneProp, tzname[0]);
|
||
#endif
|
||
|
||
/* Per the vCalendar spec, this must be "1.0" */
|
||
addPropValue (vobj, VCVersionProp, "1.0");
|
||
|
||
return vobj;
|
||
}
|
||
#endif /* 0 */
|
||
|
||
/**
|
||
* ical_object_to_string:
|
||
* @ico: A calendar object.
|
||
*
|
||
* Converts a vCalendar object to its string representation. It is wrapped
|
||
* inside a complete VCALENDAR object because other auxiliary information such
|
||
* as timezones may appear there.
|
||
*
|
||
* Return value: String representation of the object.
|
||
**/
|
||
char *
|
||
ical_object_to_string (iCalObject *ico)
|
||
{
|
||
#if 0
|
||
icalcomponent *top = icalcomponent_new (ICAL_VCALENDAR_COMPONENT);
|
||
char *out_cal_string;
|
||
icalcomponent *comp;
|
||
|
||
printf ("ical_object_to_string:\n");
|
||
printf ("-----------------------------------------------------\n");
|
||
dump_icalobject (ico);
|
||
printf ("-----------------------------------------------------\n");
|
||
|
||
comp = icalcomponent_create_from_ical_object (ico);
|
||
icalcomponent_add_component (top, comp);
|
||
out_cal_string = icalcomponent_as_ical_string (top);
|
||
return g_strdup (out_cal_string);
|
||
|
||
#else /* 1 */
|
||
VObject *vcalobj, *vobj;
|
||
char *buf, *gbuf;
|
||
|
||
vcalobj = get_calendar_base_vobject ();
|
||
vobj = ical_object_to_vobject (ico);
|
||
addVObjectProp (vcalobj, vobj);
|
||
|
||
buf = writeMemVObject (NULL, NULL, vcalobj);
|
||
|
||
cleanVObject (vcalobj);
|
||
cleanStrTbl ();
|
||
|
||
/* We have to g_strdup() it because libversit uses malloc()/realloc(),
|
||
* and we want clients to be able to use g_free(). Sigh.
|
||
*/
|
||
gbuf = g_strdup (buf);
|
||
free (buf);
|
||
|
||
return gbuf;
|
||
#endif /* 1 */
|
||
}
|
||
|
||
|
||
/**
|
||
* ical_object_compare_dates:
|
||
* @ico1: A calendar event.
|
||
* @ico2: A calendar event to compare with @ico1.
|
||
*
|
||
* Returns TRUE if the dates of both objects match, including any recurrence
|
||
* rules. Both calendar objects must have a type of ICAL_EVENT.
|
||
*
|
||
* Return value: TRUE if both calendar objects have the same dates.
|
||
**/
|
||
gboolean
|
||
ical_object_compare_dates (iCalObject *ico1,
|
||
iCalObject *ico2)
|
||
{
|
||
Recurrence *recur1, *recur2;
|
||
gint num_exdates;
|
||
GList *elem1, *elem2;
|
||
time_t *time1, *time2;
|
||
|
||
g_return_val_if_fail (ico1 != NULL, FALSE);
|
||
g_return_val_if_fail (ico2 != NULL, FALSE);
|
||
g_return_val_if_fail (ico1->type == ICAL_EVENT, FALSE);
|
||
g_return_val_if_fail (ico2->type == ICAL_EVENT, FALSE);
|
||
|
||
/* First check the base dates. */
|
||
if (ico1->dtstart != ico2->dtstart
|
||
|| ico1->dtend != ico2->dtend)
|
||
return FALSE;
|
||
|
||
recur1 = ico1->recur;
|
||
recur2 = ico2->recur;
|
||
|
||
/* If the event doesn't recur, we already know it matches. */
|
||
if (!recur1 && !recur2)
|
||
return TRUE;
|
||
|
||
/* Check that both recur. */
|
||
if (!(recur1 && recur2))
|
||
return FALSE;
|
||
|
||
/* Now we need to see if the recurrence rules are the same. */
|
||
if (recur1->type != recur2->type
|
||
|| recur1->interval != recur2->interval
|
||
|| recur1->enddate != recur2->enddate
|
||
|| recur1->weekday != recur2->weekday
|
||
|| recur1->duration != recur2->duration
|
||
|| recur1->_enddate != recur2->_enddate
|
||
|| recur1->__count != recur2->__count)
|
||
return FALSE;
|
||
|
||
switch (recur1->type) {
|
||
case RECUR_MONTHLY_BY_POS:
|
||
if (recur1->u.month_pos != recur2->u.month_pos)
|
||
return FALSE;
|
||
break;
|
||
case RECUR_MONTHLY_BY_DAY:
|
||
if (recur1->u.month_day != recur2->u.month_day)
|
||
return FALSE;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
/* Now check if the excluded dates match. */
|
||
num_exdates = g_list_length (ico1->exdate);
|
||
if (g_list_length (ico2->exdate) != num_exdates)
|
||
return FALSE;
|
||
if (num_exdates == 0)
|
||
return TRUE;
|
||
|
||
ico1->exdate = g_list_sort (ico1->exdate, compare_exdates);
|
||
ico2->exdate = g_list_sort (ico2->exdate, compare_exdates);
|
||
|
||
elem1 = ico1->exdate;
|
||
elem2 = ico2->exdate;
|
||
while (elem1) {
|
||
time1 = (time_t*) elem1->data;
|
||
time2 = (time_t*) elem2->data;
|
||
|
||
if (*time1 != *time2)
|
||
return FALSE;
|
||
|
||
elem1 = elem1->next;
|
||
elem2 = elem2->next;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
static gint
|
||
compare_exdates (gconstpointer a, gconstpointer b)
|
||
{
|
||
const time_t *ca = a, *cb = b;
|
||
time_t diff = *ca - *cb;
|
||
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
|
||
}
|
||
|
||
|
||
/* Converts any CR/LF sequences in the summary field to spaces so we just
|
||
have a one-line field. The iCalObjects summary field is changed. */
|
||
static void
|
||
ical_object_normalize_summary (iCalObject *ico)
|
||
{
|
||
gchar *src, *dest, ch;
|
||
gboolean just_output_space = FALSE;
|
||
|
||
src = dest = ico->summary;
|
||
while ((ch = *src++)) {
|
||
if (ch == '\n' || ch == '\r') {
|
||
/* We only output 1 space for each sequence of CR & LF
|
||
characters. */
|
||
if (!just_output_space) {
|
||
*dest++ = ' ';
|
||
just_output_space = TRUE;
|
||
}
|
||
} else {
|
||
*dest++ = ch;
|
||
just_output_space = FALSE;
|
||
}
|
||
}
|
||
*dest = '\0';
|
||
}
|
||
|
||
|
||
void dump_icalobject (iCalObject *ico)
|
||
{
|
||
if (!ico) {
|
||
printf ("<<NULL>>\n");
|
||
return;
|
||
}
|
||
|
||
printf ("type ");
|
||
switch (ico->type) {
|
||
case ICAL_EVENT: printf ("event"); break;
|
||
case ICAL_TODO: printf ("todo"); break;
|
||
case ICAL_JOURNAL: printf ("journal"); break;
|
||
case ICAL_FBREQUEST: printf ("fbrequest"); break;
|
||
case ICAL_FBREPLY: printf ("fbreply"); break;
|
||
case ICAL_BUSYTIME: printf ("busytime"); break;
|
||
case ICAL_TIMEZONE: printf ("timezone"); break;
|
||
}
|
||
printf ("\n");
|
||
|
||
printf ("attach-length %d\n", g_list_length (ico->attach));
|
||
|
||
printf ("attendee-length %d\n", g_list_length (ico->attendee));
|
||
|
||
printf ("catagories-length %d\n", g_list_length (ico->categories));
|
||
|
||
printf ("class '%s'\n", ico->class ? ico->class : "NULL");
|
||
|
||
printf ("comment '%s'\n", ico->comment ? ico->comment : "NULL");
|
||
|
||
printf ("completed %ld=%s",
|
||
ico->completed, ctime (&ico->completed));
|
||
|
||
printf ("created %ld=%s", ico->created, ctime (&ico->created));
|
||
|
||
printf ("contact-length %d\n", g_list_length (ico->contact));
|
||
|
||
printf ("desc '%s'\n", ico->desc ? ico->desc : "NULL");
|
||
|
||
printf ("dtstamp %ld=%s", ico->dtstamp, ctime (&ico->dtstamp));
|
||
|
||
printf ("dtstart %ld=%s", ico->dtstart, ctime (&ico->dtstart));
|
||
|
||
printf ("dtend %ld=%s", ico->dtend, ctime (&ico->dtend));
|
||
|
||
printf ("date_only %d\n", ico->date_only);
|
||
|
||
printf ("exdate-length %d\n", g_list_length (ico->exdate));
|
||
|
||
printf ("exrule-length %d\n", g_list_length (ico->exrule));
|
||
|
||
printf ("iCalGeo %d %f %f\n",
|
||
ico->geo.valid, ico->geo.latitude, ico->geo.longitude);
|
||
|
||
printf ("last_mod %ld=%s", ico->last_mod, ctime (&ico->last_mod));
|
||
|
||
printf ("location '%s'\n", ico->location ? ico->location : "NULL");
|
||
|
||
printf ("organizer %p\n", ico->organizer);
|
||
|
||
printf ("percent %d\n", ico->percent);
|
||
|
||
printf ("priority %d\n", ico->priority);
|
||
|
||
printf ("rstatus '%s'\n", ico->rstatus ? ico->rstatus : "NULL");
|
||
|
||
printf ("related-length %d\n", g_list_length (ico->related));
|
||
|
||
printf ("resources-length %d\n", g_list_length (ico->resources));
|
||
|
||
printf ("rdate-length %d\n", g_list_length (ico->rdate));
|
||
|
||
printf ("rrule-length %d\n", g_list_length (ico->rrule));
|
||
|
||
printf ("seq %d\n", ico->seq);
|
||
|
||
printf ("status '%s'\n", ico->status ? ico->status : "NULL");
|
||
|
||
printf ("summary '%s'\n", ico->summary ? ico->summary : "NULL");
|
||
|
||
printf ("transp ");
|
||
switch (ico->transp) {
|
||
case ICAL_OPAQUE: printf ("opaque"); break;
|
||
case ICAL_TRANSPARENT: printf ("transparent"); break;
|
||
}
|
||
printf ("\n");
|
||
|
||
printf ("uid '%s'\n", ico->uid ? ico->uid : "NULL");
|
||
|
||
printf ("url '%s'\n", ico->url ? ico->url : "NULL");
|
||
|
||
printf ("recurid %ld=%s", ico->recurid, ctime (&ico->recurid));
|
||
|
||
printf ("dalarm %d\n", ico->dalarm.enabled);
|
||
|
||
printf ("aalarm %d\n", ico->aalarm.enabled);
|
||
|
||
printf ("palarm %d\n", ico->palarm.enabled);
|
||
|
||
printf ("malarm %d\n", ico->malarm.enabled);
|
||
|
||
printf ("alarms-length %d\n", g_list_length (ico->alarms));
|
||
|
||
printf ("recur %p\n", ico->recur);
|
||
|
||
printf ("new %d\n", ico->new);
|
||
|
||
printf ("user_data %p\n", ico->user_data);
|
||
|
||
printf ("ref_count %d\n", ico->ref_count);
|
||
}
|