1 //******************************************************************
3 // Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
7 // Licensed under the Apache License, Version 2.0 (the "License");
8 // you may not use this file except in compliance with the License.
9 // You may obtain a copy of the License at
11 // http://www.apache.org/licenses/LICENSE-2.0
13 // Unless required by applicable law or agreed to in writing, software
14 // distributed under the License is distributed on an "AS IS" BASIS,
15 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 // See the License for the specific language governing permissions and
17 // limitations under the License.
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
21 //Not supported on Arduino due lack of absolute time need to implement iCalendar
24 #define _XOPEN_SOURCE //Needed by strptime
26 #include "iotvticalendar.h"
27 #include "oic_string.h"
29 static char dtFormat[] = "%Y%m%dT%H%M%S"; //date-time format
30 static char dFormat[] = "%Y%m%d"; // date format
32 static const char FREQ[] = "FREQ";
33 static const char UNTIL[] = "UNTIL";
34 static const char BYDAY[] = "BYDAY";
35 static const char DAILY[] = "DAILY";
37 IotvtICalResult_t ParsePeriod(const char *periodStr, IotvtICalPeriod_t *period)
39 if ((NULL == periodStr) || (NULL == period))
41 return IOTVTICAL_INVALID_PARAMETER;
49 //Finding length of startDateTime and endDateTime in period
50 //startDateTime and endDateTime can have form YYYYmmdd or YYYYmmddTHHMMSS
51 //startDateTime and endDateTime must be same form
52 //Eg: periodStr = "20150629T153050/20150630T203055"
53 // periodStr = "20150629/20150630"
54 if (NULL == (endDTPos = strchr(periodStr, '/')))
56 return IOTVTICAL_INVALID_PERIOD;
59 startDTLen = endDTPos - periodStr - 1;
60 endDTLen = strlen(endDTPos);
62 //Checking if both startDateTime and endDateTime are of same form
63 if (startDTLen == endDTLen)
65 if (8 == startDTLen) //YYYYmmdd
69 else if (15 == startDTLen) //YYYYmmddTHHMMSS
75 return IOTVTICAL_INVALID_PERIOD;
80 return IOTVTICAL_INVALID_PERIOD;
83 //Checking if startDateTime has right format
84 if (NULL != strptime(periodStr, fmt, &period->startDateTime))
86 //Checking if endDateTime has right format
87 if (NULL != strptime(endDTPos, fmt, &period->endDateTime))
89 //Checking if endDateTime is after startDateTime
90 if ((period->startDateTime.tm_year > period->endDateTime.tm_year)
91 || ((period->startDateTime.tm_year == period->endDateTime.tm_year)
92 && (period->startDateTime.tm_mon > period->endDateTime.tm_mon))
93 || ((period->startDateTime.tm_year == period->endDateTime.tm_year)
94 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
95 && (period->startDateTime.tm_mday > period->endDateTime.tm_mday))
96 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
97 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
98 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
99 && (period->startDateTime.tm_hour > period->endDateTime.tm_hour))
100 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
101 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
102 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
103 && (period->startDateTime.tm_hour == period->endDateTime.tm_hour)
104 && (period->startDateTime.tm_min > period->endDateTime.tm_min))
105 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
106 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
107 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
108 && (period->startDateTime.tm_hour == period->endDateTime.tm_hour)
109 && (period->startDateTime.tm_min == period->endDateTime.tm_min)
110 && (period->startDateTime.tm_sec > period->endDateTime.tm_sec)))
112 return IOTVTICAL_INVALID_PERIOD;
116 return IOTVTICAL_SUCCESS;
120 return IOTVTICAL_INVALID_PERIOD;
124 * Parses untilRule and populate "until" field of struct IotvtICalRecur_t.
126 * @param untilRule is a string to be parsed.
127 * @param recur is the reference to the @ref IotvtICalRecur_t to be populated.
129 * @return ::IOTVTICAL_SUCCESS is succesful, else in case of error
130 * ::IOTVTICAL_ERROR, if untilRule has invalid format or ::IOTVTICAL_INVALID_SUCCESS,
131 * if no error while parsing.
133 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
135 char *date = strchr(untilRule, '=');
139 return IOTVTICAL_ERROR;
143 if (strlen(date) == 8) //YYYYmmdd
145 if (NULL != strptime(date, dFormat, &recur->until))
147 return IOTVTICAL_SUCCESS;
150 return IOTVTICAL_ERROR;
154 * Parses bydayRule and populate "byDay" field of struct @ref IotvtICalRecur_t.
156 * @param bydayRule is a string to be parsed.
157 * @param recur is a reference to @ref IotvtICalRecur_t struct to be populated.
159 * @return ::IOTVTICAL_SUCCESS is succesful, else in case of error ::IOTVTICAL_ERROR,
160 * if bydayRule has empty weekday list or invalid weekdays.
162 static IotvtICalResult_t ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
164 if (strstr(bydayRule, "SU"))
166 recur->byDay = recur->byDay | SUNDAY;
168 if (strstr(bydayRule, "MO"))
170 recur->byDay = recur->byDay | MONDAY;
172 if (strstr(bydayRule, "TU"))
174 recur->byDay = recur->byDay | TUESDAY;
176 if (strstr(bydayRule, "WE"))
178 recur->byDay = recur->byDay | WEDNESDAY;
180 if (strstr(bydayRule, "TH"))
182 recur->byDay = recur->byDay | THURSDAY;
184 if (strstr(bydayRule, "FR"))
186 recur->byDay = recur->byDay | FRIDAY;
188 if (strstr(bydayRule, "SA"))
190 recur->byDay = recur->byDay | SATURDAY;
193 //Checking if byDay list is empty or has inValid weekdays
194 if (recur->byDay == NO_WEEKDAY)
196 return IOTVTICAL_ERROR;
199 return IOTVTICAL_SUCCESS;
202 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
204 if ((NULL == recurStr) || (NULL == recur))
206 return IOTVTICAL_INVALID_PARAMETER;
209 const char *startPos="";
210 const char *endPos="";
212 int freqFlag = 0; //valid RRULE must have "FREQ" parameter.
213 //flag to track if RRULE has "FREQ" or not
216 //Iterates though recurrence rule
217 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
218 while ('\0' != startPos)
220 endPos = strchr(startPos, ';');
225 OICStrcpy(buf, (endPos - startPos), startPos);
226 if (NULL != strstr(buf, FREQ))
228 if (NULL != strstr(buf, DAILY))
230 recur->freq = FREQ_DAILY;
235 return IOTVTICAL_INVALID_RRULE;
238 else if (NULL != strstr(buf, UNTIL))
240 if (IOTVTICAL_SUCCESS != ParseDate(buf, recur))
242 return IOTVTICAL_INVALID_RRULE;
245 else if (NULL != strstr(buf, BYDAY))
247 if (IOTVTICAL_SUCCESS != ParseByday(buf, recur))
249 return IOTVTICAL_INVALID_RRULE;
257 return IOTVTICAL_INVALID_RRULE;
260 return IOTVTICAL_SUCCESS;
264 * Computes number of days between two dates.
266 * @param date1 earlier date.
267 * @param date2 later date.
269 * @return number of days between date1 & date2.
271 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
276 if (date2->tm_year > date1->tm_year)
278 for (int y = date1->tm_year; y < date2->tm_year; y++)
281 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
288 days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
289 (365 * date1->tm_year + date1->tm_yday);
295 * Computes number of seconds between two time.
297 * @param time1 earlier time.
298 * @param date2 later time.
300 * @return number of seconds between time1 and time2.
302 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
304 return (3600 * time2->tm_hour + 60 * time2->tm_min + time2->tm_sec) -
305 (3600 * time1->tm_hour + 60 * time1->tm_min + time1->tm_sec);
309 * Validates if the @param currentTime is with in allowable period.
311 * @param period allowable period.
312 * @param currentTime the time that need to be validated against allowable time.
314 * @return ::IOTVTICAL_VALID_ACCESS, if the request is within valid time period.
315 * ::IOTVTICAL_INVALID_ACCESS, if the request is not within valid time period.
316 * ::IOTVTICAL_INVALID_PARAMETER, if parameter are invalid.
318 static IotvtICalResult_t ValidatePeriod(IotvtICalPeriod_t *period, IotvtICalDateTime_t *currentTime)
320 if (NULL == period || NULL == currentTime)
322 return IOTVTICAL_INVALID_PARAMETER;
325 bool validStartTime = true;
326 bool validEndTime = true;
327 bool validDay = false;
328 bool todayIsStartDay = (0 == DiffDays(&period->startDateTime, currentTime)) ? true : false;
329 bool todayIsEndDay = (0 == DiffDays(currentTime, &period->endDateTime)) ? true : false;
331 //If today is the start day of the allowable period then check
332 //currentTime > allowable period startTime
335 validStartTime = (0 <= DiffSecs(&period->startDateTime, currentTime)) ? true : false;
338 //If today is the end day of allowable period then check
339 //currentTime < allowable period endTime
342 validEndTime = (0 <= DiffSecs(currentTime, &period->endDateTime)) ? true :false;
345 //Check if today is valid day between startDate and EndDate inclusive
346 if ((0 <= DiffDays(&period->startDateTime, currentTime)) &&
347 (0 <= DiffDays(currentTime, &period->endDateTime)))
352 if (validDay && validStartTime && validEndTime)
354 return IOTVTICAL_VALID_ACCESS;
358 return IOTVTICAL_INVALID_ACCESS;
362 IotvtICalResult_t IsRequestWithinValidTime(const char *periodStr, const char *recurStr)
364 //NULL recur rule means no recurring patter exist.
365 //Period can't be null. Period is used with or without
366 //recur rule to compute allowable access time.
367 if (NULL == periodStr)
369 return IOTVTICAL_INVALID_PARAMETER;
372 IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
373 IotvtICalRecur_t recur = {.freq=0};
374 IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
376 time_t rawTime = time(0);
377 IotvtICalDateTime_t *currentTime = localtime(&rawTime);
379 ret = ParsePeriod(periodStr, &period);
380 if (ret != IOTVTICAL_SUCCESS)
385 //If recur is NULL then the access time is between period's startDateTime and endDateTime
386 if (NULL == recurStr)
388 ret = ValidatePeriod(&period, currentTime);
391 //If recur is not NULL then the access time is between period's startTime and
392 //endTime on days specified in "BYDAY" list. The first instance of recurrence
393 //is computed from period's startDate and the last instance is computed from
394 //"UNTIL". If "UNTIL" is not specified then the recurrence goes for forever.
395 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
396 if (NULL != recurStr)
398 ret = ParseRecur(recurStr, &recur);
399 if (ret != IOTVTICAL_SUCCESS)
404 if ((0 <= DiffSecs(&period.startDateTime, currentTime))&&
405 (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
406 (0 <= DiffDays(&period.startDateTime, currentTime)))
408 IotvtICalDateTime_t emptyDT = {.tm_sec=0};
409 ret = IOTVTICAL_VALID_ACCESS;
411 //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
412 if (0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
414 if(0 > DiffDays(currentTime, &recur.until))
416 ret = IOTVTICAL_INVALID_ACCESS;
420 //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
421 if (NO_WEEKDAY != recur.byDay)
424 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
427 ret = IOTVTICAL_INVALID_ACCESS;
433 ret = IOTVTICAL_INVALID_ACCESS;