#!/usr/bin/php
<?php

require_once("pre.php");
require_once("common/include/Group.class");
require_once('common/screenshot/ScreenshotFactory.class');
require_once('common/docman/DocumentGroupFactory.class');
require_once('common/docman/DocumentFactory.class');
require_once('common/frs/FRSPackage.class');
require_once('common/frs/FRSUrl.class');
require_once('www/frs/include/frs_utils.php');

$progname = basename($argv[0]);
$stderr = fopen("php://stderr", "w");

function db_query_no_res($query) {

	$res = db_query($query);
	if (!$res) {
		print "error\n";
		report_error("error in database query");
		report_error(db_error());
		return false;
	}

	db_free_result($res);

	return true;

} // end db_query_no_res()

function check_entity($entity, $description) {

	if (!$entity|| !is_object($entity)) {
		print "error\n";
		report_error("error getting $description");
		return false;
	}
	if ($entity->isError()) {
		print "error\n";
		report_error("error getting $description");
		report_error($entity->getErrorMessage());
		return false;
	}

	return true;

} // end check_entity()

function get_subnode_array($node, $keys, $require_all=false, $error_identifier=NULL) {

	$vals = array();
	foreach ($node->childNodes as $cn) {
		if ($cn->nodeType == XML_ELEMENT_NODE 
		    && in_array($cn->nodeName, $keys)) {
			$vals[$cn->nodeName] = $cn->nodeValue;
		}
	}

	if ($require_all) {
		foreach ($keys as $key) {
			if (!array_key_exists($key, $vals)) {
				if (is_null($error_identifier)) {
					print "error\n";
					report_error("missing key \"$key\" in $node->nodeName node");
				} else {
					print "error\n";
					report_error("missing key \"$key\" in $error_identifier");
				}
				return NULL;
			}
		}
	}

	return $vals;

} // end get_subnode_array()

function report_error($msg) {

	global $stderr;
	global $progname;

	fprintf($stderr, "$progname: $msg\n");

	return;

} // end report_error()

function get_image_and_info($fname, $err_ident) {

	$imageinfo = getimagesize($fname);
	if ($imageinfo === false) {
		print "error\n";
		report_error("error reading info for $err_ident");
		return NULL;
	}
	$mime_parts = split("/", $imageinfo['mime']);
	// the array is from $acceptable_types in www/project/admin/managescreenshots.php
	if (!in_array($mime_parts[1], array('JPG','JPEG','PJPEG','PNG','GIF','jpg','jpeg','pjpeg','png','gif'))) {
		print "error\n";
		report_error("bad file type for $err_ident");
		return NULL;
	}
	$data = file_get_contents($fname);
	if ($data === false) {
		print "error\n";
		report_error("error reading data for $err_ident");
		return NULL;
	}
	if (strlen($data) > Screenshot::MAX_IMAGE_SIZE) {
		print "error\n";
		report_error("file too large for $err_ident");
		return NULL;
	}

	return array("data" => $data, 
	             "type" => $imageinfo["mime"], 
	             "width" => $imageinfo[0], 
	             "height" => $imageinfo[1]);

} // end get_image_and_info()

function read_user_node($user_node) {

	$source_id = $user_node->getAttribute("source_id");
	if (strlen($source_id) == 0) {
		print "error\n";
		report_error("source ID not given for user");
		return NULL;
	}

	$arr = get_subnode_array($user_node, array("fullname", "user_name", "url"), true, "user source ID $source_id");
	if (is_null($arr)) {
		return NULL;
	}

	return array("source_id" => $source_id, 
	             "fullname" => $arr["fullname"], 
	             "user_name" => $arr["user_name"], 
	             "url" => $arr["url"]);

} // read_user_node()

