util.inc 27.5 KB
Newer Older
Karl Chen's avatar
Karl Chen committed
1
<?php
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC.  If not, see <http://www.gnu.org/licenses/>.
Karl Chen's avatar
Karl Chen committed
18

19
20
// Utility functions for BOINC web pages

21
22
23
24
error_reporting(E_ALL);
ini_set('display_errors', true);
ini_set('display_startup_errors', true);

25
require_once("../inc/util_basic.inc");
Karl Chen's avatar
Karl Chen committed
26
27
require_once("../project/project.inc");
require_once("../inc/countries.inc");
David Anderson's avatar
David Anderson committed
28
require_once("../inc/db.inc");
29
require_once("../inc/boinc_db.inc");
30
require_once("../inc/translation.inc");
31
require_once("../inc/profile.inc");
David Anderson's avatar
David Anderson committed
32

33
34
35
36
37
38
// don't allow /... at the end of URL

if (array_key_exists("PATH_INFO", $_SERVER)) {
    die("bad URL");
}

39
40
41
// define TIMEZONE in project.inc
//
if (defined('TIMEZONE')) {
42
    date_default_timezone_set(TIMEZONE);
43
} else {
44
    date_default_timezone_set('UTC');
45
46
}

David Anderson's avatar
web:    
David Anderson committed
47
48
49
50
51
52
53
54
55
if (!defined('DISABLE_PROFILES')) {
    define('DISABLE_PROFILES', false);
}
if (!defined('DISABLE_FORUMS')) {
    define('DISABLE_FORUMS', false);
}
if (!defined('DISABLE_TEAMS')) {
    define('DISABLE_TEAMS', false);
}
56
57
58
if (!defined('DISABLE_BADGES')) {
    define('DISABLE_BADGES', false);
}
59
60
61
62
63
64
65
66
67
if (!defined('BADGE_HEIGHT_SMALL')) {
    define('BADGE_HEIGHT_SMALL', 20);
}
if (!defined('BADGE_HEIGHT_MEDIUM')) {
    define('BADGE_HEIGHT_MEDIUM', 24);
}
if (!defined('BADGE_HEIGHT_LARGE')) {
    define('BADGE_HEIGHT_LARGE', 56);
}
68
69
if (!defined('LDAP_HOST')) {
    define('LDAP_HOST', null);
70
}
David Anderson's avatar
web:    
David Anderson committed
71

72
$caching = false;
73
    // if set, we're writing to a file rather than to client
74
$did_page_head = false;
David Anderson's avatar
David Anderson committed
75

76
77
78
79
define('KILO', 1024);
define('MEGA', 1024*KILO);
define('GIGA', 1024*MEGA);

80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// return true if this page is HTTPS
//
function is_https() {
    return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'];
}

function secure_url_base() {
    if (defined('SECURE_URL_BASE')) return SECURE_URL_BASE;
    return URL_BASE;
}

function url_base() {
    return is_https()?secure_url_base():URL_BASE;
}

95
function send_cookie($name, $value, $permanent, $ops=false) {
96
97
    // the following allows independent login for projects on the same server
    //
David Anderson's avatar
David Anderson committed
98
99
    $master_url = parse_config(get_config(), "<master_url>");
    $url = parse_url($master_url);
David Anderson's avatar
David Anderson committed
100
    $path = $url['path'];
101
102
103
104
    if ($ops) {
        $path = substr($path, 0, -1);
        $path .= "_ops/";
    }
105
106
107
    $expire = $permanent?time()+3600*24*365:0;
    setcookie($name, $value, $expire, $path);
}
108

109
function clear_cookie($name, $ops=false) {
110
111
112
    $master_url = parse_config(get_config(), "<master_url>");
    $url = parse_url($master_url);
    $path = $url['path'];
113
114
115
116
    if ($ops) {
        $path = substr($path, 0, -1);
        $path .= "_ops/";
    }
117
    setcookie($name, '', time()-3600, $path);
Karl Chen's avatar
Karl Chen committed
118
119
}

David Anderson's avatar
David Anderson committed
120
$g_logged_in_user = null;
121
$got_logged_in_user = false;
David Anderson's avatar
David Anderson committed
122

David Anderson's avatar
David Anderson committed
123
function get_logged_in_user($must_be_logged_in=true) {
124
    global $g_logged_in_user, $got_logged_in_user;
125
    if ($got_logged_in_user) return $g_logged_in_user;
126

127
    check_web_stopped();
David Anderson's avatar
   
David Anderson committed
128

129
130
131
    $authenticator = null;
    if (isset($_COOKIE['auth'])) $authenticator = $_COOKIE['auth'];

132
    $authenticator = BoincDb::escape_string($authenticator);
133
    if ($authenticator) {
134
        $g_logged_in_user = BoincUser::lookup("authenticator='$authenticator'");
135
    }
136
    if ($must_be_logged_in && !$g_logged_in_user) {
David Anderson's avatar
David Anderson committed
137
138
139
140
141
142
143
        $next_url = '';
        if (array_key_exists('REQUEST_URI', $_SERVER)) {
            $next_url = $_SERVER['REQUEST_URI'];
            $n = strrpos($next_url, "/");
            if ($n) {
                $next_url = substr($next_url, $n+1);
            }
144
        }
David Anderson's avatar
David Anderson committed
145
        $next_url = urlencode($next_url);
146
        Header("Location: login_form.php?next_url=$next_url");
147
        exit;
Karl Chen's avatar
Karl Chen committed
148
    }
149
    $got_logged_in_user = true;
David Anderson's avatar
David Anderson committed
150
    return $g_logged_in_user;
Karl Chen's avatar
Karl Chen committed
151
152
}

