Exemple #1
0
void MainWindow::loadFromIcsFile()
{
    char* line;
    icalcomponent *c;
    icalparser *parser = icalparser_new();

    FILE* stream = fopen("/home/quentin/Public/test.ics", "r");

    icalparser_set_gen_data(parser, stream);

    do
    {
        line = icalparser_get_line(parser, read_stream);
        c = icalparser_add_line(parser, line);

        if (c != 0)
        {
            icalcomponent *inner = icalcomponent_get_first_component(c, ICAL_ANY_COMPONENT);
            while (inner != NULL)
            {
                if (icalcomponent_isa(inner) == ICAL_VTODO_COMPONENT)
                {
                    const char* name   = icalcomponent_get_summary(inner);
                    const char* uid    = icalcomponent_get_uid(inner);
                    icalproperty *p    = icalcomponent_get_first_property(inner, ICAL_RELATEDTO_PROPERTY);
                    const char* parent = icalproperty_get_relatedto(p);
                    addTask(name, uid, parent);
                }
                if (icalcomponent_isa(inner) == ICAL_VEVENT_COMPONENT)
                {
                    const char* name   = icalcomponent_get_summary(inner);
                    const char* uid    = icalcomponent_get_uid(inner);
                    icalproperty *p    = icalcomponent_get_first_property(inner, ICAL_RELATEDTO_PROPERTY);
                    const char* parent = icalproperty_get_relatedto(p);
                    p                  = icalcomponent_get_first_property(inner, ICAL_DTSTART_PROPERTY);
                    icaltimetype start = icalproperty_get_dtstart(p);
                    p                  = icalcomponent_get_first_property(inner, ICAL_DTEND_PROPERTY);
                    icaltimetype end   = icalproperty_get_dtend(p);
                    Task* task         = findTask(parent);
                    addEvent(task, uid, name, start, end);
                }
                inner = icalcomponent_get_next_component(c, ICAL_ANY_COMPONENT);
            }
        }

    } while (line != 0);
}
Exemple #2
0
int cond_ICalHaveTimeItem(StrBuf *Target, WCTemplputParams *TP)
{
	icalcomponent *cal = (icalcomponent *) CTX(CTX_ICAL);
	icalproperty *p;
	icalproperty_kind Kind;

	Kind = (icalproperty_kind) GetTemplateTokenNumber(Target, TP, 2, ICAL_ANY_PROPERTY);
	p = icalcomponent_get_first_property(cal, Kind);
	if (p != NULL) {
		struct icaltimetype *t;
		struct icaltimetype tt;
		WCTemplputParams *DynamicTP;

		DynamicTP = (WCTemplputParams*) malloc(sizeof(WCTemplputParams) + 
						       sizeof(struct icaltimetype));
		t = (struct icaltimetype *) &DynamicTP[1];
		memset(&tt, 0, sizeof(struct icaltimetype));
		switch (Kind)
		{
		case ICAL_DTSTART_PROPERTY:
			tt = icalproperty_get_dtstart(p);
			break;
		case ICAL_DTEND_PROPERTY:
			tt = icalproperty_get_dtend(p);
			break;
		default:
			break;
		}
		memcpy(t, &tt, sizeof(struct icaltimetype));

		StackDynamicContext (TP, 
				     DynamicTP, 
				     t,
				     CTX_ICALTIME,
				     0,
				     TP->Tokens,
				     ReleaseIcalTimeCtx,
				     TP->Tokens->Params[1]->lvalue);

		return 1;
	}
	return 0;
}
void test_recur_file()
{
    icalset *cin = 0;
    struct icaltimetype next;
    icalcomponent *itr;
    icalproperty *desc, *dtstart, *rrule;
    struct icalrecurrencetype recur;
    icalrecur_iterator* ritr;
    time_t tt;
    char* file; 
    int num_recurs_found = 0;
    icalfileset_options options = {O_RDONLY, 0644, 0};
	
    icalerror_set_error_state(ICAL_PARSE_ERROR, ICAL_ERROR_NONFATAL);
	
#ifndef WIN32
    signal(SIGALRM,sig_alrm);
#endif
    file = getenv("ICAL_RECUR_FILE");
    if (!file)
      file = TEST_DATADIR "/recur.txt";
	
#ifndef WIN32
    alarm(15); /* to get file lock */
#endif
    cin = icalset_new(ICAL_FILE_SET, file, &options);
#ifndef WIN32
    alarm(0);
#endif
	
    ok("opening file with recurring events", (cin!=NULL));
    assert(cin!=NULL);
	
    for (itr = icalfileset_get_first_component(cin);
	itr != 0;
	itr = icalfileset_get_next_component(cin)){
      int badcomp = 0;
      int expected_events = 0;
      char msg[128];


      struct icaltimetype start = icaltime_null_time();
      struct icaltimetype startmin = icaltime_from_timet(1,0);
      struct icaltimetype endmax = icaltime_null_time();
      const char *desc_str = "malformed component";

      desc = icalcomponent_get_first_property(itr,ICAL_DESCRIPTION_PROPERTY);
      dtstart = icalcomponent_get_first_property(itr,ICAL_DTSTART_PROPERTY);
      rrule = icalcomponent_get_first_property(itr,ICAL_RRULE_PROPERTY);
      if (desc) {
	desc_str = icalproperty_get_description(desc);
      }
      
      ok((char*)desc_str, !(desc == 0 || dtstart == 0 || rrule == 0));

      if (desc == 0 || dtstart == 0 || rrule == 0) {
	badcomp = 1;
	if (VERBOSE) {
	  printf("\n******** Error in input component ********\n");
	  printf("The following component is malformed:\n %s\n", desc_str);
	}
	continue;
      }
      if (VERBOSE) {
	printf("\n\n#### %s\n",desc_str);
	printf("#### %s\n",icalvalue_as_ical_string(icalproperty_get_value(rrule)));
      }
      
      recur = icalproperty_get_rrule(rrule);
      start = icalproperty_get_dtstart(dtstart);
      
      ritr = icalrecur_iterator_new(recur,start);
      
      tt = icaltime_as_timet(start);
      
      if (VERBOSE)
	printf("#### %s\n",ctime(&tt ));
      
      icalrecur_iterator_free(ritr);
      
      for(ritr = icalrecur_iterator_new(recur,start),
	    next = icalrecur_iterator_next(ritr); 
	  !icaltime_is_null_time(next);
	  next = icalrecur_iterator_next(ritr)){
	
	tt = icaltime_as_timet(next);
	
	if (VERBOSE)
	  printf("  %s",ctime(&tt ));		
	
      }

      icalrecur_iterator_free(ritr);
      num_recurs_found = 0;
      expected_events = get_expected_numevents(itr);

      icalcomponent_foreach_recurrence(itr, startmin, endmax, 
				       recur_callback, &num_recurs_found);
      
      sprintf(msg,"   expecting total of %d events", expected_events);
      int_is(msg, num_recurs_found, expected_events);
    }
    
    icalset_free(cin);
}
Exemple #4
0
icalcomponent *icalbdbsetiter_to_next(icalset *set, icalsetiter *i)
{
    icalcomponent *comp = NULL;
    struct icaltimetype start, next;
    icalproperty *dtstart, *rrule, *prop, *due;
    struct icalrecurrencetype recur;
    icaltimezone *u_zone;
    int g = 0;
    int orig_time_was_utc = 0;

    _unused(set);

    do {

        /* no pending occurrence, read the next component */
        if (i->last_component == NULL) {
            comp = icalcompiter_next(&(i->iter));
        } else {
            comp = i->last_component;
        }

        /* no next component, simply return */
        if (comp == 0) {
            return NULL;
        }
        if (i->gauge == 0) {
            return comp;
        }

        /* finding the next matched component and return it to the caller */

        rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
        g = icalgauge_get_expand(i->gauge);

        /* a recurring component with expand query */
        if (rrule != 0 && g == 1) {

            u_zone = icaltimezone_get_builtin_timezone(i->tzid);

            /* use UTC, if that's all we have. */
            if (!u_zone) {
                u_zone = icaltimezone_get_utc_timezone();
            }

            recur = icalproperty_get_rrule(rrule);
            start = icaltime_from_timet(time(0), 0);

            if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
                dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
                if (dtstart) {
                    start = icalproperty_get_dtstart(dtstart);
                }
            } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
                due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
                if (due) {
                    start = icalproperty_get_due(due);
                }
            }

            /* Convert to the user's timezone in order to be able to compare
             * the results from the rrule iterator. */
            if (icaltime_is_utc(start)) {
                start = icaltime_convert_to_zone(start, u_zone);
                orig_time_was_utc = 1;
            }

            if (i->ritr == NULL) {
                i->ritr = icalrecur_iterator_new(recur, start);
                next = icalrecur_iterator_next(i->ritr);
                i->last_component = comp;
            } else {
                next = icalrecur_iterator_next(i->ritr);
                if (icaltime_is_null_time(next)) {
                    i->last_component = NULL;
                    icalrecur_iterator_free(i->ritr);
                    i->ritr = NULL;
                    /* no more occurrence, should go to get next component */
                    continue;
                } else {
                    i->last_component = comp;
                }
            }

            /* if it is excluded, do next one */
            if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
                next = icalrecur_iterator_next(i->ritr);
                continue;
            }

            /* set recurrence-id value to the property if the property already exist;
             * add the recurrence id property and the value if the property does not exist */
            prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
            if (prop == 0) {
                icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
            } else {
                icalproperty_set_recurrenceid(prop, next);
            }

            if (orig_time_was_utc) {
                next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
            }
        }
        /* end of recurring event with expand query */
        if (comp != 0 && (i->gauge == 0 || icalgauge_compare(i->gauge, comp) == 1)) {
            /* found a matched, return it */
            return comp;
        }
    } while (comp != 0);

    return NULL;        /*unreachable */
}
Exemple #5
0
icalcomponent *icalbdbset_form_a_matched_recurrence_component(icalsetiter *itr)
{
    icalcomponent *comp = NULL;
    struct icaltimetype start, next;
    icalproperty *dtstart, *rrule, *prop, *due;
    struct icalrecurrencetype recur;
    icaltimezone *u_zone;
    int orig_time_was_utc = 0;

    comp = itr->last_component;

    if (comp == NULL || itr->gauge == NULL) {
        return NULL;
    }

    rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
    /* if there is no RRULE, simply return to the caller */
    if (rrule == NULL) {
        return NULL;
    }

    u_zone = icaltimezone_get_builtin_timezone(itr->tzid);

    /* use UTC, if that's all we have. */
    if (!u_zone) {
        u_zone = icaltimezone_get_utc_timezone();
    }

    recur = icalproperty_get_rrule(rrule);
    start = icaltime_from_timet(time(0), 0);

    if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
        dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
        if (dtstart) {
            start = icalproperty_get_dtstart(dtstart);
        }
    } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
        due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
        if (due) {
            start = icalproperty_get_due(due);
        }
    }

    /* Convert to the user's timezone in order to be able to compare the results
     * from the rrule iterator. */
    if (icaltime_is_utc(start)) {
        start = icaltime_convert_to_zone(start, u_zone);
        orig_time_was_utc = 1;
    }

    if (itr->ritr == NULL) {
        itr->ritr = icalrecur_iterator_new(recur, start);
        next = icalrecur_iterator_next(itr->ritr);
        itr->last_component = comp;
    } else {
        next = icalrecur_iterator_next(itr->ritr);
        if (icaltime_is_null_time(next)) {
            /* no more recurrence, returns */
            itr->last_component = NULL;
            icalrecur_iterator_free(itr->ritr);
            itr->ritr = NULL;
            /* no more pending matched occurrence,
             * all the pending matched occurrences have been returned */
            return NULL;
        } else {
            itr->last_component = comp;
        }
    }

    /* if it is excluded, return NULL to the caller */
    if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
        (void)icalrecur_iterator_next(itr->ritr);
        return NULL;
    }

    /* set recurrence-id value to the property if the property already exist;
     * add the recurrence id property and the value if the property does not exist */
    prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
    if (prop == 0) {
        icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
    } else {
        icalproperty_set_recurrenceid(prop, next);
    }

    if (orig_time_was_utc) {
        next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
    }

    if (itr->gauge == 0 || icalgauge_compare(itr->gauge, comp) == 1) {
        /* find a matched and return it */
        return comp;
    }

    /* not matched */
    return NULL;
}
Exemple #6
0
icalsetiter icalbdbset_begin_component(icalset *set, icalcomponent_kind kind,
                                       icalgauge *gauge, const char *tzid)
{
    icalsetiter itr = icalsetiter_null;
    icalcomponent *comp = NULL;
    icalcompiter citr;
    icalbdbset *bset;
    struct icaltimetype start, next;
    icalproperty *dtstart, *rrule, *prop, *due;
    struct icalrecurrencetype recur;
    icaltimezone *u_zone;
    int g = 0;
    int orig_time_was_utc = 0;

    icalerror_check_arg_re((set != 0), "set", icalsetiter_null);
    bset = (icalbdbset *) set;

    itr.gauge = gauge;
    itr.tzid = tzid;

    citr = icalcomponent_begin_component(bset->cluster, kind);
    comp = icalcompiter_deref(&citr);

    if (gauge == 0) {
        itr.iter = citr;
        return itr;
    }

    /* if there is a gauge, the first matched component is returned */
    while (comp != 0) {

        /* check if it is a recurring component and with guage expand, if so
         * we need to add recurrence-id property to the given component */
        rrule = icalcomponent_get_first_property(comp, ICAL_RRULE_PROPERTY);
        g = icalgauge_get_expand(gauge);

        if (rrule != 0 && g == 1) {

            /* it is a recurring event */

            u_zone = icaltimezone_get_builtin_timezone(itr.tzid);

            /* use UTC, if that's all we have. */
            if (!u_zone) {
                u_zone = icaltimezone_get_utc_timezone();
            }

            recur = icalproperty_get_rrule(rrule);
            start = icaltime_from_timet(time(0), 0);

            if (icalcomponent_isa(comp) == ICAL_VEVENT_COMPONENT) {
                dtstart = icalcomponent_get_first_property(comp, ICAL_DTSTART_PROPERTY);
                if (dtstart) {
                    start = icalproperty_get_dtstart(dtstart);
                }
            } else if (icalcomponent_isa(comp) == ICAL_VTODO_COMPONENT) {
                due = icalcomponent_get_first_property(comp, ICAL_DUE_PROPERTY);
                if (due) {
                    start = icalproperty_get_due(due);
                }
            }

            /* Convert to the user's timezone in order to be able to compare
             * the results from the rrule iterator. */
            if (icaltime_is_utc(start)) {
                start = icaltime_convert_to_zone(start, u_zone);
                orig_time_was_utc = 1;
            }

            if (itr.last_component == NULL) {
                itr.ritr = icalrecur_iterator_new(recur, start);
                next = icalrecur_iterator_next(itr.ritr);
                itr.last_component = comp;
            } else {
                next = icalrecur_iterator_next(itr.ritr);
                if (icaltime_is_null_time(next)) {
                    itr.last_component = NULL;
                    icalrecur_iterator_free(itr.ritr);
                    itr.ritr = NULL;
                    /* no matched occurrence */
                    goto getNextComp;
                } else {
                    itr.last_component = comp;
                }
            }

            /* if it is excluded, do next one */
            if (icalproperty_recurrence_is_excluded(comp, &start, &next)) {
                next = icalrecur_iterator_next(itr.ritr);
                continue;
            }

            /* add recurrence-id value to the property if the property already exist;
             * add the recurrence id property and the value if the property does not exist */
            prop = icalcomponent_get_first_property(comp, ICAL_RECURRENCEID_PROPERTY);
            if (prop == 0) {
                icalcomponent_add_property(comp, icalproperty_new_recurrenceid(next));
            } else {
                icalproperty_set_recurrenceid(prop, next);
            }

            /* convert the next recurrence time into the user's timezone */
            if (orig_time_was_utc) {
                next = icaltime_convert_to_zone(next, icaltimezone_get_utc_timezone());
            }
        }
        /* end of a recurring event */
        if (gauge == 0 || icalgauge_compare(itr.gauge, comp) == 1) {
            /* find a matched and return it */
            itr.iter = citr;
            return itr;
        }

        /* if it is a recurring but no matched occurrence has been found OR
         * it is not a recurring and no matched component has been found,
         * read the next component to find out */
      getNextComp:
        if ((rrule != NULL && itr.last_component == NULL) || (rrule == NULL)) {
            (void)icalcompiter_next(&citr);
            comp = icalcompiter_deref(&citr);
        }
    }   /* while */

    /* no matched component has found */
    return icalsetiter_null;
}
static ECellDateEditValue *
get_dtend (ECalModelCalendar *model,
           ECalModelComponent *comp_data)
{
	struct icaltimetype tt_end;

	if (!comp_data->dtend) {
		icalproperty *prop;
		icaltimezone *zone = NULL, *model_zone = NULL;
		gboolean got_zone = FALSE;

		prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DTEND_PROPERTY);
		if (!prop)
			return NULL;

		tt_end = icalproperty_get_dtend (prop);

		if (icaltime_get_tzid (tt_end)
		    && e_cal_client_get_timezone_sync (comp_data->client, icaltime_get_tzid (tt_end), &zone, NULL, NULL))
			got_zone = TRUE;

		model_zone = e_cal_model_get_timezone (E_CAL_MODEL (model));

		if (got_zone) {
			tt_end = icaltime_from_timet_with_zone (comp_data->instance_end, tt_end.is_date, zone);
			if (model_zone)
				icaltimezone_convert_time (&tt_end, zone, model_zone);
		} else {
			tt_end = icaltime_from_timet_with_zone (
				comp_data->instance_end,
				tt_end.is_date, model_zone);
		}

		if (!icaltime_is_valid_time (tt_end) || icaltime_is_null_time (tt_end))
			return NULL;

		if (tt_end.is_date && icalcomponent_get_first_property (comp_data->icalcomp, ICAL_DTSTART_PROPERTY)) {
			struct icaltimetype tt_start;
			icaltimezone *start_zone = NULL;
			gboolean got_start_zone = FALSE;

			tt_start = icalproperty_get_dtstart (prop);

			if (icaltime_get_tzid (tt_start)
			    && e_cal_client_get_timezone_sync (comp_data->client, icaltime_get_tzid (tt_start), &start_zone, NULL, NULL))
				got_start_zone = TRUE;

			if (got_start_zone) {
				tt_start = icaltime_from_timet_with_zone (comp_data->instance_start, tt_start.is_date, start_zone);
				if (model_zone)
					icaltimezone_convert_time (&tt_start, start_zone, model_zone);
			} else {
				tt_start = icaltime_from_timet_with_zone (
					comp_data->instance_start,
					tt_start.is_date, model_zone);
			}

			icaltime_adjust (&tt_start, 1, 0, 0, 0);

			/* Decrease by a day only if the DTSTART will still be before, or the same as, DTEND */
			if (icaltime_compare (tt_start, tt_end) <= 0)
				icaltime_adjust (&tt_end, -1, 0, 0, 0);
		}

		comp_data->dtend = g_new0 (ECellDateEditValue, 1);
		comp_data->dtend->tt = tt_end;

		if (got_zone)
			comp_data->dtend->zone = zone;
		else
			comp_data->dtend->zone = NULL;
	}

	return e_cal_model_copy_cell_date_value (comp_data->dtend);
}
Exemple #8
0
int main(int argc, char *argv[])
{
    icalset *cin;
    struct icaltimetype next;
    icalcomponent *itr;
    icalproperty *desc, *dtstart, *rrule;
    struct icalrecurrencetype recur;
    icalrecur_iterator* ritr;
    time_t tt;
    char* file;

    icalerror_set_error_state(ICAL_PARSE_ERROR, ICAL_ERROR_NONFATAL);

#ifndef WIN32
    signal(SIGALRM,sig_alrm);
#endif

    if (argc <= 1) {
        file = "../../test-data/recur.txt";
    } else if (argc == 2) {
        file = argv[1];
    } else {
        fprintf(stderr,"usage: recur [input file]\n");
        exit(1);
    }

#ifndef WIN32
    alarm(300); /* to get file lock */
#endif
    cin = icalfileset_new(file);
#ifndef WIN32
    alarm(0);
#endif

    if(cin == 0) {
        fprintf(stderr,"recur: can't open file %s\n",file);
        exit(1);
    }


    for (itr = icalfileset_get_first_component(cin);
            itr != 0;
            itr = icalfileset_get_next_component(cin)) {

        struct icaltimetype start = icaltime_from_timet(1,0);
        struct icaltimetype end = icaltime_today();



        desc = icalcomponent_get_first_property(itr,ICAL_DESCRIPTION_PROPERTY);
        dtstart = icalcomponent_get_first_property(itr,ICAL_DTSTART_PROPERTY);
        rrule = icalcomponent_get_first_property(itr,ICAL_RRULE_PROPERTY);

        if (desc == 0 || dtstart == 0 || rrule == 0) {
            printf("\n******** Error in input component ********\n");
            printf("The following component is malformed:\n %s\n",
                   icalcomponent_as_ical_string(itr));
            continue;
        }

        printf("\n\n#### %s\n",icalproperty_get_description(desc));
        printf("#### %s\n",icalvalue_as_ical_string(icalproperty_get_value(rrule)));
        recur = icalproperty_get_rrule(rrule);
        start = icalproperty_get_dtstart(dtstart);

        ritr = icalrecur_iterator_new(recur,start);

        tt = icaltime_as_timet(start);

        printf("#### %s\n",ctime(&tt ));

        icalrecur_iterator_free(ritr);

        for(ritr = icalrecur_iterator_new(recur,start),
                next = icalrecur_iterator_next(ritr);
                !icaltime_is_null_time(next);
                next = icalrecur_iterator_next(ritr)) {

            tt = icaltime_as_timet(next);

            printf("  %s",ctime(&tt ));

        }
        icalrecur_iterator_free(ritr);

        icalcomponent_foreach_recurrence(itr, start, end,
                                         recur_callback, NULL);



    }

    icalset_free(cin);

    icaltimezone_free_builtin_timezones();

    icalmemory_free_ring();

    free_zone_directory();

    return 0;
}
static void
icaltimezone_expand_vtimezone		(icalcomponent	*comp,
					 int		 end_year,
					 icalarray	*changes)
{
    icaltimezonechange change;
    icalproperty *prop;
    struct icaltimetype dtstart, occ;
    struct icalrecurrencetype rrule;
    icalrecur_iterator* rrule_iterator;
    struct icaldatetimeperiodtype rdate;
    int found_dtstart = 0, found_tzoffsetto = 0, found_tzoffsetfrom = 0;
    int has_recurrence = 0;

    /* First we check if it is a STANDARD or DAYLIGHT component, and
       just return if it isn't. */
    if (icalcomponent_isa (comp) == ICAL_XSTANDARD_COMPONENT)
	change.is_daylight = 0;
    else if (icalcomponent_isa (comp) == ICAL_XDAYLIGHT_COMPONENT)
	change.is_daylight = 1;
    else 
	return;

    /* Step through each of the properties to find the DTSTART,
       TZOFFSETFROM and TZOFFSETTO. We can't expand recurrences here
       since we need these properties before we can do that. */
    prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
    while (prop) {
	switch (icalproperty_isa (prop)) {
	case ICAL_DTSTART_PROPERTY:
	    dtstart = icalproperty_get_dtstart (prop);
	    found_dtstart = 1;
	    break;
	case ICAL_TZOFFSETTO_PROPERTY:
	    change.utc_offset = icalproperty_get_tzoffsetto (prop);
	    /*printf ("Found TZOFFSETTO: %i\n", change.utc_offset);*/
	    found_tzoffsetto = 1;
	    break;
	case ICAL_TZOFFSETFROM_PROPERTY:
	    change.prev_utc_offset = icalproperty_get_tzoffsetfrom (prop);
	    /*printf ("Found TZOFFSETFROM: %i\n", change.prev_utc_offset);*/
	    found_tzoffsetfrom = 1;
	    break;
	case ICAL_RDATE_PROPERTY:
	case ICAL_RRULE_PROPERTY:
	    has_recurrence = 1;
	    break;
	default:
	    /* Just ignore any other properties. */
	    break;
	}

	prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY);
    }

    /* If we didn't find a DTSTART, TZOFFSETTO and TZOFFSETFROM we have to
       ignore the component. FIXME: Add an error property? */
    if (!found_dtstart || !found_tzoffsetto || !found_tzoffsetfrom)
	return;

