Fixed custom headers and Basic authorization, added support for redirection, functional file download interface can be used for SW updates and more.
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