validate_post_make_user()   F
last analyzed

Complexity

Conditions 32
Paths > 20000

Size

Total Lines 146
Code Lines 87

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 32
eloc 87
nc 9954360
nop 0
dl 0
loc 146
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
// functions for creating and deleting users
20
21
22
include_once("../inc/boinc_db.inc");
23
include_once("../inc/util.inc");
24
include_once("../inc/email.inc");
25
include_once("../inc/recaptchalib.inc");
26
require_once("../inc/password_compat/password.inc");
27
require_once("../inc/consent.inc");
28
29
// Password hash has old format.
30
// Update user record with new format
31
//
32
function do_passwd_rehash($user, $passwd_hash) {
33
    $database_passwd_hash = password_hash($passwd_hash, PASSWORD_DEFAULT);
34
    $result = $user->update(" passwd_hash='$database_passwd_hash' ");
35
}
36
37
// return true if the passwd hash (old format)
38
// matches the user's passwd hash (possibly new format)
39
//
40
function check_passwd_hash($user, $passwd_hash) {
41
    if (password_verify($passwd_hash, $user->passwd_hash)) {
42
        // on valid login, rehash password to upgrade hash overtime
43
        // as the defaults change.
44
        //
45
        if (password_needs_rehash($user->passwd_hash, PASSWORD_DEFAULT)) {
46
            do_passwd_rehash($user, $passwd_hash);
47
        }
48
    } else if ($passwd_hash == $user->passwd_hash) {
49
        // user record has old format.  Change to new.
50
        //
51
        do_passwd_rehash($user, $passwd_hash);
52
    } else {
53
        return false;
54
    }
55
    return true;
56
}
57
58
function check_passwd_ui($user, $passwd) {
59
    $passwd_hash = md5($passwd.$user->email_addr);
60
    if(!check_passwd_hash($user, $passwd_hash)) {
61
        sleep(LOGIN_FAIL_SLEEP_SEC);
62
        page_head("Password incorrect");
63
        echo "The password you entered is incorrect. Please go back and try again.\n";
64
        page_tail();
65
        exit;
0 ignored issues
show
Best Practice introduced by
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...
66
    }
67
}
68
69
function is_banned_email_addr($email_addr) {
70
    global $banned_email_domains;
71
    if (isset($banned_email_domains)) {
72
        foreach($banned_email_domains as $d) {
73
            $x = strstr($email_addr, $d);
74
            if ($x == $d) return true;
75
        }
76
    }
77
    return false;
78
}
79
80
function is_valid_user_name($name, &$reason) {
81
    if (trim($name) !== $name) {
82
        $reason = tra("user name cannot have leading or trailing white space");
83
        return false;
84
    }
85
    if (strlen($name) == 0) {
86
        $reason = tra("user name must be nonempty");
87
        return false;
88
    }
89
    if (filter_var($name, FILTER_SANITIZE_SPECIAL_CHARS) !== $name) {
90
        $reason = tra("user name may not contain special characters");
91
        return false;
92
    }
93
    if (is_numeric($name)) {
94
        $reason = tra("user name may not be a number");
95
        return false;
96
    }
97
    return true;
98
}
99
100
function default_show_hosts() {
101
    $config = get_config();
102
    // If enable privacy by default is TRUE, then show_hosts' default
103
    // is FALSE.
104
    return parse_bool($config, "enable_privacy_by_default") ? 0 : 1;
105
}
106
107
// the following DB-escapes its args
108
//
109
function make_user(
110
    $email_addr, $name, $passwd_hash,
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
111
    $country=null, $postal_code=null, $project_prefs=null, $teamid=0
0 ignored issues
show
Coding Style introduced by
Multi-line function declarations must define one parameter per line
Loading history...
112
) {
113
    if (!is_valid_email_syntax($email_addr)) return null;
114
        // caller generally has already checked
115
    if (is_banned_email_addr($email_addr)) return null;
116
117
    $authenticator = random_string();
118
    $cross_project_id = random_string();
119
    $now = time();
120
    if (!is_valid_country($country)) return null;
121
122
    $email_addr = BoincDb::escape_string($email_addr);
123
    $name = sanitize_tags($name);
124
    $name = BoincDb::escape_string($name);
125
    $database_passwd_hash = password_hash($passwd_hash, PASSWORD_DEFAULT);
126
127
    $country = BoincDb::escape_string($country);
128
    $postal_code = sanitize_tags(BoincDb::escape_string($postal_code));
129
130
    $show_hosts = default_show_hosts();
131
    $uid = BoincUser::insert("(create_time, email_addr, name, authenticator, country, postal_code, total_credit, expavg_credit, expavg_time, project_prefs, teamid,  venue, send_email, show_hosts, posts, seti_id, seti_nresults, seti_last_result_time, seti_total_cpu, has_profile, cross_project_id, passwd_hash, email_validated, donated) values($now, '$email_addr', '$name', '$authenticator', '$country', '$postal_code', 0, 0, unix_timestamp(), '$project_prefs', $teamid, '', 1, $show_hosts, 0, 0, 0, 0, 0, 0, '$cross_project_id', '$database_passwd_hash', 0, 0)");
132
133
    if (!$uid) {
134
        return null;
135
    }
136
    $user = BoincUser::lookup_id($uid);
137
    if (defined('RECORD_USER_IP')) {
138
        $ip = $_SERVER['REMOTE_ADDR'];
139
        $ip = BoincDb::escape_string($ip);
140
        $user->update("venue='$ip'");
141
    }
142
    return $user;
143
}
144
145
function make_user_ldap($email_addr, $name) {
146
    $email_addr = BoincDb::escape_string($email_addr);
147
    $name = sanitize_tags($name);
148
    $name = BoincDb::escape_string($name);
149
    $authenticator = random_string();
150
    $cross_project_id = random_string();
151
    $passwd_hash = random_string();
152
    $now = time();
153
154
    $show_hosts = default_show_hosts();
155
    $uid = BoincUser::insert("(create_time, email_addr, name, authenticator, country, postal_code, total_credit, expavg_credit, expavg_time, project_prefs, teamid,  send_email, show_hosts, cross_project_id, passwd_hash) values($now, '$email_addr', '$name', '$authenticator', '', '', 0, 0, unix_timestamp(), '', 0, 1, $show_hosts, '$cross_project_id', '$passwd_hash')");
156
157
    if ($uid) {
158
        return BoincUser::lookup_id($uid);
159
    } else {
160
        return null;
161
    }
162
}
163
164
function show_error($str) {
165
    page_head(tra("Can't create account"));
166
    echo "$str<br>\n";
167
    echo BoincDb::error();
168
    echo "<p>".tra("Click your browser's <b>Back</b> button to try again.")."\n</p>\n";
169
    page_tail();
170
    exit();
0 ignored issues
show
Best Practice introduced by
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...
171
}
172
173
// validate POST args and make user.
174
// If error show error page and exit.
175
// Else return user object
176
//
177
function validate_post_make_user() {
178
    $config = get_config();
179
    if (parse_bool($config, "disable_account_creation")
180
        || parse_bool($config, "no_web_account_creation")
181
    ) {
182
        error_page("Account creation is disabled");
183
    }
184
185
    if (recaptcha_private_key()) {
186
        if (!boinc_recaptcha_isValidated(recaptcha_private_key())) {
187
            show_error(
188
                tra("Your reCAPTCHA response was not correct. Please try again.")
189
            );
190
        }
191
    }
192
193
    // Check if consent to terms of use has been given.
194
    //
195
    $myconsent = FALSE;
196
    list($checkct, $ctid) = check_consent_type(CONSENT_TYPE_ENROLL);
197
    if ($checkct and check_termsofuse()) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
198
        $agree = post_str("agree_to_terms_of_use", true);
199
        if (!$agree) {
200
            error_page(tra("You have not agreed to our terms of use. Please agree to the terms of use by navigating back to the previous page, in order to create your account"));
201
        }
202
        $myconsent = TRUE;
203
    }
