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