function mka_ical($atts, $thing = '') {
	$time_start = microtime(true);
	global $txpcfg;
	extract(lAtts(array(
		'url' => '',
		'cachingtime' => 30,
		'googlemail' => '',
		'limit' => 0,
		'pastevents' => false,
		'fmtTime' => 'H:i',
		'fmtDate' => 'd.m.Y',
		'imgfolder' => '',
		'imgcat' => '',	
		'defaultimage' => 'none.gif',
		'break'  => 'li',
		'class' => __FUNCTION__,
		'cssid' => 'icalevents',
		'wraptag'  => 'ul',
		'form'  => ''
	), $atts));
	
	if (strlen($thing) > 0) {
		$body = $thing;
	} else if (!empty($form)) {
		$body = fetch_form($form);
	} else {
		// Default body
		$body = '<li><span class="date">{date}</span>'
				.'<span class="title">{summary}</span>'
				.'<span class="description">{description}</span></li>';
	}

	// get url from googlemail-address
	if (strlen($googlemail) > 0) {
		$tmp = explode("@", $googlemail);
		$gmail = $tmp[0];
		$urls[] = "http://www.google.com/calendar/ical/".$googlemail."%40googlemail.com/public/basic.ics";
	}
	
	// add given urls from attribute url
	if (strlen($url) > 0) {
		foreach (explode(",", $url) as $u)
			$urls[] = trim($u);
	}
	
	// no url --> Nothing to show.
	if (sizeof($urls) === 0) {
		return "Error: No url given.";
	}
	
	// get images from folder and/or category
	$show_img = false;
	if ((strlen($imgcat) > 0) || (strlen($imgfolder) > 0 )) {
		$images = array();
		
		// add images from given categories
		if (strlen($imgcat) > 0) {
			$images = mka_imgarrayfromcat($imgcat);	
		}
		
		// Add images from given folder
		if (file_exists($imgfolder)) {
			$images = array_merge(mka_imgarrayfromfolder($imgfolder));
		}

		if (sizeof($images) > 0) {
			$show_img = true;

			// get default image
			$defimg = ical_defimg($defaultimage);
		}
	}

	
	// get events from cache
	$events = checkCache($urls, $cachingtime);

	
	// loop trough events
	$i = 1;
	foreach ($events as $evt) {

		// is event in future or running?
		if (($pastevents || $evt->end > mktime()) && ($limit == 0 || $i <= $limit )) {

			// TODO :: TextileRestricted($text, $lite = 1, $noimage = 1, $rel = 'nofollow')
			$evt->sum = ical_formatText($evt->sum);
			$evt->des = ical_formatText($evt->des);

			$dateView = ical_getDateView($evt, $fmtDate, $fmtTime);

			if ($show_img) {
				$image = ical_img($evt->sum, $images, $defimg);
			}

			// $output[] = parse();	
			$keys = array(); $vals = array();
			$keys[] = '{date}';		$vals[] = $dateView;
			$keys[] = '{title}';		$vals[] = $evt->sum;
			$keys[] = '{description}';	$vals[] = $evt->des;
			$keys[] = '{imagesrc}';		$vals[] = $image['src'];
			$keys[] = '{imageid}';		$vals[] = $image['id'];

			
			$out[] = parse(str_replace($keys, $vals, $body));
			
			$i++; // count for attr limit
		} // if ($evt->getEnd() > mktime() && ($limit == 0  || $i <= $limit ))

	}
	
	$gen =  "<!-- mka_ical - generated in ".((microtime(true)-$time_start)*1000)."ms -->";
	// definition of doWrap
	// http://www.consking.com/txp406/nav.html?textpattern/publish/taghandlers.php.html#dowrap
	return doWrap($out, $wraptag, $break, $class, '', '', '', $cssid).$gen;
	
}


function ical_img($title, $images, $defimg) {
	$search = array(" ", ".", "-");
	$rep = array("", "", "");
	$t = str_replace($search, $rep, strtolower($title));
	foreach ($images as $im) {
		if (strpos($t, $im['name']) !== false) {
			return $im;
		}
	}
	// return default image src
	return $defimg;
}


