Imported Upstream version 1.0.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
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(difftime(mktime(&period->endDateTime),
102                         mktime(&period->startDateTime)) > 0)
103             {
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)
108                 {
109                     period->startDateTime.tm_hour =
110                    (period->startDateTime.tm_hour + TOTAL_HOURS - TM_DST_OFFSET) % TOTAL_HOURS;
111                 }
112                 if(period->endDateTime.tm_isdst)
113                 {
114                     period->endDateTime.tm_hour =
115                    (period->endDateTime.tm_hour + TOTAL_HOURS - TM_DST_OFFSET) % TOTAL_HOURS;
116                 }
117                 return IOTVTICAL_SUCCESS;
118             }
119         }
120     }
121     return IOTVTICAL_INVALID_PERIOD;
122 }
123
124
125 /**
126  * Parses untilRule and populate "until" field of struct IotvtICalRecur_t
127  *
128  * @param untilRule  string to be parsed.
129  * @param recur      IotvtICalRecur_t struct to be populated.
130  *
131  * @return  IOTVTICAL_ERRRO             -- if untilRule has invalid format
132  *          IOTVTICAL_INVALID_SUCCESS   -- if no error while parsing
133  */
134 static IotvtICalResult_t ParseDate(char *untilRule, IotvtICalRecur_t *recur)
135 {
136     char *date = strchr(untilRule, '=');
137
138     if(NULL == date)
139     {
140         return IOTVTICAL_ERROR;
141     }
142     date += 1;
143
144     if(strlen(date) == 8) //YYYYmmdd
145     {
146         if(NULL != strptime(date, dFormat, &recur->until))
147         {
148             return IOTVTICAL_SUCCESS;
149         }
150     }
151     return IOTVTICAL_ERROR;
152 }
153
154
155 /**
156  * Parses bydayRule and populate "byDay" field of struct IotvtICalRecur_t
157  *
158  * @param bydayRule  string to be parsed.
159  * @param recur      IotvtICalRecur_t struct to be populated.
160  *
161  * @return  IOTVTICAL_ERRRO             -- if bydayRule has empty weekday list or invalid weekdays
162  *          IOTVTICAL_INVALID_SUCCESS   -- if no error while parsing
163  */
164 static IotvtICalResult_t  ParseByday(char *bydayRule, IotvtICalRecur_t *recur)
165 {
166     if(strstr(bydayRule, "SU"))
167     {
168         recur->byDay = recur->byDay | SUNDAY;
169     }
170     if(strstr(bydayRule, "MO"))
171     {
172         recur->byDay = recur->byDay | MONDAY;
173     }
174     if(strstr(bydayRule, "TU"))
175     {
176         recur->byDay = recur->byDay | TUESDAY;
177     }
178     if(strstr(bydayRule, "WE"))
179     {
180         recur->byDay = recur->byDay | WEDNESDAY;
181     }
182     if(strstr(bydayRule, "TH"))
183     {
184         recur->byDay = recur->byDay | THURSDAY;
185     }
186     if(strstr(bydayRule, "FR"))
187     {
188         recur->byDay = recur->byDay | FRIDAY;
189     }
190     if(strstr(bydayRule, "SA"))
191     {
192         recur->byDay = recur->byDay | SATURDAY;
193     }
194
195     //Checking if byDay list is empty or has inValid weekdays
196     if(recur->byDay == NO_WEEKDAY)
197     {
198         return IOTVTICAL_ERROR;
199     }
200
201     return IOTVTICAL_SUCCESS;
202 }
203
204
205 /**
206  * Parses recurStr and populate struct IotvtICalRecur_t
207  *
208  * @param recurStr string to be parsed.
209  * @param recur    IotvtICalPeriod_t struct to be populated.
210  *
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
214  */
215 IotvtICalResult_t ParseRecur(const char *recurStr, IotvtICalRecur_t *recur)
216 {
217
218     if((NULL == recurStr) || (NULL == recur))
219     {
220         return IOTVTICAL_INVALID_PARAMETER;
221     }
222
223     const char *startPos="";
224     const char *endPos="";
225     char        buf[50];
226     int         freqFlag = 0; //valid RRULE must have "FREQ" parameter.
227                               //flag to track if RRULE has "FREQ" or not
228
229     startPos = recurStr;
230     //Iterates though recurrence rule
231     //Eg, RRULE: FREQ=DAILY; UNTIL=20150703; BYDAY=MO, WE, FR
232     while('\0' != startPos)
233     {
234         endPos = strchr(startPos, ';');
235         if(endPos)
236         {
237             endPos += 1;
238         }
239         OICStrcpy(buf, (endPos - startPos), startPos);
240         if(NULL != strstr(buf, FREQ))
241         {
242             if(NULL != strstr(buf, DAILY))
243             {
244                 recur->freq = FREQ_DAILY;
245                 freqFlag = 1;
246             }
247             else
248             {
249                 return IOTVTICAL_INVALID_RRULE;
250             }
251         }
252         else if(NULL != strstr(buf, UNTIL))
253         {
254             if(IOTVTICAL_SUCCESS != ParseDate(buf, recur))
255             {
256                 return IOTVTICAL_INVALID_RRULE;
257             }
258         }
259         else if(NULL != strstr(buf, BYDAY))
260         {
261             if(IOTVTICAL_SUCCESS != ParseByday(buf, recur))
262             {
263                 return IOTVTICAL_INVALID_RRULE;
264             };
265         }
266         startPos = endPos;
267     }
268
269     if(1 != freqFlag)
270     {
271         return IOTVTICAL_INVALID_RRULE;
272     }
273
274     return IOTVTICAL_SUCCESS;
275 }
276
277 /**
278  * Computes number of days between two dates.
279  *
280  * @param date1 earlier date.
281  * @param date2 later date.
282  *
283  * @return  number of days between date1 & date2.
284  */
285 static int DiffDays(IotvtICalDateTime_t *date1, IotvtICalDateTime_t *date2)
286 {
287     int days;
288     int leapDays=0;
289
290     if(date2->tm_year > date1->tm_year)
291     {
292         for(int y = date1->tm_year; y < date2->tm_year; y++)
293         {
294             y += TM_YEAR_OFFSET;
295             if(y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
296             {
297                leapDays += 1;
298             }
299         }
300     }
301
302     days = (365 * date2->tm_year + date2->tm_yday + leapDays) -
303            (365 * date1->tm_year + date1->tm_yday);
304
305     return days;
306 }
307
308
309 /**
310  * Computes number of seconds between two time.
311  *
312  * @param time1 earlier time.
313  * @param date2 later time.
314  *
315  * @return  number of seconds between time1 and time2.
316  */
317 static int DiffSecs(IotvtICalDateTime_t *time1, IotvtICalDateTime_t *time2)
318 {
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);
321 }
322
323
324 /**
325  * This API is used by policy engine to checks if the
326  * request to access resource is within valid time.
327  *
328  * @param period string representing period.
329  * @param recur string representing recurrence rule
330  *
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
336  */
337
338 IotvtICalResult_t IsRequestWithinValidTime(char *periodStr, char *recurStr)
339 {
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)
344     {
345         return IOTVTICAL_INVALID_PARAMETER;
346     }
347
348     IotvtICalPeriod_t period = {.startDateTime={.tm_sec=0}};
349     IotvtICalRecur_t recur = {.freq=0};
350     IotvtICalResult_t ret = IOTVTICAL_INVALID_ACCESS;
351
352     time_t rawTime = time(0);
353     IotvtICalDateTime_t *currentTime = localtime(&rawTime);
354
355     ret  = ParsePeriod(periodStr, &period);
356     if(ret != IOTVTICAL_SUCCESS)
357     {
358         return ret;
359     }
360
361     //If recur is NULL then the access time is between period's startDate and endDate
362     if(NULL == recurStr)
363     {
364         if((0 <= DiffDays(&period.startDateTime, currentTime)) &&
365            (0 <= DiffDays(currentTime, &period.endDateTime)))
366         {
367             ret = IOTVTICAL_VALID_ACCESS;
368         }
369     }
370
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
376     if(NULL != recurStr)
377     {
378         ret = ParseRecur(recurStr, &recur);
379         if(ret != IOTVTICAL_SUCCESS)
380         {
381             return ret;
382         }
383
384         if((0 <= DiffSecs(&period.startDateTime, currentTime))&&
385            (0 <= DiffSecs(currentTime, &period.endDateTime)) &&
386            (0 <= DiffDays(&period.startDateTime, currentTime)))
387         {
388             IotvtICalDateTime_t emptyDT = {.tm_sec=0};
389             ret = IOTVTICAL_VALID_ACCESS;
390
391             //"UNTIL" is an optional parameter of RRULE, checking if until present in recur
392             if(0 != memcmp(&recur.until, &emptyDT, sizeof(IotvtICalDateTime_t)))
393             {
394                 if(0 > DiffDays(currentTime, &recur.until))
395                 {
396                     ret = IOTVTICAL_INVALID_ACCESS;
397                 }
398             }
399
400             //"BYDAY" is an optional parameter of RRULE, checking if byday present in recur
401             if(NO_WEEKDAY != recur.byDay)
402             {
403
404                 int isValidWD = (0x1 << currentTime->tm_wday) & recur.byDay; //Valid weekdays
405                 if(!isValidWD)
406                 {
407                     ret = IOTVTICAL_INVALID_ACCESS;
408                 }
409              }
410         }
411         else
412         {
413             ret = IOTVTICAL_INVALID_ACCESS;
414         }
415     }
416     return ret;
417 }
418 #endif