CourseReg curl.php

From MWWiki

Jump to: navigation, search

Back to Course Registration

<?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;
}
 
?>