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.
data/HTTPiCal.cpp
- Committer:
- WiredHome
- Date:
- 2017-03-10
- Revision:
- 39:21fc7a4b6927
- Child:
- 40:bcb19f8dbba3
File content as of revision 39:21fc7a4b6927:
#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; }