<?php | |
// Returns portfolio scraped live from directory.gov.au | |
// or null if can't find a portfolio | |
function agency2portfolio ($agency) { | |
static $cache = array(); | |
if (isset($cache[$agency])) { return $cache[$agency]; } | |
$c = curl_init('http://www.directory.gov.au/searchres.php'); | |
curl_setopt($c, CURLOPT_POST, true); | |
curl_setopt($c, CURLOPT_HEADER, false); | |
curl_setopt($c, CURLOPT_RETURNTRANSFER, true); | |
curl_setopt($c, CURLOPT_REFERER, 'http://www.directory.gov.au/adsearch.php'); | |
curl_setopt($c, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3'); | |
curl_setopt($c, CURLOPT_POSTFIELDS, array( | |
'advkeywordfield' => '', | |
'advorgunitfield' => $agency, | |
'advrolefield' => '', | |
'advsection' => 'All', | |
'advsurnamefield' => '', | |
'search' => 'Submit Query' | |
)); | |
$results = curl_exec($c); | |
if (preg_match('#<span\s+class="standardlinks"><a\s+href="([^"]+)">#smi', $results, $m)) { | |
$nextURL = $m[1]; | |
} else { | |
$cache[$agency] = false; return false; | |
} | |
curl_setopt($c, CURLOPT_URL, 'http://www.directory.gov.au' . $nextURL); | |
curl_setopt($c, CURLOPT_HTTPGET, true); | |
curl_setopt($c, CURLOPT_REFERER, 'http://www.directory.gov.au/searchres.php'); | |
$results = curl_exec($c); | |
if (preg_match('#portfolios:\s+([^<]+)#ims', $results, $m)) { | |
$cache[$agency] = $m[1]; return $m[1]; | |
} else { | |
$cache[$agency] = false; return false; | |
} | |
} | |
?> | |
<?php | <?php |
if (php_sapi_name() != "cli") { | |
include_once("../lib/common.inc.php"); | |
include_once ("../lib/common.inc.php"); | |
auth(); | |
$query = 'update contractnotice set "parentCN" = null where "parentCN" = \'0\''; | $query = 'update contractnotice set "parentCN" = null where "parentCN" = \'0\''; |
$result = $conn->prepare($query); | $result = $conn->prepare($query); |
$result->execute(); | $result->execute(); |
$query = 'update contractnotice set "childCN" = null where "childCN" = \'0\''; | $query = 'update contractnotice set "childCN" = null where "childCN" = \'0\''; |
$result = $conn->prepare($query); | $result = $conn->prepare($query); |
$result->execute(); | $result->execute(); |
$query = 'select "CNID","parentCN" from contractnotice where char_length("CNID") > 6 and "CNID" like \'%00_\' and "parentCN" is not null'; | $query = 'select "CNID","parentCN" from contractnotice where char_length("CNID") > 6 and "CNID" like \'%00_\' and "parentCN" is not null'; |
// "CNID" not like '%-A%' | // "CNID" not like '%-A%' |
//$query = 'select "CNID","parentCN" from contractnotice where char_length("CNID") > 8 and "CNID" like \'%00__\' and "parentCN" is not null'; | //$query = 'select "CNID","parentCN" from contractnotice where char_length("CNID") > 8 and "CNID" like \'%00__\' and "parentCN" is not null'; |
$result = $conn->prepare($query); | $result = $conn->prepare($query); |
$result->execute(); | $result->execute(); |
foreach ($result->fetchAll() as $record) { | foreach ($result->fetchAll() as $record) { |
$oldCN = $record['CNID']; | $oldCN = $record['CNID']; |
$parentCN = substr($oldCN, 0, -3); | $parentCN = substr($oldCN, 0, -3); |
//$parentCN = substr($oldCN, 0, -4); | //$parentCN = substr($oldCN, 0, -4); |
if ($parentCN == $record['parentCN']) { | if ($parentCN == $record['parentCN']) { |
$newCN = $parentCN . "-A" . substr($oldCN, -1); | $newCN = $parentCN . "-A" . substr($oldCN, -1); |
//$newCN = $parentCN . "-A" . substr($oldCN, -2); | //$newCN = $parentCN . "-A" . substr($oldCN, -2); |
$updateresult = $conn->exec('UPDATE contractnotice SET "CNID" = \'' . $newCN . '\' where "CNID" = \'' . $oldCN . '\';'); | $updateresult = $conn->exec('UPDATE contractnotice SET "CNID" = \'' . $newCN . '\' where "CNID" = \'' . $oldCN . '\';'); |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7) { | if ($errors[1] == 7) { |
echo "$oldCN => $newCN (from parent CN $parentCN) BUT already exists so deleting this record<br>\n"; | echo "$oldCN => $newCN (from parent CN $parentCN) BUT already exists so deleting this record<br>\n"; |
$updateresult = $conn->exec('delete from contractnotice where "CNID" = \'' . $oldCN . '\';'); | $updateresult = $conn->exec('delete from contractnotice where "CNID" = \'' . $oldCN . '\';'); |
}else if ($errors[1] == 0) { | }else if ($errors[1] == 0) { |
echo "$oldCN => $newCN (from parent CN $parentCN) <br>\n"; | echo "$oldCN => $newCN (from parent CN $parentCN) <br>\n"; |
}else | }else |
print_r($errors); | print_r($errors); |
} else { | } else { |
echo "parent CN unexpected - $oldCN doesn't look like child of {$record['parentCN']}, rather the ID suggests child of $parentCN <br>\n"; | echo "parent CN unexpected - $oldCN doesn't look like child of {$record['parentCN']}, rather the ID suggests child of $parentCN <br>\n"; |
} | } |
} | } |
} | |
<?php | <?php |
if (php_sapi_name() != "cli") { | |
include_once ("../lib/common.inc.php"); | include_once ("../lib/common.inc.php"); |
auth(); | |
$contractNoticeFields = array( | $contractNoticeFields = array( |
"importFile", | "importFile", |
"agencyName", | "agencyName", |
"parentCN", | "parentCN", |
"CNID", | "CNID", |
"publishDate", | "publishDate", |
"amendDate", | "amendDate", |
"contractStart", | "contractStart", |
"contractEnd", | "contractEnd", |
"value", | "value", |
"description", | "description", |
"agencyID", | "agencyID", |
"category", | "category", |
"procurementMethod", | "procurementMethod", |
"atmID", | "atmID", |
"SONID", | "SONID", |
"confidentialityContract", | "confidentialityContract", |
"confidentialityContractReason", | "confidentialityContractReason", |
"confidentialityOutputs", | "confidentialityOutputs", |
"confidentialityOutputsReason", | "confidentialityOutputsReason", |
"consultancy", | "consultancy", |
"consultancyReason", | "consultancyReason", |
"amendmentReason", | "amendmentReason", |
"supplierName", | "supplierName", |
"supplierAddress", | "supplierAddress", |
"supplierCity", | "supplierCity", |
"supplierPostcode", | "supplierPostcode", |
"supplierCountry", | "supplierCountry", |
"supplierABNExempt", | "supplierABNExempt", |
"supplierABN", | "supplierABN", |
"contactBranch", | "contactBranch", |
"contactDivision", | "contactDivision", |
"contactPostcode" | "contactPostcode" |
); | ); |
$contractNoticeInsertQ = 'INSERT INTO contractnotice ("' . implode('" , "', $contractNoticeFields) . '") VALUES ( '; | $contractNoticeInsertQ = 'INSERT INTO contractnotice ("' . implode('" , "', $contractNoticeFields) . '") VALUES ( '; |
foreach ($contractNoticeFields as $key => $f) { | foreach ($contractNoticeFields as $key => $f) { |
$contractNoticeInsertQ.= ($key == 0 ? "" : ", ") . "?"; | $contractNoticeInsertQ.= ($key == 0 ? "" : ", ") . "?"; |
} | } |
$contractNoticeInsertQ.= ");"; | $contractNoticeInsertQ.= ");"; |
$contractNoticeInsertQ = $conn->prepare($contractNoticeInsertQ); | $contractNoticeInsertQ = $conn->prepare($contractNoticeInsertQ); |
function processFile($fpath) { | function processFile($fpath) { |
global $conn, $contractNoticeFields, $contractNoticeInsertQ; | global $conn, $contractNoticeFields, $contractNoticeInsertQ; |
$row = 1; | $row = 1; |
$handle = fopen($fpath, "r"); | $handle = fopen($fpath, "r"); |
//"t" mode string translates windows line breaks to unix | //"t" mode string translates windows line breaks to unix |
$datamapping0712 = array( | $datamapping0712 = array( |
"Agency" => "agencyName", | "Agency" => "agencyName", |
"Parent CN ID" => "parentCN", | "Parent CN ID" => "parentCN", |
"CN ID" => "CNID", | "CN ID" => "CNID", |
"Publish Date" => "publishDate", | "Publish Date" => "publishDate", |
"Amendment Date" => "amendDate", | "Amendment Date" => "amendDate", |
"Status" => "", | "Status" => "", |
"StartDate" => "contractStart", | "StartDate" => "contractStart", |
"EndDate" => "contractEnd", | "EndDate" => "contractEnd", |
"Value" => "value", | "Value" => "value", |
"Description" => "description", | "Description" => "description", |
"Agency Ref Id" => "agencyID", | "Agency Ref Id" => "agencyID", |
"Agency Ref. ID" => "agencyID", | "Agency Ref. ID" => "agencyID", |
"Category" => "category", | "Category" => "category", |
"Procurement Method" => "procurementMethod", | "Procurement Method" => "procurementMethod", |
"ATM ID" => "atmID", | "ATM ID" => "atmID", |
"SON ID" => "SONID", | "SON ID" => "SONID", |
"Confidentiality - Contract" => "confidentialityContract", | "Confidentiality - Contract" => "confidentialityContract", |
"Confidentiality - Contract Reason(s)" => "confidentialityContractReason", | "Confidentiality - Contract Reason(s)" => "confidentialityContractReason", |
"Confidentiality - Outputs" => "confidentialityOutputs", | "Confidentiality - Outputs" => "confidentialityOutputs", |
"Confidentiality - Outputs Reason(s)" => "confidentialityOutputsReason", | "Confidentiality - Outputs Reason(s)" => "confidentialityOutputsReason", |
"Consultancy" => "consultancy", | "Consultancy" => "consultancy", |
"Consultancy Reason(s)" => "consultancyReason", | "Consultancy Reason(s)" => "consultancyReason", |
"Amendment Reason" => "amendmentReason", | "Amendment Reason" => "amendmentReason", |
"Supplier Name" => "supplierName", | "Supplier Name" => "supplierName", |
"Supplier Address" => "supplierAddress", | "Supplier Address" => "supplierAddress", |
"Supplier City" => "supplierCity", | "Supplier City" => "supplierCity", |
"Supplier Postcode" => "supplierPostcode", | "Supplier Postcode" => "supplierPostcode", |
"Supplier Country" => "supplierCountry", | "Supplier Country" => "supplierCountry", |
"Supplier ABNExempt" => "supplierABNExempt", | "Supplier ABNExempt" => "supplierABNExempt", |
"Supplier ABN" => "supplierABN", | "Supplier ABN" => "supplierABN", |
"Agency Branch" => "contactBranch", | "Agency Branch" => "contactBranch", |
"Agency Divison" => "contactDivision", | "Agency Divison" => "contactDivision", |
"Agency Postcode" => "contactPostcode", | "Agency Postcode" => "contactPostcode", |
"" => "" | "" => "" |
); | ); |
$headers; | $headers; |
while (($data = fgetcsv($handle, 1000, "\t")) !== false) { | while (($data = fgetcsv($handle, 1000, "\t")) !== false) { |
$num = count($data); | $num = count($data); |
if ($row == 3) { | if ($row == 3) { |
$headers = $data; | $headers = $data; |
} elseif ($row > 3) { | } elseif ($row > 3) { |
if ($num > count($datamapping0712)) { | if ($num > count($datamapping0712)) { |
die("<font color=red>Error in data import; data mapping fields out of bounds or changed</font><br>" . $fname . print_r($data)); | die("<font color=red>Error in data import; data mapping fields out of bounds or changed</font><br>" . $fname . print_r($data)); |
} | } |
$contractNoticeInsert = Array(); | $contractNoticeInsert = Array(); |
$supplierInsert = Array(); | $supplierInsert = Array(); |
$agencyInsert = Array(); | $agencyInsert = Array(); |
$contractNoticeInsert[] = $fpath; | $contractNoticeInsert[] = $fpath; |
$keys = array_keys($datamapping0712); | $keys = array_keys($datamapping0712); |
for ($c = 0; $c < $num; $c++) { | for ($c = 0; $c < $num; $c++) { |
$data[$c] = trim($data[$c], "="); | $data[$c] = trim($data[$c], "="); |
$data[$c] = trim($data[$c], "\""); | $data[$c] = trim($data[$c], "\""); |
if (in_array(($datamapping0712[$headers[$c]]), $contractNoticeFields)) { | if (in_array(($datamapping0712[$headers[$c]]), $contractNoticeFields)) { |
if (($datamapping0712[$headers[$c]]) == "parentCN" || ($datamapping0712[$headers[$c]]) == "CNID") { | if (($datamapping0712[$headers[$c]]) == "parentCN" || ($datamapping0712[$headers[$c]]) == "CNID") { |
$data[$c] = substr($data[$c], 2); // take off the "CN" prefix | $data[$c] = substr($data[$c], 2); // take off the "CN" prefix |
if ($data[$c] > 0 && $data[$c] != '0') { | if ($data[$c] > 0 && $data[$c] != '0') { |
$contractNoticeInsert[] = $data[$c]; | $contractNoticeInsert[] = $data[$c]; |
} else { | } else { |
$contractNoticeInsert[] = null; | $contractNoticeInsert[] = null; |
} | } |
} elseif (($datamapping0712[$headers[$c]]) == "supplierABN") { | } elseif (($datamapping0712[$headers[$c]]) == "supplierABN") { |
if ($data[$c] > 0 && $data[$c] != '0') { | if ($data[$c] > 0 && $data[$c] != '0') { |
$contractNoticeInsert[] = $data[$c]; | $contractNoticeInsert[] = $data[$c]; |
} else { | } else { |
$contractNoticeInsert[] = null; | $contractNoticeInsert[] = null; |
} | } |
} elseif (($datamapping0712[$headers[$c]]) == "amendDate" || ($datamapping0712[$headers[$c]]) == "publishDate" || ($datamapping0712[$headers[$c]]) == "contractStart" || ($datamapping0712[$headers[$c]]) == "contractEnd") { | } elseif (($datamapping0712[$headers[$c]]) == "amendDate" || ($datamapping0712[$headers[$c]]) == "publishDate" || ($datamapping0712[$headers[$c]]) == "contractStart" || ($datamapping0712[$headers[$c]]) == "contractEnd") { |
$contractNoticeInsert[] = date('Y-m-d H:i:s', strtotime($data[$c])); | $contractNoticeInsert[] = date('Y-m-d H:i:s', strtotime($data[$c])); |
} else { | } else { |
if (strstr("\" =", $data[$c] > 0)) { | if (strstr("\" =", $data[$c] > 0)) { |
die("Invalid Description field" . $contractNoticeInsert); | die("Invalid Description field" . $contractNoticeInsert); |
} | } |
$colvalue = preg_replace('/[^[:print:]]/', '', utf8_encode($data[$c])); | $colvalue = preg_replace('/[^[:print:]]/', '', utf8_encode($data[$c])); |
$contractNoticeInsert[] = $colvalue; | $contractNoticeInsert[] = $colvalue; |
} | } |
} | } |
} | } |
flush(); | flush(); |
$contractNoticeInsertQ->execute($contractNoticeInsert); | $contractNoticeInsertQ->execute($contractNoticeInsert); |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { | if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { |
} elseif ($errors[1] == 0) { | } elseif ($errors[1] == 0) { |
$success++; | $success++; |
} else { | } else { |
foreach ($contractNoticeFields as $key => $cnf) { | foreach ($contractNoticeFields as $key => $cnf) { |
echo var_dump($contractNoticeInsert[$key]) . $cnf . "<br>"; | echo var_dump($contractNoticeInsert[$key]) . $cnf . "<br>"; |
} | } |
echo $data[2] . " failed CN insert.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; | echo $data[2] . " failed CN insert.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; |
} | } |
flush(); | flush(); |
//echo "<hr>\n"; | //echo "<hr>\n"; |
} | } |
$row++; | $row++; |
} | } |
fclose($handle); | fclose($handle); |
$contractNoticeInsertQ->closeCursor(); | $contractNoticeInsertQ->closeCursor(); |
return $success; | return $success; |
} | } |
$path = 'data/'; | $path = 'data/'; |
if ($_REQUEST["fname"] == "") { | if ($_REQUEST["fname"] == "") { |
echo "Get files from: https://www.tenders.gov.au/?event=public.reports.list<br>"; | echo "Get files from: https://www.tenders.gov.au/?event=public.reports.list<br>"; |
$dhandle = opendir($path); | $dhandle = opendir($path); |
// define an array to hold the files | // define an array to hold the files |
$files = array(); | $files = array(); |
if ($dhandle) { | if ($dhandle) { |
// loop through all of the files | // loop through all of the files |
while (false !== ($fname = readdir($dhandle))) { | while (false !== ($fname = readdir($dhandle))) { |
if (($fname != '.') && ($fname != '..')) { | if (($fname != '.') && ($fname != '..')) { |
$files[date("c", filemtime($path . $fname)) . md5($fname)] = $fname; | $files[date("c", filemtime($path . $fname)) . md5($fname)] = $fname; |
} | } |
} | } |
} | } |
ksort($files); | ksort($files); |
foreach ($files as $date => $fname) { | foreach ($files as $date => $fname) { |
echo "<a href=\"import.php?fname=$fname\">$fname</a> " . filesize($path . $fname) . " " . $date . "<br/>"; | echo "<a href=\"import.php?fname=$fname\">$fname</a> " . filesize($path . $fname) . " " . $date . "<br/>"; |
} | } |
} else { | } else { |
$success = 0; | $success = 0; |
$fname = $_REQUEST["fname"]; | $fname = $_REQUEST["fname"]; |
echo " ============== $fname ============== <br>"; | echo " ============== $fname ============== <br>"; |
flush(); | flush(); |
$success+= processFile($path . $fname, "contractnotice"); | $success+= processFile($path . $fname, "contractnotice"); |
$success+= processFile($path . $fname, "agency"); | $success+= processFile($path . $fname, "agency"); |
$success+= processFile($path . $fname, "supplier"); | $success+= processFile($path . $fname, "supplier"); |
echo "<br> $success records successfully created"; | echo "<br> $success records successfully created"; |
flush(); | flush(); |
// run post import data processing | // run post import data processing |
// | |
$dbConn->exec("update datasets set \"lastUpdated\" = NOW() where title = 'Contract Notices'"); | |
// cn | // cn |
echo "link amend<br>"; | echo "link amend<br>"; |
include ("linkAmendments.php"); | include ("linkAmendments.php"); |
echo "update UNSPSC<br>"; | echo "update UNSPSC<br>"; |
include ("updateUNSPSC.php"); | include ("updateUNSPSC.php"); |
// agency | // agency |
//include ("setAgencyStatus.php"); | //include ("setAgencyStatus.php"); |
//include ("setAgencyURLABN.php"); | //include ("setAgencyURLABN.php"); |
} | } |
} | |
?> | ?> |
<?php | <?php |
include_once("../lib/common.inc.php"); | if (php_sapi_name() != "cli") { |
include_once ("../lib/common.inc.php"); | |
auth(); | |
// display existing | // display existing |
$unspscresult= $conn->prepare('select * from "UNSPSCcategories";'); | $unspscresult= $conn->prepare('select * from "UNSPSCcategories";'); |
$unspscresult->execute(); | $unspscresult->execute(); |
foreach ($unspscresult->fetchAll() as $row) { | foreach ($unspscresult->fetchAll() as $row) { |
$unspsc[$row['UNSPSC']] = $row['Title']; | $unspsc[$row['UNSPSC']] = $row['Title']; |
} | } |
$catsresult = $conn->prepare('SELECT substr( "categoryUNSPSC"::text, 0, 2 ) as cat , SUM( "value" ) as value | $catsresult = $conn->prepare('SELECT substr( "categoryUNSPSC"::text, 0, 2 ) as cat , SUM( "value" ) as value |
FROM contractnotice | FROM contractnotice |
GROUP BY cat ;'); | GROUP BY cat ;'); |
echo "<table>"; | echo "<table>"; |
$catsresult->execute(); | $catsresult->execute(); |
foreach ($catsresult->fetchAll() as $row) { | foreach ($catsresult->fetchAll() as $row) { |
$catName = $unspsc[$row['cat']."0000000"].$row['cat']; | $catName = $unspsc[$row['cat']."0000000"].$row['cat']; |
if ($row['cat'] == "") $catName = "null"; | if ($row['cat'] == "") $catName = "null"; |
echo "<tr><td>$catName</td><td>".$row['value']."</td></tr>"; | echo "<tr><td>$catName</td><td>".$row['value']."</td></tr>"; |
} | } |
// import new from file | // import new from file |
$success = 0; | $success = 0; |
$fname = "UNSPSC_ECCMA_V13.2_UNDP_V7.csv"; | $fname = "UNSPSC_ECCMA_V13.2_UNDP_V7.csv"; |
echo " ============== $fname ============== <br>"; | echo " ============== $fname ============== <br>"; |
flush(); | flush(); |
$row = 1; | $row = 1; |
$handle = fopen($path . $fname, "r"); | $handle = fopen($path . $fname, "r"); |
$headers; | $headers; |
while (($data = fgetcsv($handle, 1000)) !== false) { | while (($data = fgetcsv($handle, 1000)) !== false) { |
$num = count($data); | $num = count($data); |
if ($row == 3) { | if ($row == 3) { |
$headers = $data; | $headers = $data; |
} elseif ($row > 3) { | } elseif ($row > 3) { |
//print_r($data); | //print_r($data); |
$query = 'insert into "UNSPSCcategories" values(".$data[1].","$data[2]");'; | $query = 'insert into "UNSPSCcategories" values(".$data[1].","$data[2]");'; |
//echo $query."<br>\n"; | //echo $query."<br>\n"; |
$conn->exec($query); | $conn->exec($query); |
flush(); | flush(); |
//echo "<hr>\n"; | //echo "<hr>\n"; |
} | } |
$row++; | $row++; |
} | } |
echo "<br> $success records successfully created"; | echo "<br> $success records successfully created"; |
flush(); | flush(); |
fclose($handle); | fclose($handle); |
} | |
?> | ?> |
<?php | <?php |
if (php_sapi_name() != "cli") { | |
include_once ("../lib/common.inc.php"); | include_once ("../lib/common.inc.php"); |
auth(); | |
$query = 'update contractnotice set "parentCN" = null where "parentCN" = \'0\''; | $query = 'update contractnotice set "parentCN" = null where "parentCN" = \'0\''; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
$query = 'update contractnotice set "supplierABN" = null where "supplierABN" = \'0\''; | $query = 'update contractnotice set "supplierABN" = null where "supplierABN" = \'0\''; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
$query = 'SELECT c."CNID",c."parentCN",p."childCN" FROM contractnotice as c LEFT OUTER JOIN contractnotice as p on c."parentCN" = p."CNID" | $query = 'SELECT c."CNID",c."parentCN",p."childCN" FROM contractnotice as c LEFT OUTER JOIN contractnotice as p on c."parentCN" = p."CNID" |
WHERE | WHERE |
c."parentCN" IS NOT NULL AND p."childCN" IS NULL '; | c."parentCN" IS NOT NULL AND p."childCN" IS NULL '; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
if ($row['parentCN'] != 0) { | if ($row['parentCN'] != 0) { |
$conn->exec('UPDATE contractnotice SET "childCN" = \'' . | $conn->exec('UPDATE contractnotice SET "childCN" = \'' . |
$row['CNID'] . '\' where "CNID" = \'' . | $row['CNID'] . '\' where "CNID" = \'' . |
$row['parentCN'] . '\';'); | $row['parentCN'] . '\';'); |
echo 'UPDATE contractnotice SET "childCN" = \'' . | echo 'UPDATE contractnotice SET "childCN" = \'' . |
$row['CNID'] . '\' where "CNID" = \'' . | $row['CNID'] . '\' where "CNID" = \'' . |
$row['parentCN'] . '\';'; | $row['parentCN'] . '\';'; |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7 || $errors[1] ==0) | if ($errors[1] == 7 || $errors[1] ==0) |
echo $row['CNID'] . " linked to parent " . $row['parentCN'] . | echo $row['CNID'] . " linked to parent " . $row['parentCN'] . |
"<br>\n"; | "<br>\n"; |
else print_r($errors); | else print_r($errors); |
} | } |
} | } |
// also need to eliminate CN 100528/100529 - check for double parent CNs with no childCN, latest sequent CN id keeps childCN = 0 | // also need to eliminate CN 100528/100529 - check for double parent CNs with no childCN, latest sequent CN id keeps childCN = 0 |
$query = 'SELECT "parentCN", array_agg("CNID"), count(*) from contractnotice WHERE "parentCN" IN | $query = 'SELECT "parentCN", array_agg("CNID"), count(*) from contractnotice WHERE "parentCN" IN |
( | ( |
SELECT "parentCN" | SELECT "parentCN" |
FROM contractnotice | FROM contractnotice |
GROUP BY "parentCN" | GROUP BY "parentCN" |
HAVING COUNT(*) > 1 | HAVING COUNT(*) > 1 |
AND "parentCN" IS NOT NULL | AND "parentCN" IS NOT NULL |
) | ) |
AND "childCN" IS NULL | AND "childCN" IS NULL |
GROUP BY "parentCN" having count(*) > 1'; | GROUP BY "parentCN" having count(*) > 1'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
$cnids = explode(",",str_replace(Array("{","}"),"",$row['array_agg'])); | $cnids = explode(",",str_replace(Array("{","}"),"",$row['array_agg'])); |
$last_cnid = array_pop($cnids); | $last_cnid = array_pop($cnids); |
foreach ($cnids as $cnid) { | foreach ($cnids as $cnid) { |
$conn->exec('UPDATE contractnotice SET "childCN" = \'' . | $conn->exec('UPDATE contractnotice SET "childCN" = \'' . |
$last_cnid . '\' where "CNID" = \'' . | $last_cnid . '\' where "CNID" = \'' . |
$cnid . '\';'); | $cnid . '\';'); |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7 || $errors[1] ==0) | if ($errors[1] == 7 || $errors[1] ==0) |
echo $cnid . " linked to latest child " . $last_cnid . | echo $cnid . " linked to latest child " . $last_cnid . |
"<br>\n"; | "<br>\n"; |
else print_r($errors); | else print_r($errors); |
} | } |
} | } |
} | |
?> | ?> |
<?php | <?php |
include_once ("../../lib/common.inc.php"); | if (php_sapi_name() == "cli") { |
include_once ("../../lib/common.inc.php"); | |
function processFile($fpath, $tablename) | function processFile($fpath, $tablename) |
{ | { |
global $conn; | global $conn; |
echo " ============== $fpath ============== <br>"; | echo " ============== $fpath ============== <br>"; |
flush(); | flush(); |
$row = 1; | $row = 1; |
$success = 0; | $success = 0; |
$dupes = 0; | $dupes = 0; |
$handle = fopen($fpath, "r"); | $handle = fopen($fpath, "r"); |
//"t" mode string translates windows line breaks to unix | //"t" mode string translates windows line breaks to unix |
$datamapping0507 = array( | $datamapping0507 = array( |
"Agency" => "agencyName", | "Agency" => "agencyName", |
"CN ID" => "CNID", | "CN ID" => "CNID", |
"Publish Date" => "publishDate", | "Publish Date" => "publishDate", |
"Contract Start Date" => "contractStart", | "Contract Start Date" => "contractStart", |
"Contract End Date" => "contractEnd", | "Contract End Date" => "contractEnd", |
"Value (AUD)" => "value", | "Value (AUD)" => "value", |
"Title" => "description", | "Title" => "description", |
"Category" => "category", | "Category" => "category", |
"ATM ID" => "atmID", | "ATM ID" => "atmID", |
"Supplier Name" => "supplierName", | "Supplier Name" => "supplierName", |
"LastUpdated" => "amendDate", | "LastUpdated" => "amendDate", |
"" => "" | "" => "" |
); | ); |
$headers; | $headers; |
$contractNoticeFields = array( | $contractNoticeFields = array( |
"importFile", | "importFile", |
"CNID", | "CNID", |
"description", | "description", |
"agencyName", | "agencyName", |
"publishDate", | "publishDate", |
"category", | "category", |
"contractStart", | "contractStart", |
"contractEnd", | "contractEnd", |
"value", | "value", |
"atmID", | "atmID", |
"supplierName", | "supplierName", |
"amendDate" | "amendDate" |
); | ); |
if ($tablename == "contractnotice") { | if ($tablename == "contractnotice") { |
$contractNoticeInsertQ = 'INSERT INTO contractnotice ("' . implode('" , "', $contractNoticeFields) . '") VALUES ( '; | $contractNoticeInsertQ = 'INSERT INTO contractnotice ("' . implode('" , "', $contractNoticeFields) . '") VALUES ( '; |
foreach ($contractNoticeFields as $key => $f) { | foreach ($contractNoticeFields as $key => $f) { |
$contractNoticeInsertQ.= ($key == 0 ? "" : ", ") . "?"; | $contractNoticeInsertQ.= ($key == 0 ? "" : ", ") . "?"; |
} | } |
$contractNoticeInsertQ.= ");"; | $contractNoticeInsertQ.= ");"; |
$contractNoticeInsertQ = $conn->prepare($contractNoticeInsertQ); | $contractNoticeInsertQ = $conn->prepare($contractNoticeInsertQ); |
} | } |
while (($data = fgetcsv($handle, 1000, "\t")) !== false) { | while (($data = fgetcsv($handle, 1000, "\t")) !== false) { |
$num = count($data); | $num = count($data); |
if ($row == 3) { | if ($row == 3) { |
$headers = $data; | $headers = $data; |
} | } |
elseif ($row > 3) { | elseif ($row > 3) { |
if ($num > count($datamapping0507)) { | if ($num > count($datamapping0507)) { |
die("<font color=red>Error in data import; data mapping fields out of bounds or changed</font><br>" . $fname . "data:" .$num. print_r($data ,true). "mapping:" . count($datamapping0507). print_r($datamapping0507 ,true)); | die("<font color=red>Error in data import; data mapping fields out of bounds or changed</font><br>" . $fname . "data:" .$num. print_r($data ,true). "mapping:" . count($datamapping0507). print_r($datamapping0507 ,true)); |
} | } |
$contractNoticeInsert = Array(); | $contractNoticeInsert = Array(); |
$contractNoticeInsert[] = $fpath; | $contractNoticeInsert[] = $fpath; |
$keys = array_keys($datamapping0507); | $keys = array_keys($datamapping0507); |
for ($c = 0; $c < $num; $c++) { | for ($c = 0; $c < $num; $c++) { |
$data[$c] = trim($data[$c], "="); | $data[$c] = trim($data[$c], "="); |
$data[$c] = trim($data[$c], "\""); | $data[$c] = trim($data[$c], "\""); |
if ($tablename == "contractnotice") { | if ($tablename == "contractnotice") { |
if (in_array(($datamapping0507[$headers[$c]]) , $contractNoticeFields)) { | if (in_array(($datamapping0507[$headers[$c]]) , $contractNoticeFields)) { |
if (($datamapping0507[$headers[$c]]) == "parentCN" || ($datamapping0507[$headers[$c]]) == "CNID") { | if (($datamapping0507[$headers[$c]]) == "parentCN" || ($datamapping0507[$headers[$c]]) == "CNID") { |
$data[$c] = substr($data[$c], 2); // take off the "CN" prefix | $data[$c] = substr($data[$c], 2); // take off the "CN" prefix |
if (!is_numeric($data[$c]) && $data[$c] != "") die($data[$c] . " is not numeric"); | if (!is_numeric($data[$c]) && $data[$c] != "") die($data[$c] . " is not numeric"); |
if ($data[$c] > 0) { | if ($data[$c] > 0) { |
$contractNoticeInsert[] = $data[$c]; | $contractNoticeInsert[] = $data[$c]; |
} | } |
else { | else { |
$contractNoticeInsert[] = 0; | $contractNoticeInsert[] = 0; |
} | } |
} | } |
elseif (($datamapping0507[$headers[$c]]) == "supplierABN") { | elseif (($datamapping0507[$headers[$c]]) == "supplierABN") { |
if ($data[$c] > 0) { | if ($data[$c] > 0) { |
$contractNoticeInsert[] = $data[$c]; | $contractNoticeInsert[] = $data[$c]; |
} | } |
else { | else { |
$contractNoticeInsert[] = null; | $contractNoticeInsert[] = null; |
} | } |
} | } |
elseif (($datamapping0507[$headers[$c]]) == "amendDate" || ($datamapping0507[$headers[$c]]) == "publishDate" || ($datamapping0507[$headers[$c]]) == "contractStart" || ($datamapping0507[$headers[$c]]) == "contractEnd") { | elseif (($datamapping0507[$headers[$c]]) == "amendDate" || ($datamapping0507[$headers[$c]]) == "publishDate" || ($datamapping0507[$headers[$c]]) == "contractStart" || ($datamapping0507[$headers[$c]]) == "contractEnd") { |
$contractNoticeInsert[] = date('Y-m-d H:i:s', strtotime($data[$c])); | $contractNoticeInsert[] = date('Y-m-d H:i:s', strtotime($data[$c])); |
} | } |
else { | else { |
if (strstr("\" =", $data[$c] > 0)) { | if (strstr("\" =", $data[$c] > 0)) { |
die("Invalid Description field" . $contractNoticeInsert); | die("Invalid Description field" . $contractNoticeInsert); |
} | } |
$colvalue = preg_replace( '/[^[:print:]]/', '',utf8_encode( $data[$c])); | $colvalue = preg_replace( '/[^[:print:]]/', '',utf8_encode( $data[$c])); |
$contractNoticeInsert[] = $colvalue; | $contractNoticeInsert[] = $colvalue; |
} | } |
} | } |
} | } |
} | } |
flush(); | flush(); |
if ($tablename == "contractnotice") { | if ($tablename == "contractnotice") { |
$contractNoticeInsertQ->execute($contractNoticeInsert); | $contractNoticeInsertQ->execute($contractNoticeInsert); |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { | if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { |
$dupes++; | $dupes++; |
} | } |
elseif ($errors[1] == 0) { | elseif ($errors[1] == 0) { |
$success++; | $success++; |
} | } |
else { | else { |
foreach ($contractNoticeFields as $key => $cnf) { | foreach ($contractNoticeFields as $key => $cnf) { |
echo var_dump($contractNoticeInsert[$key]) . $cnf . "<br>"; | echo var_dump($contractNoticeInsert[$key]) . $cnf . "<br>"; |
} | } |
echo $data[2] . " failed CN insert.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; | echo $data[2] . " failed CN insert.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; |
} | } |
} | } |
flush(); | flush(); |
//echo "<hr>\n"; | //echo "<hr>\n"; |
} | } |
$row++; | $row++; |
} | } |
fclose($handle); | fclose($handle); |
echo " $dupes duplicate records<br>"; | echo " $dupes duplicate records<br>"; |
echo " $success records successfully created<br>"; | echo " $success records successfully created<br>"; |
flush(); | flush(); |
return $success; | return $success; |
} | } |
$path = './'; | $path = './'; |
if ($_REQUEST["fname"] == "") { | if ($_REQUEST["fname"] == "") { |
echo "Get files from: https://www.tenders.gov.au/?event=public.reports.list<br>"; | echo "Get files from: https://www.tenders.gov.au/?event=public.reports.list<br>"; |
$dhandle = opendir($path); | $dhandle = opendir($path); |
// define an array to hold the files | // define an array to hold the files |
$files = array(); | $files = array(); |
if ($dhandle) { | if ($dhandle) { |
// loop through all of the files | // loop through all of the files |
while (false !== ($fname = readdir($dhandle))) { | while (false !== ($fname = readdir($dhandle))) { |
if (($fname != '.') && ($fname != '..') && (!isset($_REQUEST["filter"]) || strpos($fname,$_REQUEST["filter"]) != false)) { | if (($fname != '.') && ($fname != '..') && (!isset($_REQUEST["filter"]) || strpos($fname,$_REQUEST["filter"]) != false)) { |
echo "<a href=\"import.php?fname=$fname\">$fname</a> " . filesize($path . $fname) . " " . date("c", filemtime($path . $fname)) . "<br/>"; | echo "<a href=\"import.php?fname=$fname\">$fname</a> " . filesize($path . $fname) . " " . date("c", filemtime($path . $fname)) . "<br/>"; |
processFile($path . $fname, "contractnotice"); | processFile($path . $fname, "contractnotice"); |
} | } |
} | } |
} | } |
} | } |
else { | else { |
$success = 0; | $success = 0; |
$fname = $_REQUEST["fname"]; | $fname = $_REQUEST["fname"]; |
$success+= processFile($path . $fname, "contractnotice"); | $success+= processFile($path . $fname, "contractnotice"); |
} | } |
} | |
?> | ?> |
<?php | <?php |
include_once ("../../lib/common.inc.php"); | if (php_sapi_name() == "cli") { |
include_once ("../../lib/common.inc.php"); | |
/* | /* |
update contractnotice set "supplierABN" = a."supplierABN" | update contractnotice set "supplierABN" = a."supplierABN" |
from contractnotice as cn inner join (select "supplierABN", | from contractnotice as cn inner join (select "supplierABN", |
"supplierName" from contractnotice where "supplierABN" | "supplierName" from contractnotice where "supplierABN" |
IS NOT NULL and "supplierABN" != 0) as a on | IS NOT NULL and "supplierABN" != 0) as a on |
cn."supplierName" = a."supplierName" where | cn."supplierName" = a."supplierName" where |
cn."CNID"=contractnotice."CNID" and (contractnotice."supplierABN" | cn."CNID"=contractnotice."CNID" and (contractnotice."supplierABN" |
IS NULL or contractnotice."supplierABN" = 0) */ | IS NULL or contractnotice."supplierABN" = 0) */ |
// http://www.lastcraft.com/browser_documentation.php | // http://www.lastcraft.com/browser_documentation.php |
// http://code.google.com/p/phpquery/ | // http://code.google.com/p/phpquery/ |
require('phpQuery-onefile.php'); | require('phpQuery-onefile.php'); |
function getURL($url) { | function getURL($url) { |
//return file_get_contents($url); | //return file_get_contents($url); |
$ch = curl_init($url); | $ch = curl_init($url); |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); |
curl_setopt($ch, CURLOPT_HEADER, 0); | curl_setopt($ch, CURLOPT_HEADER, 0); |
curl_setopt($ch, CURLOPT_TIMEOUT, 45); | curl_setopt($ch, CURLOPT_TIMEOUT, 45); |
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); |
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); |
$page = curl_exec($ch); | $page = curl_exec($ch); |
if (curl_errno($ch)) { | if (curl_errno($ch)) { |
echo "<font color=red> Database temporarily unavailable: "; | echo "<font color=red> Database temporarily unavailable: "; |
echo curl_errno($ch) . " " . curl_error($ch); | echo curl_errno($ch) . " " . curl_error($ch); |
echo $url; | echo $url; |
echo "</font><br>"; | echo "</font><br>"; |
} | } |
curl_close($ch); | curl_close($ch); |
return $page; | return $page; |
} | } |
function getTextFromTHNode($Node, $Text = "") { | function getTextFromTHNode($Node, $Text = "") { |
if ($Node->tagName == null) | if ($Node->tagName == null) |
return $Text.$Node->textContent; | return $Text.$Node->textContent; |
if ($Node->tagName != "td") { | if ($Node->tagName != "td") { |
$Node = $Node->firstChild; | $Node = $Node->firstChild; |
if ($Node != null) | if ($Node != null) |
$Text = getTextFromTHNode($Node, $Text); | $Text = getTextFromTHNode($Node, $Text); |
while($Node->nextSibling != null) { | while($Node->nextSibling != null) { |
$Text = getTextFromTHNode($Node->nextSibling, $Text); | $Text = getTextFromTHNode($Node->nextSibling, $Text); |
$Node = $Node->nextSibling; | $Node = $Node->nextSibling; |
} | } |
} | } |
return $Text; | return $Text; |
} | } |
function getTextFromNode($Node, $Text = "") { | function getTextFromNode($Node, $Text = "") { |
if ($Node->tagName == null) | if ($Node->tagName == null) |
return $Text.$Node->textContent; | return $Text.$Node->textContent; |
if ($Node->tagName != "th" && $Node->tagName != "span") { | if ($Node->tagName != "th" && $Node->tagName != "span") { |
$Node = $Node->firstChild; | $Node = $Node->firstChild; |
if ($Node != null) | if ($Node != null) |
$Text = getTextFromNode($Node, $Text); | $Text = getTextFromNode($Node, $Text); |
while($Node->nextSibling != null) { | while($Node->nextSibling != null) { |
$Text = getTextFromNode($Node->nextSibling, $Text); | $Text = getTextFromNode($Node->nextSibling, $Text); |
$Node = $Node->nextSibling; | $Node = $Node->nextSibling; |
} | } |
} | } |
return $Text; | return $Text; |
} | } |
function dom_to_array($root) | function dom_to_array($root) |
{ | { |
$result = array(); | $result = array(); |
if ($root->hasAttributes()) | if ($root->hasAttributes()) |
{ | { |
$attrs = $root->attributes; | $attrs = $root->attributes; |
foreach ($attrs as $i => $attr) | foreach ($attrs as $i => $attr) |
$result[$attr->name] = $attr->value; | $result[$attr->name] = $attr->value; |
} | } |
$children = $root->childNodes; | $children = $root->childNodes; |
if ($root->childNodes) { | if ($root->childNodes) { |
if ($children->length == 1) | if ($children->length == 1) |
{ | { |
$child = $children->item(0); | $child = $children->item(0); |
if ($child->nodeType == XML_TEXT_NODE) | if ($child->nodeType == XML_TEXT_NODE) |
{ | { |
$result['_value'] = $child->nodeValue; | $result['_value'] = $child->nodeValue; |
if (count($result) == 1) | if (count($result) == 1) |
return $result['_value']; | return $result['_value']; |
else | else |
return $result; | return $result; |
} | } |
} | } |
$group = array(); | $group = array(); |
for($i = 0; $i < $children->length; $i++) | for($i = 0; $i < $children->length; $i++) |
{ | { |
$child = $children->item($i); | $child = $children->item($i); |
if (!isset($result[$child->nodeName])) | if (!isset($result[$child->nodeName])) |
$result[$child->nodeName] = dom_to_array($child); | $result[$child->nodeName] = dom_to_array($child); |
else | else |
{ | { |
if (!isset($group[$child->nodeName])) | if (!isset($group[$child->nodeName])) |
{ | { |
$tmp = $result[$child->nodeName]; | $tmp = $result[$child->nodeName]; |
$result[$child->nodeName] = array($tmp); | $result[$child->nodeName] = array($tmp); |
$group[$child->nodeName] = 1; | $group[$child->nodeName] = 1; |
} | } |
$result[$child->nodeName][] = dom_to_array($child); | $result[$child->nodeName][] = dom_to_array($child); |
} | } |
} | } |
} | } |
return $result; | return $result; |
} | } |
function importCN($cnid) { | function importCN($cnid) { |
global $conn; | global $conn; |
// check if already complete | // check if already complete |
$query = 'Select "parentCN" from contractnotice | $query = 'Select "parentCN" from contractnotice |
where "CNID" = :CNID'; | where "CNID" = :CNID'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->bindParam(":CNID", $CN); | $query->bindParam(":CNID", $CN); |
$query->execute(); | $query->execute(); |
$r = $query->fetch(PDO::FETCH_ASSOC); | $r = $query->fetch(PDO::FETCH_ASSOC); |
if ($r['parentCN'] == NULL) { | if ($r['parentCN'] == NULL) { |
$site = "https://www.tenders.gov.au/"; | $site = "https://www.tenders.gov.au/"; |
$searchResult = phpQuery::newDocument(getURL("https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN".$cnid)); | $searchResult = phpQuery::newDocument(getURL("https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN".$cnid)); |
//echo "https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN".$cnid; | //echo "https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN".$cnid; |
$url = ""; | $url = ""; |
foreach(pq('a') as $a) { | foreach(pq('a') as $a) { |
if (strpos($a->getAttribute("href"),"public.cn.view") >0 ) { | if (strpos($a->getAttribute("href"),"public.cn.view") >0 ) { |
//echo $a->getAttribute("href"); | //echo $a->getAttribute("href"); |
$url = $a->getAttribute("href"); | $url = $a->getAttribute("href"); |
break; | break; |
} | } |
} | } |
$cn = phpQuery::newDocument(getURL($site.$url)); | $cn = phpQuery::newDocument(getURL($site.$url)); |
$datamapping0711 = array( | $datamapping0711 = array( |
"Agency" => "agencyName", | "Agency" => "agencyName", |
"Parent CN" => "parentCN", | "Parent CN" => "parentCN", |
"CN ID" => "CNID", | "CN ID" => "CNID", |
"Publish Date" => "publishDate", | "Publish Date" => "publishDate", |
"Amendment Date" => "amendDate", | "Amendment Date" => "amendDate", |
"Status" => "", | "Status" => "", |
"StartDate" => "contractStart", | "StartDate" => "contractStart", |
"EndDate" => "contractEnd", | "EndDate" => "contractEnd", |
"Contract Value (AUD)" => "value", | "Contract Value (AUD)" => "value", |
"Description" => "description", | "Description" => "description", |
"Agency Reference ID" => "agencyID", | "Agency Reference ID" => "agencyID", |
"Category" => "category", | "Category" => "category", |
"Procurement Method" => "procurementMethod", | "Procurement Method" => "procurementMethod", |
"ATM ID" => "atmID", | "ATM ID" => "atmID", |
"SON ID" => "SONID", | "SON ID" => "SONID", |
"Confidentiality - Contract" => "confidentialityContract", | "Confidentiality - Contract" => "confidentialityContract", |
"Confidentiality Reason(s) - Contract" => "confidentialityContractReason", | "Confidentiality Reason(s) - Contract" => "confidentialityContractReason", |
"Confidentiality - Outputs" => "confidentialityOutputs", | "Confidentiality - Outputs" => "confidentialityOutputs", |
"Confidentiality Reason(s) - Outputs" => "confidentialityOutputsReason", | "Confidentiality Reason(s) - Outputs" => "confidentialityOutputsReason", |
"Consultancy" => "consultancy", | "Consultancy" => "consultancy", |
"Consultancy Reason(s)" => "consultancyReason", | "Consultancy Reason(s)" => "consultancyReason", |
"Amendment Reason" => "amendmentReason", | "Amendment Reason" => "amendmentReason", |
"Name" => "supplierName", | "Name" => "supplierName", |
"Postal Address" => "supplierAddress", | "Postal Address" => "supplierAddress", |
"Town/City" => "supplierCity", | "Town/City" => "supplierCity", |
"Postcode" => "supplierPostcode", | "Postcode" => "supplierPostcode", |
"Country" => "supplierCountry", | "Country" => "supplierCountry", |
"ABN Exempt" => "supplierABNExempt", | "ABN Exempt" => "supplierABNExempt", |
"ABN" => "supplierABN", | "ABN" => "supplierABN", |
"Branch" => "contactBranch", | "Branch" => "contactBranch", |
"Division" => "contactDivision", | "Division" => "contactDivision", |
"Office Postcode" => "contactPostcode" | "Office Postcode" => "contactPostcode" |
); | ); |
$cnFields = Array(); | $cnFields = Array(); |
foreach(pq('tr') as $tr) { | foreach(pq('tr') as $tr) { |
$tra = dom_to_array($tr); | $tra = dom_to_array($tr); |
if (is_array($tra['th'])) { | if (is_array($tra['th'])) { |
$fieldName = trim(getTextFromTHNode($tr)); | $fieldName = trim(getTextFromTHNode($tr)); |
} else { | } else { |
$fieldName = trim(str_replace("/th>","",$tra['th'])); | $fieldName = trim(str_replace("/th>","",$tra['th'])); |
} | } |
$fieldValue = trim(print_r($tra['td'],true)); | $fieldValue = trim(print_r($tra['td'],true)); |
if ($fieldName == "State/Territory" || $fieldName == "Contact Name" | if ($fieldName == "State/Territory" || $fieldName == "Contact Name" |
|| $fieldName == "Contact Phone" || $fieldName == "Contact Email" | || $fieldName == "Contact Phone" || $fieldName == "Contact Email" |
||$fieldName == "Amendments") { | ||$fieldName == "Amendments") { |
// do nothing | // do nothing |
} else if ($fieldName == "Contract Period") { | } else if ($fieldName == "Contract Period") { |
$contractPeriod = explode("to",$fieldValue); | $contractPeriod = explode("to",$fieldValue); |
$cnFields["contractStart"] = trim($contractPeriod[0]); | $cnFields["contractStart"] = trim($contractPeriod[0]); |
$cnFields["contractEnd"] = trim($contractPeriod[1]); | $cnFields["contractEnd"] = trim($contractPeriod[1]); |
} else { | } else { |
$fieldName = $datamapping0711[$fieldName]; | $fieldName = $datamapping0711[$fieldName]; |
if ($fieldName == "parentCN" || $fieldName == "CNID") { | if ($fieldName == "parentCN" || $fieldName == "CNID") { |
if (is_array($tra['td'])) { | if (is_array($tra['td'])) { |
$fieldValue = trim(getTextFromNode($tr)); | $fieldValue = trim(getTextFromNode($tr)); |
} | } |
$fieldValue = substr($fieldValue, 2); // take off the "CN" prefix | $fieldValue = substr($fieldValue, 2); // take off the "CN" prefix |
} elseif ($fieldName == "description") { | } elseif ($fieldName == "description") { |
if (is_array($tra['td'])) $fieldValue = print_r($tra['td']['p'],true); | if (is_array($tra['td'])) $fieldValue = print_r($tra['td']['p'],true); |
} elseif ($fieldName == "value" || $fieldName == "supplierABN") { | } elseif ($fieldName == "value" || $fieldName == "supplierABN") { |
if (is_array($tra['td'])) { | if (is_array($tra['td'])) { |
$fieldValue = trim(getTextFromNode($tr)); | $fieldValue = trim(getTextFromNode($tr)); |
} | } |
$fieldValue = str_replace(Array("$",","," "), "", $fieldValue); | $fieldValue = str_replace(Array("$",","," "), "", $fieldValue); |
//if (!is_numeric($fieldValue)) $fieldValue = 0; | //if (!is_numeric($fieldValue)) $fieldValue = 0; |
if ($fieldValue == "Exempt") $fieldValue = NULL; | if ($fieldValue == "Exempt") $fieldValue = NULL; |
} elseif ($fieldName == "amendDate" || $fieldName == "publishDate" || $fieldName == "contractStart" || $fieldName == "contractEnd") { | } elseif ($fieldName == "amendDate" || $fieldName == "publishDate" || $fieldName == "contractStart" || $fieldName == "contractEnd") { |
$fieldValue = date('Y-m-d H:i:s', strtotime($fieldValue)); | $fieldValue = date('Y-m-d H:i:s', strtotime($fieldValue)); |
} elseif (is_array($tra['td'])) { | } elseif (is_array($tra['td'])) { |
$fieldValue = trim(getTextFromNode($tr)); | $fieldValue = trim(getTextFromNode($tr)); |
} | } |
echo $fieldName. " = " .$fieldValue."<br>\n"; | echo $fieldName. " = " .$fieldValue."<br>\n"; |
$cnFields[$fieldName] = $fieldValue; | $cnFields[$fieldName] = $fieldValue; |
} | } |
} | } |
if (isset($cnFields[""])) { | if (isset($cnFields[""])) { |
$cnFields["description"] .= $cnFields[""]; | $cnFields["description"] .= $cnFields[""]; |
unset($cnFields[""]); | unset($cnFields[""]); |
} | } |
$cnFields["importFile"] = $url; | $cnFields["importFile"] = $url; |
$contractNoticeInsertQ = 'INSERT INTO contractnotice ("' . implode('" , "', array_keys($cnFields)) . '") VALUES ( '; | $contractNoticeInsertQ = 'INSERT INTO contractnotice ("' . implode('" , "', array_keys($cnFields)) . '") VALUES ( '; |
for($key = 0; $key < sizeof($cnFields); $key++) { | for($key = 0; $key < sizeof($cnFields); $key++) { |
$contractNoticeInsertQ.= ($key == 0 ? "" : ", ") . "?"; | $contractNoticeInsertQ.= ($key == 0 ? "" : ", ") . "?"; |
} | } |
$contractNoticeInsertQ.= ");"; | $contractNoticeInsertQ.= ");"; |
//echo $contractNoticeInsertQ; | //echo $contractNoticeInsertQ; |
$contractNoticeInsertQ = $conn->prepare($contractNoticeInsertQ); | $contractNoticeInsertQ = $conn->prepare($contractNoticeInsertQ); |
$contractNoticeInsertQ->execute(array_values($cnFields)); | $contractNoticeInsertQ->execute(array_values($cnFields)); |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { | if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { |
echo "Dupe {$cnFields['CNID']}<br>"; | echo "Dupe {$cnFields['CNID']}<br>"; |
} | } |
elseif ($errors[1] == 0) { | elseif ($errors[1] == 0) { |
echo "Success insert {$cnFields['CNID']} <br>"; | echo "Success insert {$cnFields['CNID']} <br>"; |
} | } |
else { | else { |
foreach ($cnFields as $key => $cnf) { | foreach ($cnFields as $key => $cnf) { |
echo var_dump($key) . $cnf . "<br>"; | echo var_dump($key) . $cnf . "<br>"; |
} | } |
echo $cnFields['CNID'] . " failed CN insert.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; | echo $cnFields['CNID'] . " failed CN insert.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; |
} | } |
$contractNoticeUpdateQ = 'UPDATE contractnotice SET '; | $contractNoticeUpdateQ = 'UPDATE contractnotice SET '; |
$count = 0; | $count = 0; |
foreach ($cnFields as $key => $f) { | foreach ($cnFields as $key => $f) { |
$count++; | $count++; |
$contractNoticeUpdateQ.= '"'.$key.'"=? '.($count >= sizeof($cnFields) ? "" : ", "); | $contractNoticeUpdateQ.= '"'.$key.'"=? '.($count >= sizeof($cnFields) ? "" : ", "); |
} | } |
$contractNoticeUpdateQ.= ' WHERE "CNID"=?;'; | $contractNoticeUpdateQ.= ' WHERE "CNID"=?;'; |
$cnFields[] = $cnFields["CNID"]; | $cnFields[] = $cnFields["CNID"]; |
//echo $contractNoticeUpdateQ; | //echo $contractNoticeUpdateQ; |
$contractNoticeUpdateQ = $conn->prepare($contractNoticeUpdateQ); | $contractNoticeUpdateQ = $conn->prepare($contractNoticeUpdateQ); |
$contractNoticeUpdateQ->execute(array_values($cnFields)); | $contractNoticeUpdateQ->execute(array_values($cnFields)); |
$errors = $conn->errorInfo(); | $errors = $conn->errorInfo(); |
if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { | if ($errors[1] == 7 && strpos($errors[2], "duplicate key")) { |
print_r($errors); | print_r($errors); |
echo "Dupe update {$cnFields['CNID']}<br>"; | echo "Dupe update {$cnFields['CNID']}<br>"; |
} | } |
elseif ($errors[1] == 0) { | elseif ($errors[1] == 0) { |
echo "Success update {$cnFields['CNID']} <br>"; | echo "Success update {$cnFields['CNID']} <br>"; |
} | } |
else { | else { |
foreach ($cnFields as $key => $cnf) { | foreach ($cnFields as $key => $cnf) { |
echo var_dump($key) . $cnf . "<br>"; | echo var_dump($key) . $cnf . "<br>"; |
} | } |
echo $cnFields['CNID'] . " failed CN update.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; | echo $cnFields['CNID'] . " failed CN update.<br>" . print_r($errors, true) . "<br> row $row <br><br>\n"; |
} | } |
} | } |
} | } |
function processFile($fpath, $tablename) | function processFile($fpath, $tablename) |
{ | { |
global $conn; | global $conn; |
echo " ============== $fpath ============== <br>"; | echo " ============== $fpath ============== <br>"; |
$handle = fopen($fpath, "r"); | $handle = fopen($fpath, "r"); |
flush(); | flush(); |
$row = 1; | $row = 1; |
while (($data = fgetcsv($handle, 1000, "\t")) !== false) { | while (($data = fgetcsv($handle, 1000, "\t")) !== false) { |
if ($row > 3) { | if ($row > 3) { |
$data[0] = trim($data[0], "="); | $data[0] = trim($data[0], "="); |
$data[0] = trim($data[0], "\""); | $data[0] = trim($data[0], "\""); |
if (strpos($data[0], "-A") > 0) { | if (strpos($data[0], "-A") > 0) { |
echo "Loading {$data[0]} ... <br>\n"; | echo "Loading {$data[0]} ... <br>\n"; |
importCN(str_replace("CN","",$data[0])); | importCN(str_replace("CN","",$data[0])); |
} | } |
} | } |
flush(); | flush(); |
//echo "<hr>\n"; | //echo "<hr>\n"; |
$row++; | $row++; |
} | } |
fclose($handle); | fclose($handle); |
} | } |
$path = './'; | $path = './'; |
if ($_REQUEST["fname"] == "") { | if ($_REQUEST["fname"] == "") { |
echo "Get files from: https://www.tenders.gov.au/?event=public.reports.list<br>"; | echo "Get files from: https://www.tenders.gov.au/?event=public.reports.list<br>"; |
$dhandle = opendir($path); | $dhandle = opendir($path); |
// define an array to hold the files | // define an array to hold the files |
$files = array(); | $files = array(); |
if ($dhandle) { | if ($dhandle) { |
// loop through all of the files | // loop through all of the files |
while (false !== ($fname = readdir($dhandle))) { | while (false !== ($fname = readdir($dhandle))) { |
if (($fname != '.') && ($fname != '..') && (strpos($fname,".xls")>0)) { | if (($fname != '.') && ($fname != '..') && (strpos($fname,".xls")>0)) { |
echo "<a href=\"import.php?fname=$fname\">$fname</a> " . filesize($path . $fname) . " " . date("c", filemtime($path . $fname)) . "<br/>"; | echo "<a href=\"import.php?fname=$fname\">$fname</a> " . filesize($path . $fname) . " " . date("c", filemtime($path . $fname)) . "<br/>"; |
processFile($path . $fname, "contractnotice"); | processFile($path . $fname, "contractnotice"); |
} | } |
} | } |
} | } |
} | } |
else { | else { |
$success = 0; | $success = 0; |
$fname = $_REQUEST["fname"]; | $fname = $_REQUEST["fname"]; |
$success+= processFile($path . $fname, "contractnotice"); | $success+= processFile($path . $fname, "contractnotice"); |
} | } |
} | |
?> | ?> |
<?php | <?php |
if (php_sapi_name() == "cli") { | |
date_default_timezone_set('Australia/Melbourne'); | date_default_timezone_set('Australia/Melbourne'); |
$split = false; | $split = false; |
function format_bytes($size) { | function format_bytes($size) { |
$units = array(' B', ' KB', ' MB', ' GB', ' TB'); | $units = array(' B', ' KB', ' MB', ' GB', ' TB'); |
for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024; | for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024; |
return round($size, 2).$units[$i]; | return round($size, 2).$units[$i]; |
} | } |
$days = 4; | $days = 4; |
if (isset($_REQUEST['days'])) $days = $_REQUEST['days']; | if (isset($_REQUEST['days'])) $days = $_REQUEST['days']; |
$startDate = strtotime("05-Jun-2008"); | $startDate = strtotime("05-Jun-2008"); |
if (isset($_REQUEST['startDate'])) $startDate = $_REQUEST['startDate']; | if (isset($_REQUEST['startDate'])) $startDate = $_REQUEST['startDate']; |
function getFile($startDate, $days, $minVal, $maxVal) { | function getFile($startDate, $days, $minVal, $maxVal) { |
global $split; | global $split; |
$endDate = strtotime(date("Y-m-d", $startDate)." +".$days." days"); | $endDate = strtotime(date("Y-m-d", $startDate)." +".$days." days"); |
$file = date("dMY",$startDate).'to'.date("dMY",$endDate).'val'.$minVal.'to'.$maxVal.'.xls'; | $file = date("dMY",$startDate).'to'.date("dMY",$endDate).'val'.$minVal.'to'.$maxVal.'.xls'; |
echo "Fetching $file ($days days) ($minVal < value < $maxVal )... "; | echo "Fetching $file ($days days) ($minVal < value < $maxVal )... "; |
$url = "https://www.tenders.gov.au/?event=public.advancedsearch.CNSONRedirect&type=cnEvent&atmType=archived%2Cclosed%2Cpublished%2Cproposed&agencyUUID=&agencyStatus=-1&portfolioUUID=&keyword=&KeywordTypeSearch=AllWord&CNID=&dateType=Publish+Date&dateStart=".date("d-M-Y",$startDate)."&dateEnd=".date("d-M-Y",$endDate)."&supplierName=&supplierABN=&valueFrom=".$minVal."&valueTo=".$maxVal."&ATMID=&AgencyRefId=&consultancy=&download=Download+results"; | $url = "https://www.tenders.gov.au/?event=public.advancedsearch.CNSONRedirect&type=cnEvent&atmType=archived%2Cclosed%2Cpublished%2Cproposed&agencyUUID=&agencyStatus=-1&portfolioUUID=&keyword=&KeywordTypeSearch=AllWord&CNID=&dateType=Publish+Date&dateStart=".date("d-M-Y",$startDate)."&dateEnd=".date("d-M-Y",$endDate)."&supplierName=&supplierABN=&valueFrom=".$minVal."&valueTo=".$maxVal."&ATMID=&AgencyRefId=&consultancy=&download=Download+results"; |
echo "<!-- $url -->"; | echo "<!-- $url -->"; |
$current = file_get_contents($url); | $current = file_get_contents($url); |
if (strpos($current,"There are no results that match your selection.")> 0 ) { | if (strpos($current,"There are no results that match your selection.")> 0 ) { |
echo "<font color=red>Empty file!</font><br>"; | echo "<font color=red>Empty file!</font><br>"; |
} | } |
if (strpos($current,"Your search returned more than 1000 results.") === false) { | if (strpos($current,"Your search returned more than 1000 results.") === false) { |
file_put_contents($file, $current); | file_put_contents($file, $current); |
echo "$file saved<br>"; | echo "$file saved<br>"; |
echo format_bytes(filesize($file))."<br>"; | echo format_bytes(filesize($file))."<br>"; |
echo '<a href="?startDate='.$endDate.'&days='.$days.'">Load next '.($days).' days </a><br>'; | echo '<a href="?startDate='.$endDate.'&days='.$days.'">Load next '.($days).' days </a><br>'; |
echo '<a href="?startDate='.$endDate.'&days='.($days*2).'">Load next '.($days*2).' days </a><br>'; | echo '<a href="?startDate='.$endDate.'&days='.($days*2).'">Load next '.($days*2).' days </a><br>'; |
echo '<a href="?startDate='.$endDate.'&days='.$days.'&split=yes">Load next '.($days).' days with split</a><br>'; | echo '<a href="?startDate='.$endDate.'&days='.$days.'&split=yes">Load next '.($days).' days with split</a><br>'; |
flush(); | flush(); |
if (!isset($_REQUEST['split']) && !$split) { | if (!isset($_REQUEST['split']) && !$split) { |
echo "Success so fetching next $days... <br>"; | echo "Success so fetching next $days... <br>"; |
getFile($endDate, $days, "" , ""); | getFile($endDate, $days, "" , ""); |
} | } |
return true; | return true; |
} else { | } else { |
echo "<font color=red>Too many records!</font><br>"; | echo "<font color=red>Too many records!</font><br>"; |
echo '<a href="?startDate='.$startDate.'&days='.floor($days/2).'">Load '.($days/2).' days instead?</a><br>'; | echo '<a href="?startDate='.$startDate.'&days='.floor($days/2).'">Load '.($days/2).' days instead?</a><br>'; |
echo '<a href="?startDate='.$startDate.'&days='.$days.'&split=yes">Split instead?</a><br>'; | echo '<a href="?startDate='.$startDate.'&days='.$days.'&split=yes">Split instead?</a><br>'; |
flush(); | flush(); |
if (!isset($_REQUEST['split']) && !$split) { | if (!isset($_REQUEST['split']) && !$split) { |
echo "Failure so splitting ... <br>"; | echo "Failure so splitting ... <br>"; |
doSplit($startDate, $days); | doSplit($startDate, $days); |
} | } |
return false; | return false; |
} | } |
} | } |
function doSplit($startDate, $days) { | function doSplit($startDate, $days) { |
global $split; | global $split; |
$split = true; | $split = true; |
set_time_limit(20); | set_time_limit(20); |
getFile($startDate, $days, 0, 12000); | getFile($startDate, $days, 0, 12000); |
getFile($startDate, $days, 12000, 16000); | getFile($startDate, $days, 12000, 16000); |
getFile($startDate, $days, 16000, 20000); | getFile($startDate, $days, 16000, 20000); |
getFile($startDate, $days, 20000, 30000); | getFile($startDate, $days, 20000, 30000); |
getFile($startDate, $days, 30000, 40000); | getFile($startDate, $days, 30000, 40000); |
// getFile($startDate, $days, 40000, 80000); | // getFile($startDate, $days, 40000, 80000); |
getFile($startDate, $days, 40000, 60000); | getFile($startDate, $days, 40000, 60000); |
getFile($startDate, $days, 60000, 80000); | getFile($startDate, $days, 60000, 80000); |
// getFile($startDate, $days, 80000, 300000); | // getFile($startDate, $days, 80000, 300000); |
getFile($startDate, $days, 80000, 150000); | getFile($startDate, $days, 80000, 150000); |
getFile($startDate, $days, 150000, 300000); | getFile($startDate, $days, 150000, 300000); |
getFile($startDate, $days, 300000, 999999999); | getFile($startDate, $days, 300000, 999999999); |
} | } |
if (isset($_REQUEST['split'])) { | if (isset($_REQUEST['split'])) { |
doSplit($startDate, $days); | doSplit($startDate, $days); |
} else { | } else { |
getFile($startDate, $days, "" , ""); | getFile($startDate, $days, "" , ""); |
} | } |
} | |
?> | ?> |
<?php | |
include_once("../lib/common.inc.php"); | |
$active = Array('0EC8D805-C293-3ADD-E51A93667D977314|Administrative Appeals Tribunal','0ECAA17D-9862-5309-101D94A126495C7F|Aged Care Standards and Accreditation Agency Ltd','CE45735B-F9FC-04DA-F299C730D9BE613E|Airservices Australia','0EC8DF71-ED98-8593-2D776EE93D9BA248|Attorney-General\'s Department','0ECA6B4C-DE4F-A04A-DBFB5F39B050B6D7|AusAid','0ECA6F64-F6B8-F406-E490A1479A7A717B|Austrade','D2724D99-F230-A93A-93D62A8DCC187940|Australia Council for the Arts','D26C4D0A-FF6E-612A-C6FEDB2D5E71AE4A|Australian Antarctic Division','C8E854CD-E966-9A35-078FF6702EC1ECED|Australian Broadcasting Corporation','0ECB50CF-028A-FB9D-3E5EF0F9708E4BC7|Australian Bureau of Statistics','0ECA671F-EA1F-8560-1D7C90408694B565|Australian Centre for International Agricultural Research','A861A42C-B32C-85A8-0F719005E27F4E23|Australian Commission for Law Enforcement Integrity','C8F9DAF8-9763-EA22-FA44B6BBC12B0B94|Australian Communications and Media Authority (ACMA)','0ECB2BB9-B19C-4929-87EC63542388D079|Australian Competition and Consumer Commission','0EC8E42E-EB6A-15DA-3F001AFB9C2B8D34|Australian Crime Commission','0EC8E8CE-9BCF-0651-D4362CF53DB8E4DC|Australian Customs and Border Protection Service','CD51BCA4-9DC7-9494-B7D944029D03A1F9|Australian Electoral Commission','D248626B-0BDD-B201-1A1C7C978AD973D2|Australian Fair Pay Commission','0EC8EDF3-E025-2F80-AEB39B350641C81F|Australian Federal Police','0EC8B90B-CBF5-8EC4-2C4E261041A25E16|Australian Fisheries Management Authority','0ECABD48-0388-F8AB-00BD4EB12FA44067|Australian Hearing Services','0EC920D5-CEB1-81FC-5A107A206F4A6BF3|Australian Human Rights Commission','D257B064-D245-51CC-F69A50FF4620915B|Australian Industrial Registry','0EC8F7B1-03ED-C4C3-70FBDF105ADB2FB6|Australian Institute of Criminology','D32EC07E-E22E-5805-1240CF68033C5E15|Australian Institute of Family Studies','0ECA9D77-A0B8-448A-53945B4DFACEE685|Australian Institute of Health and Welfare','D31CF0E2-B1A4-DE74-5D7B7B1DC97EA1DC|Australian Institute of Marine Science','0EC8FC41-C1BE-587A-A19920F43BA616BF|Australian Law Reform Commission','CE5DCAA3-D793-2FEE-B3C263B84AF2676C|Australian Maritime Safety Authority','0ECAED96-0B3B-1D24-56412EC3384996B0|Australian National Audit Office (ANAO)','D281D726-938C-D76D-A346348BEFC509F9|Australian National Maritime Museum','D31E738F-F139-2BC2-EBF2764D7812EA10|Australian Nuclear Science and Technology Organisation (ANSTO)','0ECB3D20-C837-FBE2-B1C32937F87CBB8F|Australian Office of Financial Management','82C5CFA1-0088-7B9E-9A3EF95987B55045|Australian Organ and Tissue Donation and Transplantation Authority','0EC8BF2B-B23F-0A07-0659BB9760EFC94E|Australian Pesticides and Veterinary Medicines Authority','0ECB5AFF-F4DC-06E3-97B3118B978F33BD|Australian Prudential Regulation Authority (APRA)','0ECAF18D-044A-0F5E-AD942B0CD5BB1ACC|Australian Public Service Commission','0ECA88C8-B803-ECE2-00F37CE4797C74BC|Australian Radiation Protection and Nuclear Safety Agency (ARPANSA)','D320882B-ED70-8061-6A5B2F8E64D6F575|Australian Research Council','CD5A2434-9592-2217-49970AF89E5956F8|Australian Reward Investment Alliance (ARIA)','0ECB44F1-BC3B-58E0-96CD33517E10E38B|Australian Securities and Investments Commission','0EC9056F-C13F-ED84-07B410D54C4A1013|Australian Security Intelligence Organisation','D303A5DD-B83B-EC45-588E3680B4E842E4|Australian Sports Anti-Doping Authority (ASADA)','0ECB54CA-EE15-8B26-3C430607E8DB3D4E|Australian Taxation Office','0EC900DB-D645-6C40-DD8BEA03CC15D392|Australian Transaction Reports and Analysis Centre (AUSTRAC)','77068F86-0A15-723A-C2FC1601C44FFCD2|Australian Transport Safety Bureau','0EC9AC89-DEB3-1441-2441891A3772863E|Australian War Memorial','D28B4F07-C2F2-2D4F-1C9229D177088373|Bureau of Meteorology','0ECA90FE-A44B-2EAA-08039CC6C43E728D|Cancer Australia','0ECAB1CF-D480-0C8B-70670DFC4FFDAC56|Centrelink','CE608086-A4DC-45F6-1179DCC148CF91F7|Civil Aviation Safety Authority','D24AFCAC-A2B2-4766-17C9A3EC3AAE4205|Comcare','166B9AD2-ACC9-B56F-4D5B47BD325DEB25|Commonwealth Grants Commission','CD5BDE89-9805-D921-0DAAFDBD5312D0D5|Comsuper','0ECB5EF6-CC7B-7C8E-32798557B78F6D98|Corporations and Markets Advisory Committee','0EC909FF-D902-5FD3-6660ADB353470F95|Crimtrac','0ECAC700-BDE8-458D-6A4E91C8B151A270|CRS Australia','D3275762-D634-4459-D33A26E1BEA05D14|CSIRO','0EC99E6C-0375-2DAF-A8338121FCBD6F7A|Defence Housing Australia','0EC9A29C-E478-AD19-3F0F29F6BE4914BF|Defence Materiel Organisation','0EC892FD-E156-BCB0-ADF5D534E584BC18|Department of Agriculture, Fisheries and Forestry','FB2B0C69-0AAF-1B2B-8186EC38772F8FC1|Department of Broadband, Communications and the Digital Economy','69368049-A8EF-0896-EA9C603C856788D9|Department of Climate Change and Energy Efficiency','0EC98EF9-E020-5DA9-DCB10135BE81BD2B|Department of Defence','D23E9B50-DAE3-6701-D90A2582EE48A4DA|Department of Education, Employment and Workplace Relations','D2FBF89C-D43D-4B3E-527132AC0D41ABBB|Department of Families, Housing, Community Services and Indigenous Affairs','CD39DC0D-D4B3-DFEC-DDDCD52E651F06EA|Department of Finance and Deregulation','0ECA5FC9-A4AD-0A3A-97A8DD3FD9D44CB1|Department of Foreign Affairs and Trade','0ECA80C0-EC9F-B5B9-0D3E8A4537C39808|Department of Health and Ageing','0ECAADBC-B488-A53B-F4F6672EC813849A|Department of Human Services','0ECB6A9B-BA91-CB7D-36C62C5C9191D403|Department of Immigration & Citizenship','047091E8-CEE4-A9FF-BF552F538D279807|Department of Infrastructure and Transport','FADB0367-F000-E0B9-8F79FD109F613AB6|Department of Innovation, Industry, Science and Research','0ECB7ED8-BB19-D108-324A8A05B1966ABF|Department of Parliamentary Services','2F03BD8D-F73D-C35C-729559C6C70C6602|Department of Regional Australia, Regional Development and Local Government','FADFD268-9BF5-97E4-2F247B92F74E82C0|Department of Resources, Energy and Tourism','62304115-AA13-68D1-592110A8021DA68E|Department of Sustainability, Environment, Water, Population and Communities','0ECB8490-9B07-1898-98170CDD42A9DFF7|Department of the House of Representatives','0ECAE795-F858-203D-FD1C9FF933EF4A37|Department of the Prime Minister and Cabinet','0ECB8A6E-B354-715B-C9DEDE4A6DA3C393|Department of the Senate','0ECB25C0-02C3-6BAA-4738951514F489A9|Department of the Treasury','0EC9A85E-BA37-0A60-47A828CD70D831FC|Department of Veterans\' Affairs','D2E272D5-DDD0-178B-F1BE5A9181D3ACAE|Director of National Parks','D2F85A2B-E563-118E-0CB302E02C74397C|Equal Opportunity for Women in the Workplace Agency','0ECA73FA-FAAD-62B5-48581DF753E57CCA|Export Finance and Insurance Corporation (EFIC)','A535F827-E58E-89FB-7F58107CBBD0D79A|Fair Work Australia','0EC91301-C770-6470-480E879920826ABD|Family Court of Australia','0EC91799-A81B-262C-CE6ACAB2622F0566|Federal Court of Australia','0EC91C3C-BB6B-97BF-76BEF63DD0905AAF|Federal Magistrates Court','08355C10-AB46-67AA-421E334D1B45E125|Food Standards Australia New Zealand','CD5E362E-A615-1102-67814B74731025AD|Future Fund Management Agency','D33926C6-BB30-4A30-3A29A5821CFB7AE6|Geoscience Australia','0EC8CA07-AE70-7EB2-5A174487D4BD6236|Grains Research and Development Corporation','D2E5D48D-08A4-5CC9-5378FEA3CD8E649C|Great Barrier Reef Marine Park Authority','0EC925CD-F184-26AF-DE8A84DE3CBA3790|Insolvency and Trustee Service Australia (ITSA)','0ECB302F-AADD-2BB1-392F50E0E94CB076|Inspector-General of Taxation','D3174EBA-F3C0-28E6-7A669C23CF0B3040|IP Australia','0ECAB5DF-B75F-EC35-92EA7821EFF77C0D|Medicare Australia','0ECB74A7-BE11-FC3C-8696F7F93D7612C0|Migration Review Tribunal and Refugee Review Tribunal (MRT-RRT)','F8E42DAB-9570-A008-97294775650CCE6E|Murray-Darling Basin Authority','0A18A552-0B56-257B-70BE077385CE2EDC|National Archives of Australia','0ECA8CEA-08C9-6D55-C57A7A102DFFBC8A|National Blood Authority','D2347B3A-F330-0317-68AB0C4F7240B9A5|National Capital Authority','0ECB3416-FEB1-F5E0-FB4760831CA2F66E|National Competition Council','7DEE189B-0A00-07D1-2D1C778EDAEFEF6D|National Film and Sound Archive','D2E7830B-9692-3124-1C2E1D74AAC8FFFF|National Gallery of Australia','0ECA957E-E479-2758-528FA4E576A27A5D|National Health and Medical Research Council','D2EA1497-0BB3-AFBD-76559FA87101FF06|National Library of Australia','D2EC2CF2-FEEE-8FB2-46193EDF8EA2F503|National Museum of Australia','0EC92A40-B2DA-D4FF-16895BCD00F9E20F|National Native Title Tribunal','D33DB6EB-FEE1-F686-7385E65E48749DD2|National Offshore Petroleum Safety Authority','D2EF702D-EF9B-B363-BDA3A4950C5ED57F|National Water Commission','0ECB00AF-04FE-735D-82D0B84D1A19F95A|Office of National Assessments','0EC93653-9FCE-12DD-DAAC73E23DCAE8CA|Office of Parliamentary Counsel','0ECB48E3-9198-6F2C-9737A69F9D15177A|Office of the Auditing and Assurance Standards Board','0ECB4CD6-A6D6-4334-D550948337CE9F56|Office of the Australian Accounting Standards Board','D25ACD14-ABA3-F69F-E1ADA4503ED97C82|Office of the Australian Building and Construction Commissioner (ABCC)','D331236A-BDE0-5CD7-24EF78F052CB2B50|Office of the Australian Information Commissioner','0ECAF6CA-ACBE-E3E7-6DCBF175E012F817|Office of the Commonwealth Ombudsman','0EC93B4D-DC77-6FA0-7E15530A7D7344CB|Office of the Director of Public Prosecutions','39DCC587-FF78-8759-E91BF5B9EC3D1904|Office of the Fair Work Ombudsman','0ECB049D-B7DC-1697-586AB4455B0DF251|Office of the Inspector-General of Intelligence and Security','0ECB0887-D30E-E490-3F16BCB7EF406D8E|Office of the Official Secretary to the Governor-General','E0EAE43E-B918-12DC-9F4E0E03811CACAD|Office of the Renewable Energy Regulator','D726F8EA-D648-B4DF-E25FF1EEE68C9700|Old Parliament House','87A575EB-E6CE-7E8B-1CB3D9B6580FAE5B|Private Health Insurance Ombudsman','0ECB380B-FC93-8003-9A1F04BD55A10F62|Productivity Commission','0ECA9978-D7C8-3F44-2D21C2827407BEF5|Professional Services Review','0ECB64C5-E6E4-3D23-B4409B18EB609B25|Reserve Bank of Australia','0ECB4106-02BC-64C6-1DF8032E549D88E4|Royal Australian Mint','4A350604-FD8E-C7BB-D6C6A14AEE6D8873|Safe Work Australia','D25D96A7-B9B7-21D2-57AA60EAC138107B|Seacare','D2FF2257-0E69-4B7C-FCF6AB672ECC341C|Social Security Appeals Tribunal','D2F16C5B-BCFB-E14D-9B62DEEDF992798B|Sydney Harbour Federation Trust','0ECA84C7-D6C3-3299-9F898B9C201D771B|Therapeutic Goods Administration','D340FD88-0AFE-B091-1F43D9EDBCE164D4|Tourism Australia','EB74EB82-AA02-F70B-DC648859ABFC28DA|Wheat Exports Australia'); | |
$suspended = Array('8B346DD0-D9A5-585C-1B3174A9B6292AD1|Aboriginal and Torres Strait Islander Services','0ECB16D3-9684-471C-3FFBCCCAB63728B4|Airservices Australia','0EC975AC-F94D-B8B5-FDF5CF053D78F7EA|Australia Council for the Arts','0ECA7934-9725-0D31-1BD08C6ADFD16040|Australia-Japan Foundation','0ECA043A-C02D-1838-E994DF3EC8B0A857|Australian Antarctic Division','0EC96B6B-9A30-F9E4-892DA6860B3985B2|Australian Broadcasting Corporation','0EC9521F-C38A-2FB1-54CEF555CE76807A|Australian Communications and Media Authority (ACMA)','0ECA48E7-B536-E530-CA1F4F8A4056517F|Australian Electoral Commission','0EC9D8CD-D6F8-A695-854B714011D9B2E8|Australian Fair Pay Commission','0EC9703D-9C88-C7FF-E15390103B51CE22|Australian Film Commission','D27DB409-E5C9-15AD-635A70CEF5A8E05C|Australian Film Commission','0EC96705-AED6-462D-E1579FE99DD57511|Australian Film Television and Radio School','D27F93E1-9898-8ED5-5DF9628A1CE78BBF|Australian Film Television and Radio School','0EC9DCED-ED0B-FF25-20323EF5E93EC88E|Australian Industrial Registry','0EC9BF9B-C83C-4EC5-DDE3C845EBF3CCC0|Australian Institute of Aboriginal and Torres Strait Islander Studies (AIATSIS)','D246AE91-0DD2-193E-84F9A3E13664E58E|Australian Institute of Aboriginal and Torres Strait Islander Studies (AIATSIS)','0ECA2851-AD46-9261-B3A77D28C5CC1351|Australian Institute of Family Studies','0EC9BB58-BBA8-3F3D-AD04CBFE4FABC85D|Australian Institute of Marine Science','0ECB1ACC-FAAC-3895-9EC179BC71C3FC54|Australian Maritime Safety Authority','0EC9611A-C5D2-43B5-D4039EED991272E3|Australian National Maritime Museum','0EC9C3D2-CE1C-8C81-085ADA97E2172420|Australian Nuclear Science and Technology Organisation (ANSTO)','0EC9B71F-EDBC-5E42-E44D5595BDF7B3F4|Australian Research Council','0ECA59B7-D4C6-360D-4829CB8A5E15B664|Australian Reward Investment Alliance (ARIA)','0EC95958-08E9-4A16-A79AC1CB828B9535|Australian Sports Anti-Doping Authority (ASADA)','0EC8C3DE-B6EB-88B8-0FD9460AC543DD0C|Biosecurity Australia','0ECA1921-EA6F-1BE2-3E85DBF7BBCDE734|Bureau of Meteorology','0ECAC142-B8B8-CCE0-A4B00E4B67ED0DCB|Child Support Agency','0ECB1FED-D2C6-6214-2CEB31CE9B107ED1|Civil Aviation Safety Authority','0EC9D499-F2DE-F8D9-0239ACA17C2ABFBB|Comcare','0ECA55A3-A0B2-0D36-D383E6A113AD5749|Commonwealth Grants Commission','0ECA4CF9-9312-2F3D-637C98CF2FEEB5A4|Comsuper','0EC9C7FE-F7AC-09BD-3C498B63A9DDDDA4|CSIRO','0EC8B354-F006-A824-A43120384BE56A98|Dairy Adjustment Authority','D3328CCE-DEA4-5516-AA23F77F557454FC|Department of Climate Change','0EC94B72-AD24-B695-3C5C388FBC0C23AA|Department of Communications, Information Technology and the Arts','0EC9B2BB-D5C8-8061-8CC999143D0D291C|Department of Education, Science and Training','0EC9CE2C-A998-C45A-90697489AC42B83B|Department of Employment and Workplace Relations','0ECA21F5-F5FE-7F08-52CD6FFB54E788FE|Department of Families, Community Services & Indigenous Affairs','0ECA32E7-E860-DFD4-0FBDCE1CC8E0FC84|Department of Finance and Administration','8B3539BA-EC97-7510-AF049F389DC0497A|Department of Immigration and Multicultural Affairs','8B361B4A-EAAE-D747-134D9FA97497C70F|Department of Immigration and Multicultural and Indigenous Affairs (DIMIA)','0ECACF65-D93B-D892-1862B65F15C92A6A|Department of Industry, Tourism and Resources','CE419954-CB32-1154-9E61EDB5805C222D|Department of Infrastructure, Transport, Regional Development and Local Government','0EC9F8FB-F224-1E70-F59834C1D5CE12D5|Department of the Environment and Water Resources','D2DFCF42-A87B-181C-2F48D7EEE8849AFD|Department of the Environment, Water, Heritage and the Arts','0ECB0EE4-9D78-A341-363B0D13161C5916|Department of Transport and Regional Services','0ECA0863-0F4E-5FA8-B7C522D16CCAC9BB|Director of National Parks','0EC90E81-DAF0-F67A-7FDC1AE664FF0BEE|Emergency Management Australia','0EC9E429-9FFF-7E05-41ADCC29FE17811C|Equal Opportunity for Women in the Workplace Agency','576FCE5F-E258-4A5C-92C11639C73A0442|Export Wheat Commission','0ECA518C-EF07-86F5-ADCAA84198009116|Future Fund Management Agency','0ECAE1C5-E249-022B-DCC7D944F398E1FD|Geoscience Australia','0ECA0CF1-AEF7-5C78-23878C6A5BF93C36|Great Barrier Reef Marine Park Authority','0ECADD52-F400-97E8-25F98B9FF96FFD08|IP Australia','0EC8D025-D94D-FF1D-97130B40320BAD9A|Land and Water Australia','0EC98433-EB5E-BEB1-1EEC7CADF83918A0|National Archives of Australia','CD603E31-CD1F-AAE5-F233F954A031553E|National Archives of Australia','0ECB12DC-BF41-FF5B-CB306CF040EC3218|National Capital Authority','0EC97A03-EA2E-5AE3-EC3C2C23487ECECA|National Gallery of Australia','0EC98880-F5F1-6560-605A0250EA58D139|National Library of Australia','0EC97FE7-E894-67B2-A8CE40DEF409AC23|National Museum of Australia','0ECAD957-B214-674F-3C94DEEF52BA3F46|National Offshore Petroleum Safety Authority','0ECAFC83-0B5D-045A-EE43EA12B9953CC0|National Water Commission','48499BD1-AFE0-1093-EF06293D78B3FD65|National Water Commission','0EC92FD3-F7B3-B0E0-9A8D8D2B611BB615|Office of Film and Literature Classification','0EC9E84A-A9F7-4EEF-5DDD2FC87C1F1B55|Office of the Australian Building and Construction Commissioner (ABCC)','0EC940DD-9A7E-1ECD-B44DDA0B72B2407F|Office of the Privacy Commissioner','0ECA10FE-AC03-80D8-C8DF1069C707351E|Office of the Renewable Energy Regulator','D3362537-FE00-1892-B408CABF5E0257A7|Office of the Renewable Energy Regulator','0EC9F2EC-C778-46D0-F15FDB1A8F1552F9|Office of Workplace Services','FB2A3EA0-D6FC-DB1D-19B0B72180A8A19B|Questacon','D329A60A-E914-FB37-FBA853A4CE8558EB|Questacon','0ECB788C-CC5C-2969-4B94EFB3913B0E65|Refugee Review Tribunal','A2A6EBF4-A8F5-E60B-5C25ACD3EA0679D8|Screen Australia','0EC9EC88-9B12-CB63-56A58BE69BDF3B6F|Seacare','0ECA2CE0-CBA9-9E13-AA0FB347CF4B5FD1|Social Security Appeals Tribunal','0ECA1510-B4A2-BBA4-D6FDE37DFAAC7C12|Sydney Harbour Federation Trust','0ECAA587-C851-4038-8CDC9CCE9A28F6E6|The National Institute of Clinical Studies Ltd','0ECAD567-E1E7-F5FC-72BA34FDF72F0FC0|Tourism Australia','7A592345-0410-DF1C-4B349EA35F314D3F|Workplace Authority','003B8DE1-D09A-D72F-28CEBC19DB84E866|Workplace Ombudsman','D260B27E-9744-EF72-C065072F24B4A62A|Workplace Ombudsman'); | |
foreach ($active as $agency ) { | |
$agencyParts = explode("|",$agency); | |
$agency = $agencyParts[1]; | |
$agencyInsert = "INSERT INTO agency (agencyName) VALUES ('$agency')"; | |
$result = mysql_query($agencyInsert); | |
$result = mysql_query("UPDATE agency SET status = 'active' where agencyName = '".mysql_real_escape_string($agency)."';") ; | |
if ($result) echo $agency. " set to active in ". mysql_affected_rows() . " divisions/branches <br>\n"; | |
else echo "error".mysql_error(); | |
} | |
foreach ($suspended as $agency) { | |
$agencyParts = explode("|",$agency); | |
$agency = $agencyParts[1]; | |
$agencyInsert = "INSERT INTO agency (agencyName) VALUES ('$agency')"; | |
$result = mysql_query($agencyInsert); | |
$result = mysql_query("UPDATE agency SET status = 'suspended' where agencyName = '".mysql_real_escape_string($agency)."';") ; | |
if ($result) echo $agency. " set to suspended in ". mysql_affected_rows() . " divisions/branches <br>\n"; | |
else echo "error".mysql_error(); | |
} | |
?> |
<?php | |
include_once ("../lib/common.inc.php"); | |
// to reset: update agency set abn = 0, website = '' | |
$ch = curl_init(); | |
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); | |
curl_setopt($ch, CURLOPT_REFERER, "http://contractdashboard.lambdacomplex.org"); | |
$querySel = sprintf("SELECT * FROM `agency` where ABN = 0 OR website = ''"); | |
$resultSel = mysql_query($querySel); | |
while ($row = mysql_fetch_array($resultSel, MYSQL_ASSOC)) { | |
$agency = $row['agencyName']; | |
//foreach $agency | |
$url = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&key=ABQIAAAA95XYXN0cki3Yj_Sb71CFvBSgIPe2k9-DRgEVQvbvQDV8xmTiXRTBpHxrD7bSh5rl2lswY56769CyQQ&rsz=small&filter=1&gl=au&q=" . urlencode($agency) . "%20ABN%20site:.gov.au%20-site:www.abr.business.gov.au"; | |
curl_setopt($ch, CURLOPT_URL, $url); | |
$body = curl_exec($ch); | |
$json = json_decode($body); | |
echo "<pre>"; | |
$result = $json->responseData->results[0]; | |
print_r($result); | |
echo "</pre>"; | |
echo "<b> Agency:</b> $agency"; | |
echo "<b> URL:</b> http://" . $result->visibleUrl; | |
preg_match('/\d{2} \d{3} \d{3} \d{3}/i', $result->content, $abn); | |
$abn[0] = str_replace(" ","",$abn[0]); | |
echo "<b> ABN:</b> {$abn[0]}"; | |
if ($abn[0] > 1000) { | |
$result = mysql_query("UPDATE agency SET website = 'http://" . $result->visibleUrl . "', abn = '{$abn[0]}' WHERE agencyName = '$agency';"); | |
if ($result) echo $agency . " set in " . mysql_affected_rows() . " <br>\n"; | |
} else { | |
echo "invalid ABN"; | |
$result = mysql_query("UPDATE agency SET website = 'http://" . $result->visibleUrl . "' WHERE agencyName = '$agency';"); | |
if ($result) echo $agency . " set in " . mysql_affected_rows() . " <br>\n"; | |
} | |
// fi | |
} | |
curl_close($ch); | |
?> |
<?php | <?php |
include_once ("./lib/common.inc.php"); | include_once ("./lib/common.inc.php"); |
if ($_REQUEST['agency']) { | if ($_REQUEST['agency']) { |
include_header("Agency"); | |
$agency = htmlentities(strip_tags($_REQUEST['agency'])); | $agency = htmlentities(strip_tags($_REQUEST['agency'])); |
MethodCountGraph($agency); | include_header($agency); |
CnCGraph($agency); | echo '<center><h1>'.$agency.'</h1></center>'; |
MethodValueGraph($agency); | // MethodCountGraph($agency); |
// CnCGraph($agency); | |
// MethodValueGraph($agency); | |
/* biggest contracts | /* biggest contracts |
spending by year | spending by year |
spending by industry/category | spending by industry/category |
spending by supplier | spending by supplier |
spread procurement methods (stacked bar graph) | spread procurement methods (stacked bar graph) |
+ percent consultancies + percent confidential (bar graph) | + percent consultancies + percent confidential (bar graph) |
Average value by procurement type | Average value by procurement type |
--- info | --- info |
website, procurement plan, annual reports | website, procurement plan, annual reports |
Breakdown of divisions/branches | Breakdown of divisions/branches |
Breakdown percentage,number,value by procurement type | Breakdown percentage,number,value by procurement type |
Histograph, overlaying number value reported per week over X years | Histograph, overlaying number value reported per week over X years |
Compliance statistics: amendments, delay in reporting average and number completely late */ | Compliance statistics: amendments, delay in reporting average and number completely late */ |
$query = 'SELECT "CNID", "description", "value", "agencyName", "category", | $query = 'SELECT "CNID", "description", "value", "agencyName", "category", |
"contractStart", "supplierName" | "contractStart", "supplierName" |
FROM contractnotice | FROM contractnotice |
WHERE "agencyName" = :agency | WHERE "agencyName" = :agency |
ORDER BY "value" DESC'; | ORDER BY "value" DESC limit 100'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->bindParam(":agency", $agency); | $query->bindParam(":agency", $agency); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
echo "<table> <thead> | echo "<table> <thead> |
<tr> | <tr> |
<th>Contract Notice Number</th> | <th>Contract Notice Number</th> |
<th>Contract Description</th> | <th>Contract Description</th> |
<th>Total Contract Value</th> | <th>Total Contract Value</th> |
<th>Agency</th> | <th>Agency</th> |
<th>Contract Start Date</th> | <th>Contract Start Date</th> |
<th>Supplier</th> | <th>Supplier</th> |
</tr> | </tr> |
</thead>"; | </thead>"; |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
setlocale(LC_MONETARY, 'en_US'); | setlocale(LC_MONETARY, 'en_US'); |
$value = number_format(doubleval($row['value']), 2); | $value = number_format(doubleval($row['value']), 2); |
echo ("<tr> | echo ("<tr> |
<td><a href=\"displayContract.php?CNID={$row['CNID']}\">{$row['CNID']}</a></td> | <td><a href=\"displayContract.php?CNID={$row['CNID']}\">{$row['CNID']}</a></td> |
<td><b>{$row['description']}</b></a></td> | <td><b>{$row['description']}</b></a></td> |
<td>\$$value</td><td>{$row['agencyName']}</td> | <td>\$$value</td><td>{$row['agencyName']}</td> |
<td>{$row['contractStart']}</td> | <td>{$row['contractStart']}</td> |
<td>{$row['supplierName']}</td> | <td>{$row['supplierName']}</td> |
</tr>"); | </tr>"); |
} | } |
echo "</table>"; | echo "</table>"; |
} else { | } else { |
/* | /* |
split by portfolio | split by portfolio |
*/ | */ |
include_header("Agencies"); | include_header("Agencies"); |
agenciesGraph(); | agenciesGraph(); |
$query = 'SELECT SUM("value"), "agencyName" | $query = 'SELECT SUM("value"), "agencyName" |
FROM contractnotice | FROM contractnotice |
WHERE "childCN" is null | WHERE "childCN" is null |
GROUP BY "agencyName" '; | GROUP BY "agencyName" '; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
echo "<table> <thead> | echo "<table> <thead> |
<tr> | <tr> |
<th>Agency</th> | <th>Agency</th> |
<th>Total Contracts Value</th> | <th>Total Contracts Value</th> |
</tr> | </tr> |
</thead>"; | </thead>"; |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
setlocale(LC_MONETARY, 'en_US'); | setlocale(LC_MONETARY, 'en_US'); |
$value = number_format(doubleval($row[0]), 2); | $value = number_format(doubleval($row[0]), 2); |
$agency = stripslashes($row[1]); | $agency = stripslashes($row[1]); |
echo ("<tr><td><b><a href=\"displayAgency.php?agency={$agency}\">{$agency}</a></b></td><td>\$$value</td></tr>\n"); | echo ("<tr><td><b><a href=\"displayAgency.php?agency={$agency}\">{$agency}</a></b></td><td>\$$value</td></tr>\n"); |
} | } |
echo "</table>"; | echo "</table>"; |
} | } |
include_footer(); | include_footer(); |
?> | ?> |
<?php | <?php |
include_once("./lib/common.inc.php"); | include_once("./lib/common.inc.php"); |
include_header("Contract"); | |
$query = 'SELECT * | $query = 'SELECT * |
FROM contractnotice | FROM contractnotice |
WHERE "CNID" = :CNID LIMIT 1'; | WHERE "CNID" = :CNID LIMIT 1'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->bindParam(":CNID", $_REQUEST['CNID']); | $query->bindParam(":CNID", $_REQUEST['CNID']); |
$query->execute(); | $query->execute(); |
$contractResult = $query->fetch(PDO::FETCH_ASSOC); | |
if (!$contractResult) { | |
header("Status: 404 Not Found"); | |
header("HTTP/1.0 404 Not Found"); | |
include_header("Contract Not Found"); | |
echo "<center><h1>No Contract Notice with that ID found</h1></center>"; | |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll(PDO::FETCH_ASSOC) as $row) { | } else { |
$description = ucsmart($contractResult["description"]); | |
include_header($description); | |
echo '<center><h1>'.$description.'</h1></center>'; | |
databaseError($conn->errorInfo()); | |
setlocale(LC_MONETARY, 'en_US'); | setlocale(LC_MONETARY, 'en_US'); |
foreach (array_filter($row) as $key => $value) { | foreach (array_filter($contractResult) as $key => $value) { |
echo "<b>$key</b> "; | echo "<b>$key</b> "; |
switch ($key) { | switch ($key) { |
case "supplierName": | case "supplierName": |
case "supplierABN": | case "supplierABN": |
echo '<a href="displaySupplier.php?supplier=' . $row['supplierABN'] . '-' . urlencode($row['supplierName']) . '">' . $value . "</a>"; | echo '<a href="displaySupplier.php?supplier=' . $contractResult['supplierABN'] . '-' . urlencode($contractResult['supplierName']) . '">' . $value . "</a>"; |
break; | break; |
case "agencyName": | case "agencyName": |
echo '<a href="displayAgency.php?agency=' . urlencode($value) . '">' . $value . "</a>"; | echo '<a href="displayAgency.php?agency=' . urlencode($value) . '">' . $value . "</a>"; |
break; | break; |
case "value": | case "value": |
echo "$" . number_format(doubleval($value), 2); | echo "$" . number_format(doubleval($value), 2); |
break; | break; |
default: | default: |
echo str_replace(" ", "<br>", $value); | echo str_replace(" ", "<br>", ucsmart($value)); |
} | } |
echo "<br>"; | echo "<br>"; |
} | } |
} | |
echo '<br><a href="https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN' . $_REQUEST['CNID'] . '"> View original record @ tenders.gov.au</a><br>'; | echo '<br><a href="https://www.tenders.gov.au/?event=public.advancedsearch.keyword&keyword=CN' . $_REQUEST['CNID'] . '"> View original record @ tenders.gov.au</a><br>'; |
$query = 'SELECT * FROM `heuristic_results` where "CNID" = :CNID'; | $query = 'SELECT * FROM heuristic_results where "CNID" = :CNID'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$agencyName = $input . '%'; | $agencyName = $input . '%'; |
$query->bindParam(":CNID", $_REQUEST['CNID']); | $query->bindParam(":CNID", $_REQUEST['CNID']); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll() as $r) { | foreach ($query->fetchAll() as $r) { |
echo "<b>{$r['heuristic_name']}</b>: {$r['heuristic_value']} (raw value: {$r['raw_value']}, mean: {$r['mean']}, stddev: {$r['stddev']})<br>"; | echo "<b>{$r['heuristic_name']}</b>: {$r['heuristic_value']} (raw value: {$r['raw_value']}, mean: {$r['mean']}, stddev: {$r['stddev']})<br>"; |
} | } |
} | |
include_footer(); | include_footer(); |
?> | ?> |
<?php | <?php |
include_once ("./lib/common.inc.php"); | include_once ("./lib/common.inc.php"); |
if ($_REQUEST['supplier']) { | if ($_REQUEST['supplier']) { |
include_header("Supplier"); | |
$supplierS = htmlentities(strip_tags($_REQUEST['supplier'])); | $supplierS = htmlentities(strip_tags($_REQUEST['supplier'])); |
include_header(str_replace("%","",$supplierName)); | |
echo '<center><h1>'.str_replace("%","",$supplierName).'</h1></center>'; | |
// MethodCountGraph($supplierS); | // MethodCountGraph($supplierS); |
// CnCGraph($supplierS); | // CnCGraph($supplierS); |
// MethodValueGraph($supplierS); | // MethodValueGraph($supplierS); |
/* lobbyist ties | /* lobbyist ties |
links to ABR/ASIC/Google News/ASX/Court records | links to ABR/ASIC/Google News/ASX/Court records |
total value to various agencies (bar graph) | total value to various agencies (bar graph) |
spread procurement methods + percent consultancies + percent confidential (bar graph) | spread procurement methods + percent consultancies + percent confidential (bar graph) |
spread of contract values | spread of contract values |
spread of industries (textual?) */ | spread of industries (textual?) */ |
$query = 'SELECT "CNID", "description", "value", "agencyName", "category", | $query = 'SELECT "CNID", "description", "value", "agencyName", "category", |
"contractStart", "supplierName" | "contractStart", "supplierName" |
FROM contractnotice WHERE ' . | FROM contractnotice WHERE ' . |
$supplierQ . ' ' . $standardQ | $supplierQ . ' ' . $standardQ |
. ' ORDER BY value DESC'; | . ' ORDER BY value DESC'; |
echo $query; | echo $query; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
if ($supplierParts[0] > 0) { | if ($supplierParts[0] > 0) { |
$query->bindParam(":supplierABN", $supplierABN); | $query->bindParam(":supplierABN", $supplierABN); |
} else { | } else { |
$query->bindParam(":supplierName", $supplierName); | $query->bindParam(":supplierName", $supplierName); |
} | } |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
// echo '<img src="graphs/displayMethodCountGraph.php?month=' . stripslashes($supplier) . '">'; | // echo '<img src="graphs/displayMethodCountGraph.php?month=' . stripslashes($supplier) . '">'; |
// echo '<img src="graphs/displayCnCGraph.php?month=' . stripslashes($supplier) . '">'; | // echo '<img src="graphs/displayCnCGraph.php?month=' . stripslashes($supplier) . '">'; |
echo "<table> <thead> | echo "<table> <thead> |
<tr> | <tr> |
<th>Contract Notice Number</th> | <th>Contract Notice Number</th> |
<th>Contract Description</th> | <th>Contract Description</th> |
<th>Total Contract Value</th> | <th>Total Contract Value</th> |
<th>Agency</th> | <th>Agency</th> |
<th>Contract Start Date</th> | <th>Contract Start Date</th> |
<th>Supplier</th> | <th>Supplier</th> |
</tr> | </tr> |
</thead>"; | </thead>"; |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
setlocale(LC_MONETARY, 'en_US'); | setlocale(LC_MONETARY, 'en_US'); |
$value = number_format(doubleval($row['value']), 2); | $value = number_format(doubleval($row['value']), 2); |
echo ("<tr> | echo ("<tr> |
<td><a href=\"displayContract.php?CNID={$row['CNID']}\">{$row['CNID']}</a></td> | <td><a href=\"displayContract.php?CNID={$row['CNID']}\">{$row['CNID']}</a></td> |
<td><b>{$row['description']}</b></a></td> | <td><b>{$row['description']}</b></a></td> |
<td>\$$value</td><td>{$row['agencyName']}</td> | <td>\$$value</td><td>{$row['agencyName']}</td> |
<td>{$row['contractStart']}</td> | <td>{$row['contractStart']}</td> |
<td>{$row['supplierName']}</td> | <td>{$row['supplierName']}</td> |
</tr>"); | </tr>"); |
} | } |
echo "</table>"; | echo "</table>"; |
} else { | } else { |
/* | /* |
histograph of supplier size/value | histograph of supplier size/value |
*/ | */ |
include_header("Suppliers"); | include_header("Suppliers"); |
suppliersGraph(); | suppliersGraph(); |
$query = 'SELECT SUM("value") as val, MAX("supplierName") as supplierName, "supplierABN",( | $query = 'SELECT SUM("value") as val, MAX("supplierName") as supplierName, "supplierABN",( |
case when "supplierABN" != 0 THEN "supplierABN"::text ELSE "supplierName" END) as supplierID | case when "supplierABN" != 0 THEN "supplierABN"::text ELSE "supplierName" END) as supplierID |
FROM contractnotice | FROM contractnotice |
WHERE "childCN" is null | WHERE "childCN" is null |
GROUP BY supplierID,"supplierABN" | GROUP BY supplierID,"supplierABN" |
ORDER BY val DESC | ORDER BY val DESC |
LIMIT 100'; | LIMIT 100'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
echo "<table> <thead> | echo "<table> <thead> |
<tr> | <tr> |
<th>Position</th> | <th>Position</th> |
<th>Supplier</th> | <th>Supplier</th> |
<th>Total Contract Value</th> | <th>Total Contract Value</th> |
</tr> | </tr> |
</thead>"; | </thead>"; |
$i = 1; | $i = 1; |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
setlocale(LC_MONETARY, 'en_US'); | setlocale(LC_MONETARY, 'en_US'); |
$value = number_format(doubleval($row['val']), 2); | $value = number_format(doubleval($row['val']), 2); |
$supplier = stripslashes($row['supplierABN'] . '-' . $row['suppliername']); | $supplier = stripslashes($row['supplierABN'] . '-' . $row['suppliername']); |
echo ("<tr><td>$i</td><td><b><a href=\"displaySupplier.php?supplier={$supplier}\">" . ucsmart($row['suppliername']) . "</a></b></td><td>\$$value</td></tr>\n"); | echo ("<tr><td>$i</td><td><b><a href=\"displaySupplier.php?supplier={$supplier}\">" . ucsmart($row['suppliername']) . "</a></b></td><td>\$$value</td></tr>\n"); |
$i++; | $i++; |
} | } |
echo "</table>"; | echo "</table>"; |
} | } |
include_footer(); | include_footer(); |
?> | ?> |
<?php | |
include_once ("./lib/common.inc.php"); | |
include_header("Home"); | |
echo ' <div class="hero-unit"> | |
<h1>Hello, world!</h1> | |
<p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p> | |
<p><a class="btn primary large">Learn more »</a></p> | |
</div>'; | |
include_footer(); | |
?> | |
google-site-verification: google676a414ad086cefb.html | |
<?php | <?php |
/* all | /* all |
SELECT description, count(*) as count | SELECT description, count(*) as count |
FROM `contractnotice` | FROM contractnotice |
group by description having count > 1 order by count | group by description having count > 1 order by count |
*/ | */ |
/*- duplicated description | /*- duplicated description |
- most duplicated overall, most duplicated per agency/category/supplier etc. */ | - most duplicated overall, most duplicated per agency/category/supplier etc. */ |
$heuristics["METADATA_DUPLICATED_DESCRIPTION"] = Array( | $heuristics["METADATA_DUPLICATED_DESCRIPTION"] = Array( |
"description" => "" | "description" => "" |
); | ); |
function METADATA_DUPLICATED_DESCRIPTION($cn) | function METADATA_DUPLICATED_DESCRIPTION($cn) |
{ | { |
$averageDuplicatedDescriptions = getAverageDuplicatedDescriptions(); | $averageDuplicatedDescriptions = getAverageDuplicatedDescriptions(); |
$stddevDuplicatedDescriptions = getstddevDuplicatedDescriptions(); | $stddevDuplicatedDescriptions = getstddevDuplicatedDescriptions(); |
$query = 'select count(*) from contractnotice where description = "' . $agencyName . '"'; | $query = 'select count(*) from contractnotice where description = "' . $agencyName . '"'; |
$result = $conn->query($query); | $result = $conn->query($query); |
$r = $result->fetch(PDO::FETCH_BOTH); | $r = $result->fetch(PDO::FETCH_BOTH); |
$dupeDesc = $r[0]; | $dupeDesc = $r[0]; |
if ($dupeDesc == 1) $value = 0; | if ($dupeDesc == 1) $value = 0; |
else $value = abs($dupeDesc - $averageDuplicatedDescriptions) / $stddevDuplicatedDescriptions; | else $value = abs($dupeDesc - $averageDuplicatedDescriptions) / $stddevDuplicatedDescriptions; |
return Array( | return Array( |
"heuristic_value" => $value, | "heuristic_value" => $value, |
"raw_value" => $dupeDesc, | "raw_value" => $dupeDesc, |
"mean" => $averageDuplicatedDescriptions, | "mean" => $averageDuplicatedDescriptions, |
"stddev" => $stddevDuplicatedDescriptions | "stddev" => $stddevDuplicatedDescriptions |
); | ); |
} | } |
$averageDuplicatedDescriptions; | $averageDuplicatedDescriptions; |
function getAverageDuplicatedDescriptions() | function getAverageDuplicatedDescriptions() |
{ | { |
global $averageDuplicatedDescriptions; | global $averageDuplicatedDescriptions; |
if (!$averageDuplicatedDescriptions) { | if (!$averageDuplicatedDescriptions) { |
getStatsDuplicatedDescriptions(); | getStatsDuplicatedDescriptions(); |
} | } |
return $averageDuplicatedDescriptions; | return $averageDuplicatedDescriptions; |
} | } |
$stddevDuplicatedDescriptions; | $stddevDuplicatedDescriptions; |
function getstddevDuplicatedDescriptions() | function getstddevDuplicatedDescriptions() |
{ | { |
global $stddevDuplicatedDescriptions; | global $stddevDuplicatedDescriptions; |
if (!$stddevDuplicatedDescriptions) { | if (!$stddevDuplicatedDescriptions) { |
getStatsDuplicatedDescriptions(); | getStatsDuplicatedDescriptions(); |
} | } |
return $stddevDuplicatedDescriptions; | return $stddevDuplicatedDescriptions; |
} | } |
function getStatsDuplicatedDescriptions() | function getStatsDuplicatedDescriptions() |
{ | { |
$query = "select avg(count),STDDEV(count) from ( | $query = "select avg(count),STDDEV(count) from ( |
SELECT description, count(*) as count | SELECT description, count(*) as count |
FROM `contractnotice` | FROM contractnotice |
group by description having count > 1 | group by description having count > 1 |
) as a;"; | ) as a;"; |
$result = $conn->query($query); | $result = $conn->query($query); |
$r = $result->fetch(PDO::FETCH_BOTH); | $r = $result->fetch(PDO::FETCH_BOTH); |
$averageDuplicatedDescriptions = $r[0]; | $averageDuplicatedDescriptions = $r[0]; |
$stddevDuplicatedDescriptions = $r[1]; | $stddevDuplicatedDescriptions = $r[1]; |
} | } |
<?php | <?php |
if (php_sapi_name() != "cli") { | |
include_once ("../lib/common.inc.php"); | |
auth(); | |
include_once("heuristics.inc.php"); | include_once("heuristics.inc.php"); |
$query = 'SELECT *, agency.abn as "agencyABN", case when "supplierABN" != 0 then "supplierABN"::text else "supplierName" end as "supplierID" | $query = 'SELECT *, agency.abn as "agencyABN", case when "supplierABN" != 0 then "supplierABN"::text else "supplierName" end as "supplierID" |
FROM contractnotice JOIN agency ON contractnotice."agencyName"=agency."agencyName" | FROM contractnotice JOIN agency ON contractnotice."agencyName"=agency."agencyName" |
WHERE DATE("importDate") = (select * from (SELECT DATE("importDate") | WHERE DATE("importDate") = (select * from (SELECT DATE("importDate") |
FROM contractnotice ORDER BY "importDate" DESC limit 1) alias) limit 10'; | FROM contractnotice ORDER BY "importDate" DESC limit 1) alias) limit 10'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll() as $cn) { | foreach ($query->fetchAll() as $cn) { |
//get each new CN from latest update | //get each new CN from latest update |
foreach ($heuristics as $heuristic => $description) { | foreach ($heuristics as $heuristic => $description) { |
// run all heuristics | // run all heuristics |
runHeuristic($heuristic, $cn); | runHeuristic($heuristic, $cn); |
} | } |
flush(); | flush(); |
} | } |
/*foreach agency | /*foreach agency |
aggregate agency metrics | aggregate agency metrics |
foreach supplier | foreach supplier |
aggreate supplier metrics | aggreate supplier metrics |
foreach CN | foreach CN |
aggregate CN metrics */ | aggregate CN metrics */ |
} | |
?> | ?> |
<?php | <?php |
/*// most interesting | /*// most interesting |
SELECT sum(heuristic_value) as sum, CNID | SELECT sum(heuristic_value) as sum, CNID |
FROM `heuristic_results` group by CNID order by sum DESC limit 30 | FROM heuristic_results group by CNID order by sum DESC limit 30 |
// spread of values | // spread of values |
select floor(sum) as val,count(*) from (SELECT sum(heuristic_value) | select floor(sum) as val,count(*) from (SELECT sum(heuristic_value) |
as sum FROM heuristic_results group by CNID) as a group by val*/ | as sum FROM heuristic_results group by CNID) as a group by val*/ |
$series = Array(); | $series = Array(); |
include_once("../lib/common.inc.php"); | include_once("../lib/common.inc.php"); |
$query = "select heuristic_name, floor(heuristic_value) as val,count(*) from heuristic_results group by heuristic_name, val"; | $query = "select heuristic_name, floor(heuristic_value) as val,count(*) from heuristic_results group by heuristic_name, val"; |
$result = $conn->query($query); | $result = $conn->query($query); |
foreach ($result->fetchAll() as $r) { | foreach ($result->fetchAll() as $r) { |
$series[$r["heuristic_name"]][$r["val"]] = $r[2]; | $series[$r["heuristic_name"]][$r["val"]] = $r[2]; |
} | } |
?> | ?> |
<?php | |
include_once ("./lib/common.inc.php"); | |
include_header("Home"); | |
echo ' <div class="hero-unit"> | |
<h1>Hello, world!</h1> | |
<p>Vestibulum id ligula porta felis euismod semper. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.</p> | |
<p><a class="btn primary large">Learn more »</a></p> | |
</div>'; | |
include_footer(); | |
?> | |
<?php | <?php |
date_default_timezone_set("Australia/ACT"); | date_default_timezone_set("Australia/ACT"); |
error_reporting(E_ALL ^ E_NOTICE); | error_reporting(E_ALL ^ E_NOTICE); |
$conn = new PDO("pgsql:dbname=contractDashboard;user=postgres;password=snmc;host=localhost"); | $conn = new PDO("pgsql:dbname=contractDashboard;user=postgres;password=snmc;host=localhost"); |
if (!$conn) { | if (!$conn) { |
die("A database error occurred.\n"); | die("A database error occurred.\n"); |
} | |
define('ROOT', pathinfo(__FILE__, PATHINFO_DIRNAME)); | |
if (strstr($_SERVER['PHP_SELF'], "labs/")) { | |
$basePath = "../"; | |
} | |
require ROOT . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'openid.php'; | |
$openid = new LightOpenID($_SERVER['HTTP_HOST']); | |
function login() { | |
global $openid; | |
if (!$openid->mode) { | |
$openid->required = array('contact/email'); | |
$openid->identity = 'https://www.google.com/accounts/o8/id'; | |
header('Location: ' . $openid->authUrl()); | |
} | |
} | |
function auth() { | |
global $openid; | |
if ($_SESSION['authed'] == true) { | |
return true; | |
} | |
if ($openid->mode) { | |
$attr = $openid->getAttributes(); | |
if ($attr['contact/email'] != 'maxious@gmail.com') { | |
die('Access Denied'); | |
} else { | |
$_SESSION['authed'] = true; | |
} | |
} else { | |
login(); | |
} | |
} | } |
// $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | // $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
function databaseError($errMsg) { | function databaseError($errMsg) { |
if ($errMsg[2] != "") { | if ($errMsg[2] != "") { |
echo '<div class="alert-message error">'; | echo '<div class="alert-message error">'; |
die(print_r($errMsg, true)); | die(print_r($errMsg, true)); |
echo "</div>"; | echo "</div>"; |
} | } |
} | } |
function ucsmart($str) { | function ucsmart($str) { |
$shortWords = Array("The", "Pty", "Ltd", "Inc", "Red", "Oil", "A", "An", "And", "At", "For", "In" | $shortWords = Array("The", "Pty", "Ltd", "Inc", "Red", "Oil", "A", "An", "And", "At", "For", "In" |
, "Of", "On", "Or", "The", "To", "With"); | , "Of", "On", "Or", "The", "To", "With"); |
$strArray = explode(" ", preg_replace("/(?<=(?<!:|’s)\W) | $strArray = explode(" ", preg_replace("/(?<=(?<!:|’s)\W) |
(A|An|And|At|For|In|Of|On|Or|The|To|With) | (A|An|And|At|For|In|Of|On|Or|The|To|With) |
(?=\W)/e", 'strtolower("$1")', ucwords(strtolower($str)))); | (?=\W)/e", 'strtolower("$1")', ucwords(strtolower($str)))); |
foreach ($strArray as &$word) { | foreach ($strArray as &$word) { |
if (strlen($word) <= 4 && !in_array($word, $shortWords)) | if (strlen($word) <= 4 && !in_array($word, $shortWords)) |
$word = strtoupper($word); | $word = strtoupper($word); |
} | } |
return implode(" ", $strArray); | return implode(" ", $strArray); |
} | } |
function percent($num_amount, $num_total) { | function percent($num_amount, $num_total) { |
$count1 = $num_amount / $num_total; | $count1 = $num_amount / $num_total; |
$count2 = $count1 * 100; | $count2 = $count1 * 100; |
$count = number_format($count2, 2); | $count = number_format($count2, 2); |
return $count; | return $count; |
} | } |
function array_sum_all($a) { | function array_sum_all($a) { |
if (!is_array($a)) | if (!is_array($a)) |
return $a; | return $a; |
foreach ($a as $key => $value) | foreach ($a as $key => $value) |
$totale += array_sum_all($value); | $totale += array_sum_all($value); |
return $totale; | return $totale; |
} | } |
// magic query modifiers | // magic query modifiers |
$agency = filter_var($_REQUEST['agency'], FILTER_SANITIZE_STRING); | $agency = filter_var($_REQUEST['agency'], FILTER_SANITIZE_STRING); |
if ($agency != "") | if ($agency != "") |
$agencyQ = "agencyName = '" . $agency . "' AND "; | $agencyQ = "agencyName = '" . $agency . "' AND "; |
$supplier = filter_var($_REQUEST['supplier'], FILTER_SANITIZE_STRING); | $supplier = filter_var($_REQUEST['supplier'], FILTER_SANITIZE_STRING); |
if ($supplier != "") { | if ($supplier != "") { |
$supplierParts = explode("-", $supplier); | $supplierParts = explode("-", $supplier); |
$supplierName = "%" . $supplierParts[1] . "%"; | $supplierName = "%" . $supplierParts[1] . "%"; |
$supplierABN = $supplierParts[0]; | $supplierABN = $supplierParts[0]; |
if ($supplierParts[0] > 0) | if ($supplierParts[0] > 0) |
$supplierQ = ' "supplierABN" = :supplierABN AND '; | $supplierQ = ' "supplierABN" = :supplierABN AND '; |
else | else |
$supplierQ = ' "supplierName" LIKE :supplierName AND '; | $supplierQ = ' "supplierName" LIKE :supplierName AND '; |
} | } |
$startYear = 2007; | $startYear = 2007; |
$year = filter_var($_REQUEST['year'], FILTER_SANITIZE_NUMBER_INT); | $year = filter_var($_REQUEST['year'], FILTER_SANITIZE_NUMBER_INT); |
if ($year != "") | if ($year != "") |
$yearQ = "YEAR(publishDate) = " . $year . " AND "; | $yearQ = "YEAR(publishDate) = " . $year . " AND "; |
$standardQ = ' "childCN" is null '; // AND YEAR(contractStart) >= 2007 AND YEAR(contractStart) <= 2010'; | $standardQ = ' "childCN" is null '; // AND YEAR(contractStart) >= 2007 AND YEAR(contractStart) <= 2010'; |
$start = 0.0; | $start = 0.0; |
function local_url() { | |
return "http://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/\\') . "/"; | |
} | |
function include_header($title) { | function include_header($title) { |
global $start; | global $start; |
?> | ?> |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
"http://www.w3.org/TR/html4/strict.dtd"> | "http://www.w3.org/TR/html4/strict.dtd"> |
<html> | <html> |
<head> | <head> |
<title>Contract Dashboard - <?php echo $title; ?></title> | <title>Contract Dashboard - <?php echo $title; ?></title> |
<link rel="stylesheet" type="text/css" href="bootstrap.min.css"> | <link rel="stylesheet" type="text/css" href="bootstrap.min.css"> |
<!-- Le HTML5 shim, for IE6-8 support of HTML elements --> | <!-- Le HTML5 shim, for IE6-8 support of HTML elements --> |
<!--[if lt IE 9]> | <!--[if lt IE 9]> |
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> | <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> |
<![endif]--> | <![endif]--> |
<script type="text/javascript" src="lib/bsn.AutoSuggest_2.1.3_comp.js" charset="utf-8"></script> | <script type="text/javascript" src="lib/bsn.AutoSuggest_2.1.3_comp.js" charset="utf-8"></script> |
<link rel="stylesheet" href="autosuggest_inquisitor.css" type="text/css" media="screen" charset="utf-8" /> | <link rel="stylesheet" href="autosuggest_inquisitor.css" type="text/css" media="screen" charset="utf-8" /> |
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> | <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js"></script> |
<script type="text/javascript"> | <script type="text/javascript"> |
$(document).ready(function() | $(document).ready(function() |
{ | { |
//hide the all of the element with class msg_body | //hide the all of the element with class msg_body |
$(".msg_body").hide(); | $(".msg_body").hide(); |
//toggle the componenet with class msg_body | //toggle the componenet with class msg_body |
$(".msg_head").click(function() | $(".msg_head").click(function() |
{ | { |
$(this).next(".msg_body").slideToggle(600); | $(this).next(".msg_body").slideToggle(600); |
}); | }); |
}); | }); |
</script> | </script> |
<style type="text/css" title="currentStyle"> | <style type="text/css" title="currentStyle"> |
@import "media/css/demo_table.css"; | @import "media/css/demo_table.css"; |
</style> | </style> |
<script type="text/javascript" language="javascript" src="media/js/jquery.dataTables.js"></script> | <script type="text/javascript" language="javascript" src="media/js/jquery.dataTables.js"></script> |
<script type="text/javascript" language="javascript" src="lib/bootstrap-dropdown.js"></script> | <script type="text/javascript" language="javascript" src="lib/bootstrap-dropdown.js"></script> |
<script type="text/javascript" charset="utf-8"> | <script type="text/javascript" charset="utf-8"> |
jQuery.fn.dataTableExt.aTypes.unshift( | jQuery.fn.dataTableExt.aTypes.unshift( |
function ( sData ) | function ( sData ) |
{ | { |
var sValidChars = "0123456789.-,"; | var sValidChars = "0123456789.-,"; |
var Char; | var Char; |
/* Check the numeric part */ | /* Check the numeric part */ |
for ( i=1 ; i<sData.length ; i++ ) | for ( i=1 ; i<sData.length ; i++ ) |
{ | { |
Char = sData.charAt(i); | Char = sData.charAt(i); |
if (sValidChars.indexOf(Char) == -1) | if (sValidChars.indexOf(Char) == -1) |
{ | { |
return null; | return null; |
} | } |
} | } |
/* Check prefixed by currency */ | /* Check prefixed by currency */ |
if ( sData.charAt(0) == '$' || sData.charAt(0) == '£' ) | if ( sData.charAt(0) == '$' || sData.charAt(0) == '£' ) |
{ | { |
return 'currency'; | return 'currency'; |
} | } |
return null; | return null; |
} | } |
); | ); |
jQuery.fn.dataTableExt.oSort['currency-asc'] = function(a,b) { | jQuery.fn.dataTableExt.oSort['currency-asc'] = function(a,b) { |
/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */ | /* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */ |
var x = a == "-" ? 0 : a.replace( /,/g, "" ); | var x = a == "-" ? 0 : a.replace( /,/g, "" ); |
var y = b == "-" ? 0 : b.replace( /,/g, "" ); | var y = b == "-" ? 0 : b.replace( /,/g, "" ); |
/* Remove the currency sign */ | /* Remove the currency sign */ |
x = x.substring( 1 ); | x = x.substring( 1 ); |
y = y.substring( 1 ); | y = y.substring( 1 ); |
/* Parse and return */ | /* Parse and return */ |
x = parseFloat( x ); | x = parseFloat( x ); |
y = parseFloat( y ); | y = parseFloat( y ); |
return x - y; | return x - y; |
}; | }; |
jQuery.fn.dataTableExt.oSort['currency-desc'] = function(a,b) { | jQuery.fn.dataTableExt.oSort['currency-desc'] = function(a,b) { |
/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */ | /* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */ |
var x = a == "-" ? 0 : a.replace( /,/g, "" ); | var x = a == "-" ? 0 : a.replace( /,/g, "" ); |
var y = b == "-" ? 0 : b.replace( /,/g, "" ); | var y = b == "-" ? 0 : b.replace( /,/g, "" ); |
/* Remove the currency sign */ | /* Remove the currency sign */ |
x = x.substring( 1 ); | x = x.substring( 1 ); |
y = y.substring( 1 ); | y = y.substring( 1 ); |
/* Parse and return */ | /* Parse and return */ |
x = parseFloat( x ); | x = parseFloat( x ); |
y = parseFloat( y ); | y = parseFloat( y ); |
return y - x; | return y - x; |
}; | }; |
$(document).ready(function() { | $(document).ready(function() { |
$('table').dataTable(); | $('table').dataTable(); |
} ); | } ); |
</script> | </script> |
<link type="text/css" rel="stylesheet" href="style.css"> | <link type="text/css" rel="stylesheet" href="style.css"> |
</head> | </head> |
<body> | <body> |
<div class="topbar"> | <div class="topbar"> |
<div class="topbar-inner"> | <div class="topbar-inner"> |
<div class="container-fluid"> | <div class="container-fluid"> |
<a class="brand" href="#">contract dashboard</a> | <a class="brand" href="#">contract dashboard</a> |
<ul class="nav"> | <ul class="nav"> |
<li><a href="displayAgency.php">agencies</a></li> | <li><a href="displayAgency.php">agencies</a></li> |
<li><a href="displaySupplier.php">suppliers</a></li> | <li><a href="displaySupplier.php">suppliers</a></li> |
<li><a href="displayCategory.php">categories</a></li> | <li><a href="displayCategory.php">categories</a></li> |
<li><a href="displayCalendar.php">time periods</a></li> | <li><a href="displayCalendar.php">time periods</a></li> |
<!-- <li class="dropdown"> | <!-- <li class="dropdown"> |
<a href="#" class="dropdown-toggle">metrics</a> | <a href="#" class="dropdown-toggle">metrics</a> |
<ul class="dropdown-menu">--> | <ul class="dropdown-menu">--> |
<li><a href="displayProcurementMethod.php">tenderm</a></li> | <li><a href="displayProcurementMethod.php">tenderm</a></li> |
<li><a href="displayConfidentialities.php">confidentiality</a></li> | <li><a href="displayConfidentialities.php">confidentiality</a></li> |
<li><a href="displayConsultancies.php">consultancies</a></li> | <li><a href="displayConsultancies.php">consultancies</a></li> |
<li><a href="displayAmendments.php">amendments</a></li> | <li><a href="displayAmendments.php">amendments</a></li> |
<li><a href="displayMap.php">geo</a></li> | <li><a href="displayMap.php">geo</a></li> |
<!-- </ul> | <!-- </ul> |
</li>--> | </li>--> |
</ul> | </ul> |
<form method="post" action="search.php" class="pull-right"> | <form method="post" action="search.php" class="pull-right"> |
<input type="text" id="searchKeyword" name="searchKeyword" value="" placeholder="Search" /> | <input type="text" id="searchKeyword" name="searchKeyword" value="" placeholder="Search" /> |
<input type="hidden" id="searchID" name="searchID" value=""/> | <input type="hidden" id="searchID" name="searchID" value=""/> |
</form> | </form> |
</div> | </div> |
</div><!-- /topbar-inner --> | </div><!-- /topbar-inner --> |
</div><!-- /topbar --> | </div><!-- /topbar --> |
</div><!-- /topbar-wrapper --> | </div><!-- /topbar-wrapper --> |
<script type="text/javascript"> | <script type="text/javascript"> |
var options_xml = { | var options_xml = { |
script: function (input) { return "search_autosuggest.php?input="+input; }, | script: function (input) { return "search_autosuggest.php?input="+input; }, |
varname:"input", | varname:"input", |
callback: function (obj) { document.getElementById('searchID').value = obj.id; } | callback: function (obj) { document.getElementById('searchID').value = obj.id; } |
}; | }; |
var as_xml = new bsn.AutoSuggest('searchKeyword', options_xml); | var as_xml = new bsn.AutoSuggest('searchKeyword', options_xml); |
</script> | </script> |
<div class="container-fluid"> | <div class="container-fluid"> |
<div class="sidebar"> | <div class="sidebar"> |
<div class="well"> | <div class="well"> |
Filter by:<li> | Filter by:<li> |
<li>year | <li>year |
<li><li>2008</li> | <li><li>2008</li> |
</li> | </li> |
</li> | </li> |
</li> <br> | </li> <br> |
</div> </div> | </div> </div> |
<div class="content"> | <div class="content"> |
<?php | <?php |
$start = (float) array_sum(explode(' ', microtime())); | $start = (float) array_sum(explode(' ', microtime())); |
} | } |
function include_footer() { | function include_footer() { |
global $start; | global $start; |
$end = (float) array_sum(explode(' ', microtime())); | $end = (float) array_sum(explode(' ', microtime())); |
echo ' <footer>' . "Processing time: " . sprintf("%.4f", ($end - $start)) . " seconds" . ' <footer>'; | echo ' <footer>' . "Processing time: " . sprintf("%.4f", ($end - $start)) . " seconds" . ' <footer>'; |
echo '</div> </div></body> </html>'; | |
} | if (strpos($_SERVER['SERVER_NAME'], ".gs")) { |
?> | |
include ("graphs.inc.php"); | <script type="text/javascript"> |
?> | |
var _gaq = _gaq || []; | |
_gaq.push(['_setAccount', 'UA-12341040-3']); | |
_gaq.push(['_trackPageview']); | |
(function() { | |
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; | |
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'; | |
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); | |
})(); | |
</script> | |
<?php | |
} | |
echo '</div> </div></body> </html>'; | |
} | |
include ("graphs.inc.php"); | |
<?php | |
/** | |
* This module documents the main interface with the OpenID consumer | |
* library. The only part of the library which has to be used and | |
* isn't documented in full here is the store required to create an | |
* Auth_OpenID_Consumer instance. More on the abstract store type and | |
* concrete implementations of it that are provided in the | |
* documentation for the Auth_OpenID_Consumer constructor. | |
* | |
* OVERVIEW | |
* | |
* The OpenID identity verification process most commonly uses the | |
* following steps, as visible to the user of this library: | |
* | |
* 1. The user enters their OpenID into a field on the consumer's | |
* site, and hits a login button. | |
* 2. The consumer site discovers the user's OpenID server using the | |
* YADIS protocol. | |
* 3. The consumer site sends the browser a redirect to the identity | |
* server. This is the authentication request as described in | |
* the OpenID specification. | |
* 4. The identity server's site sends the browser a redirect back | |
* to the consumer site. This redirect contains the server's | |
* response to the authentication request. | |
* | |
* The most important part of the flow to note is the consumer's site | |
* must handle two separate HTTP requests in order to perform the full | |
* identity check. | |
* | |
* LIBRARY DESIGN | |
* | |
* This consumer library is designed with that flow in mind. The goal | |
* is to make it as easy as possible to perform the above steps | |
* securely. | |
* | |
* At a high level, there are two important parts in the consumer | |
* library. The first important part is this module, which contains | |
* the interface to actually use this library. The second is the | |
* Auth_OpenID_Interface class, which describes the interface to use | |
* if you need to create a custom method for storing the state this | |
* library needs to maintain between requests. | |
* | |
* In general, the second part is less important for users of the | |
* library to know about, as several implementations are provided | |
* which cover a wide variety of situations in which consumers may use | |
* the library. | |
* | |
* This module contains a class, Auth_OpenID_Consumer, with methods | |
* corresponding to the actions necessary in each of steps 2, 3, and 4 | |
* described in the overview. Use of this library should be as easy | |
* as creating an Auth_OpenID_Consumer instance and calling the | |
* methods appropriate for the action the site wants to take. | |
* | |
* STORES AND DUMB MODE | |
* | |
* OpenID is a protocol that works best when the consumer site is able | |
* to store some state. This is the normal mode of operation for the | |
* protocol, and is sometimes referred to as smart mode. There is | |
* also a fallback mode, known as dumb mode, which is available when | |
* the consumer site is not able to store state. This mode should be | |
* avoided when possible, as it leaves the implementation more | |
* vulnerable to replay attacks. | |
* | |
* The mode the library works in for normal operation is determined by | |
* the store that it is given. The store is an abstraction that | |
* handles the data that the consumer needs to manage between http | |
* requests in order to operate efficiently and securely. | |
* | |
* Several store implementation are provided, and the interface is | |
* fully documented so that custom stores can be used as well. See | |
* the documentation for the Auth_OpenID_Consumer class for more | |
* information on the interface for stores. The implementations that | |
* are provided allow the consumer site to store the necessary data in | |
* several different ways, including several SQL databases and normal | |
* files on disk. | |
* | |
* There is an additional concrete store provided that puts the system | |
* in dumb mode. This is not recommended, as it removes the library's | |
* ability to stop replay attacks reliably. It still uses time-based | |
* checking to make replay attacks only possible within a small | |
* window, but they remain possible within that window. This store | |
* should only be used if the consumer site has no way to retain data | |
* between requests at all. | |
* | |
* IMMEDIATE MODE | |
* | |
* In the flow described above, the user may need to confirm to the | |
* lidentity server that it's ok to authorize his or her identity. | |
* The server may draw pages asking for information from the user | |
* before it redirects the browser back to the consumer's site. This | |
* is generally transparent to the consumer site, so it is typically | |
* ignored as an implementation detail. | |
* | |
* There can be times, however, where the consumer site wants to get a | |
* response immediately. When this is the case, the consumer can put | |
* the library in immediate mode. In immediate mode, there is an | |
* extra response possible from the server, which is essentially the | |
* server reporting that it doesn't have enough information to answer | |
* the question yet. | |
* | |
* USING THIS LIBRARY | |
* | |
* Integrating this library into an application is usually a | |
* relatively straightforward process. The process should basically | |
* follow this plan: | |
* | |
* Add an OpenID login field somewhere on your site. When an OpenID | |
* is entered in that field and the form is submitted, it should make | |
* a request to the your site which includes that OpenID URL. | |
* | |
* First, the application should instantiate the Auth_OpenID_Consumer | |
* class using the store of choice (Auth_OpenID_FileStore or one of | |
* the SQL-based stores). If the application has a custom | |
* session-management implementation, an object implementing the | |
* {@link Auth_Yadis_PHPSession} interface should be passed as the | |
* second parameter. Otherwise, the default uses $_SESSION. | |
* | |
* Next, the application should call the Auth_OpenID_Consumer object's | |
* 'begin' method. This method takes the OpenID URL. The 'begin' | |
* method returns an Auth_OpenID_AuthRequest object. | |
* | |
* Next, the application should call the 'redirectURL' method of the | |
* Auth_OpenID_AuthRequest object. The 'return_to' URL parameter is | |
* the URL that the OpenID server will send the user back to after | |
* attempting to verify his or her identity. The 'trust_root' is the | |
* URL (or URL pattern) that identifies your web site to the user when | |
* he or she is authorizing it. Send a redirect to the resulting URL | |
* to the user's browser. | |
* | |
* That's the first half of the authentication process. The second | |
* half of the process is done after the user's ID server sends the | |
* user's browser a redirect back to your site to complete their | |
* login. | |
* | |
* When that happens, the user will contact your site at the URL given | |
* as the 'return_to' URL to the Auth_OpenID_AuthRequest::redirectURL | |
* call made above. The request will have several query parameters | |
* added to the URL by the identity server as the information | |
* necessary to finish the request. | |
* | |
* Lastly, instantiate an Auth_OpenID_Consumer instance as above and | |
* call its 'complete' method, passing in all the received query | |
* arguments. | |
* | |
* There are multiple possible return types possible from that | |
* method. These indicate the whether or not the login was successful, | |
* and include any additional information appropriate for their type. | |
* | |
* PHP versions 4 and 5 | |
* | |
* LICENSE: See the COPYING file included in this distribution. | |
* | |
* @package OpenID | |
* @author JanRain, Inc. <openid@janrain.com> | |
* @copyright 2005-2008 Janrain, Inc. | |
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache | |
*/ | |
/** | |
* Require utility classes and functions for the consumer. | |
*/ | |
require_once "Auth/OpenID.php"; | |
require_once "Auth/OpenID/Message.php"; | |
require_once "Auth/OpenID/HMAC.php"; | |
require_once "Auth/OpenID/Association.php"; | |
require_once "Auth/OpenID/CryptUtil.php"; | |
require_once "Auth/OpenID/DiffieHellman.php"; | |
require_once "Auth/OpenID/KVForm.php"; | |
require_once "Auth/OpenID/Nonce.php"; | |
require_once "Auth/OpenID/Discover.php"; | |
require_once "Auth/OpenID/URINorm.php"; | |
require_once "Auth/Yadis/Manager.php"; | |
require_once "Auth/Yadis/XRI.php"; | |
/** | |
* This is the status code returned when the complete method returns | |
* successfully. | |
*/ | |
define('Auth_OpenID_SUCCESS', 'success'); | |
/** | |
* Status to indicate cancellation of OpenID authentication. | |
*/ | |
define('Auth_OpenID_CANCEL', 'cancel'); | |
/** | |
* This is the status code completeAuth returns when the value it | |
* received indicated an invalid login. | |
*/ | |
define('Auth_OpenID_FAILURE', 'failure'); | |
/** | |
* This is the status code completeAuth returns when the | |
* {@link Auth_OpenID_Consumer} instance is in immediate mode, and the | |
* identity server sends back a URL to send the user to to complete his | |
* or her login. | |
*/ | |
define('Auth_OpenID_SETUP_NEEDED', 'setup needed'); | |
/** | |
* This is the status code beginAuth returns when the page fetched | |
* from the entered OpenID URL doesn't contain the necessary link tags | |
* to function as an identity page. | |
*/ | |
define('Auth_OpenID_PARSE_ERROR', 'parse error'); | |
/** | |
* An OpenID consumer implementation that performs discovery and does | |
* session management. See the Consumer.php file documentation for | |
* more information. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_Consumer { | |
/** | |
* @access private | |
*/ | |
var $discoverMethod = 'Auth_OpenID_discover'; | |
/** | |
* @access private | |
*/ | |
var $session_key_prefix = "_openid_consumer_"; | |
/** | |
* @access private | |
*/ | |
var $_token_suffix = "last_token"; | |
/** | |
* Initialize a Consumer instance. | |
* | |
* You should create a new instance of the Consumer object with | |
* every HTTP request that handles OpenID transactions. | |
* | |
* @param Auth_OpenID_OpenIDStore $store This must be an object | |
* that implements the interface in {@link | |
* Auth_OpenID_OpenIDStore}. Several concrete implementations are | |
* provided, to cover most common use cases. For stores backed by | |
* MySQL, PostgreSQL, or SQLite, see the {@link | |
* Auth_OpenID_SQLStore} class and its sublcasses. For a | |
* filesystem-backed store, see the {@link Auth_OpenID_FileStore} | |
* module. As a last resort, if it isn't possible for the server | |
* to store state at all, an instance of {@link | |
* Auth_OpenID_DumbStore} can be used. | |
* | |
* @param mixed $session An object which implements the interface | |
* of the {@link Auth_Yadis_PHPSession} class. Particularly, this | |
* object is expected to have these methods: get($key), set($key), | |
* $value), and del($key). This defaults to a session object | |
* which wraps PHP's native session machinery. You should only | |
* need to pass something here if you have your own sessioning | |
* implementation. | |
* | |
* @param str $consumer_cls The name of the class to instantiate | |
* when creating the internal consumer object. This is used for | |
* testing. | |
*/ | |
function Auth_OpenID_Consumer($store, $session = null, | |
$consumer_cls = null) | |
{ | |
if ($session === null) { | |
$session = new Auth_Yadis_PHPSession(); | |
} | |
$this->session = $session; | |
if ($consumer_cls !== null) { | |
$this->consumer = new $consumer_cls($store); | |
} else { | |
$this->consumer = new Auth_OpenID_GenericConsumer($store); | |
} | |
$this->_token_key = $this->session_key_prefix . $this->_token_suffix; | |
} | |
/** | |
* Used in testing to define the discovery mechanism. | |
* | |
* @access private | |
*/ | |
function getDiscoveryObject($session, $openid_url, | |
$session_key_prefix) | |
{ | |
return new Auth_Yadis_Discovery($session, $openid_url, | |
$session_key_prefix); | |
} | |
/** | |
* Start the OpenID authentication process. See steps 1-2 in the | |
* overview at the top of this file. | |
* | |
* @param string $user_url Identity URL given by the user. This | |
* method performs a textual transformation of the URL to try and | |
* make sure it is normalized. For example, a user_url of | |
* example.com will be normalized to http://example.com/ | |
* normalizing and resolving any redirects the server might issue. | |
* | |
* @param bool $anonymous True if the OpenID request is to be sent | |
* to the server without any identifier information. Use this | |
* when you want to transport data but don't want to do OpenID | |
* authentication with identifiers. | |
* | |
* @return Auth_OpenID_AuthRequest $auth_request An object | |
* containing the discovered information will be returned, with a | |
* method for building a redirect URL to the server, as described | |
* in step 3 of the overview. This object may also be used to add | |
* extension arguments to the request, using its 'addExtensionArg' | |
* method. | |
*/ | |
function begin($user_url, $anonymous=false) | |
{ | |
$openid_url = $user_url; | |
$disco = $this->getDiscoveryObject($this->session, | |
$openid_url, | |
$this->session_key_prefix); | |
// Set the 'stale' attribute of the manager. If discovery | |
// fails in a fatal way, the stale flag will cause the manager | |
// to be cleaned up next time discovery is attempted. | |
$m = $disco->getManager(); | |
$loader = new Auth_Yadis_ManagerLoader(); | |
if ($m) { | |
if ($m->stale) { | |
$disco->destroyManager(); | |
} else { | |
$m->stale = true; | |
$disco->session->set($disco->session_key, | |
serialize($loader->toSession($m))); | |
} | |
} | |
$endpoint = $disco->getNextService($this->discoverMethod, | |
$this->consumer->fetcher); | |
// Reset the 'stale' attribute of the manager. | |
$m = $disco->getManager(); | |
if ($m) { | |
$m->stale = false; | |
$disco->session->set($disco->session_key, | |
serialize($loader->toSession($m))); | |
} | |
if ($endpoint === null) { | |
return null; | |
} else { | |
return $this->beginWithoutDiscovery($endpoint, | |
$anonymous); | |
} | |
} | |
/** | |
* Start OpenID verification without doing OpenID server | |
* discovery. This method is used internally by Consumer.begin | |
* after discovery is performed, and exists to provide an | |
* interface for library users needing to perform their own | |
* discovery. | |
* | |
* @param Auth_OpenID_ServiceEndpoint $endpoint an OpenID service | |
* endpoint descriptor. | |
* | |
* @param bool anonymous Set to true if you want to perform OpenID | |
* without identifiers. | |
* | |
* @return Auth_OpenID_AuthRequest $auth_request An OpenID | |
* authentication request object. | |
*/ | |
function beginWithoutDiscovery($endpoint, $anonymous=false) | |
{ | |
$loader = new Auth_OpenID_ServiceEndpointLoader(); | |
$auth_req = $this->consumer->begin($endpoint); | |
$this->session->set($this->_token_key, | |
$loader->toSession($auth_req->endpoint)); | |
if (!$auth_req->setAnonymous($anonymous)) { | |
return new Auth_OpenID_FailureResponse(null, | |
"OpenID 1 requests MUST include the identifier " . | |
"in the request."); | |
} | |
return $auth_req; | |
} | |
/** | |
* Called to interpret the server's response to an OpenID | |
* request. It is called in step 4 of the flow described in the | |
* consumer overview. | |
* | |
* @param string $current_url The URL used to invoke the application. | |
* Extract the URL from your application's web | |
* request framework and specify it here to have it checked | |
* against the openid.current_url value in the response. If | |
* the current_url URL check fails, the status of the | |
* completion will be FAILURE. | |
* | |
* @param array $query An array of the query parameters (key => | |
* value pairs) for this HTTP request. Defaults to null. If | |
* null, the GET or POST data are automatically gotten from the | |
* PHP environment. It is only useful to override $query for | |
* testing. | |
* | |
* @return Auth_OpenID_ConsumerResponse $response A instance of an | |
* Auth_OpenID_ConsumerResponse subclass. The type of response is | |
* indicated by the status attribute, which will be one of | |
* SUCCESS, CANCEL, FAILURE, or SETUP_NEEDED. | |
*/ | |
function complete($current_url, $query=null) | |
{ | |
if ($current_url && !is_string($current_url)) { | |
// This is ugly, but we need to complain loudly when | |
// someone uses the API incorrectly. | |
trigger_error("current_url must be a string; see NEWS file " . | |
"for upgrading notes.", | |
E_USER_ERROR); | |
} | |
if ($query === null) { | |
$query = Auth_OpenID::getQuery(); | |
} | |
$loader = new Auth_OpenID_ServiceEndpointLoader(); | |
$endpoint_data = $this->session->get($this->_token_key); | |
$endpoint = | |
$loader->fromSession($endpoint_data); | |
$message = Auth_OpenID_Message::fromPostArgs($query); | |
$response = $this->consumer->complete($message, $endpoint, | |
$current_url); | |
$this->session->del($this->_token_key); | |
if (in_array($response->status, array(Auth_OpenID_SUCCESS, | |
Auth_OpenID_CANCEL))) { | |
if ($response->identity_url !== null) { | |
$disco = $this->getDiscoveryObject($this->session, | |
$response->identity_url, | |
$this->session_key_prefix); | |
$disco->cleanup(true); | |
} | |
} | |
return $response; | |
} | |
} | |
/** | |
* A class implementing HMAC/DH-SHA1 consumer sessions. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_DiffieHellmanSHA1ConsumerSession { | |
var $session_type = 'DH-SHA1'; | |
var $hash_func = 'Auth_OpenID_SHA1'; | |
var $secret_size = 20; | |
var $allowed_assoc_types = array('HMAC-SHA1'); | |
function Auth_OpenID_DiffieHellmanSHA1ConsumerSession($dh = null) | |
{ | |
if ($dh === null) { | |
$dh = new Auth_OpenID_DiffieHellman(); | |
} | |
$this->dh = $dh; | |
} | |
function getRequest() | |
{ | |
$math = Auth_OpenID_getMathLib(); | |
$cpub = $math->longToBase64($this->dh->public); | |
$args = array('dh_consumer_public' => $cpub); | |
if (!$this->dh->usingDefaultValues()) { | |
$args = array_merge($args, array( | |
'dh_modulus' => | |
$math->longToBase64($this->dh->mod), | |
'dh_gen' => | |
$math->longToBase64($this->dh->gen))); | |
} | |
return $args; | |
} | |
function extractSecret($response) | |
{ | |
if (!$response->hasKey(Auth_OpenID_OPENID_NS, | |
'dh_server_public')) { | |
return null; | |
} | |
if (!$response->hasKey(Auth_OpenID_OPENID_NS, | |
'enc_mac_key')) { | |
return null; | |
} | |
$math = Auth_OpenID_getMathLib(); | |
$spub = $math->base64ToLong($response->getArg(Auth_OpenID_OPENID_NS, | |
'dh_server_public')); | |
$enc_mac_key = base64_decode($response->getArg(Auth_OpenID_OPENID_NS, | |
'enc_mac_key')); | |
return $this->dh->xorSecret($spub, $enc_mac_key, $this->hash_func); | |
} | |
} | |
/** | |
* A class implementing HMAC/DH-SHA256 consumer sessions. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_DiffieHellmanSHA256ConsumerSession extends | |
Auth_OpenID_DiffieHellmanSHA1ConsumerSession { | |
var $session_type = 'DH-SHA256'; | |
var $hash_func = 'Auth_OpenID_SHA256'; | |
var $secret_size = 32; | |
var $allowed_assoc_types = array('HMAC-SHA256'); | |
} | |
/** | |
* A class implementing plaintext consumer sessions. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_PlainTextConsumerSession { | |
var $session_type = 'no-encryption'; | |
var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256'); | |
function getRequest() | |
{ | |
return array(); | |
} | |
function extractSecret($response) | |
{ | |
if (!$response->hasKey(Auth_OpenID_OPENID_NS, 'mac_key')) { | |
return null; | |
} | |
return base64_decode($response->getArg(Auth_OpenID_OPENID_NS, | |
'mac_key')); | |
} | |
} | |
/** | |
* Returns available session types. | |
*/ | |
function Auth_OpenID_getAvailableSessionTypes() | |
{ | |
$types = array( | |
'no-encryption' => 'Auth_OpenID_PlainTextConsumerSession', | |
'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ConsumerSession', | |
'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ConsumerSession'); | |
return $types; | |
} | |
/** | |
* This class is the interface to the OpenID consumer logic. | |
* Instances of it maintain no per-request state, so they can be | |
* reused (or even used by multiple threads concurrently) as needed. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_GenericConsumer { | |
/** | |
* @access private | |
*/ | |
var $discoverMethod = 'Auth_OpenID_discover'; | |
/** | |
* This consumer's store object. | |
*/ | |
var $store; | |
/** | |
* @access private | |
*/ | |
var $_use_assocs; | |
/** | |
* @access private | |
*/ | |
var $openid1_nonce_query_arg_name = 'janrain_nonce'; | |
/** | |
* Another query parameter that gets added to the return_to for | |
* OpenID 1; if the user's session state is lost, use this claimed | |
* identifier to do discovery when verifying the response. | |
*/ | |
var $openid1_return_to_identifier_name = 'openid1_claimed_id'; | |
/** | |
* This method initializes a new {@link Auth_OpenID_Consumer} | |
* instance to access the library. | |
* | |
* @param Auth_OpenID_OpenIDStore $store This must be an object | |
* that implements the interface in {@link Auth_OpenID_OpenIDStore}. | |
* Several concrete implementations are provided, to cover most common use | |
* cases. For stores backed by MySQL, PostgreSQL, or SQLite, see | |
* the {@link Auth_OpenID_SQLStore} class and its sublcasses. For a | |
* filesystem-backed store, see the {@link Auth_OpenID_FileStore} module. | |
* As a last resort, if it isn't possible for the server to store | |
* state at all, an instance of {@link Auth_OpenID_DumbStore} can be used. | |
* | |
* @param bool $immediate This is an optional boolean value. It | |
* controls whether the library uses immediate mode, as explained | |
* in the module description. The default value is False, which | |
* disables immediate mode. | |
*/ | |
function Auth_OpenID_GenericConsumer($store) | |
{ | |
$this->store = $store; | |
$this->negotiator = Auth_OpenID_getDefaultNegotiator(); | |
$this->_use_assocs = (is_null($this->store) ? false : true); | |
$this->fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); | |
$this->session_types = Auth_OpenID_getAvailableSessionTypes(); | |
} | |
/** | |
* Called to begin OpenID authentication using the specified | |
* {@link Auth_OpenID_ServiceEndpoint}. | |
* | |
* @access private | |
*/ | |
function begin($service_endpoint) | |
{ | |
$assoc = $this->_getAssociation($service_endpoint); | |
$r = new Auth_OpenID_AuthRequest($service_endpoint, $assoc); | |
$r->return_to_args[$this->openid1_nonce_query_arg_name] = | |
Auth_OpenID_mkNonce(); | |
if ($r->message->isOpenID1()) { | |
$r->return_to_args[$this->openid1_return_to_identifier_name] = | |
$r->endpoint->claimed_id; | |
} | |
return $r; | |
} | |
/** | |
* Given an {@link Auth_OpenID_Message}, {@link | |
* Auth_OpenID_ServiceEndpoint} and optional return_to URL, | |
* complete OpenID authentication. | |
* | |
* @access private | |
*/ | |
function complete($message, $endpoint, $return_to) | |
{ | |
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', | |
'<no mode set>'); | |
$mode_methods = array( | |
'cancel' => '_complete_cancel', | |
'error' => '_complete_error', | |
'setup_needed' => '_complete_setup_needed', | |
'id_res' => '_complete_id_res', | |
); | |
$method = Auth_OpenID::arrayGet($mode_methods, $mode, | |
'_completeInvalid'); | |
return call_user_func_array(array($this, $method), | |
array($message, &$endpoint, $return_to)); | |
} | |
/** | |
* @access private | |
*/ | |
function _completeInvalid($message, $endpoint, $unused) | |
{ | |
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode', | |
'<No mode set>'); | |
return new Auth_OpenID_FailureResponse($endpoint, | |
sprintf("Invalid openid.mode '%s'", $mode)); | |
} | |
/** | |
* @access private | |
*/ | |
function _complete_cancel($message, $endpoint, $unused) | |
{ | |
return new Auth_OpenID_CancelResponse($endpoint); | |
} | |
/** | |
* @access private | |
*/ | |
function _complete_error($message, $endpoint, $unused) | |
{ | |
$error = $message->getArg(Auth_OpenID_OPENID_NS, 'error'); | |
$contact = $message->getArg(Auth_OpenID_OPENID_NS, 'contact'); | |
$reference = $message->getArg(Auth_OpenID_OPENID_NS, 'reference'); | |
return new Auth_OpenID_FailureResponse($endpoint, $error, | |
$contact, $reference); | |
} | |
/** | |
* @access private | |
*/ | |
function _complete_setup_needed($message, $endpoint, $unused) | |
{ | |
if (!$message->isOpenID2()) { | |
return $this->_completeInvalid($message, $endpoint); | |
} | |
$user_setup_url = $message->getArg(Auth_OpenID_OPENID2_NS, | |
'user_setup_url'); | |
return new Auth_OpenID_SetupNeededResponse($endpoint, $user_setup_url); | |
} | |
/** | |
* @access private | |
*/ | |
function _complete_id_res($message, $endpoint, $return_to) | |
{ | |
$user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, | |
'user_setup_url'); | |
if ($this->_checkSetupNeeded($message)) { | |
return new Auth_OpenID_SetupNeededResponse( | |
$endpoint, $user_setup_url); | |
} else { | |
return $this->_doIdRes($message, $endpoint, $return_to); | |
} | |
} | |
/** | |
* @access private | |
*/ | |
function _checkSetupNeeded($message) | |
{ | |
// In OpenID 1, we check to see if this is a cancel from | |
// immediate mode by the presence of the user_setup_url | |
// parameter. | |
if ($message->isOpenID1()) { | |
$user_setup_url = $message->getArg(Auth_OpenID_OPENID1_NS, | |
'user_setup_url'); | |
if ($user_setup_url !== null) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* @access private | |
*/ | |
function _doIdRes($message, $endpoint, $return_to) | |
{ | |
// Checks for presence of appropriate fields (and checks | |
// signed list fields) | |
$result = $this->_idResCheckForFields($message); | |
if (Auth_OpenID::isFailure($result)) { | |
return $result; | |
} | |
if (!$this->_checkReturnTo($message, $return_to)) { | |
return new Auth_OpenID_FailureResponse(null, | |
sprintf("return_to does not match return URL. Expected %s, got %s", | |
$return_to, | |
$message->getArg(Auth_OpenID_OPENID_NS, 'return_to'))); | |
} | |
// Verify discovery information: | |
$result = $this->_verifyDiscoveryResults($message, $endpoint); | |
if (Auth_OpenID::isFailure($result)) { | |
return $result; | |
} | |
$endpoint = $result; | |
$result = $this->_idResCheckSignature($message, | |
$endpoint->server_url); | |
if (Auth_OpenID::isFailure($result)) { | |
return $result; | |
} | |
$result = $this->_idResCheckNonce($message, $endpoint); | |
if (Auth_OpenID::isFailure($result)) { | |
return $result; | |
} | |
$signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, 'signed', | |
Auth_OpenID_NO_DEFAULT); | |
if (Auth_OpenID::isFailure($signed_list_str)) { | |
return $signed_list_str; | |
} | |
$signed_list = explode(',', $signed_list_str); | |
$signed_fields = Auth_OpenID::addPrefix($signed_list, "openid."); | |
return new Auth_OpenID_SuccessResponse($endpoint, $message, | |
$signed_fields); | |
} | |
/** | |
* @access private | |
*/ | |
function _checkReturnTo($message, $return_to) | |
{ | |
// Check an OpenID message and its openid.return_to value | |
// against a return_to URL from an application. Return True | |
// on success, False on failure. | |
// Check the openid.return_to args against args in the | |
// original message. | |
$result = Auth_OpenID_GenericConsumer::_verifyReturnToArgs( | |
$message->toPostArgs()); | |
if (Auth_OpenID::isFailure($result)) { | |
return false; | |
} | |
// Check the return_to base URL against the one in the | |
// message. | |
$msg_return_to = $message->getArg(Auth_OpenID_OPENID_NS, | |
'return_to'); | |
if (Auth_OpenID::isFailure($return_to)) { | |
// XXX log me | |
return false; | |
} | |
$return_to_parts = parse_url(Auth_OpenID_urinorm($return_to)); | |
$msg_return_to_parts = parse_url(Auth_OpenID_urinorm($msg_return_to)); | |
// If port is absent from both, add it so it's equal in the | |
// check below. | |
if ((!array_key_exists('port', $return_to_parts)) && | |
(!array_key_exists('port', $msg_return_to_parts))) { | |
$return_to_parts['port'] = null; | |
$msg_return_to_parts['port'] = null; | |
} | |
// If path is absent from both, add it so it's equal in the | |
// check below. | |
if ((!array_key_exists('path', $return_to_parts)) && | |
(!array_key_exists('path', $msg_return_to_parts))) { | |
$return_to_parts['path'] = null; | |
$msg_return_to_parts['path'] = null; | |
} | |
// The URL scheme, authority, and path MUST be the same | |
// between the two URLs. | |
foreach (array('scheme', 'host', 'port', 'path') as $component) { | |
// If the url component is absent in either URL, fail. | |
// There should always be a scheme, host, port, and path. | |
if (!array_key_exists($component, $return_to_parts)) { | |
return false; | |
} | |
if (!array_key_exists($component, $msg_return_to_parts)) { | |
return false; | |
} | |
if (Auth_OpenID::arrayGet($return_to_parts, $component) !== | |
Auth_OpenID::arrayGet($msg_return_to_parts, $component)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/** | |
* @access private | |
*/ | |
function _verifyReturnToArgs($query) | |
{ | |
// Verify that the arguments in the return_to URL are present in this | |
// response. | |
$message = Auth_OpenID_Message::fromPostArgs($query); | |
$return_to = $message->getArg(Auth_OpenID_OPENID_NS, 'return_to'); | |
if (Auth_OpenID::isFailure($return_to)) { | |
return $return_to; | |
} | |
// XXX: this should be checked by _idResCheckForFields | |
if (!$return_to) { | |
return new Auth_OpenID_FailureResponse(null, | |
"Response has no return_to"); | |
} | |
$parsed_url = parse_url($return_to); | |
$q = array(); | |
if (array_key_exists('query', $parsed_url)) { | |
$rt_query = $parsed_url['query']; | |
$q = Auth_OpenID::parse_str($rt_query); | |
} | |
foreach ($q as $rt_key => $rt_value) { | |
if (!array_key_exists($rt_key, $query)) { | |
return new Auth_OpenID_FailureResponse(null, | |
sprintf("return_to parameter %s absent from query", $rt_key)); | |
} else { | |
$value = $query[$rt_key]; | |
if ($rt_value != $value) { | |
return new Auth_OpenID_FailureResponse(null, | |
sprintf("parameter %s value %s does not match " . | |
"return_to value %s", $rt_key, | |
$value, $rt_value)); | |
} | |
} | |
} | |
// Make sure all non-OpenID arguments in the response are also | |
// in the signed return_to. | |
$bare_args = $message->getArgs(Auth_OpenID_BARE_NS); | |
foreach ($bare_args as $key => $value) { | |
if (Auth_OpenID::arrayGet($q, $key) != $value) { | |
return new Auth_OpenID_FailureResponse(null, | |
sprintf("Parameter %s = %s not in return_to URL", | |
$key, $value)); | |
} | |
} | |
return true; | |
} | |
/** | |
* @access private | |
*/ | |
function _idResCheckSignature($message, $server_url) | |
{ | |
$assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, | |
'assoc_handle'); | |
if (Auth_OpenID::isFailure($assoc_handle)) { | |
return $assoc_handle; | |
} | |
$assoc = $this->store->getAssociation($server_url, $assoc_handle); | |
if ($assoc) { | |
if ($assoc->getExpiresIn() <= 0) { | |
// XXX: It might be a good idea sometimes to re-start | |
// the authentication with a new association. Doing it | |
// automatically opens the possibility for | |
// denial-of-service by a server that just returns | |
// expired associations (or really short-lived | |
// associations) | |
return new Auth_OpenID_FailureResponse(null, | |
'Association with ' . $server_url . ' expired'); | |
} | |
if (!$assoc->checkMessageSignature($message)) { | |
// If we get a "bad signature" here, it means that the association | |
// is unrecoverabley corrupted in some way. Any futher attempts | |
// to login with this association is likely to fail. Drop it. | |
$this->store->removeAssociation($server_url, $assoc_handle); | |
return new Auth_OpenID_FailureResponse(null, | |
"Bad signature"); | |
} | |
} else { | |
// It's not an association we know about. Stateless mode | |
// is our only possible path for recovery. XXX - async | |
// framework will not want to block on this call to | |
// _checkAuth. | |
if (!$this->_checkAuth($message, $server_url)) { | |
return new Auth_OpenID_FailureResponse(null, | |
"Server denied check_authentication"); | |
} | |
} | |
return null; | |
} | |
/** | |
* @access private | |
*/ | |
function _verifyDiscoveryResults($message, $endpoint=null) | |
{ | |
if ($message->getOpenIDNamespace() == Auth_OpenID_OPENID2_NS) { | |
return $this->_verifyDiscoveryResultsOpenID2($message, | |
$endpoint); | |
} else { | |
return $this->_verifyDiscoveryResultsOpenID1($message, | |
$endpoint); | |
} | |
} | |
/** | |
* @access private | |
*/ | |
function _verifyDiscoveryResultsOpenID1($message, $endpoint) | |
{ | |
$claimed_id = $message->getArg(Auth_OpenID_BARE_NS, | |
$this->openid1_return_to_identifier_name); | |
if (($endpoint === null) && ($claimed_id === null)) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
'When using OpenID 1, the claimed ID must be supplied, ' . | |
'either by passing it through as a return_to parameter ' . | |
'or by using a session, and supplied to the GenericConsumer ' . | |
'as the argument to complete()'); | |
} else if (($endpoint !== null) && ($claimed_id === null)) { | |
$claimed_id = $endpoint->claimed_id; | |
} | |
$to_match = new Auth_OpenID_ServiceEndpoint(); | |
$to_match->type_uris = array(Auth_OpenID_TYPE_1_1); | |
$to_match->local_id = $message->getArg(Auth_OpenID_OPENID1_NS, | |
'identity'); | |
// Restore delegate information from the initiation phase | |
$to_match->claimed_id = $claimed_id; | |
if ($to_match->local_id === null) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
"Missing required field openid.identity"); | |
} | |
$to_match_1_0 = $to_match->copy(); | |
$to_match_1_0->type_uris = array(Auth_OpenID_TYPE_1_0); | |
if ($endpoint !== null) { | |
$result = $this->_verifyDiscoverySingle($endpoint, $to_match); | |
if (is_a($result, 'Auth_OpenID_TypeURIMismatch')) { | |
$result = $this->_verifyDiscoverySingle($endpoint, | |
$to_match_1_0); | |
} | |
if (Auth_OpenID::isFailure($result)) { | |
// oidutil.log("Error attempting to use stored | |
// discovery information: " + str(e)) | |
// oidutil.log("Attempting discovery to | |
// verify endpoint") | |
} else { | |
return $endpoint; | |
} | |
} | |
// Endpoint is either bad (failed verification) or None | |
return $this->_discoverAndVerify($to_match->claimed_id, | |
array($to_match, $to_match_1_0)); | |
} | |
/** | |
* @access private | |
*/ | |
function _verifyDiscoverySingle($endpoint, $to_match) | |
{ | |
// Every type URI that's in the to_match endpoint has to be | |
// present in the discovered endpoint. | |
foreach ($to_match->type_uris as $type_uri) { | |
if (!$endpoint->usesExtension($type_uri)) { | |
return new Auth_OpenID_TypeURIMismatch($endpoint, | |
"Required type ".$type_uri." not present"); | |
} | |
} | |
// Fragments do not influence discovery, so we can't compare a | |
// claimed identifier with a fragment to discovered | |
// information. | |
list($defragged_claimed_id, $_) = | |
Auth_OpenID::urldefrag($to_match->claimed_id); | |
if ($defragged_claimed_id != $endpoint->claimed_id) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
sprintf('Claimed ID does not match (different subjects!), ' . | |
'Expected %s, got %s', $defragged_claimed_id, | |
$endpoint->claimed_id)); | |
} | |
if ($to_match->getLocalID() != $endpoint->getLocalID()) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
sprintf('local_id mismatch. Expected %s, got %s', | |
$to_match->getLocalID(), $endpoint->getLocalID())); | |
} | |
// If the server URL is None, this must be an OpenID 1 | |
// response, because op_endpoint is a required parameter in | |
// OpenID 2. In that case, we don't actually care what the | |
// discovered server_url is, because signature checking or | |
// check_auth should take care of that check for us. | |
if ($to_match->server_url === null) { | |
if ($to_match->preferredNamespace() != Auth_OpenID_OPENID1_NS) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
"Preferred namespace mismatch (bug)"); | |
} | |
} else if ($to_match->server_url != $endpoint->server_url) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
sprintf('OP Endpoint mismatch. Expected %s, got %s', | |
$to_match->server_url, $endpoint->server_url)); | |
} | |
return null; | |
} | |
/** | |
* @access private | |
*/ | |
function _verifyDiscoveryResultsOpenID2($message, $endpoint) | |
{ | |
$to_match = new Auth_OpenID_ServiceEndpoint(); | |
$to_match->type_uris = array(Auth_OpenID_TYPE_2_0); | |
$to_match->claimed_id = $message->getArg(Auth_OpenID_OPENID2_NS, | |
'claimed_id'); | |
$to_match->local_id = $message->getArg(Auth_OpenID_OPENID2_NS, | |
'identity'); | |
$to_match->server_url = $message->getArg(Auth_OpenID_OPENID2_NS, | |
'op_endpoint'); | |
if ($to_match->server_url === null) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
"OP Endpoint URL missing"); | |
} | |
// claimed_id and identifier must both be present or both be | |
// absent | |
if (($to_match->claimed_id === null) && | |
($to_match->local_id !== null)) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
'openid.identity is present without openid.claimed_id'); | |
} | |
if (($to_match->claimed_id !== null) && | |
($to_match->local_id === null)) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
'openid.claimed_id is present without openid.identity'); | |
} | |
if ($to_match->claimed_id === null) { | |
// This is a response without identifiers, so there's | |
// really no checking that we can do, so return an | |
// endpoint that's for the specified `openid.op_endpoint' | |
return Auth_OpenID_ServiceEndpoint::fromOPEndpointURL( | |
$to_match->server_url); | |
} | |
if (!$endpoint) { | |
// The claimed ID doesn't match, so we have to do | |
// discovery again. This covers not using sessions, OP | |
// identifier endpoints and responses that didn't match | |
// the original request. | |
// oidutil.log('No pre-discovered information supplied.') | |
return $this->_discoverAndVerify($to_match->claimed_id, | |
array($to_match)); | |
} else { | |
// The claimed ID matches, so we use the endpoint that we | |
// discovered in initiation. This should be the most | |
// common case. | |
$result = $this->_verifyDiscoverySingle($endpoint, $to_match); | |
if (Auth_OpenID::isFailure($result)) { | |
$endpoint = $this->_discoverAndVerify($to_match->claimed_id, | |
array($to_match)); | |
if (Auth_OpenID::isFailure($endpoint)) { | |
return $endpoint; | |
} | |
} | |
} | |
// The endpoint we return should have the claimed ID from the | |
// message we just verified, fragment and all. | |
if ($endpoint->claimed_id != $to_match->claimed_id) { | |
$endpoint->claimed_id = $to_match->claimed_id; | |
} | |
return $endpoint; | |
} | |
/** | |
* @access private | |
*/ | |
function _discoverAndVerify($claimed_id, $to_match_endpoints) | |
{ | |
// oidutil.log('Performing discovery on %s' % (claimed_id,)) | |
list($unused, $services) = call_user_func($this->discoverMethod, | |
$claimed_id, | |
$this->fetcher); | |
if (!$services) { | |
return new Auth_OpenID_FailureResponse(null, | |
sprintf("No OpenID information found at %s", | |
$claimed_id)); | |
} | |
return $this->_verifyDiscoveryServices($claimed_id, $services, | |
$to_match_endpoints); | |
} | |
/** | |
* @access private | |
*/ | |
function _verifyDiscoveryServices($claimed_id, | |
$services, $to_match_endpoints) | |
{ | |
// Search the services resulting from discovery to find one | |
// that matches the information from the assertion | |
foreach ($services as $endpoint) { | |
foreach ($to_match_endpoints as $to_match_endpoint) { | |
$result = $this->_verifyDiscoverySingle($endpoint, | |
$to_match_endpoint); | |
if (!Auth_OpenID::isFailure($result)) { | |
// It matches, so discover verification has | |
// succeeded. Return this endpoint. | |
return $endpoint; | |
} | |
} | |
} | |
return new Auth_OpenID_FailureResponse(null, | |
sprintf('No matching endpoint found after discovering %s: %s', | |
$claimed_id, $result->message)); | |
} | |
/** | |
* Extract the nonce from an OpenID 1 response. Return the nonce | |
* from the BARE_NS since we independently check the return_to | |
* arguments are the same as those in the response message. | |
* | |
* See the openid1_nonce_query_arg_name class variable | |
* | |
* @returns $nonce The nonce as a string or null | |
* | |
* @access private | |
*/ | |
function _idResGetNonceOpenID1($message, $endpoint) | |
{ | |
return $message->getArg(Auth_OpenID_BARE_NS, | |
$this->openid1_nonce_query_arg_name); | |
} | |
/** | |
* @access private | |
*/ | |
function _idResCheckNonce($message, $endpoint) | |
{ | |
if ($message->isOpenID1()) { | |
// This indicates that the nonce was generated by the consumer | |
$nonce = $this->_idResGetNonceOpenID1($message, $endpoint); | |
$server_url = ''; | |
} else { | |
$nonce = $message->getArg(Auth_OpenID_OPENID2_NS, | |
'response_nonce'); | |
$server_url = $endpoint->server_url; | |
} | |
if ($nonce === null) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
"Nonce missing from response"); | |
} | |
$parts = Auth_OpenID_splitNonce($nonce); | |
if ($parts === null) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
"Malformed nonce in response"); | |
} | |
list($timestamp, $salt) = $parts; | |
if (!$this->store->useNonce($server_url, $timestamp, $salt)) { | |
return new Auth_OpenID_FailureResponse($endpoint, | |
"Nonce already used or out of range"); | |
} | |
return null; | |
} | |
/** | |
* @access private | |
*/ | |
function _idResCheckForFields($message) | |
{ | |
$basic_fields = array('return_to', 'assoc_handle', 'sig', 'signed'); | |
$basic_sig_fields = array('return_to', 'identity'); | |
$require_fields = array( | |
Auth_OpenID_OPENID2_NS => array_merge($basic_fields, | |
array('op_endpoint')), | |
Auth_OpenID_OPENID1_NS => array_merge($basic_fields, | |
array('identity')) | |
); | |
$require_sigs = array( | |
Auth_OpenID_OPENID2_NS => array_merge($basic_sig_fields, | |
array('response_nonce', | |
'claimed_id', | |
'assoc_handle', | |
'op_endpoint')), | |
Auth_OpenID_OPENID1_NS => array_merge($basic_sig_fields, | |
array('nonce')) | |
); | |
foreach ($require_fields[$message->getOpenIDNamespace()] as $field) { | |
if (!$message->hasKey(Auth_OpenID_OPENID_NS, $field)) { | |
return new Auth_OpenID_FailureResponse(null, | |
"Missing required field '".$field."'"); | |
} | |
} | |
$signed_list_str = $message->getArg(Auth_OpenID_OPENID_NS, | |
'signed', | |
Auth_OpenID_NO_DEFAULT); | |
if (Auth_OpenID::isFailure($signed_list_str)) { | |
return $signed_list_str; | |
} | |
$signed_list = explode(',', $signed_list_str); | |
foreach ($require_sigs[$message->getOpenIDNamespace()] as $field) { | |
// Field is present and not in signed list | |
if ($message->hasKey(Auth_OpenID_OPENID_NS, $field) && | |
(!in_array($field, $signed_list))) { | |
return new Auth_OpenID_FailureResponse(null, | |
"'".$field."' not signed"); | |
} | |
} | |
return null; | |
} | |
/** | |
* @access private | |
*/ | |
function _checkAuth($message, $server_url) | |
{ | |
$request = $this->_createCheckAuthRequest($message); | |
if ($request === null) { | |
return false; | |
} | |
$resp_message = $this->_makeKVPost($request, $server_url); | |
if (($resp_message === null) || | |
(is_a($resp_message, 'Auth_OpenID_ServerErrorContainer'))) { | |
return false; | |
} | |
return $this->_processCheckAuthResponse($resp_message, $server_url); | |
} | |
/** | |
* @access private | |
*/ | |
function _createCheckAuthRequest($message) | |
{ | |
$signed = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); | |
if ($signed) { | |
foreach (explode(',', $signed) as $k) { | |
$value = $message->getAliasedArg($k); | |
if ($value === null) { | |
return null; | |
} | |
} | |
} | |
$ca_message = $message->copy(); | |
$ca_message->setArg(Auth_OpenID_OPENID_NS, 'mode', | |
'check_authentication'); | |
return $ca_message; | |
} | |
/** | |
* @access private | |
*/ | |
function _processCheckAuthResponse($response, $server_url) | |
{ | |
$is_valid = $response->getArg(Auth_OpenID_OPENID_NS, 'is_valid', | |
'false'); | |
$invalidate_handle = $response->getArg(Auth_OpenID_OPENID_NS, | |
'invalidate_handle'); | |
if ($invalidate_handle !== null) { | |
$this->store->removeAssociation($server_url, | |
$invalidate_handle); | |
} | |
if ($is_valid == 'true') { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Adapt a POST response to a Message. | |
* | |
* @param $response Result of a POST to an OpenID endpoint. | |
* | |
* @access private | |
*/ | |
static function _httpResponseToMessage($response, $server_url) | |
{ | |
// Should this function be named Message.fromHTTPResponse instead? | |
$response_message = Auth_OpenID_Message::fromKVForm($response->body); | |
if ($response->status == 400) { | |
return Auth_OpenID_ServerErrorContainer::fromMessage( | |
$response_message); | |
} else if ($response->status != 200 and $response->status != 206) { | |
return null; | |
} | |
return $response_message; | |
} | |
/** | |
* @access private | |
*/ | |
function _makeKVPost($message, $server_url) | |
{ | |
$body = $message->toURLEncoded(); | |
$resp = $this->fetcher->post($server_url, $body); | |
if ($resp === null) { | |
return null; | |
} | |
return $this->_httpResponseToMessage($resp, $server_url); | |
} | |
/** | |
* @access private | |
*/ | |
function _getAssociation($endpoint) | |
{ | |
if (!$this->_use_assocs) { | |
return null; | |
} | |
$assoc = $this->store->getAssociation($endpoint->server_url); | |
if (($assoc === null) || | |
($assoc->getExpiresIn() <= 0)) { | |
$assoc = $this->_negotiateAssociation($endpoint); | |
if ($assoc !== null) { | |
$this->store->storeAssociation($endpoint->server_url, | |
$assoc); | |
} | |
} | |
return $assoc; | |
} | |
/** | |
* Handle ServerErrors resulting from association requests. | |
* | |
* @return $result If server replied with an C{unsupported-type} | |
* error, return a tuple of supported C{association_type}, | |
* C{session_type}. Otherwise logs the error and returns null. | |
* | |
* @access private | |
*/ | |
function _extractSupportedAssociationType($server_error, $endpoint, | |
$assoc_type) | |
{ | |
// Any error message whose code is not 'unsupported-type' | |
// should be considered a total failure. | |
if (($server_error->error_code != 'unsupported-type') || | |
($server_error->message->isOpenID1())) { | |
return null; | |
} | |
// The server didn't like the association/session type that we | |
// sent, and it sent us back a message that might tell us how | |
// to handle it. | |
// Extract the session_type and assoc_type from the error | |
// message | |
$assoc_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS, | |
'assoc_type'); | |
$session_type = $server_error->message->getArg(Auth_OpenID_OPENID_NS, | |
'session_type'); | |
if (($assoc_type === null) || ($session_type === null)) { | |
return null; | |
} else if (!$this->negotiator->isAllowed($assoc_type, | |
$session_type)) { | |
return null; | |
} else { | |
return array($assoc_type, $session_type); | |
} | |
} | |
/** | |
* @access private | |
*/ | |
function _negotiateAssociation($endpoint) | |
{ | |
// Get our preferred session/association type from the negotiatior. | |
list($assoc_type, $session_type) = $this->negotiator->getAllowedType(); | |
$assoc = $this->_requestAssociation( | |
$endpoint, $assoc_type, $session_type); | |
if (Auth_OpenID::isFailure($assoc)) { | |
return null; | |
} | |
if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { | |
$why = $assoc; | |
$supportedTypes = $this->_extractSupportedAssociationType( | |
$why, $endpoint, $assoc_type); | |
if ($supportedTypes !== null) { | |
list($assoc_type, $session_type) = $supportedTypes; | |
// Attempt to create an association from the assoc_type | |
// and session_type that the server told us it | |
// supported. | |
$assoc = $this->_requestAssociation( | |
$endpoint, $assoc_type, $session_type); | |
if (is_a($assoc, 'Auth_OpenID_ServerErrorContainer')) { | |
// Do not keep trying, since it rejected the | |
// association type that it told us to use. | |
// oidutil.log('Server %s refused its suggested association | |
// 'type: session_type=%s, assoc_type=%s' | |
// % (endpoint.server_url, session_type, | |
// assoc_type)) | |
return null; | |
} else { | |
return $assoc; | |
} | |
} else { | |
return null; | |
} | |
} else { | |
return $assoc; | |
} | |
} | |
/** | |
* @access private | |
*/ | |
function _requestAssociation($endpoint, $assoc_type, $session_type) | |
{ | |
list($assoc_session, $args) = $this->_createAssociateRequest( | |
$endpoint, $assoc_type, $session_type); | |
$response_message = $this->_makeKVPost($args, $endpoint->server_url); | |
if ($response_message === null) { | |
// oidutil.log('openid.associate request failed: %s' % (why[0],)) | |
return null; | |
} else if (is_a($response_message, | |
'Auth_OpenID_ServerErrorContainer')) { | |
return $response_message; | |
} | |
return $this->_extractAssociation($response_message, $assoc_session); | |
} | |
/** | |
* @access private | |
*/ | |
function _extractAssociation($assoc_response, $assoc_session) | |
{ | |
// Extract the common fields from the response, raising an | |
// exception if they are not found | |
$assoc_type = $assoc_response->getArg( | |
Auth_OpenID_OPENID_NS, 'assoc_type', | |
Auth_OpenID_NO_DEFAULT); | |
if (Auth_OpenID::isFailure($assoc_type)) { | |
return $assoc_type; | |
} | |
$assoc_handle = $assoc_response->getArg( | |
Auth_OpenID_OPENID_NS, 'assoc_handle', | |
Auth_OpenID_NO_DEFAULT); | |
if (Auth_OpenID::isFailure($assoc_handle)) { | |
return $assoc_handle; | |
} | |
// expires_in is a base-10 string. The Python parsing will | |
// accept literals that have whitespace around them and will | |
// accept negative values. Neither of these are really in-spec, | |
// but we think it's OK to accept them. | |
$expires_in_str = $assoc_response->getArg( | |
Auth_OpenID_OPENID_NS, 'expires_in', | |
Auth_OpenID_NO_DEFAULT); | |
if (Auth_OpenID::isFailure($expires_in_str)) { | |
return $expires_in_str; | |
} | |
$expires_in = Auth_OpenID::intval($expires_in_str); | |
if ($expires_in === false) { | |
$err = sprintf("Could not parse expires_in from association ". | |
"response %s", print_r($assoc_response, true)); | |
return new Auth_OpenID_FailureResponse(null, $err); | |
} | |
// OpenID 1 has funny association session behaviour. | |
if ($assoc_response->isOpenID1()) { | |
$session_type = $this->_getOpenID1SessionType($assoc_response); | |
} else { | |
$session_type = $assoc_response->getArg( | |
Auth_OpenID_OPENID2_NS, 'session_type', | |
Auth_OpenID_NO_DEFAULT); | |
if (Auth_OpenID::isFailure($session_type)) { | |
return $session_type; | |
} | |
} | |
// Session type mismatch | |
if ($assoc_session->session_type != $session_type) { | |
if ($assoc_response->isOpenID1() && | |
($session_type == 'no-encryption')) { | |
// In OpenID 1, any association request can result in | |
// a 'no-encryption' association response. Setting | |
// assoc_session to a new no-encryption session should | |
// make the rest of this function work properly for | |
// that case. | |
$assoc_session = new Auth_OpenID_PlainTextConsumerSession(); | |
} else { | |
// Any other mismatch, regardless of protocol version | |
// results in the failure of the association session | |
// altogether. | |
return null; | |
} | |
} | |
// Make sure assoc_type is valid for session_type | |
if (!in_array($assoc_type, $assoc_session->allowed_assoc_types)) { | |
return null; | |
} | |
// Delegate to the association session to extract the secret | |
// from the response, however is appropriate for that session | |
// type. | |
$secret = $assoc_session->extractSecret($assoc_response); | |
if ($secret === null) { | |
return null; | |
} | |
return Auth_OpenID_Association::fromExpiresIn( | |
$expires_in, $assoc_handle, $secret, $assoc_type); | |
} | |
/** | |
* @access private | |
*/ | |
function _createAssociateRequest($endpoint, $assoc_type, $session_type) | |
{ | |
if (array_key_exists($session_type, $this->session_types)) { | |
$session_type_class = $this->session_types[$session_type]; | |
if (is_callable($session_type_class)) { | |
$assoc_session = $session_type_class(); | |
} else { | |
$assoc_session = new $session_type_class(); | |
} | |
} else { | |
return null; | |
} | |
$args = array( | |
'mode' => 'associate', | |
'assoc_type' => $assoc_type); | |
if (!$endpoint->compatibilityMode()) { | |
$args['ns'] = Auth_OpenID_OPENID2_NS; | |
} | |
// Leave out the session type if we're in compatibility mode | |
// *and* it's no-encryption. | |
if ((!$endpoint->compatibilityMode()) || | |
($assoc_session->session_type != 'no-encryption')) { | |
$args['session_type'] = $assoc_session->session_type; | |
} | |
$args = array_merge($args, $assoc_session->getRequest()); | |
$message = Auth_OpenID_Message::fromOpenIDArgs($args); | |
return array($assoc_session, $message); | |
} | |
/** | |
* Given an association response message, extract the OpenID 1.X | |
* session type. | |
* | |
* This function mostly takes care of the 'no-encryption' default | |
* behavior in OpenID 1. | |
* | |
* If the association type is plain-text, this function will | |
* return 'no-encryption' | |
* | |
* @access private | |
* @return $typ The association type for this message | |
*/ | |
function _getOpenID1SessionType($assoc_response) | |
{ | |
// If it's an OpenID 1 message, allow session_type to default | |
// to None (which signifies "no-encryption") | |
$session_type = $assoc_response->getArg(Auth_OpenID_OPENID1_NS, | |
'session_type'); | |
// Handle the differences between no-encryption association | |
// respones in OpenID 1 and 2: | |
// no-encryption is not really a valid session type for OpenID | |
// 1, but we'll accept it anyway, while issuing a warning. | |
if ($session_type == 'no-encryption') { | |
// oidutil.log('WARNING: OpenID server sent "no-encryption"' | |
// 'for OpenID 1.X') | |
} else if (($session_type == '') || ($session_type === null)) { | |
// Missing or empty session type is the way to flag a | |
// 'no-encryption' response. Change the session type to | |
// 'no-encryption' so that it can be handled in the same | |
// way as OpenID 2 'no-encryption' respones. | |
$session_type = 'no-encryption'; | |
} | |
return $session_type; | |
} | |
} | |
/** | |
* This class represents an authentication request from a consumer to | |
* an OpenID server. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_AuthRequest { | |
/** | |
* Initialize an authentication request with the specified token, | |
* association, and endpoint. | |
* | |
* Users of this library should not create instances of this | |
* class. Instances of this class are created by the library when | |
* needed. | |
*/ | |
function Auth_OpenID_AuthRequest($endpoint, $assoc) | |
{ | |
$this->assoc = $assoc; | |
$this->endpoint = $endpoint; | |
$this->return_to_args = array(); | |
$this->message = new Auth_OpenID_Message( | |
$endpoint->preferredNamespace()); | |
$this->_anonymous = false; | |
} | |
/** | |
* Add an extension to this checkid request. | |
* | |
* $extension_request: An object that implements the extension | |
* request interface for adding arguments to an OpenID message. | |
*/ | |
function addExtension($extension_request) | |
{ | |
$extension_request->toMessage($this->message); | |
} | |
/** | |
* Add an extension argument to this OpenID authentication | |
* request. | |
* | |
* Use caution when adding arguments, because they will be | |
* URL-escaped and appended to the redirect URL, which can easily | |
* get quite long. | |
* | |
* @param string $namespace The namespace for the extension. For | |
* example, the simple registration extension uses the namespace | |
* 'sreg'. | |
* | |
* @param string $key The key within the extension namespace. For | |
* example, the nickname field in the simple registration | |
* extension's key is 'nickname'. | |
* | |
* @param string $value The value to provide to the server for | |
* this argument. | |
*/ | |
function addExtensionArg($namespace, $key, $value) | |
{ | |
return $this->message->setArg($namespace, $key, $value); | |
} | |
/** | |
* Set whether this request should be made anonymously. If a | |
* request is anonymous, the identifier will not be sent in the | |
* request. This is only useful if you are making another kind of | |
* request with an extension in this request. | |
* | |
* Anonymous requests are not allowed when the request is made | |
* with OpenID 1. | |
*/ | |
function setAnonymous($is_anonymous) | |
{ | |
if ($is_anonymous && $this->message->isOpenID1()) { | |
return false; | |
} else { | |
$this->_anonymous = $is_anonymous; | |
return true; | |
} | |
} | |
/** | |
* Produce a {@link Auth_OpenID_Message} representing this | |
* request. | |
* | |
* @param string $realm The URL (or URL pattern) that identifies | |
* your web site to the user when she is authorizing it. | |
* | |
* @param string $return_to The URL that the OpenID provider will | |
* send the user back to after attempting to verify her identity. | |
* | |
* Not specifying a return_to URL means that the user will not be | |
* returned to the site issuing the request upon its completion. | |
* | |
* @param bool $immediate If true, the OpenID provider is to send | |
* back a response immediately, useful for behind-the-scenes | |
* authentication attempts. Otherwise the OpenID provider may | |
* engage the user before providing a response. This is the | |
* default case, as the user may need to provide credentials or | |
* approve the request before a positive response can be sent. | |
*/ | |
function getMessage($realm, $return_to=null, $immediate=false) | |
{ | |
if ($return_to) { | |
$return_to = Auth_OpenID::appendArgs($return_to, | |
$this->return_to_args); | |
} else if ($immediate) { | |
// raise ValueError( | |
// '"return_to" is mandatory when | |
//using "checkid_immediate"') | |
return new Auth_OpenID_FailureResponse(null, | |
"'return_to' is mandatory when using checkid_immediate"); | |
} else if ($this->message->isOpenID1()) { | |
// raise ValueError('"return_to" is | |
// mandatory for OpenID 1 requests') | |
return new Auth_OpenID_FailureResponse(null, | |
"'return_to' is mandatory for OpenID 1 requests"); | |
} else if ($this->return_to_args) { | |
// raise ValueError('extra "return_to" arguments | |
// were specified, but no return_to was specified') | |
return new Auth_OpenID_FailureResponse(null, | |
"extra 'return_to' arguments where specified, " . | |
"but no return_to was specified"); | |
} | |
if ($immediate) { | |
$mode = 'checkid_immediate'; | |
} else { | |
$mode = 'checkid_setup'; | |
} | |
$message = $this->message->copy(); | |
if ($message->isOpenID1()) { | |
$realm_key = 'trust_root'; | |
} else { | |
$realm_key = 'realm'; | |
} | |
$message->updateArgs(Auth_OpenID_OPENID_NS, | |
array( | |
$realm_key => $realm, | |
'mode' => $mode, | |
'return_to' => $return_to)); | |
if (!$this->_anonymous) { | |
if ($this->endpoint->isOPIdentifier()) { | |
// This will never happen when we're in compatibility | |
// mode, as long as isOPIdentifier() returns False | |
// whenever preferredNamespace() returns OPENID1_NS. | |
$claimed_id = $request_identity = | |
Auth_OpenID_IDENTIFIER_SELECT; | |
} else { | |
$request_identity = $this->endpoint->getLocalID(); | |
$claimed_id = $this->endpoint->claimed_id; | |
} | |
// This is true for both OpenID 1 and 2 | |
$message->setArg(Auth_OpenID_OPENID_NS, 'identity', | |
$request_identity); | |
if ($message->isOpenID2()) { | |
$message->setArg(Auth_OpenID_OPENID2_NS, 'claimed_id', | |
$claimed_id); | |
} | |
} | |
if ($this->assoc) { | |
$message->setArg(Auth_OpenID_OPENID_NS, 'assoc_handle', | |
$this->assoc->handle); | |
} | |
return $message; | |
} | |
function redirectURL($realm, $return_to = null, | |
$immediate = false) | |
{ | |
$message = $this->getMessage($realm, $return_to, $immediate); | |
if (Auth_OpenID::isFailure($message)) { | |
return $message; | |
} | |
return $message->toURL($this->endpoint->server_url); | |
} | |
/** | |
* Get html for a form to submit this request to the IDP. | |
* | |
* form_tag_attrs: An array of attributes to be added to the form | |
* tag. 'accept-charset' and 'enctype' have defaults that can be | |
* overridden. If a value is supplied for 'action' or 'method', it | |
* will be replaced. | |
*/ | |
function formMarkup($realm, $return_to=null, $immediate=false, | |
$form_tag_attrs=null) | |
{ | |
$message = $this->getMessage($realm, $return_to, $immediate); | |
if (Auth_OpenID::isFailure($message)) { | |
return $message; | |
} | |
return $message->toFormMarkup($this->endpoint->server_url, | |
$form_tag_attrs); | |
} | |
/** | |
* Get a complete html document that will autosubmit the request | |
* to the IDP. | |
* | |
* Wraps formMarkup. See the documentation for that function. | |
*/ | |
function htmlMarkup($realm, $return_to=null, $immediate=false, | |
$form_tag_attrs=null) | |
{ | |
$form = $this->formMarkup($realm, $return_to, $immediate, | |
$form_tag_attrs); | |
if (Auth_OpenID::isFailure($form)) { | |
return $form; | |
} | |
return Auth_OpenID::autoSubmitHTML($form); | |
} | |
function shouldSendRedirect() | |
{ | |
return $this->endpoint->compatibilityMode(); | |
} | |
} | |
/** | |
* The base class for responses from the Auth_OpenID_Consumer. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_ConsumerResponse { | |
var $status = null; | |
function setEndpoint($endpoint) | |
{ | |
$this->endpoint = $endpoint; | |
if ($endpoint === null) { | |
$this->identity_url = null; | |
} else { | |
$this->identity_url = $endpoint->claimed_id; | |
} | |
} | |
/** | |
* Return the display identifier for this response. | |
* | |
* The display identifier is related to the Claimed Identifier, but the | |
* two are not always identical. The display identifier is something the | |
* user should recognize as what they entered, whereas the response's | |
* claimed identifier (in the identity_url attribute) may have extra | |
* information for better persistence. | |
* | |
* URLs will be stripped of their fragments for display. XRIs will | |
* display the human-readable identifier (i-name) instead of the | |
* persistent identifier (i-number). | |
* | |
* Use the display identifier in your user interface. Use | |
* identity_url for querying your database or authorization server. | |
* | |
*/ | |
function getDisplayIdentifier() | |
{ | |
if ($this->endpoint !== null) { | |
return $this->endpoint->getDisplayIdentifier(); | |
} | |
return null; | |
} | |
} | |
/** | |
* A response with a status of Auth_OpenID_SUCCESS. Indicates that | |
* this request is a successful acknowledgement from the OpenID server | |
* that the supplied URL is, indeed controlled by the requesting | |
* agent. This has three relevant attributes: | |
* | |
* claimed_id - The identity URL that has been authenticated | |
* | |
* signed_args - The arguments in the server's response that were | |
* signed and verified. | |
* | |
* status - Auth_OpenID_SUCCESS. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_SuccessResponse extends Auth_OpenID_ConsumerResponse { | |
var $status = Auth_OpenID_SUCCESS; | |
/** | |
* @access private | |
*/ | |
function Auth_OpenID_SuccessResponse($endpoint, $message, $signed_args=null) | |
{ | |
$this->endpoint = $endpoint; | |
$this->identity_url = $endpoint->claimed_id; | |
$this->signed_args = $signed_args; | |
$this->message = $message; | |
if ($this->signed_args === null) { | |
$this->signed_args = array(); | |
} | |
} | |
/** | |
* Extract signed extension data from the server's response. | |
* | |
* @param string $prefix The extension namespace from which to | |
* extract the extension data. | |
*/ | |
function extensionResponse($namespace_uri, $require_signed) | |
{ | |
if ($require_signed) { | |
return $this->getSignedNS($namespace_uri); | |
} else { | |
return $this->message->getArgs($namespace_uri); | |
} | |
} | |
function isOpenID1() | |
{ | |
return $this->message->isOpenID1(); | |
} | |
function isSigned($ns_uri, $ns_key) | |
{ | |
// Return whether a particular key is signed, regardless of | |
// its namespace alias | |
return in_array($this->message->getKey($ns_uri, $ns_key), | |
$this->signed_args); | |
} | |
function getSigned($ns_uri, $ns_key, $default = null) | |
{ | |
// Return the specified signed field if available, otherwise | |
// return default | |
if ($this->isSigned($ns_uri, $ns_key)) { | |
return $this->message->getArg($ns_uri, $ns_key, $default); | |
} else { | |
return $default; | |
} | |
} | |
function getSignedNS($ns_uri) | |
{ | |
$args = array(); | |
$msg_args = $this->message->getArgs($ns_uri); | |
if (Auth_OpenID::isFailure($msg_args)) { | |
return null; | |
} | |
foreach ($msg_args as $key => $value) { | |
if (!$this->isSigned($ns_uri, $key)) { | |
unset($msg_args[$key]); | |
} | |
} | |
return $msg_args; | |
} | |
/** | |
* Get the openid.return_to argument from this response. | |
* | |
* This is useful for verifying that this request was initiated by | |
* this consumer. | |
* | |
* @return string $return_to The return_to URL supplied to the | |
* server on the initial request, or null if the response did not | |
* contain an 'openid.return_to' argument. | |
*/ | |
function getReturnTo() | |
{ | |
return $this->getSigned(Auth_OpenID_OPENID_NS, 'return_to'); | |
} | |
} | |
/** | |
* A response with a status of Auth_OpenID_FAILURE. Indicates that the | |
* OpenID protocol has failed. This could be locally or remotely | |
* triggered. This has three relevant attributes: | |
* | |
* claimed_id - The identity URL for which authentication was | |
* attempted, if it can be determined. Otherwise, null. | |
* | |
* message - A message indicating why the request failed, if one is | |
* supplied. Otherwise, null. | |
* | |
* status - Auth_OpenID_FAILURE. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_FailureResponse extends Auth_OpenID_ConsumerResponse { | |
var $status = Auth_OpenID_FAILURE; | |
function Auth_OpenID_FailureResponse($endpoint, $message = null, | |
$contact = null, $reference = null) | |
{ | |
$this->setEndpoint($endpoint); | |
$this->message = $message; | |
$this->contact = $contact; | |
$this->reference = $reference; | |
} | |
} | |
/** | |
* A specific, internal failure used to detect type URI mismatch. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_TypeURIMismatch extends Auth_OpenID_FailureResponse { | |
} | |
/** | |
* Exception that is raised when the server returns a 400 response | |
* code to a direct request. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_ServerErrorContainer { | |
function Auth_OpenID_ServerErrorContainer($error_text, | |
$error_code, | |
$message) | |
{ | |
$this->error_text = $error_text; | |
$this->error_code = $error_code; | |
$this->message = $message; | |
} | |
/** | |
* @access private | |
*/ | |
static function fromMessage($message) | |
{ | |
$error_text = $message->getArg( | |
Auth_OpenID_OPENID_NS, 'error', '<no error message supplied>'); | |
$error_code = $message->getArg(Auth_OpenID_OPENID_NS, 'error_code'); | |
return new Auth_OpenID_ServerErrorContainer($error_text, | |
$error_code, | |
$message); | |
} | |
} | |
/** | |
* A response with a status of Auth_OpenID_CANCEL. Indicates that the | |
* user cancelled the OpenID authentication request. This has two | |
* relevant attributes: | |
* | |
* claimed_id - The identity URL for which authentication was | |
* attempted, if it can be determined. Otherwise, null. | |
* | |
* status - Auth_OpenID_SUCCESS. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_CancelResponse extends Auth_OpenID_ConsumerResponse { | |
var $status = Auth_OpenID_CANCEL; | |
function Auth_OpenID_CancelResponse($endpoint) | |
{ | |
$this->setEndpoint($endpoint); | |
} | |
} | |
/** | |
* A response with a status of Auth_OpenID_SETUP_NEEDED. Indicates | |
* that the request was in immediate mode, and the server is unable to | |
* authenticate the user without further interaction. | |
* | |
* claimed_id - The identity URL for which authentication was | |
* attempted. | |
* | |
* setup_url - A URL that can be used to send the user to the server | |
* to set up for authentication. The user should be redirected in to | |
* the setup_url, either in the current window or in a new browser | |
* window. Null in OpenID 2. | |
* | |
* status - Auth_OpenID_SETUP_NEEDED. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_SetupNeededResponse extends Auth_OpenID_ConsumerResponse { | |
var $status = Auth_OpenID_SETUP_NEEDED; | |
function Auth_OpenID_SetupNeededResponse($endpoint, | |
$setup_url = null) | |
{ | |
$this->setEndpoint($endpoint); | |
$this->setup_url = $setup_url; | |
} | |
} | |
<?php | |
/** | |
* OpenID server protocol and logic. | |
* | |
* Overview | |
* | |
* An OpenID server must perform three tasks: | |
* | |
* 1. Examine the incoming request to determine its nature and validity. | |
* 2. Make a decision about how to respond to this request. | |
* 3. Format the response according to the protocol. | |
* | |
* The first and last of these tasks may performed by the {@link | |
* Auth_OpenID_Server::decodeRequest()} and {@link | |
* Auth_OpenID_Server::encodeResponse} methods. Who gets to do the | |
* intermediate task -- deciding how to respond to the request -- will | |
* depend on what type of request it is. | |
* | |
* If it's a request to authenticate a user (a 'checkid_setup' or | |
* 'checkid_immediate' request), you need to decide if you will assert | |
* that this user may claim the identity in question. Exactly how you | |
* do that is a matter of application policy, but it generally | |
* involves making sure the user has an account with your system and | |
* is logged in, checking to see if that identity is hers to claim, | |
* and verifying with the user that she does consent to releasing that | |
* information to the party making the request. | |
* | |
* Examine the properties of the {@link Auth_OpenID_CheckIDRequest} | |
* object, and if and when you've come to a decision, form a response | |
* by calling {@link Auth_OpenID_CheckIDRequest::answer()}. | |
* | |
* Other types of requests relate to establishing associations between | |
* client and server and verifing the authenticity of previous | |
* communications. {@link Auth_OpenID_Server} contains all the logic | |
* and data necessary to respond to such requests; just pass it to | |
* {@link Auth_OpenID_Server::handleRequest()}. | |
* | |
* OpenID Extensions | |
* | |
* Do you want to provide other information for your users in addition | |
* to authentication? Version 1.2 of the OpenID protocol allows | |
* consumers to add extensions to their requests. For example, with | |
* sites using the Simple Registration | |
* Extension | |
* (http://openid.net/specs/openid-simple-registration-extension-1_0.html), | |
* a user can agree to have their nickname and e-mail address sent to | |
* a site when they sign up. | |
* | |
* Since extensions do not change the way OpenID authentication works, | |
* code to handle extension requests may be completely separate from | |
* the {@link Auth_OpenID_Request} class here. But you'll likely want | |
* data sent back by your extension to be signed. {@link | |
* Auth_OpenID_ServerResponse} provides methods with which you can add | |
* data to it which can be signed with the other data in the OpenID | |
* signature. | |
* | |
* For example: | |
* | |
* <pre> // when request is a checkid_* request | |
* $response = $request->answer(true); | |
* // this will a signed 'openid.sreg.timezone' parameter to the response | |
* response.addField('sreg', 'timezone', 'America/Los_Angeles')</pre> | |
* | |
* Stores | |
* | |
* The OpenID server needs to maintain state between requests in order | |
* to function. Its mechanism for doing this is called a store. The | |
* store interface is defined in Interface.php. Additionally, several | |
* concrete store implementations are provided, so that most sites | |
* won't need to implement a custom store. For a store backed by flat | |
* files on disk, see {@link Auth_OpenID_FileStore}. For stores based | |
* on MySQL, SQLite, or PostgreSQL, see the {@link | |
* Auth_OpenID_SQLStore} subclasses. | |
* | |
* Upgrading | |
* | |
* The keys by which a server looks up associations in its store have | |
* changed in version 1.2 of this library. If your store has entries | |
* created from version 1.0 code, you should empty it. | |
* | |
* PHP versions 4 and 5 | |
* | |
* LICENSE: See the COPYING file included in this distribution. | |
* | |
* @package OpenID | |
* @author JanRain, Inc. <openid@janrain.com> | |
* @copyright 2005-2008 Janrain, Inc. | |
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache | |
*/ | |
/** | |
* Required imports | |
*/ | |
require_once "Auth/OpenID.php"; | |
require_once "Auth/OpenID/Association.php"; | |
require_once "Auth/OpenID/CryptUtil.php"; | |
require_once "Auth/OpenID/BigMath.php"; | |
require_once "Auth/OpenID/DiffieHellman.php"; | |
require_once "Auth/OpenID/KVForm.php"; | |
require_once "Auth/OpenID/TrustRoot.php"; | |
require_once "Auth/OpenID/ServerRequest.php"; | |
require_once "Auth/OpenID/Message.php"; | |
require_once "Auth/OpenID/Nonce.php"; | |
define('AUTH_OPENID_HTTP_OK', 200); | |
define('AUTH_OPENID_HTTP_REDIRECT', 302); | |
define('AUTH_OPENID_HTTP_ERROR', 400); | |
/** | |
* @access private | |
*/ | |
global $_Auth_OpenID_Request_Modes; | |
$_Auth_OpenID_Request_Modes = array('checkid_setup', | |
'checkid_immediate'); | |
/** | |
* @access private | |
*/ | |
define('Auth_OpenID_ENCODE_KVFORM', 'kfvorm'); | |
/** | |
* @access private | |
*/ | |
define('Auth_OpenID_ENCODE_URL', 'URL/redirect'); | |
/** | |
* @access private | |
*/ | |
define('Auth_OpenID_ENCODE_HTML_FORM', 'HTML form'); | |
/** | |
* @access private | |
*/ | |
function Auth_OpenID_isError($obj, $cls = 'Auth_OpenID_ServerError') | |
{ | |
return is_a($obj, $cls); | |
} | |
/** | |
* An error class which gets instantiated and returned whenever an | |
* OpenID protocol error occurs. Be prepared to use this in place of | |
* an ordinary server response. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_ServerError { | |
/** | |
* @access private | |
*/ | |
function Auth_OpenID_ServerError($message = null, $text = null, | |
$reference = null, $contact = null) | |
{ | |
$this->message = $message; | |
$this->text = $text; | |
$this->contact = $contact; | |
$this->reference = $reference; | |
} | |
function getReturnTo() | |
{ | |
if ($this->message && | |
$this->message->hasKey(Auth_OpenID_OPENID_NS, 'return_to')) { | |
return $this->message->getArg(Auth_OpenID_OPENID_NS, | |
'return_to'); | |
} else { | |
return null; | |
} | |
} | |
/** | |
* Returns the return_to URL for the request which caused this | |
* error. | |
*/ | |
function hasReturnTo() | |
{ | |
return $this->getReturnTo() !== null; | |
} | |
/** | |
* Encodes this error's response as a URL suitable for | |
* redirection. If the response has no return_to, another | |
* Auth_OpenID_ServerError is returned. | |
*/ | |
function encodeToURL() | |
{ | |
if (!$this->message) { | |
return null; | |
} | |
$msg = $this->toMessage(); | |
return $msg->toURL($this->getReturnTo()); | |
} | |
/** | |
* Encodes the response to key-value form. This is a | |
* machine-readable format used to respond to messages which came | |
* directly from the consumer and not through the user-agent. See | |
* the OpenID specification. | |
*/ | |
function encodeToKVForm() | |
{ | |
return Auth_OpenID_KVForm::fromArray( | |
array('mode' => 'error', | |
'error' => $this->toString())); | |
} | |
function toFormMarkup($form_tag_attrs=null) | |
{ | |
$msg = $this->toMessage(); | |
return $msg->toFormMarkup($this->getReturnTo(), $form_tag_attrs); | |
} | |
function toHTML($form_tag_attrs=null) | |
{ | |
return Auth_OpenID::autoSubmitHTML( | |
$this->toFormMarkup($form_tag_attrs)); | |
} | |
function toMessage() | |
{ | |
// Generate a Message object for sending to the relying party, | |
// after encoding. | |
$namespace = $this->message->getOpenIDNamespace(); | |
$reply = new Auth_OpenID_Message($namespace); | |
$reply->setArg(Auth_OpenID_OPENID_NS, 'mode', 'error'); | |
$reply->setArg(Auth_OpenID_OPENID_NS, 'error', $this->toString()); | |
if ($this->contact !== null) { | |
$reply->setArg(Auth_OpenID_OPENID_NS, 'contact', $this->contact); | |
} | |
if ($this->reference !== null) { | |
$reply->setArg(Auth_OpenID_OPENID_NS, 'reference', | |
$this->reference); | |
} | |
return $reply; | |
} | |
/** | |
* Returns one of Auth_OpenID_ENCODE_URL, | |
* Auth_OpenID_ENCODE_KVFORM, or null, depending on the type of | |
* encoding expected for this error's payload. | |
*/ | |
function whichEncoding() | |
{ | |
global $_Auth_OpenID_Request_Modes; | |
if ($this->hasReturnTo()) { | |
if ($this->message->isOpenID2() && | |
(strlen($this->encodeToURL()) > | |
Auth_OpenID_OPENID1_URL_LIMIT)) { | |
return Auth_OpenID_ENCODE_HTML_FORM; | |
} else { | |
return Auth_OpenID_ENCODE_URL; | |
} | |
} | |
if (!$this->message) { | |
return null; | |
} | |
$mode = $this->message->getArg(Auth_OpenID_OPENID_NS, | |
'mode'); | |
if ($mode) { | |
if (!in_array($mode, $_Auth_OpenID_Request_Modes)) { | |
return Auth_OpenID_ENCODE_KVFORM; | |
} | |
} | |
return null; | |
} | |
/** | |
* Returns this error message. | |
*/ | |
function toString() | |
{ | |
if ($this->text) { | |
return $this->text; | |
} else { | |
return get_class($this) . " error"; | |
} | |
} | |
} | |
/** | |
* Error returned by the server code when a return_to is absent from a | |
* request. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_NoReturnToError extends Auth_OpenID_ServerError { | |
function Auth_OpenID_NoReturnToError($message = null, | |
$text = "No return_to URL available") | |
{ | |
parent::Auth_OpenID_ServerError($message, $text); | |
} | |
function toString() | |
{ | |
return "No return_to available"; | |
} | |
} | |
/** | |
* An error indicating that the return_to URL is malformed. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_MalformedReturnURL extends Auth_OpenID_ServerError { | |
function Auth_OpenID_MalformedReturnURL($message, $return_to) | |
{ | |
$this->return_to = $return_to; | |
parent::Auth_OpenID_ServerError($message, "malformed return_to URL"); | |
} | |
} | |
/** | |
* This error is returned when the trust_root value is malformed. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_MalformedTrustRoot extends Auth_OpenID_ServerError { | |
function Auth_OpenID_MalformedTrustRoot($message = null, | |
$text = "Malformed trust root") | |
{ | |
parent::Auth_OpenID_ServerError($message, $text); | |
} | |
function toString() | |
{ | |
return "Malformed trust root"; | |
} | |
} | |
/** | |
* The base class for all server request classes. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_Request { | |
var $mode = null; | |
} | |
/** | |
* A request to verify the validity of a previous response. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_CheckAuthRequest extends Auth_OpenID_Request { | |
var $mode = "check_authentication"; | |
var $invalidate_handle = null; | |
function Auth_OpenID_CheckAuthRequest($assoc_handle, $signed, | |
$invalidate_handle = null) | |
{ | |
$this->assoc_handle = $assoc_handle; | |
$this->signed = $signed; | |
if ($invalidate_handle !== null) { | |
$this->invalidate_handle = $invalidate_handle; | |
} | |
$this->namespace = Auth_OpenID_OPENID2_NS; | |
$this->message = null; | |
} | |
static function fromMessage($message, $server=null) | |
{ | |
$required_keys = array('assoc_handle', 'sig', 'signed'); | |
foreach ($required_keys as $k) { | |
if (!$message->getArg(Auth_OpenID_OPENID_NS, $k)) { | |
return new Auth_OpenID_ServerError($message, | |
sprintf("%s request missing required parameter %s from \ | |
query", "check_authentication", $k)); | |
} | |
} | |
$assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, 'assoc_handle'); | |
$sig = $message->getArg(Auth_OpenID_OPENID_NS, 'sig'); | |
$signed_list = $message->getArg(Auth_OpenID_OPENID_NS, 'signed'); | |
$signed_list = explode(",", $signed_list); | |
$signed = $message; | |
if ($signed->hasKey(Auth_OpenID_OPENID_NS, 'mode')) { | |
$signed->setArg(Auth_OpenID_OPENID_NS, 'mode', 'id_res'); | |
} | |
$result = new Auth_OpenID_CheckAuthRequest($assoc_handle, $signed); | |
$result->message = $message; | |
$result->sig = $sig; | |
$result->invalidate_handle = $message->getArg(Auth_OpenID_OPENID_NS, | |
'invalidate_handle'); | |
return $result; | |
} | |
function answer($signatory) | |
{ | |
$is_valid = $signatory->verify($this->assoc_handle, $this->signed); | |
// Now invalidate that assoc_handle so it this checkAuth | |
// message cannot be replayed. | |
$signatory->invalidate($this->assoc_handle, true); | |
$response = new Auth_OpenID_ServerResponse($this); | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'is_valid', | |
($is_valid ? "true" : "false")); | |
if ($this->invalidate_handle) { | |
$assoc = $signatory->getAssociation($this->invalidate_handle, | |
false); | |
if (!$assoc) { | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'invalidate_handle', | |
$this->invalidate_handle); | |
} | |
} | |
return $response; | |
} | |
} | |
/** | |
* A class implementing plaintext server sessions. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_PlainTextServerSession { | |
/** | |
* An object that knows how to handle association requests with no | |
* session type. | |
*/ | |
var $session_type = 'no-encryption'; | |
var $needs_math = false; | |
var $allowed_assoc_types = array('HMAC-SHA1', 'HMAC-SHA256'); | |
static function fromMessage($unused_request) | |
{ | |
return new Auth_OpenID_PlainTextServerSession(); | |
} | |
function answer($secret) | |
{ | |
return array('mac_key' => base64_encode($secret)); | |
} | |
} | |
/** | |
* A class implementing DH-SHA1 server sessions. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_DiffieHellmanSHA1ServerSession { | |
/** | |
* An object that knows how to handle association requests with | |
* the Diffie-Hellman session type. | |
*/ | |
var $session_type = 'DH-SHA1'; | |
var $needs_math = true; | |
var $allowed_assoc_types = array('HMAC-SHA1'); | |
var $hash_func = 'Auth_OpenID_SHA1'; | |
function Auth_OpenID_DiffieHellmanSHA1ServerSession($dh, $consumer_pubkey) | |
{ | |
$this->dh = $dh; | |
$this->consumer_pubkey = $consumer_pubkey; | |
} | |
static function getDH($message) | |
{ | |
$dh_modulus = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_modulus'); | |
$dh_gen = $message->getArg(Auth_OpenID_OPENID_NS, 'dh_gen'); | |
if ((($dh_modulus === null) && ($dh_gen !== null)) || | |
(($dh_gen === null) && ($dh_modulus !== null))) { | |
if ($dh_modulus === null) { | |
$missing = 'modulus'; | |
} else { | |
$missing = 'generator'; | |
} | |
return new Auth_OpenID_ServerError($message, | |
'If non-default modulus or generator is '. | |
'supplied, both must be supplied. Missing '. | |
$missing); | |
} | |
$lib = Auth_OpenID_getMathLib(); | |
if ($dh_modulus || $dh_gen) { | |
$dh_modulus = $lib->base64ToLong($dh_modulus); | |
$dh_gen = $lib->base64ToLong($dh_gen); | |
if ($lib->cmp($dh_modulus, 0) == 0 || | |
$lib->cmp($dh_gen, 0) == 0) { | |
return new Auth_OpenID_ServerError( | |
$message, "Failed to parse dh_mod or dh_gen"); | |
} | |
$dh = new Auth_OpenID_DiffieHellman($dh_modulus, $dh_gen); | |
} else { | |
$dh = new Auth_OpenID_DiffieHellman(); | |
} | |
$consumer_pubkey = $message->getArg(Auth_OpenID_OPENID_NS, | |
'dh_consumer_public'); | |
if ($consumer_pubkey === null) { | |
return new Auth_OpenID_ServerError($message, | |
'Public key for DH-SHA1 session '. | |
'not found in query'); | |
} | |
$consumer_pubkey = | |
$lib->base64ToLong($consumer_pubkey); | |
if ($consumer_pubkey === false) { | |
return new Auth_OpenID_ServerError($message, | |
"dh_consumer_public is not base64"); | |
} | |
return array($dh, $consumer_pubkey); | |
} | |
static function fromMessage($message) | |
{ | |
$result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message); | |
if (is_a($result, 'Auth_OpenID_ServerError')) { | |
return $result; | |
} else { | |
list($dh, $consumer_pubkey) = $result; | |
return new Auth_OpenID_DiffieHellmanSHA1ServerSession($dh, | |
$consumer_pubkey); | |
} | |
} | |
function answer($secret) | |
{ | |
$lib = Auth_OpenID_getMathLib(); | |
$mac_key = $this->dh->xorSecret($this->consumer_pubkey, $secret, | |
$this->hash_func); | |
return array( | |
'dh_server_public' => | |
$lib->longToBase64($this->dh->public), | |
'enc_mac_key' => base64_encode($mac_key)); | |
} | |
} | |
/** | |
* A class implementing DH-SHA256 server sessions. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_DiffieHellmanSHA256ServerSession | |
extends Auth_OpenID_DiffieHellmanSHA1ServerSession { | |
var $session_type = 'DH-SHA256'; | |
var $hash_func = 'Auth_OpenID_SHA256'; | |
var $allowed_assoc_types = array('HMAC-SHA256'); | |
static function fromMessage($message) | |
{ | |
$result = Auth_OpenID_DiffieHellmanSHA1ServerSession::getDH($message); | |
if (is_a($result, 'Auth_OpenID_ServerError')) { | |
return $result; | |
} else { | |
list($dh, $consumer_pubkey) = $result; | |
return new Auth_OpenID_DiffieHellmanSHA256ServerSession($dh, | |
$consumer_pubkey); | |
} | |
} | |
} | |
/** | |
* A request to associate with the server. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_AssociateRequest extends Auth_OpenID_Request { | |
var $mode = "associate"; | |
static function getSessionClasses() | |
{ | |
return array( | |
'no-encryption' => 'Auth_OpenID_PlainTextServerSession', | |
'DH-SHA1' => 'Auth_OpenID_DiffieHellmanSHA1ServerSession', | |
'DH-SHA256' => 'Auth_OpenID_DiffieHellmanSHA256ServerSession'); | |
} | |
function Auth_OpenID_AssociateRequest($session, $assoc_type) | |
{ | |
$this->session = $session; | |
$this->namespace = Auth_OpenID_OPENID2_NS; | |
$this->assoc_type = $assoc_type; | |
} | |
static function fromMessage($message, $server=null) | |
{ | |
if ($message->isOpenID1()) { | |
$session_type = $message->getArg(Auth_OpenID_OPENID_NS, | |
'session_type'); | |
if ($session_type == 'no-encryption') { | |
// oidutil.log('Received OpenID 1 request with a no-encryption ' | |
// 'assocaition session type. Continuing anyway.') | |
} else if (!$session_type) { | |
$session_type = 'no-encryption'; | |
} | |
} else { | |
$session_type = $message->getArg(Auth_OpenID_OPENID_NS, | |
'session_type'); | |
if ($session_type === null) { | |
return new Auth_OpenID_ServerError($message, | |
"session_type missing from request"); | |
} | |
} | |
$session_class = Auth_OpenID::arrayGet( | |
Auth_OpenID_AssociateRequest::getSessionClasses(), | |
$session_type); | |
if ($session_class === null) { | |
return new Auth_OpenID_ServerError($message, | |
"Unknown session type " . | |
$session_type); | |
} | |
$session = call_user_func(array($session_class, 'fromMessage'), | |
$message); | |
if (is_a($session, 'Auth_OpenID_ServerError')) { | |
return $session; | |
} | |
$assoc_type = $message->getArg(Auth_OpenID_OPENID_NS, | |
'assoc_type', 'HMAC-SHA1'); | |
if (!in_array($assoc_type, $session->allowed_assoc_types)) { | |
$fmt = "Session type %s does not support association type %s"; | |
return new Auth_OpenID_ServerError($message, | |
sprintf($fmt, $session_type, $assoc_type)); | |
} | |
$obj = new Auth_OpenID_AssociateRequest($session, $assoc_type); | |
$obj->message = $message; | |
$obj->namespace = $message->getOpenIDNamespace(); | |
return $obj; | |
} | |
function answer($assoc) | |
{ | |
$response = new Auth_OpenID_ServerResponse($this); | |
$response->fields->updateArgs(Auth_OpenID_OPENID_NS, | |
array( | |
'expires_in' => sprintf('%d', $assoc->getExpiresIn()), | |
'assoc_type' => $this->assoc_type, | |
'assoc_handle' => $assoc->handle)); | |
$response->fields->updateArgs(Auth_OpenID_OPENID_NS, | |
$this->session->answer($assoc->secret)); | |
if (! ($this->session->session_type == 'no-encryption' | |
&& $this->message->isOpenID1())) { | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'session_type', | |
$this->session->session_type); | |
} | |
return $response; | |
} | |
function answerUnsupported($text_message, | |
$preferred_association_type=null, | |
$preferred_session_type=null) | |
{ | |
if ($this->message->isOpenID1()) { | |
return new Auth_OpenID_ServerError($this->message); | |
} | |
$response = new Auth_OpenID_ServerResponse($this); | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'error_code', 'unsupported-type'); | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'error', $text_message); | |
if ($preferred_association_type) { | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'assoc_type', | |
$preferred_association_type); | |
} | |
if ($preferred_session_type) { | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'session_type', | |
$preferred_session_type); | |
} | |
$response->code = AUTH_OPENID_HTTP_ERROR; | |
return $response; | |
} | |
} | |
/** | |
* A request to confirm the identity of a user. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_CheckIDRequest extends Auth_OpenID_Request { | |
/** | |
* Return-to verification callback. Default is | |
* Auth_OpenID_verifyReturnTo from TrustRoot.php. | |
*/ | |
var $verifyReturnTo = 'Auth_OpenID_verifyReturnTo'; | |
/** | |
* The mode of this request. | |
*/ | |
var $mode = "checkid_setup"; // or "checkid_immediate" | |
/** | |
* Whether this request is for immediate mode. | |
*/ | |
var $immediate = false; | |
/** | |
* The trust_root value for this request. | |
*/ | |
var $trust_root = null; | |
/** | |
* The OpenID namespace for this request. | |
* deprecated since version 2.0.2 | |
*/ | |
var $namespace; | |
static function make($message, $identity, $return_to, $trust_root = null, | |
$immediate = false, $assoc_handle = null, $server = null) | |
{ | |
if ($server === null) { | |
return new Auth_OpenID_ServerError($message, | |
"server must not be null"); | |
} | |
if ($return_to && | |
!Auth_OpenID_TrustRoot::_parse($return_to)) { | |
return new Auth_OpenID_MalformedReturnURL($message, $return_to); | |
} | |
$r = new Auth_OpenID_CheckIDRequest($identity, $return_to, | |
$trust_root, $immediate, | |
$assoc_handle, $server); | |
$r->namespace = $message->getOpenIDNamespace(); | |
$r->message = $message; | |
if (!$r->trustRootValid()) { | |
return new Auth_OpenID_UntrustedReturnURL($message, | |
$return_to, | |
$trust_root); | |
} else { | |
return $r; | |
} | |
} | |
function Auth_OpenID_CheckIDRequest($identity, $return_to, | |
$trust_root = null, $immediate = false, | |
$assoc_handle = null, $server = null, | |
$claimed_id = null) | |
{ | |
$this->namespace = Auth_OpenID_OPENID2_NS; | |
$this->assoc_handle = $assoc_handle; | |
$this->identity = $identity; | |
if ($claimed_id === null) { | |
$this->claimed_id = $identity; | |
} else { | |
$this->claimed_id = $claimed_id; | |
} | |
$this->return_to = $return_to; | |
$this->trust_root = $trust_root; | |
$this->server = $server; | |
if ($immediate) { | |
$this->immediate = true; | |
$this->mode = "checkid_immediate"; | |
} else { | |
$this->immediate = false; | |
$this->mode = "checkid_setup"; | |
} | |
} | |
function equals($other) | |
{ | |
return ( | |
(is_a($other, 'Auth_OpenID_CheckIDRequest')) && | |
($this->namespace == $other->namespace) && | |
($this->assoc_handle == $other->assoc_handle) && | |
($this->identity == $other->identity) && | |
($this->claimed_id == $other->claimed_id) && | |
($this->return_to == $other->return_to) && | |
($this->trust_root == $other->trust_root)); | |
} | |
/* | |
* Does the relying party publish the return_to URL for this | |
* response under the realm? It is up to the provider to set a | |
* policy for what kinds of realms should be allowed. This | |
* return_to URL verification reduces vulnerability to data-theft | |
* attacks based on open proxies, corss-site-scripting, or open | |
* redirectors. | |
* | |
* This check should only be performed after making sure that the | |
* return_to URL matches the realm. | |
* | |
* @return true if the realm publishes a document with the | |
* return_to URL listed, false if not or if discovery fails | |
*/ | |
function returnToVerified() | |
{ | |
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); | |
return call_user_func_array($this->verifyReturnTo, | |
array($this->trust_root, $this->return_to, $fetcher)); | |
} | |
static function fromMessage($message, $server) | |
{ | |
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode'); | |
$immediate = null; | |
if ($mode == "checkid_immediate") { | |
$immediate = true; | |
$mode = "checkid_immediate"; | |
} else { | |
$immediate = false; | |
$mode = "checkid_setup"; | |
} | |
$return_to = $message->getArg(Auth_OpenID_OPENID_NS, | |
'return_to'); | |
if (($message->isOpenID1()) && | |
(!$return_to)) { | |
$fmt = "Missing required field 'return_to' from checkid request"; | |
return new Auth_OpenID_ServerError($message, $fmt); | |
} | |
$identity = $message->getArg(Auth_OpenID_OPENID_NS, | |
'identity'); | |
$claimed_id = $message->getArg(Auth_OpenID_OPENID_NS, 'claimed_id'); | |
if ($message->isOpenID1()) { | |
if ($identity === null) { | |
$s = "OpenID 1 message did not contain openid.identity"; | |
return new Auth_OpenID_ServerError($message, $s); | |
} | |
} else { | |
if ($identity && !$claimed_id) { | |
$s = "OpenID 2.0 message contained openid.identity but not " . | |
"claimed_id"; | |
return new Auth_OpenID_ServerError($message, $s); | |
} else if ($claimed_id && !$identity) { | |
$s = "OpenID 2.0 message contained openid.claimed_id " . | |
"but not identity"; | |
return new Auth_OpenID_ServerError($message, $s); | |
} | |
} | |
// There's a case for making self.trust_root be a TrustRoot | |
// here. But if TrustRoot isn't currently part of the | |
// "public" API, I'm not sure it's worth doing. | |
if ($message->isOpenID1()) { | |
$trust_root_param = 'trust_root'; | |
} else { | |
$trust_root_param = 'realm'; | |
} | |
$trust_root = $message->getArg(Auth_OpenID_OPENID_NS, | |
$trust_root_param); | |
if (! $trust_root) { | |
$trust_root = $return_to; | |
} | |
if (! $message->isOpenID1() && | |
($return_to === null) && | |
($trust_root === null)) { | |
return new Auth_OpenID_ServerError($message, | |
"openid.realm required when openid.return_to absent"); | |
} | |
$assoc_handle = $message->getArg(Auth_OpenID_OPENID_NS, | |
'assoc_handle'); | |
$obj = Auth_OpenID_CheckIDRequest::make($message, | |
$identity, | |
$return_to, | |
$trust_root, | |
$immediate, | |
$assoc_handle, | |
$server); | |
if (is_a($obj, 'Auth_OpenID_ServerError')) { | |
return $obj; | |
} | |
$obj->claimed_id = $claimed_id; | |
return $obj; | |
} | |
function idSelect() | |
{ | |
// Is the identifier to be selected by the IDP? | |
// So IDPs don't have to import the constant | |
return $this->identity == Auth_OpenID_IDENTIFIER_SELECT; | |
} | |
function trustRootValid() | |
{ | |
if (!$this->trust_root) { | |
return true; | |
} | |
$tr = Auth_OpenID_TrustRoot::_parse($this->trust_root); | |
if ($tr === false) { | |
return new Auth_OpenID_MalformedTrustRoot($this->message, | |
$this->trust_root); | |
} | |
if ($this->return_to !== null) { | |
return Auth_OpenID_TrustRoot::match($this->trust_root, | |
$this->return_to); | |
} else { | |
return true; | |
} | |
} | |
/** | |
* Respond to this request. Return either an | |
* {@link Auth_OpenID_ServerResponse} or | |
* {@link Auth_OpenID_ServerError}. | |
* | |
* @param bool $allow Allow this user to claim this identity, and | |
* allow the consumer to have this information? | |
* | |
* @param string $server_url DEPRECATED. Passing $op_endpoint to | |
* the {@link Auth_OpenID_Server} constructor makes this optional. | |
* | |
* When an OpenID 1.x immediate mode request does not succeed, it | |
* gets back a URL where the request may be carried out in a | |
* not-so-immediate fashion. Pass my URL in here (the fully | |
* qualified address of this server's endpoint, i.e. | |
* http://example.com/server), and I will use it as a base for the | |
* URL for a new request. | |
* | |
* Optional for requests where {@link $immediate} is false or | |
* $allow is true. | |
* | |
* @param string $identity The OP-local identifier to answer with. | |
* Only for use when the relying party requested identifier | |
* selection. | |
* | |
* @param string $claimed_id The claimed identifier to answer | |
* with, for use with identifier selection in the case where the | |
* claimed identifier and the OP-local identifier differ, | |
* i.e. when the claimed_id uses delegation. | |
* | |
* If $identity is provided but this is not, $claimed_id will | |
* default to the value of $identity. When answering requests | |
* that did not ask for identifier selection, the response | |
* $claimed_id will default to that of the request. | |
* | |
* This parameter is new in OpenID 2.0. | |
* | |
* @return mixed | |
*/ | |
function answer($allow, $server_url = null, $identity = null, | |
$claimed_id = null) | |
{ | |
if (!$this->return_to) { | |
return new Auth_OpenID_NoReturnToError(); | |
} | |
if (!$server_url) { | |
if ((!$this->message->isOpenID1()) && | |
(!$this->server->op_endpoint)) { | |
return new Auth_OpenID_ServerError(null, | |
"server should be constructed with op_endpoint to " . | |
"respond to OpenID 2.0 messages."); | |
} | |
$server_url = $this->server->op_endpoint; | |
} | |
if ($allow) { | |
$mode = 'id_res'; | |
} else if ($this->message->isOpenID1()) { | |
if ($this->immediate) { | |
$mode = 'id_res'; | |
} else { | |
$mode = 'cancel'; | |
} | |
} else { | |
if ($this->immediate) { | |
$mode = 'setup_needed'; | |
} else { | |
$mode = 'cancel'; | |
} | |
} | |
if (!$this->trustRootValid()) { | |
return new Auth_OpenID_UntrustedReturnURL(null, | |
$this->return_to, | |
$this->trust_root); | |
} | |
$response = new Auth_OpenID_ServerResponse($this); | |
if ($claimed_id && | |
($this->message->isOpenID1())) { | |
return new Auth_OpenID_ServerError(null, | |
"claimed_id is new in OpenID 2.0 and not " . | |
"available for ".$this->namespace); | |
} | |
if ($identity && !$claimed_id) { | |
$claimed_id = $identity; | |
} | |
if ($allow) { | |
if ($this->identity == Auth_OpenID_IDENTIFIER_SELECT) { | |
if (!$identity) { | |
return new Auth_OpenID_ServerError(null, | |
"This request uses IdP-driven identifier selection. " . | |
"You must supply an identifier in the response."); | |
} | |
$response_identity = $identity; | |
$response_claimed_id = $claimed_id; | |
} else if ($this->identity) { | |
if ($identity && | |
($this->identity != $identity)) { | |
$fmt = "Request was for %s, cannot reply with identity %s"; | |
return new Auth_OpenID_ServerError(null, | |
sprintf($fmt, $this->identity, $identity)); | |
} | |
$response_identity = $this->identity; | |
$response_claimed_id = $this->claimed_id; | |
} else { | |
if ($identity) { | |
return new Auth_OpenID_ServerError(null, | |
"This request specified no identity and " . | |
"you supplied ".$identity); | |
} | |
$response_identity = null; | |
} | |
if (($this->message->isOpenID1()) && | |
($response_identity === null)) { | |
return new Auth_OpenID_ServerError(null, | |
"Request was an OpenID 1 request, so response must " . | |
"include an identifier."); | |
} | |
$response->fields->updateArgs(Auth_OpenID_OPENID_NS, | |
array('mode' => $mode, | |
'return_to' => $this->return_to, | |
'response_nonce' => Auth_OpenID_mkNonce())); | |
if (!$this->message->isOpenID1()) { | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'op_endpoint', $server_url); | |
} | |
if ($response_identity !== null) { | |
$response->fields->setArg( | |
Auth_OpenID_OPENID_NS, | |
'identity', | |
$response_identity); | |
if ($this->message->isOpenID2()) { | |
$response->fields->setArg( | |
Auth_OpenID_OPENID_NS, | |
'claimed_id', | |
$response_claimed_id); | |
} | |
} | |
} else { | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'mode', $mode); | |
if ($this->immediate) { | |
if (($this->message->isOpenID1()) && | |
(!$server_url)) { | |
return new Auth_OpenID_ServerError(null, | |
'setup_url is required for $allow=false \ | |
in OpenID 1.x immediate mode.'); | |
} | |
$setup_request = new Auth_OpenID_CheckIDRequest( | |
$this->identity, | |
$this->return_to, | |
$this->trust_root, | |
false, | |
$this->assoc_handle, | |
$this->server, | |
$this->claimed_id); | |
$setup_request->message = $this->message; | |
$setup_url = $setup_request->encodeToURL($server_url); | |
if ($setup_url === null) { | |
return new Auth_OpenID_NoReturnToError(); | |
} | |
$response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'user_setup_url', | |
$setup_url); | |
} | |
} | |
return $response; | |
} | |
function encodeToURL($server_url) | |
{ | |
if (!$this->return_to) { | |
return new Auth_OpenID_NoReturnToError(); | |
} | |
// Imported from the alternate reality where these classes are | |
// used in both the client and server code, so Requests are | |
// Encodable too. That's right, code imported from alternate | |
// realities all for the love of you, id_res/user_setup_url. | |
$q = array('mode' => $this->mode, | |
'identity' => $this->identity, | |
'claimed_id' => $this->claimed_id, | |
'return_to' => $this->return_to); | |
if ($this->trust_root) { | |
if ($this->message->isOpenID1()) { | |
$q['trust_root'] = $this->trust_root; | |
} else { | |
$q['realm'] = $this->trust_root; | |
} | |
} | |
if ($this->assoc_handle) { | |
$q['assoc_handle'] = $this->assoc_handle; | |
} | |
$response = new Auth_OpenID_Message( | |
$this->message->getOpenIDNamespace()); | |
$response->updateArgs(Auth_OpenID_OPENID_NS, $q); | |
return $response->toURL($server_url); | |
} | |
function getCancelURL() | |
{ | |
if (!$this->return_to) { | |
return new Auth_OpenID_NoReturnToError(); | |
} | |
if ($this->immediate) { | |
return new Auth_OpenID_ServerError(null, | |
"Cancel is not an appropriate \ | |
response to immediate mode \ | |
requests."); | |
} | |
$response = new Auth_OpenID_Message( | |
$this->message->getOpenIDNamespace()); | |
$response->setArg(Auth_OpenID_OPENID_NS, 'mode', 'cancel'); | |
return $response->toURL($this->return_to); | |
} | |
} | |
/** | |
* This class encapsulates the response to an OpenID server request. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_ServerResponse { | |
function Auth_OpenID_ServerResponse($request) | |
{ | |
$this->request = $request; | |
$this->fields = new Auth_OpenID_Message($this->request->namespace); | |
} | |
function whichEncoding() | |
{ | |
global $_Auth_OpenID_Request_Modes; | |
if (in_array($this->request->mode, $_Auth_OpenID_Request_Modes)) { | |
if ($this->fields->isOpenID2() && | |
(strlen($this->encodeToURL()) > | |
Auth_OpenID_OPENID1_URL_LIMIT)) { | |
return Auth_OpenID_ENCODE_HTML_FORM; | |
} else { | |
return Auth_OpenID_ENCODE_URL; | |
} | |
} else { | |
return Auth_OpenID_ENCODE_KVFORM; | |
} | |
} | |
/* | |
* Returns the form markup for this response. | |
* | |
* @return str | |
*/ | |
function toFormMarkup($form_tag_attrs=null) | |
{ | |
return $this->fields->toFormMarkup($this->request->return_to, | |
$form_tag_attrs); | |
} | |
/* | |
* Returns an HTML document containing the form markup for this | |
* response that autosubmits with javascript. | |
*/ | |
function toHTML() | |
{ | |
return Auth_OpenID::autoSubmitHTML($this->toFormMarkup()); | |
} | |
/* | |
* Returns True if this response's encoding is ENCODE_HTML_FORM. | |
* Convenience method for server authors. | |
* | |
* @return bool | |
*/ | |
function renderAsForm() | |
{ | |
return $this->whichEncoding() == Auth_OpenID_ENCODE_HTML_FORM; | |
} | |
function encodeToURL() | |
{ | |
return $this->fields->toURL($this->request->return_to); | |
} | |
function addExtension($extension_response) | |
{ | |
$extension_response->toMessage($this->fields); | |
} | |
function needsSigning() | |
{ | |
return $this->fields->getArg(Auth_OpenID_OPENID_NS, | |
'mode') == 'id_res'; | |
} | |
function encodeToKVForm() | |
{ | |
return $this->fields->toKVForm(); | |
} | |
} | |
/** | |
* A web-capable response object which you can use to generate a | |
* user-agent response. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_WebResponse { | |
var $code = AUTH_OPENID_HTTP_OK; | |
var $body = ""; | |
function Auth_OpenID_WebResponse($code = null, $headers = null, | |
$body = null) | |
{ | |
if ($code) { | |
$this->code = $code; | |
} | |
if ($headers !== null) { | |
$this->headers = $headers; | |
} else { | |
$this->headers = array(); | |
} | |
if ($body !== null) { | |
$this->body = $body; | |
} | |
} | |
} | |
/** | |
* Responsible for the signature of query data and the verification of | |
* OpenID signature values. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_Signatory { | |
// = 14 * 24 * 60 * 60; # 14 days, in seconds | |
var $SECRET_LIFETIME = 1209600; | |
// keys have a bogus server URL in them because the filestore | |
// really does expect that key to be a URL. This seems a little | |
// silly for the server store, since I expect there to be only one | |
// server URL. | |
var $normal_key = 'http://localhost/|normal'; | |
var $dumb_key = 'http://localhost/|dumb'; | |
/** | |
* Create a new signatory using a given store. | |
*/ | |
function Auth_OpenID_Signatory($store) | |
{ | |
// assert store is not None | |
$this->store = $store; | |
} | |
/** | |
* Verify, using a given association handle, a signature with | |
* signed key-value pairs from an HTTP request. | |
*/ | |
function verify($assoc_handle, $message) | |
{ | |
$assoc = $this->getAssociation($assoc_handle, true); | |
if (!$assoc) { | |
// oidutil.log("failed to get assoc with handle %r to verify sig %r" | |
// % (assoc_handle, sig)) | |
return false; | |
} | |
return $assoc->checkMessageSignature($message); | |
} | |
/** | |
* Given a response, sign the fields in the response's 'signed' | |
* list, and insert the signature into the response. | |
*/ | |
function sign($response) | |
{ | |
$signed_response = $response; | |
$assoc_handle = $response->request->assoc_handle; | |
if ($assoc_handle) { | |
// normal mode | |
$assoc = $this->getAssociation($assoc_handle, false, false); | |
if (!$assoc || ($assoc->getExpiresIn() <= 0)) { | |
// fall back to dumb mode | |
$signed_response->fields->setArg(Auth_OpenID_OPENID_NS, | |
'invalidate_handle', $assoc_handle); | |
$assoc_type = ($assoc ? $assoc->assoc_type : 'HMAC-SHA1'); | |
if ($assoc && ($assoc->getExpiresIn() <= 0)) { | |
$this->invalidate($assoc_handle, false); | |
} | |
$assoc = $this->createAssociation(true, $assoc_type); | |
} | |
} else { | |
// dumb mode. | |
$assoc = $this->createAssociation(true); | |
} | |
$signed_response->fields = $assoc->signMessage( | |
$signed_response->fields); | |
return $signed_response; | |
} | |
/** | |
* Make a new association. | |
*/ | |
function createAssociation($dumb = true, $assoc_type = 'HMAC-SHA1') | |
{ | |
$secret = Auth_OpenID_CryptUtil::getBytes( | |
Auth_OpenID_getSecretSize($assoc_type)); | |
$uniq = base64_encode(Auth_OpenID_CryptUtil::getBytes(4)); | |
$handle = sprintf('{%s}{%x}{%s}', $assoc_type, intval(time()), $uniq); | |
$assoc = Auth_OpenID_Association::fromExpiresIn( | |
$this->SECRET_LIFETIME, $handle, $secret, $assoc_type); | |
if ($dumb) { | |
$key = $this->dumb_key; | |
} else { | |
$key = $this->normal_key; | |
} | |
$this->store->storeAssociation($key, $assoc); | |
return $assoc; | |
} | |
/** | |
* Given an association handle, get the association from the | |
* store, or return a ServerError or null if something goes wrong. | |
*/ | |
function getAssociation($assoc_handle, $dumb, $check_expiration=true) | |
{ | |
if ($assoc_handle === null) { | |
return new Auth_OpenID_ServerError(null, | |
"assoc_handle must not be null"); | |
} | |
if ($dumb) { | |
$key = $this->dumb_key; | |
} else { | |
$key = $this->normal_key; | |
} | |
$assoc = $this->store->getAssociation($key, $assoc_handle); | |
if (($assoc !== null) && ($assoc->getExpiresIn() <= 0)) { | |
if ($check_expiration) { | |
$this->store->removeAssociation($key, $assoc_handle); | |
$assoc = null; | |
} | |
} | |
return $assoc; | |
} | |
/** | |
* Invalidate a given association handle. | |
*/ | |
function invalidate($assoc_handle, $dumb) | |
{ | |
if ($dumb) { | |
$key = $this->dumb_key; | |
} else { | |
$key = $this->normal_key; | |
} | |
$this->store->removeAssociation($key, $assoc_handle); | |
} | |
} | |
/** | |
* Encode an {@link Auth_OpenID_ServerResponse} to an | |
* {@link Auth_OpenID_WebResponse}. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_Encoder { | |
var $responseFactory = 'Auth_OpenID_WebResponse'; | |
/** | |
* Encode an {@link Auth_OpenID_ServerResponse} and return an | |
* {@link Auth_OpenID_WebResponse}. | |
*/ | |
function encode($response) | |
{ | |
$cls = $this->responseFactory; | |
$encode_as = $response->whichEncoding(); | |
if ($encode_as == Auth_OpenID_ENCODE_KVFORM) { | |
$wr = new $cls(null, null, $response->encodeToKVForm()); | |
if (is_a($response, 'Auth_OpenID_ServerError')) { | |
$wr->code = AUTH_OPENID_HTTP_ERROR; | |
} | |
} else if ($encode_as == Auth_OpenID_ENCODE_URL) { | |
$location = $response->encodeToURL(); | |
$wr = new $cls(AUTH_OPENID_HTTP_REDIRECT, | |
array('location' => $location)); | |
} else if ($encode_as == Auth_OpenID_ENCODE_HTML_FORM) { | |
$wr = new $cls(AUTH_OPENID_HTTP_OK, array(), | |
$response->toHTML()); | |
} else { | |
return new Auth_OpenID_EncodingError($response); | |
} | |
/* Allow the response to carry a custom error code (ex: for Association errors) */ | |
if(isset($response->code)) { | |
$wr->code = $response->code; | |
} | |
return $wr; | |
} | |
} | |
/** | |
* An encoder which also takes care of signing fields when required. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_SigningEncoder extends Auth_OpenID_Encoder { | |
function Auth_OpenID_SigningEncoder($signatory) | |
{ | |
$this->signatory = $signatory; | |
} | |
/** | |
* Sign an {@link Auth_OpenID_ServerResponse} and return an | |
* {@link Auth_OpenID_WebResponse}. | |
*/ | |
function encode($response) | |
{ | |
// the isinstance is a bit of a kludge... it means there isn't | |
// really an adapter to make the interfaces quite match. | |
if (!is_a($response, 'Auth_OpenID_ServerError') && | |
$response->needsSigning()) { | |
if (!$this->signatory) { | |
return new Auth_OpenID_ServerError(null, | |
"Must have a store to sign request"); | |
} | |
if ($response->fields->hasKey(Auth_OpenID_OPENID_NS, 'sig')) { | |
return new Auth_OpenID_AlreadySigned($response); | |
} | |
$response = $this->signatory->sign($response); | |
} | |
return parent::encode($response); | |
} | |
} | |
/** | |
* Decode an incoming query into an Auth_OpenID_Request. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_Decoder { | |
function Auth_OpenID_Decoder($server) | |
{ | |
$this->server = $server; | |
$this->handlers = array( | |
'checkid_setup' => 'Auth_OpenID_CheckIDRequest', | |
'checkid_immediate' => 'Auth_OpenID_CheckIDRequest', | |
'check_authentication' => 'Auth_OpenID_CheckAuthRequest', | |
'associate' => 'Auth_OpenID_AssociateRequest' | |
); | |
} | |
/** | |
* Given an HTTP query in an array (key-value pairs), decode it | |
* into an Auth_OpenID_Request object. | |
*/ | |
function decode($query) | |
{ | |
if (!$query) { | |
return null; | |
} | |
$message = Auth_OpenID_Message::fromPostArgs($query); | |
if ($message === null) { | |
/* | |
* It's useful to have a Message attached to a | |
* ProtocolError, so we override the bad ns value to build | |
* a Message out of it. Kinda kludgy, since it's made of | |
* lies, but the parts that aren't lies are more useful | |
* than a 'None'. | |
*/ | |
$old_ns = $query['openid.ns']; | |
$query['openid.ns'] = Auth_OpenID_OPENID2_NS; | |
$message = Auth_OpenID_Message::fromPostArgs($query); | |
return new Auth_OpenID_ServerError( | |
$message, | |
sprintf("Invalid OpenID namespace URI: %s", $old_ns)); | |
} | |
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode'); | |
if (!$mode) { | |
return new Auth_OpenID_ServerError($message, | |
"No mode value in message"); | |
} | |
if (Auth_OpenID::isFailure($mode)) { | |
return new Auth_OpenID_ServerError($message, | |
$mode->message); | |
} | |
$handlerCls = Auth_OpenID::arrayGet($this->handlers, $mode, | |
$this->defaultDecoder($message)); | |
if (!is_a($handlerCls, 'Auth_OpenID_ServerError')) { | |
return call_user_func_array(array($handlerCls, 'fromMessage'), | |
array($message, $this->server)); | |
} else { | |
return $handlerCls; | |
} | |
} | |
function defaultDecoder($message) | |
{ | |
$mode = $message->getArg(Auth_OpenID_OPENID_NS, 'mode'); | |
if (Auth_OpenID::isFailure($mode)) { | |
return new Auth_OpenID_ServerError($message, | |
$mode->message); | |
} | |
return new Auth_OpenID_ServerError($message, | |
sprintf("Unrecognized OpenID mode %s", $mode)); | |
} | |
} | |
/** | |
* An error that indicates an encoding problem occurred. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_EncodingError { | |
function Auth_OpenID_EncodingError($response) | |
{ | |
$this->response = $response; | |
} | |
} | |
/** | |
* An error that indicates that a response was already signed. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_AlreadySigned extends Auth_OpenID_EncodingError { | |
// This response is already signed. | |
} | |
/** | |
* An error that indicates that the given return_to is not under the | |
* given trust_root. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_UntrustedReturnURL extends Auth_OpenID_ServerError { | |
function Auth_OpenID_UntrustedReturnURL($message, $return_to, | |
$trust_root) | |
{ | |
parent::Auth_OpenID_ServerError($message, "Untrusted return_to URL"); | |
$this->return_to = $return_to; | |
$this->trust_root = $trust_root; | |
} | |
function toString() | |
{ | |
return sprintf("return_to %s not under trust_root %s", | |
$this->return_to, $this->trust_root); | |
} | |
} | |
/** | |
* I handle requests for an OpenID server. | |
* | |
* Some types of requests (those which are not checkid requests) may | |
* be handed to my {@link handleRequest} method, and I will take care | |
* of it and return a response. | |
* | |
* For your convenience, I also provide an interface to {@link | |
* Auth_OpenID_Decoder::decode()} and {@link | |
* Auth_OpenID_SigningEncoder::encode()} through my methods {@link | |
* decodeRequest} and {@link encodeResponse}. | |
* | |
* All my state is encapsulated in an {@link Auth_OpenID_OpenIDStore}. | |
* | |
* Example: | |
* | |
* <pre> $oserver = new Auth_OpenID_Server(Auth_OpenID_FileStore($data_path), | |
* "http://example.com/op"); | |
* $request = $oserver->decodeRequest(); | |
* if (in_array($request->mode, array('checkid_immediate', | |
* 'checkid_setup'))) { | |
* if ($app->isAuthorized($request->identity, $request->trust_root)) { | |
* $response = $request->answer(true); | |
* } else if ($request->immediate) { | |
* $response = $request->answer(false); | |
* } else { | |
* $app->showDecidePage($request); | |
* return; | |
* } | |
* } else { | |
* $response = $oserver->handleRequest($request); | |
* } | |
* | |
* $webresponse = $oserver->encode($response);</pre> | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_Server { | |
function Auth_OpenID_Server($store, $op_endpoint=null) | |
{ | |
$this->store = $store; | |
$this->signatory = new Auth_OpenID_Signatory($this->store); | |
$this->encoder = new Auth_OpenID_SigningEncoder($this->signatory); | |
$this->decoder = new Auth_OpenID_Decoder($this); | |
$this->op_endpoint = $op_endpoint; | |
$this->negotiator = Auth_OpenID_getDefaultNegotiator(); | |
} | |
/** | |
* Handle a request. Given an {@link Auth_OpenID_Request} object, | |
* call the appropriate {@link Auth_OpenID_Server} method to | |
* process the request and generate a response. | |
* | |
* @param Auth_OpenID_Request $request An {@link Auth_OpenID_Request} | |
* returned by {@link Auth_OpenID_Server::decodeRequest()}. | |
* | |
* @return Auth_OpenID_ServerResponse $response A response object | |
* capable of generating a user-agent reply. | |
*/ | |
function handleRequest($request) | |
{ | |
if (method_exists($this, "openid_" . $request->mode)) { | |
$handler = array($this, "openid_" . $request->mode); | |
return call_user_func($handler, $request); | |
} | |
return null; | |
} | |
/** | |
* The callback for 'check_authentication' messages. | |
*/ | |
function openid_check_authentication($request) | |
{ | |
return $request->answer($this->signatory); | |
} | |
/** | |
* The callback for 'associate' messages. | |
*/ | |
function openid_associate($request) | |
{ | |
$assoc_type = $request->assoc_type; | |
$session_type = $request->session->session_type; | |
if ($this->negotiator->isAllowed($assoc_type, $session_type)) { | |
$assoc = $this->signatory->createAssociation(false, | |
$assoc_type); | |
return $request->answer($assoc); | |
} else { | |
$message = sprintf('Association type %s is not supported with '. | |
'session type %s', $assoc_type, $session_type); | |
list($preferred_assoc_type, $preferred_session_type) = | |
$this->negotiator->getAllowedType(); | |
return $request->answerUnsupported($message, | |
$preferred_assoc_type, | |
$preferred_session_type); | |
} | |
} | |
/** | |
* Encodes as response in the appropriate format suitable for | |
* sending to the user agent. | |
*/ | |
function encodeResponse($response) | |
{ | |
return $this->encoder->encode($response); | |
} | |
/** | |
* Decodes a query args array into the appropriate | |
* {@link Auth_OpenID_Request} object. | |
*/ | |
function decodeRequest($query=null) | |
{ | |
if ($query === null) { | |
$query = Auth_OpenID::getQuery(); | |
} | |
return $this->decoder->decode($query); | |
} | |
} | |
<?php | |
/** | |
* Functions for dealing with OpenID trust roots | |
* | |
* PHP versions 4 and 5 | |
* | |
* LICENSE: See the COPYING file included in this distribution. | |
* | |
* @package OpenID | |
* @author JanRain, Inc. <openid@janrain.com> | |
* @copyright 2005-2008 Janrain, Inc. | |
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache | |
*/ | |
require_once 'Auth/OpenID/Discover.php'; | |
/** | |
* A regular expression that matches a domain ending in a top-level domains. | |
* Used in checking trust roots for sanity. | |
* | |
* @access private | |
*/ | |
define('Auth_OpenID___TLDs', | |
'/\.(ac|ad|ae|aero|af|ag|ai|al|am|an|ao|aq|ar|arpa|as|asia' . | |
'|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|biz|bj|bm|bn|bo|br' . | |
'|bs|bt|bv|bw|by|bz|ca|cat|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co' . | |
'|com|coop|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg' . | |
'|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl' . | |
'|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie' . | |
'|il|im|in|info|int|io|iq|ir|is|it|je|jm|jo|jobs|jp|ke|kg|kh' . | |
'|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly' . | |
'|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mo|mobi|mp|mq|mr|ms|mt' . | |
'|mu|museum|mv|mw|mx|my|mz|na|name|nc|ne|net|nf|ng|ni|nl|no' . | |
'|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|pro|ps|pt' . | |
'|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl' . | |
'|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm' . | |
'|tn|to|tp|tr|travel|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve' . | |
'|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g' . | |
'|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d' . | |
'|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp' . | |
'|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)\.?$/'); | |
define('Auth_OpenID___HostSegmentRe', | |
"/^(?:[-a-zA-Z0-9!$&'\\(\\)\\*+,;=._~]|%[a-zA-Z0-9]{2})*$/"); | |
/** | |
* A wrapper for trust-root related functions | |
*/ | |
class Auth_OpenID_TrustRoot { | |
/* | |
* Return a discovery URL for this realm. | |
* | |
* Return null if the realm could not be parsed or was not valid. | |
* | |
* @param return_to The relying party return URL of the OpenID | |
* authentication request | |
* | |
* @return The URL upon which relying party discovery should be | |
* run in order to verify the return_to URL | |
*/ | |
static function buildDiscoveryURL($realm) | |
{ | |
$parsed = Auth_OpenID_TrustRoot::_parse($realm); | |
if ($parsed === false) { | |
return false; | |
} | |
if ($parsed['wildcard']) { | |
// Use "www." in place of the star | |
if ($parsed['host'][0] != '.') { | |
return false; | |
} | |
$www_domain = 'www' . $parsed['host']; | |
return sprintf('%s://%s%s', $parsed['scheme'], | |
$www_domain, $parsed['path']); | |
} else { | |
return $parsed['unparsed']; | |
} | |
} | |
/** | |
* Parse a URL into its trust_root parts. | |
* | |
* @static | |
* | |
* @access private | |
* | |
* @param string $trust_root The url to parse | |
* | |
* @return mixed $parsed Either an associative array of trust root | |
* parts or false if parsing failed. | |
*/ | |
static function _parse($trust_root) | |
{ | |
$trust_root = Auth_OpenID_urinorm($trust_root); | |
if ($trust_root === null) { | |
return false; | |
} | |
if (preg_match("/:\/\/[^:]+(:\d+){2,}(\/|$)/", $trust_root)) { | |
return false; | |
} | |
$parts = @parse_url($trust_root); | |
if ($parts === false) { | |
return false; | |
} | |
$required_parts = array('scheme', 'host'); | |
$forbidden_parts = array('user', 'pass', 'fragment'); | |
$keys = array_keys($parts); | |
if (array_intersect($keys, $required_parts) != $required_parts) { | |
return false; | |
} | |
if (array_intersect($keys, $forbidden_parts) != array()) { | |
return false; | |
} | |
if (!preg_match(Auth_OpenID___HostSegmentRe, $parts['host'])) { | |
return false; | |
} | |
$scheme = strtolower($parts['scheme']); | |
$allowed_schemes = array('http', 'https'); | |
if (!in_array($scheme, $allowed_schemes)) { | |
return false; | |
} | |
$parts['scheme'] = $scheme; | |
$host = strtolower($parts['host']); | |
$hostparts = explode('*', $host); | |
switch (count($hostparts)) { | |
case 1: | |
$parts['wildcard'] = false; | |
break; | |
case 2: | |
if ($hostparts[0] || | |
($hostparts[1] && substr($hostparts[1], 0, 1) != '.')) { | |
return false; | |
} | |
$host = $hostparts[1]; | |
$parts['wildcard'] = true; | |
break; | |
default: | |
return false; | |
} | |
if (strpos($host, ':') !== false) { | |
return false; | |
} | |
$parts['host'] = $host; | |
if (isset($parts['path'])) { | |
$path = strtolower($parts['path']); | |
if (substr($path, 0, 1) != '/') { | |
return false; | |
} | |
} else { | |
$path = '/'; | |
} | |
$parts['path'] = $path; | |
if (!isset($parts['port'])) { | |
$parts['port'] = false; | |
} | |
$parts['unparsed'] = $trust_root; | |
return $parts; | |
} | |
/** | |
* Is this trust root sane? | |
* | |
* A trust root is sane if it is syntactically valid and it has a | |
* reasonable domain name. Specifically, the domain name must be | |
* more than one level below a standard TLD or more than two | |
* levels below a two-letter tld. | |
* | |
* For example, '*.com' is not a sane trust root, but '*.foo.com' | |
* is. '*.co.uk' is not sane, but '*.bbc.co.uk' is. | |
* | |
* This check is not always correct, but it attempts to err on the | |
* side of marking sane trust roots insane instead of marking | |
* insane trust roots sane. For example, 'kink.fm' is marked as | |
* insane even though it "should" (for some meaning of should) be | |
* marked sane. | |
* | |
* This function should be used when creating OpenID servers to | |
* alert the users of the server when a consumer attempts to get | |
* the user to accept a suspicious trust root. | |
* | |
* @static | |
* @param string $trust_root The trust root to check | |
* @return bool $sanity Whether the trust root looks OK | |
*/ | |
static function isSane($trust_root) | |
{ | |
$parts = Auth_OpenID_TrustRoot::_parse($trust_root); | |
if ($parts === false) { | |
return false; | |
} | |
// Localhost is a special case | |
if ($parts['host'] == 'localhost') { | |
return true; | |
} | |
$host_parts = explode('.', $parts['host']); | |
if ($parts['wildcard']) { | |
// Remove the empty string from the beginning of the array | |
array_shift($host_parts); | |
} | |
if ($host_parts && !$host_parts[count($host_parts) - 1]) { | |
array_pop($host_parts); | |
} | |
if (!$host_parts) { | |
return false; | |
} | |
// Don't allow adjacent dots | |
if (in_array('', $host_parts, true)) { | |
return false; | |
} | |
// Get the top-level domain of the host. If it is not a valid TLD, | |
// it's not sane. | |
preg_match(Auth_OpenID___TLDs, $parts['host'], $matches); | |
if (!$matches) { | |
return false; | |
} | |
$tld = $matches[1]; | |
if (count($host_parts) == 1) { | |
return false; | |
} | |
if ($parts['wildcard']) { | |
// It's a 2-letter tld with a short second to last segment | |
// so there needs to be more than two segments specified | |
// (e.g. *.co.uk is insane) | |
$second_level = $host_parts[count($host_parts) - 2]; | |
if (strlen($tld) == 2 && strlen($second_level) <= 3) { | |
return count($host_parts) > 2; | |
} | |
} | |
return true; | |
} | |
/** | |
* Does this URL match the given trust root? | |
* | |
* Return whether the URL falls under the given trust root. This | |
* does not check whether the trust root is sane. If the URL or | |
* trust root do not parse, this function will return false. | |
* | |
* @param string $trust_root The trust root to match against | |
* | |
* @param string $url The URL to check | |
* | |
* @return bool $matches Whether the URL matches against the | |
* trust root | |
*/ | |
static function match($trust_root, $url) | |
{ | |
$trust_root_parsed = Auth_OpenID_TrustRoot::_parse($trust_root); | |
$url_parsed = Auth_OpenID_TrustRoot::_parse($url); | |
if (!$trust_root_parsed || !$url_parsed) { | |
return false; | |
} | |
// Check hosts matching | |
if ($url_parsed['wildcard']) { | |
return false; | |
} | |
if ($trust_root_parsed['wildcard']) { | |
$host_tail = $trust_root_parsed['host']; | |
$host = $url_parsed['host']; | |
if ($host_tail && | |
substr($host, -(strlen($host_tail))) != $host_tail && | |
substr($host_tail, 1) != $host) { | |
return false; | |
} | |
} else { | |
if ($trust_root_parsed['host'] != $url_parsed['host']) { | |
return false; | |
} | |
} | |
// Check path and query matching | |
$base_path = $trust_root_parsed['path']; | |
$path = $url_parsed['path']; | |
if (!isset($trust_root_parsed['query'])) { | |
if ($base_path != $path) { | |
if (substr($path, 0, strlen($base_path)) != $base_path) { | |
return false; | |
} | |
if (substr($base_path, strlen($base_path) - 1, 1) != '/' && | |
substr($path, strlen($base_path), 1) != '/') { | |
return false; | |
} | |
} | |
} else { | |
$base_query = $trust_root_parsed['query']; | |
$query = @$url_parsed['query']; | |
$qplus = substr($query, 0, strlen($base_query) + 1); | |
$bqplus = $base_query . '&'; | |
if ($base_path != $path || | |
($base_query != $query && $qplus != $bqplus)) { | |
return false; | |
} | |
} | |
// The port and scheme need to match exactly | |
return ($trust_root_parsed['scheme'] == $url_parsed['scheme'] && | |
$url_parsed['port'] === $trust_root_parsed['port']); | |
} | |
} | |
/* | |
* If the endpoint is a relying party OpenID return_to endpoint, | |
* return the endpoint URL. Otherwise, return None. | |
* | |
* This function is intended to be used as a filter for the Yadis | |
* filtering interface. | |
* | |
* @see: C{L{openid.yadis.services}} | |
* @see: C{L{openid.yadis.filters}} | |
* | |
* @param endpoint: An XRDS BasicServiceEndpoint, as returned by | |
* performing Yadis dicovery. | |
* | |
* @returns: The endpoint URL or None if the endpoint is not a | |
* relying party endpoint. | |
*/ | |
function filter_extractReturnURL($endpoint) | |
{ | |
if ($endpoint->matchTypes(array(Auth_OpenID_RP_RETURN_TO_URL_TYPE))) { | |
return $endpoint; | |
} else { | |
return null; | |
} | |
} | |
function &Auth_OpenID_extractReturnURL(&$endpoint_list) | |
{ | |
$result = array(); | |
foreach ($endpoint_list as $endpoint) { | |
if (filter_extractReturnURL($endpoint)) { | |
$result[] = $endpoint; | |
} | |
} | |
return $result; | |
} | |
/* | |
* Is the return_to URL under one of the supplied allowed return_to | |
* URLs? | |
*/ | |
function Auth_OpenID_returnToMatches($allowed_return_to_urls, $return_to) | |
{ | |
foreach ($allowed_return_to_urls as $allowed_return_to) { | |
// A return_to pattern works the same as a realm, except that | |
// it's not allowed to use a wildcard. We'll model this by | |
// parsing it as a realm, and not trying to match it if it has | |
// a wildcard. | |
$return_realm = Auth_OpenID_TrustRoot::_parse($allowed_return_to); | |
if (// Parses as a trust root | |
($return_realm !== false) && | |
// Does not have a wildcard | |
(!$return_realm['wildcard']) && | |
// Matches the return_to that we passed in with it | |
(Auth_OpenID_TrustRoot::match($allowed_return_to, $return_to))) { | |
return true; | |
} | |
} | |
// No URL in the list matched | |
return false; | |
} | |
/* | |
* Given a relying party discovery URL return a list of return_to | |
* URLs. | |
*/ | |
function Auth_OpenID_getAllowedReturnURLs($relying_party_url, $fetcher, | |
$discover_function=null) | |
{ | |
if ($discover_function === null) { | |
$discover_function = array('Auth_Yadis_Yadis', 'discover'); | |
} | |
$xrds_parse_cb = array('Auth_OpenID_ServiceEndpoint', 'consumerFromXRDS'); | |
list($rp_url_after_redirects, $endpoints) = | |
Auth_Yadis_getServiceEndpoints($relying_party_url, $xrds_parse_cb, | |
$discover_function, $fetcher); | |
if ($rp_url_after_redirects != $relying_party_url) { | |
// Verification caused a redirect | |
return false; | |
} | |
call_user_func_array($discover_function, | |
array($relying_party_url, $fetcher)); | |
$return_to_urls = array(); | |
$matching_endpoints = Auth_OpenID_extractReturnURL($endpoints); | |
foreach ($matching_endpoints as $e) { | |
$return_to_urls[] = $e->server_url; | |
} | |
return $return_to_urls; | |
} | |
/* | |
* Verify that a return_to URL is valid for the given realm. | |
* | |
* This function builds a discovery URL, performs Yadis discovery on | |
* it, makes sure that the URL does not redirect, parses out the | |
* return_to URLs, and finally checks to see if the current return_to | |
* URL matches the return_to. | |
* | |
* @return true if the return_to URL is valid for the realm | |
*/ | |
function Auth_OpenID_verifyReturnTo($realm_str, $return_to, $fetcher, | |
$_vrfy='Auth_OpenID_getAllowedReturnURLs') | |
{ | |
$disco_url = Auth_OpenID_TrustRoot::buildDiscoveryURL($realm_str); | |
if ($disco_url === false) { | |
return false; | |
} | |
$allowable_urls = call_user_func_array($_vrfy, | |
array($disco_url, $fetcher)); | |
// The realm_str could not be parsed. | |
if ($allowable_urls === false) { | |
return false; | |
} | |
if (Auth_OpenID_returnToMatches($allowable_urls, $return_to)) { | |
return true; | |
} else { | |
return false; | |
} | |
} | |
<?php | |
/** | |
* Yadis service manager to be used during yadis-driven authentication | |
* attempts. | |
* | |
* @package OpenID | |
*/ | |
/** | |
* The base session class used by the Auth_Yadis_Manager. This | |
* class wraps the default PHP session machinery and should be | |
* subclassed if your application doesn't use PHP sessioning. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_PHPSession { | |
/** | |
* Set a session key/value pair. | |
* | |
* @param string $name The name of the session key to add. | |
* @param string $value The value to add to the session. | |
*/ | |
function set($name, $value) | |
{ | |
$_SESSION[$name] = $value; | |
} | |
/** | |
* Get a key's value from the session. | |
* | |
* @param string $name The name of the key to retrieve. | |
* @param string $default The optional value to return if the key | |
* is not found in the session. | |
* @return string $result The key's value in the session or | |
* $default if it isn't found. | |
*/ | |
function get($name, $default=null) | |
{ | |
if (array_key_exists($name, $_SESSION)) { | |
return $_SESSION[$name]; | |
} else { | |
return $default; | |
} | |
} | |
/** | |
* Remove a key/value pair from the session. | |
* | |
* @param string $name The name of the key to remove. | |
*/ | |
function del($name) | |
{ | |
unset($_SESSION[$name]); | |
} | |
/** | |
* Return the contents of the session in array form. | |
*/ | |
function contents() | |
{ | |
return $_SESSION; | |
} | |
} | |
/** | |
* A session helper class designed to translate between arrays and | |
* objects. Note that the class used must have a constructor that | |
* takes no parameters. This is not a general solution, but it works | |
* for dumb objects that just need to have attributes set. The idea | |
* is that you'll subclass this and override $this->check($data) -> | |
* bool to implement your own session data validation. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_SessionLoader { | |
/** | |
* Override this. | |
* | |
* @access private | |
*/ | |
function check($data) | |
{ | |
return true; | |
} | |
/** | |
* Given a session data value (an array), this creates an object | |
* (returned by $this->newObject()) whose attributes and values | |
* are those in $data. Returns null if $data lacks keys found in | |
* $this->requiredKeys(). Returns null if $this->check($data) | |
* evaluates to false. Returns null if $this->newObject() | |
* evaluates to false. | |
* | |
* @access private | |
*/ | |
function fromSession($data) | |
{ | |
if (!$data) { | |
return null; | |
} | |
$required = $this->requiredKeys(); | |
foreach ($required as $k) { | |
if (!array_key_exists($k, $data)) { | |
return null; | |
} | |
} | |
if (!$this->check($data)) { | |
return null; | |
} | |
$data = array_merge($data, $this->prepareForLoad($data)); | |
$obj = $this->newObject($data); | |
if (!$obj) { | |
return null; | |
} | |
foreach ($required as $k) { | |
$obj->$k = $data[$k]; | |
} | |
return $obj; | |
} | |
/** | |
* Prepares the data array by making any necessary changes. | |
* Returns an array whose keys and values will be used to update | |
* the original data array before calling $this->newObject($data). | |
* | |
* @access private | |
*/ | |
function prepareForLoad($data) | |
{ | |
return array(); | |
} | |
/** | |
* Returns a new instance of this loader's class, using the | |
* session data to construct it if necessary. The object need | |
* only be created; $this->fromSession() will take care of setting | |
* the object's attributes. | |
* | |
* @access private | |
*/ | |
function newObject($data) | |
{ | |
return null; | |
} | |
/** | |
* Returns an array of keys and values built from the attributes | |
* of $obj. If $this->prepareForSave($obj) returns an array, its keys | |
* and values are used to update the $data array of attributes | |
* from $obj. | |
* | |
* @access private | |
*/ | |
function toSession($obj) | |
{ | |
$data = array(); | |
foreach ($obj as $k => $v) { | |
$data[$k] = $v; | |
} | |
$extra = $this->prepareForSave($obj); | |
if ($extra && is_array($extra)) { | |
foreach ($extra as $k => $v) { | |
$data[$k] = $v; | |
} | |
} | |
return $data; | |
} | |
/** | |
* Override this. | |
* | |
* @access private | |
*/ | |
function prepareForSave($obj) | |
{ | |
return array(); | |
} | |
} | |
/** | |
* A concrete loader implementation for Auth_OpenID_ServiceEndpoints. | |
* | |
* @package OpenID | |
*/ | |
class Auth_OpenID_ServiceEndpointLoader extends Auth_Yadis_SessionLoader { | |
function newObject($data) | |
{ | |
return new Auth_OpenID_ServiceEndpoint(); | |
} | |
function requiredKeys() | |
{ | |
$obj = new Auth_OpenID_ServiceEndpoint(); | |
$data = array(); | |
foreach ($obj as $k => $v) { | |
$data[] = $k; | |
} | |
return $data; | |
} | |
function check($data) | |
{ | |
return is_array($data['type_uris']); | |
} | |
} | |
/** | |
* A concrete loader implementation for Auth_Yadis_Managers. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_ManagerLoader extends Auth_Yadis_SessionLoader { | |
function requiredKeys() | |
{ | |
return array('starting_url', | |
'yadis_url', | |
'services', | |
'session_key', | |
'_current', | |
'stale'); | |
} | |
function newObject($data) | |
{ | |
return new Auth_Yadis_Manager($data['starting_url'], | |
$data['yadis_url'], | |
$data['services'], | |
$data['session_key']); | |
} | |
function check($data) | |
{ | |
return is_array($data['services']); | |
} | |
function prepareForLoad($data) | |
{ | |
$loader = new Auth_OpenID_ServiceEndpointLoader(); | |
$services = array(); | |
foreach ($data['services'] as $s) { | |
$services[] = $loader->fromSession($s); | |
} | |
return array('services' => $services); | |
} | |
function prepareForSave($obj) | |
{ | |
$loader = new Auth_OpenID_ServiceEndpointLoader(); | |
$services = array(); | |
foreach ($obj->services as $s) { | |
$services[] = $loader->toSession($s); | |
} | |
return array('services' => $services); | |
} | |
} | |
/** | |
* The Yadis service manager which stores state in a session and | |
* iterates over <Service> elements in a Yadis XRDS document and lets | |
* a caller attempt to use each one. This is used by the Yadis | |
* library internally. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_Manager { | |
/** | |
* Intialize a new yadis service manager. | |
* | |
* @access private | |
*/ | |
function Auth_Yadis_Manager($starting_url, $yadis_url, | |
$services, $session_key) | |
{ | |
// The URL that was used to initiate the Yadis protocol | |
$this->starting_url = $starting_url; | |
// The URL after following redirects (the identifier) | |
$this->yadis_url = $yadis_url; | |
// List of service elements | |
$this->services = $services; | |
$this->session_key = $session_key; | |
// Reference to the current service object | |
$this->_current = null; | |
// Stale flag for cleanup if PHP lib has trouble. | |
$this->stale = false; | |
} | |
/** | |
* @access private | |
*/ | |
function length() | |
{ | |
// How many untried services remain? | |
return count($this->services); | |
} | |
/** | |
* Return the next service | |
* | |
* $this->current() will continue to return that service until the | |
* next call to this method. | |
*/ | |
function nextService() | |
{ | |
if ($this->services) { | |
$this->_current = array_shift($this->services); | |
} else { | |
$this->_current = null; | |
} | |
return $this->_current; | |
} | |
/** | |
* @access private | |
*/ | |
function current() | |
{ | |
// Return the current service. | |
// Returns None if there are no services left. | |
return $this->_current; | |
} | |
/** | |
* @access private | |
*/ | |
function forURL($url) | |
{ | |
return in_array($url, array($this->starting_url, $this->yadis_url)); | |
} | |
/** | |
* @access private | |
*/ | |
function started() | |
{ | |
// Has the first service been returned? | |
return $this->_current !== null; | |
} | |
} | |
/** | |
* State management for discovery. | |
* | |
* High-level usage pattern is to call .getNextService(discover) in | |
* order to find the next available service for this user for this | |
* session. Once a request completes, call .cleanup() to clean up the | |
* session state. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_Discovery { | |
/** | |
* @access private | |
*/ | |
var $DEFAULT_SUFFIX = 'auth'; | |
/** | |
* @access private | |
*/ | |
var $PREFIX = '_yadis_services_'; | |
/** | |
* Initialize a discovery object. | |
* | |
* @param Auth_Yadis_PHPSession $session An object which | |
* implements the Auth_Yadis_PHPSession API. | |
* @param string $url The URL on which to attempt discovery. | |
* @param string $session_key_suffix The optional session key | |
* suffix override. | |
*/ | |
function Auth_Yadis_Discovery($session, $url, | |
$session_key_suffix = null) | |
{ | |
/// Initialize a discovery object | |
$this->session = $session; | |
$this->url = $url; | |
if ($session_key_suffix === null) { | |
$session_key_suffix = $this->DEFAULT_SUFFIX; | |
} | |
$this->session_key_suffix = $session_key_suffix; | |
$this->session_key = $this->PREFIX . $this->session_key_suffix; | |
} | |
/** | |
* Return the next authentication service for the pair of | |
* user_input and session. This function handles fallback. | |
*/ | |
function getNextService($discover_cb, $fetcher) | |
{ | |
$manager = $this->getManager(); | |
if (!$manager || (!$manager->services)) { | |
$this->destroyManager(); | |
list($yadis_url, $services) = call_user_func($discover_cb, | |
$this->url, | |
$fetcher); | |
$manager = $this->createManager($services, $yadis_url); | |
} | |
if ($manager) { | |
$loader = new Auth_Yadis_ManagerLoader(); | |
$service = $manager->nextService(); | |
$this->session->set($this->session_key, | |
serialize($loader->toSession($manager))); | |
} else { | |
$service = null; | |
} | |
return $service; | |
} | |
/** | |
* Clean up Yadis-related services in the session and return the | |
* most-recently-attempted service from the manager, if one | |
* exists. | |
* | |
* @param $force True if the manager should be deleted regardless | |
* of whether it's a manager for $this->url. | |
*/ | |
function cleanup($force=false) | |
{ | |
$manager = $this->getManager($force); | |
if ($manager) { | |
$service = $manager->current(); | |
$this->destroyManager($force); | |
} else { | |
$service = null; | |
} | |
return $service; | |
} | |
/** | |
* @access private | |
*/ | |
function getSessionKey() | |
{ | |
// Get the session key for this starting URL and suffix | |
return $this->PREFIX . $this->session_key_suffix; | |
} | |
/** | |
* @access private | |
* | |
* @param $force True if the manager should be returned regardless | |
* of whether it's a manager for $this->url. | |
*/ | |
function getManager($force=false) | |
{ | |
// Extract the YadisServiceManager for this object's URL and | |
// suffix from the session. | |
$manager_str = $this->session->get($this->getSessionKey()); | |
$manager = null; | |
if ($manager_str !== null) { | |
$loader = new Auth_Yadis_ManagerLoader(); | |
$manager = $loader->fromSession(unserialize($manager_str)); | |
} | |
if ($manager && ($manager->forURL($this->url) || $force)) { | |
return $manager; | |
} | |
} | |
/** | |
* @access private | |
*/ | |
function createManager($services, $yadis_url = null) | |
{ | |
$key = $this->getSessionKey(); | |
if ($this->getManager()) { | |
return $this->getManager(); | |
} | |
if ($services) { | |
$loader = new Auth_Yadis_ManagerLoader(); | |
$manager = new Auth_Yadis_Manager($this->url, $yadis_url, | |
$services, $key); | |
$this->session->set($this->session_key, | |
serialize($loader->toSession($manager))); | |
return $manager; | |
} | |
} | |
/** | |
* @access private | |
* | |
* @param $force True if the manager should be deleted regardless | |
* of whether it's a manager for $this->url. | |
*/ | |
function destroyManager($force=false) | |
{ | |
if ($this->getManager($force) !== null) { | |
$key = $this->getSessionKey(); | |
$this->session->del($key); | |
} | |
} | |
} | |
<?php | |
/** | |
* This module contains the XRDS parsing code. | |
* | |
* PHP versions 4 and 5 | |
* | |
* LICENSE: See the COPYING file included in this distribution. | |
* | |
* @package OpenID | |
* @author JanRain, Inc. <openid@janrain.com> | |
* @copyright 2005-2008 Janrain, Inc. | |
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache | |
*/ | |
/** | |
* Require the XPath implementation. | |
*/ | |
require_once 'Auth/Yadis/XML.php'; | |
/** | |
* This match mode means a given service must match ALL filters passed | |
* to the Auth_Yadis_XRDS::services() call. | |
*/ | |
define('SERVICES_YADIS_MATCH_ALL', 101); | |
/** | |
* This match mode means a given service must match ANY filters (at | |
* least one) passed to the Auth_Yadis_XRDS::services() call. | |
*/ | |
define('SERVICES_YADIS_MATCH_ANY', 102); | |
/** | |
* The priority value used for service elements with no priority | |
* specified. | |
*/ | |
define('SERVICES_YADIS_MAX_PRIORITY', pow(2, 30)); | |
/** | |
* XRD XML namespace | |
*/ | |
define('Auth_Yadis_XMLNS_XRD_2_0', 'xri://$xrd*($v*2.0)'); | |
/** | |
* XRDS XML namespace | |
*/ | |
define('Auth_Yadis_XMLNS_XRDS', 'xri://$xrds'); | |
function Auth_Yadis_getNSMap() | |
{ | |
return array('xrds' => Auth_Yadis_XMLNS_XRDS, | |
'xrd' => Auth_Yadis_XMLNS_XRD_2_0); | |
} | |
/** | |
* @access private | |
*/ | |
function Auth_Yadis_array_scramble($arr) | |
{ | |
$result = array(); | |
while (count($arr)) { | |
$index = array_rand($arr, 1); | |
$result[] = $arr[$index]; | |
unset($arr[$index]); | |
} | |
return $result; | |
} | |
/** | |
* This class represents a <Service> element in an XRDS document. | |
* Objects of this type are returned by | |
* Auth_Yadis_XRDS::services() and | |
* Auth_Yadis_Yadis::services(). Each object corresponds directly | |
* to a <Service> element in the XRDS and supplies a | |
* getElements($name) method which you should use to inspect the | |
* element's contents. See {@link Auth_Yadis_Yadis} for more | |
* information on the role this class plays in Yadis discovery. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_Service { | |
/** | |
* Creates an empty service object. | |
*/ | |
function Auth_Yadis_Service() | |
{ | |
$this->element = null; | |
$this->parser = null; | |
} | |
/** | |
* Return the URIs in the "Type" elements, if any, of this Service | |
* element. | |
* | |
* @return array $type_uris An array of Type URI strings. | |
*/ | |
function getTypes() | |
{ | |
$t = array(); | |
foreach ($this->getElements('xrd:Type') as $elem) { | |
$c = $this->parser->content($elem); | |
if ($c) { | |
$t[] = $c; | |
} | |
} | |
return $t; | |
} | |
function matchTypes($type_uris) | |
{ | |
$result = array(); | |
foreach ($this->getTypes() as $typ) { | |
if (in_array($typ, $type_uris)) { | |
$result[] = $typ; | |
} | |
} | |
return $result; | |
} | |
/** | |
* Return the URIs in the "URI" elements, if any, of this Service | |
* element. The URIs are returned sorted in priority order. | |
* | |
* @return array $uris An array of URI strings. | |
*/ | |
function getURIs() | |
{ | |
$uris = array(); | |
$last = array(); | |
foreach ($this->getElements('xrd:URI') as $elem) { | |
$uri_string = $this->parser->content($elem); | |
$attrs = $this->parser->attributes($elem); | |
if ($attrs && | |
array_key_exists('priority', $attrs)) { | |
$priority = intval($attrs['priority']); | |
if (!array_key_exists($priority, $uris)) { | |
$uris[$priority] = array(); | |
} | |
$uris[$priority][] = $uri_string; | |
} else { | |
$last[] = $uri_string; | |
} | |
} | |
$keys = array_keys($uris); | |
sort($keys); | |
// Rebuild array of URIs. | |
$result = array(); | |
foreach ($keys as $k) { | |
$new_uris = Auth_Yadis_array_scramble($uris[$k]); | |
$result = array_merge($result, $new_uris); | |
} | |
$result = array_merge($result, | |
Auth_Yadis_array_scramble($last)); | |
return $result; | |
} | |
/** | |
* Returns the "priority" attribute value of this <Service> | |
* element, if the attribute is present. Returns null if not. | |
* | |
* @return mixed $result Null or integer, depending on whether | |
* this Service element has a 'priority' attribute. | |
*/ | |
function getPriority() | |
{ | |
$attributes = $this->parser->attributes($this->element); | |
if (array_key_exists('priority', $attributes)) { | |
return intval($attributes['priority']); | |
} | |
return null; | |
} | |
/** | |
* Used to get XML elements from this object's <Service> element. | |
* | |
* This is what you should use to get all custom information out | |
* of this element. This is used by service filter functions to | |
* determine whether a service element contains specific tags, | |
* etc. NOTE: this only considers elements which are direct | |
* children of the <Service> element for this object. | |
* | |
* @param string $name The name of the element to look for | |
* @return array $list An array of elements with the specified | |
* name which are direct children of the <Service> element. The | |
* nodes returned by this function can be passed to $this->parser | |
* methods (see {@link Auth_Yadis_XMLParser}). | |
*/ | |
function getElements($name) | |
{ | |
return $this->parser->evalXPath($name, $this->element); | |
} | |
} | |
/* | |
* Return the expiration date of this XRD element, or None if no | |
* expiration was specified. | |
* | |
* @param $default The value to use as the expiration if no expiration | |
* was specified in the XRD. | |
*/ | |
function Auth_Yadis_getXRDExpiration($xrd_element, $default=null) | |
{ | |
$expires_element = $xrd_element->$parser->evalXPath('/xrd:Expires'); | |
if ($expires_element === null) { | |
return $default; | |
} else { | |
$expires_string = $expires_element->text; | |
// Will raise ValueError if the string is not the expected | |
// format | |
$t = strptime($expires_string, "%Y-%m-%dT%H:%M:%SZ"); | |
if ($t === false) { | |
return false; | |
} | |
// [int $hour [, int $minute [, int $second [, | |
// int $month [, int $day [, int $year ]]]]]] | |
return mktime($t['tm_hour'], $t['tm_min'], $t['tm_sec'], | |
$t['tm_mon'], $t['tm_day'], $t['tm_year']); | |
} | |
} | |
/** | |
* This class performs parsing of XRDS documents. | |
* | |
* You should not instantiate this class directly; rather, call | |
* parseXRDS statically: | |
* | |
* <pre> $xrds = Auth_Yadis_XRDS::parseXRDS($xml_string);</pre> | |
* | |
* If the XRDS can be parsed and is valid, an instance of | |
* Auth_Yadis_XRDS will be returned. Otherwise, null will be | |
* returned. This class is used by the Auth_Yadis_Yadis::discover | |
* method. | |
* | |
* @package OpenID | |
*/ | |
class Auth_Yadis_XRDS { | |
/** | |
* Instantiate a Auth_Yadis_XRDS object. Requires an XPath | |
* instance which has been used to parse a valid XRDS document. | |
*/ | |
function Auth_Yadis_XRDS($xmlParser, $xrdNodes) | |
{ | |
$this->parser = $xmlParser; | |
$this->xrdNode = $xrdNodes[count($xrdNodes) - 1]; | |
$this->allXrdNodes = $xrdNodes; | |
$this->serviceList = array(); | |
$this->_parse(); | |
} | |
/** | |
* Parse an XML string (XRDS document) and return either a | |
* Auth_Yadis_XRDS object or null, depending on whether the | |
* XRDS XML is valid. | |
* | |
* @param string $xml_string An XRDS XML string. | |
* @return mixed $xrds An instance of Auth_Yadis_XRDS or null, | |
* depending on the validity of $xml_string | |
*/ | |
static function parseXRDS($xml_string, $extra_ns_map = null) | |
{ | |
$_null = null; | |
if (!$xml_string) { | |
return $_null; | |
} | |
$parser = Auth_Yadis_getXMLParser(); | |
$ns_map = Auth_Yadis_getNSMap(); | |
if ($extra_ns_map && is_array($extra_ns_map)) { | |
$ns_map = array_merge($ns_map, $extra_ns_map); | |
} | |
if (!($parser && $parser->init($xml_string, $ns_map))) { | |
return $_null; | |
} | |
// Try to get root element. | |
$root = $parser->evalXPath('/xrds:XRDS[1]'); | |
if (!$root) { | |
return $_null; | |
} | |
if (is_array($root)) { | |
$root = $root[0]; | |
} | |
$attrs = $parser->attributes($root); | |
if (array_key_exists('xmlns:xrd', $attrs) && | |
$attrs['xmlns:xrd'] != Auth_Yadis_XMLNS_XRDS) { | |
return $_null; | |
} else if (array_key_exists('xmlns', $attrs) && | |
preg_match('/xri/', $attrs['xmlns']) && | |
$attrs['xmlns'] != Auth_Yadis_XMLNS_XRD_2_0) { | |
return $_null; | |
} | |
// Get the last XRD node. | |
$xrd_nodes = $parser->evalXPath('/xrds:XRDS[1]/xrd:XRD'); | |
if (!$xrd_nodes) { | |
return $_null; | |
} | |
$xrds = new Auth_Yadis_XRDS($parser, $xrd_nodes); | |
return $xrds; | |
} | |
/** | |
* @access private | |
*/ | |
function _addService($priority, $service) | |
{ | |
$priority = intval($priority); | |
if (!array_key_exists($priority, $this->serviceList)) { | |
$this->serviceList[$priority] = array(); | |
} | |
$this->serviceList[$priority][] = $service; | |
} | |
/** | |
* Creates the service list using nodes from the XRDS XML | |
* document. | |
* | |
* @access private | |
*/ | |
function _parse() | |
{ | |
$this->serviceList = array(); | |
$services = $this->parser->evalXPath('xrd:Service', $this->xrdNode); | |
foreach ($services as $node) { | |
$s = new Auth_Yadis_Service(); | |
$s->element = $node; | |
$s->parser = $this->parser; | |
$priority = $s->getPriority(); | |
if ($priority === null) { | |
$priority = SERVICES_YADIS_MAX_PRIORITY; | |
} | |
$this->_addService($priority, $s); | |
} | |
} | |
/** | |
* Returns a list of service objects which correspond to <Service> | |
* elements in the XRDS XML document for this object. | |
* | |
* Optionally, an array of filter callbacks may be given to limit | |
* the list of returned service objects. Furthermore, the default | |
* mode is to return all service objects which match ANY of the | |
* specified filters, but $filter_mode may be | |
* SERVICES_YADIS_MATCH_ALL if you want to be sure that the | |
* returned services match all the given filters. See {@link | |
* Auth_Yadis_Yadis} for detailed usage information on filter | |
* functions. | |
* | |
* @param mixed $filters An array of callbacks to filter the | |
* returned services, or null if all services are to be returned. | |
* @param integer $filter_mode SERVICES_YADIS_MATCH_ALL or | |
* SERVICES_YADIS_MATCH_ANY, depending on whether the returned | |
* services should match ALL or ANY of the specified filters, | |
* respectively. | |
* @return mixed $services An array of {@link | |
* Auth_Yadis_Service} objects if $filter_mode is a valid | |
* mode; null if $filter_mode is an invalid mode (i.e., not | |
* SERVICES_YADIS_MATCH_ANY or SERVICES_YADIS_MATCH_ALL). | |
*/ | |
function services($filters = null, | |
$filter_mode = SERVICES_YADIS_MATCH_ANY) | |
{ | |
$pri_keys = array_keys($this->serviceList); | |
sort($pri_keys, SORT_NUMERIC); | |
// If no filters are specified, return the entire service | |
// list, ordered by priority. | |
if (!$filters || | |
(!is_array($filters))) { | |
$result = array(); | |
foreach ($pri_keys as $pri) { | |
$result = array_merge($result, $this->serviceList[$pri]); | |
} | |
return $result; | |
} | |
// If a bad filter mode is specified, return null. | |
if (!in_array($filter_mode, array(SERVICES_YADIS_MATCH_ANY, | |
SERVICES_YADIS_MATCH_ALL))) { | |
return null; | |
} | |
// Otherwise, use the callbacks in the filter list to | |
// determine which services are returned. | |
$filtered = array(); | |
foreach ($pri_keys as $priority_value) { | |
$service_obj_list = $this->serviceList[$priority_value]; | |
foreach ($service_obj_list as $service) { | |
$matches = 0; | |
foreach ($filters as $filter) { | |
if (call_user_func_array($filter, array($service))) { | |
$matches++; | |
if ($filter_mode == SERVICES_YADIS_MATCH_ANY) { | |
$pri = $service->getPriority(); | |
if ($pri === null) { | |
$pri = SERVICES_YADIS_MAX_PRIORITY; | |
} | |
if (!array_key_exists($pri, $filtered)) { | |
$filtered[$pri] = array(); | |
} | |
$filtered[$pri][] = $service; | |
break; | |
} | |
} | |
} | |
if (($filter_mode == SERVICES_YADIS_MATCH_ALL) && | |
($matches == count($filters))) { | |
$pri = $service->getPriority(); | |
if ($pri === null) { | |
$pri = SERVICES_YADIS_MAX_PRIORITY; | |
} | |
if (!array_key_exists($pri, $filtered)) { | |
$filtered[$pri] = array(); | |
} | |
$filtered[$pri][] = $service; | |
} | |
} | |
} | |
$pri_keys = array_keys($filtered); | |
sort($pri_keys, SORT_NUMERIC); | |
$result = array(); | |
foreach ($pri_keys as $pri) { | |
$result = array_merge($result, $filtered[$pri]); | |
} | |
return $result; | |
} | |
} | |
<?php | |
/** | |
* This class provides a simple interface for OpenID (1.1 and 2.0) authentication. | |
* Supports Yadis discovery. | |
* The authentication process is stateless/dumb. | |
* | |
* Usage: | |
* Sign-on with OpenID is a two step process: | |
* Step one is authentication with the provider: | |
* <code> | |
* $openid = new LightOpenID('my-host.example.org'); | |
* $openid->identity = 'ID supplied by user'; | |
* header('Location: ' . $openid->authUrl()); | |
* </code> | |
* The provider then sends various parameters via GET, one of them is openid_mode. | |
* Step two is verification: | |
* <code> | |
* if ($this->data['openid_mode']) { | |
* $openid = new LightOpenID('my-host.example.org'); | |
* echo $openid->validate() ? 'Logged in.' : 'Failed'; | |
* } | |
* </code> | |
* | |
* Change the 'my-host.example.org' to your domain name. Do NOT use $_SERVER['HTTP_HOST'] | |
* for that, unless you know what you are doing. | |
* | |
* Optionally, you can set $returnUrl and $realm (or $trustRoot, which is an alias). | |
* The default values for those are: | |
* $openid->realm = (!empty($_SERVER['HTTPS']) ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST']; | |
* $openid->returnUrl = $openid->realm . $_SERVER['REQUEST_URI']; | |
* If you don't know their meaning, refer to any openid tutorial, or specification. Or just guess. | |
* | |
* AX and SREG extensions are supported. | |
* To use them, specify $openid->required and/or $openid->optional before calling $openid->authUrl(). | |
* These are arrays, with values being AX schema paths (the 'path' part of the URL). | |
* For example: | |
* $openid->required = array('namePerson/friendly', 'contact/email'); | |
* $openid->optional = array('namePerson/first'); | |
* If the server supports only SREG or OpenID 1.1, these are automaticaly | |
* mapped to SREG names, so that user doesn't have to know anything about the server. | |
* | |
* To get the values, use $openid->getAttributes(). | |
* | |
* | |
* The library requires PHP >= 5.1.2 with curl or http/https stream wrappers enabled. | |
* @author Mewp | |
* @copyright Copyright (c) 2010, Mewp | |
* @license http://www.opensource.org/licenses/mit-license.php MIT | |
*/ | |
class LightOpenID | |
{ | |
public $returnUrl | |
, $required = array() | |
, $optional = array() | |
, $verify_peer = null | |
, $capath = null | |
, $cainfo = null | |
, $data; | |
private $identity, $claimed_id; | |
protected $server, $version, $trustRoot, $aliases, $identifier_select = false | |
, $ax = false, $sreg = false, $setup_url = null; | |
static protected $ax_to_sreg = array( | |
'namePerson/friendly' => 'nickname', | |
'contact/email' => 'email', | |
'namePerson' => 'fullname', | |
'birthDate' => 'dob', | |
'person/gender' => 'gender', | |
'contact/postalCode/home' => 'postcode', | |
'contact/country/home' => 'country', | |
'pref/language' => 'language', | |
'pref/timezone' => 'timezone', | |
); | |
function __construct($host) | |
{ | |
$this->trustRoot = (strpos($host, '://') ? $host : 'http://' . $host); | |
if ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') | |
|| (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) | |
&& $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') | |
) { | |
$this->trustRoot = (strpos($host, '://') ? $host : 'https://' . $host); | |
} | |
if(($host_end = strpos($this->trustRoot, '/', 8)) !== false) { | |
$this->trustRoot = substr($this->trustRoot, 0, $host_end); | |
} | |
$uri = rtrim(preg_replace('#((?<=\?)|&)openid\.[^&]+#', '', $_SERVER['REQUEST_URI']), '?'); | |
$this->returnUrl = $this->trustRoot . $uri; | |
$this->data = ($_SERVER['REQUEST_METHOD'] === 'POST') ? $_POST : $_GET; | |
if(!function_exists('curl_init') && !in_array('https', stream_get_wrappers())) { | |
throw new ErrorException('You must have either https wrappers or curl enabled.'); | |
} | |
} | |
function __set($name, $value) | |
{ | |
switch ($name) { | |
case 'identity': | |
if (strlen($value = trim((String) $value))) { | |
if (preg_match('#^xri:/*#i', $value, $m)) { | |
$value = substr($value, strlen($m[0])); | |
} elseif (!preg_match('/^(?:[=@+\$!\(]|https?:)/i', $value)) { | |
$value = "http://$value"; | |
} | |
if (preg_match('#^https?://[^/]+$#i', $value, $m)) { | |
$value .= '/'; | |
} | |
} | |
$this->$name = $this->claimed_id = $value; | |
break; | |
case 'trustRoot': | |
case 'realm': | |
$this->trustRoot = trim($value); | |
} | |
} | |
function __get($name) | |
{ | |
switch ($name) { | |
case 'identity': | |
# We return claimed_id instead of identity, | |
# because the developer should see the claimed identifier, | |
# i.e. what he set as identity, not the op-local identifier (which is what we verify) | |
return $this->claimed_id; | |
case 'trustRoot': | |
case 'realm': | |
return $this->trustRoot; | |
case 'mode': | |
return empty($this->data['openid_mode']) ? null : $this->data['openid_mode']; | |
} | |
} | |
/** | |
* Checks if the server specified in the url exists. | |
* | |
* @param $url url to check | |
* @return true, if the server exists; false otherwise | |
*/ | |
function hostExists($url) | |
{ | |
if (strpos($url, '/') === false) { | |
$server = $url; | |
} else { | |
$server = @parse_url($url, PHP_URL_HOST); | |
} | |
if (!$server) { | |
return false; | |
} | |
return !!gethostbynamel($server); | |
} | |
protected function request_curl($url, $method='GET', $params=array()) | |
{ | |
$params = http_build_query($params, '', '&'); | |
$curl = curl_init($url . ($method == 'GET' && $params ? '?' . $params : '')); | |
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); | |
curl_setopt($curl, CURLOPT_HEADER, false); | |
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); | |
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); | |
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*')); | |
if($this->verify_peer !== null) { | |
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verify_peer); | |
if($this->capath) { | |
curl_setopt($curl, CURLOPT_CAPATH, $this->capath); | |
} | |
if($this->cainfo) { | |
curl_setopt($curl, CURLOPT_CAINFO, $this->cainfo); | |
} | |
} | |
if ($method == 'POST') { | |
curl_setopt($curl, CURLOPT_POST, true); | |
curl_setopt($curl, CURLOPT_POSTFIELDS, $params); | |
} elseif ($method == 'HEAD') { | |
curl_setopt($curl, CURLOPT_HEADER, true); | |
curl_setopt($curl, CURLOPT_NOBODY, true); | |
} else { | |
curl_setopt($curl, CURLOPT_HTTPGET, true); | |
} | |
$response = curl_exec($curl); | |
if($method == 'HEAD') { | |
$headers = array(); | |
foreach(explode("\n", $response) as $header) { | |
$pos = strpos($header,':'); | |
$name = strtolower(trim(substr($header, 0, $pos))); | |
$headers[$name] = trim(substr($header, $pos+1)); | |
} | |
# Updating claimed_id in case of redirections. | |
$effective_url = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL); | |
if($effective_url != $url) { | |
$this->identity = $this->claimed_id = $effective_url; | |
} | |
return $headers; | |
} | |
if (curl_errno($curl)) { | |
throw new ErrorException(curl_error($curl), curl_errno($curl)); | |
} | |
return $response; | |
} | |
protected function request_streams($url, $method='GET', $params=array()) | |
{ | |
if(!$this->hostExists($url)) { | |
throw new ErrorException("Could not connect to $url.", 404); | |
} | |
$params = http_build_query($params, '', '&'); | |
switch($method) { | |
case 'GET': | |
$opts = array( | |
'http' => array( | |
'method' => 'GET', | |
'header' => 'Accept: application/xrds+xml, */*', | |
'ignore_errors' => true, | |
), 'ssl' => array( | |
'CN_match' => parse_url($url, PHP_URL_HOST), | |
), | |
); | |
$url = $url . ($params ? '?' . $params : ''); | |
break; | |
case 'POST': | |
$opts = array( | |
'http' => array( | |
'method' => 'POST', | |
'header' => 'Content-type: application/x-www-form-urlencoded', | |
'content' => $params, | |
'ignore_errors' => true, | |
), 'ssl' => array( | |
'CN_match' => parse_url($url, PHP_URL_HOST), | |
), | |
); | |
break; | |
case 'HEAD': | |
# We want to send a HEAD request, | |
# but since get_headers doesn't accept $context parameter, | |
# we have to change the defaults. | |
$default = stream_context_get_options(stream_context_get_default()); | |
stream_context_get_default( | |
array( | |
'http' => array( | |
'method' => 'HEAD', | |
'header' => 'Accept: application/xrds+xml, */*', | |
'ignore_errors' => true, | |
), 'ssl' => array( | |
'CN_match' => parse_url($url, PHP_URL_HOST), | |
), | |
) | |
); | |
$url = $url . ($params ? '?' . $params : ''); | |
$headers_tmp = get_headers ($url); | |
if(!$headers_tmp) { | |
return array(); | |
} | |
# Parsing headers. | |
$headers = array(); | |
foreach($headers_tmp as $header) { | |
$pos = strpos($header,':'); | |
$name = strtolower(trim(substr($header, 0, $pos))); | |
$headers[$name] = trim(substr($header, $pos+1)); | |
# Following possible redirections. The point is just to have | |
# claimed_id change with them, because get_headers() will | |
# follow redirections automatically. | |
# We ignore redirections with relative paths. | |
# If any known provider uses them, file a bug report. | |
if($name == 'location') { | |
if(strpos($headers[$name], 'http') === 0) { | |
$this->identity = $this->claimed_id = $headers[$name]; | |
} elseif($headers[$name][0] == '/') { | |
$parsed_url = parse_url($this->claimed_id); | |
$this->identity = | |
$this->claimed_id = $parsed_url['scheme'] . '://' | |
. $parsed_url['host'] | |
. $headers[$name]; | |
} | |
} | |
} | |
# And restore them. | |
stream_context_get_default($default); | |
return $headers; | |
} | |
if($this->verify_peer) { | |
$opts['ssl'] += array( | |
'verify_peer' => true, | |
'capath' => $this->capath, | |
'cafile' => $this->cainfo, | |
); | |
} | |
$context = stream_context_create ($opts); | |
return file_get_contents($url, false, $context); | |
} | |
protected function request($url, $method='GET', $params=array()) | |
{ | |
if (function_exists('curl_init') | |
&& (!in_array('https', stream_get_wrappers()) || !ini_get('safe_mode') && !ini_get('open_basedir')) | |
) { | |
return $this->request_curl($url, $method, $params); | |
} | |
return $this->request_streams($url, $method, $params); | |
} | |
protected function build_url($url, $parts) | |
{ | |
if (isset($url['query'], $parts['query'])) { | |
$parts['query'] = $url['query'] . '&' . $parts['query']; | |
} | |
$url = $parts + $url; | |
$url = $url['scheme'] . '://' | |
. (empty($url['username'])?'' | |
:(empty($url['password'])? "{$url['username']}@" | |
:"{$url['username']}:{$url['password']}@")) | |
. $url['host'] | |
. (empty($url['port'])?'':":{$url['port']}") | |
. (empty($url['path'])?'':$url['path']) | |
. (empty($url['query'])?'':"?{$url['query']}") | |
. (empty($url['fragment'])?'':"#{$url['fragment']}"); | |
return $url; | |
} | |
/** | |
* Helper function used to scan for <meta>/<link> tags and extract information | |
* from them | |
*/ | |
protected function htmlTag($content, $tag, $attrName, $attrValue, $valueName) | |
{ | |
preg_match_all("#<{$tag}[^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*$valueName=['\"](.+?)['\"][^>]*/?>#i", $content, $matches1); | |
preg_match_all("#<{$tag}[^>]*$valueName=['\"](.+?)['\"][^>]*$attrName=['\"].*?$attrValue.*?['\"][^>]*/?>#i", $content, $matches2); | |
$result = array_merge($matches1[1], $matches2[1]); | |
return empty($result)?false:$result[0]; | |
} | |
/** | |
* Performs Yadis and HTML discovery. Normally not used. | |
* @param $url Identity URL. | |
* @return String OP Endpoint (i.e. OpenID provider address). | |
* @throws ErrorException | |
*/ | |
function discover($url) | |
{ | |
if (!$url) throw new ErrorException('No identity supplied.'); | |
# Use xri.net proxy to resolve i-name identities | |
if (!preg_match('#^https?:#', $url)) { | |
$url = "https://xri.net/$url"; | |
} | |
# We save the original url in case of Yadis discovery failure. | |
# It can happen when we'll be lead to an XRDS document | |
# which does not have any OpenID2 services. | |
$originalUrl = $url; | |
# A flag to disable yadis discovery in case of failure in headers. | |
$yadis = true; | |
# We'll jump a maximum of 5 times, to avoid endless redirections. | |
for ($i = 0; $i < 5; $i ++) { | |
if ($yadis) { | |
$headers = $this->request($url, 'HEAD'); | |
$next = false; | |
if (isset($headers['x-xrds-location'])) { | |
$url = $this->build_url(parse_url($url), parse_url(trim($headers['x-xrds-location']))); | |
$next = true; | |
} | |
if (isset($headers['content-type']) | |
&& (strpos($headers['content-type'], 'application/xrds+xml') !== false | |
|| strpos($headers['content-type'], 'text/xml') !== false) | |
) { | |
# Apparently, some providers return XRDS documents as text/html. | |
# While it is against the spec, allowing this here shouldn't break | |
# compatibility with anything. | |
# --- | |
# Found an XRDS document, now let's find the server, and optionally delegate. | |
$content = $this->request($url, 'GET'); | |
preg_match_all('#<Service.*?>(.*?)</Service>#s', $content, $m); | |
foreach($m[1] as $content) { | |
$content = ' ' . $content; # The space is added, so that strpos doesn't return 0. | |
# OpenID 2 | |
$ns = preg_quote('http://specs.openid.net/auth/2.0/'); | |
if(preg_match('#<Type>\s*'.$ns.'(server|signon)\s*</Type>#s', $content, $type)) { | |
if ($type[1] == 'server') $this->identifier_select = true; | |
preg_match('#<URI.*?>(.*)</URI>#', $content, $server); | |
preg_match('#<(Local|Canonical)ID>(.*)</\1ID>#', $content, $delegate); | |
if (empty($server)) { | |
return false; | |
} | |
# Does the server advertise support for either AX or SREG? | |
$this->ax = (bool) strpos($content, '<Type>http://openid.net/srv/ax/1.0</Type>'); | |
$this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') | |
|| strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); | |
$server = $server[1]; | |
if (isset($delegate[2])) $this->identity = trim($delegate[2]); | |
$this->version = 2; | |
$this->server = $server; | |
return $server; | |
} | |
# OpenID 1.1 | |
$ns = preg_quote('http://openid.net/signon/1.1'); | |
if (preg_match('#<Type>\s*'.$ns.'\s*</Type>#s', $content)) { | |
preg_match('#<URI.*?>(.*)</URI>#', $content, $server); | |
preg_match('#<.*?Delegate>(.*)</.*?Delegate>#', $content, $delegate); | |
if (empty($server)) { | |
return false; | |
} | |
# AX can be used only with OpenID 2.0, so checking only SREG | |
$this->sreg = strpos($content, '<Type>http://openid.net/sreg/1.0</Type>') | |
|| strpos($content, '<Type>http://openid.net/extensions/sreg/1.1</Type>'); | |
$server = $server[1]; | |
if (isset($delegate[1])) $this->identity = $delegate[1]; | |
$this->version = 1; | |
$this->server = $server; | |
return $server; | |
} | |
} | |
$next = true; | |
$yadis = false; | |
$url = $originalUrl; | |
$content = null; | |
break; | |
} | |
if ($next) continue; | |
# There are no relevant information in headers, so we search the body. | |
$content = $this->request($url, 'GET'); | |
$location = $this->htmlTag($content, 'meta', 'http-equiv', 'X-XRDS-Location', 'content'); | |
if ($location) { | |
$url = $this->build_url(parse_url($url), parse_url($location)); | |
continue; | |
} | |
} | |
if (!$content) $content = $this->request($url, 'GET'); | |
# At this point, the YADIS Discovery has failed, so we'll switch | |
# to openid2 HTML discovery, then fallback to openid 1.1 discovery. | |
$server = $this->htmlTag($content, 'link', 'rel', 'openid2.provider', 'href'); | |
$delegate = $this->htmlTag($content, 'link', 'rel', 'openid2.local_id', 'href'); | |
$this->version = 2; | |
if (!$server) { | |
# The same with openid 1.1 | |
$server = $this->htmlTag($content, 'link', 'rel', 'openid.server', 'href'); | |
$delegate = $this->htmlTag($content, 'link', 'rel', 'openid.delegate', 'href'); | |
$this->version = 1; | |
} | |
if ($server) { | |
# We found an OpenID2 OP Endpoint | |
if ($delegate) { | |
# We have also found an OP-Local ID. | |
$this->identity = $delegate; | |
} | |
$this->server = $server; | |
return $server; | |
} | |
throw new ErrorException("No OpenID Server found at $url", 404); | |
} | |
throw new ErrorException('Endless redirection!', 500); | |
} | |
protected function sregParams() | |
{ | |
$params = array(); | |
# We always use SREG 1.1, even if the server is advertising only support for 1.0. | |
# That's because it's fully backwards compatibile with 1.0, and some providers | |
# advertise 1.0 even if they accept only 1.1. One such provider is myopenid.com | |
$params['openid.ns.sreg'] = 'http://openid.net/extensions/sreg/1.1'; | |
if ($this->required) { | |
$params['openid.sreg.required'] = array(); | |
foreach ($this->required as $required) { | |
if (!isset(self::$ax_to_sreg[$required])) continue; | |
$params['openid.sreg.required'][] = self::$ax_to_sreg[$required]; | |
} | |
$params['openid.sreg.required'] = implode(',', $params['openid.sreg.required']); | |
} | |
if ($this->optional) { | |
$params['openid.sreg.optional'] = array(); | |
foreach ($this->optional as $optional) { | |
if (!isset(self::$ax_to_sreg[$optional])) continue; | |
$params['openid.sreg.optional'][] = self::$ax_to_sreg[$optional]; | |
} | |
$params['openid.sreg.optional'] = implode(',', $params['openid.sreg.optional']); | |
} | |
return $params; | |
} | |
protected function axParams() | |
{ | |
$params = array(); | |
if ($this->required || $this->optional) { | |
$params['openid.ns.ax'] = 'http://openid.net/srv/ax/1.0'; | |
$params['openid.ax.mode'] = 'fetch_request'; | |
$this->aliases = array(); | |
$counts = array(); | |
$required = array(); | |
$optional = array(); | |
foreach (array('required','optional') as $type) { | |
foreach ($this->$type as $alias => $field) { | |
if (is_int($alias)) $alias = strtr($field, '/', '_'); | |
$this->aliases[$alias] = 'http://axschema.org/' . $field; | |
if (empty($counts[$alias])) $counts[$alias] = 0; | |
$counts[$alias] += 1; | |
${$type}[] = $alias; | |
} | |
} | |
foreach ($this->aliases as $alias => $ns) { | |
$params['openid.ax.type.' . $alias] = $ns; | |
} | |
foreach ($counts as $alias => $count) { | |
if ($count == 1) continue; | |
$params['openid.ax.count.' . $alias] = $count; | |
} | |
# Don't send empty ax.requied and ax.if_available. | |
# Google and possibly other providers refuse to support ax when one of these is empty. | |
if($required) { | |
$params['openid.ax.required'] = implode(',', $required); | |
} | |
if($optional) { | |
$params['openid.ax.if_available'] = implode(',', $optional); | |
} | |
} | |
return $params; | |
} | |
protected function authUrl_v1($immediate) | |
{ | |
$returnUrl = $this->returnUrl; | |
# If we have an openid.delegate that is different from our claimed id, | |
# we need to somehow preserve the claimed id between requests. | |
# The simplest way is to just send it along with the return_to url. | |
if($this->identity != $this->claimed_id) { | |
$returnUrl .= (strpos($returnUrl, '?') ? '&' : '?') . 'openid.claimed_id=' . $this->claimed_id; | |
} | |
$params = array( | |
'openid.return_to' => $returnUrl, | |
'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', | |
'openid.identity' => $this->identity, | |
'openid.trust_root' => $this->trustRoot, | |
) + $this->sregParams(); | |
return $this->build_url(parse_url($this->server) | |
, array('query' => http_build_query($params, '', '&'))); | |
} | |
protected function authUrl_v2($immediate) | |
{ | |
$params = array( | |
'openid.ns' => 'http://specs.openid.net/auth/2.0', | |
'openid.mode' => $immediate ? 'checkid_immediate' : 'checkid_setup', | |
'openid.return_to' => $this->returnUrl, | |
'openid.realm' => $this->trustRoot, | |
); | |
if ($this->ax) { | |
$params += $this->axParams(); | |
} | |
if ($this->sreg) { | |
$params += $this->sregParams(); | |
} | |
if (!$this->ax && !$this->sreg) { | |
# If OP doesn't advertise either SREG, nor AX, let's send them both | |
# in worst case we don't get anything in return. | |
$params += $this->axParams() + $this->sregParams(); | |
} | |
if ($this->identifier_select) { | |
$params['openid.identity'] = $params['openid.claimed_id'] | |
= 'http://specs.openid.net/auth/2.0/identifier_select'; | |
} else { | |
$params['openid.identity'] = $this->identity; | |
$params['openid.claimed_id'] = $this->claimed_id; | |
} | |
return $this->build_url(parse_url($this->server) | |
, array('query' => http_build_query($params, '', '&'))); | |
} | |
/** | |
* Returns authentication url. Usually, you want to redirect your user to it. | |
* @return String The authentication url. | |
* @param String $select_identifier Whether to request OP to select identity for an user in OpenID 2. Does not affect OpenID 1. | |
* @throws ErrorException | |
*/ | |
function authUrl($immediate = false) | |
{ | |
if ($this->setup_url && !$immediate) return $this->setup_url; | |
if (!$this->server) $this->discover($this->identity); | |
if ($this->version == 2) { | |
return $this->authUrl_v2($immediate); | |
} | |
return $this->authUrl_v1($immediate); | |
} | |
/** | |
* Performs OpenID verification with the OP. | |
* @return Bool Whether the verification was successful. | |
* @throws ErrorException | |
*/ | |
function validate() | |
{ | |
# If the request was using immediate mode, a failure may be reported | |
# by presenting user_setup_url (for 1.1) or reporting | |
# mode 'setup_needed' (for 2.0). Also catching all modes other than | |
# id_res, in order to avoid throwing errors. | |
if(isset($this->data['openid_user_setup_url'])) { | |
$this->setup_url = $this->data['openid_user_setup_url']; | |
return false; | |
} | |
if($this->mode != 'id_res') { | |
return false; | |
} | |
$this->claimed_id = isset($this->data['openid_claimed_id'])?$this->data['openid_claimed_id']:$this->data['openid_identity']; | |
$params = array( | |
'openid.assoc_handle' => $this->data['openid_assoc_handle'], | |
'openid.signed' => $this->data['openid_signed'], | |
'openid.sig' => $this->data['openid_sig'], | |
); | |
if (isset($this->data['openid_ns'])) { | |
# We're dealing with an OpenID 2.0 server, so let's set an ns | |
# Even though we should know location of the endpoint, | |
# we still need to verify it by discovery, so $server is not set here | |
$params['openid.ns'] = 'http://specs.openid.net/auth/2.0'; | |
} elseif (isset($this->data['openid_claimed_id']) | |
&& $this->data['openid_claimed_id'] != $this->data['openid_identity'] | |
) { | |
# If it's an OpenID 1 provider, and we've got claimed_id, | |
# we have to append it to the returnUrl, like authUrl_v1 does. | |
$this->returnUrl .= (strpos($this->returnUrl, '?') ? '&' : '?') | |
. 'openid.claimed_id=' . $this->claimed_id; | |
} | |
if ($this->data['openid_return_to'] != $this->returnUrl) { | |
# The return_to url must match the url of current request. | |
# I'm assuing that noone will set the returnUrl to something that doesn't make sense. | |
return false; | |
} | |
$server = $this->discover($this->claimed_id); | |
foreach (explode(',', $this->data['openid_signed']) as $item) { | |
# Checking whether magic_quotes_gpc is turned on, because | |
# the function may fail if it is. For example, when fetching | |
# AX namePerson, it might containg an apostrophe, which will be escaped. | |
# In such case, validation would fail, since we'd send different data than OP | |
# wants to verify. stripslashes() should solve that problem, but we can't | |
# use it when magic_quotes is off. | |
$value = $this->data['openid_' . str_replace('.','_',$item)]; | |
$params['openid.' . $item] = get_magic_quotes_gpc() ? stripslashes($value) : $value; | |
} | |
$params['openid.mode'] = 'check_authentication'; | |
$response = $this->request($server, 'POST', $params); | |
return preg_match('/is_valid\s*:\s*true/i', $response); | |
} | |
protected function getAxAttributes() | |
{ | |
$alias = null; | |
if (isset($this->data['openid_ns_ax']) | |
&& $this->data['openid_ns_ax'] != 'http://openid.net/srv/ax/1.0' | |
) { # It's the most likely case, so we'll check it before | |
$alias = 'ax'; | |
} else { | |
# 'ax' prefix is either undefined, or points to another extension, | |
# so we search for another prefix | |
foreach ($this->data as $key => $val) { | |
if (substr($key, 0, strlen('openid_ns_')) == 'openid_ns_' | |
&& $val == 'http://openid.net/srv/ax/1.0' | |
) { | |
$alias = substr($key, strlen('openid_ns_')); | |
break; | |
} | |
} | |
} | |
if (!$alias) { | |
# An alias for AX schema has not been found, | |
# so there is no AX data in the OP's response | |
return array(); | |
} | |
$attributes = array(); | |
foreach (explode(',', $this->data['openid_signed']) as $key) { | |
$keyMatch = $alias . '.value.'; | |
if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { | |
continue; | |
} | |
$key = substr($key, strlen($keyMatch)); | |
if (!isset($this->data['openid_' . $alias . '_type_' . $key])) { | |
# OP is breaking the spec by returning a field without | |
# associated ns. This shouldn't happen, but it's better | |
# to check, than cause an E_NOTICE. | |
continue; | |
} | |
$value = $this->data['openid_' . $alias . '_value_' . $key]; | |
$key = substr($this->data['openid_' . $alias . '_type_' . $key], | |
strlen('http://axschema.org/')); | |
$attributes[$key] = $value; | |
} | |
return $attributes; | |
} | |
protected function getSregAttributes() | |
{ | |
$attributes = array(); | |
$sreg_to_ax = array_flip(self::$ax_to_sreg); | |
foreach (explode(',', $this->data['openid_signed']) as $key) { | |
$keyMatch = 'sreg.'; | |
if (substr($key, 0, strlen($keyMatch)) != $keyMatch) { | |
continue; | |
} | |
$key = substr($key, strlen($keyMatch)); | |
if (!isset($sreg_to_ax[$key])) { | |
# The field name isn't part of the SREG spec, so we ignore it. | |
continue; | |
} | |
$attributes[$sreg_to_ax[$key]] = $this->data['openid_sreg_' . $key]; | |
} | |
return $attributes; | |
} | |
/** | |
* Gets AX/SREG attributes provided by OP. should be used only after successful validaton. | |
* Note that it does not guarantee that any of the required/optional parameters will be present, | |
* or that there will be no other attributes besides those specified. | |
* In other words. OP may provide whatever information it wants to. | |
* * SREG names will be mapped to AX names. | |
* * @return Array Array of attributes with keys being the AX schema names, e.g. 'contact/email' | |
* @see http://www.axschema.org/types/ | |
*/ | |
function getAttributes() | |
{ | |
if (isset($this->data['openid_ns']) | |
&& $this->data['openid_ns'] == 'http://specs.openid.net/auth/2.0' | |
) { # OpenID 2.0 | |
# We search for both AX and SREG attributes, with AX taking precedence. | |
return $this->getAxAttributes() + $this->getSregAttributes(); | |
} | |
return $this->getSregAttributes(); | |
} | |
} | |
User-agent: * | User-agent: * |
Disallow: /admin | Disallow: /admin |
Sitemap: http://contracts.disclosurelo.gs/sitemap.xml.php |
<?php | <?php |
/* | /* |
search ABNs | search ABNs |
search agency name | search agency name |
search categories | search categories |
search supplier names | search supplier names |
--search supplier postcodes/suburbs/cities-- | --search supplier postcodes/suburbs/cities-- |
search CN number | search CN number |
search description full text | search description full text |
*/ | */ |
include('./lib/common.inc.php'); | include('./lib/common.inc.php'); |
if ($_REQUEST['searchID']) { | if ($_REQUEST['searchID']) { |
$searchIDParts = explode("-",$_REQUEST['searchID']); | $searchIDParts = explode("-", $_REQUEST['searchID']); |
$type = array_shift($searchIDParts); | $type = array_shift($searchIDParts); |
$host = $_SERVER['HTTP_HOST']; | $host = $_SERVER['HTTP_HOST']; |
$uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\'); | $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\'); |
if ($type == "agency") { | if ($type == "agency") { |
header("Location: http://$host$uri/displayAgency.php?agency=".implode("-",$searchIDParts)); | header("Location: http://$host$uri/displayAgency.php?agency=" . implode("-", $searchIDParts)); |
} | } |
if ($type == "supplier") { | if ($type == "supplier") { |
header("Location: http://$host$uri/displaySupplier.php?supplier=".implode("-",$searchIDParts)); | header("Location: http://$host$uri/displaySupplier.php?supplier=" . implode("-", $searchIDParts)); |
} | } |
exit; | if ($type == "cnid") { |
header("Location: http://$host$uri/displayContract.php?CNID=" . implode("-", $searchIDParts)); | |
} | |
exit; | |
} else { | } else { |
include_header("Search Results"); | include_header("Search Results"); |
print_r($_REQUEST); | print_r($_REQUEST); |
include_footer(); | include_footer(); |
} | } |
?> | ?> |
<?php | <?php |
include_once ("./lib/common.inc.php"); | include_once ("./lib/common.inc.php"); |
$input = strtolower($_REQUEST['input']); | $input = strtolower($_REQUEST['input']); |
$len = strlen($input); | $len = strlen($input); |
$limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 0; | $limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 0; |
$aResults = array(); | $aResults = array(); |
$count = 0; | $count = 0; |
if ($len) { | if ($len) { |
$query = 'SELECT MAX("supplierName"), MAX("supplierABN"), count(*) as count | $query = 'SELECT MAX("supplierName"), MAX("supplierABN"), count(*) as count |
FROM contractnotice | FROM contractnotice |
WHERE "supplierName" ILIKE :supplierName | WHERE "supplierName" ILIKE :supplierName |
GROUP BY "supplierName" | GROUP BY "supplierName" |
ORDER BY count(*) DESC | ORDER BY count(*) DESC |
LIMIT 4; | LIMIT 4; |
'; | '; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$supplierName = $input . '%'; | $supplierName = $input . '%'; |
$query->bindParam(":supplierName", $supplierName); | $query->bindParam(":supplierName", $supplierName); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
$count++; | $count++; |
$aResults[] = array( | $aResults[] = array( |
"id" => "supplier-" . $row[1] . '-' . $row[0], | "id" => "supplier-" . $row[1] . '-' . $row[0], |
"value" => htmlspecialchars($row[0]), | "value" => htmlspecialchars($row[0]), |
"info" => htmlspecialchars("Supplier - " . $row['count'] . " records") | "info" => htmlspecialchars("Supplier - " . $row['count'] . " records") |
); | ); |
} | } |
$query = 'SELECT MAX("agencyName"), count(*) as count | $query = 'SELECT MAX("agencyName"), count(*) as count |
FROM contractnotice | FROM contractnotice |
WHERE "agencyName" ILIKE :agencyName | WHERE "agencyName" ILIKE :agencyName |
GROUP BY "agencyName" | GROUP BY "agencyName" |
ORDER BY count DESC | ORDER BY count DESC |
LIMIT 4;'; | LIMIT 4;'; |
$query = $conn->prepare($query); | $query = $conn->prepare($query); |
$agencyName = $input . '%'; | $agencyName = $input . '%'; |
$query->bindParam(":agencyName", $agencyName); | $query->bindParam(":agencyName", $agencyName); |
$query->execute(); | $query->execute(); |
databaseError($conn->errorInfo()); | databaseError($conn->errorInfo()); |
foreach ($query->fetchAll() as $row) { | foreach ($query->fetchAll() as $row) { |
$count++; | $count++; |
$aResults[] = array( | $aResults[] = array( |
"id" => "agency-" . $row[0], | "id" => "agency-" . $row[0], |
"value" => htmlspecialchars($row[0]), | "value" => htmlspecialchars($row[0]), |
"info" => htmlspecialchars("Government Agency - " . $row['count'] . " records") | "info" => htmlspecialchars("Government Agency - " . $row['count'] . " records") |
); | ); |
} | } |
$query = "SELECT \"CNID\", description, value FROM contractnotice | |
WHERE to_tsvector('english', description) @@ to_tsquery('english', :input)"; | |
$query = $conn->prepare($query); | |
$agencyName = $input . '%'; | |
$query->bindParam(":input", $input); | |
$query->execute(); | |
databaseError($conn->errorInfo()); | |
foreach ($query->fetchAll() as $row) { | |
$count++; | |
$aResults[] = array( | |
"id" => "cnid-" . $row[0], | |
"value" => htmlspecialchars($row['description']), | |
"info" => htmlspecialchars("Contract Notice - Value ".$row['value']) | |
); | |
} | |
} | } |
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past | header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past |
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified | header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified |
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 | header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 |
header("Pragma: no-cache"); // HTTP/1.0 | header("Pragma: no-cache"); // HTTP/1.0 |
if (isset($_REQUEST['json'])) { | if (isset($_REQUEST['json'])) { |
header("Content-Type: application/json"); | header("Content-Type: application/json"); |
echo "{\"results\": ["; | echo "{\"results\": ["; |
$arr = array(); | $arr = array(); |
for ($i = 0; $i < count($aResults); $i++) { | for ($i = 0; $i < count($aResults); $i++) { |
$arr[] = "{\"id\": \"" . $aResults[$i]['id'] . "\", \"value\": \"" . $aResults[$i]['value'] . "\", \"info\": \"\"}"; | $arr[] = "{\"id\": \"" . $aResults[$i]['id'] . "\", \"value\": \"" . $aResults[$i]['value'] . "\", \"info\": \"\"}"; |
} | } |
echo implode(", ", $arr); | echo implode(", ", $arr); |
echo "]}"; | echo "]}"; |
} else { | } else { |
header("Content-Type: text/xml"); | header("Content-Type: text/xml"); |
echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?><results>"; | echo "<?xml version=\"1.0\" encoding=\"utf-8\" ?><results>"; |
for ($i = 0; $i < count($aResults); $i++) { | for ($i = 0; $i < count($aResults); $i++) { |
echo "<rs id=\"" . $aResults[$i]['id'] . "\" info=\"" . $aResults[$i]['info'] . "\">" . $aResults[$i]['value'] . "</rs>"; | echo "<rs id=\"" . $aResults[$i]['id'] . "\" info=\"" . $aResults[$i]['info'] . "\">" . $aResults[$i]['value'] . "</rs>"; |
} | } |
echo "</results>"; | echo "</results>"; |
} | } |
?> | ?> |
<?php | <?php |
include ('include/common.inc.php'); | include ('lib/common.inc.php'); |
$last_updated = date('Y-m-d',@filemtime('cbrfeed.zip')); | $last_updated = date('Y-m-d',@filemtime('cbrfeed.zip')); |
header("Content-Type: text/xml"); | header("Content-Type: text/xml"); |
echo "<?xml version='1.0' encoding='UTF-8'?>"; | echo "<?xml version='1.0' encoding='UTF-8'?>"; |
echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; | echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"; |
echo " <url><loc>".curPageURL()."index.php</loc><priority>1.0</priority></url>\n"; | echo " <url><loc>".local_url()."index.php</loc><priority>1.0</priority></url>\n"; |
foreach (scandir("./") as $file) { | foreach (scandir("./") as $file) { |
if (strpos($file,".php") !== false && $file != "index.php" && $file != "sitemap.xml.php") echo " <url><loc>".curPageURL()."$file</loc><priority>0.3</priority></url>\n"; | if (strpos($file,".php") !== false && $file != "index.php" && $file != "sitemap.xml.php") echo " <url><loc>".local_url()."$file</loc><priority>0.3</priority></url>\n"; |
} | } |
foreach (getStops() as $stop) { | |
echo " <url><loc>".curPageURL()."stop.php?stopid=".htmlspecialchars ($stop["stop_id"])."</loc>"; | |
echo "<lastmod>" . $last_updated . "</lastmod>"; | |
echo "<changefreq>monthly</changefreq>"; | |
echo "<priority>0.9</priority>"; | |
echo "</url>\n"; | |
} | |
foreach (getRoutes() as $route) { | |
echo " <url><loc>".curPageURL()."trip.php?routeid=".htmlspecialchars ($route["route_id"])."</loc>"; | |
echo "<lastmod>" . $last_updated . "</lastmod>"; | |
echo "<changefreq>monthly</changefreq>"; | |
echo "<priority>0.9</priority>"; | |
echo "</url>\n"; | |
} | |
echo '</urlset>'; | echo '</urlset>'; |
?> | ?> |