<?php
/******************************************\
* Created on Apr 7, 2009 *
********************************************
* This script functions more as a POC for *
* registering someone with the CMU WebISO *
* course registration system. Currently, *
* everything is done ad-hoc through global *
* variables set within this script. *
\******************************************/
// curl documentation:
// http://www.php.net/manual/en/book.curl.php
//
// list of options
// http://www.php.net/manual/en/function.curl-setopt.php
//
// Currently, there are 8 known steps from start to fully registered.
// 1 - GET the main registration page
// 2 - GET the WebISO authentication page
// 3 - POST the WebISO login credentials
// *was login successful? if yes, proceed to step 4
// 4 - GET the main registration page (yes, again)
// 5 - POST past the welcome page
// 6 - POST the semester for which we are registering
// 7 - POST that we are advised
// 8 - POST that we wish to add a course
// this array contains the handler names for each step, which will execute
// in sequence, setting and executing the necessary cURL parameters to make
// each step work
$steps = array(
'getMainRegistration',
'getAuthentication',
'postAuthentication',
'getMainRegistration',
'postWelcome',
'postSemester',
'postAdvised',
'postAddCourse'
);
// username and password for authentication into andrew
$USER = 'username';
$PASS = 'password';
$semester = 'N09';
$toAdd = array('03121' => 'u');
// initialize the cURL handle
$handle = curl_init();
// set a few main options that won't change
$options = array(
// a few basics
CURLOPT_USERAGENT => 'Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
// SSL handling
CURLOPT_SSL_VERIFYHOST => '0',
CURLOPT_SSL_VERIFYPEER => '0',
CURLOPT_CAINFO => '/tmp/CMU-CA-server-2.cer',
// cookies
CURLOPT_COOKIESESSION => true,
CURLOPT_COOKIEFILE => '/tmp/cookiefile',
CURLOPT_COOKIEJAR => '/tmp/cookiefile'
);
// set the options
curl_setopt_array($handle, $options);
// THE LOOP...dun dun dunnnn
$retval = "";
foreach ($steps as $step) {
if (is_callable($step)) {
echo 'Running ' . $step . "...\n";
$retval = $step($retval);
// error handling
if (curl_errno($handle)) {
die('Error in ' . $step . ': ' . curl_error($handle));
}
}
}
// clean up and finish
curl_close($handle);
////// STEP FUNCTIONS ///////
/**
* The first function that is executed. This one is pretty simple: just
* set up the initial parameters and roll with it.
*/
function getMainRegistration() {
global $handle;
// set the URL
curl_setopt($handle, CURLOPT_URL, 'https://olr.as.cmu.edu/');
// execute
return curl_exec($handle);
}
/**
* This is the second step. Things are also pretty simple here; just get
* the main WebISO page.
*
* @return string The HTML resulting from a successful GET request.
*/
function getAuthentication() {
global $handle;
// set the URL
curl_setopt($handle, CURLOPT_URL, 'https://webiso.andrew.cmu.edu/');
// set the referer for good measure
curl_setopt($handle, CURLOPT_REFERER, 'https://olr.as.cmu.edu/');
// execute
return curl_exec($handle);
}
/**
* The third step. This posts the login credentials, hopefully
* successfully authenticating the user and allowing them passage to the
* next step.
*
* @param string $content The HTML from the previous page query.
*/
function postAuthentication($content) {
global $handle, $USER, $PASS;
// set the URL
curl_setopt($handle, CURLOPT_URL, 'https://webiso.andrew.cmu.edu/login.cgi');
// turn on POST
curl_setopt($handle, CURLOPT_POST, '1');
// parse out the content for all the POST vars
$vars = extractHidden($USER, $PASS, $content);
curl_setopt($handle, CURLOPT_POSTFIELDS, encode($vars));
// execute
$res = curl_exec($handle);
// help the next guy out a bit...
curl_setopt($handle, CURLOPT_POST, '0');
curl_setopt($handle, CURLOPT_REFERER, 'https://webiso.andrew.cmu.edu/login.cgi');
// return the HTML page so we know if we're successful
return $res;
}
/**
* We've logged in and reached the main registration page. Time to move
* to bigger and better things.
*/
function postWelcome() {
global $handle;
// set a few options we're going to need
curl_setopt($handle, CURLOPT_POST, '1');
$postvars = array('FUNCNAME' => 'welcome',
'VAR1' => 'def',
'ARGNUM' => '1',
'submit' => '');
curl_setopt($handle, CURLOPT_POSTFIELDS, encode($postvars));
curl_setopt($handle, CURLOPT_URL, 'https://olr.as.cmu.edu/proc.formproc');
// execute!
return curl_exec($handle);
}
/**
* The next step: we need to select the semester we want from a list of choices.
* Given that this window only occasionally shows up, however, we're going to
* test and make sure that it's there. Otherwise we'll simply exit.
*
* @param string $content The HTML content of the previous query.
* @return string The HTML content from this query.
*/
function postSemester($content) {
global $handle, $semester;
// first, check to make sure we've actually received this screen
if (strstr($content, 'Choose a Semester') === false) {
// no good
return $content;
}
// set up the handle
$postvars = array('FUNCNAME' => 'choose_sem',
'PORT' => '42',
'ARGNUM' => '1',
// and here's the semester!...
'VAR1' => $semester);
curl_setopt($handle, CURLOPT_POSTFIELDS, encode($postvars));
// do it!
return curl_exec($handle);
}
/**
* This time around, we want to submit that we have spoken with our advisor
* about our course choices. This screen can also be intermittent, so we will
* perform simple content checking to make sure we are on the right page.
*
* @param string $content The HTML content of the previous query.
* @return string The HTML content from this query.
*/
function postAdvised($content) {
global $handle;
// first, are we on the right page?
if (strstr($content, 'academic advisor') === false) {
return $content;
}
// reset the form variables again
$postvars = array('FUNCNAME' => 'are_u_advised',
'VAR1' => 'yes',
'VAR2' => 'Continue',
'ARGNUM' => '1',
'PORT' => '42');
curl_setopt($handle, CURLOPT_POSTFIELDS, encode($postvars));
// execute
return curl_exec($handle);
}
/**
* Finally, now we're getting somewhere. This function submits the course we
* want to register for. Also, a sanity check is performed to make sure we
* are where we think we are.
*
* @param string $content The HTML content from the previous query.
* @return string The HTML content from this query.
*/
function postAddCourse($content) {
global $handle, $toAdd;
// first, sanity check
if (strstr($content, 'Registration Menu') === false) {
die('FATAL ERROR: We\'re lost in space!!!');
}
// do it to it
foreach($toAdd as $course => $section) {
$postvars = array('FUNCNAME' => 'add_course',
'ARGNUM' => '1',
'PORT' => '42',
'VAR1' => 'def',
'VAR2' => 'AddCourse');
curl_setopt($handle, CURLOPT_POSTFIELDS, encode($postvars));
if (strstr(curl_exec($handle), 'enter Course Number')) {
// success! one more step...
$postvars = array('FUNCNAME' => 'add_this_course',
'ARGNUM' => '4',
'PORT' => '42',
'VAR1' => $course,
'VAR2' => $section,
'VAR3' => '03121',
'VAR4' => 'b',
'VAR3' => 'Add');
curl_setopt($handle, CURLOPT_POSTFIELDS, encode($postvars));
if (strstr(curl_exec($handle), 'You are NOW registered')) {
// WOOHOO!
echo 'Registration for ' . $course . ' successful!' . "\n";
} else {
echo 'Something went wrong with ' . $course . ' registration.' . "\n";
}
} else {
// for some reason, requesting to add a course didn't lead us to where it should have...
die('An error was encountered, bailing...' . "\n");
}
}
}
////// HELPER FUNCTIONS ///////
/**
* Helper function for encoding POST variables.
*
* @param array $postvars Array of key => value POST variables.
* @return string A string of URL-encoded key=value&... pairs.
*/
function encode($postvars) {
$retval = "";
foreach ($postvars as $key => $val) {
$retval .= $key . '=' . urlencode($val) . '&';
}
// chop off the trailing & and return the string
return substr($retval, 0, strlen($retval) - 1);
}
/**
* Given a username and password, along with the content of the HTML login page,
* this function extracts hidden form values and builds an array that can be
* used to POST the values to the login form.
*
* @param string $username The login name of the user.
* @param string $password The password of the user.
* @param string $content HTML content of the page to extract input values from.
* @return array An array of all values in POST format, key-value.
*/
function extractHidden($username, $password, $content) {
$pattern = '/<input[\s]+type="hidden"[\s]+name="([a-zA-Z0-9_]*)"[\s]+value="(.*)"[\s]?>/';
$matches = "";
$postvars = array();
if (preg_match_all($pattern, $content, $matches) > 0) {
// we have matches, let's put them together
$numPairs = count($matches[1]);
for ($i = 0; $i < $numPairs; $i++) {
$postvars[$matches[1][$i]] = $matches[2][$i];
}
// assemble the rest of the array
$postvars['user'] = $username;
$postvars['pass'] = $password;
$postvars['realm'] = 'ANDREW.CMU.EDU';
$postvars['submit'] = 'Login';
}
// all done
return $postvars;
}
?>