function read_group_node($group_node) {

	$source_id = $group_node->getAttribute("source_id");
	if (strlen($source_id) == 0) {
		print "error\n";
		report_error("source ID not given for group");
		return NULL;
	}

	$group_array = get_subnode_array($group_node, array("description", "name", "url", "source_url"), true, "group source ID $source_id");
	if (is_null($group_array)) {
		return NULL;
	}

	$group_array["source_id"] = $source_id;
	$group_array["trove"] = array();
	$group_array["extra_trove"] = array();
	$group_array["users"] = array();
	$group_array["screenshots"] = array();
	$group_array["doc_groups"] = array();
	$group_array["docs"] = array();
	$group_array["packages"] = array();
	$group_array["releases"] = array();
	$group_array["files"] = array();

	foreach ($group_node->childNodes as $cn) {
		if ($cn->nodeType != XML_ELEMENT_NODE) {
			continue;
		}
		if ($cn->nodeName == "trove") {
			$id = $cn->getAttribute("id");
			if (strlen($id) == 0) {
				print "error\n";
				report_error("trove node with no id in group source ID $source_id");
				return NULL;
			}
			$group_array["trove"][] = intval($id);
		} elseif ($cn->nodeName == "extra_trove") {
			$group_array["extra_trove"][] = $cn->nodeValue;
		} elseif ($cn->nodeName == "user") {
			$user_source_id = $cn->getAttribute("source_id");
			if (strlen($user_source_id) == 0) {
				print "error\n";
				report_error("user node with no source ID in group source ID $source_id");
				return NULL;
			}
			$role_id = $cn->getAttribute("role_id");
			if (strlen($role_id) == 0) {
				print "error\n";
				report_error("user node with no role ID in group source ID $source_id");
				return NULL;
			}
			$group_array["users"][] = array("source_id" => $user_source_id, 
			                                "role_id" => intval($role_id));
		} elseif ($cn->nodeName == "screenshot") {
			$ss = read_screenshot_node($cn, $source_id);
			if (is_null($ss)) {
				return NULL;
			}
			$group_array["screenshots"][$ss["source_id"]] = $ss;
		} elseif ($cn->nodeName == "doc_group") {
			$dg = read_doc_group_node($cn, $source_id);
			if (is_null($dg)) {
				return NULL;
			}
			foreach ($dg["docs"] as $doc) {
				$group_array["docs"][$doc["source_id"]] = $doc;
			}
			unset($dg["docs"]);
			$group_array["doc_groups"][$dg["source_id"]] = $dg;
		} elseif ($cn->nodeName == "package") {
			$package = read_package_node($cn, $source_id);
			if (is_null($package)) {
				return NULL;
			}
			foreach ($package["files"] as $file) {
				$group_array["files"][$file["source_id"]] = $file;
			}
			unset($package["files"]);
			foreach ($package["releases"] as $rel) {
				$group_array["releases"][$rel["source_id"]] = $rel;
			}
			unset($package["releases"]);
			$group_array["packages"][$package["source_id"]] = $package;
		}
	}

	return $group_array;

} // end read_group_node()

function read_screenshot_node($ss_node, $group_source_id) {

	$ss_source_id = $ss_node->getAttribute("source_id");
	if (strlen($ss_source_id) == 0) {
		print "error\n";
		report_error("screenshot node with no source ID in group source ID $group_source_id");
		return NULL;
	}
	$ss_arr = get_subnode_array($ss_node, array("file", "description", "is_default", "last_updated"), true, "screenshot source ID $ss_source_id in group source ID $group_source_id");
	if (is_null($ss_arr)) {
		return NULL;
	}
	$ss_arr["is_default"] = intval($ss_arr["is_default"]);
	$ss_arr["source_id"] = $ss_source_id;

	return $ss_arr;

} // end read_screenshot_node()

function read_doc_group_node($dg_node, $group_source_id) {

	$dg_source_id = $dg_node->getAttribute("source_id");
	if (strlen($dg_source_id) == 0) {
		print "error\n";
		report_error("doc_group node with no source ID in group source ID $group_source_id");
		return NULL;
	}
	$dg_arr = get_subnode_array($dg_node, array("name"), true, "doc_group source ID $dg_source_id in group source ID $group_source_id");
	if (is_null($dg_arr)) {
		return NULL;
	}
	$dg_arr["source_id"] = $dg_source_id;

	$dg_arr["docs"] = array();
	foreach ($dg_node->childNodes as $cn) {
		if ($cn->nodeType != XML_ELEMENT_NODE 
		    || $cn->nodeName != "doc") {
			continue;
		}
		$doc = read_doc_node($cn, $group_source_id, $dg_source_id);
		if (is_null($doc)) {
			return NULL;
		}
		$doc["doc_group_source_id"] = $dg_source_id;
		$dg_arr["docs"][$doc["source_id"]] = $doc;
	}

	return $dg_arr;

} // end read_doc_group_node()

function read_doc_node($doc_node, $group_source_id, $dg_source_id) {

	$doc_source_id = $doc_node->getAttribute("source_id");
	if (strlen($doc_source_id) == 0) {
		print "error\n";
		report_error("doc node with no source ID in doc group source ID $dg_source_id in group source ID $group_source_id");
		return NULL;
	}
	$doc_arr = get_subnode_array($doc_node, array("title", "description", "url"), true, "doc source ID $doc_source_id in doc_group source ID $dg_source_id in group source ID $group_source_id");
	if (is_null($doc_arr)) {
		return NULL;
	}
	$doc_arr["source_id"] = $doc_source_id;

	return $doc_arr;
	
} // end read_doc_node()

function read_package_node($p_node, $group_source_id) {

	$p_source_id = $p_node->getAttribute("source_id");
	if (strlen($p_source_id) == 0) {
		print "error\n";
		report_error("package node with no source ID in group source ID $group_source_id");
		return NULL;
	}
	$p_arr = get_subnode_array($p_node, array("name"), true, "package source ID $p_source_id in group source ID $group_source_id");
	if (is_null($p_arr)) {
		return NULL;
	}
	$p_arr["source_id"] = $p_source_id;

	$p_arr["releases"] = array();
	$p_arr["files"] = array();
	foreach ($p_node->childNodes as $cn) {
		if ($cn->nodeType != XML_ELEMENT_NODE || $cn->nodeName != "release") {
			continue;
		}
		$rel = read_release_node($cn, $group_source_id, $p_source_id);
		if (is_null($rel)) {
			return NULL;
		}
		$rel["package_source_id"] = $p_source_id;
		foreach ($rel["files"] as $file) {
			$p_arr["files"][] = $file;
		}
		unset($rel["files"]);
		$p_arr["releases"][$rel["source_id"]] = $rel;
	}

	return $p_arr;

} // end read_package_node()