David Anderson's avatar
David Anderson committed
153
function show_login_info($prefix="") {
154
155
    $user = get_logged_in_user(false);
    echo "
156
157
        <table width=\"100%\" cellpadding=0 cellspacing=0>
        <tr><td align=right>
158
    ";
Karl Chen's avatar
Karl Chen committed
159
    if ($user) {
160
        $url_tokens = url_tokens($user->authenticator);
161
        echo "<nobr>$user->name &middot; <a href=".$prefix."logout.php?$url_tokens>".tra("log out")."</a></nobr>";
Karl Chen's avatar
Karl Chen committed
162
    } else {
163
        echo "<a href=".$prefix."login_form.php>".tra("log in")."</a>";
Karl Chen's avatar
Karl Chen committed
164
    }
165
166
167
168
169
    echo "
        </td>
        </tr>
        </table>
    ";
Karl Chen's avatar
Karl Chen committed
170
171
}

172
173
$cache_control_extra="";

174
175
// Page_head() is overridable so that projects that want to integrate BOINC
// with an existing web framework can more easily do so.
176
// To do so, define page_head() in the project include file.
177
//
178
if (!function_exists("page_head")){
David Anderson's avatar
David Anderson committed
179
180
181
function page_head(
    $title, $java_onload=null, $title_plain=null, $prefix="", $head_extra=null
) {
182
    global $caching, $cache_control_extra, $did_page_head;
183

184
    $did_page_head = true;
185
186
    $url_base = url_base();
    $stylesheet = $url_base.STYLESHEET;
David Anderson's avatar
David Anderson committed
187
    $rssname = PROJECT . " RSS 2.0";
188
    $rsslink = $url_base."rss_main.php";
189
    if (defined('STYLESHEET2')) {
190
        $stylesheet2 = $url_base.STYLESHEET2;
191
192
193
194
    } else {
        $stylesheet2 = null;
    }

David Anderson's avatar
David Anderson committed
195

196
    if (!$caching) {
197
        header("Content-type: text/html; charset=utf-8");
198
199
        header ("Expires: Mon, 26 Jul 1997 05:00:00 UTC");    // Date in the past
        header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " UTC"); // always modified
200
        header ("Cache-Control: $cache_control_extra no-cache, must-revalidate, post-check=0, pre-check=0");  // HTTP/1.1
201
202
        header ("Pragma: no-cache");                          // HTTP/1.0
    }
David Anderson's avatar
David Anderson committed
203

204
    echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">";
205
    
David Anderson's avatar
David Anderson committed
206
207
208
209
    echo "<html><head>\n";
    if ($head_extra) {
        echo "\n$head_extra\n";
    }
David Anderson's avatar
David Anderson committed
210
    if (!$title_plain) {
David Anderson's avatar
David Anderson committed
211
        echo "<title>".sanitize_tags($title)."</title>\n";
David Anderson's avatar
David Anderson committed
212
    } else {
David Anderson's avatar
David Anderson committed
213
        echo "<title>".sanitize_tags($title_plain)."</title>\n";
David Anderson's avatar
David Anderson committed
214
    }
215
    echo "<link rel=stylesheet type=\"text/css\" href=\"".$url_base."main.css\" media=\"all\">
David Anderson's avatar
David Anderson committed
216
        <link rel=stylesheet type=\"text/css\" href=\"$stylesheet\">
217
218
219
220
    ";
    if ($stylesheet2) {
        echo "<link rel=stylesheet type=\"text/css\" href=\"$stylesheet2\">\n";
    }
221
222
223
224
    if (defined("SHORTCUT_ICON")) {
        echo '<link rel="icon" type="image/x-icon", href="'.SHORTCUT_ICON.'"/>
';
    }
225
    echo "
226
        <link rel=alternate type=\"application/rss+xml\" title=\"$rssname\" href=\"$rsslink\">
227
228
        </head>
    ";
David Anderson's avatar
David Anderson committed
229
    if ($java_onload){
David Anderson's avatar
David Anderson committed
230
        echo "<body onload=\"".$java_onload."\">";
231
    } else {
David Anderson's avatar
David Anderson committed
232
        echo "<body>";
David Anderson's avatar
David Anderson committed
233
    }
234
    display_cvs_versions();
235

David Anderson's avatar
David Anderson committed
236
    project_banner($title, $prefix);
237
238
239
    switch($title) {    //kludge
    case tra("Log in"):
    case tra("Create an account"):
240
    case tra("Server status page"):
241
242
        break;
    default:
David Anderson's avatar
David Anderson committed
243
        show_login_info($prefix);
244
    }
245

Karl Chen's avatar
Karl Chen committed
246
}
247
}
Karl Chen's avatar
Karl Chen committed
248