#if 0
    printf ("\n Expanding component DTSTART (Y/M/D): %i/%i/%i %i:%02i:%02i\n",
	    dtstart.year, dtstart.month, dtstart.day,
	    dtstart.hour, dtstart.minute, dtstart.second);
#endif

    /* If the STANDARD/DAYLIGHT component has no recurrence data, we just add
       a single change for the DTSTART. */
    if (!has_recurrence) {
	change.year   = dtstart.year;
	change.month  = dtstart.month;
	change.day    = dtstart.day;
	change.hour   = dtstart.hour;
	change.minute = dtstart.minute;
	change.second = dtstart.second;

	/* Convert to UTC. */
	icaltimezone_adjust_change (&change, 0, 0, 0, -change.prev_utc_offset);

#if 0
	printf ("  Appending single DTSTART (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
		change.year, change.month, change.day,
		change.hour, change.minute, change.second);
#endif

	/* Add the change to the array. */
	icalarray_append (changes, &change);
	return;
    }

    /* The component has recurrence data, so we expand that now. */
    prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
    while (prop) {
#if 0
	printf ("Expanding property...\n");
#endif
	switch (icalproperty_isa (prop)) {
	case ICAL_RDATE_PROPERTY:
	    rdate = icalproperty_get_rdate (prop);
	    change.year   = rdate.time.year;
	    change.month  = rdate.time.month;
	    change.day    = rdate.time.day;
	    /* RDATEs with a DATE value inherit the time from
	       the DTSTART. */
	    if (icaltime_is_date(rdate.time)) {
		change.hour   = dtstart.hour;
		change.minute = dtstart.minute;
		change.second = dtstart.second;
	    } else {
		change.hour   = rdate.time.hour;
		change.minute = rdate.time.minute;
		change.second = rdate.time.second;

		/* The spec was a bit vague about whether RDATEs were in local
		   time or UTC so we support both to be safe. So if it is in
		   UTC we have to add the UTC offset to get a local time. */
		if (!icaltime_is_utc(rdate.time))
		    icaltimezone_adjust_change (&change, 0, 0, 0,
						-change.prev_utc_offset);
	    }

#if 0
	    printf ("  Appending RDATE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
		    change.year, change.month, change.day,
		    change.hour, change.minute, change.second);
#endif

	    icalarray_append (changes, &change);
	    break;
	case ICAL_RRULE_PROPERTY:
	    rrule = icalproperty_get_rrule (prop);

	    /* If the rrule UNTIL value is set and is in UTC, we convert it to
	       a local time, since the recurrence code has no way to convert
	       it itself. */
	    if (!icaltime_is_null_time (rrule.until) && rrule.until.is_utc) {
#if 0
		printf ("  Found RRULE UNTIL in UTC.\n");
#endif

		/* To convert from UTC to a local time, we use the TZOFFSETFROM
		   since that is the offset from UTC that will be in effect
		   when each of the RRULE occurrences happens. */
		icaltime_adjust (&rrule.until, 0, 0, 0,
				 change.prev_utc_offset);
		rrule.until.is_utc = 0;
	    }

	    rrule_iterator = icalrecur_iterator_new (rrule, dtstart);
	    for (;;) {
		occ = icalrecur_iterator_next (rrule_iterator);
		if (occ.year > end_year || icaltime_is_null_time (occ))
		    break;

		change.year   = occ.year;
		change.month  = occ.month;
		change.day    = occ.day;
		change.hour   = occ.hour;
		change.minute = occ.minute;
		change.second = occ.second;

#if 0
		printf ("  Appending RRULE element (Y/M/D): %i/%02i/%02i %i:%02i:%02i\n",
			change.year, change.month, change.day,
			change.hour, change.minute, change.second);
#endif

		icaltimezone_adjust_change (&change, 0, 0, 0,
					    -change.prev_utc_offset);

		icalarray_append (changes, &change);
	    }

	    icalrecur_iterator_free (rrule_iterator);
	    break;
	default:
	    break;
	}

	prop = icalcomponent_get_next_property (comp, ICAL_ANY_PROPERTY);
    }
}
/** Gets the TZNAMEs used for the last STANDARD & DAYLIGHT components
   in a VTIMEZONE. If both STANDARD and DAYLIGHT components use the
   same TZNAME, it returns that. If they use different TZNAMEs, it
   formats them like "EST/EDT". The returned string should be freed by
   the caller. */
