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
22 #if !defined(WITH_ARDUINO)
24 #define _XOPEN_SOURCE //Needed by strptime
25 #include "iotivity_config.h"
27 #include "iotvticalendar.h"
28 #include "oic_string.h"
31 char *strptime(const char *buf, const char *fmt, struct tm *tm);
34 static char dtFormat[] = "%Y%m%dT%H%M%S"; //date-time format
35 static char dFormat[] = "%Y%m%d"; // date format
37 static const char FREQ[] = "FREQ";
38 static const char UNTIL[] = "UNTIL";
39 static const char BYDAY[] = "BYDAY";
40 static const char DAILY[] = "DAILY";
42 IotvtICalResult_t ParsePeriod(const char *periodStr, IotvtICalPeriod_t *period)
44 if ((NULL == periodStr) || (NULL == period))
46 return IOTVTICAL_INVALID_PARAMETER;
54 //Finding length of startDateTime and endDateTime in period
55 //startDateTime and endDateTime can have form YYYYmmdd or YYYYmmddTHHMMSS
56 //startDateTime and endDateTime must be same form
57 //Eg: periodStr = "20150629T153050/20150630T203055"
58 // periodStr = "20150629/20150630"
59 if (NULL == (endDTPos = strchr(periodStr, '/')))
61 return IOTVTICAL_INVALID_PERIOD;
64 startDTLen = endDTPos - periodStr - 1;
65 endDTLen = strlen(endDTPos);
67 //Checking if both startDateTime and endDateTime are of same form
68 if (startDTLen == endDTLen)
70 if (8 == startDTLen) //YYYYmmdd
74 else if (15 == startDTLen) //YYYYmmddTHHMMSS
80 return IOTVTICAL_INVALID_PERIOD;
85 return IOTVTICAL_INVALID_PERIOD;
88 //Checking if startDateTime has right format
89 if (NULL != strptime(periodStr, fmt, &period->startDateTime))
91 //Checking if endDateTime has right format
92 if (NULL != strptime(endDTPos, fmt, &period->endDateTime))
94 //Checking if endDateTime is after startDateTime
95 if ((period->startDateTime.tm_year > period->endDateTime.tm_year)
96 || ((period->startDateTime.tm_year == period->endDateTime.tm_year)
97 && (period->startDateTime.tm_mon > period->endDateTime.tm_mon))
98 || ((period->startDateTime.tm_year == period->endDateTime.tm_year)
99 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
100 && (period->startDateTime.tm_mday > period->endDateTime.tm_mday))
101 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
102 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
103 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
104 && (period->startDateTime.tm_hour > period->endDateTime.tm_hour))
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 || (( fmt == dtFormat) && (period->startDateTime.tm_year == period->endDateTime.tm_year)
111 && (period->startDateTime.tm_mon == period->endDateTime.tm_mon)
112 && (period->startDateTime.tm_mday == period->endDateTime.tm_mday)
113 && (period->startDateTime.tm_hour == period->endDateTime.tm_hour)
114 && (period->startDateTime.tm_min == period->endDateTime.tm_min)
115 && (period->startDateTime.tm_sec > period->endDateTime.tm_sec)))
117 return IOTVTICAL_INVALID_PERIOD;
121 return IOTVTICAL_SUCCESS;
125 return IOTVTICAL_INVALID_PERIOD;
129 * Parses untilRule and populate "until" field of struct IotvtICalRecur_t.
131 * @param untilRule is a string to be parsed.
132 * @param recur is the reference to the @ref IotvtICalRecur_t to be populated.
134 * @return ::IOTVTICAL_SUCCESS is succesful, else in case of error
135 * ::IOTVTICAL_ERROR, if untilRule has invalid format or ::IOTVTICAL_INVALID_SUCCESS,
136 * if no error while parsing.
138 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
140 char *date = strchr(untilRule, '=');
144 return IOTVTICAL_ERROR;
148 if (strlen(date) == 8) //YYYYmmdd
150 if (NULL != strptime(date, dFormat, &recur->until))
152 return IOTVTICAL_SUCCESS;
155 return IOTVTICAL_ERROR;
159 * Parses bydayRule and populate "byDay" field of struct @ref IotvtICalRecur_t.
161 * @param bydayRule is a string to be parsed.
162 * @param recur is a reference to @ref IotvtICalRecur_t struct to be populated.
164 * @return ::IOTVTICAL_SUCCESS is succesful, else in case of error ::IOTVTICAL_ERROR,
165 * if bydayRule has empty weekday list or invalid weekdays.
167 static IotvtICalResult_t ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
169 if (strstr(bydayRule, "SU"))
171 recur->byDay = recur->byDay | SUNDAY;
173 if (strstr(bydayRule, "MO"))
175 recur->byDay = recur->byDay | MONDAY;
177 if (strstr(bydayRule, "TU"))
179 recur->byDay = recur->byDay | TUESDAY;
181 if (strstr(bydayRule, "WE"))
183 recur->byDay = recur->byDay | WEDNESDAY;
185 if (strstr(bydayRule, "TH"))
187 recur->byDay = recur->byDay | THURSDAY;
189 if (strstr(bydayRule, "FR"))
191 recur->byDay = recur->byDay | FRIDAY;
193 if (strstr(bydayRule, "SA"))
195 recur->byDay = recur->byDay | SATURDAY;
198 //Checking if byDay list is empty or has inValid weekdays
199 if (recur->byDay == NO_WEEKDAY)
201 return IOTVTICAL_ERROR;
204 return IOTVTICAL_SUCCESS;
207 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
209 if ((NULL == recurStr) || (NULL == recur))
211 return IOTVTICAL_INVALID_PARAMETER;
214 const char *startPos="";
215 const char *endPos="";
217 int freqFlag = 0; //valid RRULE must have "FREQ" parameter.
218 //flag to track if RRULE has "FREQ" or not
221 //Iterates though recurrence rule
222 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
223 while ('\0' != startPos)
225 endPos = strchr(startPos, ';');
230 OICStrcpy(buf, (endPos - startPos), startPos);
231 if (NULL != strstr(buf, FREQ))
233 if (NULL != strstr(buf, DAILY))
235 recur->freq = FREQ_DAILY;
240 return IOTVTICAL_INVALID_RRULE;
243 else if (NULL != strstr(buf, UNTIL))
245 if (IOTVTICAL_SUCCESS != ParseDate(buf, recur))
247 return IOTVTICAL_INVALID_RRULE;
250 else if (NULL != strstr(buf, BYDAY))
252 if (IOTVTICAL_SUCCESS != ParseByday(buf, recur))
254 return IOTVTICAL_INVALID_RRULE;
262 return IOTVTICAL_INVALID_RRULE;
265 return IOTVTICAL_SUCCESS;
269 * Computes number of days between two dates.
271 * @param date1 earlier date.
272 * @param date2 later date.
274 * @return number of days between date1 & date2.
276 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
281 if (date2->tm_year > date1->tm_year)
283 for (int y = date1->tm_year; y < date2->tm_year; y++)
286 if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
293 days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
294 (365 * date1->tm_year + date1->tm_yday);
300 * Computes number of seconds between two time.
302 * @param time1 earlier time.
303 * @param date2 later time.
305 * @return number of seconds between time1 and time2.
307 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
309 return (3600 * time2->tm_hour + 60 * time2->tm_min + time2->tm_sec) -
310 (3600 * time1->tm_hour + 60 * time1->tm_min + time1->tm_sec);
314 * Validates if the @param currentTime is with in allowable period.
316 * @param period allowable period.
317 * @param currentTime the time that need to be validated against allowable time.
319 * @return ::IOTVTICAL_VALID_ACCESS, if the request is within valid time period.
320 * ::IOTVTICAL_INVALID_ACCESS, if the request is not within valid time period.
321 * ::IOTVTICAL_INVALID_PARAMETER, if parameter are invalid.
323 static IotvtICalResult_t ValidatePeriod(IotvtICalPeriod_t *period, IotvtICalDateTime_t *currentTime)
325 if (NULL == period || NULL == currentTime)
327 return IOTVTICAL_INVALID_PARAMETER;
330 bool validStartTime = true;
331 bool validEndTime = true;
332 bool validDay = false;
333 bool todayIsStartDay = (0 == DiffDays(&period->startDateTime, currentTime)) ? true : false;
334 bool todayIsEndDay = (0 == DiffDays(currentTime, &period->endDateTime)) ? true : false;
336 //If today is the start day of the allowable period then check
337 //currentTime > allowable period startTime
340 validStartTime = (0 <= DiffSecs(&period->startDateTime, currentTime)) ? true : false;
343 //If today is the end day of allowable period then check
344 //currentTime < allowable period endTime
347 validEndTime = (0 <= DiffSecs(currentTime, &period->endDateTime)) ? true :false;
350 //Check if today is valid day between startDate and EndDate inclusive
351 if ((0 <= DiffDays(&period->startDateTime, currentTime)) &&
352 (0 <= DiffDays(currentTime, &period->endDateTime)))
357 if (validDay && validStartTime && validEndTime)
359 return IOTVTICAL_VALID_ACCESS;
363 return IOTVTICAL_INVALID_ACCESS;
367 IotvtICalResult_t IsRequestWithinValidTime(const char *periodStr, const char *recurStr)
369 //NULL recur rule means no recurring patter exist.
370 //Period can't be null. Period is used with or without
371 //recur rule to compute allowable access time.
372 if (NULL == periodStr)
374 return IOTVTICAL_INVALID_PARAMETER;
377 IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
378 IotvtICalRecur_t recur = {.freq=0};
379 IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
381 time_t rawTime = time(0);
382 IotvtICalDateTime_t *currentTime = localtime(&rawTime);
384 ret = ParsePeriod(periodStr, &period);
385 if (ret != IOTVTICAL_SUCCESS)
390 //If recur is NULL then the access time is between period's startDateTime and endDateTime
391 if (NULL == recurStr)
393 ret = ValidatePeriod(&period, currentTime);
396 //If recur is not NULL then the access time is between period's startTime and
397 //endTime on days specified in "BYDAY" list. The first instance of recurrence
398 //is computed from period's startDate and the last instance is computed from
399 //"UNTIL". If "UNTIL" is not specified then the recurrence goes for forever.
400 //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
401 if (NULL != recurStr)
403 ret = ParseRecur(recurStr, &recur);
404 if (ret != IOTVTICAL_SUCCESS)
409 if ((0 <= DiffSecs(&period.startDateTime, currentTime))&&
410 (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
411 (0 <= DiffDays(&period.startDateTime, currentTime)))
413 IotvtICalDateTime_t emptyDT = {.tm_sec=0};
414 ret = IOTVTICAL_VALID_ACCESS;
416 //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
417 if (0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
419 if(0 > DiffDays(currentTime, &recur.until))
421 ret = IOTVTICAL_INVALID_ACCESS;
425 //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
426 if (NO_WEEKDAY != recur.byDay)
429 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
432 ret = IOTVTICAL_INVALID_ACCESS;
438 ret = IOTVTICAL_INVALID_ACCESS;