David Anderson's avatar
David Anderson committed
249
250
function page_tail_aux($show_return, $show_date, $prefix="") {
    project_footer($show_return, $show_date, $prefix);
251
252
253
    echo "</body>
        </html>
    ";
David Anderson's avatar
David Anderson committed
254
255
256
257
}
function page_tail_main($show_date=false) {
    page_tail_aux(false, $show_date);
}
Karl Chen's avatar
Karl Chen committed
258

259
260
// See the comments for page_head()
//
261
if (!function_exists("page_tail")){
David Anderson's avatar
David Anderson committed
262
263
function page_tail($show_date=false, $prefix="") {
    page_tail_aux(true, $show_date, $prefix);
Karl Chen's avatar
Karl Chen committed
264
}
265
}
Karl Chen's avatar
Karl Chen committed
266

267
268
function display_cvs_versions(){
    global $cvs_version_tracker;
269
    echo "\n<!-- SVN VERSIONS -->\n";
David Anderson's avatar
David Anderson committed
270
271
    for ($i=0;$i<sizeof($cvs_version_tracker);$i++) {
        echo "<!-- ".$cvs_version_tracker[$i]." -->\n";
272
273
274
    }
}

Karl Chen's avatar
Karl Chen committed
275
276
function db_error_page() {
    page_head("Database error");
277
    echo tra("A database error occurred while handling your request; please try again later.");
Karl Chen's avatar
Karl Chen committed
278
279
280
    page_tail();
}

David Anderson's avatar
David Anderson committed
281
function error_page($msg) {
David Anderson's avatar
David Anderson committed
282
283
284
285
    global $generating_xml;
    if ($generating_xml) {
        xml_error(-1, $msg);
    }
286
    page_head(tra("Unable to handle request"));
David Anderson's avatar
David Anderson committed
287
288
289
290
291
    echo $msg;
    page_tail();
    exit();
}

292
293
294
295
// takes argument in second and returns a human formatted time string
// in the form D days + h Hours + m Min + s sec.

function time_diff($x) {
David Anderson's avatar
David Anderson committed
296
297
298
299
300
301
302
    $days    = (int)($x/86400);
    $hours   = (int)(($x-$days*86400)/3600);
    $minutes = (int)(($x-$days*86400-$hours*3600)/60);
    $seconds = (int)($x % 60);

    $datestring = "";
    if ($days) {
303
        $datestring .= "$days ".tra("days")." ";
David Anderson's avatar
David Anderson committed
304
305
    }
    if ($hours || strlen($datestring)) {
306
        $datestring .= "$hours ".tra("hours")." ";
David Anderson's avatar
David Anderson committed
307
308
    }
    if ($minutes || strlen($datestring)) {
309
        $datestring .= "$minutes ".tra("min")." ";
David Anderson's avatar
David Anderson committed
310
311
    }
    if ($seconds) {
312
        $datestring .= "$seconds ".tra("sec")." ";
David Anderson's avatar
David Anderson committed
313
314
315
    }

    return $datestring;
316
317
318
}


Karl Chen's avatar
Karl Chen committed
319
320
function date_str($x) {
    if ($x == 0) return "---";
David Anderson's avatar
David Anderson committed
321
    return gmdate('j M Y', $x);
Karl Chen's avatar
Karl Chen committed
322
323
324
325
}

function time_str($x) {
    if ($x == 0) return "---";
326
    return gmdate('j M Y, G:i:s', $x) . " UTC";
Karl Chen's avatar
Karl Chen committed
327
328
}

David Anderson's avatar
David Anderson committed
329
330
function local_time_str($x) {
    if ($x == 0) return "---";
331
    return date('j M Y, H:i T', $x);
David Anderson's avatar
David Anderson committed
332
333
}

Karl Chen's avatar
Karl Chen committed
334
function pretty_time_str($x) {
David Anderson's avatar
David Anderson committed
335
    return time_str($x);
Karl Chen's avatar
Karl Chen committed
336
}
337

338
339
340
// TODO: currently only a few calls use the arguments, styling should only happen through CSS clases so $extra should be dropped in the future
function start_table($extra="width=\"100%\"", $class="table-bordered") {
    echo "<table class=\"table $class\" $extra>";
Karl Chen's avatar
Karl Chen committed
341
342
}

343
344
345
// TODO: currently no call uses the arguments, styling should only happen through CSS clases so $extra could be dropped in the future
function start_table_noborder($extra="width=\"100%\"", $class="noborder") {
    echo "<table class=\"table $class\" $extra>";
Karl Chen's avatar
Karl Chen committed
346
347
348
349
350
351
}

