/** * @brief * Get the occurrence as defined by the given recurrence rule, * index, and start time. This function assumes that the * time dtsart passed in is the one to start the occurrence from. * * @par NOTE: This function should be made reentrant such that * it can be looped over without having to loop over every occurrence * over and over again. * * @param[in] rrule - The recurrence rule as defined by the user * @param[in] dtstart - The start time from which to start * @param[in] tz - The timezone associated to the recurrence rule * @param[in] idx - The index of the occurrence to start counting from * * @return time_t * @retval The date of the next occurrence or -1 if the date exceeds libical's * Unix time in 2038 * */ time_t get_occurrence(char *rrule, time_t dtstart, char *tz, int idx) { #ifdef LIBICAL struct icalrecurrencetype rt; struct icaltimetype start; icaltimezone *localzone; struct icaltimetype next; struct icalrecur_iterator_impl *itr; int i; time_t next_occr = dtstart; if (rrule == NULL) return dtstart; if (tz == NULL) return -1; icalerror_clear_errno(); icalerror_set_error_state(ICAL_PARSE_ERROR, ICAL_ERROR_NONFATAL); #ifdef LIBICAL_API2 icalerror_set_errors_are_fatal(0); #else icalerror_errors_are_fatal = 0; #endif localzone = icaltimezone_get_builtin_timezone(tz); if (localzone == NULL) return -1; rt = icalrecurrencetype_from_string(rrule); start = icaltime_from_timet_with_zone(dtstart, 0, NULL); icaltimezone_convert_time(&start, icaltimezone_get_utc_timezone(), localzone); next = start; itr = (struct icalrecur_iterator_impl*) icalrecur_iterator_new(rt, start); /* Skip as many occurrences as specified by idx */ for (i = 0; i < idx && !icaltime_is_null_time(next); i++) next = icalrecur_iterator_next(itr); if (!icaltime_is_null_time(next)) { icaltimezone_convert_time(&next, localzone, icaltimezone_get_utc_timezone()); next_occr = icaltime_as_timet(next); } else next_occr = -1; /* If reached end of possible date-time return -1 */ icalrecur_iterator_free(itr); return next_occr; #else return dtstart; #endif }
/** * @brief * Returns the number of occurrences defined by a recurrence rule. * * @par The total number of occurrences is currently limited to a hardcoded * 3 years limit from the current date. * * @par NOTE: Determine whether 3 years limit is the right way to go about setting * a limit on the total number of occurrences. * * @param[in] rrule - The recurrence rule as defined by the user * @param[in] tt - The start time of the first occurrence * @param[in] tz - The timezone associated to the recurrence rule * * @return int * @retval the total number of occurrences * */ int get_num_occurrences(char *rrule, time_t dtstart, char *tz) { #ifdef LIBICAL struct icalrecurrencetype rt; struct icaltimetype start; icaltimezone *localzone; struct icaltimetype next; struct icalrecur_iterator_impl *itr; time_t now; time_t date_limit; int num_resv = 0; /* if any of the argument is NULL, we are dealing with * advance reservation, so return 1 occurrence */ if (rrule == NULL || tz == NULL) return 1; icalerror_clear_errno(); icalerror_set_error_state(ICAL_PARSE_ERROR, ICAL_ERROR_NONFATAL); icalerror_errors_are_fatal = 0; localzone = icaltimezone_get_builtin_timezone(tz); if (localzone == NULL) return 0; now = time((time_t *)0); date_limit = now + DATE_LIMIT; rt = icalrecurrencetype_from_string(rrule); start = icaltime_from_timet(dtstart, 0); icaltimezone_convert_time(&start, icaltimezone_get_utc_timezone(), localzone); itr = (struct icalrecur_iterator_impl*) icalrecur_iterator_new(rt, start); next = icalrecur_iterator_next(itr); /* Compute the total number of occurrences. * Breaks out if the total number of allowed occurrences is exceeded */ while (!icaltime_is_null_time(next) && (icaltime_as_timet(next) < date_limit)) { num_resv++; next = icalrecur_iterator_next(itr); } icalrecur_iterator_free(itr); return num_resv; #else if (rrule == NULL) return 1; return 0; #endif }
VALUE occurrences( VALUE self, VALUE dtstart, VALUE dtend, VALUE rrule ) { char * _rrule; struct icaltimetype start, end; time_t tt; VALUE tv_sec, occurr = rb_ary_new(); /* Get method ID for Time.tv_sec */ ID time_tv_sec = rb_intern( "tv_sec" ); ID to_string = rb_intern( "to_string" ); if( TYPE( rrule ) != T_STRING && rb_respond_to( rrule, to_string ) ) rrule = rb_funcall( rrule, to_string, 0 ); Check_Type(rrule, T_STRING); _rrule = RSTRING(rrule)->ptr; dtstart = to_time( dtstart, "dtstart" ); dtend = to_time( dtend, "dtend" ); /* Apply .tv_sec to our Time objects (if they are Times ...) */ tv_sec = rb_funcall( dtstart, time_tv_sec, 0 ); tt = NUM2INT( tv_sec ); start = icaltime_from_timet( tt, 0 ); tv_sec = rb_funcall( dtend, time_tv_sec, 0 ); tt = NUM2INT( tv_sec ); end = icaltime_from_timet( tt, 0 ); icalerror_clear_errno(); icalerror_set_error_state( ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL); struct icalrecurrencetype recur = icalrecurrencetype_from_string( _rrule ); if( icalerrno != ICAL_NO_ERROR ) { rb_raise(rb_eArgError, "Malformed RRule"); return Qnil; } icalrecur_iterator* ritr = icalrecur_iterator_new( recur, start ); while(1) { struct icaltimetype next = icalrecur_iterator_next(ritr); if( icaltime_is_null_time(next) || ( icaltime_compare( next, end ) > 0 ) ) { icalrecur_iterator_free(ritr); return occurr; } rb_ary_push( occurr, rb_time_new( icaltime_as_timet( next ), 0 ) ); }; icalrecur_iterator_free(ritr); return occurr; }
void program_errors() { /*Most routines will set icalerrno on errors. This is an enumeration defined in icalerror.h */ icalerror_clear_errno(); (void)icalcomponent_new(ICAL_VEVENT_COMPONENT); if (icalerrno != ICAL_NO_ERROR){ fprintf(stderr,"Horrible libical error: %s\n", icalerror_strerror(icalerrno)); } }
icalset *icalcalendar_get_booked(icalcalendar *impl) { char dir[MAXPATHLEN]; icalerror_check_arg_rz((impl != 0), "impl"); dir[0] = '\0'; strncpy(dir, impl->dir, MAXPATHLEN - 1); strncat(dir, "/", MAXPATHLEN - strlen(dir) - 1); strncat(dir, BOOKED_DIR, MAXPATHLEN - strlen(dir) - 1); dir[MAXPATHLEN - 1] = '\0'; if (impl->booked == 0) { icalerror_clear_errno(); impl->booked = icaldirset_new(dir); assert(icalerrno == ICAL_NO_ERROR); } return impl->booked; }
VALUE duration_to_fixnum( VALUE self, VALUE duration ) { struct icaldurationtype dur_struct; char * _duration; ID to_string = rb_intern( "to_string" ); if( TYPE( duration ) != T_STRING && rb_respond_to( duration, to_string ) ) duration = rb_funcall( duration, to_string, 0 ); Check_Type(duration, T_STRING); _duration = RSTRING(duration)->ptr; icalerror_clear_errno(); icalerror_set_error_state( ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL); dur_struct = icaldurationtype_from_string( _duration ); if( icaldurationtype_is_bad_duration( dur_struct ) ) rb_raise(rb_eArgError, "Malformed Duration"); return LONG2FIX(icaldurationtype_as_int( dur_struct )); }
icalspanlist* icalspanlist_new(icalset *set, struct icaltimetype start, struct icaltimetype end) { struct icaltime_span range; pvl_elem itr; icalcomponent *c,*inner; icalcomponent_kind kind, inner_kind; icalspanlist *sl; struct icaltime_span *freetime; if ( ( sl = (struct icalspanlist_impl*) malloc(sizeof(struct icalspanlist_impl))) == 0) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); return 0; } sl->spans = pvl_newlist(); sl->start = start; sl->end = end; range.start = icaltime_as_timet(start); range.end = icaltime_as_timet(end); /* Get a list of spans of busy time from the events in the set and order the spans based on the start time */ for(c = icalset_get_first_component(set); c != 0; c = icalset_get_next_component(set)){ kind = icalcomponent_isa(c); inner = icalcomponent_get_inner(c); if(!inner){ continue; } inner_kind = icalcomponent_isa(inner); if( kind != ICAL_VEVENT_COMPONENT && inner_kind != ICAL_VEVENT_COMPONENT){ continue; } icalerror_clear_errno(); icalcomponent_foreach_recurrence(c, start, end, icalspanlist_new_callback, (void*)sl); } /* Now Fill in the free time spans. loop through the spans. if the start of the range is not within the span, create a free entry that runs from the start of the range to the start of the span. */ for( itr = pvl_head(sl->spans); itr != 0; itr = pvl_next(itr)) { struct icaltime_span *s = (struct icaltime_span*)pvl_data(itr); if ((freetime=(struct icaltime_span *) malloc(sizeof(struct icaltime_span))) == 0){ icalerror_set_errno(ICAL_NEWFAILED_ERROR); return 0; } if(range.start < s->start){ freetime->start = range.start; freetime->end = s->start; freetime->is_busy = 0; pvl_insert_ordered(sl->spans,compare_span,(void*)freetime); } else { free(freetime); } range.start = s->end; } /* If the end of the range is null, then assume that everything after the last item in the calendar is open and add a span that indicates this */ if( icaltime_is_null_time(end)){ struct icaltime_span* last_span; last_span = (struct icaltime_span*)pvl_data(pvl_tail(sl->spans)); if (last_span != 0){ if ((freetime=(struct icaltime_span *) malloc(sizeof(struct icaltime_span))) == 0){ icalerror_set_errno(ICAL_NEWFAILED_ERROR); return 0; } freetime->is_busy = 0; freetime->start = last_span->end; freetime->end = freetime->start; pvl_insert_ordered(sl->spans,compare_span,(void*)freetime); } } return sl; }
/** * @brief * Check if a recurrence rule is valid and consistent. * The recurrence rule is verified against a start date and checks * that the frequency of the recurrence matches the duration of the * submitted reservation. If the duration of a reservation exceeds the * granularity of the frequency then an error message is displayed. * * @par The recurrence rule is checked to contain a COUNT or an UNTIL. * * @par Note that the PBS_TZID environment variable HAS to be set for the occurrence's * dates to be correctly computed. * * @param[in] rrule - The recurrence rule to unroll * @param[in] dtstart - The start time associated to the reservation (1st occurrence) * @param[in] dtend - The end time associated to the reservation (1st occurrence) * @param[in] duration - The duration of an occurrence. This is used when a reservation is * submitted using the -D (duration) param instead of an end time * @param[in] tz - The timezone associated to the recurrence rule * @param[in] err_code - A pointer to the error code to return. Codes are defined in pbs_error.h * * @return int * @retval The total number of occurrences that the recurrence rule and start date * * define. 1 for an advance reservation. * */ int check_rrule(char *rrule, time_t dtstart, time_t dtend, char *tz, int *err_code) { #ifdef LIBICAL /* Standing Reservation Recurrence */ int count = 1; struct icalrecurrencetype rt; struct icaltimetype start; struct icaltimetype next; struct icaltimetype first; struct icaltimetype prev; struct icalrecur_iterator_impl *itr; icaltimezone *localzone; int time_err = 0; int i; long min_occr_duration = -1; long tmp_occr_duration = 0; long duration; *err_code = 0; icalerror_clear_errno(); icalerror_set_error_state(ICAL_PARSE_ERROR, ICAL_ERROR_NONFATAL); icalerror_errors_are_fatal = 0; if (tz == NULL || rrule == NULL) return 0; localzone = icaltimezone_get_builtin_timezone(tz); /* If the timezone info directory is not accessible * then bail */ if (localzone == NULL) { *err_code = PBSE_BAD_ICAL_TZ; return 0; } rt = icalrecurrencetype_from_string(rrule); /* Check if by_day rules are defined and valid * the first item in the array of by_* rule * determines whether the item exists or not. */ for (i = 0; rt.by_day[i] < 8; i++) { if (rt.by_day[i] <= 0) { *err_code = PBSE_BAD_RRULE_SYNTAX; return 0; } } /* Check if by_hour rules are defined and valid * the first item in the array of by_* rule * determines whether the item exists or not. */ for (i = 0; rt.by_hour[i] < 25; i++) { if (rt.by_hour[i] < 0) { *err_code = PBSE_BAD_RRULE_SYNTAX; return 0; } } /* Check if the rest of the by_* rules are defined * and valid. * currently no support for * BYMONTHDAY, BYYEARDAY, BYSECOND, * BYMINUTE, BYWEEKNO, or BYSETPOS * */ if (rt.by_second[0] < 61 || /* by_second is negative such as in -10 */ rt.by_minute[0] < 61 || /* by_minute is negative such as in -10 */ rt.by_year_day[0] < 367 || /* a year day is defined */ rt.by_month_day[0] < 31 || /* a month day is defined */ rt.by_week_no[0] < 52 || /* a week number is defined */ rt.by_set_pos[0] < 367) { /* a set pos is defined */ *err_code = PBSE_BAD_RRULE_SYNTAX; return 0; } /* Require that either a COUNT or UNTIL be passed. But not both. */ if ((rt.count == 0 && icaltime_is_null_time(rt.until)) || (rt.count != 0 && !icaltime_is_null_time(rt.until))) { *err_code = PBSE_BAD_RRULE_SYNTAX2; /* Undefined iCalendar synax. A valid COUNT or UNTIL is required */ return 0; } start = icaltime_from_timet(dtstart, 0); icaltimezone_convert_time(&start, icaltimezone_get_utc_timezone(), localzone); itr = (struct icalrecur_iterator_impl*) icalrecur_iterator_new(rt, start); duration = dtend -dtstart; /* First check if the syntax of the iCalendar rule is valid */ next = icalrecur_iterator_next(itr); /* Catch case where first occurrence date is in the past */ if (icaltime_is_null_time(next)) { *err_code = PBSE_BADTSPEC; icalrecur_iterator_free(itr); return 0; } first = next; prev = first; for (next=icalrecur_iterator_next(itr); !icaltime_is_null_time(next); next=icalrecur_iterator_next(itr), count++) { /* The interval duration between two occurrences * is the time between the end of an occurrence and the * start of the next one */ tmp_occr_duration = icaltime_as_timet(next) - icaltime_as_timet(prev); /* Set the minimum time interval between occurrences */ if (min_occr_duration == -1) min_occr_duration = tmp_occr_duration; else if (tmp_occr_duration > 0 && tmp_occr_duration < min_occr_duration) min_occr_duration = tmp_occr_duration; prev = next; } /* clean up */ icalrecur_iterator_free(itr); if (icalerrno != ICAL_NO_ERROR) { *err_code = PBSE_BAD_RRULE_SYNTAX; /* Undefined iCalendar syntax */ return 0; } /* Then check if the duration fits in the frequency rule */ switch (rt.freq) { case ICAL_SECONDLY_RECURRENCE: { if (duration > 1) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno = 1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_SECONDLY; /* SECONDLY recurrence duration cannot exceed 1 second */ time_err++; } break; } case ICAL_MINUTELY_RECURRENCE: { if (duration > 60) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno = 1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_MINUTELY; /* MINUTELY recurrence duration cannot exceed 1 minute */ time_err++; } break; } case ICAL_HOURLY_RECURRENCE: { if (duration > (60*60)) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno =1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_HOURLY; /* HOURLY recurrence duration cannot exceed 1 hour */ time_err++; } break; } case ICAL_DAILY_RECURRENCE: { if (duration > (60*60*24)) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno = 1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_DAILY; /* DAILY recurrence duration cannot exceed 24 hours */ time_err++; } break; } case ICAL_WEEKLY_RECURRENCE: { if (duration > (60*60*24*7)) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno = 1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_WEEKLY; /* WEEKLY recurrence duration cannot exceed 1 week */ time_err++; } break; } case ICAL_MONTHLY_RECURRENCE: { if (duration > (60*60*24*30)) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno = 1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_MONTHLY; /* MONTHLY recurrence duration cannot exceed 1 month */ time_err++; } break; } case ICAL_YEARLY_RECURRENCE: { if (duration > (60*60*24*30*365)) { #ifdef NAS /* localmod 005 */ icalerrno = ICAL_BADARG_ERROR; #else icalerrno = 1; #endif /* localmod 005 */ *err_code = PBSE_BAD_RRULE_YEARLY; /* YEARLY recurrence duration cannot exceed 1 year */ time_err++; } break; } default: { icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR); return 0; } } if (time_err) return 0; /* If the requested reservation duration exceeds * the occurrence's duration then print an error * message and return */ if (count != 1 && duration > min_occr_duration) { *err_code = PBSE_BADTSPEC; /* Bad Time Specification(s) */ return 0; } return count; #else *err_code = PBSE_BAD_RRULE_SYNTAX; /* iCalendar is undefined */ return 0; #endif }