API Version 2 * This is the current version of the API. It should be used for any new code * development. Any older code needs to be migrated to this version.\n\n * Authentication is handled by 2 additional HTTP headers you will need to * send:\n * \b X-User - the userid you would use to log in to the VCL site, followed * by the at sign (@), followed by your affiliation\n * example: myuserid\@NCSU\n * You can obtain a list of the affiliations by using the XMLRPCaffiliations() * call\n\n * \b X-Pass - the password you would use to log in to the VCL site\n * \n * There is one other additional HTTP header you must send:\n * \b X-APIVERSION - set this to 2\n\n * The X-User and X-Pass HTTP headers do not need to be passed to call the * XMLRPCaffiliations() function. */ /// \example xmlrpc_example.php //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaffiliations() /// /// \return an array of affiliation arrays, each with 2 indices:\n /// \b id - id of the affiliation\n /// \b name - name of the affiliation /// /// \brief gets all of the affilations for which users can log in to VCL\n /// \b NOTE: This is the only function available for which the X-User and X-Pass /// HTTP headers do not need to be passed /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaffiliations() { $affils = getAffiliations(); $return = array(); foreach($affils as $key => $val) { $tmp = array('id' => $key, 'name' => $val); array_push($return, $tmp); } return $return; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCtest($string) /// /// \param $string - a string /// /// \return an array with 3 indices:\n /// \b status - will be 'success'\n /// \b message - will be 'RPC call worked successfully'\n /// \b string - contents of $string (after being sanatized) /// /// \brief this is a test function that call be called when getting XML RPC /// calls to this site to work /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCtest($string) { $string = processInputData($string, ARG_STRING); return array('status' => 'success', 'message' => 'RPC call worked successfully', 'string' => $string); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetImages() /// /// \return an array of image arrays, each with these indices:\n /// \b id - id of the image\n /// \b name - name of the image\n /// \b description - description of image\n /// \b usage - usage instructions for image /// /// \brief gets the images to which the user has access /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetImages() { $resources = getUserResources(array("imageAdmin", "imageCheckOut")); $resources["image"] = removeNoCheckout($resources["image"]); $return = array(); foreach($resources['image'] as $key => $val) { $notes = getImageNotes($key); $tmp = array('id' => $key, 'name' => $val, 'description' => $notes['description'], 'usage' => $notes['usage']); array_push($return, $tmp); } return $return; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddRequest($imageid, $start, $length, $foruser) /// /// \param $imageid - id of an image /// \param $start - "now" or unix timestamp for start of reservation; will /// use a floor function to round down to the nearest 15 minute increment /// for actual reservation /// \param $length - length of reservation in minutes (must be in 15 minute /// increments) /// \param $foruser - (optional) login to be used when setting up the account /// on the reserved machine - CURRENTLY, THIS IS UNSUPPORTED /// /// \return an array with at least one index named '\b status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b notavailable - no computers were available for the request\n /// \b success - there will be an additional element in the array: /// \li \b requestid - identifier that should be passed to later calls when /// acting on the request /// /// \brief tries to make a request /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddRequest($imageid, $start, $length, $foruser='') { global $user; $imageid = processInputData($imageid, ARG_NUMERIC); $start = processInputData($start, ARG_STRING, 1); $length = processInputData($length, ARG_NUMERIC); #$foruser = processInputData($foruser, ARG_STRING, 1); // 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']); if(! in_array($imageid, $validImageids)) { return array('status' => 'error', 'errorcode' => 3, 'errormsg' => "access denied to $imageid"); } # validate $start if($start != 'now' && ! is_numeric($start)) { return array('status' => 'error', 'errorcode' => 4, 'errormsg' => "received invalid input for start"); } # validate $length $maxtimes = getUserMaxTimes(); if($maxtimes['initial'] < $length) { return array('status' => 'error', 'errorcode' => 6, 'errormsg' => "max allowed initial length is {$maxtimes['initial']} minutes"); } $nowfuture = 'future'; if($start == 'now') { $start = time(); $nowfuture = 'now'; } else if($start < (time() - 30)) return array('status' => 'error', 'errorcode' => 5, 'errormsg' => "start time is in the past"); $start = unixFloor15($start); $end = $start + $length * 60; if($end % (15 * 60)) $end = unixFloor15($end) + (15 * 60); $max = getMaxOverlap($user['id']); if(checkOverlap($start, $end, $max)) { return array('status' => 'error', 'errorcode' => 7, 'errormsg' => "reservation overlaps with another one you " . "have, and you are allowed $max " . "overlapping reservations at a time"); } $images = getImages(); $revisionid = getProductionRevisionid($imageid); $rc = isAvailable($images, $imageid, $revisionid, $start, $end); if($rc < 1) { addLogEntry($nowfuture, unixToDatetime($start), unixToDatetime($end), 0, $imageid); return array('status' => 'notavailable'); } $return['requestid']= addRequest(); $return['status'] = 'success'; return $return; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddRequestWithEnding($imageid, $start, $end, $foruser) /// /// \param $imageid - id of an image /// \param $start - "now" or unix timestamp for start of reservation; will /// use a floor function to round down to the nearest 15 minute increment /// for actual reservation /// \param $end - unix timestamp for end of reservation; will be rounded up to /// the nearest 15 minute increment /// \param $foruser - (optional) login to be used when setting up the account /// on the reserved machine - CURRENTLY, THIS IS UNSUPPORTED /// /// \return an array with at least one index named '\b status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b notavailable - no computers were available for the request\n /// \b success - there will be an additional element in the array: /// \li \b requestid - identifier that should be passed to later calls when /// acting on the request /// /// \brief tries to make a request with the specified ending time /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddRequestWithEnding($imageid, $start, $end, $foruser='') { global $user; $imageid = processInputData($imageid, ARG_NUMERIC); $start = processInputData($start, ARG_STRING, 1); $end = processInputData($end, ARG_STRING); #$foruser = processInputData($foruser, ARG_STRING, 1); // make sure user is a member of the 'Specify End Time' group $groupid = getUserGroupID('Specify End Time'); $members = getUserGroupMembers($groupid); if(! array_key_exists($user['id'], $members)) { return array('status' => 'error', 'errorcode' => 35, 'errormsg' => "access denied to specify end time"); } // 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']); if(! in_array($imageid, $validImageids)) { return array('status' => 'error', 'errorcode' => 3, 'errormsg' => "access denied to $imageid"); } # validate $start if($start != 'now' && ! is_numeric($start)) { return array('status' => 'error', 'errorcode' => 4, 'errormsg' => "received invalid input for start"); } # validate $end if(! is_numeric($end)) { return array('status' => 'error', 'errorcode' => 36, 'errormsg' => "received invalid input for end"); } if($start != 'now' && $start >= $end) { return array('status' => 'error', 'errorcode' => 37, 'errormsg' => "start must be less than end"); } $nowfuture = 'future'; if($start == 'now') { $start = time(); $nowfuture = 'now'; } else if($start < (time() - 30)) return array('status' => 'error', 'errorcode' => 5, 'errormsg' => "start time is in the past"); $start = unixFloor15($start); if($end % (15 * 60)) $end = unixFloor15($end) + (15 * 60); $max = getMaxOverlap($user['id']); if(checkOverlap($start, $end, $max)) { return array('status' => 'error', 'errorcode' => 7, 'errormsg' => "reservation overlaps with another one you " . "have, and you are allowed $max " . "overlapping reservations at a time"); } $images = getImages(); $revisionid = getProductionRevisionid($imageid); $rc = isAvailable($images, $imageid, $revisionid, $start, $end); if($rc < 1) { addLogEntry($nowfuture, unixToDatetime($start), unixToDatetime($end), 0, $imageid); return array('status' => 'notavailable'); } $return['requestid']= addRequest(); $return['status'] = 'success'; return $return; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCdeployServer($imageid, $start, $end, $admingroup, $logingroup, /// $ipaddr, $macaddr, $monitored, $foruser, $name) /// /// \param $imageid - id of an image /// \param $start - "now" or unix timestamp for start of reservation; will /// use a floor function to round down to the nearest 15 minute increment /// for actual reservation /// \param $end - "indefinite" or unix timestamp for end of reservation; will /// use a floor function to round up to the nearest 15 minute increment /// for actual reservation /// \param $admingroup - (optional, default='') admin user group for reservation /// \param $logingroup - (optional, default='') login user group for reservation /// \param $ipaddr - (optional, default='') IP address to use for public IP of /// server /// \param $macaddr - (optional, default='') MAC address to use for public NIC /// of server /// \param $monitored - (optional, default=0) whether or not the server should /// be monitored - CURRENTLY, THIS IS UNSUPPORTED /// \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 /// /// \return an array with at least one index named '\b status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b notavailable - no computers were available for the request\n /// \b success - there will be an additional element in the array: /// \li \b requestid - identifier that should be passed to later calls when /// acting on the request /// /// \brief tries to make a server request /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCdeployServer($imageid, $start, $end, $admingroup='', $logingroup='', $ipaddr='', $macaddr='', $monitored=0, $foruser='', $name='') { global $user, $remoteIP; if(! in_array("serverProfileAdmin", $user["privileges"])) { return array('status' => 'error', 'errorcode' => 60, 'errormsg' => "access denied to deploy server"); } $imageid = processInputData($imageid, ARG_NUMERIC); $resources = getUserResources(array("imageAdmin", "imageCheckOut")); $images = removeNoCheckout($resources["image"]); $extraimages = getServerProfileImages($user['id']); if(! array_key_exists($imageid, $images) && ! array_key_exists($imageid, $extraimages)) { return array('status' => 'error', 'errorcode' => 3, 'errormsg' => "access denied to $imageid"); } if($admingroup != '') { $admingroup = processInputData($admingroup, ARG_STRING); if(get_magic_quotes_gpc()) $admingroup = stripslashes($admingroup); if(preg_match('/@/', $admingroup)) { $tmp = explode('@', $admingroup); $escadmingroup = mysql_real_escape_string($tmp[0]); $affilid = getAffiliationID($tmp[1]); if(is_null($affilid)) { return array('status' => 'error', 'errorcode' => 51, 'errormsg' => "unknown affiliation for admin user group: {$tmp[1]}"); } } else { $escadmingroup = mysql_real_escape_string($admingroup); $affilid = DEFAULT_AFFILID; } $admingroupid = getUserGroupID($escadmingroup, $affilid, 1); if(is_null($admingroupid)) { return array('status' => 'error', 'errorcode' => 52, 'errormsg' => "unknown admin user group: $admingroup"); } } else $admingroupid = ''; if($logingroup != '') { $logingroup = processInputData($logingroup, ARG_STRING); if(get_magic_quotes_gpc()) $logingroup = stripslashes($logingroup); if(preg_match('/@/', $logingroup)) { $tmp = explode('@', $logingroup); $esclogingroup = mysql_real_escape_string($tmp[0]); $affilid = getAffiliationID($tmp[1]); if(is_null($affilid)) { return array('status' => 'error', 'errorcode' => 54, 'errormsg' => "unknown affiliation for login user group: {$tmp[1]}"); } } else { $esclogingroup = mysql_real_escape_string($logingroup); $affilid = DEFAULT_AFFILID; } $logingroupid = getUserGroupID($esclogingroup, $affilid, 1); if(is_null($logingroupid)) { return array('status' => 'error', 'errorcode' => 55, 'errormsg' => "unknown login user group: $logingroup"); } } else $logingroupid = ''; $ipaddr = processInputData($ipaddr, ARG_STRING); $ipaddrArr = explode('.', $ipaddr); if($ipaddr != '' && (! preg_match('/^(([0-9]){1,3}\.){3}([0-9]){1,3}$/', $ipaddr) || $ipaddrArr[0] < 1 || $ipaddrArr[0] > 255 || $ipaddrArr[1] < 0 || $ipaddrArr[1] > 255 || $ipaddrArr[2] < 0 || $ipaddrArr[2] > 255 || $ipaddrArr[3] < 0 || $ipaddrArr[3] > 255)) { return array('status' => 'error', 'errorcode' => 57, 'errormsg' => "Invalid IP address. Must be w.x.y.z with each of " . "w, x, y, and z being between 1 and 255 (inclusive)"); } $macaddr = processInputData($macaddr, ARG_STRING); if($macaddr != '' && ! preg_match('/^(([A-Fa-f0-9]){2}:){5}([A-Fa-f0-9]){2}$/', $macaddr)) { return array('status' => 'error', 'errorcode' => 58, 'errormsg' => "Invalid MAC address. Must be XX:XX:XX:XX:XX:XX " . "with each pair of XX being from 00 to FF (inclusive)"); } $monitored = processInputData($monitored, ARG_NUMERIC); if($monitored != 0 && $monitored != 1) $monitored = 0; $start = processInputData($start, ARG_STRING, 1); $end = processInputData($end, ARG_STRING, 1); #$foruser = processInputData($foruser, ARG_STRING, 1); $name = processInputData($name, ARG_STRING); if(get_magic_quotes_gpc()) $name = stripslashes($name); if(! preg_match('/^([-a-zA-Z0-9_\. ]){0,255}$/', $name)) { return array('status' => 'error', 'errorcode' => 58, 'errormsg' => "Invalid name. Can only contain letters, numbers, " . "spaces, dashes(-), underscores(_), and periods(.) " . "and be up to 255 characters long"); } $name = mysql_real_escape_string($name); # validate $start if($start != 'now' && ! is_numeric($start)) { return array('status' => 'error', 'errorcode' => 4, 'errormsg' => "received invalid input for start"); } # validate $end if($end != 'indefinite' && ! is_numeric($end)) { return array('status' => 'error', 'errorcode' => 59, 'errormsg' => "received invalid input for end"); } $nowfuture = 'future'; if($start == 'now') { $start = unixFloor15(time()); $nowfuture = 'now'; } else if($start < (time() - 30)) return array('status' => 'error', 'errorcode' => 5, 'errormsg' => "start time is in the past"); if($end == 'indefinite') $end = datetimeToUnix("2038-01-01 00:00:00"); elseif($end % (15 * 60)) $end = unixFloor15($end) + (15 * 60); elseif($end < ($start + 900)) return array('status' => 'error', 'errorcode' => 88, 'errormsg' => "end time must be at least 15 minutes after start time"); $max = getMaxOverlap($user['id']); if(checkOverlap($start, $end, $max)) { return array('status' => 'error', 'errorcode' => 7, 'errormsg' => "reservation overlaps with another one you " . "have, and you are allowed $max " . "overlapping reservations at a time"); } $images = getImages(); $revisionid = getProductionRevisionid($imageid); $rc = isAvailable($images, $imageid, $revisionid, $start, $end, 0, 0, 0, 0, $ipaddr, $macaddr); if($rc < 1) { addLogEntry($nowfuture, unixToDatetime($start), unixToDatetime($end), 0, $imageid); return array('status' => 'notavailable'); } $return['requestid']= addRequest(); $query = "UPDATE reservation " . "SET remoteIP = '$remoteIP' " . "WHERE requestid = {$return['requestid']}"; doQuery($query); $fields = array('requestid'); $values = array($return['requestid']); if($name != '') { $fields[] = 'name'; $values[] = "'$name'"; } if($ipaddr != '') { $fields[] = 'fixedIP'; $values[] = "'$ipaddr'"; } if($macaddr != '') { $fields[] = 'fixedMAC'; $values[] = "'$macaddr'"; } if($admingroupid != 0) { $fields[] = 'admingroupid'; $values[] = $admingroupid; } if($logingroupid != 0) { $fields[] = 'logingroupid'; $values[] = $logingroupid; } if($monitored != 0) { $fields[] = 'monitored'; $values[] = 1; } $allfields = implode(',', $fields); $allvalues = implode(',', $values); $query = "INSERT INTO serverrequest ($allfields) VALUES ($allvalues)"; doQuery($query, 101); $return['status'] = 'success'; return $return; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetRequestIds() /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - request was successfully ended; there will be an additional /// element whose index is 'requests' which is an array of arrays, each having /// these elements (or empty if no existing requests):\n /// \li \b requestid - id of the request\n /// \li \b imageid - id of the image\n /// \li \b imagename - name of the image\n /// \li \b start - unix timestamp of start time\n /// \li \b end - unix timestamp of end time\n /// \li \b OS - name of OS used in image\n /// \li \b isserver - 0 or 1 - whether or not this is a server reservation\n /// \li \b state - current state of reservation\n /// \li \b servername - only included if isserver == 1 - name of the reservation /// /// \brief gets information about all of user's requests /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetRequestIds() { global $user; $requests = getUserRequests("all"); if(empty($requests)) return array('status' => 'success', 'requests' => array()); $states = getStates(); $ret = array(); foreach($requests as $req) { $start = datetimeToUnix($req['start']); $end = datetimeToUnix($req['end']); $tmp = array('requestid' => $req['id'], 'imageid' => $req['imageid'], 'imagename' => $req['prettyimage'], 'start' => $start, 'end' => $end, 'OS' => $req['OS'], 'isserver' => $req['server']); if($req['currstateid'] == 14) $tmp['state'] = $states[$req['laststateid']]; else $tmp['state'] = $states[$req['currstateid']]; if($req['server']) $tmp['servername'] = $req['servername']; array_push($ret, $tmp); } return array('status' => 'success', 'requests' => $ret); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetRequestStatus($requestid) /// /// \param $requestid - id of a request /// /// \return an array with at least one index named '\b status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b ready - request is ready\n /// \b failed - request failed to load properly\n /// \b timedout - request timed out (user didn't connect before timeout /// expired)\n /// \b loading - request is still loading; there will be an additional element /// in the array: /// \li \b time - the estimated wait time (in minutes) for loading to complete\n /// /// \b future - start time of request is in the future\n /// /// \brief determines and returns the status of the request /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetRequestStatus($requestid) { global $user; $requestid = processInputData($requestid, ARG_NUMERIC); $userRequests = getUserRequests('all', $user['id']); $found = 0; foreach($userRequests as $req) { if($req['id'] == $requestid) { $request = $req; $found = 1; break; } } if(! $found) return array('status' => 'error', 'errorcode' => 1, 'errormsg' => 'unknown requestid'); $now = time(); # request is ready if(requestIsReady($request)) return array('status' => 'ready'); # request failed elseif($request["currstateid"] == 5) return array('status' => 'failed'); # other cases where the reservation start time has been reached elseif(datetimeToUnix($request["start"]) < $now) { # request has timed out if($request["currstateid"] == 12 || $request["currstateid"] == 11 || ($request["currstateid"] == 14 && $request["laststateid"] == 11)) { return array('status' => 'timedout'); } # computer is loading else { $imageid = $request['imageid']; $images = getImages(0, $imageid); $remaining = 1; $computers = getComputers(0, 0, $request['computerid']); if(isComputerLoading($request, $computers)) { if(datetimeToUnix($request["daterequested"]) >= datetimeToUnix($request["start"])) $startload = datetimeToUnix($request["daterequested"]); else $startload = datetimeToUnix($request["start"]); $imgLoadTime = getImageLoadEstimate($imageid); if($imgLoadTime == 0) $imgLoadTime = $images[$imageid]['reloadtime'] * 60; $tmp = ($imgLoadTime - ($now - $startload)) / 60; $remaining = sprintf("%d", $tmp) + 1; if($remaining < 1) { $remaining = 1; } } return array('status' => 'loading', 'time' => $remaining); } } # reservation is in the future else return array('status' => 'future'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetRequestConnectData($requestid, $remoteIP) /// /// \param $requestid - id of a request /// \param $remoteIP - ip address of connecting user's computer /// /// \return an array with at least one index named '\b status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b ready - request is ready; there will be 3 additional elements in the /// array:\n /// \li \b serverIP - address of the reserved machine /// \li \b user - user to use when connecting to the machine /// \li \b password - password to use when connecting to the machine /// /// \b notready - request is not ready for connection /// /// \brief if request is ready, adds the connecting user's computer to the /// request and returns info about how to connect to the computer /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetRequestConnectData($requestid, $remoteIP) { global $user; $requestid = processInputData($requestid, ARG_NUMERIC); $remoteIP = processInputData($remoteIP, ARG_STRING, 1); if(! preg_match('/^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/', $remoteIP, $matches) || $matches[1] < 1 || $matches[1] > 223 || $matches[2] > 255 || $matches[3] > 255 || $matches[4] > 255) { return array('status' => 'error', 'errorcode' => 2, 'errormsg' => 'invalid IP address'); } $userRequests = getUserRequests('all', $user['id']); $found = 0; foreach($userRequests as $req) { if($req['id'] == $requestid) { $request = $req; $found = 1; break; } } if(! $found) return array('status' => 'error', 'errorcode' => 1, 'errormsg' => 'unknown requestid'); // FIXME - add support for cluster requests if(requestIsReady($request)) { $requestData = getRequestInfo($requestid); $query = "UPDATE reservation " . "SET remoteIP = '$remoteIP' " . "WHERE requestid = $requestid"; $qh = doQuery($query, 101); addChangeLogEntry($requestData["logid"], $remoteIP); $serverIP = $requestData["reservations"][0]["reservedIP"]; $passwd = $requestData["reservations"][0]["password"]; $connectport = $requestData["reservations"][0]["connectport"]; $connectMethods = getImageConnectMethodTexts( $requestData["reservations"][0]["imageid"], $requestData["reservations"][0]["imagerevisionid"]); if($requestData["forimaging"]) $thisuser = 'Administrator'; else if(preg_match('/(.*)@(.*)/', $user['unityid'], $matches)) $thisuser = $matches[1]; else $thisuser = $user['unityid']; 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("/#connectport#/", $connectport, $connecttext); $connectMethods[$key]["connecttext"] = $connecttext; } return array('status' => 'ready', 'serverIP' => $serverIP, 'user' => $thisuser, 'password' => $passwd, 'connectport' => $connectport, 'connectMethods' => $connectMethods); } return array('status' => 'notready'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCextendRequest($requestid, $extendtime) /// /// \param $requestid - id of a request /// \param $extendtime - time in minutes to extend reservation /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - request was successfully extended\n /// /// \brief extends the length of an active request; if a request that has not /// started needs to be extended, delete the request and submit a new one /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCextendRequest($requestid, $extendtime) { global $user; $requestid = processInputData($requestid, ARG_NUMERIC); $extendtime = processInputData($extendtime, ARG_NUMERIC); $userRequests = getUserRequests('all', $user['id']); $found = 0; foreach($userRequests as $req) { if($req['id'] == $requestid) { $request = getRequestInfo($requestid); $found = 1; break; } } if(! $found) return array('status' => 'error', 'errorcode' => 1, 'errormsg' => 'unknown requestid'); $startts = datetimeToUnix($request['start']); $endts = datetimeToUnix($request['end']); $newendts = $endts + ($extendtime * 60); if($newendts % (15 * 60)) $newendts= unixFloor15($newendts) + (15 * 60); // check that reservation has started if($startts > time()) { return array('status' => 'error', 'errorcode' => 38, 'errormsg' => 'reservation has not started'); } // check for allowed extension length $maxtimes = getUserMaxTimes(); if($extendtime > $maxtimes['extend']) { return array('status' => 'error', 'errorcode' => 39, 'errormsg' => 'extendtime exceeds allowable extension', 'allowed' => $maxtimes['extend']); } $newlength = ($endts - $startts) / 60 + $extendtime; if($newlength > $maxtimes['total']) { return array('status' => 'error', 'errorcode' => 40, 'errormsg' => 'new reservation length exceeds allowable length', 'allowed' => $maxtimes['total']); } // check for overlap $max = getMaxOverlap($user['id']); if(checkOverlap($startts, $newendts, $max, $requestid)) { return array('status' => 'error', 'errorcode' => 41, 'errormsg' => 'overlapping reservation restriction', 'maxoverlap' => $max); } // check for computer being available for extended time? $timeToNext = timeToNextReservation($request); $movedall = 1; if($timeToNext > -1) { foreach($request["reservations"] as $res) { if(! moveReservationsOffComputer($res["computerid"])) { $movedall = 0; break; } } } if(! $movedall) { $timeToNext = timeToNextReservation($request); if($timeToNext >= 15) $timeToNext -= 15; // reservation immediately after this one, cannot extend if($timeToNext < 15) { return array('status' => 'error', 'errorcode' => 42, 'errormsg' => 'cannot extend due to another reservation immediately after this one'); } // check that requested extension < $timeToNext elseif($extendtime > $timeToNext) { $extra = $timeToNext - ($timeToNext % 15); return array('status' => 'error', 'errorcode' => 43, 'errormsg' => 'cannot extend by requested amount', 'availablelength' => $extra); } } $rc = isAvailable(getImages(), $request['reservations'][0]["imageid"], $request['reservations'][0]['imagerevisionid'], $startts, $newendts, $requestid); // conflicts with scheduled maintenance if($rc == -2) { addChangeLogEntry($request["logid"], NULL, unixToDatetime($newendts), $request['start'], NULL, NULL, 0); return array('status' => 'error', 'errorcode' => 46, 'errormsg' => 'requested time is during a maintenance window'); } // concurrent license overlap elseif($rc == -1) { addChangeLogEntry($request["logid"], NULL, unixToDatetime($newendts), $request['start'], NULL, NULL, 0); return array('status' => 'error', 'errorcode' => 44, 'errormsg' => 'concurrent license restriction'); } // could not extend for some other reason elseif($rc == 0) { addChangeLogEntry($request["logid"], NULL, unixToDatetime($newendts), $request['start'], NULL, NULL, 0); return array('status' => 'error', 'errorcode' => 45, 'errormsg' => 'cannot extend at this time'); } // success updateRequest($requestid); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCsetRequestEnding($requestid, $end) /// /// \param $requestid - id of a request /// \param $end - unix timestamp for end of reservation; will be rounded up to /// the nearest 15 minute increment /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - request was successfully extended\n /// /// \brief modifies the end time of an active request; if a request that has not /// started needs to be modifed, delete the request and submit a new one /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCsetRequestEnding($requestid, $end) { global $user; $requestid = processInputData($requestid, ARG_NUMERIC); $userRequests = getUserRequests('all', $user['id']); $found = 0; foreach($userRequests as $req) { if($req['id'] == $requestid) { $request = getRequestInfo($requestid); $found = 1; break; } } if(! $found) return array('status' => 'error', 'errorcode' => 1, 'errormsg' => 'unknown requestid'); // make sure user is a member of the 'Specify End Time' group $groupid = getUserGroupID('Specify End Time'); $members = getUserGroupMembers($groupid); if(! $request['serverrequest'] && ! array_key_exists($user['id'], $members)) { return array('status' => 'error', 'errorcode' => 35, 'errormsg' => "access denied to specify end time"); } $end = processInputData($end, ARG_NUMERIC); $maxend = datetimeToUnix("2038-01-01 00:00:00"); if($end < 0 || $end > $maxend) { return array('status' => 'error', 'errorcode' => 36, 'errormsg' => "received invalid input for end"); } $startts = datetimeToUnix($request['start']); if($end % (15 * 60)) $end= unixFloor15($end) + (15 * 60); // check that reservation has started if($startts > time()) { return array('status' => 'error', 'errorcode' => 38, 'errormsg' => 'reservation has not started'); } // check for overlap $max = getMaxOverlap($user['id']); if(checkOverlap($startts, $end, $max, $requestid)) { return array('status' => 'error', 'errorcode' => 41, 'errormsg' => 'overlapping reservation restriction', 'maxoverlap' => $max); } // check for computer being available for extended time? $timeToNext = timeToNextReservation($request); $movedall = 1; if($timeToNext > -1) { foreach($request["reservations"] as $res) { if(! moveReservationsOffComputer($res["computerid"])) { $movedall = 0; break; } } } if(! $movedall) { $timeToNext = timeToNextReservation($request); if($timeToNext >= 15) $timeToNext -= 15; $oldendts = datetimeToUnix($request['end']); // reservation immediately after this one, cannot extend if($timeToNext < 15) { return array('status' => 'error', 'errorcode' => 42, 'errormsg' => 'cannot extend due to another reservation immediately after this one'); } // check that requested extension < $timeToNext elseif((($end - $oldendts) / 60) > $timeToNext) { $maxend = $oldendts + ($timeToNext * 60); return array('status' => 'error', 'errorcode' => 43, 'errormsg' => 'cannot extend by requested amount due to another reservation', 'maxend' => $maxend); } } $rc = isAvailable(getImages(), $request['reservations'][0]["imageid"], $request['reservations'][0]['imagerevisionid'], $startts, $end, $requestid); // conflicts with scheduled maintenance if($rc == -2) { addChangeLogEntry($request["logid"], NULL, unixToDatetime($end), $request['start'], NULL, NULL, 0); return array('status' => 'error', 'errorcode' => 46, 'errormsg' => 'requested time is during a maintenance window'); } // concurrent license overlap elseif($rc == -1) { addChangeLogEntry($request["logid"], NULL, unixToDatetime($end), $request['start'], NULL, NULL, 0); return array('status' => 'error', 'errorcode' => 44, 'errormsg' => 'concurrent license restriction'); } // could not extend for some other reason elseif($rc == 0) { addChangeLogEntry($request["logid"], NULL, unixToDatetime($end), $request['start'], NULL, NULL, 0); return array('status' => 'error', 'errorcode' => 45, 'errormsg' => 'cannot extend at this time'); } // success updateRequest($requestid); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCendRequest($requestid) /// /// \param $requestid - id of a request /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - request was successfully ended\n /// /// \brief ends/deletes a request /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCendRequest($requestid) { global $user; $requestid = processInputData($requestid, ARG_NUMERIC); $userRequests = getUserRequests('all', $user['id']); $found = 0; foreach($userRequests as $req) { if($req['id'] == $requestid) { $request = getRequestInfo($requestid); $found = 1; break; } } if(! $found) return array('status' => 'error', 'errorcode' => 1, 'errormsg' => 'unknown requestid'); deleteRequest($request); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCautoCapture($requestid) /// /// \param $requestid - id of request to be captured /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - image was successfully set to be captured /// /// \brief creates entries in appropriate tables to capture an image and sets /// the request state to image /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCautoCapture($requestid) { global $user, $xmlrpcBlockAPIUsers; if(! in_array($user['id'], $xmlrpcBlockAPIUsers)) { return array('status' => 'error', 'errorcode' => 47, 'errormsg' => 'access denied to XMLRPCautoCapture'); } $query = "SELECT id FROM request WHERE id = $requestid"; $qh = doQuery($query, 101); if(! mysql_num_rows($qh)) { return array('status' => 'error', 'errorcode' => 52, 'errormsg' => 'specified request does not exist'); } $reqData = getRequestInfo($requestid); # check state of reservation if($reqData['stateid'] != 14 || $reqData['laststateid'] != 8) { return array('status' => 'error', 'errorcode' => 51, 'errormsg' => 'reservation not in valid state'); } # check that not a cluster reservation if(count($reqData['reservations']) > 1) { return array('status' => 'error', 'errorcode' => 48, 'errormsg' => 'cannot image a cluster reservation'); } require_once(".ht-inc/images.php"); $imageid = $reqData['reservations'][0]['imageid']; $imageData = getImages(0, $imageid); $captime = unixToDatetime(time()); $comments = "start: {$reqData['start']}
" . "end: {$reqData['end']}
" . "computer: {$reqData['reservations'][0]['reservedIP']}
" . "capture time: $captime"; # create new revision if requestor is owner and not a kickstart image if($imageData[$imageid]['installtype'] != 'kickstart' && $reqData['userid'] == $imageData[$imageid]['ownerid']) { $rc = updateExistingImage($requestid, $reqData['userid'], $comments, 1); if($rc == 0) { return array('status' => 'error', 'errorcode' => 49, 'errormsg' => 'error encountered while attempting to create new revision'); } } # create a new image if requestor is not owner or a kickstart image else { $ownerdata = getUserInfo($reqData['userid'], 1, 1); $desc = "This is an autocaptured image.
" . "captured from image: {$reqData['reservations'][0]['prettyimage']}
" . "captured on: $captime
" . "owner: {$ownerdata['unityid']}@{$ownerdata['affiliation']}
"; $connectmethods = getImageConnectMethods($imageid, $reqData['reservations'][0]['imagerevisionid']); $data = array('requestid' => $requestid, 'description' => $desc, 'usage' => '', 'owner' => "{$ownerdata['unityid']}@{$ownerdata['affiliation']}", 'prettyname' => "Autocaptured ({$ownerdata['unityid']} - $requestid)", 'minram' => 64, 'minprocnumber' => 1, 'minprocspeed' => 500, 'minnetwork' => 10, 'maxconcurrent' => '', 'checkuser' => 1, 'rootaccess' => 1, 'sysprep' => 1, 'comments' => $comments, 'connectmethodids' => implode(',', array_keys($connectmethods))); $rc = submitAddImage($data, 1); if($rc == 0) { return array('status' => 'error', 'errorcode' => 50, 'errormsg' => 'error encountered while attempting to create image'); } } return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetGroupImages($name) /// /// \param $name - the name of an imageGroup /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - returns an array of images; there will be an additional element /// in the array with an index of 'images' that is an array of images with /// each element having the following two keys:\n /// \li \b id - id of the image\n /// \li \b name - name of the image /// /// \brief gets a list of all images in a particular group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetGroupImages($name) { if($groupid = getResourceGroupID("image/$name")) { $membership = getResourceGroupMemberships('image'); $resources = getUserResources(array("imageAdmin"), array("manageGroup")); $images = array(); foreach($resources['image'] as $imageid => $image) { if(array_key_exists($imageid, $membership['image']) && in_array($groupid, $membership['image'][$imageid])) array_push($images, array('id' => $imageid, 'name' => $image)); } return array('status' => 'success', 'images' => $images); } else { return array('status' => 'error', 'errorcode' => 83, 'errormsg' => 'invalid resource group name'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddImageToGroup($name, $imageid) /// /// \param $name - the name of an imageGroup /// \param $imageid - the id of an image /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - image was added to the group\n /// /// \brief adds an image to a resource group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddImageToGroup($name, $imageid) { if($groupid = getResourceGroupID("image/$name")) { $groups = getUserResources(array("imageAdmin"), array("manageGroup"), 1); if(! array_key_exists($groupid, $groups['image'])) { return array('status' => 'error', 'errorcode' => 46, 'errormsg' => 'Unable to access image group'); } $resources = getUserResources(array("imageAdmin"), array("manageGroup")); if(! array_key_exists($imageid, $resources['image'])) { return array('status' => 'error', 'errorcode' => 47, 'errormsg' => 'Unable to access image'); } $allimages = getImages(0, $imageid); $query = "INSERT IGNORE INTO resourcegroupmembers " . "(resourceid, " . "resourcegroupid) " . "VALUES " . "({$allimages[$imageid]['resourceid']}, " . "$groupid)"; doQuery($query); return array('status' => 'success'); } else { return array('status' => 'error', 'errorcode' => 83, 'errormsg' => 'invalid resource group name'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveImageFromGroup($name, $imageid) /// /// \param $name - the name of an imageGroup /// \param $imageid - the id of an image /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - image was removed from the group\n /// /// \brief removes an image from a resource group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveImageFromGroup($name, $imageid) { if($groupid = getResourceGroupID("image/$name")) { $groups = getUserResources(array("imageAdmin"), array("manageGroup"), 1); if(! array_key_exists($groupid, $groups['image'])) { return array('status' => 'error', 'errorcode' => 46, 'errormsg' => 'Unable to access image group'); } $resources = getUserResources(array("imageAdmin"), array("manageGroup")); if(! array_key_exists($imageid, $resources['image'])) { return array('status' => 'error', 'errorcode' => 47, 'errormsg' => 'Unable to access image'); } $allimages = getImages(0, $imageid); $query = "DELETE FROM resourcegroupmembers " . "WHERE resourceid = {$allimages[$imageid]['resourceid']} AND " . "resourcegroupid = $groupid"; doQuery($query); return array('status' => 'success'); } else { return array('status' => 'error', 'errorcode' => 83, 'errormsg' => 'invalid resource group name'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddImageGroupToComputerGroup($imageGroup, $computerGroup) /// /// \param $imageGroup - the name of an imageGroup /// \param $computerGroup - the name of a computerGroup /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - successfully mapped an image group to a computer group\n /// /// \brief map an image group to a computer group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddImageGroupToComputerGroup($imageGroup, $computerGroup) { $imageid = getResourceGroupID("image/$imageGroup"); $compid = getResourceGroupID("computer/$computerGroup"); if($imageid && $compid) { $tmp = getUserResources(array("imageAdmin"), array("manageMapping"), 1); $imagegroups = $tmp['image']; $tmp = getUserResources(array("computerAdmin"), array("manageMapping"), 1); $computergroups = $tmp['computer']; if(array_key_exists($compid, $computergroups) && array_key_exists($imageid, $imagegroups)) { $mapping = getResourceMapping("image", "computer", $imageid, $compid); if(! array_key_exists($imageid, $mapping) || ! in_array($compid, $mapping[$imageid])) { $query = "INSERT INTO resourcemap " . "(resourcegroupid1, " . "resourcetypeid1, " . "resourcegroupid2, " . "resourcetypeid2) " . "VALUES ($imageid, " . "13, " . "$compid, " . "12)"; doQuery($query, 101); } return array('status' => 'success'); } else { return array('status' => 'error', 'errorcode' => 84, 'errormsg' => 'cannot access computer and/or image group'); } } else { return array('status' => 'error', 'errorcode' => 83, 'errormsg' => 'invalid resource group name'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveImageGroupFromComputerGroup($imageGroup, $computerGroup) /// /// \param $imageGroup - the name of an imageGroup /// \param $computerGroup - the name of a computerGroup /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - successfully removed the mapping from an image group to a /// computer group\n /// /// \brief remove the mapping of an image group to a computer group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveImageGroupFromComputerGroup($imageGroup, $computerGroup) { $imageid = getResourceGroupID("image/$imageGroup"); $compid = getResourceGroupID("computer/$computerGroup"); if($imageid && $compid) { $tmp = getUserResources(array("imageAdmin"), array("manageMapping"), 1); $imagegroups = $tmp['image']; $tmp = getUserResources(array("computerAdmin"), array("manageMapping"), 1); $computergroups = $tmp['computer']; if(array_key_exists($compid, $computergroups) && array_key_exists($imageid, $imagegroups)) { $mapping = getResourceMapping("image", "computer", $imageid, $compid); if(array_key_exists($imageid, $mapping) && in_array($compid, $mapping[$imageid])) { $query = "DELETE FROM resourcemap " . "WHERE resourcegroupid1 = $imageid AND " . "resourcetypeid1 = 13 AND " . "resourcegroupid2 = $compid AND " . "resourcetypeid2 = 12"; doQuery($query, 101); } return array('status' => 'success'); } else { return array('status' => 'error', 'errorcode' => 84, 'errormsg' => 'cannot access computer and/or image group'); } } else { return array('status' => 'error', 'errorcode' => 83, 'errormsg' => 'invalid resource group name'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetNodes($root) /// /// \param $root - (optional, default=top of tree) the ID of the node forming /// the root of the hierarchy /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - returns an array of nodes; there will be an additional element /// in the array with an index of 'nodes' that is an array of nodes with each /// element having the following three keys:\n /// \li \b id - id of the node\n /// \li \b name - name of the node\n /// \li \b parent - id of the parent node /// /// \brief gets a list of all nodes in the privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetNodes($root=NULL) { global $user; if(in_array("userGrant", $user["privileges"]) || in_array("resourceGrant", $user["privileges"]) || in_array("nodeAdmin", $user["privileges"])) { $root = processInputData($root, ARG_NUMERIC); $topNodes = $root ? getChildNodes($root) : getChildNodes(); $nodes = array(); $stack = array(); foreach($topNodes as $id => $node) { $node['id'] = $id; array_push($nodes, $node); array_push($stack, $node); } while(count($stack)) { $item = array_shift($stack); $children = getChildNodes($item['id']); foreach($children as $id => $node) { $node['id'] = $id; array_push($nodes, $node); array_push($stack, $node); } } return array('status' => 'success', 'nodes' => $nodes); } else { return array('status' => 'error', 'errorcode' => 70, 'errormsg' => 'User cannot access node content'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCnodeExists($nodeName, $parentNode) /// /// \param $nodeName - the name of a node /// \param $parentNode - the ID of the parent node /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - returns an 'exists' element set to either 1 or 0\n /// /// \brief indicates whether a node with that name already exists at this /// location in the privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCnodeExists($nodeName, $parentNode) { global $user; if(! is_numeric($parentNode)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(in_array("userGrant", $user["privileges"]) || in_array("resourceGrant", $user["privileges"]) || in_array("nodeAdmin", $user["privileges"])) { if(get_magic_quotes_gpc()) $nodeName = stripslashes($nodeName); $nodeName = mysql_real_escape_string($nodeName); // does a node with this name already exist? $query = "SELECT id " . "FROM privnode " . "WHERE name = '$nodeName' AND parent = $parentNode"; $qh = doQuery($query, 335); if(mysql_num_rows($qh)) return array('status' => 'success', 'exists' => TRUE); else return array('status' => 'success', 'exists' => FALSE); } else { return array('status' => 'error', 'errorcode' => 70, 'errormsg' => 'User cannot access node content'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddNode($nodeName, $parentNode) /// /// \param $nodeName - the name of the new node /// \param $parentNode - the ID of the node parent /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - node was successfully added /// /// \brief add a node to the privilege tree as a child of the specified parent /// node /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddNode($nodeName, $parentNode) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($parentNode)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(in_array("nodeAdmin", $user['privileges'])) { $nodeInfo = getNodeInfo($parentNode); if(is_null($nodeInfo)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! validateNodeName($nodeName, $tmp)) { return array('status' => 'error', 'errorcode' => 81, 'errormsg' => 'Invalid node name'); } if(checkUserHasPriv("nodeAdmin", $user['id'], $parentNode)) { $query = "SELECT id " . "FROM privnode " . "WHERE name = '$nodeName' AND parent = $parentNode"; $qh = doQuery($query); if(mysql_num_rows($qh)) { return array('status' => 'error', 'errorcode' => 82, 'errormsg' => 'A node of that name already exists under ' . $nodeInfo['name']); } $query = "INSERT IGNORE INTO privnode " . "(parent, name) " . "VALUES " . "($parentNode, '$nodeName')"; doQuery($query); $qh = doQuery("SELECT LAST_INSERT_ID() FROM privnode", 101); if(! $row = mysql_fetch_row($qh)) { return array('status' => 'error', 'errorcode' => 85, 'errormsg' => 'Could not add node to database'); } $nodeid = $row[0]; return array('status' => 'success', 'nodeid' => $nodeid); } else { return array('status' => 'error', 'errorcode' => 49, 'errormsg' => 'Unable to add node at this location'); } } else { return array('status' => 'error', 'errorcode' => 70, 'errormsg' => 'User cannot access node content'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveNode($nodeID) /// /// \param $nodeID - the ID of a node /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - node was successfully deleted /// /// \brief delete a node from the privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveNode($nodeID) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($nodeID)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! in_array("nodeAdmin", $user['privileges'])) { return array('status' => 'error', 'errorcode' => 70, 'errormsg' => 'User cannot administer nodes'); } if(! checkUserHasPriv("nodeAdmin", $user['id'], $nodeID)) { return array('status' => 'error', 'errorcode' => 57, 'errormsg' => 'User cannot edit this node'); } $nodes = recurseGetChildren($nodeID); array_push($nodes, $nodeID); $deleteNodes = implode(',', $nodes); $query = "DELETE FROM privnode " . "WHERE id IN ($deleteNodes)"; doQuery($query, 345); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetUserGroupPrivs($name, $affiliation, $nodeid) /// /// \param $name - the name of the user group /// \param $affiliation - the affiliation of the group /// \param $nodeid - the ID of the node in the privilege tree /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - an additional element is returned:\n /// \li \b privileges - array of privileges assigned at the node /// /// \brief get a list of privileges for a user group at a particular node in the /// privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetUserGroupPrivs($name, $affiliation, $nodeid) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($nodeid)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! in_array("userGrant", $user["privileges"]) && ! in_array("resourceGrant", $user["privileges"]) && ! in_array("nodeAdmin", $user["privileges"])) { return array('status' => 'error', 'errorcode' => 62, 'errormsg' => 'Unable to view user group privileges'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $privileges = array(); $nodePrivileges = getNodePrivileges($nodeid, 'usergroups'); $cascadedNodePrivileges = getNodeCascadePrivileges($nodeid, 'usergroups'); $cngp = $cascadedNodePrivileges['usergroups']; $ngp = $nodePrivileges['usergroups']; if(array_key_exists($name, $cngp)) { foreach($cngp[$name]['privs'] as $p) { if(! array_key_exists($name, $ngp) || ! in_array("block", $ngp[$name]['privs'])) array_push($privileges, $p); } } if(array_key_exists($name, $ngp)) { foreach($ngp[$name]['privs'] as $p) { if($p != "block") array_push($privileges, $p); } } return array('status' => 'success', 'privileges' => array_unique($privileges)); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddUserGroupPriv($name, $affiliation, $nodeid, $permissions) /// /// \param $name - the name of the user group /// \param $affiliation - the affiliation of the user group /// \param $nodeid - the ID of the node in the privilege tree /// \param $permissions - a colon (:) delimited list of privileges to add /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - privileges were successfully added /// /// \brief add privileges for a user group at a particular node in the /// privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddUserGroupPriv($name, $affiliation, $nodeid, $permissions) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($nodeid)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! checkUserHasPriv("userGrant", $user['id'], $nodeid)) { return array('status' => 'error', 'errorcode' => 52, 'errormsg' => 'Unable to add a user group to this node'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $groupid = $rc['id']; #$name = "$name@$affiliation"; $perms = explode(':', $permissions); $usertypes = getTypes('users'); array_push($usertypes["users"], "block"); array_push($usertypes["users"], "cascade"); $diff = array_diff($perms, $usertypes['users']); if(! count($perms) || count($diff) || (count($perms) == 1 && $perms[0] == 'cascade')) { return array('status' => 'error', 'errorcode' => 66, 'errormsg' => 'Invalid or missing permissions list supplied'); } $cnp = getNodeCascadePrivileges($nodeid, "usergroups"); $np = getNodePrivileges($nodeid, "usergroups", $cnp); if(array_key_exists($name, $np['usergroups'])) { $diff = array_diff($perms, $np['usergroups'][$name]['privs']); if(empty($diff)) return array('status' => 'success'); } else $diff = $perms; updateUserOrGroupPrivs($groupid, $nodeid, $diff, array(), "group"); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveUserGroupPriv($name, $affiliation, $nodeid, /// $permissions) /// /// \param $name - the name of the user group /// \param $affiliation - the affiliation of the user group /// \param $nodeid - the ID of the node in the privilege tree /// \param $permissions - a colon (:) delimited list of privileges to remove /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - privileges were successfully removed /// /// \brief remove privileges for a resource group at a particular node in the /// privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveUserGroupPriv($name, $affiliation, $nodeid, $permissions) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($nodeid)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! checkUserHasPriv("userGrant", $user['id'], $nodeid)) { return array('status' => 'error', 'errorcode' => 65, 'errormsg' => 'Unable to remove user group privileges on this node'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $groupid = $rc['id']; #$name = "$name@$affiliation"; $perms = explode(':', $permissions); $usertypes = getTypes('users'); array_push($usertypes["users"], "block"); array_push($usertypes["users"], "cascade"); $diff = array_diff($perms, $usertypes['users']); if(count($diff)) { return array('status' => 'error', 'errorcode' => 66, 'errormsg' => 'Invalid or missing permissions list supplied'); } $cnp = getNodeCascadePrivileges($nodeid, "usergroups"); $np = getNodePrivileges($nodeid, "usergroups"); if(array_key_exists($name, $cnp['usergroups']) && (! array_key_exists($name, $np['usergroups']) || ! in_array('block', $np['usergroups'][$name]))) { $intersect = array_intersect($cnp['usergroups'][$name]['privs'], $perms); if(count($intersect)) { return array('status' => 'error', 'errorcode' => 80, 'errormsg' => 'Unable to modify privileges cascaded to this node'); } } $diff = array_diff($np['usergroups'][$name]['privs'], $perms); if(count($diff) == 1 && in_array("cascade", $diff)) array_push($perms, "cascade"); updateUserOrGroupPrivs($groupid, $nodeid, array(), $perms, "group"); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetResourceGroupPrivs($name, $type, $nodeid) /// /// \param $name - the name of the resource group /// \param $type - the resource group type /// \param $nodeid - the ID of the node in the privilege tree /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - an additional element is returned:\n /// \li \b privileges - array of privileges assigned at the node /// /// \brief get a list of privileges for a resource group at a particular node in /// the privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetResourceGroupPrivs($name, $type, $nodeid) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($nodeid)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! in_array("userGrant", $user["privileges"]) && ! in_array("resourceGrant", $user["privileges"]) && ! in_array("nodeAdmin", $user["privileges"])) { return array('status' => 'error', 'errorcode' => 63, 'errormsg' => 'Unable to view resource group privileges'); } if($typeid = getResourceTypeID($type)) { if(! $groupid = getResourceGroupID("$type/$name")) { return array('status' => 'error', 'errorcode' => 74, 'errormsg' => 'resource group does not exist'); } $np = getNodePrivileges($nodeid, 'resources'); $cnp = getNodeCascadePrivileges($nodeid, 'resources'); $key = "$type/$name/$groupid"; if(array_key_exists($key, $np['resources']) && (in_array('block', $np['resources'][$key]) || ! array_key_exists($key, $cnp['resources']))) $privs = $np['resources'][$key]; elseif(array_key_exists($key, $cnp['resources']) && array_key_exists($key, $np['resources'])) { $allprivs = array_merge($cnp['resources'][$key], $np['resources'][$key]); $privs = array_unique($allprivs); } elseif(array_key_exists($key, $cnp['resources'])) $privs = $cnp['resources'][$key]; else $privs = array(); return array('status' => 'success', 'privileges' => $privs); } else { return array('status' => 'error', 'errorcode' => 71, 'errormsg' => 'Invalid resource type'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddResourceGroupPriv($name, $type, $nodeid, $permissions) /// /// \param $name - the name of the resource group /// \param $type - the resource group type /// \param $nodeid - the ID of the node in the privilege tree /// \param $permissions - a colon (:) delimited list of privileges to add /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - privileges were successfully added /// /// \brief add privileges for a resource group at a particular node in the /// privilege tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddResourceGroupPriv($name, $type, $nodeid, $permissions) { return _XMLRPCchangeResourceGroupPriv_sub('add', $name, $type, $nodeid, $permissions); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveResourceGroupPriv($name, $type, $nodeid, $permissions) /// /// \param $name - the name of the resource group /// \param $type - the resource type /// \param $nodeid - the ID of the node in the privilege tree /// \param $permissions - a colon (:) delimited list of privileges to remove /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - privileges were successfully removed /// /// \brief remove privileges for a resource group from a node in the privilege /// tree /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveResourceGroupPriv($name, $type, $nodeid, $permissions) { return _XMLRPCchangeResourceGroupPriv_sub('remove', $name, $type, $nodeid, $permissions); } ################################################################################## ### ### fn _XMLRPCchangeResourceGroupPriv_sub($mode, $name, $type, $nodeid, ### $permissions) ### ### param $mode - 'add' or 'remove' ### param $name - the name of the resource group ### param $type - the resource type ### param $nodeid - the ID of the node in the privilege tree ### param $permissions - a colon (:) delimited list of privileges to remove ### ### return an array with at least one index named 'status' which will have ### one of these values\n ### error - error occurred; there will be 2 additional elements in the array: ### * errorcode - error number\n ### * errormsg - error string\n ### ### success - privileges were successfully added or removed ### ### brief internal function to be called from XMLRPCremoveResourceGroupPriv and ### XMLRPCaddResourceGroupPriv - adds or removes privileges for a resource group ### from a node in the privilege tree ### ################################################################################ function _XMLRPCchangeResourceGroupPriv_sub($mode, $name, $type, $nodeid, $permissions) { require_once(".ht-inc/privileges.php"); global $user; if(! is_numeric($nodeid)) { return array('status' => 'error', 'errorcode' => 78, 'errormsg' => 'Invalid nodeid specified'); } if(! checkUserHasPriv("resourceGrant", $user['id'], $nodeid)) { return array('status' => 'error', 'errorcode' => 61, 'errormsg' => 'Unable to remove resource group privileges on this node'); } $resourcetypes = getTypes('resources'); if(! in_array($type, $resourcetypes['resources'])) { return array('status' => 'error', 'errorcode' => 71, 'errormsg' => 'Invalid resource type'); } $groupid = getResourceGroupID("$type/$name"); if(is_null($groupid)) { return array('status' => 'error', 'errorcode' => 74, 'errormsg' => 'resource group does not exist'); } $changeperms = explode(':', $permissions); $allperms = getResourcePrivs(); $diff = array_diff($changeperms, $allperms); if(count($diff)) { return array('status' => 'error', 'errorcode' => 66, 'errormsg' => 'Invalid or missing permissions list supplied'); } $nocheckperms = array('block', 'cascade', 'available'); $checkperms = array_diff($changeperms, $nocheckperms); $groupdata = getResourceGroups($type, $groupid); if(count($checkperms) && ! array_key_exists($groupdata[$groupid]["ownerid"], $user["groups"])) { return array('status' => 'error', 'errorcode' => 79, 'errormsg' => 'Unable to modify privilege set for resource group'); } $key = "$type/$name/$groupid"; $cnp = getNodeCascadePrivileges($nodeid, "resources"); $np = getNodePrivileges($nodeid, 'resources'); if(array_key_exists($key, $cnp['resources']) && (! array_key_exists($key, $np['resources']) || ! in_array('block', $np['resources'][$key]))) { $intersect = array_intersect($cnp['resources'][$key], $changeperms); if(count($intersect)) { return array('status' => 'error', 'errorcode' => 80, 'errormsg' => 'Unable to modify privileges cascaded to this node'); } } if($mode == 'remove') { $diff = array_diff($np['resources'][$key], $changeperms); if(count($diff) == 1 && in_array("cascade", $diff)) $changeperms[] = 'cascade'; } if($mode == 'add') updateResourcePrivs("$groupid", $nodeid, $changeperms, array()); elseif($mode == 'remove') updateResourcePrivs("$groupid", $nodeid, array(), $changeperms); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetUserGroups($groupType, $affiliationid) /// /// \param $groupType - (optional, default=0) specify 0 for all groups, 1 for /// only custom groups, 2 for only courseroll groups /// \param $affiliationid - (optional, default=0) specifiy an affiliationid to /// limit returned groups to only those matching the affiliation; pass 0 for /// all affiliations /// /// \return an array with two indices, one named 'status' which will have a /// value of 'success', the other named 'groups' which will be an array of /// arrays, each one having the following keys:\n /// \li \b id /// \li \b name /// \li \b groupaffiliation /// \li \b groupaffiliationid /// \li \b ownerid /// \li \b owner /// \li \b affiliation /// \li \b editgroupid /// \li \b editgroup /// \li \b editgroupaffiliationid /// \li \b editgroupaffiliation /// \li \b custom /// \li \b courseroll /// \li \b initialmaxtime /// \li \b maxextendtime /// \li \b overlapResCount /// /// \brief builds a list of user groups /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetUserGroups($groupType=0, $affiliationid=0) { global $user; $groupType = processInputData($groupType, ARG_NUMERIC, 0, 0); $affiliationid = processInputData($affiliationid, ARG_NUMERIC, 0, 0); $groups = getUserGroups($groupType, $affiliationid); // Filter out any groups to which the user does not have access. $usergroups = array(); foreach($groups as $id => $group) { if($group['ownerid'] == $user['id'] || (array_key_exists("editgroupid", $group) && array_key_exists($group['editgroupid'], $user["groups"])) || (array_key_exists($id, $user["groups"]))) { array_push($usergroups, $group); } } return array("status" => "success", "groups" => $usergroups); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetUserGroupAttributes($name, $affiliation) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - there will be six additional elements in this case: /// \li \b owner - user that will be the owner of the group in /// username\@affiliation form /// \li \b managingGroup - user group that can manage membership of this one in /// groupname\@affiliation form /// \li \b initialMaxTime - (minutes) max initial time users in this group can /// select for length of reservations /// \li \b totalMaxTime - (minutes) total length users in the group can have for /// a reservation (including all extensions) /// \li \b maxExtendTime - (minutes) max length of time users can request as an /// extension to a reservation at a time /// \li \b overlapResCount - maximum allowed number of overlapping reservations /// allowed for users in this group /// /// \brief gets information about a user group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetUserGroupAttributes($name, $affiliation) { global $user; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $query = "SELECT ug.id, " . "ug.ownerid, " . "CONCAT(u.unityid, '@', a.name) AS owner, " . "ug.editusergroupid AS editgroupid, " . "eug.name AS editgroup, " . "eug.affiliationid AS editgroupaffiliationid, " . "euga.name AS editgroupaffiliation, " . "ug.initialmaxtime, " . "ug.totalmaxtime, " . "ug.maxextendtime, " . "ug.overlapResCount " . "FROM usergroup ug " . "LEFT JOIN user u ON (ug.ownerid = u.id) " . "LEFT JOIN affiliation a ON (u.affiliationid = a.id) " . "LEFT JOIN usergroup eug ON (ug.editusergroupid = eug.id) " . "LEFT JOIN affiliation euga ON (eug.affiliationid = euga.id) " . "WHERE ug.id = {$rc['id']}"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 18, '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'] && ! array_key_exists($row['editgroupid'], $user['groups'])) { return array('status' => 'error', 'errorcode' => 69, 'errormsg' => 'access denied to user group with submitted name and affiliation'); } $ret = array('status' => 'success', 'owner' => $row['owner'], 'managingGroup' => "{$row['editgroup']}@{$row['editgroupaffiliation']}", 'initialMaxTime' => $row['initialmaxtime'], 'totalMaxTime' => $row['totalmaxtime'], 'maxExtendTime' => $row['maxextendtime'], 'overlapResCount' => $row['overlapResCount']); return $ret; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddUserGroup($name, $affiliation, $owner, $managingGroup, /// $initialMaxTime, $totalMaxTime, $maxExtendTime, /// $custom) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// \param $owner - user that will be the owner of the group in /// username\@affiliation form /// \param $managingGroup - user group that can manage membership of this one /// \param $initialMaxTime - (minutes) max initial time users in this group can /// select for length of reservations /// \param $totalMaxTime - (minutes) total length users in the group can have /// for a reservation (including all extensions) /// \param $maxExtendTime - (minutes) max length of time users can request as an /// extension to a reservation at a time /// \param $custom - (optional, default=1) set custom flag for user group; if /// set to 0, $owner and $managingGroup will be ignored and group /// membership will be managed via authentication protocol /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string /// /// \b success - user group was successfully created /// /// \brief creates a new user group with the specified parameters /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddUserGroup($name, $affiliation, $owner, $managingGroup, $initialMaxTime, $totalMaxTime, $maxExtendTime, $custom=1) { global $user; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('name' => $name, 'affiliation' => $affiliation, 'owner' => $owner, 'managingGroup' => $managingGroup, 'initialMaxTime' => $initialMaxTime, 'totalMaxTime' => $totalMaxTime, 'maxExtendTime' => $maxExtendTime, 'custom' => $custom); $rc = validateAPIgroupInput($validate, 0); if($rc['status'] == 'error') return $rc; if($custom != 0 && $custom != 1) $custom = 1; if(! $custom) $rc['managingGroupID'] = NULL; $data = array('type' => 'user', 'owner' => $owner, 'name' => $name, 'affiliationid' => $rc['affiliationid'], 'editgroupid' => $rc['managingGroupID'], 'initialmax' => $initialMaxTime, 'totalmax' => $totalMaxTime, 'maxextend' => $maxExtendTime, 'overlap' => 0, 'custom' => $custom); if(! addGroup($data)) { return array('status' => 'error', 'errorcode' => 26, 'errormsg' => 'failure while adding group to database'); } return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCeditUserGroup($name, $affiliation, $newName, $newAffiliation, /// $newOwner, $newManagingGroup, $newInitialMaxTime, /// $newTotalMaxTime, $newMaxExtendTime) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// \param $newName - new name for user group /// \param $newAffiliation - new affiliation for user group /// \param $newOwner - (optional, default='') user that will be the owner of /// the group in username\@affiliation form /// \param $newManagingGroup - (optional, default='') user group that can /// manage membership of this one /// \param $newInitialMaxTime - (optional, default='') (minutes) max initial /// time users in this group can select for length /// of reservations /// \param $newTotalMaxTime - (optional, default='') (minutes) total length /// users in the group can have for a reservation /// (including all extensions) /// \param $newMaxExtendTime - (optional, default='') (minutes) max length of /// time users can request as an extension to a /// reservation at a time /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - user group was successfully updated /// /// \brief modifies attributes of a user group\n /// \b NOTE: an empty string may be passed for any of the new* fields to leave /// that item unchanged /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCeditUserGroup($name, $affiliation, $newName, $newAffiliation, $newOwner='', $newManagingGroup='', $newInitialMaxTime='', $newTotalMaxTime='', $newMaxExtendTime='') { global $user, $mysql_link_vcl; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $updates = array(); # validate group exists and new values other than newName and newAffiliation # are valid $validate = array('name' => $name, 'affiliation' => $affiliation); if(get_magic_quotes_gpc()) $newOwner = stripslashes($newOwner); if(! empty($newOwner)) $validate['owner'] = $newOwner; if(! empty($newManagingGroup)) $validate['managingGroup'] = $newManagingGroup; if(! empty($newInitialMaxTime)) { $validate['initialMaxTime'] = $newInitialMaxTime; $updates[] = "initialmaxtime = $newInitialMaxTime"; } if(! empty($newTotalMaxTime)) { $validate['totalMaxTime'] = $newTotalMaxTime; $updates[] = "totalmaxtime = $newTotalMaxTime"; } if(! empty($newMaxExtendTime)) { $validate['maxExtendTime'] = $newMaxExtendTime; $updates[] = "maxextendtime = $newMaxExtendTime"; } $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; # get info about group $query = "SELECT ownerid, " . "affiliationid, " . "custom, " . "courseroll " . "FROM usergroup " . "WHERE id = {$rc['id']}"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 18, 'errormsg' => 'user group with submitted name and affiliation does not exist'); } // if custom and not owner or custom/courseroll and no federated user group access, no access to edit group if(($row['custom'] == 1 && $user['id'] != $row['ownerid']) || (($row['custom'] == 0 || $row['courseroll'] == 1) && ! checkUserHasPerm('Manage Federated User Groups (global)') && (! checkUserHasPerm('Manage Federated User Groups (affiliation only)') || $row['affiliationid'] != $user['affiliationid']))) { return array('status' => 'error', 'errorcode' => 32, 'errormsg' => 'access denied to modify attributes for user group with submitted name and affiliation'); } # validate that newName and newAffiliation are valid if(($name != $newName || $affiliation != $newAffiliation) && (! empty($newName) || ! empty($newAffiliation))) { $validate = array('name' => $name, 'affiliation' => $affiliation); if(! empty($newName)) { if(get_magic_quotes_gpc()) $newName = stripslashes($newName); $validate['name'] = $newName; $tmp = mysql_real_escape_string($newName); $updates[] = "name = '$tmp'"; } if(! empty($newAffiliation)) $validate['affiliation'] = $newAffiliation; $rc2 = validateAPIgroupInput($validate, 0); if($rc2['status'] == 'error') { if($rc2['errorcode'] == 27) { $rc2['errorcode'] = 31; $rc2['errormsg'] = 'existing user group with new form of name@affiliation'; } return $rc2; } if(! empty($newAffiliation)) $updates[] = "affiliationid = {$rc2['affiliationid']}"; } if($row['custom']) { if(! empty($newOwner)) { $newownerid = getUserlistID(mysql_real_escape_string($newOwner)); $updates[] = "ownerid = $newownerid"; } if(! empty($newManagingGroup)) $updates[] = "editusergroupid = {$rc['managingGroupID']}"; } $sets = implode(',', $updates); if(count($updates) == 0) { return array('status' => 'error', 'errorcode' => 33, 'errormsg' => 'no new values submitted'); } $query = "UPDATE usergroup " . "SET $sets " . "WHERE id = {$rc['id']}"; doQuery($query, 101); return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveUserGroup($name, $affiliation) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - user group was successfully removed /// /// \brief removes a user group along with all of its privileges /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveUserGroup($name, $affiliation) { global $user, $mysql_link_vcl; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $query = "SELECT ownerid, " . "affiliationid, " . "custom, " . "courseroll " . "FROM usergroup " . "WHERE id = {$rc['id']}"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 18, 'errormsg' => 'user group with submitted name and affiliation does not exist'); } // if custom and not owner or custom/courseroll and no federated user group access, no access to delete group if(($row['custom'] == 1 && $user['id'] != $row['ownerid']) || (($row['custom'] == 0 || $row['courseroll'] == 1) && ! checkUserHasPerm('Manage Federated User Groups (global)') && (! checkUserHasPerm('Manage Federated User Groups (affiliation only)') || $row['affiliationid'] != $user['affiliationid']))) { return array('status' => 'error', 'errorcode' => 29, 'errormsg' => 'access denied to delete user group with submitted name and affiliation'); } if(checkForGroupUsage($rc['id'], 'user')) { return array('status' => 'error', 'errorcode' => 72, 'errormsg' => 'group currently in use and cannot be removed'); } $query = "DELETE FROM usergroup " . "WHERE id = {$rc['id']}"; doQuery($query, 101); # validate something deleted if(mysql_affected_rows($mysql_link_vcl) == 0) { return array('status' => 'error', 'errorcode' => 30, 'errormsg' => 'failure while deleting group from database'); } return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCdeleteUserGroup($name, $affiliation) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - user group was successfully removed /// /// \brief alias for XMLRPCremoveUserGroup /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCdeleteUserGroup($name, $affiliation) { # This was the original function. All other functions use 'remove' rather # than 'delete'. The function was renamed to XMLRPCremoveUserGroup. This was # kept for compatibility reasons return XMLRPCremoveUserGroup($name, $affiliation); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetUserGroupMembers($name, $affiliation) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - there will be one additional element in this case: /// \li \b members - array of members of the group in username\@affiliation form /// /// \brief gets members of a user group\n /// \b NOTE: it is possible to have a group with no members in which case /// success will be returned with an empty array for members /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetUserGroupMembers($name, $affiliation) { global $user; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $query = "SELECT ownerid, " . "editusergroupid AS editgroupid, " . "affiliationid, " . "custom, " . "courseroll " . "FROM usergroup " . "WHERE id = {$rc['id']}"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', '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 // 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'])) || (($row['custom'] == 0 || $row['courseroll'] == 1) && ! checkUserHasPerm('Manage Federated User Groups (global)') && (! checkUserHasPerm('Manage Federated User Groups (affiliation only)') || $row['affiliationid'] != $user['affiliationid']))) { return array('status' => 'error', 'errorcode' => 28, 'errormsg' => 'access denied to user group with submitted name and affiliation'); } $query = "SELECT CONCAT(u.unityid, '@', a.name) AS member " . "FROM usergroupmembers ugm, " . "user u, " . "affiliation a " . "WHERE ugm.usergroupid = {$rc['id']} AND " . "ugm.userid = u.id AND " . "u.affiliationid = a.id"; $qh = doQuery($query, 101); $members = array(); while($row = mysql_fetch_assoc($qh)) $members[] = $row['member']; return array('status' => 'success', 'members' => $members); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddUsersToGroup($name, $affiliation, $users) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// \param $users - array of users in username\@affiliation form to be added to /// the group /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - users successfully added to the group\n /// \b warning - there was a non-fatal issue that occurred while processing /// the call; there will be three additional elements in this case: /// \li \b warningcode - warning number /// \li \b warningmsg - warning string /// \li \b failedusers - array of users in username\@affiliation form that could /// not be added /// /// \brief adds users to a group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddUsersToGroup($name, $affiliation, $users) { global $user; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $query = "SELECT ownerid, " . "editusergroupid AS editgroupid " . "FROM usergroup " . "WHERE id = {$rc['id']}"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 18, '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'] && ! array_key_exists($row['editgroupid'], $user['groups'])) { return array('status' => 'error', 'errorcode' => 28, 'errormsg' => 'access denied to user group with submitted name and affiliation'); } $fails = array(); foreach($users as $_user) { if(empty($_user)) continue; if(get_magic_quotes_gpc()) $_user = stripslashes($_user); $esc_user = mysql_real_escape_string($_user); if(validateUserid($_user) == 1) addUserGroupMember($esc_user, $rc['id']); else $fails[] = $_user; } if(count($fails)) { $cnt = 'some'; $code = 34; if(count($fails) == count($users)) { $cnt = 'all submitted'; $code = 35; } return array('status' => 'warning', 'failedusers' => $fails, 'warningcode' => $code, 'warningmsg' => "failed to add $cnt users to user group"); } return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveUsersFromGroup($name, $affiliation, $users) /// /// \param $name - name of user group /// \param $affiliation - affiliation of user group /// \param $users - array of users in username\@affiliation form to be removed /// from the group /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b success - users successfully removed from the group\n /// \b warning - there was a non-fatal issue that occurred while processing /// the call; there will be three additional elements in this case: /// \li \b warningcode - warning number /// \li \b warningmsg - warning string /// \li \b failedusers - array of users in username\@affiliation form that could /// not be removed /// /// \brief removes users from a group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveUsersFromGroup($name, $affiliation, $users) { global $user, $findAffilFuncs; if(! in_array('groupAdmin', $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('name' => $name, 'affiliation' => $affiliation); $rc = validateAPIgroupInput($validate, 1); if($rc['status'] == 'error') return $rc; $query = "SELECT ownerid, " . "editusergroupid AS editgroupid " . "FROM usergroup " . "WHERE id = {$rc['id']}"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 18, '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'] && ! array_key_exists($row['editgroupid'], $user['groups'])) { return array('status' => 'error', 'errorcode' => 28, 'errormsg' => 'access denied to user group with submitted name and affiliation'); } $fails = array(); foreach($users as $_user) { if(empty($_user)) continue; if(get_magic_quotes_gpc()) $_user = stripslashes($_user); $esc_user = mysql_real_escape_string($_user); # check that affiliation of user can be determined because getUserlistID # will abort if it cannot find it $affilok = 0; foreach($findAffilFuncs as $func) { if($func($_user, $dump)) $affilok = 1; } if(! $affilok) { $fails[] = $_user; continue; } $userid = getUserlistID($esc_user, 1); if(is_null($userid)) $fails[] = $_user; else deleteUserGroupMember($userid, $rc['id']); } if(count($fails)) { $cnt = 'some'; $code = 36; if(count($fails) == count($users)) { $cnt = 'any'; $code = 37; } return array('status' => 'warning', 'failedusers' => $fails, 'warningcode' => $code, 'warningmsg' => "failed to remove $cnt users from user group"); } return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCgetResourceGroups($type) /// /// \param $type - the resource group type /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - a 'groups' element will contain an array of groups of the given /// type\n /// /// \brief get a list of resource groups of a particular type /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCgetResourceGroups($type) { global $user; $resources = getUserResources(array("groupAdmin"), array("manageGroup"), 1); if(array_key_exists($type, $resources)) { return array('status' => 'success', 'groups' => $resources[$type]); } else { return array('status' => 'error', 'errorcode' => 73, 'errormsg' => 'invalid resource group type'); } } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCaddResourceGroup($name, $managingGroup, $type) /// /// \param $name - the name of the resource group /// \param $managingGroup - the name of the managing group /// \param $type - the type of resource group /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - the resource group was added /// /// \brief add a resource group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCaddResourceGroup($name, $managingGroup, $type) { global $user; if(! in_array("groupAdmin", $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } $validate = array('managingGroup' => $managingGroup); $rc = validateAPIgroupInput($validate, 0); if($rc['status'] == 'error') return $rc; if($typeid = getResourceTypeID($type)) { if(checkForGroupName($name, 'resource', '', $typeid)) { return array('status' => 'error', 'errorcode' => 76, 'errormsg' => 'resource group already exists'); } if(get_magic_quotes_gpc()) $name = stripslashes($name); if(! preg_match('/^[-a-zA-Z0-9_\. ]{3,30}$/', $name)) { return array('status' => 'error', 'errorcode' => 87, 'errormsg' => 'Name must be between 3 and 30 characters and can only contain letters, numbers, spaces, and these characters: - . _'); } $name = mysql_real_escape_string($name); $data = array('type' => 'resource', 'ownergroup' => $rc['managingGroupID'], 'resourcetypeid' => $typeid, 'name' => $name); if(! addGroup($data)) { return array('status' => 'error', 'errorcode' => 26, 'errormsg' => 'failure while adding group to database'); } } else { return array('status' => 'error', 'errorcode' => 68, 'errormsg' => 'invalid resource type'); } return array('status' => 'success'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCremoveResourceGroup($name, $type) /// /// \param $name - the name of the resource group /// \param $type - the resource group type /// /// \return an array with at least one index named 'status' which will have /// one of these values\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - the resource group was removed\n /// /// \brief remove a resource group /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCremoveResourceGroup($name, $type) { global $user; if(! in_array("groupAdmin", $user['privileges'])) { return array('status' => 'error', 'errorcode' => 16, 'errormsg' => 'access denied for managing groups'); } if($groupid = getResourceGroupID("$type/$name")) { $userresources = getUserResources(array("groupAdmin"), array("manageGroup"), 1); if(array_key_exists($type, $userresources)) { if(array_key_exists($groupid, $userresources[$type])) { if(checkForGroupUsage($groupid, 'resource')) { return array('status' => 'error', 'errorcode' => 72, 'errormsg' => 'group currently in use and cannot be removed'); } $query = "DELETE FROM resourcegroup " . "WHERE id = $groupid"; doQuery($query, 315); return array('status' => 'success'); } else return array('status' => 'error', 'errorcode' => 75, 'errormsg' => 'access denied to specified resource group'); } } return array('status' => 'error', 'errorcode' => 83, 'errormsg' => 'invalid resource group name'); } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCblockAllocation($imageid, $start, $end, $numMachines, /// $usergroupid, $ignoreprivileges) /// /// \param $imageid - id of the image to be used /// \param $start - mysql datetime for the start time (i.e. machines should be /// prep'd and ready by this time) /// \param $end - mysql datetime for the end time /// \param $numMachines - number of computers to allocate /// \param $usergroupid - id of user group for checking user access to machines /// \param $ignoreprivileges - (optional, default=0) 0 (false) or 1 (true) - set /// to 1 to select computers from any that are mapped to be able to run the /// image; set to 0 to only select computers from ones that are both mapped and /// that users in the usergroup assigned to this block allocation have been /// granted access to through the privilege tree /// /// \return an array with blockTimesid as an index with the value of the newly /// created block time and at least one other index named 'status' which will /// have one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the /// array:\n /// \li \b errorcode - error number\n /// \li \b errormsg - error string\n /// /// \b success - blockTimesid was processed; there will be two additional /// elements in this case:\n /// \li \b allocated - total number of desired allocations that have been /// processed\n /// \li \b unallocated - total number of desired allocations that have not been /// processed\n /// /// \b warning - there was a non-fatal issue that occurred while processing /// the call; there will be four additional elements in this case:\n /// \li \b warningcode - warning number\n /// \li \b warningmsg - warning string\n /// \li \b allocated - total number of desired allocations that have been /// processed\n /// \li \b unallocated - total number of desired allocations that have not been /// processed\n\n /// /// \b NOTE: status may be warning, but allocated may be 0 indicating there /// were no errors that occurred, but there simply were not any machines /// available /// /// \brief creates and processes a block allocation according to the passed /// in criteria /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCblockAllocation($imageid, $start, $end, $numMachines, $usergroupid, $ignoreprivileges=0) { global $user, $xmlrpcBlockAPIUsers; if(! in_array($user['id'], $xmlrpcBlockAPIUsers)) { return array('status' => 'error', 'errorcode' => 34, 'errormsg' => 'access denied for managing block allocations'); } # valid $imageid $resources = getUserResources(array("imageAdmin", "imageCheckOut")); $resources["image"] = removeNoCheckout($resources["image"]); if(! array_key_exists($imageid, $resources['image'])) { return array('status' => 'error', 'errorcode' => 3, 'errormsg' => "access denied to $imageid"); } # validate $start and $end $dtreg = '([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})'; $startts = datetimeToUnix($start); $endts = datetimeToUnix($end); $maxend = datetimeToUnix("2038-01-01 00:00:00"); if(! preg_match("/^$dtreg$/", $start) || $startts < 0 || $startts > $maxend) { return array('status' => 'error', 'errorcode' => 4, 'errormsg' => "received invalid input for start"); } if(! preg_match("/^$dtreg$/", $end) || $endts < 0 || $endts > $maxend) { return array('status' => 'error', 'errorcode' => 36, 'errormsg' => "received invalid input for end"); } # validate $numMachines if(! is_numeric($numMachines) || $numMachines < MIN_BLOCK_MACHINES || $numMachines > MAX_BLOCK_MACHINES) { return array('status' => 'error', 'errorcode' => 64, 'errormsg' => 'The submitted number of seats must be between ' . MIN_BLOCK_MACHINES . ' and ' . MAX_BLOCK_MACHINES . '.'); } # validate $usergroupid $groups = getUserGroups(); if(! array_key_exists($usergroupid, $groups)) { return array('status' => 'error', 'errorcode' => 67, 'errormsg' => 'Submitted user group does not exist'); } # validate ignoreprivileges if(! is_numeric($ignoreprivileges) || $ignoreprivileges < 0 || $ignoreprivileges > 1) { return array('status' => 'error', 'errorcode' => 86, 'errormsg' => 'ignoreprivileges must be 0 or 1'); } $ownerid = getUserlistID('vclreload@Local'); $name = "API:$start"; $managementnodes = getManagementNodes('future'); if(empty($managementnodes)) { return array('status' => 'error', 'errorcode' => 12, 'errormsg' => 'could not allocate a management node to handle block allocation'); } $mnid = array_rand($managementnodes); $query = "INSERT INTO blockRequest " . "(name, " . "imageid, " . "numMachines, " . "groupid, " . "repeating, " . "ownerid, " . "admingroupid, " . "managementnodeid, " . "expireTime, " . "status) " . "VALUES " . "('$name', " . "$imageid, " . "$numMachines, " . "$usergroupid, " . "'list', " . "$ownerid, " . "0, " . "$mnid, " . "'$end', " . "'accepted')"; doQuery($query, 101); $brid = dbLastInsertID(); $query = "INSERT INTO blockTimes " . "(blockRequestid, " . "start, " . "end) " . "VALUES " . "($brid, " . "'$start', " . "'$end')"; doQuery($query, 101); $btid = dbLastInsertID(); $query = "INSERT INTO blockWebDate " . "(blockRequestid, " . "start, " . "end, " . "days) " . "VALUES " . "($brid, " . "'$start', " . "'$end', " . "0)"; doQuery($query); $sh = date('g', $startts); $smi = date('i', $startts); $sme = date('a', $startts); $eh = date('g', $startts); $emi = date('i', $startts); $eme = date('a', $startts); $query = "INSERT INTO blockWebTime " . "(blockRequestid, " . "starthour, " . "startminute, " . "startmeridian, " . "endhour, " . "endminute, " . "endmeridian, " . "`order`) " . "VALUES " . "($brid, " . "$sh," . "$smi," . "'$sme'," . "$eh," . "$emi," . "'$eme'," . "0)"; doQuery($query); $return = XMLRPCprocessBlockTime($btid, $ignoreprivileges); $return['blockTimesid'] = $btid; return $return; } //////////////////////////////////////////////////////////////////////////////// /// /// \fn XMLRPCprocessBlockTime($blockTimesid, $ignoreprivileges) /// /// \param $blockTimesid - id from the blockTimes table /// \param $ignoreprivileges - (optional, default=0) 0 (false) or 1 (true) - set /// to 1 to select computers from any that are mapped to be able to run the /// image; set to 0 to only select computers from ones that are both mapped and /// that users in the usergroup assigned to this block allocation have been /// granted access to through the privilege tree /// /// \return an array with at least one index named 'status' which will have /// one of these values:\n /// \b error - error occurred; there will be 2 additional elements in the array: /// \li \b errorcode - error number /// \li \b errormsg - error string /// /// \b completed - blockTimesid was previously successfully processed\n /// \b success - blockTimesid was processed; there will be two additional /// elements in this case:\n /// \li \b allocated - total number of desired allocations that have been /// processed\n /// \li \b unallocated - total number of desired allocations that have not been /// processed\n /// /// \b warning - there was a non-fatal issue that occurred while processing /// the call; there will be four additional elements in this case:\n /// \li \b warningcode - warning number\n /// \li \b warningmsg - warning string\n /// \li \b allocated - total number of desired allocations that have been /// processed\n /// \li \b unallocated - total number of desired allocations that have not been /// processed\n\n /// /// \b NOTE: status may be warning, but allocated may be 0 indicating there /// were no errors that occurred, but there simply were not any machines /// available /// /// \brief processes a block allocation for the blockTimes entry associated /// with blockTimesid /// //////////////////////////////////////////////////////////////////////////////// function XMLRPCprocessBlockTime($blockTimesid, $ignoreprivileges=0) { global $requestInfo, $user, $xmlrpcBlockAPIUsers; if(! in_array($user['id'], $xmlrpcBlockAPIUsers)) { return array('status' => 'error', 'errorcode' => 34, 'errormsg' => 'access denied for managing block allocations'); } # validate $blockTimesid if(! is_numeric($blockTimesid)) { return array('status' => 'error', 'errorcode' => 77, 'errormsg' => 'Invalid blockTimesid specified'); } # validate ignoreprivileges if(! is_numeric($ignoreprivileges) || $ignoreprivileges < 0 || $ignoreprivileges > 1) { return array('status' => 'error', 'errorcode' => 86, 'errormsg' => 'ignoreprivileges must be 0 or 1'); } $return = array('status' => 'success'); $query = "SELECT bt.start, " . "bt.end, " . "br.imageid, " . "br.numMachines, " . "br.groupid, " . "br.expireTime " . "FROM blockRequest br, " . "blockTimes bt " . "WHERE bt.blockRequestid = br.id AND " . "bt.id = $blockTimesid"; $qh = doQuery($query, 101); if(! $rqdata = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 8, 'errormsg' => 'unknown blockTimesid'); } if(datetimeToUnix($rqdata['expireTime']) < time()) { return array('status' => 'error', 'errorcode' => 9, 'errormsg' => 'expired block allocation'); } $images = getImages(0, $rqdata['imageid']); if(empty($images)) { return array('status' => 'error', 'errorcode' => 10, 'errormsg' => 'invalid image associated with block allocation'); } $unixstart = datetimeToUnix($rqdata['start']); $unixend = datetimeToUnix($rqdata['end']); $revisionid = getProductionRevisionid($rqdata['imageid']); $imgLoadTime = getImageLoadEstimate($rqdata['imageid']); if($imgLoadTime == 0) $imgLoadTime = $images[$rqdata['imageid']]['reloadtime'] * 60; $vclreloadid = getUserlistID('vclreload@Local'); $groupmembers = getUserGroupMembers($rqdata['groupid']); $userids = array_keys($groupmembers); # add any computers from future reservations users in the group made if(! empty($groupmembers)) { ## find reservations by users $allids = implode(',', $userids); $query = "SELECT rq.id AS reqid, " . "UNIX_TIMESTAMP(rq.start) AS start, " . "rq.userid " . "FROM request rq, " . "reservation rs " . "WHERE rs.requestid = rq.id AND " . "rq.userid IN ($allids) AND " . "rq.start < '{$rqdata['end']}' AND " . "rq.end > '{$rqdata['start']}' AND " . "rs.imageid = {$rqdata['imageid']} AND " . "rs.computerid NOT IN (SELECT computerid " . "FROM blockComputers " . "WHERE blockTimeid = $blockTimesid)"; $qh = doQuery($query); $donereqids = array(); $blockCompVals = array(); $checkstartbase = $unixstart - $imgLoadTime - 300; $reloadstartbase = unixToDatetime($checkstartbase); $rows = mysql_num_rows($qh); while($row = mysql_fetch_assoc($qh)) { if(array_key_exists($row['reqid'], $donereqids)) continue; $donereqids[$row['reqid']] = 1; if($row['start'] < datetimeToUnix($rqdata['start'])) { $checkstart = $row['start'] - $imgLoadTime - 300; $reloadstart = unixToDatetime($checkstart); $reloadend = unixToDatetime($row['start']); } else { $checkstart = $checkstartbase; $reloadstart = $reloadstartbase; $reloadend = $rqdata['start']; } # check to see if computer is available for whole block $rc = isAvailable($images, $rqdata['imageid'], $revisionid, $checkstart, $unixend, $row['reqid'], $row['userid'], $ignoreprivileges, 0, '', '', 1); // if not available for whole block, just skip this one if($rc < 1) continue; $compid = $requestInfo['computers'][0]; # create reload reservation $reqid = simpleAddRequest($compid, $rqdata['imageid'], $revisionid, $reloadstart, $reloadend, 19, $vclreloadid); if($reqid == 0) continue; # add to blockComputers $blockCompVals[] = "($blockTimesid, $compid, {$rqdata['imageid']}, $reqid)"; # process any subimages for($key = 1; $key < count($requestInfo['computers']); $key++) { $subimageid = $requestInfo['images'][$key]; $subrevid = getProductionRevisionid($subimageid); $compid = $requestInfo['computers'][$key]; $mgmtnodeid = $requestInfo['mgmtnodes'][$key]; $blockCompVals[] = "($blockTimesid, $compid, $subimageid, $reqid)"; $query = "INSERT INTO reservation " . "(requestid, " . "computerid, " . "imageid, " . "imagerevisionid, " . "managementnodeid) " . "VALUES " . "($reqid, " . "$compid, " . "$subimageid, " . "$subrevid, " . "$mgmtnodeid)"; doQuery($query, 101); } } if(count($blockCompVals)) { $blockComps = implode(',', $blockCompVals); $query = "INSERT INTO blockComputers " . "(blockTimeid, computerid, imageid, reloadrequestid) " . "VALUES $blockComps"; doQuery($query); } } # check to see if all computers have been allocated $query = "SELECT COUNT(computerid) AS allocated " . "FROM blockComputers " . "WHERE blockTimeid = $blockTimesid"; $qh = doQuery($query, 101); if(! $row = mysql_fetch_assoc($qh)) { return array('status' => 'error', 'errorcode' => 15, 'errormsg' => 'failure to communicate with database'); } $compCompleted = $row['allocated']; if(array_key_exists('subimages', $images[$rqdata['imageid']])) $compsPerAlloc = 1 + count($images[$rqdata['imageid']]['subimages']); else $compsPerAlloc = 1; $toallocate = ($rqdata['numMachines'] * $compsPerAlloc) - $compCompleted; if($toallocate == 0) { if(count($blockCompVals)) { return array('status' => 'success', 'allocated' => $rqdata['numMachines'], 'unallocated' => 0); } return array('status' => 'completed'); } $reqToAlloc = $toallocate / $compsPerAlloc; if(! $ignoreprivileges) { # get userids in user group if(empty($groupmembers)) { return array('status' => 'error', 'errorcode' => 11, 'errormsg' => 'empty user group and ignoreprivileges set to 0'); } # make length of $userids match $reqToAlloc by duplicating or trimming some users while($reqToAlloc > count($userids)) $userids = array_merge($userids, $userids); if($reqToAlloc < count($userids)) $userids = array_splice($userids, 0, $reqToAlloc); } # staggering: stagger start times for this round (ie, do not worry about # previous processing of this block time) such that there is 1 minute # between the start times for each allocation $stagExtra = $reqToAlloc * 60; # determine estimated load time $loadtime = $imgLoadTime + (10 * 60); # add 10 minute fudge factor if((time() + $loadtime + $stagExtra) > $unixstart) { $return['status'] = 'warning'; $return['warningcode'] = 13; $return['warningmsg'] = 'possibly insufficient time to load machines'; } $start = unixToDatetime($unixstart - $loadtime); $userid = 0; $allocated = 0; $blockCompVals = array(); # FIXME (maybe) - if some subset of users in the user group have available # computers, but others do not, $allocated will be less than the desired # number of machines; however, calling this function enough times will # result in enough machines being allocated because they will continue to be # allocated based on the ones with machines available; this seems like odd # behavior $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 for($i = 0; $i < $reqToAlloc; $i++) { $stagunixstart = $unixstart - $loadtime - ($stagCnt * $stagTime); $stagstart = unixToDatetime($stagunixstart); if(! $ignoreprivileges) $userid = array_pop($userids); # use end of block time to find available computers, but... $rc = isAvailable($images, $rqdata['imageid'], $revisionid, $stagunixstart, $unixend, 0, $userid, $ignoreprivileges); if($rc < 1) continue; $compid = $requestInfo['computers'][0]; # ...use start of block time as end of reload reservation $reqid = simpleAddRequest($compid, $rqdata['imageid'], $revisionid, $stagstart, $rqdata['start'], 19, $vclreloadid); if($reqid == 0) continue; $stagCnt++; $allocated++; $blockCompVals[] = "($blockTimesid, $compid, {$rqdata['imageid']}, $reqid)"; # process any subimages for($key = 1; $key < count($requestInfo['computers']); $key++) { $subimageid = $requestInfo['images'][$key]; $subrevid = getProductionRevisionid($subimageid); $compid = $requestInfo['computers'][$key]; $mgmtnodeid = $requestInfo['mgmtnodes'][$key]; $blockCompVals[] = "($blockTimesid, $compid, $subimageid, $reqid)"; $query = "INSERT INTO reservation " . "(requestid, " . "computerid, " . "imageid, " . "imagerevisionid, " . "managementnodeid) " . "VALUES " . "($reqid, " . "$compid, " . "$subimageid, " . "$subrevid, " . "$mgmtnodeid)"; doQuery($query, 101); } semUnlock(); $blockComps = implode(',', $blockCompVals); $query = "INSERT INTO blockComputers " . "(blockTimeid, computerid, imageid, reloadrequestid) " . "VALUES $blockComps"; doQuery($query, 101); $blockCompVals = array(); } if($allocated == 0) { $return['status'] = 'warning'; $return['warningcode'] = 14; $return['warningmsg'] = 'unable to allocate any machines'; } $return['allocated'] = ($compCompleted / $compsPerAlloc) + $allocated; $return['unallocated'] = $rqdata['numMachines'] - $return['allocated']; return $return; } ?>