function end_table() {
    echo "</table>\n";
}

352
353
// Table header row with unlimited number of columns

354
355
356
357
358
359
360
function table_header() {
    echo "<tr>\n";
    for ($i = 0; $i < func_num_args(); $i++) {
        if (is_array(func_get_arg($i))) {
            $col = func_get_arg($i);
            echo "<th ".$col[1].">".$col[0]."</th>\n";
        } else {
361
            echo "<th valign=top>".func_get_arg($i)."</th>\n";
362
363
364
365
366
        }
    }
    echo "</tr>\n";
}

367
368
// Table row with unlimited number of columns

369
370
371
372
373
374
375
376
377
378
379
380
381
function table_row() {
    echo "<tr>\n";
    for ($i = 0; $i < func_num_args(); $i++) {
        if (is_array(func_get_arg($i))) {
            $col = func_get_arg($i);
            echo "<td ".$col[1].">".$col[0]."</td>\n";
        } else {
            echo "<td>".func_get_arg($i)."</td>\n";
        }
    }
    echo "</tr>\n";
}

David Anderson's avatar
David Anderson committed
382
function row1($x, $ncols=2, $class="heading") {
383
384
385
386
387
    if ($class == "heading") {
        echo "<tr><th colspan=\"$ncols\">$x</th></tr>\n";
    } else {
        echo "<tr><td class=\"$class\" colspan=\"$ncols\">$x</td></tr>\n";
    }
Karl Chen's avatar
Karl Chen committed
388
389
}

David Anderson's avatar
David Anderson committed
390
function row2($x, $y, $show_error=false) {
Karl Chen's avatar
Karl Chen committed
391
    if ($x=="") $x="<br>";
392
    if ($y==="") $y="<br>";
David Anderson's avatar
David Anderson committed
393
394
395
396
397
398
399
    if ($show_error) {
        $class1 = 'fieldname_error';
        $class2 = 'fieldvalue_error';
    } else {
        $class1 = 'fieldname';
        $class2 = 'fieldvalue';
    }
400
    echo "<tr><td width=\"40%\" class=$class1>$x</td><td class=$class2>$y</td></tr>\n";
Karl Chen's avatar
Karl Chen committed
401
}
David Anderson's avatar
David Anderson committed
402

Karl Chen's avatar
Karl Chen committed
403
function row2_init($x, $y) {
404
    echo "<tr><td class=fieldname width=\"40%\">$x</td><td class=fieldvalue>$y\n";
Karl Chen's avatar
Karl Chen committed
405
406
407
408
409
410
411
412
413
414
}

function row2_plain($x, $y) {
    echo "<tr><td>$x</td><td>$y</td></tr>\n";
}

function rowify($string) {
    echo "<tr><td>$string</td></tr>";
}

David Anderson's avatar
David Anderson committed
415
416
417
418
419
420
function row_array($x, $class=null) {
    if ($class) {
        echo "<tr class='$class'>";
    } else {
        echo "<tr>";
    }
David Anderson's avatar
David Anderson committed
421
    foreach ($x as $h) {
David Anderson's avatar
David Anderson committed
422
        echo "<td>$h</td>";
David Anderson's avatar
David Anderson committed
423
424
425
426
    }
    echo "</tr>\n";
}

David Anderson's avatar
David Anderson committed
427
function row_heading_array($x) {
David Anderson's avatar
David Anderson committed
428
429
    echo "<tr>";
    foreach ($x as $h) {
430
        echo "<th>$h</th>";
David Anderson's avatar
David Anderson committed
431
432
433
434
    }
    echo "</tr>\n";
}

Karl Chen's avatar
Karl Chen committed
435
function random_string() {
David Anderson's avatar
David Anderson committed
436
    return md5(uniqid(rand(), true));
Karl Chen's avatar
Karl Chen committed
437
438
}

David Anderson's avatar
David Anderson committed
439
440
441
function url_tokens($auth) {
    $now = time();
    $ttok = md5((string)$now.$auth);
Rytis Slatkevičius's avatar
Rytis Slatkevičius committed
442
    return "&amp;tnow=$now&amp;ttok=$ttok";
David Anderson's avatar
David Anderson committed
443
444
445
446
447
}

function form_tokens($auth) {
    $now = time();
    $ttok = md5((string)$now.$auth);
Rytis Slatkevičius's avatar
Rytis Slatkevičius committed
448
449
    return "<input type=\"hidden\" name=\"tnow\" value=\"$now\">
        <input type=\"hidden\" name=\"ttok\" value=\"$ttok\">
David Anderson's avatar
David Anderson committed
450
451
452
453
    ";
}

