root/trunk/gregarius/extlib/rss_fetch.inc

Revision 1629, 16.2 kB (checked in by mbonetti, 2 years ago)

403 Error message, updated Vietnamese, tiny changes to Admin, API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2/*
3 * Project:     MagpieRSS: a simple RSS integration tool
4 * File:        rss_fetch.inc, a simple functional interface
5                to fetching and parsing RSS files, via the
6                function fetch_rss()
7 * Author:      Kellan Elliott-McCrea <kellan@protest.net>
8 * License:     GPL
9 *
10 * The lastest version of MagpieRSS can be obtained from:
11 * http://magpierss.sourceforge.net
12 *
13 * For questions, help, comments, discussion, etc., please join the
14 * Magpie mailing list:
15 * magpierss-general@lists.sourceforge.net
16 *
17 */
18
19// Setup MAGPIE_DIR for use on hosts that don't include
20// the current path in include_path.
21// with thanks to rajiv and smarty
22if (!defined('DIR_SEP')) {
23    define('DIR_SEP', DIRECTORY_SEPARATOR);
24}
25
26if (!defined('MAGPIE_DIR')) {
27    define('MAGPIE_DIR', dirname(__FILE__) . DIR_SEP);
28}
29
30require_once( MAGPIE_DIR . 'rss_parse.inc' );
31require_once( MAGPIE_DIR . 'rss_dbcache.inc' ); //sameer: changed to use db
32
33// for including 3rd party libraries
34define('MAGPIE_EXTLIB', MAGPIE_DIR);
35require_once( MAGPIE_EXTLIB . 'Snoopy.class.inc');
36
37////mbi: define origin codes:
38// Fetched from network
39define ('MAGPIE_FEED_ORIGIN_NETWORK', 1);
40// Fetched from disk
41define ('MAGPIE_FEED_ORIGIN_CACHE', 2);
42// Network, downloaded
43define ('MAGPIE_FEED_ORIGIN_HTTP_200', 4);
44// Network, not modified
45define ('MAGPIE_FEED_ORIGIN_HTTP_304', 8);
46// Cache, 404
47define ('MAGPIE_FEED_ORIGIN_HTTP_404', 16);
48// Network, timeout
49define ('MAGPIE_FEED_ORIGIN_HTTP_TIMEOUT', 32);
50// Not fetched because age < MAGPIE_CACHE_AGE
51define ('MAGPIE_FEED_ORIGIN_NOT_FETCHED', 64);
52// 403 Forbidden
53define ('MAGPIE_FEED_ORIGIN_HTTP_403', 128);
54
55/*
56 * CONSTANTS - redefine these in your script to change the
57 * behaviour of fetch_rss() currently, most options effect the cache
58 *
59 * MAGPIE_CACHE_ON - Should Magpie cache parsed RSS objects?
60 * For me a built in cache was essential to creating a "PHP-like"
61 * feel to Magpie, see rss_cache.inc for rationale
62 *
63 *
64 * MAGPIE_CACHE_DIR - Where should Magpie cache parsed RSS objects?
65 * This should be a location that the webserver can write to.   If this
66 * directory does not already exist Mapie will try to be smart and create
67 * it.  This will often fail for permissions reasons.
68 *
69 *
70 * MAGPIE_CACHE_AGE - How long to store cached RSS objects? In seconds.
71 *
72 *
73 * MAGPIE_CACHE_FRESH_ONLY - If remote fetch fails, throw error
74 * instead of returning stale object?
75 *
76 * MAGPIE_DEBUG - Display debugging notices?
77 *
78*/
79
80
81/*=======================================================================*\
82    Function: fetch_rss:
83    Purpose:  return RSS object for the give url
84              maintain the cache
85    Input:    url of RSS file
86    Output:   parsed RSS object (see rss_parse.inc)
87 
88    NOTES ON CACHEING: 
89    If caching is on (MAGPIE_CACHE_ON) fetch_rss will first check the cache.
90   
91    NOTES ON RETRIEVING REMOTE FILES:
92    If conditional gets are on (MAGPIE_CONDITIONAL_GET_ON) fetch_rss will
93    return a cached object, and touch the cache object upon recieving a
94    304.
95   
96    NOTES ON FAILED REQUESTS:
97    If there is an HTTP error while fetching an RSS object, the cached
98    version will be return, if it exists (and if MAGPIE_CACHE_FRESH_ONLY is off)
99\*=======================================================================*/
100
101define('MAGPIE_VERSION', '0.7');
102
103$MAGPIE_ERROR = "";
104
105function fetch_rss ($url) {
106    // initialize constants
107    init();
108
109    if ( !isset($url) ) {
110        error("fetch_rss called without a url");
111        return false;
112    }
113
114    // if cache is disabled
115    if ( !MAGPIE_CACHE_ON ) {
116        // fetch file, and parse it
117        $resp = _fetch_remote_file( $url );
118        if ( is_success( $resp->status ) ) {
119            return _response_to_rss( $resp,
120                                     MAGPIE_FEED_ORIGIN_NETWORK | MAGPIE_FEED_ORIGIN_HTTP_200 );
121        } else {
122            error("Failed to fetch $url and cache is off");
123            return false;
124        }
125    }
126    // else cache is ON
127    else {
128        // Flow
129        // 1. check cache
130        // 2. if there is a hit, make sure its fresh
131        // 3. if cached obj fails freshness check, fetch remote
132        // 4. if remote fails, return stale object, or error
133
134        // sameer: changed to use db
135        $cache = new RSSdbCache( MAGPIE_CACHE_DIR, MAGPIE_CACHE_AGE );
136
137        if (MAGPIE_DEBUG and $cache->ERROR) {
138            debug($cache->ERROR, E_USER_WARNING);
139        }
140
141
142        $cache_status    = 0;       // response of check_cache
143        $request_headers = array(); // HTTP headers to send with fetch
144        $rss             = 0;       // parsed RSS object
145        $errormsg        = 0;       // errors, if any
146
147        // store parsed XML by desired output encoding
148        // as character munging happens at parse time
149        //$cache_key       = $url . MAGPIE_OUTPUT_ENCODING;
150        // sameer: removed the encoding from the key
151        $cache_key       = $url;
152
153        if (!$cache->ERROR) {
154            // return cache HIT, MISS, or STALE
155            $cache_status = $cache->check_cache( $cache_key );
156        }
157
158        // if object cached, and cache is fresh, return cached obj
159        if ( $cache_status == 'HIT' ) {
160            $rss = $cache->get
161                   ( $cache_key );
162            if ( isset($rss) and $rss ) {
163                $rss->from_cache = 1;
164                if ( MAGPIE_DEBUG > 1) {
165                    debug("MagpieRSS: Cache HIT", E_USER_NOTICE);
166                }
167                $rss -> rss_origin = MAGPIE_FEED_ORIGIN_CACHE | MAGPIE_FEED_ORIGIN_NOT_FETCHED;
168                return $rss;
169            }
170        }
171
172        // else attempt a conditional get
173
174        // setup headers
175        if ( $cache_status == 'STALE' ) {
176            $rss = $cache->get
177                   ( $cache_key );
178            if ( isset($rss->etag) &&  $rss->etag and
179                    isset($rss->last_modified) && $rss->last_modified ) {
180                $request_headers['If-None-Match'] = $rss->etag;
181                $request_headers['If-Last-Modified'] = $rss->last_modified;
182            }
183        }
184
185        $resp = _fetch_remote_file( $url, $request_headers );
186
187        if (isset($resp) and $resp) {
188            if ($resp->status == '304' ) {
189                // we have the most current copy
190                if ( MAGPIE_DEBUG > 1) {
191                    debug("Got 304 for $url");
192                }
193                // reset cache on 304 (at minutillo insistent prodding)
194                $rss->rss_origin = MAGPIE_FEED_ORIGIN_CACHE | MAGPIE_FEED_ORIGIN_HTTP_304;
195                $cache->set
196                ($cache_key, $rss);
197                return $rss;
198            }
199            elseif ( is_success( $resp->status ) ) {
200                $rss = _response_to_rss( $resp, MAGPIE_FEED_ORIGIN_NETWORK | MAGPIE_FEED_ORIGIN_HTTP_200 );
201                if ( $rss ) {
202                    if (MAGPIE_DEBUG > 1) {
203                        debug("Fetch successful");
204                    }
205                    // add object to cache
206                    $cache->set
207                    ( $cache_key, $rss );
208                    return $rss;
209                }
210            }
211            else {
212                $errormsg = "Failed to fetch $url. ";
213                if ( $resp->status == '-100' ) {
214                    $errormsg .= "(Request timed out after " . MAGPIE_FETCH_TIME_OUT . " seconds)";
215                }
216                elseif ( $resp->error ) {
217# compensate for Snoopy's annoying habbit to tacking
218                    # on '\n'
219                    $http_error = substr($resp->error, 0, -2);
220                    $errormsg .= "(HTTP Error: $http_error)";
221                }
222                else {
223                    $errormsg .=  "(HTTP Response: " . $resp->response_code .')';
224                }
225            }
226        } else {
227            $errormsg = "Unable to retrieve RSS file for unknown reasons.";
228        }
229
230        // else fetch failed
231
232        // attempt to return cached object
233        if ($rss) {
234            if ( MAGPIE_DEBUG ) {
235                debug("Returning STALE object for $url");
236            }
237            $rss -> rss_origin = MAGPIE_FEED_ORIGIN_CACHE;
238            if (is_object($resp) && isset($resp->status)) {
239                switch ($resp->status) {
240                case '404':
241                        $rss -> rss_origin |= MAGPIE_FEED_ORIGIN_HTTP_404;
242                    break;
243                                case '403':
244                                                $rss -> rss_origin |= MAGPIE_FEED_ORIGIN_HTTP_403;
245                                                break;
246                default:
247                    $rss -> rss_origin |= MAGPIE_FEED_ORIGIN_HTTP_TIMEOUT;
248                    break;
249                }
250            } else {
251                $rss -> rss_origin |= MAGPIE_FEED_ORIGIN_HTTP_TIMEOUT;
252            }
253            return $rss;
254        }
255
256        // else we totally failed
257        if ($errormsg) {
258            global $MAGPIE_ERROR;
259            $MAGPIE_ERROR = $errormsg;
260        }
261        //error( $errormsg );
262
263        return false;
264
265    } // end if ( !MAGPIE_CACHE_ON ) {
266} // end fetch_rss()
267
268/*=======================================================================*\
269    Function:   error
270    Purpose:    set MAGPIE_ERROR, and trigger error
271\*=======================================================================*/
272
273function error ($errormsg, $lvl=E_USER_WARNING) {
274    global $MAGPIE_ERROR;
275
276    // append PHP's error message if track_errors enabled
277    if ( isset($php_errormsg) ) {
278        $errormsg .= " ($php_errormsg)";
279    }
280    if ( $errormsg ) {
281        $errormsg = "MagpieRSS: $errormsg";
282        $MAGPIE_ERROR = $errormsg;
283        trigger_error( $errormsg, $lvl);
284    }
285}
286
287function debug ($debugmsg, $lvl=E_USER_NOTICE) {
288    trigger_error("MagpieRSS [debug] $debugmsg", $lvl);
289}
290
291/*=======================================================================*\
292    Function:   magpie_error
293    Purpose:    accessor for the magpie error variable
294\*=======================================================================*/
295function magpie_error ($errormsg="") {
296    global $MAGPIE_ERROR;
297
298    if ( isset($errormsg) and $errormsg ) {
299        $MAGPIE_ERROR = $errormsg;
300    }
301
302    return $MAGPIE_ERROR;
303}
304
305/*=======================================================================*\
306    Function:   _fetch_remote_file
307    Purpose:    retrieve an arbitrary remote file
308    Input:      url of the remote file
309                headers to send along with the request (optional)
310    Output:     an HTTP response object (see Snoopy.class.inc) 
311\*=======================================================================*/
312function _fetch_remote_file ($url, $headers = "" ) {
313    // Snoopy is an HTTP client in PHP
314    $client = new Snoopy();
315    $client->agent = MAGPIE_USER_AGENT;
316    $client->read_timeout = MAGPIE_FETCH_TIME_OUT;
317    $client->use_gzip = MAGPIE_USE_GZIP;
318    if (is_array($headers) ) {
319        $client->rawheaders = $headers;
320    }
321
322    @$client->fetch($url);
323    return $client;
324
325}
326
327/*=======================================================================*\
328    Function:   _response_to_rss
329    Purpose:    parse an HTTP response object into an RSS object
330    Input:      an HTTP response object (see Snoopy)
331    Output:     parsed RSS object (see rss_parse)
332\*=======================================================================*/
333function _response_to_rss ($resp, $rss_origin = 0) {
334    $rss = new MagpieRSS( $resp->results, MAGPIE_OUTPUT_ENCODING, MAGPIE_INPUT_ENCODING, MAGPIE_DETECT_ENCODING );
335
336    // if RSS parsed successfully
337    if ( $rss and !$rss->ERROR) {
338
339        // find Etag, and Last-Modified
340        foreach($resp->headers as $h) {
341            // 2003-03-02 - Nicola Asuni (www.tecnick.com) - fixed bug "Undefined offset: 1"
342            if (strpos($h, ": ")) {
343                list($field, $val) = explode(": ", $h, 2);
344            } else {
345                $field = $h;
346                $val = "";
347            }
348
349            if ( $field == 'ETag' ) {
350                $rss->etag = $val;
351            }
352
353            if ( $field == 'Last-Modified' ) {
354                $rss->last_modified = $val;
355            }
356        }
357
358        $rss -> rss_origin = $rss_origin;
359        return $rss;
360    } // else construct error message
361    else {
362        $errormsg = "Failed to parse RSS file.";
363
364        if ($rss) {
365            $errormsg .= " (" . $rss->ERROR . ")";
366        }
367        error($errormsg);
368
369        return false;
370    } // end if ($rss and !$rss->error)
371}
372
373/*=======================================================================*\
374    Function:   init
375    Purpose:    setup constants with default values
376                check for user overrides
377\*=======================================================================*/
378function init () {
379    if ( defined('MAGPIE_INITALIZED') ) {
380        return;
381    } else {
382        define('MAGPIE_INITALIZED', true);
383    }
384
385    if ( !defined('MAGPIE_CACHE_ON') ) {
386        define('MAGPIE_CACHE_ON', true);
387    }
388
389    if ( !defined('MAGPIE_CACHE_DIR') ) {
390        define('MAGPIE_CACHE_DIR', './cache');
391    }
392
393    if ( !defined('MAGPIE_CACHE_AGE') ) {
394        define('MAGPIE_CACHE_AGE', 60*60); // one hour
395    }
396
397    if ( !defined('MAGPIE_CACHE_FRESH_ONLY') ) {
398        define('MAGPIE_CACHE_FRESH_ONLY', false);
399    }
400
401    if ( !defined('MAGPIE_OUTPUT_ENCODING') ) {
402        define('MAGPIE_OUTPUT_ENCODING', 'UTF-8');
403    }
404
405    if ( !defined('MAGPIE_INPUT_ENCODING') ) {
406        define('MAGPIE_INPUT_ENCODING', null);
407    }
408
409    if ( !defined('MAGPIE_DETECT_ENCODING') ) {
410        define('MAGPIE_DETECT_ENCODING', true);
411    }
412
413    if ( !defined('MAGPIE_DEBUG') ) {
414        define('MAGPIE_DEBUG', 0);
415    }
416
417    if ( !defined('MAGPIE_USER_AGENT') ) {
418        $ua = 'MagpieRSS/'. MAGPIE_VERSION . ' (+http://magpierss.sf.net';
419
420        if ( MAGPIE_CACHE_ON ) {
421            $ua = $ua . ')';
422        } else {
423            $ua = $ua . '; No cache)';
424        }
425
426        define('MAGPIE_USER_AGENT', $ua);
427    }
428
429    if ( !defined('MAGPIE_FETCH_TIME_OUT') ) {
430        define('MAGPIE_FETCH_TIME_OUT', 5); // 5 second timeout
431    }
432
433    // use gzip encoding to fetch rss files if supported?
434    if ( !defined('MAGPIE_USE_GZIP') ) {
435        define('MAGPIE_USE_GZIP', true);
436    }
437}
438
439// NOTE: the following code should really be in Snoopy, or at least
440// somewhere other then rss_fetch!
441
442/*=======================================================================*\
443    HTTP STATUS CODE PREDICATES
444    These functions attempt to classify an HTTP status code
445    based on RFC 2616 and RFC 2518.
446   
447    All of them take an HTTP status code as input, and return true or false
448 
449    All this code is adapted from LWP's HTTP::Status.
450\*=======================================================================*/
451
452
453/*=======================================================================*\
454    Function:   is_info
455    Purpose:    return true if Informational status code
456\*=======================================================================*/
457function is_info ($sc) {
458    return $sc >= 100 && $sc < 200;
459}
460
461/*=======================================================================*\
462    Function:   is_success
463    Purpose:    return true if Successful status code
464\*=======================================================================*/
465function is_success ($sc) {
466    return $sc >= 200 && $sc < 300;
467}
468
469/*=======================================================================*\
470    Function:   is_redirect
471    Purpose:    return true if Redirection status code
472\*=======================================================================*/
473function is_redirect ($sc) {
474    return $sc >= 300 && $sc < 400;
475}
476
477/*=======================================================================*\
478    Function:   is_error
479    Purpose:    return true if Error status code
480\*=======================================================================*/
481function is_error ($sc) {
482    return $sc >= 400 && $sc < 600;
483}
484
485/*=======================================================================*\
486    Function:   is_client_error
487    Purpose:    return true if Error status code, and its a client error
488\*=======================================================================*/
489function is_client_error ($sc) {
490    return $sc >= 400 && $sc < 500;
491}
492
493/*=======================================================================*\
494    Function:   is_client_error
495    Purpose:    return true if Error status code, and its a server error
496\*=======================================================================*/
497function is_server_error ($sc) {
498    return $sc >= 500 && $sc < 600;
499}
500
501?>
Note: See TracBrowser for help on using the browser.