icalcomponent* icalmime_parse(char* (*get_string)(char *s, size_t size, void *d), void *data) { struct sspm_part *parts; int i, last_level=0; icalcomponent *root=0, *parent=0, *comp=0, *last = 0; if ( (parts = (struct sspm_part *) malloc(NUM_PARTS*sizeof(struct sspm_part)))==0) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); return 0; } memset(parts,0,sizeof(parts)); sspm_parse_mime(parts, NUM_PARTS, /* Max parts */ icalmime_local_action_map, /* Actions */ get_string, data, /* data for get_string*/ 0 /* First header */); for(i = 0; i <NUM_PARTS && parts[i].header.major != SSPM_NO_MAJOR_TYPE ; i++){ #define TMPSZ 1024 char mimetype[TMPSZ]; const char* major = sspm_major_type_string(parts[i].header.major); const char* minor = sspm_minor_type_string(parts[i].header.minor); if(parts[i].header.minor == SSPM_UNKNOWN_MINOR_TYPE ){ assert(parts[i].header.minor_text !=0); minor = parts[i].header.minor_text; } snprintf(mimetype,sizeof(mimetype),"%s/%s",major,minor); comp = icalcomponent_new(ICAL_XLICMIMEPART_COMPONENT); if(comp == 0){ /* HACK Handle Error */ assert(0); } if(parts[i].header.error!=SSPM_NO_ERROR){ const char *str="Unknown error"; char temp[256]; if(parts[i].header.error==SSPM_MALFORMED_HEADER_ERROR){ str = "Malformed header, possibly due to input not in MIME format"; } if(parts[i].header.error==SSPM_UNEXPECTED_BOUNDARY_ERROR){ str = "Got an unexpected boundary, possibly due to a MIME header for a MULTIPART part that is missing the Content-Type line"; } if(parts[i].header.error==SSPM_WRONG_BOUNDARY_ERROR){ str = "Got the wrong boundary for the opening of a MULTIPART part."; } if(parts[i].header.error==SSPM_NO_BOUNDARY_ERROR){ str = "Got a multipart header that did not specify a boundary"; } if(parts[i].header.error==SSPM_NO_HEADER_ERROR){ str = "Did not get a header for the part. Is there a blank\ line between the header and the previous boundary\?"; } if(parts[i].header.error_text != 0){ snprintf(temp,256, "%s: %s",str,parts[i].header.error_text); } else { strcpy(temp,str); } icalcomponent_add_property (comp, icalproperty_vanew_xlicerror( temp, icalparameter_new_xlicerrortype( ICAL_XLICERRORTYPE_MIMEPARSEERROR), 0)); } if(parts[i].header.major != SSPM_NO_MAJOR_TYPE && parts[i].header.major != SSPM_UNKNOWN_MAJOR_TYPE){ icalcomponent_add_property(comp, icalproperty_new_xlicmimecontenttype((char*) icalmemory_strdup(mimetype))); } if (parts[i].header.encoding != SSPM_NO_ENCODING){ icalcomponent_add_property(comp, icalproperty_new_xlicmimeencoding( sspm_encoding_string(parts[i].header.encoding))); } if (parts[i].header.filename != 0){ icalcomponent_add_property(comp, icalproperty_new_xlicmimefilename(parts[i].header.filename)); } if (parts[i].header.content_id != 0){ icalcomponent_add_property(comp, icalproperty_new_xlicmimecid(parts[i].header.content_id)); } if (parts[i].header.charset != 0){ icalcomponent_add_property(comp, icalproperty_new_xlicmimecharset(parts[i].header.charset)); } /* Add iCal components as children of the component */ if(parts[i].header.major == SSPM_TEXT_MAJOR_TYPE && parts[i].header.minor == SSPM_CALENDAR_MINOR_TYPE && parts[i].data != 0){ icalcomponent_add_component(comp, (icalcomponent*)parts[i].data); parts[i].data = 0; } else if(parts[i].header.major == SSPM_TEXT_MAJOR_TYPE && parts[i].header.minor != SSPM_CALENDAR_MINOR_TYPE && parts[i].data != 0){ /* Add other text components as "DESCRIPTION" properties */ icalcomponent_add_property(comp, icalproperty_new_description( (char*)icalmemory_strdup((char*)parts[i].data))); parts[i].data = 0; } if(root!= 0 && parts[i].level == 0){ /* We've already assigned the root, but there is another part at the root level. This is probably a parse error*/ icalcomponent_free(comp); continue; } if(parts[i].level == last_level && last_level != 0){ icalerror_assert(parent!=0,"No parent for adding component"); icalcomponent_add_component(parent,comp); } else if (parts[i].level == last_level && last_level == 0 && root == 0) { root = comp; parent = comp; } else if (parts[i].level > last_level){ parent = last; icalcomponent_add_component(parent,comp); last_level = parts[i].level; } else if (parts[i].level < last_level){ if (parent) parent = icalcomponent_get_parent(parent); icalcomponent_add_component(parent,comp); last_level = parts[i].level; } else { assert(0); } last = comp; last_level = parts[i].level; assert(parts[i].data == 0); } sspm_free_parts(parts,NUM_PARTS); free(parts); return root; }
/* Calculates the UTC offset of a given local time in the given timezone. It is the number of seconds to add to UTC to get local time. The is_daylight flag is set to 1 if the time is in daylight-savings time. */ int icaltimezone_get_utc_offset (icaltimezone *zone, struct icaltimetype *tt, int *is_daylight) { icaltimezonechange *zone_change, *prev_zone_change, tt_change, tmp_change; int change_num, step, utc_offset_change, cmp; int change_num_to_use; int want_daylight; if (tt == NULL) return 0; if (is_daylight) *is_daylight = 0; /* For local times and UTC return 0. */ if (zone == NULL || zone == &utc_timezone) return 0; /* Use the builtin icaltimezone if possible. */ if (zone->builtin_timezone) zone = zone->builtin_timezone; /* Make sure the changes array is expanded up to the given time. */ icaltimezone_ensure_coverage (zone, tt->year); if (!zone->changes || zone->changes->num_elements == 0) return 0; /* Copy the time parts of the icaltimetype to an icaltimezonechange so we can use our comparison function on it. */ tt_change.year = tt->year; tt_change.month = tt->month; tt_change.day = tt->day; tt_change.hour = tt->hour; tt_change.minute = tt->minute; tt_change.second = tt->second; /* This should find a change close to the time, either the change before it or the change after it. */ change_num = icaltimezone_find_nearby_change (zone, &tt_change); /* Sanity check. */ icalerror_assert (change_num >= 0, "Negative timezone change index"); icalerror_assert (change_num < zone->changes->num_elements, "Timezone change index out of bounds"); /* Now move backwards or forwards to find the timezone change that applies to tt. It should only have to do 1 or 2 steps. */ zone_change = icalarray_element_at (zone->changes, change_num); step = 1; change_num_to_use = -1; for (;;) { /* Copy the change, so we can adjust it. */ tmp_change = *zone_change; /* If the clock is going backward, check if it is in the region of time that is used twice. If it is, use the change with the daylight setting which matches tt, or use standard if we don't know. */ if (tmp_change.utc_offset < tmp_change.prev_utc_offset) { /* If the time change is at 2:00AM local time and the clock is going back to 1:00AM we adjust the change to 1:00AM. We may have the wrong change but we'll figure that out later. */ icaltimezone_adjust_change (&tmp_change, 0, 0, 0, tmp_change.utc_offset); } else { icaltimezone_adjust_change (&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset); } cmp = icaltimezone_compare_change_fn (&tt_change, &tmp_change); /* If the given time is on or after this change, then this change may apply, but we continue as a later change may be the right one. If the given time is before this change, then if we have already found a change which applies we can use that, else we need to step backwards. */ if (cmp >= 0) change_num_to_use = change_num; else step = -1; /* If we are stepping backwards through the changes and we have found a change that applies, then we know this is the change to use so we exit the loop. */ if (step == -1 && change_num_to_use != -1) break; change_num += step; /* If we go past the start of the changes array, then we have no data for this time so we return a UTC offset of 0. */ if (change_num < 0) return 0; if (change_num >= zone->changes->num_elements) break; zone_change = icalarray_element_at (zone->changes, change_num); } /* If we didn't find a change to use, then we have a bug! */ icalerror_assert (change_num_to_use != -1, "No applicable timezone change found"); /* Now we just need to check if the time is in the overlapped region of time when clocks go back. */ zone_change = icalarray_element_at (zone->changes, change_num_to_use); utc_offset_change = zone_change->utc_offset - zone_change->prev_utc_offset; if (utc_offset_change < 0 && change_num_to_use > 0) { tmp_change = *zone_change; icaltimezone_adjust_change (&tmp_change, 0, 0, 0, tmp_change.prev_utc_offset); if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) < 0) { /* The time is in the overlapped region, so we may need to use either the current zone_change or the previous one. If the time has the is_daylight field set we use the matching change, else we use the change with standard time. */ prev_zone_change = icalarray_element_at (zone->changes, change_num_to_use - 1); /* I was going to add an is_daylight flag to struct icaltimetype, but iCalendar doesn't let us distinguish between standard and daylight time anyway, so there's no point. So we just use the standard time instead. */ want_daylight = (tt->is_daylight == 1) ? 1 : 0; #if 0 if (zone_change->is_daylight == prev_zone_change->is_daylight) printf (" **** Same is_daylight setting\n"); #endif if (zone_change->is_daylight != want_daylight && prev_zone_change->is_daylight == want_daylight) zone_change = prev_zone_change; } } /* Now we know exactly which timezone change applies to the time, so we can return the UTC offset and whether it is a daylight time. */ if (is_daylight) *is_daylight = zone_change->is_daylight; return zone_change->utc_offset; }
/** Calculates the UTC offset of a given UTC time in the given timezone. It is the number of seconds to add to UTC to get local time. The is_daylight flag is set to 1 if the time is in daylight-savings time. */ int icaltimezone_get_utc_offset_of_utc_time (icaltimezone *zone, struct icaltimetype *tt, int *is_daylight) { icaltimezonechange *zone_change, tt_change, tmp_change; int change_num, step, change_num_to_use; if (is_daylight) *is_daylight = 0; /* For local times and UTC return 0. */ if (zone == NULL || zone == &utc_timezone) return 0; /* Use the builtin icaltimezone if possible. */ if (zone->builtin_timezone) zone = zone->builtin_timezone; /* Make sure the changes array is expanded up to the given time. */ icaltimezone_ensure_coverage (zone, tt->year); if (!zone->changes || zone->changes->num_elements == 0) return 0; /* Copy the time parts of the icaltimetype to an icaltimezonechange so we can use our comparison function on it. */ tt_change.year = tt->year; tt_change.month = tt->month; tt_change.day = tt->day; tt_change.hour = tt->hour; tt_change.minute = tt->minute; tt_change.second = tt->second; /* This should find a change close to the time, either the change before it or the change after it. */ change_num = icaltimezone_find_nearby_change (zone, &tt_change); /* Sanity check. */ icalerror_assert (change_num >= 0, "Negative timezone change index"); icalerror_assert (change_num < zone->changes->num_elements, "Timezone change index out of bounds"); /* Now move backwards or forwards to find the timezone change that applies to tt. It should only have to do 1 or 2 steps. */ zone_change = icalarray_element_at (zone->changes, change_num); step = 1; change_num_to_use = -1; for (;;) { /* Copy the change and adjust it to UTC. */ tmp_change = *zone_change; /* If the given time is on or after this change, then this change may apply, but we continue as a later change may be the right one. If the given time is before this change, then if we have already found a change which applies we can use that, else we need to step backwards. */ if (icaltimezone_compare_change_fn (&tt_change, &tmp_change) >= 0) change_num_to_use = change_num; else step = -1; /* If we are stepping backwards through the changes and we have found a change that applies, then we know this is the change to use so we exit the loop. */ if (step == -1 && change_num_to_use != -1) break; change_num += step; /* If we go past the start of the changes array, then we have no data for this time so we return a UTC offset of 0. */ if (change_num < 0) return 0; if (change_num >= zone->changes->num_elements) break; zone_change = icalarray_element_at (zone->changes, change_num); } /* If we didn't find a change to use, then we have a bug! */ icalerror_assert (change_num_to_use != -1, "No applicable timezone change found"); /* Now we know exactly which timezone change applies to the time, so we can return the UTC offset and whether it is a daylight time. */ zone_change = icalarray_element_at (zone->changes, change_num_to_use); if (is_daylight) *is_daylight = zone_change->is_daylight; return zone_change->utc_offset; }
/** This parses the zones.tab file containing the names and locations of the builtin timezones. It creates the builtin_timezones array which is an icalarray of icaltimezone structs. It only fills in the location, latitude and longtude fields; the rest are left blank. The VTIMEZONE component is loaded later if it is needed. The timezones in the zones.tab file are sorted by their name, which is useful for binary searches. */ static void icaltimezone_parse_zone_tab (void) { char *filename; FILE *fp; char buf[1024]; /* Used to store each line of zones.tab as it is read. */ char location[1024]; /* Stores the city name when parsing buf. */ unsigned int filename_len; int latitude_degrees, latitude_minutes, latitude_seconds; int longitude_degrees, longitude_minutes, longitude_seconds; icaltimezone zone; icalerror_assert (builtin_timezones == NULL, "Parsing zones.tab file multiple times"); builtin_timezones = icalarray_new (sizeof (icaltimezone), 32); filename_len = strlen (get_zone_directory()) + strlen (ZONES_TAB_FILENAME) + 2; filename = (char*) malloc (filename_len); if (!filename) { icalerror_set_errno(ICAL_NEWFAILED_ERROR); return; } snprintf (filename, filename_len, "%s/%s", get_zone_directory(), ZONES_TAB_FILENAME); fp = fopen (filename, "r"); free (filename); if (!fp) { icalerror_set_errno(ICAL_FILE_ERROR); return; } while (fgets (buf, sizeof(buf), fp)) { if (*buf == '#') continue; /* The format of each line is: "latitude longitude location". */ if (sscanf (buf, "%4d%2d%2d %4d%2d%2d %s", &latitude_degrees, &latitude_minutes, &latitude_seconds, &longitude_degrees, &longitude_minutes, &longitude_seconds, location) != 7) { fprintf (stderr, "Invalid timezone description line: %s\n", buf); continue; } icaltimezone_init (&zone); zone.location = strdup (location); if (latitude_degrees >= 0) zone.latitude = (double) latitude_degrees + (double) latitude_minutes / 60 + (double) latitude_seconds / 3600; else zone.latitude = (double) latitude_degrees - (double) latitude_minutes / 60 - (double) latitude_seconds / 3600; if (longitude_degrees >= 0) zone.longitude = (double) longitude_degrees + (double) longitude_minutes / 60 + (double) longitude_seconds / 3600; else zone.longitude = (double) longitude_degrees - (double) longitude_minutes / 60 - (double) longitude_seconds / 3600; icalarray_append (builtin_timezones, &zone); #if 0 printf ("Found zone: %s %f %f\n", location, zone.latitude, zone.longitude); #endif free (zone.location); } fclose (fp); }