diff --git a/bin/test.php b/bin/test.php deleted file mode 100755 index c0713b82062c16426e161ac94863a51c580f4f2b..0000000000000000000000000000000000000000 --- a/bin/test.php +++ /dev/null @@ -1,637 +0,0 @@ -#!/usr/bin/env php -<?php - -/* - * This script can be used to test a login-logout sequence to a specific IdP. - * It is configured from the config/test.php file. A template for that file - * can be found in config/test-template.php. - */ - -$tests = array(); - -/* The configuration file is relative to this script. */ -$configFile = dirname(dirname(__FILE__)) . '/config/test.php'; - -/* Check if the configuration file exists. */ -if(!file_exists($configFile)) { - echo('Missing configuration file: ' . $configFile . "\n"); - echo('Maybe you need to copy config/test-template.php to config/test.php and update it?.' . "\n"); - exit(1); -} - -/* Load the configuration file. */ -require_once($configFile); - -/** - * This function creates a curl handle and initializes it. - * - * @return A curl handler. - */ -function curlCreate() { - - $ch = curl_init($url); - - curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); - curl_setopt($ch, CURLOPT_COOKIEFILE, ''); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); - - return $ch; -} - - -/** - * This function requests a url with a GET request. - * - * @param $curl The curl handle which should be used. - * @param $url The url which should be requested. - * @param $parameters Associative array with parameters which should be appended to the url. - * @return The content of the returned page. - */ -function urlGet($curl, $url, $parameters = array()) { - - $p = ''; - foreach($parameters as $k => $v) { - if($p != '') { - $p .= '&'; - } - - $p .= urlencode($k) . '=' . urlencode($v); - } - - if(strpos($url, '?') === FALSE) { - $url .= '?' . $p; - } else { - $url .= '&' . $p; - } - - curl_setopt($curl, CURLOPT_HTTPGET, TRUE); - curl_setopt($curl, CURLOPT_URL, $url); - - $curl_scraped_page = curl_exec($curl); - if($curl_scraped_page === FALSE) { - echo('Failed to get url: ' . $url . "\n"); - echo('Curl error: ' . curl_error($curl) . "\n"); - return FALSE; - } - - return $curl_scraped_page; -} - - -/** - * This function posts data to a specific url. - * - * @param $curl The curl handle which should be used for the request. - * @param $url The url the POST request should be directed to. - * @param $post Associative array with the post parameters. - * $return The returned page. - */ -function urlPost($curl, $url, $post) { - - $postparams = ''; - - foreach($post as $k => $v) { - if($postparams != '') { - $postparams .= '&'; - } - - $postparams .= urlencode($k) . '=' . urlencode($v); - } - - - curl_setopt($curl, CURLOPT_POSTFIELDS, $postparams); - curl_setopt($curl, CURLOPT_POST, TRUE); - curl_setopt($curl, CURLOPT_URL, $url); - - $curl_scraped_page = curl_exec($curl); - if($curl_scraped_page === FALSE) { - echo('Failed to get url: ' . $url . "\n"); - echo('Curl error: ' . curl_error($curl) . "\n"); - return FALSE; - } - - return $curl_scraped_page; -} - - -/** - * This function parses a simpleSAMLphp HTTP-REDIRECT debug page. - * - * @param $page The content of the page. - * @return FALSE if $page isn't a HTTP-REDIRECT debug page, destination url if it is. - */ -function parseSimpleSamlHttpRedirectDebug($page) { - if(strpos($page, '<h2>Sending a SAML message using HTTP-REDIRECT</h2>') === FALSE) { - return FALSE; - } - - if(!preg_match('/<a id="sendlink" href="([^"]*)">send SAML message<\\/a>/', $page, $matches)) { - echo('Invalid simpleSAMLphp debug page. Missing link.' . "\n"); - return FALSE; - } - - $url = $matches[1]; - $url = html_entity_decode($url); - - return $url; -} - - -/** - * This function parses a simpleSAMLphp HTTP-POST page. - * - * @param $page The content of the page. - * @return FALSE if $page isn't a HTTP-POST page. If it is a HTTP-POST page, it will return an associative array with - * the post destination in 'url' and the post arguments as an associative array in 'post'. - */ -function parseSimpleSamlHttpPost($page) { - if(strpos($page, '<title>SAML 2.0 POST</title>') === FALSE - && strpos($page, '<title>SAML Response Debug-mode</title>') === FALSE - && strpos($page, '<title>SAML (Shibboleth 1.3) Response Debug-mode</title>') === FALSE) { - return FALSE; - } - - if(!preg_match('/<form method="post" action="([^"]*)">/', $page, $matches)) { - echo('Invalid simpleSAMLphp HTTP-POST page. Missing form target.' . "\n"); - return FALSE; - } - $url = html_entity_decode($matches[1]); - - $params = array(); - - if(!preg_match('/<input type="hidden" name="SAMLResponse" value="([^"]*)" \\/>/', $page, $matches)) { - echo('Invalid simpleSAMLphp HTTP-POST page. Missing SAMLResponse.' . "\n"); - return FALSE; - } - $params['SAMLResponse'] = html_entity_decode($matches[1]); - - if(preg_match('/<input type="hidden" name="RelayState" value="([^"]*)" \\/>/', $page, $matches)) { - $params['RelayState'] = html_entity_decode($matches[1]); - } - - if(preg_match('/<input type="hidden" name="TARGET" value="([^"]*)" \\/>/', $page, $matches)) { - $params['TARGET'] = html_entity_decode($matches[1]); - } - - - return array('url' => $url, 'post' => $params); -} - - -/** - * This function parses a simpleSAMLphp HTTP-POST debug page. - * - * @param $page The content of the page. - * @return FALSE if $page isn't a HTTP-POST page. If it is a HTTP-POST page, it will return an associative array with - * the post destination in 'url' and the post arguments as an associative array in 'post'. - */ -function parseSimpleSamlHttpPostDebug($page) { - if(strpos($page, '<title>SAML Response Debug-mode</title>') === FALSE) { - return FALSE; - } - - if(!preg_match('/<form method="post" action="([^"]*)">/', $page, $matches)) { - echo('Invalid simpleSAMLphp HTTP-POST page. Missing form target.' . "\n"); - return FALSE; - } - $url = html_entity_decode($matches[1]); - - if(!preg_match('/<input type="hidden" name="SAMLResponse" value="([^"]*)" \\/>/', $page, $matches)) { - echo('Invalid simpleSAMLphp HTTP-POST page. Missing SAMLResponse.' . "\n"); - return FALSE; - } - $samlResponse = html_entity_decode($matches[1]); - - if(!preg_match('/<input type="hidden" name="RelayState" value="([^"]*)" \\/>/', $page, $matches)) { - echo('Invalid simpleSAMLphp HTTP-POST page. Missing RelayState.' . "\n"); - return FALSE; - } - $relayState = html_entity_decode($matches[1]); - - - return array('url' => $url, 'post' => array('SAMLResponse' => $samlResponse, 'RelayState' => $relayState)); -} - - -/** - * This function parses a simpleSAMLphp login page. - * - * @param $curl The curl handle the page was fetched with. - * @param $page The content of the login page. - * @return FALSE if $page isn't a login page, associative array with the destination url in 'url' and the relaystate in 'relaystate'. - */ -function parseSimpleSamlLoginPage($curl, $page) { - - $url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); - - $pos = strpos($url, '?'); - if($pos === FALSE) { - echo('Unexpected login page url: ' . $url); - return FALSE; - } - $url = substr($url, 0, $pos + 1); - - - if(!preg_match('/<input type="hidden" name="RelayState" value="([^"]*)" \\/>/', $page, $matches)) { - echo('Could not find relaystate in simpleSAMLphp login page.' . "\n"); - return FALSE; - } - - $relaystate = $matches[1]; - $relaystate = html_entity_decode($relaystate); - - return array('url' => $url, 'relaystate' => $relaystate); -} - - -/** - * This function parses a FEIDE login page. - * - * @param $curl The curl handle the page was fetched with. - * @param $page The content of the login page. - * @return FALSE if $page isn't a login page, associative array with the destination url in 'url' and the goto attribute in 'goto'. - */ -function parseFeideLoginPage($curl, $page) { - - if(strpos($page, '<title> Moria-innlogging </title>') === FALSE) { - echo('Not a FEIDE login page.' . "\n"); - return FALSE; - } - - $url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); - - $pos = strpos($url, '/amserver/UI/Login'); - if($pos === FALSE) { - echo('Unexpected login page url: ' . $url); - return FALSE; - } - $url = substr($url, 0, $pos) . '/amserver/UI/Login'; - - if(!preg_match('/<input type="hidden" name="goto" value="([^"]*)"\\/>/', $page, $matches)) { - echo('Could not find goto in FEIDE login page.' . "\n"); - return FALSE; - } - - $goto = $matches[1]; - $goto = html_entity_decode($goto); - - return array('url' => $url, 'goto' => $goto); -} - - -/** - * This function parses the FEIDE HTTP-POST page. - * - * @param $page The content of the page. - * @return FALSE if $page isn't a HTTP-POST page. If it is a HTTP-POST page, it will return an associative array with - * the post destination in 'url' and the post arguments as an associative array in 'post'. - */ -function parseFeideHttpPost($page) { - - if(strpos($page, '<TITLE>Access rights validated</TITLE>') === FALSE) { - return FALSE; - } - - if(!preg_match('/<FORM METHOD="POST" ACTION="([^"]*)">/', $page, $matches)) { - echo('Invalid FEIDE HTTP-POST page. Missing form target.' . "\n"); - return FALSE; - } - $url = html_entity_decode($matches[1]); - - if(!preg_match('/<INPUT TYPE="HIDDEN" NAME="SAMLResponse" VALUE="([^"]*)">/m', $page, $matches)) { - echo('Invalid FEIDE HTTP-POST page. Missing SAMLResponse.' . "\n"); - return FALSE; - } - $samlResponse = html_entity_decode($matches[1]); - - if(!preg_match('/<INPUT TYPE="HIDDEN" NAME="RelayState" VALUE="([^"]*)">/', $page, $matches)) { - echo('Invalid FEIDE HTTP-POST page. Missing RelayState.' . "\n"); - return FALSE; - } - $relayState = html_entity_decode($matches[1]); - - - return array('url' => $url, 'post' => array('SAMLResponse' => $samlResponse, 'RelayState' => $relayState)); -} - - -/** - * This function handles simpleSAMLphp debug pages, and follows redirects in them. - * - * @param $curl The curl handle we should use. - * @param $page The page which may be a simpleSAMLphp debug page. $page may be FALSE, in which case this function - * will return FALSE. - * @return $page if $page isn't a debug page, or the result from following the redirect if not. - * FALSE will be returned on failure. - */ -function skipDebugPage($curl, $page) { - - if($page === FALSE) { - return FALSE; - } - - $url = parseSimpleSamlHttpRedirectDebug($page); - if($url !== FALSE) { - $page = urlGet($curl, $url); - } - - return $page; -} - - -/** - * This function contacts the test page to initialize SSO. - * - * @param $test The test we are running. - * @param $curl The curl handle we should use. - * @return TRUE on success, FALSE on failure. - */ -function initSSO($test, $curl) { - if(!array_key_exists('url', $test)) { - echo('Missing required attribute url in test.' . "\n"); - return FALSE; - } - - $params = array('op' => 'login'); - if(array_key_exists('idp', $test)) { - $params['idp'] = $test['idp']; - } - - /* Add the protocol which simpleSAMLphp should use to authenticate. */ - if(array_key_exists('protocol', $test)) { - $params['protocol'] = $test['protocol']; - } - - /* Add attribute tests. */ - if(array_key_exists('attributes', $test)) { - $i = 0; - foreach($test['attributes'] as $name => $values) { - if(!is_array($values)) { - $values = array($values); - } - - foreach($values as $value) { - $params['attr_test_' . $i] = $name . ':' . $value; - $i++; - } - } - } - - echo('Initializing SSO.' . "\n"); - $loginPage = urlGet($curl, $test['url'], $params); - if($loginPage === FALSE) { - echo('Failed to initialize SSO.' . "\n"); - return FALSE; - } - - /* Skip HTTP-REDIRECT debug page if it appears. */ - $loginPage = skipDebugPage($curl, $loginPage); - - return $loginPage; -} - - -/** - * This function handles login to a simpleSAMLphp login page. - * - * @param $test The current test. - * @param $curl The curl handle in use. - * @param $page The login page. - * @return FALSE on failure, or the resulting page on success. - */ -function doSimpleSamlLogin($test, $curl, $page) { - - if(!array_key_exists('username', $test)) { - echo('Missing username in test.' . "\n"); - return FALSE; - } - - if(!array_key_exists('password', $test)) { - echo('Missing password in test.' . "\n"); - return FALSE; - } - - $info = parseSimpleSamlLoginPage($curl, $page); - if($info === FALSE) { - return FALSE; - } - - $post = array(); - $post['username'] = $test['username']; - $post['password'] = $test['password']; - $post['RelayState'] = $info['relaystate']; - - $page = urlPost($curl, $info['url'], $post); - - - /* Follow HTTP-POST redirect. */ - $pi = parseSimpleSamlHttpPost($page); - if($pi === FALSE) { - echo($page); - echo('Didn\'t get a simpleSAMLphp post redirect page.' . "\n"); - return FALSE; - } - - $page = urlPost($curl, $pi['url'], $pi['post']); - - return $page; -} - - -/** - * This function handles login to the FEIDE login page. - * - * @param $test The current test. - * @param $curl The curl handle in use. - * @param $page The login page. - * @return FALSE on failure, or the resulting page on success. - */ -function doFeideLogin($test, $curl, $page) { - - if(!array_key_exists('username', $test)) { - echo('Missing username in test.' . "\n"); - return FALSE; - } - - if(!array_key_exists('password', $test)) { - echo('Missing password in test.' . "\n"); - return FALSE; - } - - if(!array_key_exists('organization', $test)) { - echo('Missing organization in test.' . "\n"); - return FALSE; - } - - - $info = parseFeideLoginPage($curl, $page); - if($info === FALSE) { - return FALSE; - } - - $post = array(); - $post['username'] = $test['username']; - $post['password'] = $test['password']; - $post['organization'] = $test['organization']; - $post['goto'] = $info['goto']; - - $page = urlPost($curl, $info['url'], $post); - - - /* Follow HTTP-POST redirect. */ - $pi = parseFeideHttpPost($page); - if($pi === FALSE) { - echo('Unable to parse FEIDE HTTP-POST redirect page.' . "\n"); - return FALSE; - } - - $page = urlPost($curl, $pi['url'], $pi['post']); - - return $page; -} - -/** - * This function logs in using the configuration of the given test. - * - * @param $test The current test. - * @param $curl The curl handle in use. - * @param $page The login page. - * @return FALSE on failure, or the resulting page on success. - */ -function doLogin($test, $curl, $page) { - if(!array_key_exists('logintype', $test)) { - echo('Missing option \'logintype\' in test configuration.' . "\n"); - return FALSE; - } - - switch($test['logintype']) { - case 'simplesaml': - return doSimpleSamlLogin($test, $curl, $page); - case 'feide': - return doFeideLogin($test, $curl, $page); - default: - echo('Unknown login type: ' . $test['logintype'] . "\n"); - echo($page); - return FALSE; - } -} - - -/** - * This function contacts the test page to initialize SSO. - * - * @param $test The test we are running. - * @param $curl The curl handle we should use. - * @return TRUE on success, FALSE on failure. - */ -function doLogout($test, $curl) { - if(!array_key_exists('url', $test)) { - echo('Missing required attribute url in test.' . "\n"); - return FALSE; - } - - $params = array('op' => 'logout'); - - $page = urlGet($curl, $test['url'], $params); - if($page === FALSE) { - echo('Failed to log out.' . "\n"); - return FALSE; - } - - /* Skip HTTP-REDIRECT debug pagess. */ - while(TRUE) { - $newPage = skipDebugPage($curl, $page); - if($newPage === $page) { - break; - } - $page = $newPage; - } - - return $page; -} - - -/** - * This function runs the specified test. - * - * @param $test Associative array with the test parameters. - * @return TRUE on success, FALSE on failure. - */ -function doTest($test) { - $curl = curlCreate(); - - $res = TRUE; - - /* Initialize SSO. */ - do { - $loginPage = initSSO($test, $curl); - if($loginPage === FALSE) { - $res = FALSE; - break; - } - - echo('Logging in.' . "\n"); - - $result = doLogin($test, $curl, $loginPage); - if($result !== "OK") { - if(is_string($result)) { - echo('Failed to log in. Result from SP: ' . $result . "\n"); - } else { - echo('Failed to log in.' . "\n"); - } - $res = FALSE; - break; - } - - echo('Logged in, attributes OK' . "\n"); - - if(array_key_exists('protocol', $test) && $test['protocol'] === 'shib13') { - echo('Shib13: Logout not implemented.' . "\n"); - break; - } - - echo('Logging out.' . "\n"); - - $result = doLogout($test, $curl); - if($result !== "OK") { - if(is_string($result)) { - echo('Failed to log out. Result from SP: ' . $result . "\n"); - } else { - echo('Failed to log out.' . "\n"); - } - $res = FALSE; - break; - } - - echo('Logged out.' . "\n"); - - } while(0); - - curl_close($curl); - - return $res; -} - - -$ret = 0; -/* Run the tests. */ -foreach($tests as $i => $test) { - echo('############################################################' . "\n"); - echo('Running test #' . ($i + 1) . '.' . "\n"); - - $res = doTest($test); - - if($res === FALSE) { - $ret = 1; - echo('Test #' . ($i + 1) . ' failed.' . "\n"); - } else { - echo('Test #' . ($i + 1) . ' succeeded.' . "\n"); - } -} -echo('############################################################' . "\n"); - -exit($ret); - -?> \ No newline at end of file diff --git a/config-templates/test.php b/config-templates/test.php deleted file mode 100644 index 63b1ba6f1917958e2644b80805a9b6635a80f45b..0000000000000000000000000000000000000000 --- a/config-templates/test.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/* - * This is the configuration file for the test script which can be found at - * bin/test.php. - * - */ - -/* Add a test towards the default IdP using the simpleSAMLphp login handler. */ -$tests[] = array( - - /* The full url to the admin/test.php page on the SP. */ - 'url' => 'https://example.org/simplesaml/admin/test.php', - - /* The username and password which should be used for logging in. ('simplesaml' login type) */ - 'username' => 'username', - 'password' => 'secretpassword', - - /* The type of login page we expect. */ - 'logintype' => 'simplesaml', - - /* Expected attributes in the result. */ - 'attributes' => array( - 'uid' => 'test', - ), - ); - - -/* Add a test towards the default IdP using the shib13 protocol. */ -$tests[] = array( - - /* The full url to the admin/test.php page on the SP. */ - 'url' => 'https://example.org/simplesaml/admin/test.php', - - /* The protocol we are going to test. */ - 'protocol' => 'shib13', - - /* The username and password which should be used for logging in. ('simplesaml' login type) */ - 'username' => 'username', - 'password' => 'secretpassword', - - /* The type of login page we expect. */ - 'logintype' => 'simplesaml', - - /* Expected attributes in the result. */ - 'attributes' => array( - 'uid' => 'test', - ), - ); - - -/* Add a test towards the specified IdP using the FEIDE login handler. */ -$tests[] = array( - - /* The full url to the admin/test.php page on the SP. */ - 'url' => 'https://example.org/simplesaml/admin/test.php', - - /* The idp we should test. */ - 'idp' => 'max.feide.no', - - /* The username, password and organization which should be used for logging in. ('feide' login type) */ - 'username' => 'username', - 'password' => 'secretpassword', - 'organization' => 'feide.no', - - /* The type of login page we expect. */ - 'logintype' => 'feide', - - /* Expected attributes in the result. */ - 'attributes' => array( - 'eduPersonAffiliation' => array( - 'employee', - 'staff', - 'student', - ), - ), - ); - diff --git a/www/admin/test.php b/www/admin/test.php deleted file mode 100644 index cea34faf24fb84688b7eccd847f337490f237cac..0000000000000000000000000000000000000000 --- a/www/admin/test.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php - -/** - * This file defines a webpage that can be used to test interaction between this SP and a selected IdP. - * - * Note: This page is deprecated in favor of the autotest module. It will be - * removed in a future version of simpleSAMLphp. - * - * It has several query parameters: - * - 'op': The operation. - * - 'login' Initialize login. - * - 'logout' Initialize logout. - * - 'testnosession': Used by the logout test. - * - 'idp': The entity id of the IdP we should log in to. Will use the default idp from the configuration - * if this parameter isn't given. - * - 'protocol': Which protocol to use. Can be 'saml2' or 'shib13'. The default is 'saml2'. - * - 'attr_test[_<number>]: Make sure that the returned attributes contains a this name-value-pair. The - * name-value-pair is given as <name>:<value>. - * - * Examples: - * - http://.../test.php?attr_test=cn:Test&attr_test_2=uid:test - * Attempt to contact the default IdP. Make sure that the IdP returns the attributes cn=Test and uid=test. - * - * - http://.../test.php?op=logout - * Attempt to log out. - * - * - http://.../test.php?idp=example.com&attr_test=cn:Test%20user - * Attempt to contact the idp with the entity id "example.com". Make sure that the idp returns - * the attribute cn="Test user". - * - * This page will print out "OK" on success or "ERROR: <message>" on failure. - */ - -require_once('../_include.php'); - -$config = SimpleSAML_Configuration::getInstance(); - -function error($message = 'Unknown error') { - header('Content-Type: text/plain'); - echo('ERROR: ' . $message); - exit; -} - - -if (array_key_exists('op', $_GET)) { - $op = $_GET['op']; -} else { - $op = 'login'; -} - -if (array_key_exists('idp', $_GET)) { - $idp = $_GET['idp']; -} else { - $idp = NULL; -} - -if (array_key_exists('protocol', $_GET)) { - $protocol = $_GET['protocol']; - if($protocol !== 'saml2' && $protocol !== 'shib13') { - error('Unknown protocol "' . $protocol . '".'); - } -} else { - $protocol = 'saml2'; -} - - -$attr_test = array(); - -foreach ($_GET as $k => $v) { - if(preg_match('/^attr_test(?:_\d+)?$/D', $k)) { - $pos = strpos($v, ':'); - if($pos === FALSE) { - error('Invalid attribute test: $v'); - } - - $name = substr($v, 0, $pos); - $value = substr($v, $pos + 1); - - $attr_test[] = array('name' => $name, 'value' => $value); - } -} - - - -if ($op === 'login') { - $session = SimpleSAML_Session::getInstance(); - - /* Initialize SSO if we aren't authenticated. */ - if (!$session->isValid($protocol) ) { - $params = array(); - $params['RelayState'] = SimpleSAML_Utilities::selfURL(); - if($idp) { - $params['idpentityid'] = $idp; - } - - if($protocol === 'saml2') { - $url = '/' . $config->getBaseURL() . 'saml2/sp/initSSO.php'; - } elseif($protocol === 'shib13') { - $url = '/' . $config->getBaseURL() . 'shib13/sp/initSSO.php'; - } else { - error('Unable to log in with protocol "' . $protocol . '".'); - } - - SimpleSAML_Utilities::redirect($url, $params); - } - - /* We are authenticated. Validate attributes. */ - - $attributes = $session->getAttributes(); - - foreach ($attr_test as $at) { - $name = $at['name']; - $value = $at['value']; - - if(!array_key_exists($name, $attributes)) { - error('No attribute with the name "' . $name . '".'); - } - - if(!in_array($value, $attributes[$name])) { - error('No attribute with the name "' . $name . '" and the value "' . $value . '".'); - } - } - -} elseif ($op === 'logout') { - $session = SimpleSAML_Session::getInstance(); - - if (!$session->isValid('saml2')) { - error('Not logged in.'); - } - - if ($protocol === 'saml2') { - $url = '/' . $config->getBaseURL() . 'saml2/sp/initSLO.php'; - } else { - error('Logout unsupported for protocol "' . $protocol . '".'); - } - - $relayState = SimpleSAML_Utilities::selfURLNoQuery() . '?op=testnosession'; - - - SimpleSAML_Utilities::redirect( - $url, - array('RelayState' => $relayState) - ); - -} elseif ($op === 'testnosession') { - $session = SimpleSAML_Session::getInstance(); - if ($session->isValid('saml2')) { - error('Still logged in.'); - } - -} else { - error('Unknown operation: "' . $op . '"'); -} - -header('Content-Type: text/plain'); - -echo 'OK'; - - -?> \ No newline at end of file