204
205
    // see whether the new account should be pre-enrolled in a team,
206
    // and initialized with its founder's project prefs
207
    //
208
    $teamid = post_int("teamid", true);
209
    if ($teamid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $teamid of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null 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...
210
        $team = BoincTeam::lookup_id($teamid);
211
        $clone_user = BoincUser::lookup_id($team->userid);
212
        if (!$clone_user) {
213
            error_page("User $team->userid not found");
214
        }
215
        $project_prefs = $clone_user->project_prefs;
216
    } else {
217
        $teamid = 0;
218
        $project_prefs = "";
219
    }
220
221
    if (defined('INVITE_CODES')) {
222
        $invite_code = post_str("invite_code");
223
        if (strlen($invite_code) == 0) {
224
            show_error(tra("You must supply an invitation code to create an account."));
225
        }
226
        if (!preg_match(INVITE_CODES, $invite_code)) {
227
            show_error(tra("The invitation code you gave is not valid."));
228
        }
229
    }
230
231
    $new_name = post_str("new_name");
232
    if (!is_valid_user_name($new_name, $reason)) {
233
        show_error($reason);
234
    }
235
    if (UNIQUE_USER_NAME) {
236
        $u = BoincUser::lookup_name($new_name);
237
        if ($u) {
238
            page_head("That name is in use");
239
            echo "<p>The following user names are taken;
240
                please go back and use a different one.<p>
241
            ";
242
            $users = BoincUser::enum(
243
                sprintf("name like '%s%%'", $new_name)
244
            );
245
            foreach ($users as $u){
246
                echo "<p>$u->name\n";
247
            }
248
            page_tail();
249
            exit;
0 ignored issues
show
Best Practice introduced by
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...
250
        }
251
    }
