[Tickets #12773] Re: Thunderbird lightning caldav serial event time failure with summer/wintertime change

noreply at bugs.horde.org noreply at bugs.horde.org
Wed Mar 5 23:27:04 UTC 2014


DO NOT REPLY TO THIS MESSAGE. THIS EMAIL ADDRESS IS NOT MONITORED.

Ticket URL: http://bugs.horde.org/ticket/12773
------------------------------------------------------------------------------
  Ticket             | 12773
  Updated By         | benrose at math.princeton.edu
  Summary            | Thunderbird lightning caldav serial event time failure
                     | with summer/wintertime change
  Queue              | Kronolith
  Version            | 4.1.3
  Type               | Bug
  State              | Not A Bug
  Priority           | 1. Low
  Milestone          |
  Patch              |
  Owners             |
------------------------------------------------------------------------------


benrose at math.princeton.edu (2014-03-05 23:27) wrote:

So I have a workaround... but not quite a fix yet.

I started knowing that something is wonky with sending UTC in DTSTART  
and DTEND. Google wasn't doing that in their iCal. Also Google was  
sending a VTIMEZONE section. Investigating the RFC for iCal formats.It  
says:

-----
The "VTIMEZONE" calendar component MUST be present if the iCalendar  
object contains an RRULE that generates dates on both sides of a time  
zone shift (e.g. both in Standard Time and Daylight Saving Time)  
unless the iCalendar object intends to convey a floating time (See the  
section "4.1.10.11 Time" for proper interpretation of floating time).  
It can be present if the iCalendar object does not contain such a  
RRULE. In addition, if a RRULE is present, there MUST be valid time  
zone information for all recurrence instances.
-----

So I added a VTIMEZONE section to my file I posted earlier, horde's  
output.ics. That didn't work. I took that back out, and then just  
changed the dates in DTSTART and DTEND to include a TZID definition  
like in google's ics file. Upon importing this ICS file, it worked  
properly! Clearly the answer is to be using "floating times" as the  
RFC suggests, i.e. not UTC which changes relative to timezones with  
observe DST.

Of course, I'm sure Horde wasn't violating RFC on purpose. So I  
started looking in the Horde code where DTSTART and DTEND are parsed.  
I found this of interest in kronolith/lib/Event.php:

         // For certain recur types, we must output in the event's timezone
         // so that the BYDAY values do not get out of sync with the UTC
         // date-time. See Bug: 11339
         if ($this->recurs()) {
             switch ($this->recurrence->getRecurType()) {
             case Horde_Date_Recurrence::RECUR_WEEKLY:
             case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY:
             case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY:
                 if (!$this->timezone) {
                     $this->timezone = date_default_timezone_get();
                 }
             }
         }

Clearly Horde is trying to set a timezone to stay in RFC compliance  
for recurring events. But the TZID parameter was not being sent in the  
DTSTART and DTEND parameters in the ICS download. Skip down to:

             if ($this->timezone) {
                 try {
                     $tz = $GLOBALS['injector']->getInstance('Horde_Timezone');
                     $vEvents[] = $tz->getZone($this->timezone)->toVtimezone();
                     $params['TZID'] = $this->timezone;
                 } catch (Horde_Exception $e) {
                 }
             }

where execution was actually ending in an exception. The line that was  
failing was:

                     $vEvents[] = $tz->getZone($this->timezone)->toVtimezone();

which I just found by inserting some syslog lines here and there until  
I narrowed down the failure. So then inside toVtimezone() I added some  
more debug, but it was never being executed. So I looked in  
pear/php/Horde/Timezone.php to see what was failing. It was the  
function getZone being called above before passing to toVtimezone():

     public function getZone($zone)
     {
         if (!$this->_zones) {
             $this->_extractAndParse();
         }
         $alias = isset($this->_links[$zone]) ? $this->_links[$zone] : $zone;
         if (!isset($this->_zones[$alias])) {
             throw new Horde_Timezone_Exception(sprintf('Timezone %s  
not found', $zone));
         }
         $this->_zones[$alias]->setTzid($alias);
         return $this->_zones[$alias];
     }

I found that

             $this->_extractAndParse();

was the line failing. Looking there, I found what was failing:

     protected function _extractAndParse()
     {
     ....

     if (!$this->_tmpfile) {
         $this->_download();
     }

It was the _download() call failing. Well, turns out _download is  
trying to pull from $conf['timezone']['location'] which is defined in  
kronolith/config/conf.php as ftp://ftp.iana.org/tz/tzdata-latest.tar.gz'

So I found that if you've got SELinux enforcing (which you should),  
you'll need to make sure you enable httpd_can_network_connect so that  
kronolith can download this data from the iana ftp server. You'll also  
need to open iptables/firewall/network access to ftp.iana.org if you  
lock that down. Once I did this, Horde was sending ICS properly again,  
every recurring event had their DTSTART and DTEND parameters with a  
TZID specification. Hooray!

However, for those of us more security-minded, I'd like to *not* have  
httpd_can_network_connect enabled. I'd like to be able to load this  
file in outside of CGI. So I downloaded the file manually and set the  
following in kronolith/config/conf.php:

$conf['timezone']['location'] = 'file:///var/www/horde/tzdata-latest.tar.gz';

But now the code in _download in pear/php/Horde/Timezone.php failed around:

         try {
             if ($url['scheme'] == 'ftp') {
                 $vfs = new Horde_Vfs_Ftp(array('hostspec' => $url['host'],
                                                'username' => 'anonymous',
                                                'password' => 'anonymous'));
             } else {
                 $vfs = new Horde_Vfs_File();
             }
             $this->_tmpfile = $vfs->readFile(dirname($url['path']),
                                              basename($url['path']));
         } catch (Horde_Vfs_Exception $e) {
             throw new Horde_Timezone_Exception($e);
         }

At the point:

                 $vfs = new Horde_Vfs_File();

Should this perhaps be:

                 $vfs = new Horde_Vfs_File(array('vfsroot' =>  
Horde::getTempDir()));

as a proper construction?





More information about the bugs mailing list