Commit 565cd3d4 authored by Leigh Stoller's avatar Leigh Stoller

Checkpoint lots of changes and bug fixes.

parent eef59cc6
......@@ -209,6 +209,8 @@ sub Create($$$$$)
$query .= ",rspec=$rspec";
$query .= ",public=1"
if (exists($argref->{'public'}) && $argref->{'public'});
$query .= ",listed=1"
if (exists($argref->{'listed'}) && $argref->{'listed'});
if (! DBQueryWarn($query)) {
DBQueryWarn("unlock tables");
......@@ -272,5 +274,25 @@ sub Delete($)
return 0;
}
#
# Mark the update time.
#
sub MarkModified()
{
my ($self) = @_;
# Must be a real reference.
return -1
if (! ref($self));
my $idx = $self->idx();
DBQueryWarn("update apt_profiles set modified=now() ".
"where idx='$idx'")
or return -1;
return 0;
}
# _Always_ make sure that this 1 is at the end of the file...
1;
......@@ -129,6 +129,7 @@ my %xmlfields =
"profile_pid" => ["pid", $SLOT_REQUIRED],
"profile_creator" => ["creator", $SLOT_OPTIONAL],
"profile_description" => ["description", $SLOT_REQUIRED|$SLOT_UPDATE],
"profile_listed" => ["listed", $SLOT_OPTIONAL|$SLOT_UPDATE],
"profile_public" => ["public", $SLOT_OPTIONAL|$SLOT_UPDATE],
"rspec" => ["rspec", $SLOT_REQUIRED|$SLOT_UPDATE],
);
......@@ -241,6 +242,8 @@ if ($update) {
}
$profile->Update(\%update_args) == 0 or
fatal("Could not update profile record");
# Bump the modtime.
$profile->MarkModified();
}
else {
my $usererror;
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2000-2013 University of Utah and the Flux Group.
# Copyright (c) 2000-2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -62,6 +62,7 @@ my $user;
use lib "@prefix@/lib";
use GeniDB;
use GeniUser;
use emutil;
# Connect to the SA DB.
GeniDB::DBConnect(GeniDB::GENISA_DBNAME());
......@@ -136,18 +137,14 @@ system("$SSH -host $CONTROL '/usr/sbin/chown -R nobody:nobody $dir'")
== 0 or fatal("Could not mkdir $dir: $!");
system("$SSH -host $CONTROL '/bin/chmod -R 700 $dir'")
== 0 or fatal("Could not mkdir $dir: $!");
system("/usr/bin/fsync $udir");
system("/usr/bin/fsync $udir/id_rsa.pub");
sleep(5);
$UID = $SAVEUID;
# Grab a copy for the DB.
my $pubkey = `cat $udir/id_rsa.pub`;
my $pubkey =
emutil::ExecQuiet("$SSH -host $CONTROL '/bin/cat $dir/id_dsa.pub'");
if ($?) {
fatal("Could not read new key from file");
}
chomp($pubkey);
$UID = $SAVEUID;
# Only one.
$geniuser->DeleteInternalKeys();
......
......@@ -503,6 +503,7 @@ my $response =
if (!defined($response) || $response->code() != GENIRESPONSE_SUCCESS) {
$slice->Delete();
SetQuickVMStatus($quickvm_uuid, "failed");
fatal("CreateSliver failed: ".
(defined($response) ? $response->output() : "") . "\n");
}
......@@ -605,6 +606,7 @@ sub Terminate($)
fatal("No such quick VM: $uuid");
}
my $row = $query_result->fetchrow_hashref();
my $status = $row->{"status"};
my $geniuser = GeniUser->Lookup($row->{'creator_uuid'}, 1);
if (!defined($geniuser)) {
......@@ -612,6 +614,9 @@ sub Terminate($)
}
my $slice = GeniSlice->Lookup($row->{'slice_uuid'});
if (!defined($slice)) {
if ($status eq "failed") {
goto done;
}
fatal("No slice for quick VM: $uuid");
}
# Create a slice credential
......@@ -660,6 +665,7 @@ sub Terminate($)
(defined($response) ? $response->output() : "") . "\n");
}
$slice->Delete();
done:
DBQueryWarn("delete from quickvms where uuid=$safe_uuid");
exit(0);
}
......
......@@ -67,8 +67,12 @@ CREATE TABLE `apt_profiles` (
`pid` varchar(48) NOT NULL default '',
`pid_idx` mediumint(8) unsigned NOT NULL default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
`uuid` varchar(40) NOT NULL,
`public` tinyint(1) NOT NULL default '0',
`shared` tinyint(1) NOT NULL default '0',
`listed` tinyint(1) NOT NULL default '0',
`weburi` tinytext,
`description` mediumtext,
`rspec` mediumtext,
PRIMARY KEY (`idx`),
......
......@@ -1273,6 +1273,8 @@ REPLACE INTO table_regex VALUES ('apt_profiles','pid','text','redirect','project
REPLACE INTO table_regex VALUES ('apt_profiles','creator','text','redirect','users:uid',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','name','text','redirect','images:imagename',0,64,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','public','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','listed','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','shared','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','description','text','redirect','default:html_fulltext',0,512,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','rspec','text','redirect','default:html_fulltext',0,8192,NULL);
......
#
# Modify APT profiles table.
#
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBSlotExists("apt_profiles", "modified")) {
DBQueryFatal("alter table apt_profiles add ".
" `modified` datetime default NULL ".
" after created");
}
if (!DBSlotExists("apt_profiles", "shared")) {
DBQueryFatal("alter table apt_profiles add ".
" `shared` tinyint(1) NOT NULL default '0' ".
" after public");
}
if (!DBSlotExists("apt_profiles", "listed")) {
DBQueryFatal("alter table apt_profiles add ".
" `listed` tinyint(1) NOT NULL default '0' ".
" after shared");
DBQueryFatal("update apt_profiles set listed=public");
}
if (!DBSlotExists("apt_profiles", "weburi")) {
DBQueryFatal("alter table apt_profiles add ".
" `weburi` tinytext ".
" after listed");
}
DBQueryFatal("REPLACE INTO table_regex VALUES ".
"('apt_profiles','listed','int','redirect',".
"'default:boolean',0,0,NULL)");
DBQueryFatal("REPLACE INTO table_regex VALUES ".
"('apt_profiles','shared','int','redirect',".
"'default:boolean',0,0,NULL)");
return 0;
}
# Local Variables:
# mode:perl
# End:
......@@ -601,12 +601,16 @@ while (my ($sim) = $query_result->fetchrow_array) {
# we need to convert port:ip above to lan:ip.
#
$query_result =
DBQueryFatal("select v.vname,member,mask,cost,vlink from virt_lans as v ".
DBQueryFatal("select v.vname,member,mask,cost,vlink,s.capkey ".
" from virt_lans as v ".
"left join virt_lan_settings as s on ".
" s.exptidx=v.exptidx and s.vname=v.vname and ".
" s.capkey='portvlan' ".
"left join virt_bridges as b on b.vname=v.bridge_vname and ".
" b.pid=v.pid and b.eid=v.eid ".
"where v.pid='$pid' and v.eid='$eid' ");
while (my ($vname,$member,$mask,$cost,$bridgelink) =
while (my ($vname,$member,$mask,$cost,$bridgelink,$lantype) =
$query_result->fetchrow_array()) {
my ($node,$port) = split(":", $member);
......@@ -620,7 +624,12 @@ while (my ($vname,$member,$mask,$cost,$bridgelink) =
$lans{$vname} = {};
$lans{$vname}->{"mask"} = $mask;
$lans{$vname}->{"cost"} = $cost;
$lans{$vname}->{"noroute"} = 0;
$lans{$vname}->{"members"} = {};
if (defined($lantype)) {
$lans{$vname}->{"noroute"} = 1;
}
}
# Store lan:ip into the portlist for the node.
......@@ -664,8 +673,9 @@ if ($experiment->geniflags()) {
last;
}
$lans{$vname} = {};
$lans{$vname}->{"mask"} = $mask;
$lans{$vname}->{"cost"} = 1;
$lans{$vname}->{"mask"} = $mask;
$lans{$vname}->{"cost"} = 1;
$lans{$vname}->{"noroute"} = 1;
}
if ($member->GetAttribute("tunnel_ip", \$ip)) {
print STDERR "Could not get ip for $member\n";
......@@ -730,12 +740,14 @@ foreach my $node (sort keys(%nodes)) {
#
# Then spit out the lans. As above, ALWAYS print the header.
#
print $OUT "# lans: vname,mask,cost\n";
print $OUT "# lans: vname,mask,cost,noroute\n";
foreach my $lan (sort keys(%lans)) {
my $cost = $lans{$lan}->{"cost"};
my $mask = $lans{$lan}->{"mask"};
print $OUT "$lan,$mask,$cost\n";
my $cost = $lans{$lan}->{"cost"};
my $mask = $lans{$lan}->{"mask"};
my $noroute = $lans{$lan}->{"noroute"};
print $OUT "$lan,$mask,$cost,$noroute\n";
}
#
......
......@@ -30,7 +30,7 @@ use Exporter;
@EXPORT =
qw ( AuditStart AuditEnd AuditAbort AuditFork AuditSetARGV AuditGetARGV
AddAuditInfo
LogStart LogEnd LogAbort
LogStart LogEnd LogAbort AuditDisconnect
LIBAUDIT_NODAEMON LIBAUDIT_DAEMON LIBAUDIT_LOGONLY
LIBAUDIT_NODELETE LIBAUDIT_FANCY LIBAUDIT_LOGTBOPS LIBAUDIT_LOGTBLOGS
);
......@@ -285,6 +285,20 @@ sub AuditGetARGV()
return @SAVEARGV;
}
sub AuditDisconnect()
{
if ($auditing) {
if (!$daemon && $PERL_VERSION >= 5.008 && $libaudit::SAVE_STDOUT) {
close($libaudit::SAVE_STDOUT);
close($libaudit::SAVE_STDERR);
open(FOO, "> /dev/null");
$libaudit::SAVE_STDOUT = *FOO;
$libaudit::SAVE_STDERR = *FOO;
}
}
}
#
# Abort an Audit. Dump the log file and do not send email.
#
......@@ -293,7 +307,7 @@ sub AuditAbort()
if ($auditing) {
$auditing = 0;
if (!$daemon && $PERL_VERSION >= 5.008) {
if (!$daemon && $PERL_VERSION >= 5.008 && $libaudit::SAVE_STDOUT) {
eval("open(STDOUT, \">&\", \$libaudit::SAVE_STDOUT); ".
"open(STDERR, \">&\", \$libaudit::SAVE_STDERR);");
}
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2003-2012 University of Utah and the Flux Group.
# Copyright (c) 2003-2012, 2014 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -35,16 +35,19 @@ use Getopt::Std;
sub usage()
{
print STDERR "Usage: grantnodetype [-h] [-r] [-m] -p <pid> <type> [count]\n";
print STDERR " grantnodetype [-h] -R <type>\n";
print STDERR " -h This message\n";
print STDERR " -r Revoke access instead of grant\n";
print STDERR " -m Force insert of missing minus policy\n";
print STDERR " -R Remove all projects for type, and minus policy\n";
print STDERR " count Overdide default count of 999999\n";
exit(-1);
}
my $optlist = "hp:dnrm";
my $optlist = "hp:dnrmR";
my $impotent = 0;
my $debug = 0;
my $revoke = 0;
my $revokeall= 0;
my $addminus = 0;
my %newtypes = ();
my $pid;
......@@ -106,13 +109,17 @@ if (defined($options{n})) {
if (defined($options{r})) {
$revoke = 1;
}
if (defined($options{R})) {
$revokeall = 1;
}
if (defined($options{d})) {
$debug = 1;
}
if (defined($options{p})) {
$pid = $options{p};
}
if (scalar(@ARGV) < 1 || scalar(@ARGV) > 2 || !defined($pid)) {
if (!$revokeall &&
(scalar(@ARGV) < 1 || scalar(@ARGV) > 2 || !defined($pid))) {
usage();
}
my $type = $ARGV[0];
......@@ -128,6 +135,19 @@ else {
die("Tainted type name: $type");
}
#
# Clear the type from the group_policies table, including the minus policy,
# which allows the type to be used by everyone.
#
if ($revokeall) {
DBQueryFatal("delete from group_policies ".
"where policy='type' and auxdata='$type'")
if (!$impotent);
system($update_perms);
exit($? >> 8);
}
if ($pid =~ /^([-\w]+)$/) {
$pid = $1;
}
......
......@@ -43,9 +43,10 @@ function ($, sup)
console.info(topo);
sup.ShowModal("#quickvm_topomodal");
sup.maketopmap("#showtopo_div",
($("#showtopo_dialog").outerWidth()) - 90,
// Subtract -2 cause of the border.
sup.maketopmap("#showtopo_nopicker",
($("#showtopo_nopicker").outerWidth() - 2),
300, topo);
}
......
......@@ -16,7 +16,6 @@ function ($, sup)
$('#quickvm_topomodal').on('hidden.bs.modal', function() {
sup.ShowProfileList($('.current'))
});
$('button#profile').click(function (event) {
event.preventDefault();
sup.ShowModal('#quickvm_topomodal');
......@@ -30,6 +29,12 @@ function ($, sup)
sup.UpdateProfileSelection($('.selected'));
sup.HideModal('#quickvm_topomodal');
});
// We have to get the selected profile from the hidden form variable.
$('a#instantiate').click(function (event) {
event.preventDefault();
var profile = $('#selected_profile').attr('value');
window.location.replace("quickvm.php?profile=" + profile);
});
}
$(document).ready(initialize);
......
......@@ -130,8 +130,9 @@ function ShowTopo(uuid)
console.info(topo);
$("#showtopo_container").removeClass("invisible");
// Subtract -2 cause of the border.
maketopmap("#showtopo_container",
$("#showtopo_div").width() - 30,
$("#showtopo_statuspage").outerWidth() - 2,
300, topo);
}
......@@ -568,6 +569,10 @@ function StartCountdownClock(when)
// Clock reset
if (StartCountdownClock.reset != when) {
when = StartCountdownClock.reset;
if (when === "n/a") {
StartCountdownClock.stop = 1;
return;
}
// Reformat in local time and show the user.
var local_date = new Date(when);
......@@ -818,11 +823,22 @@ function ConvertManifestToJSON(name, xml)
"nodes": [],
"links": [],
};
var interfaces = new Array();
var count = 0;
$(xml).find("node").each(function(){
var client_id = $(this).attr("client_id");
var jobj = {"name" : client_id};
$(this).find("interface").each(function() {
var interface_id = $(this).attr("client_id");
var interface = new Object();
interface.client_id = interface_id;
interface.node_id = client_id;
interface.node_index = count;
interfaces.push(interface);
});
var login = $(this).find("login");
if (login) {
var user = login.attr("username");
......@@ -834,18 +850,22 @@ function ConvertManifestToJSON(name, xml)
jobj.hostport = host + ":" + port;
jobj.sshurl = sshurl;
}
json.nodes.push(jobj);
json.nodes[count] = jobj;
count++;
});
$(xml).find("link").each(function(){
var client_id = $(this).attr("client_id");
var link_type = $(this).find("link_type");
var ifacerefs = $(this).find("interface_ref");
if (link_type && $(link_type).attr("name") == "lan") {
console.info("Oops, a lan");
if (ifacerefs.length < 2) {
console.info("Oops, not enough interfaces in " + client_id);
}
else if (ifacerefs.length > 2) {
console.info("Oops, too many interfaces in " + client_id);
}
else {
var ifacerefs = $(this).find("interface_ref");
var source = ifacerefs[0];
var target = ifacerefs[1];
......@@ -854,20 +874,26 @@ function ConvertManifestToJSON(name, xml)
var source_ifname = source.attr("client_id");
var target_ifname = target.attr("client_id");
var source_ifpair = source_ifname.split(":");
var target_ifpair = target_ifname.split(":");
var source_name = source_ifpair[0];
var target_name = target_ifpair[0];
var source_name = null;
var target_name = null;
var source_index = null;
var target_index = null;
// Javascript does not do dictionaries. Too bad.
for (i = 0; i < json.nodes.length; i++) {
if (json.nodes[i].name == source_name) {
source_index = i;
/*
* First we have map the client_ids to the node by
* searching all of the interfaces we put into the
* list above.
*
* Javascript does not do dictionaries. Too bad.
*/
for (i = 0; i < interfaces.length; i++) {
if (interfaces[i].client_id == source_ifname) {
source_name = interfaces[i].node_id;
source_index = interfaces[i].node_index;
}
if (json.nodes[i].name == target_name) {
target_index = i;
if (interfaces[i].client_id == target_ifname) {
target_name = interfaces[i].node_id;
target_index = interfaces[i].node_index;
}
}
json.links.push({"name" : client_id,
......@@ -916,11 +942,16 @@ function LoginByModal()
else {
// Clear previous error.
$("#quickvm_login_form_error").html("");
HideModal("#quickvm_login_modal");
$("#loginstatus").html("<a>" + uid + " logged in</a>");
$("#loginstatus").removeClass("hidden");
$("#quickvm_actions_menu").removeClass("hidden");
$("#loginbutton").addClass("hidden");
$("#quickvm_login_form_error").html(
"<center>" + "Login successful</center><br>");
setTimeout(function() {
HideModal("#quickvm_login_modal");
$("#quickvm_login_form_error").html("");
}, 2000);
}
}
var xmlthing = $.ajax({
......
......@@ -27,6 +27,11 @@ chdir("apt");
include("quickvm_sup.php");
$page_title = "Login";
#
# Get current user in case we need an error message.
#
$this_user = CheckLogin($check_status);
#
# Verify page arguments.
#
......@@ -71,6 +76,11 @@ function SPITFORM($uid, $referrer, $error)
{
global $TBDB_UIDLEN, $TBBASE, $refer;
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache, max-age=0, must-revalidate, no-store");
header("Pragma: no-cache");
SPITHEADER();
echo "<div class='row'>
......@@ -98,6 +108,9 @@ function SPITFORM($uid, $referrer, $error)
case "timedout":
echo "Your login has timed out!";
break;
case "alreadyloggedin":
echo "You are already logged in. Logout first?";
break;
default:
echo "Unknown Error ($error)!";
}
......@@ -140,7 +153,8 @@ function SPITFORM($uid, $referrer, $error)
# If not clicked, then put up a form.
#
if (!$ajax_request && !isset($login)) {
SPITFORM(REMEMBERED_ID(), $referrer, null);
SPITFORM(REMEMBERED_ID(), $referrer,
($this_user ? "alreadyloggedin" : null));
return;
}
......@@ -182,6 +196,11 @@ else {
}
}
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Cache-Control: no-cache, max-age=0, must-revalidate, no-store");
header("Pragma: no-cache");
#
# Failed, then try again with an error message.
#
......
......@@ -53,5 +53,5 @@ if ($ajax_request) {
SPITAJAX_RESPONSE("");
exit();
}
header("Location: quickvm.php");
header("Location: login.php");
?>
......@@ -25,7 +25,9 @@ chdir("..");
include("defs.php3");
chdir("apt");
include("quickvm_sup.php");
include("profile_defs.php");
$page_title = "Manage Profile";
$notifyupdate = 0;
#
# Get current user.
......@@ -38,6 +40,7 @@ $this_user = CheckLogin($check_status);
$optargs = OptionalPageArguments("create", PAGEARG_STRING,
"action", PAGEARG_STRING,
"idx", PAGEARG_INTEGER,
"finished", PAGEARG_BOOLEAN,
"formfields", PAGEARG_ARRAY);
#
......@@ -45,7 +48,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
#
function SPITFORM($formfields, $errors)
{
global $this_user, $projlist, $action, $idx;
global $this_user, $projlist, $action, $idx, $notifyupdate;
$editing = 0;
if ($action == "edit") {
......@@ -118,7 +121,11 @@ function SPITFORM($formfields, $errors)
# Look for non-specific error.
#
if ($errors && array_key_exists("error", $errors)) {
echo "<font color=red>" . $errors["error"] . "</font>";
echo "<font color=red><center>" . $errors["error"] . "</center></font>";
}
# Did we just complete an update.
if ($notifyupdate) {
echo "<font color=green><center>Update Successful!</center></font>";
}
# Mark as editing mode on post.
if ($editing) {
......@@ -215,11 +222,11 @@ function SPITFORM($formfields, $errors)
}
$formatter("profile_rspec", "Your rspec", $rspec_html);
$formatter("profile_public", "Public?",
$formatter("profile_listed", "Listed?",
"<div class='checkbox'>
<label><input name=\"formfields[profile_public]\" ".
$formfields["profile_public"] .
" id='profile_public' value=checked
<label><input name=\"formfields[profile_listed]\" ".
$formfields["profile_listed"] .
" id='profile_listed' value=checked
type=checkbox> ".
"List on the public page for anyone to use?</label></div>");
......@@ -227,15 +234,15 @@ function SPITFORM($formfields, $errors)
echo "<div class='form-group'>
<div class='col-sm-offset-2 col-sm-10'>
<button class='btn btn-primary btm-xs pull-right'
<button class='btn btn-primary btn-sm pull-right'
id='profile_submit_button'
type='submit' name='create'>$button_label</button>\n";
echo " <a class='btn btn-primary btm-xs pull-right'
if ($editing) {
echo " <a class='btn btn-primary btn-sm pull-right'
style='margin-right: 10px;'
href='quickvm.php?profile=$idx'
type='submit' name='create'>Instantiate</a>\n";
if ($editing) {
echo " <a class='btn btn-danger btm-xs pull-left'
echo " <a class='btn btn-danger btn-sm pull-left'
style='margin-right: 10px;'
href='manage_profile.php?action=delete&idx=$idx'
type='button' name='delete'>Delete</a>\n";
......@@ -292,7 +299,7 @@ function SPITFORM($formfields, $errors)
<div class='panel panel-default'
id='showtopo_container'>
<div class='panel-body'>
<div id='showtopo_div'></div>
<div id='showtopo_nopicker'></div>
</div>
</div>
</div>
......@@ -336,11 +343,9 @@ if (! isset($create)) {
$errors["error"] = "No profile specified for edit/delete!";
}
else {
$query_result =
DBQueryFatal("select * from apt_profiles ".
"where idx='$idx' and creator_idx='$this_idx'");
if (!$query_result || !mysql_num_rows($query_result)) {
$profile = Profile::Lookup($idx);
if (!$profile || $this_idx != $profile->creator_idx()) {
$errors["error"] = "No such profile!";
}
else if ($action == "delete") {
......@@ -349,13 +354,30 @@ if (! isset($create)) {
return;
}
else {
$row = mysql_fetch_array($query_result);
$defaults["profile_pid"] = $row["pid"];
$defaults["profile_description"] = $row["description"];
$defaults["profile_name"] = $row["name"];
$defaults["profile_rspec"] = $row["rspec"];
$defaults["profile_public"] =
($row["public"] ? "checked" : "");
$defaults["profile_pid"] = $profile->pid();
$defaults["profile_description"] = $profile->description();
$defaults["profile_name"] = $profile->name();
$defaults["profile_rspec"] = $profile->rspec();
$defaults["profile_listed"] =
($profile->listed() ? "checked" : "");
#
# If we are displaying after a successful edit, and it
# just happened (by looking at the modify time), show
# a message that the update was successful. This is pretty
# crappy, but I do not want to go for a fancy thing (popover)
# just yet, maybe later.
#
if (isset($finished) && $profile->modified()) {
$mod = new DateTime($profile->modified());
if ($mod) {
$now = new DateTime("now");
$diff = $now->getTimestamp() - $mod->getTimestamp();
if ($diff < 2) {
$notifyupdate = 1;
}
}
}
}
}
}
......@@ -470,12 +492,15 @@ else {
fwrite($fp, "<attribute name='rspec'>");
fwrite($fp, " <value>" . htmlspecialchars($rspec) . "</value>");
fwrite($fp, "</attribute>\n");
if (isset($formfields["profile_public"]) &&
$formfields["profile_public"] == "checked") {
fwrite($fp, "<attribute name='profile_public'>");
fwrite($fp, " <value>1</value>");
fwrite($fp, "</attribute>\n");
fwrite($fp, "<attribute name='profile_listed'><value>");
if (isset($formfields["profile_listed"]) &&
$formfields["profile_listed"] == "checked") {
fwrite($fp, "1");
}
else {
fwrite($fp, "0");