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(difftime(mktime(&period->endDateTime),
102 mktime(&period->startDateTime)) > 0)
104 //mktime increases value of tm_hour by 1 if tm_isdst is set.
105 //The tm_hour value in period's startDateTime and endDatetime
106 //should remain same irrespective of daylight saving time.
107 if(period->startDateTime.tm_isdst)
109 period->startDateTime.tm_hour =
110 (period->startDateTime.tm_hour + TOTAL_HOURS - TM_DST_OFFSET) % TOTAL_HOURS;
112 if(period->endDateTime.tm_isdst)
114 period->endDateTime.tm_hour =
115 (period->endDateTime.tm_hour + TOTAL_HOURS - TM_DST_OFFSET) % TOTAL_HOURS;
117 return IOTVTICAL_SUCCESS;
121 return IOTVTICAL_INVALID_PERIOD;
126 * Parses untilRule and populate "until" field of struct IotvtICalRecur_t
128 * @param untilRule string to be parsed.
129 * @param recur IotvtICalRecur_t struct to be populated.
131 * @return IOTVTICAL_ERRRO -- if untilRule has invalid format
132 * IOTVTICAL_INVALID_SUCCESS -- if no error while parsing
134 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
136 char *date = strchr(untilRule, '=');
140 return IOTVTICAL_ERROR;
144 if(strlen(date) == 8) //YYYYmmdd
146 if(NULL != strptime(date, dFormat, &recur->until))
148 return IOTVTICAL_SUCCESS;
151 return IOTVTICAL_ERROR;
156 * Parses bydayRule and populate "byDay" field of struct IotvtICalRecur_t
158 * @param bydayRule string to be parsed.
159 * @param recur IotvtICalRecur_t struct to be populated.
161 * @return IOTVTICAL_ERRRO -- if bydayRule has empty weekday list or invalid weekdays
162 * IOTVTICAL_INVALID_SUCCESS -- if no error while parsing
164 static IotvtICalResult_t ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
166 if(strstr(bydayRule, "SU"))
168 recur->byDay = recur->byDay | SUNDAY;
170 if(strstr(bydayRule, "MO"))
172 recur->byDay = recur->byDay | MONDAY;
174 if(strstr(bydayRule, "TU"))
176 recur->byDay = recur->byDay | TUESDAY;
178 if(strstr(bydayRule, "WE"))
180 recur->byDay = recur->byDay | WEDNESDAY;
182 if(strstr(bydayRule, "TH"))
184 recur->byDay = recur->byDay | THURSDAY;
186 if(strstr(bydayRule, "FR"))
188 recur->byDay = recur->byDay | FRIDAY;
190 if(strstr(bydayRule, "SA"))
192 recur->byDay = recur->byDay | SATURDAY;
195 //Checking if byDay list is empty or has inValid weekdays
196 if(recur->byDay == NO_WEEKDAY)
198 return IOTVTICAL_ERROR;
201 return IOTVTICAL_SUCCESS;
206 * Parses recurStr and populate struct IotvtICalRecur_t
208 * @param recurStr string to be parsed.
209 * @param recur IotvtICalPeriod_t struct to be populated.
211 * @return IOTVTICAL_INVALID_PARAMETER -- if parameter are invalid
212 * IOTVTICAL_INVALID_PERIOD -- if period string has invalid format
213 * IOTVTICAL_INVALID_RRULE -- if rrule string has invalid format
215 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
218 if((NULL == recurStr) || (NULL == recur))
220 return IOTVTICAL_INVALID_PARAMETER;
223 const char *startPos="";
224 const char *endPos="";
226 int freqFlag = 0; //valid RRULE must have "FREQ" parameter.
227 //flag to track if RRULE has "FREQ" or not
230 //Iterates though recurrence rule
231 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
232 while('\0' != startPos)
234 endPos = strchr(startPos, ';');
239 OICStrcpy(buf, (endPos - startPos), startPos);
240 if(NULL != strstr(buf, FREQ))
242 if(NULL != strstr(buf, DAILY))
244 recur->freq = FREQ_DAILY;
249 return IOTVTICAL_INVALID_RRULE;
252 else if(NULL != strstr(buf, UNTIL))
254 if(IOTVTICAL_SUCCESS != ParseDate(buf, recur))
256 return IOTVTICAL_INVALID_RRULE;
259 else if(NULL != strstr(buf, BYDAY))
261 if(IOTVTICAL_SUCCESS != ParseByday(buf, recur))
263 return IOTVTICAL_INVALID_RRULE;
271 return IOTVTICAL_INVALID_RRULE;
274 return IOTVTICAL_SUCCESS;
278 * Computes number of days between two dates.
280 * @param date1 earlier date.
281 * @param date2 later date.
283 * @return number of days between date1 & date2.
285 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
290 if(date2->tm_year > date1->tm_year)
292 for(int y = date1->tm_year; y < date2->tm_year; y++)
295 if(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
302 days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
303 (365 * date1->tm_year + date1->tm_yday);
310 * Computes number of seconds between two time.
312 * @param time1 earlier time.
313 * @param date2 later time.
315 * @return number of seconds between time1 and time2.
317 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
319 return (3600 * time2->tm_hour + 60 * time2->tm_min + time2->tm_sec) -
320 (3600 * time1->tm_hour + 60 * time1->tm_min + time1->tm_sec);
325 * This API is used by policy engine to checks if the
326 * request to access resource is within valid time.
328 * @param period string representing period.
329 * @param recur string representing recurrence rule
331 * @return IOTVTICAL_VALID_ACCESS -- if the request is within valid time period
332 * IOTVTICAL_INVALID_ACCESS -- if the request is not within valid time period
333 * IOTVTICAL_INVALID_PARAMETER -- if parameter are invalid
334 * IOTVTICAL_INVALID_PERIOD -- if period string has invalid format
335 * IOTVTICAL_INVALID_RRULE -- if rrule string has invalid format
338 IotvtICalResult_t IsRequestWithinValidTime(char *periodStr, char *recurStr)
340 //NULL recur rule means no recurring patter exist.
341 //Period can't be null. Period is used with or without
342 //recur rule to compute allowable access time.
343 if(NULL == periodStr)
345 return IOTVTICAL_INVALID_PARAMETER;
348 IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
349 IotvtICalRecur_t recur = {.freq=0};
350 IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
352 time_t rawTime = time(0);
353 IotvtICalDateTime_t *currentTime = localtime(&rawTime);
355 ret = ParsePeriod(periodStr, &period);
356 if(ret != IOTVTICAL_SUCCESS)
361 //If recur is NULL then the access time is between period's startDate and endDate
364 if((0 <= DiffDays(&period.startDateTime, currentTime)) &&
365 (0 <= DiffDays(currentTime, &period.endDateTime)))
367 ret = IOTVTICAL_VALID_ACCESS;
371 //If recur is not NULL then the access time is between period's startTime and
372 //endTime on days specified in "BYDAY" list. The first instance of recurrence
373 //is computed from period's startDate and the last instance is computed from
374 //"UNTIL". If "UNTIL" is not specified then the recurrence goes for forever.
375 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
378 ret = ParseRecur(recurStr, &recur);
379 if(ret != IOTVTICAL_SUCCESS)
384 if((0 <= DiffSecs(&period.startDateTime, currentTime))&&
385 (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
386 (0 <= DiffDays(&period.startDateTime, currentTime)))
388 IotvtICalDateTime_t emptyDT = {.tm_sec=0};
389 ret = IOTVTICAL_VALID_ACCESS;
391 //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
392 if(0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
394 if(0 > DiffDays(currentTime, &recur.until))
396 ret = IOTVTICAL_INVALID_ACCESS;
400 //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
401 if(NO_WEEKDAY != recur.byDay)
404 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
407 ret = IOTVTICAL_INVALID_ACCESS;
413 ret = IOTVTICAL_INVALID_ACCESS;