instantiate.php 15.3 KB
Newer Older
Leigh B Stoller's avatar
Leigh B Stoller committed
1 2
<?php
#
3
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
Leigh B Stoller's avatar
Leigh B Stoller committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file 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 Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
chdir("..");
include("defs.php3");
include_once("osinfo_defs.php");
include_once("geni_defs.php");
28
chdir("apt");
Leigh B Stoller's avatar
Leigh B Stoller committed
29
include("quickvm_sup.php");
30
$page_title = "QuickVM Create";
Leigh B Stoller's avatar
Leigh B Stoller committed
31
$dblink = GetDBLink("sa");
Leigh B Stoller's avatar
Leigh B Stoller committed
32

33 34 35 36 37 38
#
# Get current user but make sure coming in on SSL.
#
RedirectSecure();
$this_user = CheckLogin($check_status);

Leigh B Stoller's avatar
Leigh B Stoller committed
39 40 41 42 43 44
#
# Verify page arguments.
#
$optargs = OptionalPageArguments("create",      PAGEARG_STRING,
				 "username",    PAGEARG_STRING,
				 "email",	PAGEARG_STRING,
Leigh B Stoller's avatar
Leigh B Stoller committed
45
				 "profile",     PAGEARG_STRING,
Leigh B Stoller's avatar
Leigh B Stoller committed
46 47
				 "stuffing",    PAGEARG_STRING,
				 "verify",      PAGEARG_STRING,
Leigh B Stoller's avatar
Leigh B Stoller committed
48
				 "sshkey",	PAGEARG_STRING,
49
				 "project",     PAGEARG_PROJECT,
Leigh B Stoller's avatar
Leigh B Stoller committed
50 51 52 53 54 55 56
				 "ajax_request",  PAGEARG_BOOLEAN,
				 "ajax_method",   PAGEARG_STRING,
				 "ajax_argument", PAGEARG_STRING);

#
# Deal with ajax requests.
#
57 58
# XXX Need permission checks here. 
#
Leigh B Stoller's avatar
Leigh B Stoller committed
59 60
if (isset($ajax_request)) {
    if ($ajax_method == "getprofile") {
61
	$profile_idx = addslashes($ajax_argument);
Leigh B Stoller's avatar
Leigh B Stoller committed
62
	$query_result =
63 64
	    DBQueryWarn("select * from apt_profiles ".
			"where idx='$profile_idx'");
Leigh B Stoller's avatar
Leigh B Stoller committed
65 66

	if (!$query_result || !mysql_num_rows($query_result)) {
67
	    SPITAJAX_ERROR(1, "No such profile $profile_idx!");
Leigh B Stoller's avatar
Leigh B Stoller committed
68 69 70 71 72 73
	    exit();
	}
	$row = mysql_fetch_array($query_result);
	
	SPITAJAX_RESPONSE(array('rspec' => $row['rspec'],
				'name'  => $row['name'],
74
				'idx'   => $row['idx'],
Leigh B Stoller's avatar
Leigh B Stoller committed
75 76 77 78
				'description' => $row['description']));
    }
    exit();
}
Leigh B Stoller's avatar
Leigh B Stoller committed
79 80 81 82 83

# Form defaults.
$username_default = "Pick a user name";
$email_default    = "Your email address";
$sshkey_default   = "Your SSH public key";
84
$profile_default  = "ThreeVMs";
Leigh B Stoller's avatar
Leigh B Stoller committed
85
$profile_array    = array();
Leigh B Stoller's avatar
Leigh B Stoller committed
86

Leigh B Stoller's avatar
Leigh B Stoller committed
87
$query_result =
88 89 90
    DBQueryFatal("select * from apt_profiles ".
		 "where public=1 " .
		 ($this_user ? "or creator_idx=" . $this_user->uid_idx() : ""));