252
253
    $new_email_addr = strtolower(post_str("new_email_addr"));
254
    if (!is_valid_email_syntax($new_email_addr)) {
255
        show_error(tra("Invalid email address: please enter a valid address of the form [email protected]"));
256
    }
257
    if (!is_valid_email_sfs($new_email_addr)) {
258
        show_error("$new_email_addr was flagged by stopforumspam.com");
259
    }
260
261
    $user = BoincUser::lookup_email_addr($new_email_addr);
262
    if ($user) {
263
        show_error(tra("There's already an account with that email address."));
264
    }
265
266
    $tmpuser = BoincUser::lookup_prev_email_addr($new_email_addr);
267
    if ($tmpuser) {
268
        show_error(tra("There's already an account with that email address."));
269
    }
270
271
    $passwd = post_str("passwd");
272
273
    $min_passwd_length = parse_config($config, "<min_passwd_length>");
274
    if (!$min_passwd_length) $min_passwd_length = 6;
275
276
    if (!is_ascii($passwd)) {
277
        show_error(tra("Passwords may only include ASCII characters."));
278
    }
279
280
    if (strlen($passwd)<$min_passwd_length) {
281
        show_error(
282
            tra("New password is too short: minimum password length is %1 characters.", $min_passwd_length)
283
        );
284
    }
285
286
    $passwd_hash = md5($passwd.$new_email_addr);
287
288
    $country = "";
289
    if (USER_COUNTRY) {
290
        $country = post_str("country", true);
291
        if ($country && !is_valid_country($country)) {
292
            error_page("bad country");
293
        }
294
    }
295
296
    if (POSTAL_CODE) {
297
        $postal_code = sanitize_tags(post_str("postal_code", true));
298
    } else {
299
        $postal_code = '';
300
    }
301
302
    $user = make_user(
303
        $new_email_addr, $new_name, $passwd_hash,
304
        $country, $postal_code, $project_prefs, $teamid
305
    );
306
    if (!$user) {
307
        show_error(
308
            tra("Couldn't create account").": ".BoincDb::error()
309
        );
310
    }
311
312
    if ($myconsent) {
313
        $rc = consent_to_a_policy($user, $ctid, 1, 0, 'Webreg');
314
        if (!$rc) {
315
            show_error(tra("database error, please contact site administrators"));
316
        }
317
    }
318
319
    if (defined('INVITE_CODES')) {
320
        error_log("Account '$new_email_addr' created using invitation code '$invite_code'");
0 ignored issues
show
Coding Style introduced by
The use of function error_log() is discouraged
Loading history...
Comprehensibility Best Practice introduced by
The variable $invite_code does not seem to be defined for all execution paths leading up to this point.
Loading history...
321
    }
322
    return $user;
323
}
324
325
// delete a user and all associated records except
326
// result
327
// host
328
// batch
329
// team
330
// user_submit
331
// user_submit_app
332
// credited_job
333
// donation_paypal
334
// sent_email
335
//
336
function delete_user($user) {
337
    delete_profile($user);
338
    forum_delete_user($user);
339
        // deletes post, thread, subscription, forum_preferences, forum_logging
340
    BoincPrivateMessage::delete_aux("userid=$user->id or senderid=$user->id");
341
    BoincNotify::delete_aux("userid=$user->id");
342
    BoincCreditUser::delete_user($user);
343
    BoincBadgeUser::delete("user_id=$user->id");
344
    BoincFriend::delete_aux("user_src=$user->id or user_dest=$user->id");
345
    BoincToken::delete_for_user($user->id);
346
    $user->delete();
347
}
348
?>
349