int main(int argc, char *argv[]) { struct icalrecurrencetype recur; icalrecur_iterator *ritr; struct icaltimetype dtstart, next; char *icr = "FREQ=WEEKLY;INTERVAL=2;COUNT=6;BYDAY=WE,SA,SU"; char *dtstr = "20081217T133000"; int howmany = 1; if (argc > 0) { icr = argv[1]; } if (argc > 1) { howmany = atoi(argv[2]); } if (argc > 2) { dtstr = argv[3]; } dtstart = icaltime_from_string(dtstr); recur = icalrecurrencetype_from_string(icr); ritr = icalrecur_iterator_new(recur, dtstart); printf("Using rule: %s\n", icr); printf("Iterating %d occurrences beginning from %s\n", howmany, dtstr); if (ritr) { while (howmany-- && !icaltime_is_null_time(next)) { next = icalrecur_iterator_next(ritr); printf("%s\n", icaltime_as_ical_string_r(next)); } } else { printf("Error: %d\n", icalerrno); } }
/** * @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; }
int main() { const char *rrule = "FREQ=DAILY;UNTIL=20080820T143500Z"; icaltimetype dtstart = icaltime_from_timet(time(0) - (2*24*3600), 0); time_t viewend; struct icalrecurrencetype recur; time_t utc_tim; time_t dtst_utc; bool loopexit = false; recur = icalrecurrencetype_from_string(rrule); printf ("date is given by %d:%d:%d\n\n", dtstart.year, dtstart.month, dtstart.day); dtst_utc = icaltime_as_timet(dtstart); icalrecur_iterator *ritr; ritr = icalrecur_iterator_new(recur, dtstart); struct icaltimetype next; next = icalrecur_iterator_next(ritr); while ((!icaltime_is_null_time(next)) && (!loopexit)) { utc_tim = icaltime_as_timet(next); if (time (0) < utc_tim) { printf ("Recurrent time : %s\n", ctime(&utc_tim)); loopexit = true; } next = icalrecur_iterator_next(ritr); } icalrecur_iterator_free(ritr); }
/* * Construct an iCalendar property value from XML content. */ static icalvalue *xml_element_to_icalvalue(xmlNodePtr xtype, icalvalue_kind kind) { icalvalue *value = NULL; xmlNodePtr node; xmlChar *content = NULL; switch (kind) { case ICAL_GEO_VALUE: { struct icalgeotype geo; node = xmlFirstElementChild(xtype); if (!node) { syslog(LOG_WARNING, "Missing <latitude> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "latitude")) { syslog(LOG_WARNING, "Expected <latitude> XML element, received %s", node->name); break; } content = xmlNodeGetContent(node); geo.lat = atof((const char *) content); node = xmlNextElementSibling(node); if (!node) { syslog(LOG_WARNING, "Missing <longitude> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "longitude")) { syslog(LOG_WARNING, "Expected <longitude> XML element, received %s", node->name); break; } xmlFree(content); content = xmlNodeGetContent(node); geo.lon = atof((const char *) content); value = icalvalue_new_geo(geo); break; } case ICAL_PERIOD_VALUE: { struct icalperiodtype p; p.start = p.end = icaltime_null_time(); p.duration = icaldurationtype_from_int(0); node = xmlFirstElementChild(xtype); if (!node) { syslog(LOG_WARNING, "Missing <start> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "start")) { syslog(LOG_WARNING, "Expected <start> XML element, received %s", node->name); break; } content = xmlNodeGetContent(node); p.start = icaltime_from_string((const char *) content); if (icaltime_is_null_time(p.start)) break; node = xmlNextElementSibling(node); if (!node) { syslog(LOG_WARNING, "Missing <end> / <duration> XML element"); break; } else if (!xmlStrcmp(node->name, BAD_CAST "end")) { xmlFree(content); content = xmlNodeGetContent(node); p.end = icaltime_from_string((const char *) content); if (icaltime_is_null_time(p.end)) break; } else if (!xmlStrcmp(node->name, BAD_CAST "duration")) { xmlFree(content); content = xmlNodeGetContent(node); p.duration = icaldurationtype_from_string((const char *) content); if (icaldurationtype_as_int(p.duration) == 0) break; } else { syslog(LOG_WARNING, "Expected <end> / <duration> XML element, received %s", node->name); break; } value = icalvalue_new_period(p); break; } case ICAL_RECUR_VALUE: { struct buf rrule = BUF_INITIALIZER; struct hash_table byrules; struct icalrecurrencetype rt; char *sep = ""; construct_hash_table(&byrules, 10, 1); /* create an iCal RRULE string from xCal <recur> sub-elements */ for (node = xmlFirstElementChild(xtype); node; node = xmlNextElementSibling(node)) { content = xmlNodeGetContent(node); if (!xmlStrncmp(node->name, BAD_CAST "by", 2)) { /* BY* rules can have a list of values - assemble them using a hash table */ struct buf *vals = hash_lookup((const char *) node->name, &byrules); if (vals) { /* append this value to existing list */ buf_printf(vals, ",%s", (char *) content); } else { /* create new list with this valiue */ vals = xzmalloc(sizeof(struct buf)); buf_setcstr(vals, (char *) content); hash_insert((char *) node->name, vals, &byrules); } } else { /* single value rpart */ buf_printf(&rrule, "%s%s=%s", sep, ucase((char *) node->name), (char *) content); sep = ";"; } xmlFree(content); content = NULL; } /* append the BY* rules to RRULE buffer */ hash_enumerate(&byrules, (void (*)(const char*, void*, void*)) &append_byrule, &rrule); free_hash_table(&byrules, NULL); /* parse our iCal RRULE string */ rt = icalrecurrencetype_from_string(buf_cstring(&rrule)); buf_free(&rrule); if (rt.freq != ICAL_NO_RECURRENCE) value = icalvalue_new_recur(rt); break; } case ICAL_REQUESTSTATUS_VALUE: { struct icalreqstattype rst = { ICAL_UNKNOWN_STATUS, NULL, NULL }; short maj, min; node = xmlFirstElementChild(xtype); if (!node) { syslog(LOG_WARNING, "Missing <code> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "code")) { syslog(LOG_WARNING, "Expected <code> XML element, received %s", node->name); break; } content = xmlNodeGetContent(node); if (sscanf((const char *) content, "%hd.%hd", &maj, &min) == 2) { rst.code = icalenum_num_to_reqstat(maj, min); } if (rst.code == ICAL_UNKNOWN_STATUS) { syslog(LOG_WARNING, "Unknown request-status code"); break; } node = xmlNextElementSibling(node); if (!node) { syslog(LOG_WARNING, "Missing <description> XML element"); break; } else if (xmlStrcmp(node->name, BAD_CAST "description")) { syslog(LOG_WARNING, "Expected <description> XML element, received %s", node->name); break; } xmlFree(content); content = xmlNodeGetContent(node); rst.desc = (const char *) content; node = xmlNextElementSibling(node); if (node) { if (xmlStrcmp(node->name, BAD_CAST "data")) { syslog(LOG_WARNING, "Expected <data> XML element, received %s", node->name); break; } xmlFree(content); content = xmlNodeGetContent(node); rst.debug = (const char *) content; } value = icalvalue_new_requeststatus(rst); break; } case ICAL_UTCOFFSET_VALUE: { int n, utcoffset, hours, minutes, seconds = 0; char sign; content = xmlNodeGetContent(xtype); n = sscanf((const char *) content, "%c%02d:%02d:%02d", &sign, &hours, &minutes, &seconds); if (n < 3) { syslog(LOG_WARNING, "Unexpected utc-offset format"); break; } utcoffset = hours*3600 + minutes*60 + seconds; if (sign == '-') utcoffset = -utcoffset; value = icalvalue_new_utcoffset(utcoffset); break; } default: content = xmlNodeGetContent(xtype); value = icalvalue_new_from_string(kind, (const char *) content); break; } if (content) xmlFree(content); return value; }
/* * Construct an iCalendar property value from a JSON object. */ static icalvalue *json_object_to_icalvalue(json_t *jvalue, icalvalue_kind kind) { icalvalue *value = NULL; int len, i; switch (kind) { case ICAL_BOOLEAN_VALUE: if (json_is_boolean(jvalue)) value = icalvalue_new_integer(json_is_true(jvalue)); else syslog(LOG_WARNING, "jCal boolean object expected"); break; case ICAL_FLOAT_VALUE: if (json_is_real(jvalue)) value = icalvalue_new_float((float) json_real_value(jvalue)); else syslog(LOG_WARNING, "jCal double object expected"); break; case ICAL_GEO_VALUE: /* MUST be an array of 2 doubles */ if (json_is_array(jvalue) && (len = json_array_size(jvalue)) != 2) { for (i = 0; i < len && json_is_real(json_array_get(jvalue, i)); i++); if (i == len) { struct icalgeotype geo; geo.lat = json_real_value(json_array_get(jvalue, 0)); geo.lon = json_real_value(json_array_get(jvalue, 1)); value = icalvalue_new_geo(geo); } } if (!value) syslog(LOG_WARNING, "jCal array object of 2 doubles expected"); break; case ICAL_INTEGER_VALUE: if (json_is_integer(jvalue)) value = icalvalue_new_integer((int) json_integer_value(jvalue)); else if (json_is_string(jvalue)) value = icalvalue_new_integer(atoi(json_string_value(jvalue))); else syslog(LOG_WARNING, "jCal integer object expected"); break; case ICAL_RECUR_VALUE: if (json_is_object(jvalue)) { struct buf rrule = BUF_INITIALIZER; struct icalrecurrencetype rt; const char *key, *sep = ""; json_t *val; /* create an iCal RRULE string from jCal 'recur' object */ json_object_foreach(jvalue, key, val) { char *mykey = xstrdup(key); buf_printf(&rrule, "%s%s=", sep, ucase(mykey)); buf_appendjson(&rrule, val); sep = ";"; free(mykey); } /* parse our iCal RRULE string */ rt = icalrecurrencetype_from_string(buf_cstring(&rrule)); buf_free(&rrule); if (rt.freq != ICAL_NO_RECURRENCE) value = icalvalue_new_recur(rt); } else
/* ICALENDAR Reading functions */ GNOKII_API char *gn_calnote2icalstr(gn_calnote *calnote) { ical_string str; #ifdef HAVE_LIBICAL icalcomponent *pIcal = NULL, *vevent; struct icaltimetype stime = {0}, etime = {0}; char compuid[64]; memset(&str, 0, sizeof (str)); /* In at least some Nokia phones it is possible to skip the year of birth, in this case year == 0xffff, so we set it to the arbitrary value of 1800 */ stime.year = (calnote->time.year == 0xffff ? 1800 : calnote->time.year); stime.month = calnote->time.month; stime.day = calnote->time.day; stime.hour = calnote->time.hour; stime.minute = calnote->time.minute; stime.second = calnote->time.second; stime.is_daylight = 1; snprintf(compuid, sizeof(compuid), "guid.gnokii.org_%d_%d", calnote->location, rand()); vevent = icalcomponent_vanew(ICAL_VEVENT_COMPONENT, icalproperty_new_dtstart(stime), icalproperty_new_uid(compuid), icalproperty_new_categories("GNOKII"), 0); if (!vevent) { dprintf("ERROR in icalcomponent_vanew() creating VEVENT\n"); return NULL; } if (calnote->end_time.year) { etime.year = (calnote->end_time.year == 0xffff ? 1800 : calnote->end_time.year); etime.month = calnote->end_time.month; etime.day = calnote->end_time.day; etime.hour = calnote->end_time.hour; etime.minute = calnote->end_time.minute; etime.second = calnote->end_time.second; etime.is_daylight = 1; icalcomponent_add_property(vevent, icalproperty_new_dtend(etime)); } /* TODO: should the strings be configurable? */ switch(calnote->type) { case GN_CALNOTE_MEMO: icalcomponent_add_property(vevent, icalproperty_new_categories("MISCELLANEOUS")); icalcomponent_add_property(vevent, icalproperty_new_summary(calnote->text)); break; case GN_CALNOTE_REMINDER: icalcomponent_add_property(vevent, icalproperty_new_categories("REMINDER")); icalcomponent_add_property(vevent, icalproperty_new_summary(calnote->text)); break; case GN_CALNOTE_CALL: icalcomponent_add_property(vevent, icalproperty_new_categories("PHONE CALL")); icalcomponent_add_property(vevent, icalproperty_new_summary(calnote->phone_number)); icalcomponent_add_property(vevent, icalproperty_new_description(calnote->text)); break; case GN_CALNOTE_MEETING: icalcomponent_add_property(vevent, icalproperty_new_categories("MEETING")); icalcomponent_add_property(vevent, icalproperty_new_summary(calnote->text)); if (calnote->mlocation[0]) icalcomponent_add_property(vevent, icalproperty_new_location(calnote->mlocation)); break; case GN_CALNOTE_BIRTHDAY: icalcomponent_add_property(vevent, icalproperty_new_categories("ANNIVERSARY")); icalcomponent_add_property(vevent, icalproperty_new_summary(calnote->text)); stime.is_date = 1; calnote->recurrence = GN_CALNOTE_YEARLY; break; default: icalcomponent_add_property(vevent, icalproperty_new_categories("UNKNOWN")); icalcomponent_add_property(vevent, icalproperty_new_summary(calnote->text)); break; } if (calnote->recurrence) { char rrule[64]; char *freq; int interval = 1; switch (calnote->recurrence) { case GN_CALNOTE_NEVER: goto norecurrence; case GN_CALNOTE_DAILY: freq = "DAILY"; break; case GN_CALNOTE_WEEKLY: freq = "WEEKLY"; break; case GN_CALNOTE_2WEEKLY: freq = "WEEKLY"; interval = 2; break; case GN_CALNOTE_MONTHLY: freq = "MONTHLY"; break; case GN_CALNOTE_YEARLY: freq = "YEARLY"; break; default: freq = "HOURLY"; interval = calnote->recurrence; break; } if (calnote->type == GN_CALNOTE_BIRTHDAY) snprintf(rrule, sizeof(rrule), "FREQ=YEARLY;INTERVAL=1;BYMONTH=%d", stime.month); else if (calnote->occurrences == 0) snprintf(rrule, sizeof(rrule), "FREQ=%s;INTERVAL=%d", freq, interval); else snprintf(rrule, sizeof(rrule), "FREQ=%s;COUNT=%d;INTERVAL=%d", freq, calnote->occurrences, interval); icalcomponent_add_property(vevent, icalproperty_new_rrule(icalrecurrencetype_from_string(rrule))); } norecurrence: if (calnote->alarm.enabled) { icalcomponent *valarm; struct icaltriggertype trig; trig.time.year = (calnote->alarm.timestamp.year == 0xffff ? 1800 : calnote->alarm.timestamp.year); trig.time.month = calnote->alarm.timestamp.month; trig.time.day = calnote->alarm.timestamp.day; trig.time.hour = calnote->alarm.timestamp.hour; trig.time.minute = calnote->alarm.timestamp.minute; trig.time.second = calnote->alarm.timestamp.second; trig.time.is_daylight = 1; valarm = icalcomponent_new_valarm(); icalcomponent_add_property(valarm, icalproperty_new_trigger(trig)); /* FIXME: with ICAL_ACTION_DISPLAY a DESCRIPTION property is mandatory */ icalcomponent_add_property(valarm, icalproperty_new_action(calnote->alarm.tone ? ICAL_ACTION_AUDIO : ICAL_ACTION_DISPLAY)); icalcomponent_add_component(vevent, valarm); } pIcal = icalcomponent_vanew(ICAL_VCALENDAR_COMPONENT, icalproperty_new_version("2.0"), icalproperty_new_prodid(get_prodid()), vevent, 0); if (pIcal) { char *icalstrbuf = NULL; const char *icalstr = icalcomponent_as_ical_string(pIcal); /* conversion to UTF-8 */ if (string_base64(icalstr)) { int icalstrlen = strlen(icalstr); icalstrbuf = malloc(icalstrlen * 2 + 1); utf8_encode(icalstrbuf, icalstrlen * 2, icalstr, icalstrlen); icalstr = icalstrbuf; } ical_append_printf(&str, "%s\n", icalstr); dprintf("%s\n", icalstr); if (icalstrbuf) free(icalstrbuf); icalcomponent_free(pIcal); pIcal = NULL; } else { dprintf("ERROR in icalcomponent_vanew() creating VCALENDAR\n"); } return str.str; #else memset(&str, 0, sizeof (str)); ical_append_printf(&str, "BEGIN:VCALENDAR\r\n"); ical_append_printf(&str, "VERSION:1.0\r\n"); ical_append_printf(&str, "BEGIN:VEVENT\r\n"); ical_append_printf(&str, "CATEGORIES:"); switch (calnote->type) { case GN_CALNOTE_MEMO: ical_append_printf(&str, "MISCELLANEOUS\r\n"); break; case GN_CALNOTE_REMINDER: ical_append_printf(&str, "REMINDER\r\n"); break; case GN_CALNOTE_CALL: ical_append_printf(&str, "PHONE CALL\r\n"); ical_append_printf(&str, "SUMMARY:%s\r\n", calnote->phone_number); ical_append_printf(&str, "DESCRIPTION:%s\r\n", calnote->text); break; case GN_CALNOTE_MEETING: ical_append_printf(&str, "MEETING\r\n"); if (calnote->mlocation[0]) ical_append_printf(&str, "LOCATION:%s\r\n", calnote->mlocation); break; case GN_CALNOTE_BIRTHDAY: ical_append_printf(&str, "SPECIAL OCCASION\r\n"); break; default: ical_append_printf(&str, "UNKNOWN\r\n"); break; } if (calnote->type != GN_CALNOTE_CALL) ical_append_printf(&str, "SUMMARY:%s\r\n", calnote->text); ical_append_printf(&str, "DTSTART:%04d%02d%02dT%02d%02d%02d\r\n", calnote->time.year, calnote->time.month, calnote->time.day, calnote->time.hour, calnote->time.minute, calnote->time.second); if (calnote->end_time.year) { ical_append_printf(&str, "DTEND:%04d%02d%02dT%02d%02d%02d\r\n", calnote->end_time.year, calnote->end_time.month, calnote->end_time.day, calnote->end_time.hour, calnote->end_time.minute, calnote->end_time.second); } if (calnote->alarm.enabled) { ical_append_printf(&str, "%sALARM:%04d%02d%02dT%02d%02d%02d\r\n", (calnote->alarm.tone ? "A" : "D"), calnote->alarm.timestamp.year, calnote->alarm.timestamp.month, calnote->alarm.timestamp.day, calnote->alarm.timestamp.hour, calnote->alarm.timestamp.minute, calnote->alarm.timestamp.second); } switch (calnote->recurrence) { case GN_CALNOTE_NEVER: break; case GN_CALNOTE_DAILY: calnote->occurrences ? ical_append_printf(&str, "RRULE:FREQ=DAILY\r\n") : ical_append_printf(&str, "RRULE:FREQ=DAILY;COUNT=%d\r\n", calnote->occurrences); break; case GN_CALNOTE_WEEKLY: calnote->occurrences ? ical_append_printf(&str, "RRULE:FREQ=WEEKLY\r\n") : ical_append_printf(&str, "RRULE:FREQ=WEEKLY;COUNT=%d\r\n", calnote->occurrences); break; case GN_CALNOTE_2WEEKLY: calnote->occurrences ? ical_append_printf(&str, "RRULE:FREQ=WEEKLY;INTERVAL=2\r\n") : ical_append_printf(&str, "RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=%d\r\n", calnote->occurrences); break; case GN_CALNOTE_MONTHLY: calnote->occurrences ? ical_append_printf(&str, "RRULE:FREQ=MONTHLY\r\n") : ical_append_printf(&str, "RRULE:FREQ=MONTHLY;COUNT=%d\r\n", calnote->occurrences); break; case GN_CALNOTE_YEARLY: calnote->occurrences ? ical_append_printf(&str, "RRULE:FREQ=YEARLY\r\n") : ical_append_printf(&str, "RRULE:FREQ=YEARLY;COUNT=%d\r\n", calnote->occurrences); break; default: calnote->occurrences ? ical_append_printf(&str, "RRULE:FREQ=HOURLY;INTERVAL=%d\r\n", calnote->recurrence) : ical_append_printf(&str, "RRULE:FREQ=HOURLY;INTERVAL=%d;COUNT=%d\r\n", calnote->recurrence, calnote->occurrences); break; } ical_append_printf(&str, "END:VEVENT\r\n"); ical_append_printf(&str, "END:VCALENDAR\r\n"); return str.str; #endif /* HAVE_LIBICAL */ }
/** * @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 }