Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
/**
3
 * PEAR_REST
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * LICENSE: This source file is subject to version 3.0 of the PHP license
8
 * that is available through the world-wide-web at the following URI:
9
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10
 * the PHP License and are unable to obtain it through the web, please
11
 * send a note to license@php.net so we can mail you a copy immediately.
12
 *
13
 * @category   pear
14
 * @package    PEAR
15
 * @author     Greg Beaver <cellog@php.net>
16
 * @copyright  1997-2006 The PHP Group
17
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
18
 * @version    CVS: $Id: REST.php,v 1.21 2006/03/27 04:33:11 cellog Exp $
19
 * @link       http://pear.php.net/package/PEAR
20
 * @since      File available since Release 1.4.0a1
21
 */
22
 
23
/**
24
 * For downloading xml files
25
 */
26
require_once 'PEAR.php';
27
require_once 'PEAR/XMLParser.php';
28
 
29
/**
30
 * Intelligently retrieve data, following hyperlinks if necessary, and re-directing
31
 * as well
32
 * @category   pear
33
 * @package    PEAR
34
 * @author     Greg Beaver <cellog@php.net>
35
 * @copyright  1997-2006 The PHP Group
36
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
37
 * @version    Release: 1.5.1
38
 * @link       http://pear.php.net/package/PEAR
39
 * @since      Class available since Release 1.4.0a1
40
 */
