CRED payload conversion from JSON to CBOR
[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
38 /**
39  * Parses periodStr and populate struct IotvtICalPeriod_t
40  *
41  * @param periodStr string to be parsed.
42  * @param period    IotvtICalPeriod_t struct to be populated.
43  *
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
47  */
48 IotvtICalResult_t ParsePeriod(const char *periodStr, IotvtICalPeriod_t *period)
49 {
50     if((NULL == periodStr) || (NULL == period))
51     {
52         return IOTVTICAL_INVALID_PARAMETER;
53     }
54
55     char *endDTPos;
56     char *fmt = "";
57     int   startDTLen;
58     int   endDTLen;
59
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, '/')))
66     {
67         return IOTVTICAL_INVALID_PERIOD;
68     }
69     endDTPos += 1;
70     startDTLen = endDTPos - periodStr - 1;
71     endDTLen   = strlen(endDTPos);
72
73     //Checking if both startDateTime and endDateTime are of same form
74     if(startDTLen == endDTLen)
75     {
76         if(8 == startDTLen) //YYYYmmdd
77         {
78             fmt = dFormat;
79         }
80         else if(15 == startDTLen) //YYYYmmddTHHMMSS
81         {
82             fmt = dtFormat;
83         }
84         else
85         {
86             return IOTVTICAL_INVALID_PERIOD;
87         }
88     }
89     else
90     {
91         return IOTVTICAL_INVALID_PERIOD;
92     }
93
94     //Checking if startDateTime has right format
95     if(NULL != strptime(periodStr, fmt, &period->startDateTime))
96     {
97         //Checking if endDateTime has right format
98         if(NULL != strptime(endDTPos, fmt, &period->endDateTime))
99         {
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)))
122             {
123                 return IOTVTICAL_INVALID_PERIOD;
124             }
125             else
126             {
127                 return IOTVTICAL_SUCCESS;
128             }
129         }
130     }
131     return IOTVTICAL_INVALID_PERIOD;
132 }
133
134
135 /**
136  * Parses untilRule and populate "until" field of struct IotvtICalRecur_t
137  *
138  * @param untilRule  string to be parsed.
139  * @param recur      IotvtICalRecur_t struct to be populated.
140  *
141  * @return  IOTVTICAL_ERRRO             -- if untilRule has invalid format
142  *          IOTVTICAL_INVALID_SUCCESS   -- if no error while parsing
143  */
144 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
145 {
146     char *date = strchr(untilRule, '=');
147
148     if(NULL == date)
149     {
150         return IOTVTICAL_ERROR;
151     }
152     date += 1;
153
154     if(strlen(date) == 8) //YYYYmmdd
155     {
156         if(NULL != strptime(date, dFormat, &recur->until))
157         {
158             return IOTVTICAL_SUCCESS;
159         }
160     }
161     return IOTVTICAL_ERROR;
162 }
163
164
165 /**
166  * Parses bydayRule and populate "byDay" field of struct IotvtICalRecur_t
167  *
168  * @param bydayRule  string to be parsed.
169  * @param recur      IotvtICalRecur_t struct to be populated.
170  *
171  * @return  IOTVTICAL_ERRRO             -- if bydayRule has empty weekday list or invalid weekdays
172  *          IOTVTICAL_INVALID_SUCCESS   -- if no error while parsing
173  */
174 static IotvtICalResult_t  ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
175 {
176     if(strstr(bydayRule, "SU"))
177     {
178         recur->byDay = recur->byDay | SUNDAY;
179     }
180     if(strstr(bydayRule, "MO"))
181     {
182         recur->byDay = recur->byDay | MONDAY;
183     }
184     if(strstr(bydayRule, "TU"))
185     {
186         recur->byDay = recur->byDay | TUESDAY;
187     }
188     if(strstr(bydayRule, "WE"))
189     {
190         recur->byDay = recur->byDay | WEDNESDAY;
191     }
192     if(strstr(bydayRule, "TH"))
193     {
194         recur->byDay = recur->byDay | THURSDAY;
195     }
196     if(strstr(bydayRule, "FR"))
197     {
198         recur->byDay = recur->byDay | FRIDAY;
199     }
200     if(strstr(bydayRule, "SA"))
201     {
202         recur->byDay = recur->byDay | SATURDAY;
203     }
204
205     //Checking if byDay list is empty or has inValid weekdays
206     if(recur->byDay == NO_WEEKDAY)
207     {
208         return IOTVTICAL_ERROR;
209     }
210
211     return IOTVTICAL_SUCCESS;
212 }
213
214
215 /**
216  * Parses recurStr and populate struct IotvtICalRecur_t
217  *
218  * @param recurStr string to be parsed.
219  * @param recur    IotvtICalPeriod_t struct to be populated.
220  *
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
224  */
225 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
226 {
227
228     if((NULL == recurStr) || (NULL == recur))
229     {
230         return IOTVTICAL_INVALID_PARAMETER;
231     }
232
233     const char *startPos="";
234     const char *endPos="";
235     char        buf[50];
236     int         freqFlag = 0; //valid RRULE must have "FREQ" parameter.
237                               //flag to track if RRULE has "FREQ" or not
238
239     startPos = recurStr;
240     //Iterates though recurrence rule
241     //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
242     while('\0' != startPos)
243     {
244         endPos = strchr(startPos, ';');
245         if(endPos)
246         {
247             endPos += 1;
248         }
249         OICStrcpy(buf, (endPos - startPos), startPos);
250         if(NULL != strstr(buf, FREQ))
251         {
252             if(NULL != strstr(buf, DAILY))
253             {
254                 recur->freq = FREQ_DAILY;
255                 freqFlag = 1;
256             }
257             else
258             {
259                 return IOTVTICAL_INVALID_RRULE;
260             }
261         }
262         else if(NULL != strstr(buf, UNTIL))
263         {
264             if(IOTVTICAL_SUCCESS != ParseDate(buf, recur))
265             {
266                 return IOTVTICAL_INVALID_RRULE;
267             }
268         }
269         else if(NULL != strstr(buf, BYDAY))
270         {
271             if(IOTVTICAL_SUCCESS != ParseByday(buf, recur))
272             {
273                 return IOTVTICAL_INVALID_RRULE;
274             };
275         }
276         startPos = endPos;
277     }
278
279     if(1 != freqFlag)
280     {
281         return IOTVTICAL_INVALID_RRULE;
282     }
283
284     return IOTVTICAL_SUCCESS;
285 }
286
287 /**
288  * Computes number of days between two dates.
289  *
290  * @param date1 earlier date.
291  * @param date2 later date.
292  *
293  * @return  number of days between date1 & date2.
294  */
295 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
296 {
297     int days;
298     int leapDays=0;
299
300     if(date2->tm_year > date1->tm_year)
301     {
302         for(int y = date1->tm_year; y < date2->tm_year; y++)
303         {
304             y += TM_YEAR_OFFSET;
305             if(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
306             {
307                leapDays += 1;
308             }
309         }
310     }
311
312     days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
313            (365 * date1->tm_year + date1->tm_yday);
314
315     return days;
316 }
317
318
319 /**
320  * Computes number of seconds between two time.
321  *
322  * @param time1 earlier time.
323  * @param date2 later time.
324  *
325  * @return  number of seconds between time1 and time2.
326  */
327 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
328 {
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);
331 }
332
333 /**
334  * Validates if the @param currentTime is with in allowable period
335  *
336  * @param   period         -- allowable period
337  * @param   currentTime    -- the time that need to be validated against allowable time
338  *
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
342  */
343 static IotvtICalResult_t ValidatePeriod(IotvtICalPeriod_t *period, IotvtICalDateTime_t *currentTime)
344 {
345     if(NULL == period || NULL == currentTime)
346     {
347         return IOTVTICAL_INVALID_PARAMETER;
348     }
349
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;
355
356     //If today is the start day of the allowable period then check
357     //currentTime > allowable period startTime
358     if(todayIsStartDay)
359     {
360         validStartTime = (0 <= DiffSecs(&period->startDateTime, currentTime)) ? true : false;
361     }
362
363     //If today is the end day of allowable period then check
364     //currentTime < allowable period endTime
365     if(todayIsEndDay)
366     {
367         validEndTime = (0 <= DiffSecs(currentTime, &period->endDateTime)) ? true :false;
368     }
369
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)))
373     {
374         validDay = true;
375     }
376
377     if(validDay && validStartTime && validEndTime)
378     {
379         return IOTVTICAL_VALID_ACCESS;
380     }
381     else
382     {
383         return IOTVTICAL_INVALID_ACCESS;
384     }
385 }
386
387 /**
388  * This API is used by policy engine to checks if the
389  * request to access resource is within valid time.
390  *
391  * @param period string representing period.
392  * @param recur string representing recurrence rule
393  *
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
399  */
400
401 IotvtICalResult_t IsRequestWithinValidTime(char *periodStr, char *recurStr)
402 {
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)
407     {
408         return IOTVTICAL_INVALID_PARAMETER;
409     }
410
411     IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
412     IotvtICalRecur_t recur = {.freq=0};
413     IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
414
415     time_t rawTime = time(0);
416     IotvtICalDateTime_t *currentTime = localtime(&rawTime);
417
418     ret  = ParsePeriod(periodStr, &period);
419     if(ret != IOTVTICAL_SUCCESS)
420     {
421         return ret;
422     }
423
424     //If recur is NULL then the access time is between period's startDateTime and endDateTime
425     if(NULL == recurStr)
426     {
427         ret = ValidatePeriod(&period, currentTime);
428     }
429
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
435     if(NULL != recurStr)
436     {
437         ret = ParseRecur(recurStr, &recur);
438         if(ret != IOTVTICAL_SUCCESS)
439         {
440             return ret;
441         }
442
443         if((0 <= DiffSecs(&period.startDateTime, currentTime))&&
444            (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
445            (0 <= DiffDays(&period.startDateTime, currentTime)))
446         {
447             IotvtICalDateTime_t emptyDT = {.tm_sec=0};
448             ret = IOTVTICAL_VALID_ACCESS;
449
450             //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
451             if(0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
452             {
453                 if(0 > DiffDays(currentTime, &recur.until))
454                 {
455                     ret = IOTVTICAL_INVALID_ACCESS;
456                 }
457             }
458
459             //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
460             if(NO_WEEKDAY != recur.byDay)
461             {
462
463                 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
464                 if(!isValidWD)
465                 {
466                     ret = IOTVTICAL_INVALID_ACCESS;
467                 }
468              }
469         }
470         else
471         {
472             ret = IOTVTICAL_INVALID_ACCESS;
473         }
474     }
475     return ret;
476 }
477 #endif