function valid_tokens($auth) {
454
455
    $tnow = get_str('tnow', true);
    $ttok = get_str('ttok', true);
456
457
458
459
460
461
    if (!$tnow) {
        $tnow = $_POST['tnow'];
    }
    if (!$ttok) {
        $ttok = $_POST['ttok'];
    }
David Anderson's avatar
David Anderson committed
462
463
464
465
466
467
468
469
470
471
472
    if (!$tnow) return false;
    if (!$ttok) return false;
    $t = md5((string)$tnow.$auth);
    if ($t != $ttok) return false;
    if (time() > $tnow + 86400) return false;
    return true;
}

function check_tokens($auth) {
    if (valid_tokens($auth)) return;
    error_page(
473
        tra("Link has timed out. Please click Back, refresh the page, and try again.")
David Anderson's avatar
David Anderson committed
474
475
476
    );
}

477
478
479
480
function no_computing() {
    return parse_bool(get_config(), "no_computing");
}

Karl Chen's avatar
Karl Chen committed
481
482
483
// Generates a legal filename from a parameter string.

function get_legal_filename($name) {
484
    return strtr($name, array(','=>'', ' '=>'_'));
Karl Chen's avatar
Karl Chen committed
485
486
487
488
489
490
491
492
493
494
495
}

// Returns a string containing as many words
// (being collections of characters separated by the character $delimiter)
// as possible such that the total string length is <= $chars characters long.
// If $ellipsis is true, then an ellipsis is added to any sentence which
// is cut short.

function sub_sentence($sentence, $delimiter, $max_chars, $ellipsis=false) {
    $words = explode($delimiter, $sentence);
    $total_chars = 0;
496
    $trunc = false;
497
    $result = "";
Karl Chen's avatar
Karl Chen committed
498

499
500
501
502
503
504
505
    foreach ($words as $word) {
        if (strlen($result) + strlen($word) > $max_chars) {
            $trunc = true;
            break;
        }
        if ($result) {
            $result .= " $word";
Karl Chen's avatar
Karl Chen committed
506
        } else {
507
            $result = $word;
Karl Chen's avatar
Karl Chen committed
508
        }
509
    }
Karl Chen's avatar
Karl Chen committed
510

511
512
    if ($ellipsis && $trunc) {
        $result .= "...";
Karl Chen's avatar
Karl Chen committed
513
514
515
516
517
    }

    return $result;
}

518
519
// use this for user RAC and result credit
//
David Anderson's avatar
David Anderson committed
520
function format_credit($x) {
521
522
523
524
525
526
    return number_format($x, 2);
}

// use this when credit is likely to be large, e.g. team RAC
//
function format_credit_large($x) {
527
    return number_format($x, 0);
Karl Chen's avatar
Karl Chen committed
528
529
}

David Anderson's avatar
David Anderson committed
530
531
function host_link($hostid) {
    if ($hostid) {
David Anderson's avatar
David Anderson committed
532
        return "<a href=\"show_host_detail.php?hostid=$hostid\">$hostid</a>";
David Anderson's avatar
David Anderson committed
533
534
535
536
537
    } else {
        return "---";
    }
}

Daniel Hsu's avatar
Daniel Hsu committed
538
539
540
541
542
543
544
545
546
547
548
549
550
function open_output_buffer() {
    ob_start();
    ob_implicit_flush(0);
}

function close_output_buffer($filename) {
    $fh = fopen($filename, "w");
    $page = ob_get_contents();
    ob_end_clean();
    fwrite($fh, $page);
    fclose($fh);
}

David Anderson's avatar
David Anderson committed
551
function html_info() {
552
    return "<br><a href=bbcode.php target=new><small>".tra("Use BBCode tags to format your text")."</small></a>\n";
David Anderson's avatar
David Anderson committed
553
554
}

555
556
557
558
559
560
561
562
// strip slashes if magic quotes in effect
function undo_magic_quotes($x) {
    if (get_magic_quotes_gpc()) {
        return stripslashes($x);
    }
    return $x;
}

563
564
565
566
567
// check for bogus GET args
//
function check_get_args($args) {
    foreach ($_GET as $key => $val) {
        if (!in_array($key, $args)) {
568
569
            Header("Location: extra_arg_$key.html");
            die;
570
571
572
573
        }
    }
}

574
575
// returns null if the arg is optional and missing
//
David Anderson's avatar
David Anderson committed
576
function get_int($name, $optional=false) {
577
578
    $x=null;
    if (isset($_GET[$name])) $x = $_GET[$name];
David Anderson's avatar
David Anderson committed
579
580
    if (!is_numeric($x)) {
        if ($optional) {
581
582
583
584
            if ($x) {
                Header("Location: non_num_arg.html");
                die;
            }
David Anderson's avatar
David Anderson committed
585
586
            return null;
        } else {
David Anderson's avatar
David Anderson committed
587
            Header("Location: missing_arg_$name.html");
588
            die;
David Anderson's avatar
David Anderson committed
589
590
591
592
593
        }
    }
    return (int)$x;
}

