Home
Navigation
Impressum
Coder Welten - Programmierung und Optimierung
Script für einen sicheren Feedreader für RSS- und Atom-Feeds

Beschreibung und Einzelheiten zum Code

HM-Feedleser (Variante mit file_get_contents - Release-Version 1.04)

Vorstellung und Beschreibung des Scripts:

  1. Feedreader mit SimpleXML (Einführung und Vorstellung eines Scripts für einen sicheren Feedreader...)
  2. Feedreader File (Einzelheiten zum Code - Variante mit file_get_contents)
  3. Feedreader mit cURL (Code mit der cURL-Variante und ergänzende Hinweise)
  4. Zugehörige Dateien (Formular, Formular-Klasse und eine CSS-Dateie)
  5. Variante mit Feed-Klasse (zur leichteren Einbindung in HTML-Seiten, Version 1.06)
  6. Feed-Klasse und Methoden (nur für HM-Feedleser ab Version 1.06)
  7. Plugin für WordPress (Feedreader als Plugin für WordPress, Version 1.08)

Ein kleiner Nachtrag zur Sicherheit

Warum und weshalb Newsfeeds nach Möglichkeit niemals unkon­trolliert über­nommen und ausgegeben werden sollten, wissen Sie bereits, falls Sie die Seite "Feedreader mit SimpleXML" nicht über­sprungen haben. Nachfolgend noch ein kleines Beispiel.

Ein Image-Tag wie das folgende, eingebunden in einem CDATA-Abschnitt, würde bei fehlender Prüfung von Images sicherlich pro­blem­los mit durchschlüpfen. Weiterhin würde ein Fehler bei der Ausgabe ausbleiben, so lange ein referenziertes Image erreichbar wäre. Wird dieses Image nach einer gewissen Zeitspanne jedoch gelöscht, würde der Event-Handler onError stattdessen den Code laden. 

<img src="http://...com/bild.jpg" onerror="Schadcode laden">

Wer seine Seiten so gut es geht absichern möchte, sollte den Content aus CDATA-Abschnitten filtern und Images überprüfen. Das auf dieser Seite vorgestellte Script wandelt, prüft und filtert:

Ergänzender Hinweis zum Thema Sicherheit: Wir empfehlen Ihnen das Formular nur zur Erprobung und für Testzwecke zu benutzen oder in einem geschützten Bereich. Nur so können Sie sich sicher sein, dass Unbe­fugte keine Feeds aus fragwürdigen oder unsi­che­ren Quellen laden können.

Weitere Einzelheiten