41
class PEAR_REST
42
{
43
    var $config;
44
    var $_options;
45
    function PEAR_REST(&$config, $options = array())
46
    {
47
        $this->config = &$config;
48
        $this->_options = $options;
49
    }
50
 
51
    /**
52
     * Retrieve REST data, but always retrieve the local cache if it is available.
53
     *
54
     * This is useful for elements that should never change, such as information on a particular
55
     * release
56
     * @param string full URL to this resource
57
     * @param array|false contents of the accept-encoding header
58
     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
59
     *                    parsed using PEAR_XMLParser
60
     * @return string|array
61
     */
62
    function retrieveCacheFirst($url, $accept = false, $forcestring = false)
63
    {
64
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
65
            md5($url) . 'rest.cachefile';
66
        if (file_exists($cachefile)) {
67
            return unserialize(implode('', file($cachefile)));
68
        }
69
        return $this->retrieveData($url, $accept, $forcestring);
70
    }
71
 
72
    /**
73
     * Retrieve a remote REST resource
74
     * @param string full URL to this resource
75
     * @param array|false contents of the accept-encoding header
76
     * @param boolean     if true, xml will be returned as a string, otherwise, xml will be
77
     *                    parsed using PEAR_XMLParser
78
     * @return string|array
79
     */
80
    function retrieveData($url, $accept = false, $forcestring = false)
81
    {
82
        $cacheId = $this->getCacheId($url);
83
        if ($ret = $this->useLocalCache($url, $cacheId)) {
84
            return $ret;
85
        }
86
        if (!isset($this->_options['offline'])) {
87
            $trieddownload = true;
88
            $file = $this->downloadHttp($url, $cacheId ? $cacheId['lastChange'] : false, $accept);
89
        } else {
90
            $trieddownload = false;
91
            $file = false;
92
        }
93
        if (PEAR::isError($file)) {
94
            if ($file->getCode() == -9276) {
95
                $trieddownload = false;
96
                $file = false; // use local copy if available on socket connect error
97
            } else {
98
                return $file;
99
            }
100
        }
101
        if (!$file) {
102
            $ret = $this->getCache($url);
103
            if (!PEAR::isError($ret) && $trieddownload) {
104
                // reset the age of the cache if the server says it was unmodified
105
                $this->saveCache($url, $ret, null, true, $cacheId);
106
            }
107
            return $ret;
108
        }
109
        if (is_array($file)) {
110
            $headers = $file[2];
111
            $lastmodified = $file[1];
112
            $content = $file[0];
113
        } else {
114
            $content = $file;
115
            $lastmodified = false;
116
            $headers = array();
117
        }
118
        if ($forcestring) {
119
            $this->saveCache($url, $content, $lastmodified, false, $cacheId);
120
            return $content;
121
        }
122
        if (isset($headers['content-type'])) {
123
            switch ($headers['content-type']) {
124
                case 'text/xml' :
125
                case 'application/xml' :
126
                    $parser = new PEAR_XMLParser;
127
                    PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
128
                    $err = $parser->parse($content);
129
                    PEAR::popErrorHandling();
130
                    if (PEAR::isError($err)) {
131
                        return PEAR::raiseError('Invalid xml downloaded from "' . $url . '": ' .
132
                            $err->getMessage());
133
                    }
134
                    $content = $parser->getData();
135
                case 'text/html' :
136
                default :
137
                    // use it as a string
138
            }
139
        } else {
140
            // assume XML
141
            $parser = new PEAR_XMLParser;
142
            $parser->parse($content);
143
            $content = $parser->getData();
144
        }
145
        $this->saveCache($url, $content, $lastmodified, false, $cacheId);
146
        return $content;
147
    }
148
 
149
    function useLocalCache($url, $cacheid = null)
150
    {
151
        if ($cacheid === null) {
152
            $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
153
                md5($url) . 'rest.cacheid';
154
            if (file_exists($cacheidfile)) {
155
                $cacheid = unserialize(implode('', file($cacheidfile)));
156
            } else {
157
                return false;
158
            }
159
        }
160
        $cachettl = $this->config->get('cache_ttl');
161
        // If cache is newer than $cachettl seconds, we use the cache!
162
        if (time() - $cacheid['age'] < $cachettl) {
163
            return $this->getCache($url);
164
        }
165
        return false;
166
    }
167
 
168
    function getCacheId($url)
169
    {
170
        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
171
            md5($url) . 'rest.cacheid';
172
        if (file_exists($cacheidfile)) {
173
            $ret = unserialize(implode('', file($cacheidfile)));
174
            return $ret;
175
        } else {
176
            return false;
177
        }
178
    }
179
 
180
    function getCache($url)
181
    {
182
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
183
            md5($url) . 'rest.cachefile';
184
        if (file_exists($cachefile)) {
185
            return unserialize(implode('', file($cachefile)));
186
        } else {
187
            return PEAR::raiseError('No cached content available for "' . $url . '"');
188
        }
189
    }
190
 
191
    /**
192
     * @param string full URL to REST resource
193
     * @param string original contents of the REST resource
194
     * @param array  HTTP Last-Modified and ETag headers
195
     * @param bool   if true, then the cache id file should be regenerated to
196
     *               trigger a new time-to-live value
197
     */
198
    function saveCache($url, $contents, $lastmodified, $nochange = false, $cacheid = null)
199
    {
200
        $cacheidfile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
201
            md5($url) . 'rest.cacheid';
202
        $cachefile = $this->config->get('cache_dir') . DIRECTORY_SEPARATOR .
203
            md5($url) . 'rest.cachefile';
204
        if ($cacheid === null && $nochange) {
205
            $cacheid = unserialize(implode('', file($cacheidfile)));
206
        }
207
 
208
        $fp = @fopen($cacheidfile, 'wb');
209
        if (!$fp) {
210
            $cache_dir = $this->config->get('cache_dir');
211
            if (!is_dir($cache_dir)) {
212
                System::mkdir(array('-p', $cache_dir));
213
                $fp = @fopen($cacheidfile, 'wb');
214
                if (!$fp) {
215
                    return false;
216
                }
217
            } else {
218
                return false;
219
            }
220
        }
221
 
222
        if ($nochange) {
223
            fwrite($fp, serialize(array(
224
                'age'        => time(),
225
                'lastChange' => $cacheid['lastChange'],
226
                )));
227
            fclose($fp);
228
            return true;
229
        } else {
230
            fwrite($fp, serialize(array(
231
                'age'        => time(),
232
                'lastChange' => $lastmodified,
233
                )));
234
        }
235
        fclose($fp);
236
        $fp = @fopen($cachefile, 'wb');
237
        if (!$fp) {
238
            if (file_exists($cacheidfile)) {
239
                @unlink($cacheidfile);
240
            }
241
            return false;
242
        }
243
        fwrite($fp, serialize($contents));
244
        fclose($fp);
245
        return true;
246
    }
247
 
248
    /**
249
     * Efficiently Download a file through HTTP.  Returns downloaded file as a string in-memory
250
     * This is best used for small files
251
     *
252
     * If an HTTP proxy has been configured (http_proxy PEAR_Config
253
     * setting), the proxy will be used.
254
     *
255
     * @param string  $url       the URL to download
256
     * @param string  $save_dir  directory to save file in
257
     * @param false|string|array $lastmodified header values to check against for caching
258
     *                           use false to return the header values from this download
259
     * @param false|array $accept Accept headers to send
260
     * @return string|array  Returns the contents of the downloaded file or a PEAR
261
     *                       error on failure.  If the error is caused by
262
     *                       socket-related errors, the error object will
263
     *                       have the fsockopen error code available through
264
     *                       getCode().  If caching is requested, then return the header
265
     *                       values.
266
     *
267
     * @access public
268
     */
269
    function downloadHttp($url, $lastmodified = null, $accept = false)
270
    {
271
        $info = parse_url($url);
272
        if (!isset($info['scheme']) || !in_array($info['scheme'], array('http', 'https'))) {
273
            return PEAR::raiseError('Cannot download non-http URL "' . $url . '"');
274
        }
275
        if (!isset($info['host'])) {
276
            return PEAR::raiseError('Cannot download from non-URL "' . $url . '"');
277
        } else {
278
            $host = $info['host'];
279
            if (!array_key_exists('port', $info)) {
280
                $info['port'] = null;
281
            }
282
            if (!array_key_exists('path', $info)) {
283
                $info['path'] = null;
284
            }
285
            $port = $info['port'];
286
            $path = $info['path'];
287
        }
288
        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
289
        if ($this->config->get('http_proxy')&&
290
              $proxy = parse_url($this->config->get('http_proxy'))) {
291
            $proxy_host = isset($proxy['host']) ? $proxy['host'] : null;
292
            if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') {
293
                $proxy_host = 'ssl://' . $proxy_host;
294
            }
295
            $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080;
296
            $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null;
297
            $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null;
298
        }
299
        if (empty($port)) {
300
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
301
                $port = 443;
302
            } else {
303
                $port = 80;
304
            }
305
        }
