<?php
/*
 * This file incudes functions for parsing iCal data files duringan import.
 /* It endeavours to parse as incluisive;y as much as possible.
 /* It includes functions to cache the file
 /* It is not a validator!
 /* The function will return a nested array
	properties
		vevents
			event1
				parameters
				repeatable parameters
					repeat 1
					repeat 2
			event2
		vtodos etc
 *
 * The iCal specification is available online at:
 *	http://www.ietf.org/rfc/rfc2445.txt
 *
 */
include 'timezones/amr-windows-zones.php';
 
/* Return the full path to the cache file for the specified URL.*/
function get_cache_file($url) {
		return get_cache_path() .'/'. amr_get_cache_filename($url);
	}
 
/* Attempt to create the cache directory if it doesn't exist.
	 * Return the path if successful.
*/
function get_cache_path() {
	global $amr_options;
		$cache_path = (ICAL_EVENTS_CACHE_LOCATION. '/ical-events-cache');
		if (!file_exists($cache_path)) { /* if there is no folder */
			if (wp_mkdir_p($cache_path, 0777)) {
				printf('<br />'.__('Your cache directory %s has been created','amr-ical-events-list'),'<code>'.$cache_path.'</code>');
			}
			else {
				die( '<br />'.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 '<br /> No VCALENDAR in file. Start has: '.$checkstart.' end';
		}
		echo '<a class="error" href="#" title="'
			.__('Unexpected data contents. Please tell administrator.','amr-ical-events-list' ). ' '
			.__('See comments in source for response received from ics server.','amr-ical-events-list' )
			.'">!</a>';
		echo '<!-- Some of the content returned is: '; var_dump(substr ($data,0,200)); echo ' end of dump -->';
		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 '<hr />url before decode: '.$url.'<br />';
		$url = html_entity_decode($url);
//		If (ICAL_EVENTS_DEBUG) echo '<br />url decoded: '.$url.'<hr />';
		$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 '<br>Get ical file remotely, it is time to refresh or it is not cached: <br />';
			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 '<hr /><b>Http request failed </b><br /> Dumping response: ';
				var_dump($check);
				}
				if (is_wp_error($check))
					$text = '<br />'.$check->get_error_message().'</br>';
				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 '<br />This message is only shown to the administrator, and only when we refresh the file.';
					echo '<br />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. <br />';					
					}
				}
				else {
					if (ICAL_EVENTS_DEBUG) {
						echo '<br />The url given is not returning a calendar file';
						echo '<br />The response was '; var_dump($check['response']);
						echo '<br />The content type is '.$check['headers']['content-type'];
						echo '<br />The content type of an ics file should be text/calendar. <br />';
						
					}
					$data = false;
				}
			
			}
			else $data = $check['body'];  // from the http request		
			
			if (!amr_check_start_of_file ($data)) {			

					$text .= '&nbsp;'.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 .= '&nbsp;...'.sprintf(__('Using File last cached at %s','amr-ical-events-list'), $amr_lastcache->format('D c'));
						else 
							$text .= '&nbsp;...'.__('File last cached time not available','amr-ical-events-list');
						echo '<a class="error" href="#" title="'
						.__('Warning: Events may be out of date. ','amr-ical-events-list' )
						. $text.'">!</a>';
						return($cachedfile);  //return file not data
						}
					else {
						echo '<a class="error" href="#" title="'
						.__('No cached ical file for events','amr-ical-events-list' )
						. $text.'">!</a>';
						return (false);
					}
					return (false);
				}
			else If (ICAL_EVENTS_DEBUG) { echo '<br />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 '<br />Error opening or creating the cached file <br />'.$cachedfile;
					return (false);
				}
			}
			else {	echo '<br>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 '<br />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 '<br />'.$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 '<br />Properties:';var_dump($properties); 
		$parameters = amr_explodeBYSemi ($properties);
		//echo '<br />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 '<br />Error: Bad attendee parameters <br />';
			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' <br />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 '<br/>m : <br />'; var_dump($m); echo '<br/>p0 : <br />'; 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 '<br />Got Range '.$range.' with '.$daterange.'<br />';	}
		$r = explode (':', $daterange);
		if (!($thisanddate = amr_parseDateTime($r[1], $tzobj))) return (false);
		If (isset ($_REQUEST['debugexc'])) {	echo '<br />Got range '.$range.' "THISAND" date '.$thisanddate ->format('c').'<br />';	}
		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 '<br />Unable to create DateTime object from '.$text.' <br />'.$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 '<br/>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 '<br/>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 '<br />Do we have a City? <br />';print_r($icstzcities);}
			$globalcontcity = explode ('/',$globaltzstring);
			if (isset ($globalcontcity[1]) ) 
				$globalcity = $globalcontcity[1];
			else 
				$globalcity = $globalcontcity[0];
