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