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 Vincent Wochnik

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPiCal.cpp Source File

HTTPiCal.cpp

00001 #include "HTTPiCal.h"
00002 
00003 //#include "Utility.h"            // private memory manager
00004 #ifndef UTILITY_H
00005 #define swMalloc malloc         // use the standard
00006 #define swFree free
00007 #endif
00008 
00009 //#define DEBUG "iCal"
00010 #include <cstdio>
00011 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
00012 #define DBG(x, ...)  std::printf("[DBG %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00013 #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00014 #define ERR(x, ...)  std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00015 #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00016 #else
00017 #define DBG(x, ...)
00018 #define WARN(x, ...)
00019 #define ERR(x, ...)
00020 #define INFO(x, ...)
00021 #endif
00022 
00023 HTTPiCal::HTTPiCal(int count) {
00024     EventList = (Event_T *)swMalloc(count * sizeof(Event_T));
00025     if (EventList) {
00026         EventSpaceCount = count;
00027         EventCount = 0;
00028         seeking = idle;
00029     } else {
00030         error("no space for event list");
00031     }
00032 }
00033 
00034 HTTPiCal::~HTTPiCal() {
00035     if (EventList)
00036         swFree(EventList);
00037 }
00038 
00039 void HTTPiCal::SetTimeWindow(time_t StartTime, time_t EndTime) {
00040     gridStartTime = StartTime;
00041     gridEndTime = EndTime;
00042 }
00043 
00044 bool HTTPiCal::GetEvent(unsigned int i, Event_T * event) {
00045     if (i < EventCount) {
00046         *event = EventList[i];
00047         return true;
00048     } else {
00049         return false;
00050     }
00051 }
00052 
00053 void HTTPiCal::writeReset() {
00054     //INFO("writeReset()");
00055     EventCount = 0;
00056     seeking = idle;
00057     lineBuf[0] = '\0';
00058 }
00059 
00060 int HTTPiCal::write(const char * buf, size_t len) {
00061     const char * pStart = buf;
00062     const char * pEOL;
00063     size_t origLen = len;
00064         
00065     pEOL = strchr(pStart, '\n');
00066     //INFO("\r\n\r\nwrite[%d:%d] = \r\n%s\r\nend-write\r\n", len, pEOL - pStart, buf);
00067     //printf("lineBuf:[%d] %s\r\n", len, buf);
00068     while (pEOL && (pEOL - pStart) < len) {
00069         int lbLen = strlen(lineBuf);
00070         strncpy(lineBuf + lbLen, pStart, (pEOL - pStart));
00071         lineBuf[lbLen + (pEOL - pStart) + 1] = '\0';
00072         if (lineBuf[pEOL - pStart + lbLen - 1] == '\r')
00073             lineBuf[pEOL - pStart + lbLen - 1] = '\0';
00074         //printf("lineBuf:[%s]\r\n", lineBuf);
00075         ParseICalStream(lineBuf, gridStartTime, gridEndTime, -300);
00076         //INFO("");
00077         lineBuf[0] = '\0';
00078         len -= (pEOL - pStart);
00079         pStart = pEOL + 1;
00080         while (pStart && *pStart && *pStart <= ' ') {
00081             pStart++;
00082             len--;
00083         }
00084         if (*pStart)
00085             pEOL = strchr(pStart, '\n');
00086         else
00087             pEOL = NULL;
00088     }
00089     if (len) {
00090         strncpy(lineBuf, pStart, len);
00091         lineBuf[len] = '\0';
00092         //INFO("fragment:[%s]", lineBuf);
00093     }
00094     //INFO("write returns %d", origLen);
00095     return origLen;
00096 }
00097 
00098 void HTTPiCal::setDataType(const char* type) {
00099     //INFO("setDataType(%s)", type);
00100 }
00101 
00102 //void HTTPiCal::setLocation(const char * location) {
00103 //
00104 //}
00105 
00106 void HTTPiCal::setIsChunked(bool chunked) {
00107     INFO("setIsChunked(%d)", chunked);
00108     m_chunked = chunked;
00109 }
00110 
00111 void HTTPiCal::setDataLen(size_t len) {
00112     //INFO("setDataLen(%d)", len);
00113 }
00114 
00115 
00116 static const char * RPT_DAYS[] = { "SU", "MO", "TU", "WE", "TH", "FR", "SA", "" };
00117 
00118 
00119 const char * HTTPiCal::RepeatDayAbbrev(int i)
00120 {
00121     if (i < 7)
00122         return RPT_DAYS[i];
00123     else
00124         return RPT_DAYS[7];
00125 }
00126 
00127 void HTTPiCal::SortEvents()
00128 {
00129     bool swapped;
00130     int e;
00131     Event_T Event;
00132 
00133     do  {
00134         swapped = false;
00135         for (e=0; e<EventCount-1; e++) {
00136             if (EventList[e].Start > EventList[e+1].Start) {
00137                 Event = EventList[e];
00138                 EventList[e] = EventList[e+1];
00139                 EventList[e+1] = Event;
00140                 swapped = true;
00141             }
00142         }
00143     } while (swapped);
00144 }
00145 
00146 uint16_t HTTPiCal::AtoIxN(const char * p, int n)
00147 {
00148     uint16_t res = 0;
00149 
00150     while (n--) {
00151         res = (res * 10) + (*p - '0');
00152         p++;
00153     }
00154     return res;
00155 }
00156 
00157 // YYYYMMDD[THHMMSS[Z]]
00158 // VALUE=DATE:YYYYMMDD
00159 time_t HTTPiCal::ParseDateStamp(const char * string, tz_sec_t tzoSec)
00160 {
00161     time_t tStamp;
00162     struct tm t;
00163     struct tm * tnow;
00164 
00165     time(&tStamp);
00166     tnow = localtime(&tStamp);
00167     if (strncmp(string, "VALUE=DATE:", 11) == 0) {
00168         string += 11;
00169     }
00170     //INFO("ParseDateStamp(%s,%d)\n", string, tzoSec);
00171     t.tm_year = AtoIxN(string, 4) - 1900;
00172     t.tm_mon  = AtoIxN(string+4, 2) - 1;
00173     t.tm_mday = AtoIxN(string+6, 2);
00174     if (strlen(string) > 8) {
00175         t.tm_hour = AtoIxN(string+9, 2);
00176         t.tm_min  = AtoIxN(string+11, 2);
00177         t.tm_sec  = AtoIxN(string+13, 2);
00178         t.tm_isdst = tnow->tm_isdst;
00179     } else {
00180         t.tm_hour = 0;
00181         t.tm_min = 0;
00182         t.tm_sec = 0;
00183         t.tm_isdst = tnow->tm_isdst;
00184     }
00185     tStamp = mktime(&t);
00186     if (string[strlen(string)-1] == 'Z') {
00187         //INFO("Applying tzoSec %d", tzoSec);
00188         tStamp = tStamp + tzoTZIDSec;
00189     } else {
00190         tStamp = tStamp + tzoSec;
00191     }
00192     return tStamp;
00193 }
00194 
00195 // since this returns the string from a static buffer, and unknowing users
00196 // might call this twice in a single command (e.g. printf(..., FormatCTime(time1), FormatCTime(time2));
00197 // this define controls how many of these can execute.
00198 #define NumCallsPerArgList 2
00199 char * HTTPiCal::FormatCTime(time_t t)
00200 {
00201     static char temp[NumCallsPerArgList][30];
00202     static int i = 0;
00203 
00204     i %= NumCallsPerArgList;
00205     strcpy(temp[i], ctime(&t));
00206     temp[i][strlen(temp[i])-1] = '\0';
00207     return temp[i++];
00208 }
00209 
00210 
00211 void HTTPiCal::ShowEventInfo(Event_T & Event)
00212 {
00213     char scratch[80];
00214     #define LF "\r\n"
00215 
00216     printf("*******    Summary: %s" LF, Event.Summary);
00217     printf("          Location: %s" LF, Event.Location);
00218     printf("          Category: %s" LF, Event.Category);
00219     printf("          Priority: %d" LF, Event.Priority);
00220     sprintf(scratch, "%lu ", Event.Start);
00221     sprintf(scratch + strlen(scratch), "%s", (Event.Start == 0) ? "" : ctime(&Event.Start));
00222     scratch[strlen(scratch)-1] = '\0';
00223     printf("             Start: %s" LF, scratch);
00224     sprintf(scratch, "%lu ", Event.End);
00225     sprintf(scratch + strlen(scratch), "%s", (Event.End == 0) ? "" : ctime(&Event.End));
00226     scratch[strlen(scratch)-1] = '\0';
00227     printf("               End: %s" LF, scratch);
00228     printf("             Count: %d" LF,  Event.Count);
00229     printf("          Interval: %d" LF, Event.Interval);
00230     printf("         RepeatFrq: %d" LF,  Event.RepeatFreq);
00231     printf("         RepeatDay: %02X" LF,  Event.RepeatDays);
00232     printf("    RepeatMonthDay: %08X" LF,  Event.RepeatMonthDay);
00233     printf(" RepeatMonthDayRev: %08X" LF, Event.RepeatMonthDayRev);
00234     printf("       RepeatMonth: %04X" LF,  Event.RepeatMonths);
00235     sprintf(scratch, "%lu ", Event.Until);
00236     sprintf(scratch + strlen(scratch), "%s", (Event.Until == 0) ? "" : ctime(&Event.Until));
00237     scratch[strlen(scratch)-1] = '\0';
00238     printf("             Until: %s" LF, scratch);
00239     printf("" LF);
00240 }
00241 
00242 
00243 /// Computes the intersection of time1 and time2 ranges, and modifies time1
00244 /// range to represent the intersection.
00245 ///
00246 /// start1 is input as the start of the time1 range, and is written
00247 ///     to represent the intersection of the two ranges.
00248 /// end1 is input as the end of the time1 range and is written to
00249 ///     represent the intersection of the two ranges.
00250 /// start2 is the start of the time2 range.
00251 /// end2 is the end of the time2 range.
00252 /// returns true if the ranges have an intersection, and the time1 range
00253 ///     values have been modified.
00254 ///
00255 bool HTTPiCal::TimeIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2)
00256 {
00257     // |----Time1----|
00258     //                  |--Time2--|            false
00259     //
00260     //                 |----Time1----|
00261     //   |--Time2--|                           false
00262     //
00263     // |----Time1----|
00264     //       |----Time2----|
00265     //       |-Time1-|                         true
00266     //
00267     //       |----Time1----|
00268     // |----Time2----|
00269     //       |-Time1-|                         true
00270     //
00271     // |----Time1-------|
00272     //       |-Time2-|
00273     //       |-Time1-|                         true
00274     //
00275     // | Time1 (end1 == 0)
00276     // | Time2 (end2 == 0)                     true
00277     //
00278     if (*start1 == *start2 && *end1 == 0 && *end2 == 0)
00279         return true;
00280     if (*end1 < *start2 || *end2 < *start1)
00281         return false;
00282     if (max(*start1,*start2) < min(*end1,*end2)) {
00283         *start1 = max(*start1,*start2);
00284         *end1 = min(*end1,*end2);
00285         return true;
00286     } else {
00287         return false;
00288     }
00289 }
00290 
00291 bool HTTPiCal::isLeapYear(time_t t)
00292 {
00293     int year;
00294     struct tm * ts;
00295     ts = localtime(&t);
00296 
00297     year = 1900 + ts->tm_year + 1;
00298     if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0))
00299         return true;
00300     else
00301         return false;
00302 }
00303 
00304 time_t HTTPiCal::NextInterval(time_t curTime, int repeatFreq, int interval)
00305 {
00306     const time_t secperday = 60*60*24;
00307     const int repeatFactor[] = {0, 1, 7, 30, 365};
00308     int delta = repeatFactor[repeatFreq];
00309     if (repeatFreq == 4 && isLeapYear(curTime))
00310         delta += 1;
00311     //INFO("freq %d, interval %d, delta %d", repeatFreq, interval, delta);
00312     return delta * interval * secperday;
00313 }
00314 
00315 
00316 // start1,end1 is the time range representing the visible grid
00317 // start2,end2 is the time range of the event being tested
00318 // Event is also the event being tested and permits testing the repeat information.
00319 //
00320 // If the event repeat pattern intersects with the display pattern, indicate this as "true"
00321 //
00322 bool HTTPiCal::RepeatMaskIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T * Event)
00323 {
00324     bool intersects = false;
00325     
00326     //INFO("RepeatFreq: %d", Event->RepeatFreq);
00327     if (Event->RepeatFreq == rptfDaily) {
00328         //INFO("rptfDaily is not handled");
00329     } else if (Event->RepeatFreq == rptfWeekly) {
00330         struct tm * timeinfo;
00331         timeinfo = localtime(start1);
00332         uint8_t daymask = Event->RepeatDays;
00333         // now, check the tm_wday (0=Sunday, 1=Monday, ...) and see if we intersect with the event time
00334         uint8_t testmask = 1 << timeinfo->tm_wday;
00335         //INFO("Mask: Event mask: %02X, test mask: %02X", daymask, testmask);
00336         if (daymask & testmask)
00337             intersects = true;
00338         else
00339             intersects = false;
00340         //INFO("  intersects: %02X", daymask & testmask);
00341         return intersects;
00342     } else if (Event->RepeatFreq == rptfYearly) {
00343         //struct tm * timeinfo;
00344         //timeinfo = localtime(start1);
00345         //INFO("rptfYearly is not handled well yet");        
00346     }
00347     //INFO("Mask: no handler, returning true");
00348     return true;
00349 }
00350 
00351 bool HTTPiCal::RepeatIntersects(time_t * start1, time_t * end1, time_t * start2, time_t * end2, Event_T * Event)
00352 {
00353     INFO("** 1: (%s, %s)", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "");
00354     INFO("   2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
00355     INFO("  ev: (%s, %s)", FormatCTime(Event->Start), Event->End ? FormatCTime(Event->End) : "");
00356     if (TimeIntersects(start1, end1, start2, end2))
00357         return true;
00358     if (Event && Event->RepeatFreq) {
00359         INFO("RepeatFreq: %d", Event->RepeatFreq);
00360         if (Event->Start < *start2 && Event->Until > *start2 ) {           // Until=....
00361             INFO("Repeat until: %d", Event->Until);
00362             do {
00363                 time_t interval = NextInterval(*start1, Event->RepeatFreq, (Event->Interval == 0) ? 1 : Event->Interval);
00364                 *start1 = *start1 + interval;
00365                 if (*end1)
00366                     *end1   = *end1   + interval;
00367                 INFO("** 1: (%s, %s)", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "");
00368                 INFO("until (%24s, %s)", " ", FormatCTime(Event->Until));
00369                 INFO("   2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
00370                 if (!RepeatMaskIntersects(start1, end1, start2, end2, Event)) {
00371                     continue;   // we're not on a repeat cycle (e.g. wrong day of the week)
00372                 }
00373                 if (TimeIntersects(start1, end1, start2, end2)) {
00374                     return true;
00375                 }
00376             } while ((*end2 == 0 || *start1 < *end2) && *start1 < Event->Until);
00377         } else if (Event->Start < *start2 && Event->Count) {                      // Count=
00378             INFO("Repeat count %d", Event->Count);
00379             int count = Event->Count - 1;
00380             do {
00381                 time_t interval = NextInterval(*start1, Event->RepeatFreq, (Event->Interval == 0) ? 1 : Event->Interval);
00382                 *start1 = *start1 + interval;
00383                 if (*end1)
00384                     *end1 = *end1 + interval;
00385                 INFO("** 1: (%s, %s) - %d", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "", count);
00386                 INFO("   2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
00387                 if (!RepeatMaskIntersects(start1, end1, start2, end2, Event)) {
00388                     continue;   // we're not on a repeat cycle (e.g. wrong day of the week)
00389                 }
00390                 if (TimeIntersects(start1, end1, start2, end2)) {
00391                     return true;
00392                 }
00393             } while (--count && *end1 < *start2);
00394         } else if (Event->Start < *start2) {                      // no Count= and no Until=
00395             INFO(" no Repeat end");
00396             do {
00397                 int rptFreq = Event->RepeatFreq;
00398                 if (Event->RepeatFreq == 2 && Event->RepeatDays != 0)
00399                     rptFreq--;
00400                 time_t interval = NextInterval(*start1, rptFreq, (Event->Interval == 0) ? 1 : Event->Interval);
00401                 *start1 = *start1 + interval;
00402                 if (*end1)
00403                     *end1   = *end1   + interval;
00404                 INFO("== 1: (%s, %s)", FormatCTime(*start1), *end1 ? FormatCTime(*end1) : "");
00405                 INFO("   2: (%s, %s)", FormatCTime(*start2), *end2 ? FormatCTime(*end2) : "");
00406                 if (!RepeatMaskIntersects(start1, end1, start2, end2, Event)) {
00407                     continue;   // we're not on a repeat cycle (e.g. wrong day of the week)
00408                 }
00409                 if (TimeIntersects(start1, end1, start2, end2)) {
00410                     return true;
00411                 }
00412             } while (*start1 < *end2 || (*end2 == 0 && *start1 < *start2));
00413         } else {
00414             INFO("falling out");
00415         }
00416     }
00417     INFO("  no intersection");
00418     return false;
00419 }
00420 
00421 // All the stuff between
00422 // BEGIN:VEVENT
00423 // ...
00424 // END:VEVENT
00425 //
00426 void HTTPiCal::ParseEvent(Event_T * Event, const char * pStart, tz_sec_t tzoSec)
00427 {
00428     INFO("ParseEvent(...,'%s',%d)", pStart, tzoSec);
00429     if (strncmp(pStart, "DTSTART:", 8) == 0) {
00430         Event->Start = ParseDateStamp(pStart+8, tzoSec);
00431         INFO("  Start: %s\n", ctime(&Event->Start));
00432     } else if (strncmp(pStart, "DTSTART;", 8) == 0) {
00433         const char * p = pStart + 8;
00434         tzoSec = ParseTZID(p);
00435         p = strrchr(pStart, ':');
00436         if (p) {
00437             Event->Start = ParseDateStamp(p+1, 0);  // example was localtime with GMT -06:00 tzoSec);
00438             INFO("  Start: %s", ctime(&Event->Start));
00439         }
00440     } else if (strncmp(pStart, "DTEND:", 6) == 0) {
00441         Event->End = ParseDateStamp(pStart+6, tzoSec);
00442         //INFO("    End: %d\n", mktime(&Event->eventEnd));
00443     } else if (strncmp(pStart, "DTEND;", 6) == 0) {
00444         const char * p = pStart + 6;
00445         tzoSec = ParseTZID(p);
00446         p = strrchr(pStart, ':');
00447         if (p) {
00448             Event->End = ParseDateStamp(p+1, 0);  // example was localtime with GMT -06:00 tzoSec);
00449             INFO("    End: %s", ctime(&Event->End));
00450         }
00451     } else if (strncmp(pStart, "SUMMARY:", 8) == 0) {
00452         strncpy(Event->Summary, pStart+8, SUMMARY_CHARS-1);
00453         Event->Summary[SUMMARY_CHARS-1] = '\0';
00454         //INFO(" Summary: %s\n", Event->Summary);
00455     } else if (strncmp(pStart, "LOCATION:", 9) == 0) {
00456         strncpy(Event->Location, pStart+9, LOCATION_CHARS-1);
00457         Event->Location[LOCATION_CHARS-1] = '\0';
00458         //INFO(" Location: %s\n", Event->Location);
00459     } else if (strncmp(pStart, "PRIORITY:", 9) == 0) {
00460         Event->Priority = *(pStart+9) - '0';
00461         //INFO("  Priority: %d\n", Event->Priority);
00462     } else if (strncmp(pStart, "CATEGORIES:", 11) == 0) {
00463         strncpy(Event->Category, pStart+11, CATEGORY_CHARS-1);
00464         Event->Category[CATEGORY_CHARS-1] = '\0';
00465         //INFO(" Category: %s\n", Event->Category);
00466     } else if (strncmp(pStart, "RRULE:", 6) == 0) {
00467         //RRULE:FREQ=WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR
00468         const char * p1, *p2;
00469         //INFO("%s", pStart);
00470         p1 = pStart + 6;                        // p1 = FREQ=WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR
00471         p2 = strchr(p1, ';');
00472         //if (p2)
00473         //    *p2++ = '\0';
00474         while (*p1) {
00475             INFO("%s", p1);
00476             if (strncmp(p1, "FREQ=", 5) == 0) {
00477                 //INFO("%s", p1);
00478                 p1 += 5;                            // p1 = WEEKLY;UNTIL=20140502T180000;BYDAY=MO,TU,WE,TH,FR
00479                 if (strncmp(p1, "WEEKLY", 6) == 0) {
00480                     //INFO("  %s", p1);
00481                     Event->RepeatFreq = rptfWeekly;
00482                     p1 += 6;
00483                 } else if (strncmp(p1, "DAILY", 5) == 0) {
00484                     //INFO("  %s", p1);
00485                     Event->RepeatFreq = rptfDaily;
00486                     p1 += 5;
00487                 } else if (strncmp(p1, "MONTHLY", 7) == 0) {
00488                     //INFO("  %s", p1);
00489                     Event->RepeatFreq = rptfMonthly;
00490                     p1 += 7;
00491                 } else if (strncmp(p1, "YEARLY", 6) == 0) {
00492                     //INFO("  %s", p1);
00493                     Event->RepeatFreq = rptfYearly;
00494                     p1 += 6;
00495                 }
00496             } else if (strncmp(p1, "INTERVAL=", 9) == 0) { // INTERVAL=2
00497                 //INFO("%s", p1);
00498                 p1 += 9;
00499                 Event->Interval = atoi(p1);
00500             } else if (strncmp(p1, "COUNT=", 6) == 0) { // COUNT=12;
00501                 //INFO("%s", p1);
00502                 p1 += 6;                // p1 =
00503                 Event->Count = atoi(p1);
00504             } else if (strncmp(p1, "UNTIL=", 6) == 0) {
00505                 //INFO("%s", p1);
00506                 p1 += 6;                // p1 = 20140502T180000;BYDAY=MO,TU,WE,TH,FR
00507                 //printf("UNTIL= {%s}\n", p1);
00508                 Event->Until = ParseDateStamp(p1, tzoSec);
00509                 //printf("UNTIL:: %d: %d\n", Event->Until, tzoSec);
00510             } else if (strncmp(p1, "BYDAY=", 6) == 0) {
00511                 //INFO("%s", p1);
00512                 p1 += 6;        // p1 = MO,TU,WE,TH,FR
00513                 while (*p1 >= ' ') {
00514                     //INFO("  %s", p1);
00515                     for (int d=0; d<7; d++) {
00516                         if (strncmp(p1,RepeatDayAbbrev(d),2) == 0) {
00517                             Event->RepeatDays |= (1 << d);
00518                             //INFO("    %s %02X", RepeatDayAbbrev(d), Event->RepeatDays);
00519                             break;
00520                         }
00521                     }
00522                     p1 += 3;
00523                 }
00524                 //INFO("  RepeatDay: %02X", Event->RepeatDays);
00525             } else if (strncmp(p1, "BYMONTHDAY=", 11) == 0) {
00526                 // RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15
00527                 p1 += 11;
00528                 while (*p1 >= ' ') {
00529                     const char * px = p1;
00530                     while (*px >= ' ' && *px != ',') {    // find , or ; or <nul>
00531                         px++;
00532                     }
00533                     //if (*px)
00534                     //    *px++ = '\0';
00535                     int num = atoi(p1);
00536                     if (num >= 0)
00537                         Event->RepeatMonthDay |= (1 << num);
00538                     else
00539                         Event->RepeatMonthDayRev |= (1 << -num);
00540                     p1 = px;
00541                 }
00542                 INFO("  RepeatMonthDay: %08X", Event->RepeatMonthDay);
00543             } else if (strncmp(p1, "BYMONTH=", 8) == 0) {
00544                 // RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3
00545                 p1 += 8;
00546                 while (*p1 >= ' ') {
00547                     const char * px = p1;
00548                     while (*px >= ' ' && *px != ',') {    // find , or ; or <nul>
00549                         px++;
00550                     }
00551                     //if (*px)
00552                     //    *px++ = '\0';
00553                     int num = atoi(p1);
00554                     if (num >= 0)
00555                         Event->RepeatMonths |= (1 << num);
00556                     //else
00557                     //    ; // Event->RepeatMonthsRev |= (1 << -num);
00558                     p1 = px;
00559                 }
00560                 INFO("  RepeatMonths: %04X", Event->RepeatMonths);                
00561             }
00562             if (!p2)
00563                 break;
00564             p1 = p2 + 1;
00565             p2 = strchr(p1, ';');
00566             //if (p2)
00567             //    *p2++ = '\0';
00568         }
00569     }
00570 }
00571 
00572 
00573 // TZID="(GMT -06:00)":20140519T063000
00574 // TZID:(UTC-06:00) Central Time (US & Canada)
00575 // TZID:(GMT -06:00)
00576 HTTPiCal::tz_sec_t HTTPiCal::ParseTZID(const char * string)
00577 {
00578     tz_sec_t tzo = 0;
00579     bool sign = false;
00580 
00581     INFO("ParseTZID(%s)", string);  // TZID="(GMT -06:00)":20140519T063000
00582     string += 5;                    // "(GMT -06:00)":20140519T063000
00583     if (*string == '"')
00584         string++;                   // (GMT -06:00)":20140519T063000
00585     if ((strncmp(string, "(UTC", 4) == 0) 
00586     ||  (strncmp(string, "(GMT", 4) == 0) ){
00587         string += 4;
00588         if (*string == ' ')
00589             string++;
00590         if (*string == '-') {
00591             sign = true;
00592             string++;
00593         }
00594         tzo = atoi(string) * 3600;
00595         string = strchr(string, ':');
00596         if (string) {
00597             string++;
00598             tzo += atoi(string) * 60;
00599         }
00600         if (sign)
00601             tzo = -tzo;
00602         INFO("  tzo = %d", tzo);
00603     } else {
00604         ERR("Unhandled TZID(%s)", string);
00605     }
00606     return tzo;
00607 }
00608 
00609 int HTTPiCal::ParseICalStream(const char * pStart, time_t gridStartTime, time_t gridEndTime, tz_min_t tzoMin, bool showEvents)
00610 {
00611     INFO("Parse(%s)", pStart);
00612     //INFO("EventCount: %d, EventSpaceCount: %d, seeking: %d", EventCount, EventSpaceCount, seeking);
00613     if (pStart && *pStart && EventCount < EventSpaceCount) {
00614         switch (seeking) {
00615             case idle:
00616                 if (strncmp(pStart, "BEGIN:VTIMEZONE", 15) == 0) {
00617                     //INFO("begin:timezone");
00618                     seeking = inTimeZone;
00619                 } else if (strncmp(pStart, "BEGIN:VEVENT", 12) == 0) {
00620                     //INFO("begin:vevent");
00621                     seeking = inEvent;
00622                     EventList[EventCount].Start = 0;
00623                     EventList[EventCount].End = 0;
00624                     EventList[EventCount].Until = 0;
00625                     EventList[EventCount].Summary[0] = '\0';
00626                     EventList[EventCount].Location[0] = '\0';
00627                     EventList[EventCount].Category[0] = '\0';
00628                     EventList[EventCount].Count = 0;
00629                     EventList[EventCount].Interval = 0;
00630                     EventList[EventCount].RepeatFreq = rptfNone;
00631                     EventList[EventCount].RepeatDays = 0;
00632                     EventList[EventCount].RepeatMonthDay = 0;
00633                     EventList[EventCount].RepeatMonthDayRev = 0;
00634                     EventList[EventCount].RepeatMonths = 0;
00635                     EventList[EventCount].Priority = 5;     // 5 is Normal
00636                 }
00637                 break;
00638             case inTimeZone:
00639                 //INFO("inTimeZone:");
00640                 // Can also pick up daylight savings time
00641                 if (strncmp(pStart, "END:VTIMEZONE", 13) == 0) {
00642                     seeking = idle;
00643                 } else if ((strncmp(pStart, "TZID:", 5) == 0) 
00644                 || (strncmp(pStart, "TZID=", 5) == 0) ) {
00645                     tzoTZIDSec = ParseTZID(pStart);
00646                     tzAdjusted = true;
00647                     pStart += 5;
00648                 } else if (strncmp(pStart, "BEGIN:STANDARD", 14) == 0) {
00649                     
00650                 } else if (strncmp(pStart, "BEGIN:DAYLIGHT", 14) == 0) {
00651                     
00652                 }
00653                 break;
00654             case inEvent:
00655                 //INFO("inEvent:");
00656                 // inEvent
00657                 if (strncmp(pStart, "END:VEVENT", 10) == 0) {
00658                     // Timezone offset
00659                     if (!tzAdjusted) {
00660                         EventList[EventCount].Start += (60 * tzoMin);
00661                         if (EventList[EventCount].End)
00662                             EventList[EventCount].End += (60 * tzoMin);
00663                     }
00664                     // Process it
00665                     if (showEvents) {
00666                         //printf("ev: %d\r\n", EventCount);
00667                         ShowEventInfo(EventList[EventCount]);
00668                     }
00669                     // Force to ALWAYS
00670                     time_t aStart = EventList[EventCount].Start;
00671                     time_t aEnd   = EventList[EventCount].End;
00672                     if (gridStartTime 
00673                     && RepeatIntersects(&aStart, &aEnd, &gridStartTime, &gridEndTime, &EventList[EventCount])) {
00674                         EventCount++;
00675                         if (showEvents) {
00676                             INFO(" +++++ Added Event %d", EventCount);//, EventList[EventCount].Summary);
00677                         }
00678                     }
00679                     seeking = idle;
00680                 } else {
00681                     //INFO("parse event data");
00682                     ParseEvent(&EventList[EventCount], pStart, 60 * tzoMin);
00683                 }
00684                 // End of inEvent
00685                 break;
00686             default:
00687                 INFO("default:");
00688                 seeking = idle;
00689                 break;
00690         }
00691     } // while
00692     return EventCount;
00693 }