<?php

// Adaptions by Arnout Engelen:
// - add required DTSTAMP property
// - do not add METHOD:PUBLISH
// - put link in URL: property

header("Expires: " gmdate("D, d M Y H:i:s"0) . " GMT"); 

// RSS2iCal by Dean Sanvitale @2003
// My version displays one event per item with specific dates for each item
// Code used from RSSiCal Version 0.8.3 by Noel David Jackson (noel@noeljackson.com)
// RSS Parser by Edward Swindelles (ed@readinged.com)
//

//include("rssical/parseRSS.php");


////////////////////////////////////////////////////////////
//RSS Parser included inline for easy distribution

/* Copyright 2002-2003 Edward Swindelles (ed@readinged.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* Default settings, you may change them at your whim.  See README. */
$rss_cache_path         '';
$rss_default_cache_time 180;
$rss_debug_mode         true;


/* Private variables, do not change. */
$rss_contents           = array();
$rss_cache_age          0;
$rss_tag                '';
$rss_isItem             false;
$rss_isChannel          false;
$rss_isImage            false;
$rss_isTextInput        false;
$rss_index              0;

function 
stream_last_modified($url)
{
    if (
function_exists('version_compare') && version_compare(phpversion(), '4.3.0') > 0)
    {
        if (!(
$fp = @fopen($url'r')))
            return 
NULL;

        
$meta stream_get_meta_data($fp);
        for (
$j 0; isset($meta['wrapper_data'][$j]); $j++)
        {
            if (
strstr(strtolower($meta['wrapper_data'][$j]), 'last-modified'))
            {
                
$modtime substr($meta['wrapper_data'][$j], 15);
                break;
            }
        }
        
fclose($fp);
    }
    else
    {
        
$parts parse_url($url);
        
$host  $parts['host'];
        
$path  $parts['path'];

        if (!(
$fp = @fsockopen($host80)))
            return 
NULL;

        
$req "HEAD $path HTTP/1.0\r\nUser-Agent: PHP/".phpversion()."\r\nHost: $host:80\r\nAccept: */*\r\n\r\n";
        
fputs($fp$req);

        while (!
feof($fp))
        {
            
$str fgets($fp4096);
            if (
strstr(strtolower($str), 'last-modified'))
            {
                
$modtime substr($str15);
                break;
            }
        }
        
fclose($fp);
    }
    return isset(
$modtime) ? strtotime($modtime) : time();
}

