MDL-14589 initial file storage implementation, temporary file manager, migration...
[moodle.git] / lib / filelib.php
1 <?php //$Id$
3 define('BYTESERVING_BOUNDARY', 's1k2o3d4a5k6s7'); //unique string constant
5 require_once("$CFG->libdir/file/file_exceptions.php");
6 require_once("$CFG->libdir/file/file_storage.php");
7 require_once("$CFG->libdir/file/file_browser.php");
9 function get_file_url($path, $options=null, $type='coursefile') {
10     global $CFG;
12     $path = str_replace('//', '/', $path);
13     $path = trim($path, '/'); // no leading and trailing slashes
15     // type of file
16     switch ($type) {
17        case 'questionfile':
18             $url = $CFG->wwwroot."/question/exportfile.php";
19             break;
20        case 'rssfile':
21             $url = $CFG->wwwroot."/rss/file.php";
22             break;
23         case 'user':
24             $url = $CFG->wwwroot."/user/pix.php";
25             break;
26         case 'usergroup':
27             $url = $CFG->wwwroot."/user/pixgroup.php";
28             break;
29         case 'httpscoursefile':
30             $url = $CFG->httpswwwroot."/file.php";
31             break;
32          case 'coursefile':
33         default:
34             $url = $CFG->wwwroot."/file.php";
35     }
37     if ($CFG->slasharguments) {
38         $parts = explode('/', $path);
39         $parts = array_map('rawurlencode', $parts);
40         $path  = implode('/', $parts);
41         $ffurl = $url.'/'.$path;
42         $separator = '?';
43     } else {
44         $path = rawurlencode('/'.$path);
45         $ffurl = $url.'?file='.$path;
46         $separator = '&amp;';
47     }
49     if ($options) {
50         foreach ($options as $name=>$value) {
51             $ffurl = $ffurl.$separator.$name.'='.$value;
52             $separator = '&amp;';
53         }
54     }
56     return $ffurl;
57 }
59 /**
60  * Fetches content of file from Internet (using proxy if defined). Uses cURL extension if present.
61  * Due to security concerns only downloads from http(s) sources are supported.
62  *
63  * @param string $url file url starting with http(s)://
64  * @param array $headers http headers, null if none. If set, should be an
65  *   associative array of header name => value pairs.
66  * @param array $postdata array means use POST request with given parameters
67  * @param bool $fullresponse return headers, responses, etc in a similar way snoopy does
68  *   (if false, just returns content)
69  * @param int $timeout timeout for complete download process including all file transfer
70  *   (default 5 minutes)
71  * @param int $connecttimeout timeout for connection to server; this is the timeout that
72  *   usually happens if the remote server is completely down (default 20 seconds);
73  *   may not work when using proxy
74  * @param bool $skipcertverify If true, the peer's SSL certificate will not be checked. Only use this when already in a trusted location.
75  * @return mixed false if request failed or content of the file as string if ok.
76  */
77 function download_file_content($url, $headers=null, $postdata=null, $fullresponse=false, $timeout=300, $connecttimeout=20, $skipcertverify=false) {
78     global $CFG;
80     // some extra security
81     $newlines = array("\r", "\n");
82     if (is_array($headers) ) {
83         foreach ($headers as $key => $value) {
84             $headers[$key] = str_replace($newlines, '', $value);
85         }
86     }
87     $url = str_replace($newlines, '', $url);
88     if (!preg_match('|^https?://|i', $url)) {
89         if ($fullresponse) {
90             $response = new object();
91             $response->status        = 0;
92             $response->headers       = array();
93             $response->response_code = 'Invalid protocol specified in url';
94             $response->results       = '';
95             $response->error         = 'Invalid protocol specified in url';
96             return $response;
97         } else {
98             return false;
99         }
100     }
102     // check if proxy (if used) should be bypassed for this url
103     $proxybypass = is_proxybypass( $url );
105     if (!extension_loaded('curl') or ($ch = curl_init($url)) === false) {
106         require_once($CFG->libdir.'/snoopy/Snoopy.class.inc');
107         $snoopy = new Snoopy();
108         $snoopy->read_timeout = $timeout;
109         $snoopy->_fp_timeout  = $connecttimeout;
110         if (!$proxybypass) {
111             $snoopy->proxy_host   = $CFG->proxyhost;
112             $snoopy->proxy_port   = $CFG->proxyport;
113             if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
114                 // this will probably fail, but let's try it anyway
115                 $snoopy->proxy_user     = $CFG->proxyuser;
116                 $snoopy->proxy_password = $CFG->proxypassword;
117             }
118         }
120         if (is_array($headers) ) {
121             $client->rawheaders = $headers;
122         }
124         if (is_array($postdata)) {
125             $fetch = @$snoopy->fetch($url, $postdata); // use more specific debug code bellow
126         } else {
127             $fetch = @$snoopy->fetch($url); // use more specific debug code bellow
128         }
130         if ($fetch) {
131             if ($fullresponse) {
132                 //fix header line endings
133                 foreach ($snoopy->headers as $key=>$unused) {
134                     $snoopy->headers[$key] = trim($snoopy->headers[$key]);
135                 }
136                 $response = new object();
137                 $response->status        = $snoopy->status;
138                 $response->headers       = $snoopy->headers;
139                 $response->response_code = trim($snoopy->response_code);
140                 $response->results       = $snoopy->results;
141                 $response->error         = $snoopy->error;
142                 return $response;
144             } else if ($snoopy->status != 200) {
145                 debugging("Snoopy request for \"$url\" failed, http response code: ".$snoopy->response_code, DEBUG_ALL);
146                 return false;
148             } else {
149                 return $snoopy->results;
150             }
151         } else {
152             if ($fullresponse) {
153                 $response = new object();
154                 $response->status        = $snoopy->status;
155                 $response->headers       = array();
156                 $response->response_code = $snoopy->response_code;
157                 $response->results       = '';
158                 $response->error         = $snoopy->error;
159                 return $response;
160             } else {
161                 debugging("Snoopy request for \"$url\" failed with: ".$snoopy->error, DEBUG_ALL);
162                 return false;
163             }
164         }
165     }
167     // set extra headers
168     if (is_array($headers) ) {
169         $headers2 = array();
170         foreach ($headers as $key => $value) {
171             $headers2[] = "$key: $value";
172         }
173         curl_setopt($ch, CURLOPT_HTTPHEADER, $headers2);
174     }
177     if ($skipcertverify) {
178         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
179     }
181     // use POST if requested
182     if (is_array($postdata)) {
183         foreach ($postdata as $k=>$v) {
184             $postdata[$k] = urlencode($k).'='.urlencode($v);
185         }
186         $postdata = implode('&', $postdata);
187         curl_setopt($ch, CURLOPT_POST, true);
188         curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
189     }
191     curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
192     curl_setopt($ch, CURLOPT_HEADER, true);
193     curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $connecttimeout);
194     curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
195     if (!ini_get('open_basedir') and !ini_get('safe_mode')) {
196         // TODO: add version test for '7.10.5'
197         curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
198         curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
199     }
201     if (!empty($CFG->proxyhost) and !$proxybypass) {
202         // SOCKS supported in PHP5 only
203         if (!empty($CFG->proxytype) and ($CFG->proxytype == 'SOCKS5')) {
204             if (defined('CURLPROXY_SOCKS5')) {
205                 curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
206             } else {
207                 curl_close($ch);
208                 if ($fullresponse) {
209                     $response = new object();
210                     $response->status        = '0';
211                     $response->headers       = array();
212                     $response->response_code = 'SOCKS5 proxy is not supported in PHP4';
213                     $response->results       = '';
214                     $response->error         = 'SOCKS5 proxy is not supported in PHP4';
215                     return $response;
216                 } else {
217                     debugging("SOCKS5 proxy is not supported in PHP4.", DEBUG_ALL);
218                     return false;
219                 }
220             }
221         }
223         curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
225         if (empty($CFG->proxyport)) {
226             curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost);
227         } else {
228             curl_setopt($ch, CURLOPT_PROXY, $CFG->proxyhost.':'.$CFG->proxyport);
229         }
231         if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
232             curl_setopt($ch, CURLOPT_PROXYUSERPWD, $CFG->proxyuser.':'.$CFG->proxypassword);
233             if (defined('CURLOPT_PROXYAUTH')) {
234                 // any proxy authentication if PHP 5.1
235                 curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
236             }
237         }
238     }
240     $data = curl_exec($ch);
242     // try to detect encoding problems
243     if ((curl_errno($ch) == 23 or curl_errno($ch) == 61) and defined('CURLOPT_ENCODING')) {
244         curl_setopt($ch, CURLOPT_ENCODING, 'none');
245         $data = curl_exec($ch);
246     }
248     if (curl_errno($ch)) {
249         $error    = curl_error($ch);
250         $error_no = curl_errno($ch);
251         curl_close($ch);
253         if ($fullresponse) {
254             $response = new object();
255             if ($error_no == 28) {
256                 $response->status    = '-100'; // mimic snoopy
257             } else {
258                 $response->status    = '0';
259             }
260             $response->headers       = array();
261             $response->response_code = $error;
262             $response->results       = '';
263             $response->error         = $error;
264             return $response;
265         } else {
266             debugging("cURL request for \"$url\" failed with: $error ($error_no)", DEBUG_ALL);
267             return false;
268         }
270     } else {
271         $info = curl_getinfo($ch);
272         curl_close($ch);
274         if (empty($info['http_code'])) {
275             // for security reasons we support only true http connections (Location: file:// exploit prevention)
276             $response = new object();
277             $response->status        = '0';
278             $response->headers       = array();
279             $response->response_code = 'Unknown cURL error';
280             $response->results       = ''; // do NOT change this!
281             $response->error         = 'Unknown cURL error';
283         } else {
284             // strip redirect headers and get headers array and content
285             $data = explode("\r\n\r\n", $data, $info['redirect_count'] + 2);
286             $results = array_pop($data);
287             $headers = array_pop($data);
288             $headers = explode("\r\n", trim($headers));
290             $response = new object();;
291             $response->status        = (string)$info['http_code'];
292             $response->headers       = $headers;
293             $response->response_code = $headers[0];
294             $response->results       = $results;
295             $response->error         = '';
296         }
298         if ($fullresponse) {
299             return $response;
300         } else if ($info['http_code'] != 200) {
301             debugging("cURL request for \"$url\" failed, HTTP response code: ".$response->response_code, DEBUG_ALL);
302             return false;
303         } else {
304             return $response->results;
305         }
306     }
309 /**
310  * @return List of information about file types based on extensions.
311  *   Associative array of extension (lower-case) to associative array
312  *   from 'element name' to data. Current element names are 'type' and 'icon'.
313  *   Unknown types should use the 'xxx' entry which includes defaults.
314  */
315 function get_mimetypes_array() {
316     static $mimearray = array (
317         'xxx'  => array ('type'=>'document/unknown', 'icon'=>'unknown.gif'),
318         '3gp'  => array ('type'=>'video/quicktime', 'icon'=>'video.gif'),
319         'ai'   => array ('type'=>'application/postscript', 'icon'=>'image.gif'),
320         'aif'  => array ('type'=>'audio/x-aiff', 'icon'=>'audio.gif'),
321         'aiff' => array ('type'=>'audio/x-aiff', 'icon'=>'audio.gif'),
322         'aifc' => array ('type'=>'audio/x-aiff', 'icon'=>'audio.gif'),
323         'applescript'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
324         'asc'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
325         'asm'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
326         'au'   => array ('type'=>'audio/au', 'icon'=>'audio.gif'),
327         'avi'  => array ('type'=>'video/x-ms-wm', 'icon'=>'avi.gif'),
328         'bmp'  => array ('type'=>'image/bmp', 'icon'=>'image.gif'),
329         'c'    => array ('type'=>'text/plain', 'icon'=>'text.gif'),
330         'cct'  => array ('type'=>'shockwave/director', 'icon'=>'flash.gif'),
331         'cpp'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
332         'cs'   => array ('type'=>'application/x-csh', 'icon'=>'text.gif'),
333         'css'  => array ('type'=>'text/css', 'icon'=>'text.gif'),
334         'csv'  => array ('type'=>'text/csv', 'icon'=>'excel.gif'),
335         'dv'   => array ('type'=>'video/x-dv', 'icon'=>'video.gif'),
336         'dmg'  => array ('type'=>'application/octet-stream', 'icon'=>'dmg.gif'),
337         'doc'  => array ('type'=>'application/msword', 'icon'=>'word.gif'),
338         'docx' => array ('type'=>'application/msword', 'icon'=>'docx.gif'),
339         'docm' => array ('type'=>'application/msword', 'icon'=>'docm.gif'),
340         'dotx' => array ('type'=>'application/msword', 'icon'=>'dotx.gif'),
341         'dcr'  => array ('type'=>'application/x-director', 'icon'=>'flash.gif'),
342         'dif'  => array ('type'=>'video/x-dv', 'icon'=>'video.gif'),
343         'dir'  => array ('type'=>'application/x-director', 'icon'=>'flash.gif'),
344         'dxr'  => array ('type'=>'application/x-director', 'icon'=>'flash.gif'),
345         'eps'  => array ('type'=>'application/postscript', 'icon'=>'pdf.gif'),
346         'fdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf.gif'),
347         'flv'  => array ('type'=>'video/x-flv', 'icon'=>'video.gif'),
348         'gif'  => array ('type'=>'image/gif', 'icon'=>'image.gif'),
349         'gtar' => array ('type'=>'application/x-gtar', 'icon'=>'zip.gif'),
350         'tgz'  => array ('type'=>'application/g-zip', 'icon'=>'zip.gif'),
351         'gz'   => array ('type'=>'application/g-zip', 'icon'=>'zip.gif'),
352         'gzip' => array ('type'=>'application/g-zip', 'icon'=>'zip.gif'),
353         'h'    => array ('type'=>'text/plain', 'icon'=>'text.gif'),
354         'hpp'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
355         'hqx'  => array ('type'=>'application/mac-binhex40', 'icon'=>'zip.gif'),
356         'htc'  => array ('type'=>'text/x-component', 'icon'=>'text.gif'),
357         'html' => array ('type'=>'text/html', 'icon'=>'html.gif'),
358         'xhtml'=> array ('type'=>'application/xhtml+xml', 'icon'=>'html.gif'),
359         'htm'  => array ('type'=>'text/html', 'icon'=>'html.gif'),
360         'ico'  => array ('type'=>'image/vnd.microsoft.icon', 'icon'=>'image.gif'),
361         'ics'  => array ('type'=>'text/calendar', 'icon'=>'text.gif'),
362         'isf'  => array ('type'=>'application/inspiration', 'icon'=>'isf.gif'),
363         'ist'  => array ('type'=>'application/inspiration.template', 'icon'=>'isf.gif'),
364         'java' => array ('type'=>'text/plain', 'icon'=>'text.gif'),
365         'jcb'  => array ('type'=>'text/xml', 'icon'=>'jcb.gif'),
366         'jcl'  => array ('type'=>'text/xml', 'icon'=>'jcl.gif'),
367         'jcw'  => array ('type'=>'text/xml', 'icon'=>'jcw.gif'),
368         'jmt'  => array ('type'=>'text/xml', 'icon'=>'jmt.gif'),
369         'jmx'  => array ('type'=>'text/xml', 'icon'=>'jmx.gif'),
370         'jpe'  => array ('type'=>'image/jpeg', 'icon'=>'image.gif'),
371         'jpeg' => array ('type'=>'image/jpeg', 'icon'=>'image.gif'),
372         'jpg'  => array ('type'=>'image/jpeg', 'icon'=>'image.gif'),
373         'jqz'  => array ('type'=>'text/xml', 'icon'=>'jqz.gif'),
374         'js'   => array ('type'=>'application/x-javascript', 'icon'=>'text.gif'),
375         'latex'=> array ('type'=>'application/x-latex', 'icon'=>'text.gif'),
376         'm'    => array ('type'=>'text/plain', 'icon'=>'text.gif'),
377         'mov'  => array ('type'=>'video/quicktime', 'icon'=>'video.gif'),
378         'movie'=> array ('type'=>'video/x-sgi-movie', 'icon'=>'video.gif'),
379         'm3u'  => array ('type'=>'audio/x-mpegurl', 'icon'=>'audio.gif'),
380         'mp3'  => array ('type'=>'audio/mp3', 'icon'=>'audio.gif'),
381         'mp4'  => array ('type'=>'video/mp4', 'icon'=>'video.gif'),
382         'mpeg' => array ('type'=>'video/mpeg', 'icon'=>'video.gif'),
383         'mpe'  => array ('type'=>'video/mpeg', 'icon'=>'video.gif'),
384         'mpg'  => array ('type'=>'video/mpeg', 'icon'=>'video.gif'),
386         'odt'  => array ('type'=>'application/vnd.oasis.opendocument.text', 'icon'=>'odt.gif'),
387         'ott'  => array ('type'=>'application/vnd.oasis.opendocument.text-template', 'icon'=>'odt.gif'),
388         'oth'  => array ('type'=>'application/vnd.oasis.opendocument.text-web', 'icon'=>'odt.gif'),
389         'odm'  => array ('type'=>'application/vnd.oasis.opendocument.text-master', 'icon'=>'odm.gif'),
390         'odg'  => array ('type'=>'application/vnd.oasis.opendocument.graphics', 'icon'=>'odg.gif'),
391         'otg'  => array ('type'=>'application/vnd.oasis.opendocument.graphics-template', 'icon'=>'odg.gif'),
392         'odp'  => array ('type'=>'application/vnd.oasis.opendocument.presentation', 'icon'=>'odp.gif'),
393         'otp'  => array ('type'=>'application/vnd.oasis.opendocument.presentation-template', 'icon'=>'odp.gif'),
394         'ods'  => array ('type'=>'application/vnd.oasis.opendocument.spreadsheet', 'icon'=>'ods.gif'),
395         'ots'  => array ('type'=>'application/vnd.oasis.opendocument.spreadsheet-template', 'icon'=>'ods.gif'),
396         'odc'  => array ('type'=>'application/vnd.oasis.opendocument.chart', 'icon'=>'odc.gif'),
397         'odf'  => array ('type'=>'application/vnd.oasis.opendocument.formula', 'icon'=>'odf.gif'),
398         'odb'  => array ('type'=>'application/vnd.oasis.opendocument.database', 'icon'=>'odb.gif'),
399         'odi'  => array ('type'=>'application/vnd.oasis.opendocument.image', 'icon'=>'odi.gif'),
401         'pct'  => array ('type'=>'image/pict', 'icon'=>'image.gif'),
402         'pdf'  => array ('type'=>'application/pdf', 'icon'=>'pdf.gif'),
403         'php'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
404         'pic'  => array ('type'=>'image/pict', 'icon'=>'image.gif'),
405         'pict' => array ('type'=>'image/pict', 'icon'=>'image.gif'),
406         'png'  => array ('type'=>'image/png', 'icon'=>'image.gif'),
407         'pps'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint.gif'),
408         'ppt'  => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'powerpoint.gif'),
409         'pptx' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'pptx.gif'),
410         'pptm' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'pptm.gif'),
411         'potx' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'potx.gif'),
412         'potm' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'potm.gif'),
413         'ppam' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'ppam.gif'),
414         'ppsx' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'ppsx.gif'),
415         'ppsm' => array ('type'=>'application/vnd.ms-powerpoint', 'icon'=>'ppsm.gif'),
416         'ps'   => array ('type'=>'application/postscript', 'icon'=>'pdf.gif'),
417         'qt'   => array ('type'=>'video/quicktime', 'icon'=>'video.gif'),
418         'ra'   => array ('type'=>'audio/x-realaudio', 'icon'=>'audio.gif'),
419         'ram'  => array ('type'=>'audio/x-pn-realaudio', 'icon'=>'audio.gif'),
420         'rhb'  => array ('type'=>'text/xml', 'icon'=>'xml.gif'),
421         'rm'   => array ('type'=>'audio/x-pn-realaudio', 'icon'=>'audio.gif'),
422         'rtf'  => array ('type'=>'text/rtf', 'icon'=>'text.gif'),
423         'rtx'  => array ('type'=>'text/richtext', 'icon'=>'text.gif'),
424         'sh'   => array ('type'=>'application/x-sh', 'icon'=>'text.gif'),
425         'sit'  => array ('type'=>'application/x-stuffit', 'icon'=>'zip.gif'),
426         'smi'  => array ('type'=>'application/smil', 'icon'=>'text.gif'),
427         'smil' => array ('type'=>'application/smil', 'icon'=>'text.gif'),
428         'sqt'  => array ('type'=>'text/xml', 'icon'=>'xml.gif'),
429         'svg'  => array ('type'=>'image/svg+xml', 'icon'=>'image.gif'),
430         'svgz' => array ('type'=>'image/svg+xml', 'icon'=>'image.gif'),
431         'swa'  => array ('type'=>'application/x-director', 'icon'=>'flash.gif'),
432         'swf'  => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash.gif'),
433         'swfl' => array ('type'=>'application/x-shockwave-flash', 'icon'=>'flash.gif'),
435         'sxw'  => array ('type'=>'application/vnd.sun.xml.writer', 'icon'=>'odt.gif'),
436         'stw'  => array ('type'=>'application/vnd.sun.xml.writer.template', 'icon'=>'odt.gif'),
437         'sxc'  => array ('type'=>'application/vnd.sun.xml.calc', 'icon'=>'odt.gif'),
438         'stc'  => array ('type'=>'application/vnd.sun.xml.calc.template', 'icon'=>'odt.gif'),
439         'sxd'  => array ('type'=>'application/vnd.sun.xml.draw', 'icon'=>'odt.gif'),
440         'std'  => array ('type'=>'application/vnd.sun.xml.draw.template', 'icon'=>'odt.gif'),
441         'sxi'  => array ('type'=>'application/vnd.sun.xml.impress', 'icon'=>'odt.gif'),
442         'sti'  => array ('type'=>'application/vnd.sun.xml.impress.template', 'icon'=>'odt.gif'),
443         'sxg'  => array ('type'=>'application/vnd.sun.xml.writer.global', 'icon'=>'odt.gif'),
444         'sxm'  => array ('type'=>'application/vnd.sun.xml.math', 'icon'=>'odt.gif'),
446         'tar'  => array ('type'=>'application/x-tar', 'icon'=>'zip.gif'),
447         'tif'  => array ('type'=>'image/tiff', 'icon'=>'image.gif'),
448         'tiff' => array ('type'=>'image/tiff', 'icon'=>'image.gif'),
449         'tex'  => array ('type'=>'application/x-tex', 'icon'=>'text.gif'),
450         'texi' => array ('type'=>'application/x-texinfo', 'icon'=>'text.gif'),
451         'texinfo'  => array ('type'=>'application/x-texinfo', 'icon'=>'text.gif'),
452         'tsv'  => array ('type'=>'text/tab-separated-values', 'icon'=>'text.gif'),
453         'txt'  => array ('type'=>'text/plain', 'icon'=>'text.gif'),
454         'wav'  => array ('type'=>'audio/wav', 'icon'=>'audio.gif'),
455         'wmv'  => array ('type'=>'video/x-ms-wmv', 'icon'=>'avi.gif'),
456         'asf'  => array ('type'=>'video/x-ms-asf', 'icon'=>'avi.gif'),
457         'xdp'  => array ('type'=>'application/pdf', 'icon'=>'pdf.gif'),
458         'xfd'  => array ('type'=>'application/pdf', 'icon'=>'pdf.gif'),
459         'xfdf' => array ('type'=>'application/pdf', 'icon'=>'pdf.gif'),
460         'xls'  => array ('type'=>'application/vnd.ms-excel', 'icon'=>'excel.gif'),
461         'xlsx' => array ('type'=>'application/vnd.ms-excel', 'icon'=>'xlsx.gif'),
462         'xlsm' => array ('type'=>'application/vnd.ms-excel', 'icon'=>'xlsm.gif'),
463         'xltx' => array ('type'=>'application/vnd.ms-excel', 'icon'=>'xltx.gif'),
464         'xltm' => array ('type'=>'application/vnd.ms-excel', 'icon'=>'xltm.gif'),
465         'xlsb' => array ('type'=>'application/vnd.ms-excel', 'icon'=>'xlsb.gif'),
466         'xlam' => array ('type'=>'application/vnd.ms-excel', 'icon'=>'xlam.gif'),
467         'xml'  => array ('type'=>'application/xml', 'icon'=>'xml.gif'),
468         'xsl'  => array ('type'=>'text/xml', 'icon'=>'xml.gif'),
469         'zip'  => array ('type'=>'application/zip', 'icon'=>'zip.gif')
470     );
471     return $mimearray;
474 /**
475  * Obtains information about a filetype based on its extension. Will
476  * use a default if no information is present about that particular
477  * extension.
478  * @param string $element Desired information (usually 'icon'
479  *   for icon filename or 'type' for MIME type)
480  * @param string $filename Filename we're looking up
481  * @return string Requested piece of information from array
482  */
483 function mimeinfo($element, $filename) {
484     $mimeinfo = get_mimetypes_array();
486     if (eregi('\.([a-z0-9]+)$', $filename, $match)) {
487         if (isset($mimeinfo[strtolower($match[1])][$element])) {
488             return $mimeinfo[strtolower($match[1])][$element];
489         } else {
490             return $mimeinfo['xxx'][$element];   // By default
491         }
492     } else {
493         return $mimeinfo['xxx'][$element];   // By default
494     }
497 /**
498  * Obtains information about a filetype based on the MIME type rather than
499  * the other way around.
500  * @param string $element Desired information (usually 'icon')
501  * @param string $mimetype MIME type we're looking up
502  * @return string Requested piece of information from array
503  */
504 function mimeinfo_from_type($element, $mimetype) {
505     $mimeinfo = get_mimetypes_array();
507     foreach($mimeinfo as $values) {
508         if($values['type']==$mimetype) {
509             if(isset($values[$element])) {
510                 return $values[$element];
511             }
512             break;
513         }
514     }
515     return $mimeinfo['xxx'][$element]; // Default
518 /**
519  * Get information about a filetype based on the icon file.
520  * @param string $element Desired information (usually 'icon')
521  * @param string $icon Icon file path.
522  * @return string Requested piece of information from array
523  */
524 function mimeinfo_from_icon($element, $icon) {
525     $mimeinfo = get_mimetypes_array();
527     if (preg_match("/\/(.*)/", $icon, $matches)) {
528         $icon = $matches[1];
529     }
530     $info = $mimeinfo['xxx'][$element]; // Default
531     foreach($mimeinfo as $values) {
532         if($values['icon']==$icon) {
533             if(isset($values[$element])) {
534                 $info = $values[$element];
535             }
536             //No break, for example for 'excel.gif' we don't want 'csv'!
537         }
538     }
539     return $info;
542 /**
543  * Obtains descriptions for file types (e.g. 'Microsoft Word document') from the
544  * mimetypes.php language file.
545  * @param string $mimetype MIME type (can be obtained using the mimeinfo function)
546  * @param bool $capitalise If true, capitalises first character of result
547  * @return string Text description
548  */
549 function get_mimetype_description($mimetype,$capitalise=false) {
550     $result=get_string($mimetype,'mimetypes');
551     // Surrounded by square brackets indicates that there isn't a string for that
552     // (maybe there is a better way to find this out?)
553     if(strpos($result,'[')===0) {
554         $result=get_string('document/unknown','mimetypes');
555     }
556     if($capitalise) {
557         $result=ucfirst($result);
558     }
559     return $result;
562 /**
563  * Handles the sending of temporary file to user, download is forced.
564  * File is deleted after abort or succesful sending.
565  * @param string $path path to file, preferably from moodledata/temp/something; or content of file itself
566  * @param string $filename proposed file name when saving file
567  * @param bool $path is content of file
568  */
569 function send_temp_file($path, $filename, $pathisstring=false) {
570     global $CFG;
572     // close session - not needed anymore
573     @session_write_close();
575     if (!$pathisstring) {
576         if (!file_exists($path)) {
577             header('HTTP/1.0 404 not found');
578             error(get_string('filenotfound', 'error'), $CFG->wwwroot.'/');
579         }
580         // executed after normal finish or abort
581         @register_shutdown_function('send_temp_file_finished', $path);
582     }
584     //IE compatibiltiy HACK!
585     if (ini_get('zlib.output_compression')) {
586         ini_set('zlib.output_compression', 'Off');
587     }
589     // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
590     if (check_browser_version('MSIE')) {
591         $filename = urlencode($filename);
592     }
594     $filesize = $pathisstring ? strlen($path) : filesize($path);
596     @header('Content-Disposition: attachment; filename='.$filename);
597     @header('Content-Length: '.$filesize);
598     if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
599         @header('Cache-Control: max-age=10');
600         @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
601         @header('Pragma: ');
602     } else { //normal http - prevent caching at all cost
603         @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
604         @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
605         @header('Pragma: no-cache');
606     }
607     @header('Accept-Ranges: none'); // Do not allow byteserving
609     while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
610     if ($pathisstring) {
611         echo $path;
612     } else {
613         readfile_chunked($path);
614     }
616     die; //no more chars to output
619 /**
620  * Internal callnack function used by send_temp_file()
621  */
622 function send_temp_file_finished($path) {
623     if (file_exists($path)) {
624         @unlink($path);
625     }
628 /**
629  * Handles the sending of file data to the user's browser, including support for
630  * byteranges etc.
631  * @param string $path Path of file on disk (including real filename), or actual content of file as string
632  * @param string $filename Filename to send
633  * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
634  * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
635  * @param bool $pathisstring If true (default false), $path is the content to send and not the pathname
636  * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
637  * @param string $mimetype Include to specify the MIME type; leave blank to have it guess the type from $filename
638  */
639 function send_file($path, $filename, $lifetime=86400 , $filter=0, $pathisstring=false, $forcedownload=false, $mimetype='') {
640     global $CFG, $COURSE, $SESSION;
642     session_write_close(); // unlock session during fileserving
644     // Use given MIME type if specified, otherwise guess it using mimeinfo.
645     // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O
646     // only Firefox saves all files locally before opening when content-disposition: attachment stated
647     $isFF         = check_browser_version('Firefox', '1.5'); // only FF > 1.5 properly tested
648     $mimetype     = ($forcedownload and !$isFF) ? 'application/x-forcedownload' :
649                          ($mimetype ? $mimetype : mimeinfo('type', $filename));
650     $lastmodified = $pathisstring ? time() : filemtime($path);
651     $filesize     = $pathisstring ? strlen($path) : filesize($path);
653 /* - MDL-13949
654     //Adobe Acrobat Reader XSS prevention
655     if ($mimetype=='application/pdf' or mimeinfo('type', $filename)=='application/pdf') {
656         //please note that it prevents opening of pdfs in browser when http referer disabled
657         //or file linked from another site; browser caching of pdfs is now disabled too
658         if (!empty($_SERVER['HTTP_RANGE'])) {
659             //already byteserving
660             $lifetime = 1; // >0 needed for byteserving
661         } else if (empty($_SERVER['HTTP_REFERER']) or strpos($_SERVER['HTTP_REFERER'], $CFG->wwwroot)!==0) {
662             $mimetype = 'application/x-forcedownload';
663             $forcedownload = true;
664             $lifetime = 0;
665         } else {
666             $lifetime = 1; // >0 needed for byteserving
667         }
668     }
669 */
671     //IE compatibiltiy HACK!
672     if (ini_get('zlib.output_compression')) {
673         ini_set('zlib.output_compression', 'Off');
674     }
676     //try to disable automatic sid rewrite in cookieless mode
677     @ini_set("session.use_trans_sid", "false");
679     //do not put '@' before the next header to detect incorrect moodle configurations,
680     //error should be better than "weird" empty lines for admins/users
681     //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
682     header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
684     // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
685     if (check_browser_version('MSIE')) {
686         $filename = rawurlencode($filename);
687     }
689     if ($forcedownload) {
690         @header('Content-Disposition: attachment; filename="'.$filename.'"');
691     } else {
692         @header('Content-Disposition: inline; filename="'.$filename.'"');
693     }
695     if ($lifetime > 0) {
696         @header('Cache-Control: max-age='.$lifetime);
697         @header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
698         @header('Pragma: ');
700         if (empty($CFG->disablebyteserving) && !$pathisstring && $mimetype != 'text/plain' && $mimetype != 'text/html') {
702             @header('Accept-Ranges: bytes');
704             if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
705                 // byteserving stuff - for acrobat reader and download accelerators
706                 // see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
707                 // inspired by: http://www.coneural.org/florian/papers/04_byteserving.php
708                 $ranges = false;
709                 if (preg_match_all('/(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) {
710                     foreach ($ranges as $key=>$value) {
711                         if ($ranges[$key][1] == '') {
712                             //suffix case
713                             $ranges[$key][1] = $filesize - $ranges[$key][2];
714                             $ranges[$key][2] = $filesize - 1;
715                         } else if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) {
716                             //fix range length
717                             $ranges[$key][2] = $filesize - 1;
718                         }
719                         if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) {
720                             //invalid byte-range ==> ignore header
721                             $ranges = false;
722                             break;
723                         }
724                         //prepare multipart header
725                         $ranges[$key][0] =  "\r\n--".BYTESERVING_BOUNDARY."\r\nContent-Type: $mimetype\r\n";
726                         $ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/$filesize\r\n\r\n";
727                     }
728                 } else {
729                     $ranges = false;
730                 }
731                 if ($ranges) {
732                     $handle = fopen($filename, 'rb');
733                     byteserving_send_file($handle, $mimetype, $ranges, $filesize);
734                 }
735             }
736         } else {
737             /// Do not byteserve (disabled, strings, text and html files).
738             @header('Accept-Ranges: none');
739         }
740     } else { // Do not cache files in proxies and browsers
741         if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
742             @header('Cache-Control: max-age=10');
743             @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
744             @header('Pragma: ');
745         } else { //normal http - prevent caching at all cost
746             @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
747             @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
748             @header('Pragma: no-cache');
749         }
750         @header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
751     }
753     if (empty($filter)) {
754         if ($mimetype == 'text/html' && !empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
755             //cookieless mode - rewrite links
756             @header('Content-Type: text/html');
757             $path = $pathisstring ? $path : implode('', file($path));
758             $path = $SESSION->sid_ob_rewrite($path);
759             $filesize = strlen($path);
760             $pathisstring = true;
761         } else if ($mimetype == 'text/plain') {
762             @header('Content-Type: Text/plain; charset=utf-8'); //add encoding
763         } else {
764             @header('Content-Type: '.$mimetype);
765         }
766         @header('Content-Length: '.$filesize);
767         while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
768         if ($pathisstring) {
769             echo $path;
770         } else {
771             readfile_chunked($path);
772         }
773     } else {     // Try to put the file through filters
774         if ($mimetype == 'text/html') {
775             $options = new object();
776             $options->noclean = true;
777             $options->nocache = true; // temporary workaround for MDL-5136
778             $text = $pathisstring ? $path : implode('', file($path));
780             $text = file_modify_html_header($text);
781             $output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
782             if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
783                 //cookieless mode - rewrite links
784                 $output = $SESSION->sid_ob_rewrite($output);
785             }
787             @header('Content-Length: '.strlen($output));
788             @header('Content-Type: text/html');
789             while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
790             echo $output;
791         // only filter text if filter all files is selected
792         } else if (($mimetype == 'text/plain') and ($filter == 1)) {
793             $options = new object();
794             $options->newlines = false;
795             $options->noclean = true;
796             $text = htmlentities($pathisstring ? $path : implode('', file($path)));
797             $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
798             if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
799                 //cookieless mode - rewrite links
800                 $output = $SESSION->sid_ob_rewrite($output);
801             }
803             @header('Content-Length: '.strlen($output));
804             @header('Content-Type: text/html; charset=utf-8'); //add encoding
805             while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
806             echo $output;
807         } else {    // Just send it out raw
808             @header('Content-Length: '.$filesize);
809             @header('Content-Type: '.$mimetype);
810             while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
811             if ($pathisstring) {
812                 echo $path;
813             }else {
814                 readfile_chunked($path);
815             }
816         }
817     }
818     die; //no more chars to output!!!
821 /**
822  * Handles the sending of file data to the user's browser, including support for
823  * byteranges etc.
824  * @param object $stored_file local file object
825  * @param int $lifetime Number of seconds before the file should expire from caches (default 24 hours)
826  * @param int $filter 0 (default)=no filtering, 1=all files, 2=html files only
827  * @param bool $forcedownload If true (default false), forces download of file rather than view in browser/plugin
828  * @param string $filename Override filename
829  * @param string $mimetype Include to specify the MIME type; leave blank to have it guess the type from $filename
830  */
831 function send_stored_file($stored_file, $lifetime=86400 , $filter=0, $forcedownload=false, $filename=null) {
832     global $CFG, $COURSE, $SESSION;
834     session_write_close(); // unlock session during fileserving
836     // Use given MIME type if specified, otherwise guess it using mimeinfo.
837     // IE, Konqueror and Opera open html file directly in browser from web even when directed to save it to disk :-O
838     // only Firefox saves all files locally before opening when content-disposition: attachment stated
839     $filename     = is_null($filename) ? $stored_file->get_filename() : $filename;
840     $isFF         = check_browser_version('Firefox', '1.5'); // only FF > 1.5 properly tested
841     $mimetype     = ($forcedownload and !$isFF) ? 'application/x-forcedownload' :
842                          ($stored_file->get_mimetype() ? $stored_file->get_mimetype() : mimeinfo('type', $filename));
843     $lastmodified = $stored_file->get_timemodified();
844     $filesize     = $stored_file->get_filesize();
846     //IE compatibiltiy HACK!
847     if (ini_get('zlib.output_compression')) {
848         ini_set('zlib.output_compression', 'Off');
849     }
851     //try to disable automatic sid rewrite in cookieless mode
852     @ini_set("session.use_trans_sid", "false");
854     //do not put '@' before the next header to detect incorrect moodle configurations,
855     //error should be better than "weird" empty lines for admins/users
856     //TODO: should we remove all those @ before the header()? Are all of the values supported on all servers?
857     header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
859     // if user is using IE, urlencode the filename so that multibyte file name will show up correctly on popup
860     if (check_browser_version('MSIE')) {
861         $filename = rawurlencode($filename);
862     }
864     if ($forcedownload) {
865         @header('Content-Disposition: attachment; filename="'.$filename.'"');
866     } else {
867         @header('Content-Disposition: inline; filename="'.$filename.'"');
868     }
870     if ($lifetime > 0) {
871         @header('Cache-Control: max-age='.$lifetime);
872         @header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
873         @header('Pragma: ');
875         if (empty($CFG->disablebyteserving) && $mimetype != 'text/plain' && $mimetype != 'text/html') {
877             @header('Accept-Ranges: bytes');
879             if (!empty($_SERVER['HTTP_RANGE']) && strpos($_SERVER['HTTP_RANGE'],'bytes=') !== FALSE) {
880                 // byteserving stuff - for acrobat reader and download accelerators
881                 // see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35
882                 // inspired by: http://www.coneural.org/florian/papers/04_byteserving.php
883                 $ranges = false;
884                 if (preg_match_all('/(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $ranges, PREG_SET_ORDER)) {
885                     foreach ($ranges as $key=>$value) {
886                         if ($ranges[$key][1] == '') {
887                             //suffix case
888                             $ranges[$key][1] = $filesize - $ranges[$key][2];
889                             $ranges[$key][2] = $filesize - 1;
890                         } else if ($ranges[$key][2] == '' || $ranges[$key][2] > $filesize - 1) {
891                             //fix range length
892                             $ranges[$key][2] = $filesize - 1;
893                         }
894                         if ($ranges[$key][2] != '' && $ranges[$key][2] < $ranges[$key][1]) {
895                             //invalid byte-range ==> ignore header
896                             $ranges = false;
897                             break;
898                         }
899                         //prepare multipart header
900                         $ranges[$key][0] =  "\r\n--".BYTESERVING_BOUNDARY."\r\nContent-Type: $mimetype\r\n";
901                         $ranges[$key][0] .= "Content-Range: bytes {$ranges[$key][1]}-{$ranges[$key][2]}/$filesize\r\n\r\n";
902                     }
903                 } else {
904                     $ranges = false;
905                 }
906                 if ($ranges) {
907                     byteserving_send_file($stored_file->get_content_file_handle(), $mimetype, $ranges, $filesize);
908                 }
909             }
910         } else {
911             /// Do not byteserve (disabled, strings, text and html files).
912             @header('Accept-Ranges: none');
913         }
914     } else { // Do not cache files in proxies and browsers
915         if (strpos($CFG->wwwroot, 'https://') === 0) { //https sites - watch out for IE! KB812935 and KB316431
916             @header('Cache-Control: max-age=10');
917             @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
918             @header('Pragma: ');
919         } else { //normal http - prevent caching at all cost
920             @header('Cache-Control: private, must-revalidate, pre-check=0, post-check=0, max-age=0');
921             @header('Expires: '. gmdate('D, d M Y H:i:s', 0) .' GMT');
922             @header('Pragma: no-cache');
923         }
924         @header('Accept-Ranges: none'); // Do not allow byteserving when caching disabled
925     }
927     if (empty($filter)) {
928         $filtered = false;
929         if ($mimetype == 'text/html' && !empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
930             //cookieless mode - rewrite links
931             @header('Content-Type: text/html');
932             $text = $stored_file->get_content();
933             $text = $SESSION->sid_ob_rewrite($text);
934             $filesize = strlen($text);
935             $filtered = true;
936         } else if ($mimetype == 'text/plain') {
937             @header('Content-Type: Text/plain; charset=utf-8'); //add encoding
938         } else {
939             @header('Content-Type: '.$mimetype);
940         }
941         @header('Content-Length: '.$filesize);
942         while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
943         if ($filtered) {
944             echo $text;
945         } else {
946             $stored_file->readfile();
947         }
949     } else {     // Try to put the file through filters
950         if ($mimetype == 'text/html') {
951             $options = new object();
952             $options->noclean = true;
953             $options->nocache = true; // temporary workaround for MDL-5136
954             $text = $stored_file->get_content();
955             $text = file_modify_html_header($text);
956             $output = format_text($text, FORMAT_HTML, $options, $COURSE->id);
957             if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
958                 //cookieless mode - rewrite links
959                 $output = $SESSION->sid_ob_rewrite($output);
960             }
962             @header('Content-Length: '.strlen($output));
963             @header('Content-Type: text/html');
964             while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
965             echo $output;
966         // only filter text if filter all files is selected
967         } else if (($mimetype == 'text/plain') and ($filter == 1)) {
968             $options = new object();
969             $options->newlines = false;
970             $options->noclean = true;
971             $text = $stored_file->get_content();
972             $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
973             if (!empty($CFG->usesid) && empty($_COOKIE['MoodleSession'.$CFG->sessioncookie])) {
974                 //cookieless mode - rewrite links
975                 $output = $SESSION->sid_ob_rewrite($output);
976             }
978             @header('Content-Length: '.strlen($output));
979             @header('Content-Type: text/html; charset=utf-8'); //add encoding
980             while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
981             echo $output;
982         } else {    // Just send it out raw
983             @header('Content-Length: '.$filesize);
984             @header('Content-Type: '.$mimetype);
985             while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
986             $stored_file->readfile();
987         }
988     }
989     die; //no more chars to output!!!
992 function get_records_csv($file, $table) {
993     global $CFG, $DB;
995     if (!$metacolumns = $DB->get_columns($table)) {
996         return false;
997     }
999     if(!($handle = @fopen($file, 'r'))) {
1000         print_error('get_records_csv failed to open '.$file);
1001     }
1003     $fieldnames = fgetcsv($handle, 4096);
1004     if(empty($fieldnames)) {
1005         fclose($handle);
1006         return false;
1007     }
1009     $columns = array();
1011     foreach($metacolumns as $metacolumn) {
1012         $ord = array_search($metacolumn->name, $fieldnames);
1013         if(is_int($ord)) {
1014             $columns[$metacolumn->name] = $ord;
1015         }
1016     }
1018     $rows = array();
1020     while (($data = fgetcsv($handle, 4096)) !== false) {
1021         $item = new stdClass;
1022         foreach($columns as $name => $ord) {
1023             $item->$name = $data[$ord];
1024         }
1025         $rows[] = $item;
1026     }
1028     fclose($handle);
1029     return $rows;
1032 function put_records_csv($file, $records, $table = NULL) {
1033     global $CFG, $DB;
1035     if (empty($records)) {
1036         return true;
1037     }
1039     $metacolumns = NULL;
1040     if ($table !== NULL && !$metacolumns = $DB->get_columns($table)) {
1041         return false;
1042     }
1044     echo "x";
1046     if(!($fp = @fopen($CFG->dataroot.'/temp/'.$file, 'w'))) {
1047         print_error('put_records_csv failed to open '.$file);
1048     }
1050     $proto = reset($records);
1051     if(is_object($proto)) {
1052         $fields_records = array_keys(get_object_vars($proto));
1053     }
1054     else if(is_array($proto)) {
1055         $fields_records = array_keys($proto);
1056     }
1057     else {
1058         return false;
1059     }
1060     echo "x";
1062     if(!empty($metacolumns)) {
1063         $fields_table = array_map(create_function('$a', 'return $a->name;'), $metacolumns);
1064         $fields = array_intersect($fields_records, $fields_table);
1065     }
1066     else {
1067         $fields = $fields_records;
1068     }
1070     fwrite($fp, implode(',', $fields));
1071     fwrite($fp, "\r\n");
1073     foreach($records as $record) {
1074         $array  = (array)$record;
1075         $values = array();
1076         foreach($fields as $field) {
1077             if(strpos($array[$field], ',')) {
1078                 $values[] = '"'.str_replace('"', '\"', $array[$field]).'"';
1079             }
1080             else {
1081                 $values[] = $array[$field];
1082             }
1083         }
1084         fwrite($fp, implode(',', $values)."\r\n");
1085     }
1087     fclose($fp);
1088     return true;
1092 /**
1093  * Recursively delete the file or folder with path $location. That is,
1094  * if it is a file delete it. If it is a folder, delete all its content
1095  * then delete it. If $location does not exist to start, that is not
1096  * considered an error.
1097  *
1098  * @param $location the path to remove.
1099  */
1100 function fulldelete($location) {
1101     if (is_dir($location)) {
1102         $currdir = opendir($location);
1103         while (false !== ($file = readdir($currdir))) {
1104             if ($file <> ".." && $file <> ".") {
1105                 $fullfile = $location."/".$file;
1106                 if (is_dir($fullfile)) {
1107                     if (!fulldelete($fullfile)) {
1108                         return false;
1109                     }
1110                 } else {
1111                     if (!unlink($fullfile)) {
1112                         return false;
1113                     }
1114                 }
1115             }
1116         }
1117         closedir($currdir);
1118         if (! rmdir($location)) {
1119             return false;
1120         }
1122     } else if (file_exists($location)) {
1123         if (!unlink($location)) {
1124             return false;
1125         }
1126     }
1127     return true;
1130 /**
1131  * Improves memory consumptions and works around buggy readfile() in PHP 5.0.4 (2MB readfile limit).
1132  */
1133 function readfile_chunked($filename, $retbytes=true) {
1134     $chunksize = 1*(1024*1024); // 1MB chunks - must be less than 2MB!
1135     $buffer = '';
1136     $cnt =0;
1137     $handle = fopen($filename, 'rb');
1138     if ($handle === false) {
1139         return false;
1140     }
1142     while (!feof($handle)) {
1143         @set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
1144         $buffer = fread($handle, $chunksize);
1145         echo $buffer;
1146         flush();
1147         if ($retbytes) {
1148             $cnt += strlen($buffer);
1149         }
1150     }
1151     $status = fclose($handle);
1152     if ($retbytes && $status) {
1153         return $cnt; // return num. bytes delivered like readfile() does.
1154     }
1155     return $status;
1158 /**
1159  * Send requested byterange of file.
1160  */
1161 function byteserving_send_file($handle, $mimetype, $ranges, $filesize) {
1162     $chunksize = 1*(1024*1024); // 1MB chunks - must be less than 2MB!
1163     if ($handle === false) {
1164         die;
1165     }
1166     if (count($ranges) == 1) { //only one range requested
1167         $length = $ranges[0][2] - $ranges[0][1] + 1;
1168         @header('HTTP/1.1 206 Partial content');
1169         @header('Content-Length: '.$length);
1170         @header('Content-Range: bytes '.$ranges[0][1].'-'.$ranges[0][2].'/'.$filesize);
1171         @header('Content-Type: '.$mimetype);
1172         while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
1173         $buffer = '';
1174         fseek($handle, $ranges[0][1]);
1175         while (!feof($handle) && $length > 0) {
1176             @set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
1177             $buffer = fread($handle, ($chunksize < $length ? $chunksize : $length));
1178             echo $buffer;
1179             flush();
1180             $length -= strlen($buffer);
1181         }
1182         fclose($handle);
1183         die;
1184     } else { // multiple ranges requested - not tested much
1185         $totallength = 0;
1186         foreach($ranges as $range) {
1187             $totallength += strlen($range[0]) + $range[2] - $range[1] + 1;
1188         }
1189         $totallength += strlen("\r\n--".BYTESERVING_BOUNDARY."--\r\n");
1190         @header('HTTP/1.1 206 Partial content');
1191         @header('Content-Length: '.$totallength);
1192         @header('Content-Type: multipart/byteranges; boundary='.BYTESERVING_BOUNDARY);
1193         //TODO: check if "multipart/x-byteranges" is more compatible with current readers/browsers/servers
1194         while (@ob_end_flush()); //flush the buffers - save memory and disable sid rewrite
1195         foreach($ranges as $range) {
1196             $length = $range[2] - $range[1] + 1;
1197             echo $range[0];
1198             $buffer = '';
1199             fseek($handle, $range[1]);
1200             while (!feof($handle) && $length > 0) {
1201                 @set_time_limit(60*60); //reset time limit to 60 min - should be enough for 1 MB chunk
1202                 $buffer = fread($handle, ($chunksize < $length ? $chunksize : $length));
1203                 echo $buffer;
1204                 flush();
1205                 $length -= strlen($buffer);
1206             }
1207         }
1208         echo "\r\n--".BYTESERVING_BOUNDARY."--\r\n";
1209         fclose($handle);
1210         die;
1211     }
1214 /**
1215  * add includes (js and css) into uploaded files
1216  * before returning them, useful for themes and utf.js includes
1217  * @param string text - text to search and replace
1218  * @return string - text with added head includes
1219  */
1220 function file_modify_html_header($text) {
1221     // first look for <head> tag
1222     global $CFG;
1224     $stylesheetshtml = '';
1225     foreach ($CFG->stylesheets as $stylesheet) {
1226         $stylesheetshtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
1227     }
1229     $filters = explode(",", $CFG->textfilters);
1230     if (in_array('filter/mediaplugin', $filters)) {
1231         // this script is needed by most media filter plugins.
1232         $ufo = "\n".'<script type="text/javascript" src="'.$CFG->wwwroot.'/lib/ufo.js"></script>'."\n";
1233     } else {
1234         $ufo = '';
1235     }
1237     preg_match('/\<head\>|\<HEAD\>/', $text, $matches);
1238     if ($matches) {
1239         $replacement = '<head>'.$ufo.$stylesheetshtml;
1240         $text = preg_replace('/\<head\>|\<HEAD\>/', $replacement, $text, 1);
1241         return $text;
1242     }
1244     // if not, look for <html> tag, and stick <head> right after
1245     preg_match('/\<html\>|\<HTML\>/', $text, $matches);
1246     if ($matches) {
1247         // replace <html> tag with <html><head>includes</head>
1248         $replacement = '<html>'."\n".'<head>'.$ufo.$stylesheetshtml.'</head>';
1249         $text = preg_replace('/\<html\>|\<HTML\>/', $replacement, $text, 1);
1250         return $text;
1251     }
1253     // if not, look for <body> tag, and stick <head> before body
1254     preg_match('/\<body\>|\<BODY\>/', $text, $matches);
1255     if ($matches) {
1256         $replacement = '<head>'.$ufo.$stylesheetshtml.'</head>'."\n".'<body>';
1257         $text = preg_replace('/\<body\>|\<BODY\>/', $replacement, $text, 1);
1258         return $text;
1259     }
1261     // if not, just stick a <head> tag at the beginning
1262     $text = '<head>'.$ufo.$stylesheetshtml.'</head>'."\n".$text;
1263     return $text;
1266 /**
1267  * RESTful cURL class
1268  *
1269  * This is a wrapper class for curl, it is quite easy to use:
1270  *
1271  * $c = new curl;
1272  * // enable cache
1273  * $c = new curl(array('cache'=>true));
1274  * // enable cookie
1275  * $c = new curl(array('cookie'=>true));
1276  * // enable proxy
1277  * $c = new curl(array('proxy'=>true));
1278  *
1279  * // HTTP GET Method
1280  * $html = $c->get('http://example.com');
1281  * // HTTP POST Method
1282  * $html = $c->post('http://example.com/', array('q'=>'words', 'name'=>'moodle'));
1283  * // HTTP PUT Method
1284  * $html = $c->put('http://example.com/', array('file'=>'/var/www/test.txt');
1285  *
1286  * @author Dongsheng Cai <dongsheng@cvs.moodle.org>
1287  * @version 0.4 dev
1288  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
1289  */
1291 class curl {
1292     public  $cache    = false;
1293     public  $proxy    = false;
1294     public  $version  = '0.4 dev';
1295     public  $response = array();
1296     public  $header   = array();
1297     public  $info;
1298     public  $error;
1300     private $options;
1301     private $proxy_host = '';
1302     private $proxy_auth = '';
1303     private $proxy_type = '';
1304     private $debug    = false;
1305     private $cookie   = false;
1307     public function __construct($options = array()){
1308         global $CFG;
1309         if (!function_exists('curl_init')) {
1310             $this->error = 'cURL module must be enabled!';
1311             trigger_error($this->error, E_USER_ERROR);
1312             return false;
1313         }
1314         // the options of curl should be init here.
1315         $this->resetopt();
1316         if (!empty($options['debug'])) {
1317             $this->debug = true;
1318         }
1319         if(!empty($options['cookie'])) {
1320             if($options['cookie'] === true) {
1321                 $this->cookie = $CFG->dataroot.'/curl_cookie.txt';
1322             } else {
1323                 $this->cookie = $options['cookie'];
1324             }
1325         }
1326         if (!empty($options['cache'])) {
1327             if (class_exists('curl_cache')) {
1328                 $this->cache = new curl_cache;
1329             }
1330         }
1331         if (!empty($options['proxy'])) {
1332             if (!empty($CFG->proxyhost)) {
1333                 if (empty($CFG->proxyport)) {
1334                     $this->proxy_host = $CFG->proxyhost;
1335                 } else {
1336                     $this->proxy_host = $CFG->proxyhost.':'.$CFG->proxyport;
1337                 }
1338                 if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
1339                     $this->proxy_auth = $CFG->proxyuser.':'.$CFG->proxypassword;
1340                     $this->setopt(array(
1341                                 'proxyauth'=> CURLAUTH_BASIC | CURLAUTH_NTLM,
1342                                 'proxyuserpwd'=>$this->proxy_auth));
1343                 }
1344                 if (!empty($CFG->proxytype)) {
1345                     if ($CFG->proxytype == 'SOCKS5') {
1346                         $this->proxy_type = CURLPROXY_SOCKS5;
1347                     } else {
1348                         $this->proxy_type = CURLPROXY_HTTP;
1349                         $this->setopt(array('httpproxytunnel'=>true));
1350                     }
1351                     $this->setopt(array('proxytype'=>$this->proxy_type));
1352                 }
1353             }
1354             if (!empty($this->proxy_host)) {
1355                 $this->proxy = array('proxy'=>$this->proxy_host);
1356             }
1357         }
1358     }
1359     public function resetopt(){
1360         $this->options = array();
1361         $this->options['CURLOPT_USERAGENT']         = 'MoodleBot/1.0';
1362         // True to include the header in the output
1363         $this->options['CURLOPT_HEADER']            = 0;
1364         // True to Exclude the body from the output
1365         $this->options['CURLOPT_NOBODY']            = 0;
1366         // TRUE to follow any "Location: " header that the server
1367         // sends as part of the HTTP header (note this is recursive,
1368         // PHP will follow as many "Location: " headers that it is sent,
1369         // unless CURLOPT_MAXREDIRS is set).
1370         $this->options['CURLOPT_FOLLOWLOCATION']    = 1;
1371         $this->options['CURLOPT_MAXREDIRS']         = 10;
1372         $this->options['CURLOPT_ENCODING']          = '';
1373         // TRUE to return the transfer as a string of the return
1374         // value of curl_exec() instead of outputting it out directly.
1375         $this->options['CURLOPT_RETURNTRANSFER']    = 1;
1376         $this->options['CURLOPT_BINARYTRANSFER']    = 0;
1377         $this->options['CURLOPT_SSL_VERIFYPEER']    = 0;
1378         $this->options['CURLOPT_SSL_VERIFYHOST']    = 2;
1379         $this->options['CURLOPT_CONNECTTIMEOUT']    = 30;
1380     }
1382     /**
1383      * Reset Cookie
1384      *
1385      * @param array $options If array is null, this function will
1386      * reset the options to default value.
1387      *
1388      */
1389     public function resetcookie() {
1390         if (!empty($this->cookie)) {
1391             if (is_file($this->cookie)) {
1392                 $fp = fopen($this->cookie, 'w');
1393                 if (!empty($fp)) {
1394                     fwrite($fp, '');
1395                     fclose($fp);
1396                 }
1397             }
1398         }
1399     }
1401     /**
1402      * Set curl options
1403      *
1404      * @param array $options If array is null, this function will
1405      * reset the options to default value.
1406      *
1407      */
1408     public function setopt($options = array()) {
1409         if (is_array($options)) {
1410             foreach($options as $name => $val){
1411                 if (stripos($name, 'CURLOPT_') === false) {
1412                     $name = strtoupper('CURLOPT_'.$name);
1413                 }
1414                 $this->options[$name] = $val;
1415             }
1416         }
1417     }
1418     /**
1419      * Reset http method
1420      *
1421      */
1422     public function cleanopt(){
1423         unset($this->options['CURLOPT_HTTPGET']);
1424         unset($this->options['CURLOPT_POST']);
1425         unset($this->options['CURLOPT_POSTFIELDS']);
1426         unset($this->options['CURLOPT_PUT']);
1427         unset($this->options['CURLOPT_INFILE']);
1428         unset($this->options['CURLOPT_INFILESIZE']);
1429         unset($this->options['CURLOPT_CUSTOMREQUEST']);
1430     }
1432     /**
1433      * Set HTTP Request Header
1434      *
1435      * @param array $headers
1436      *
1437      */
1438     public function setHeader($header) {
1439         if (is_array($header)){
1440             foreach ($header as $v) {
1441                 $this->setHeader($v);
1442             }
1443         } else {
1444             $this->header[] = $header;
1445         }
1446     }
1447     /**
1448      * Set HTTP Response Header
1449      *
1450      */
1451     public function getResponse(){
1452         return $this->response;
1453     }
1454     /**
1455      * private callback function
1456      * Formatting HTTP Response Header
1457      *
1458      */
1459     private function formatHeader($ch, $header)
1460     {
1461         $this->count++;
1462         if (strlen($header) > 2) {
1463             list($key, $value) = explode(" ", rtrim($header, "\r\n"), 2);
1464             $key = rtrim($key, ':');
1465             if (!empty($this->response[$key])) {
1466                 if (is_array($this->response[$key])){
1467                     $this->response[$key][] = $value;
1468                 } else {
1469                     $tmp = $this->response[$key];
1470                     $this->response[$key] = array();
1471                     $this->response[$key][] = $tmp;
1472                     $this->response[$key][] = $value;
1474                 }
1475             } else {
1476                 $this->response[$key] = $value;
1477             }
1478         }
1479         return strlen($header);
1480     }
1482     /**
1483      * Set options for individual curl instance
1484      */
1485     private function apply_opt($curl, $options) {
1486         // Clean up
1487         $this->cleanopt();
1488         // set cookie
1489         if (!empty($this->cookie) || !empty($options['cookie'])) {
1490             $this->setopt(array('cookiejar'=>$this->cookie,
1491                             'cookiefile'=>$this->cookie
1492                              ));
1493         }
1495         // set proxy
1496         if (!empty($this->proxy) || !empty($options['proxy'])) {
1497             $this->setopt($this->proxy);
1498         }
1499         $this->setopt($options);
1500         // reset before set options
1501         curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this,'formatHeader'));
1502         // set headers
1503         if (empty($this->header)){
1504             $this->setHeader(array(
1505                 'User-Agent: MoodleBot/1.0',
1506                 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7',
1507                 'Connection: keep-alive'
1508                 ));
1509         }
1510         curl_setopt($curl, CURLOPT_HTTPHEADER, $this->header);
1512         if ($this->debug){
1513             echo '<h1>Options</h1>';
1514             var_dump($this->options);
1515             echo '<h1>Header</h1>';
1516             var_dump($this->header);
1517         }
1519         // set options
1520         foreach($this->options as $name => $val) {
1521             if (is_string($name)) {
1522                 $name = constant(strtoupper($name));
1523             }
1524             curl_setopt($curl, $name, $val);
1525         }
1526         return $curl;
1527     }
1528     /*
1529      * Download multiple files in parallel
1530      * $c = new curl;
1531      * $c->download(array(
1532      *              array('url'=>'http://localhost/', 'file'=>fopen('a', 'wb')),
1533      *              array('url'=>'http://localhost/20/', 'file'=>fopen('b', 'wb'))
1534      *              ));
1535      */
1536     public function download($requests, $options = array()) {
1537         $options['CURLOPT_BINARYTRANSFER'] = 1;
1538         $options['RETURNTRANSFER'] = false;
1539         return $this->multi($requests, $options);
1540     }
1541     /*
1542      * Mulit HTTP Requests
1543      * This function could run multi-requests in parallel.
1544      */
1545     protected function multi($requests, $options = array()) {
1546         $count   = count($requests);
1547         $handles = array();
1548         $results = array();
1549         $main    = curl_multi_init();
1550         for ($i = 0; $i < $count; $i++) {
1551             $url = $requests[$i];
1552             foreach($url as $n=>$v){
1553                 $options[$n] = $url[$n];
1554             }
1555             $handles[$i] = curl_init($url['url']);
1556             $this->apply_opt($handles[$i], $options);
1557             curl_multi_add_handle($main, $handles[$i]);
1558         }
1559         $running = 0;
1560         do {
1561             curl_multi_exec($main, $running);
1562         } while($running > 0);
1563         for ($i = 0; $i < $count; $i++) {
1564             if (!empty($optins['CURLOPT_RETURNTRANSFER'])) {
1565                 $results[] = true;
1566             } else {
1567                 $results[] = curl_multi_getcontent($handles[$i]);
1568             }
1569             curl_multi_remove_handle($main, $handles[$i]);
1570         }
1571         curl_multi_close($main);
1572         return $results;
1573     }
1574     /**
1575      * Single HTTP Request
1576      */
1577     protected function request($url, $options = array()){
1578         // create curl instance
1579         $curl = curl_init($url);
1580         $options['url'] = $url;
1581         $this->apply_opt($curl, $options);
1582         if ($this->cache && $ret = $this->cache->get($this->options)) {
1583             return $ret;
1584         } else {
1585             $ret = curl_exec($curl);
1586             if ($this->cache) {
1587                 $this->cache->set($this->options, $ret);
1588             }
1589         }
1591         $this->info  = curl_getinfo($curl);
1592         $this->error = curl_error($curl);
1594         if ($this->debug){
1595             echo '<h1>Return Data</h1>';
1596             var_dump($ret);
1597             echo '<h1>Info</h1>';
1598             var_dump($this->info);
1599             echo '<h1>Error</h1>';
1600             var_dump($this->error);
1601         }
1603         curl_close($curl);
1605         if (empty($this->error)){
1606             return $ret;
1607         } else {
1608             throw new moodle_exception($this->error, 'curl');
1609         }
1610     }
1612     /**
1613      * HTTP HEAD method
1614      */
1615     public function head($url, $options = array()){
1616         $options['CURLOPT_HTTPGET'] = 0;
1617         $options['CURLOPT_HEADER']  = 1;
1618         $options['CURLOPT_NOBODY']  = 1;
1619         return $this->request($url, $options);
1620     }
1622     /**
1623      * HTTP POST method
1624      */
1625     public function post($url, $params = array(), $options = array()){
1626         $options['CURLOPT_POST']       = 1;
1627         $options['CURLOPT_POSTFIELDS'] = $params;
1628         return $this->request($url, $options);
1629     }
1631     /**
1632      * HTTP GET method
1633      */
1634     public function get($url, $params = array(), $options = array()){
1635         $options['CURLOPT_HTTPGET'] = 1;
1637         if (!empty($params)){
1638             $url .= (stripos($url, '?') !== false) ? '&' : '?';
1639             $url .= http_build_query($params, '', '&');
1640         }
1641         return $this->request($url, $options);
1642     }
1644     /**
1645      * HTTP PUT method
1646      */
1647     public function put($url, $params = array(), $options = array()){
1648         $file = $params['file'];
1649         if (!is_file($file)){
1650             return null;
1651         }
1652         $fp   = fopen($file, 'r');
1653         $size = filesize($file);
1654         $options['CURLOPT_PUT']        = 1;
1655         $options['CURLOPT_INFILESIZE'] = $size;
1656         $options['CURLOPT_INFILE']     = $fp;
1657         if (!isset($this->options['CURLOPT_USERPWD'])){
1658             $this->setopt(array('CURLOPT_USERPWD'=>'anonymous: noreply@moodle.org'));
1659         }
1660         $ret = $this->request($url, $options);
1661         fclose($fp);
1662         return $ret;
1663     }
1665     /**
1666      * HTTP DELETE method
1667      */
1668     public function delete($url, $param = array(), $options = array()){
1669         $options['CURLOPT_CUSTOMREQUEST'] = 'DELETE';
1670         if (!isset($options['CURLOPT_USERPWD'])) {
1671             $options['CURLOPT_USERPWD'] = 'anonymous: noreply@moodle.org';
1672         }
1673         $ret = $this->request($url, $options);
1674         return $ret;
1675     }
1676     /**
1677      * HTTP TRACE method
1678      */
1679     public function trace($url, $options = array()){
1680         $options['CURLOPT_CUSTOMREQUEST'] = 'TRACE';
1681         $ret = $this->request($url, $options);
1682         return $ret;
1683     }
1684     /**
1685      * HTTP OPTIONS method
1686      */
1687     public function options($url, $options = array()){
1688         $options['CURLOPT_CUSTOMREQUEST'] = 'OPTIONS';
1689         $ret = $this->request($url, $options);
1690         return $ret;
1691     }
1694 /**
1695  * This class is used by cURL class, use case:
1696  *
1697  * $CFG->repository_cache_expire = 120;
1698  * $c = new curl(array('cache'=>true));
1699  * $ret = $c->get('http://www.google.com');
1700  *
1701  */
1702 class curl_cache {
1703     public $dir = '';
1704     function __construct(){
1705         global $CFG;
1706         if (!file_exists($CFG->dataroot.'/repository/cache')) {
1707             mkdir($CFG->dataroot.'/repository/cache/', 0777, true);
1708         }
1709         if(is_dir($CFG->dataroot.'/repository/cache')) {
1710             $this->dir = $CFG->dataroot.'/repository/cache/';
1711         }
1712     }
1713     public function get($param){
1714         global $CFG;
1715         $filename = md5(serialize($param));
1716         if(file_exists($this->dir.$filename)) {
1717             $lasttime = filemtime($this->dir.$filename);
1718             if(time()-$lasttime > $CFG->repository_cache_expire) {
1719                 return false;
1720             } else {
1721                 $fp = fopen($this->dir.$filename, 'r');
1722                 $size = filesize($this->dir.$filename);
1723                 $content = fread($fp, $size);
1724                 return unserialize($content);
1725             }
1726         }
1727         return false;
1728     }
1729     public function set($param, $val){
1730         $filename = md5(serialize($param));
1731         $fp = fopen($this->dir.$filename, 'w');
1732         fwrite($fp, serialize($val));
1733         fclose($fp);
1734     }
1735     public function cleanup($expire){
1736         if($dir = opendir($this->dir)){
1737             while (false !== ($file = readdir($dir))) {
1738                 if(!is_dir($file) && $file != '.' && $file != '..') {
1739                     $lasttime = @filemtime($this->dir.$file);
1740                     if(time() - $lasttime > $expire){
1741                         @unlink($this->dir.$file);
1742                     }
1743                 }
1744             }
1745         }
1746     }