594
595
// returns null if the arg is optional and missing
//
596
function post_num($name, $optional=false) {
Bruce Allen's avatar
Bruce Allen committed
597
598
    $x = null;
    if (isset($_POST[$name])) $x = $_POST[$name];
David Anderson's avatar
David Anderson committed
599
600
601
602
    if (!is_numeric($x)) {
        if ($optional) {
            return null;
        } else {
603
            error_page("missing or bad parameter: $name; supplied: ".htmlspecialchars($x));
David Anderson's avatar
David Anderson committed
604
605
        }
    }
606
607
608
    return (double)$x;
}

609
610
// returns null if the arg is optional and missing
//
611
612
function post_int($name, $optional=false) {
    $x = post_num($name, $optional);
613
    if (is_null($x)) return null;
614
615
616
617
618
    $y = (int)$x;
    if ($x != $y) {
        error_page("param $name must be an integer");
    }
    return $y;
David Anderson's avatar
David Anderson committed
619
620
621
}

function get_str($name, $optional=false) {
622
623
624
    if (isset($_GET[$name])) {
        $x = $_GET[$name];
    } else {
625
626
627
628
        if (!$optional) {
            error_page("missing or bad parameter: $name");
        }
        $x = null;
David Anderson's avatar
David Anderson committed
629
    }
630
    return undo_magic_quotes($x);
David Anderson's avatar
David Anderson committed
631
632
633
}

function post_str($name, $optional=false) {
634
635
636
637
638
639
640
    if (isset($_POST[$name])) {
        $x = $_POST[$name];
    } else {
        if (!$optional) {
            error_page("missing or bad parameter: $name");
        }
        $x = null;
David Anderson's avatar
David Anderson committed
641
    }
642
    return undo_magic_quotes($x);
David Anderson's avatar
David Anderson committed
643
644
}

David Anderson's avatar
David Anderson committed
645
646
647
648
649
650
651
652
653
654
function is_ascii($str) {
    // the mb_* functions are not included by default
    // return (mb_detect_encoding($passwd) -= 'ASCII');

    for ($i=0; $i<strlen($str); $i++) {
        $c = ord(substr($str, $i));
        if ($c < 32 || $c > 127) return false;
    }
    return true;
}
655

David Anderson's avatar
David Anderson committed
656
657
658
659
660
661
// This function replaces some often made mistakes while entering numbers
// and gives back an error if there are false characters
// It will also be checked if the value is within certain borders
// @param string &$value reference to the value that should be verified
// @param double $low the lowest number of value if verified
// @param double $high the highest number of value if verified
662
663
// @return bool true if $value is numeric and within the defined borders,
//   false if $value is not numeric, no changes were made in this case
David Anderson's avatar
David Anderson committed
664
//
David Anderson's avatar
David Anderson committed
665
666
667
668
669
670
671
672
function verify_numeric(&$value, $low, $high = false) {
    $number = trim($value);
    $number = str_replace('o', '0', $number);
    $number = str_replace('O', '0', $number);
    $number = str_replace('x', '', $number); //if someone enters '0x100'
    $number = str_replace(',', '.', $number); // replace the german decimal separator
    // if no value was entered and this is ok
    if ($number=='' && $low=='') return true;
David Anderson's avatar
David Anderson committed
673

David Anderson's avatar
David Anderson committed
674
675
    // the supplied value contains alphabetic characters
    if (!is_numeric($number)) return false;
David Anderson's avatar
David Anderson committed
676

677
678
679
    //if ($number < $low) $number = $low;
    if ($number < $low) return false;

David Anderson's avatar
David Anderson committed
680
    if ($high) {
681
682
        //if ($number > $high) $number = $high;
        if ($number > $high) return false;
David Anderson's avatar
David Anderson committed
683
684
685
686
687
    }
    $value = (double)$number;
    return true;
}

688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
// Generate a "select" HTML element from an array of values
function select_from_array($name, $array, $selection) {
    $out = "<select name=\"$name\">";

    foreach ($array as $key => $value) {
        if ($value) {
            $out .= "<option ";
            if ($key == $selection) {
                $out .= "selected ";
            }
            $out .= "value=\"".$key."\">".$value."</option>";
        }
    }
    $out.= "</select>";
    return $out;
}

David Anderson's avatar
David Anderson committed
705
// Convert to entities, while preserving already-encoded entities.
706
// Do NOT use if $str contains valid HTML tags.
707
//
David Anderson's avatar
David Anderson committed
708
709
710
711
712
function boinc_htmlentities($str) {
    $str = html_entity_decode($str, ENT_COMPAT, "UTF-8");
    $str = htmlentities($str, ENT_COMPAT, "UTF-8");
    return $str;
}
713

Matt Lebofsky's avatar
   
Matt Lebofsky committed
714
715
716
717
function strip_bbcode($string){
    return preg_replace("/((\[.+\])+?)(.+?)((\[\/.+\])+?)/","",$string);
}

