$darkmode
Libical API Documentation 4.0 STABLE VERSION Visit the v3.0 documentation
icalrecur.c
Go to the documentation of this file.
1 /*======================================================================
2  FILE: icalrecur.c
3  CREATOR: eric 16 May 2000
4 
5  SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
6  SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
7 ========================================================================*/
8 
123 #ifdef HAVE_CONFIG_H
124 #include <config.h>
125 #endif
126 
127 #include "icalrecur.h"
128 #include "icalerror_p.h"
129 #include "icalerror.h"
130 #include "icallimits.h"
131 #include "icalmemory.h"
132 #include "icaltimezone.h"
133 #include "icalvalue.h" /* for print_date[time]_to_string() */
134 
135 #include <ctype.h>
136 #include <stddef.h> /* For offsetof() macro */
137 #include <stdint.h>
138 #include <stdlib.h>
139 
140 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
141 #include <pthread.h>
142 static pthread_mutex_t invalid_rrule_mutex = PTHREAD_MUTEX_INITIALIZER;
143 #endif
144 
145 static ICAL_GLOBAL_VAR ical_invalid_rrule_handling invalidRruleHandling = ICAL_RRULE_TREAT_AS_ERROR;
146 
147 #if defined(HAVE_LIBICU)
148 #include <unicode/ucal.h>
149 #include <unicode/ustring.h>
150 #include <stdbool.h>
151 #else
152 
153 /* The maximums below are based on Gregorian leap years */
154 #undef ICAL_BY_MONTH_SIZE
155 #undef ICAL_BY_WEEKNO_SIZE
156 #undef ICAL_BY_YEARDAY_SIZE
157 #define ICAL_BY_MONTH_SIZE 13 /* 1 to 12 */
159 #define ICAL_BY_WEEKNO_SIZE 54 /* 1 to 53 */
160 #define ICAL_BY_YEARDAY_SIZE 367 /* 1 to 366 */
161 #endif
163 
165 #if defined(HAVE_LIBICU)
166 #define MAX_TIME_T_YEAR 20000
167 #else
168 #if (SIZEOF_ICALTIME_T > 4)
169 
171 #define MAX_TIME_T_YEAR 2582
172 #else
173 
175 #define MAX_TIME_T_YEAR 2037
176 #endif
177 #endif
178 
179 #define LEAP_MONTH 0x1000
180 
182 /****************** Forward declarations ******************/
183 static void icalrecurrencetype_clear(struct icalrecurrencetype *recur);
184 static short daymask_find_next_bit(const unsigned long *days, short start_index);
185 
186 /****************** Enumeration Routines ******************/
187 
188 static const struct freq_map {
190  const char str[9];
191 } freq_map[] = {
192  {ICAL_SECONDLY_RECURRENCE, "SECONDLY"},
193  {ICAL_MINUTELY_RECURRENCE, "MINUTELY"},
194  {ICAL_HOURLY_RECURRENCE, "HOURLY"},
195  {ICAL_DAILY_RECURRENCE, "DAILY"},
196  {ICAL_WEEKLY_RECURRENCE, "WEEKLY"},
197  {ICAL_MONTHLY_RECURRENCE, "MONTHLY"},
198  {ICAL_YEARLY_RECURRENCE, "YEARLY"},
199  {ICAL_NO_RECURRENCE, ""}};
200 
202 {
203  int i;
204 
205  for (i = 0; freq_map[i].kind != ICAL_NO_RECURRENCE; i++) {
206  if (strcasecmp(str, freq_map[i].str) == 0) {
207  return freq_map[i].kind;
208  }
209  }
210  return ICAL_NO_RECURRENCE;
211 }
212 
214 {
215  int i;
216 
217  for (i = 0; freq_map[i].kind != ICAL_NO_RECURRENCE; i++) {
218  if (freq_map[i].kind == kind) {
219  return freq_map[i].str;
220  }
221  }
222  return 0;
223 }
224 
225 static const struct skip_map {
227  const char str[9];
228 } skip_map[] = {
229  {ICAL_SKIP_BACKWARD, "BACKWARD"},
230  {ICAL_SKIP_FORWARD, "FORWARD"},
231  {ICAL_SKIP_OMIT, "OMIT"},
232  {ICAL_SKIP_UNDEFINED, ""}};
233 
235 {
236  int i;
237 
238  for (i = 0; skip_map[i].kind != ICAL_SKIP_UNDEFINED; i++) {
239  if (strcasecmp(str, skip_map[i].str) == 0) {
240  return skip_map[i].kind;
241  }
242  }
243  return ICAL_SKIP_UNDEFINED;
244 }
245 
247 {
248  int i;
249 
250  for (i = 0; skip_map[i].kind != ICAL_SKIP_UNDEFINED; i++) {
251  if (skip_map[i].kind == kind) {
252  return skip_map[i].str;
253  }
254  }
255  return 0;
256 }
257 
258 static const struct wd_map {
260  const char str[3];
261 } wd_map[] = {
262  {ICAL_SUNDAY_WEEKDAY, "SU"},
263  {ICAL_MONDAY_WEEKDAY, "MO"},
264  {ICAL_TUESDAY_WEEKDAY, "TU"},
265  {ICAL_WEDNESDAY_WEEKDAY, "WE"},
266  {ICAL_THURSDAY_WEEKDAY, "TH"},
267  {ICAL_FRIDAY_WEEKDAY, "FR"},
268  {ICAL_SATURDAY_WEEKDAY, "SA"},
269  {ICAL_NO_WEEKDAY, ""}};
270 
272 {
273  int i;
274 
275  for (i = 0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
276  if (wd_map[i].wd == kind) {
277  return wd_map[i].str;
278  }
279  }
280 
281  return 0;
282 }
283 
285 {
286  int i;
287 
288  for (i = 0; wd_map[i].wd != ICAL_NO_WEEKDAY; i++) {
289  if (strcasecmp(str, wd_map[i].str) == 0) {
290  return wd_map[i].wd;
291  }
292  }
293 
294  return ICAL_NO_WEEKDAY;
295 }
296 
297 /*********************** Memory management helper routines ************************/
298 
299 static void icalrecur_free_by(icalrecurrence_by_data *by)
300 {
301  icalmemory_free_buffer(by->data);
302  by->data = NULL;
303  by->size = 0;
304 }
305 
306 bool icalrecur_resize_by(icalrecurrence_by_data *by, short size)
307 {
308  if (by->size == size) {
309  return true;
310  }
311 
312  if (size == 0) {
313  icalrecur_free_by(by);
314  return true;
315  }
316 
317  if ((by->data == NULL) || (by->size == 0)) {
318  if ((by->data != NULL) || (by->size != 0)) {
320  return false;
321  }
322 
323  by->data = (short *)icalmemory_new_buffer((size_t)size * sizeof(by->data[0]));
324  if (!by->data) {
325  return false;
326  }
327  } else {
328  short *new_data = (short *)icalmemory_resize_buffer(by->data, (size_t)size * sizeof(by->data[0]));
329  if (!new_data) {
330  return false;
331  }
332 
333  by->data = new_data;
334  }
335 
336  if (size > by->size) {
337  memset(&by->data[by->size], 0, (size_t)(size - by->size) * sizeof(by->data[0]));
338  }
339 
340  by->size = size;
341 
342  return true;
343 }
344 
345 /*********************** Rule parsing routines ************************/
346 
347 struct icalrecur_parser {
348  const char *rule;
349  char *copy;
350  char *this_clause;
351  char *next_clause;
352 
353  struct icalrecurrencetype *rt;
354 };
355 
357 enum expand_table
358 {
359  UNKNOWN = 0,
360  CONTRACT = 1,
361  EXPAND = 2,
362  ILLEGAL = 3
363 };
365 
366 struct expand_split_map_struct {
368 
369  /* Elements of the 'map' array correspond to the BYxxx rules:
370  Second,Minute,Hour,Day,Month Day,Year Day,Week No,Month,SetPos */
371 
372  short map[ICAL_BY_NUM_PARTS];
373 };
374 
382 static const struct expand_split_map_struct expand_map[] = {
383  /* M W YD MD D h m s P */
384  {ICAL_SECONDLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 1, 1}},
385  {ICAL_MINUTELY_RECURRENCE, {1, 3, 1, 1, 1, 1, 1, 2, 1}},
386  {ICAL_HOURLY_RECURRENCE, {1, 3, 1, 1, 1, 1, 2, 2, 1}},
387  {ICAL_DAILY_RECURRENCE, {1, 3, 3, 1, 1, 2, 2, 2, 1}},
388  {ICAL_WEEKLY_RECURRENCE, {1, 3, 3, 3, 2, 2, 2, 2, 1}},
389  {ICAL_MONTHLY_RECURRENCE, {1, 3, 3, 2, 2, 2, 2, 2, 1}},
390  {ICAL_YEARLY_RECURRENCE, {2, 2, 2, 2, 2, 2, 2, 2, 1}},
391  {ICAL_NO_RECURRENCE, {0, 0, 0, 0, 0, 0, 0, 0, 0}}};
392 
393 static const struct recur_map {
394  const char *str;
395  int size;
396  int min;
397  int isTime;
398 } recur_map[] = {
399  {"BYMONTH", ICAL_BY_MONTH_SIZE, 1, 0},
400  {"BYWEEKNO", ICAL_BY_WEEKNO_SIZE, -1, 0},
401  {"BYYEARDAY", ICAL_BY_YEARDAY_SIZE, -1, 0},
402  {"BYMONTHDAY", ICAL_BY_MONTHDAY_SIZE, -1, 0},
403  {"BYDAY", ICAL_BY_DAY_SIZE, 0, 0},
404  {"BYHOUR", ICAL_BY_HOUR_SIZE, 0, 1},
405  {"BYMINUTE", ICAL_BY_MINUTE_SIZE, 0, 1},
406  {"BYSECOND", ICAL_BY_SECOND_SIZE, 0, 1},
407  {"BYSETPOS", ICAL_BY_SETPOS_SIZE, -1, 0},
408 };
409 
410 static const char *icalrecur_first_clause(struct icalrecur_parser *parser)
411 {
412  char *idx;
413 
414  parser->this_clause = parser->copy;
415 
416  idx = strchr(parser->this_clause, ';');
417 
418  if (idx == 0) {
419  parser->next_clause = 0;
420  return 0;
421  }
422 
423  *idx = 0;
424  idx++;
425  parser->next_clause = idx;
426 
427  return parser->this_clause;
428 }
429 
430 static const char *icalrecur_next_clause(struct icalrecur_parser *parser)
431 {
432  char *idx;
433 
434  parser->this_clause = parser->next_clause;
435 
436  if (parser->this_clause == 0) {
437  return 0;
438  }
439 
440  idx = strchr(parser->this_clause, ';');
441 
442  if (idx == 0) {
443  parser->next_clause = 0;
444  } else {
445  *idx = 0;
446  idx++;
447  parser->next_clause = idx;
448  }
449 
450  return parser->this_clause;
451 }
452 
453 static void icalrecur_clause_name_and_value(struct icalrecur_parser *parser,
454  char **name, char **value)
455 {
456  char *idx;
457 
458  *name = parser->this_clause;
459 
460  idx = strchr(parser->this_clause, '=');
461 
462  if (idx == 0) {
463  *name = 0;
464  *value = 0;
465  return;
466  }
467 
468  *idx = 0;
469  idx++;
470  *value = idx;
471 }
472 
473 /*
474  * We expect BYHOUR, BYMINUTE, and BYSECOND data to be sorted.
475  */
476 static void sort_byrules(icalrecurrence_by_data *by)
477 {
478  short *array = by->data;
479 
480  int i, j;
481 
482  for (i = 1; i < by->size; i++) {
483  for (j = i - 1; j >= 0 && array[j] > array[j + 1]; j--) {
484  short tmp = array[j + 1];
485 
486  array[j + 1] = array[j];
487  array[j] = tmp;
488  }
489  }
490 }
491 
492 /*
493  * Sort BYSETPOS list in ascending order of magnitude,
494  * with negatives after positives
495  */
496 static void sort_bysetpos(icalrecurrence_by_data *by)
497 {
499 #define SIGN(A) ((A) < 0 ? -1 : 1)
500  short *array = by->data;
502 
503  int i, j;
504 
505  for (i = 1; i < by->size; i++) {
506  for (j = i - 1;
507  j >= 0 && ((SIGN(array[j]) == SIGN(array[j + 1]) && abs(array[j]) > abs(array[j + 1])) ||
508  (array[j] < 0 && array[j + 1] > 0));
509  j--) {
510  short tmp = array[j + 1];
511 
512  array[j + 1] = array[j];
513  array[j] = tmp;
514  }
515  }
516 #undef SIGN
517 }
518 
519 /* returns < 0 if a parsing problem:
520  -2 if an RSCALE rule is encountered yet we don't RSCALE support enabled
521  -1 for all other parsing problems
522 */
523 static int icalrecur_add_byrules(const struct icalrecur_parser *parser, icalrecurrence_by_data *by,
524  int min, int size, char *vals)
525 {
526  char *t, *n;
527  int i = 0;
528  int max = size - (min == 0);
529 
530  n = vals;
531 
532  if (!icalrecur_resize_by(by, size)) {
533  return -1;
534  }
535 
536  while (n != 0) {
537  if (i == size) {
538  return -1;
539  }
540 
541  t = n;
542 
543  n = strchr(t, ',');
544 
545  if (n != 0) {
546  *n = 0;
547  n++;
548  }
549 
550  // empty string is not allowed here
551  if (!*t) {
552  return -1;
553  }
554 
555  char *t_end;
556  int v = strtol(t, &t_end, 10);
557 
558  // We check for parsing errors later, but not if the string ends with 'L',
559  // so explicitly check the value here.
560  if (t == t_end) {
561  return -1;
562  }
563  t = t_end;
564 
565  /* Sanity check value */
566  if (v < 0) {
567  if (min >= 0 || v <= -max) {
568  return -1;
569  }
570  } else if (v > 0) {
571  if (v >= max) {
572  return -1;
573  }
574  } else if (min) {
575  return -1;
576  }
577 
578  if (*t) {
579  /* Check for leap month suffix (RSCALE only) */
580  if (by == &parser->rt->by[ICAL_BY_MONTH] && strcmp(t, "L") == 0) {
581  /* The "L" suffix in a BYMONTH recur-rule-part
582  is encoded by setting a high-order bit */
583  v |= LEAP_MONTH;
584  } else {
585  return -1;
586  }
587  }
588 
589  by->data[i++] = (short)v;
590  }
591 
592  if (!icalrecur_resize_by(by, i)) {
593  return -1;
594  }
595 
596  /* Sort time bylists.
597  * Date bylists do not require sorting because they are implemented
598  * differently (with a bitmask), and are not directly used to find
599  * the next occurrence.
600  */
601  if (by == &parser->rt->by[ICAL_BY_HOUR] ||
602  by == &parser->rt->by[ICAL_BY_MINUTE] ||
603  by == &parser->rt->by[ICAL_BY_SECOND]) {
604  sort_byrules(by);
605  }
606  /* BYSETPOS is sorted specially */
607  else if (by == &parser->rt->by[ICAL_BY_SET_POS]) {
608  sort_bysetpos(by);
609  }
610 
611  return 0;
612 }
613 
614 /*
615  * Days in the BYDAY rule are expected by the code to be sorted, and while
616  * this may be the common case, the RFC doesn't actually mandate it. This
617  * function sorts the days taking into account the first day of week.
618  */
619 static void sort_bydayrules(struct icalrecur_parser *parser)
620 {
621  icalrecurrence_by_data *by = &parser->rt->by[ICAL_BY_DAY];
622  short *array = by->data;
623 
624  int week_start, i, j;
625 
626  week_start = (int)parser->rt->week_start;
627 
628  for (i = 0; i < by->size; i++) {
629  for (j = 0; j < i; j++) {
630  int one = (int)icalrecurrencetype_day_day_of_week(array[j]) - week_start;
631  if (one < 0) {
632  one += 7;
633  }
634  int two = (int)icalrecurrencetype_day_day_of_week(array[i]) - week_start;
635  if (two < 0) {
636  two += 7;
637  }
638 
639  if (one > two) {
640  short tmp = array[j];
641 
642  array[j] = array[i];
643  array[i] = tmp;
644  }
645  }
646  }
647 }
648 
649 static int icalrecur_add_bydayrules(struct icalrecur_parser *parser,
650  const char *vals)
651 {
652  char *t, *n;
653  icalrecurrence_by_data *by = &parser->rt->by[ICAL_BY_DAY];
654 
655  char *vals_copy;
656  int idx = 0;
657 
658  if (!icalrecur_resize_by(by, ICAL_BY_DAY_SIZE)) {
659  return -1;
660  }
661 
662  vals_copy = icalmemory_strdup(vals);
663  n = vals_copy;
664 
665  while (n != 0) {
666  int sign = 1;
667  signed char weekno;
669 
670  if (idx >= by->size) {
671  icalmemory_free_buffer(vals_copy);
672  return -1;
673  }
674 
675  t = n;
676 
677  n = strchr(t, ',');
678 
679  if (n != 0) {
680  *n = 0;
681  n++;
682  }
683 
684  // empty string is not allowed here
685  if (!t[0]) {
686  icalmemory_free_buffer(vals_copy);
687  return -1;
688  }
689 
690  /* Get Optional weekno */
691  char *t_end;
692  long tmpl = strtol(t, &t_end, 10);
693  weekno = (signed char)tmpl;
694 
695  // overflow?
696  /* cppcheck-suppress knownConditionTrueFalse */
697  if (weekno != tmpl) {
698  icalmemory_free_buffer(vals_copy);
699  return -1;
700  }
701 
702  // WeekNo 0 doesn't exist
703  if ((weekno == 0) && (t != t_end)) {
704  icalmemory_free_buffer(vals_copy);
705  return -1;
706  }
707  t = t_end;
708 
709  if (weekno < 0) {
710  weekno = -weekno;
711  sign = -1;
712  }
713 
714  /* Outlook/Exchange generate "BYDAY=MO, FR" and "BYDAY=2 TH".
715  * Cope with that.
716  */
717  if (*t == ' ') {
718  t++;
719  }
720 
722 
723  /* Sanity check value */
724  if (wd == ICAL_NO_WEEKDAY || weekno >= ICAL_BY_WEEKNO_SIZE) {
725  icalmemory_free_buffer(vals_copy);
726  return -1;
727  }
728 
729  by->data[idx++] = icalrecurrencetype_encode_day(wd, sign * weekno);
730  }
731 
732  icalmemory_free_buffer(vals_copy);
733 
734  if (!icalrecur_resize_by(by, idx)) {
735  return -1;
736  }
737 
738  sort_bydayrules(parser);
739 
740  return 0;
741 }
742 
744 {
745  struct icalrecurrencetype *rule;
746 
747  rule = (struct icalrecurrencetype *)icalmemory_new_buffer(sizeof(*rule));
748 
749  if (!rule) {
750  return NULL;
751  }
752 
753  memset(rule, 0, sizeof(*rule));
754  rule->refcount = 1;
755  icalrecurrencetype_clear(rule);
756 
757  return rule;
758 }
759 
760 static void icalrecurrencetype_free(struct icalrecurrencetype *recur, int free_self)
761 {
763 #define SAFEFREE(p) \
764  if (p) { \
765  icalmemory_free_buffer(p); \
766  (p) = 0; \
767  }
768 
770  SAFEFREE(recur->rscale);
771  for (int i = 0; i < ICAL_BY_NUM_PARTS; i++) {
772  SAFEFREE(recur->by[i].data);
773  }
774 
775 #undef SAFEFREE
776 
777  if (free_self) {
778  icalmemory_free_buffer(recur);
779  }
780 }
781 
783 {
784  icalerror_check_arg_rv((recur != NULL), "recur");
785  icalerror_check_arg_rv((recur->refcount > 0), "recur->refcount > 0");
786 
787  recur->refcount++;
788 }
789 
791 {
792  icalerror_check_arg_rv((recur != NULL), "recur");
793  icalerror_check_arg_rv((recur->refcount > 0), "recur->refcount > 0");
794 
795  recur->refcount--;
796 
797  if (recur->refcount != 0) {
798  return;
799  }
800 
801  icalrecurrencetype_free(recur, 1);
802 }
803 
804 static void *icalrecur_memdup(void *p, size_t size, int *error)
805 {
806  if ((p == NULL) || (size == 0)) {
807  return p;
808  }
809 
810  void *newp = icalmemory_new_buffer(size);
811  if (newp) {
812  memcpy(newp, p, size);
813  } else {
814  *error = 1;
815  }
816 
817  return newp;
818 }
819 
820 static icalrecurrence_by_data icalrecur_by_dup(icalrecurrence_by_data *by, int *error)
821 {
822  icalrecurrence_by_data newby = {0, 0};
823 
824  newby.data = icalrecur_memdup(by->data, (size_t)by->size * sizeof(by->data[0]), error);
825  if (newby.data) {
826  newby.size = by->size;
827  }
828 
829  return newby;
830 }
831 
833 {
834  struct icalrecurrencetype *res;
835  int error = 0;
836 
837  icalerror_check_arg_rz((recur != NULL), "recur");
838 
839  res = icalrecurrencetype_new();
840  if (!res) {
841  return NULL;
842  }
843 
844  memcpy(res, recur, sizeof(*res));
845 
846  res->refcount = 1;
847 
848  if (res->rscale) {
849  res->rscale = icalmemory_strdup(res->rscale);
850  if (!res->rscale) {
851  error = 1;
852  }
853  }
854 
855  for (int i = 0; i < ICAL_BY_NUM_PARTS; i++) {
856  icalrecurrence_by_data *src_by = &recur->by[i];
857  icalrecurrence_by_data *dst_by = &res->by[i];
858  *dst_by = icalrecur_by_dup(src_by, &error);
859  }
860 
861  if (error) {
862  icalrecurrencetype_free(res, 1);
863  return NULL;
864  }
865 
866  return res;
867 }
868 
870 {
871  struct icalrecur_parser parser = {0};
873 
874  icalerror_check_arg_re(str != 0, "str", 0);
875 
876  parser.rt = icalrecurrencetype_new();
877  if (!parser.rt) {
878  return NULL;
879  }
880 
881  /* Set up the parser struct */
882  parser.rule = str;
883  parser.copy = icalmemory_strdup(parser.rule);
884  parser.this_clause = parser.copy;
885 
886  if (parser.copy == 0) {
888  icalrecurrencetype_unref(parser.rt);
889  return NULL;
890  }
891 
892  /* Loop through all of the clauses */
893  for (icalrecur_first_clause(&parser);
894  parser.this_clause != 0; icalrecur_next_clause(&parser)) {
895  char *name, *value;
896  int r = 0;
897 
898  icalrecur_clause_name_and_value(&parser, &name, &value);
899 
900  if (name == 0) {
901  if (strlen(parser.this_clause) > 0) {
902  r = -1;
903  } else {
904  /* Hit an empty name/value pair,
905  but we're also at the end of the string.
906  This was probably a trailing semicolon with no data
907  (e.g. "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;")
908  */
909  break;
910  }
911  } else if (strcasecmp(name, "FREQ") == 0) {
912  if (parser.rt->freq != ICAL_NO_RECURRENCE) {
913  /* Don't allow multiple FREQs */
914  r = -1;
915  } else {
916  parser.rt->freq = icalrecur_string_to_freq(value);
917  if (parser.rt->freq == ICAL_NO_RECURRENCE) {
918  r = -1;
919  }
920  }
921  } else if (strcasecmp(name, "RSCALE") == 0) {
922  if (parser.rt->rscale != NULL) {
923  /* Don't allow multiple RSCALEs */
924  r = -1;
925  } else {
926  parser.rt->rscale = icalmemory_strdup(value);
927  }
928  } else if (strcasecmp(name, "SKIP") == 0) {
929  if (parser.rt->skip != ICAL_SKIP_OMIT) {
930  /* Don't allow multiple SKIPs */
931  r = -1;
932  } else {
933  parser.rt->skip = icalrecur_string_to_skip(value);
934  if (parser.rt->skip == ICAL_SKIP_UNDEFINED) {
935  r = -1;
936  }
937  }
938  } else if (strcasecmp(name, "COUNT") == 0) {
939  if (parser.rt->count > 0 || !icaltime_is_null_time(parser.rt->until)) {
940  /* Don't allow multiple COUNTs, or both COUNT and UNTIL */
941  r = -1;
942  } else {
943  parser.rt->count = atoi(value);
944  /* don't allow count to be less than 1 */
945  if (parser.rt->count < 1) {
946  r = -1;
947  }
948  }
949  } else if (strcasecmp(name, "UNTIL") == 0) {
950  if (parser.rt->count > 0 || !icaltime_is_null_time(parser.rt->until)) {
951  /* Don't allow multiple COUNTs, or both COUNT and UNTIL */
952  r = -1;
953  } else {
954  parser.rt->until = icaltime_from_string(value);
955  if (icaltime_is_null_time(parser.rt->until)) {
956  r = -1;
957  }
958  }
959  } else if (strcasecmp(name, "INTERVAL") == 0) {
960  if (parser.rt->interval > 1) {
961  /* Don't allow multiple INTERVALs */
962  r = -1;
963  } else {
964  int tmp = atoi(value);
965  parser.rt->interval = (short)tmp;
966 
967  // overflow?
968  /* cppcheck-suppress knownConditionTrueFalse */
969  if (parser.rt->interval != tmp) {
970  r = -1;
971  }
972 
973  /* don't allow an interval to be less than 1
974  (RFC specifies an interval must be a positive integer) */
975  if (parser.rt->interval < 1) {
976  r = -1;
977  }
978  }
979  } else if (strcasecmp(name, "WKST") == 0) {
980  if (parser.rt->week_start != ICAL_MONDAY_WEEKDAY) {
981  /* Don't allow multiple WKSTs */
982  r = -1;
983  } else {
984  parser.rt->week_start = icalrecur_string_to_weekday(value);
985  if (parser.rt->week_start == ICAL_NO_WEEKDAY) {
986  r = -1;
987  } else {
988  sort_bydayrules(&parser);
989  }
990  }
991  } else if (strncasecmp(name, "BY", 2) == 0) {
992  r = -1;
993 
994  for (byrule = 0; byrule < ICAL_BY_NUM_PARTS; ++byrule) {
995  if (strcasecmp(name + 2, recur_map[byrule].str + 2) == 0) {
996  if (byrule == ICAL_BY_DAY) {
997  r = icalrecur_add_bydayrules(&parser, value);
998  } else {
999  icalrecurrence_by_data *by = &parser.rt->by[byrule];
1000  r = icalrecur_add_byrules(&parser, by,
1001  recur_map[byrule].min,
1002  recur_map[byrule].size,
1003  value);
1004  }
1005  break;
1006  }
1007  }
1008  } else {
1009  r = -1;
1010  }
1011 
1012  if (r) {
1013  /* Note: silently ignore when we have a leap month, yet don't have RSCALE support.
1014  The magic value "-2" indicates when that happens.
1015  */
1016  if (r != -2) {
1018  }
1019  icalrecurrencetype_clear(parser.rt);
1020  break;
1021  }
1022  }
1023 
1024  for (byrule = 0; byrule < ICAL_BY_NUM_PARTS; ++byrule) {
1025  icalrecurrence_by_data *by = &parser.rt->by[byrule];
1026 
1027  if (by->size > 0 &&
1028  expand_map[parser.rt->freq].map[byrule] == ILLEGAL) {
1029  ical_invalid_rrule_handling rruleHandlingSetting =
1031 
1032  if (rruleHandlingSetting == ICAL_RRULE_TREAT_AS_ERROR) {
1034  icalrecurrencetype_clear(parser.rt);
1035  break;
1036  } else {
1037  icalrecur_free_by(by);
1038  }
1039  }
1040  }
1041 
1042  icalmemory_free_buffer(parser.copy);
1043 
1044  if (parser.rt->freq == ICAL_NO_RECURRENCE) {
1045  icalrecurrencetype_unref(parser.rt);
1046  parser.rt = NULL;
1047  }
1048 
1049  return parser.rt;
1050 }
1051 
1053 {
1054  char *buf;
1055 
1056  buf = icalrecurrencetype_as_string_r(recur);
1058  return buf;
1059 }
1060 
1062 {
1063  char *str;
1064  char *str_p;
1065  size_t buf_sz = 200;
1066  char temp[20] = {0};
1067  int i, j;
1068 
1069  if (recur == 0 || recur->freq == ICAL_NO_RECURRENCE) {
1070  return 0;
1071  }
1072 
1073  str = (char *)icalmemory_new_buffer(buf_sz);
1074  str_p = str;
1075 
1076  if (recur->rscale != 0) {
1077  icalmemory_append_string(&str, &str_p, &buf_sz, "RSCALE=");
1078  icalmemory_append_string(&str, &str_p, &buf_sz, recur->rscale);
1079 
1080  /* Omit is the default, so no need to write that out */
1081  if (recur->skip != ICAL_SKIP_OMIT) {
1082  const char *skipstr = icalrecur_skip_to_string(recur->skip);
1083  icalmemory_append_string(&str, &str_p, &buf_sz, ";SKIP=");
1084  icalmemory_append_string(&str, &str_p, &buf_sz, skipstr);
1085  }
1086  icalmemory_append_char(&str, &str_p, &buf_sz, ';');
1087  }
1088 
1089  icalmemory_append_string(&str, &str_p, &buf_sz, "FREQ=");
1090  icalmemory_append_string(&str, &str_p, &buf_sz,
1091  icalrecur_freq_to_string(recur->freq));
1092 
1093  /* 1 is the default, so no need to write that out */
1094  if (recur->interval != 1) {
1095  snprintf(temp, sizeof(temp), "%d", recur->interval);
1096  icalmemory_append_string(&str, &str_p, &buf_sz, ";INTERVAL=");
1097  icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1098  }
1099 
1100  /* Monday is the default, so no need to write that out */
1101  if (recur->week_start != ICAL_MONDAY_WEEKDAY &&
1102  recur->week_start != ICAL_NO_WEEKDAY) {
1103  int dow = (int)icalrecurrencetype_day_day_of_week(recur->week_start);
1104  const char *daystr = icalrecur_weekday_to_string((enum icalrecurrencetype_weekday)dow);
1105  icalmemory_append_string(&str, &str_p, &buf_sz, ";WKST=");
1106  icalmemory_append_string(&str, &str_p, &buf_sz, daystr);
1107  }
1108 
1109  for (j = 0; j < ICAL_BY_NUM_PARTS; j++) {
1110  const icalrecurrence_by_data *by = &recur->by[j];
1111 
1112  /* Skip unused arrays */
1113  if (by->size > 0) {
1114  icalmemory_append_char(&str, &str_p, &buf_sz, ';');
1115  icalmemory_append_string(&str, &str_p, &buf_sz, recur_map[j].str);
1116  icalmemory_append_char(&str, &str_p, &buf_sz, '=');
1117 
1118  int limit = recur_map[j].size - 1;
1119  for (i = 0; i < limit && i < by->size; i++) {
1120  if (j == ICAL_BY_DAY) {
1121  int pos = icalrecurrencetype_day_position(by->data[i]);
1122  int dow = (int)icalrecurrencetype_day_day_of_week(by->data[i]);
1123  const char *daystr = icalrecur_weekday_to_string((enum icalrecurrencetype_weekday)dow);
1124 
1125  if (pos == 0) {
1126  icalmemory_append_string(&str, &str_p, &buf_sz, daystr);
1127  } else {
1128  snprintf(temp, sizeof(temp), "%d%s", pos, daystr);
1129  icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1130  }
1131 
1132  } else if (j == ICAL_BY_MONTH &&
1133  icalrecurrencetype_month_is_leap(by->data[i])) {
1134  snprintf(temp, sizeof(temp), "%dL",
1135  icalrecurrencetype_month_month(by->data[i]));
1136  icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1137  } else {
1138  snprintf(temp, sizeof(temp), "%d", by->data[i]);
1139  icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1140  }
1141 
1142  if ((i + 1) < limit && by->size > i + 1) {
1143  icalmemory_append_char(&str, &str_p, &buf_sz, ',');
1144  }
1145  }
1146  }
1147  }
1148 
1149  if (recur->until.year != 0) {
1150  temp[0] = 0;
1151  if (recur->until.is_date) {
1152  print_date_to_string(temp, &(recur->until));
1153  } else {
1154  print_datetime_to_string(temp, &(recur->until));
1155  }
1156 
1157  icalmemory_append_string(&str, &str_p, &buf_sz, ";UNTIL=");
1158  icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1159  }
1160 
1161  else if (recur->count != 0) {
1162  snprintf(temp, sizeof(temp), "%d", recur->count);
1163  icalmemory_append_string(&str, &str_p, &buf_sz, ";COUNT=");
1164  icalmemory_append_string(&str, &str_p, &buf_sz, temp);
1165  }
1166 
1167  return str;
1168 }
1169 
1170 /************************* occurrence iteration routines ******************/
1171 
1173 /* Number of bits in an unsigned long */
1174 #define BITS_PER_LONG ((unsigned short)(8 * sizeof(unsigned long)))
1175 
1176 /* Number of longs in mask of n bits */
1177 #define LONGS_PER_BITS(n) (((n) + BITS_PER_LONG - 1) / BITS_PER_LONG)
1178 
1179 #define ICAL_YEARDAYS_MASK_SIZE (ICAL_BY_YEARDAY_SIZE + 7)
1180 #define ICAL_YEARDAYS_MASK_OFFSET 4
1181 
1183 typedef struct icalrecurrence_iterator_by_data {
1184  icalrecurrence_by_data by;
1185  short index;
1186  short orig_data;
1187 
1188  // Static buffer for BY values that need to be modified by the iterator, so we don't modify the rule.
1189  // We have one value for each BY rule.
1190  short buffer_value;
1191 } icalrecurrence_iterator_by_data;
1192 
1193 struct icalrecur_iterator_impl {
1194  struct icaltimetype dtstart; /* copy of DTSTART: to fill in defaults */
1195  struct icalrecurrencetype *rule; /* reference to RRULE */
1196 
1197  struct icaltimetype rstart; /* DTSTART in RSCALE */
1198  struct icaltimetype istart; /* Gregorian start time for iterator */
1199  struct icaltimetype iend; /* Gregorian end time for iterator */
1200  struct icaltimetype last; /* last time returned from iterator */
1201  int32_t occurrence_no; /* number of steps made on the iterator */
1202 
1203  int32_t set_pos; /* our position in the recurrence set */
1204  int32_t recurrence_set_size; /* the size of the recurrence set */
1205  short sp_idxp, sp_idxn; /* positive and negative BYSETPOS indices */
1206  short sp_pmax; /* the last index of the BYSETPOS array with a positive value */
1207 
1208 #if defined(HAVE_LIBICU)
1209  UCalendar *greg; /* Gregorian calendar */
1210  UCalendar *rscale; /* RSCALE calendar */
1211 #endif
1212 
1213  struct icaltimetype period_start; /* Start date of monthly/yearly period */
1214 
1215  /* days[] is a bitmask of year days. A bit value of 1 marks an occurrence.
1216  The size of the bitmask is 7 + max days in year to accommodate full first
1217  and last weeks of the year: up to 3 days in previous year and
1218  up to 4 days in following year. As a result, the days are offset by 4:
1219  bit 0 is day -3 (3rd last day of previous year) and bit 4 is day 1
1220  of the current year. Days in the following year use higher day numbers,
1221  e.g. day 367 is day 1 or 2 of following year depending on whether the
1222  current year is a leap year.
1223 
1224  days_index is the day of year of the next occurrence,
1225  with a range of -3 to 4 + days in year.
1226  */
1227  unsigned long days[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
1228  short days_index;
1229 
1231  icalrecurrence_iterator_by_data bydata[ICAL_BY_NUM_PARTS];
1232 };
1233 
1234 static void daysmask_clearall(unsigned long mask[])
1235 {
1236  memset(mask, 0,
1237  sizeof(unsigned long) * LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE));
1238 }
1239 
1240 static void daysmask_set_range(unsigned long days[], int fromDayIncl, int untilDayExcl, int v)
1241 {
1242  int fromBitIdx = fromDayIncl + ICAL_YEARDAYS_MASK_OFFSET;
1243  int untilBitIdx = untilDayExcl + ICAL_YEARDAYS_MASK_OFFSET;
1244 
1245  for (int word_idx = fromBitIdx / BITS_PER_LONG;
1246  word_idx < (int)((untilBitIdx + BITS_PER_LONG - 1) / BITS_PER_LONG);
1247  word_idx++) {
1248  int lowerBitIdxIncl = (fromBitIdx <= (int)(word_idx * BITS_PER_LONG))
1249  ? 0
1250  : (fromBitIdx - (int)(word_idx * BITS_PER_LONG));
1251  int upperBitIdxExcl = (untilBitIdx >= (int)((word_idx + 1) * BITS_PER_LONG))
1252  ? (int)BITS_PER_LONG
1253  : (int)(untilBitIdx - (int)(word_idx * BITS_PER_LONG));
1254 
1255  unsigned long mask = (unsigned long)-1;
1256  if (lowerBitIdxIncl > 0) {
1257  mask &= ((unsigned long)-1) << lowerBitIdxIncl;
1258  }
1259  if (upperBitIdxExcl < (int)BITS_PER_LONG) {
1260  mask &= ((unsigned long)-1) >> (BITS_PER_LONG - upperBitIdxExcl);
1261  }
1262 
1263  if (v) {
1264  days[word_idx] |= mask;
1265  } else {
1266  days[word_idx] &= ~mask;
1267  }
1268  }
1269 }
1270 
1271 static int daysmask_setbit(unsigned long mask[], short n, int v)
1272 {
1273  int prev;
1274 
1275  n += ICAL_YEARDAYS_MASK_OFFSET;
1276 
1277  if (n >= 0) {
1278  prev = (mask[n / BITS_PER_LONG] & (1UL << (n % BITS_PER_LONG))) ? 1 : 0;
1279  } else {
1280  prev = (mask[n / BITS_PER_LONG] & (1UL >> (-n % BITS_PER_LONG))) ? 1 : 0;
1281  }
1282  if (v != prev) {
1283  if (v) {
1284  if (n >= 0) {
1285  mask[n / BITS_PER_LONG] |= (1UL << (n % BITS_PER_LONG));
1286  } else {
1287  mask[n / BITS_PER_LONG] |= (1UL >> (-n % BITS_PER_LONG));
1288  }
1289  } else {
1290  if (n >= 0) {
1291  mask[n / BITS_PER_LONG] &= ~(1UL << (n % BITS_PER_LONG));
1292  } else {
1293  mask[n / BITS_PER_LONG] &= ~(1UL >> (-n % BITS_PER_LONG));
1294  }
1295  }
1296  }
1297 
1298  return prev;
1299 }
1300 
1301 static unsigned long daysmask_getbit(const unsigned long mask[], short n)
1302 {
1303  n += ICAL_YEARDAYS_MASK_OFFSET;
1304  return (mask[n / BITS_PER_LONG] >> (n % BITS_PER_LONG)) & 1;
1305 }
1306 
1307 static bool has_by_data(icalrecur_iterator *impl, icalrecurrencetype_byrule byrule)
1308 {
1309  return (impl->bydata[byrule].orig_data == 1);
1310 }
1311 
1312 static void recur_iterator_set_static_single_by_value(icalrecur_iterator *impl,
1313  icalrecurrencetype_byrule byrule, short value)
1314 {
1315  icalrecurrence_iterator_by_data *by = &impl->bydata[byrule];
1316  by->by.size = 1;
1317  by->by.data = &by->buffer_value;
1318  by->by.data[0] = value;
1319 }
1320 
1321 static void setup_defaults(icalrecur_iterator *impl,
1322  icalrecurrencetype_byrule byrule, int deftime)
1323 {
1324  icalrecurrencetype_frequency freq = impl->rule->freq;
1325 
1326  if (impl->dtstart.is_date && recur_map[byrule].isTime) {
1327  // The BYSECOND, BYMINUTE and BYHOUR rule parts MUST NOT be specified
1328  // when the associated "DTSTART" property has a DATE value type.
1329  // These rule parts MUST be ignored in RECUR value that violate the
1330  // above requirement (e.g., generated by applications that pre-date
1331  // this revision of iCalendar).
1332  recur_iterator_set_static_single_by_value(impl, byrule, 0);
1333  } else if (expand_map[freq].map[byrule] == EXPAND) {
1334  /* Re-write the BY rule arrays with data from the DTSTART time so
1335  we don't have to explicitly deal with DTSTART */
1336  if (impl->bydata[byrule].by.size == 0) {
1337  recur_iterator_set_static_single_by_value(impl, byrule, (short)deftime);
1338  }
1339  }
1340 }
1341 
1344 static int weeks_in_year(int year)
1345 {
1346  /* Long years occur when year starts on Thu or leap year starts on Wed */
1347  int dow = icaltime_day_of_week(icaltime_from_day_of_year(1, year));
1348  int is_long = (dow == 5 || (dow == 4 && icaltime_is_leap_year(year)));
1349 
1350  return (52 + is_long);
1351 }
1352 
1354 static int __greg_month_diff(icaltimetype a, icaltimetype b)
1355 {
1356  return (12 * (b.year - a.year) + (b.month - a.month));
1357 }
1358 
1359 static void __get_start_time(icalrecur_iterator *impl, icaltimetype date,
1360  int *hour, int *minute, int *second)
1361 {
1362  icalrecurrencetype_frequency freq = impl->rule->freq;
1363 
1364  if (freq == ICAL_HOURLY_RECURRENCE) {
1365  *hour = date.hour;
1366  } else if (has_by_data(impl, ICAL_BY_HOUR)) {
1367  *hour = impl->bydata[ICAL_BY_HOUR].by.data[0];
1368  } else {
1369  *hour = impl->rstart.hour;
1370  }
1371 
1372  if (freq == ICAL_MINUTELY_RECURRENCE) {
1373  *minute = date.minute;
1374  } else if (has_by_data(impl, ICAL_BY_MINUTE)) {
1375  *minute = impl->bydata[ICAL_BY_MINUTE].by.data[0];
1376  } else {
1377  *minute = impl->rstart.minute;
1378  }
1379 
1380  if (freq == ICAL_SECONDLY_RECURRENCE) {
1381  *second = date.second;
1382  } else if (has_by_data(impl, ICAL_BY_SECOND)) {
1383  *second = impl->bydata[ICAL_BY_SECOND].by.data[0];
1384  } else {
1385  *second = impl->rstart.second;
1386  }
1387 }
1388 
1389 static int __day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b);
1390 
1391 #if defined(HAVE_LIBICU)
1392 /*
1393  * Callbacks for recurrence rules with RSCALE support (using ICU)
1394  *
1395  * References:
1396  * - tools.ietf.org/html/rfc7529
1397  * - en.wikipedia.org/wiki/Intercalation_%28timekeeping%29
1398  * - icu-project.org/apiref/icu4c/ucal_8h.html
1399  * - cldr.unicode.org/development/development-process/design-proposals/chinese-calendar-support
1400  * - cldr.unicode.org/development/development-process/design-proposals/islamic-calendar-types
1401  *
1402  * ICU Notes:
1403  * - Months are 0-based
1404  * - Leap months in Chinese and Hebrew calendars are handled differently
1405  */
1406 
1408 {
1409  UErrorCode status = U_ZERO_ERROR;
1410  UEnumeration *en;
1411  icalarray *calendars;
1412  const char *cal;
1413 
1414  calendars = icalarray_new(sizeof(const char **), 20);
1415 
1416  en = ucal_getKeywordValuesForLocale("calendar", "", false, &status);
1417  while ((cal = uenum_next(en, NULL, &status))) {
1418  cal = icalmemory_tmp_copy(cal);
1419  icalarray_append(calendars, (const void *)&cal);
1420  }
1421  uenum_close(en);
1422 
1423  return calendars;
1424 }
1425 
1426 static void set_second(icalrecur_iterator *impl, int second)
1427 {
1428  ucal_set(impl->rscale, UCAL_SECOND, (int32_t)second);
1429 }
1430 
1431 static void set_minute(icalrecur_iterator *impl, int minute)
1432 {
1433  ucal_set(impl->rscale, UCAL_MINUTE, (int32_t)minute);
1434 }
1435 
1436 static void set_hour(icalrecur_iterator *impl, int hour)
1437 {
1438  ucal_set(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)hour);
1439 }
1440 
1441 static void __set_month(icalrecur_iterator *impl, int month)
1442 {
1443  bool is_leap_month = icalrecurrencetype_month_is_leap(month);
1444 
1445  month = icalrecurrencetype_month_month(month) - 1; /* UCal is 0-based */
1446 
1447  ucal_set(impl->rscale, UCAL_MONTH, (int32_t)month);
1448  if (is_leap_month) {
1449  ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1450  }
1451 }
1452 
1453 static int set_month(icalrecur_iterator *impl, int month)
1454 {
1455  UErrorCode status = U_ZERO_ERROR;
1456  int actual_month;
1457 
1458  __set_month(impl, month);
1459 
1460  ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)1);
1461 
1462  actual_month = 1 + /* UCal is 0-based */
1463  (int)ucal_get(impl->rscale, UCAL_MONTH, &status);
1464 
1465  if (ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status)) {
1466  actual_month |= LEAP_MONTH;
1467  }
1468 
1469  if (actual_month != month) {
1470  switch (impl->rule->skip) {
1471  default:
1472  /* Should never get here! */
1473 
1474  case ICAL_SKIP_OMIT:
1475  /* Invalid month */
1476  return 0;
1477 
1478  case ICAL_SKIP_BACKWARD:
1479  /* Skip back to next valid month */
1480  ucal_add(impl->rscale, UCAL_MONTH, (int32_t)-1, &status);
1481  break;
1482 
1483  case ICAL_SKIP_FORWARD:
1484  /* UCal skips forward to valid month by default */
1485  break;
1486  }
1487  }
1488 
1489  return (1 + /* UCal is 0-based */
1490  (int)ucal_get(impl->rscale, UCAL_MONTH, &status));
1491 }
1492 
1493 static int get_months_in_year(icalrecur_iterator *impl, int year)
1494 {
1495  UErrorCode status = U_ZERO_ERROR;
1496 
1497  if (year) {
1498  ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1499  }
1500 
1501  return (1 + /* UCal is 0-based */
1502  (int)ucal_getLimit(impl->rscale, UCAL_MONTH,
1503  UCAL_ACTUAL_MAXIMUM, &status));
1504 }
1505 
1506 static int get_days_in_year(icalrecur_iterator *impl, int year)
1507 {
1508  UErrorCode status = U_ZERO_ERROR;
1509 
1510  if (year) {
1511  ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1512  }
1513 
1514  return (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_YEAR,
1515  UCAL_ACTUAL_MAXIMUM, &status);
1516 }
1517 
1518 static void set_day_of_year(icalrecur_iterator *impl, int doy)
1519 {
1520  if (doy < 1) {
1521  doy += get_days_in_year(impl, 0);
1522  }
1523 
1524  ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)doy);
1525 }
1526 
1527 static int get_start_of_week(icalrecur_iterator *impl)
1528 {
1529  UErrorCode status = U_ZERO_ERROR;
1530  int doy, dow;
1531 
1532  doy = (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1533  dow = (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1534  dow -= (int)impl->rule->week_start;
1535  if (dow < 0) {
1536  dow += 7;
1537  }
1538 
1539  return (doy - dow);
1540 }
1541 
1542 static int get_day_of_week(icalrecur_iterator *impl)
1543 {
1544  UErrorCode status = U_ZERO_ERROR;
1545 
1546  return (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1547 }
1548 
1549 static int get_week_number(icalrecur_iterator *impl, struct icaltimetype tt)
1550 {
1551  UErrorCode status = U_ZERO_ERROR;
1552  UDate last_millis;
1553  int month, weekno;
1554 
1555  /* Save existing rscale date */
1556  last_millis = ucal_getMillis(impl->rscale, &status);
1557 
1558  month = icalrecurrencetype_month_month(tt.month) - 1; /* UCal is 0-based */
1559  ucal_setDate(impl->rscale,
1560  (int32_t)tt.year, (int32_t)month, (int32_t)tt.day, &status);
1562  ucal_set(impl->rscale, UCAL_IS_LEAP_MONTH, 1);
1563  }
1564 
1565  weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1566 
1567  /* Restore saved rscale date */
1568  ucal_setMillis(impl->rscale, last_millis, &status);
1569 
1570  return weekno;
1571 }
1572 
1573 static int get_days_in_month(icalrecur_iterator *impl, int month, int year)
1574 {
1575  UErrorCode status = U_ZERO_ERROR;
1576 
1577  ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1578 
1579  if (!month) {
1580  month = impl->rstart.month;
1581  }
1582  __set_month(impl, month);
1583 
1584  return (int)ucal_getLimit(impl->rscale,
1585  UCAL_DAY_OF_MONTH, UCAL_ACTUAL_MAXIMUM, &status);
1586 }
1587 
1588 static void prepare_rscale_adjusted(icalrecur_iterator *impl,
1589  int year, int month, int day, UErrorCode *status)
1590 {
1591  ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1592 
1593  if (!month) {
1594  month = impl->rstart.month;
1595  }
1596  __set_month(impl, month);
1597 
1598  if (!day) {
1599  day = impl->rstart.day;
1600  } else if (day < 0) {
1601  day += 1 + (int)ucal_getLimit(impl->rscale, UCAL_DAY_OF_MONTH,
1602  UCAL_ACTUAL_MAXIMUM, status);
1603  }
1604  ucal_set(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)day);
1605 }
1606 
1607 static int get_day_of_year(icalrecur_iterator *impl,
1608  int year, int month, int day)
1609 {
1610  UErrorCode status = U_ZERO_ERROR;
1611  prepare_rscale_adjusted(impl, year, month, day, &status);
1612  return (int)ucal_get(impl->rscale, UCAL_DAY_OF_YEAR, &status);
1613 }
1614 
1615 static int get_day_of_week_adjusted(icalrecur_iterator *impl,
1616  int year, int month, int day)
1617 {
1618  UErrorCode status = U_ZERO_ERROR;
1619  prepare_rscale_adjusted(impl, year, month, day, &status);
1620  return (int)ucal_get(impl->rscale, UCAL_DAY_OF_WEEK, &status);
1621 }
1622 
1623 static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
1624  int normalize)
1625 {
1626  struct icaltimetype tt = impl->dtstart;
1627  UErrorCode status = U_ZERO_ERROR;
1628  UCalendar *cal = impl->rscale;
1629  int is_leap_month = 0;
1630 
1631  if (normalize && (impl->rscale != impl->greg)) {
1632  /* Convert to Gregorian date */
1633  UDate millis = ucal_getMillis(impl->rscale, &status);
1634 
1635  ucal_setMillis(impl->greg, millis, &status);
1636  cal = impl->greg;
1637  } else {
1638  is_leap_month =
1639  (int)ucal_get(impl->rscale, UCAL_IS_LEAP_MONTH, &status);
1640  }
1641 
1642  tt.year = (int)ucal_get(cal, UCAL_YEAR, &status);
1643  tt.day = (int)ucal_get(cal, UCAL_DATE, &status);
1644  tt.month = 1 + /* UCal is 0-based */
1645  (int)ucal_get(cal, UCAL_MONTH, &status);
1646  if (is_leap_month) {
1647  tt.month |= LEAP_MONTH;
1648  }
1649 
1650  if (!tt.is_date) {
1651  tt.hour = (int)ucal_get(cal, UCAL_HOUR_OF_DAY, &status);
1652  tt.minute = (int)ucal_get(cal, UCAL_MINUTE, &status);
1653  tt.second = (int)ucal_get(cal, UCAL_SECOND, &status);
1654  }
1655 
1656  return tt;
1657 }
1658 
1659 static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
1660  int day, int year, int *weekno)
1661 {
1662  ucal_set(impl->rscale, UCAL_YEAR, (int32_t)year);
1663  if (day < 0) {
1664  day += get_days_in_year(impl, 0) + 1;
1665  }
1666 
1667  ucal_set(impl->rscale, UCAL_DAY_OF_YEAR, (int32_t)day);
1668 
1669  if (weekno) {
1670  UErrorCode status = U_ZERO_ERROR;
1671 
1672  *weekno = (int)ucal_get(impl->rscale, UCAL_WEEK_OF_YEAR, &status);
1673  }
1674 
1675  return occurrence_as_icaltime(impl, 0);
1676 }
1677 
1678 static void increment_year(icalrecur_iterator *impl, int inc)
1679 {
1680  UErrorCode status = U_ZERO_ERROR;
1681 
1682  ucal_add(impl->rscale, UCAL_YEAR, (int32_t)inc, &status);
1683 }
1684 
1685 static void __increment_month(icalrecur_iterator *impl, int inc)
1686 {
1687  UErrorCode status = U_ZERO_ERROR;
1688 
1689  ucal_add(impl->rscale, UCAL_MONTH, (int32_t)inc, &status);
1690 }
1691 
1692 static void increment_monthday(icalrecur_iterator *impl, int inc)
1693 {
1694  UErrorCode status = U_ZERO_ERROR;
1695 
1696  ucal_add(impl->rscale, UCAL_DAY_OF_MONTH, (int32_t)inc, &status);
1697 }
1698 
1699 static void increment_hour(icalrecur_iterator *impl, int inc)
1700 {
1701  UErrorCode status = U_ZERO_ERROR;
1702 
1703  ucal_add(impl->rscale, UCAL_HOUR_OF_DAY, (int32_t)inc, &status);
1704 }
1705 
1706 static void increment_minute(icalrecur_iterator *impl, int inc)
1707 {
1708  UErrorCode status = U_ZERO_ERROR;
1709 
1710  ucal_add(impl->rscale, UCAL_MINUTE, (int32_t)inc, &status);
1711 }
1712 
1713 static void increment_second(icalrecur_iterator *impl, int inc)
1714 {
1715  UErrorCode status = U_ZERO_ERROR;
1716 
1717  ucal_add(impl->rscale, UCAL_SECOND, (int32_t)inc, &status);
1718 }
1719 
1720 static bool validate_byrule(icalrecur_iterator *impl,
1721  icalrecurrencetype_byrule byrule, UCalendarDateFields field,
1722  short (*decode_val)(short *, bool),
1723  bool decode_flags)
1724 {
1725  if (has_by_data(impl, byrule)) {
1726  UErrorCode status = U_ZERO_ERROR;
1727  const icalrecurrence_by_data *by_ptr = &impl->bydata[byrule].by;
1728  short max =
1729  (short)ucal_getLimit(impl->rscale, field, UCAL_MAXIMUM, &status);
1730  short idx;
1731 
1732  for (idx = 0; idx < by_ptr->size; idx++) {
1733  short val = decode_val ? decode_val(&by_ptr->data[idx], decode_flags) : by_ptr->data[idx];
1734 
1735  if (abs(val) > max) {
1736  return false;
1737  }
1738  }
1739  }
1740 
1741  return true;
1742 }
1743 
1744 static short decode_month(short *month, bool is_hebrew)
1745 {
1746  if (is_hebrew && *month > 5) { /* 5L == 0x1005 */
1747  /* Hebrew calendar:
1748  Translate RSCALE months to ICU (numbered 1-13, where 6 is leap).
1749  Hence, 5L maps to 6 and 6-12 map to 7-13. */
1750  *month = icalrecurrencetype_month_month(*month) + 1;
1751  }
1752 
1753  return icalrecurrencetype_month_month(*month) - 1; /* UCal is 0-based */
1754 }
1755 
1756 /* cppcheck-suppress constParameterCallback */
1757 static short decode_day(short *day, bool flags) //NOLINT(readability-non-const-parameter)
1758 {
1759  _unused(flags);
1760 
1761  return icalrecurrencetype_day_position(*day);
1762 }
1763 
1764 static bool initialize_rscale(icalrecur_iterator *impl)
1765 {
1766  struct icalrecurrencetype *rule = impl->rule;
1767  struct icaltimetype dtstart = impl->dtstart;
1768  char locale[ULOC_KEYWORD_AND_VALUES_CAPACITY] = {0};
1769  UErrorCode status = U_ZERO_ERROR;
1770  UChar *tzid = (UChar *)UCAL_UNKNOWN_ZONE_ID;
1771  bool is_hebrew = false;
1772 
1773  /* Convert the UTF8 timezoneid of dstart to ICU UChar. */
1774  char *src = (char *)icaltimezone_get_location((icaltimezone *)dtstart.zone);
1775  if (!src) {
1776  const char *prefix = icaltimezone_tzid_prefix();
1777  src = (char *)icaltimezone_get_tzid((icaltimezone *)dtstart.zone);
1778  /* coverity[use_after_free] */
1779  if (src && !strncmp(src, prefix, strlen(prefix))) {
1780  /* Skip past our prefix */
1781  src += strlen(prefix);
1782  }
1783  }
1784  if (src) {
1785  size_t len = (strlen(src) + 1) * U_SIZEOF_UCHAR;
1786  tzid = icalmemory_tmp_buffer(len);
1787  tzid = u_strFromUTF8Lenient(tzid, (int32_t)len, NULL, src, -1, &status);
1788  if (U_FAILURE(status)) {
1790  return false;
1791  }
1792  }
1793 
1794  /* Create locale for Gregorian calendar */
1795  (void)uloc_setKeywordValue("calendar", "gregorian",
1796  locale, sizeof(locale), &status);
1797 
1798  /* Create Gregorian calendar and set to DTSTART */
1799  impl->greg = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1800  if (impl->greg) {
1801  ucal_setDateTime(impl->greg,
1802  (int32_t)dtstart.year,
1803  (int32_t)(dtstart.month - 1), /* UCal is 0-based */
1804  (int32_t)dtstart.day,
1805  (int32_t)dtstart.hour,
1806  (int32_t)dtstart.minute,
1807  (int32_t)dtstart.second, &status);
1808  }
1809  if (!impl->greg || U_FAILURE(status)) {
1811  return false;
1812  }
1813 
1814  if (!rule->rscale) {
1815  /* Use Gregorian as RSCALE */
1816  impl->rscale = impl->greg;
1817  } else {
1818  UEnumeration *en;
1819  const char *cal;
1820  char *r;
1821 
1822  /* Lowercase the specified calendar */
1823  for (r = rule->rscale; *r; r++) {
1824  *r = tolower((int)*r);
1825  }
1826 
1827  /* Check if specified calendar is supported */
1828  en = ucal_getKeywordValuesForLocale("calendar", "", false, &status);
1829  while ((cal = uenum_next(en, NULL, &status))) {
1830  if (!strcmp(cal, rule->rscale)) {
1831  is_hebrew = !strcmp(rule->rscale, "hebrew");
1832  break;
1833  }
1834  }
1835  uenum_close(en);
1836  if (!cal) {
1838  return false;
1839  }
1840 
1841  /* Create locale for RSCALE calendar */
1842  (void)uloc_setKeywordValue("calendar", rule->rscale,
1843  locale, sizeof(locale), &status);
1844 
1845  /* Create RSCALE calendar and set to DTSTART */
1846  impl->rscale = ucal_open(tzid, -1, locale, UCAL_DEFAULT, &status);
1847  if (impl->rscale) {
1848  UDate millis = ucal_getMillis(impl->greg, &status);
1849 
1850  ucal_setMillis(impl->rscale, millis, &status);
1851  }
1852  if (!impl->rscale || U_FAILURE(status)) {
1854  return false;
1855  }
1856  }
1857 
1858  /* Validate BY_* array values whose legal maximums differ based on RSCALE */
1859  if (!validate_byrule(impl, ICAL_BY_MONTH, UCAL_MONTH,
1860  &decode_month, is_hebrew) ||
1861  !validate_byrule(impl, ICAL_BY_DAY, UCAL_WEEK_OF_YEAR, &decode_day, 0) ||
1862  !validate_byrule(impl, ICAL_BY_MONTH_DAY, UCAL_DAY_OF_MONTH, NULL, 0) ||
1863  !validate_byrule(impl, ICAL_BY_YEAR_DAY, UCAL_DAY_OF_YEAR, NULL, 0) ||
1864  !validate_byrule(impl, ICAL_BY_WEEK_NO, UCAL_WEEK_OF_YEAR, NULL, 0) ||
1865  !validate_byrule(impl, ICAL_BY_SET_POS, UCAL_DAY_OF_YEAR, NULL, 0)) {
1867  return false;
1868  }
1869 
1870  /* Set iCalendar defaults */
1871  ucal_setAttribute(impl->rscale, UCAL_MINIMAL_DAYS_IN_FIRST_WEEK, 4);
1872  ucal_setAttribute(impl->rscale, UCAL_FIRST_DAY_OF_WEEK, (int32_t)rule->week_start);
1873 
1874  /* Get rstart (DTSTART in RSCALE) */
1875  impl->rstart = occurrence_as_icaltime(impl, 0);
1876 
1877  return true;
1878 }
1879 
1881 static void set_start(icalrecur_iterator *impl, icaltimetype date)
1882 {
1883  UErrorCode status = U_ZERO_ERROR;
1884 
1885  impl->last.is_date = impl->rstart.is_date;
1886  impl->last.zone = impl->rstart.zone;
1887 
1888  if (impl->rstart.is_date) {
1889  ucal_setDate(impl->greg,
1890  (int32_t)date.year,
1891  (int32_t)(date.month - 1), /* UCal is 0-based */
1892  (int32_t)date.day, &status);
1893  } else {
1894  int hour, minute, second;
1895 
1896  __get_start_time(impl, date, &hour, &minute, &second);
1897 
1898  ucal_setDateTime(impl->greg,
1899  (int32_t)date.year,
1900  (int32_t)(date.month - 1), /* UCal is 0-based */
1901  (int32_t)date.day,
1902  (int32_t)hour,
1903  (int32_t)minute,
1904  (int32_t)second,
1905  &status);
1906  }
1907 
1908  if (impl->rscale != impl->greg) {
1909  UDate millis = ucal_getMillis(impl->greg, &status);
1910  ucal_setMillis(impl->rscale, millis, &status);
1911  }
1912 }
1913 
1914 static void set_datetime(icalrecur_iterator *impl, icaltimetype date)
1915 {
1916  UErrorCode status = U_ZERO_ERROR;
1917 
1918  impl->last.is_date = impl->rstart.is_date;
1919  impl->last.zone = impl->rstart.zone;
1920 
1921  if (impl->rstart.is_date) {
1922  ucal_setDate(impl->greg,
1923  (int32_t)date.year,
1924  (int32_t)(date.month - 1), /* UCal is 0-based */
1925  (int32_t)date.day, &status);
1926  } else {
1927  ucal_setDateTime(impl->greg,
1928  (int32_t)date.year,
1929  (int32_t)(date.month - 1), /* UCal is 0-based */
1930  (int32_t)date.day,
1931  (int32_t)date.hour,
1932  (int32_t)date.minute,
1933  (int32_t)date.second,
1934  &status);
1935  }
1936 
1937  if (impl->rscale != impl->greg) {
1938  UDate millis = ucal_getMillis(impl->greg, &status);
1939  ucal_setMillis(impl->rscale, millis, &status);
1940  }
1941 }
1942 
1944 static int month_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1945 {
1946  int diff;
1947 
1948  if (impl->rscale == impl->greg) {
1949  /* Use simple Gregorian math */
1950  diff = __greg_month_diff(a, b);
1951  } else if (a.year == b.year) {
1952  diff = b.month - a.month;
1953  } else {
1954  /* Count months in each year to account for leap months */
1955  UErrorCode status = U_ZERO_ERROR;
1956  UDate millis;
1957  int year = a.year;
1958 
1959  /* Save current date */
1960  millis = ucal_getMillis(impl->rscale, &status);
1961 
1962  set_day_of_year(impl, 1);
1963  diff = get_months_in_year(impl, year) - a.month;
1964  while (++year < b.year) {
1965  diff += get_months_in_year(impl, year);
1966  }
1967  diff += b.month;
1968 
1969  /* Restore date */
1970  ucal_setMillis(impl->rscale, millis, &status);
1971  }
1972 
1973  return diff;
1974 }
1975 
1977 static int day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
1978 {
1979  UErrorCode status = U_ZERO_ERROR;
1980  UDate millis;
1981  int diff;
1982 
1983  /* Save current date */
1984  millis = ucal_getMillis(impl->rscale, &status);
1985 
1986  set_day_of_year(impl, 1);
1987 
1988  diff = __day_diff(impl, a, b);
1989 
1990  /* Restore date */
1991  ucal_setMillis(impl->rscale, millis, &status);
1992 
1993  return diff;
1994 }
1995 
1996 static void reset_period_start(icalrecur_iterator *impl)
1997 {
1998  struct icaltimetype start = impl->period_start;
1999 
2000  (void)get_day_of_year(impl, start.year, start.month, start.day);
2001 }
2002 
2003 #else /* !HAVE_LIBICU */
2004 
2005 /*
2006  * Callbacks for recurrence rules without RSCALE (Gregorian only)
2007  */
2008 
2010 {
2011  icalarray *calendars = icalarray_new(sizeof(const char **), 1);
2012  const char *cal = "GREGORIAN";
2013 
2014  icalarray_append(calendars, &cal);
2015 
2016  return calendars;
2017 }
2018 
2019 static void set_second(icalrecur_iterator *impl, int second)
2020 {
2021  impl->last.second = second;
2022 }
2023 
2024 static void set_minute(icalrecur_iterator *impl, int minute)
2025 {
2026  impl->last.minute = minute;
2027 }
2028 
2029 static void set_hour(icalrecur_iterator *impl, int hour)
2030 {
2031  impl->last.hour = hour;
2032 }
2033 
2034 static int set_month(icalrecur_iterator *impl, int month)
2035 {
2036  return (impl->last.month = month);
2037 }
2038 
2040 #define get_months_in_year(impl, year) (12)
2041 
2043 static int get_days_in_year(icalrecur_iterator *impl, int year)
2044 {
2045  _unused(impl);
2046 
2047  return icaltime_days_in_year(year);
2048 }
2049 
2050 static void set_day_of_year(icalrecur_iterator *impl, int doy)
2051 {
2052  struct icaltimetype next;
2053 
2054  if (doy < 1) {
2055  doy += get_days_in_year(impl, impl->last.year);
2056  }
2057 
2058  next = icaltime_from_day_of_year(doy, impl->last.year);
2059 
2060  impl->last.day = next.day;
2061  impl->last.month = next.month;
2062  impl->last.year = next.year;
2063 }
2064 
2065 static int get_start_of_week(const icalrecur_iterator *impl)
2066 {
2067  return icaltime_start_doy_week(impl->last, (int)impl->rule->week_start);
2068 }
2069 
2070 static int get_day_of_week(const icalrecur_iterator *impl)
2071 {
2072  return icaltime_day_of_week(impl->last);
2073 }
2074 
2077 static int get_week_number(icalrecur_iterator *impl, struct icaltimetype tt)
2078 {
2079  int dow, week;
2080 
2081  _unused(impl);
2082 
2083  /* Normalize day of week so that week_start day is 1 */
2084  dow = icaltime_day_of_week(tt) - (int)(impl->rule->week_start - 1);
2085  if (dow <= 0) {
2086  dow += 7;
2087  }
2088 
2089  week = (icaltime_day_of_year(tt) - dow + 10) / 7;
2090  if (week < 1) {
2091  /* Last week of preceding year */
2092  week = weeks_in_year(tt.year - 1);
2093  } else if (week > weeks_in_year(tt.year)) {
2094  /* First week of following year */
2095  week = 1;
2096  }
2097 
2098  return week;
2099 }
2100 
2101 static int get_days_in_month(icalrecur_iterator *impl, int month, int year)
2102 {
2103  _unused(impl);
2104 
2105  return icaltime_days_in_month(month, year);
2106 }
2107 
2108 static struct icaltimetype get_dtstart_adjusted(icalrecur_iterator *impl,
2109  int year, int month, int day)
2110 {
2111  struct icaltimetype t = impl->dtstart;
2112 
2113  t.is_date = 1;
2114  t.year = year;
2115 
2116  if (!month) {
2117  month = impl->dtstart.month;
2118  }
2119  t.month = month;
2120 
2121  if (!day) {
2122  day = impl->dtstart.day;
2123  } else if (day < 0) {
2124  day += icaltime_days_in_month(month, year) + 1;
2125  }
2126  t.day = day;
2127 
2128  return t;
2129 }
2130 
2131 static int get_day_of_year(icalrecur_iterator *impl,
2132  int year, int month, int day)
2133 {
2134  return icaltime_day_of_year(get_dtstart_adjusted(impl, year, month, day));
2135 }
2136 
2137 static int get_day_of_week_adjusted(icalrecur_iterator *impl,
2138  int year, int month, int day)
2139 {
2140  return icaltime_day_of_week(get_dtstart_adjusted(impl, year, month, day));
2141 }
2142 
2143 static struct icaltimetype occurrence_as_icaltime(icalrecur_iterator *impl,
2144  int normalize)
2145 {
2146  return (normalize ? icaltime_normalize(impl->last) : impl->last);
2147 }
2148 
2149 static struct icaltimetype __icaltime_from_day_of_year(icalrecur_iterator *impl,
2150  int day, int year, int *weekno)
2151 {
2152  struct icaltimetype tt;
2153 
2154  if (day < 0) {
2155  day += get_days_in_year(impl, year) + 1;
2156  }
2157 
2158  tt = icaltime_from_day_of_year(day, year);
2159 
2160  if (weekno) {
2161  *weekno = get_week_number(impl, tt);
2162  }
2163  return tt;
2164 }
2165 
2166 static void increment_year(icalrecur_iterator *impl, int inc)
2167 {
2168  impl->last.year += inc;
2169 }
2170 
2171 static void __increment_month(icalrecur_iterator *impl, int inc)
2172 {
2173  int years;
2174 
2175  impl->last.month += inc;
2176 
2177  /* Months are offset by one */
2178  impl->last.month--;
2179 
2180  years = impl->last.month / 12;
2181 
2182  impl->last.month = impl->last.month % 12;
2183 
2184  if (impl->last.month < 0) {
2185  impl->last.month = impl->last.month + 12;
2186  years--;
2187  }
2188 
2189  impl->last.month++;
2190 
2191  if (years != 0) {
2192  increment_year(impl, years);
2193  }
2194 }
2195 
2196 static void increment_monthday(icalrecur_iterator *impl, int inc)
2197 {
2198  icaltime_adjust(&impl->last, inc, 0, 0, 0);
2199 }
2200 
2201 static void increment_hour(icalrecur_iterator *impl, int inc)
2202 {
2203  icaltime_adjust(&impl->last, 0, inc, 0, 0);
2204 }
2205 
2206 static void increment_minute(icalrecur_iterator *impl, int inc)
2207 {
2208  icaltime_adjust(&impl->last, 0, 0, inc, 0);
2209 }
2210 
2211 static void increment_second(icalrecur_iterator *impl, int inc)
2212 {
2213  icaltime_adjust(&impl->last, 0, 0, 0, inc);
2214 }
2215 
2216 static bool initialize_rscale(icalrecur_iterator *impl)
2217 {
2218  if (impl->rule->rscale && strcasecmp(impl->rule->rscale, "GREGORIAN")) {
2220  return false;
2221  }
2222 
2223  impl->rstart = impl->dtstart;
2224 
2225  return true;
2226 }
2227 
2229 static void set_start(icalrecur_iterator *impl, icaltimetype date)
2230 {
2231  impl->last.year = date.year;
2232  impl->last.month = date.month;
2233  impl->last.day = date.day;
2234  impl->last.is_date = impl->dtstart.is_date;
2235  impl->last.zone = impl->dtstart.zone;
2236 
2237  if (!impl->dtstart.is_date) {
2238  __get_start_time(impl, date, &impl->last.hour,
2239  &impl->last.minute, &impl->last.second);
2240  }
2241 }
2242 
2243 static void set_datetime(icalrecur_iterator *impl, icaltimetype date)
2244 {
2245  impl->last = date;
2246 }
2247 
2249 static int month_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2250 {
2251  _unused(impl);
2252 
2253  return __greg_month_diff(a, b);
2254 }
2255 
2257 static int day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2258 {
2259  return __day_diff(impl, a, b);
2260 }
2261 
2262 static void reset_period_start(icalrecur_iterator *impl)
2263 {
2264  /* We only want to set the date, not the time */
2265  impl->last.year = impl->period_start.year;
2266  impl->last.month = impl->period_start.month;
2267  impl->last.day = impl->period_start.day;
2268 }
2269 
2270 #endif /* HAVE_LIBICU */
2271 
2272 static int get_second(icalrecur_iterator *impl)
2273 {
2274  return occurrence_as_icaltime(impl, 1).second;
2275 }
2276 
2277 static int get_minute(icalrecur_iterator *impl)
2278 {
2279  return occurrence_as_icaltime(impl, 1).minute;
2280 }
2281 
2282 static int get_hour(icalrecur_iterator *impl)
2283 {
2284  return occurrence_as_icaltime(impl, 1).hour;
2285 }
2286 
2287 static bool __iterator_set_start(icalrecur_iterator *impl, icaltimetype start);
2288 static void increment_month(icalrecur_iterator *impl, int inc);
2289 static void expand_month_days(icalrecur_iterator *impl, int year, int month);
2290 static void expand_year_days(icalrecur_iterator *impl, int year);
2291 static int next_yearday(icalrecur_iterator *impl,
2292  void (*next_period)(icalrecur_iterator *, int));
2293 static int prev_yearday(icalrecur_iterator *impl,
2294  void (*next_period)(icalrecur_iterator *, int));
2295 
2296 static void adjust_to_byday(icalrecur_iterator *impl)
2297 {
2298  /* If there is ICAL_BY_DAY data, then we need to move the initial
2299  time to the start of the ICAL_BY_DAY data. That is if the
2300  start time is on a Wednesday, and the rule has
2301  BYDAY=MO,WE,FR, move the initial time back to
2302  monday. Otherwise, jumping to the next week ( jumping 7
2303  days ahead ) will skip over some occurrences in the
2304  second week. */
2305 
2306  /* This depends on impl->bydata[ICAL_BY_DAY].by.data being correctly sorted by
2307  * day. This should probably be abstracted to make such assumption
2308  * more explicit. */
2309  short this_dow = (short)get_day_of_week(impl);
2310  short dow = (short)(impl->bydata[ICAL_BY_DAY].by.data[0] - this_dow);
2311 
2312  /* Normalize day of week around week start */
2313  if (dow != 0 && this_dow < (short)impl->rule->week_start) {
2314  dow -= 7;
2315  }
2316 
2317  if ((this_dow < impl->bydata[ICAL_BY_DAY].by.data[0] && dow >= 0) || dow < 0) {
2318  /* initial time is after first day of ICAL_BY_DAY data */
2319  increment_monthday(impl, dow);
2320  }
2321 }
2322 
2323 icalrecur_iterator *icalrecur_iterator_new(struct icalrecurrencetype *rule,
2324  struct icaltimetype dtstart)
2325 {
2326  if (rule == NULL) {
2328  return 0;
2329  }
2330 
2331  icalrecur_iterator *impl;
2332  icalrecurrencetype_frequency freq = rule->freq;
2334 
2336 
2337  if (freq == ICAL_NO_RECURRENCE) {
2339  return 0;
2340  }
2341 
2343 #define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max))
2344  /* Make sure that DTSTART is a sane value */
2346  if (!icaltime_is_valid_time(dtstart) ||
2347  !IN_RANGE(dtstart.year, 0, MAX_TIME_T_YEAR) ||
2348  !IN_RANGE(dtstart.month, 1, 12) ||
2349  !IN_RANGE(dtstart.day, 1,
2350  icaltime_days_in_month(dtstart.month, dtstart.year)) ||
2351  (!dtstart.is_date && (!IN_RANGE(dtstart.hour, 0, 23) ||
2352  !IN_RANGE(dtstart.minute, 0, 59) ||
2353  !IN_RANGE(dtstart.second, 0, 59)))) {
2355  return 0;
2356  }
2357 
2358  if (!(impl = (icalrecur_iterator *)icalmemory_new_buffer(sizeof(icalrecur_iterator)))) {
2360  return 0;
2361  }
2362 
2363  memset(impl, 0, sizeof(icalrecur_iterator));
2364 
2365  impl->dtstart = dtstart;
2366 
2367 #if defined(HAVE_LIBICU)
2368  if (rule->rscale) {
2369  // The referenced rule should be treated as immutable, but in case of rscale we need
2370  // to modify it (change rscale to lower, shift months in decode_month()), so we
2371  // clone the whole rule and leave the original one untouched.
2372  rule = icalrecurrencetype_clone(rule);
2373  if (!rule) {
2375  icalmemory_free_buffer(impl);
2376  return 0;
2377  }
2378  } else
2379 #endif
2380  {
2381  // Without rscale we don't need to modify the rule state. We need to populate some by
2382  // values if they aren't set, but we have dedicated storage for that within the
2383  // iterator (i.e. icalrecurrence_iterator_by_data.buffer_value). So we simply ref
2384  // the rule but don't clone it.
2385  icalrecurrencetype_ref(rule);
2386  }
2387 
2388  impl->rule = rule;
2389 
2390  impl->iend = icaltime_null_time();
2391 
2392  for (byrule = 0; byrule < ICAL_BY_NUM_PARTS; ++byrule) {
2393  impl->bydata[byrule].by = impl->rule->by[byrule];
2394 
2395  /* Note which by rules had data in them when the iterator was
2396  created. We can't use the actual by_x arrays, because the
2397  empty ones will be given default values later in this
2398  routine. The orig_data array will be used later in has_by_data */
2399 
2400  impl->bydata[byrule].orig_data =
2401  (short)(impl->rule->by[byrule].size > 0);
2402 
2403  /* Check if the recurrence rule is legal */
2404  if (expand_map[freq].map[byrule] == ILLEGAL &&
2405  has_by_data(impl, byrule)) {
2406  ical_invalid_rrule_handling rruleHandlingSetting =
2408  if (rruleHandlingSetting == ICAL_RRULE_IGNORE_INVALID) {
2409  impl->bydata[byrule].orig_data = 0;
2410  } else {
2412  icalmemory_free_buffer(impl);
2413  return 0;
2414  }
2415  }
2416  }
2417 
2418  if (initialize_rscale(impl) == 0) {
2420  return 0;
2421  }
2422 
2423  /* Set up defaults for BY_* arrays */
2424  setup_defaults(impl, ICAL_BY_SECOND, impl->rstart.second);
2425 
2426  setup_defaults(impl, ICAL_BY_MINUTE, impl->rstart.minute);
2427 
2428  setup_defaults(impl, ICAL_BY_HOUR, impl->rstart.hour);
2429 
2430  setup_defaults(impl, ICAL_BY_MONTH_DAY, impl->rstart.day);
2431 
2432  setup_defaults(impl, ICAL_BY_MONTH, impl->rstart.month);
2433 
2434  if (has_by_data(impl, ICAL_BY_SET_POS)) {
2435  impl->sp_pmax = 0;
2436  while (impl->sp_pmax < impl->bydata[ICAL_BY_SET_POS].by.size &&
2437  impl->bydata[ICAL_BY_SET_POS].by.data[impl->sp_pmax] > 0) {
2438  impl->sp_pmax++;
2439  }
2440  impl->sp_pmax--;
2441  }
2442 
2443  if (!__iterator_set_start(impl, dtstart)) {
2445  return 0;
2446  }
2447 
2448  return impl;
2449 }
2450 
2451 void icalrecur_iterator_free(icalrecur_iterator *impl)
2452 {
2453  icalerror_check_arg_rv((impl != 0), "impl");
2454 
2455 #if defined(HAVE_LIBICU)
2456  if (impl->greg) {
2457  if (impl->rscale && (impl->rscale != impl->greg)) {
2458  ucal_close(impl->rscale);
2459  }
2460 
2461  ucal_close(impl->greg);
2462  }
2463 #endif
2464 
2465  icalrecurrencetype_unref(impl->rule);
2466  icalmemory_free_buffer(impl);
2467 }
2468 
2470 static int __day_diff(icalrecur_iterator *impl, icaltimetype a, icaltimetype b)
2471 {
2472  int diff;
2473 
2474  if (a.year == b.year) {
2475  diff = get_day_of_year(impl, b.year, b.month, b.day) -
2476  get_day_of_year(impl, a.year, a.month, a.day);
2477  } else {
2478  /* Swap a and b if a is greater than b */
2479  int flipped = 0;
2480  int year;
2481 
2482  if (a.year > b.year) {
2483  icaltimetype temp = a;
2484 
2485  a = b;
2486  b = temp;
2487  flipped = 1;
2488  }
2489 
2490  /* Count days in each year to account for leap days/months */
2491  year = a.year;
2492 
2493  diff = get_days_in_year(impl, year) -
2494  get_day_of_year(impl, a.year, a.month, a.day);
2495  while (++year < b.year) {
2496  diff += get_days_in_year(impl, year);
2497  }
2498  diff += get_day_of_year(impl, b.year, b.month, b.day);
2499 
2500  if (flipped) {
2501  /* The difference is negative because a was greater than b */
2502  diff = -diff;
2503  }
2504  }
2505 
2506  return diff;
2507 }
2508 
2512 static void increment_month(icalrecur_iterator *impl, int inc)
2513 {
2514  __increment_month(impl, inc);
2515 
2516  if (has_by_data(impl, ICAL_BY_MONTH)) {
2517  struct icaltimetype this = occurrence_as_icaltime(impl, 0);
2518 
2519  while (this.year < MAX_TIME_T_YEAR) {
2520  icalrecurrence_iterator_by_data *bydata = &impl->bydata[ICAL_BY_MONTH];
2521  for (bydata->index = 0;
2522  bydata->index < bydata->by.size; bydata->index++) {
2523  if (this.month == bydata->by.data[bydata->index]) {
2524  return;
2525  }
2526  }
2527 
2528  __increment_month(impl, inc);
2529  this = occurrence_as_icaltime(impl, 0);
2530  }
2531  }
2532 }
2533 
2534 static int next_unit(icalrecur_iterator *impl,
2535  int by_unit, icalrecurrencetype_frequency frequency,
2536  int (*next_sub_unit)(icalrecur_iterator *),
2537  void (*set_unit)(icalrecur_iterator *, int),
2538  int (*get_unit)(icalrecur_iterator *),
2539  int period_len,
2540  void (*increment_unit)(icalrecur_iterator *, int))
2541 {
2542  int has_by_unit = (by_unit > ICAL_BYRULE_NO_CONTRACTION) &&
2543  (impl->bydata[by_unit].by.size > 0);
2544  int this_frequency = (impl->rule->freq == frequency);
2545 
2546  int end_of_data = 0;
2547 
2548  icalassert(has_by_unit || this_frequency);
2549 
2550  if (next_sub_unit && next_sub_unit(impl) == 0) {
2551  return 0;
2552  }
2553 
2554  const size_t max_recurrence_time_count = icallimit_get(ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL);
2555  if (has_by_unit) {
2556  /* Frequency must be hours, minutes or seconds */
2557  icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2558  if (this_frequency) {
2559  bydata->index++;
2560  /* Take the frequency into account and treat the byrule data as limiting */
2561  size_t stalledCnt = 0;
2562  while ((impl->last.year < MAX_TIME_T_YEAR) && (stalledCnt++ < max_recurrence_time_count)) {
2563  int last_unit = get_unit(impl);
2564  /* Find a BY* value that works with the interval length */
2565  while (bydata->index < bydata->by.size) {
2566  int cur_by = bydata->by.data[bydata->index];
2567  if ((cur_by >= last_unit) &&
2568  ((cur_by - last_unit) % impl->rule->interval) == 0) {
2569  set_unit(impl, cur_by);
2570  return 1;
2571  }
2572  bydata->index++;
2573  }
2574  /* If none found, increment to next period (i.e., increment super unit,
2575  * but take into account interval length). */
2576  bydata->index = 0;
2577  int multiplier = 1;
2578  if (last_unit + impl->rule->interval < period_len) {
2579  int diff = period_len - last_unit;
2580  multiplier = (diff / impl->rule->interval + (diff % impl->rule->interval > 0));
2581  }
2582  increment_unit(impl, multiplier * impl->rule->interval);
2583  }
2584  } else {
2585  bydata->index++;
2586 
2587  if (bydata->by.size <= bydata->index) {
2588  bydata->index = 0;
2589 
2590  end_of_data = 1;
2591  }
2592 
2593  if (bydata->index < bydata->by.size) {
2594  set_unit(impl, bydata->by.data[bydata->index]);
2595  } else {
2597  }
2598  }
2599  } else {
2600  /* Compute the next value from the last time and the freq interval */
2601  increment_unit(impl, impl->rule->interval);
2602  end_of_data = 1;
2603  }
2604 
2605  return end_of_data;
2606 }
2607 
2608 static int next_second(icalrecur_iterator *impl)
2609 {
2610  return next_unit(impl, ICAL_BY_SECOND, ICAL_SECONDLY_RECURRENCE, NULL,
2611  &set_second, &get_second, 60, &increment_second);
2612 }
2613 
2614 static int next_minute(icalrecur_iterator *impl)
2615 {
2616  return next_unit(impl, ICAL_BY_MINUTE, ICAL_MINUTELY_RECURRENCE, &next_second,
2617  &set_minute, &get_minute, 60, &increment_minute);
2618 }
2619 
2620 static int next_hour(icalrecur_iterator *impl)
2621 {
2622  return next_unit(impl, ICAL_BY_HOUR, ICAL_HOURLY_RECURRENCE, &next_minute,
2623  &set_hour, &get_hour, 24, &increment_hour);
2624 }
2625 
2626 static int next_day(icalrecur_iterator *impl)
2627 {
2628  return next_unit(impl, ICAL_BYRULE_NO_CONTRACTION, ICAL_DAILY_RECURRENCE, &next_hour,
2629  NULL, NULL, 0, &increment_monthday);
2630 }
2631 
2632 static int prev_unit(icalrecur_iterator *impl,
2633  int by_unit, icalrecurrencetype_frequency frequency,
2634  int (*prev_sub_unit)(icalrecur_iterator *),
2635  void (*set_unit)(icalrecur_iterator *, int),
2636  int (*get_unit)(icalrecur_iterator *),
2637  void (*increment_unit)(icalrecur_iterator *, int))
2638 {
2639  int has_by_unit = (by_unit > ICAL_BYRULE_NO_CONTRACTION) &&
2640  (impl->bydata[by_unit].by.size > 0);
2641  int this_frequency = (impl->rule->freq == frequency);
2642 
2643  int end_of_data = 0;
2644 
2645  icalassert(has_by_unit || this_frequency);
2646 
2647  if (prev_sub_unit && prev_sub_unit(impl) == 0) {
2648  return 0;
2649  }
2650 
2651  if (has_by_unit) {
2652  icalrecurrence_iterator_by_data *bydata = &impl->bydata[by_unit];
2653  if (this_frequency) {
2654  bydata->index--;
2655 
2656  while (impl->last.year > 0) {
2657  int last_unit = get_unit(impl);
2658  while (bydata->index >= 0) {
2659  int cur_by = bydata->by.data[bydata->index];
2660  if ((cur_by <= last_unit) && (impl->rule->interval > 0) &&
2661  ((last_unit - cur_by) % impl->rule->interval) == 0) {
2662  set_unit(impl, cur_by);
2663  return 1;
2664  }
2665  bydata->index--;
2666  }
2667  bydata->index = bydata->by.size - 1;
2668  int multiplier = 1;
2669  if (last_unit - impl->rule->interval > 0) {
2670  multiplier = (last_unit / impl->rule->interval + (last_unit % impl->rule->interval > 0));
2671  }
2672  increment_unit(impl, -multiplier * impl->rule->interval);
2673  }
2674  } else {
2675  bydata->index--;
2676 
2677  if (bydata->index < 0) {
2678  bydata->index =
2679  bydata->by.size - 1;
2680 
2681  end_of_data = 1;
2682  }
2683 
2684  set_unit(impl, bydata->by.data[bydata->index]);
2685  }
2686 
2687  } else {
2688  /* Compute the next value from the last time and the freq interval */
2689  increment_unit(impl, -impl->rule->interval);
2690  end_of_data = 1;
2691  }
2692 
2693  return end_of_data;
2694 }
2695 
2696 static int prev_second(icalrecur_iterator *impl)
2697 {
2698  return prev_unit(impl, ICAL_BY_SECOND, ICAL_SECONDLY_RECURRENCE, NULL,
2699  &set_second, &get_second, &increment_second);
2700 }
2701 
2702 static int prev_minute(icalrecur_iterator *impl)
2703 {
2704  return prev_unit(impl, ICAL_BY_MINUTE, ICAL_MINUTELY_RECURRENCE, &prev_second,
2705  &set_minute, &get_minute, &increment_minute);
2706 }
2707 
2708 static int prev_hour(icalrecur_iterator *impl)
2709 {
2710  return prev_unit(impl, ICAL_BY_HOUR, ICAL_HOURLY_RECURRENCE, &prev_minute,
2711  &set_hour, &get_hour, &increment_hour);
2712 }
2713 
2714 static int prev_day(icalrecur_iterator *impl)
2715 {
2716  return prev_unit(impl, ICAL_BYRULE_NO_CONTRACTION, ICAL_DAILY_RECURRENCE, &prev_hour,
2717  NULL, NULL, &increment_monthday);
2718 }
2719 
2721 static void expand_bymonth_days(icalrecur_iterator *impl, int year, int month)
2722 {
2723  int i;
2724  int days_in_month = get_days_in_month(impl, month, year);
2725 
2726  for (i = 0; i < impl->bydata[ICAL_BY_MONTH_DAY].by.size; i++) {
2727  short doy = ICAL_BY_YEARDAY_SIZE, mday = impl->bydata[ICAL_BY_MONTH_DAY].by.data[i];
2728  int this_month = month;
2729 
2730  if (abs(mday) > days_in_month) {
2731  int days_in_year = get_days_in_year(impl, year);
2732 
2733  switch (impl->rule->skip) {
2734  default:
2735  /* Should never get here! */
2736 
2737  case ICAL_SKIP_OMIT:
2738  continue;
2739 
2740  case ICAL_SKIP_FORWARD:
2741  if (mday > 0) {
2742  this_month++; /* Next month */
2743  }
2744 
2745  if (this_month > get_months_in_year(impl, year)) {
2746  doy = days_in_year + 1; /* First day of next year */
2747  } else {
2748  mday = 1; /* First day of month */
2749  }
2750  break;
2751 
2752  case ICAL_SKIP_BACKWARD:
2753  if (mday < 0) {
2754  this_month--; /* Prev month */
2755  }
2756 
2757  if (this_month == 0) {
2758  doy = 0; /* Last day of prev year */
2759  } else {
2760  mday = -1; /* Last day of month */
2761  }
2762  break;
2763  }
2764  }
2765 
2766  if (doy == ICAL_BY_YEARDAY_SIZE) {
2767  doy = get_day_of_year(impl, year, this_month, mday);
2768  }
2769 
2770  daysmask_setbit(impl->days, doy, 1);
2771  if (doy < impl->days_index) {
2772  impl->days_index = doy;
2773  }
2774  }
2775 }
2776 
2778 static void expand_by_day(icalrecur_iterator *impl, int year,
2779  int doy_offset, int last_day,
2780  int first_dow, int last_dow,
2781  int is_limiting)
2782 {
2783  /* Try to calculate each of the occurrences. */
2784  unsigned long bydays[LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)];
2785  int i;
2786 
2787  memcpy(bydays, impl->days, sizeof(bydays));
2788 
2789  daysmask_set_range(impl->days, doy_offset + 1, doy_offset + last_day + 1, 0);
2790 
2791  for (i = 0; i < impl->bydata[ICAL_BY_DAY].by.size; i++) {
2792  /* This is 1 (Sun) to 7 (Sat). */
2793  int dow = (int)icalrecurrencetype_day_day_of_week(impl->bydata[ICAL_BY_DAY].by.data[i]);
2794  int pos = icalrecurrencetype_day_position(impl->bydata[ICAL_BY_DAY].by.data[i]);
2795  int first_matching_day, last_matching_day;
2796  int day, this_weekno;
2797 
2798  /* Calculate the first day in the period
2799  with the given weekday, and the last day. */
2800  first_matching_day = ((dow + 7 - first_dow) % 7) + 1;
2801  last_matching_day = last_day - ((last_dow + 7 - dow) % 7);
2802 
2803  if (pos == 0) {
2804  /* First instance of the weekday within the period.
2805  (Remaining instances added by loop below. */
2806  day = first_matching_day;
2807 
2808  } else if (pos > 0) {
2809  /* nth instance of the weekday within the period. */
2810  day = first_matching_day + (pos - 1) * 7;
2811 
2812  if (day > last_matching_day) {
2813  continue;
2814  }
2815 
2816  } else { /* pos < 0 */
2817  /* -nth instance of the weekday within the period. */
2818  day = last_matching_day + (pos + 1) * 7;
2819 
2820  if (day < first_matching_day) {
2821  continue;
2822  }
2823  }
2824 
2825  if (doy_offset < 0) {
2826  this_weekno = 1;
2827  } else {
2828  (void)__icaltime_from_day_of_year(impl, day + doy_offset, year,
2829  &this_weekno);
2830  }
2831 
2832  /* Add instance(s) of the weekday within the period */
2833  do {
2834  int valid = 0;
2835 
2836  if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
2837  /* Make sure our day falls in one of the BYWEEKNO */
2838  int nweeks = weeks_in_year(year);
2839  int j;
2840 
2841  for (j = 0; j < impl->bydata[ICAL_BY_WEEK_NO].by.size; j++) {
2842  int weekno = impl->bydata[ICAL_BY_WEEK_NO].by.data[j];
2843 
2844  if (weekno < 0) {
2845  weekno += nweeks + 1;
2846  }
2847 
2848  if (weekno == this_weekno) {
2849  valid = 1;
2850  break;
2851  }
2852  }
2853  } else {
2854  valid = 1;
2855  }
2856 
2857  if (valid) {
2858  int new_val = is_limiting
2859  /* "Filter" the year days bitmask with the bydays bitmask */
2860  ? (int)daysmask_getbit(bydays, day + doy_offset)
2861  /* Add each BYDAY to the year days bitmask */
2862  : 1;
2863 
2864  if (!daysmask_setbit(impl->days, day + doy_offset, new_val) && new_val) {
2865  if (day + doy_offset < impl->days_index) {
2866  impl->days_index = day + doy_offset;
2867  }
2868  }
2869  }
2870 
2871  } while (!pos && ((day += 7) <= last_day) && ++this_weekno);
2872  }
2873 }
2874 
2878 static void expand_month_days(icalrecur_iterator *impl, int year, int month)
2879 {
2880  int doy_offset, days_in_month, first_dow;
2881 
2882  daysmask_clearall(impl->days);
2883 
2884  /* We may end up skipping fwd/bwd a month during expansion.
2885  Mark our current start date so next_month() can increment from here */
2886  impl->period_start = occurrence_as_icaltime(impl, 0);
2887 
2888  doy_offset = get_day_of_year(impl, year, month, 1) - 1;
2889  first_dow = get_day_of_week_adjusted(impl, year, month, 1);
2890  days_in_month = get_days_in_month(impl, month, year);
2891 
2892  /* Add each BYMONTHDAY to the year days bitmask */
2893  expand_bymonth_days(impl, year, month);
2894 
2895  if (has_by_data(impl, ICAL_BY_DAY)) {
2896  /* Apply each BYDAY to the year days bitmask */
2897  int last_dow;
2898 
2899  impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
2900 
2901  last_dow = get_day_of_week_adjusted(impl, year, month, days_in_month);
2902 
2903  expand_by_day(impl, year, doy_offset, days_in_month,
2904  first_dow, last_dow,
2905  has_by_data(impl, ICAL_BY_MONTH_DAY));
2906  }
2907 }
2908 
2909 static void __next_month(icalrecur_iterator *impl, int inc)
2910 {
2911  struct icaltimetype this;
2912 
2913  /* Increment to and expand the next month */
2914  increment_month(impl, inc);
2915  this = occurrence_as_icaltime(impl, 0);
2916  expand_month_days(impl, this.year, this.month);
2917 }
2918 
2919 static int next_month(icalrecur_iterator *impl)
2920 {
2921  return next_yearday(impl, &__next_month);
2922 }
2923 
2924 static int prev_month(icalrecur_iterator *impl)
2925 {
2926  return prev_yearday(impl, &__next_month);
2927 }
2928 
2929 static int next_weekday_by_week(icalrecur_iterator *impl)
2930 {
2931  int end_of_data = 0;
2932 
2933  if (next_hour(impl) == 0) {
2934  return 0;
2935  }
2936 
2937  if (!has_by_data(impl, ICAL_BY_DAY)) {
2938  return 1;
2939  }
2940 
2941  /* If we get here, we need to step to the next day */
2942 
2943  for (;;) {
2944  impl->bydata[ICAL_BY_DAY].index++; /* Look at next elem in BYDAY array */
2945 
2946  /* Are we at the end of the BYDAY array? */
2947  if (impl->bydata[ICAL_BY_DAY].index >= impl->bydata[ICAL_BY_DAY].by.size) {
2948  impl->bydata[ICAL_BY_DAY].index = 0; /* Reset to 0 */
2949  end_of_data = 1; /* Signal that we're at the end */
2950  }
2951 
2952  /* Add the day of week offset to the start of this week, and use
2953  that to get the next day */
2954  /* ignore position of dow ("4FR"), only use dow ("FR") */
2955  int dow = (int)icalrecurrencetype_day_day_of_week(
2956  impl->bydata[ICAL_BY_DAY].by.data[impl->bydata[ICAL_BY_DAY].index]);
2957  dow -= (int)impl->rule->week_start; /* Set Sunday to be 0 */
2958  if (dow < 0) {
2959  dow += 7;
2960  }
2961 
2962  int start_of_week = get_start_of_week(impl);
2963 
2964  if (dow + start_of_week < 1) {
2965  /* The selected date is in the previous year. */
2966  if (!end_of_data) {
2967  continue;
2968  }
2969 
2970  increment_year(impl, -1);
2971  }
2972 
2973  set_day_of_year(impl, start_of_week + dow);
2974 
2975  return end_of_data;
2976  }
2977 }
2978 
2979 static bool next_week(icalrecur_iterator *impl)
2980 {
2981  /* Increment to the next week day,
2982  if there is data at a level less than a week */
2983  if (next_weekday_by_week(impl) == 0) {
2984  return 0; /* Have not reached end of week yet */
2985  }
2986 
2987  /* If we get here, we have incremented through the entire week, and
2988  can increment to the next week */
2989 
2990  /* Jump to the next week */
2991  increment_monthday(impl, 7 * impl->rule->interval);
2992 
2993  return 1;
2994 }
2995 
2996 static int prev_weekday_by_week(icalrecur_iterator *impl)
2997 {
2998  int end_of_data = 0;
2999  int start_of_week, dow;
3000 
3001  if (prev_hour(impl) == 0) {
3002  return 0;
3003  }
3004 
3005  if (!has_by_data(impl, ICAL_BY_DAY)) {
3006  return 1;
3007  }
3008 
3009  /* If we get here, we need to step to the previous day */
3010 
3011  impl->bydata[ICAL_BY_DAY].index--; /* Look at previous elem in BYDAY array */
3012 
3013  /* Are we at the end of the BYDAY array? */
3014  if (impl->bydata[ICAL_BY_DAY].index < 0) {
3015  impl->bydata[ICAL_BY_DAY].index = impl->bydata[ICAL_BY_DAY].by.size - 1;
3016  end_of_data = 1; /* Signal that we're at the end */
3017  }
3018 
3019  /* Add the day of week offset to the start of this week, and use
3020  that to get the next day */
3021  /* ignore position of dow ("4FR"), only use dow ("FR") */
3022  dow = (int)icalrecurrencetype_day_day_of_week(impl->bydata[ICAL_BY_DAY].by.data[impl->bydata[ICAL_BY_DAY].index]);
3023  dow -= (int)impl->rule->week_start; /* Set Sunday to be 0 */
3024  if (dow < 0) {
3025  dow += 7;
3026  }
3027 
3028  start_of_week = get_start_of_week(impl);
3029 
3030  if (dow + start_of_week < 1) {
3031  /* The selected date is in the previous year. */
3032  increment_year(impl, -1);
3033  }
3034 
3035  set_day_of_year(impl, start_of_week + dow);
3036 
3037  return end_of_data;
3038 }
3039 
3040 static int prev_week(icalrecur_iterator *impl)
3041 {
3042  /* Decrement to the previous week day,
3043  if there is data at a level less than a week */
3044  if (prev_weekday_by_week(impl) == 0) {
3045  return 0; /* Have not reached start of week yet */
3046  }
3047 
3048  /* If we get here, we have decremented through the entire week, and
3049  can decrement to the previous week */
3050 
3051  /* Jump to the previous week */
3052  increment_monthday(impl, 7 * -impl->rule->interval);
3053 
3054  return 1;
3055 }
3056 
3057 /* For INTERVAL=YEARLY, set up the year days bitmask in the iterator to
3058  list all of the days of the current year that are specified in this
3059  rule. */
3060 static void expand_year_days(icalrecur_iterator *impl, int year)
3061 {
3062  int i;
3063  short days_in_year = (short)get_days_in_year(impl, year);
3064  short doy;
3065 
3066  daysmask_clearall(impl->days);
3067 
3068  /* We may end up skipping fwd/bwd a year during expansion.
3069  Mark our current start date so next_year() can increment from here */
3070  impl->period_start = occurrence_as_icaltime(impl, 0);
3071 
3072  if (has_by_data(impl, ICAL_BY_YEAR_DAY)) {
3073  /* We only support BYYEARDAY + BYDAY */
3074  if (has_by_data(impl, ICAL_BY_WEEK_NO) ||
3075  has_by_data(impl, ICAL_BY_MONTH) || has_by_data(impl, ICAL_BY_MONTH_DAY)) {
3077  return;
3078  }
3079 
3080  /* Add each BYYEARDAY to the year days bitmask */
3081  for (i = 0; i < impl->bydata[ICAL_BY_YEAR_DAY].by.size; i++) {
3082  doy = impl->bydata[ICAL_BY_YEAR_DAY].by.data[i];
3083 
3084  if (abs(doy) > days_in_year) {
3085  switch (impl->rule->skip) {
3086  default:
3087  /* Should never get here! */
3088 
3089  case ICAL_SKIP_OMIT:
3090  /* Invalid day */
3091  continue;
3092 
3093  case ICAL_SKIP_FORWARD:
3094  if (doy < 0) {
3095  doy = 1; /* First day of this year */
3096  } else {
3097  doy = days_in_year + 1; /* First day of next year */
3098  }
3099  break;
3100 
3101  case ICAL_SKIP_BACKWARD:
3102  if (doy < 0) {
3103  doy = 0; /* Last day of prev year */
3104  } else {
3105  doy = days_in_year; /* Last day of this year */
3106  }
3107  break;
3108  }
3109  } else if (doy < 0) {
3110  doy += days_in_year + 1;
3111  }
3112 
3113  daysmask_setbit(impl->days, doy, 1);
3114  if (doy < impl->days_index) {
3115  impl->days_index = doy;
3116  }
3117  }
3118  } else if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
3119  int weekno;
3120 
3121  /* We only support BYWEEKNO + BYDAY */
3122  if (has_by_data(impl, ICAL_BY_YEAR_DAY) ||
3123  has_by_data(impl, ICAL_BY_MONTH_DAY) ||
3124  (has_by_data(impl, ICAL_BY_MONTH) && !has_by_data(impl, ICAL_BY_DAY))) {
3126  return;
3127  }
3128 
3129  /* BYWEEKNO + BYDAY handled below */
3130  if (!has_by_data(impl, ICAL_BY_DAY)) {
3131  int nweeks = weeks_in_year(year);
3132 
3133  int start_doy = 1;
3134  /* See which week contains Jan 1 */
3135  (void)__icaltime_from_day_of_year(impl, 1, year, &weekno);
3136  if (weekno > 1) {
3137  /* Jan 1 is in last week of previous year - jump ahead */
3138  start_doy += 7;
3139  }
3140  /* Get the first day of the first week,
3141  * accounting for the week start */
3142  set_day_of_year(impl, 1);
3143  start_doy += get_start_of_week(impl) - 1;
3144  /* Adjust to the next instance of DTSTART's week day */
3145  start_doy += (get_day_of_week_adjusted(impl, impl->dtstart.year,
3146  impl->dtstart.month, impl->dtstart.day) -
3147  (int)impl->rule->week_start + 7) %
3148  7;
3149  /* Reset impl to this year */
3150  (void)get_days_in_year(impl, year);
3151 
3152  /* Add day of week in each BYWEEKNO to the year days bitmask */
3153  for (i = 0; i < impl->bydata[ICAL_BY_WEEK_NO].by.size; i++) {
3154  weekno = impl->bydata[ICAL_BY_WEEK_NO].by.data[i];
3155 
3156  if (weekno < 0) {
3157  weekno += nweeks + 1;
3158  } else if (weekno > nweeks) {
3159  continue;
3160  }
3161 
3162  doy = start_doy + 7 * (weekno - 1);
3163 
3164  daysmask_setbit(impl->days, doy, 1);
3165  if (doy < impl->days_index) {
3166  impl->days_index = doy;
3167  }
3168  }
3169  }
3170  } else {
3171  /* Add each BYMONTHDAY in each BYMONTH to the year days bitmask */
3172  for (i = 0; i < impl->bydata[ICAL_BY_MONTH].by.size; i++) {
3173  int month = set_month(impl, impl->bydata[ICAL_BY_MONTH].by.data[i]);
3174 
3175  if (month > 0 && month < ICAL_BY_MONTH_SIZE) {
3176  expand_bymonth_days(impl, year, month);
3177  }
3178  }
3179  }
3180 
3181  if (has_by_data(impl, ICAL_BY_DAY)) {
3182  /* Apply each BYDAY to the year days bitmask */
3183  int limiting =
3184  has_by_data(impl, ICAL_BY_YEAR_DAY) || has_by_data(impl, ICAL_BY_MONTH_DAY);
3185  int first_dow, last_dow;
3186 
3187  impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3188 
3189  if (has_by_data(impl, ICAL_BY_MONTH)) {
3190  /* Numeric BYDAY are within each month */
3191 
3192  for (i = 0; i < impl->bydata[ICAL_BY_MONTH].by.size; i++) {
3193  short month = impl->bydata[ICAL_BY_MONTH].by.data[i];
3194  if (month > 0 && month < ICAL_BY_MONTH_SIZE) {
3195  int doy_offset, days_in_month;
3196 
3197  /* Get offset within year & day of week of first day of month */
3198  doy_offset =
3199  get_day_of_year(impl, year, month, 1) - 1;
3200  first_dow = get_day_of_week_adjusted(impl, year, month, 1);
3201 
3202  /* Get day of week of last day of month */
3203  days_in_month = get_days_in_month(impl, month, year);
3204  last_dow = get_day_of_week_adjusted(impl, year,
3205  month, days_in_month);
3206 
3207  expand_by_day(impl, year, doy_offset, days_in_month,
3208  first_dow, last_dow, limiting);
3209  }
3210  }
3211  } else {
3212  /* Numeric BYDAY are within the year */
3213  short doy_offset = 0, last_day;
3214 
3215  if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
3216  int weekno;
3217 
3218  /* See which week contains Jan 1 */
3219  (void)__icaltime_from_day_of_year(impl, 1, year, &weekno);
3220  if (weekno > 1) {
3221  /* Jan 1 is in last week of previous year - jump ahead */
3222  doy_offset += 7;
3223  }
3224 
3225  /* Set start and end of ISO week-numbering year */
3226  set_day_of_year(impl, 1);
3227  doy_offset += get_start_of_week(impl) - 1;
3228  last_day = (7 * weeks_in_year(year)) - doy_offset - 1;
3229 
3230  first_dow = (int)impl->rule->week_start;
3231  last_dow = (first_dow + 6) % 7;
3232  } else {
3233  /* Get day of week of first day of year */
3234  first_dow = get_day_of_week_adjusted(impl, year, 1, 1);
3235 
3236  /* Get day of week of last day of year */
3237  set_day_of_year(impl, days_in_year);
3238  last_dow = get_day_of_week(impl);
3239 
3240  last_day = days_in_year;
3241  }
3242 
3243  expand_by_day(impl, year, doy_offset, last_day, first_dow, last_dow, limiting);
3244  }
3245  }
3246 }
3247 
3248 static void __next_year(icalrecur_iterator *impl, int inc)
3249 {
3250  struct icaltimetype this;
3251 
3252  /* Increment to and expand the next year */
3253  increment_year(impl, inc);
3254  this = occurrence_as_icaltime(impl, 0);
3255  expand_year_days(impl, this.year);
3256 }
3257 
3258 static int next_year(icalrecur_iterator *impl)
3259 {
3260  return next_yearday(impl, &__next_year);
3261 }
3262 
3263 static int prev_year(icalrecur_iterator *impl)
3264 {
3265  return prev_yearday(impl, &__next_year);
3266 }
3267 
3268 static short daymask_find_next_bit(const unsigned long *days, short start_index)
3269 {
3270  short days_index = start_index;
3271  unsigned long v;
3272  short startBitIndex;
3273  unsigned short wordIdx;
3274 
3275  if (days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3276  return ICAL_YEARDAYS_MASK_SIZE;
3277  }
3278 
3279  // Prepare the first word, where searching might not start at the beginning
3280  startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3281  wordIdx = (unsigned short)(startBitIndex / BITS_PER_LONG);
3282  v = days[wordIdx];
3283  if (startBitIndex >= 0) {
3284  v >>= startBitIndex % BITS_PER_LONG;
3285  } else {
3286  v <<= -startBitIndex % BITS_PER_LONG;
3287  }
3288 
3289  if (!v) {
3290  // so the first word didn't contain any bits of interest.
3291  days_index += BITS_PER_LONG - startBitIndex % BITS_PER_LONG;
3292 
3293  // Are there more empty words following? Skip them.
3294  unsigned short maxWordIdx = (unsigned short)(LONGS_PER_BITS(ICAL_YEARDAYS_MASK_SIZE)) - 1;
3295  while (days_index < ICAL_YEARDAYS_MASK_SIZE && wordIdx < maxWordIdx) {
3296  wordIdx++;
3297  v = days[wordIdx];
3298 
3299  if (v) {
3300  break;
3301  }
3302 
3303  days_index += BITS_PER_LONG;
3304  }
3305  }
3306 
3307  if (v) {
3308  // We found a word containing the next bit but don't know the exact
3309  // position yet. Do a b-search to find it.
3310 
3311  unsigned long mask;
3312  int maskSize = (int)(BITS_PER_LONG / 2);
3313  mask = (((unsigned long)1) << maskSize) - 1;
3314 
3315  while (maskSize) {
3316  if ((v & mask) == 0) {
3317  v >>= maskSize;
3318  days_index += maskSize;
3319  }
3320  maskSize /= 2;
3321  mask >>= maskSize;
3322  }
3323  }
3324 
3325  return days_index;
3326 }
3327 
3328 static short daymask_find_prev_bit(const unsigned long *days, short start_index)
3329 {
3330  short days_index = start_index;
3331  unsigned long v;
3332  short startBitIndex;
3333  int wordIdx;
3334 
3335  if (days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3336  return -ICAL_YEARDAYS_MASK_OFFSET;
3337  }
3338 
3339  // Prepare the first word, where searching might not start at the beginning
3340  startBitIndex = days_index + ICAL_YEARDAYS_MASK_OFFSET;
3341  wordIdx = (int)(startBitIndex / BITS_PER_LONG);
3342  v = days[wordIdx];
3343  v <<= BITS_PER_LONG - (startBitIndex % BITS_PER_LONG) - 1;
3344 
3345  if (!v) {
3346  // so the first word didn't contain any bits of interest.
3347  days_index -= (startBitIndex % BITS_PER_LONG) + 1;
3348 
3349  // Are there more empty words leading? Skip them.
3350  while (days_index > -ICAL_YEARDAYS_MASK_OFFSET) {
3351  wordIdx--;
3352  v = days[wordIdx];
3353 
3354  if (v) {
3355  break;
3356  }
3357 
3358  days_index -= BITS_PER_LONG;
3359  }
3360  }
3361 
3362  if (v) {
3363  // We found a word containing the next bit but don't know the exact
3364  // position yet. Do a b-search to find it.
3365 
3366  unsigned long mask;
3367  int maskSize = (int)(BITS_PER_LONG / 2);
3368  mask = ((((unsigned long)1) << maskSize) - 1) << maskSize;
3369 
3370  while (maskSize) {
3371  if ((v & mask) == 0) {
3372  v <<= maskSize;
3373  days_index -= maskSize;
3374  }
3375  maskSize /= 2;
3376  /* coverity[integer_overflow] */
3377  mask <<= maskSize;
3378  }
3379  }
3380 
3381  return days_index;
3382 }
3383 
3384 static int next_yearday(icalrecur_iterator *impl,
3385  void (*next_period)(icalrecur_iterator *, int))
3386 {
3387  if (next_hour(impl) == 0) {
3388  return 0;
3389  }
3390 
3391  /* We may have skipped fwd/bwd a month/year with previous occurrence.
3392  Reset the period start date so we can increment properly */
3393  reset_period_start(impl);
3394 
3395  /* Find next year day that is set */
3396  impl->days_index = daymask_find_next_bit(impl->days, impl->days_index + 1);
3397 
3398  int ret = 0;
3399 
3400  if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3401  ret = 1;
3402  if (next_period) {
3403  for (;;) {
3404  /* Increment to and expand the next period */
3405  next_period(impl, impl->rule->interval);
3406 
3407  if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
3408  break; /* break when a matching day is found */
3409  }
3410  }
3411  } else {
3412  /* When next_period is NULL,
3413  we only indicate that we have
3414  reached the end of the period */
3415  return 1;
3416  }
3417  }
3418 
3419  if (impl->days_index < 1) {
3420  /* Day is in previous year */
3421  increment_year(impl, -1);
3422  }
3423 
3424  set_day_of_year(impl, impl->days_index);
3425 
3426  return ret;
3427 }
3428 
3429 static int prev_yearday(icalrecur_iterator *impl,
3430  void (*next_period)(icalrecur_iterator *, int))
3431 {
3432  if (prev_hour(impl) == 0) {
3433  return 0;
3434  }
3435 
3436  /* We may have skipped fwd/bwd a month/year with previous occurrence.
3437  Reset the period start date so we can decrement properly */
3438  reset_period_start(impl);
3439 
3440  /* Find previous year day that is set */
3441  impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3442 
3443  int ret = 0;
3444 
3445  while (impl->days_index <= -ICAL_YEARDAYS_MASK_OFFSET) {
3446  if (next_period) {
3447  ret = 1;
3448  /* Decrement to and expand the previous period */
3449  next_period(impl, -impl->rule->interval);
3450 
3451  impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3452  impl->days_index = daymask_find_prev_bit(impl->days, impl->days_index - 1);
3453  } else {
3454  /* When next_period is NULL,
3455  we only indicate that we have
3456  reached the end of the period */
3457  return 1;
3458  }
3459  }
3460 
3461  if (impl->days_index < 1) {
3462  /* Day is in previous year */
3463  increment_year(impl, -1);
3464  }
3465 
3466  set_day_of_year(impl, impl->days_index);
3467 
3468  return ret;
3469 }
3470 
3471 static int days_in_current_month(icalrecur_iterator *impl)
3472 {
3473  return get_days_in_month(impl, impl->last.month, impl->last.year);
3474 }
3475 
3476 static int days_in_current_year(icalrecur_iterator *impl)
3477 {
3478  return get_days_in_year(impl, impl->last.year);
3479 }
3480 
3481 static inline int has_contract_restriction(icalrecur_iterator *impl,
3483 {
3484  return impl->bydata[byrule].by.size > 0 &&
3485  expand_map[impl->rule->freq].map[byrule] == CONTRACT;
3486 }
3487 
3488 static bool check_contract_restriction(icalrecur_iterator *impl,
3489  icalrecurrencetype_byrule byrule, int v,
3490  int (*get_total)(icalrecur_iterator *))
3491 {
3492  if (has_contract_restriction(impl, byrule)) {
3493  int total = 0;
3494  bool pass = false;
3495  for (int itr = 0; itr < impl->bydata[byrule].by.size; itr++) {
3496  short byval = impl->bydata[byrule].by.data[itr];
3497  if ((byval < 0) && (total == 0)) {
3498  if (get_total) {
3499  // load total value lazily only when needed
3500  total = get_total(impl);
3501  } else {
3502  // limiting by negative values is only allowed for
3503  // BYMONTHDAY, BYYEARDAY (BYDAY is handled separately)
3505  continue;
3506  }
3507  }
3508 
3509  if (v == ((byval >= 0) ? byval : (total + 1 + byval))) {
3510  pass = true;
3511  break;
3512  }
3513  }
3514 
3515  return pass;
3516  }
3517 
3518  /* This is not a contracting byrule, or it has no data, so the test passes */
3519  return true;
3520 }
3521 
3522 static bool check_contracting_rules(icalrecur_iterator *impl)
3523 {
3524  struct icaltimetype last = occurrence_as_icaltime(impl, 0);
3525 
3527 // Check `has_contract_restriction` before calling `check_contract_restriction` to avoid
3528 // evaluating potentially expensive `v` if not needed.
3529 #define CHECK_CONTRACT_RESTRICTION(by, v, get_total) \
3530  (!has_contract_restriction(impl, (by)) || check_contract_restriction(impl, (by), (v), (get_total)))
3531 
3532  if (
3533  CHECK_CONTRACT_RESTRICTION(ICAL_BY_SECOND, last.second, NULL) &&
3534  CHECK_CONTRACT_RESTRICTION(ICAL_BY_MINUTE, last.minute, NULL) &&
3535  CHECK_CONTRACT_RESTRICTION(ICAL_BY_HOUR, last.hour, NULL) &&
3536  CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH_DAY, last.day, days_in_current_month) &&
3537  CHECK_CONTRACT_RESTRICTION(ICAL_BY_MONTH, last.month, NULL) &&
3538  CHECK_CONTRACT_RESTRICTION(ICAL_BY_WEEK_NO, get_week_number(impl, last), NULL) &&
3539  CHECK_CONTRACT_RESTRICTION(
3540  ICAL_BY_DAY, get_day_of_week_adjusted(impl, last.year, last.month, last.day), NULL) &&
3541  CHECK_CONTRACT_RESTRICTION(
3542  ICAL_BY_YEAR_DAY, get_day_of_year(impl, last.year, last.month, last.day), days_in_current_year)) {
3543  return true;
3544  }
3545 
3546 #undef CHECK_CONTRACT_RESTRICTION
3547 
3549  return false;
3550 }
3551 
3552 /* Initialize data relating to BYSETPOS, in particular:
3553  * set_pos, sp_idxp, sp_idxn, and recurrence_set_size.
3554  * This must be called at the start of each new period
3555  *
3556  * next == 1 indicates we are advancing the iterator,
3557  * and so are at the start of a new period, while
3558  * next == 0 indicates we are at the end of one
3559  */
3560 static void setup_setpos(icalrecur_iterator *impl, int next)
3561 {
3562  /* Save data that may be modified */
3563  int days_index = impl->days_index;
3564  int bydata_indices[ICAL_BY_NUM_PARTS];
3565  for (int byrule = 0; byrule < ICAL_BY_NUM_PARTS; byrule++) {
3566  bydata_indices[byrule] = impl->bydata[byrule].index;
3567  }
3568  struct icaltimetype last = impl->last;
3569 
3570  impl->recurrence_set_size = 1;
3571  int period_change = 1;
3572  do {
3573  switch (impl->rule->freq) {
3575  break;
3577  /* call next_second instead of next_minute
3578  * to avoid going to the next minute */
3579  period_change = (next ? next_second : prev_second)(impl);
3580  break;
3582  period_change = (next ? next_minute : prev_minute)(impl);
3583  break;
3584  case ICAL_DAILY_RECURRENCE:
3585  period_change = (next ? next_hour : prev_hour)(impl);
3586  break;
3588  period_change = (next ? next_weekday_by_week : prev_weekday_by_week)(impl);
3589  break;
3591  /* call next_yearday instead of next_month
3592  * to avoid expanding month days */
3593  period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3594  break;
3596  period_change = (next ? next_yearday : prev_yearday)(impl, NULL);
3597  break;
3598  default:
3600  return;
3601  }
3602  if (period_change == 0 && check_contracting_rules(impl)) {
3603  impl->recurrence_set_size++;
3604  }
3605  } while (period_change == 0);
3606 
3607  if (next) {
3608  impl->set_pos = 1;
3609  impl->sp_idxp = 0;
3610  impl->sp_idxn = impl->bydata[ICAL_BY_SET_POS].by.size - 1;
3611  } else {
3612  impl->set_pos = impl->recurrence_set_size;
3613  impl->sp_idxp = impl->sp_pmax;
3614  impl->sp_idxn = impl->sp_pmax + 1;
3615  }
3616 
3617  /* Restore what was modified
3618  * Because we do not expand month/year days,
3619  * the days bitfield is not modified */
3620  set_datetime(impl, last);
3621  impl->last = last;
3622  impl->days_index = days_index;
3623  for (int byrule = 0; byrule < ICAL_BY_NUM_PARTS; byrule++) {
3624  impl->bydata[byrule].index = bydata_indices[byrule];
3625  }
3626 }
3627 
3628 /* If s1 occurs before s2 in the recurrence set, return -1
3629  * If s1 occurs after, return 1
3630  * If they are equal, return 0
3631  */
3632 static inline int setpos_cmp(int s1, int s2, int next)
3633 {
3634  if (s1 < s2) {
3635  return (next ? -1 : 1);
3636  } else if (s2 < s1) {
3637  return (next ? 1 : -1);
3638  }
3639  return 0;
3640 }
3641 
3642 /* Check whether impl->set_pos is a valid recurrence set position
3643  *
3644  * next == 1 indicates that we should increase and decrease
3645  * sp_idxp and sp_idxn, respectively, while
3646  * next == 0 indicates that we should decrease and increase them
3647  */
3648 static bool check_setpos(icalrecur_iterator *impl, int next)
3649 {
3650  if (!has_by_data(impl, ICAL_BY_SET_POS)) {
3651  return true;
3652  }
3653  icalrecurrence_by_data *by = &(impl->bydata[ICAL_BY_SET_POS].by);
3654  int32_t set_pos;
3655 
3656  /* If we have positive BYSETPOS data */
3657  if (impl->sp_pmax >= 0) {
3658  set_pos = by->data[impl->sp_idxp];
3659  /* Increment positive index while set_pos is before impl->set_pos */
3660  while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3661  if (next && impl->sp_idxp < impl->sp_pmax) {
3662  impl->sp_idxp++;
3663  } else if (!next && impl->sp_idxp > 0) {
3664  impl->sp_idxp--;
3665  } else {
3666  break;
3667  }
3668  set_pos = by->data[impl->sp_idxp];
3669  }
3670  if (impl->set_pos == set_pos) {
3671  return true;
3672  }
3673  }
3674 
3675  if (impl->sp_pmax < by->size - 1) {
3676  set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3677  while (setpos_cmp(set_pos, impl->set_pos, next) < 0) {
3678  if (next && impl->sp_idxn > impl->sp_pmax + 1) {
3679  impl->sp_idxn--;
3680  } else if (!next && impl->sp_idxn < by->size - 1) {
3681  impl->sp_idxn++;
3682  } else {
3683  break;
3684  }
3685  set_pos = by->data[impl->sp_idxn] + impl->recurrence_set_size + 1;
3686  }
3687  if (impl->set_pos == set_pos) {
3688  return true;
3689  }
3690  }
3691  return false;
3692 }
3693 
3694 struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
3695 {
3696  /* Quit if we reached COUNT or if last time is after the UNTIL time */
3697  if (!impl ||
3698  (impl->rule->count != 0 && impl->occurrence_no >= impl->rule->count) ||
3699  (!icaltime_is_null_time(impl->rule->until) &&
3700  icaltime_compare(impl->last, impl->rule->until) > 0)) {
3701  return icaltime_null_time();
3702  }
3703 
3704  /* If initial time is valid, return it */
3705  if ((impl->occurrence_no == 0) &&
3706  (icaltime_compare(impl->last, impl->istart) >= 0) &&
3707  check_setpos(impl, 1) &&
3708  check_contracting_rules(impl)) {
3709  impl->occurrence_no++;
3710  return impl->last;
3711  }
3712 
3713  int period_change = 1;
3714  /* store previous instance, including iterator structures
3715  * (e.g., bydata) */
3716  icalrecur_iterator impl_last = *impl;
3717 
3718  /* Iterate until we get the next valid time */
3719  size_t stalledCnt = 0;
3720  const size_t max_recurrence_time_count = icallimit_get(ICAL_LIMIT_RECURRENCE_TIME_STANDING_STILL);
3721  int lastTimeCompare = 0;
3722  bool hasByData = false;
3723  int checkContractingRules = 0;
3724  size_t cntRecurrences = 0;
3725  const size_t max_recurrences = icallimit_get(ICAL_LIMIT_RECURRENCE_SEARCH);
3726  do {
3727  switch (impl->rule->freq) {
3729  /* period_change is always true for secondly recurrence */
3730  next_second(impl);
3731  break;
3732 
3734  period_change = next_minute(impl);
3735  break;
3736 
3738  period_change = next_hour(impl);
3739  break;
3740 
3741  case ICAL_DAILY_RECURRENCE:
3742  period_change = next_day(impl);
3743  break;
3744 
3746  period_change = next_week(impl);
3747  break;
3748 
3750  period_change = next_month(impl);
3751  break;
3752 
3754  period_change = next_year(impl);
3755  break;
3756 
3757  default:
3759  return icaltime_null_time();
3760  }
3761 
3762  impl->last = occurrence_as_icaltime(impl, 1);
3763 
3764  /* Ignore times that are after the MAX year,
3765  or the UNTIL time, or the end time */
3766  if (impl->last.year > MAX_TIME_T_YEAR ||
3767  (!icaltime_is_null_time(impl->rule->until) &&
3768  icaltime_compare(impl->last, impl->rule->until) > 0) ||
3769  (!icaltime_is_null_time(impl->iend) &&
3770  icaltime_compare(impl->last, impl->iend) >= 0)) {
3771  /* reset to valid instance */
3772  *impl = impl_last;
3773  set_datetime(impl, impl_last.last);
3774  return icaltime_null_time();
3775  }
3776 
3777  hasByData = has_by_data(impl, ICAL_BY_SET_POS);
3778  checkContractingRules = -1;
3779  if (hasByData) {
3780  checkContractingRules = check_contracting_rules(impl) ? 1 : 0;
3781  if (checkContractingRules == 1) {
3782  if (period_change) {
3783  setup_setpos(impl, 1);
3784  } else {
3785  impl->set_pos++;
3786  }
3787  }
3788  }
3789 
3790  // is time standing still? if so, break out of here
3791  lastTimeCompare = icaltime_compare(impl->last, impl_last.last);
3792  if (lastTimeCompare == 0) {
3793  if (stalledCnt++ == max_recurrence_time_count) {
3794  break;
3795  }
3796  } else {
3797  stalledCnt = 0;
3798  }
3799  } while ((cntRecurrences++ < max_recurrences) &&
3800  ((lastTimeCompare == 0) ||
3801  (hasByData && !check_setpos(impl, 1)) ||
3802  icaltime_compare(impl->last, impl->istart) < 0 ||
3803  (checkContractingRules == 0) ||
3804  (checkContractingRules == -1 && !check_contracting_rules(impl))));
3805 
3806  impl->occurrence_no++;
3807 
3808  return impl->last;
3809 }
3810 
3811 struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
3812 {
3813  /* Quit if last time is before the DTSTART time */
3814  if (!impl || icaltime_compare(impl->last, impl->dtstart) < 0) {
3815  return icaltime_null_time();
3816  }
3817 
3818  int period_change = 1;
3819  icalrecur_iterator impl_last = *impl;
3820 
3821  /* Iterate until we get the next valid time */
3822  do {
3823  switch (impl->rule->freq) {
3825  prev_second(impl);
3826  break;
3827 
3829  period_change = prev_minute(impl);
3830  break;
3831 
3833  period_change = prev_hour(impl);
3834  break;
3835 
3836  case ICAL_DAILY_RECURRENCE:
3837  period_change = prev_day(impl);
3838  break;
3839 
3841  period_change = prev_week(impl);
3842  break;
3843 
3845  period_change = prev_month(impl);
3846  break;
3847 
3849  period_change = prev_year(impl);
3850  break;
3851 
3852  default:
3854  return icaltime_null_time();
3855  }
3856 
3857  impl->last = occurrence_as_icaltime(impl, 1);
3858 
3859  /* Ignore times that are before the DTSTART time */
3860  if (icaltime_compare(impl->last, impl->dtstart) < 0 ||
3861  (!icaltime_is_null_time(impl->istart) &&
3862  icaltime_compare(impl->last, impl->istart) < 0)) {
3863  *impl = impl_last;
3864  set_datetime(impl, impl_last.last);
3865  return icaltime_null_time();
3866  }
3867 
3868  if (has_by_data(impl, ICAL_BY_SET_POS) && check_contracting_rules(impl)) {
3869  if (period_change) {
3870  setup_setpos(impl, 0);
3871  } else {
3872  impl->set_pos--;
3873  }
3874  }
3875 
3876  } while (impl->last.year > MAX_TIME_T_YEAR ||
3877  (!icaltime_is_null_time(impl->rule->until) &&
3878  icaltime_compare(impl->last, impl->rule->until) > 0) ||
3879  (!icaltime_is_null_time(impl->iend) &&
3880  icaltime_compare(impl->last, impl->iend) > 0) ||
3881  icaltime_compare(impl->last, impl_last.last) == 0 ||
3882  (has_by_data(impl, ICAL_BY_SET_POS) && !check_setpos(impl, 0)) ||
3883  !check_contracting_rules(impl));
3884 
3885  impl->occurrence_no--;
3886 
3887  return impl->last;
3888 }
3889 
3892 static void set_bydata_start(icalrecurrence_iterator_by_data *bydata, int tfield)
3893 {
3894  int bdi;
3895  for (bdi = 0;
3896  bdi < bydata->by.size; bdi++) {
3897  if (bydata->by.data[bdi] == tfield) {
3898  bydata->index = bdi;
3899  return;
3900  }
3901  }
3902 }
3903 
3904 static bool __iterator_set_start(icalrecur_iterator *impl, icaltimetype start)
3905 {
3906  icalrecurrencetype_frequency freq = impl->rule->freq;
3907  short interval = impl->rule->interval;
3908  int diff;
3909 
3910  impl->istart = start;
3911  impl->occurrence_no = 0;
3912  impl->days_index = ICAL_YEARDAYS_MASK_SIZE;
3913 
3914  /* Set Gregorian start date */
3915  set_start(impl, start);
3916 
3917  switch (freq) {
3919  /* For YEARLY rule, begin by setting up the year days array.
3920  The YEARLY rules work by expanding one year at a time. */
3921 
3922  if ((interval > 1) &&
3923  (diff = (impl->istart.year - impl->rstart.year) % interval)) {
3924  /* Specified start year doesn't match interval -
3925  bump start to first day of next year that matches interval */
3926  set_day_of_year(impl, 1);
3927  increment_year(impl, interval - diff);
3928  }
3929 
3930  /* Get (adjusted) start date as RSCALE date */
3931  start = occurrence_as_icaltime(impl, 0);
3932 
3933  if (has_by_data(impl, ICAL_BY_WEEK_NO)) {
3934  int start_weekno = get_week_number(impl, start);
3935  if (start_weekno > 5 &&
3936  start.month == 1) {
3937  /* if we are in the last week of the previous year,
3938  * expand year days for the previous year
3939  */
3940  increment_year(impl, -1);
3941  expand_year_days(impl, start.year - 1);
3942  int days_in_year = get_days_in_year(impl, start.year - 1);
3943  impl->days_index = daymask_find_next_bit(impl->days, days_in_year + 1);
3944  if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3945  increment_year(impl, 1);
3946  }
3947  } else if (start_weekno < 45 &&
3948  start.month == 12) {
3949  /* if we are in the first week of the next year,
3950  * expand year days for the next year
3951  */
3952  increment_year(impl, 1);
3953  expand_year_days(impl, start.year + 1);
3954  impl->days_index = daymask_find_next_bit(impl->days, -ICAL_YEARDAYS_MASK_OFFSET);
3955  if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3956  increment_year(impl, -1);
3957  }
3958  }
3959  }
3960 
3961  /* Expand days array for (adjusted) start year -
3962  fail after hitting the year MAX_TIME_T_YEAR if no expanded days match */
3963  while (start.year < MAX_TIME_T_YEAR && impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3964  expand_year_days(impl, start.year);
3965 
3966  icalerrorenum err = icalerrno;
3967  switch (err) {
3968  case ICAL_NO_ERROR:
3969  break;
3971  return false;
3972  default:
3974  return false;
3975  }
3976 
3977  if (impl->days_index >= ICAL_YEARDAYS_MASK_SIZE) {
3978  increment_year(impl, interval);
3979  start = occurrence_as_icaltime(impl, 0);
3980  }
3981  }
3982 
3983  /* Copy the first day into last */
3984  set_day_of_year(impl, impl->days_index);
3985  if (impl->days_index < 1) {
3986  increment_year(impl, -1);
3987  }
3988 
3989  break;
3990 
3992  /* For MONTHLY rule, begin by setting up the year days array.
3993  The MONTHLY rules work by expanding one month at a time. */
3994 
3995  if ((interval > 1) &&
3996  (diff = month_diff(impl, impl->rstart, impl->istart) % interval)) {
3997  /* Specified month doesn't match interval -
3998  bump start to first day of next month that matches interval */
3999  increment_monthday(impl, -impl->istart.day + 1);
4000  __increment_month(impl, interval - diff);
4001  }
4002 
4003  /* Get (adjusted) start date as RSCALE date */
4004  start = occurrence_as_icaltime(impl, 0);
4005 
4006  /* Expand days array for (adjusted) start month -
4007  fail after hitting the year 20000 if no expanded days match */
4008  while (start.year < 20000) {
4009  expand_month_days(impl, start.year, start.month);
4010  if (impl->days_index < ICAL_YEARDAYS_MASK_SIZE) {
4011  break; /* break when a matching day is found */
4012  }
4013  increment_month(impl, impl->rule->interval);
4014  start = occurrence_as_icaltime(impl, 0);
4015  }
4016 
4017  /* Copy the first day into last */
4018  set_day_of_year(impl, impl->days_index);
4019 
4020  break;
4021 
4023  if (impl->bydata[ICAL_BY_DAY].by.size <= 0) {
4024  /* Weekly recurrences with no ICAL_BY_DAY data should occur on the
4025  same day of the week as the start time . */
4026  recur_iterator_set_static_single_by_value(impl, ICAL_BY_DAY, (short)get_day_of_week(impl));
4027  } else {
4028  adjust_to_byday(impl);
4029 
4030  /* If start == DTSTART, adjust rstart */
4031  if (icaltime_compare(start, impl->dtstart) == 0) {
4032  impl->rstart = occurrence_as_icaltime(impl, 0);
4033  }
4034 
4035  /* Get (adjusted) start date as RSCALE date */
4036  start = occurrence_as_icaltime(impl, 0);
4037 
4038  if ((interval > 1) &&
4039  (diff = (day_diff(impl, impl->rstart, start) + 6) / 7) % interval) {
4040  /* Specified week doesn't match interval -
4041  bump start to next week that matches interval */
4042  increment_monthday(impl, 7 * (interval - diff));
4043  }
4044  }
4045  break;
4046 
4047  case ICAL_DAILY_RECURRENCE:
4048  if ((interval > 1) &&
4049  (diff = day_diff(impl, impl->rstart, impl->istart) % interval)) {
4050  /* Specified day doesn't match interval -
4051  bump start to next day that matches interval */
4052  increment_monthday(impl, interval - diff);
4053  }
4054  break;
4055 
4057  if ((interval > 1) &&
4058  (diff = abs(impl->istart.hour - impl->rstart.hour) % interval)) {
4059  /* Specified hour doesn't match interval -
4060  bump start to next hour that matches interval */
4061  increment_hour(impl, interval - diff);
4062  }
4063  set_bydata_start(&impl->bydata[ICAL_BY_HOUR], impl->istart.hour);
4064  break;
4065 
4067  if ((interval > 1) &&
4068  (diff = abs(impl->istart.minute - impl->rstart.minute) % interval)) {
4069  /* Specified minute doesn't match interval -
4070  bump start to next minute that matches interval */
4071  increment_minute(impl, interval - diff);
4072  }
4073  set_bydata_start(&impl->bydata[ICAL_BY_MINUTE], impl->istart.minute);
4074  break;
4075 
4077  if ((interval > 1) &&
4078  (diff = abs(impl->istart.second - impl->rstart.second) % interval)) {
4079  /* Specified second doesn't match interval -
4080  bump start to next second that matches interval */
4081  increment_second(impl, interval - diff);
4082  }
4083  set_bydata_start(&impl->bydata[ICAL_BY_SECOND], impl->istart.second);
4084  break;
4085 
4086  default:
4087  break;
4088  }
4089 
4090  /* Get start date as Gregorian date */
4091  impl->last = occurrence_as_icaltime(impl, 1);
4092  if (has_by_data(impl, ICAL_BY_SET_POS)) {
4093  setup_setpos(impl, 1);
4094  }
4095 
4096  /* Fail if first instance exceeds MAX_TIME_T_YEAR */
4097  if (impl->last.year > MAX_TIME_T_YEAR) {
4099  return false;
4100  }
4101 
4102  return true;
4103 }
4104 
4105 bool icalrecur_iterator_set_start(icalrecur_iterator *impl,
4106  struct icaltimetype start)
4107 {
4108  /* We can't adjust start date if we need to count occurrences */
4109  if (impl->rule->count > 0) {
4111  return false;
4112  }
4113 
4114  /* Convert start to same time zone as DTSTART */
4115  start = icaltime_convert_to_zone(start, (icaltimezone *)impl->dtstart.zone);
4116 
4117  if (icaltime_compare(start, impl->dtstart) < 0) {
4118  /* If start is before DTSTART, use DTSTART */
4119  start = impl->dtstart;
4120  } else if (!icaltime_is_null_time(impl->rule->until) &&
4121  icaltime_compare(start, impl->rule->until) > 0) {
4122  /* If start is after UNTIL, we're done */
4123  impl->last = start;
4124  return true;
4125  }
4126 
4127  return __iterator_set_start(impl, start);
4128 }
4129 
4130 bool icalrecur_iterator_set_end(icalrecur_iterator *impl,
4131  struct icaltimetype end)
4132 {
4133  /* Convert end to same time zone as DTSTART */
4134  end = icaltime_convert_to_zone(end, (icaltimezone *)impl->dtstart.zone);
4135 
4136  impl->iend = end;
4137 
4138  return true;
4139 }
4140 
4141 bool icalrecur_iterator_set_range(icalrecur_iterator *impl,
4142  struct icaltimetype from,
4143  struct icaltimetype to)
4144 {
4145  if (impl->rule->count > 0 || icaltime_is_null_time(from)) {
4146  /* Can't set a range without 'from' or if we need to count occurrences */
4148  return false;
4149  }
4150 
4151  if (!icaltime_is_null_time(to) && icaltime_compare(to, from) < 0) {
4152  /* Setting up for the reverse iterator */
4153  const icaltimezone *zone = impl->dtstart.zone;
4154 
4155  /* Convert 'from' to same time zone as DTSTART */
4156  from = icaltime_convert_to_zone(from, (icaltimezone *)zone);
4157 
4158  if (icaltime_compare(from, impl->rule->until) > 0) {
4159  /* If 'from' is after UNTIL, use UNTIL */
4160  from = impl->rule->until;
4161  } else if (icaltime_compare(from, impl->dtstart) < 0) {
4162  /* If 'from' is before START, we're done */
4163  impl->last = from;
4164  return true;
4165  }
4166 
4167  if (!__iterator_set_start(impl, from)) {
4168  return false;
4169  }
4170 
4171  /* __iterator_set_start() may back us up earlier than 'from'
4172  Iterate forward until we are later than 'from'.
4173  */
4174  while (icaltime_compare(impl->last, from) < 0) {
4175  (void)icalrecur_iterator_next(impl);
4176  }
4177 
4178  /* Convert 'to' to same time zone as DTSTART */
4179  to = icaltime_convert_to_zone(to, (icaltimezone *)zone);
4180 
4181  if (icaltime_compare(to, impl->dtstart) < 0) {
4182  /* If 'to' is before DTSTART, use DTSTART */
4183  to = impl->dtstart;
4184  }
4185 
4186  impl->istart = to;
4187  impl->iend = from;
4188  impl->days_index = 0;
4189  } else {
4190  if (!icalrecur_iterator_set_start(impl, from)) {
4191  return false;
4192  }
4193 
4194  icalrecur_iterator_set_end(impl, to);
4195  }
4196 
4197  return true;
4198 }
4199 
4200 /************************** Type Routines **********************/
4201 
4202 static void icalrecurrencetype_clear(struct icalrecurrencetype *recur)
4203 {
4204  int refcount = recur->refcount;
4205 
4206  icalrecurrencetype_free(recur, 0);
4207 
4208  memset(recur, 0, sizeof(*recur));
4209 
4210  recur->refcount = refcount;
4211 
4213  recur->freq = ICAL_NO_RECURRENCE;
4214  recur->interval = 1;
4215  recur->until = icaltime_null_time();
4216  recur->count = 0;
4217  recur->rscale = NULL;
4218  recur->skip = ICAL_SKIP_OMIT;
4219 }
4220 
4222 {
4223  return (enum icalrecurrencetype_weekday)(abs(day) % 8);
4224 }
4225 
4227 {
4228  int wd, pos;
4229 
4230  wd = (int)icalrecurrencetype_day_day_of_week(day);
4231 
4232  pos = (abs(day) - wd) / 8 * ((day < 0) ? -1 : 1);
4233 
4234  return pos;
4235 }
4236 
4238 {
4239  short s_weekday = (short)weekday;
4240  short a_position = (short)(8 * abs(position));
4241  return (s_weekday + a_position) * ((position < 0) ? -1 : 1);
4242 }
4243 
4245 {
4246  return (month & LEAP_MONTH);
4247 }
4248 
4250 {
4251  return (month & ~LEAP_MONTH);
4252 }
4253 
4254 short icalrecurrencetype_encode_month(int month, bool is_leap)
4255 {
4256  return (short)month | (is_leap ? LEAP_MONTH : 0);
4257 }
4258 
4259 bool icalrecur_expand_recurrence(const char *rule,
4260  icaltime_t start, int count, icaltime_t *array)
4261 {
4262  struct icalrecurrencetype *recur;
4263  icalrecur_iterator *ritr;
4264  struct icaltimetype icstart;
4265 
4266  memset(array, 0, (size_t)count * sizeof(icaltime_t));
4267 
4268  icstart = icaltime_from_timet_with_zone(start, 0, 0);
4269 
4270  recur = icalrecurrencetype_new_from_string(rule);
4271  if (!recur) {
4272  return false;
4273  }
4274 
4275  ritr = icalrecur_iterator_new(recur, icstart);
4276  if (ritr) {
4277  int i = 0;
4278  for (struct icaltimetype next = icalrecur_iterator_next(ritr);
4279  !icaltime_is_null_time(next) && i < count;
4280  next = icalrecur_iterator_next(ritr)) {
4281  icaltime_t tt = icaltime_as_timet(next);
4282 
4283  if (tt >= start) {
4284  array[i++] = tt;
4285  }
4286  }
4288  }
4289 
4290  icalrecurrencetype_unref(recur);
4291 
4292  return true;
4293 }
4294 
4296 {
4297  ical_invalid_rrule_handling myHandling;
4298 
4299 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4300  if (pthread_mutex_lock(&invalid_rrule_mutex) != 0) {
4302  }
4303 #endif
4304 
4305  myHandling = invalidRruleHandling;
4306 
4307 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4308  if (pthread_mutex_unlock(&invalid_rrule_mutex) != 0) {
4310  }
4311 #endif
4312 
4313  return myHandling;
4314 }
4315 
4317 {
4318 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4319  if (pthread_mutex_lock(&invalid_rrule_mutex) != 0) {
4321  }
4322 #endif
4323 
4324  invalidRruleHandling = newSetting;
4325 
4326 #if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
4327  if (pthread_mutex_unlock(&invalid_rrule_mutex) != 0) {
4329  }
4330 #endif
4331 }
int is_date
Definition: icaltime.h:98
icalrecurrencetype_frequency
Definition: icalrecur.h:84
Routines for dealing with recurring time.
void * icalmemory_resize_buffer(void *buf, size_t size)
Resizes a buffer created with icalmemory_new_buffer().
Definition: icalmemory.c:336
icalarray * icalarray_new(size_t element_size, size_t increment_size)
Definition: icalarray.c:36
int icaltime_days_in_year(const int year)
Definition: icaltime.c:460
icalrecurrencetype_weekday
Definition: icalrecur.h:99
bool icalrecurrencetype_month_is_leap(short month)
Definition: icalrecur.c:4244
Common memory management routines.
Timezone handling routines.
const char * icalrecur_skip_to_string(icalrecurrencetype_skip kind)
Definition: icalrecur.c:246
struct icaltimetype icaltime_normalize(const struct icaltimetype tt)
Definition: icaltime.c:366
Defines the data structure representing iCalendar parameter values.
void ical_set_invalid_rrule_handling_setting(ical_invalid_rrule_handling newSetting)
Definition: icalrecur.c:4316
bool icalrecur_expand_recurrence(const char *rule, icaltime_t start, int count, icaltime_t *array)
Definition: icalrecur.c:4259
icalrecurrencetype_skip icalrecur_string_to_skip(const char *str)
Definition: icalrecur.c:234
icalrecurrencetype_skip
Definition: icalrecur.h:114
icalrecurrence_by_data by[ICAL_BY_NUM_PARTS]
Definition: icalrecur.h:286
int second
Definition: icaltime.h:96
short icalrecurrencetype_encode_month(int month, bool is_leap)
Definition: icalrecur.c:4254
icalarray * icalrecurrencetype_rscale_supported_calendars(void)
Definition: icalrecur.c:2009
const char * icaltimezone_get_location(const icaltimezone *zone)
void * icalmemory_tmp_buffer(size_t size)
Creates a new temporary buffer on the ring and returns it.
Definition: icalmemory.c:182
bool icalrecur_iterator_set_end(icalrecur_iterator *impl, struct icaltimetype end)
Definition: icalrecur.c:4130
char * icalmemory_strdup(const char *s)
Creates a duplicate of a string.
Definition: icalmemory.c:242
void icalmemory_free_buffer(void *buf)
Releases a buffer.
Definition: icalmemory.c:355
icalrecurrencetype_frequency freq
Definition: icalrecur.h:255
const char * icalrecur_weekday_to_string(icalrecurrencetype_weekday kind)
Definition: icalrecur.c:271
const icaltimezone * zone
Definition: icaltime.h:102
struct icalrecurrencetype * icalrecurrencetype_new_from_string(const char *str)
Definition: icalrecur.c:869
char * icalrecurrencetype_as_string_r(struct icalrecurrencetype *recur)
Definition: icalrecur.c:1061
int icalrecurrencetype_month_month(short month)
Definition: icalrecur.c:4249
size_t icallimit_get(icallimits_kind kind)
Definition: icallimits.c:29
struct icaltimetype icalrecur_iterator_next(icalrecur_iterator *impl)
Definition: icalrecur.c:3694
void icalerror_set_errno(icalerrorenum x)
Sets the icalerrno to a given error.
Definition: icalerror.c:90
void icalerror_clear_errno(void)
Resets icalerrno to ICAL_NO_ERROR.
Definition: icalerror.c:85
icalerrorenum
Represents the different types of errors that can be triggered in libical.
Definition: icalerror.h:41
int icaltime_day_of_week(const struct icaltimetype t)
Definition: icaltime.c:497
int icaltime_compare(const struct icaltimetype a_in, const struct icaltimetype b_in)
Definition: icaltime.c:639
void icaltime_adjust(struct icaltimetype *tt, const int days, const int hours, const int minutes, const int seconds)
Definition: icaltime.c:755
struct icaltimetype icalrecur_iterator_prev(icalrecur_iterator *impl)
Definition: icalrecur.c:3811
Error handling for libical.
icalrecurrencetype_weekday week_start
Definition: icalrecur.h:267
icalrecurrencetype_byrule
Definition: icalrecur.h:125
icalrecurrencetype_skip skip
Definition: icalrecur.h:292
bool icalrecur_iterator_set_range(icalrecur_iterator *impl, struct icaltimetype from, struct icaltimetype to)
Definition: icalrecur.c:4141
void icalmemory_append_char(char **buf, char **pos, size_t *buf_size, char ch)
Appends a character to a buffer.
Definition: icalmemory.c:406
int minute
Definition: icaltime.h:95
bool icalrecur_resize_by(icalrecurrence_by_data *by, short size)
Definition: icalrecur.c:306
struct icaltimetype icaltime_from_day_of_year(const int _doy, const int _year)
Definition: icaltime.c:544
struct icaltimetype icaltime_null_time(void)
Definition: icaltime.c:579
ical_invalid_rrule_handling ical_get_invalid_rrule_handling_setting(void)
Definition: icalrecur.c:4295
struct icaltimetype icaltime_convert_to_zone(const struct icaltimetype tt, icaltimezone *zone)
Definition: icaltime.c:846
struct _icaltimezone icaltimezone
Definition: icaltimezone.h:29
bool icaltime_is_valid_time(const struct icaltimetype t)
Definition: icaltime.c:607
int icalrecurrencetype_day_position(short day)
Definition: icalrecur.c:4226
void icalarray_append(icalarray *array, const void *element)
Appends an element to an array.
Definition: icalarray.c:119
void * icalmemory_new_buffer(size_t size)
Creates new buffer with the specified size.
Definition: icalmemory.c:315
void icalmemory_append_string(char **buf, char **pos, size_t *buf_size, const char *string)
Appends a string to a buffer.
Definition: icalmemory.c:365
int icaltime_day_of_year(const struct icaltimetype t)
Definition: icaltime.c:533
struct icaltimetype icaltime_from_string(const char *str)
Definition: icaltime.c:374
enum icalrecurrencetype_weekday icalrecurrencetype_day_day_of_week(short day)
Definition: icalrecur.c:4221
const char * icalrecur_freq_to_string(icalrecurrencetype_frequency kind)
Definition: icalrecur.c:213
Defines the interface for getting/setting internal library limits.
void icalrecur_iterator_free(icalrecur_iterator *impl)
Definition: icalrecur.c:2451
struct icaltimetype icaltime_from_timet_with_zone(const icaltime_t tm, const bool is_date, const icaltimezone *zone)
Constructor.
Definition: icaltime.c:209
struct icaltimetype until
Definition: icalrecur.h:258
void icalrecurrencetype_unref(struct icalrecurrencetype *recur)
Definition: icalrecur.c:790
ical_invalid_rrule_handling
Definition: icalrecur.h:616
struct icalrecurrencetype * icalrecurrencetype_new(void)
Definition: icalrecur.c:743
icalrecurrencetype_weekday icalrecur_string_to_weekday(const char *str)
Definition: icalrecur.c:284
icalrecurrencetype_frequency icalrecur_string_to_freq(const char *str)
Definition: icalrecur.c:201
char * icalrecurrencetype_as_string(struct icalrecurrencetype *recur)
Definition: icalrecur.c:1052
#define icalerrno
Access the current icalerrno value.
Definition: icalerror.h:133
icaltime_t icaltime_as_timet(const struct icaltimetype tt)
Definition: icaltime.c:259
int icaltime_days_in_month(const int month, const int year)
Definition: icaltime.c:471
bool icaltime_is_null_time(const struct icaltimetype t)
Definition: icaltime.c:630
char * icalmemory_tmp_copy(const char *str)
Creates a copy of the given string, stored on the ring buffer, and returns it.
Definition: icalmemory.c:222
bool icalrecur_iterator_set_start(icalrecur_iterator *impl, struct icaltimetype start)
Definition: icalrecur.c:4105
icalrecur_iterator * icalrecur_iterator_new(struct icalrecurrencetype *rule, struct icaltimetype dtstart)
Definition: icalrecur.c:2323
struct icalrecurrencetype * icalrecurrencetype_clone(struct icalrecurrencetype *recur)
Definition: icalrecur.c:832
void icalmemory_add_tmp_buffer(void *buf)
Adds an externally allocated buffer to the ring.
Definition: icalmemory.c:158
short icalrecurrencetype_encode_day(enum icalrecurrencetype_weekday weekday, int position)
Definition: icalrecur.c:4237
const char * icaltimezone_get_tzid(icaltimezone *zone)
int icaltime_start_doy_week(const struct icaltimetype t, int fdow)
Definition: icaltime.c:512
const char * icaltimezone_tzid_prefix(void)
Definition: icaltimezone.c:239
void icalrecurrencetype_ref(struct icalrecurrencetype *recur)
Definition: icalrecur.c:782
bool icaltime_is_leap_year(const int year)
Definition: icaltime.c:451