#!/usr/bin/env perl ############################################################################# # @(#) RSS2cisco, An RSS feed to Cisco IP Phone Script version 2.0 # Copyright 2004, Joshua Cantara # # then heavily hacked in 2005 by Dirk Jagdmann to support # caching and better RSS parsing and new title overview. # # Then in 2008, Nic Tjirkalli hacked some more to # make it work with SIP images on a range of CISCO IP Phones # and translate German Characters # Some basic info on the script is available at :- # http://www.tjir.za.net/rss.html # # This program is licensed under the GPL: http://www.gnu.org/licenses/gpl.txt # Needs a whole wack of perl modules to work - I needed :- # If you need them, you can search/download them from http://search.cpan.org # # LWP::Simple # HTML::Parser # HTML::Tagset # URI # XML::RAI # Date::Format # XML::RSS::Parser # XML::Elemental # XML::NamespaceSupport # Class::ErrorHandler # Class::XPath # Some global variable initialisation # Do Not alter these Global definitions my $DEBUG = "NO"; my $FixGerman = "NO"; # # ============================================================= # Variables/items taht are user changeable or customisable # ============================================================= # # Add the URL of the RSS feed and a title to the array bellow # 'http://www.sportal.de/rss/sportal.rss', 'German Test', my @feeds = ( 'http://newsrss.bbc.co.uk/rss/newsplayer_uk_edition/world/rss.xml', 'BBC News', 'http://rss.cnn.com/rss/edition.rss', 'CNN Top Stories', 'http://rss.cnn.com/rss/edition_business.rss', 'CNN World Business', 'http://www.eskom.co.za/live/loadshedding.rss' , 'Eskom Loadshedding', 'http://feeds.iol.co.za/rss/feed_news.rss', 'IOL ZA', 'http://www.itweb.co.za/sections/newsfeed/download/Top.xml','IT Web ZA', 'http://www.moneyweb.co.za/mw/rss/mw/en/rss_daily_news.xml','Moneyweb ZA', 'http://www.news24.com/news24RSSFeed/0,,2,00.xml', 'News 24 Top Stories', 'http://www.sky.com/skynews/rss/article/0,,30000-1-100,00.xml', 'SKY News', ); # Static sites that are not RSS feeds - just sites that have static content my @sites = ( ); # Change any of these menu strings etc as required my $menutitle='News of the World'; my $menuprompt='Choose your propaganda'; my $defaultrefresh=600; # If you want strange characters in German RSS feeds to be translated to # hopefully correct German leave this uncomented. might break other # character sets. # my $FixGerman = "YES"; # Comment out if you do not want a menu option that displays the HTTP # environment variables sent by the phone when making a request # my $DEBUG="YES"; #################################################################### ## There should be nothing to change after here ## Unless you wanna rewrite the internals #################################################################### use strict; use LWP::Simple qw($ua get); use XML::RAI; use CGI; use URI::Escape; my %property; # construct url to script $ENV{SERVER_NAME} =~ s!/+$!!; $ENV{SCRIPT_NAME} =~ s!^/+!!; my $pathto = "http://$ENV{SERVER_NAME}/$ENV{SCRIPT_NAME}"; # Get environment variables returned by phone web browser getenv(); # Get phone model my $model = $property{'HTTP_X_CISCOIPPHONEMODELNAME'}; # Determine if phone supports XML 3.1 (only newer 7970s do this # From what we can see, 7960s with SIP iamges do XML 3.0 # XML 3.0 dose not support SoftKeyItem directive my $age = "NEW"; if ($model =~ /79[4,6]0/) { $age = "OLD"; } my $query = new CGI; my $item=$query->param('item'); my $url=$query->param('opts'); if ($url && $item) { printitem($url, $item, $query->param('maxitem')); } elsif ($url) { if ($url eq "DEBUG") { printenv() ; } else { printtitles($url); } } else { printmenu(); } sub getenv { my $var; my $val; foreach $var (sort(keys(%ENV))) { $val = $ENV{$var}; $val =~ s|\n|\\n|g; $val =~ s|"|\\"|g; $property{$var} = $val; } } sub printenv { my $k; print "Content-Type: text/xml\n\n"; print "\n"; print "HTTP Header Environment"; foreach my $k (sort keys %property) { #print uri_escape($k).":".uri_escape($property{$k})."\n"; my $value = $property{$k}; $value =~ s/\<|\>/ /g; print "$k:$value \n"; } print ""; if ($age eq "NEW") { my $pth = substr($pathto,0,255); print ""; print " Back"; print " $pth"; print " 3"; print ""; } print ""; } sub printmenu { my $pth= "$pathto?opts="; print "Content-Type: text/xml\n\n"; #foreach my $k (sort keys %ENV) #{ # print "$k: $ENV{$k}\n"; #} print "\n"; print "".substr($menutitle,0,31)."".substr($menuprompt,0,31)."\n"; for (my $i = 0; $i <= ($#feeds) && $i<=64*2;) { my $fe = $feeds[$i++]; my $nm = $feeds[$i++]; print ""; print "".substr(cmxml_escape($nm), 0, 63).""; print "".substr($pth.uri_escape($fe), 0, 255).""; print "\n"; } for (my $i = 0; $i <= ($#sites) && $i<=64*2;) { my $fe = $sites[$i++]; my $nm = $sites[$i++]; print ""; print "".substr(cmxml_escape($nm), 0, 63).""; print "".substr($fe, 0, 255).""; print "\n"; } # If DEBUG set to YES print an option to display HTTP Header # Environment variables if ($DEBUG eq "YES") { print ""; print "HTTP Header Variables"; print "$pth"."DEBUG"; print "\n"; } print "\n"; exit 0; } sub printitem { my ($url, $item, $maxitem) = @_; my $maxlength=4000; my $rai=XML::RAI->parse(getfeed($url)); printmenu() unless $rai; my $title=''; $title=substr(cmxml_escape($rai->channel->title),0,31) if $rai->channel->title; my $it=${$rai->items}[$item-1]; # get item my $date=''; $date=substr(cmxml_escape($it->issued),0,31) if $it->issued; my $txt=''; $txt .= cmxml_escape("$item: ".$it->title) if $it->title; $txt .= "\n--------------------------------\n"; $txt .= cmxml_escape($it->content) if $it->content; my $txt1 = $it->content; $txt=substr($txt,0,3999); print "Content-Type: text/xml\n\n"; print "\n"; print ""; print "$title" if $title; print "$date" if $date; print "$txt"; #print "$txt1"; if ($age eq "NEW") { if($item>1) { my $i=$item-1; my $pth = substr("$pathto?opts=".uri_escape($url)."\&maxitem=$maxitem\&item=$i",0,255); print ""; print " <<"; print " $pth"; print " 1"; print ""; } if($item<$maxitem) { my $i=$item+1; my $pth = substr("$pathto?opts=".uri_escape($url)."\&maxitem=$maxitem\&item=$i",0,255); print ""; print " >>"; print " $pth"; print " 2"; print ""; } } if ($age eq "NEW") { my $pth = substr("$pathto?opts=".uri_escape($url),0,255); print ""; print " Back"; print " $pth"; print " 3"; print ""; } print "\n"; } sub printtitles { my ($url) = @_; my $rai=XML::RAI->parse(getfeed($url)); printmenu() unless $rai; my $title=''; $title=substr(cmxml_escape($rai->channel->title),0,31) if $rai->channel->title; my $date=''; $date=substr(cmxml_escape($rai->channel->issued),0,31) if $rai->channel->issued; my $refresh=$defaultrefresh; if($rai->channel->src->query('ttl')) { $refresh=$rai->channel->src->query('ttl')*60; } print "Content-Type: text/xml\nRefresh: $refresh\n\n"; print "\n"; print ""; print "$title" if $title; print "$date" if $date; my $maxitem=0; foreach my $item (@{$rai->items}) { $maxitem++; } # todo: better array size detection my $pth = "$pathto?opts=".uri_escape($url)."\&maxitem=$maxitem\&item="; my $z=0; foreach my $item (@{$rai->items}) { last if ++$z >99; print ""; # print "Item $z"; print "".substr(cmxml_escape($item->title), 0, 63).""; print "".substr("$pth$z",0,255).""; # print "http://www.tjir.za.net"; print "\n"; } if ($age eq "NEW") { print ""; print " Select"; print " SoftKey:Select"; print " 1"; print ""; print ""; print " Reload"; print " ".substr($pth,0,255).""; print " 2"; print ""; print ""; print " Back"; print " ".substr($pathto,0,255).""; print " 3"; print ""; } print "\n"; } sub getfeed { my ($url) = @_; my $feed; # construct cache filename my $fn=$url; $fn =~ s/[^\w\d]/_/g; $fn = "/tmp/$fn"; # check if cached rss is valid if(-f $fn) { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size, $atime,$mtime,$ctime,$blksize,$blocks) = stat($fn); if($mtime > time()-($defaultrefresh-30) && $size>100) { open(A, $fn); $feed.=$_ while ; close(A); return $feed; } } # get RSS data $ua->timeout(15); $feed = get($url); printmenu() if length($feed)==0; # save into cache open(A, ">$fn"); print A $feed; close(A); return $feed; } sub cmxml_escape { ($_) = @_; return unless $_; $_=HTMLentity2latin1($_); s/\s+/ /g; # strip ws s/<.+?>//g; # strip html tags # escape special chars # s//\>/g; # s/ß/\ß/g; # s/ü/\ü/g; # # # Translating German characters fudged by web browser to # hopefully real German - list is far from complete # if ($FixGerman eq "YES") { s/Ã/ü/g; s/ü¼/ü/g; s/ü¶/ö/g; s/ü¤/ä/g; s/üŸ/ß/g; s/üœ/Ü/g; s/Â//g; s/ü„u/Ä/g; s/©/é/g; } if ($age eq "NEW") { # Older phone's SIP iamge web browser appear to have an issue # with &apos and possibly " so we wont change this s/'/\'/g; # ' s/"/\"/g; # " } s/\&(?!lt|gt|apos|quot|amp)/\&/g; return $_; } sub HTMLentity2latin1 { ($_) = @_; return unless $_; s/\Æ/AE/g; s/\Æ/AE/g; s/\Á/Á/g; s/\Á/Á/g; s/\Â/Â/g; s/\Â/Â/g; s/\À/À/g; s/\À/À/g; s/\Α/A/g; s/\Α/A/g; s/\Å/Å/g; s/\Å/Å/g; s/\Ã/Ã/g; s/\Ã/Ã/g; s/\Ä/Ä/g; s/\Ä/Ä/g; s/\Β/B/g; s/\Β/B/g; s/\Ç/Ç/g; s/\Ç/Ç/g; s/\Χ/X/g; s/\Χ/X/g; s/\‡/++/g; s/\‡/++/g; s/\Δ/D/g; s/\Δ/D/g; s/\Ð/Ð/g; s/\Ð/Ð/g; s/\É/É/g; s/\É/É/g; s/\Ê/Ê/g; s/\Ê/Ê/g; s/\È/È/g; s/\È/È/g; s/\Ε/E/g; s/\Ε/E/g; s/\Η/H/g; s/\Η/H/g; s/\Ë/Ë/g; s/\Ë/Ë/g; s/\Γ/G/g; s/\Γ/G/g; s/\Í/Í/g; s/\Í/Í/g; s/\Î/Î/g; s/\Î/Î/g; s/\Ì/Ì/g; s/\Ì/Ì/g; s/\Ι/I/g; s/\Ι/I/g; s/\Ï/Ï/g; s/\Ï/Ï/g; s/\Κ/K/g; s/\Κ/K/g; s/\Λ/L/g; s/\Λ/L/g; s/\Μ/M/g; s/\Μ/M/g; s/\Ñ/Ñ/g; s/\Ñ/Ñ/g; s/\Ν/N/g; s/\Ν/N/g; s/\Œ/½/g; s/\Œ/½/g; s/\Ó/Ó/g; s/\Ó/Ó/g; s/\Ô/Ô/g; s/\Ô/Ô/g; s/\Ò/Ò/g; s/\Ò/Ò/g; s/\Ω/O/g; s/\Ω/O/g; s/\Ο/o/g; s/\Ο/o/g; s/\Ø/Ø/g; s/\Ø/Ø/g; s/\Õ/Õ/g; s/\Õ/Õ/g; s/\Ö/Ö/g; s/\Ö/Ö/g; s/\Φ/P/g; s/\Φ/P/g; s/\Π/pi/g; s/\Π/pi/g; s/\″/"/g;s/\″/"/g; s/\Ψ/psi/g; s/\Ψ/psi/g; s/\Ρ/P/g; s/\Ρ/P/g; s/\Š/¦/g; s/\Š/¦/g; s/\Σ/E/g; s/\Σ/E/g; s/\Þ/Þ/g; s/\Þ/Þ/g; s/\Τ/T/g; s/\Τ/T/g; s/\Θ/(x)/g; s/\Θ/(x)/g; s/\Ú/Ú/g; s/\Ú/Ú/g; s/\Û/Û/g; s/\Û/Û/g; s/\Ù/Ù/g; s/\Ù/Ù/g; s/\Υ/Y/g; s/\Υ/Y/g; s/\Ü/Ü/g; s/\Ü/Ü/g; s/\Ξ/[x]/g; s/\Ξ/[x]/g; s/\Ý/Ý/g; s/\Ý/Ý/g; s/\Ÿ/¾/g; s/\Ÿ/¾/g; s/\Ζ/Z/g; s/\Ζ/Z/g; s/\á/á/g; s/\á/á/g; s/\â/â/g; s/\â/â/g; s/\´/'/g; s/\´/'/g; s/\æ/æ/g; s/\æ/æ/g; s/\à/à/g; s/\à/à/g; s/\ℵ/x/g; s/\ℵ/x/g; s/\α/a/g; s/\α/a/g; s/\∧/&/g; s/\∧/&/g; s/\∠/</g; s/\∠/</g; s/\å/å/g; s/\å/å/g; s/\≈/~/g; s/\≈/~/g; s/\ã/ã/g; s/\ã/ã/g; s/\ä/ä/g; s/\ä/ä/g; s/\„/"/g;s/\„/"/g; s/\β/ß/g; s/\β/ß/g; s/\¦/|/g; s/\¦/|/g; s/\•/·/g; s/\•/·/g; s/\∩/cap/g; s/\∩/cap/g; s/\ç/ç/g; s/\ç/ç/g; s/\¸/,/g; s/\¸/,/g; s/\¢/¢/g; s/\¢/¢/g; s/\χ/æ/g; s/\χ/æ/g; s/\ˆ/^/g; s/\ˆ/^/g; s/\♣/*/g; s/\♣/*/g; s/\≅/~=/g; s/\≅/~=/g; s/\©/(c)/g; s/\©/(c)/g; s/\↵/<-|/g;s/\↵/<-|/g; s/\∪/u/g; s/\∪/u/g; s/\¤/¤/g; s/\¤/¤/g; s/\⇓/V/g; s/\⇓/V/g; s/\†/+/g; s/\†/+/g; s/\↓/v/g; s/\↓/v/g; s/\°/°/g; s/\°/°/g; s/\δ/ð/g; s/\δ/ð/g; s/\♦/#/g; s/\♦/#/g; s/\÷/÷/g; s/\÷/÷/g; s/\é/é/g; s/\é/é/g; s/\ê/ê/g; s/\ê/ê/g; s/\è/è/g; s/\è/è/g; s/\∅/ø/g; s/\∅/ø/g; s/\ / /g; s/\ / /g; s/\ / /g; s/\ / /g; s/\ε/e/g; s/\ε/e/g; s/\≡/=/g; s/\≡/=/g; s/\η/n/g; s/\η/n/g; s/\ð/ô/g; s/\ð/ô/g; s/\ë/ë/g; s/\ë/ë/g; s/\€/¤/g; s/\€/¤/g; s/\∃/E/g; s/\∃/E/g; s/\ƒ/f/g; s/\ƒ/f/g; s/\∀/V/g; s/\∀/V/g; s/\½/1\/2/g; s/\½/1\/2/g; s/\¼/1\/4/g; s/\¼/1\/4/g; s/\¾/3\/4/g; s/\¾/3\/4/g; s/\⁄/\//g; s/\⁄/\//g; s/\γ/y/g; s/\γ/y/g; s/\≥/>=/g; s/\≥/>=/g; s/\⇔/<=>/g;s/\⇔/<=>/g; s/\↔/<->/g;s/\↔/<->/g; s/\♥/V/g; s/\♥/V/g; s/\…/.../g; s/\…/.../g; s/\í/í/g; s/\í/í/g; s/\î/î/g; s/\î/î/g; s/\¡/¡/g; s/\¡/¡/g; s/\ì/ì/g; s/\ì/ì/g; s/\ℑ/I/g; s/\ℑ/I/g; s/\∞/oo/g; s/\∞/oo/g; s/\∫/f/g; s/\∫/f/g; s/\ι/i/g; s/\ι/i/g; s/\¿/¿/g; s/\¿/¿/g; s/\∈/E/g; s/\∈/E/g; s/\ï/ï/g; s/\ï/ï/g; s/\κ/k/g; s/\κ/k/g; s/\⇐/<=/g; s/\⇐/<=/g; s/\λ/l/g; s/\λ/l/g; s/\⟨/</g; s/\〈/</g; s/\«/«/g; s/\«/«/g; s/\»/»/g; s/\»/»/g; s/\←/<-/g; s/\←/<-/g; s/\⌈/|-/g; s/\⌈/|-/g; s/\“/"/g;s/\“/"/g; s/\≤/<=/g; s/\≤/<=/g; s/\⌊/|_/g; s/\⌊/|_/g; s/\∗/*/g; s/\∗/*/g; s/\◊/<>/g;s/\◊/<>/g; s/\‎/<-->/g;s/\‎/<-->/g; s/\‹/</g; s/\‹/</g; s/\‘/'/g;s/\‘/'/g; s/\¯/'/g; s/\¯/'/g; s/\—/-/g; s/\—/-/g; s/\µ/µ/g; s/\µ/µ/g; s/\·/·/g; s/\·/·/g; s/\−/-/g; s/\−/-/g; s/\μ/µ/g; s/\μ/µ/g; s/\∇/V/g; s/\∇/V/g; s/\ / /g; s/\ / /g; s/\–/-/g; s/\–/-/g; s/\≠/!=/g; s/\≠/!=/g; s/\∋/E/g; s/\∋/E/g; s/\¬/¬/g; s/\¬/¬/g; s/\∉/¬E/g; s/\∉/¬E/g; s/\⊄/¬</g; s/\⊄/¬</g; s/\ñ/ñ/g; s/\ñ/ñ/g; s/\ν/v/g; s/\ν/v/g; s/\ó/ó/g; s/\ó/ó/g; s/\ô/ô/g; s/\ô/ô/g; s/\œ/½/g; s/\œ/½/g; s/\ò/ò/g; s/\ò/ò/g; s/\‾/'/g; s/\‾/'/g; s/\ω/w/g; s/\ω/w/g; s/\ο/o/g; s/\ο/o/g; s/\⊕/(+)/g; s/\⊕/(+)/g; s/\∨/v/g; s/\∨/v/g; s/\ª/ª/g; s/\ª/ª/g; s/\º/º/g; s/\º/º/g; s/\ø/ø/g; s/\ø/ø/g; s/\õ/ô/g; s/\õ/ô/g; s/\⊗/(x)/g; s/\⊗/(x)/g; s/\ö/ö/g; s/\ö/ö/g; s/\¶/¶/g; s/\¶/¶/g; s/\∂/a/g; s/\∂/a/g; s/\‰/0\/oo/g;s/\‰/0\/oo/g; s/\⊥/^/g; s/\⊥/^/g; s/\φ/phi/g; s/\φ/phi/g; s/\π/pi/g; s/\π/pi/g; s/\ϖ/piv/g; s/\ϖ/piv/g; s/\±/±/g; s/\±/±/g; s/\£/£/g; s/\£/£/g; s/\′/'/g; s/\′/'/g; s/\∏/|'|/g; s/\∏/|'|/g; s/\∝/~/g; s/\∝/~/g; s/\ψ/V/g; s/\ψ/V/g; s/\⇒/=>/g; s/\⇒/=>/g; s/\√/sqrt/g; s/\√/sqrt/g; s/\⟩/>/g; s/\〉/>/g; s/\⌉/'|/g; s/\⌉/'|/g; s/\”/"/g;s/\”/"/g; s/\ℜ/R/g; s/\ℜ/R/g; s/\®/®/g; s/\®/®/g; s/\⌋/_|/g; s/\⌋/_|/g; s/\ρ/p/g; s/\ρ/p/g; s/\‏/<->/g;s/\‏/<->/g; s/\›/>/g; s/\›/>/g; s/\’/'/g; s/\’/'/g; s/\‚/,/g; s/\‚/,/g; s/\š/¦/g; s/\š/¦/g; s/\⋅/·/g; s/\⋅/·/g; s/\§/§/g; s/\§/§/g; s/\­/-/g; s/\­/-/g; s/\σ/o/g; s/\σ/o/g; s/\ς/l/g; s/\ς/l/g; s/\∼/~/g; s/\∼/~/g; s/\♠/*/g; s/\♠/*/g; s/\⊂/sub/g; s/\⊂/sub/g; s/\⊆/sub=/g; s/\⊆/sub=/g; s/\∑/E/g; s/\∑/E/g; s/\¹/¹/g; s/\¹/¹/g; s/\²/²/g; s/\²/²/g; s/\³/³/g; s/\³/³/g; s/\⊃/sup/g; s/\⊃/sup/g; s/\⊇/sup=/g; s/\⊇/sup=/g; s/\ß/ß/g; s/\ß/ß/g; s/\τ/t/g; s/\τ/t/g; s/\∴/:=/g; s/\∴/:=/g; s/\θ/Ø/g; s/\θ/Ø/g; s/\ϑ/v/g; s/\ϑ/v/g; s/\ / /g; s/\ / /g; s/\þ/þ/g; s/\þ/þ/g; s/\˜/~/g; s/\˜/~/g; s/\×/x/g; s/\×/x/g; s/\™/(TM)/g; s/\™/(TM)/g; s/\⇑/^/g; s/\⇑/^/g; s/\ú/ú/g; s/\ú/ú/g; s/\↑/^/g; s/\↑/^/g; s/\û/û/g; s/\û/û/g; s/\ù/ù/g; s/\ù/ù/g; s/\¨/"/g; s/\¨/"/g; s/\ϒ/Y/g; s/\ϒ/Y/g; s/\υ/y/g; s/\υ/y/g; s/\ü/ü/g; s/\ü/ü/g; s/\℘/P/g; s/\℘/P/g; s/\ξ/x/g; s/\ξ/x/g; s/\ý/ý/g; s/\ý/ý/g; s/\¥/¥/g; s/\¥/¥/g; s/\ÿ/ÿ/g; s/\ÿ/ÿ/g; s/\ζ/z/g; s/\ζ/z/g; s/\‍//g; s/\‍//g; s/\‌//g; s/\‌//g; return $_; }