function read_release_node($rel_node, $group_source_id, $package_source_id) {

	$rel_source_id = $rel_node->getAttribute("source_id");
	if (strlen($rel_source_id) == 0) {
		print "error\n";
		report_error("release node with no source ID in package source ID $package_source_id in group source ID $group_source_id");
		return NULL;
	}
	$rel_arr = get_subnode_array($rel_node, array("name", "notes", "changes", "preformatted", "release_date"), true, "release source ID $rel_source_id in package source ID $package_source_id in group source ID $group_source_id");
	if (is_null($rel_arr)) {
		return NULL;
	}
	$rel_arr["source_id"] = $rel_source_id;
	$rel_arr["preformatted"] = intval($rel_arr["preformatted"]);
	$rel_arr["release_date"] = intval($rel_arr["release_date"]);

	$rel_arr["files"] = array();
	foreach ($rel_node->childNodes as $cn) {
		if ($cn->nodeType != XML_ELEMENT_NODE || $cn->nodeName != "file") {
			continue;
		}
		$file = read_file_node($cn, $group_source_id, $package_source_id, $release_source_id);
		if (is_null($file)) {
			return NULL;
		}
		$file["release_source_id"] = $rel_source_id;
		$rel_arr["files"][$file["source_id"]] = $file;
	}

	return $rel_arr;

} // end read_release_node()

function read_file_node($file_node, $group_source_id, $package_source_id, $release_source_id) {

	$file_source_id = $file_node->getAttribute("source_id");
	if (strlen($file_source_id) == 0) {
		print "error\n";
		report_error("file node with no source ID in release source ID $release_source_id in package source ID $package_source_id in group source ID $group_source_id");
		return NULL;
	}
	$file_arr = get_subnode_array($file_node, array("filename", "url", "release_date"), true, "in file source ID $file_source_id in release source ID $release_source_id in package source ID $package_source_id in group source ID $group_source_id");
	if (is_null($file_arr)) {
		return NULL;
	}
	if (!util_is_valid_url($file_arr["url"])) {
		print "error\n";
		report_error("invalid URL for file source ID $source_id");
		return NULL;
	}
	$file_arr["source_id"] = $file_source_id;
	$file_arr["release_date"] = intval($file_arr["release_date"]);

	return $file_arr;

} // end read_release_node()

