8a024a450d1c08ba8257f1d30db0944517460125
[mailer.git] / inc / rdf.class.php
1 <?php
2 /* $Id: rdf.class.php,v 1.40 2003/07/06 20:33:58 sts Exp $ */
3
4 //
5 // +----------------------------------------------------------------------+
6 // | rss Parser                                                           |
7 // | Copyright (c) 2001 Stefan Saasen                                     |
8 // +----------------------------------------------------------------------+
9 // | The contents of this file are subject to the Mozilla Public License  |
10 // | Version 1.1 (the "License"); you may not use this file except in     |
11 // | compliance with the License. You may obtain a copy of the License at |
12 // | http://www.mozilla.org/MPL/                                          |
13 // |                                                                      |
14 // | Software distributed under the License is distributed on an "AS IS"  |
15 // | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See  |
16 // | the License for the specific language governing rights and           |
17 // | limitations under the License.                                       |
18 // +----------------------------------------------------------------------+
19 // |                                                                      |
20 // | Maintainer and initial developer:                                    |
21 // | Stefan Saasen <s@fase4.com>                                          |
22 // |                                                                      |
23 // | Proxy and authentication methods added by:                           |
24 // | Marco Kraus <marco.kraus@siemens.com>                                |
25 // |                                                                      |
26 // | Decoding of data by htmlentities or utf8_decode added by:            |
27 // | Roland Haeder <webmaster@mxchange.org>                               |
28 // |                                                                      |
29 // +----------------------------------------------------------------------+
30 // | Ref:                                                                 |
31 // |   @link http://www.fase4.com/rdf/                   Latest release   |
32 // +----------------------------------------------------------------------+
33
34 /**
35 * Class RSS Parser
36 *
37 * This class offers methods to parse RSS Files
38 *
39 * @link      http://www.fase4.com/rdf/ Latest release of this class
40 * @package   rss
41 * @copyright Copyright (c) 2001 fase4.com. All rights reserved.
42 * @author    Stefan Saasen <s@fase4.com>
43 * @author    Roland Haeder <webmaster@mxchange.org>
44 * @version   1.7 ($Date: 2009-03-06 20:24:32 +0100 (Fr, 06. March 2009)$) $Revision: 856 $
45 * @access    public
46 */
47
48 class fase4_rdf {
49
50     /**
51     * Word-wrapping mode for description, set it to 0 do disable this feature! Ommits _use_nl2br!
52     *
53     * @access private
54     * @var    integer
55     */
56     var $_word_wrap = 0;
57
58     /**
59     * Wether to recode \n -> <br /> or not in description
60     *
61     * @access private
62     * @var    boolean
63     */
64     var $_use_nl2br = true;
65
66     /**
67     * Sets the decoding mode of the read data (UTF8 scrambles some german umlauts here!)
68     *
69     * "htmlentities" - Use the function htmlentities()
70     * "utf8_decode"  - Use the function ut8_decode() when you have UTF8 encoded text
71     *
72     * @access private
73     * @var    string
74     */
75     var $_decoding_mode = "utf8_decode";
76
77     /**
78     * If $_link_target is set a target='xxx' attribute in each <a /> and <form /> html tag will be added
79     *
80     * Possible values are "_blank", "_content", "_parent", "_self", "_top"
81     *
82     * @access private
83     * @var    string
84     */
85     var $_link_target = "_blank";
86
87     /**
88     * vars for proxy settings - Prox Host
89     *
90     * @access private
91     * @var      string
92     */
93     var $_phost = "";
94
95     /**
96     * vars for proxy settings - Prox Port
97     *
98     * @access private
99     * @var      string
100     */
101     var $_pport = "";
102
103     /**
104     * vars for proxy settings - Prox Username
105     *
106     * @access private
107     * @var      string
108     */
109     var $_pname = "";
110
111     /**
112     * vars for proxy settings - Prox Password
113     *
114     * @access private
115     * @var      string
116     */
117     var $_ppasswd = "";
118
119     /**
120     * just a flag for checking if proxy-support should be enabled
121     * set default to false (will be enabled if set_proxy is called)
122     *
123     * @access   private
124     * @see      set_proxy()
125     * @var      bool
126     */
127     var $_use_proxy = false;
128
129     /**
130     * just a flag for checking if proxy-support with authentication
131     * should be enabled
132     * set default to false (will be enabled if set_proxy is called)
133     *
134     * @access   private
135     * @see      set_proxy()
136     * @var      boolean
137     */
138     var $_use_proxy_auth = false;
139
140     /**
141     * The time the Files will be cached (in seconds).
142     *
143     * @access private
144     * @var    int
145     */
146     var $_refresh = 60;   // int
147
148     /**
149     * The Name of the cached File.
150     *
151     * @access private
152     * @var    string
153     */
154     var $_cached_file = "";   // String
155
156     /**
157     * Indicates whether the cached or the remote file was used.
158     *
159     * @access private
160     * @var    boolean
161     */
162     var $_use_cached_file = true;
163
164     /**
165     * (fast|normal) depends on _use_dynamic_display(). _use_dynamic_display( TRUE ) -> 'normal', otherwise 'fast'
166     *
167     * @access private
168     * @var    string
169     */
170     var $_cache_type = "fast";
171
172     /**
173     * The Name of the Remote File.
174     *
175     * @access private
176     * @var    string
177     */
178     var $_remote_file = "";
179
180     /**
181     * Path to the Cache Directory.
182     *
183     * @access private
184     * @var    string
185     */
186     var $_cache_dir = "cache/";  // String
187
188     /**
189     * Indicates whether the Creating of the Cache Directory needs to be done or not.
190     *
191     * @access private
192     * @var    boolean
193     */
194     var $_cache_dir_ok = false;
195
196     /**
197     * Type of the file to be parsed (RSS or RDF).
198     *
199     * The Type depends on the root node
200     *
201     * @access private
202     * @var    string
203     */
204     var $_type = "rss"; // string (rss or rdf)
205
206     /**
207     * Array of Display Settings.
208     *
209     * Specific Parameters can be set to hidden. These are:
210     * image, channel and textinput. If set to "hidden" those elements won't be displayed.
211     *
212     * @access private
213     * @var    array
214     */
215     var $_display_opt = array(
216                 'build' => "",
217                 'image' => "",
218                 'channel' => "",
219                 'textinput' => "",
220                 'cache_update' => "",
221                 'sitelink' => "",
222                 'refid' => "",
223                 'reflink' => "",
224     );
225
226     /**
227     * Defines the width attribute in the table that holds the rdf/rss representation
228     *
229     * @access private
230     * @var    int
231     * @see    see_table_width()
232     */
233     var $_table_width = "100%";
234
235     /**
236     * Indicates whether or not to use dynamic Display Settings
237     *
238     * @access private
239     * @var    array
240     */
241     var $_use_dynamic_display = false;
242
243     /**
244     * <item> count
245     *
246     * @access private
247     * @var    int
248     */
249     var $_item_count = 0;
250
251     /**
252     * No of max <item>s
253     *
254     * @access private
255     * @var    boolean
256     */
257     var $_max_count = false;
258
259     /**
260     * Array containing the content of <channel />
261     *
262     * @access private
263     * @var    array
264     */
265     var $_array_channel = array();
266
267     /**
268     * Array containing the content of each <item />
269     *
270     * @access private
271     * @var    array
272     */
273     var $_array_item = array();
274
275     /**
276     * Array containing the content of <textinput />
277     *
278     * @access private
279     * @var    array
280     */
281     var $_array_textinput = array();
282
283     /**
284     * Array containing the content of <image />
285     *
286     * @access private
287     * @var    array
288     */
289     var $_array_image = array();
290
291     /**
292     * Array containing the Channel content. Just For internal XML Parser Purposes.
293     *
294     * @access private
295     * @var    array
296     */
297     var $_citem = array();
298
299     /**
300     * Array containing the Channel Parser Depth. Just For internal XML Parser Purposes.
301     *
302     * @access private
303     * @var    array
304     */
305     var $_cdepth = array();
306
307     /**
308     * Array containing the Channel tags. Just For internal XML Parser Purposes.
309     *
310     * @access private
311     * @var    array
312     */
313     var $_ctags = array( "x" );
314
315     /**
316     * Array containing the Channel content. Just For internal XML Parser Purposes.
317     *
318     * @access private
319     * @var    array
320     */
321     var $_item = array();   // Array
322
323     /**
324     * Array containing the Channel Parser Depth. Just For internal XML Parser Purposes.
325     *
326     * @access private
327     * @var    array
328     */
329     var $_depth = array();  // Array
330
331     /**
332     * Array containing the tags. Just For internal XML Parser Purposes.
333     *
334     * @access private
335     * @var    array
336     */
337     var $_tags = array( "x" );  // Array
338
339     /**
340     * Garbage collection: probability in percent
341     *
342     * @var      integer     0 => never
343     * @access   public
344     */
345     var $gc_probability = 1;
346
347     /**
348     * HTML Output
349     *
350     * @var      string
351     * @access   private
352     */
353     var $_output = "";
354
355     /**
356     * @var  string
357     */
358     var $_parse_mode = "";
359
360     // Output variable
361     var $out = "";
362
363     // Salt for hashing
364     var $salt = "";
365
366     /**
367     * Constructor of our Class
368     *
369     * This Method checks if the Cache Directory can be found. Otherwise it tries to creat the Cache Directory at the specified Path.
370     * Additionally the Refresh Time is set to a default Value of 1200s (20 min).
371         *
372     * @access    public
373         * @author    Stefan Saasen <s@fase4.com>
374         * @see           _refresh
375     */
376     function fase4_rdf()
377     {
378         // default Value, to be overwritten in set_refresh()
379         $this->_refresh = (time() - 1200);
380         $this->_clear_cItems();
381         $this->_clear_Items();
382     }
383
384     /**
385     * This Method starts the parsing of the specified RDF File. The File can be a local or a remote File.
386     *
387     * @access    public
388         * @author    Stefan Saasen <s@fase4.com>
389     * @param     string $rdf    RDF File (Location)
390     * @return    string Displays RDF Content ( using _display() )
391         * @see           _remote_file, cache()
392     */
393     function parse_RDF( $rdf )
394     {
395         unset($this->_array_item);
396         $this->_remote_file = $rdf;
397         $this->out .= "<!-- http://www.fase4.com/rdf/ -->";
398         $this->out .= "<table width=\"".$this->_table_width."\">";
399         $this->out .= $this->cache();
400         $this->out .= "</table>";
401         $this->_output = "";
402         $this->_item_count = 0;
403         return true;
404     }
405
406     /**
407     * This Method is called when all parsing is finished to use the garbage collection
408     *
409     * @access    public
410         * @author    Stefan Saasen <s@fase4.com>
411     * @param     string $rdf    RDF File (Location)
412     * @return    string Displays RDF Content ( using _display() )
413         * @see           _remote_file, cache()
414     */
415     function finish($return = false)
416     {
417                 if (!$return) {
418                         echo $this->out;
419                 } else {
420                         return $this->out;
421                 }
422         flush();
423         $this->_garbage_collection();
424     }
425
426     /**
427     * With this method you can decide whether to use the normal cache and dynamic display Options or to use a static cache.
428     *
429     * In the first case the rdf/rss File will be stored locally, in the second case the html output of the specified source will be stored.
430     * In this case you can not modify the display settings.
431     * processing time: ( 1.4792) --> remote file
432     * processing time: ( 0.0313) --> using 'normal cache' with display Modification turned on.
433     * processing time: ( 0.0019) --> using 'fast cache'
434     *
435     * @access    public
436         * @author    Stefan Saasen <s@fase4.com>
437     * @param     string $rdf    RDF File (Location)
438     * @return    string Displays RDF Content ( using _display() )
439         * @see           _remote_file, cache()
440     */
441     function use_dynamic_display( $bool )
442     {
443         $this->_use_dynamic_display = $bool;
444         return true;
445     }
446
447     /**
448     * This Method avtually parses the XML data.
449     *
450     * @access    private
451     * @author    Stefan Saasen <s@fase4.com>
452     * @param     string $data    RDF File XML Data
453     * @see       _clear_Items()
454     */
455     function _parse_xRDF( $data )
456     {
457         $this->_clear_Items();
458         $xml_parser = xml_parser_create();
459         xml_set_object($xml_parser,$this);
460         xml_parser_set_option($xml_parser,XML_OPTION_CASE_FOLDING,0);
461         xml_set_element_handler($xml_parser, "_startElement", "_endElement");
462         xml_set_character_data_handler($xml_parser, "_parseData");
463         if (!xml_parse($xml_parser, trim($data))) {
464                 $this->_throw_exception(sprintf("XML error: %s at line %d",
465                 xml_error_string(xml_get_error_code($xml_parser)),
466                 xml_get_current_line_number($xml_parser))."<br /><br />Exception in function parse_RDF().");
467             }
468         xml_parser_free($xml_parser);
469     }
470
471
472     /**
473     * This Methods allows you to set the Refresh Time
474     *
475     * @access    public
476         * @author    Stefan Saasen <s@fase4.com>
477     * @param     int $seconds time files will be cached (in seconds).
478         * @return        boolean
479         * @see           _refresh
480     */
481     function set_refresh( $seconds )
482     {
483         $this->_refresh = (time() - $seconds);
484         return true;
485     }
486
487     function set_salt( $saltPara )
488     {
489         $this->salt = $saltPara;
490         return true;
491     }
492
493     /**
494     * This Methods allows you to set the No. of <item>s to display
495     *
496     * @access    public
497     * @param     int $int No of max <item>s
498         * @author    Stefan Saasen <s@fase4.com>
499         * @return        boolean
500         * @see           _max_count, _endElement()
501     */
502     function set_max_item( $int )
503     {
504         $this->_max_count = $int;
505         return true;
506     }
507
508     /**
509     * This Methods allows you to set the Cache Directory
510     *
511     * @access    public
512         * @author    Stefan Saasen <s@fase4.com>
513     * @param     string $dir Path to Directory.
514         * @return        boolean
515         * @see           _cache_dir
516     */
517     function set_CacheDir( $dir )
518     {
519         if(substr($dir, -1) != "/") {
520             $dir = $dir."/";
521         }
522         $this->_cache_dir = $dir;
523     }
524
525     /**
526     * This Method displays Error Messages and terminates the Execution of the Script
527     *
528     * @access    private
529     * @param     string $msg Message to display on failure
530         * @author    Stefan Saasen <s@fase4.com>
531     */
532     function _throw_exception( $msg )
533     {
534         $this->out .= "<div style=\"font-family: verdana, helvetica, arial, sans-serif;font-size:11px; color: #6699cc;margin-top:10px;margin-bottom:10px;\" align=\"center\">fase4 RDF Error: ".$msg."</div>";
535         return true;
536     }
537
538     /**
539     * This Method clears the Array containig the Items.
540     *
541     * @access    private
542         * @author    Stefan Saasen <s@fase4.com>
543     * @see       _item
544     */
545     function _clear_Items( ) {
546         $this->_item = array(
547             'title'=>"",
548             'link'=>"",
549             'description'=>"",
550             'url'=>"",
551             'language'=>"",
552             'pubDate'=>"",
553             'lastBuildDate'=>"",
554             'width'=>"",
555             'height'=>""
556         );
557     }
558     /**
559     * This Method clears the Array containig the Channel Items.
560     *
561     * @access    private
562         * @author    Stefan Saasen <s@fase4.com>
563     * @see       _item
564     */
565     function _clear_cItems( ) {
566         $this->_citem = array(
567             'title'=>"",
568             'link'=>"",
569             'description'=>"",
570             'url'=>"",
571             'language'=>"",
572             'copyright'=>"",
573             'managingEditor'=>"",
574             'webMaster'=>"",
575             'pubDate'=>"",
576             'lastBuildDate'=>"",
577             'category'=>"",
578             'generator'=>"",
579             'docs'=>"",
580             'cloud'=>"",
581             'ttl'=>"",
582             'image'=>"",
583             'textinput'=>"",
584             'skipHours'=>"",
585             'skipDays' =>"",
586             'sitelink'=>"",
587             'refid'=>"",
588             'reflink'=>"",
589         );
590     }
591
592     /**
593     * XML Parser Start Element Handler
594     *
595     * @access    private
596         * @author    Stefan Saasen <s@fase4.com>
597     * @param     mixed  $parser a reference to the XML parser calling the handler.
598     * @param     string $name contains the name of the element for which this handler is called.
599     * @param     string $attrs contains an associative array with the element's attributes (if any).
600         * @see           _get_ChannelData(), _clear_Items(), _type, _parse_mode, _depth, _tags, _cdepth, _ctags
601     */
602     function _startElement($parser, $name, $attrs) {
603         // We have to determine, which type of xml data we have to parse
604         if($name == "rss") {
605             $this->_type = "rss";
606         } elseif($name == "rdf:RDF" OR $name == "rdf") {
607             $this->_type = "rdf";
608         }
609
610
611         if ( $name == "channel" AND $this->_type != "rdf" ) {
612             $this->_parse_mode = "channel";
613         } elseif ( ($name=="item")
614                     ||($name=="image")
615                     ||($name=="textinput")
616                     ||(($name=="channel") && ($this->_type != "rss")) ) {
617             if($this->_parse_mode=="channel") {
618                 $this->_get_ChannelData( $parser );
619             }
620             $this->_parse_mode = "all";
621         }
622
623         if( !isset( $this->_depth[$this->get_parser_id($parser)] ) ) {
624             $this->_depth[$this->get_parser_id($parser)] = 0;
625         }
626         $this->_depth[$this->get_parser_id($parser)]++;
627         array_push($this->_tags, $name);
628
629         if( !isset( $this->_cdepth[$this->get_parser_id($parser)] ) ) {
630             $this->_cdepth[$this->get_parser_id($parser)] = 0;
631         }
632         $this->_cdepth[$this->get_parser_id($parser)]++;
633         array_push($this->_ctags, $name);
634     }   // END _startElement()
635
636     /**
637     * Retrives the Channel Data in <rss> File
638     *
639     * @access    private
640         * @author    Stefan Saasen <s@fase4.com>
641     * @param     mixed  $parser a reference to the XML parser calling the handler.
642         * @see           _output, _display_opt, _citem
643     */
644     function _get_ChannelData( $parser )
645     {
646                 $this->_citem["link"] = trim($this->_citem["link"]);
647                          if (($this->_display_opt["sitelink"] == $this->_citem["link"]) && (!empty($this->_display_opt["reflink"])) && (!empty($this->_display_opt["refid"])))
648                 {
649                     $this->_citem["link"] .= $this->_display_opt["reflink"].$this->_display_opt["refid"];
650                 }
651
652                 if( empty($this->_display_opt["channel"]) OR
653                     $this->_display_opt["channel"] != "hidden") {
654                 $this->_output .= "<tr><td>\n";
655                 $this->_output .= "<table border=\"0\" width=\"100%\" class=\"fase4_rdf_meta\" cellspacing=\"5\" cellpadding=\"2\">\n";
656                 $this->_output .= "<tr><td class=\"fase4_rdf_main_title\"><div class=\"fase4_rdf_main_title\">".htmlspecialchars($this->_citem["title"])."</div></td></tr>\n";
657                 $this->_output .= "<tr><td class=\"fase4_rdf\">".strip_tags($this->_citem["description"], "<a>, <img>")."</td></tr>\n";
658                 $this->_output .= "<tr><td>&nbsp;</td></tr>\n";
659                 $this->_output .= "<tr><td class=\"fase4_rdf\">\n";
660                 if(isset($this->_display_opt["build"]) && $this->_display_opt["build"] != "hidden") {
661                     if($this->_citem["lastBuildDate"]){$this->_output .= "build: ". $this->_citem["lastBuildDate"]."<br />";}
662                 }
663                 if(isset($this->_display_opt["cache_update"]) && $this->_display_opt["cache_update"] != "hidden" && ( $_update = $this->get_cache_update_time()) ) {
664                 $this->_output .= "cache update: ".$_update."<br />\n";
665                 }
666                 $this->_output .= "<a href=\"".$this->_citem["link"]."\" ";
667                 if(isset($this->_link_target)) { $this->_output .= "target=\"".$this->_link_target."\" "; }
668                 $this->_output .= ">".$this->_cut_string($this->_citem["link"])."</a>";
669                 $this->_output .= "</td></tr>\n";
670                 $this->_output .= "<tr><td><hr noshade width=\"100%\" size=\"1\"></td></tr>\n";
671                 $this->_output .= "</table></td></tr>";
672                 }
673                     $this->_array_channel = array(  "title"=>$this->_citem["title"],
674                                                     "link"=>$this->_citem["link"],
675                                                     "description"=>$this->_citem["description"],
676                                                     "lastBuildDate"=>$this->_citem["lastBuildDate"]);
677     }
678
679     /**
680     * XML Parser End Element Handler
681     *
682     * @access    private
683         * @author    Stefan Saasen <s@fase4.com>
684     * @param     mixed  $parser a reference to the XML parser calling the handler.
685     * @param     string $name contains the name of the element for which this handler is called.
686         * @see           _clear_Items(), _type, _parse_mode, _depth, _tags, _cdepth, _ctags, _item, _output, _display_opt
687     */
688     function _endElement($parser, $name) {
689         array_pop($this->_tags);
690         $this->_depth[$this->get_parser_id($parser)]--;
691         array_pop($this->_ctags);
692         $this->_cdepth[$this->get_parser_id($parser)]--;
693         $this->_item["link"] = trim($this->_item["link"]);
694            if ((!empty($this->_display_opt["refid"])) && (!empty($this->_item["link"])))
695            {
696             if (!ereg("refid=", $this->_item["link"])) $this->_item["link"] .= "?refid=" . $this->_display_opt["refid"];
697            }
698         switch ($name) {
699             case "item":
700                 if(empty($this->_max_count) OR $this->_item_count < $this->_max_count) {
701                     if($this->_item["title"] != $this->_item["description"]
702                                             AND $this->_item["description"]) {
703
704                         // word-wrapping added by Roland Haeder <webmaster@mxchange.org>
705                            if (($this->_word_wrap > 0) && (strlen($this->_item["description"]) > $this->_word_wrap))
706                         {
707                             // Switch off _use_nl2br
708                             $this->_use_nl2br = false;
709                             // First remove all \n
710                             $this->_item["description"] = str_replace('\n', ' ', $this->_item["description"]);
711                             // Wrap with <br />\n
712                             $this->_item["description"] = wordwrap($this->_item["description"], $this->_word_wrap, "*<br>\n");
713                         }
714                          elseif (($this->_word_wrap == 0) && (!$this->_use_nl2br))
715                         {
716                             // Strip tags out instead when word-wrap is disabled
717                             $this->_item["description"] = strip_tags($this->_item["description"], "<a>, <img>");
718                         }
719
720                         // nl2br added by Roland Haeder <webmaster@mxchange.org>
721                         if ($this->_use_nl2br) $this->_item["description"] = nl2br($this->_item["description"]);
722
723                         $this->_output .= "<tr><td class=\"fase4_rdf_title\"><div class=\"fase4_rdf_title\"><a class=\"fase4_rdf_title\" href=\"".$this->_item["link"]."\" ";
724                         if(isset($this->_link_target)) { $this->_output .= "target=\"".$this->_link_target."\" "; }
725                         $this->_output .= ">".strip_tags($this->_item["title"], "<a>, <img>")."</a> (".$this->_item["pubDate"].")</div></td></tr>\n";
726                                         $this->_output .= "<tr><td class=\"fase4_rdf\">".$this->_item["description"]."</td></tr>\n";
727                         // we just display the <hr> if there is a description
728                         $this->_output .= "<tr><td><hr noshade=\"noshade\" size=\"1\" /></td></tr>\n";
729                     } else {
730                         $this->_output .= "<tr><td class=\"fase4_rdf\">\n";
731                         $this->_output .= "<a href=\"".$this->_item["link"]."\" ";
732                         if(isset($this->_link_target)) { $this->_output .= "target=\"".$this->_link_target."\" "; }
733                         $this->_output .= ">".$this->_item["title"]."</a></td></tr>\n";
734                     }
735                         $this->_array_item[] = array(   "title"=>$this->_item["title"],
736                                                         "link"=>$this->_item["link"],
737                                                         "description"=>$this->_item["description"]);
738                         ++$this->_item_count;
739                 }
740                     $this->_clear_Items();
741             break;
742             case "image":
743                 if(isset($this->_display_opt["image"]) && ($this->_display_opt["image"] != "hidden") && $this->_item["url"]) {
744                     $this->_output .= "<tr><td class=\"fase4_rdf\">\n";
745                     $this->_output .= "<a href=\"".$this->_item["link"]."\" ";
746                     if(isset($this->_link_target)) { $this->_output .= "target=\"".$this->_link_target."\" "; }
747                     $this->_output .= "><img src=\"".$this->_item["url"]."\"";
748                 if(isset($this->_item["width"]) && isset($this->_item["height"])) {
749                     $this->_output .= " width=\"".$this->_item["width"]."\" height=\"".$this->_item["height"]."\"";
750                 }
751                 $this->_output .= " alt=\"".$this->_item["title"]."\" border=\"0\" /></a></td></tr>\n";
752
753                     $this->_array_image[] = array(  "url"=>$this->_item["url"],
754                                                     "link"=>$this->_item["link"],
755                                                     "width"=>$this->_item["width"],
756                                                     "height"=>$this->_item["height"]);
757                     $this->_clear_Items();
758                 } elseif( isset($this->_display_opt["image"] ) && ($this->_display_opt["image"] == "hidden") ) {
759                     $this->_clear_Items();
760                 }
761
762             break;
763             case "channel":
764                 if(isset($this->_display_opt["channel"]) AND $this->_display_opt["channel"] != "hidden" AND $this->_item["title"] != '') {
765                     $this->_output .= "<tr><td>\n";
766                     $this->_output .= '<table border="0" width="100%" class="fase4_rdf_meta" cellspacing="5" cellpadding="2">'."\n";
767                     $this->_output .= "<tr><td class=\"fase4_rdf\"><div class=\"fase4_rdf_title\">".htmlspecialchars($this->_item["title"])."</div></td></tr>\n";
768                     $this->_output .= "<tr><td class=\"fase4_rdf\">".strip_tags($this->_item["description"], "<a>, <img>")."</td></tr>\n";
769                     $this->_output .= "<tr><td>&nbsp;</td></tr>\n";
770                     $this->_output .= "<tr><td class=\"fase4_rdf\">\n";
771                 if($this->_display_opt["build"] != "hidden") {
772                     if($this->_item["lastBuildDate"]){$this->_output .= "build: ". $this->_item["lastBuildDate"]."<br />";}
773                 }
774                 if($this->_display_opt["cache_update"] != "hidden" && ( $_update = $this->get_cache_update_time()) ) {
775                     $this->_output .= "cache update: ".$_update."<br />\n";
776                 }
777                 $this->_output .= "<a href=\"".$this->_item["link"]."\" ";
778                 if(isset($this->_link_target)) { $this->_output .= "target=\"".$this->_link_target."\" "; }
779                 $this->_output .= ">".$this->_cut_string($this->_item["link"])."</a>\n";
780                 $this->_output .= "</td></tr>\n";
781                 $this->_output .= "</table></td></tr>\n";
782                 }
783                     $this->_array_channel = array(  "title"=>$this->_item["title"],
784                                                     "link"=>$this->_item["link"],
785                                                     "description"=>$this->_item["description"],
786                                                     "lastBuildDate"=>$this->_item["lastBuildDate"]);
787                     $this->_clear_Items();
788                     $this->_clear_cItems();
789             break;
790                         case "textinput":
791                 if(isset($this->_display_opt["textinput"]) && ($this->_display_opt["textinput"] != "hidden") && $this->_item["name"] && $this->_item["link"]) {
792                     $this->_output .= "<tr><td class=\"fase4_rdf\">\n";
793                                 $this->_output .= "<form action=\"".$this->_item["link"]."\" ";
794                     if(isset($this->_link_target)) { $this->_output .= "target=\"".$this->_link_target."\" "; }
795                     $this->_output .= "method=\"get\">\n";
796                     $this->_output .= "<div class=\"fase4_rdf_title\">".$this->_item["title"]."</div>";
797                     $this->_output .= strip_tags($this->_item["description"], "<a>, <img>")."<br><br>\n";
798                     $this->_output .= "<input class=\"fase4_rdf_input\" type=\"text\" name=\"".$this->_item["name"]."\">&nbsp;\n";
799                     $this->_output .= "<input class=\"fase4_rdf_input\" type=\"submit\" value=\"go\">";
800                     $this->_output .= "</form>\n";
801                     $this->_output .= "</td></tr>\n";
802                     $this->_array_textinput = array(    "title"=>$this->_item["title"],
803                                                         "name"=>$this->_item["name"],
804                                                         "link"=>$this->_item["link"],
805                                                         "description"=>$this->_item["description"]);
806                     $this->_clear_Items();
807                 } elseif( isset($this->_display_opt["textinput"]) && ($this->_display_opt["textinput"] == "hidden") ) {
808                     $this->_clear_Items();
809                 }
810
811                         break;
812         }
813     }
814
815     /**
816     * This Method returns the data from the <channel /> paragraph.
817     *
818     * @access    public
819         * @author    Stefan Saasen <s@fase4.com>
820     * @return    array
821         * @see           _array_channel
822     */
823     function get_array_channel( )
824     {
825         return $this->_array_channel;
826     }
827
828     /**
829     * This Method returns the data from each <item /> paragraph.
830     *
831     * @access    public
832         * @author    Stefan Saasen <s@fase4.com>
833     * @return    array
834         * @see           _array_item
835     */
836     function get_array_item( )
837     {
838         return $this->_array_item;
839     }
840
841     /**
842     * This Method returns the data from <textinput />.
843     *
844     * @access    public
845         * @author    Stefan Saasen <s@fase4.com>
846     * @return    array
847         * @see           _array_textinput
848     */
849     function get_array_textinput( )
850     {
851         return $this->_array_textinput;
852     }
853
854         /**
855         * Getter for parser id from resource
856         *
857         * @access private
858         * @author       Roland Haeder <webmaster@mxchange.org>
859         * @return       int
860         */
861         function get_parser_id ($parser) {
862                 // Default is zero
863                 $id = 0;
864
865                 // Is it a resource?
866                 if (is_resource($parser)) {
867                         // Cast the resource into id
868                         $id = (int) $parser;
869                 } // END - if
870
871                 // Return the id
872                 return $id;
873         }
874
875     /**
876     * This Method returns the data from <image />.
877     *
878     * @access    public
879         * @author    Stefan Saasen <s@fase4.com>
880     * @return    array
881         * @see           _array_image
882     */
883     function get_array_image( )
884     {
885         return $this->_array_image;
886     }
887
888     /**
889     * XML Parser Data Handler
890     *
891     * @access    private
892         * @author    Stefan Saasen <s@fase4.com>
893     * @param     mixed  $parser a reference to the XML parser calling the handler.
894     * @param     string $text contains the character data as a string.
895         * @see           _parse_mode, _item, _tags, _depth, _citem, _ctags, _cdepth
896     */
897     function _parseData($parser, $text)
898     {
899         // Deocing mode added by Roland Haeder <webmaster@mxchange.org>
900         switch ($this->_decoding_mode)
901         {
902         case "utf8_decode":
903             $text = utf8_decode($text);
904             break;
905
906         case "htmlentities":
907             $text = htmlentities($text);
908             break;
909         }
910
911         $clean = preg_replace("/\s/", "", $text);
912         if ($clean) {
913             $text = preg_replace("/^\s+/", "", $text)."\n";
914                 if($this->_parse_mode == "all") {
915                         if ( isset($this->_item[$this->_tags[$this->_depth[$this->get_parser_id($parser)]]]) &&
916                             $this->_item[$this->_tags[$this->_depth[$this->get_parser_id($parser)]]] ) {
917                            $this->_item[$this->_tags[$this->_depth[$this->get_parser_id($parser)]]] .= $text;
918                         } else {
919                            $this->_item[$this->_tags[$this->_depth[$this->get_parser_id($parser)]]] = $text;
920                         }
921                 } elseif (isset($this->_parse_mode) && $this->_parse_mode == "channel") {
922                         if ( isset($this->_citem[$this->_ctags[$this->_cdepth[$this->get_parser_id($parser)]]]) ) {
923                            $this->_citem[$this->_ctags[$this->_cdepth[$this->get_parser_id($parser)]]] .= $text;
924                         } else {
925                            $this->_citem[$this->_ctags[$this->_cdepth[$this->get_parser_id($parser)]]] = $text;
926                         }
927                 }
928         }
929     }
930
931     /**
932     * This Method allows you to choose if specific Parameters are displayed or not. These are:
933     * image, channel, textinput, build and cache_update. If set to "hidden" those elements won't be displayed.
934     *
935     * @access    public
936         * @author    Stefan Saasen <s@fase4.com>
937     * @param     array  $options
938         * @see           _display_opt
939     */
940     function set_Options( $options = "" )
941     {
942         if(is_array( $options )) {
943             $this->_display_opt = $options;
944             return true;
945         } else {
946             unset($this->_display_opt);
947             return false;
948         }
949     }
950
951     /**
952     * This Method allows you to define the width of the table that holds the representation of the rdf/rss file.
953     *
954     * @access    public
955         * @author    Stefan Saasen <s@fase4.com>
956     * @param     int  $width  attribute width in tag <table>
957         * @see           _table_width
958     */
959     function set_table_width( $width = 400 )
960     {
961         $this->_table_width = $width;
962         return true;
963     }
964
965     /**
966     * This Method returns an assocative Array with available Options.
967     *
968     * The Keys are the Name of the Options to be set.
969     * The Values are  short Description of available Options.
970     *
971     * @access    public
972         * @author    Stefan Saasen <s@fase4.com>
973     * @return    array  $options
974         * @see           _display_opt
975     */
976     function get_Options()
977     {
978         $options = array(   "image"=>"If 'image' is set to \"hidden\" no image provided by the RDF Publisher will be displayed.",
979                             "channel"=>"If 'channel' is set to \"hidden\" the Channel Meta Data (i.e the Title and the short description regarding the RDF Publisher will not be displayed",
980                             "textinput"=>"If set to \"hidden\" no Input Form will be displayed",
981                             "build"=>"If set to \"hidden\" the Build Date (if provided) of the RDF File will not be displayed",
982                             "cache_update"=>"If set to \"hidden\" the Update Date/Time of the cached Rdf File will not be displayed");
983         return $options;
984     }
985
986     /**
987     * This Method returns the Content of the RDF File in one string. The String actually holds the whole XML Document.
988     *
989     * @access    public
990         * @author    Stefan Saasen <s@fase4.com>
991     * @param     string $rdf    RDF File (Location)
992     * @return    string XML Presentation of parsed RDF File
993         * @see           _cached_file, _remote_file, _cache_dir, _refresh, _update_cache()
994     */
995     function cache()
996     {
997         // checks if the cache directory already exists
998         // if not, the cache directory will be created
999         if(!$this->_cache_dir_ok) {
1000             $this->_create_cache_dir();
1001         }
1002         if($this->_use_dynamic_display == true) {
1003             $this->_cached_file = md5("dynamic".$this->salt.$this->_remote_file);
1004             $this->_cache_type = "normal";
1005         } else {
1006             $this->_cached_file = md5($this->salt.$this->_remote_file);
1007             $this->_cache_type = "fast";
1008         }
1009
1010         $_cache_f = $this->_cache_dir.$this->_cached_file;
1011
1012         if ( (!file_exists($_cache_f)) || (filemtime($_cache_f) < $this->_refresh) || (filesize($_cache_f) == 0)) {
1013         // We have to parse the remote file
1014         $this->_use_cached_file = false;
1015             // --> we want to provide proper Information for Use in
1016             // get_cache_update_time()
1017             clearstatcache();
1018             if($this->_use_dynamic_display == true) {
1019                 $_rdf = @implode(" ", $this->_rdf_data()); // -> proxy
1020                 if(!$_rdf) {
1021                     $this->_throw_exception( $this->_remote_file." is not available" );
1022                 }
1023                 $this->_parse_xRDF( $_rdf );
1024                 $this->_update_cache( $_rdf );
1025                 $data = $this->_output;
1026             } else {
1027                 $_rdf = @implode(" ", $this->_rdf_data()); // -> proxy
1028                 if(!$_rdf) {
1029                     $this->_throw_exception( $this->_remote_file." is not available" );
1030                 }
1031                 $this->_parse_xRDF( $_rdf );
1032                 $this->_update_cache( $this->_output );
1033                 $data = $this->_output;
1034             }
1035         } else {
1036         // we can use the cached file
1037         $this->_use_cached_file = true;
1038             if($this->_use_dynamic_display == true) {
1039                 $this->_parse_xRDF( implode(" ", file($_cache_f)) );
1040                 $data = $this->_output;
1041             } else {
1042                 $data = @implode(" ", file($_cache_f));
1043             }
1044         }
1045         return trim($data);
1046     }   // END cache()
1047
1048     /**
1049     * This Methods creates the Cache Directory if the specified Directory does not exist.
1050     *
1051     * @access    private
1052         * @author    Stefan Saasen <s@fase4.com>
1053     * @param     string $dir Path to Directory.
1054         * @return        boolean
1055         * @see           _cache_dir, _cache_dir_ok
1056     */
1057     function _create_cache_dir()
1058     {
1059         $path = "";
1060         if(!@is_dir($this->_cache_dir)) {
1061             $arr = explode("/", $this->_cache_dir);
1062             $c = count($arr);
1063             if($arr[0]=="") {
1064                 $path = "/";
1065             }
1066             for($i = 0;$i<$c;$i++)
1067             {
1068                 if($arr[$i]!="") {
1069                     $path .= $arr[$i]."/";
1070                     if(!@is_dir($path)) {
1071                        if(!@mkdir($path, 0777)) {
1072                             $this->_throw_exception("failed to create directory:<b>".$this->_cache_dir."</b>.<br /><br />Exception on Line: ".__LINE__);
1073                         return false;
1074                         }
1075                     }
1076                 }
1077             }
1078             $this->_cache_dir_ok = true;
1079             return true;
1080         } else {
1081             $this->_cache_dir_ok = true;
1082             return true;
1083         }
1084     }   // END _create_cache_dir()
1085
1086     /**
1087     * This Method updates the cached RDF Files and synchronises them with their remote Counterparts.
1088     *
1089     * @access    private
1090         * @author    Stefan Saasen <s@fase4.com>
1091     * @param     string $rdf    RDF File (Location)
1092         * @see           _cache_dir, _cached_file, _throw_exception()
1093     */
1094     function _update_cache( $content = "" )
1095     {
1096              $_local = @fopen( $this->_cache_dir.$this->_cached_file, 'w' );
1097              if(!$_local) {
1098                 $this->_throw_exception( "Cannot open ".$this->_cached_file."<br /><br />Exception at Line: ".__LINE__ );
1099                 return false;
1100              }
1101              if (fwrite( $_local, $content) === false) {
1102                         $this->_throw_exception( "Cannot write to ".$this->_cached_file."<br /<br />Exeception at Line: ".__LINE__);
1103                         return false;
1104                    }
1105              fclose( $_local );
1106                    @chmod( $this->_cache_dir.$this->_cached_file, 0666);
1107              return true;
1108     }   // END _update_cache()
1109
1110     /**
1111     * This Method returns the Date/Time of last Cache Update of the actually parsed RDF File.
1112         *
1113     * @access    public
1114         * @author    Stefan Saasen <s@fase4.com>
1115         * @return        string Date/Time of last Update
1116         * @see           _cache_dir, _cached_file
1117     */
1118     function get_cache_update_time()
1119     {
1120             return (file_exists($this->_cache_dir.$this->_cached_file))?date("d.m.Y H:i:s", filemtime($this->_cache_dir.$this->_cached_file)):"Cachemiss";
1121     }   // END get_cache_update_time()
1122
1123     /**
1124     * This Method returns the Type of Cache that was used ('normal' or 'fast')
1125     *
1126     * @access    public
1127         * @author    Stefan Saasen <s@fase4.com>
1128     * @param     string $rdf    RDF File (Location)
1129     * @return    string Displays RDF Content ( using _display() )
1130         * @see           _remote_file, cache()
1131     */
1132     function get_CacheType()
1133     {
1134         return $this->_cache_type;
1135     }
1136
1137     /**
1138     * Returns true if cached file was used, otherwise false
1139     *
1140     * @access    public
1141         * @author    Stefan Saasen <s@fase4.com>
1142     * @return    array  $options
1143         * @see           _use_cached_file
1144     */
1145     function is_cachedFile()
1146     {
1147         return $this->_use_cached_file;
1148     }
1149
1150     /**
1151     * This Method deletes all the cached Files.
1152     *
1153     * Please keep in mind to use this method just as a 'manual garbage collection'
1154     * You should cache the rss/rdf files locally to avoid unnecessary traffic.
1155     * (Both for your visitors and the Publisher)
1156         *
1157     * @access    public
1158         * @author    Stefan Saasen <s@fase4.com>
1159         * @see           _cache_dir
1160     */
1161     function clear_cache()
1162     {
1163         $dir = dir($this->_cache_dir);
1164         while($file=$dir->read()) {
1165             // Exclude directories
1166             if (is_file($dir->path.$file) && substr($file, -6, 6) != ".cache" && substr($file, -4, 4) != ".log")  {
1167                 if(!@unlink($dir->path.$file)) {
1168                     $this->_throw_exception("Unable to unlink ".$dir->path.$file."<br />\n<br />\nException at Line: ".__LINE__ );
1169                     return false;
1170                 } // END - if
1171             } // END - if
1172         } // END - while
1173         $dir->close();
1174         return true;
1175     }   // END clear_cache()
1176
1177     /**
1178     * Cuts the String $string after $str_len and adds "... "
1179     *
1180     * @access   private
1181     * @param    string  $string String to be shortened
1182     * @param    int     $str_len length of the returned String (overall length including "... ")
1183     * @return   string  Cut String
1184     */
1185     function _cut_string( $string, $str_len = "30" )
1186     {
1187         if(strlen(trim($string))>$str_len) {
1188             $string = substr( trim($string) , 0, $str_len - 4);
1189             $string .= " ...";
1190         }
1191         return $string;
1192     }   // END _cut_string()
1193
1194     /**
1195     * this Method implements simple Garbage Collection
1196     *
1197     * @access    private
1198         * @author    Stefan Saasen <s@fase4.com>
1199         * @see           _cache_dir, gc_probability, gc_maxlifetime
1200     */
1201     function _garbage_collection()
1202     {
1203         srand((double) microtime() * 1000000);
1204         if (mt_rand(1, 100) <= $this->gc_probability) {
1205             $dir = dir($this->_cache_dir);
1206             while($file=$dir->read()) {
1207                 if (is_file($dir->path.$file) && substr($file, -6, 6) != ".cache" && substr($file, -4, 4) != ".log" && filemtime($dir->path.$file) <= time() - $this->_refresh )  {
1208                     @unlink($dir->path.$file);
1209                 } // END - if
1210         }
1211         $dir->close();
1212         }   // END if
1213     }
1214
1215     /* ==== Proxy/Auth methods ==== */
1216
1217    /**
1218     * this method sets a proxy server
1219     *
1220     * @access    public
1221     * @param     string $phost Proxy Host
1222     * @param     string $pport Prox Port
1223     * @author    Marco Kraus <marco.kraus@siemens.com>
1224     */
1225     function set_proxy($phost, $pport)
1226     {
1227      $this->_use_proxy = true;
1228
1229      if ($phost != '')
1230         $this->_phost = $phost;
1231
1232      if ($pport != '')
1233         $this->_pport = $pport;
1234     }
1235
1236     /**
1237     * this method sets a proxy server authentification
1238     *
1239     * @access    public
1240     * @param     string $pname Username
1241     * @param     string $ppaswd Password
1242     * @author    Marco Kraus <marco.kraus@siemens.com>
1243     */
1244     function set_proxy_auth( $pname, $ppasswd )
1245     {
1246      $this->_use_proxy_auth = true;
1247
1248      if ($pname != '')
1249         $this->_pname = $pname;
1250
1251      if ($ppasswd != '')
1252         $this->_ppasswd = $ppasswd;
1253     }
1254
1255
1256    /**
1257     * gets _remote_file into an array
1258     *
1259     * needed, cause if you use a proxy, you have to open
1260     * a raw-tcp-socket to get the data
1261     *
1262     * @access    private
1263     * @author    Marco Kraus <Marco.Kraus@siemens.com>
1264     * @return array
1265     * @see _use_proxy, cache()
1266     */
1267     function _rdf_data()
1268     {
1269       if ( $this->_use_proxy == true )
1270       {
1271        // we need a raw socket here to connect to proxy
1272        $fp = fsockopen($this->_phost,$this->_pport);
1273
1274        if (!$fp) {
1275            $this->_throw_exception( $this->_remote_file." is not available with proxy" );
1276        } else {
1277         if ( $this->_use_proxy_auth == true ) {
1278              fputs($fp, "GET ".$this->_remote_file." HTTP/1.0\r\nUser-Agent: Fase4 RDF-Reader/1.40 modified by Quix0r\r\n\r\n");
1279            } else {
1280              fputs($fp, "GET ".$this->_remote_file." HTTP/1.0\r\nUser-Agent: Fase4 RDF-Reader/1.40 modified by Quix0r\r\nProxy-Authorization: Basic ".base64_encode("$this->_pname:$this->_ppasswd") ."\r\n\r\n");
1281            }
1282         }
1283
1284
1285        for ( $i = 0; !feof ($fp) ; $i++)
1286        {
1287           $usable_data[$i] = "";
1288           $usable_data[$i] = fgets($fp,4096);
1289
1290         // PARSE HEADER ---- first line has to be <?xml, second rdf or rss, and third is blank
1291
1292         // strstr did not fit (ask Rasmus why), so we compare each character
1293             if ( ($usable_data[$i][0] == "<" ) &&
1294                ($usable_data[$i][1] == "?" ) &&
1295                ($usable_data[$i][2] == "x" ) &&
1296                ($usable_data[$i][3] == "m" ) &&
1297                ($usable_data[$i][4] == "l" ) ) {
1298                     $usable_data[0] = $usable_data[$i]; // save current field
1299                       $i = 1; // just reset array to start
1300               }
1301
1302         // there seems to be proxystuff after the <?xml....we delete this
1303             if ( (
1304                ($usable_data[$i][0] == "<" ) &&
1305                ($usable_data[$i][1] == "r" ) &&
1306                ($usable_data[$i][2] == "d" ) &&
1307                ($usable_data[$i][3] == "f" ) &&
1308                ($usable_data[$i][4] == ":" )
1309                )
1310                ||
1311                (
1312                ($usable_data[$i][0] == "<" ) &&
1313                ($usable_data[$i][1] == "r" ) &&
1314                ($usable_data[$i][2] == "s" ) &&
1315                ($usable_data[$i][3] == "s" )
1316                )
1317             ) {
1318
1319                 $usable_data[1] = $usable_data[$i]; // save current field
1320                 $usable_data[2] = "\n";
1321                 $i = 2; // just reset array to start
1322           }
1323        }
1324
1325        fclose($fp);
1326        return $usable_data;
1327      } else {
1328                 if (substr($this->_remote_file, 0, 7) != "http://") {
1329                         $this->_throw_exception( "Cannot find http:// in ".$this->_remote_file."!" );
1330                         return "";
1331                 } else {
1332                         // Extract host information
1333                         $host = substr($this->_remote_file, 7);
1334                         // Extract the GET part
1335                         $get = "/";
1336                         if (strpos($host, "/") > 0) {
1337                                 $get = substr($host, strpos($host, "/"));
1338                                 $host = substr($host, 0, strpos($host, "/"));
1339                         }
1340                         // Extract port
1341                         $port = "80";
1342                         if (strpos($host, ":") > 0) {
1343                                 $port = substr($host, (strpos($host, ":") + 1));
1344                                 $host = substr($host, 0, (strpos($host, ":") - 1));
1345                         }
1346
1347                         // Start connection to server
1348                         $fp = fsockopen($host, $port);
1349                         if (!$fp) {
1350                     $this->_throw_exception( $this->_remote_file." is maybe not available." );
1351                           return "";
1352                         }
1353                         // Repare request line
1354                         $request = sprintf("GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: Fase4 RDF-Reader/1.40 modified by Quix0r\r\n\r\n", $get, $host);
1355                         // Send request out
1356                         fputs($fp, $request);
1357                         $reply = ""; $isContent = false; $dummy = "";
1358                         // Read reply
1359                         $i=0;
1360                         while ( !feof($fp) ) {
1361                                 $read = trim(fgets($fp, 4096));
1362                                 if (substr($read, 0, 5) == "<?xml" || $isContent) {
1363                                         // Add content
1364                                         $reply[] = $read;
1365                                         $isContent = true;
1366                                 } else {
1367                                         // Put in dummy
1368                                         $dummy[] = $read;
1369                                 }
1370                                 $i++;
1371                         }
1372                         if ((count($dummy) > 0) && (count($reply) == 0) && (!$isContent)) {
1373                                 // Transfer content from dummy
1374                                 $reply = $content;
1375                         }
1376                         fclose($fp);
1377                         //die(htmlentities($reply));
1378                         return $reply;
1379                 }
1380      }
1381    }    // END _rdf_data()
1382 }   // END class
1383 ?>