Die jeweiligen Feeds werden mit der PHP-Funktion file_get_contents() eingelesen, wobei der Verbindungsaufbau nur bei Feeds von entfernten Hosts mit Angabe der jeweiligen URL und mit der Angabe des HTTP-Protokolls (http://) erfolgen muss. Für das Öffnen von Feed-Dateien, die unter der eigenen Domain erreichbar sind bzw. inner­halb des eigenen Webspaces liegen, sollte es genügen, den Pfad und den Dateinamen anzugeben.

Der Wert für error_reporting sollte bei der Erprobung auf E_ALL gesetzt werden und ist gegebenenfalls durch eine zweite Zeile mit

ini_set("display_errors", true);

zu ergänzen. Nach der Erprobung sollte diese zweite Zeile wieder entfernt oder auskommentiert werden. Weiterhin sollte der Wert für error_reporting auf 0 gesetzt werden, um nur noch eigene Fehler­mitteilungen durch das Script ausgeben zu lassen.

Weiterhin empfehlen wir, ohne einschlägige Kenntnisse nur die erfor­der­lichen Werte zu editieren, da anderenfalls die Funktionali­tät des Scripts beeinträchtigt sein könnte.


Code der zum HM-Feedleser gehörenden Dateien:

Bitte vor einem Download lesen:

» Script für einen sicheren Feedreader...

» Lizenz für Software

Download »

Neuste Version » Zum leichteren Einbinden in bestehende Webseiten wurden bei dieser Version die Funktionen zu einer Klasse vereint und ausgelagert »

Feedleser 1.06 (PHP Version ab 5.3)

Weitere Versionen »


Code-Listing der Datei feedleser.php

<?php
/*------------------------------------------------------------------------------------------------------------------
 Software-Name: HM-Feedleser (http://www.coder-welten.de/projekte/feedreader.htm)
 Beschreibung:  PHP-Script für einen sicheren Feedreader mit SimpleXML für RSS- und Atom-Feeds.
 Einzelheiten:  http://www.coder-welten.de/projekte/feedreader-file.htm
 Autor:         Horst Müller
 Version:       Release 1.04 (Variante mit file_get_contents)
 Datum:         09. Juni 2013
 Lizenz:        Lizenz für Software - http://www.coder-welten.de/projekte/lizenz-fuer-software.htm
 Copyright:     © 2006/2013 - Verlag Horst Müller - Stendal
 -------------------------------------------------------------------------------------------------------------------
*/
ini_set("user_agent", "Mozilla/5.0 (compatible; HM-Feedleser +".$_SERVER["HTTP_HOST"].$_SERVER["PHP_SELF"].")");
header("Content-Type: text/html; charset=UTF-8");

/*-- Die folgenden Werte sollten bei Bedarf editiert werden, $seite = "" muss editiert werden. -------------------*/

error_reporting(0);                      // Der Wert kann nach Erprobung von E_ALL auf 0 gesetzt werden

/*-- Die Datei oder die Web-Adresse des zu ladenden Feeds, Datei ohne http:// und URL mit http:// eintragen. -----*/

$seite = "http://www.coder-welten.de/projekte/testfeed.rss";
$linkt = "» Weiterlesen «";              // Linktext für den unteren Link
$feedt = "Feed - ";                      // Für den einleitenden Titel des Feeds, kann auch leer bleiben
$maxim = 12;                             // Maximale Anzahl der Ergebnisse pro Seite
$formu = true;                           // Formular anzeigen gleich "true" oder nicht anzeigen gleich "false"
$descr = true;                           // Description anzeigen gleich "true" oder ausblenden gleich "false"
$conte = true;                           // Content anzeigen gleich "true" oder ausblenden gleich "false"
$summa = true;                           // Summary anzeigen gleich "true" oder ausblenden gleich "false"
$maxho = 250;                            // Maximale Höhe für eingebundene Bilder angeben
$maxwe = 250;                            // Maximale Weite für eingebundene Bilder angeben
define("SCHLIESSE", ">");                // Für valides HTML je nach Dokumenttyp-Deklaration bei HTML ">" oder bei XHTML " />"

/*------------------------------------------------------------------------------------------------------------------
 Nachfolgend der HTML-Header der Seite. Sollte das Script in eine andere HTML-Seite eingebunden werden, so ist zu
 berücksichtigen, dass vor ini_set und header() keine Ausgabe von HTML erfolgen darf, auch keine Ausgabe von
 Quelltext oder Leerzeichen.
 -------------------------------------------------------------------------------------------------------------------
*/
?>
<!DOCTYPE html>
<html>

<head>
<title>Feedreader</title>
<?php echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"feedstyle.css\"".SCHLIESSE."\n"; ?>
</head>

<body>
<h1>Feedreader für RSS und Atom</h1>
<?php
/*------------------------------------------------------------------------------------------------------------------
 Ab hier sollte ohne einschlägige Kenntnisse nichts mehr editiert werden. Das Formular für den Aufruf von Feeds von
 unterschiedlichen Web-Adressen kann bei Bedarf über den Wert von $formu ein- oder ausgeblendet werden.
 -------------------------------------------------------------------------------------------------------------------
*/
if ($formu == true) {

    if (file_exists("feedformular.php")) {

        include_once "feedformular.php";

    } else {echo "Formular konnte nicht geladen werden!\n";
    }
}

/*-- Die Funktion wandelt Tags und behandelt Entities außerhalb von CDATA-Abschnitten, falls erforderlich. ------*/

function wandleTags($daten) {

    $wandle  = array(

        "content:encoded" => "content",
        "dc:creator"      => "creator",
        "dc:date"         => "published"
    );
    $daten = strtr($daten, $wandle);
    $daten = preg_replace_callback("/(&[#a-z0-9]+;)/",

        function($enti) {

            return htmlspecialchars(mb_convert_encoding($enti[1], "UTF-8", "HTML-ENTITIES"));
        }, $daten);

    /*-- Für den Fall, dass der Feed nur als String ohne Zeilenumbrüche ausgeliefert wird. ----------------------*/

    if (strpos($daten, "><item>") !== false) {

        $eing  = array(

            "><item>"        => ">\r\n<item>",
            "><title>"       => ">\r\n<title>",
            "><link>"        => ">\r\n<link>",
            "><description>" => ">\r\n<description>",
            "><content>"     => ">\r\n<content>",
            "><summary>"     => ">\r\n<summary>",
            "><enclosure>"   => ">\r\n<enclosure>",
            "><pubDate>"     => ">\r\n<pubDate>",
            "><updated>"     => ">\r\n<updated>",
            "><published>"   => ">\r\n<published",
            "><author>"      => ">\r\n<author>",
            "><creator>"     => ">\r\n<creator>"
        );
        $daten = strtr($daten, $eing);
    }
    return $daten;
}

/*------------------------------------------------------------------------------------------------------------------
 Die Funktion filtert HTML-Tags innerhalb und außerhalb von CDATA-Abschnitten und wandelt erlaubte Tags in BBCode.
 Weiterhin findet eine erste Überprüfung von Images statt, falls $image nicht gleich false.
 -------------------------------------------------------------------------------------------------------------------
*/
function filtereHTML($daten, $image) {

    $daten = preg_replace("/<p.*?>(.+?)<\/p>/is", "$1[br]", $daten);
    $daten = preg_replace("/<div.*?>(.+?)<\/div>/is", "$1[br]", $daten);
    $daten = preg_replace("/<span.*?>(.*?)<\/span>/is", "$1", $daten);
    $daten = preg_replace("/<br.*?>/i", "[br]", $daten);
    $daten = preg_replace("/<a href.+?>(.*?)<\/a>/is", "$1", $daten);

    if ($image === true) {
        $daten = preg_replace("/<img.*?src=\"([a-z0-9_\/=.:;&?-]+?)\.(jpg|png).*?>/is", "[img]$1.$2[/img]", $daten);
    }
    $eing  = array(

        "<b>"        => "[b]",
        "</b>"       => "[/b]",
        "<i>"        => "[i]",
        "</i>"       => "[/i]",
        "<em>"       => "[i]",
        "</em>"      => "[/i]",
        "<ul>"       => "[ul]",
        "</ul>"      => "[/ul]",
        "<li>"       => "[li]",
        "</li>"      => "[/li]",
        "<strong>"   => "[b]",
        "</strong>"  => "[/b]",
        "{"          => "",
        "}"          => ""
    );
    $daten = strtr($daten, $eing);
    $daten = preg_replace("/<.+?>/is", "", $daten);
    return $daten;
}

/*------------------------------------------------------------------------------------------------------------------
 Die Funktion wandelt erlaubte BBcode-Tags in HTML-Tags zurück und überprüft Images. Falls Bilder die angegeben
 Höchstmaße überschreiten, werden diese gleichmäßig skaliert.
 -------------------------------------------------------------------------------------------------------------------
*/
function formeHTML($daten) {

    $eing  = array(

        "[br]"       => "<br".SCHLIESSE,
        "[b]"        => "<b>",
        "[/b]"       => "</b>",
        "[i]"        => "<em>",
        "[/i]"       => "</em>",
        "[ul]"       => "<ul>",
        "[/ul]"      => "</ul>",
        "[li]"       => "<li>",
        "[/li]"      => "</li>"
    );
    $daten = strtr($daten, $eing);

    $daten = preg_replace("/[\n]/", "\n\t", $daten);
    $daten = preg_replace_callback("/\[img\](.+?)\[\/img\]/",

    function ($bilder) {

        @$format = getimagesize($bilder[1]);

        if($format != false) {

            if ($format["mime"] == "image/jpeg" or $format["mime"] == "image/png") {

                global $maxho, $maxwe;

                $height = $format[1];
                $width  = $format[0];

                if ($height > $maxho){

                    $height  = $maxho;
                    $prozent = ($format[1] / $height);
                    $width   = ($format[0] / $prozent);
                }
                if ($width > $maxwe){

                    $width   = $maxwe;
                    $prozent = ($format[0] / $width);
                    $height  = ($format[1] / $prozent);
                }
                return "<img src=\"".htmlspecialchars($bilder[1], ENT_QUOTES)."\" alt=\"Bild\"".
                       " height=\"".round($height)."\" width=\"".round($width)."\"".SCHLIESSE;
            }
        }
    }, $daten);
    return $daten;
}

/*-- Die Funktion wandelt Datum und Uhrzeit in ein lesbares Format um. -------------------------------------------*/

function formeDatumZeit($daten) {

    $daten = preg_replace("/(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})/", "$3.$2.$1 um $4:$5 Uhr", $daten);
    $daten = substr($daten, 0, 23);
    return $daten;
}

/*-- Die Funktion prüft enthaltene Links auf unerlaubte Zeichen und entfernt diese erforderlichenfalls. ----------*/

function filtereLinks($daten) {

    if (($pos = strpos($daten, "#")) !== false) {
        $daten = substr($daten, 0, $pos);
    }
    $daten = preg_replace("/[^a-z0-9_\/=.:;&?-]/is", "", $daten);
    return $daten;
}

$link  = false;                     // Bekanntmachung der Variablen $link, $lesen und $enco sowie Startwert von $si
$lesen = false;
$image = false;                     // $image false zuweisen
$enco  = "";
$si =  0;

echo "<div class=\"feed\">\n";

/*-- Der Feed wird geladen und im Anschluß erfolgt die Ausgabe des Feeds -----------------------------------------*/

if ((@$data = file_get_contents($seite)) != false) {

    $pxml = simplexml_load_string(wandleTags($data), "SimpleXMLElement", LIBXML_NOCDATA);

    /*--------------------------------------------------------------------------------------------------------------
     Auswählen, ob Feedtitel und Eintritt für RSS oder für Atom, wobei channel->title und channel->item der RSS 2.0
     Specification für Feeds entspricht, title und entry hingegen der Specification für Atom-Feeds und item RSS 1.0
     ---------------------------------------------------------------------------------------------------------------
    */
    if ($pxml->channel->title) echo "<h2>".$feedt.formeHTML(htmlspecialchars(filtereHTML($pxml->channel->title, $image), ENT_QUOTES))."</h2>\n";
    if ($pxml->title)          echo "<h2>".$feedt.formeHTML(htmlspecialchars(filtereHTML($pxml->title, $image), ENT_QUOTES))."</h2>\n";

    if ($pxml->channel->item) $lesen = $pxml->channel->item;
    if ($pxml->entry)         $lesen = $pxml->entry;
    if ($pxml->item)          $lesen = $pxml->item;

    /*--------------------------------------------------------------------------------------------------------------
     Kontrolle, ob zumindest das erste oder das zweite Item einen Titel enthält. Falls dies wie erwartet der Fall
     sein sollte, werden noch einmal alle Titel einzeln auf Vorhandensein überprüft.
     ---------------------------------------------------------------------------------------------------------------
    */
    if (strlen($lesen[0]->title) > 0 or strlen($lesen[1]->title) > 0) {

        foreach ($lesen as $nachricht) {

            if ($nachricht->title != false) {

                /*-- Auswählen, ob Link für RSS $nachricht->link oder Atom $nachricht->link->attributes() --------*/

                if ($nachricht->link) {

                    if ((string)$nachricht->link) {
                        $link = $nachricht->link;
                    }
                    elseif ($nachricht->link->attributes()) {
                        $attr = $nachricht->link->attributes();
                        $link = $attr["href"];
                    }
                }

                /*-- Titel mit Link ------------------------------------------------------------------------------*/

                if ($nachricht->title) {
                    echo "\x20\x20<h3><a href=\"".htmlspecialchars(filtereLinks($link), ENT_QUOTES)."\" target=\"_blank\">".
                    formeHTML(htmlspecialchars(filtereHTML($nachricht->title, $image), ENT_QUOTES))."</a></h3>\n";
                }

                /*--------------------------------------------------------------------------------------------------
                 Einen Hinweis anzeigen, falls Bilder als Beilage mit enclosure der Nachricht hinzugefügt wurden.
                 ---------------------------------------------------------------------------------------------------
                */
                if ($nachricht->enclosure) {

                    if ($nachricht->enclosure->attributes()) {
                        $url = $nachricht->enclosure->attributes();

                        if (strpos($url["url"], ".jpg") or strpos($url["url"], ".png")) {

                            $enco = "<br".SCHLIESSE."\n\x20\x20<span class=\"klein\">Beilage Medien: ".
                                     htmlspecialchars(filtereLinks($url["url"]), ENT_QUOTES)."</span>";
                        }
                    }
                }

                /*-- true erst einmal nur für description und falls kein Fund, dann  auch für content und summary */

                $image = true;

                /*-- description und content für RSS und summary für Atom ----------------------------------------*/

                if ($nachricht->description and $descr != false) {
                    echo "\x20\x20<p>".formeHTML(htmlspecialchars(filtereHTML($nachricht->description, $image), ENT_QUOTES))."</p>\n";

                    if ((strpos((string)$nachricht->description, "<img")) !== false) {
                        $image = false;
                    }
                }
                if ($nachricht->content and $conte != false) {
                    echo "\x20\x20<p>".formeHTML(htmlspecialchars(filtereHTML($nachricht->content, $image), ENT_QUOTES))."</p>\n";
                }
                if ($nachricht->summary and $summa != false) {
                    echo "\x20\x20<p>".formeHTML(htmlspecialchars(filtereHTML($nachricht->summary, $image), ENT_QUOTES))."</p>\n";
                }

                /*-- Nur für den Fall, dass false nicht durch description ausgelöst wurde ------------------------*/

                $image = false;

                /*-- Link unten ----------------------------------------------------------------------------------*/

                if ($link != false) {
                    echo "\x20\x20<p class=\"unten\"><a href=\"".htmlspecialchars(filtereLinks($link), ENT_QUOTES)."\" target=\"_blank\">".
                         $linkt."</a><br".SCHLIESSE."\n";
                }

                /*-- pubDate für RSS und published und updated für Atom ------------------------------------------*/

                if ($nachricht->pubDate) {
                    echo "\x20\x20<br".SCHLIESSE."".htmlspecialchars(substr($nachricht->pubDate,  0, 16), ENT_QUOTES).
                         " um ".htmlspecialchars(substr($nachricht->pubDate, 17,  5), ENT_QUOTES)." Uhr";

                } elseif ($nachricht->updated) {
                    echo "\x20\x20<br".SCHLIESSE.formeDatumZeit($nachricht->updated)."\n";

                } elseif ($nachricht->published) {
                    echo "\x20\x20<br".SCHLIESSE.formeDatumZeit($nachricht->published)."\n";
                }

                /*-- Autor oder Creator für RSS und Atom ---------------------------------------------------------*/

                if ($nachricht->author) {
                    echo " von ".htmlspecialchars(filtereHTML($nachricht->author, $image), ENT_QUOTES).$enco."</p>\n";

                } elseif ($nachricht->creator) {
                    echo " von ".htmlspecialchars(filtereHTML($nachricht->creator, $image), ENT_QUOTES).$enco."</p>\n";

                } else {
                    echo "\x20\x20".$enco."</p>\n";
                }

                /*-- Trennlinie zwischen den einzelnen Mitteilungen ----------------------------------------------*/

                echo "\x20\x20<hr class=\"linie\"".SCHLIESSE."\n";
                $si++;
                if ($si == $maxim) {break;
                }
            }
        }
    } else { echo "<br".SCHLIESSE."Mehrere Titel oder Items scheinen fehlerhaft zu sein!\n";
    }
} else { echo "<br".SCHLIESSE."Feed konnte nicht geladen werden!\n";
}
?>
</div>
</body>
</html>

Copyright © Verlag Horst Müller - Stendal - 2006 - Impressum - Datenschutz - Nutzungsbedingungen