Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: Sample_HTTPClient Sample_HTTPClient LWM2M_NanoService_Ethernet LWM2M_NanoService_Ethernet ... more
Fork of HTTPClient by
More recent changes - added iCal processing.
Derivative of a derivative, however this one works when it comes to supplying Basic authorization to access a protected resource. Some additional changes to the debug interface to clean it up for consistency with many other components I have.
Revision 39:21fc7a4b6927, committed 2017-03-10
- Comitter:
- WiredHome
- Date:
- Fri Mar 10 02:53:09 2017 +0000
- Parent:
- 38:2ef07232f65c
- Child:
- 40:bcb19f8dbba3
- Commit message:
- Added an iCal interface to extract events on the fly, rather than requiring a big buffer.
Changed in this revision
| data/HTTPiCal.cpp | Show annotated file Show diff for this revision Revisions of this file |
| data/HTTPiCal.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/data/HTTPiCal.cpp Fri Mar 10 02:53:09 2017 +0000
@@ -0,0 +1,696 @@
+#include "HTTPiCal.h"
+
+#define DEBUG "iCal"
+#include <cstdio>
+#if (defined(DEBUG) && !defined(TARGET_LPC11U24))
+#define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
+#else
+#define DBG(x, ...)
+#define WARN(x, ...)
+#define ERR(x, ...)
+#define INFO(x, ...)
+#endif
+
+HTTPiCal::HTTPiCal(int count) {
+ EventList = (Event_T *)malloc(count * sizeof(Event_T));
+ if (EventList) {
+ EventSpaceCount = count;
+ EventCount = 0;
+ seeking = idle;
+ } else {
+ error("no space for event list");
+ }
+}
+
+HTTPiCal::~HTTPiCal() {
+ if (EventList)
+ free(EventList);
+}
+
+void HTTPiCal::SetTimeWindow(time_t StartTime, time_t EndTime) {
+ gridStartTime = StartTime;
+ gridEndTime = EndTime;
+}
+
+bool HTTPiCal::GetEvent(unsigned int i, Event_T * event) {
+ if (i < EventCount) {
+ *event = EventList[i];
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void HTTPiCal::writeReset() {
+ //INFO("writeReset()");
+ EventCount = 0;
+ lineBuf[0] = '\0';
+}
+
+int HTTPiCal::write(const char * buf, size_t len) {
+ const char * pStart = buf;
+ const char * pEOL;
+ size_t origLen = len;
+
+ pEOL = strchr(pStart, '\n');
+ //INFO("\r\n\r\nwrite[%d:%d] = \r\n%s\r\nend-write\r\n", len, pEOL - pStart, buf);
+ while (pEOL && (pEOL - pStart) < len) {
+ int lbLen = strlen(lineBuf);
+ strncpy(lineBuf + lbLen, pStart, (pEOL - pStart));
+ lineBuf[lbLen + (pEOL - pStart) + 1] = '\0';
+ if (lineBuf[pEOL - pStart + lbLen - 1] == '\r')
+ lineBuf[pEOL - pStart + lbLen - 1] = '\0';
+ //INFO("lineBuf:[%s]", lineBuf);
+ ParseICalStream(lineBuf, gridStartTime, gridEndTime, -300);
+ //INFO("");
+ lineBuf[0] = '\0';
+ len -= (pEOL - pStart);
+ pStart = pEOL + 1;
+ while (pStart && *pStart && *pStart <= ' ') {
+ pStart++;
+ len--;
+ }
+ if (*pStart)
+ pEOL = strchr(pStart, '\n');
+ else
+ pEOL = NULL;
+ }
+ if (len) {
+ strncpy(lineBuf, pStart, len);
+ lineBuf[len] = '\0';
+ //INFO("fragment:[%s]", lineBuf);
+ }
+ //INFO("write returns %d", origLen);
+ return origLen;
+}
+
+void HTTPiCal::setDataType(const char* type) {
+ //INFO("setDataType(%s)", type);
+}
+
+//void HTTPiCal::setLocation(const char * location) {
+//
+//}
+
+void HTTPiCal::setIsChunked(bool chunked) {
+ INFO("setIsChunked(%d)", chunked);
+ m_chunked = chunked;
+}
+
+void HTTPiCal::setDataLen(size_t len) {
+ //INFO("setDataLen(%d)", len);
+}
+
+
+const char * RPT_DAYS[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA", "" };
+
+int HTTPiCal::GetNumEvents(void)
+{
+ return EventCount;
+}
+
+const char * HTTPiCal::RepeatDayAbbrev(int i)
+{
+ if (i < 7)
+ return RPT_DAYS[i];
+ else
+ return RPT_DAYS[7];
+}
+
+void HTTPiCal::SortEvents()
+{
+ bool swapped;
+ int e;
+ Event_T Event;
+
+ do {
+ swapped = false;
+ for (e=0; e<EventCount-1; e++) {
+ if (EventList[e].Start > EventList[e+1].Start) {
+ Event = EventList[e];
+ EventList[e] = EventList[e+1];
+ EventList[e+1] = Event;
+ swapped = true;
+ }
+ }
+ } while (swapped);
+}
+
+uint16_t HTTPiCal::AtoIxN(const char * p, int n)
+{
+ uint16_t res = 0;
+
+ while (n--) {
+ res = (res * 10) + (*p - '0');
+ p++;
+ }
+ return res;
+}
+
+// YYYYMMDD[THHMMSS[Z]]
+// VALUE=DATE:YYYYMMDD
+time_t HTTPiCal::ParseDateStamp(const char * string, tz_sec_t tzoSec)
+{
+ time_t tStamp;
+ struct tm t;
+ struct tm * tnow;
+
+ time(&tStamp);
+ tnow = localtime(&tStamp);
+ if (strncmp(string, "VALUE=DATE:", 11) == 0) {
+ string += 11;
+ }
+ //INFO("ParseDateStamp(%s,%d)\n", string, tzoSec);
+ t.tm_year = AtoIxN(string, 4) - 1900;
+ t.tm_mon = AtoIxN(string+4, 2) - 1;
+ t.tm_mday = AtoIxN(string+6, 2);
+ if (strlen(string) > 8) {
+ t.tm_hour = AtoIxN(string+9, 2);
+ t.tm_min = AtoIxN(string+11, 2);
+ t.tm_sec = AtoIxN(string+13, 2);
+ t.tm_isdst = tnow->tm_isdst;
+ } else {
+ t.tm_hour = 0;
+ t.tm_min = 0;
+ t.tm_sec = 0;
+ t.tm_isdst = tnow->tm_isdst;
+ }
+ tStamp = mktime(&t);
+ if (string[strlen(string)-1] == 'Z') {
+ //INFO("Applying tzoSec %d", tzoSec);
+ tStamp = tStamp + tzoTZIDSec;
+ } else {
+ tStamp = tStamp + tzoSec;
+ }
+ return tStamp;
+}
+
+char * HTTPiCal::FormatCTime(time_t t)
+{
+ static char temp[4][80];
+ static int i = 0;
+
+ i &= 3;
+ strcpy(temp[i], ctime(&t));
+ temp[i][strlen(temp[i])-1] = '\0';
+ return temp[i++];
+}
+
+
+void HTTPiCal::ShowEventInfo(Event_T & Event)
+{
+ char scratch[80];
+ #define LF "\r\n"
+
+ printf("******* Summary: %s" LF, Event.Summary);
+ printf(" Location: %s" LF, Event.Location);
+ printf(" Category: %s" LF, Event.Category);
+ printf(" Priority: %d" LF, Event.Priority);
+ sprintf(scratch, "%lu ", Event.Start);
+ sprintf(scratch + strlen(scratch), "%s", (Event.Start == 0) ? "" : ctime(&Event.Start));
+ scratch[strlen(scratch)-1] = '\0';
+ printf(" Start: %s" LF, scratch);
+ sprintf(scratch, "%lu ", Event.End);
+ sprintf(scratch + strlen(scratch), "%s", (Event.End == 0) ? "" : ctime(&Event.End));
+ scratch[strlen(scratch)-1] = '\0';
+ printf(" End: %s" LF, scratch);
+ printf(" Count: %d" LF, Event.Count);
+ printf(" Interval: %d" LF, Event.Interval);
+ printf(" RepeatFrq: %d" LF, Event.RepeatFreq);
+ printf(" RepeatDay: %02X" LF, Event.RepeatDays);
+ printf(" RepeatMonthDay: %08X" LF, Event.RepeatMonthDay);
+ printf(" RepeatMonthDayRev: %08X" LF, Event.RepeatMonthDayRev);
+ printf(" RepeatMonth: %04X" LF, Event.RepeatMonths);
+ sprintf(scratch, "%lu ", Event.Until);
+ sprintf(scratch + strlen(scratch), "%s", (Event.Until == 0) ? "" : ctime(&Event.Until));
+ scratch[strlen(scratch)-1] = '\0';
+ printf(" Until: %s" LF, scratch);
+ printf("" LF);
+}
+
+
+/// Computes the intersection of time1 and time2 ranges, and modifies time1
+/// range to represent the intersection.
+///
+/// start1 is input as the start of the time1 range, and is written
+/// to represent the intersection of the two ranges.
+/// end1 is input as the end of the time1 range and is written to
+/// represent the intersection of the two ranges.
+/// start2 is the start of the time2 range.
+/// end2 is the end of the time2 range.
+/// returns true if the ranges have an intersection, and the time1 range
+/// values have been modified.
+///
+bool HTTPiCal::TimeIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2)
+{
+ // |----Time1----|
+ // |--Time2--| false
+ //
+ // |----Time1----|
+ // |--Time2--| false
+ //
+ // |----Time1----|
+ // |----Time2----|
+ // |-Time1-| true
+ //
+ // |----Time1----|
+ // |----Time2----|
+ // |-Time1-| true
+ //
+ // |----Time1-------|
+ // |-Time2-|
+ // |-Time1-| true
+ //
+ // | Time1 (end1 == 0)
+ // | Time2 (end2 == 0) true
+ //
+ if (*start1 == *start2 && *end1 == 0 && *end2 == 0)
+ return true;
+ if (*end1 < *start2 || *end2 < *start1)
+ return false;
+ if (max(*start1,*start2) < min(*end1,*end2)) {
+ *start1 = max(*start1,*start2);
+ *end1 = min(*end1,*end2);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool HTTPiCal::isLeapYear(time_t t)
+{
+ int year;
+ struct tm * ts;
+ ts = localtime(&t);
+
+ year = 1900 + ts->tm_year + 1;
+ if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0))
+ return true;
+ else
+ return false;
+}
+
+time_t HTTPiCal::NextInterval(time_t curTime, int repeatFreq, int interval)
+{
+ const time_t secperday = 60*60*24;
+ const int repeatFactor[] = {0, 1, 7, 30, 365};
+ int delta = repeatFactor[repeatFreq];
+ if (repeatFreq == 4 && isLeapYear(curTime))
+ delta += 1;
+ //INFO("freq %d, interval %d, delta %d", repeatFreq, interval, delta);
+ return delta * interval * secperday;
+}
+
+
+// start1,end1 is the time range representing the visible grid
+// start2,end2 is the time range of the event being tested
+// Event is also the event being tested and permits testing the repeat information.
+//
+// If the event repeat pattern intersects with the display pattern, indicate this as "true"
+//
+bool HTTPiCal::RepeatMaskIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T * Event)
+{
+ bool intersects = false;
+
+ //INFO("RepeatFreq: %d", Event->RepeatFreq);
+ if (Event->RepeatFreq == rptfDaily) {
+ //INFO("rptfDaily is not handled");
+ } else if (Event->RepeatFreq == rptfWeekly) {
+ struct tm * timeinfo;
+ timeinfo = localtime(start1);
+ uint8_t daymask = Event->RepeatDays;
+ // now, check the tm_wday (0=Sunday, 1=Monday, ...) and see if we intersect with the event time
+ uint8_t testmask = 1 << timeinfo->tm_wday;
+ //INFO("Mask: Event mask: %02X, test mask: %02X", daymask, testmask);
+ if (daymask & testmask)
+ intersects = true;
+ else
+ intersects = false;
+ //INFO(" intersects: %02X", daymask & testmask);
+ return intersects;
+ } else if (Event->RepeatFreq == rptfYearly) {
+ //struct tm * timeinfo;
+ //timeinfo = localtime(start1);
+ //INFO("rptfYearly is not handled well yet");
+ }
+ //INFO("Mask: no handler, returning true");
+ return true;
+}
+
+bool HTTPiCal::RepeatIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T * Event)
+{
+ INFO("** 1: (%s, %s)", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "");
+ INFO(" 2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
+ INFO(" ev: (%s, %s)", FormatCTime(Event->Start), Event->End ? FormatCTime(Event->End) : "");
+ if (TimeIntersects(start1, end1, start2, end2))
+ return true;
+ if (Event && Event->RepeatFreq) {
+ INFO("RepeatFreq: %d", Event->RepeatFreq);
+ if (Event->Start < *start2 && Event->Until > *start2 ) { // Until=....
+ INFO("Repeat until: %d", Event->Until);
+ do {
+ time_t interval = NextInterval(*start1, Event->RepeatFreq, (Event->Interval == 0) ? 1 : Event->Interval);
+ *start1 = *start1 + interval;
+ if (*end1)
+ *end1 = *end1 + interval;
+ INFO("** 1: (%s, %s)", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "");
+ INFO("until (%24s, %s)", " ", FormatCTime(Event->Until));
+ INFO(" 2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
+ if (!RepeatMaskIntersects(start1, end1, start2, end2, Event)) {
+ continue; // we're not on a repeat cycle (e.g. wrong day of the week)
+ }
+ if (TimeIntersects(start1, end1, start2, end2)) {
+ return true;
+ }
+ } while ((*end2 == 0 || *start1 < *end2) && *start1 < Event->Until);
+ } else if (Event->Start < *start2 && Event->Count) { // Count=
+ INFO("Repeat count %d", Event->Count);
+ int count = Event->Count - 1;
+ do {
+ time_t interval = NextInterval(*start1, Event->RepeatFreq, (Event->Interval == 0) ? 1 : Event->Interval);
+ *start1 = *start1 + interval;
+ if (*end1)
+ *end1 = *end1 + interval;
+ INFO("** 1: (%s, %s) - %d", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "", count);
+ INFO(" 2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
+ if (!RepeatMaskIntersects(start1, end1, start2, end2, Event)) {
+ continue; // we're not on a repeat cycle (e.g. wrong day of the week)
+ }
+ if (TimeIntersects(start1, end1, start2, end2)) {
+ return true;
+ }
+ } while (--count && *end1 < *start2);
+ } else if (Event->Start < *start2) { // no Count= and no Until=
+ INFO(" no Repeat end");
+ do {
+ int rptFreq = Event->RepeatFreq;
+ if (Event->RepeatFreq == 2 && Event->RepeatDays != 0)
+ rptFreq--;
+ time_t interval = NextInterval(*start1, rptFreq, (Event->Interval == 0) ? 1 : Event->Interval);
+ *start1 = *start1 + interval;
+ if (*end1)
+ *end1 = *end1 + interval;
+ INFO("== 1: (%s, %s)", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "");
+ INFO(" 2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
+ if (!RepeatMaskIntersects(start1, end1, start2, end2, Event)) {
+ continue; // we're not on a repeat cycle (e.g. wrong day of the week)
+ }
+ if (TimeIntersects(start1, end1, start2, end2)) {
+ return true;
+ }
+ } while (*start1 < *end2 || (*end2 == 0 && *start1 < *start2));
+ } else {
+ INFO("falling out");
+ }
+ }
+ INFO(" no intersection");
+ return false;
+}
+
+// All the stuff between
+// BEGIN:VEVENT
+// ...
+// END:VEVENT
+//
+void HTTPiCal::ParseEvent(Event_T * Event, const char * pStart, tz_sec_t tzoSec)
+{
+ INFO("ParseEvent(...,'%s',%d)", pStart, tzoSec);
+ if (strncmp(pStart, "DTSTART:", 8) == 0) {
+ Event->Start = ParseDateStamp(pStart+8, tzoSec);
+ INFO(" Start: %s\n", ctime(&Event->Start));
+ } else if (strncmp(pStart, "DTSTART;", 8) == 0) {
+ const char * p = pStart + 8;
+ tzoSec = ParseTZID(p);
+ p = strrchr(pStart, ':');
+ if (p) {
+ Event->Start = ParseDateStamp(p+1, 0); // example was localtime with GMT -06:00 tzoSec);
+ INFO(" Start: %s", ctime(&Event->Start));
+ }
+ } else if (strncmp(pStart, "DTEND:", 6) == 0) {
+ Event->End = ParseDateStamp(pStart+6, tzoSec);
+ //INFO(" End: %d\n", mktime(&Event->eventEnd));
+ } else if (strncmp(pStart, "DTEND;", 6) == 0) {
+ const char * p = pStart + 6;
+ tzoSec = ParseTZID(p);
+ p = strrchr(pStart, ':');
+ if (p) {
+ Event->End = ParseDateStamp(p+1, 0); // example was localtime with GMT -06:00 tzoSec);
+ INFO(" End: %s", ctime(&Event->End));
+ }
+ } else if (strncmp(pStart, "SUMMARY:", 8) == 0) {
+ strncpy(Event->Summary, pStart+8, SUMMARY_CHARS-1);
+ Event->Summary[SUMMARY_CHARS-1] = '\0';
+ //INFO(" Summary: %s\n", Event->Summary);
+ } else if (strncmp(pStart, "LOCATION:", 9) == 0) {
+ strncpy(Event->Location, pStart+9, LOCATION_CHARS-1);
+ Event->Location[LOCATION_CHARS-1] = '\0';
+ //INFO(" Location: %s\n", Event->Location);
+ } else if (strncmp(pStart, "PRIORITY:", 9) == 0) {
+ Event->Priority = *(pStart+9) - '0';
+ //INFO(" Priority: %d\n", Event->Priority);
+ } else if (strncmp(pStart, "CATEGORIES:", 11) == 0) {
+ strncpy(Event->Category, pStart+11, CATEGORY_CHARS-1);
+ Event->Category[CATEGORY_CHARS-1] = '\0';
+ //INFO(" Category: %s\n", Event->Category);
+ } else if (strncmp(pStart, "RRULE:", 6) == 0) {
+ //RRULE:FREQ=WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR
+ const char * p1, *p2;
+ //INFO("%s", pStart);
+ p1 = pStart + 6; // p1 = FREQ=WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR
+ p2 = strchr(p1, ';');
+ //if (p2)
+ // *p2++ = '\0';
+ while (*p1) {
+ INFO("%s", p1);
+ if (strncmp(p1, "FREQ=", 5) == 0) {
+ //INFO("%s", p1);
+ p1 += 5; // p1 = WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR
+ if (strncmp(p1, "WEEKLY", 6) == 0) {
+ //INFO(" %s", p1);
+ Event->RepeatFreq = rptfWeekly;
+ p1 += 6;
+ } else if (strncmp(p1, "DAILY", 5) == 0) {
+ //INFO(" %s", p1);
+ Event->RepeatFreq = rptfDaily;
+ p1 += 5;
+ } else if (strncmp(p1, "MONTHLY", 7) == 0) {
+ //INFO(" %s", p1);
+ Event->RepeatFreq = rptfMonthly;
+ p1 += 7;
+ } else if (strncmp(p1, "YEARLY", 6) == 0) {
+ //INFO(" %s", p1);
+ Event->RepeatFreq = rptfYearly;
+ p1 += 6;
+ }
+ } else if (strncmp(p1, "INTERVAL=", 9) == 0) { // INTERVAL=2
+ //INFO("%s", p1);
+ p1 += 9;
+ Event->Interval = atoi(p1);
+ } else if (strncmp(p1, "COUNT=", 6) == 0) { // COUNT=12;
+ //INFO("%s", p1);
+ p1 += 6; // p1 =
+ Event->Count = atoi(p1);
+ } else if (strncmp(p1, "UNTIL=", 6) == 0) {
+ //INFO("%s", p1);
+ p1 += 6; // p1 = 20140502T180000;BYDAY=MO,TU,WE,TH,FR
+ //printf("UNTIL= {%s}\n", p1);
+ Event->Until = ParseDateStamp(p1, tzoSec);
+ //printf("UNTIL:: %d: %d\n", Event->Until, tzoSec);
+ } else if (strncmp(p1, "BYDAY=", 6) == 0) {
+ //INFO("%s", p1);
+ p1 += 6; // p1 = MO,TU,WE,TH,FR
+ while (*p1 >= ' ') {
+ //INFO(" %s", p1);
+ for (int d=0; d<7; d++) {
+ if (strncmp(p1,RepeatDayAbbrev(d),2) == 0) {
+ Event->RepeatDays |= (1 << d);
+ //INFO(" %s %02X", RepeatDayAbbrev(d), Event->RepeatDays);
+ break;
+ }
+ }
+ p1 += 3;
+ }
+ //INFO(" RepeatDay: %02X", Event->RepeatDays);
+ } else if (strncmp(p1, "BYMONTHDAY=", 11) == 0) {
+ // RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
+ p1 += 11;
+ while (*p1 >= ' ') {
+ const char * px = p1;
+ while (*px >= ' ' && *px != ',') { // find , or ; or <nul>
+ px++;
+ }
+ //if (*px)
+ // *px++ = '\0';
+ int num = atoi(p1);
+ if (num >= 0)
+ Event->RepeatMonthDay |= (1 << num);
+ else
+ Event->RepeatMonthDayRev |= (1 << -num);
+ p1 = px;
+ }
+ INFO(" RepeatMonthDay: %08X", Event->RepeatMonthDay);
+ } else if (strncmp(p1, "BYMONTH=", 8) == 0) {
+ // RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
+ p1 += 8;
+ while (*p1 >= ' ') {
+ const char * px = p1;
+ while (*px >= ' ' && *px != ',') { // find , or ; or <nul>
+ px++;
+ }
+ //if (*px)
+ // *px++ = '\0';
+ int num = atoi(p1);
+ if (num >= 0)
+ Event->RepeatMonths |= (1 << num);
+ //else
+ // ; // Event->RepeatMonthsRev |= (1 << -num);
+ p1 = px;
+ }
+ INFO(" RepeatMonths: %04X", Event->RepeatMonths);
+ }
+ if (!p2)
+ break;
+ p1 = p2 + 1;
+ p2 = strchr(p1, ';');
+ //if (p2)
+ // *p2++ = '\0';
+ }
+ }
+}
+
+
+// TZID="(GMT -06:00)":20140519T063000
+// TZID:(UTC-06:00) Central Time (US & Canada)
+// TZID:(GMT -06:00)
+HTTPiCal::tz_sec_t HTTPiCal::ParseTZID(const char * string)
+{
+ tz_sec_t tzo = 0;
+ bool sign = false;
+
+ INFO("ParseTZID(%s)", string); // TZID="(GMT -06:00)":20140519T063000
+ string += 5; // "(GMT -06:00)":20140519T063000
+ if (*string == '"')
+ string++; // (GMT -06:00)":20140519T063000
+ if ((strncmp(string, "(UTC", 4) == 0)
+ || (strncmp(string, "(GMT", 4) == 0) ){
+ string += 4;
+ if (*string == ' ')
+ string++;
+ if (*string == '-') {
+ sign = true;
+ string++;
+ }
+ tzo = atoi(string) * 3600;
+ string = strchr(string, ':');
+ if (string) {
+ string++;
+ tzo += atoi(string) * 60;
+ }
+ if (sign)
+ tzo = -tzo;
+ INFO(" tzo = %d", tzo);
+ } else {
+ ERR("Unhandled TZID(%s)", string);
+ }
+ return tzo;
+}
+
+void HTTPiCal::ParseICalStart(void) {
+ tzAdjusted = false;
+ seeking = idle;
+ EventCount = 0;
+
+}
+
+int HTTPiCal::ParseICalClose(void) {
+ if (EventCount > 0)
+ SortEvents();
+ return GetNumEvents();
+}
+
+int HTTPiCal::ParseICalStream(const char * pStart, time_t gridStartTime, time_t gridEndTime, tz_min_t tzoMin, bool showEvents)
+{
+ INFO("Parse(%s)", pStart);
+ //INFO("EventCount: %d, EventSpaceCount: %d, seeking: %d", EventCount, EventSpaceCount, seeking);
+ if (pStart && *pStart && EventCount < EventSpaceCount) {
+ switch (seeking) {
+ case idle:
+ if (strncmp(pStart, "BEGIN:VTIMEZONE", 15) == 0) {
+ //INFO("begin:timezone");
+ seeking = inTimeZone;
+ } else if (strncmp(pStart, "BEGIN:VEVENT", 12) == 0) {
+ //INFO("begin:vevent");
+ seeking = inEvent;
+ EventList[EventCount].Start = 0;
+ EventList[EventCount].End = 0;
+ EventList[EventCount].Until = 0;
+ EventList[EventCount].Summary[0] = '\0';
+ EventList[EventCount].Location[0] = '\0';
+ EventList[EventCount].Category[0] = '\0';
+ EventList[EventCount].Count = 0;
+ EventList[EventCount].Interval = 0;
+ EventList[EventCount].RepeatFreq = rptfNone;
+ EventList[EventCount].RepeatDays = 0;
+ EventList[EventCount].RepeatMonthDay = 0;
+ EventList[EventCount].RepeatMonthDayRev = 0;
+ EventList[EventCount].RepeatMonths = 0;
+ EventList[EventCount].Priority = 5; // 5 is Normal
+ }
+ break;
+ case inTimeZone:
+ //INFO("inTimeZone:");
+ // Can also pick up daylight savings time
+ if (strncmp(pStart, "END:VTIMEZONE", 13) == 0) {
+ seeking = idle;
+ } else if ((strncmp(pStart, "TZID:", 5) == 0)
+ || (strncmp(pStart, "TZID=", 5) == 0) ) {
+ tzoTZIDSec = ParseTZID(pStart);
+ tzAdjusted = true;
+ pStart += 5;
+ } else if (strncmp(pStart, "BEGIN:STANDARD", 14) == 0) {
+
+ } else if (strncmp(pStart, "BEGIN:DAYLIGHT", 14) == 0) {
+
+ }
+ break;
+ case inEvent:
+ //INFO("inEvent:");
+ // inEvent
+ if (strncmp(pStart, "END:VEVENT", 10) == 0) {
+ // Timezone offset
+ if (!tzAdjusted) {
+ EventList[EventCount].Start += (60 * tzoMin);
+ if (EventList[EventCount].End)
+ EventList[EventCount].End += (60 * tzoMin);
+ }
+ // Process it
+ if (showEvents)
+ ShowEventInfo(EventList[EventCount]);
+ // Force to ALWAYS
+ time_t aStart = EventList[EventCount].Start;
+ time_t aEnd = EventList[EventCount].End;
+ if (gridStartTime
+ && RepeatIntersects(&aStart, &aEnd, &gridStartTime, &gridEndTime, &EventList[EventCount])) {
+ EventCount++;
+ if (showEvents) {
+ INFO(" +++++ Added Event %d", EventCount);//, EventList[EventCount].Summary);
+ }
+ }
+ seeking = idle;
+ } else {
+ //INFO("parse event data");
+ ParseEvent(&EventList[EventCount], pStart, 60 * tzoMin);
+ }
+ // End of inEvent
+ break;
+ default:
+ INFO("default:");
+ seeking = idle;
+ break;
+ }
+ } // while
+ return EventCount;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/data/HTTPiCal.h Fri Mar 10 02:53:09 2017 +0000
@@ -0,0 +1,361 @@
+#ifndef HTTPICAL_H
+#define HTTPICAL_H
+#include <mbed.h>
+#include "../IHTTPData.h"
+
+
+/// An iCal handling mechanism - downloads and parses calendar events
+class HTTPiCal : public IHTTPDataIn {
+
+ public:
+ #define SUMMARY_CHARS 100
+ #define LOCATION_CHARS 100
+ #define CATEGORY_CHARS 20
+ #define LINEBUFLEN 200
+
+ /// The repeat attribute for an event
+ typedef enum {
+ rptfNone, ///< no repeat for this event
+ rptfDaily, ///< daily repeat
+ rptfWeekly, ///< weekly repeat
+ rptfMonthly, ///< monthly repeat
+ rptfYearly ///< yearly repeat
+ } RepeatFreq_t;
+
+ typedef int32_t tz_sec_t;
+ typedef int16_t tz_min_t;
+
+ /// A single event consists of quite a number of attributes.
+ typedef struct {
+ time_t Start;
+ time_t End;
+ time_t Until;
+ uint16_t Count;
+ uint16_t Interval;
+ RepeatFreq_t RepeatFreq;
+ uint8_t RepeatDays; // bit mapped (bit 0 = sunday, bit 1=monday, ...)
+ uint16_t RepeatMonths; // bit mapped (bit 0 = jan, 1=feb, ...)
+ uint32_t RepeatMonthDay; // bit mapped (bit 1 = 1st, 2=2nd, ...)
+ uint32_t RepeatMonthDayRev; // reverse -1 = last day = bit 1, -2=bit 2, ...
+ char Summary[SUMMARY_CHARS];
+ char Location[LOCATION_CHARS];
+ char Category[CATEGORY_CHARS]; // "Green", ...
+ int Priority; // 1 == High, 5 == Normal, 9 == Low
+ } Event_T;
+
+
+ /// Instantiate HTTPiCal
+ ///
+ /// @code
+ /// HTTPClient http;
+ /// HTTPiCal iCal(10); // define a limit of 10 events to be held
+ ///
+ /// http.basicAuth(calInfo.user, calInfo.pass);
+ ///
+ /// time_t now = t.timelocal(); // Set a 4-hour window from now
+ /// time_t nxt = now + 4 * 3600;
+ /// ical.SetTimeWindow(now, nxt);
+ /// HTTPErrorCode = http.get(calInfo.url, &iCal);
+ /// if (HTTPErrorCode == HTTP_OK) {
+ /// // calendar successfully downloaded
+ /// for (int i=0; i<iCal.GetEventCount(); i++) {
+ /// HTTPiCal::Event_T event;
+ /// if (ical.GetEvent(i, &event)) {
+ /// printf("Event %d\r\n", i);
+ /// printf("Summary : %s\r\n", event.Summary);
+ /// printf("Location: %s\r\n", event.Location);
+ /// printf("Category: %s\r\n", event.Category);
+ /// }
+ /// }
+ /// }
+ /// @endcode
+ ///
+ /// @param count is the number of Event_T entries to reserve space for.
+ ///
+ HTTPiCal(int count);
+
+ /// Destructor to free memory
+ ~HTTPiCal();
+
+ /// Set the time window of interest, for which to retain events.
+ ///
+ /// This sets the time window of interest. Any event, whether directly
+ /// scheduled in this window, or indirectly via repeat attributes, will
+ /// be retained in the list of available events. Any event not in this
+ /// window will be ignored.
+ ///
+ /// @param StartTime is the optional time value for the beginning of
+ /// interest. If gridStartTime is zero, no filtering is performed.
+ /// @param EndTime is the optional time value ending the period of interest.
+ ///
+ void SetTimeWindow(time_t StartTime = 0, time_t EndTime = 0);
+
+ /// Get the count of Events currently available.
+ ///
+ /// @returns the count of events.
+ ///
+ int GetEventCount(void) { return EventCount; }
+
+ /// Get a copy of the specified event.
+ ///
+ /// @param i is the event number, ranging from 0 to GetEventCount()
+ /// @param event is a pointer to where the event will be copied.
+ /// @returns true if the event was available and copied.
+ ///
+ bool GetEvent(unsigned int i, Event_T * event);
+
+ protected:
+
+ friend class HTTPClient;
+
+ /** Reset stream to its beginning
+ * Called by the HTTPClient on each new request
+ */
+ virtual void writeReset();
+
+ /** Write a piece of data transmitted by the server
+ * @param[in] buf Pointer to the buffer from which to copy the data
+ * @param[in] len Length of the buffer
+ * @returns number of bytes written.
+ */
+ virtual int write(const char* buf, size_t len);
+
+ /** Set MIME type
+ * @param[in] type Internet media type from Content-Type header
+ */
+ virtual void setDataType(const char* type);
+
+ /** Determine whether the data is chunked
+ * Recovered from Transfer-Encoding header
+ * @param[in] chunked indicates the transfer is chunked.
+ */
+ virtual void setIsChunked(bool chunked);
+
+ /** If the data is not chunked, set its size
+ * From Content-Length header
+ * @param[in] len defines the size of the non-chunked transfer.
+ */
+ virtual void setDataLen(size_t len);
+
+ private:
+ char lineBuf[LINEBUFLEN]; ///< workspace to copy [partial] lines into
+
+ Event_T * EventList; ///< Pointer to the array of events
+ int EventSpaceCount; ///< Maximum number of events that can be tracked
+ int EventCount; ///< Current Count of events
+
+ time_t gridStartTime; ///< defines the start of the time window of interest
+ time_t gridEndTime; ///< defines the end of the time winedow of interest
+
+ int32_t tzoTZIDSec;
+ bool tzAdjusted;
+
+ typedef enum {
+ idle,
+ inTimeZone,
+ inEvent
+ } seekstate_t;
+ seekstate_t seeking;
+
+ #define min(a,b) (((a)<(b))?(a):(b))
+ #define max(a,b) (((a)>(b))?(a):(b))
+
+ /// Determine if a specific timestamp is in a leap year
+ ///
+ /// @param[in] t is the timestamp to evaluate
+ /// @returns true if the specified timestamp is within a leap year
+ ///
+ bool isLeapYear(time_t t);
+
+ /// Inspect the event to determine if its repeat attribute places it in the specified time window
+ ///
+ /// @param[in] start1 represents the start of the timerange of interest.
+ /// @param[in] end1 is the time range representing the end of the range of interest
+ /// @param[in] start2 is the time range of the event being tested
+ /// @param[in] end2 is the time range of the event being tested
+ /// @param[in] Event is a pointer to the event being tested and permits testing the repeat information.
+ /// @returns true if the event repeat pattern intersects with the display pattern
+ ///
+ bool RepeatMaskIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T * Event);
+
+ /// Convert 'n' characters in a string to an unsigned integer.
+ ///
+ /// @param[in] p is a pointer to the string.
+ /// @param[in] n is the number of characters to convert.
+ /// @returns an unsigned integer of the converted value.
+ ///
+ uint16_t AtoIxN(const char * p, int n);
+
+ /// Parse some information from the stream and extract event information.
+ ///
+ /// @param[in] Event is a pointer to the current event to populate.
+ /// @param[in] pStart is a pointer to the start of a text stream to evaluate.
+ /// @param[in] tzoSec is the time zone offset in seconds.
+ ///
+ void ParseEvent(Event_T * Event, const char * pStart, tz_sec_t tzoSec);
+
+
+ /// Prepare to start processing an iCal stream
+ ///
+ /// This initializes the data structures for parsing.
+ ///
+ /// @code
+ /// ParseICalStart();
+ /// while (receive(buf, ... ))
+ /// ParseICalStream(buf, ...);
+ /// count = ParseICalClose();
+ /// @endcode
+ ///
+ void ParseICalStart(void);
+
+
+ /// End processing an iCal stream and return the number of events
+ ///
+ /// @returns number of events in range
+ ///
+ int ParseICalClose(void);
+
+
+ /// Parse an iCal stream, and extract everything useful.
+ ///
+ /// This accepts a pointer to a [large] buffer, which is the contents
+ /// of an iCal stream. It walked through all of the available
+ /// information to extract the Event list.
+ ///
+ /// @param[in] pStart is a pointer to the start of the stream.
+ /// @param[in] gridStartTime is a time value representing the start of the time-window of interest.
+ /// @param[in] gridEndTime is a time value representing the end of the time-window of interest.
+ /// @param[in] tzoMin is the time-zone offset in minutes.
+ /// @param[in] showEvents when true causes it to print the events as parsed.
+ /// @returns number of events in range.
+ ///
+ int ParseICalStream(const char * pStart, time_t gridStartTime, time_t gridEndTime, tz_min_t tzoMin, bool showEvents = true);
+
+
+ /// Get the number of events that have been cached.
+ ///
+ /// @returns the number of events.
+ ///
+ int GetNumEvents(void);
+
+ /// Compute if the referenced event occurs within the specified time window.
+ ///
+ /// @param[in] startWindow is the starting timestamp.
+ /// @param[in] endWindow is the ending timestamp.
+ /// @param[in] Event is a pointer to the event, which can have repeat rules.
+ /// @returns true if the event laps into the current time window.
+ ///
+ bool EventIntersects(time_t startWindow, time_t endWindow, Event_T * Event);
+
+ /// Compute the intersection of two time ranges, and evaluate the recurringing events.
+ ///
+ /// This compares a pair of time ranges, each by a start and end time. If they overlap
+ /// it then computes the intersection of those two ranges. Additionally, for a
+ /// specified Event, it will evaluate the recurring events that may also fall into
+ /// the target time range.
+ ///
+ /// @note This is usually the only API you need, as this will first call
+ /// the TimeIntersects function, and if that fails, then it will evaluate
+ /// repeat information.
+ ///
+ /// @param[in,out] start1 is the starting time of range 1.
+ /// @param[in,out] end1 is the ending time of range 1.
+ /// @param[in] start2 is the starting time of range 2.
+ /// @param[in] end2 is the ending time of range 2.
+ /// @param[in] Event is a pointer to the event of interest that may have recurring series.
+ /// @returns true if the ranges overlap, and then start1 and end1 are set to the
+ /// intersection.
+ ///
+ bool RepeatIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T * Event = NULL);
+
+
+ /// Compute the intersection of two time ranges, and returns that intersection.
+ ///
+ /// This compares a pair of time ranges, each by a start and end time. If they overlap
+ /// it then computes the intersection of those two ranges.
+ ///
+ /// @param[in,out] start1 is the starting time of range 1.
+ /// @param[in,out] end1 is the ending time of range 1.
+ /// @param[in] start2 is the starting time of range 2.
+ /// @param[in] end2 is the ending time of range 2.
+ /// @returns true if the ranges overlap, and then start1 and end1 are set to the
+ /// intersection.
+ ///
+ bool TimeIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2);
+
+
+ // Private Functions - no real value external to the iCal public interface
+ // other than for test code.
+
+ /// Computes the next interval for iCal events that have recurrence.
+ ///
+ /// @param[in] baseT is the base time value which is a factor only in leap-year.
+ /// @param[in] repeatFreq is a value representing the frequency of recurrence -
+ /// 0=none, 1=daily, 2=weekly, 3=monthly, 4=yearly
+ /// @param[in] interval is the multiplier of that repeat frequency to the next
+ /// event.
+ /// @returns a time_t value which is the incremental interval, and would be added
+ /// to a reference time.
+ ///
+ time_t NextInterval(time_t baseT, int repeatFreq, int interval);
+
+
+ /// Format a ctime value as a string, without the trailing <cr>
+ ///
+ /// This uses the normal ctime function, which appends a <cr>, but
+ /// it then removes that trailing line ending character. Additionally,
+ /// this keeps a few local static buffers to return the time string in
+ /// which permits a printf (for example) to call this api a few times
+ /// and get the proper representation of each.
+ ///
+ /// @param[in] t is a time value;
+ /// @returns a pointer to a static buffer containing the converted time.
+ ///
+ char * FormatCTime(time_t t);
+
+ /// Sort the Events that have been extracted from the iCal results.
+ ///
+ void SortEvents();
+
+ /// Show the details for a specific Event, on the specified serial stream.
+ ///
+ /// Most useful during development, and perhaps no value after that.
+ ///
+ /// @param[in] Event is the event of interest.
+ ///
+ void ShowEventInfo(Event_T & Event);
+
+ /// Access the 2-letter abbreviation for the day of the week.
+ ///
+ /// @param[in] i is the day of the week, where 0 = sunday
+ /// @returns a pointer to the 2-letter abbreviation.
+ ///
+ const char * RepeatDayAbbrev(int i);
+
+ /// Parse a Datestamp string into a time value.
+ ///
+ /// Parses a string which can look like this: 20140505T200000
+ /// into a time_t value.
+ ///
+ /// @param[in] string is the string to parse.
+ /// @param[in] tzoSec is the time-zone offset in seconds.
+ /// @returns time_t value.
+ ///
+ time_t ParseDateStamp(const char * string, tz_sec_t tzoSec);
+
+ /// Parse a Time Zone ID value from the front-end of a Datestamp
+ ///
+ /// Parses a string which can look like one of these:
+ /// @li TZID="(GMT -06:00)":20140519T063000
+ /// @li TZID:(UTC-06:00) Central Time (US & Canada)
+ /// @li TZID:(GMT -06:00)
+ ///
+ /// @param[in] string to be parsed.
+ /// @returns time zone offset in seconds.
+ ///
+ tz_sec_t ParseTZID(const char * string);
+
+
+ bool m_chunked;
+};
+#endif // HTTPICAL_H
\ No newline at end of file