function 
parseRSS($url$cache_file=NULL$cache_time=NULL)
{
    global   
$rss_contents$rss_default_cache_time$rss_isTextInput,
         
$rss_cache_path$rss_cache_age$rss_tag$rss_isImage,
         
$rss_isItem$rss_isChannel$rss_index$rss_debug_mode;

    
$rss_error '<br /><strong>Error on line %s of '.__FILE__.'</strong>: %s<br />';

    if (!
function_exists('xml_parser_create'))
    {
        if (
$rss_debug_mode)
            
printf($rss_error, (__LINE__-3), '<a href="http://www.php.net/manual/en/ref.xml.php">PHP\'s XML Extension</a> is not loaded or available.');

        return 
false;
    }

    
$rss_contents = array();

    if (!
is_null($cache_file))
    {
        if (!isset(
$rss_cache_path) || !strlen($rss_cache_path))
            
$rss_cache_path dirname(__FILE__);

        
$cache_file str_replace('//''/'$rss_cache_path.'/'.$cache_file);

        if (
is_null($cache_time))
            
$cache_time $rss_default_cache_time;

        
$rss_cache_age file_exists($cache_file) ? ceil((time() - filemtime($cache_file)) / 60) : 0;
        
$remotemodtime stream_last_modified($url);
        if (
is_null($remotemodtime))
        {
            if (
$rss_debug_mode)
                
printf($rss_error, (__LINE__-4), 'Could not connect to remote RSS file ('.$url.').');

            return 
false;
        }
    }

    if (
is_null($cache_file) ||
       (!
is_null($cache_file) && !file_exists($cache_file)) ||
       (!
is_null($cache_file) && file_exists($cache_file) && $rss_cache_age $cache_time && $remotemodtime > ((time()) - ($rss_cache_age 60))))
    {
        
$rss_tag     '';
        
$rss_isItem  false;
        
$rss_isChannel false;
        
$rss_index   0;

        
$saxparser = @xml_parser_create();
        if (!
is_resource($saxparser))
        {
            if (
$rss_debug_mode)
                
printf($rss_error, (__LINE__-4), 'Could not create an instance of <a href="http://www.php.net/manual/en/ref.xml.php">PHP\'s XML parser</a>.');

            return 
false;
        }

        
xml_parser_set_option($saxparserXML_OPTION_CASE_FOLDINGfalse);
        
xml_set_element_handler($saxparser'sax_start''sax_end');
        
xml_set_character_data_handler($saxparser'sax_data');

        if (!(
$fp = @fopen($url'r')))
        {
            if (
$rss_debug_mode)
                
printf($rss_error, (__LINE__-3), 'Could not connect to remote RSS file ('.$url.').');

            return 
false;
        }

        while (
$data fread($fp4096))
        {
            
$parsedOkay xml_parse($saxparser$datafeof($fp));

            if (!
$parsedOkay && xml_get_error_code($saxparser) != XML_ERROR_NONE)
            {
                if (
$rss_debug_mode)
                    
printf($rss_error, (__LINE__-3), 'File has an XML error (<em>'.xml_error_string(xml_get_error_code($saxparser)).'</em> at line <em>'.xml_get_current_line_number($saxparser).'</em>).');

                return 
false;
            }
        }

        
xml_parser_free($saxparser);
        
fclose($fp);

        if (!
is_null($cache_file))
        {
            if (!(
$cache = @fopen($cache_file'w')))
            {
                if (
$rss_debug_mode)
                    
printf($rss_error, (__LINE__-3), 'Could not right to cache file (<em>'.$cache_file.'</em>).  The path may be invalid or you may not have write permissions.');

                return 
false;
            }

            
fwrite($cacheserialize($rss_contents));
            
fclose($cache);
        }
    }
    else
    {
        if (!(
$fp = @fopen($cache_file'r')))
        {
            if (
$rss_debug_mode)
                
printf($rss_error, (__LINE__-3), 'Could not read contents of cache file (<em>'.$cache_file.'</em>).');

            return 
false;
        }

        
$rss_contents unserialize(fread($fpfilesize($cache_file)));
        
fclose($fp);
    }

    return 
$rss_contents;
}

function 
sax_start($parser$name$attribs)
{
    global 
$rss_tag$rss_isItem$rss_isChannel$rss_isImage$rss_index$rss_isTextInput;

    
$rss_tag $name strtolower($name);

    if (
$name == 'channel')
    {
        
$rss_isChannel true;
        
$rss_isImage false;
        
$rss_isItem false;
    }
    elseif (
$name == 'image')
    {
        
$rss_isChannel false;
        
$rss_isImage true;
        
$rss_isItem false;
    }
    elseif (
$name == 'item')
    {
        
$rss_index++;
        
$rss_isChannel false;
        
$rss_isImage false;
        
$rss_isItem true;
    }
    elseif (
$name == 'textinput')
    {
        
$rss_isChannel false;
        
$rss_isImage false;
        
$rss_isItem false;
        
$rss_isTextInput true;
    }
}

function 
sax_end($parser$name){}

function 
sax_data($parser$data)
{
    global 
$rss_tag$rss_isItem$rss_isChannel$rss_contents$rss_isTextInput$rss_isImage$rss_index;
    if (
$data != "\n")
    {
        if (
$rss_isChannel && !$rss_isItem && strlen($data))
            (!isset(
$rss_contents['channel'][$rss_tag]) || !strlen($rss_contents['channel'][$rss_tag])) ?
                
$rss_contents['channel'][$rss_tag] = $data :
                
$rss_contents['channel'][$rss_tag].= $data ;
        elseif (
$rss_isItem && strlen($data))
            (!isset(
$rss_contents[$rss_index-1][$rss_tag]) || !strlen($rss_contents[$rss_index-1][$rss_tag])) ? 
                
$rss_contents[$rss_index-1][$rss_tag] = $data :
                
$rss_contents[$rss_index-1][$rss_tag].= $data ;
        elseif (
$rss_isImage && strlen($data))
            (!isset(
$rss_contents['image'][$rss_tag]) || !strlen($rss_contents['image'][$rss_tag])) ?
                
$rss_contents['image'][$rss_tag] = $data :
                
$rss_contents['image'][$rss_tag].= $data ;
        elseif (
$rss_isTextInput && strlen($data))
            (!isset(
$rss_contents['textinput'][$rss_tag]) || !strlen($rss_contents['textinput'][$rss_tag])) ?
                
$rss_contents['textinput'][$rss_tag] = $data :
                
$rss_contents['textinput'][$rss_tag].= $data ;
    }
}
///////////////////////////////


