diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2013-05-04 21:39:27 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2013-05-04 21:39:27 +0000 |
commit | fec6336699f34758d3e6cb41b2edf902fedb9035 (patch) | |
tree | 8256c1dbf3ca7c9e58a3dbecf07cf826fb2e0ce2 /src/libical/icalrecur.c | |
parent | 7dbffd7e2b0067e834801617c5c486e3177f6709 (diff) | |
download | libical-master.tar.gz |
libical-1.0HEADlibical-1.0master
Diffstat (limited to 'src/libical/icalrecur.c')
-rw-r--r-- | src/libical/icalrecur.c | 214 |
1 files changed, 168 insertions, 46 deletions
diff --git a/src/libical/icalrecur.c b/src/libical/icalrecur.c index 850e46d..c0e5eca 100644 --- a/src/libical/icalrecur.c +++ b/src/libical/icalrecur.c @@ -139,16 +139,23 @@ #include <stdint.h> #endif +#include <stdio.h> +#include <stdarg.h> + +#if defined(_MSC_VER) +#define snprintf _snprintf +#endif + #include <limits.h> #ifndef HAVE_INTPTR_T -#if defined (WIN32) || defined (XP_BEOS) +#if (defined (WIN32) && !defined (__MINGW32__)) || defined (XP_BEOS) typedef long intptr_t; #endif #endif -#ifdef WIN32 -#define strcasecmp stricmp +#ifdef _MSC_VER +#define strcasecmp stricmp #endif #include "icalrecur.h" @@ -156,7 +163,7 @@ typedef long intptr_t; #include "icalerror.h" #include "icalmemory.h" -#include <stdlib.h> /* for malloc */ +#include <stdlib.h> /* for malloc, strtol */ #include <errno.h> /* for errno */ #include <string.h> /* for strdup and strchr*/ #include <assert.h> @@ -354,7 +361,7 @@ void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals) char *t, *n; int i=0; int sign = 1; - int weekno = 0; + char weekno = 0; /* note: Novell/Groupwise sends BYDAY=255SU, so we fit in a signed char to get -1 SU for last sunday. */ icalrecurrencetype_weekday wd; short *array = parser->rt.by_day; char* end; @@ -389,7 +396,7 @@ void icalrecur_add_bydayrules(struct icalrecur_parser *parser, const char* vals) } /* Get Optional weekno */ - weekno = strtol(t,&t,10); + weekno = (char)strtol(t,&t,10); /* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH". * Cope with that. @@ -441,7 +448,7 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str) if(name == 0){ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); icalrecurrencetype_clear(&parser.rt); - free(parser.copy); + free(parser.copy); return parser.rt; } @@ -453,6 +460,11 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str) parser.rt.until = icaltime_from_string(value); } else if (strcasecmp(name,"INTERVAL") == 0){ parser.rt.interval = (short)atoi(value); + /* don't allow an interval to be less than 1 + (RFC specifies an interval must be a positive integer) */ + if (parser.rt.interval < 1){ + parser.rt.interval = 1; + } } else if (strcasecmp(name,"WKST") == 0){ parser.rt.week_start = icalrecur_string_to_weekday(value); sort_bydayrules(&parser); @@ -485,7 +497,7 @@ struct icalrecurrencetype icalrecurrencetype_from_string(const char* str) } else { icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); icalrecurrencetype_clear(&parser.rt); - free(parser.copy); + free(parser.copy); return parser.rt; } @@ -810,6 +822,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, icalrecur_iterator* impl; icalrecurrencetype_frequency freq; + icalerror_clear_errno(); + if ( ( impl = (icalrecur_iterator*) malloc(sizeof(icalrecur_iterator))) == 0) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); @@ -875,26 +889,18 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, icalrecur_two_byrule(impl,BY_YEAR_DAY,BY_DAY) ){ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - + free(impl); return 0; } - /* BYWEEKNO and BYMONTH rule parts may not both appear.*/ - - if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH)){ - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return 0; - } + /* BYWEEKNO and BYMONTHDAY rule parts may not both appear.*/ if(icalrecur_two_byrule(impl,BY_WEEK_NO,BY_MONTH_DAY)){ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - - icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return 0; + free(impl); + return 0; } @@ -904,7 +910,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, if(freq == ICAL_MONTHLY_RECURRENCE && icalrecur_one_byrule(impl,BY_WEEK_NO)){ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return 0; + free(impl); + return 0; } @@ -914,13 +921,15 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, if(freq == ICAL_WEEKLY_RECURRENCE && icalrecur_one_byrule(impl,BY_MONTH_DAY )) { icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return 0; + free(impl); + return 0; } /* BYYEARDAY may only appear in YEARLY rules */ if(freq != ICAL_YEARLY_RECURRENCE && icalrecur_one_byrule(impl,BY_YEAR_DAY )) { icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + free(impl); return 0; } @@ -990,9 +999,16 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, if(impl->rule.freq == ICAL_YEARLY_RECURRENCE){ struct icaltimetype next; + icalerror_clear_errno(); - for (;;) { + /* Fail after hitting the year 20000 if no expanded days match */ + while (impl->last.year < 20000) { expand_year_days(impl, impl->last.year); + if( icalerrno != ICAL_NO_ERROR) { + icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); + free(impl); + return 0; + } if (impl->days[0] != ICAL_RECURRENCE_ARRAY_MAX) break; /* break when no days are expanded */ increment_year(impl,impl->rule.interval); @@ -1054,7 +1070,8 @@ icalrecur_iterator* icalrecur_iterator_new(struct icalrecurrencetype rule, if(impl->last.day > days_in_month || impl->last.day == 0){ icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); - return 0; + free(impl); + return 0; } } else if (has_by_data(impl,BY_MONTH_DAY)) { @@ -1158,7 +1175,7 @@ static void increment_hour(icalrecur_iterator* impl, int inc) days = impl->last.hour / 24; impl->last.hour = impl->last.hour % 24; - if (impl->days != 0){ + if (days != 0){ increment_monthday(impl,days); } } @@ -1494,6 +1511,20 @@ static int is_day_in_byday(icalrecur_iterator* impl,struct icaltimetype tt){ return 0; } +int check_set_position(icalrecur_iterator* impl, int set_pos) +{ + int i; + int found = 0; + for (i = 0; impl->rule.by_set_pos[i] != ICAL_RECURRENCE_ARRAY_MAX && + i != ICAL_BY_SETPOS_SIZE; i++){ + if (impl->rule.by_set_pos[i] == set_pos) { + found = 1; + break; + } + } + return found; +} + static int next_month(icalrecur_iterator* impl) { int data_valid = 1; @@ -1575,15 +1606,44 @@ static int next_month(icalrecur_iterator* impl) int day; int days_in_month = icaltime_days_in_month(impl->last.month, impl->last.year); - assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + int set_pos_counter = 0; + int set_pos_total = 0; + int found = 0; + + assert( BYDAYPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + + /* Count the past positions for the BYSETPOS calculation */ + if(has_by_data(impl,BY_SET_POS)){ + int last_day = impl->last.day; + for(day = 1; day <= days_in_month; day++){ + impl->last.day = day; + + if(is_day_in_byday(impl,impl->last)){ + set_pos_total++; + if(day <= last_day) + set_pos_counter++; + } + } + impl->last.day = last_day; + } + for(day = impl->last.day+1; day <= days_in_month; day++){ impl->last.day = day; + if(is_day_in_byday(impl,impl->last)){ - data_valid = 1; - break; - } + /* If there is no BYSETPOS rule, calculate only by BYDAY + If there is BYSETPOS rule, take into account the occurence + matches with BYDAY */ + if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl, ++set_pos_counter) + || check_set_position(impl, set_pos_counter-set_pos_total-1)) { + found = 1; + break; + } + } } + + data_valid = found; if ( day > days_in_month){ impl->last.day = 1; @@ -1594,7 +1654,9 @@ static int next_month(icalrecur_iterator* impl) invalid */ if(is_day_in_byday(impl,impl->last)){ - data_valid = 1; + /* If there is no BYSETPOS rule or BYSETPOS=1, new data is valid */ + if(!has_by_data(impl,BY_SET_POS) || check_set_position(impl,1)) + data_valid = 1; } else { data_valid = 0; /* signal that impl->last is invalid */ } @@ -1647,12 +1709,15 @@ static int next_month(icalrecur_iterator* impl) } else { int days_in_month; + assert( BYMDPTR[0]!=ICAL_RECURRENCE_ARRAY_MAX); + impl->last.day = BYMDPTR[0]; + increment_month(impl); days_in_month = icaltime_days_in_month(impl->last.month, impl->last.year); if (impl->last.day > days_in_month){ - data_valid = 0; /* signal that impl->last is invalid */ + impl->last.day = days_in_month; } } @@ -1850,7 +1915,7 @@ static pvl_list expand_by_day(icalrecur_iterator* impl, int year) static int expand_year_days(icalrecur_iterator* impl, int year) { - int j,k; + int i,j,k; int days_index=0; struct icaltimetype t; int flags; @@ -1872,6 +1937,46 @@ static int expand_year_days(icalrecur_iterator* impl, int year) (HBD(BY_YEAR_DAY) ? 1<<BY_YEAR_DAY : 0); + /* BY_WEEK_NO together with BY_MONTH - may conflict, in this case BY_MONTH wins */ + if( (flags & 1<<BY_MONTH) && (flags & 1<<BY_WEEK_NO) ){ + int valid_weeks[ICAL_BY_WEEKNO_SIZE]; + int valid = 1; + memset(valid_weeks, 0, sizeof(valid_weeks)); + t.year = year; + t.is_date = 1; + + /* calculate valid week numbers */ + for(j=0; impl->by_ptrs[BY_MONTH][j]!=ICAL_RECURRENCE_ARRAY_MAX; j++){ + int month = impl->by_ptrs[BY_MONTH][j]; + int first_week, last_week; + t.month = month; + t.day = 1; + first_week = icaltime_week_number(t); + t.day = icaltime_days_in_month(month,year); + last_week = icaltime_week_number(t); + for(j=first_week; j<last_week; j++) { + valid_weeks[j] = 1; + } + } + + /* check valid weeks */ + for(i = 0; BYWEEKPTR[i] != ICAL_RECURRENCE_ARRAY_MAX && valid; i++){ + int weekno = BYWEEKPTR[i]; + if(weekno < ICAL_BY_WEEKNO_SIZE) + valid &= valid_weeks[i]; /* check if the week number is valid */ + else + valid = 0; /* invalid week number */ + } + + /* let us make the decision which rule to keep */ + if(valid) { /* BYWEEKNO wins */ + flags -= 1<<BY_MONTH; + } + else { /* BYMONTH vins */ + flags -= 1<<BY_WEEK_NO; + } + } + switch(flags) { case 0: { @@ -2011,7 +2116,24 @@ static int expand_year_days(icalrecur_iterator* impl, int year) t.day = days_in_month; last_dow = icaltime_day_of_week(t); - for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){ + if(has_by_data(impl,BY_SET_POS)) { + /*FREQ=YEARLY; BYDAY=TH,20MO,-10FR; BYMONTH = 12; BYSETPOS=1*/ + int day; + int set_pos_counter = 0; + int set_pos_total = 0; + int by_month_day[ICAL_BY_MONTHDAY_SIZE]; + for(day = 1; day <= days_in_month; day++){ + t.day = day; + if(is_day_in_byday(impl,t)) + by_month_day[set_pos_total++] = day; + } + for(set_pos_counter = 0; set_pos_counter < set_pos_total; set_pos_counter++){ + if(check_set_position(impl, set_pos_counter+1) || + check_set_position(impl, set_pos_counter-set_pos_total)) + impl->days[days_index++] = doy_offset + by_month_day[set_pos_counter]; + } + } + else for(k=0;impl->by_ptrs[BY_DAY][k]!=ICAL_RECURRENCE_ARRAY_MAX;k++){ short day_coded = impl->by_ptrs[BY_DAY][k]; enum icalrecurrencetype_weekday dow = icalrecurrencetype_day_day_of_week(day_coded); @@ -2252,8 +2374,8 @@ static int check_contracting_rules(icalrecur_iterator* impl) struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl) { int valid = 1; - - if( (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) || + + if( !impl || (impl->rule.count!=0 &&impl->occurrence_no >= impl->rule.count) || (!icaltime_is_null_time(impl->rule.until) && icaltime_compare(impl->last,impl->rule.until) > 0)) { return icaltime_null_time(); @@ -2470,21 +2592,21 @@ int icalrecur_expand_recurrence(char* rule, time_t start, icstart = icaltime_from_timet_with_zone(start,0,0); recur = icalrecurrencetype_from_string(rule); + ritr = icalrecur_iterator_new(recur,icstart); + if(ritr) { + for(next = icalrecur_iterator_next(ritr); + !icaltime_is_null_time(next) && i < count; + next = icalrecur_iterator_next(ritr)){ - for(ritr = icalrecur_iterator_new(recur,icstart), - next = icalrecur_iterator_next(ritr); - !icaltime_is_null_time(next) && i < count; - next = icalrecur_iterator_next(ritr)){ - - tt = icaltime_as_timet(next); + tt = icaltime_as_timet(next); - if (tt >= start ){ - array[i++] = tt; - } - + if (tt >= start ){ + array[i++] = tt; + } + } + icalrecur_iterator_free(ritr); } - icalrecur_iterator_free(ritr); return 1; } |