Issues (1839)

html/inc/cache.inc (20 issues)

1
<?php
2
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2008 University of California
5
//
6
// BOINC is free software; you can redistribute it and/or modify it
7
// under the terms of the GNU Lesser General Public License
8
// as published by the Free Software Foundation,
9
// either version 3 of the License, or (at your option) any later version.
10
//
11
// BOINC is distributed in the hope that it will be useful,
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
// See the GNU Lesser General Public License for more details.
15
//
16
// You should have received a copy of the GNU Lesser General Public License
17
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
18
19
// mechanism for caching commonly-accessed pages
20
21
require_once("../project/cache_parameters.inc");
22
23
// If we can't see request headers, don't do caching
24
//
25
$no_cache = false;
26
if (!function_exists("apache_request_headers")) {
27
    $no_cache = true;
28
}
29
30
function make_cache_dirs() {
31
    if (!@filemtime("../cache")) {
32
        mkdir("../cache", 0770);
33
        chmod("../cache", 0770);
34
    }
35
    for ($i=0;$i<256;$i++) {
0 ignored issues
show
Expected 1 space after first semicolon of FOR loop; 0 found
Loading history...
Expected 1 space after second semicolon of FOR loop; 0 found
Loading history...
36
        $j=sprintf("%02x",$i);
37
        if (!@filemtime("../cache/$j")) {
38
            mkdir("../cache/$j", 0770);
39
            chmod("../cache/$j", 0770);
40
        }
41
    }
42
}
43
44
function get_path($params, $phpfile=null) {
45
    if (!@filemtime("../cache/00")) make_cache_dirs();
46
    if ($phpfile) {
47
        $z = $phpfile;
48
    } else {
49
        $y = pathinfo($_SERVER["PHP_SELF"]);
50
        $z = $y["basename"];
51
    }
52
53
    // add a layer of subdirectories for reducing file lookup time
54
    $sz = substr(md5($z."_".urlencode($params)),1,2);
55
    $path = "../cache/".$sz."/".$z;
56
    if ($params) {
57
        $path = $path."_".urlencode($params);
58
    }
59
    return $path;
60
}
61
62
function disk_usage($dir) {
63
    $usage=0;
64
    if ($handle=@opendir($dir)) {
65
        while ($file=readdir($handle)) {
66
            if (($file != ".") && ($file != "..")) {
67
                if (@is_dir($dir."/".$file)) {
68
                    $usage+=disk_usage($dir."/".$file);
69
                } else {
70
                    $usage+=@filesize($dir."/".$file);
71
                }
72
            }
73
        }
74
        @closedir($handle);
0 ignored issues
show
Are you sure the usage of closedir($handle) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for closedir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

74
        /** @scrutinizer ignore-unhandled */ @closedir($handle);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
75
    }
76
    return $usage;
77
}
78
79
function clean_cache($max_age, $dir) {
80
    $start_dir = getcwd();
81
    if (!chdir($dir)) {
82
        return;
83
    }
84
    if ($handle=@opendir(".")) {
85
        while ($file=readdir($handle)) {
86
            if ($file == ".") continue;
87
            if ($file == "..") continue;
88
89
            // don't let hackers trick us into deleting other files!
90
            if (strstr($file, "..")) {
91
                continue;
92
            }
93
            if (@is_dir($file)) {
94
                clean_cache($max_age, $file);
95
            } else {
96
                if ((time()-@filemtime($file))>$max_age) {
97
                    //echo "unlinking ".getcwd()."/$file\n";
98
                    @unlink($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

98
                    /** @scrutinizer ignore-unhandled */ @unlink($file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
99
                }
100
            }
101
        }
102
       @closedir($handle);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for closedir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

102
       /** @scrutinizer ignore-unhandled */ @closedir($handle);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
Are you sure the usage of closedir($handle) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
103
    }
104
    chdir($start_dir);
105
}
106
107
108
// check cache size every once in a while, purge if too big
109
//
110
function cache_check_diskspace(){
111
    if ((rand() % CACHE_SIZE_CHECK_FREQ)) return;
112
    if (disk_usage("../cache") < MAX_CACHE_USAGE) return;
113
    $x = max(TEAM_PAGE_TTL, USER_PAGE_TTL, USER_HOST_TTL,
114
        USER_PROFILE_TTL, TOP_PAGES_TTL, INDEX_PAGE_TTL
115
    );
116
    clean_cache($x, "../cache");
117
}
118
119
function cache_need_to_regenerate($path, $max_age){
120
    $regenerate = false;
121
    $request = apache_request_headers();
122
123
    clearstatcache();
124
    $lastmodified = @filemtime($path);
125
    if ($lastmodified) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lastmodified of type false|integer is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
126
        // See if cached copy is too old.
127
        // If so regenerate,
128
        // and touch the cached copy so other processes
129
        // don't regenerate at the same time
130
        //
131
        if ($lastmodified<time()-$max_age) {
132
            $regenerate = true;
133
            @touch($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for touch(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

133
            /** @scrutinizer ignore-unhandled */ @touch($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
134
        }
135
    } else {
136
        $regenerate = true;
137
    }
138
    return $regenerate;
139
}
140
141
// Returns cached data or false if nothing was found
142
function get_cached_data($max_age, $params=""){
143
    global $no_cache;
144
145
    if ($no_cache) return;
146
147
    $path = get_path($params);
148
    if ($max_age) {
149
        if (defined('MEMCACHE_SERVERS')) {
150
            $cache = BoincMemcache::get()->get($path);
151
            if ($cache['content']) {
152
                return $cache['content'];
153
            } else {
154
                return $cache;
155
            }
156
        } else {
157
            cache_check_diskspace();
158
            $regenerate=cache_need_to_regenerate($path, $max_age);
159
            if (!$regenerate) {
160
                return file_get_contents($path);
161
            }
162
        }
163
    }
164
    return false; //No data was cached, just return
165
}
166
167
// DEPRECATED
168
function start_cache($max_age, $params=""){
169
    global $no_cache, $caching, $memcache;
170
171
    if ($no_cache) return;
172
    $caching = true;
173
174
    if ($max_age) {
175
        $path = get_path($params);
176
        $cache = null;
177
        if (defined('MEMCACHE_SERVERS')) {
178
            $cache = BoincMemcache::get()->get($path);
179
            if ($cache) {
180
                $regenerate = false;
181
                $lastmodified = abs($cache->timestamp);
182
            } else {
183
                $regenerate = true;
184
            }
185
        } else {
186
            $lastmodified = @filemtime($path);
187
            cache_check_diskspace(); //Check free disk space once in a while
188
            $regenerate = cache_need_to_regenerate($path, $max_age);
189
        }
190
        //Is the stored version too old, do we need to regenerate it?
191
        if ($regenerate){
192
            // If cached version is too old (or non-existent)
193
            // generate the page and write to cache
194
            //
195
            ob_start();
196
            ob_implicit_flush(0);
197
            Header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
0 ignored issues
show
Calls to inbuilt PHP functions must be lowercase; expected "header" but found "Header"
Loading history...
198
            Header("Expires: " . gmdate("D, d M Y H:i:s",time()+$max_age) . " GMT");
0 ignored issues
show
Calls to inbuilt PHP functions must be lowercase; expected "header" but found "Header"
Loading history...
199
            Header("Cache-Control: public, max-age=" . $max_age);
0 ignored issues
show
Calls to inbuilt PHP functions must be lowercase; expected "header" but found "Header"
Loading history...
200
201
            // allow the calling page to see cache period
202
            //
203
            global $cached_max_age;
204
            $cached_max_age = $max_age;
205
        } else {
206
            // Otherwise serve the cached version and exit
207
            //
208
            if (strstr($params, "format=xml")) {
209
                header('Content-type: text/xml');
210
            }
211
            Header("Last-Modified: " . gmdate("D, d M Y H:i:s",$lastmodified) . " GMT");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $lastmodified does not seem to be defined for all execution paths leading up to this point.
Loading history...
Calls to inbuilt PHP functions must be lowercase; expected "header" but found "Header"
Loading history...
212
            Header("Expires: " . gmdate("D, d M Y H:i:s",$lastmodified+$max_age) . " GMT");
0 ignored issues
show
Calls to inbuilt PHP functions must be lowercase; expected "header" but found "Header"
Loading history...
213
            Header("Cache-Control: public, max-age=" . $max_age );
0 ignored issues
show
Calls to inbuilt PHP functions must be lowercase; expected "header" but found "Header"
Loading history...
214
            if ($cache && $cache->content) {
215
                echo $cache->content;
216
                exit;
217
            }
218
            if (!@readfile($path)) {
219
                //echo "can't read $path; lastmod $lastmodified\n";
220
                @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

220
                /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
221
                //Proceed to regenerate content
222
            } else {
223
                exit;
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
224
            }
225
        }
226
    }
227
}
228
229
// write output buffer both to client and to cache
230
// DEPRECATED
231
function end_cache($max_age,$params=""){
232
    global $no_cache;
233
    if ($no_cache) return;
234
235
    // for the benefit of hackers
236
    if (strstr($params, "..")) {
237
        return;
238
    }
239
    if ($max_age) {
240
        $path = get_path($params);
241
242
        if (defined('MEMCACHE_SERVERS')) {
243
            $cache = array('content' => ob_get_contents(), 'timestamp' => time());
244
            ob_end_flush();
245
            $cache = BoincMemcache::get()->set($path, $cache, $max_age);
246
        } else {
247
            $fhandle = fopen($path, "w");
248
            $page = ob_get_contents();
249
            ob_end_flush();
250
            fwrite($fhandle, $page);
251
            fclose($fhandle);
252
        }
253
    }
254
}
255
256
function set_cached_data($max_age, $data, $params=""){
257
    // for the benefit of hackers
258
    if (strstr($params, "..")) {
259
        return "bad params";
260
    }
261
    $path = get_path($params);
262
    if (defined('MEMCACHE_SERVERS')) {
263
        $cache = array('content' => $data, 'timestamp' => time());
264
        BoincMemcache::get()->set($path, $cache, $max_age);
265
    } else {
266
        $fhandle = @fopen($path, "w");
267
        if (!$fhandle) {
0 ignored issues
show
$fhandle is of type false|resource, thus it always evaluated to false.
Loading history...
268
            return "can't open $path";
269
        }
270
        fwrite($fhandle, $data);
271
        fclose($fhandle);
272
    }
273
    return "";
274
}
275
276
function clear_cache_entry($phpfile, $params) {
277
    if (strstr($phpfile, "..")) {
278
        return;
279
    }
280
    if (strstr($params, "..")) {
281
        return;
282
    }
283
    $path = get_path($params, $phpfile);
284
    @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

284
    /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
285
}
286
287
// Memcached class
288
class BoincMemcache {
289
    static $instance;
290
291
    static function get() {
292
        self::$instance = new Memcached;
293
        if (defined('MEMCACHE_PREFIX')) {
294
            self::$instance->setOption(Memcached::OPT_PREFIX_KEY, MEMCACHE_PREFIX);
295
        }
296
        $servers = explode('|', MEMCACHE_SERVERS);
297
        foreach($servers as &$server) {
298
            list($ip, $port) = explode(':', $server);
299
            if (!$port) { $port = 11211; }
300
            $server = array($ip, $port);
301
        }
302
        self::$instance->addServers($servers);
303
        return self::$instance;
304
    }
305
}
306
307
?>
308