718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
function current_url() {
    $url = "http";
    if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") {
        $url .= "s";
    }
    $url .= "://";
    $url .= $_SERVER['SERVER_NAME'];
    $url .= ":".$_SERVER['SERVER_PORT'];
    if (isset($_SERVER['REQUEST_URI'])) {
        $url .= $_SERVER['REQUEST_URI'];
    } else {
        if ($_SERVER['QUERY_STRING']) {
            $url .= "?".$_SERVER['QUERY_STRING'];
        }
    }
    return $url;
}

736
737
738
739
740
// Show a single link formatted to look like a button.
// @param url The destination URL of the button
// @param text The text to display on the button
// @param desc The title of the destination - typically used as a popup
// @param class The optional CSS class of the button. Defaults to a standard button
David Anderson's avatar
David Anderson committed
741
//
742
function show_button($url, $text, $desc=null, $class="btn btn-default") {
743
    if (!$desc) $desc = $text;
744
    echo "<a href=\"".$url."\" title=\"$desc\" class=\"".$class."\">".$text."</a>\n";
David Anderson's avatar
David Anderson committed
745
}
746
function button_text($url, $text, $desc=null, $class="btn btn-default") {
747
748
749
    if (!$desc) $desc = $text;
    return "<a href=\"".$url."\" title=\"$desc\" class=\"".$class."\">".$text."</a>\n";
}
David Anderson's avatar
David Anderson committed
750

751
752
753
754
755
756
757
// When multiple buttons (or actions) are presented in a list you can
// use this convenience method to avoid having to wrap each button in <li></li> elements
// @param url The destination URL of the button
// @param text The text to display on the button
// @param desc The title of the destination - typically used as a popup
// @param class The optional CSS class of the button. Defaults to a standard button
//
758
function show_actionlist_button($url, $text, $desc, $class="btn btn-default"){
759
760
761
762
763
    echo "<li>";
    echo show_button($url, $text, $desc, $class);
    echo "</li>";
}

764
function show_image($src, $title, $alt, $height=null) {
David Anderson's avatar
David Anderson committed
765
766
767
768
    $h = "";
    if ($height) {
        $h = "height=\"$height\"";
    }
David Anderson's avatar
David Anderson committed
769
    echo "<img border=\"0\" title=\"$title\" alt=\"$alt\" src=\"$src\" $h>";
770
771
}

772
function check_web_stopped() {
773
    global $generating_xml, $did_page_head;
774
775
776
777
    if (web_stopped()) {
        if ($generating_xml) {
            xml_error(-183);
        } else {
778
779
780
781
782
783
784
			if (!$did_page_head) {
				page_head(tra("Project down for maintenance"));
			}
            echo tra(
				"%1 is temporarily shut down for maintenance.  Please try again later.",
				PROJECT
			);
785
786
787
788
789
790
791
792
793
            page_tail();
            exit();
        }
    }
}

// Connects to database server and selects database as noted in config.xml
// If only read-only access is necessary,
// tries instead to connect to <replica_db_host> if tag exists.
794
// DEPRECATED - use boinc_db.inc
795
796
797
798
799
//
function db_init($try_replica=false) {
    check_web_stopped();
    $retval = db_init_aux($try_replica);
    if ($retval == 1) {
800
        echo tra("Unable to connect to database - please try again later");
801
802
803
        exit();
    }
    if ($retval == 2) {
804
        echo tra("Unable to select database - please try again later");
805
806
807
808
809
        exit();
    }
    return 0;
}

810
811
812
// return a structure indicating whether project has non-deprecated
// apps versions for various resource types,
// and with a count of app versions
813
814
//
function get_app_types() {
815
    $t = new StdClass;
816
817
818
    $t->cpu = false;
    $t->cuda = false;
    $t->ati = false;
819
    $t->intel_gpu = false;
820
821
822
823
824
825
826
827
828
    $t->count = 0;
    $avs = BoincAppVersion::enum("deprecated=0");
    foreach ($avs as $av) {
        if (strstr($av->plan_class, "ati")) {
            $t->ati = true;
            $t->count++;
        } else if (strstr($av->plan_class, "cuda")) {
            $t->cuda = true;
            $t->count++;
829
830
831
        } else if (strstr($av->plan_class, "nvidia")) {
            $t->cuda = true;
            $t->count++;
832
833
834
        } else if (strstr($av->plan_class, "intel_gpu")) {
            $t->intel_gpu = true;
            $t->count++;
835
836
837
838
839
840
841
842
        } else {
            $t->cpu = true;
            $t->count++;
        }
    }
    return $t;
}

843
844
845
// Functions to sanitize GET and POST args

// "next_url" arguments (must be local, not full URLs)
846
//
847
function sanitize_local_url($x) {
848
    $x = trim($x, "/");
849
    if (strstr($x, "/")) return "";
850
851
    if (strstr($x, "<")) return "";
    if (strstr($x, "\"")) return "";
852
853
854
    return $x;
}

855
856
857
// strip HTML tags
//
function sanitize_tags($x) {
858
    return strip_tags($x);
859
860
861
}

function sanitize_numeric($x) {
862
863
864
    if (is_numeric($x)) {
        return $x;
    } else {
David Anderson's avatar
   
David Anderson committed
865
        return "not numeric";
866
    }
867
868
}