306
        If (isset($proxy['host'])) {
307
            $request = "GET $url HTTP/1.1\r\n";
308
        } else {
309
            $request = "GET $path HTTP/1.1\r\n";
310
        }
311
 
312
        $ifmodifiedsince = '';
313
        if (is_array($lastmodified)) {
314
            if (isset($lastmodified['Last-Modified'])) {
315
                $ifmodifiedsince = 'If-Modified-Since: ' . $lastmodified['Last-Modified'] . "\r\n";
316
            }
317
            if (isset($lastmodified['ETag'])) {
318
                $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
319
            }
320
        } else {
321
            $ifmodifiedsince = ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
322
        }
323
        $request .= "Host: $host:$port\r\n" . $ifmodifiedsince .
324
            "User-Agent: PEAR/1.5.1/PHP/" . PHP_VERSION . "\r\n";
325
        $username = $this->config->get('username');
326
        $password = $this->config->get('password');
327
        if ($username && $password) {
328
            $tmp = base64_encode("$username:$password");
329
            $request .= "Authorization: Basic $tmp\r\n";
330
        }
331
        if ($proxy_host != '' && $proxy_user != '') {
332
            $request .= 'Proxy-Authorization: Basic ' .
333
                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
334
        }
335
        if ($accept) {
336
            $request .= 'Accept: ' . implode(', ', $accept) . "\r\n";
337
        }
338
        $request .= "Connection: close\r\n";
339
        $request .= "\r\n";
340
        if ($proxy_host != '') {
341
            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr, 15);
342
            if (!$fp) {
343
                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr",
344
                    -9276);
345
            }
346
        } else {
347
            if (isset($info['scheme']) && $info['scheme'] == 'https') {
348
                $host = 'ssl://' . $host;
349
            }
350
            $fp = @fsockopen($host, $port, $errno, $errstr);
351
            if (!$fp) {
352
                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
353
            }
354
        }
355
        fwrite($fp, $request);
356
        $headers = array();
357
        while (trim($line = fgets($fp, 1024))) {
358
            if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
359
                $headers[strtolower($matches[1])] = trim($matches[2]);
360
            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
361
                if ($matches[1] == 304 && ($lastmodified || ($lastmodified === false))) {
362
                    return false;
363
                }
364
                if ($matches[1] != 200) {
365
                    return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)", (int) $matches[1]);
366
                }
367
            }
368
        }
369
        if (isset($headers['content-length'])) {
370
            $length = $headers['content-length'];
371
        } else {
372
            $length = -1;
373
        }
374
        $data = '';
375
        while ($chunk = @fread($fp, 8192)) {
376
            $data .= $chunk;
377
        }
378
        fclose($fp);
379
        if ($lastmodified === false || $lastmodified) {
380
            if (isset($headers['etag'])) {
381
                $lastmodified = array('ETag' => $headers['etag']);
382
            }
383
            if (isset($headers['last-modified'])) {
384
                if (is_array($lastmodified)) {
385
                    $lastmodified['Last-Modified'] = $headers['last-modified'];
386
                } else {
387
                    $lastmodified = $headers['last-modified'];
388
                }
389
            }
390
            return array($data, $lastmodified, $headers);
391
        }
392
        return $data;
393
    }
394
}
395
?>