function ical_getDateView($evt, $fmtDate, $fmtTime) {
	$fmtDaTi = $fmtDate." ".$fmtTime;
	// event on one day
	if (date("dmY",$evt->start) == date("dmY",$evt->end-1)) {
		// no exact time definition
		if (date("H:i",$evt->start) == "00:00" && date("H:i",$evt->end) == "00:00") {
			$dateView = date($fmtDate, $evt->start);
		}
		else {
			$dateView = date($fmtDaTi, $evt->start)."-".date($fmtTime, $evt->start + $evt->duration);
		}
	
	}	
	else {
		$dateView  = date($fmtDate, $evt->start)."-".date($fmtDate, $evt->end-1);
	}
	return $dateView;
}

function mka_makearr_sql($str, $type='string', $delimeter =',') {
	$ret = array();
	$arr = explode($delimeter, $str);
	foreach ($arr as $el) {
		if ($type == 'string')
			$ret[] = "'".trim($el)."'";
		else 
			$ret[] = $el;
	}
	return $ret;
}

function mka_imgarrayfromcat($cats) {
	$imgs = array();
	
	$cats = mka_makearr_sql($cats);
	$clause = 'category IN ('.implode(",", $cats).')';
	$attrs = 'id, name, ext';
	$rs = safe_rows($attrs,'txp_image', $clause);
	
	$prefix = "http://".$GLOBALS['prefs']["siteurl"]."/".$GLOBALS['prefs']['img_dir']."/";
	
	if (count($rs) != 0) {
		foreach ($rs as $col) {
			$col['src'] = $prefix.$col['id'].$col['ext'];
			$imgs[] = $col;	
		}
	}
	return $imgs;
}

function mka_imgarrayfromfolder($imgfolder) {
	$imgs = array();
	if ($handle = @opendir($imgfolder)) {
		while (($file = @readdir($handle)) !== false) {
			$img = array();
			$parts = explode(".", $file);
			$img['ext']  = (count($parts) > 1) ? ".".array_pop($parts) : '';
			$img['name'] = implode(".", $parts);
			$img['src'] = "http://".$GLOBALS['prefs']["siteurl"]."/".$imgfolder."/".$file;
			if (strlen($img['name']) > 0 && in_array(strtolower($img['ext']), array(".png", ".jpg", ".jpeg", ".gif"))) {
				$imgs[] = $img;
			}
		}
		closedir($handle);
	}
	return $imgs;
}


function ical_defimg($defimg) {
	$url = "http://".$GLOBALS['prefs']["siteurl"]."/";
	$img = array();
	// is defimg id?
	if (intval($defimg) !== 0) {
		$id = intval($defimg);
		$img = safe_row('id, name, ext', 'txp_image', 'id = '.$id);
	}
	
	// image with id not found?
	if (sizeof($img) === 0) {
		$parts = explode(".", basename($defimg));
		$img['ext']  = (count($parts) > 1) ? ".".array_pop($parts) : '';
		$img['name'] = implode(".", $parts);
		$img['src'] = $url.$defimg;
	}
	else {
		$img["src"] = $url.$GLOBALS['prefs']['img_dir']."/".$img['id'].$img['ext'];
	}
	return $img;
}

function ical_shorten_link($string) {
	$text_word_maxlength = 55;
	if(count($string) == 2) { $pre = ""; $url = $string[1]; }
	else { $pre = $string[1]; $url = $string[2]; }
	$shortened_url = $url;
	if (strlen($url) > $text_word_maxlength) $shortened_url = substr($url, 0, ($text_word_maxlength/2)) . "..." . substr($url, - ($text_word_maxlength-3-$text_word_maxlength/2));
	return $pre."<a href=\"".$url."\">".$shortened_url."</a>";
}

