'.__('Your cache directory %s has been created','amr-ical-events-list'),''.$cache_path.''); } else { die( '
'.sprintf(__('Error creating cache directory %s. Please check permissions','amr-ical-events-list'),$cache_path)); } } return $cache_path; } /* Return the cache filename for the specified URL. */ function amr_get_cache_filename($url) { $extension = ICAL_EVENTS_CACHE_DEFAULT_EXTENSION; $matches = array(); if (preg_match('/\.(\w+)$/', $url, $matches)) { $extension = $matches[1]; } return md5($url) . ".$extension"; } /* Cache the specified URL and return the name of the destination file. */ if( !class_exists( 'WP_Http' ) ) include_once( ABSPATH . WPINC. '/class-http.php' ); function amr_check_start_of_file ($data) {// check if the file looks like a icsfile if (empty($data)) return false; $checkstart = substr($data,0,15); if (!($checkstart == 'BEGIN:VCALENDAR')) { If (ICAL_EVENTS_DEBUG) { echo '
No VCALENDAR in file. Start has: '.$checkstart.' end'; } echo '!'; echo ''; return false; } return true; } function amr_set_http_timeout($val) { global $amr_options; if (!empty($amr_options['timeout'])) return ($amr_options['timeout']); else return $val; } function amr_cache_url($url, $cache=ICAL_EVENTS_CACHE_TTL) { global $amr_lastcache; global $amr_globaltz; global $amr_options; $text = ''; // if any args are sent then all must be sent - use wp defaults more or less // so better to use filters add_filter( 'http_request_timeout', 'amr_set_http_timeout' ); //add_filter( 'http_request_redirection_count', 'amr_' ); //'httpversion' => apply_filters( 'http_request_version', '1.0' ), //or 1.1 /* curl_setopt($c, CURLOPT_RETURNTRANSFER, true); // just says to return rather than echo curl_setopt($c, CURLOPT_USERAGENT, 'PHP/'.PHP_VERSION); curl_setopt($c, CURLOPT_ENCODING, ''); if( strstr( $resource, 'https' ) !== FALSE ) { curl_setopt($c, CURLOPT_SSLVERSION, 3); curl_setopt($c, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 2); } curl_setopt($c, CURLOPT_COOKIESESSION, true); curl_setopt($c, CURLOPT_HEADER, true); if( !ini_get('safe_mode') ){ curl_setopt($c, CURLOPT_FOLLOWLOCATION, true); } */ // If (ICAL_EVENTS_DEBUG) echo '
url before decode: '.$url.'
'; $url = html_entity_decode($url); // If (ICAL_EVENTS_DEBUG) echo '
url decoded: '.$url.'
'; $cachedfile = get_cache_file($url); if ( file_exists($cachedfile) ) { $c = filemtime($cachedfile); if ($c) $amr_lastcache = amr_newDateTime(strftime('%c',$c)); else $amr_lastcache = ''; } else { $c = false; $amr_lastcache = amr_newDateTime(strftime('%c',0)); } // must we refresh ? if ( isset($_REQUEST['nocache']) or isset($_REQUEST['refresh']) or (!(file_exists($cachedfile))) or ((time() - ($c)) >= ($cache*60*60))) { If (ICAL_EVENTS_DEBUG) echo '
Get ical file remotely, it is time to refresh or it is not cached:
'; amrical_mem_debug('We are going to refresh next'); //$url = urlencode($u); - do NOT encode - that gives an invalid URL response $check = wp_remote_get($url); // if use args, must set all - rather use filters and let wp do its thing if (( is_wp_error($check) ) or (isset ($check['response']['code']) and !($check['response']['code'] == 200)) or (isset ($check[0]) and preg_match ('#404#', $check[0]))) {/* is this bit still meaningful or needed ? */ If (ICAL_EVENTS_DEBUG) { echo '
Http request failed
Dumping response: '; var_dump($check); } if (is_wp_error($check)) $text = '
'.$check->get_error_message().'
'; else $text = ''; $data = false; } elseif (!stristr($check['headers']['content-type'],'text/calendar')) { if (amr_check_start_of_file ($data = $check['body'])) { // well wrong content type, but has the content!! - bad calendar provider $data = $check['body']; if (current_user_can('manage_options')) { echo '
This message is only shown to the administrator, and only when we refresh the file.'; echo '
The ics url given is issuing an incorrect content type of text/html.' .' It should be text/calendar. ' .' Luckily we persevere and check if the content looks like an ics file.' .'Please inform the provider of the url. ' .'Their urls may not be recognised by browsers as ics files.
'; } } else { if (ICAL_EVENTS_DEBUG) { echo '
The url given is not returning a calendar file'; echo '
The response was '; var_dump($check['response']); echo '
The content type is '.$check['headers']['content-type']; echo '
The content type of an ics file should be text/calendar.
'; } $data = false; } } else $data = $check['body']; // from the http request if (!amr_check_start_of_file ($data)) { $text .= ' '.sprintf(__('Error getting calendar file with htpp or curl %s','amr-ical-events-list'), $url); if ( file_exists($cachedfile) ) { // Try use cached file if it exists if (is_object($amr_lastcache)) $text .= ' ...'.sprintf(__('Using File last cached at %s','amr-ical-events-list'), $amr_lastcache->format('D c')); else $text .= ' ...'.__('File last cached time not available','amr-ical-events-list'); echo '!'; return($cachedfile); //return file not data } else { echo '!'; return (false); } return (false); } else If (ICAL_EVENTS_DEBUG) { echo '
We have vcalendar in start of file';} // somebody wanted to pre process ics files that were not well generated? // A filter could be added here, but I'm not keen - could add to support load? if ($data) { /* now save it as a cached file */ $data = apply_filters('amr-ics-filter', $data, $url); if ($dest = fopen($cachedfile, 'w')) { if (!(fwrite($dest, $data))) die ('Error writing cache file'.$dest); fclose($dest); $amr_lastcache = amr_newDateTime (date('Y-m-d H:i:s')); } else { echo '
Error opening or creating the cached file
'.$cachedfile; return (false); } } else { echo '
Error opening remote file for refresh '.$url; return false;} if (!isset($amr_lastcache)) $amr_lastcache = amr_newDateTime (date('Y-m-d H:i:s')); } else {}// no need to refresh, use the cached file return ($cachedfile); } function amr_checkQuotedString($delimiter, $replace, $input) { // rfc 5545 says colons, semicoln and quoted strings can/must be inside doublequotes $t = $input; if (stristr($t, '="')) { //if have '="' then have a quoted parameter value // get the substring within double quotes if (preg_match_all('/"([^"]+)"/', $t, $m)) { // then we got our string //echo '
PREG ALL';var_dump ($m[0]); foreach ($m[0] as $i=> $quotedstring) { $s = str_replace($delimiter, $replace, $quotedstring); $t = str_replace($quotedstring, $s, $t); // swop colons only in the quoted string //echo '
'.$i.$t; } } } return $t; } function amr_explodeByColon( $input ) { // rfc 5545 says colons, semicoln and quoted strings can/must be inside doublequotes // maybe only required for parameter values? else to much overhead - actually maybe only needed for attendee stuff // and then only if there are quoted strings // NOTE PARAMETER VALUES may NOT contain quotes, so can have =" // HOWEVER can have colon, semicolon or comma // DESCRIPTION with colon does not have to be double quoted // actually I think it's only the first non quoted colon we need ? $t = amr_checkQuotedString(':','%3A', $input) ; $t = str_replace('mailto:', 'mailto ', $t); //stop any other mailtos from exploding //now we can explode by colon //$arr = explode( ':', $t ); //only want the first unescaped colon, else colons ok in DESCRIPTION etc 20150814 $colon = stripos ( $t , ':' ); If (!$colon) return false; $arr[0] = substr ( $t, 0, $colon ); $arr[1] = substr ( $t, $colon+1 ); return $arr; } function amr_explodeBySemi( $input ) { // rfc 5545 says colons, semicoln and quoted strings can/must be inside doublequotes // maybe only required for parameter values? else to much overhead - actually maybe only needed for attendee stuff // and then only if there are quoted strings // NOTE PARAMETER VALUES may NOT contain quotes, so can have =" // HOWEVER can have colon, semicolon or comma $t = amr_checkQuotedString(';','%3B', $input) ; //now we can explode by colon $arr = explode( ';', $t ); //else acn treat normally return $arr; } function amr_parseParameters($properties) { // anything from name separated by semicolons until the ':' //echo '
Properties:';var_dump($properties); $parameters = amr_explodeBYSemi ($properties); //echo '
Parameters:';var_dump($parameters); return $parameters; } function amr_parseAttendees ($arraybycolon) { /* receive full string parsed to array */ /* ATTENDEE;X-NUM-GUESTS=0:mailto:1bfb88li8v385q41k6s7fnl9ls@group.calendar.go ogle.com ATTENDEE;ROLE=REQ-PARTICIPANT;DELEGATED-FROM="mailto:bob@ example.com";PARTSTAT=ACCEPTED;CN=Jane Doe:mailto:jdoe@ example.com ATTENDEE;SENT-BY=mailto:jan_doe@example.com;CN=John Smith: mailto:jsmith@example.com NOT USING FOR NOW - INTERNAL ATTENDEES ONLY */ // the first one should start with ATTENDEE $parameters = amr_parseParameters($arraybycolon[0]); if ((!$parameters[0]) == 'ATTENDEE') { //garbage ? echo '
Error: Bad attendee parameters
'; return false; } unset($parameters[0]); foreach ($parameters as $param) { $parts = explode('=',$param); if (count($parts) == 2) { //X-NUM-GUESTS=0, CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Common Name; $attendee[$parts[0]] = trim($parts[1],' "'); } else { // ignore it ? bad parameter ? } } $pos_email = trim($arraybycolon[1],' '); // 201710 allow for crap data if (!stristr($pos_email, 'mailto')) { $email = $pos_email; } else { $email = amr_parseMailto($pos_email); } if ((!empty($email) and is_email($email))) $attendee['MAILTO'] = $email; // some files issue mailto's with an email address //201506 sometimes those mail addresses are not valid at all //ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;CN=Common Name;X-NUM-GUESTS=0 else $attendee['text'] = $email; // set default values if (!isset($attendee['PARTSTAT'])) // assume it needs action since not accepted $attendee['PARTSTAT'] = 'NEEDS-ACTION'; //echo'
Whata values ';var_dump($attendee); //amr_parseMailto ($text) return($attendee); // a single attendee } function amr_parseMailto ($text) { //mailto:ovcweb@uoguelph.ca return email //2017 somehow we have a space instead of : $text = str_replace('mailto','',$text); $text = str_replace('%3A','',$text); $text = (trim($text,' :"')); return ($text); } function amr_parseOrganiser($arraybysemicolon) { /* receive full string parsed to array split by the semicolon [0]=>ORGANIZER;SENT-BY="mailto [1]=>dwood@uoguelph.ca":mailto:ovcweb@uoguelph.ca or [0]=>ORGANIZER;CN=Webmaster - OVC;SENT-BY="mailto [1] => bagunn@uoguelph.ca":mailto:ovcweb@uoguelph.ca ORGANIZER;CN="Cyrus Daboo":mailto:cyrus@example.com */ $org = array(); $p0 = explode(';',$arraybysemicolon[0]); // we don't have a ':' for some reason to do with possible colon clash $m = str_replace( 'mailto', '',$arraybysemicolon[1]); $mailto = trim($m,' :'); //201710 //$m = explode(':',$arraybysemicolon[1]); // if (ICAL_EVENTS_DEBUG) {echo '
m :
'; var_dump($m); echo '
p0 :
'; var_dump($p0);} /* foreach ($m as $i => $m2) { if (strtoupper($m2) == 'mailto') { $mailto = rtrim($m[$i+1],'"'); } } */ foreach ($p0 as $i => $p) { $p1 = explode('=',$p); if (isset ($p1[0])) { $org['type'] = $p1[0]; /* if (!empty($p1[1])) $org['typevalue'] = $p1[1]; *** Parse this properly if we wantto handle complex attendees */ if ( ($p1[0] == 'SENT-BY') and (!empty($p1[1]))) { $sentby = trim($m[0],'"'); $org['SENT-BY'] = $sentby; } else { if (($p1[0] == 'CN') and (!empty($p1[1]))) { $org['CN'] = trim( $p1[1], '"'); } } } } if (!empty($mailto)) $org['MAILTO'] = $mailto; if (empty($org)) return ($arraybysemicolon); return ($org); } /** * Parse a Time Period field. */ function amr_parsePeriod($text,$tzobj) { $periodParts = explode('/', $text); if (!($start = amr_parseDateTime($periodParts[0], $tzobj))) return (false); if ($duration = amr_parseDuration($periodParts[1])) return array('start' => $start, 'duration' => $duration); else { if (!($end = amr_parseDateTime($periodParts[1], $tzobj))) return (false); else { return array('start' => $start, 'end' => $end); } } } /** * Parses a DateTime field and returns a datetime object, with either it's own tz if it has one, or the passed one */ function amr_parseDateTime($d, $tzobj) { global $amr_globaltz; global $utczobj; /* 19970714T133000 ;Local time 19970714T173000Z ;UTC time tz dealt with already ?*/ if (empty($d)) { echo 'Unexpected error - empty date string to parse '; return false; } if ((substr($d, strlen($d)-1, 1) === 'Z')) { /*datetime is specifed in UTC */ $tzobj = $utczobj; $d = substr($d, 0, strlen($d)-1); } $date = substr($d,0, 4).'-'.substr($d,4, 2).'-'.substr($d,6, 2); if (strlen ($d) > 8) { $time = substr($d,9 ,2 ).':'.substr($d,11 ,2 ) ; /* has to at least have hours and mins */ } else $time = '00:00'; if (strlen ($d) > 13) { $time .= ':'.substr($d,13 ,2 ); } else $time .= ':00'; /* Now create our date with the timezone in which it was defined , or if local, then in the plugin glovbal timezone */ $dt = amr_create_date_time ($date.' '.$time, $tzobj); //if (!$dt) { amr_tell_admin_the_error ('Failed to parse'.$d); } return ($dt); } /* Parses a Date field. */ function amr_parseRange($range, $daterange, $tzobj) { /* For RECURRENCE-ID; Strings like: VALUE=DATE:19960401 RANGE=THISANDFUTURE:19960120T120000Z RANGE=THISANDPRIOR:19960120T120000Z */ If (isset ($_REQUEST['debugexc'])) { echo '
Got Range '.$range.' with '.$daterange.'
'; } $r = explode (':', $daterange); if (!($thisanddate = amr_parseDateTime($r[1], $tzobj))) return (false); If (isset ($_REQUEST['debugexc'])) { echo '
Got range '.$range.' "THISAND" date '.$thisanddate ->format('c').'
'; } return (array('RANGE'=>$p[0],'DATE'=> $thisanddate)); } /* Parses a Date field. */ function amr_parseDate($text, $tzobj) { /* VALUE=DATE: 19970101,19970120,19970217,19970421 19970526,19970704,19970901,19971014,19971128,19971129,19971225 VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Berlin:20061223 */ $p = explode (',',$text); /* if only a single will still return one array value */ foreach ($p as $i => $v) { $datestring = substr($v, 0, 4).'-'.substr($v,4, 2).'-'.substr($v,6, 2); $dates[] = amr_create_date_time ($datestring, $tzobj); /*try { // $dates[] = new DateTime(substr($v,0, 4).'-'.substr($v,4, 2).'-'.substr($v,6, 2), $tzobj); //} //catch(Exception $e) { // echo '
Unable to create DateTime object from '.$text.'
'.$e->getMessage(); // return (false); }*/ } return ($dates); } /* ------------------------------------------------------------------ */ function amr_parseTZDate ($value, $tzid) { $tzobj = amr_parseTZID($text); if (!($d = amr_parseDateTime ($value, $tzobj))) { return(false); } else return ($d); } /* ------------------------------------------------------------------ */ function amr_deduceTZID($icstzid) { // we have something that php didn't like, so we will try work something out // Not great, really should use the filter function rather global $amr_globaltz, $globaltzstring; // $strip = array ('(',' '); // $icstzid = str_replace($strip,'',$icstzid); $gmtend = stripos($icstzid,')'); /* do we have a brackedt GMT ? */ //if (isset ($_REQUEST['tzdebug'])) {echo '
Check for a bracketed gmt? = '.$gmtend.' in string '.$icstzid ; } if (!empty ($gmtend) ) { $icstzid = str_replace(')','/',$icstzid); $icstzcities = explode ('/',$icstzid); /* could be commas, could be slashes */ if (isset ($_REQUEST['tzdebug'])) {echo '
strip the gmt out '; print_r($icstzcities);} $gmt = stripos( $icstzid, 'GMT'); /* do we have a brackedt GMT ? */ if (!empty($gmt)) unset ($icstzcities[0]); /* don't want the GMT - potentially misleading */ } else { /* Maybe we have a list of cities and maybe we do not */ $icstzcities = array(); $temp = explode (',',$icstzid); /* could be commas, could be slashes */ foreach ($temp as $temp2) { $temp3 = explode ('/',$temp2); $icstzcities = array_merge($icstzcities, $temp3); } } foreach ($icstzcities as $i=>$icscity) { $icstzcities[$i] = trim($icscity,' '); } //if (isset ($_REQUEST['tzdebug'])) { echo '
Do we have a City?
';print_r($icstzcities);} $globalcontcity = explode ('/',$globaltzstring); if (isset ($globalcontcity[1]) ) $globalcity = $globalcontcity[1]; else $globalcity = $globalcontcity[0]; // if (isset ($_REQUEST['tzdebug'])) { echo '
text = '.$text.'
icstzid = '.$icstzid.' and wp tz = '.$globalcity.'
'; print_r($icstzcities); } if (in_array($globalcity, $icstzcities)) { /* if one of the cities in the tzid matches ours, again we can use the matched one */ $tzname = $globaltzstring; } else { $alltzcities = amr_get_timezone_cities (); if (isset($alltzcities[$icstzid])) { /* then it is a normal php timezone we know about, so we can proceed */ $tzname = $icstzid; } else { foreach ($icstzcities as $i=>$c) { if (isset ($alltzcities[$c] )) { /* try each of the cities if we have mutiple */ $tzname = $alltzcities[$c]; break; } } if (isset ($_REQUEST['tzdebug'])) {echo '
No match to known cities'; } } } /* */ if (!isset ($tzname)) { /* see if we do it with GMT after all ? */ if (isset($icstzcities[0])) { $tryoffset = str_replace('GMT','',$icstzcities[0]); if (empty($tryoffset) or (is_int($tryoffset))) { $tzname = amr_getTimeZone($tryoffset); if (isset ($_REQUEST['tzdebug'])) {echo '
Try see if offset:'.$tryoffset. ' gave '.$tzname; } } else { $tzname = amr_unknown_timezone($icstzid, $globaltzstring); } } else { $tzname = amr_unknown_timezone($icstzid, $globaltzstring); } } if (isset ($_REQUEST['tzdebug'])) echo '
Timezone must be: '.$tzname.'
'; $tz = amr_try_timezone ($tzname); if (!$tz) $tz = $amr_globaltz; return ($tz); } /* ------------------------------------------------------------------ */ function amr_tz_error_handler () { //cannot have anonymous function in php < 5.3 } /* ------------------------------------------------------------------ */ function amr_parseTZID($text) { //also used when editing an event global $amr_globaltz, // the main timezone object $globaltzstring, // the string for the timezone object $icsfile_tzname, // the timezone name string in the ics file $icsfile_tz; // the ics timezone object that we last parsed and ended up with (often it's all the same /* take a string that may have a olson tz object and try to return a tz object */ /* accept long and short TZ's, --- assume website tz if not valid eg Zimbra's: GMT+01.00/+02.00 */ $icstzid = trim($text,'"=' ); /* check for enclosing quotes like zimbra issues */ //----------------------------------- //----------------------------------- is it same as wordpress ? if (empty($globaltzstring)) $globaltzstring = timezone_name_get($amr_globaltz); if ($globaltzstring == $icstzid ) {/* if the timezone matches the wordpress or shortcode time zone, then we are cool ! */ $icsfile_tzname = $icstzid; // set the global $icsfile_tz = $amr_globaltz; if (isset ($_REQUEST['tzdebug'])) echo '
'.$icstzid.' Matches wordpress tz.'; return ($amr_globaltz); } //----------------------------------- is it same as previously parsed ? if yes, go with that if (!empty($icsfile_tzname) and ($icsfile_tzname == $icstzid )) { if (isset ($_REQUEST['tzdebug'])) echo '
'.$icstzid.' Matches previously parsed tz.' ; return ($icsfile_tz); } //----------------------------------- is it a valid php timezone ? $timezone_identifiers = (DateTimeZone::listIdentifiers()); //foreach ($timezone_identifiers as $i=> $z) {echo '
'.$i; var_dump($z);} if (in_array($icstzid,$timezone_identifiers, false)) { // we now have a valid php timezone if (isset ($_REQUEST['tzdebug'])) {echo '
Php should like:'.$icstzid; } $tz = amr_try_timezone ($icstzid); // this should work else simething weird going on if (!empty($tz)) return($tz); } //----------------------------------- if not already a valid php timezone, can we make it a valid zone ? $tzid = apply_filters('amr-timezoneid-filter',$icstzid); //apply filters like for windows zones etc // let us see if php likes it try { set_error_handler( 'amr_tz_error_handler'); /* ignore errors , just giveit a go*/ $tz = timezone_open($tzid); restore_error_handler(); if ($tz) { $tzname = $tzid; if (isset ($_REQUEST['tzdebug'])) {echo '
Php liked:'.$tzid; } } else $tz = amr_deduceTZID($tzid) ; } catch(Exception $e) {/* we tried the filter but php did't like, so lets try fix it */ /* else try figure the timezone out */ $tz = amr_deduceTZID($tzid) ; } if (empty($tz)) { if (isset ($_REQUEST['tzdebug'])) {echo '
Giving up on timezone: '.$icstzid; } return false; // we couldn't figure it out } if (empty($icsfile_tzname)) { // if this is the first time we parsed, lets save the hard work $icsfile_tzname = $icstzid; // what was actually in the file $icsfile_tz = $tz; // this is the php tz we ended up with } return ( $tz); } /* ------------------------------------------------------------------ */ function amr_try_timezone ($tzname) { try { $tz = timezone_open($tzname); } catch(Exception $e) { $text = 'Unable to create Time zone object., Using wp default.
'.$e->getMessage(); amr_tell_admin_the_error ($text); //echo '
Unable to create Time zone object., Using wp default.
'.$e->getMessage(); return ($amr_globaltz); } return ($tz); } /* ------------------------------------------------------------------ */ function amr_unknown_timezone ($text, $tzname) { $emessage = 'Unable to deal with timezone like this: '.$text; // echo ''; // if (isset ($_REQUEST['tzdebug']) or ICAL_EVENTS_DEBUG) { if (is_super_admin()) { echo '
Message to logged-in admin only: '.$emessage.''; echo '- Making an assumption! Using '.$tzname.'
'; } return ($tzname); } /* ------------------------------------------------------------------ */ function amr_parseSingleDate($VALUE='DATE-TIME', $text, $tzobj) { /* used for those properties that should only have one value - since many other dates can have multiple date specs, the parsing function returns an array Reduce the array to a single value */ $arr = amr_parseVALUE($VALUE, $text, $tzobj); if (is_array($arr)) { if (count($arr) > 1) { error_log ( '
Unexpected multiple date values'.$text); } else return ($arr[0]); } return ($arr); } function amr_parseVALUE($VALUE, $text, $tzobj) { /* amr parsing a value like VALUE=PERIOD:19960403T020000Z/19960403T040000Z, 19960404T010000Z/PT3H VALUE=DATE:19970101,19970120,19970217,19970421,.. 19970526,19970704,19970901,19971014,19971128,19971129,19971225 VALUE=DATE;TZID=/mozilla.org/20070129_1/Europe/Berlin:20061223 */ //if (ICAL_EVENTS_DEBUG) {echo '
For value: '.$VALUE.' '.$text;} if (empty($text)) { if (ICAL_EVENTS_DEBUG) {echo '
For value: '.$VALUE.' text is blank';} return (false); } switch ($VALUE) { case 'DATE-TIME': { if (!($d = amr_parseDateTime($text, $tzobj))) return (false); else return ($d); } case 'DATE': {if (!($d = amr_parseDate($text, $tzobj))) return (false); else return ($d); } case 'PERIOD': {if (!($d = amr_parsePeriod($text, $tzobj))) return (false); else return ($d); } default: { /* something like DATE;TZID=/mozilla.org/20070129_1/Europe/Berlin */ $p = explode (';',$VALUE); if (!($p[0] === 'DATE')) { if (ICAL_EVENTS_DEBUG) {echo 'Error: Unexpected data in file '; print_r($p);} return (false); } else { if (substr ($p[1], 0, 4) === 'TZID') {/* then we have a weird TZ */ $tzobj = amr_deal_with_tzpath_in_date (substr($p[1],5)); /* pass the rest of the string over for tz extraction */ if (!($d = amr_parseDate($text, $tzobj))) { amr_tell_admin_the_error ('Fail parse tz date'.$p[1]); return (false); } else return ($d); } else { if (ICAL_EVENTS_DEBUG) {echo 'Error: Unexpected data in file '; print_r($p[1]);} return (false); }; } } return (false); } } /** * Parse a Duration Value field.*/ function amr_parseDuration($text) { /* A duration of 15 days, 5 hours and 20 seconds would be: P15DT5H0M20S A duration of 7 weeks would be: P7W, can be days or weeks, but not both we want to convert so can use like this +1 week 2 days 4 hours 2 seconds ether for calc with modify or output. Could be neg (eg: for trigger) */ if (isset($_GET['debugdur'])) {echo '
Entering Pregmatch.. '.$text;} if (preg_match('/([+]?|[-])P(([0-9]+W)|([0-9]+D)|)(T(([0-9]+H)|([0-9]+M)|([0-9]+S))+)?/', trim($text), $durvalue)) { /* 0 is the full string, 1 is the sign, 2 is the , 3 is the week , 6 is th T*/ if (isset($_GET['debugdur'])) {echo '
Pregmatch gives '; var_dump($durvalue);} if ($durvalue[1] == "-") { // Sign. $dur['sign'] = '-'; } // Weeks if (!empty($durvalue[3])) $dur['weeks'] = rtrim($durvalue[3],'W'); if (count($durvalue) > 4) { // Days. if (!empty($durvalue[4])) $dur['days'] = rtrim($durvalue[4],"D"); } if (count($durvalue) > 5) { // Hours. if (!empty($durvalue[7])) $dur['hours'] = rtrim($durvalue[7],"H"); if (isset($durvalue[8])) { // Mins. $dur['mins'] = rtrim($durvalue[8],"M"); } if (isset($durvalue[9])) { // Secs. $dur['secs'] = rtrim($durvalue[9],"S"); } } if (!empty ($dur)) return $dur; else { // possibly error in input data if (isset($_GET['debugdur'])) {echo '
Possibly error in input data that pregmatch did not deal with .. '.$text;} return false; } return $dur; } else { return false; } } function amr_parse_CATEGORIES ($text ) { $cats = explode(',',$text); // will return an array, but since we can have multiple CATEGORIES lines, will get an array of arrays .. confusing return($cats); } function amr_parseRDATE ($string, $tzobj ) { /* RDATE:19970714T123000Z RDATE:19970714T083000 RDATE;TZID=US-EASTERN:19970714T083000 RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H - not supported yet RDATE;VALUE=DATE:19970101,19970120,19970217,19970421,19970526,19970704,19970901,19971014,19971128,19971129,19971225 could be multiple dates after : */ if (empty($string)) return false; if (is_object($string)) {/* already parsed */ return($string); } //echo '*** Here'; var_dump($string); if (is_array($string) ) { $rdatearray = array(); foreach ($string as $i => $rdatestring) { // $r = $string[0]; if (is_object($rdatestring)) {/* already parsed and is an array of dates */ return($string); } else { if (isset($_GET['debugexc'])) {echo '
***Doing next r or exdate '.$i.'
'.print_r($rdatestring,true).'
'; } $rdate = amr_parseRDATE ($rdatestring, $tzobj ); } if (is_array($rdate)) $rdatearray = array_merge ($rdatearray, $rdate); } //if (isset($_GET['debugexc'])) { //echo '
*** Array of r or exdate '; var_dump($rdatearray);// } return ($rdatearray); } $rdatestring = explode(':',$string); /* $VALUE=DATE: or VALUE=DATE-TIME: and a series of dates (no time) */ // if (isset($_GET['rdebug'])) {echo '
Ok now really parse it '; var_dump($rdatestring); echo '
'; } if (isset($rdatestring[0])) { if (($rdatestring[0] == 'VALUE=DATE') and (isset($rdatestring[1])) ) { $rdate = explode(',',$rdatestring[1]); /* that' sall we are doing for now */ // if (isset($_GET['rdebug'])) {echo '
Parsing value=date...
'; var_dump($rdate);} foreach ($rdate as $i => $r) { $temp = amr_parseValue ('DATE', $r, $tzobj); //amr 20150622 only variables by reference $dates[$i] = array_shift($temp); /*returns array, but there should only be 1 value */ } return($dates); } else if (($rdatestring[0] == 'VALUE=PERIOD') and (isset($rdatestring[1]))) { echo "
HELP cannot yet deal with RDATE with VALUE=PERIOD
"; return (false); } else { if (($rdatestring[0] == 'VALUE=DATE-TIME') and (isset($rdatestring[1]))) { $rdate = explode(',',$rdatestring[1]); } else { $rdate = explode(',',$rdatestring[0]); } foreach ($rdate as $i => $r) { if (empty($r)) { return false; } $dates[$i] = amr_parseDateTime ( $r, $tzobj); if (isset($_GET['debugexc'])) {echo '
*** Parsed as: '.$dates[$i]->format('c');} } if (empty($dates)) return (false); else return ($dates); } } else return (false); } function amr_parseAttach ($parts) { /* This property can be specified multiple times in a "VEVENT", "VTODO", "VJOURNAL", or "VALARM" calendar component with the exception of AUDIO alarm that only allows this property to occur once. Default is a URL ATTACH:http://example.com/public/quarterly-report.doc But could also have : ATTACH:CID:jsmith.part3.960817T083000.xyzMail@example.com ATTACH;FMTTYPE=audio/basic:ftp://example.com/pub/ sounds/bell-01.aud ATTACH;FMTTYPE=application/msword:http://example.com/ templates/agenda.doc ATTACH;FMTTYPE=text/plain;ENCODING=BASE64;VALUE=BINARY:VGhlIH F1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4 */ if (ICAL_EVENTS_DEBUG) {echo '

Attach to parse
'; var_dump($parts); echo '
';} if (!empty($parts[0])) { if ($parts[0] === 'ATTACH') { // then we have a simple URL or CID if (!empty ($parts[1])) { if (substr($parts[1],0,3) === 'CID' ) { $newattach['type'] = 'CID'; $newattach['CID'] = esc_attr(substr($parts[1],4)); } else { // must be htpp or ftp $newattach['type'] = 'url'; $newattach['url'] = esc_url_raw (rawurldecode($parts[1])); } } else return (null); } else { // we have an FMTTYPE $newattach['type'] = str_replace('ATTACH;FMTTYPE=','', $parts[0]); //should be something like application/msword, FMTTYPE=audio/basic if (!stristr($newattach['type'], 'VALUE=BINARY')) { // not binary $newattach['url'] = esc_url_raw($parts[1]); } else { $newattach['binary'] = amr_remove_folding ($parts[1]); } } } else return (null); return($newattach); } function amr_parseDefault($prop, $parts) { $func = 'amr_parse'.str_replace('-','_',$prop); //if (isset($_REQUEST['debugcustom'])) {echo '
Parsing..'.$prop. 'Look for '.$func; } if (function_exists($func)) { $result = call_user_func ($func, $parts); if (isset($_REQUEST['debugcustom'])) {echo '
Used custom function..'.$func; var_dump($result);} return ($result); // return (amr_parseCustomModifiers($p)); } else { //if (isset($_REQUEST['debugcustom'])) {echo '
No function..'.$func.' for '; var_dump($parts);} if (isset ($parts[1])) { // its a straight value return (str_replace ('\,', ',', $parts[1])); } /* replace any slashes added by ical generator */ else { //nothing to see here folks... move along return ''; } return; } } function amr_parse_property ($parts) { /* would receive something like array ('DTSTART; VALUE=DATE', '20060315')) */ /* NOTE: parts[0] has the long tag eg: RDATE;TZID=US-EASTERN or could be DTEND;TZID=America/New_York;VALUE=DATE-TIME: and the date 20110724T100000 in parts 1 (see forum note from dusty - he is generating the DATETIME parts[1] the bit after the : 19960403T020000Z/19960403T040000Z, 19960404T010000Z/PT3H IF 'Z' then must be in UTC If no Z */ global $amr_globaltz; if (empty($parts[1])) return false; // we got crap $tzobj = $amr_globaltz; /* Because if there is no timezone specified in any way for the date time then it must a floating value, and so should be created in the global timezone.*/ // if (ICAL_EVENTS_DEBUG or isset($_REQUEST['tzdebug'])) {echo '
Property : '.$parts[0];} $p0 = explode (';', $parts[0]); /* Looking for ; VALUE = something...; or TZID=... or both, or more than one anyway ???*/ // the first bit will be the property like PRODID, or X_WR_TIMEZONE // the next will be the modifiers $prop = array_shift($p0); if (!empty($p0)) { // if we have some modifiers foreach ($p0 as $p) { // handle special modifiers , could be VALUE=DATE, TZID= if (stristr($p, 'TZID')) { /* Normal TZ, not the one with the path eg: DTSTART;TZID=US-Eastern:19980119T020000 or zimbras TZID="GMT+01.00/+02.00 */ // $TZID = substr($p0[1], 4 ); $TZID = substr($p, 4 ); $tzobj = amr_parseTZID($TZID); } /* should create datetime object with it's own TZ, datetime maths works correctly with TZ's */ else {/* might be just a value=date, in which case we use the global tz? no may still have TZid */ $tzobj = $amr_globaltz; if (stristr($p, 'VALUE')) { // we have a possibly redundant modified $VALUE = substr($p,6); // take everything after the '=' } else {// not necessary to deal with modifier here or unknown maybe custom modifiers // only urgent to get tZID's.. or maybe we could even do those later? //check it out on major code revamp, meanwhile if it aint broke, dont fix it } } } } // else $tzobj = $amr_globaltz; /* Because if there is no timezone specified in any way for the date time then it must a floating value, and so should be created in the global timezone.*/ // switch ($p0[0]) { switch ($prop) { case 'CREATED': case 'COMPLETED': case 'LAST-MODIFIED': case 'DTSTART': case 'DTEND': case 'DTSTAMP': case 'DUE': if (isset($VALUE)) { $date = amr_parseValue($VALUE, $parts[1], $tzobj); } /* return (amr_parseSingleDate($VALUE, $parts[1], $tzobj)); } */ else { $date = amr_parseSingleDate('DATE-TIME', $parts[1], $tzobj); } if (is_object($date) and (($prop === 'LAST-MODIFIED') or ($prop === 'CREATED'))) { amr_track_last_mod($date); } return ($date); case 'ALARM': case 'RECURRENCE-ID': /* could also have range ?*/ if (isset($VALUE)) { return (amr_parseValue($VALUE, $parts[1], $tzobj)); } elseif (isset($RANGE)){ return (amr_parseRange($RANGE, $parts[1], $tzobj)); } else { return (amr_parseSingleDate('DATE-TIME', $parts[1], $tzobj)); } case 'EXRULE': case 'RRULE': { return (amr_parseRRULE($parts[1])); } case 'BDAY': return (amr_parseDate ($parts[1])); case 'EXDATE': {if (isset($_REQUEST['debugexc'])) {echo '
Parsing EXDATE ';}} case 'RDATE': return (amr_parseRDATE ($parts[1],$tzobj)); case 'TRIGGER': /* not supported yet, check for datetime and / or duration */ case 'DURATION': $dur = amr_parseDuration ($parts[1]); if (isset($_GET['debugdur'])) {echo '
Parts1 = '.$parts[1].'
Duration = '; var_dump($dur);} return ($dur); case 'FREEBUSY': return ( amr_parsePeriod ($parts[1], $tzobj)); case 'TZID': /* ie TZID is a property, not part of a date spec */ return ($parts[1]); case 'ORGANIZER': { return(amr_parseOrganiser($parts)); } case 'ATTENDEE': { return(amr_parseAttendees($parts)); } case 'ATTACH': { $attach = amr_parseAttach($parts); If (ICAL_EVENTS_DEBUG) echo '
ATTACH returned:
'.print_r($attach,true); return($attach); } case 'CATEGORIES': { $cats = amr_parse_CATEGORIES($parts[1]); return($cats ); } default:{ return (amr_parseDefault($prop, $parts)); } } } function amr_parse_component($type) { /* so we know we have a vcalendar at lines[$n] - check for properties or components */ global $amr_lines; global $amr_totallines; global $amr_n; global $amr_validrepeatablecomponents; global $amr_validrepeatableproperties; global $amr_globaltz; while (($amr_n < $amr_totallines) ) { $amr_n++; $parts = amr_explodeByColon($amr_lines[$amr_n]); // 201507 if ((!$parts) or ($parts === $amr_lines[$amr_n])) { // ie no colon // } else { // ok if ($parts[0] === 'BEGIN') { /* the we are starting a new sub component - end of the properties, so drop down */ if (in_array ($parts[1], $amr_validrepeatablecomponents)) { $subarray[$parts[1]][] = amr_parse_component($parts[1]); } else { $subarray[$parts[1]] = amr_parse_component($parts[1]); } } else { if ($parts[0] === 'END') { if (empty($subarray)) return (false); return ($subarray ); } /* now grab the value - just in case there may have been ";" in the value we will take all the rest of the string */ else { if ($parts[0] === 'X-WR-TIMEZONE;VALUE=TEXT') $parts[0] === 'X-WR-TIMEZONE'; $basepart = explode (';', $parts[0], 2); /* Looking for RRULE; something...*/ //if (isset($_GET['debugcustom'])) {echo '
checking basepart '; var_dump($basepart); echo ' against '; var_dump($amr_validrepeatableproperties); } if (in_array ($basepart[0], $amr_validrepeatableproperties)) { $temp = amr_parse_property ($parts); // now this might return an array (eg categories), which can be multiple lines too if ($basepart[0] == 'CATEGORIES') { //if (is_array($temp)) { //if (WP_DEBUG) {echo '
Got an array:'.$basepart[0].' '; var_dump($temp);} if (!empty($subarray[$basepart[0]]) and is_array($subarray[$basepart[0]])) { // ie we got an array already, must be multiple lines $subarray[$basepart[0]] = array_merge($subarray[$basepart[0]], $temp); } else $subarray[$basepart[0]]= $temp; } else $subarray[$basepart[0]][] = $temp; } else { $subarray [$basepart[0]] = amr_parse_property($parts); if (($basepart[0] === 'DTSTART') and (isset($basepart[1]))) { // we have a DTSTART // save the timezone separately in case we want to show events in event timezone if (is_array($subarray['DTSTART'])) { $subarray['DTSTART'] = $subarray['DTSTART'][0]; } if (is_object($subarray['DTSTART'])) { $subarray['timezone'] = $subarray['DTSTART']->getTimezone(); if (isset($_REQUEST['debugtz'])) { echo '
check tz'; var_dump($subarray['DTSTART']); var_dump($subarray['timezone']); } if (amr_is_untimed($basepart[1])) { /* ie has VALUE=DATE */ //$subarray ['Untimed'] = true; // removed v4.0.28 //$subarray ['allday'] = true; // v4.0.17 $subarray ['allday'] = 'allday'; // v4.0.28 } } //else return false; } else if (($basepart[0] === 'X-MOZ-GENERATION') and (!isset( $subarray ['SEQUENCE']))) { $subarray ['SEQUENCE'] = $subarray ['X-MOZ-GENERATION'] ; /* If we have an mozilla funny thing, convert it to the sequence if there is no sequence */ } else { if (isset($_GET['debugcustom']) and ($basepart[0] === 'X-TRUMBA-CUSTOMFIELD')) { echo '
Base Part = '.$basepart[0]; //var_dump($subarray [$basepart[0]]); } } } } } } } //if (ICAL_EVENTS_DEBUG) var_dump($subarray); if (empty($subarray)) return false; return ($subarray); /* return the possibly nested component */ } // Parse the ical file and return an array ('Properties' => array ( name & params, value), 'Items' => array(( name & params, value), ) function amr_parse_ical ( $cal_file ) { /* we will try to continue as much as possible, ignore lines that are problems */ global $icsfile_tzname, $amr_lines, $amr_totallines, $amr_n, $amr_validrepeatablecomponents, $amr_last_modified; $icsfile_tzname = ''; $line = 0; $event = ''; //If (ICAL_EVENTS_DEBUG) { echo '
Calfile = '; var_dump($cal_file);echo '
';} $data = file_get_contents($cal_file); // Now fix folding. According to RFC, lines can fold by having // a CRLF and then a single white space character. // We will allow it to be CRLF, CR or LF or any repeated sequence // so long as there is a single white space character next. /**** we may also need to cope with backslahed backslashes, commas, semicolons as per http://www.kanzaki.com/docs/ical/text.html*/ $data = amr_remove_folding ($data); $data = str_replace ( "\;", ";", $data ); $data = str_replace ( "\,", ",", $data ); $amr_n = 0; $amr_lines = explode ( "\n", $data ); $amr_totallines = count ($amr_lines) - 1; /* because we start from 0 */ If (ICAL_EVENTS_DEBUG) { echo '
Lines in ics file: '.$amr_totallines.'' ; //echo '
first line: '; var_dump($amr_lines); echo '
'; } $parts = explode (':', $amr_lines[$amr_n],2 ); /* explode faster than the preg, just split first : */ if ($parts[0] === 'BEGIN') { $ical = amr_parse_component('VCALENDAR'); if (!empty ($amr_last_modified)) $ical['LastModificationTime'] = $amr_last_modified; //if (isset($_GET['debugcustom'])) {var_dump($ical);} return($ical); } else { If (ICAL_EVENTS_DEBUG) { echo '
VCALENDAR not found in file:'.$cal_file; echo '
Line has: '.$amr_lines[$amr_n] ; } return false; } } function amr_deal_with_tzpath_in_date ( $tzstring ) { /* Receive something like /mozilla.org/20070129_1/Europe/Berlin and return a tz object */ $tz = explode ('/',$tzstring); $l = count ($tz); if ($l>1) { $tzid= $tz[$l-2].'/'.$tz[$l-1]; } else $tzid = $tz[0] ; $tzobj = timezone_open ( $tzid ); If (ICAL_EVENTS_DEBUG or isset ($_REQUEST['tzdebug'])) { echo '
Timezone Reduced to: '.$tzid.' Result of timezone object creation:'; print_r($tzobj); } return ($tzobj); } function amr_get_timezone_cities () { // $timezone_identifiers = DateTimeZone::listIdentifiers(); /* Africa/Abidjan Africa/Accra Africa/Addis_Ababa Africa/Algiers Africa/Asmara */ foreach( $timezone_identifiers as $i=> $value ){ $c = explode("/",$value);//obtain continent,city $tzcities[$value]['continent'] = $c[0]; if (isset($c[1])) $tzcities[$c[1]] = $value; else $tzcities[$c[0]] = $value; } return ($tzcities); } function amr_remove_folding ($data) { $data = preg_replace ( "/[\r\n]+ /", "", $data ); $data = preg_replace ( "/[\r\n]+\t/", "", $data ); // unfold any htab folding 20151125 corrected $data = preg_replace ( "/[\r\n]+/", "\n", $data ); return($data); } // Replace RFC 2445 escape characters function amr_format_ical_text($value) { $output = str_replace( array('\\\\', '\;', '\,', '\N', '\n'), array('\\', ';', ',', "\n", "\n"), $value ); return $output; } function amr_is_untimed($text) { /* checks for VALUE=DATE */ if (stristr ($text, 'VALUE=DATE') and (!stristr($text, 'VALUE=DATE-TIME'))) return (true); else return (false); } function amr_track_last_mod($date) { global $amr_last_modified; $amr_globaltz; if (empty ($amr_last_modified)) $amr_last_modified = amr_newDateTime('0000-00-00 00:00:01'); if ($date->format('c') > $amr_last_modified->format('c')) { $amr_last_modified = clone $date; } }