function resolve_group(&$group_array) {

	global $source;
	global $trove_cat_ids;
	global $external_users;

	$query = "SELECT group_id " . 
	         "  FROM groups " . 
	         " WHERE source = '" . pg_escape_string($source) . "' " . 
	         "   AND source_id = '" . pg_escape_string($group_array["source_id"]) . "'";
	$res = db_query($query);
	if (!$res) {
		print "error\n";
		report_error("error getting group");
		report_error(db_error());
		return false;
	}
	if (pg_num_rows($res) == 0) {
		print "error\n";
		report_error("can't find group source \"$source\" ID \"" . $group_array["source_id"] . "\"");
		return false;
	}
	$group_id = pg_fetch_result($res, 0, "group_id");
	pg_free_result($res);

	$group = group_get_object($group_id);
	if (!check_entity($group, "group ID $group_id")) {
		return false;
	}

	$group_array["group"] = &$group;

	if (strlen($group_array["description"]) > 1023) {
		print "error\n";
		report_error("description is too long for group $group_id");
		return false;
	}

	if (strlen($group_array["name"]) > 40) {
		print "error\n";
		report_error("name is too long for group $group_id");
		return false;
	}

	if (strlen($group_array["url"]) > 128) {
		print "error\n";
		report_error("URL is too long for group $group_id");
		return false;
	}

	if (substr($group_array["url"], 0, 7) != "http://") {
		print "error\n";
		report_error("bad URL for group $group_id");
		return false;
	}
	$group_array["url"] = substr($group_array["url"], 7);

	foreach ($group_array["trove"] as $trove_id) {
		if (!in_array($trove_id, $trove_cat_ids)) {
			print "error\n";
			report_error("unknown trove category ID $trove_id in group source ID " . $group_array["source_id"]);
			return false;
		}
	}

	$query = "SELECT user_id FROM user_group WHERE group_id = $group_id";
	$res = db_query($query);
	if (!$res) {
		print "error\n";
		report_error("error getting current users");
		return false;
	}
	$group_array["old_users"] = array();
	while ($row = db_fetch_array($res)) {
		$group_array["old_users"][] = intval($row["user_id"]);
	}
	db_free_result($res);

	foreach ($group_array["users"] as &$user) {
		if (!array_key_exists($user["source_id"], $external_users)) {
			print "error\n";
			report_error("unknown user \"" . $user["source_id"] . "\" in group source ID " . $group_array["source_id"]);
			return false;
		}
		$user["external_user_id"] = $external_users[$user["source_id"]];
		$query = "SELECT role_id " . 
		         "FROM role " . 
		         "WHERE role_id = " . $user["role_id"] . 
		         "AND group_id = " . $group->getID();
		$res = db_query($query);
		if (!$res) {
			print "error\n";
			report_error("error checking for role");
			report_error(db_error());
			return false;
		}
		$n_rows = pg_num_rows($res);
		pg_free_result($res);
		if($n_rows == 0) {
			print "error\n";
			report_error("role ID " . $user["role_id"] . " not found for group " . $group->getID());
			return false;
		}
	}

	$sf = new ScreenshotFactory($group);
	if (!check_entity($sf, "screenshots for group ID " . $group->getID())) {
		return false;
	}

	$group_array["old_screenshots"] = array();
	$screenshots = $sf->getScreenshots();
	if (count($screenshots) > 0) {
		foreach ($sf->getScreenshots() as $ss) {
			if (!check_entity($ss, "screenshot")) {
				return false;
			}
			$source_id = $ss->getSourceID();
			if (array_key_exists($source_id, $group_array["screenshots"])) {
				$group_array["screenshots"][$source_id]["screenshot"] = $ss;
			} else {
				$group_array["old_screenshots"][] = $ss;
			}
		}
	}

	$dgf = new DocumentGroupFactory($group);
	if (!check_entity($dgf, "document groups for group ID " . $group->getID())) {
		return false;
	}

	$group_array["old_doc_groups"] = array();
	$group_array["all_doc_groups"] = array();
	foreach ($dgf->getDocumentGroups() as $dg) {
		if (!check_entity($dg, "document group")) {
			return false;
		}
		$source_id = $dg->getSourceID();
		$group_array["all_doc_groups"][$source_id] = $dg;
		if (array_key_exists($source_id, $group_array["doc_groups"])) {
			$group_array["doc_groups"][$source_id]["doc_group"] = $dg;
		} else {
			$group_array["old_doc_groups"][] = $dg;
		}
	}

	$group_array["old_docs"] = array();

	$res = db_query("SELECT docid FROM docdata_vw WHERE group_id = " . $group->getID());
	if (!$res) {
		print "error\n";
		report_error("error getting documents");
		report_error(db_error());
		return false;
	}
	while ($row = pg_fetch_array($res)) {
		$doc = new Document($group, $row["docid"]);
		if (!check_entity($doc, "document")) {
			return false;
		}
		$source_id = $doc->getSourceID();
		if (array_key_exists($source_id, $group_array["docs"])) {
			$group_array["docs"][$source_id]["doc"] = $doc;
		} else {
			$group_array["old_docs"][] = $doc;
		}
	}
	pg_free_result($res);

	$res = db_query("SELECT COUNT(*) FROM frs_package WHERE group_id = " . $group->getID());
	if (!$res) {
		print "error\n";
		report_error("error checking for packages for group ID $group_id");
		report_error(db_error());
		return false;
	}
	$n_packages = intval(db_result($res, 0, 0));
	db_free_result($res);

	if ($n_packages == 0) {
		$packages = array();
	} else {
		$packages = get_frs_packages($group);
		if (!is_array($packages)) {
			print "error\n";
			report_error("error getting packages for group ID " . $group->getID());
			return false;
		}
	}

	$group_array["old_packages"] = array();
	$group_array["old_releases"] = array();
	$group_array["old_files"] = array();
	$group_array["all_packages"] = array();
	$group_array["all_releases"] = array();
	foreach ($packages as $package) {
		if (!check_entity($package, "package for group ID ". $group->getID())) {
			return false;
		}
		$source_id = $package->getSourceID();
		$group_array["all_packages"][$source_id] = $package;
		if (array_key_exists($source_id, $group_array["packages"])) {
			$group_array["packages"][$source_id]["package"] = $package;
		} else {
			$group_array["old_packages"][] = $package;
		}
		$releases = $package->getReleases();
		if (!is_array($releases)) {
			print "error\n";
			report_error("error getting releases for package ID " . $package->getID() . " for group ID " . $group->getID());
			return false;
		}
		foreach ($releases as $release) {
			if (!check_entity($release, "release for package ID " . $release->getID() . " for group ID " . $group->getID())) {
				return false;
			}
			$source_id = $release->getSourceID();
			$group_array["all_releases"][$source_id] = $release;
			if (array_key_exists($source_id, $group_array["releases"])) {
				$group_array["releases"][$source_id]["release"] = $release;
			} else {
				$group_array["old_releases"][] = $release;
			}
			$files = $release->getFiles();
			if (!is_array($files)) {
				print "error\n";
				report_error("error getting files for release ID " . $release->getID() . " for package ID " . $package->getID() . " for group ID " . $group->getID());
				return false;
			}
			foreach ($files as $file) {
				if (!check_entity($file, "file for release ID " . $release->getID() . " for package ID " . $package->getID() . " for group ID " . $group->getID())) {
					return false;
				}
				$source_id = $file->getSourceID();
				if (array_key_exists($source_id, $group_array["files"])) {
					if ($file->FRSRelease->getSourceID() != $group_array["files"][$source_id]["release_source_id"]) {
						print "error\n";
						report_error("changing FRS source ID for file source ID $source_id");
						return false;
					}
					$group_array["files"][$source_id]["file"] = $file;
				} else {
					$group_array["old_files"][] = $file;
				}
			}
		}
	}

	return true;

} // end resolve_group()