function ical_formatText($string) {
	$string = ' ' . $string;
	$string = preg_replace_callback("#(^|[\n ])([\w]+?://.*?[^ \"\n\r\t<]*)#is", "ical_shorten_link", $string);
	$string = preg_replace("#(^|[\n ])((www|ftp)\.[\w\-]+\.[\w\-.\~]+(?:/[^ \"\t\n\r<]*)?)#is", "$1<a href=\"http://$2\">$2</a>", $string);
	$string = str_replace("\n","<br/>",$string);
	return $string;
}
 function checkCache($urls, $cachingtime) { $hash = md5(implode(",", $urls)); $fn = "files/mka_ical_".$hash.".json"; if (file_exists($fn) && (time()-filemtime($fn) < ($cachingtime*60))) {  $cache_file = fopen($fn,"r"); $data = fread($cache_file, filesize($fn)); fclose($cache_file); return json_decode($data); } else {  $events = getEvents($urls); $cf = fopen($fn,"w+"); fwrite($cf, json_encode($events)); fclose($cf); return $events; } } function getEvents($urls) { $events = array(); $reader = new SG_iCal(); foreach($urls as $u) { $reader->setUrl($u, true); $e = $reader->getEvents(); foreach($e as $event) { $events[] = getPlainEvent($event); } }  usort($events, "cmp_event"); return $events; } function getPlainEvent($evt) { $plainEvent = new stdClass; $plainEvent->start = $evt->getStart(); $plainEvent->end = $evt->getEnd(); $plainEvent->duration = $evt->getDuration(); $plainEvent->sum = $evt->getSummary(); $plainEvent->des = $evt->getDescription(); $plainEvent->loc = $evt->getLocation(); return $plainEvent; } function cmp_event($a, $b) { $da = $a->start; $db = $b->start; if ($da == $db) { return 1; } return ($da < $db) ? -1 : 1; }  define('SG_ICALREADER_VERSION', '0.5');  class SG_iCal { private $information; private $events; private $timezones;  public function __construct($url = false) {                     if( $url !== false ) { SG_iCal_Parser::Parse($url, $this); } }  public function setUrl( $url = false ) { if( $url !== false ) { SG_iCal_Parser::Parse($url, $this); } }  public function getCalendarInfo() { return $this->information; }  public function setCalendarInfo( SG_iCal_VCalendar $info ) { $this->information = $info; }  public function getTimeZoneInfo( $tzid = null ) { if( $tzid == null ) { return $this->timezones; } else { if ( !isset($this->timezones)) { return null; } foreach( $this->timezones AS $tz ) { if( $tz->getTimeZoneId() == $tzid ) { return $tz; } } return null; } }  public function addTimeZone( SG_iCal_VTimeZone $tz ) { $this->timezones[] = $tz; }  public function getEvents() { return $this->events; }  public function addEvent( SG_iCal_VEvent $event ) { $this->events[] = $event; } }  class SG_iCalReader extends SG_iCal {}   class SG_iCal_VCalendar implements IteratorAggregate { private $data;  public function __construct($data) { $this->data = $data; }  public function getTitle() { if( isset($this->data['x-wr-calname']) ) { return $this->data['x-wr-calname']; } else { return null; } }  public function getDescription() { if( isset($this->data['x-wr-caldesc']) ) { return $this->data['x-wr-caldesc']; } else { return null; } }  public function getIterator() { return new ArrayIterator($this->data); } }   class SG_iCal_VEvent { const DEFAULT_CONFIRMED = true; private $uid; private $start; private $end; private $recurrence; private $summary; private $description; private $location; private $data;  public function __construct($data, SG_iCal $ical ) { $this->uid = $data['uid']->getData(); unset($data['uid']); if ( isset($data['rrule']) ) { $this->recurrence = new SG_iCal_Recurrence($data['rrule']); unset($data['rrule']); } if( isset($data['dtstart']) ) { $this->start = $this->getTimestamp( $data['dtstart'], $ical ); unset($data['dtstart']); } if( isset($data['dtend']) ) { $this->end = $this->getTimestamp($data['dtend'], $ical); unset($data['dtend']); } elseif( isset($data['duration']) ) {    $dur = new SG_iCal_Duration( $data['duration']->getData() ); $this->end = $this->start + $dur->getDuration(); unset($data['duration']); } elseif ( isset($this->recurrence) ) {  $until = $this->recurrence->getUntil(); $count = $this->recurrence->getCount();  if ( $this->recurrence->getUntil() or $this->recurrence->getCount() ) {  if ( $until ) { $this->end = strtotime( $until ); }  } } $imports = array('summary','description','location'); foreach( $imports AS $import ) { if( isset($data[$import]) ) { $this->$import = $data[$import]->getData(); unset($data[$import]); } } $this->data = SG_iCal_Line::Remove_Line($data); }  public function getUID() { return $this->uid; }  public function getSummary() { return $this->summary; }  public function getDescription() { return $this->description; }  public function getLocation() { return $this->location; }  public function isBlocking() { return !(isset($this->data['transp']) && $this->data['transp'] == 'TRANSPARENT'); }  public function isConfirmed() { if( !isset($this->data['status']) ) { return self::DEFAULT_CONFIRMED; } else { return $this->data['status'] == 'CONFIRMED'; } }  public function getStart() { return $this->start; }  public function getEnd() { return $this->end; }  public function getDuration() { return $this->end - $this->start; }  public function getProperty( $prop ) { if( isset($this->$prop) ) { return $this->$prop; } elseif( isset($this->data[$prop]) ) { return $this->data[$prop]; } else { return null; } }  private function getTimestamp( SG_iCal_Line $line, SG_iCal $ical ) { $ts = strtotime($line->getData()); if( isset($line['tzid']) ) { $tz = $ical->getTimeZoneInfo($line['tzid']); $offset = $tz->getOffset($ts); $ts = strtotime(date('D, d M Y H:i:s', $ts) . ' ' . $offset); } return $ts; } }   class SG_iCal_VTimeZone { private $tzid; private $daylight; private $standard; private $cache = array();  public function __construct( $data ) {    $this->tzid = $data['tzid']; $this->daylight = $data['daylight']; $this->standard = $data['standard']; }  public function getTimeZoneId() { return $this->tzid; }  public function getOffset( $ts ) { $act = $this->getActive($ts); return $this->{$act}['tzoffsetto']; }  public function getTimeZoneName($ts) { $act = $this->getActive($ts); return $this->{$act}['tzname']; }  private function getActive( $ts ) { if( isset($this->cache[$ts]) ) { return $this->cache[$ts]; } $daylight_freq = new SG_iCal_Freq($this->daylight['rrule'], strtotime($this->daylight['dtstart'])); $standard_freq = new SG_iCal_Freq($this->standard['rrule'], strtotime($this->standard['dtstart'])); $last_standard = $standard_freq->previousOccurrence($ts); $last_dst = $daylight_freq->previousOccurrence($ts); if( $last_dst > $last_standard ) { $this->cache[$ts] = 'daylight'; } else { $this->cache[$ts] = 'standard'; } return $this->cache[$ts]; } }   class SG_iCal_Duration { private $dur;  public function __construct( $duration ) { if( $duration{0} == 'P' || (($duration{0} == '+' || $duration{0} == '-') && $duration{1} == 'P') ) { preg_match('/P((\d+)W)?((\d+)D)?(T)?((\d+)H)?((\d+)M)?((\d+)S)?/', $duration, $matches); $results = array('weeks'=>(int)$matches[2], 'days'=>(int)$matches[4], 'hours'=>(int)$matches[7], 'minutes'=>(int)$matches[9], 'seconds'=>(int)$matches[11]); $ts = 0; $ts += $results['seconds']; $ts += 60 * $results['minutes']; $ts += 60 * 60 * $results['hours']; $ts += 24 * 60 * 60 * $results['days']; $ts += 7 * 24 * 60 * 60 * $results['weeks']; $dir = ($duration{0} == '-') ? -1 : 1; $this->dur = $dir * $ts; } else {  $this->dur = 0; } }  public function getDuration() { return $this->dur; } }   class SG_iCal_Factory {  public static function factory( SG_iCal $ical, $section, $data ) { switch( $section ) { case "vcalendar":    return new SG_iCal_VCalendar(SG_iCal_Line::Remove_Line($data), $ical ); case "vtimezone":    return new SG_iCal_VTimeZone(SG_iCal_Line::Remove_Line($data), $ical ); case "vevent":    return new SG_iCal_VEvent($data, $ical ); default: return new ArrayObject(SG_iCal_Line::Remove_Line((array) $data) ); } } }   class SG_iCal_Freq { private $weekdays = array('MO'=>'monday', 'TU'=>'tuesday', 'WE'=>'wednesday', 'TH'=>'thursday', 'FR'=>'friday', 'SA'=>'saturday', 'SU'=>'sunday'); private $knownRules = array('month', 'weekno', 'day', 'monthday', 'yearday', 'hour', 'minute'); private $simpleMode = true; private $rules = array('freq'=>'yearly', 'interval'=>1); private $start = 0; private $freq = '';  public function __construct( $rule, $start ) { $this->start = $start; $rules = array(); foreach( explode(';', $rule) AS $v) { list($k, $v) = explode('=', $v); $this->rules[ strtolower($k) ] = $v; } if( isset($this->rules['until']) && is_string($this->rules['until']) ) { $this->rules['until'] = strtotime($this->rules['until']); } $this->freq = strtolower($this->rules['freq']); foreach( $this->knownRules AS $rule ) { if( isset($this->rules['by' . $rule]) ) { if( $this->isPrerule($rule, $this->freq) ) { $this->simpleMode = false; } } } if(!$this->simpleMode) { if(! (isset($this->rules['byday']) || isset($this->rules['bymonthday']) || isset($this->rules['byyearday']))) { $this->rules['bymonthday'] = date('d', $this->start); } } if( isset($this->rules['count']) ) { $n = $start; for($i=0;$i<$this->rules['count'];$i++) { $n = $this->findNext($n); } $this->rules['until'] = $n; } }  public function previousOccurrence( $offset ) { $t1 = $this->start; while( ($t2 = $this->findNext($t1)) < $offset) { if( $t2 == false ){ break; } $t1 = $t2; } return $t1; }  public function nextOccurrence( $offset ) { return $this->findNext( $this->previousOccurrence( $offset) ); }  public function lastOccurrence() { $temp_timestamp = $this->findNext($this->start); $timestamp = 0; while ($temp_timestamp) { $timestamp = $temp_timestamp; $temp_timestamp = $this->findNext($temp_timestamp); } return $timestamp; }  public function findNext($offset) { $echo = false;  if( $offset === false || (isset($this->rules['until']) && $this->rules['until'] <= $offset) ) { if($echo) echo 'STOP: ' . date('r', $offset) . "\n"; return false; } $found = true;   if($echo) echo 'O: ' . date('r', $offset) . "\n"; $hour = (in_array($this->freq, array('hourly','minutely')) && $offset > $this->start) ? date('H', $offset) : date('H', $this->start); $minute = (($this->freq == 'minutely' || isset($this->rules['byminute'])) && $offset > $this->start) ? date('i', $offset) : date('i', $this->start); $t = mktime($hour, $minute, date('s', $this->start), date('m', $offset), date('d', $offset), date('Y',$offset)); if($echo) echo 'START: ' . date('r', $t) . "\n"; if( $this->simpleMode ) { if( $offset < $t ) { return $t; } $next = $this->findStartingPoint( $t, $this->rules['interval'], false ); if( !$this->validDate( $next ) ) { return $this->findNext($next); } return $next; } $eop = $this->findEndOfPeriod($offset); if($echo) echo 'EOP: ' . date('r', $eop) . "\n"; foreach( $this->knownRules AS $rule ) { if( $found && isset($this->rules['by' . $rule]) ) { if( $this->isPrerule($rule, $this->freq) ) { $subrules = explode(',', $this->rules['by' . $rule]); $_t = null; foreach( $subrules AS $subrule ) { $imm = call_user_func_array(array($this, 'ruleBy' . $rule), array($subrule, $t)); if( $imm === false ) { break; } if($echo) echo strtoupper($rule) . ': ' . date('r', $imm) . ' A: ' . ((int) ($imm > $offset && $imm < $eop)) . "\n"; if( $imm > $offset && $imm < $eop && ($_t == null || $imm < $_t) ) { $_t = $imm; } } if( $_t !== null ) { $t = $_t; } else { $found = $this->validDate($t); } } } } if( $this->start > $offset && $this->start < $t ) { return $this->start; } else if( $found && ($t != $offset)) { if( $this->validDate( $t ) ) { if($echo) echo 'OK' . "\n"; return $t; } else { if($echo) echo 'Invalid' . "\n"; return $this->findNext($t); } } else { if($echo) echo 'Not found' . "\n"; return $this->findNext( $this->findStartingPoint( $offset, $this->rules['interval'] ) ); } }  private function findStartingPoint( $offset, $interval, $truncate = true ) { $_freq = ($this->freq == 'daily') ? 'day__' : $this->freq; $t = '+' . $interval . ' ' . substr($_freq,0,-2) . 's'; if( $_freq == 'monthly' && $truncate ) { if( $interval > 1) { $offset = strtotime('+' . ($interval - 1) . ' months ', $offset); } $t = '+' . (date('t', $offset) - date('d', $offset) + 1) . ' days'; } $sp = strtotime($t, $offset); if( $truncate ) { $sp = $this->truncateToPeriod($sp, $this->freq); } return $sp; }  public function findEndOfPeriod($offset) { return $this->findStartingPoint($offset, 1); }  private function truncateToPeriod( $time, $freq ) { $date = getdate($time); switch( $freq ) { case "yearly": $date['mon'] = 1; case "monthly": $date['mday'] = 1; case "daily": $date['hours'] = 0; case 'hourly': $date['minutes'] = 0; case "minutely": $date['seconds'] = 0; break; case "weekly": if( date('N', $time) == 1) { $date['hours'] = 0; $date['minutes'] = 0; $date['seconds'] = 0; } else { $date = getdate(strtotime("last monday 0:00", $time)); } break; } $d = mktime($date['hours'], $date['minutes'], $date['seconds'], $date['mon'], $date['mday'], $date['year']); return $d; }  private function ruleByday($rule, $t) { $dir = ($rule{0} == '-') ? -1 : 1; $dir_t = ($dir == 1) ? 'next' : 'last'; $d = $this->weekdays[substr($rule,-2)]; $s = $dir_t . ' ' . $d . ' ' . date('H:i:s',$t); if( $rule == substr($rule, -2) ) { if( date('l', $t) == ucfirst($d) ) { $s = 'today ' . date('H:i:s',$t); } $_t = strtotime($s, $t); if( $_t == $t && in_array($this->freq, array('monthly', 'yearly')) ) {  $s = 'next ' . $d . ' ' . date('H:i:s',$t); $_t = strtotime($s, $_t); } return $_t; } else { $_f = $this->freq; if( isset($this->rules['bymonth']) && $this->freq == 'yearly' ) { $this->freq = 'monthly'; } if( $dir == -1 ) { $_t = $this->findEndOfPeriod($t); } else { $_t = $this->truncateToPeriod($t, $this->freq); } $this->freq = $_f; $c = preg_replace('/[^0-9]/','',$rule); $c = ($c == '') ? 1 : $c; $n = $_t; while($c > 0 ) { if( $dir == 1 && $c == 1 && date('l', $t) == ucfirst($d) ) { $s = 'today ' . date('H:i:s',$t); } $n = strtotime($s, $n); $c--; } return $n; } } private function ruleBymonth($rule, $t) { $_t = mktime(date('H',$t), date('i',$t), date('s',$t), $rule, date('d', $t), date('Y', $t)); if( $t == $_t && isset($this->rules['byday']) ) {  return false; } else { return $_t; } } private function ruleBymonthday($rule, $t) { if( $rule < 0 ) { $rule = date('t', $t) + $rule + 1; } return mktime(date('H',$t), date('i',$t), date('s',$t), date('m', $t), $rule, date('Y', $t)); } private function ruleByyearday($rule, $t) { if( $rule < 0 ) { $_t = $this->findEndOfPeriod(); $d = '-'; } else { $_t = $this->truncateToPeriod($t, $this->freq); $d = '+'; } $s = $d . abs($rule -1) . ' days ' . date('H:i:s',$t); return strtotime($s, $_t); } private function ruleByweekno($rule, $t) { if( $rule < 0 ) { $_t = $this->findEndOfPeriod(); $d = '-'; } else { $_t = $this->truncateToPeriod($t, $this->freq); $d = '+'; } $sub = (date('W', $_t) == 1) ? 2 : 1; $s = $d . abs($rule - $sub) . ' weeks ' . date('H:i:s',$t); $_t = strtotime($s, $_t); return $_t; } private function ruleByhour($rule, $t) { $_t = mktime($rule, date('i',$t), date('s',$t), date('m',$t), date('d', $t), date('Y', $t)); return $_t; } private function ruleByminute($rule, $t) { $_t = mktime(date('h',$t), $rule, date('s',$t), date('m',$t), date('d', $t), date('Y', $t)); return $_t; } private function validDate( $t ) { if( isset($this->rules['until']) && $this->rules['until'] <= $t ) { return false; } if( isset($this->rules['bymonth']) ) { $months = explode(',', $this->rules['bymonth']); if( !in_array(date('m', $t), $months)) { return false; } } if( isset($this->rules['byday']) ) { $days = explode(',', $this->rules['byday']); foreach( $days As $i => $k ) { $days[$i] = $this->weekdays[ preg_replace('/[^A-Z]/', '', $k)]; } if( !in_array(strtolower(date('l', $t)), $days)) { return false; } } if( isset($this->rules['byweekno']) ) { $weeks = explode(',', $this->rules['byweekno']); if( !in_array(date('W', $t), $weeks)) { return false; } } if( isset($this->rules['bymonthday'])) { $weekdays = explode(',', $this->rules['bymonthday']); foreach( $weekdays As $i => $k ) { if( $k < 0 ) { $weekdays[$i] = date('t', $t) + $k + 1; } } if( !in_array(date('d', $t), $weekdays)) { return false; } } if( isset($this->rules['byhour']) ) { $hours = explode(',', $this->rules['byhour']); if( !in_array(date('H', $t), $hours)) { return false; } } return true; } private function isPrerule($rule, $freq) { if( $rule == 'year') return false; if( $rule == 'month' && $freq == 'yearly') return true; if( $rule == 'monthday' && in_array($freq, array('yearly', 'monthly')) && !isset($this->rules['byday'])) return true;  if( $rule == 'yearday' && $freq == 'yearly' ) return true; if( $rule == 'weekno' && $freq == 'yearly' ) return true; if( $rule == 'day' && in_array($freq, array('yearly', 'monthly', 'weekly'))) return true; if( $rule == 'hour' && in_array($freq, array('yearly', 'monthly', 'weekly', 'daily'))) return true; if( $rule == 'minute' ) return true; return false; } }   class SG_iCal_Line implements ArrayAccess, Countable, IteratorAggregate { private $ident; private $data; private $params = array(); private $replacements = array('from'=>array('\\,', '\\n', '\\;', '\\:', '\\"'), 'to'=>array(',', "\n", ';', ':', '"'));  public function __construct( $line ) { $split = strpos($line, ':'); $idents = explode(';', substr($line, 0, $split)); $ident = strtolower(array_shift($idents)); $data = trim(substr($line, $split+1)); $data = str_replace($this->replacements['from'], $this->replacements['to'], $data); $params = array(); foreach( $idents AS $v) { list($k, $v) = explode('=', $v); $params[ strtolower($k) ] = $v; } $this->ident = $ident; $this->params = $params; $this->data = $data; }  public function isBegin() { return $this->ident == 'begin'; }  public function isEnd() { return $this->ident == 'end'; }  public function getIdent() { return $this->ident; }  public function getData() { return $this->data; }  public static function Remove_Line($arr) { $rtn = array(); foreach( $arr AS $k => $v ) { if(is_array($v)) { $rtn[$k] = self::Remove_Line($v); } elseif( $v instanceof SG_iCal_Line ) { $rtn[$k] = $v->getData(); } else { $rtn[$k] = $v; } } return $rtn; }  public function offsetExists( $param ) { return isset($this->params[ strtolower($param) ]); }  public function offsetGet( $param ) { $index = strtolower($param); if (isset($this->params[ $index ])) { return $this->params[ $index ]; } }  public function offsetSet( $param, $val ) { return false; }  public function offsetUnset( $param ) { return false; }  public function __toString() { return $this->getData(); }  public function count() { return count($this->params); }  public function getIterator() { return new ArrayIterator($this->params); } }  class SG_iCal_Parser {  public static function Parse( $url, SG_iCal $ical ) { $content = self::Fetch( $url ); $content = self::UnfoldLines($content); self::_Parse( $content, $ical ); }  public static function ParseString($content, SG_iCal $ical ) { $content = self::UnfoldLines($content); self::_Parse( $content, $ical ); }  protected static function Fetch( $url ) { $c = curl_init(); $is_utf8 = true; curl_setopt($c, CURLOPT_URL, $url); curl_setopt($c, CURLOPT_RETURNTRANSFER, true); if( !ini_get('safe_mode') ){ curl_setopt($c, CURLOPT_FOLLOWLOCATION, true); } $content = curl_exec($c); $ct = curl_getinfo($c, CURLINFO_CONTENT_TYPE); $enc = preg_replace('/^.*charset=([-a-zA-Z0-9]+).*$/', '$1', $ct); if( $ct != '' && strtolower(str_replace('-','', $enc)) != 'utf8' ) {  $is_utf8 = false; } elseif( ! self::_ValidUtf8( $content ) ) {  $is_utf8 = false; } if( !$is_utf8 ) { $content = utf8_encode($content); } return $content; }  protected static function UnfoldLines($content) { $data = array(); $content = explode("\n", $content); for( $i=0; $i < count($content); $i++) { $line = rtrim($content[$i]); while( isset($content[$i+1]) && strlen($content[$i+1]) > 0 && ($content[$i+1]{0} == ' ' || $content[$i+1]{0} == "\t" )) { $line .= rtrim(substr($content[++$i],1)); } $data[] = $line; } return $data; }  private static function _Parse( $content, SG_iCal $ical ) { $main_sections = array('vevent', 'vjournal', 'vtodo', 'vtimezone', 'vcalendar'); $sections = array(); $section = ''; $current_data = array(); foreach( $content AS $line ) { $line = new SG_iCal_Line($line); if( $line->isBegin() ) {  $section = strtolower($line->getData()); $sections[] = strtolower($line->getData()); } elseif( $line->isEnd() ) {  $removed = array_pop($sections); $section = end($sections); if( array_search($removed, $main_sections) !== false ) { self::StoreSection( $removed, $current_data[$removed], $ical); $current_data[$removed] = array(); } } else {  foreach( $main_sections AS $s ) {  if( array_search($s, $sections) !== false ) {  if( $section == $s ) {  $current_data[$s][$line->getIdent()] = $line; } else {  $current_data[$s][$section][$line->getIdent()] = $line; } break; } } } } $current_data = array(); }  protected static function storeSection( $section, $data, SG_iCal $ical ) { $data = SG_iCal_Factory::Factory($ical, $section, $data); switch( $section ) { case 'vcalendar': return $ical->setCalendarInfo( $data ); case 'vevent': return $ical->addEvent( $data ); case 'vjournal': case 'vtodo': return true;  case 'vtimezone': return $ical->addTimeZone( $data ); } }  private static function _ValidUtf8( $data ) { $rx = '[\xC0-\xDF]([^\x80-\xBF]|$)'; $rx .= '|[\xE0-\xEF].{0,1}([^\x80-\xBF]|$)'; $rx .= '|[\xF0-\xF7].{0,2}([^\x80-\xBF]|$)'; $rx .= '|[\xF8-\xFB].{0,3}([^\x80-\xBF]|$)'; $rx .= '|[\xFC-\xFD].{0,4}([^\x80-\xBF]|$)'; $rx .= '|[\xFE-\xFE].{0,5}([^\x80-\xBF]|$)'; $rx .= '|[\x00-\x7F][\x80-\xBF]'; $rx .= '|[\xC0-\xDF].[\x80-\xBF]'; $rx .= '|[\xE0-\xEF]..[\x80-\xBF]'; $rx .= '|[\xF0-\xF7]...[\x80-\xBF]'; $rx .= '|[\xF8-\xFB]....[\x80-\xBF]'; $rx .= '|[\xFC-\xFD].....[\x80-\xBF]'; $rx .= '|[\xFE-\xFE]......[\x80-\xBF]'; $rx .= '|^[\x80-\xBF]'; return ( ! (bool) preg_match('!'.$rx.'!', $data) ); } }   class SG_iCal_Query {  public static function Between( $ical, $start, $end ) { if( $ical instanceof SG_iCalReader ) { $ical = $ical->getEvents(); } if( !is_array($evs) ) { throw new Exception('SG_iCal_Query::Between called with invalid input!'); } $rtn = array(); foreach( $evs AS $e ) { if( ($start <= $e->getStart() && $e->getStart() < $end) || ($start < $e->getEnd() && $e->getEnd() <= $end) ) { $rtn[] = $e; } } return $rtn; }  public static function After( $ical, $start ) { if( $ical instanceof SG_iCalReader ) { $ical = $ical->getEvents(); } if( !is_array($ical) ) { throw new Exception('SG_iCal_Query::After called with invalid input!'); } $rtn = array(); foreach( $ical AS $e ) { if( $start <= $e->getStart() ) { $rtn[] = $e; } } return $rtn; }  public static function Sort( $ical, $column ) { if( $ical instanceof SG_iCalReader ) { $ical = $ical->getEvents(); } if( !is_array($ical) ) { throw new Exception('SG_iCal_Query::Sort called with invalid input!'); } $cmp = create_function('$a, $b', 'return strcmp($a->getProperty("' . $column . '"), $b->getProperty("' . $column . '"));'); usort($ical, $cmp); return $ical; } } 