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";
39 * Parses periodStr and populate struct IotvtICalPeriod_t
41 * @param periodStr string to be parsed.
42 * @param period IotvtICalPeriod_t struct to be populated.
44 * @return IOTVTICAL_INVALID_PARAMETER -- if parameter are invalid
45 * IOTVTICAL_INVALID_PERIOD -- if period string has invalid format
46 * IOTVTICAL_INVALID_SUCCESS -- if no error while parsing
48 IotvtICalResult_t ParsePeriod(const char *periodStr, IotvtICalPeriod_t *period)
50 if((NULL == periodStr) || (NULL == period))
52 return IOTVTICAL_INVALID_PARAMETER;
60 //Finding length of startDateTime and endDateTime in period
61 //startDateTime and endDateTime can have form YYYYmmdd or YYYYmmddTHHMMSS
62 //startDateTime and endDateTime must be same form
63 //Eg: periodStr = "20150629T153050/20150630T203055"
64 // periodStr = "20150629/20150630"
65 if(NULL == (endDTPos = strchr(periodStr, '/')))
67 return IOTVTICAL_INVALID_PERIOD;
70 startDTLen = endDTPos - periodStr - 1;
71 endDTLen = strlen(endDTPos);
73 //Checking if both startDateTime and endDateTime are of same form
74 if(startDTLen == endDTLen)
76 if(8 == startDTLen) //YYYYmmdd
80 else if(15 == startDTLen) //YYYYmmddTHHMMSS
86 return IOTVTICAL_INVALID_PERIOD;
91 return IOTVTICAL_INVALID_PERIOD;
94 //Checking if startDateTime has right format
95 if(NULL != strptime(periodStr, fmt, &period->startDateTime))
97 //Checking if endDateTime has right format
98 if(NULL != strptime(endDTPos, fmt, &period->endDateTime))
100 //Checking if endDateTime is after startDateTime
101 if ((period->startDateTime.tm_year > period->endDateTime.tm_year)
102 || ((period->startDateTime.tm_year == period->endDateTime.tm_year)
103 && (period->startDateTime.tm_mon > period->endDateTime.tm_mon))
104 || ((period->startDateTime.tm_year == period->endDateTime.tm_year)
105 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
106 && (period->startDateTime.tm_mday > period->endDateTime.tm_mday))
107 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
108 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
109 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
110 && (period->startDateTime.tm_hour > period->endDateTime.tm_hour))
111 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
112 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
113 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
114 && (period->startDateTime.tm_hour == period->endDateTime.tm_hour)
115 && (period->startDateTime.tm_min > period->endDateTime.tm_min))
116 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
117 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
118 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
119 && (period->startDateTime.tm_hour == period->endDateTime.tm_hour)
120 && (period->startDateTime.tm_min == period->endDateTime.tm_min)
121 && (period->startDateTime.tm_sec > period->endDateTime.tm_sec)))
123 return IOTVTICAL_INVALID_PERIOD;
127 return IOTVTICAL_SUCCESS;
131 return IOTVTICAL_INVALID_PERIOD;
136 * Parses untilRule and populate "until" field of struct IotvtICalRecur_t
138 * @param untilRule string to be parsed.
139 * @param recur IotvtICalRecur_t struct to be populated.
141 * @return IOTVTICAL_ERRRO -- if untilRule has invalid format
142 * IOTVTICAL_INVALID_SUCCESS -- if no error while parsing
144 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
146 char *date = strchr(untilRule, '=');
150 return IOTVTICAL_ERROR;
154 if(strlen(date) == 8) //YYYYmmdd
156 if(NULL != strptime(date, dFormat, &recur->until))
158 return IOTVTICAL_SUCCESS;
161 return IOTVTICAL_ERROR;
166 * Parses bydayRule and populate "byDay" field of struct IotvtICalRecur_t
168 * @param bydayRule string to be parsed.
169 * @param recur IotvtICalRecur_t struct to be populated.
171 * @return IOTVTICAL_ERRRO -- if bydayRule has empty weekday list or invalid weekdays
172 * IOTVTICAL_INVALID_SUCCESS -- if no error while parsing
174 static IotvtICalResult_t ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
176 if(strstr(bydayRule, "SU"))
178 recur->byDay = recur->byDay | SUNDAY;
180 if(strstr(bydayRule, "MO"))
182 recur->byDay = recur->byDay | MONDAY;
184 if(strstr(bydayRule, "TU"))
186 recur->byDay = recur->byDay | TUESDAY;
188 if(strstr(bydayRule, "WE"))
190 recur->byDay = recur->byDay | WEDNESDAY;
192 if(strstr(bydayRule, "TH"))
194 recur->byDay = recur->byDay | THURSDAY;
196 if(strstr(bydayRule, "FR"))
198 recur->byDay = recur->byDay | FRIDAY;
200 if(strstr(bydayRule, "SA"))
202 recur->byDay = recur->byDay | SATURDAY;
205 //Checking if byDay list is empty or has inValid weekdays
206 if(recur->byDay == NO_WEEKDAY)
208 return IOTVTICAL_ERROR;
211 return IOTVTICAL_SUCCESS;
216 * Parses recurStr and populate struct IotvtICalRecur_t
218 * @param recurStr string to be parsed.
219 * @param recur IotvtICalPeriod_t struct to be populated.
221 * @return IOTVTICAL_INVALID_PARAMETER -- if parameter are invalid
222 * IOTVTICAL_INVALID_PERIOD -- if period string has invalid format
223 * IOTVTICAL_INVALID_RRULE -- if rrule string has invalid format
225 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
228 if((NULL == recurStr) || (NULL == recur))
230 return IOTVTICAL_INVALID_PARAMETER;
233 const char *startPos="";
234 const char *endPos="";
236 int freqFlag = 0; //valid RRULE must have "FREQ" parameter.
237 //flag to track if RRULE has "FREQ" or not
240 //Iterates though recurrence rule
241 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
242 while('\0' != startPos)
244 endPos = strchr(startPos, ';');
249 OICStrcpy(buf, (endPos - startPos), startPos);
250 if(NULL != strstr(buf, FREQ))
252 if(NULL != strstr(buf, DAILY))
254 recur->freq = FREQ_DAILY;
259 return IOTVTICAL_INVALID_RRULE;
262 else if(NULL != strstr(buf, UNTIL))
264 if(IOTVTICAL_SUCCESS != ParseDate(buf, recur))
266 return IOTVTICAL_INVALID_RRULE;
269 else if(NULL != strstr(buf, BYDAY))
271 if(IOTVTICAL_SUCCESS != ParseByday(buf, recur))
273 return IOTVTICAL_INVALID_RRULE;
281 return IOTVTICAL_INVALID_RRULE;
284 return IOTVTICAL_SUCCESS;
288 * Computes number of days between two dates.
290 * @param date1 earlier date.
291 * @param date2 later date.
293 * @return number of days between date1 & date2.
295 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
300 if(date2->tm_year > date1->tm_year)
302 for(int y = date1->tm_year; y < date2->tm_year; y++)
305 if(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
312 days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
313 (365 * date1->tm_year + date1->tm_yday);
320 * Computes number of seconds between two time.
322 * @param time1 earlier time.
323 * @param date2 later time.
325 * @return number of seconds between time1 and time2.
327 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
329 return (3600 * time2->tm_hour + 60 * time2->tm_min + time2->tm_sec) -
330 (3600 * time1->tm_hour + 60 * time1->tm_min + time1->tm_sec);
334 * Validates if the @param currentTime is with in allowable period
336 * @param period -- allowable period
337 * @param currentTime -- the time that need to be validated against allowable time
339 * @return IOTVTICAL_VALID_ACCESS -- if the request is within valid time period
340 * IOTVTICAL_INVALID_ACCESS -- if the request is not within valid time period
341 * IOTVTICAL_INVALID_PARAMETER -- if parameter are invalid
343 static IotvtICalResult_t ValidatePeriod(IotvtICalPeriod_t *period, IotvtICalDateTime_t *currentTime)
345 if(NULL == period || NULL == currentTime)
347 return IOTVTICAL_INVALID_PARAMETER;
350 bool validStartTime = true;
351 bool validEndTime = true;
352 bool validDay = false;
353 bool todayIsStartDay = (0 == DiffDays(&period->startDateTime, currentTime)) ? true : false;
354 bool todayIsEndDay = (0 == DiffDays(currentTime, &period->endDateTime)) ? true : false;
356 //If today is the start day of the allowable period then check
357 //currentTime > allowable period startTime
360 validStartTime = (0 <= DiffSecs(&period->startDateTime, currentTime)) ? true : false;
363 //If today is the end day of allowable period then check
364 //currentTime < allowable period endTime
367 validEndTime = (0 <= DiffSecs(currentTime, &period->endDateTime)) ? true :false;
370 //Check if today is valid day between startDate and EndDate inclusive
371 if((0 <= DiffDays(&period->startDateTime, currentTime)) &&
372 (0 <= DiffDays(currentTime, &period->endDateTime)))
377 if(validDay && validStartTime && validEndTime)
379 return IOTVTICAL_VALID_ACCESS;
383 return IOTVTICAL_INVALID_ACCESS;
388 * This API is used by policy engine to checks if the
389 * request to access resource is within valid time.
391 * @param period string representing period.
392 * @param recur string representing recurrence rule
394 * @return IOTVTICAL_VALID_ACCESS -- if the request is within valid time period
395 * IOTVTICAL_INVALID_ACCESS -- if the request is not within valid time period
396 * IOTVTICAL_INVALID_PARAMETER -- if parameter are invalid
397 * IOTVTICAL_INVALID_PERIOD -- if period string has invalid format
398 * IOTVTICAL_INVALID_RRULE -- if rrule string has invalid format
401 IotvtICalResult_t IsRequestWithinValidTime(char *periodStr, char *recurStr)
403 //NULL recur rule means no recurring patter exist.
404 //Period can't be null. Period is used with or without
405 //recur rule to compute allowable access time.
406 if(NULL == periodStr)
408 return IOTVTICAL_INVALID_PARAMETER;
411 IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
412 IotvtICalRecur_t recur = {.freq=0};
413 IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
415 time_t rawTime = time(0);
416 IotvtICalDateTime_t *currentTime = localtime(&rawTime);
418 ret = ParsePeriod(periodStr, &period);
419 if(ret != IOTVTICAL_SUCCESS)
424 //If recur is NULL then the access time is between period's startDateTime and endDateTime
427 ret = ValidatePeriod(&period, currentTime);
430 //If recur is not NULL then the access time is between period's startTime and
431 //endTime on days specified in "BYDAY" list. The first instance of recurrence
432 //is computed from period's startDate and the last instance is computed from
433 //"UNTIL". If "UNTIL" is not specified then the recurrence goes for forever.
434 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
437 ret = ParseRecur(recurStr, &recur);
438 if(ret != IOTVTICAL_SUCCESS)
443 if((0 <= DiffSecs(&period.startDateTime, currentTime))&&
444 (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
445 (0 <= DiffDays(&period.startDateTime, currentTime)))
447 IotvtICalDateTime_t emptyDT = {.tm_sec=0};
448 ret = IOTVTICAL_VALID_ACCESS;
450 //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
451 if(0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
453 if(0 > DiffDays(currentTime, &recur.until))
455 ret = IOTVTICAL_INVALID_ACCESS;
459 //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
460 if(NO_WEEKDAY != recur.byDay)
463 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
466 ret = IOTVTICAL_INVALID_ACCESS;
472 ret = IOTVTICAL_INVALID_ACCESS;