PageRenderTime 52ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/components/com_jevents/libraries/iCalImport.php

https://bitbucket.org/kraymitchell/saiu
PHP | 780 lines | 567 code | 70 blank | 143 comment | 122 complexity | f190a59541a163dc351b6a713eb6d606 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-3.0, BSD-3-Clause, LGPL-2.1, GPL-3.0
  1. b<?php
  2. /**
  3. * JEvents Component for Joomla 1.5.x
  4. *
  5. * @version $Id: iCalImport.php 3467 2012-04-03 09:36:16Z geraintedwards $
  6. * @package JEvents
  7. * @copyright Copyright (C) 2008-2009 GWE Systems Ltd, 2006-2008 JEvents Project Group
  8. * @license GNU/GPLv2, see http://www.gnu.org/licenses/gpl-2.0.html
  9. * @link http://www.jevents.net
  10. */
  11. // no direct access
  12. defined( '_JEXEC' ) or die( 'Restricted access' );
  13. // This class doesn't yet deal with repeating events
  14. class iCalImport
  15. {
  16. /**
  17. * This array saves the iCalendar parsed data as an array - may make a class later!
  18. *
  19. * @var array
  20. */
  21. var $cal = array();
  22. var $key;
  23. var $rawData = '';
  24. var $srcURL = '';
  25. var $eventCount = -1;
  26. var $todoCount = -1;
  27. var $vevents = array();
  28. function __construct () {
  29. }
  30. // constructor
  31. function import($filename,$rawtext="")
  32. {
  33. @ini_set("max_execution_time",600);
  34. echo JText::sprintf("Importing events from ical file %s", $filename)."<br/>";
  35. $cfg = & JEVConfig::getInstance();
  36. $option = JEV_COM_COMPONENT;
  37. // resultant data goes here
  38. if ($filename!=""){
  39. $file = $filename;
  40. if (!@file_exists($file)) {
  41. $file = JPATH_SITE."/components/$option/".$filename;
  42. }
  43. if (!file_exists($file)) {
  44. echo "I hope this is a URL!!<br/>";
  45. $file = $filename;
  46. }
  47. // get name
  48. $isFile = false;
  49. if (isset($_FILES['upload']) && is_array($_FILES['upload']) ) {
  50. $uploadfile = $_FILES['upload'];
  51. // MSIE sets a mime-type of application/octet-stream
  52. if ($uploadfile['size']!=0 && ($uploadfile['type']=="text/calendar" || $uploadfile['type']=="text/csv" || $uploadfile['type']=="application/octet-stream" || $uploadfile['type']=="text/html")){
  53. $this->srcURL = $uploadfile['name'];
  54. $isFile = true;
  55. }
  56. }
  57. if ($this->srcURL =="") {
  58. $this->srcURL = $file;
  59. }
  60. // $this->rawData = iconv("ISO-8859-1","UTF-8",file_get_contents($file));
  61. if (!$isFile && is_callable("curl_exec")){
  62. $ch = curl_init();
  63. curl_setopt($ch, CURLOPT_URL,($isFile?"file://":"").$file);
  64. curl_setopt($ch, CURLOPT_VERBOSE, 1);
  65. curl_setopt($ch, CURLOPT_POST, 0);
  66. curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
  67. $this->rawData = curl_exec($ch);
  68. curl_close ($ch);
  69. // try file_get_contents as a backup
  70. if ($isFile && $this->rawData === false) {
  71. $this->rawData = @file_get_contents($file);
  72. }
  73. }
  74. else {
  75. $this->rawData = @file_get_contents($file);
  76. }
  77. if ($this->rawData === false) {
  78. // file_get_contents: no or blocked wrapper for $file
  79. JError::raiseNotice(0, 'file_get_contents() failed, try fsockopen');
  80. $parsed_url = parse_url($file);
  81. if ($parsed_url === false) {
  82. JError::raiseWarning(0, 'url not parsed: ' . $file);
  83. } else {
  84. if ($parsed_url['scheme'] == 'http' || $parsed_url['scheme'] == 'https' || $parsed_url['scheme'] == 'webcal') {
  85. // try socked connection
  86. $fsockhost = $parsed_url['host'];
  87. $fsockport = 80;
  88. if ($parsed_url['scheme'] == 'https') {
  89. $fsockhost = 'ssl://' . $fsockhost;
  90. $fsockport = 443;
  91. }
  92. if (array_key_exists('port', $parsed_url)) $fsockport = $parsed_url['port'];
  93. $fh = @fsockopen($fsockhost, $fsockport, $errno, $errstr, 3);
  94. if ($fh === false) {
  95. // fsockopen: no connect
  96. JError::raiseWarning(0, 'fsockopen: no connect for ' . $file.' - '.$errstr );
  97. return false;
  98. } else {
  99. $fsock_path = ((array_key_exists('path', $parsed_url)) ? $parsed_url['path'] : '')
  100. . ((array_key_exists('query', $parsed_url)) ? $parsed_url['query'] : '')
  101. . ((array_key_exists('fragment', $parsed_url)) ? $parsed_url['fragment'] : '');
  102. fputs($fh, "GET $fsock_path HTTP/1.0\r\n");
  103. fputs($fh, "Host: ".$parsed_url['host']."\r\n\r\n");
  104. while(!feof($fh)) {
  105. $this->rawData .= fread($fh,4096);
  106. }
  107. fclose($fh);
  108. $this->rawData = JString::substr($this->rawData, JString::strpos($this->rawData, "\r\n\r\n")+4);
  109. }
  110. }
  111. }
  112. }
  113. // Returns true if $string is valid UTF-8 and false otherwise.
  114. /*
  115. $isutf8 = $this->detectUTF8($this->rawData);
  116. if ($isutf8) {
  117. $this->rawData = iconv("ISO-8859-1","UTF-8",$this->rawData);
  118. }
  119. */
  120. }
  121. else {
  122. $this->srcURL="n/a";
  123. $this->rawData = $rawtext;
  124. }
  125. ;
  126. // get rid of spurious carriage returns and spaces
  127. //$this->rawData = preg_replace("/[\r\n]+ ([:;])/","$1",$this->rawData);
  128. // simplify line feed
  129. $this->rawData = str_replace("\r\n","\n",$this->rawData);
  130. // remove spurious lines before calendar start
  131. if (!JString::stristr($this->rawData,'BEGIN:VCALENDAR')) {
  132. // check for CSV format
  133. $firstLine = JString::substr($this->rawData,0,JString::strpos($this->rawData,"\n")+1);
  134. if (JString::stristr($firstLine,'SUMMARY') && JString::stristr($firstLine,'DTSTART')
  135. && JString::stristr($firstLine,'DTEND') && JString::stristr($firstLine,'CATEGORIES')
  136. && JString::stristr($firstLine,'TIMEZONE')) {
  137. $timezone= date_default_timezone_get();
  138. $csvTrans = new CsvToiCal($file);
  139. $this->rawData = $csvTrans->getRawData();
  140. date_default_timezone_set($timezone);
  141. } else {
  142. JError::raiseWarning(0, 'Not a valid VCALENDAR data file: ' . $this->srcURL);
  143. //JError::raiseWarning(0, 'Not a valid VCALENDAR or CSV data file: ' . $this->srcURL);
  144. // return false so that we don't remove a valid calendar because of a bad URL load!
  145. return false;
  146. }
  147. }
  148. $begin = JString::strpos($this->rawData,"BEGIN:VCALENDAR",0);
  149. $this->rawData = JString::substr($this->rawData,$begin);
  150. // $this->rawData = preg_replace('/^.*\n(BEGIN:VCALENDAR)/s', '$1', $this->rawData, 1);
  151. // unfold content lines according the unfolding procedure of rfc2445
  152. $this->rawData = str_replace("\n ","",$this->rawData);
  153. $this->rawData = str_replace("\n\t","",$this->rawData);
  154. // TODO make sure I can always ignore the second line
  155. // Some google calendars has spaces and carriage returns in their UIDs
  156. // Convert string into array for easier processing
  157. $this->rawData = explode("\n", $this->rawData);
  158. $skipuntil = null;
  159. foreach ($this->rawData as $vcLine) {
  160. //$vcLine = trim($vcLine); // trim one line
  161. if (!empty($vcLine))
  162. {
  163. // skip unhandled block
  164. if ($skipuntil) {
  165. if (trim($vcLine) == $skipuntil) {
  166. // found end of block to skip
  167. $skipuntil = null;
  168. }
  169. continue;
  170. }
  171. $matches = explode(":",$vcLine,2);
  172. if (count($matches) == 2) {
  173. list($this->key,$value)= $matches;
  174. //$value = str_replace('\n', "\n", $value);
  175. //$value = stripslashes($value);
  176. $append=false;
  177. // Treat Accordingly
  178. switch ($vcLine) {
  179. case "BEGIN:VTODO":
  180. // start of VTODO section
  181. $this->todoCount++;
  182. $parent = "VTODO";
  183. break;
  184. case "BEGIN:VEVENT":
  185. // start of VEVENT section
  186. $this->eventCount++;
  187. $parent = "VEVENT";
  188. break;
  189. case "BEGIN:VCALENDAR":
  190. case "BEGIN:DAYLIGHT":
  191. case "BEGIN:VTIMEZONE":
  192. case "BEGIN:STANDARD":
  193. $parent = $value; // save tu array under value key
  194. break;
  195. case "END:VTODO":
  196. case "END:VEVENT":
  197. case "END:VCALENDAR":
  198. case "END:DAYLIGHT":
  199. case "END:VTIMEZONE":
  200. case "END:STANDARD":
  201. $parent = "VCALENDAR";
  202. break;
  203. default:
  204. // skip unknown BEGIN/END blocks
  205. if ($this->key == 'BEGIN') {
  206. $skipuntil = 'END:' . $value;
  207. break;
  208. }
  209. // Generic processing
  210. $this->add_to_cal($parent, $this->key, $value,$append);
  211. break;
  212. }
  213. } else {
  214. // ignore these lines go
  215. }
  216. }
  217. }
  218. // Sort the events into start date order
  219. // there's little point in doing this id an RRULE is present!
  220. // usort($this->cal['VEVENT'], array("iCalImport","comparedates"));
  221. // Populate vevent class - should do this first trawl through !!
  222. if (array_key_exists("VEVENT",$this->cal)) {
  223. foreach ($this->cal["VEVENT"] as $vevent){
  224. // trap for badly constructed all day events
  225. if (isset($vevent["DTSTARTRAW"]) && isset($vevent["DTENDRAW"]) && $vevent["DTENDRAW"] == $vevent["DTSTARTRAW"]){
  226. $vevent["DTEND"] += 86400;
  227. }
  228. $this->vevents[] = iCalEvent::iCalEventFromData($vevent);
  229. }
  230. }
  231. return $this;
  232. }
  233. function add_to_cal($parent, $key, $value, $append)
  234. {
  235. // I'm not interested in when the events were created/modified
  236. if (($key == "DTSTAMP") or ($key == "LAST-MODIFIED") or ($key == "CREATED")) return;
  237. if ($key == "RRULE" && $value!="") {
  238. $value = $this->parseRRULE($value,$parent);
  239. }
  240. $rawkey="";
  241. if (JString::stristr($key,"DTSTART") || JString::stristr($key,"DTEND") || JString::stristr($key,"EXDATE") ) {
  242. list($key,$value,$rawkey,$rawvalue) = $this->handleDate($key,$value);
  243. // if midnight then move back one day (ISO 8601 uses seconds past midnight http://www.iso.org/iso/date_and_time_format)
  244. // because of the odd way we record midnights
  245. if (JString::stristr($key,"DTEND") == "DTEND" && JString::strlen($rawvalue)>=15 && JString::substr($rawvalue,9,6)=="000000") {
  246. $value -= 1; // 1 second
  247. //$value -= 86400; // 1 day
  248. }
  249. if (JString::stristr($key,"DTEND") == "DTEND" && JString::strlen($rawvalue) == 8) {
  250. // all day event detected YYYYMMDD, set DTEND to last second of previous day
  251. $value -= 1; // 1 second
  252. }
  253. }
  254. if (JString::stristr($key,"DURATION")) {
  255. list($key,$value,$rawkey,$rawvalue) = $this->handleDuration($key,$value);
  256. }
  257. switch ($parent)
  258. {
  259. case "VTODO":
  260. $this->cal[$parent][$this->todoCount][$key] = $value;
  261. break;
  262. case "VEVENT":
  263. // strip off unnecessary quoted printable encoding message
  264. $parts = explode(';',$key);
  265. if (count($parts)>1 ){
  266. $key=$parts[0];
  267. for ($i=1; $i<count($parts);$i++) {
  268. if ($parts[$i]=="ENCODING=QUOTED-PRINTABLE"){
  269. //$value=str_replace("=0D=0A","<br/>",$value);
  270. $value=quoted_printable_decode($value);
  271. }
  272. // drop other ibts like language etc.
  273. }
  274. }
  275. // Special treatment of
  276. if (JString::strpos($key,"EXDATE")===false){
  277. $target =& $this->cal[$parent][$this->eventCount][$key];
  278. $rawtarget =& $this->cal[$parent][$this->eventCount][$rawkey];
  279. }
  280. else {
  281. if (!array_key_exists("EXDATE",$this->cal[$parent][$this->eventCount])){
  282. $this->cal[$parent][$this->eventCount]["EXDATE"]=array();
  283. $this->cal[$parent][$this->eventCount]["RAWEXDATE"]=array();
  284. }
  285. if (is_array($value)){
  286. $this->cal[$parent][$this->eventCount]["EXDATE"] = array_merge($this->cal[$parent][$this->eventCount]["EXDATE"], $value);
  287. $this->cal[$parent][$this->eventCount]["RAWEXDATE"][count($this->cal[$parent][$this->eventCount]["RAWEXDATE"])] = $rawvalue;
  288. break;
  289. }
  290. else {
  291. $target =& $this->cal[$parent][$this->eventCount]["EXDATE"][count($this->cal[$parent][$this->eventCount]["EXDATE"])];
  292. $rawtarget =& $this->cal[$parent][$this->eventCount]["RAWEXDATE"][count($this->cal[$parent][$this->eventCount]["RAWEXDATE"])];
  293. }
  294. }
  295. // Remove escaping of text
  296. $value = str_replace('\,',',',$value);
  297. $value = str_replace('\;',';',$value);
  298. // convert URLs to links but NOT in uid field!!
  299. //$value = ereg_replace("[[:alpha:]]+://[^<>[:space:]]+[[:alnum:]/]","<a href=\"\\0\">\\0</a>", $value);
  300. //$value = preg_replace('@(?<![">])\b(?:(?:https?|ftp)://|www\.|ftp\.)[-A-Z0-9+&@#/%=~_|$?!:,.]*[A-Z0-9+&@#/%=~_|$]@',"<a href=\"\\0\">\\0</a>", $value);
  301. if (is_string($value) && $key!="UID"){
  302. if (JString::strpos(str_replace(" ","",JString::strtolower($value)),"<ahref=")===false && JString::strpos(str_replace(" ","",JString::strtolower($value)),"<img")===false){
  303. $value = preg_replace('@(https?://([\w-.]+)+(:\d+)?(/([\w/_\-.]*(\?\S+)?)?)?)@', '<a href="$1">$1</a>', $value);
  304. }
  305. }
  306. // Fix some stupid Microsoft IIS driven calendars which don't encode the data properly!
  307. // see section 2 of http://www.the-art-of-web.com/html/character-codes/
  308. if ($key=="DESCRIPTION" || $key=="SUMMARY"){
  309. $len = strlen($value);
  310. $ulen = JString::strlen($value);
  311. // Can cause problems with multibyte strings so skip this
  312. if ($len == $ulen){
  313. $value =str_replace(array("\205","\221","\222","\223","\224","\225","\226","\227","\240"),array("...","'","'",'"','"',"*","-","--"," "),$value);
  314. }
  315. }
  316. // Strip http:// from UID !!!
  317. if ($key=="UID" && JString::strpos($value,"http://")!==false){
  318. $value = str_replace('http://',"http",$value);
  319. }
  320. if ($key=="RECURRENCE-ID"){
  321. if (count($parts)>1 ){
  322. for ($i=1; $i<count($parts);$i++) {
  323. if (JString::stristr($parts[$i],"TZID")){
  324. $value = $parts[$i].";".$value;
  325. }
  326. }
  327. }
  328. }
  329. // THIS IS NEEDED BECAUSE OF DODGY carriage returns in google calendar UID
  330. // TODO check its enough
  331. if ($append){
  332. $target .= $value;
  333. }
  334. else {
  335. $target = $value;
  336. }
  337. if ($rawkey!=""){
  338. $rawtarget = $rawvalue;
  339. }
  340. break;
  341. default:
  342. $this->cal[$parent][$key] = $value;
  343. break;
  344. }
  345. }
  346. function parseRRULE($value, $parent)
  347. {
  348. $result = array();
  349. $parts = explode(';',$value);
  350. foreach ($parts as $part) {
  351. if (JString::strlen($part)==0) continue;
  352. $portion = explode('=', $part);
  353. if (JString::stristr($portion[0],"UNTIL")){
  354. $untilArray = $this->handleDate($portion[0],$portion[1]);
  355. $result[$untilArray[0]] = $untilArray[1];
  356. $result[$untilArray[2]] = $untilArray[3];
  357. }
  358. else $result[$portion[0]] = $portion[1];
  359. }
  360. return $result;
  361. }
  362. /**
  363. * iCal spec represents date in ISO 8601 format followed by "T" then the time
  364. * a "Z at the end means the time is UTC and not local time zone
  365. *
  366. * TODO make sure if time is UTC we take account of system time offset properly
  367. *
  368. */
  369. function unixTime($ical_date, $tz=false)
  370. {
  371. jimport("joomla.utilities.date");
  372. static $offset = null;
  373. if (is_null($offset)) {
  374. $config =& JFactory::getConfig();
  375. $offset = $config->getValue('config.offset', 0);
  376. }
  377. if (!is_numeric($ical_date)){
  378. $t = JevDate::strtotime($ical_date);
  379. if (JString::strpos($ical_date,"Z")>0){
  380. if (is_callable("date_default_timezone_set")){
  381. $timezone= date_default_timezone_get();
  382. // See http://www.php.net/manual/en/timezones.php
  383. $params = JComponentHelper::getParams(JEV_COM_COMPONENT);
  384. // server offset tiemzone
  385. if ($params->get("icaltimezone","")!=""){
  386. date_default_timezone_set($params->get("icaltimezone",""));
  387. }
  388. // server offset PARAMS
  389. $serveroffset1 = (JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%S',$t))-JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%SZ',$t)))/3600;
  390. // server offset SERVER
  391. date_default_timezone_set($timezone);
  392. $serveroffset2 = (JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%S',$t))-JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%SZ',$t)))/3600;
  393. $t = new JevDate($ical_date,($serveroffset1-$serveroffset2) );
  394. //$t = new JevDate($ical_date );
  395. date_default_timezone_set($timezone);
  396. echo "icaldate = ".$ical_date." imported date=".$t->toMySQL()."<br/>";
  397. }
  398. else {
  399. // Summer Time adjustment
  400. list($y,$m,$d,$h,$min,$s) = explode(":", JevDate::strftime('%Y:%m:%d:%H:%M:%S',$t));
  401. $dst = (JevDate::mktime($h,$min,$s,$m,$d,$y,0)-JevDate::mktime($h,$min,$s,$m,$d,$y,-1))/3600;
  402. // server offset including DST
  403. $serveroffset = (JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%S',$t))-JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%SZ',$t)))/3600;
  404. $serveroffset += $dst;
  405. $t = new JevDate($ical_date , -($serveroffset+$offset));
  406. }
  407. /*
  408. echo "<h3>SET TIMEZONE</h3>";
  409. $timezone= date_default_timezone_get();
  410. date_default_timezone_set('America/New_York');
  411. $tempIcal = "20091020T163000Z";
  412. echo $tempIcal."<br/>";
  413. $temp = JevDate::strtotime($tempIcal);
  414. list($y,$m,$d,$h,$min,$s) = explode(":", JevDate::strftime('%Y:%m:%d:%H:%M:%S',$temp));
  415. echo "$y,$m,$d,$h,$min,$s<br/>";
  416. $dst = (JevDate::mktime($h,$min,$s,$m,$d,$y,0)-JevDate::mktime($h,$min,$s,$m,$d,$y,-1))/3600;
  417. $so = (JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%S',$temp))-JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%SZ',$temp)))/3600;
  418. echo " dst=".$dst." serverforoffset=".$so."<br/>";
  419. $so += $dst;
  420. $t = new JevDate($tempIcal);
  421. echo $t->toMySQL()."<br><br/>";
  422. $tempIcal = "20091029T163000Z";
  423. echo $tempIcal."<br/>";
  424. $temp = JevDate::strtotime($tempIcal);
  425. list($y,$m,$d,$h,$min,$s) = explode(":", JevDate::strftime('%Y:%m:%d:%H:%M:%S',$temp));
  426. echo "$y,$m,$d,$h,$min,$s<br/>";
  427. $dst = (JevDate::mktime($h,$min,$s,$m,$d,$y,0)-JevDate::mktime($h,$min,$s,$m,$d,$y,-1))/3600;
  428. $so = (JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%S',$temp))-JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%SZ',$temp)))/3600;
  429. echo " dst=".$dst." serverforoffset=".$so."<br/>";
  430. $so += $dst;
  431. $t = new JevDate($tempIcal );
  432. echo $t->toMySQL()."<br><br/>";
  433. $tempIcal = "20091103T163000Z";
  434. echo $tempIcal."<br/>";
  435. $temp = JevDate::strtotime($tempIcal);
  436. list($y,$m,$d,$h,$min,$s) = explode(":", JevDate::strftime('%Y:%m:%d:%H:%M:%S',$temp));
  437. echo "$y,$m,$d,$h,$min,$s<br/>";
  438. $dst = (JevDate::mktime($h,$min,$s,$m,$d,$y,0)-JevDate::mktime($h,$min,$s,$m,$d,$y,-1))/3600;
  439. $so = (JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%S',$temp))-JevDate::strtotime(JevDate::strftime('%Y%m%dT%H%M%SZ',$temp)))/3600;
  440. echo " dst=".$dst." serverforoffset=".$so."<br/>";
  441. $so += $dst;
  442. $t = new JevDate($tempIcal);
  443. echo $t->toMySQL()."<br>";
  444. */
  445. }
  446. else if ($tz != false && $tz != ""){
  447. // really should use the timezone of the inputted date
  448. $tz = new DateTimeZone($tz);
  449. $t = new JevDate($ical_date, $tz);
  450. echo "icaldate = ".$ical_date." imported date=".$t->toMySQL()."<br/>";
  451. }
  452. else {
  453. $compparams = JComponentHelper::getParams(JEV_COM_COMPONENT);
  454. $jtz = $compparams->get("icaltimezonelive", "");
  455. if ($jtz){
  456. $t = new JevDate($ical_date,$jtz);
  457. }
  458. else {
  459. $t = new JevDate($ical_date);
  460. }
  461. }
  462. //$result = $t->toMySQL();
  463. $result = $t->toUnix();
  464. return $result;
  465. }
  466. $isUTC = false;
  467. if (JString::strpos($ical_date,"Z")!== false){
  468. $isUTC = true;
  469. }
  470. // strip "T" and "Z" from the string
  471. $ical_date = str_replace('T', '', $ical_date);
  472. $ical_date = str_replace('Z', '', $ical_date);
  473. // split it out intyo YYYY MM DD HH MM SS
  474. preg_match("#([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{0,2})([0-9]{0,2})([0-9]{0,2})#", $ical_date,$date);
  475. list($temp,$y,$m,$d,$h,$min,$s)=$date;
  476. if (!$min) $min=0;
  477. if (!$h) $h=0;
  478. if (!$d) $d=0;
  479. if (!$s) $s=0;
  480. // Trap unix dated beofre 1970
  481. $y = max($y,1970);
  482. if ($isUTC) {
  483. $t = gmJevDate::mktime($h,$min,$s,$m,$d,$y) + 3600 * $offset;
  484. $result = JevDate::strtotime(gmdate('Y-m-d H:i:s', $t));
  485. } else {
  486. $result = JevDate::mktime($h,$min,$s,$m,$d,$y);
  487. }
  488. // double check!!
  489. //list($y1,$m1,$d1,$h1,$min1,$s1)=explode(":",JevDate::strftime('%Y:%m:%d:%H:%M:%S',$result));
  490. return $result;
  491. }
  492. // function to convert windows timezone IDs into Olsen equivalent
  493. function convertWindowsTzid($wtzid){
  494. $wtzdata = array();
  495. $wtzdata["Midway Island, Samoa"] = "Pacific/Midway";
  496. $wtzdata["Hawaii-Aleutian"] = "America/Adak";
  497. $wtzdata["Hawaii"] = "Etc/GMT+10";
  498. $wtzdata["Marquesas Islands"] = "Pacific/Marquesas";
  499. $wtzdata["Gambier Islands"] = "Pacific/Gambier";
  500. $wtzdata["Alaska"] = "America/Anchorage";
  501. $wtzdata["Tijuana, Baja California"] = "America/Ensenada";
  502. $wtzdata["Pitcairn Islands"] = "Etc/GMT+8";
  503. $wtzdata["Pacific Time (US & Canada)"] = "America/Los_Angeles";
  504. $wtzdata["Mountain Time (US & Canada)"] = "America/Denver";
  505. $wtzdata["Chihuahua, La Paz, Mazatlan"] = "America/Chihuahua";
  506. $wtzdata["Arizona"] = "America/Dawson_Creek";
  507. $wtzdata["Saskatchewan, Central America"] = "America/Belize";
  508. $wtzdata["Guadalajara, Mexico City, Monterrey"] = "America/Cancun";
  509. $wtzdata["Easter Island"] = "Chile/EasterIsland";
  510. $wtzdata["Central Time (US & Canada)"] = "America/Chicago";
  511. $wtzdata["Eastern Time (US & Canada)"] = "America/New_York";
  512. $wtzdata["Cuba"] = "America/Havana";
  513. $wtzdata["Bogota, Lima, Quito, Rio Branco"] = "America/Bogota";
  514. $wtzdata["Caracas"] = "America/Caracas";
  515. $wtzdata["Santiago"] = "America/Santiago";
  516. $wtzdata["La Paz"] = "America/La_Paz";
  517. $wtzdata["Faukland Islands"] = "Atlantic/Stanley";
  518. $wtzdata["Brazil"] = "America/Campo_Grande";
  519. $wtzdata["Atlantic Time (Goose Bay)"] = "America/Goose_Bay";
  520. $wtzdata["Atlantic Time (Canada)"] = "America/Glace_Bay";
  521. $wtzdata["Newfoundland"] = "America/St_Johns";
  522. $wtzdata["UTC-3"] = "America/Araguaina";
  523. $wtzdata["Montevideo"] = "America/Montevideo";
  524. $wtzdata["Miquelon, St. Pierre"] = "America/Miquelon";
  525. $wtzdata["Greenland"] = "America/Godthab";
  526. $wtzdata["Buenos Aires"] = "America/Argentina/Buenos_Aires";
  527. $wtzdata["Brasilia"] = "America/Sao_Paulo";
  528. $wtzdata["Mid-Atlantic"] = "America/Noronha";
  529. $wtzdata["Cape Verde Is."] = "Atlantic/Cape_Verde";
  530. $wtzdata["Azores"] = "Atlantic/Azores";
  531. $wtzdata["Greenwich Mean Time : Belfast"] = "Europe/Belfast";
  532. $wtzdata["Greenwich Mean Time : Dublin"] = "Europe/Dublin";
  533. $wtzdata["Greenwich Mean Time : Lisbon"] = "Europe/Lisbon";
  534. $wtzdata["Greenwich Mean Time : London"] = "Europe/London";
  535. $wtzdata["Monrovia, Reykjavik"] = "Africa/Abidjan";
  536. $wtzdata["Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"] = "Europe/Amsterdam";
  537. $wtzdata["Belgrade, Bratislava, Budapest, Ljubljana, Prague"] = "Europe/Belgrade";
  538. $wtzdata["Brussels, Copenhagen, Madrid, Paris"] = "Europe/Brussels";
  539. $wtzdata["West Central Africa"] = "Africa/Algiers";
  540. $wtzdata["Windhoek"] = "Africa/Windhoek";
  541. $wtzdata["Beirut"] = "Asia/Beirut";
  542. $wtzdata["Cairo"] = "Africa/Cairo";
  543. $wtzdata["Gaza"] = "Asia/Gaza";
  544. $wtzdata["Harare, Pretoria"] = "Africa/Blantyre";
  545. $wtzdata["Jerusalem"] = "Asia/Jerusalem";
  546. $wtzdata["Minsk"] = "Europe/Minsk";
  547. $wtzdata["Syria"] = "Asia/Damascus";
  548. $wtzdata["Moscow, St. Petersburg, Volgograd"] = "Europe/Moscow";
  549. $wtzdata["Nairobi"] = "Africa/Addis_Ababa";
  550. $wtzdata["Tehran"] = "Asia/Tehran";
  551. $wtzdata["Abu Dhabi, Muscat"] = "Asia/Dubai";
  552. $wtzdata["Yerevan"] = "Asia/Yerevan";
  553. $wtzdata["Kabul"] = "Asia/Kabul";
  554. $wtzdata["Ekaterinburg"] = "Asia/Yekaterinburg";
  555. $wtzdata["Tashkent"] = "Asia/Tashkent";
  556. $wtzdata["Chennai, Kolkata, Mumbai, New Delhi"] = "Asia/Kolkata";
  557. $wtzdata["Kathmandu"] = "Asia/Katmandu";
  558. $wtzdata["Astana, Dhaka"] = "Asia/Dhaka";
  559. $wtzdata["Novosibirsk"] = "Asia/Novosibirsk";
  560. $wtzdata["Yangon (Rangoon)"] = "Asia/Rangoon";
  561. $wtzdata["Bangkok, Hanoi, Jakarta"] = "Asia/Bangkok";
  562. $wtzdata["Krasnoyarsk"] = "Asia/Krasnoyarsk";
  563. $wtzdata["Beijing, Chongqing, Hong Kong, Urumqi"] = "Asia/Hong_Kong";
  564. $wtzdata["Irkutsk, Ulaan Bataar"] = "Asia/Irkutsk";
  565. $wtzdata["Perth"] = "Australia/Perth";
  566. $wtzdata["Eucla"] = "Australia/Eucla";
  567. $wtzdata["Osaka, Sapporo, Tokyo"] = "Asia/Tokyo";
  568. $wtzdata["Seoul"] = "Asia/Seoul";
  569. $wtzdata["Yakutsk"] = "Asia/Yakutsk";
  570. $wtzdata["Adelaide"] = "Australia/Adelaide";
  571. $wtzdata["Darwin"] = "Australia/Darwin";
  572. $wtzdata["Brisbane"] = "Australia/Brisbane";
  573. $wtzdata["Hobart"] = "Australia/Hobart";
  574. $wtzdata["Vladivostok"] = "Asia/Vladivostok";
  575. $wtzdata["Lord Howe Island"] = "Australia/Lord_Howe";
  576. $wtzdata["Solomon Is., New Caledonia"] = "Etc/GMT-11";
  577. $wtzdata["Magadan"] = "Asia/Magadan";
  578. $wtzdata["Norfolk Island"] = "Pacific/Norfolk";
  579. $wtzdata["Anadyr, Kamchatka"] = "Asia/Anadyr";
  580. $wtzdata["Auckland, Wellington"] = "Pacific/Auckland";
  581. $wtzdata["Fiji, Kamchatka, Marshall Is."] = "Etc/GMT-12";
  582. $wtzdata["Chatham Islands"] = "Pacific/Chatham";
  583. $wtzdata["Nuku'alofa"] = "Pacific/Tongatapu";
  584. $wtzdata["Kiritimati"] = "Pacific/Kiritimati";
  585. $wtzdata["Central Standard Time"] = "America/Chicago";
  586. // manual entries
  587. $wtzdata["GMT -0500 (Standard) / GMT -0400 (Daylight)"] = "America/New_York";
  588. $wtzdata["Eastern Standard Time"] = "America/New_York";
  589. $wtzdata["W. Europe Standard Time"] = "Europe/Paris";
  590. $wtzid = str_replace('"','',$wtzid);
  591. return array_key_exists($wtzid,$wtzdata ) ? $wtzdata[$wtzid] : $wtzid;
  592. }
  593. function handleDate($key, $value)
  594. {
  595. $rawvalue = $value;
  596. // we have an array of exdates
  597. if (JString::strpos($key,"EXDATE")===0 && JString::strpos($value,",")>0){
  598. $parts = explode(",",$value);
  599. $value = array();
  600. foreach ($parts as $val){
  601. $value[] = $this->unixTime($val);
  602. }
  603. }
  604. else {
  605. $tz = false;
  606. if (JString::strpos($key,"TZID=")>0){
  607. $parts = explode(";",$key);
  608. if (count($parts)>=2 && JString::strpos($parts[1],"TZID=")!==false){
  609. $tz = str_replace("TZID=", "",$parts[1]);
  610. $tz = $this->convertWindowsTzid($tz);
  611. }
  612. }
  613. $value = $this->unixTime($value, $tz);
  614. }
  615. $parts = explode(";",$key);
  616. if (count($parts)<2 || JString::strlen($parts[1])==0)
  617. {
  618. $rawkey=$key."RAW";
  619. return array($key,$value, $rawkey, $rawvalue);
  620. }
  621. $key = $parts[0];
  622. $rawkey=$key."RAW";
  623. return array($key,$value, $rawkey, $rawvalue);
  624. }
  625. function handleDuration($key,$value)
  626. {
  627. $rawvalue = $value;
  628. // strip "P" from the string
  629. $value = str_replace('P', '', $value);
  630. // split it out intyo W D H M S
  631. preg_match("/([0-9]*W)*([0-9]*D)*T?([0-9]*H)*([0-9]*M)*([0-9]*S)*/",$value,$details);
  632. @list($temp,$w,$d,$h,$min,$s)=$details;
  633. $duration = 0;
  634. $multiplier=1;
  635. $duration += intval(str_replace('S','',$s))*$multiplier;
  636. $multiplier=60;
  637. $duration += intval(str_replace('M','',$min))*$multiplier;
  638. $multiplier=3600;
  639. $duration += intval(str_replace('H','',$h))*$multiplier;
  640. $multiplier=86400;
  641. $duration += intval(str_replace('D','',$d))*$multiplier;
  642. $multiplier=604800;
  643. $duration += intval(str_replace('W','',$w))*$multiplier;
  644. $rawkey=$key."RAW";
  645. return array($key, $duration, $rawkey, $rawvalue);
  646. }
  647. /**
  648. * Compare two unix timestamp
  649. *
  650. * @param array $a
  651. * @param array $b
  652. * @return integer
  653. */
  654. function comparedates($a, $b)
  655. {
  656. if (!array_key_exists('DTSTART',$a) || !array_key_exists('DTSTART',$b) ){
  657. echo "help<br/>";
  658. }
  659. if ($a['DTSTART'] == $b['DTSTART']) return 0;
  660. return ($a['DTSTART'] > $b['DTSTART'])? +1 : -1;
  661. }
  662. // from http://fr3.php.net/manual/en/function.mb-detect-encoding.php#50087
  663. function is_utf8($string) {
  664. // From http://w3.org/International/questions/qa-forms-utf-8.html
  665. $result = preg_match('%^(?:
  666. [\x09\x0A\x0D\x20-\x7E] # ASCII
  667. | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
  668. | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
  669. | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
  670. | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
  671. | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
  672. | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
  673. | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
  674. )*$%xs', $string);
  675. return $result;
  676. } // function is_utf8
  677. // from http://fr3.php.net/manual/en/function.mb-detect-encoding.php#68607
  678. function detectUTF8($string)
  679. {
  680. return preg_match('%(?:
  681. [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
  682. |\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
  683. |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
  684. |\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
  685. |\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
  686. |[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
  687. |\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
  688. )+%xs', $string);
  689. }
  690. }