Update snapshot(2017-12-20)
[platform/upstream/iotivity.git] / resource / csdk / security / src / iotvticalendar.c
1 //******************************************************************
2 //
3 // Copyright 2015 Intel Mobile Communications GmbH All Rights Reserved.
4 //
5 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
6 //
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
10 //
11 //      http://www.apache.org/licenses/LICENSE-2.0
12 //
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.
18 //
19 //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
20
21 //Not supported on Arduino due lack of absolute time need to implement iCalendar
22 #if !defined(WITH_ARDUINO)
23
24 #define _XOPEN_SOURCE  //Needed by strptime
25 #include "iotivity_config.h"
26 #include <string.h>
27 #include "iotvticalendar.h"
28 #include "oic_string.h"
29
30 #ifndef HAVE_STRPTIME
31 char *strptime(const char *buf, const char *fmt, struct tm *tm);
32 #endif
33
34 static char dtFormat[] =  "%Y%m%dT%H%M%S"; //date-time format
35 static char dFormat[] =  "%Y%m%d";         // date format
36
37 static const char FREQ[]  = "FREQ";
38 static const char UNTIL[] = "UNTIL";
39 static const char BYDAY[] = "BYDAY";
40 static const char DAILY[] = "DAILY";
41
42 IotvtICalResult_t ParsePeriod(const char *periodStr, IotvtICalPeriod_t *period)
43 {
44     if ((NULL == periodStr) || (NULL == period))
45     {
46         return IOTVTICAL_INVALID_PARAMETER;
47     }
48
49     char *endDTPos;
50     char *fmt = "";
51     int   startDTLen;
52     int   endDTLen;
53
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, '/')))
60     {
61         return IOTVTICAL_INVALID_PERIOD;
62     }
63     endDTPos += 1;
64     startDTLen = endDTPos - periodStr - 1;
65     endDTLen   = strlen(endDTPos);
66
67     //Checking if both startDateTime and endDateTime are of same form
68     if (startDTLen == endDTLen)
69     {
70         if (8 == startDTLen) //YYYYmmdd
71         {
72             fmt = dFormat;
73         }
74         else if (15 == startDTLen) //YYYYmmddTHHMMSS
75         {
76             fmt = dtFormat;
77         }
78         else
79         {
80             return IOTVTICAL_INVALID_PERIOD;
81         }
82     }
83     else
84     {
85         return IOTVTICAL_INVALID_PERIOD;
86     }
87
88     //Checking if startDateTime has right format
89     if (NULL != strptime(periodStr, fmt, &period->startDateTime))
90     {
91         //Checking if endDateTime has right format
92         if (NULL != strptime(endDTPos, fmt, &period->endDateTime))
93         {
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)))
116             {
117                 return IOTVTICAL_INVALID_PERIOD;
118             }
119             else
120             {
121                 return IOTVTICAL_SUCCESS;
122             }
123         }
124     }
125     return IOTVTICAL_INVALID_PERIOD;
126 }
127
128 /**
129  * Parses untilRule and populate "until" field of struct IotvtICalRecur_t.
130  *
131  * @param untilRule is a string to be parsed.
132  * @param recur is the reference to the @ref IotvtICalRecur_t to be populated.
133  *
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.
137  */
138 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
139 {
140     char *date = strchr(untilRule, '=');
141
142     if (NULL == date)
143     {
144         return IOTVTICAL_ERROR;
145     }
146     date += 1;
147
148     if (strlen(date) == 8) //YYYYmmdd
149     {
150         if (NULL != strptime(date, dFormat, &recur->until))
151         {
152             return IOTVTICAL_SUCCESS;
153         }
154     }
155     return IOTVTICAL_ERROR;
156 }
157
158 /**
159  * Parses bydayRule and populate "byDay" field of struct @ref IotvtICalRecur_t.
160  *
161  * @param bydayRule is a string to be parsed.
162  * @param recur is a reference to @ref IotvtICalRecur_t struct to be populated.
163  *
164  * @return ::IOTVTICAL_SUCCESS is succesful, else in case of error ::IOTVTICAL_ERROR,
165  * if bydayRule has empty weekday list or invalid weekdays.
166  */
167 static IotvtICalResult_t  ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
168 {
169     if (strstr(bydayRule, "SU"))
170     {
171         recur->byDay = recur->byDay | SUNDAY;
172     }
173     if (strstr(bydayRule, "MO"))
174     {
175         recur->byDay = recur->byDay | MONDAY;
176     }
177     if (strstr(bydayRule, "TU"))
178     {
179         recur->byDay = recur->byDay | TUESDAY;
180     }
181     if (strstr(bydayRule, "WE"))
182     {
183         recur->byDay = recur->byDay | WEDNESDAY;
184     }
185     if (strstr(bydayRule, "TH"))
186     {
187         recur->byDay = recur->byDay | THURSDAY;
188     }
189     if (strstr(bydayRule, "FR"))
190     {
191         recur->byDay = recur->byDay | FRIDAY;
192     }
193     if (strstr(bydayRule, "SA"))
194     {
195         recur->byDay = recur->byDay | SATURDAY;
196     }
197
198     //Checking if byDay list is empty or has inValid weekdays
199     if (recur->byDay == NO_WEEKDAY)
200     {
201         return IOTVTICAL_ERROR;
202     }
203
204     return IOTVTICAL_SUCCESS;
205 }
206
207 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
208 {
209     if ((NULL == recurStr) || (NULL == recur))
210     {
211         return IOTVTICAL_INVALID_PARAMETER;
212     }
213
214     const char *startPos="";
215     const char *endPos="";
216     char        buf[50];
217     int         freqFlag = 0; //valid RRULE must have "FREQ" parameter.
218                               //flag to track if RRULE has "FREQ" or not
219
220     startPos = recurStr;
221     //Iterates though recurrence rule
222     //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
223     while ('\0' != startPos)
224     {
225         endPos = strchr(startPos, ';');
226         if (endPos)
227         {
228             endPos += 1;
229         }
230         OICStrcpy(buf, (endPos - startPos), startPos);
231         if (NULL != strstr(buf, FREQ))
232         {
233             if (NULL != strstr(buf, DAILY))
234             {
235                 recur->freq = FREQ_DAILY;
236                 freqFlag = 1;
237             }
238             else
239             {
240                 return IOTVTICAL_INVALID_RRULE;
241             }
242         }
243         else if (NULL != strstr(buf, UNTIL))
244         {
245             if (IOTVTICAL_SUCCESS != ParseDate(buf, recur))
246             {
247                 return IOTVTICAL_INVALID_RRULE;
248             }
249         }
250         else if (NULL != strstr(buf, BYDAY))
251         {
252             if (IOTVTICAL_SUCCESS != ParseByday(buf, recur))
253             {
254                 return IOTVTICAL_INVALID_RRULE;
255             };
256         }
257         startPos = endPos;
258     }
259
260     if (1 != freqFlag)
261     {
262         return IOTVTICAL_INVALID_RRULE;
263     }
264
265     return IOTVTICAL_SUCCESS;
266 }
267
268 /**
269  * Computes number of days between two dates.
270  *
271  * @param date1 earlier date.
272  * @param date2 later date.
273  *
274  * @return  number of days between date1 & date2.
275  */
276 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
277 {
278     int days;
279     int leapDays=0;
280
281     if (date2->tm_year > date1->tm_year)
282     {
283         for (int y = date1->tm_year; y < date2->tm_year; y++)
284         {
285             y += TM_YEAR_OFFSET;
286             if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
287             {
288                leapDays += 1;
289             }
290         }
291     }
292
293     days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
294            (365 * date1->tm_year + date1->tm_yday);
295
296     return days;
297 }
298
299 /**
300  * Computes number of seconds between two time.
301  *
302  * @param time1 earlier time.
303  * @param date2 later time.
304  *
305  * @return  number of seconds between time1 and time2.
306  */
307 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
308 {
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);
311 }
312
313 /**
314  * Validates if the @param currentTime is with in allowable period.
315  *
316  * @param period allowable period.
317  * @param currentTime the time that need to be validated against allowable time.
318  *
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.
322  */
323 static IotvtICalResult_t ValidatePeriod(IotvtICalPeriod_t *period, IotvtICalDateTime_t *currentTime)
324 {
325     if (NULL == period || NULL == currentTime)
326     {
327         return IOTVTICAL_INVALID_PARAMETER;
328     }
329
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;
335
336     //If today is the start day of the allowable period then check
337     //currentTime > allowable period startTime
338     if (todayIsStartDay)
339     {
340         validStartTime = (0 <= DiffSecs(&period->startDateTime, currentTime)) ? true : false;
341     }
342
343     //If today is the end day of allowable period then check
344     //currentTime < allowable period endTime
345     if (todayIsEndDay)
346     {
347         validEndTime = (0 <= DiffSecs(currentTime, &period->endDateTime)) ? true :false;
348     }
349
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)))
353     {
354         validDay = true;
355     }
356
357     if (validDay && validStartTime && validEndTime)
358     {
359         return IOTVTICAL_VALID_ACCESS;
360     }
361     else
362     {
363         return IOTVTICAL_INVALID_ACCESS;
364     }
365 }
366
367 IotvtICalResult_t IsRequestWithinValidTime(const char *periodStr, const char *recurStr)
368 {
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)
373     {
374         return IOTVTICAL_INVALID_PARAMETER;
375     }
376
377     IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
378     IotvtICalRecur_t recur = {.freq=0};
379     IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
380
381     time_t rawTime = time(0);
382     IotvtICalDateTime_t *currentTime = localtime(&rawTime);
383
384     ret  = ParsePeriod(periodStr, &period);
385     if (ret != IOTVTICAL_SUCCESS)
386     {
387         return ret;
388     }
389
390     //If recur is NULL then the access time is between period's startDateTime and endDateTime
391     if (NULL == recurStr)
392     {
393         ret = ValidatePeriod(&period, currentTime);
394     }
395
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)
402     {
403         ret = ParseRecur(recurStr, &recur);
404         if (ret != IOTVTICAL_SUCCESS)
405         {
406             return ret;
407         }
408
409         if ((0 <= DiffSecs(&period.startDateTime, currentTime))&&
410            (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
411            (0 <= DiffDays(&period.startDateTime, currentTime)))
412         {
413             IotvtICalDateTime_t emptyDT = {.tm_sec=0};
414             ret = IOTVTICAL_VALID_ACCESS;
415
416             //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
417             if (0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
418             {
419                 if(0 > DiffDays(currentTime, &recur.until))
420                 {
421                     ret = IOTVTICAL_INVALID_ACCESS;
422                 }
423             }
424
425             //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
426             if (NO_WEEKDAY != recur.byDay)
427             {
428
429                 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
430                 if (!isValidWD)
431                 {
432                     ret = IOTVTICAL_INVALID_ACCESS;
433                 }
434              }
435         }
436         else
437         {
438             ret = IOTVTICAL_INVALID_ACCESS;
439         }
440     }
441     return ret;
442 }
443 #endif