function getVar($varName$varDefaultVal) {
   global 
$HTTP_GET_VARS$HTTP_POST_VARS$HTTP_POST_FILES$HTTP_COOKIE_VARS;

   if (isset(
$HTTP_GET_VARS[$varName])) {
      
$varVal $HTTP_GET_VARS[$varName];
   }
   elseif (isset(
$HTTP_POST_VARS[$varName])) {
      
$varVal $HTTP_POST_VARS[$varName];
   }
   elseif (isset(
$HTTP_POST_FILES[$varName])) {
      
$varVal $HTTP_POST_FILES[$varName];
   }
   elseif (isset(
$HTTP_COOKIE_VARS[$varName])) {
      
$varVal $HTTP_COOKIE_VARS[$varName];
   }
   else {
      
$varVal $varDefaultVal;
   }

   return 
$varVal;
}

function 
write_log ($logentry) {
   global 
$logfile;

   if (!
$logfile) {
      
$logfile fopen ("./log.txt""a");
   }
   
fwrite($logfiledate("m/d/y h:i:s A - ") . $logentry "\n");
}

function 
pre_fix($strText) {
    
$strResult $strText;
    
    
//$strResult = strip_tags($strResult);
    
    
$trans_array = array();

    
// Newlines should be equivalent to other whitespace 
    
$trans_array["\n"] = " ";
    
$trans_array["\r"] = " ";

    for (
$i=127$i<255$i++) {
        
$trans_array[chr($i)] = "&#" $i ";";
    }
    
$strResult strtr($strResult$trans_array);
    
    
$strResult ltrim($strResult);
    
$strResult rtrim($strResult);
    
    return 
$strResult;
}

function 
fix($data) {

$data pre_fix($data);

$patterns = array(
            
'/<br \/>/',
            
'/<\/p>/',
            
'/<p.*?>/',
            
            
'/(.*?)<a.*? href="(.*?http:\/\/.+?)".*?>(.*?)<\/a>(.*?)/',            
            
            
'/<code.*?>(.*?)<\/code>/',
            
'/<img.*?src=".*?".*?[\/]*>/',        
            
            
'/<blockquote.*?cite="(http:\/\/.+?)".*?>(.*?)<\/blockquote>/',    
            
'/<cite.*?>(.*?)<\/cite>/',
            
            
'/<[ou]l*?>(.+?)<\/[ou]l>/',
            
'/<li*?>(.+?)<\/li>/',

            
            
'/<span.*?>(.*?)<\/span>/',
            
'/<strong.*?>(.*?)<\/strong>/',
            
'/<b.*?>(.*?)<\/b>/',
            
'/<em.*?>(.*?)<\/em>/',
            
'/<i.*?>(.*?)<\/i>/',
            
'/,/',
            
'/;/',
            
            
'/&lt;/',
            
'/&gt;/',
            
'/&amp;/',
            
'/&#821[67];/',
            
'/&#822[01];/'
            
);
            
$replace = array(
//br and p    
            
"\\n",
            
"\\n\\n",
            
"",
//a element            
            //"\\1\\3 [link: \\2]\\4",
            
"\\1\\3 [link]\\4",
//code
            
"\\1",
//img
            
"[img]\\n",
//cite=,cite    
            
"\\n\\2[cite: \\1]\\n\\n",            
            
"[cite]\\1",
//ul/ol, li
            
"\\n\\1",
            
"* \\1\\n",

            
"\\1",
            
"\\1",
            
"\\1",
            
"\\1",
            
"\\1",
            
"\\,",
            
"\\;",
            
            
"<",
            
">",
            
"&",
            
"'",
            
"\""
            
);
            
        
$data preg_replace($patterns$replace$data);

$data strip_tags($data);

return 
$data;
}

$url getVar("url""");