function update_group($group_array) {

	$group = $group_array["group"];
	$group_id = $group->getID();

	print "updating group ID $group_id (" . $group_array["group"]->getUnixName() . ")\n";

	print "    updating basic information\n";

	$query = "UPDATE groups " . 
	         "   SET short_description = '" .  pg_escape_string($group_array["description"]) . "', " . 
	         "       group_name = '" .  pg_escape_string($group_array["name"]) . "', " . 
	         "       homepage = '" .  pg_escape_string($group_array["url"]) . "', " . 
	         "       source_url = '" .  pg_escape_string($group_array["source_url"]) . "'" . 
	         " WHERE group_id = $group_id";
	if (!db_query_no_res($query)) {
		return false;
	}

	print "    removing old users\n";

	foreach ($group_array["old_users"] as $user_id) {
		print "        removing user ID $user_id\n";
		if (!$group->removeUser($user_id)) {
			print "error\n";
			report_error("error removing user $user_id from group $group_id");
			report_error($group->getErrorMessage());
			return false;
		}
	}

	print "        removing external users\n";
	$query = "DELETE FROM external_user_group WHERE group_id = $group_id";
	if (!db_query_no_res($query)) {
		return false;
	}

	print "    adding users\n";

	foreach ($group_array["users"] as $user) {
		print "        adding source ID " . $user["external_user_id"] . "\n";
		$query = "INSERT INTO external_user_group (external_user_id, group_id, role_id) VALUES (" . $user["external_user_id"] . ", $group_id, " .  $user["role_id"] . ")";
		if (!db_query_no_res($query)) {
			return false;
		}
	}

	print "    updating trove\n";

	print "        clearing trove\n";
	$query = "DELETE FROM trove_group_link WHERE group_id = $group_id";
	if (!db_query_no_res($query)) {
		return false;
	}

	foreach ($group_array["trove"] as $trove_cat_id) {
		print "        adding trove category ID $trove_cat_id\n";
		if (trove_setnode($group_id, $trove_cat_id) != 0) {
			print "error\n";
			report_error("error setting trove (ID $trove_cat_id) for group ID $group_id");
			return false;
		}
	}

	print "        updating extra trove\n";

	print "        clearing extra trove\n";
	$query = "DELETE FROM trove_group_extra WHERE group_id = $group_id";
	if (!db_query_no_res($query)) {
		return false;
	}

	foreach ($group_array["extra_trove"] as $trove_cat) {
		print "        adding extra trove category \"$trove_cat\"\n";
		$query = "INSERT INTO trove_group_extra (group_id, fullname) " . 
		         "VALUES ($group_id, '" . pg_escape_string($trove_cat) . "')";
		if (!db_query_no_res($query)) {
			return false;
		}
	}

	print "    updating screenshots\n";

	foreach ($group_array["old_screenshots"] as $ss) {
		print "        removing screenshot ID " . $ss->getID() . "\n";
		if (!$ss->delete(1)) {
			print "error\n";
			report_error("error removing screenshot ID " . $ss->getID());
			report_error($ss->getErrorMessage());
			return false;
		}
	}

	foreach ($group_array["screenshots"] as $ss_arr) {
		if (!array_key_exists("screenshot", $ss_arr)) {
			print "        creating source ID " . $ss_arr["source_id"] . "\n";
			if(ScreenshotFactory::getScreenshotCount($group_id) >= 6) {
				print "error\n";
				report_error("screenshot limit reached for group ID $group_id\n");
				return false;
			}
			$im_arr = get_image_and_info($ss_arr["file"], "screenshot source ID " . $ss_arr["source_id"] . " for group ID $group_id");
			if (is_null($im_arr)) {
				return false;
			}
			$ss = new Screenshot($group);
			if (!check_entity($ss, "screenshot source ID " . $ss_arr["source_id"] . " for group ID $group_id")) {
				return false;
			}
			if (!$ss->create($group_id, strlen($im_arr["data"]), $im_arr["data"], $ss_arr["description"], $im_arr["type"], basename($ss_arr["file"]), $im_arr["width"], $im_arr["height"], $ss_arr["is_default"], $ss_arr["source_id"])) {
				print "error\n";
				report_error("error creating screenshot");
				report_error($ss->getErrorMessage());
				return false;
			}
			print "            NITRC ID " . $ss->getID() . "\n";
		} else {
			$ss = $ss_arr["screenshot"];
			if ($ss_arr["last_updated"] > $ss->getUpdateDate()) {
				print "        updating source ID " . $ss_arr["source_id"] . " (NITRC ID " . $ss->getID() . ")\n";
				if (!$ss->update($group_id, $ss_arr[$description], $ss->getID(), $ss->isDefault())) {
					print "error\n";
					report_error("error creating screenshot source ID " . $ss_arr["source_id"] . " for group ID $group_id");
					report_error($ss->getErrorMessage());
					return false;
				}
				$im_arr = get_image_and_info($ss_arr["file"], "screenshot source ID " . $ss_arr["source_id"] . " for group ID $group_id");
				if (is_null($im_arr)) {
					return false;
				}
				if (!$ss->updateImage(strlen($im_arr["data"]), $im_arr["data"], $im_arr["type"], basename($ss_arr["file"]), $im_arr["width"], $im_arr["height"])) {
					print "error\n";
					report_error("error updating screenshot source ID " . $ss_arr["source_id"] . " for group ID $group_id");
					report_error($ss->getErrorMessage());
					return false;
				}
			} else {
				print "        not updating source ID " . $ss_arr["source_id"] . "\n";
			}
		}
	}

	print "    creating doc groups\n";
	foreach ($group_array["doc_groups"] as $dg_arr) {
		if (array_key_exists("doc_group", $dg_arr)) {
			continue;
		}
		print "        creating doc group source ID " . $dg_arr["source_id"] . "\n";
		$dg = new DocumentGroup($group);
		if (!check_entity($dg, "document group for group ID $group_id")) {
			return false;
		}
		if (!$dg->create($dg_arr["name"], 0, $dg_arr["source_id"])) {
			print "error\n";
			report_error("error creating doc group source ID " . $dg_arr["source_id"] . " for group ID $group_id");
			report_error($dg->getErrorMessage());
			return false;
		}
		$group_array["all_doc_groups"][$dg_arr["source_id"]] = $dg;
	}

	print "    creating docs\n";
	foreach ($group_array["docs"] as $doc_arr) {
		if (array_key_exists("doc", $doc_arr)) {
			continue;
		}
		print "        creating doc source ID " . $doc_arr["source_id"] . "\n";
		if (!array_key_exists($doc_arr["doc_group_source_id"], $group_array["all_doc_groups"])) {
			print "error\n";
			report_error("can't find doc group source ID " . $doc_arr["doc_group_source_id"] . " to create doc source ID " . $doc_arr["source_id"] . " for group ID $group_id");
			return false;
		}
		$dg = $group_array["all_doc_groups"][$doc_arr["doc_group_source_id"]];
		$doc = new Document($group);
		if (!check_entity($doc, "document for group ID $group_id")) {
			return false;
		}
		if (!$doc->create($doc_arr["url"], "URL", "", $dg->getID(), $doc_arr["title"], 1, $doc_arr["description"], $doc_arr["source_id"])) {
			print "error\n";
			report_error("error creating document source ID " . $doc_arr["source_id"] . " for group ID $group_id");
			report_error($doc->getErrorMessage());
			return false;
		}
		print "            NITRC ID " . $doc->getID() . "\n";
	}

	print "    updating doc groups\n";
	foreach ($group_array["doc_groups"] as $dg_arr) {
		if (!array_key_exists("doc_group", $dg_arr)) {
			continue;
		}
		$dg = $dg_arr["doc_group"];
		print "        updating doc group source ID " . $dg_arr["source_id"] . " (NITRC ID " . $dg->getID() . ")\n";
		if (!$dg->update($dg_arr["name"], $dg->getParentID())) {
			print "error\n";
			report_error("error updating doc group source ID " . $dg_arr["source_id"] . " for group ID $group_id");
			report_error($dg->getErrorMessage());
			return false;
		}
	}

	print "    updating docs\n";
	foreach ($group_array["docs"] as $doc_arr) {
		if (!array_key_exists("doc", $doc_arr)) {
			continue;
		}
		$doc = $doc_arr["doc"];
		if (!array_key_exists($doc_arr["doc_group_source_id"], $group_array["all_doc_groups"])) {
			print "error\n";
			report_error("can't find doc group source ID " . $doc_arr["doc_group_source_id"] . " to update doc source ID " . $doc_arr["source_id"] . " for group ID $group_id");
			return false;
		}
		$dg = $group_array["all_doc_groups"][$doc_arr["doc_group_source_id"]];
		print "        updating doc source ID " . $doc_arr["source_id"] . " (NITRC ID " . $doc->getID() . ")\n";
		if (!$doc->update($doc_arr["url"], "URL", "", $dg->getID(), $doc_arr["title"], $doc->getLanguageID(), $doc_arr["description"], $doc->getStateID())) {
			print "error\n";
			report_error("error updating document source ID " . $doc_arr["source_id"] . " for group ID $group_id");
			report_error($doc->getErrorMessage());
			return false;
		}
	}

	print "    removing docs\n";
	foreach ($group_array["old_docs"] as $doc) {
		print "        removing doc ID " . $doc->getID() . "\n";
		if (!$doc->delete()) {
			print "error\n";
			report_error("error removing doc ID " . $doc->getID());
			report_error($doc->getErrorMessage());
			return false;
		}
	}

	print "    creating packages\n";
	foreach ($group_array["packages"] as $package_arr) {
		if (array_key_exists("package", $package_arr)) {
			continue;
		}
		print "        creating package source ID " . $package_arr["source_id"] . "\n";
		$package = new FRSPackage($group);
		if (!check_entity($package, "package for group ID $group_id")) {
			return false;
		}
		if (!$package->create($package_arr["name"], 1, $package_arr["source_id"])) {
			print "error\n";
			report_error("error creating package source ID " . $package_arr["source_id"] . " for group ID $group_id");
			report_error($package->getErrorMessage());
			return false;
		}
		$group_array["all_packages"][$package_arr["source_id"]] = $package;
		print "            NITRC ID " . $package->getID() . "\n";
	}

	print "    creating releases\n";
	foreach ($group_array["releases"] as $release_arr) {
		if (array_key_exists("release", $release_arr)) {
			continue;
		}
		print "        creating release source ID " . $release_arr["source_id"] . "\n";
		if (!array_key_exists($release_arr["package_source_id"], $group_array["all_packages"])) {
			print "error\n";
			report_error("can't find package source ID " . $release_arr["package_source_id"] . " to create release source ID " . $release_arr["source_id"] . " for group ID $group_id");
			return false;
		}
		$package = $group_array["all_packages"][$release_arr["package_source_id"]];
		$release = new FRSRelease($package);
		if (!check_entity($release, "release for package ID " . $package->getID() . " for group ID $group_id")) {
			return false;
		}
		if (!$release->create($release_arr["name"], $release_arr["notes"], $release_arr["changes"], $release_arr["preformatted"], $release_arr["release_date"], $release_arr["source_id"])) {
			print "error\n";
			report_error("error creating release source ID " . $release_arr["source_id"] . " for package ID " . $package->getID() . " for group ID $group_id");
			report_error($release->getErrorMessage());
			return false;
		}
		$group_array["all_releases"][$release_arr["source_id"]] = $release;
		print "            NITRC ID " . $release->getID() . "\n";
	}

	print "    creating files\n";
	foreach ($group_array["files"] as $file_arr) {
		if (array_key_exists("file", $file_arr)) {
			continue;
		}
		print "        creating file source ID " . $file_arr["source_id"] . "\n";
		if (!array_key_exists($file_arr["release_source_id"], $group_array["all_releases"])) {
			print "error\n";
			report_error("can't find release source ID " . $file_arr["release_source_id"] . " to create file source ID " . $file_arr["source_id"] . " for group ID $group_id");
			return false;
		}
		$release = $group_array["all_releases"][$file_arr["release_source_id"]];
		$file = new FRSUrl($release);
		if (!check_entity($file, "file for release ID " . $release->getID() . " for group ID $group_id")) {
			return false;
		}
		if (!$file->create($file_arr["filename"], $file_arr["url"], 8400, 9999, $file_arr["release_date"], $file_arr["source_id"])) {
			print "error\n";
			report_error("error creating file source ID " . $file_arr["source_id"] . " for release ID " . $release->getID() . " for group ID $group_id");
			report_error($file->getErrorMessage());
			return false;
		}
		print "            NITRC ID " . $file->getID() . "\n";
	}

	print "    updating packages\n";
	foreach ($group_array["packages"] as $package_arr) {
		if (!array_key_exists("package", $package_arr)) {
			continue;
		}
		$package = $package_arr["package"];
		print "        updating package source ID " . $package_arr["source_id"] . "\n";
		if (!$package->update($package_arr["name"], $package->getStatus())) {
			print "error\n";
			report_error("error updating package source ID " . $package_arr["source_id"] . " for group ID $group_id");
			report_error($package->getErrorMessage());
			return false;
		}
	}

	print "    updating releases\n";
	foreach ($group_array["releases"] as $release_arr) {
		if (!array_key_exists("release", $release_arr)) {
			continue;
		}
		$release = $release_arr["release"];
		print "        updating release source ID " . $release_arr["source_id"] . "\n";
		if (!$release->update($release->getStatus(), $release_arr["name"], $release_arr["notes"], $release_arr["changes"], $release_arr["preformatted"], $release_arr["release_date"])) {
			print "error\n";
			report_error("error updating release source ID " . $release_arr["source_id"] . " for group $group_id");
			report_error($release->getErrorMessage());
			return false;
		}
	}

	print "    updating files\n";
	foreach ($group_array["files"] as $file_arr) {
		if (!array_key_exists("file", $file_arr)) {
			continue;
		}
		$file = $file_arr["file"];
		print "        updating file source ID " . $file_arr["source_id"] . "\n";
		if (!$file->update(8400, $file->getProcessorID(), $file_arr["release_date"])) {
			print "error\n";
			report_error("error updating attributes of file source ID " . $file_arr["source_id"] . " for group $group_id");
			report_error($file->getErrorMessage());
			return false;
		}
		$url = new FRSUrl($file->FRSRelease, $file->getID());
		if (!$url->updateContent($file_arr["filename"], $file_arr["url"])) {
			print "error\n";
			report_error("error updating content of file source ID " . $file_arr["source_id"] . " for group $group_id");
			report_error($url->getErrorMessage());
			return false;
		}
	}

	print "    removing files\n";
	foreach ($group_array["old_files"] as $file) {
		print "        removing file ID " . $file->getID() . "\n";
		if (!$file->delete()) {
			print "error\n";
			report_error("error removing file ID " . $file->getID());
			report_error($file->getErrorMessage());
			return false;
		}
	}

	print "    removing releases\n";
	foreach ($group_array["old_releases"] as $release) {
		print "        removing release ID " . $release->getID() . "\n";
		if (!$release->delete(true, true)) {
			print "error\n";
			report_error("error removing release ID " . $release->getID());
			report_error($release->getErrorMessage());
			return false;
		}
	}

	print "    removing packages\n";
	foreach ($group_array["old_packages"] as $package) {
		print "        removing package ID " . $package->getID() . "\n";
		if (!$package->delete(true, true)) {
			print "error\n";
			report_error("error removing package ID " . $package->getID());
			report_error($package->getErrorMessage());
			return false;
		}
	}

	print "    updating files\n";

	return true;

} // end update_group()

