Files
evolution/calendar/calobj.c
Federico Mena Quintero ca082de77a Added get_uids() method to get a list of UIDs based on object types.
2000-02-08  Federico Mena Quintero  <federico@helixcode.com>

	* evolution-calendar.idl (Cal): Added get_uids() method to get a
	list of UIDs based on object types.

	* cal-backend.c (cal_backend_get_uids): Implemented get_uids() in
	the backend.

	* cal.c (Cal_get_uids): Implemented get_uids() method.

	* cal-client.c (cal_client_get_uids): Implemented client-side
	function.

	* cal-util.c (cal_obj_instance_list_free): Doh.  Free the list,
	not the last link.
	(cal_obj_uid_list_free): New function to free a list of UIDs.

	* GnomeCal.idl (Repository): Removed unused method
	get_object_by_id_list().  This is just for cleanup purposes and to
	remind me exactly of what needs to be moved over to
	evolution-calendar.idl.
	(Repository): Removed unused get_objects() method.

	* corba-cal.c (init_calendar_repo_class): Removed the unused
	get_objects method.

	* calobj.h (CalObjFindStatus): New status value enumeration for
	the find function.

	* calobj.c (ical_object_find_in_string): New function to parse a
	complete calendar and find a calendar object in it.  This should
	be used instead ical_object_new_from_string() in the future.

	* evolution-calendar.idl (CalObjInstance): Added an uid field.
	Now the idea is that whenever calendar object strings are passed
	around, their UIDs are passed along with them so that the actual
	object can be pulled from the whole VCAL object using its UID to
	identify it.

	* cal-util.h (CalObjInstance): Added uid field.

	* cal-util.c (cal_obj_instance_list_free): Free the UIDs.

	* cal-backend.c (build_event_list): Store the object's UID in the
	instance structure.

	* cal.c (Cal_get_events_in_range): Copy the UID field to the CORBA
	structure.

	* cal-client.c (cal_client_get_events_in_range): Copy the UID
	field from the CORBA structure.

	* main.c (gnome_cal_file_menu): Removed unfinished html-month stuff.

	* Makefile.am (gnomecal_SOURCES): Removed html-month.c.

	* gnome-cal.c: #include "alarm.h"
	(mail_notify): Made static.

	* alarm.h: #include "calobj.h"

	* corba-cal-factory.h (init_corba_server): Fixed prototype.

	* quick-view.c (create_items_for_event): Made static.

	* gncal-todo.c (column_resized): Made static.

	* layout.c (find_index): Made static.

svn path=/trunk/; revision=1699
2000-02-09 08:04:33 +00:00

1568 lines
32 KiB
C

/*
* 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 "calobj.h"
#include "timeutil.h"
#include "../libversit/vcc.h"
extern CalendarAlarm alarm_defaults[4];
static 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;
return ico;
}
static void
default_alarm (iCalObject *ical, CalendarAlarm *alarm, char *def_mail, enum AlarmType type)
{
alarm->type = type;
alarm->enabled = alarm_defaults[type].enabled;
alarm->count = alarm_defaults[type].count;
alarm->units = alarm_defaults[type].units;
if (alarm_defaults[type].data)
alarm->data = g_strdup (alarm_defaults[type].data);
else
alarm->data = g_strdup ("");
}
iCalObject *
ical_new (char *comment, char *organizer, char *summary)
{
iCalObject *ico;
ico = ical_object_new ();
ico->comment = g_strdup (comment);
ico->organizer = g_strdup (organizer);
ico->summary = g_strdup (summary);
ico->class = g_strdup ("PUBLIC");
ico->status = g_strdup ("NEEDS ACTION");
default_alarm (ico, &ico->dalarm, organizer, ALARM_DISPLAY);
default_alarm (ico, &ico->palarm, organizer, ALARM_PROGRAM);
default_alarm (ico, &ico->malarm, organizer, ALARM_MAIL);
default_alarm (ico, &ico->aalarm, organizer, ALARM_AUDIO);
return 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);
}
#define free_if_defined(x) if (x){ g_free (x); x = 0; }
#define lfree_if_defined(x) if (x){ list_free (x); x = 0; }
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);
}
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
*/
iCalObject *
ical_object_duplicate (iCalObject *o)
{
VObject *vo;
iCalObject *new;
/* FIXME!!!!! The UID needs to change!!! */
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;
}
/* 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);
} 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 = "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_strdup (str_val (vo));
free (the_str);
}
/* related */
if (has (o, VCRelatedToProp)){
ical->related = set_list (str_val (vo));
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)){
ical->url = g_strdup (str_val (vo));
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_destroy (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_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 (strlen (ical->summary)){
s = addPropValue (o, VCSummaryProp, ical->summary);
if (strchr (ical->summary, '\n'))
addProp (s, VCQuotedPrintableProp);
} else {
addPropValue (o, VCSummaryProp, _("Appointment"));
}
/* 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));
/* Owenr/organizer */
if (ical->organizer)
addPropValue (o, VCOrgNameProp, ical->organizer);
/* related */
if (ical->related)
store_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;
}
static int
generate (iCalObject *ico, time_t reference, calendarfn cb, void *closure)
{
struct tm dt_start, dt_end, ref;
time_t s_t, e_t;
dt_start = *localtime (&ico->dtstart);
dt_end = *localtime (&ico->dtend);
ref = *localtime (&reference);
dt_start.tm_mday = ref.tm_mday;
dt_start.tm_mon = ref.tm_mon;
dt_start.tm_year = ref.tm_year;
dt_end.tm_mday = ref.tm_mday;
dt_end.tm_mon = ref.tm_mon;
dt_end.tm_year = ref.tm_year;
if (ref.tm_isdst > dt_start.tm_isdst){
dt_start.tm_hour--;
dt_end.tm_hour--;
} else if (ref.tm_isdst < dt_start.tm_isdst){
dt_start.tm_hour++;
dt_end.tm_hour++;
}
s_t = mktime (&dt_start);
if (ico->exdate && is_date_in_list (ico->exdate, &dt_start))
return 1;
e_t = mktime (&dt_end);
if ((s_t == -1) || (e_t == -1)) {
g_warning ("Produced invalid dates!\n");
return 0;
}
return (*cb) (ico, s_t, e_t, 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))) {
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);
}
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 (&current);
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 (&current);
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 (&current);
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;
}
iCalObject *
ical_object_new_from_string (const char *vcal_string)
{
iCalObject *ical = NULL;
VObject *cal, *event;
VObjectIterator i;
const char *object_name;
cal = Parse_MIME (vcal_string, strlen (vcal_string));
initPropIterator (&i, cal);
while (moreIteration (&i)){
event = nextVObject (&i);
object_name = vObjectName (event);
if (strcmp (object_name, VCEventProp) == 0){
ical = ical_object_create_from_vobject (event, object_name);
break;
}
}
cleanVObject (cal);
cleanStrTbl ();
return ical;
}
/**
* 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)
{
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;
}