Issues (1963)

html/inc/util_ops.inc (12 issues)

1
<?php
2
// This file is part of BOINC.
3
// http://boinc.berkeley.edu
4
// Copyright (C) 2017 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
// utility functions for admin web pages
20
21
require_once("../inc/db_ops.inc");
22
require_once("../inc/util.inc");
23
require_once("../project/project.inc");
24
25
display_errors();
26
27
function admin_page_head($title) {
28
    echo "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">";
29
    echo sprintf('<html><head><title>%s</title>
30
        <meta http-equiv="content-type" content="text/html;charset=utf-8" />
31
        <link type="text/css" rel="stylesheet" href="%s/bootstrap.min.css" media="all">
32
        <link type="text/css" rel="stylesheet" href="%s/custom.css" media="all">
33
        </head>
34
        <body>
35
        <div class="container-fluid">
36
        <h2>%s: %s</h2>
37
        ',
38
        $title,
39
        secure_url_base(),
40
        secure_url_base(),
41
        PROJECT,
42
        $title
43
    );
44
    show_login_info();
45
    echo "<p>";
46
}
47
48
function admin_page_tail() {
49
    echo sprintf('
50
        <hr><center><a href=index.php>Main page</a></center>
51
        <script src="%s/jquery.min.js"></script>
52
        <script src="%s/bootstrap.min.js"></script>
53
        </div>
54
        </body>
55
        </html>
56
        ',
57
        secure_url_base(),
58
        secure_url_base()
59
    );
60
}
61
62
// TODO: get rid of all the following
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
63
64
function print_checkbox($text,$name,$checked) {
65
    echo "<input type=\"checkbox\" name=\"$name\""
66
        . (strlen($checked) ? " checked=\"checked\"" : "") . ">"
67
        . "$text\n"
68
        . "<p>\n";
69
}
70
71
function print_radio_button($text,$name,$value,$checked) {
72
    echo "<input type=\"radio\" name=\"$name\" value=\"$value\""
73
        . (strlen($checked) ? " checked=\"checked\"" : "") . ">"
74
        . "$text\n"
75
        . "<br>\n";
76
}
77
78
function print_text_field($text,$name,$value) {
79
    echo "$text <input type=\"text\" size=\"10\" name=\"$name\" value=\"$value\">\n"
80
         . "<p>\n";
81
}
82
83
function row($x, $y) {
84
    echo "<tr><td width=30% valign=\"top\" align=\"right\">$x &nbsp;&nbsp; </td>\n<td>$y</td>\n</tr>\n";
85
}
86
87
function c_row2($color, $x, $y) {
88
    echo "<tr bgcolor=\"$color\"><td align=\"right\">$x</td><td>$y</td></tr>\n";
89
}
90
91
function show_profile_link_ops($user) {
92
    if ($user->has_profile) {
93
        row2("Profile",
94
            "<a href=\"".url_base()."view_profile.php?userid=$user->id\">View</a>"
95
        );
96
    }
97
}
98
99
// initialize database connection with username & password from
100
// command line instead of config.xml
101
//
102
function db_init_cli() {
103
    $config = get_config();
104
    $db_name = parse_config($config, "<db_name>");
105
    $host = parse_config($config, "<db_host>");
106
    if ($host == null) {
0 ignored issues
show
It seems like you are loosely comparing $host of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
107
        $host = "localhost";
108
    }
109
    $in = fopen("php://stdin","r");
110
    print "Database username (default: owner of mysqld process): ";
111
    $user = rtrim(fgets($in, 80));
112
    print "Database password (if any): ";
113
    $pass = rtrim(fgets($in, 80));
114
115
    $retval = _mysql_connect($host, $user, $pass, $db_name);
0 ignored issues
show
Are you sure the assignment to $retval is correct as _mysql_connect($host, $user, $pass, $db_name) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

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

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

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

Loading history...
116
    if (!$retval) {
0 ignored issues
show
$retval is of type null, thus it always evaluated to false.
Loading history...
117
        die("Can't connect to DB\n");
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...
118
    }
119
}
120
121
function print_login_form_ops($next_url='') {
122
    if ($next_url == '') $next_url = $_SERVER['REQUEST_URI'];
123
    start_table();
124
    echo "
125
        <form method=post action=login_action.php>
126
        <input type=hidden name=next_url value=$next_url>
127
    ";
128
    row2("Email", "<input name=email_addr size=40>");
129
    row2("Password", "<input type=password name=passwd size=40>");
130
    row2(tra("Stay logged in on this computer"), '<input type="checkbox" name="stay_logged_in" checked>');
131
    row2("", "<input class=\"btn btn-primary\" type=submit value=OK>");
132
    end_table();
133
}
134
135
function get_logged_in_user_ops() {
136
    global $g_logged_in_user;
137
    if ($g_logged_in_user) return $g_logged_in_user;
138
    $authenticator = null;
139
    if (isset($_COOKIE['auth'])) $authenticator = $_COOKIE['auth'];
140
141
    $authenticator = BoincDb::escape_string($authenticator);
142
    if ($authenticator) {
143
        $g_logged_in_user = BoincUser::lookup("authenticator='$authenticator'");
144
    }
145
    return $g_logged_in_user;
146
}
147
148
////////// functions for access control of admin web pages /////////////
149
150
// allow access only if logged in as user in a given set
151
//
152
function auth_ops_userid($admin_user_ids) {
153
    $user = get_logged_in_user_ops();
154
    if (!$user) {
155
        admin_page_head("Log in");
156
        echo "You must log in to performance admin functions.<p>\n";
157
        print_login_form_ops();
158
        admin_page_tail();
159
        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...
160
    } else if (!in_array($user->id, $admin_user_ids)) {
161
        admin_page_head("Log in");
162
        echo "
163
            You must be logged in as an admin to perform admin functions.
164
            <p>
165
            <a href=logout.php>Log out</a>
166
        ";
167
        admin_page_tail();
168
        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...
169
    }
170
}
171
172
// allow access only to users with ADMIN/DEV flags in forum prefs.
173
// If you use this, make sure you know who has these privileges
174
//
175
function auth_ops_privilege() {
176
    $user = get_logged_in_user_ops();
177
    if (!$user) {
178
        admin_page_head("Log in");
179
        echo "You must log in to performance admin functions.<p>\n";
180
        print_login_form_ops();
181
        admin_page_tail();
182
        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...
183
    }
184
    BoincForumPrefs::lookup($user);
185
    if ($user->prefs->privilege(S_ADMIN) || $user->prefs->privilege(S_DEV)) {
186
        return;
187
    }
188
    error_page("Access denied");
189
}
190
191
// if project hasn't specified a policy in project.inc,
192
// and no .htaccess, don't allow access
193
//
194
if (!function_exists('auth_ops')) {
195
    function auth_ops() {
196
        if (!file_exists(".htaccess")) {
197
            error_page("
198
                You must protect the admin interface
199
                with either a .htaccess file or an auto_ops() function.
200
                <p>
201
                <a href=https://github.com/BOINC/boinc/wiki/HtmlOps>See how here</a>"
202
            );
203
        }
204
    }
205
}
206
207
function admin_error_page($msg) {
208
    admin_page_head("Unable to handle request");
209
    echo $msg;
210
    admin_page_tail();
211
    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...
212
}
213
214
// given a list of app versions,
215
// return a list of the current, non-deprecated ones
216
//
217
function current_versions($avs) {
218
    foreach($avs as $av) {
219
        foreach ($avs as $av2) {
220
            if ($av->id == $av2->id) continue;
221
            if ($av->platformid == $av2->platformid && $av->plan_class == $av2->plan_class && $av->version_num > $av2->version_num) {
222
                $av2->deprecated = 1;
223
            }
224
        }
225
    }
226
    $x = array();
227
    foreach($avs as $av) {
228
        if (!$av->deprecated) $x[] = $av;
229
    }
230
    return $x;
231
}
232
233
// cancel WUs with IDs in a given range.
234
// This means:
235
//
236
// - for any results w/ server state UNSENT, set server state to OVER
237
// - set the CANCELLED bit in workunit.error_mask
238
//
239
function cancel_wus($wuid1, $wuid2) {
240
    $retval = BoincResult::update_aux(
241
        sprintf(
242
            'server_state=%d, outcome=%d where server_state=%d and workunitid>=%d and workunitid<=%d',
243
            RESULT_SERVER_STATE_OVER,
244
            RESULT_OUTCOME_DIDNT_NEED,
245
            RESULT_SERVER_STATE_UNSENT,
246
            $wuid1, $wuid2
247
        )
248
    );
249
    if (!$retval) {
250
        error_page("Result update failed");
251
    }
252
253
    // mark WUs as cancelled and trigger the transitioner
254
    //
255
    $retval = BoincWorkunit::update_aux(
256
        sprintf(
257
            'error_mask=error_mask|%d, transition_time=%d where id>=%d and id<=%d',
258
            WU_ERROR_CANCELLED,
259
            time(),
260
            $wuid1, $wuid2
261
        )
262
    );
263
    if (!$retval) {
264
        error_page("Workunit update failed");
265
    }
266
    return 0;
267
}
268
269
// like above, but if a workunit has a result that's already sent,
270
// don't cancel the workunit
271
//
272
function cancel_wus_if_unsent($id1, $id2) {
273
    $wus = BoincWorkunit::enum("id >= $id1 and id <= $id2");
274
    foreach ($wus as $wu) {
275
        $results = BoincResult::enum(
276
            sprintf(
277
                'workunitid=%d and server_state > %d',
278
                $wu->id, RESULT_SERVER_STATE_UNSENT
279
            )
280
        );
281
        if (count($results)) continue;
282
        $retval = BoincResult::update_aux(
283
            sprintf(
284
                'server_state=%d, outcome=%d where workunitid=%d',
285
                RESULT_SERVER_STATE_OVER,
286
                RESULT_OUTCOME_DIDNT_NEED,
287
                $wu->id
288
            )
289
        );
290
        if (!$retval) {
291
            error_page("result update failed");
292
        }
293
        if (!$wu->update("error_mask=error_mask|16")) {
294
            error_page("WU update failed");
295
        }
296
    }
297
    return 0;
298
}
299
300
function app_version_desc($avid) {
301
    switch ($avid) {
302
    case ANON_PLATFORM_UNKNOWN:
303
        return "Anonymous platform: unknown type";
304
    case ANON_PLATFORM_CPU:
305
        return "Anonymous platform: CPU";
306
    case ANON_PLATFORM_NVIDIA:
307
        return "Anonymous platform: NVIDIA GPU";
308
    case ANON_PLATFORM_ATI:
309
        return "Anonymous platform: AMD GPU";
310
    case ANON_PLATFORM_INTEL_GPU:
311
        return "Anonymous platform: Intel GPU";
312
    case ANON_PLATFORM_APPLE_GPU:
313
        return "Anonymous platform: Apple GPU";
314
    }
315
    if ($avid <= 0) {
316
        return "unknown: $avid";
317
    }
318
    $av = BoincAppVersion::lookup_id($avid);
319
    if ($av) {
320
        $p = BoincPlatform::lookup_id($av->platformid);
321
        if ($p) {
322
            return sprintf("%.2f", $av->version_num/100)." $p->name [$av->plan_class]";
323
        } else {
324
            return sprintf("%.2f", $av->version_num/100)." MISSING PLATFORM $av->platformid [$av->plan_class]";
325
        }
326
    } else {
327
        return "App version missing ($avid)";
328
    }
329
}
330
331
////// badge-related stuff
332
333
function get_badge($name, $title, $image_url) {
334
    $name = BoincDb::escape_string($name);
335
    $title = BoincDb::escape_string($title);
336
    $image_url = BoincDb::escape_string($image_url);
337
    $b = BoincBadge::lookup("name='$name'");
338
    if ($b) return $b;
339
    $now = time();
340
    $id = BoincBadge::insert("(create_time, type, name, title, description, image_url, level, tags, sql_rule) values ($now, 0, '$name', '$title', '', 'img/$image_url', '', '', '')");
341
    $b = BoincBadge::lookup_id($id);
342
    if ($b) return $b;
343
    die("can't create badge $name\n");
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...
344
}
345
346
function assign_badge($is_user, $item, $badge) {
347
    $now = time();
348
    if ($is_user) {
349
        $bbu = BoincBadgeUser::lookup("user_id=$item->id and badge_id=$badge->id");
350
        if ($bbu) {
351
            $bbu->update("reassign_time=$now where user_id=$item->id and badge_id=$badge->id");
352
        } else {
353
            BoincBadgeUser::insert("(create_time, user_id, badge_id, reassign_time) values ($now, $item->id, $badge->id, $now)");
354
        }
355
    } else {
356
        $bbt = BoincBadgeTeam::lookup("team_id=$item->id and badge_id=$badge->id");
357
        if ($bbt) {
358
            $bbt->update("reassign_time=$now where team_id=$item->id and badge_id=$badge->id");
359
        } else {
360
            BoincBadgeTeam::insert("(create_time, team_id, badge_id, reassign_time) values ($now, $item->id, $badge->id, $now)");
361
        }
362
    }
363
}
364
365
// unassign all badges except the given one
366
//
367
function unassign_badges($is_user, $item, $badges, $k) {
368
    $list = null;
369
    for ($i=0; $i<count($badges); $i++) {
0 ignored issues
show
Coding Style Performance introduced by
The use of count() inside a loop condition is not allowed; assign the return value to a variable and use the variable in the loop condition instead
Loading history...
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
370
        if ($i == $k) continue;
371
        $badge = $badges[$i];
372
        if ($list) {
373
            $list .= ",$badge->id";
374
        } else {
375
            $list = "$badge->id";
376
        }
377
    }
378
    if ($is_user) {
379
        BoincBadgeUser::delete("user_id=$item->id and badge_id in ($list)");
380
    } else {
381
        BoincBadgeTeam::delete("team_id=$item->id and badge_id in ($list)");
382
    }
383
}
384
385
////// end badge-related stuff
386
387
function running_from_web_server() {
388
    return array_key_exists("SERVER_PORT", $_SERVER);
389
}
390
391
if (isset($cli_only)) {
392
    if (running_from_web_server()) {
393
        die("This script is intended to be run from the command line,
394
            not from the web server."
395
        );
396
    }
397
}
398
399
if (!isset($skip_auth_ops) && array_key_exists("SERVER_PORT", $_SERVER)) {
400
    auth_ops();
401
}
402
403
// returns true when this is a readonly ops section
404
// currently a dummy because this needs to be ported from Einstein@home
405
//
406
function in_rops() {
407
    return false;
408
}
409
410
function cancel_wus_where($clause) {
411
    $q1 = "CREATE TEMPORARY TABLE tmp SELECT id FROM workunit WHERE $clause;";
412
    $q2 = sprintf(
413
        'UPDATE result r INNER JOIN tmp t on r.workunitid=t.id SET outcome=%d WHERE server_state=%d;',
414
        RESULT_OUTCOME_DIDNT_NEED, RESULT_SERVER_STATE_UNSENT
415
    );
416
    $q3 = "UPDATE workunit w INNER JOIN tmp t on w.id=t.id SET error_mask=error_mask|16, transition_time=0;";
417
    $q4 = "DROP TABLE tmp;";
418
419
    $db = BoincDb::get();
420
421
    if (!$db->do_query($q1)) {
422
        echo "MySQL command '$q1' failed:<br/>unable to create temporary WU id table.<br>\n";
423
        return 1;
424
    } else if (!$db->do_query($q2)) {
425
        echo "MySQL command '$q2' failed:<br/>unable to cancel unsent results.<br>\n";
426
        $db->do_query($q4);
427
        return 2;
428
    } else if (!$db->do_query($q3)) {
429
      echo "MySQL command '$q3' failed:<br/>unable to cancel workunits and trigger transitioner.<br>\n";
430
        $db->do_query($q4);
431
        return 3;
432
    }
433
    $db->do_query($q4);
434
    echo "Successfully canceled WUs WHERE '$clause'<br>\n";
435
    return 0;
436
}
437
438
?>
439