// --- begin execution ---

if (count($argv) != 2) {
	fprintf($stderr, "usage: $progname <update file>\n");
	exit(1);
}

$user = user_get_object_by_name("nitrcadmin");
if (!$user) {
	print "error\n";
	report_error("error setting user");
	exit(1);
}
$user->setLoggedIn();
$G_SESSION = $user;

// --- first pass: just read the XML ---

$doc = new DOMDocument();
if (!$doc->load($argv[1])) {
	exit(1);
}

$import_node = $doc->childNodes->item(0);
if ($import_node->nodeName != "import") {
	print "error\n";
	report_error("can't find import node");
	exit(1);
}
$source = $import_node->getAttribute("source");
if (strlen($source) == 0) {
	print "error\n";
	report_error("no source attribute");
	exit(1);
}

$users = array();
$groups = array();

foreach ($import_node->childNodes as $cn) {
	if ($cn->nodeType != XML_ELEMENT_NODE) {
		continue;
	}
	if($cn->nodeName == "group") {
		$group = read_group_node($cn);
		if (is_null($group)) {
			exit(1);
		}
		$groups[] = $group;
	}
	if($cn->nodeName == "user") {
		$user = read_user_node($cn);
		if (is_null($user)) {
			exit(1);
		}
		$users[] = $user;
	}
}

