Merge branch 'develop' into VCL-1114_misc_small_web_updates
diff --git a/web/.ht-inc/authmethods/ldapauth.php b/web/.ht-inc/authmethods/ldapauth.php
index 1c2ee53..3848ebe 100644
--- a/web/.ht-inc/authmethods/ldapauth.php
+++ b/web/.ht-inc/authmethods/ldapauth.php
@@ -257,7 +257,7 @@
 function validateLDAPUser($type, $loginid) {
 	global $authMechs;
 	$auth = $authMechs[$type];
-	$savehdlr = set_error_handler(create_function('', ''));
+	$savehdlr = set_error_handler(function() {});
 	if(! ($fh = fsockopen($auth['server'], 636, $errno, $errstr, 5)))
 		return -1;
 	set_error_handler($savehdlr);
diff --git a/web/.ht-inc/blockallocations.php b/web/.ht-inc/blockallocations.php
index 9bf25e2..ed0fffa 100644
--- a/web/.ht-inc/blockallocations.php
+++ b/web/.ht-inc/blockallocations.php
@@ -1448,15 +1448,15 @@
 	$rt .= "title=\"" . i("Block Allocation Times") . "\">\n";
 	$rt .= "<h2>" . i("Block Allocation Times") . "</h2>\n";
 	$rt .= "<table dojoType=\"dojox.grid.DataGrid\" jsId=\"blockTimesGrid\" sortInfo=1 ";
-	$rt .= "style=\"width: 278px; height: 200px;\">\n";
+	$rt .= "style=\"width: 328px; height: 200px;\">\n";
 	$rt .= "<script type=\"dojo/method\" event=\"onStyleRow\" args=\"row\">\n";
 	$rt .= "blockTimeRowStyle(row);\n";
 	$rt .= "</script>\n";
 	$rt .= "<thead>\n";
 	$rt .= "<tr>\n";
-	$rt .= "<th field=\"start\" width=\"65px\" formatter=\"blockTimesGridDate\">" . i("Date") . "</th>\n";
-	$rt .= "<th field=\"start\" width=\"54px\" formatter=\"blockTimesGridStart\">" . i("Start") . "</th>\n";
-	$rt .= "<th field=\"end\" width=\"54px\" formatter=\"blockTimesGridEnd\">" . i("End") . "</th>\n";
+	$rt .= "<th field=\"start\" width=\"70px\" formatter=\"blockTimesGridDate\">" . i("Date") . "</th>\n";
+	$rt .= "<th field=\"start\" width=\"85px\" formatter=\"blockTimesGridStart\">" . i("Start") . "</th>\n";
+	$rt .= "<th field=\"end\" width=\"85px\" formatter=\"blockTimesGridEnd\">" . i("End") . "</th>\n";
 	$rt .= "<th field=\"delbtn\" width=\"60px\">" . i("Skip") . "</th>\n";
 	$rt .= "</tr>\n";
 	$rt .= "</thead>\n";
@@ -1741,17 +1741,17 @@
 	$rt .= i("Block Allocation Times") . "\">\n";
 	$rt .= "<h2>" . i("Block Allocation Times") . "</h2>\n";
 	$rt .= "<table dojoType=\"dojox.grid.DataGrid\" jsId=\"blockTimesGrid\" sortInfo=1 ";
-	$rt .= "style=\"width: 278px; height: 200px;\">\n";
+	$rt .= "style=\"width: 328px; height: 200px;\">\n";
 	$rt .= "<script type=\"dojo/method\" event=\"onStyleRow\" args=\"row\">\n";
 	$rt .= "blockTimeRowStyle(row);\n";
 	$rt .= "</script>\n";
 	$rt .= "<thead>\n";
 	$rt .= "<tr>\n";
-	$rt .= "<th field=\"start\" width=\"60px\" formatter=\"blockTimesGridDate\">";
+	$rt .= "<th field=\"start\" width=\"70px\" formatter=\"blockTimesGridDate\">";
 	$rt .= i("Date") . "</th>\n";
-	$rt .= "<th field=\"start\" width=\"54px\" formatter=\"blockTimesGridStart\">";
+	$rt .= "<th field=\"start\" width=\"85px\" formatter=\"blockTimesGridStart\">";
 	$rt .= i("Start") . "</th>\n";
-	$rt .= "<th field=\"end\" width=\"54px\" formatter=\"blockTimesGridEnd\">";
+	$rt .= "<th field=\"end\" width=\"85px\" formatter=\"blockTimesGridEnd\">";
 	$rt .= i("End") . "</th>\n";
 	$rt .= "<th field=\"delbtn\" width=\"60px\">" . i("Skip") . "</th>\n";
 	$rt .= "</tr>\n";
@@ -3133,35 +3133,41 @@
 	if(! $err) {
 		if($type == 'list') {
 			$slots = processInputVar('slots', ARG_STRING);
-			$return['slots'] = explode(',', $slots);
+			if(! preg_match('/^(\d{8}\|\d{2}:\d{2}\|\d{2}:\d{2})(,(\d{8}\|\d{2}:\d{2}\|\d{2}:\d{2}))*$/', $slots)) {
+				$errmsg = i("Invalid time slot submitted.");
+				$err = 1;
+			}
 			$return['times'] = array();
-			$lastdate = array('day' => '', 'ts' => 0);
-			foreach($return['slots'] as $slot) {
-				$tmp = explode('|', $slot);
-				if(count($tmp) != 3) {
-					$errmsg = i("Invalid date/time submitted.");
-					$err = 1;
-					break;
-				}
-				$date = $tmp[0];
-				if(! $err) {
-					$datets = strtotime($date);
-					if($method != 'edit' && $datets < (time() - SECINDAY)) {
-						$errmsg = i("The date must be today or later.");
+			if(! $err) {
+				$return['slots'] = explode(',', $slots);
+				$lastdate = array('day' => '', 'ts' => 0);
+				foreach($return['slots'] as $slot) {
+					$tmp = explode('|', $slot);
+					if(count($tmp) != 3) {
+						$errmsg = i("Invalid date/time submitted.");
 						$err = 1;
 						break;
 					}
+					$date = $tmp[0];
+					if(! $err) {
+						$datets = strtotime($date);
+						if($method != 'edit' && $datets < (time() - SECINDAY)) {
+							$errmsg = i("The date must be today or later.");
+							$err = 1;
+							break;
+						}
+					}
+					$return['times'][] = "{$tmp[1]}|{$tmp[2]}";
+					if($datets > $lastdate['ts']) {
+						$lastdate['ts'] = $datets;
+						$lastdate['day'] = $date;
+					}
 				}
-				$return['times'][] = "{$tmp[1]}|{$tmp[2]}";
-				if($datets > $lastdate['ts']) {
-					$lastdate['ts'] = $datets;
-					$lastdate['day'] = $date;
+				if(! $err) {
+					$expirets = strtotime("{$lastdate['day']} 23:59:59");
+					$return['expiretime'] = unixToDatetime($expirets);
 				}
 			}
-			if(! $err) {
-				$expirets = strtotime("{$lastdate['day']} 23:59:59");
-				$return['expiretime'] = unixToDatetime($expirets);
-			}
 		}
 		if($type == 'weekly' || $type == 'monthly') {
 			$return['startdate'] = processInputVar('startdate', ARG_NUMERIC);
diff --git a/web/.ht-inc/computer.php b/web/.ht-inc/computer.php
index 3a9e117..b67a75a 100644
--- a/web/.ht-inc/computer.php
+++ b/web/.ht-inc/computer.php
@@ -4280,27 +4280,33 @@
 
 		$query = "SELECT rs.computerid "
 		       . "FROM request rq, "
-		       .      "reservation rs "
+		       .      "reservation rs, "
+		       .      "state s "
 		       . "WHERE rs.requestid = rq.id AND "
+		       .       "rq.stateid = s.id AND "
 		       .       "rs.computerid IN ($allids) AND "
 		       .       "rq.start <= '$startcheckdt' AND "
-		       .       "rq.end > NOW()";
+		       .       "rq.end > NOW() AND "
+		       .       "s.name != 'complete'";
 		$qh = doQuery($query);
 		while($row = mysqli_fetch_assoc($qh))
 			$fails[] = $row['computerid'];
 
 		$nowids = array_diff($compids, $fails);
-		$allids = implode(',', $nowids);
-		$query = "UPDATE computer "
-		       . "SET provisioningid = $provisioningid "
-		       . "WHERE id in ($allids)";
-		doQuery($query);
+		if(! empty($nowids)) {
+			$allids = implode(',', $nowids);
+			$query = "UPDATE computer "
+			       . "SET provisioningid = $provisioningid "
+			       . "WHERE id in ($allids)";
+			doQuery($query);
+		}
 
 		$resources = getUserResources(array($this->restype . "Admin"), array("administer"));
 		$compdata = $resources[$this->restype];
 
+		$msg = '';
 		if(count($nowids)) {
-			$msg  = "The following computers had their Provisioning Engine set to $provname:<br><br>\n";
+			$msg  .= "The following computers had their Provisioning Engine set to $provname:<br><br>\n";
 			foreach($nowids as $compid)
 				$msg .= "{$compdata[$compid]}<br>\n";
 			$msg .= "<br>";
@@ -4872,9 +4878,11 @@
 		       .        "i.prettyname AS image, "
 		       .        "ir.revision, "
 		       .        "c.hostname AS hostname, "
+		       .        "s.IPaddress, "
 		       .        "mn.hostname AS managementnode, "
 		       .        "l.ending, "
-		       .        "CONCAT(u.unityid, '@', a.name) AS username "
+		       .        "CONCAT(u.unityid, '@', a.name) AS username, "
+		       .        "l.requestid "
 		       . "FROM computer c "
 		       . "LEFT JOIN sublog s ON (c.id = s.computerid) "
 		       . "LEFT JOIN image i ON (s.imageid = i.id) "
@@ -4907,6 +4915,8 @@
 				$msg .= "End: " . prettyDatetime($row['end'], 1) . "<br>";
 			$msg .= "Management Node: {$row['managementnode']}<br>";
 			$msg .= "Ending: {$row['ending']}<br>";
+			$msg .= "Request ID: {$row['requestid']}<br>";
+			$msg .= "IP Address: {$row['IPaddress']}<br>";
 			$msg .= "<hr>";
 			$data[] = array('name' => $row['hostname'], 'msg' => $msg);
 		}
diff --git a/web/.ht-inc/conf-default.php b/web/.ht-inc/conf-default.php
index 50f204b..cff15a3 100644
--- a/web/.ht-inc/conf-default.php
+++ b/web/.ht-inc/conf-default.php
@@ -183,9 +183,9 @@
 		$updateUserFuncArgs[$item['affiliationid']] = $key;
 	}
 	elseif($item['type'] == 'local') {
-		$affilValFunc[$item['affiliationid']] = create_function('', 'return 0;');
-		$addUserFunc[$item['affiliationid']] = create_function('', 'return NULL;');
-		$updateUserFunc[$item['affiliationid']] = create_function('', 'return NULL;');
+		$affilValFunc[$item['affiliationid']] = function() {return 0;};
+		$addUserFunc[$item['affiliationid']] = function() {return NULL;};
+		$updateUserFunc[$item['affiliationid']] = function() {return NULL;};
 	}
 }
 
diff --git a/web/.ht-inc/dashboard.php b/web/.ht-inc/dashboard.php
index 3c2df3f..9018c20 100644
--- a/web/.ht-inc/dashboard.php
+++ b/web/.ht-inc/dashboard.php
@@ -42,7 +42,7 @@
 
 	# -------- left column ---------
 	print "<div id=\"dashleft\">\n";
-	print addWidget('status', 'Current Status');
+	print addWidget('status', 'Current Status', '(Hover for description)');
 	print addWidget('topimages', 'Top 5 Images in Use', '(Reservations &lt; 24 hours long)');
 	print addWidget('toplongimages', 'Top 5 Long Term Images in Use', '(Reservations &gt; 24 hours long)');
 	print addWidget('toppastimages', 'Top 5 Images From Past Day', '(Reservations with a start<br>time within past 24 hours)');
@@ -168,10 +168,11 @@
 function getStatusData() {
 	$affilid = getDashboardAffilID();
 	$data = array();
-	$data[] = array('key' => 'Active Reservations', 'val' => 0);
-	$data[] = array('key' => 'Online Computers', 'val' => 0, 'tooltip' => 'Computers in states available, reserved,<br>reloading, inuse, or timeout');
-	$data[] = array('key' => 'In Use Computers', 'val' => 0, 'tooltip' => 'Computers in inuse state');
-	$data[] = array('key' => 'Failed Computers', 'val' => 0);
+	$data[0] = array('key' => 'Active Reservations', 'val' => 0, 'tooltip' => 'Reservations currently running');
+	$data[1] = array('key' => 'Active Short Reservations', 'val' => 0, 'tooltip' => 'Reservations with a duration < 24 hours');
+	$data[2] = array('key' => 'Online Computers', 'val' => 0, 'tooltip' => 'Computers in states available, reserved,<br>reloading, inuse, or timeout');
+	$data[3] = array('key' => 'In Use Computers', 'val' => 0, 'tooltip' => 'Computers in inuse state');
+	$data[4] = array('key' => 'Failed Computers', 'val' => 0);
 	$reloadid = getUserlistID('vclreload@Local');
 	if($affilid == 0) {
 		$query = "SELECT COUNT(id) "
@@ -196,20 +197,45 @@
 	if($row = mysqli_fetch_row($qh))
 		$data[0]['val'] = $row[0];
 
-	$query = "SELECT COUNT(id) FROM computer WHERE stateid IN (2, 3, 6, 8, 11)";
+	if($affilid == 0) {
+		$query = "SELECT COUNT(id) "
+		       . "FROM request "
+		       . "WHERE userid != $reloadid AND "
+		       .       "stateid NOT IN (1, 5, 10, 12) AND "
+		       .       "start < NOW() AND "
+				 .       "end > NOW() AND "
+		       .       "(UNIX_TIMESTAMP(end) - UNIX_TIMESTAMP(start)) < 86400";
+	}
+	else {
+		$query = "SELECT COUNT(rq.id) "
+		       . "FROM request rq, "
+		       .      "user u "
+		       . "WHERE rq.userid != $reloadid AND "
+		       .       "rq.userid = u.id AND "
+		       .       "u.affiliationid = $affilid AND "
+		       .       "rq.stateid NOT IN (1, 5, 10, 12) AND "
+		       .       "rq.start < NOW() AND "
+				 .       "rq.end > NOW() AND "
+		       .       "(UNIX_TIMESTAMP(end) - UNIX_TIMESTAMP(start)) < 86400";
+	}
 	$qh = doQuery($query, 101);
 	if($row = mysqli_fetch_row($qh))
 		$data[1]['val'] = $row[0];
 
-	$query = "SELECT COUNT(id) FROM computer WHERE stateid = 8";
+	$query = "SELECT COUNT(id) FROM computer WHERE stateid IN (2, 3, 6, 8, 11)";
 	$qh = doQuery($query, 101);
 	if($row = mysqli_fetch_row($qh))
 		$data[2]['val'] = $row[0];
 
-	$query = "SELECT COUNT(id) FROM computer WHERE stateid = 5";
+	$query = "SELECT COUNT(id) FROM computer WHERE stateid = 8";
 	$qh = doQuery($query, 101);
 	if($row = mysqli_fetch_row($qh))
 		$data[3]['val'] = $row[0];
+
+	$query = "SELECT COUNT(id) FROM computer WHERE stateid = 5";
+	$qh = doQuery($query, 101);
+	if($row = mysqli_fetch_row($qh))
+		$data[4]['val'] = $row[0];
 	return $data;
 }
 
@@ -674,6 +700,7 @@
 function getNewReservationData() {
 	$affilid = getDashboardAffilID();
 	$query = "SELECT c.hostname AS computer, "
+	       .        "h.hostname AS vmhost, "
 	       .        "i.prettyname AS image, "
 	       .        "rq.id, "
 	       .        "UNIX_TIMESTAMP(rq.start) AS start, "
@@ -684,6 +711,8 @@
 	       . "FROM request rq "
 	       . "LEFT JOIN reservation rs ON (rs.requestid = rq.id) "
 	       . "LEFT JOIN computer c ON (c.id = rs.computerid) "
+	       . "LEFT JOIN vmhost vh ON (c.vmhostid = vh.id) "
+	       . "LEFT JOIN computer h ON (h.id = vh.computerid) "
 	       . "LEFT JOIN image i ON (i.id = rs.imageid) "
 	       . "LEFT JOIN OS o ON (o.id = i.OSid) "
 	       . "LEFT JOIN state s1 ON (s1.id = rq.stateid) "
@@ -703,6 +732,8 @@
 	while($row = mysqli_fetch_assoc($qh)) {
 		$tmp = explode('.', $row['computer']);
 		$row['computer'] = $tmp[0];
+		$tmp = explode('.', $row['vmhost']);
+		$row['vmhost'] = $tmp[0];
 		$row['start'] = date('D h:i', $row['start']);
 		$tmp = explode('.', $row['managementnode']);
 		$row['managementnode'] = $tmp[0];
diff --git a/web/.ht-inc/doxyfile.xmlrpc b/web/.ht-inc/doxyfile.xmlrpc
index 4880847..f8519e5 100644
--- a/web/.ht-inc/doxyfile.xmlrpc
+++ b/web/.ht-inc/doxyfile.xmlrpc
@@ -1,5 +1,3 @@
-# Doxyfile 1.4.7
-
 #  Licensed to the Apache Software Foundation (ASF) under one or more
 #  contributor license agreements.  See the NOTICE file distributed with
 #  this work for additional information regarding copyright ownership.
@@ -14,10 +12,6 @@
 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #  See the License for the specific language governing permissions and
 #  limitations under the License.
-
-#---------------------------------------------------------------------------
-# Project related configuration options
-#---------------------------------------------------------------------------
 PROJECT_NAME           = "VCL XML RPC"
 PROJECT_NUMBER         = 
 OUTPUT_DIRECTORY       = xmlrpcdocs/
@@ -53,9 +47,6 @@
 BUILTIN_STL_SUPPORT    = NO
 DISTRIBUTE_GROUP_DOC   = NO
 SUBGROUPING            = NO
-#---------------------------------------------------------------------------
-# Build related configuration options
-#---------------------------------------------------------------------------
 EXTRACT_ALL            = NO
 EXTRACT_PRIVATE        = NO
 EXTRACT_STATIC         = NO
@@ -81,9 +72,6 @@
 MAX_INITIALIZER_LINES  = 30
 SHOW_USED_FILES        = NO
 FILE_VERSION_FILTER    = 
-#---------------------------------------------------------------------------
-# configuration options related to warning and progress messages
-#---------------------------------------------------------------------------
 QUIET                  = NO
 WARNINGS               = YES
 WARN_IF_UNDOCUMENTED   = YES
@@ -91,9 +79,6 @@
 WARN_NO_PARAMDOC       = NO
 WARN_FORMAT            = "$file:$line: $text"
 WARN_LOGFILE           = 
-#---------------------------------------------------------------------------
-# configuration options related to the input files
-#---------------------------------------------------------------------------
 INPUT                  = .
 FILE_PATTERNS          = xmlrpcWrappers.php
 RECURSIVE              = NO
@@ -107,9 +92,6 @@
 INPUT_FILTER           = 
 FILTER_PATTERNS        = 
 FILTER_SOURCE_FILES    = NO
-#---------------------------------------------------------------------------
-# configuration options related to source browsing
-#---------------------------------------------------------------------------
 SOURCE_BROWSER         = NO
 INLINE_SOURCES         = NO
 STRIP_CODE_COMMENTS    = YES
@@ -118,15 +100,9 @@
 REFERENCES_LINK_SOURCE = YES
 USE_HTAGS              = NO
 VERBATIM_HEADERS       = NO
-#---------------------------------------------------------------------------
-# configuration options related to the alphabetical class index
-#---------------------------------------------------------------------------
 ALPHABETICAL_INDEX     = NO
 COLS_IN_ALPHA_INDEX    = 5
 IGNORE_PREFIX          = 
-#---------------------------------------------------------------------------
-# configuration options related to the HTML output
-#---------------------------------------------------------------------------
 GENERATE_HTML          = YES
 HTML_OUTPUT            = .
 HTML_FILE_EXTENSION    = .html
@@ -143,9 +119,6 @@
 ENUM_VALUES_PER_LINE   = 4
 GENERATE_TREEVIEW      = NO
 TREEVIEW_WIDTH         = 250
-#---------------------------------------------------------------------------
-# configuration options related to the LaTeX output
-#---------------------------------------------------------------------------
 GENERATE_LATEX         = NO
 LATEX_OUTPUT           = latex
 LATEX_CMD_NAME         = latex
@@ -158,44 +131,26 @@
 USE_PDFLATEX           = NO
 LATEX_BATCHMODE        = NO
 LATEX_HIDE_INDICES     = NO
-#---------------------------------------------------------------------------
-# configuration options related to the RTF output
-#---------------------------------------------------------------------------
 GENERATE_RTF           = NO
 RTF_OUTPUT             = rtf
 COMPACT_RTF            = NO
 RTF_HYPERLINKS         = NO
 RTF_STYLESHEET_FILE    = 
 RTF_EXTENSIONS_FILE    = 
-#---------------------------------------------------------------------------
-# configuration options related to the man page output
-#---------------------------------------------------------------------------
 GENERATE_MAN           = NO
 MAN_OUTPUT             = man
 MAN_EXTENSION          = .3
 MAN_LINKS              = NO
-#---------------------------------------------------------------------------
-# configuration options related to the XML output
-#---------------------------------------------------------------------------
 GENERATE_XML           = NO
 XML_OUTPUT             = xml
 XML_SCHEMA             = 
 XML_DTD                = 
 XML_PROGRAMLISTING     = YES
-#---------------------------------------------------------------------------
-# configuration options for the AutoGen Definitions output
-#---------------------------------------------------------------------------
 GENERATE_AUTOGEN_DEF   = NO
-#---------------------------------------------------------------------------
-# configuration options related to the Perl module output
-#---------------------------------------------------------------------------
 GENERATE_PERLMOD       = NO
 PERLMOD_LATEX          = NO
 PERLMOD_PRETTY         = YES
 PERLMOD_MAKEVAR_PREFIX = 
-#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor   
-#---------------------------------------------------------------------------
 ENABLE_PREPROCESSING   = YES
 MACRO_EXPANSION        = NO
 EXPAND_ONLY_PREDEF     = NO
@@ -205,17 +160,11 @@
 PREDEFINED             = 
 EXPAND_AS_DEFINED      = 
 SKIP_FUNCTION_MACROS   = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to external references   
-#---------------------------------------------------------------------------
 TAGFILES               = 
 GENERATE_TAGFILE       = 
 ALLEXTERNALS           = NO
 EXTERNAL_GROUPS        = YES
 PERL_PATH              = /usr/bin/perl
-#---------------------------------------------------------------------------
-# Configuration options related to the dot tool   
-#---------------------------------------------------------------------------
 CLASS_DIAGRAMS         = YES
 HIDE_UNDOC_RELATIONS   = YES
 HAVE_DOT               = NO
@@ -238,7 +187,4 @@
 DOT_MULTI_TARGETS      = NO
 GENERATE_LEGEND        = YES
 DOT_CLEANUP            = YES
-#---------------------------------------------------------------------------
-# Configuration::additions related to the search engine   
-#---------------------------------------------------------------------------
 SEARCHENGINE           = NO
diff --git a/web/.ht-inc/help.php b/web/.ht-inc/help.php
index f632f6b..b038d97 100644
--- a/web/.ht-inc/help.php
+++ b/web/.ht-inc/help.php
@@ -130,7 +130,7 @@
 		$testname = stripslashes($name);
 	if(! preg_match('/^([-A-Za-z \']{1,} [-A-Za-z \']{2,})*$/', $testname)) {
 		$submitErr |= NAMEERR;
-		$submitErrMsg[NAMEERR] = "Name can only contain letters, spaces, apostrophes ('), and dashes (-)";
+		$submitErrMsg[NAMEERR] = "Name can only contain letters, spaces, apostrophes ('), and dashes (-). Both first and last name must be specified.";
 	}
 	if(! preg_match('/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$/i',
 	   $email)) {
@@ -186,9 +186,9 @@
 	$recentrequests = "";
 	foreach($requests as $request) {
 		$thisstart = str_replace('&nbsp;', ' ', 
-				prettyDatetime($request["start"]));
+				prettyDatetime($request["start"], 0, 1));
 		$thisend = str_replace('&nbsp;', ' ', 
-				prettyDatetime($request["end"]));
+				prettyDatetime($request["end"], 0, 1));
 		$recentrequests .= "Image: {$request["prettyimage"]}\n"
 		                .  "Computer: {$computers[$request["computerid"]]["hostname"]}\n"
 		                .  "Start: $thisstart\n"
@@ -202,16 +202,79 @@
 		$message .= "User has no recent reservations\n";
 	}
 
+	# login history
+	$query = "SELECT authmech, "
+	       .        "timestamp, "
+	       .        "passfail, "
+	       .        "remoteIP, "
+	       .        "code "
+	       . "FROM loginlog "
+	       . "WHERE (user = '{$user['unityid']}' OR "
+	       .       "user = '{$user['unityid']}@{$user['affiliation']}') AND "
+	       .       "affiliationid = {$user['affiliationid']} "
+	       . "ORDER BY timestamp DESC "
+	       . "LIMIT 5";
+	$methodlen = strlen(i('Authentication Method'));
+	$timelen = strlen(i('Timestamp'));
+	$resultlen = strlen(i('Result'));
+	$remoteiplen = strlen(i('Remote IP'));
+	$extralen = strlen(i('Extra Info'));
+	if(strlen(i('Pass')) > $resultlen)
+		$resultlen = strlen(i('Pass'));
+	if(strlen(i('Fail')) > $resultlen)
+		$resultlen = strlen(i('Fail'));
+	$qh = doQuery($query);
+	$logins = array();
+	while($row = mysql_fetch_assoc($qh)) {
+		$tmp = prettyDatetime($row['timestamp'], 1, 1);
+		$row['timestamp'] = str_replace('&nbsp;', ' ', $tmp);
+		if($row['passfail'])
+			$row['passfail'] = 'Pass';
+		else
+			$row['passfail'] = 'Fail';
+		if(strlen($row['authmech']) > $methodlen)
+			$methodlen = strlen($row['authmech']);
+		if(strlen($row['timestamp']) > $timelen)
+			$timelen = strlen($row['timestamp']);
+		if(strlen($row['remoteIP']) > $remoteiplen)
+			$remoteiplen = strlen($row['remoteIP']);
+		if(strlen($row['code']) > $extralen)
+			$extralen = strlen($row['code']);
+		$logins[] = $row;
+	}
+	if(count($logins)) {
+		$logins = array_reverse($logins);
+		$message .= "-----------------------------------------------\n";
+		$message .= "User's Login History (up to last 5 attempts):\n\n";
+		$message .= sprintf("%-{$methodlen}s | ", i('Authentication Method'));
+		$message .= sprintf("%-{$timelen}s | ", i('Timestamp'));
+		$message .= sprintf("%-{$resultlen}s | ", i('Result'));
+		$message .= sprintf("%-{$remoteiplen}s | ", i('Remote IP'));
+		$message .= sprintf("%-{$extralen}s\n", i('Extra Info'));
+		foreach($logins as $login) {
+			$message .= sprintf("%-{$methodlen}s | ", $login['authmech']);
+			$message .= sprintf("%-{$timelen}s | ", $login['timestamp']);
+			$message .= sprintf("%-{$resultlen}s | ", $login['passfail']);
+			$message .= sprintf("%-{$remoteiplen}s | ", $login['remoteIP']);
+			$message .= sprintf("%-{$extralen}s\n", $login['code']);
+		}
+	}
+	if(isset($_SERVER['HTTP_USER_AGENT'])) {
+		$message .= "-----------------------------------------------\n";
+		$message .= "User agent: {$_SERVER['HTTP_USER_AGENT']}\n";
+	}
+
 	$indrupal = getContinuationVar('indrupal', 0);
 	if(! $indrupal)
 		print "<H2>VCL Help</H2>\n";
 	$mailParams = "-f" . ENVELOPESENDER;
 	if(get_magic_quotes_gpc())
 		$summary = stripslashes($summary);
-	if(! mail(HELPEMAIL, "$summary", $message,
+	$helpemail = getHelpEmail($user['affiliationid']);
+	if(! mail($helpemail, "$summary", $message,
 	   "From: $from\r\nReply-To: $email\r\n", $mailParams)){
 		print "The Server was unable to send mail at this time. Please e-mail ";
-		print "<a href=\"mailto:" . HELPEMAIL . "\">" . HELPEMAIL . "</a> for ";
+		print "<a href=\"mailto:$helpemail\">$helpemail</a> for ";
 		print "help with your problem.";
 	}
 	else {
@@ -220,4 +283,28 @@
 	}
 }
 
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn getHelpEmail($affil)
+///
+/// \param $affil - id or name of an affiliation
+///
+/// \return email address for $affil or HELPEMAIL from conf.php if $affil does
+/// not have a specific email address set
+///
+/// \brief gets the email address for support for a $affil
+///
+////////////////////////////////////////////////////////////////////////////////
+function getHelpEmail($affil) {
+	if(is_numeric($affil))
+		$field = 'id';
+	else
+		$field = 'name';
+	$query = "SELECT helpaddress FROM affiliation WHERE $field = '$affil'";
+	$qh = doQuery($query);
+	if($row = mysql_fetch_assoc($qh))
+		if($row['helpaddress'] != '')
+			return $row['helpaddress'];
+	return HELPEMAIL;
+}
 ?>
diff --git a/web/.ht-inc/image.php b/web/.ht-inc/image.php
index 8f7bc88..329cec8 100644
--- a/web/.ht-inc/image.php
+++ b/web/.ht-inc/image.php
@@ -366,34 +366,34 @@
 		$h .= "<legend>" . i("Image Description") . "</legend>\n";
 		$h .= i("Description of image (required - users will see this on the <strong>New Reservations</strong> page):");
 		$h .= "<br>\n";
-		$h .= "<textarea dojoType=\"dijit.form.Textarea\" id=\"description\" ";
-		$h .= "style=\"width: 400px; text-align: left;\"></textarea>\n";
+		$h .= "<textarea dojoType=\"dijit.form.Textarea\" id=\"description\">";
+		$h .= "</textarea>\n";
 		$h .= "</fieldset>\n";
 		# usage notes
 		$h .= "<fieldset>\n";
 		$h .= "<legend>" . i("Usage Notes") . "</legend>\n";
 		$msg = i("Optional notes to the user explaining how to use the image (users will see this on the <strong>Connect!</strong> page):");
 		$h .= preg_replace("/(.{1,100}([ \n]|$))/", '\1<br>', $msg);
-		$h .= "<textarea dojoType=\"dijit.form.Textarea\" id=\"usage\" ";
-		$h .= "style=\"width: 400px; text-align: left;\"></textarea>\n";
+		$h .= "<textarea dojoType=\"dijit.form.Textarea\" id=\"usage\">";
+		$h .= "</textarea>\n";
 		$h .= "</fieldset>\n";
 		if($add) {
 			$h .= "<fieldset>\n";
 			$h .= "<legend>" . i("Revision Comments") . "</legend>\n";
 			$msg = i("Notes for yourself and other admins about how the image was setup/installed. These are optional and are not visible to end users.");
 			$h .= preg_replace("/(.{1,80}([ \n]|$))/", '\1<br>', $msg);
-			$h .= "<textarea dojoType=\"dijit.form.Textarea\" id=\"imgcomments\" ";
-			$h .= "style=\"width: 400px; text-align: left;\"></textarea>";
+			$h .= "<textarea dojoType=\"dijit.form.Textarea\" id=\"imgcomments\">";
+			$h .= "</textarea>";
 			$h .= "</fieldset>\n";
 		}
 		# advanced options
 		$h .= "<div dojoType=\"dijit.TitlePane\" title=\"";
 		$h .= i("Advanced Options - leave default values unless you really know what you are doing (click to expand)");
-		$h .= "\" open=\"false\" style=\"width: 460px\" id=\"advancedoptions\" ";
+		$h .= "\" open=\"false\" id=\"advancedoptions\" ";
 		$h .= "onShow=\"delayedEditResize();\" onHide=\"delayedEditResize();\">\n";
 		# RAM
 		$extra = array('smallDelta' => 256, 'largeDelta' => 1024);
-		$h .= labeledFormItem('ram', i('Required RAM'), 'spinner', '{min:512, max:8388607}',
+		$h .= labeledFormItem('ram', i('Required RAM') . ' (MB)', 'spinner', '{min:512, max:8388607}',
 		                      1, 1024, '', '', $extra);
 		# cores
 		$extra = array('smallDelta' => 1, 'largeDelta' => 2);
@@ -1644,7 +1644,7 @@
 		$return["checkuser"] = processInputVar("checkuser", ARG_NUMERIC);
 		$return["rootaccess"] = processInputVar("rootaccess", ARG_NUMERIC);
 		$return["sethostname"] = processInputVar("sethostname", ARG_NUMERIC);
-		$return["maxinitialtime"] = processInputVar("maxinitialtime", ARG_NUMERIC);
+		$return["maxinitialtime"] = processInputVar("maxinitialtime", ARG_NUMERIC, 0);
 		$return["sysprep"] = processInputVar("sysprep", ARG_NUMERIC); # only in add
 		$return["connectmethodids"] = processInputVar("connectmethodids", ARG_STRING); # only in add
 		$return["adauthenabled"] = processInputVar("adauthenabled", ARG_NUMERIC);
@@ -1767,7 +1767,7 @@
 			$extraaddomainid = getContinuationVar('extraaddomainid', 0);
 			$extraaddomainou = getContinuationVar('extraaddomainou', '');
 			if(! array_key_exists($return['addomainid'], $vals) &&
-			   $return['addomainid'] != $extraaddomainid) {
+			   $return['addomainid'] !== $extraaddomainid) {
 				$return['error'] = 1;
 				$errormsg[] = i("Invalid AD Domain submitted");
 			}
diff --git a/web/.ht-inc/privileges.php b/web/.ht-inc/privileges.php
index 165e14b..714b910 100644
--- a/web/.ht-inc/privileges.php
+++ b/web/.ht-inc/privileges.php
@@ -36,8 +36,11 @@
 ////////////////////////////////////////////////////////////////////////////////
 function viewNodes() {
 	global $user;
-	if(! empty($_COOKIE["VCLACTIVENODE"]) &&
-		nodeExists($_COOKIE['VCLACTIVENODE']))
+	if(isset($_COOKIE["VCLACTIVENODE"]) &&
+	   is_numeric($_COOKIE['VCLACTIVENODE']) &&
+	   $_COOKIE['VCLACTIVENODE'] > 0 &&
+	   $_COOKIE['VCLACTIVENODE'] < 16777216 &&
+	   nodeExists($_COOKIE['VCLACTIVENODE']))
 		$activeNode = $_COOKIE["VCLACTIVENODE"];
 	else {
 		$topNodes = getChildNodes();
@@ -1629,6 +1632,12 @@
 		print "      Total: {$times['total']}<br>\n";
 		print "  </TR>\n";
 
+		$maxconcurrent = getMaxOverlap($userdata['id']);
+		print "  <TR>\n";
+		print "    <TH align=right>Max Overlapping Reservations:</TH>\n";
+		print "    <TD>$maxconcurrent</TD>\n";
+		print "  </TR>\n";
+
 		print "  <TR>\n";
 		print "    <TH align=right style=\"vertical-align: top\">Privileges (found somewhere in the tree):</TH>\n";
 		print "    <TD>\n";
@@ -1724,6 +1733,30 @@
 		}
 		print "</div>\n";
 
+		# owned images
+		$ownedimages = array();
+		$query = "SELECT prettyname "
+		       . "FROM image "
+		       . "WHERE ownerid = {$userdata['id']} AND "
+		       .       "deleted = 0 "
+		       . "ORDER BY prettyname";
+		$qh = doQuery($query);
+		while($row = mysqli_fetch_row($qh))
+			$ownedimages[] = $row[0];
+		print "<table>\n";
+		print "  <tr>\n";
+		print "    <th style=\"vertical-align: top;\">Images Owned by User:<th>\n";
+		print "    <td>\n";
+		if(count($ownedimages)) {
+			foreach($ownedimages as $image)
+				print "      $image<br>\n";
+		}
+		else
+			print "      None\n";
+		print "    </td>\n";
+		print "  </tr>\n";
+		print "</table>\n";
+
 		# image access
 		print "<table>\n";
 		print "  <tr>\n";
diff --git a/web/.ht-inc/requests.php b/web/.ht-inc/requests.php
index 565c53c..c836668 100644
--- a/web/.ht-inc/requests.php
+++ b/web/.ht-inc/requests.php
@@ -276,12 +276,14 @@
 					$text .= getViewRequestHTMLitem('openmoreoptions');
 					$text .= getViewRequestHTMLitem('editoption', $editcont);
 					if(array_key_exists($imageid, $resources['image']) && ! $cluster &&            # imageAdmin access, not a cluster,
+					   $requests[$i]['OSinstalltype'] != 'none' &&
 					   ($requests[$i]['currstateid'] == 8 || $requests[$i]['laststateid'] == 8)) { # reservation has been in inuse state
 						$text .= getViewRequestHTMLitem('endcreateoption', $imgcont);
 					}
 					/*else
 						$text .= getViewRequestHTMLitem('endcreateoptiondisable');*/
 					if(array_key_exists($imageid, $resources['image']) && ! $cluster &&
+					   $requests[$i]['OSinstalltype'] != 'none' &&
 					   $requests[$i]['server'] && ($requests[$i]['currstateid'] == 8 ||
 						($requests[$i]['currstateid'] == 14 && $requests[$i]['laststateid'] == 8))) {
 						$chkcdata = $cdata;
@@ -291,7 +293,8 @@
 					}
 					elseif($requests[$i]['server'] && $requests[$i]['currstateid'] == 24)
 						$text .= getViewRequestHTMLitem('checkpointoptiondisable');
-					if($requests[$i]['currstateid'] == 8 ||
+					if((! $cluster && $requests[$i]['OSinstalltype'] != 'none' &&
+					   $requests[$i]['currstateid'] == 8) ||
 					   (! $cluster &&
 					   $requests[$i]['OSinstalltype'] != 'none' &&
 					   $requests[$i]['currstateid'] != 3 &&
@@ -2093,6 +2096,45 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
+/// \fn AJfetchRouterDNS()
+///
+/// \brief get router and dns information for a given IP address
+///
+////////////////////////////////////////////////////////////////////////////////
+function AJfetchRouterDNS() {
+	$data = array('status' => 'none');
+	$page = processInputVar('page', ARG_STRING);
+	if($page != 'deploy') {
+		sendJSON($data);
+		return;
+	}
+	$ipaddr = processInputVar('ipaddr', ARG_STRING);
+	# validate fixed IP address
+	if(! validateIPv4addr($ipaddr)) {
+		sendJSON($data);
+		return;
+	}
+	# validate netmask
+	$netmask = processInputVar('netmask', ARG_STRING);
+	$bnetmask = ip2long($netmask);
+	if(! preg_match('/^[1]+0[^1]+$/', sprintf('%032b', $bnetmask))) {
+		sendJSON($data);
+		return;
+	}
+	$network = ip2long($ipaddr) & $bnetmask;
+	$availnets = getVariable('fixedIPavailnetworks', array());
+	$key = long2ip($network) . "/$netmask";
+	if(array_key_exists($key, $availnets)) {
+		$data = array('status' => 'success',
+		              'page' => $page,
+		              'router' => $availnets[$key]['router'],
+		              'dns' => implode(',', $availnets[$key]['dns']));
+	}
+	sendJSON($data);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
 /// \fn AJshowRequestSuggestedTimes()
 ///
 /// \brief builds html to display list of available times the selected image
@@ -2359,7 +2401,8 @@
 		return;
 	}
 	elseif($availablerc == -3) {
-		$msg = i("The IP or MAC address you specified overlaps with another reservation using the same IP or MAC address you specified. Please use a different IP or MAC or select a different time to deploy the server.");
+		#$msg = i("The IP or MAC address you specified overlaps with another reservation using the same IP or MAC address you specified. Please use a different IP or MAC or select a different time to deploy the server.");
+		$msg = i("The IP address you specified overlaps with another reservation using the same IP address. Please use a different IP address or select a different time to deploy the server.");
 		$data = array('err' => 1,
 		              'errmsg' => $msg);
 		sendJSON($data);
@@ -3859,7 +3902,8 @@
 			$msgip = " ($ip)";
 		if(! empty($mac))
 			$msgmac = " ($mac)";
-		$h .= sprintf(i("The reserved IP (%s) or MAC address (%s) conflicts with another reservation using the same IP or MAC address. Please select a different time to use the image."), $msgip, $msgmac);
+		#$h .= sprintf(i("The reserved IP (%s) or MAC address (%s) conflicts with another reservation using the same IP or MAC address. Please select a different time to use the image."), $msgip, $msgmac);
+		$h .= sprintf(i("The reserved IP address (%s) conflicts with another reservation using the same IP address. Please select a different time to use the image."), $msgip);
 		$h = preg_replace("/(.{1,60}([ \n]|$))/", '\1<br>', $h);
 		$cdata = getContinuationVar();
 		$cont = addContinuationsEntry('AJsubmitEditRequest', $cdata, SECINDAY, 1, 0);
@@ -5040,10 +5084,14 @@
 function AJsetImageProduction() {
 	$requestid = getContinuationVar('requestid');
 	$data = getRequestInfo($requestid);
-	foreach($data["reservations"] as $res) {
-		if($res["forcheckout"]) {
-			$prettyimage = $res["prettyimage"];
-			break;
+	if($data['forimaging'])
+		$prettyimage = $data['reservations'][0]['prettyimage'];
+	else {
+		foreach($data["reservations"] as $res) {
+			if($res["forcheckout"]) {
+				$prettyimage = $res["prettyimage"];
+				break;
+			}
 		}
 	}
 	$title = "<big><strong>" . i("Change Test Image to Production") . "</strong></big><br><br>\n";
@@ -5068,10 +5116,14 @@
 function AJsubmitSetImageProduction() {
 	$requestid = getContinuationVar('requestid');
 	$data = getRequestInfo($requestid);
-	foreach($data["reservations"] as $res) {
-		if($res["forcheckout"]) {
-			$prettyimage = $res["prettyimage"];
-			break;
+	if($data['forimaging'])
+		$prettyimage = $data['reservations'][0]['prettyimage'];
+	else {
+		foreach($data["reservations"] as $res) {
+			if($res["forcheckout"]) {
+				$prettyimage = $res["prettyimage"];
+				break;
+			}
 		}
 	}
 	$query = "UPDATE request SET stateid = 17 WHERE id = $requestid";
diff --git a/web/.ht-inc/resource.php b/web/.ht-inc/resource.php
index cc55d09..04a5e10 100644
--- a/web/.ht-inc/resource.php
+++ b/web/.ht-inc/resource.php
@@ -1731,6 +1731,11 @@
 	               'baserevisionid' => $revid,
 	               'checkpoint' => $checkpoint,
 	               'add' => 1);
+	$cdata['addomainvals'] = array();
+	if(in_array("addomainAdmin", $user["privileges"])) {
+		$vals = getUserResources(array('addomainAdmin'), array("manageGroup"));
+		$cdata['addomainvals'] = $vals['addomain'];
+	}
 	$cont = addContinuationsEntry('AJsaveResource', $cdata, SECINDAY, 0);
 	$arr = array('newcont' => $cont,
 	             'enableupdate' => 0,
diff --git a/web/.ht-inc/serverprofiles.php b/web/.ht-inc/serverprofiles.php
index 2de4826..9d9ad37 100644
--- a/web/.ht-inc/serverprofiles.php
+++ b/web/.ht-inc/serverprofiles.php
@@ -1138,43 +1138,4 @@
 	sendJSON($arr);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-///
-/// \fn AJfetchRouterDNS()
-///
-/// \brief get router and dns information for a given IP address
-///
-////////////////////////////////////////////////////////////////////////////////
-function AJfetchRouterDNS() {
-	$data = array('status' => 'none');
-	$page = processInputVar('page', ARG_STRING);
-	if($page != 'deploy' && $page != 'profile') {
-		sendJSON($data);
-		return;
-	}
-	$ipaddr = processInputVar('ipaddr', ARG_STRING);
-	# validate fixed IP address
-	if(! validateIPv4addr($ipaddr)) {
-		sendJSON($data);
-		return;
-	}
-	# validate netmask
-	$netmask = processInputVar('netmask', ARG_STRING);
-	$bnetmask = ip2long($netmask);
-	if(! preg_match('/^[1]+0[^1]+$/', sprintf('%032b', $bnetmask))) {
-		sendJSON($data);
-		return;
-	}
-	$network = ip2long($ipaddr) & $bnetmask;
-	$availnets = getVariable('fixedIPavailnetworks', array());
-	$key = long2ip($network) . "/$netmask";
-	if(array_key_exists($key, $availnets)) {
-		$data = array('status' => 'success',
-		              'page' => $page,
-		              'router' => $availnets[$key]['router'],
-		              'dns' => implode(',', $availnets[$key]['dns']));
-	}
-	sendJSON($data);
-}
-
 ?>
diff --git a/web/.ht-inc/states.php b/web/.ht-inc/states.php
index ae4227c..2e7cb06 100644
--- a/web/.ht-inc/states.php
+++ b/web/.ht-inc/states.php
@@ -150,8 +150,8 @@
                         'AJremGroupFromProfile',
                         'AJaddProfileToGroup',
                         'AJremProfileFromGroup',
-                        'AJserverProfileStoreData',
-                        'AJfetchRouterDNS',*/
+                        'AJserverProfileStoreData',*/
+                        'AJfetchRouterDNS',
                         'AJconfirmDeleteRequest',
                         'AJsubmitDeleteRequest',
                         'AJconfirmRemoveRequest',
@@ -257,6 +257,7 @@
 $actions['mode']['sendRDPfile'] = "sendRDPfile";
 $actions['mode']['AJcheckConnectTimeout'] = "AJcheckConnectTimeout";
 $actions['mode']['AJpreviewClickThrough'] = "AJpreviewClickThrough";
+$actions['mode']['AJfetchRouterDNS'] = "AJfetchRouterDNS";
 #$actions['mode']['connectMindterm'] = "connectMindterm";
 #$actions['mode']['connectRDPapplet'] = "connectRDPapplet";
 $actions['pages']['AJnewRequest'] = "reservations";
@@ -281,6 +282,7 @@
 $actions['pages']['sendRDPfile'] = "reservations";
 $actions['pages']['AJcheckConnectTimeout'] = "reservations";
 $actions['pages']['AJpreviewClickThrough'] = "reservations";
+$actions['pages']['AJfetchRouterDNS'] = "reservations";
 #$actions['pages']['connectMindterm'] = "currentReservations";
 #$actions['pages']['connectRDPapplet'] = "currentReservations";
 
@@ -386,7 +388,6 @@
 $actions['mode']['AJaddProfileToGroup'] = "AJaddProfileToGroup";
 $actions['mode']['AJremProfileFromGroup'] = "AJremProfileFromGroup";
 $actions['mode']['AJserverProfileStoreData'] = "AJserverProfileStoreData";
-$actions['mode']['AJfetchRouterDNS'] = "AJfetchRouterDNS";
 $actions['pages']['serverProfiles'] = "serverProfiles";
 $actions['pages']['AJsaveServerProfile'] = "serverProfiles";
 $actions['pages']['AJserverProfileData'] = "serverProfiles";
@@ -397,8 +398,7 @@
 $actions['pages']['AJremGroupFromProfile'] = "serverProfiles";
 $actions['pages']['AJaddProfileToGroup'] = "serverProfiles";
 $actions['pages']['AJremProfileFromGroup'] = "serverProfiles";
-$actions['pages']['AJserverProfileStoreData'] = "serverProfiles";
-$actions['pages']['AJfetchRouterDNS'] = "serverProfiles";*/
+$actions['pages']['AJserverProfileStoreData'] = "serverProfiles";*/
 
 # time table
 # TODO a few of these belong to new reservation
diff --git a/web/.ht-inc/statistics.php b/web/.ht-inc/statistics.php
index f30998f..9d749fe 100644
--- a/web/.ht-inc/statistics.php
+++ b/web/.ht-inc/statistics.php
@@ -213,7 +213,8 @@
 		       .        "l.wasavailable, "
 		       .        "l.ending, "
 		       .        "i.prettyname, "
-		       .        "o.prettyname AS OS "
+		       .        "o.prettyname AS OS, "
+		       .        "o.type AS OStype "
 		       . "FROM log l, "
 		       .      "image i, "
 		       .      "user u, "
@@ -234,7 +235,8 @@
 		       .        "l.wasavailable, "
 		       .        "l.ending, "
 		       .        "i.prettyname, "
-		       .        "o.prettyname AS OS "
+		       .        "o.prettyname AS OS, "
+		       .        "o.type AS OStype "
 		       . "FROM image i, "
 		       .      "user u, "
 		       .      "OS o, "
@@ -282,6 +284,7 @@
 	$imageload6to8 = array();
 	$imageload8more = array();
 	$imagefails = array();
+	$imageostype = array();
 	$lengths = array("30min" => 0,
 	                 "1hour" => 0,
 	                 "2hours" => 0,
@@ -301,6 +304,8 @@
 			$imageload6to8[$row["prettyname"]] = 0;
 		if(! array_key_exists($row["prettyname"], $imageload8more))
 			$imageload8more[$row["prettyname"]] = 0;
+		if(! isset($imageostype[$row["prettyname"]]))
+			$imageostype[$row["prettyname"]] = ucfirst($row['OStype']);
 
 		# notavailable
 		if($row["wasavailable"] == 0) {
@@ -452,6 +457,7 @@
 	print "    <TH>" . i("6-8 min wait") . "</TH>\n";
 	print "    <TH>" . i("&gt;= 8 min wait") . "</TH>\n";
 	print "    <TH>" . i("Failures") . "</TH>\n";
+	print "    <TH>" . i("OS Type") . "</TH>\n";
 	print "  </TR>\n";
 	foreach($imagecount as $key => $value) {
 		print "  <TR>\n";
@@ -477,6 +483,7 @@
 		}
 		else
 			print "    <TD align=center>{$imagefails[$key]}</TD>\n";
+		print "    <TD align=center>{$imageostype[$key]}</TD>\n";
 		print "  </TR>\n";
 	}
 	print "</TABLE>\n";
diff --git a/web/.ht-inc/utils.php b/web/.ht-inc/utils.php
index 662ab77..d27373f 100644
--- a/web/.ht-inc/utils.php
+++ b/web/.ht-inc/utils.php
@@ -56,8 +56,8 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function initGlobals() {
-	global $mode, $user, $remoteIP, $authed, $oldmode, $semid;
-	global $days, $phpVer, $keys, $pemkey, $AUTHERROR;
+	global $mode, $user, $remoteIP, $authed, $oldmode;
+	global $days, $phpVer, $keys, $pemkey, $submitErr, $submitErrMsg;
 	global $passwdArray, $skin, $contdata, $lastmode, $inContinuation;
 	global $ERRORS, $actions;
 	global $affilValFunc, $addUserFunc, $updateUserFunc, $addUserFuncArgs;
@@ -87,7 +87,7 @@
 
 	if(function_exists('openssl_encrypt')) {
 		define('USE_PHPSECLIB', 0);
-		$crytpkey = base64_decode($cryptkey);
+		$cryptkey = base64_decode($cryptkey);
 	}
 	else {
 		define('USE_PHPSECLIB', 1);
@@ -279,9 +279,9 @@
 		$id = $row['id'];
 		if(! array_key_exists($id, $affilValFunc)) {
 			if(ALLOWADDSHIBUSERS)
-				$affilValFunc[$id] = create_function('', 'return 1;');
+				$affilValFunc[$id] = function() {return 1;};
 			else
-				$affilValFunc[$id] = create_function('', 'return 0;');
+				$affilValFunc[$id] = function() {return 0;};
 		}
 		if(! array_key_exists($id, $addUserFunc)) {
 			if(ALLOWADDSHIBUSERS) {
@@ -289,10 +289,10 @@
 				$addUserFuncArgs[$id] = $id;
 			}
 			else
-				$addUserFunc[$id] = create_function('', 'return 0;');
+				$addUserFunc[$id] = function() {return 0;};
 		}
 		if(! array_key_exists($id, $updateUserFunc))
-			$updateUserFunc[$id] = create_function('', 'return NULL;');
+			$updateUserFunc[$id] = function() {return NULL;};
 	}
 
 	# include appropriate files
@@ -382,8 +382,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 function checkAccess() {
 	global $mode, $user, $actionFunction, $authMechs;
-	global $itecsauthkey, $ENABLE_ITECSAUTH, $actions, $noHTMLwrappers;
-	global $inContinuation, $docreaders, $apiValidateFunc;
+	global $itecsauthkey, $ENABLE_ITECSAUTH, $actions;
+	global $inContinuation, $apiValidateFunc;
 	if($mode == 'xmlrpccall') {
 		// double check for SSL
 		if(! isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") {
@@ -391,6 +391,11 @@
 			dbDisconnect();
 			exit;
 		}
+		if(! isset($_SERVER['HTTP_X_USER'])) {
+			printXMLRPCerror(3);   # access denied
+			dbDisconnect();
+			exit;
+		}
 		$xmluser = processInputData($_SERVER['HTTP_X_USER'], ARG_STRING, 1);
 		if(! $user = getUserInfo($xmluser)) {
 			// if first call to getUserInfo fails, try calling with $noupdate set
@@ -769,7 +774,8 @@
 			# check to see if end time has been reached
 			$fh = fopen($file, 'r');
 			$msg = '';
-			while($line = fgetss($fh)) {
+			while($line = fgets($fh)) {
+			    $line = strip_tags($line);
 				if(preg_match("/^END=([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$/", $line, $matches)) {
 					$tmp = "{$matches[1]}-{$matches[2]}-{$matches[3]} {$matches[4]}:{$matches[5]}:00";
 					$end = datetimeToUnix($tmp);
@@ -969,7 +975,7 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function main() {
-	global $user, $authed, $mode;
+	global $user, $authed;
 	print "<H2>" . i("Welcome to the Virtual Computing Lab") . "</H2>\n";
 	if($authed) {
 		if(! empty($user['lastname']) && ! empty($user['preferredname']))
@@ -1023,10 +1029,10 @@
 		xmlRPCabort($errcode, $query);
 	if(ONLINEDEBUG && checkUserHasPerm('View Debug Information')) {
 		if($errcode >= 100 && $errcode < 400) {
-			print "<font color=red>" . mysqli_error($mysqli_link_vcl) . "</font><br>\n";
+			print "<span class=\"rederrormsg\">" . mysqli_error($mysqli_link_vcl) . "</span><br>\n";
 			error_log(mysqli_error($mysqli_link_vcl));
 			if($ENABLE_ITECSAUTH) {
-				print "<font color=red>" . mysqli_error($mysqli_link_acct) . "</font><br>\n";
+				print "<span class=\"rederrormsg\">" . mysqli_error($mysqli_link_acct) . "</span><br>\n";
 				error_log(mysqli_error($mysqli_link_acct));
 			}
 			print "$query<br>\n";
@@ -1453,7 +1459,8 @@
 	       . "WHERE i.userid = u.id AND ";
 	if(! $includedeleted)
 		$query .=   "i.deleted = 0 AND ";
-	$query .=      "u.affiliationid = a.id";
+	$query .=      "u.affiliationid = a.id "
+	       . "ORDER BY i.imageid, i.revision";
 	$qh = doQuery($query, 101);
 	while($row = mysqli_fetch_assoc($qh)) {
 		$id = $row['imageid'];
@@ -2099,7 +2106,6 @@
 		$bygroup = 1;
 	if(! $userid)
 		$userid = $user["id"];
-	$return = array();
 
 	$nodeprivs = array();
 	$startnodes = array();
@@ -2861,7 +2867,7 @@
 	}
 	else
 		$padding = constant('OPENSSL_PKCS1_OAEP_PADDING');
-	$savehdlr = set_error_handler(create_function('', ''));
+	$savehdlr = set_error_handler(function() {});
 	if(@openssl_public_encrypt($secret, $encdata, $cryptkey, $padding)) {
 		set_error_handler($savehdlr);
 		$b64data = base64_encode($encdata);
@@ -2891,7 +2897,7 @@
 	$file .= "/.ht-inc/cryptkey/private.pem";
 	$prikey = openssl_pkey_get_private("file://$file", $pemkey);
 	# decrypt secret using private key
-	$savehdlr = set_error_handler(create_function('', ''));
+	$savehdlr = set_error_handler(function() {});
 	if(ASYMOPT == 'OAEP')
 		$padding = constant('OPENSSL_PKCS1_OAEP_PADDING');
 	# OAEP currently only supported padding option
@@ -4258,7 +4264,6 @@
 			return NULL;
 	}
 
-	$user = array();
 	$query = "SELECT u.unityid AS unityid, "
 	       .        "u.affiliationid, "
 	       .        "af.name AS affiliation, "
@@ -4496,7 +4501,6 @@
 		$qh = doQuery($query, 101);
 		if($row = mysqli_fetch_assoc($qh)) {
 			$id = $row['unityid'];
-			$type = 'loginid';
 			$affilid = $row['affiliationid'];
 		}
 		else
@@ -5085,6 +5089,8 @@
 			while($row = mysqli_fetch_assoc($qh))
 				$newcompids[] = $row['id'];
 			$computerids = $newcompids;
+			if(empty($computerids) && empty($blockids))
+				return debugIsAvailable(0, 23, $start, $end, $imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
 		}
 
 		# check for use of specified IP address, have to wait until here
@@ -5106,7 +5112,7 @@
 			$qh = doQuery($query);
 			if(mysqli_num_rows($qh)) {
 				if($now)
-					return debugIsAvailable(-4, 18, $start, $end, $imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
+					return debugIsAvailable(-4, 24, $start, $end, $imagerevisionid, $computerids, $currentids, $blockids, array(), $virtual);
 				$requestInfo['ipwarning'] = 1;
 			}
 			$query = "SELECT id "
@@ -5208,7 +5214,7 @@
 function debugIsAvailable($rc, $loc, $start, $end, $imagerevisionid,
 	                       $compids=array(), $currentids=array(),
 	                       $blockids=array(), $failedids=array(), $virtual='') {
-	global $user, $mode, $requestInfo;
+	global $mode, $requestInfo;
 	$debug = getContinuationVar('debug', 0);
 	if(! $debug ||
 	   $mode != 'AJupdateWaitTime' ||
@@ -5266,9 +5272,6 @@
 		case "13":
 			$msg = "no computers available (after virtual host resource checks/before performing overlapping IP address check)";
 			break;
-		case "18":
-			$msg = "requested IP address in use by another computer";
-			break;
 		case "22":
 			$msg = "at least 2 computers have the requested IP address assigned to them";
 			break;
@@ -5278,6 +5281,12 @@
 		case "12":
 			$msg = "successfully found a computer (id: {$requestInfo['computers'][0]})";
 			break;
+		case "23":
+			$msg = "no computers available after performing VM host check";
+			break;
+		case "24":
+			$msg = "requested IP address in use by another computer";
+			break;
 	}
 	print "console.log('$msg');";
 	return $rc;
@@ -5412,7 +5421,6 @@
 function allocComputer($blockids, $currentids, $computerids, $start, $end,
                        $nowfuture, $imageid, $imagerevisionid, $holdcomps,
                        $requestid) {
-	global $requestInfo;
 	$ret = array();
 	if(SCHEDULER_ALLOCATE_RANDOM_COMPUTER) {
 		shuffle($blockids);
@@ -5696,7 +5704,6 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function checkOverlap($start, $end, $max, $requestid=0) {
-	global $user;
 	$requests = getUserRequests("all");
 	$count = 0;
 	if($max > 0)
@@ -5875,7 +5882,7 @@
 	       .       "'$endstamp', "
 	       .       "NOW(), "
 	       .       "$checkuser)";
-	$qh = doQuery($query, 136);
+	doQuery($query, 136);
 
 	$qh = doQuery("SELECT LAST_INSERT_ID() FROM request", 134);
 	if(! $row = mysqli_fetch_row($qh)) {
@@ -6037,7 +6044,6 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function findManagementNode($compid, $start, $nowfuture) {
-	global $HTMLheader;
 	$allmgmtnodes = array_keys(getManagementNodes($nowfuture));
 	$mapped = getMappedResources($compid, "computer", "managementnode");
 	$usablemgmtnodes = array_intersect($allmgmtnodes, $mapped);
@@ -6359,7 +6365,7 @@
 				       . "WHERE id = {$request['id']}";
 			}
 		}
-		$qh = doQuery($query, 150);
+		doQuery($query, 150);
 
 		addChangeLogEntry($request["logid"], NULL, unixToDatetime($now), NULL,
 		                  NULL, "released");
@@ -6402,7 +6408,7 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function moveReservationsOffComputer($compid=0, $count=0) {
-	global $requestInfo, $user;
+	global $requestInfo;
 	$resInfo = array();
 	$checkstart = unixToDatetime(time() + 180);
 	if($compid == 0) {
@@ -7183,29 +7189,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getDepartmentName($id)
-///
-/// \param $id - id for a department in the department table
-///
-/// \return if found, department name; if not, 0
-///
-/// \brief looks up the name field corresponding to $id in the department table
-/// and returns it
-///
-////////////////////////////////////////////////////////////////////////////////
-function getDepartmentName($id) {
-	$query = "SELECT name FROM department WHERE id = '$id'";
-	$qh = doQuery($query, 101);
-	if($row = mysqli_fetch_row($qh)) {
-		return $row[0];
-	}
-	else {
-		return 0;
-	}
-}
-
-////////////////////////////////////////////////////////////////////////////////
-///
 /// \fn getImageId($image)
 ///
 /// \param $image - name of an image (must match name (not prettyname) in the
@@ -7580,11 +7563,9 @@
 	while($row = mysqli_fetch_assoc($qh))
 		$compgroups[] = $row['resourcegroupid'];
 	$mngrps = array();
-	foreach($compgroups as $grpid) {
-		$mngrpset = getResourceMapping('managementnode', 'computer', '', implode(',', $compgroups));
-		foreach($mngrpset as $mngrpid => $compgrpset)
-			$mngrps[$mngrpid] = 1;
-	}
+	$mngrpset = getResourceMapping('managementnode', 'computer', '', implode(',', $compgroups));
+	foreach($mngrpset as $mngrpid => $compgrpset)
+		$mngrps[$mngrpid] = 1;
 	$mngrpnames = array();
 	foreach(array_keys($mngrps) as $mnid) {
 		$mngrpnames[] = getResourceGroupName($mnid);
@@ -8562,6 +8543,7 @@
 	       .      "reservation rs, "
 	       .      "image i, "
 	       .      "state s, "
+	       .      "state rqs, "
 	       .      "computer c, "
 	       .      "provisioningOSinstalltype poi, "
 	       .      "OSinstalltype oi, "
@@ -8569,6 +8551,8 @@
 	       . "WHERE rs.requestid = rq.id AND "
 	       .       "(rq.start > '$startdt' OR "
 	       .        "(DATE_ADD(rq.end, INTERVAL 15 MINUTE) > '$startdt' AND rq.start <= '$startdt')) AND "
+	       .       "rq.stateid = rqs.id AND "
+	       .       "rqs.name NOT IN ('failed', 'complete', 'reload') AND "
 	       .       "rs.computerid IN ($incompids) AND "
 	       .       "i.id = $imageid AND "
 	       .       "c.id = rs.computerid AND "
@@ -9445,7 +9429,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 ///
-/// \fn getReservationNATports($resid)
+/// \fn getNATports($resid)
 ///
 /// \param $resid - id of a reservation
 ///
@@ -13113,7 +13097,7 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function sendHeaders() {
-	global $mode, $user, $authed, $oldmode, $actionFunction;
+	global $mode, $user, $authed, $actionFunction;
 	global $shibauthed, $authFuncs;
 	if(! $authed && $mode == "auth") {
 		header("Location: " . BASEURL . SCRIPT . "?mode=selectauth");
@@ -13208,8 +13192,8 @@
 ///
 ////////////////////////////////////////////////////////////////////////////////
 function printHTMLHeader() {
-	global $mode, $user, $authed, $oldmode, $HTMLheader, $contdata;
-	global $printedHTMLheader, $docreaders, $noHTMLwrappers, $actions;
+	global $mode, $user, $authed, $HTMLheader, $contdata;
+	global $printedHTMLheader, $noHTMLwrappers;
 	if($printedHTMLheader)
 		return;
 	$refresh = 0;
@@ -13785,23 +13769,6 @@
 			                      'dijit.form.ValidationTextBox',
 			                      'dijit.layout.TabContainer');
 			break;
-		# TODO clean up
-		/*case 'serverProfiles':
-			$filename = 'vclServerProfiles.js';
-			$dojoRequires = array('dojo.parser',
-			                      'dijit.Dialog',
-			                      'dijit.form.Button',
-			                      'dijit.form.FilteringSelect',
-			                      'dijit.form.Select',
-			                      'dijit.form.TextBox',
-			                      'dijit.form.ValidationTextBox',
-			                      'dijit.form.CheckBox',
-			                      'dijit.form.Textarea',
-			                      'dijit.layout.ContentPane',
-			                      'dijit.layout.TabContainer',
-			                      'dojox.string.sprintf',
-			                      'dojo.data.ItemFileWriteStore');
-			break;*/
 		/*case 'testDojoREST':
 			$filename = '';
 			$dojoRequires = array('dojo.parser',
@@ -13853,8 +13820,6 @@
 			$rt .= "      testJS();\n";
 			$rt .= "      document.onmousemove = updateMouseXY;\n";
 			$rt .= "      showScriptOnly();\n";
-			/*$cont = addContinuationsEntry('AJserverProfileStoreData', array(), 120, 1, 0);
-			$rt .= "   populateProfileStore('$cont');\n";*/
 			$rt .= "   });\n";
 			if($refresh)
 				$rt .= "   refresh_timer = setTimeout(resRefresh, 12000);\n";
diff --git a/web/.ht-inc/vm.php b/web/.ht-inc/vm.php
index 90c96a5..df10b43 100644
--- a/web/.ht-inc/vm.php
+++ b/web/.ht-inc/vm.php
@@ -437,9 +437,7 @@
 	$currvms = array_merge($currvms);
 	$noaccess = array_merge($noaccess);
 	$freevms = array_merge($freevms);
-	$cont = addContinuationsEntry('AJchangeVMprofile', array(), 3600, 1, 0);
 	$arr = array('profile' => $data[$vmhostid]['vmprofiledata'],
-	             'continuation' => $cont,
 	             'allvms' => $allvms,
 	             'currvms' => $currvms,
 	             'noaccess' => $noaccess,
@@ -516,6 +514,10 @@
 	$fails = array();
 
 	$vmlistids = processInputVar('listids', ARG_STRING);
+	if(! preg_match('/^(\d+)(,\d+)*$/', $vmlistids)) {
+		sendJSON(array('failed' => 'invaliddata'));
+		return;
+	}
 	$vmids = explode(',', $vmlistids);
 
 	# get data about submitted vms to add
@@ -583,6 +585,10 @@
 
 	$fails = array();
 	$vmlistids = processInputVar('listids', ARG_STRING);
+	if(! preg_match('/^(\d+)(,\d+)*$/', $vmlistids)) {
+		sendJSON(array('failed' => 'invaliddata'));
+		return;
+	}
 	$vmids = explode(',', $vmlistids);
 	$rems = array();
 	$checks = array();
@@ -718,6 +724,10 @@
 
 	$fails = array();
 	$requestids = processInputVar('listids', ARG_STRING);
+	if(! preg_match('/^(\d+)(,\d+)*$/', $requestids)) {
+		sendJSON(array('failed' => 'invaliddata'));
+		return;
+	}
 	$now = time();
 	$msg = 'FAIL';
 	foreach(explode(',', $requestids) AS $reqid) {
@@ -944,10 +954,9 @@
 ////////////////////////////////////////////////////////////////////////////////
 function AJnewProfile() {
 	$newprofile = processInputVar('newname', ARG_STRING);
-	if(get_magic_quotes_gpc()) {
+	if(get_magic_quotes_gpc())
 		$newprofile = stripslashes($newprofile);
-		$newprofile = vcl_mysql_escape_string($newprofile);
-	}
+	$newprofile = vcl_mysql_escape_string($newprofile);
 	$query = "SELECT id FROM vmprofile WHERE profilename = '$newprofile'";
 	$qh = doQuery($query, 101);
 	if($row = mysqli_fetch_assoc($qh)) {
diff --git a/web/.ht-inc/xmlrpcWrappers.php b/web/.ht-inc/xmlrpcWrappers.php
index 33b1587..750000e 100644
--- a/web/.ht-inc/xmlrpcWrappers.php
+++ b/web/.ht-inc/xmlrpcWrappers.php
@@ -19,9 +19,9 @@
 /**
  * \file
  * The functions listed here are for making VCL requests from other applications.
- * They are implemented according to the XML RPC spec defined at 
+ * They are implemented according to the XML RPC spec defined at
  * http://www.xmlrpc.com/ \n
- * There is one function called \b XMLRPCtest() that can be used during 
+ * There is one function called \b XMLRPCtest() that can be used during
  * initial development to get started without actually making a request.\n
  * \n
  * The URL you will use to submit RPC calls is the URL for your VCL site
@@ -171,7 +171,7 @@
 	$length = processInputData($length, ARG_NUMERIC);
 	#$foruser = processInputData($foruser, ARG_STRING, 1);
 
-	// make sure user didn't submit a request for an image he 
+	// make sure user didn't submit a request for an image he
 	// doesn't have access to
 	$resources = getUserResources(array("imageAdmin", "imageCheckOut"));
 	$validImageids = array_keys($resources['image']);
@@ -233,7 +233,7 @@
 	$revisionid = getProductionRevisionid($imageid);
 	$rc = isAvailable($images, $imageid, $revisionid, $start, $end, 1);
 	if($rc < 1) {
-		addLogEntry($nowfuture, unixToDatetime($start), 
+		addLogEntry($nowfuture, unixToDatetime($start),
 		            unixToDatetime($end), 0, $imageid);
 		return array('status' => 'notavailable');
 	}
@@ -354,7 +354,7 @@
 	$revisionid = getProductionRevisionid($imageid);
 	$rc = isAvailable($images, $imageid, $revisionid, $start, $end, 1);
 	if($rc < 1) {
-		addLogEntry($nowfuture, unixToDatetime($start), 
+		addLogEntry($nowfuture, unixToDatetime($start),
 		            unixToDatetime($end), 0, $imageid);
 		return array('status' => 'notavailable');
 	}
@@ -387,7 +387,7 @@
 /// \param $foruser - (optional) login to be used when setting up the account
 /// on the reserved machine - CURRENTLY, THIS IS UNSUPPORTED
 /// \param $name - (optional) name for reservation
-/// \param $userdata - (optional) text that will be placed in 
+/// \param $userdata - (optional) text that will be placed in
 /// /root/.vclcontrol/post_reserve_userdata on the reserved node
 ///
 /// \return an array with at least one index named '\b status' which will have
@@ -562,7 +562,7 @@
 	$rc = isAvailable($images, $imageid, $revisionid, $start, $end,
 	                  1, 0, 0, 0, 0, $ipaddr, $macaddr);
 	if($rc < 1) {
-		addLogEntry($nowfuture, unixToDatetime($start), 
+		addLogEntry($nowfuture, unixToDatetime($start),
 		            unixToDatetime($end), 0, $imageid);
 		return array('status' => 'notavailable');
 	}
@@ -851,23 +851,23 @@
 		$portdata = array();
 		foreach($connectMethods as $key => $cm) {
 			$connecttext = $cm["connecttext"];
-			$connecttext = preg_replace("/#userid#/", $thisuser, $connecttext); 
-			$connecttext = preg_replace("/#password#/", $passwd, $connecttext); 
-			$connecttext = preg_replace("/#connectIP#/", $serverIP, $connecttext); 
+			$connecttext = preg_replace("/#userid#/", $thisuser, $connecttext);
+			$connecttext = preg_replace("/#password#/", $passwd, $connecttext);
+			$connecttext = preg_replace("/#connectIP#/", $serverIP, $connecttext);
 			foreach($cm['ports'] as $port) {
 				if(! empty($natports) && array_key_exists($port['key'], $natports[$key])) {
-					$connecttext = preg_replace("/{$port['key']}/", $natports[$key][$port['key']]['publicport'], $connecttext); 
+					$connecttext = preg_replace("/{$port['key']}/", $natports[$key][$port['key']]['publicport'], $connecttext);
 					$connectMethods[$key]['connectports'][] = "{$port['protocol']}:{$port['port']}:{$natports[$key][$port['key']]['publicport']}";
 				}
 				else {
 					if((preg_match('/remote desktop/i', $cm['description']) ||
-					   preg_match('/RDP/i', $cm['description'])) && 
+					   preg_match('/RDP/i', $cm['description'])) &&
 					   $port['key'] == '#Port-TCP-3389#') {
-						$connecttext = preg_replace("/{$port['key']}/", $user['rdpport'], $connecttext); 
+						$connecttext = preg_replace("/{$port['key']}/", $user['rdpport'], $connecttext);
 						$connectMethods[$key]['connectports'][] = "{$port['protocol']}:{$port['port']}:{$user['rdpport']}";
 					}
 					else {
-						$connecttext = preg_replace("/{$port['key']}/", $port['port'], $connecttext); 
+						$connecttext = preg_replace("/{$port['key']}/", $port['port'], $connecttext);
 						$connectMethods[$key]['connectports'][] = "{$port['protocol']}:{$port['port']}:{$port['port']}";
 					}
 				}
@@ -880,7 +880,7 @@
 		$cmid = $tmp[0];
 		if(empty($natports))
 			if((preg_match('/remote desktop/i', $connectMethods[$cmid]['description']) ||
-			   preg_match('/RDP/i', $connectMethods[$cmid]['description'])) && 
+			   preg_match('/RDP/i', $connectMethods[$cmid]['description'])) &&
 				$portdata[$cmid][0]['port'] == 3389)
 				$connectport = $user['rdpport'];
 			else
@@ -1005,7 +1005,7 @@
 	if($timeToNext > -1) {
 		$lockedall = 1;
 		if(count($request['reservations']) > 1) {
-			# get semaphore on each existing node in cluster so that nothing 
+			# get semaphore on each existing node in cluster so that nothing
 			# can get moved to the nodes during this process
 			$checkend = unixToDatetime($endts + 900);
 			foreach($request["reservations"] as $res) {
@@ -1721,7 +1721,7 @@
 			$node['id'] = $id;
 			array_push($nodes, $node);
 			array_push($stack, $node);
-		} 
+		}
 		while(count($stack)) {
 			$item = array_shift($stack);
 			$children = getChildNodes($item['id']);
@@ -1962,7 +1962,7 @@
 
 	$privileges = array();
 	$nodePrivileges = getNodePrivileges($nodeid, 'usergroups');
-	$cascadedNodePrivileges = getNodeCascadePrivileges($nodeid, 'usergroups'); 
+	$cascadedNodePrivileges = getNodeCascadePrivileges($nodeid, 'usergroups');
 	$cngp = $cascadedNodePrivileges['usergroups'];
 	$ngp = $nodePrivileges['usergroups'];
 	if(array_key_exists($groupid, $cngp)) {
@@ -2180,7 +2180,7 @@
 			             'errormsg' => 'resource group does not exist');
 		}
 		$np = getNodePrivileges($nodeid, 'resources');
-		$cnp = getNodeCascadePrivileges($nodeid, 'resources'); 
+		$cnp = getNodeCascadePrivileges($nodeid, 'resources');
 		$key = "$type/$name/$groupid";
 		if(isset($np['resources'][$key]['block']) || ! isset($cnp['resources'][$key]))
 			$privs = array_keys($np['resources'][$key]);
@@ -2399,9 +2399,9 @@
 	// Filter out any groups to which the user does not have access.
 	$usergroups = array();
 	foreach($groups as $id => $group) {
-		if($group['ownerid'] == $user['id'] || 
+		if($group['ownerid'] == $user['id'] ||
 		   (array_key_exists("editgroupid", $group) &&
-		   array_key_exists($group['editgroupid'], $user["groups"])) || 
+		   array_key_exists($group['editgroupid'], $user["groups"])) ||
 		   (array_key_exists($id, $user["groups"]))) {
 			array_push($usergroups, $group);
 		}
@@ -2476,7 +2476,7 @@
 		             'errormsg' => 'user group with submitted name and affiliation does not exist');
 	}
 	// if not owner and not member of managing group, no access
-	if($user['id'] != $row['ownerid'] && 
+	if($user['id'] != $row['ownerid'] &&
 	   ! array_key_exists($row['editgroupid'], $user['groups'])) {
 		return array('status' => 'error',
 		             'errorcode' => 69,
@@ -2854,7 +2854,7 @@
 		             'errorcode' => 18,
 		             'errormsg' => 'user group with submitted name and affiliation does not exist');
 	}
-	// if custom and not owner and not member of managing group or 
+	// if custom and not owner and not member of managing group or
 	//    custom/courseroll and no federated user group access, no access to delete group
 	if(($row['custom'] == 1 && $user['id'] != $row['ownerid'] &&
 	   ! array_key_exists($row['editgroupid'], $user['groups'])) ||
@@ -2930,7 +2930,7 @@
 		             'errormsg' => 'user group with submitted name and affiliation does not exist');
 	}
 	// if not owner and not member of managing group, no access
-	if($user['id'] != $row['ownerid'] && 
+	if($user['id'] != $row['ownerid'] &&
 	   ! array_key_exists($row['editgroupid'], $user['groups'])) {
 		return array('status' => 'error',
 		             'errorcode' => 28,
@@ -3012,7 +3012,7 @@
 		             'errormsg' => 'user group with submitted name and affiliation does not exist');
 	}
 	// if not owner and not member of managing group, no access
-	if($user['id'] != $row['ownerid'] && 
+	if($user['id'] != $row['ownerid'] &&
 	   ! array_key_exists($row['editgroupid'], $user['groups'])) {
 		return array('status' => 'error',
 		             'errorcode' => 28,
@@ -3655,7 +3655,7 @@
 	$stagCnt = 0;
 	$stagTime = 60;        # stagger reload reservations by 1 min
 	if($imgLoadTime > 840) // if estimated load time is > 14 min
-		$stagTime = 120;    #    stagger reload reservations by 2 min 
+		$stagTime = 120;    #    stagger reload reservations by 2 min
 	for($i = 0; $i < $reqToAlloc; $i++) {
 		$stagunixstart = $unixstart - $loadtime - ($stagCnt * $stagTime);
 		$stagstart = unixToDatetime($stagunixstart);
@@ -4003,8 +4003,8 @@
 	        . "LEFT JOIN OS os ON (i.OSid = os.id) "
 	        . "LEFT JOIN ("
 	        .      "SELECT rs.imageid, "
-	        .             "MAX(rq.id) AS requestid, " 
-	        .             "COUNT(rq.id) AS reqcount " 
+	        .             "MAX(rq.id) AS requestid, "
+	        .             "COUNT(rq.id) AS reqcount "
 	        .      "FROM reservation rs, "
 	        .           "request rq "
 	        .      "WHERE rs.requestid = rq.id AND "
@@ -4200,7 +4200,7 @@
 		             'errormsg' => "Specified duration is too long",
 		             'maxduration' => $allowed);
 	}
-	
+
 	$query = "SELECT id "
 	       . "FROM oneclick "
 	       . "WHERE id = $oneclickid AND "
@@ -4218,7 +4218,7 @@
 		             'errorcode' => 90,
 		             'errormsg' => "The OneClick with ID $oneclickid does not belong to the user that requested it.");
 	}*/
-	
+
 	$query = "UPDATE oneclick "
 	       . "SET imageid = $imageid, "
 	       .     "name = '$name', "
@@ -4255,7 +4255,7 @@
 function XMLRPCdeleteOneClick($oneclickid) {
 	global $user;
 	$oneclickid = processInputData($oneclickid, ARG_NUMERIC);
-	
+
 	$query = "SELECT id "
 	       . "FROM oneclick "
 	       . "WHERE id = $oneclickid AND "
diff --git a/web/index.php b/web/index.php
index 0d059c9..7a8e908 100644
--- a/web/index.php
+++ b/web/index.php
@@ -28,9 +28,7 @@
     }
 }
 
-$user = '';
-$mysqli_link_vcl = '';
-$mysqli_link_acct = '';
+$user = array();
 $mode = '';
 $oldmode = '';
 $submitErr = '';
diff --git a/web/js/code.js b/web/js/code.js
index 0b2c024..3391500 100644
--- a/web/js/code.js
+++ b/web/js/code.js
@@ -226,7 +226,7 @@
 	if(document.styleSheets[0].cssRules)  // Standards Compliant
 		cssobj = document.styleSheets[0].cssRules;
 	else
-		cssobj = document.styleSheets[0].rules;  // IE 
+		cssobj = document.styleSheets[0].rules;  // IE
 	var stop = 0;
 	for(var i = 0; i < cssobj.length; i++) {
 		if(cssobj[i].selectorText) {
diff --git a/web/js/dashboard.js b/web/js/dashboard.js
index 59922f2..0591c5c 100644
--- a/web/js/dashboard.js
+++ b/web/js/dashboard.js
@@ -203,6 +203,7 @@
 	    +  '<th>ReqID</th>'
 	    +  '<th>User</th>'
 	    +  '<th>Computer</th>'
+	    +  '<th>VM Host</th>'
 	    +  '<th>States</th>'
 	    +  '<th>Image</th>'
 	    +  '<th>Install Type</th>'
@@ -222,6 +223,8 @@
 		    + '</td><td style=\"padding: 1px; border-right: 1px solid;\">'
 		    + data[i].computer
 		    + '</td><td style=\"padding: 1px; border-right: 1px solid;\">'
+		    + data[i].vmhost
+		    + '</td><td style=\"padding: 1px; border-right: 1px solid;\">'
 		    + data[i].state
 		    + '</td><td style=\"padding: 1px; border-right: 1px solid;\">'
 		    + data[i].image
diff --git a/web/js/requests.js b/web/js/requests.js
index 4a28222..b8272f9 100644
--- a/web/js/requests.js
+++ b/web/js/requests.js
@@ -875,7 +875,7 @@
 	oldstore = dijit.byId('configvariables').store;
 	dijit.byId('configvariables').setStore(newstore2, '', {query: {id: ''}});
 	delete oldstore;
-	
+
 	// finishconfigs
 	/*if(data.items.configs.length == 0)
 		dijit.byId('newResDlgShowConfigBtn').set('disabled', true);
@@ -1388,7 +1388,7 @@
 		dijit.byId('serverdeletedlg').show();
 		return;
 	}
-	if(dijit.byId('serverdeletedlg') && 
+	if(dijit.byId('serverdeletedlg') &&
 	   dijit.byId('serverdeletedlg').open) {
 		dijit.byId('serverdeletedlg').hide();
 		dijit.byId('serverDeleteDlgBtn').set('disabled', false);
@@ -1552,7 +1552,7 @@
 		data.day = dijit.byId('day').value;
 	if(dijit.byId('editstarttime')) {
 		var t = dijit.byId('editstarttime').value;
-		data.starttime = dojox.string.sprintf('%02d%02d', 
+		data.starttime = dojox.string.sprintf('%02d%02d',
 		                                      t.getHours(),
 		                                      t.getMinutes());
 		var tmp = dijit.byId('day').value.match(/([0-9]{4})([0-9]{2})([0-9]{2})/);
@@ -1583,7 +1583,7 @@
 		data.endmode = 'length';
 	}
 	else if((dojo.byId('dateradio') && dojo.byId('dateradio').checked) ||
-	        (dijit.byId('openenddate') && ! dojo.byId('indefiniteradio')) || 
+	        (dijit.byId('openenddate') && ! dojo.byId('indefiniteradio')) ||
 	        (dijit.byId('openenddate') && dojo.byId('indefiniteradio') && ! dojo.byId('indefiniteradio').checked)) {
 		var d = dijit.byId('openenddate').value;
 		var t = dijit.byId('openendtime').value;
diff --git a/web/js/resources/computer.js b/web/js/resources/computer.js
index 6e511ea..dd08e37 100644
--- a/web/js/resources/computer.js
+++ b/web/js/resources/computer.js
@@ -210,6 +210,32 @@
 	return 0;
 }
 
+Computer.prototype.ipsort = function(a, b) {
+	var aparts = a.split('.');
+	var bparts = b.split('.');
+	for(var i = 0; i < 4; i++) {
+		aparts[i] = parseInt(aparts[i]);
+		bparts[i] = parseInt(bparts[i]);
+	}
+	if(aparts[0] < bparts[0])
+		return -1;
+	if(aparts[0] > bparts[0])
+		return 1;
+	if(aparts[1] < bparts[1])
+		return -1;
+	if(aparts[1] > bparts[1])
+		return 1;
+	if(aparts[2] < bparts[2])
+		return -1;
+	if(aparts[2] > bparts[2])
+		return 1;
+	if(aparts[3] < bparts[3])
+		return -1;
+	if(aparts[3] > bparts[3])
+		return 1;
+	return 0;
+}
+
 Computer.prototype.comparehostnames = function(a, b) {
 	// get hostname
 	var tmp = a.split('.');
@@ -291,6 +317,8 @@
 		resourcestore.comparatorMap['procnumber'] = resource.nocasesort;
 		resourcestore.comparatorMap['procspeed'] = resource.nocasesort;
 		resourcestore.comparatorMap['network'] = resource.nocasesort;
+		resourcestore.comparatorMap['IPaddress'] = resource.ipsort;
+		resourcestore.comparatorMap['privateIPaddress'] = resource.ipsort;
 
 		dojo.connect(resourcegrid, '_onFetchComplete', function() {dojo.byId('computercount').innerHTML = 'Computers in table: ' + resourcegrid.rowCount;});
 	}
@@ -1216,7 +1244,9 @@
 	                               procnumber: resource.nocasesort,
 	                               procspeed: resource.nocasesort,
 	                               network: resource.nocasesort,
-	                               ram: resource.nocasesort};
+	                               ram: resource.nocasesort,
+	                               IPaddress: resource.ipsort,
+	                               privateIPaddress: resource.ipsort};
 	resourcestore.fetch();
 	savescroll = resourcegrid.scrollTop;
 	resourcegrid.setStore(resourcestore, resourcegrid.query);
diff --git a/web/js/resources/image.js b/web/js/resources/image.js
index b710c55..4ad565d 100644
--- a/web/js/resources/image.js
+++ b/web/js/resources/image.js
@@ -137,6 +137,12 @@
 	}
 }
 
+function initAddDialog() {
+	if(dijit.byId('addomainid').options.length == 0) {
+		dijit.byId('adauthenable').set('disabled', true);
+	}
+}
+
 function delayedEditResize() {
 	setTimeout(function() {resizeRecenterDijitDialog('addeditdlg');}, 300);
 }
@@ -781,6 +787,7 @@
 function submitCreateUpdateImage() {
 	if(dojo.byId('newimage').checked) {
 		dijit.byId('addeditdlg').show();
+		initAddDialog();
 		dijit.byId('startimagedlg').hide();
 		return;
 	}
diff --git a/web/js/vm.js b/web/js/vm.js
index 0864f9a..b56313b 100644
--- a/web/js/vm.js
+++ b/web/js/vm.js
@@ -183,6 +183,8 @@
 	if(data.items.failed) {
 		if(data.items.failed == 'nohostaccess')
 			alert('You do not have access to manage this VM host.');
+		if(data.items.failed == 'invaliddata')
+			alert('Invalid list of VMs submitted.');
 		document.body.style.cursor = 'default';
 		return;
 	}
@@ -300,15 +302,18 @@
 		handleAs: "json",
 		error: errorHandler,
 		content: {continuation: cont,
-					 listids: listids.join(','),
-					 hostid: hostid},
+		          listids: listids.join(','),
+		          hostid: hostid},
 		timeout: 15000
 	});
 }
 
 function vmFromHostCB(data, ioArgs) {
 	if(data.items.failed) {
-		alert('You do not have access to manage this VM host.');
+		if(data.items.failed == 'nohostaccess')
+			alert('You do not have access to manage this VM host.');
+		if(data.items.failed == 'invaliddata')
+			alert('Invalid list of VMs submitted.');
 		document.body.style.cursor = 'default';
 		return;
 	}
@@ -412,7 +417,10 @@
 
 function reloadVMhostCB(data, ioArgs) {
 	if(data.items.failed) {
-		alert('You do not have access to manage this VM host.');
+		if(data.items.failed == 'nohostaccess')
+			alert('You do not have access to manage this VM host.');
+		if(data.items.failed == 'invaliddata')
+			alert('Invalid list of VMs submitted.');
 		document.body.style.cursor = 'default';
 		return;
 	}