869
870
function sanitize_email($x) {
    if (function_exists('filter_var')) {
871
        return filter_var($x, FILTER_SANITIZE_EMAIL);
872
873
874
875
876
    } else {
        return strip_tags($x);
    }
}

877
878
879
880
881
882
883
function flops_to_credit($f) {
    return $f*(200/86400e9);
}

function credit_to_gflop_hours($c) {
    return $c/(200/24);
}
884

885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
function do_download($path,$name="") {
    if (strcmp($name,"") == 0) {
        $name=basename($path);
    }
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.$name);
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($path));
    flush();
    readfile($path);
}
900

901
902
903
function redirect_to_secure_url($url) {
    if (defined('SECURE_URL_BASE')
        && strstr(SECURE_URL_BASE, "https://")
David Anderson's avatar
David Anderson committed
904
        && (!isset($_SERVER['HTTPS']) || !$_SERVER['HTTPS'])
905
906
907
908
909
910
911
    ) {
        Header("Location: ".SECURE_URL_BASE."/$url");
        exit;

    }
}

912
913
function badges_string($is_user, $item, $height) {
    if (DISABLE_BADGES) return null;
David Anderson's avatar
David Anderson committed
914
915
916
917
918
    if ($is_user) {
        $bus = BoincBadgeUser::enum("user_id=$item->id");
    } else {
        $bus = BoincBadgeTeam::enum("team_id=$item->id");
    }
919
    if (!$bus) return null;
David Anderson's avatar
David Anderson committed
920
921
922
    $x = "";
    foreach ($bus as $bu) {
        $badge = BoincBadge::lookup_id($bu->badge_id);
923
924
925
926
927
928
        $x .= "<img title=\"$badge->title\" valign=top height=$height src=$badge->image_url> ";
    }
    return $x;
}

function show_badges_row($is_user, $item) {
929
930
    if (BADGE_HEIGHT_LARGE == 0) return;
    $x = badges_string($is_user, $item, BADGE_HEIGHT_LARGE);
931
932
    if ($x) {
        row2("Badges", $x);
David Anderson's avatar
David Anderson committed
933
934
935
    }
}

936
937
938
939
function dtime() {
    return microtime(true);
}

David Anderson's avatar
David Anderson committed
940
941
942
943
944
// If this request is from a BOINC client, return its version as MMmmRR.
// Otherwise return 0.
// Format of user agent string is "BOINC client (windows_x86_64 7.3.17)"
//
function boinc_client_version(){
945
946
    if (!array_key_exists('HTTP_USER_AGENT', $_SERVER)) return 0;
    $x = $_SERVER['HTTP_USER_AGENT'];
David Anderson's avatar
David Anderson committed
947
948
949
950
951
952
953
    $e =  "/BOINC client [^ ]* (\d+).(\d+).(\d+)\)/";
    if (preg_match($e, $x, $matches)) {
        return $matches[1]*10000 + $matches[2]*100 + $matches[3];
    }
    return 0;
}

954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
// output a script for counting chars left in text field
//
function text_counter_script() {
    echo "<script type=\"text/javascript\">
        function text_counter(field, countfield, maxlimit) {
            if (field.value.length > maxlimit) {
                field.value =field.value.substring(0, maxlimit);
            } else {
                countfield.value = maxlimit - field.value.length
            }
        }
        </script>
    ";
}

// return HTML for a textarea with chars-remaining counter.
// Call text_counter_script() before using this.
//
function textarea_with_counter($name, $maxlen, $text) {
    $rem_name = $name."_remaining";
    return "<textarea name=\"$name\" rows=4 cols=50 id=\"$name\" onkeydown=\"text_counter(this.form.$name, this.form.$rem_name, $maxlen);\"
        onkeyup=\"text_counter(this.form.$name, this.form.$rem_name, $maxlen);\">".$text."</textarea>
        <br><input name=\"$rem_name\" type=\"text\" id=\"$rem_name\" value=\"".($maxlen-strlen($text))."\" size=\"3\" maxlength=\"3\" readonly> ".tra("characters remaining")
    ;
}

980
// convert number MMmmrr to string MM.mm.rr
981
//
982
function version_string_maj_min_rel($v) {
983
984
985
986
987
    $maj = (int)($v/10000);
    $v -= $maj*10000;
    $min = (int)($v/100);
    $v -= $min*100;
    return sprintf("%d.%d.%d", $maj, $min, $v);
988
989
}

990
991
992
993
994
995
996
997
998
999
1000
// recaptcha utilities

function recaptcha_get_head_extra() {
    // are we using recaptcha?
    $publickey = parse_config(get_config(), "<recaptcha_public_key>");
    if ($publickey) {
        // the meta tag must be included
        // for Recaptcha to work with some IE browsers
        return '<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >
        <script src="https://www.google.com/recaptcha/api.js" async defer></script>';
    } else {
For faster browsing, not all history is shown. View entire blame