Leigh B Stoller's avatar
Leigh B Stoller committed
91
while ($row = mysql_fetch_array($query_result)) {
92 93 94 95
    $profile_array[$row["idx"]] = $row["name"];
    if ($row["pid"] == $TBOPSPID && $row["name"] == $profile_default) {
	$profile_default = $row["idx"];
    }
96 97 98 99 100 101 102 103 104 105 106
    if (isset($profile)) {
        # Look for the profile by project/name and switch to index.
	if (isset($project) &&
	    $row["pid"] == $project->pid() &&
	    $row["name"] == $profile) {
	    $profile = $row["idx"];
	}
        # Look for the profile by uuid and switch to index.
	elseif ($profile == $row["uuid"]) {
	    $profile = $row["idx"];
	}
107
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
108
}
Leigh B Stoller's avatar
Leigh B Stoller committed
109

Leigh B Stoller's avatar
Leigh B Stoller committed
110
function SPITFORM($username, $email, $sshkey, $profile, $newuser, $errors)
Leigh B Stoller's avatar
Leigh B Stoller committed
111 112 113
{
    global $TBBASE, $TBMAIL_OPS;
    global $username_default, $email_default, $sshkey_default;
Leigh B Stoller's avatar
Leigh B Stoller committed
114
    global $profile_default, $profile_array;
Leigh B Stoller's avatar
Leigh B Stoller committed
115 116 117 118

    $username_value   = "";
    $email_value      = "";
    $sshkey_value     = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
119
    $profile_value    = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
120 121 122
    $username_error   = "";
    $email_error      = "";
    $sshkey_error     = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
123
    $profile_error    = "";
Leigh B Stoller's avatar
Leigh B Stoller committed
124 125 126 127 128 129 130 131 132 133 134
    $internal_error   = null;

    if (isset($username) && $username != "") {
	$username_value = CleanString($username);
    }
    if (isset($email) && $email != "") {
	$email_value = CleanString($email);
    }
    if (isset($sshkey) && $sshkey != "") {
	$sshkey_value = CleanString($sshkey);
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
135 136
    if (isset($profile) && $profile != "") {
	$profile_value = CleanString($profile);
Leigh B Stoller's avatar
Leigh B Stoller committed
137 138 139 140 141 142 143 144 145 146 147
    }
    if ($errors) {
	while (list ($name, $message) = each ($errors)) {
	# XSS prevention.
	    $message = CleanString($message);
	    if ($name == "username")
		$username_error = $message;
	    elseif ($name == "email")
		$email_error = $message;
	    elseif ($name == "sshkey")
		$sshkey_error = $message;
Leigh B Stoller's avatar
Leigh B Stoller committed
148 149
	    elseif ($name == "profile")
		$profile_error = $message;
Leigh B Stoller's avatar
Leigh B Stoller committed
150 151 152 153 154
	    elseif ($name == "internal") {
		$internal_error = $message;
	    }
	}
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
155
    SPITHEADER(1);
Leigh B Stoller's avatar
Leigh B Stoller committed
156 157 158 159

    if ($internal_error) {
	echo "<center><h2>$internal_error</h2></center><br>\n";
    }
160
    echo "<div class='row'>
Leigh B Stoller's avatar
Leigh B Stoller committed
161
          <div class='col-lg-6  col-lg-offset-3
162
                      col-md-6  col-md-offset-3
Leigh B Stoller's avatar
Leigh B Stoller committed
163 164
                      col-sm-8  col-sm-offset-2
                      col-xs-12 col-xs-offset-0'>\n";
165
    echo "<form id='quickvm_form' role='form'
166
            method='post' action='quickv.php'>\n";
167 168 169 170 171 172
    echo "<div class='panel panel-default'>
           <div class='panel-heading'>
              <h3 class='panel-title'>
                 Create an Experiment</h3></div>
           <div class='panel-body'>
            <div class='form-group'>
Leigh B Stoller's avatar
Leigh B Stoller committed
173 174
                <input name='username' id='username'
                       value='$username_value'
175
                       class='form-control'
Leigh B Stoller's avatar
Leigh B Stoller committed
176
                       placeholder='$username_default' autofocus type='text'>
177
		<label style='color: red'
Leigh B Stoller's avatar
Leigh B Stoller committed
178 179
                       for='username'>$username_error</label>
            </div>
180 181
            <div class='form-group'>
                <input name='email' id='email' type='text'
Leigh B Stoller's avatar
Leigh B Stoller committed
182
                       value='$email_value'
183
                       class='form-control'
Leigh B Stoller's avatar
Leigh B Stoller committed
184
                       placeholder='$email_default' type='text' />
185
		<label 
Leigh B Stoller's avatar
Leigh B Stoller committed
186 187 188
                       style='color: red'
                       for='email'>$email_error</label>
            </div>
189
            <div class='form-group'>
Leigh B Stoller's avatar
Leigh B Stoller committed
190 191
                <textarea id='sshkey' name='sshkey'
                          placeholder='$sshkey_default'
192
                          class='form-control'
Leigh B Stoller's avatar
Leigh B Stoller committed
193
                          rows=4 cols=45>$sshkey_value</textarea>
194
		<label
Leigh B Stoller's avatar
Leigh B Stoller committed
195 196 197
                       style='color: red'
                       for='sshkey'>$sshkey_error</label>
            </div>
Keith Downie's avatar
Keith Downie committed
198 199 200 201
            <div id='profile_well' class='form-group well well-md'>

            <span id='selected_profile_text' class='pull-left'>
            </span>
202
            <input id='selected_profile' type='hidden' name='profile'/>
Keith Downie's avatar
Keith Downie committed
203 204

              <button id='profile' class='btn btn-primary btn-xs pull-right' 
205
              type='button' name='profile_button'>
Keith Downie's avatar
Keith Downie committed
206 207 208
              Select a Profile
              </button>\n";
    echo " <label
Leigh B Stoller's avatar
Leigh B Stoller committed
209
                       style='color: red'
Leigh B Stoller's avatar
Leigh B Stoller committed
210
                       for='profile'>$profile_error</label>
Leigh B Stoller's avatar
Leigh B Stoller committed
211
            </div>
Keith Downie's avatar
Keith Downie committed
212
            <button class='btn btn-primary btn-sm pull-left'
213
                type='button' name='reset' id='reset-form'>
Keith Downie's avatar
Keith Downie committed
214 215 216 217
                      Reset Form</button>
            <button class='btn btn-success pull-right'
              type='submit' name='create'>Create!
            </button>
218
            <br> 
Keith Downie's avatar
Keith Downie committed
219
            
220 221 222
        </div>
        </div>
        </div>
Leigh B Stoller's avatar
Leigh B Stoller committed
223 224
        </div>\n";

225 226
    SpitVerifyModal("verify_modal", "Create");
    
Leigh B Stoller's avatar
Leigh B Stoller committed
227 228 229 230 231 232 233
    if ($newuser) {
	if (is_string($newuser)) {
	    $stuffing = $newuser;
	}
	else {
	    $stuffing = substr(GENHASH(), 0, 16);
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
234 235 236
	mail($email, "Confirm your email to create your Experiment",
	     "Here is your user verification code. Please copy and\n".
	     "paste this code into the box on the experiment page.\n\n".
Leigh B Stoller's avatar
Leigh B Stoller committed
237 238 239 240 241 242
	     "      $stuffing\n",
	     "From: $TBMAIL_OPS");
	echo "<input type='hidden' name='stuffing' value='$stuffing' />";
    }
    echo "</form>\n";

243
    SpitTopologyViewModal("quickvm_topomodal", $profile_array);
Keith Downie's avatar
Keith Downie committed
244

245 246
    echo "<script type='text/javascript'>\n";
    if (isset($profile) && $profile != "") {
247
        echo "window.PROFILE = '$profile_value';\n";
248 249
    }
    else {
250
        echo "window.PROFILE = '$profile_default';\n";
251 252 253 254 255 256
    }
    if ($newuser) {
	echo "window.APT_OPTIONS.isNewUser = true;\n";
    }
    echo "</script>\n";
    echo "<script src='js/lib/require.js' data-main='js/quickvm'></script>";
Leigh B Stoller's avatar
Leigh B Stoller committed
257 258 259 260 261
}

if (!isset($create)) {
    $username = null;
    $email    = null;
Leigh B Stoller's avatar
Leigh B Stoller committed
262
    $sshkey   = null;
263 264

    # 
265
    # Look for current user or cookie that tells us who the user is. 
266
    #
267 268 269 270 271
    if ($this_user) {
	$username = $this_user->uid();
	$email    = $this_user->email();
    }
    elseif (isset($_COOKIE['quickvm_user'])) {
Leigh B Stoller's avatar
Leigh B Stoller committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
	$geniuser = GeniUser::Lookup("sa", $_COOKIE['quickvm_user']);
	if ($geniuser) {
	    #
	    # Look for existing quickvm. User not allowed to create
	    # another one.
	    #
	    $quickvm = QuickVM::LookupByCreator($geniuser->uuid());
	    if ($quickvm && $quickvm->status() != "terminating") {
		header("Location: quickvm_status.php?uuid=" . $quickvm->uuid());
		return;
	    }
	    $username = $geniuser->name();
	    $email    = $geniuser->email();
	    $sshkey   = $geniuser->SSHKey();
	}
    }
288
    SPITFORM($username, $email, $sshkey, $profile, false, null);
Leigh B Stoller's avatar
Leigh B Stoller committed
289 290 291 292 293 294 295 296 297
    SPITFOOTER();
    return;
}
#
# Otherwise, must validate and redisplay if errors
#
$errors = array();
$args   = array();

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
if (!$this_user) {
    #
    # These check do not matter for a logged in user; we ignore the values.
    #
    if (!isset($email) || $email == "" || $email == $email_default) {
	$errors["email"] = "Missing Field";
    }
    elseif (! TBvalid_email($email)) {
	$errors["email"] = TBFieldErrorString();
    }
    if (!isset($username) || $username == "" ||
	$username == $username_default) {
	$errors["username"] = "Missing Field";
    }
    elseif (! TBvalid_uid($username)) {
	$errors["username"] = TBFieldErrorString();
    }
    elseif (User::LookupByUid($username)) {
        # Do not allow uid overlap with real users.
	$errors["username"] = "Already in use";
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
319
}
Leigh B Stoller's avatar
Leigh B Stoller committed
320 321
if (!isset($profile) || $profile == "") {
    $errors["profile"] = "No selection made";
Leigh B Stoller's avatar
Leigh B Stoller committed
322
}
Leigh B Stoller's avatar
Leigh B Stoller committed
323 324
elseif (! array_key_exists($profile, $profile_array)) {
    $errors["profile"] = "Invalid Profile: $profile";
Leigh B Stoller's avatar
Leigh B Stoller committed
325 326 327
}

if (count($errors)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
328
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
329 330 331 332 333 334 335
    SPITFOOTER();
    return;
}

#
# More sanity checks. 
#
336 337 338 339 340 341 342
if (!$this_user) {
    $geniuser = GeniUser::LookupByEmail("sa", $email);
    if ($geniuser) {
	if ($geniuser->name() != $username) {    
	    $errors["email"] = "Already in use by another user";
	    unset($geniuser);
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
343 344 345 346 347
    }
}
# Existing users are allowed to resuse their ssh key, but can supply
# a new one if they want.
if (!isset($sshkey) || $sshkey == "" || $sshkey == $sshkey_default) {
348
    if (!($geniuser || $this_user)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
349 350 351 352 353 354 355 356
	$errors["sshkey"] = "Missing Field";
    }
}
else {
    $args["sshkey"] = $sshkey;
}

if (count($errors)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
357
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
358 359 360
    SPITFOOTER();
    return;
}
361 362 363
# Silently ignore the form for a logged in user. 
$args["username"] = ($this_user ? $this_user->uid() : $username);
$args["email"]    = ($this_user ? $this_user->email() : $email);
Leigh B Stoller's avatar
Leigh B Stoller committed
364
$args["profile"]  = $profile;
Leigh B Stoller's avatar
Leigh B Stoller committed
365 366 367 368 369

#
# See if user exists and is verified. We send email with a code, which
# they have to paste back into a box we add to the form. See above.
#
Leigh B Stoller's avatar
Leigh B Stoller committed
370 371 372 373 374
# We also get here if the user exists, but the browser did not have
# the tokens, as will happen if switching to another browser. We
# force the user to repeat the verification with the same code we
# have stored in the DB.
#
375 376 377
if (!$this_user &&
    (!$geniuser || !isset($_COOKIE['quickvm_authkey']) ||
     $_COOKIE['quickvm_authkey'] != $geniuser->auth_token())) {
Leigh B Stoller's avatar
Leigh B Stoller committed
378 379
    if (isset($stuffing) && $stuffing != "") {
	if (! (isset($verify) && $verify == $stuffing)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
380
	    SPITFORM($username, $email, $sshkey, $profile, $stuffing, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
381 382 383
	    SPITFOOTER();
	    return;
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
384 385 386 387 388
	#
	# If this is an existing user and they give us the right code,
	# we can check again for an existing VM and redirect to the
	# status page, like we do above.
	#
389 390
	if ($geniuser) {
	    $quickvm = QuickVM::LookupByCreator($geniuser->uuid());
Leigh B Stoller's avatar
Leigh B Stoller committed
391 392 393 394 395
	    if ($quickvm && $quickvm->status() != "terminating") {
		header("Location: quickvm_status.php?uuid=" . $quickvm->uuid());
		return;
	    }
	}
Leigh B Stoller's avatar
Leigh B Stoller committed
396 397 398 399
	# Pass to backend to save in user object.
	$args["auth_token"] = $stuffing;
    }
    else {
Leigh B Stoller's avatar
Leigh B Stoller committed
400 401
	# Existing user, use existing auth token.
	# New user, we create a new one.
402
	$token = ($geniuser ? $geniuser->auth_token() : true);
Leigh B Stoller's avatar
Leigh B Stoller committed
403

Leigh B Stoller's avatar
Leigh B Stoller committed
404
	SPITFORM($username, $email, $sshkey, $profile, $token, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	SPITFOOTER();
	return;
    }
}

# This is so we can look up the slice after the backend creates it.
$args["name"] = substr(GENHASH(), 0, 16);

#
# Generate a temporary file and write in the XML goo. 
#
$xmlname = tempnam("/tmp", "quickvm");
if (! $xmlname) {
    TBERROR("Could not create temporary filename", 0);
    $errors["internal"] = "Transient error(1); please try again later.";
}
elseif (! ($fp = fopen($xmlname, "w"))) {
    TBERROR("Could not open temp file $xmlname", 0);
    $errors["internal"] = "Transient error(2); please try again later.";
}
else {
    fwrite($fp, "<quickvm>\n");
    foreach ($args as $name => $value) {
	fwrite($fp, "<attribute name=\"$name\">");
	fwrite($fp, "  <value>" . htmlspecialchars($value) . "</value>");
	fwrite($fp, "</attribute>\n");
    }
    fwrite($fp, "</quickvm>\n");
    fclose($fp);
    chmod($xmlname, 0666);
}
if (count($errors)) {
Leigh B Stoller's avatar
Leigh B Stoller committed
437
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
438 439 440 441 442 443 444 445 446 447 448
    SPITFOOTER();
    return;
}

#
# Invoke the backend. This will create the user and the slice record
# in the SA database, and then fork off in the background. If the
# first part works, we can return to the user and use some nifty ajax
# and javascript to watch for progress. We use a cookie that holds
# the slice uuid so that the JS code can ask about it.
#
449 450
# This option is used to tell the backend that it is okay to look
# in the emulab users table.
Leigh B Stoller's avatar
Leigh B Stoller committed
451
#
452 453 454
$opt = ($this_user ? "-l" : "");

$retval = SUEXEC("nobody", "nobody", "webquickvm $opt $xmlname",
Leigh B Stoller's avatar
Leigh B Stoller committed
455
		 SUEXEC_ACTION_CONTINUE);
Leigh B Stoller's avatar
Leigh B Stoller committed
456 457 458 459 460 461 462 463 464 465 466 467 468 469

if ($retval != 0) {
    if ($retval < 0) {
	$errors["internal"] = "Transient error(3); please try again later.";
    }
    else {
	if (count($suexec_output_array)) {
	    $line = $suexec_output_array[$i];
	    $errors["internal"] = $line;
	}
	else {
	    $errors["internal"] = "Transient error(4); please try again later.";
	}
    }
Leigh B Stoller's avatar
Leigh B Stoller committed
470
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
471 472 473 474 475 476 477 478
    SPITFOOTER();
    return;
}
unlink($xmlname);

$quickvm = QuickVM::LookupByName($args["name"]);
if (!$quickvm) {
    $errors["internal"] = "Transient error(5); please try again later.";
Leigh B Stoller's avatar
Leigh B Stoller committed
479
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
480 481 482
    SPITFOOTER();
    return;
}
483 484 485 486 487 488
if ($this_user) {
    $creator = $this_user;
}
else {
    $creator = GeniUser::Lookup("sa", $quickvm->creator_uuid());
}
Leigh B Stoller's avatar
Leigh B Stoller committed
489 490
if (! $creator) {
    $errors["internal"] = "Transient error(6); please try again later.";
Leigh B Stoller's avatar
Leigh B Stoller committed
491
    SPITFORM($username, $email, $sshkey, $profile, false, $errors);
Leigh B Stoller's avatar
Leigh B Stoller committed
492 493 494
    SPITFOOTER();
    return;
}
495
#
Leigh B Stoller's avatar
Leigh B Stoller committed
496
# Remember the user and auth key so that we can verify.
497 498 499 500 501 502
#
# The cookie handling is a pain since we run this under the aptlab
# virtual host, but the config uses a different domain, and so the
# cookies do not work. So, we have to look at our SERVER_NAME and
# set the cookie appropriately. 
#
503
if (!$this_user) {
Leigh B Stoller's avatar
Leigh B Stoller committed
504 505
    $cookiedomain = $TBAUTHDOMAIN;

506 507 508 509 510 511
    setcookie("quickvm_user",
	      $creator->uuid(), time() + (24 * 3600 * 30),
	      "/", $cookiedomain, 0);
    setcookie("quickvm_authkey",
	      $creator->auth_token(), time() + (24 * 3600 * 30),
	      "/", $cookiedomain, 0);
512
}
Leigh B Stoller's avatar
Leigh B Stoller committed
513 514
header("Location: quickvm_status.php?uuid=" . $quickvm->uuid());
?>