// --- add and update the users ---

foreach ($users as $user) {

	if (!db_query_no_res("BEGIN")) {
		exit(1);
	}

	if (!db_query_no_res("LOCK TABLE external_user IN SHARE ROW EXCLUSIVE MODE")) {
		exit(1);
	}

	$query = "SELECT id " . 
	         "  FROM external_user " . 
	         " WHERE source = '" . pg_escape_string($source) . "' " . 
	         "   AND source_id = '" . pg_escape_string($user["source_id"]) . "'";
	$res = db_query($query);
	if (!$res) {
		fprintf($stderr, "$progname: error in database query\n");
		fprintf($stderr, db_error() . "\n");
		exit(1);
	}
	if (strlen(trim($user["url"])) == 0) {
		$url_esc = "NULL";
	} else {
		$url_esc = "'" . pg_escape_string($user["url"]) . "'";
	}
	if (pg_num_rows($res) == 0) {
		$query = "INSERT INTO external_user (source, source_id, fullname, source_user_name, url) " . 
		         "VALUES ('" . pg_escape_string($source) . "', " . 
		         "        '" . pg_escape_string($user["source_id"]) . "', " . 
		         "        '" . pg_escape_string($user["fullname"]) . "', " . 
		         "        '" . pg_escape_string($user["user_name"]) . "', " . 
		         "        $url_esc)";
		print "creating user source ID " . $user["source_id"] . "\n";
	} else {
		$query = "UPDATE external_user " . 
		         "   SET fullname = '" . pg_escape_string($user["fullname"]) . "', " . 
		         "       source_user_name = '" . pg_escape_string($user["user_name"]) . "', " . 
		         "       url = $url_esc " . 
		         " WHERE id = " . pg_fetch_result($res, 0, "id");
		print "updating user source ID " . $user["source_id"] . "\n";
	}
	pg_free_result($res);

	$res = db_query($query);
	if (!$res) {
		fprintf($stderr, "$progname: error updating user\n");
		fprintf($stderr, db_error() . "\n");
		exit(1);
	}
	pg_free_result($res);

	if (!db_query_no_res("COMMIT")) {
		exit(1);
	}

}

// --- resolve the referenced entities in the groups ---

$trove_cat_ids = array();
$res = db_query("SELECT trove_cat_id FROM trove_cat");
if (!$res) {
	print "error\n";
	report_error("error getting trove category IDs");
	fprintf($stderr, db_error() . "\n");
	exit(1);
}
while ($row = db_fetch_array($res)) {
	$trove_cat_ids[] = intval($row["trove_cat_id"]);
}

$external_users = array();
$query = "SELECT id, source_id " . 
         "  FROM external_user " . 
         " WHERE source = '" . pg_escape_string($source) . "'";
$res = db_query($query);
if (!$res) {
	print "error\n";
	report_error("error getting external users");
	fprintf($stderr, db_error() . "\n");
	exit(1);
}
while ($row = pg_fetch_array($res)) {
	$external_users[$row["source_id"]] = $row["id"];
}
pg_free_result($res);

foreach ($groups as &$group) {
	if (!resolve_group($group)) {
		exit(1);
	}
}

// --- update the groups ---

foreach ($groups as &$group) {
	if (!update_group($group)) {
		exit(1);
	}
}

exit(0);

?>