static char*
icaltimezone_get_tznames_from_vtimezone (icalcomponent *component)
{
    icalcomponent *comp;
    icalcomponent_kind type;
    icalproperty *prop;
    struct icaltimetype dtstart;
    struct icaldatetimeperiodtype rdate;
    const char *current_tzname;
    const char *standard_tzname = NULL, *daylight_tzname = NULL;
    struct icaltimetype standard_max_date, daylight_max_date;
    struct icaltimetype current_max_date;

    standard_max_date = icaltime_null_time();
    daylight_max_date = icaltime_null_time();

    /* Step through the STANDARD & DAYLIGHT subcomponents. */
    comp = icalcomponent_get_first_component (component, ICAL_ANY_COMPONENT);
    while (comp) {
	type = icalcomponent_isa (comp);
	if (type == ICAL_XSTANDARD_COMPONENT
	    || type == ICAL_XDAYLIGHT_COMPONENT) {
	    current_max_date = icaltime_null_time ();
	    current_tzname = NULL;

	    /* Step through the properties. We want to find the TZNAME, and
	       the largest DTSTART or RDATE. */
	    prop = icalcomponent_get_first_property (comp, ICAL_ANY_PROPERTY);
	    while (prop) {
		switch (icalproperty_isa (prop)) {
		case ICAL_TZNAME_PROPERTY:
		    current_tzname = icalproperty_get_tzname (prop);
		    break;

		case ICAL_DTSTART_PROPERTY:
		    dtstart = icalproperty_get_dtstart (prop);
		    if (icaltime_compare (dtstart, current_max_date) > 0)
			current_max_date = dtstart;

		    break;

		case ICAL_RDATE_PROPERTY:
		    rdate = icalproperty_get_rdate (prop);
		    if (icaltime_compare (rdate.time, current_max_date) > 0)
			current_max_date = rdate.time;

		    break;

		default:
		    break;
		}

		prop = icalcomponent_get_next_property (comp,
							ICAL_ANY_PROPERTY);
	    }

	    if (current_tzname) {
		if (type == ICAL_XSTANDARD_COMPONENT) {
		    if (!standard_tzname
			|| icaltime_compare (current_max_date,
					     standard_max_date) > 0) {
			standard_max_date = current_max_date;
			standard_tzname = current_tzname;
		    }
		} else {
		    if (!daylight_tzname
			|| icaltime_compare (current_max_date,
					     daylight_max_date) > 0) {
			daylight_max_date = current_max_date;
			daylight_tzname = current_tzname;
		    }
		}
	    }
	}

        comp = icalcomponent_get_next_component (component,
						 ICAL_ANY_COMPONENT);
    }

    /* Outlook (2000) places "Standard Time" and "Daylight Time" in the TZNAME
       strings, which is totally useless. So we return NULL in that case. */
    if (standard_tzname && !strcmp (standard_tzname, "Standard Time"))
	return NULL;

    /* If both standard and daylight TZNAMEs were found, if they are the same
       we return just one, else we format them like "EST/EDT". */
    if (standard_tzname && daylight_tzname) {
	unsigned int standard_len, daylight_len;
	char *tznames;

	if (!strcmp (standard_tzname, daylight_tzname))
	    return strdup (standard_tzname);

	standard_len = strlen (standard_tzname);
	daylight_len = strlen (daylight_tzname);
	tznames = malloc (standard_len + daylight_len + 2);
	strcpy (tznames, standard_tzname);
	tznames[standard_len] = '/';
	strcpy (tznames + standard_len + 1, daylight_tzname);
	return tznames;
    } else {
	const char *tznames;

	/* If either of the TZNAMEs was found just return that, else NULL. */
	tznames = standard_tzname ? standard_tzname : daylight_tzname;
	return tznames ? strdup (tznames) : NULL;
    }
}