//			if (isset ($_REQUEST['tzdebug'])) {	echo '<hr> text = '.$text.'<br/>icstzid = '.$icstzid.' and wp tz = '.$globalcity.' <br >'; 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 '<br/>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 '<br/>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 '<br /><b>Timezone must be: </b> '.$tzname.'<br />';
		$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 '<br />'.$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 '<br />'.$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 '<br />'.$i; var_dump($z);}
	    if (in_array($icstzid,$timezone_identifiers, false)) { // we now have a valid php timezone
			if (isset ($_REQUEST['tzdebug'])) {echo '<br/>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 '<br/>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 '<br/>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.<br />'.$e->getMessage();
				amr_tell_admin_the_error ($text);			
				//echo '<br />Unable to create Time zone object., Using wp default.<br />'.$e->getMessage();
				return ($amr_globaltz);
			}
	return ($tz);
}
/* ------------------------------------------------------------------ */
function amr_unknown_timezone ($text, $tzname) {	
	$emessage = 'Unable to deal with timezone like this: '.$text;
//	echo '<!-- '.$emessage.' -->';
//	if (isset ($_REQUEST['tzdebug']) or ICAL_EVENTS_DEBUG) {
	if (is_super_admin()) {
		echo  '<br />Message to logged-in admin only: <b>'.$emessage.'</b>';
		echo '- Making an assumption! Using '.$tzname.'<br />';
	}
	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 ( '<br>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 '<br />For value: '.$VALUE.' '.$text;}
		if (empty($text)) {
			if (ICAL_EVENTS_DEBUG) {echo '<br />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 '<br />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 '<br />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 '<br />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 '<br />***Doing next r or exdate '.$i.' <pre>'.print_r($rdatestring,true).'</pre>'; }
				$rdate = amr_parseRDATE ($rdatestring, $tzobj );

			}
			if (is_array($rdate)) $rdatearray = array_merge ($rdatearray, $rdate);
		}

		//if (isset($_GET['debugexc'])) {
		//echo '<br />*** 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 '<br />Ok now really parse it '; var_dump($rdatestring); echo '<br />'; }

	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 '<br />Parsing value=date...<br/> '; 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 "<br />HELP cannot yet deal with RDATE with VALUE=PERIOD<br />"; 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 '<br />*** 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 '<hr><br/>Attach to parse <br />'; var_dump($parts); echo '<hr>';}

	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 '<br />Parsing..'.$prop. 'Look for '.$func; }
			if (function_exists($func))		{	
				$result = call_user_func ($func, $parts);
				if (isset($_REQUEST['debugcustom'])) {echo '<br />Used custom function..'.$func; var_dump($result);}
				return ($result);
			//		return (amr_parseCustomModifiers($p));
			}
			else {
				//if (isset($_REQUEST['debugcustom'])) {echo '<br>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 '<br /> 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 '<br>  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 '<br />Parts1 = '.$parts[1].'<br />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 '<br />ATTACH returned:<br />'.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 '<br />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 '<br />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 '<br />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 '<br />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 '<br />Calfile = '; var_dump($cal_file);echo '<br />';}
	$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 '<br><b>Lines in ics file: '.$amr_totallines.'</b>' ;
			//echo '<br />first line: ';	var_dump($amr_lines);
			echo '<br />';
			}

		$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 '<br>VCALENDAR not found in file:'.$cal_file;
				echo '<br>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 '<br />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;
		}
}