if (
strlen($url) == 0) {
    echo 
"<HTML>\n";
    echo 
"<HEAD>\n";
    echo 
"<TITLE>RSS2iCal</TITLE>\n";
    echo 
"</HEAD>\n";
    echo 
"<BODY>\n";
    echo 
"View and/or Subscribe to RSS/RDF News Feeds in iCalendar (vCal 2.0) format.<P>\n";
    echo 
"Subscriptions have only been tested with Apple's iCal application.<P>\n";
    echo 
"Once you have subscribed to a news feed in Apple's iCal, iSync can be used to synchronize it with your iPod, Palm, or cell phone.<P>\n";
    echo 
"<HR>\n";
    echo 
"<FORM NAME=\"Form_View\" METHOD=GET ACTION=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "\">\n";
    echo 
"RSS/RDF URL: <INPUT TYPE=TEXT SIZE=50 NAME=\"url\" VALUE=\"http://www.e-queue.com/index.rdf\">\n";
    echo 
"<INPUT TYPE=HIDDEN NAME=\"format\" VALUE=\".ics\">\n";
    echo 
"<INPUT TYPE=SUBMIT VALUE=\"View\">\n";
    echo 
"<INPUT TYPE=BUTTON VALUE=\"Subscribe\" onClick=\"document.Form_Subscribe.url.value = document.Form_View.url.value; document.Form_Subscribe.submit();\">\n";
    echo 
"</FORM>\n";
    echo 
"<FORM NAME=\"Form_Subscribe\" METHOD=GET ACTION=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "\">\n";
    echo 
"<INPUT TYPE=HIDDEN SIZE=50 NAME=\"url\" VALUE=\"http://www.e-queue.com/index.rdf\">\n";
    echo 
"<INPUT TYPE=HIDDEN NAME=\"format\" VALUE=\".ics\">\n";
    echo 
"</FORM>\n";
    echo 
"Links to Common RSS Feeds Converted to iCal format and subscriptions to them:\n";
    echo 
"<UL>\n";
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.bbc.co.uk/syndication/feeds/news/ukfs_news/front_page/rss091.xml&format=*.ics\">BBC News | Front Page</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.bbc.co.uk/syndication/feeds/news/ukfs_news/front_page/rss091.xml&format=*.ics\">subscribe</A>]</LI>\n";
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://rss.com.com/2547-12-0-20.xml&format=*.ics\">CNET News.com</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://rss.com.com/2547-12-0-20.xml&format=*.ics\">subscribe</A>]</LI>\n";
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.e-queue.com/index.rdf&format=*.ics\">E-Queue</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.e-queue.com/index.rdf&format=*.ics\">subscribe</A>]</LI>\n";
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://maccentral.macworld.com/mnn.cgi&format=*.ics\">MacCentral</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://maccentral.macworld.com/mnn.cgi&format=*.ics\">subscribe</A>]</LI>\n";
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.salon.com/feed/RDF/salon_use.rdf&format=*.ics\">Salon</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.salon.com/feed/RDF/salon_use.rdf&format=*.ics\">subscribe</A>]</LI>\n";    
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://slashdot.org/slashdot.rss&format=*.ics\">Slashdot</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://slashdot.org/slashdot.rss&format=*.ics\">subscribe</A>]</LI>\n";
    echo 
"<LI><A HREF=\"http://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.wired.com/news_drop/netcenter/netcenter.rdf&format=*.ics\">Wired News</A> [<A HREF=\"webcal://" $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'] . "?url=http://www.wired.com/news_drop/netcenter/netcenter.rdf&format=*.ics\">subscribe</A>]</LI>\n";
    echo 
"</UL>\n";
    echo 
"</BODY>\n";
    echo 
"</HTML>\n";
}
else {

$url preg_replace('/http:\/\//'''$url);

header("Content-type: text/plain");
//header("Content-Disposition: inline; filename=MyiCalFile.ics");

if ($rssData parseRSS "http://$url")) {
    
$channel_title fix($rssData["channel"]["title"]);
    
$channel_description fix($rssData["channel"]["description"]);
    if (
strlen($channel_description) == 0) {
        
$channel_description $channel_title;
    }
    
$channel_date pre_fix($rssData["channel"]["dc:date"]);
    
$channel_pubdate pre_fix($rssData["channel"]["pubdate"]);
    
$channel_lastbuilddate pre_fix($rssData["channel"]["lastbuilddate"]);
    
    if (
strlen($channel_pubdate) > && (strlen($channel_date) == 0)) {
        
$channel_date $channel_pubdate;
    }
    if (
strlen($channel_lastbuilddate) > && (strlen($channel_date) == 0)) {
        
$channel_date $channel_lastbuilddate;
    }
        
    echo 
"BEGIN:VCALENDAR\n";
    echo 
"CALSCALE:GREGORIAN\n";
    
//echo "X-WR-TIMEZONE;VALUE=TEXT:US/Eastern\n";
    //echo "METHOD:PUBLISH\n";
    
echo "PRODID:RSS2iCal 0.0.1\n";
    echo 
"X-WR-CALNAME;VALUE=TEXT:" $channel_title "\n";
    echo 
"X-WR-CALDESC;VALUE=TEXT:" $channel_description "\n";
    
//echo "X-WR-RELCALID;VALUE=TEXT:123456789\n";
    
echo "VERSION:2.0\n";
    for ( 
$i 0; isset ( $rssData[$i] ); $i++ ) {
        
$item_title fix($rssData[$i]["title"]);
        
$item_link fix($rssData[$i]["link"]);
        if (
strlen(fix($rssData[$i]["guid"])) > && (strlen($item_link) == 0)) {
            
$item_link fix($rssData[$i]["guid"]);
        }
        
$item_description fix($rssData[$i]["description"]);
        if (
strlen(fix($rssData[$i]["content:encoded"])) > 0) {
            
$item_description fix($rssData[$i]["content:encoded"]);
        }
        if ((
strlen($item_description) > 0) && (strlen($item_title) == 0)) {
            
$item_title substr($item_description030) . "...";
        }
        
        
$item_date pre_fix($rssData[$i]["dc:date"]);
        
$item_pubdate pre_fix($rssData[$i]["pubdate"]);
        if (
strlen($item_pubdate) > && (strlen($item_date) == 0)) {
            
$item_date $item_pubdate;
        }
        
//If we use the channel date, all entries have the same datetime
        //This datetime is erroneous because it only applies to the latest item
        //For now don't use it.  This results in prettier iCal display
        //if (strlen($channel_date) > 0 && (strlen($item_date) == 0)) {
            //$item_date = $channel_date;
        //}

        //echo $item_date;
        
if (strlen($item_date) == 25) {
            
$strDT substr($item_date52) . "/";
            
$strDT $strDT substr($item_date82) . "/";
            
$strDT $strDT substr($item_date04) . " ";
            
$strDT $strDT substr($item_date118) . " GMT";
            
//echo "strDT: " . $strDT;
            
$gmt_offset substr($item_date193);
            
$gmt_offset 60 60 $gmt_offset;
            
//echo "gmt_offset: " . $gmt_offset;
            
$unix_time strtotime($strDT) - $gmt_offset;
            
//echo "unixtime: " . $unix_time;
            
$item_date gmdate("Ymd\THis\Z"$unix_time);
        }
        
// might be 30: i.e. "Sat, 1 Sep 2007 09:38:40 +0200"
        
elseif (strlen($item_date) >= 29) {
            
$item_date gmdate("Ymd\THis\Z"strtotime($item_date));
        }
        else {
            
$item_date date("Ymd");
        }
        
        echo 
"BEGIN:VEVENT\n";
        echo 
"UID:" md5($item_link $item_title) . "\n";
        echo 
"DTSTAMP:" $item_date "T000000\n";
        echo 
"SUMMARY:" $item_title "\n";
                
$item_description str_replace("[img]\\n"""$item_description);
                
$item_description str_replace("[link]"""$item_description);
        
//if ((strlen($item_link) > 0) && (strlen($item_description) > 0)) {
        //    echo "DESCRIPTION:" . $item_link . "\\n\\n" . $item_description . "\n";
        //}
        
if (strlen($item_description) > 0) {
            echo 
"DESCRIPTION:" $item_description "\n";
        }
        if (
strlen($item_link) > 0) {
            echo 
"URL:" $item_link "\n";
        }
        echo 
"DTSTART;VALUE=DATE:$item_date\n";
        echo 
"DTEND;VALUE=DATE:$item_date\n";

        
// nonstandard iCal tags:
        
echo "X-LINK:" $item_link "\n";

        echo 
"END:VEVENT\n";
    }
    echo 
"END:VCALENDAR\n";
}
else {
   echo 
"Unable to parse RSS feed.";
}

}
?>