123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <kopano/platform.h>
- #include <utility>
- #include "vtimezone.h"
- #include <mapidefs.h>
- #include <mapicode.h>
- #include <cstdlib>
- #include <cmath>
- #include <ctime>
- using namespace std;
- namespace KC {
- /**
- * Converts icaltimetype to Unix timestamp.
- * Here server zone refers to timezone with which the server started,
- * not the config file option in ical.cfg
- *
- * @param[in] tt icaltimetype
- * @return Unix timestamp
- */
- time_t icaltime_as_timet_with_server_zone(const struct icaltimetype tt)
- {
- struct tm stm;
- time_t t;
- /* If the time is the special null time, return 0. */
- if (icaltime_is_null_time(tt)) {
- return 0;
- }
- /* Copy the icaltimetype to a struct tm. */
- memset (&stm, 0, sizeof (struct tm));
- if (icaltime_is_date(tt)) {
- stm.tm_sec = stm.tm_min = stm.tm_hour = 0;
- } else {
- stm.tm_sec = tt.second;
- stm.tm_min = tt.minute;
- stm.tm_hour = tt.hour;
- }
- stm.tm_mday = tt.day;
- stm.tm_mon = tt.month-1;
- stm.tm_year = tt.year-1900;
- stm.tm_isdst = -1;
- t = mktime(&stm);
- return t;
- }
- // time only, not date!
- static time_t SystemTimeToUnixTime(const SYSTEMTIME &stime)
- {
- return stime.wSecond + (stime.wMinute*60) + ((stime.wHour)*60*60);
- }
- static SYSTEMTIME TMToSystemTime(const struct tm &t)
- {
- SYSTEMTIME stime = {0};
- stime.wYear = t.tm_year;
- stime.wMonth = t.tm_mon;
- stime.wDayOfWeek = t.tm_wday;
- stime.wDay = t.tm_mday;
- stime.wHour = t.tm_hour;
- stime.wMinute = t.tm_min;
- stime.wSecond = t.tm_sec;
- stime.wMilliseconds = 0;
- return stime;
- }
- /**
- * Converts icaltimetype to UTC Unix timestamp
- *
- * @param[in] lpicRoot root icalcomponent to get timezone
- * @param[in] lpicProp icalproperty containing time
- * @return UTC Unix timestamp
- */
- time_t ICalTimeTypeToUTC(icalcomponent *lpicRoot, icalproperty *lpicProp)
- {
- time_t tRet = 0;
- icalparameter *lpicTZParam = NULL;
- const char *lpszTZID = NULL;
- icaltimezone *lpicTimeZone = NULL;
- lpicTZParam = icalproperty_get_first_parameter(lpicProp, ICAL_TZID_PARAMETER);
- if (lpicTZParam) {
- lpszTZID = icalparameter_get_tzid(lpicTZParam);
- lpicTimeZone = icalcomponent_get_timezone(lpicRoot, lpszTZID);
- }
- tRet = icaltime_as_timet_with_zone(icalvalue_get_datetime(icalproperty_get_value(lpicProp)), lpicTimeZone);
- return tRet;
- }
- /**
- * Converts icaltimetype to local Unix timestamp.
- * Here local refers to timezone with which the server started,
- * not the config file option in ical.cfg
- *
- * @param[in] lpicProp icalproperty containing time
- * @return local Unix timestamp
- */
- time_t ICalTimeTypeToLocal(icalproperty *lpicProp)
- {
- return icaltime_as_timet_with_server_zone(icalvalue_get_datetime(icalproperty_get_value(lpicProp)));
- }
- /**
- * Converts icaltimetype to tm structure
- *
- * @param[in] tt icaltimetype time
- * @return tm structure
- */
- static struct tm UTC_ICalTime2UnixTime(icaltimetype tt)
- {
- struct tm stm = {0};
- memset(&stm, 0, sizeof(struct tm));
- if (icaltime_is_null_time(tt))
- return stm;
- stm.tm_sec = tt.second;
- stm.tm_min = tt.minute;
- stm.tm_hour = tt.hour;
- stm.tm_mday = tt.day;
- stm.tm_mon = tt.month-1;
- stm.tm_year = tt.year-1900;
- stm.tm_isdst = -1;
- return stm;
- }
- /**
- * Converts icaltimetype to TIMEZONE_STRUCT structure
- *
- * @param[in] kind icalcomponent kind, either STD or DST time component (ICAL_XSTANDARD_COMPONENT, ICAL_XDAYLIGHT_COMPONENT)
- * @param[in] lpVTZ vtimezone icalcomponent
- * @param[out] lpsTimeZone returned TIMEZONE_STRUCT structure
- * @return MAPI error code
- * @retval MAPI_E_NOT_FOUND icalcomponent kind not found in vtimezone component, or some part of timezone not found
- */
- static HRESULT HrZoneToStruct(icalcomponent_kind kind, icalcomponent *lpVTZ,
- TIMEZONE_STRUCT *lpsTimeZone)
- {
- // HRESULT hr = hrSuccess;
- icalcomponent *icComp = NULL;
- icalcomponent *iterComp = NULL;
- icalproperty *tzFrom, *tzTo, *rRule, *dtStart;
- icaltimetype icTime;
- SYSTEMTIME *lpSysTime = NULL;
- SYSTEMTIME stRecurTime;
- icalrecurrencetype recur;
- /* Assumes that definitions are sorted on dtstart, in ascending order. */
- iterComp = icalcomponent_get_first_component(lpVTZ, kind);
- while (iterComp != NULL) {
- icTime = icalcomponent_get_dtstart(iterComp);
- icTime.is_utc = 1;
- struct tm start = UTC_ICalTime2UnixTime(icTime);
- if (time(NULL) < mktime(&start) && icComp != nullptr)
- break;
- icComp = iterComp;
- iterComp = icalcomponent_get_next_component(lpVTZ, kind);
- }
- if (icComp == NULL)
- return MAPI_E_NOT_FOUND;
- dtStart = icalcomponent_get_first_property(icComp, ICAL_DTSTART_PROPERTY);
- tzFrom = icalcomponent_get_first_property(icComp, ICAL_TZOFFSETFROM_PROPERTY);
- tzTo = icalcomponent_get_first_property(icComp, ICAL_TZOFFSETTO_PROPERTY);
- rRule = icalcomponent_get_first_property(icComp, ICAL_RRULE_PROPERTY);
- //rDate = icalcomponent_get_first_property(icComp, ICAL_RDATE_PROPERTY);
- if (tzFrom == NULL || tzTo == NULL || dtStart == NULL)
- return MAPI_E_NOT_FOUND;
- icTime = icalcomponent_get_dtstart(icComp);
- icTime.is_utc = 1;
- if (kind == ICAL_XSTANDARD_COMPONENT) {
- // this is set when we request the STD timezone part.
- lpsTimeZone->lBias = -(icalproperty_get_tzoffsetto(tzTo) / 60); // STD time is set as bias for timezone
- lpsTimeZone->lStdBias = 0;
- lpsTimeZone->lDstBias = (icalproperty_get_tzoffsetto(tzTo) - icalproperty_get_tzoffsetfrom(tzFrom)) / 60; // DST bias == standard from
- lpsTimeZone->wStdYear = 0;
- lpSysTime = &lpsTimeZone->stStdDate;
- } else {
- lpsTimeZone->wDstYear = 0;
- lpSysTime = &lpsTimeZone->stDstDate;
- }
- memset(lpSysTime, 0, sizeof(SYSTEMTIME));
- // eg. japan doesn't have daylight saving switches.
- if (!rRule) {
- stRecurTime = TMToSystemTime(UTC_ICalTime2UnixTime(icTime));
- lpSysTime->wMonth = stRecurTime.wMonth + 1; // fix for -1 in UTC_ICalTime2UnixTime, since TMToSystemTime doesn't do +1
- lpSysTime->wDayOfWeek = stRecurTime.wDayOfWeek;
- lpSysTime->wDay = int(stRecurTime.wDay / 7.0) + 1;
- lpSysTime->wHour = stRecurTime.wHour;
- lpSysTime->wMinute = stRecurTime.wMinute;
- return hrSuccess;
- }
- recur = icalproperty_get_rrule(rRule);
- // can daylight saving really be !yearly ??
- if (recur.freq != ICAL_YEARLY_RECURRENCE ||
- recur.by_month[0] == ICAL_RECURRENCE_ARRAY_MAX ||
- recur.by_month[1] != ICAL_RECURRENCE_ARRAY_MAX)
- return hrSuccess;
- stRecurTime = TMToSystemTime(UTC_ICalTime2UnixTime(icTime));
- lpSysTime->wHour = stRecurTime.wHour;
- lpSysTime->wMinute = stRecurTime.wMinute;
- lpSysTime->wMonth = recur.by_month[0];
- if (icalrecurrencetype_day_position(recur.by_day[0]) == -1)
- lpSysTime->wDay = 5; // last day of month
- else
- lpSysTime->wDay = icalrecurrencetype_day_position(recur.by_day[0]); // 1..4
- lpSysTime->wDayOfWeek = icalrecurrencetype_day_day_of_week(recur.by_day[0]) - 1;
- return hrSuccess;
- }
- /**
- * Converts VTIMEZONE block in to TIMEZONE_STRUCT structure
- *
- * @param[in] lpVTZ VTIMEZONE icalcomponent
- * @param[out] lpstrTZID timezone string
- * @param[out] lpTimeZone returned TIMEZONE_STRUCT structure
- * @return MAPI error code
- * @retval MAPI_E_NOT_FOUND standard component not found
- * @retval MAPI_E_CALL_FAILED TZID property not found
- */
- HRESULT HrParseVTimeZone(icalcomponent* lpVTZ, std::string* lpstrTZID, TIMEZONE_STRUCT* lpTimeZone)
- {
- HRESULT hr = hrSuccess;
- std::string strTZID;
- TIMEZONE_STRUCT tzRet;
- icalproperty *icProp = NULL;
- memset(&tzRet, 0, sizeof(TIMEZONE_STRUCT));
- icProp = icalcomponent_get_first_property(lpVTZ, ICAL_TZID_PROPERTY);
- if (icProp == NULL)
- return MAPI_E_CALL_FAILED;
- strTZID = icalproperty_get_tzid(icProp);
- if (strTZID.at(0) == '\"') {
- // strip "" around timezone name
- strTZID.erase(0, 1);
- strTZID.erase(strTZID.size()-1);
- }
- hr = HrZoneToStruct(ICAL_XSTANDARD_COMPONENT, lpVTZ, &tzRet);
- if (hr != hrSuccess)
- return hr;
- // if the timezone does no switching, daylight is not given, so we ignore the error (which is only MAPI_E_NOT_FOUND)
- HrZoneToStruct(ICAL_XDAYLIGHT_COMPONENT, lpVTZ, &tzRet);
- // unsupported case: only exceptions in the timezone switches, and no base rule (eg. very old Asia/Kolkata timezone)
- {
- icalcomponent *icComp = NULL;
- icalproperty *tzSTDRule = NULL, *tzDSTRule = NULL;
- icalproperty *tzSTDDate = NULL, *tzDSTDate = NULL;
- icComp = icalcomponent_get_first_component(lpVTZ, ICAL_XSTANDARD_COMPONENT);
- if (icComp) {
- tzSTDRule = icalcomponent_get_first_property(icComp, ICAL_RRULE_PROPERTY);
- tzSTDDate = icalcomponent_get_first_property(icComp, ICAL_RDATE_PROPERTY);
- }
- icComp = icalcomponent_get_first_component(lpVTZ, ICAL_XDAYLIGHT_COMPONENT);
- if (icComp) {
- tzDSTRule = icalcomponent_get_first_property(icComp, ICAL_RRULE_PROPERTY);
- tzDSTDate = icalcomponent_get_first_property(icComp, ICAL_RDATE_PROPERTY);
- }
- if (tzSTDRule == NULL && tzDSTRule == NULL && tzSTDDate != NULL && tzDSTDate != NULL) {
- // clear rule data
- memset(&tzRet.stStdDate, 0, sizeof(SYSTEMTIME));
- memset(&tzRet.stDstDate, 0, sizeof(SYSTEMTIME));
- }
- }
- if (lpstrTZID)
- *lpstrTZID = std::move(strTZID);
- if (lpTimeZone)
- *lpTimeZone = tzRet;
- return hrSuccess;
- }
- /**
- * Converts TIMEZONE_STRUCT structure to VTIMEZONE component
- *
- * @param[in] strTZID timezone string
- * @param[in] tsTimeZone TIMEZONE_STRUCT to be converted
- * @param[out] lppVTZComp returned VTIMEZONE component
- * @return MAPI error code
- * @retval MAPI_E_INVALID_PARAMETER timezone contains invalid data for a yearly daylightsaving
- */
- HRESULT HrCreateVTimeZone(const std::string &strTZID, TIMEZONE_STRUCT &tsTimeZone, icalcomponent** lppVTZComp)
- {
- icalcomponent *icTZComp = NULL;
- icalcomponent *icComp = NULL;
- icaltimetype icTime;
- icalrecurrencetype icRec;
- // wDay in a timezone context means "week in month", 5 for last week in month
- if (tsTimeZone.stStdDate.wYear > 0 || tsTimeZone.stStdDate.wDay > 5 ||
- tsTimeZone.stStdDate.wDayOfWeek > 7 ||
- tsTimeZone.stDstDate.wYear > 0 || tsTimeZone.stDstDate.wDay > 5 ||
- tsTimeZone.stDstDate.wDayOfWeek > 7)
- return MAPI_E_INVALID_PARAMETER;
- // make a new timezone
- icTZComp = icalcomponent_new(ICAL_VTIMEZONE_COMPONENT);
- icalcomponent_add_property(icTZComp, icalproperty_new_tzid(strTZID.c_str()));
- // STD
- icComp = icalcomponent_new_xstandard();
- icTime = icaltime_from_timet_with_zone(SystemTimeToUnixTime(tsTimeZone.stStdDate), 0, nullptr);
- icalcomponent_add_property(icComp, icalproperty_new_dtstart(icTime));
- if (tsTimeZone.lStdBias == tsTimeZone.lDstBias || tsTimeZone.stStdDate.wMonth == 0 || tsTimeZone.stDstDate.wMonth == 0) {
- // std == dst
- icalcomponent_add_property(icComp, icalproperty_new_tzoffsetfrom(-tsTimeZone.lBias *60));
- icalcomponent_add_property(icComp, icalproperty_new_tzoffsetto(-tsTimeZone.lBias *60));
- } else {
- icalcomponent_add_property(icComp, icalproperty_new_tzoffsetfrom( ((-tsTimeZone.lBias) + (-tsTimeZone.lDstBias)) *60) );
- icalcomponent_add_property(icComp, icalproperty_new_tzoffsetto(-tsTimeZone.lBias *60));
- // create rrule for STD zone
- icalrecurrencetype_clear(&icRec);
- icRec.freq = ICAL_YEARLY_RECURRENCE;
- icRec.interval = 1;
- icRec.by_month[0] = tsTimeZone.stStdDate.wMonth;
- icRec.by_month[1] = ICAL_RECURRENCE_ARRAY_MAX;
- icRec.week_start = ICAL_SUNDAY_WEEKDAY;
- // by_day[0] % 8 = weekday, by_day[0]/8 = Nth week, 0 is 'any', and -1 = last
- icRec.by_day[0] = tsTimeZone.stStdDate.wDay == 5 ? -1*(8+tsTimeZone.stStdDate.wDayOfWeek+1) : (tsTimeZone.stStdDate.wDay)*8+tsTimeZone.stStdDate.wDayOfWeek+1;
- icRec.by_day[1] = ICAL_RECURRENCE_ARRAY_MAX;
-
- icalcomponent_add_property(icComp, icalproperty_new_rrule(icRec));
- }
- icalcomponent_add_component(icTZComp, icComp);
- // DST, optional
- if (tsTimeZone.lStdBias != tsTimeZone.lDstBias && tsTimeZone.stStdDate.wMonth != 0 && tsTimeZone.stDstDate.wMonth != 0) {
- icComp = icalcomponent_new_xdaylight();
- icTime = icaltime_from_timet_with_zone(SystemTimeToUnixTime(tsTimeZone.stDstDate), 0, nullptr);
- icalcomponent_add_property(icComp, icalproperty_new_dtstart(icTime));
- icalcomponent_add_property(icComp, icalproperty_new_tzoffsetfrom(-tsTimeZone.lBias *60));
- icalcomponent_add_property(icComp, icalproperty_new_tzoffsetto( ((-tsTimeZone.lBias) + (-tsTimeZone.lDstBias)) *60) );
- // create rrule for DST zone
- icalrecurrencetype_clear(&icRec);
- icRec.freq = ICAL_YEARLY_RECURRENCE;
- icRec.interval = 1;
- icRec.by_month[0] = tsTimeZone.stDstDate.wMonth;
- icRec.by_month[1] = ICAL_RECURRENCE_ARRAY_MAX;
- icRec.week_start = ICAL_SUNDAY_WEEKDAY;
- icRec.by_day[0] = tsTimeZone.stDstDate.wDay == 5 ? -1*(8+tsTimeZone.stDstDate.wDayOfWeek+1) : (tsTimeZone.stDstDate.wDay)*8+tsTimeZone.stDstDate.wDayOfWeek+1;
- icRec.by_day[1] = ICAL_RECURRENCE_ARRAY_MAX;
-
- icalcomponent_add_property(icComp, icalproperty_new_rrule(icRec));
- icalcomponent_add_component(icTZComp, icComp);
- }
- *lppVTZComp = icTZComp;
- return hrSuccess;
- }
- /**
- * Returns TIMEZONE_STRUCT structure from the string Olson city name.
- * Function searches zoneinfo data in linux
- *
- * @param[in] strTimezone timezone string
- * @param[out] ttTimeZone TIMEZONE_STRUCT of the cityname
- * @return MAPI error code
- * @retval MAPI_E_INVALID_PARAMETER strTimezone is empty
- * @retval MAPI_E_NOT_FOUND cannot find the timezone string in zoneinfo
- */
- HRESULT HrGetTzStruct(const std::string &strTimezone, TIMEZONE_STRUCT *ttTimeZone)
- {
- icaltimezone *lpicTimeZone = NULL;
- icalcomponent *lpicComponent = NULL;
-
- if (strTimezone.empty())
- return MAPI_E_INVALID_PARAMETER;
- lpicTimeZone = icaltimezone_get_builtin_timezone(strTimezone.c_str());
- if (lpicTimeZone == NULL)
- return MAPI_E_NOT_FOUND;
- lpicComponent = icaltimezone_get_component(lpicTimeZone);
- if (lpicComponent == NULL)
- return MAPI_E_NOT_FOUND;
- return HrParseVTimeZone(lpicComponent, NULL, ttTimeZone);
- }
- } /* namespace */
|