Migrated and modified maintenance.php, where session garbage collection should live -- so it can be called by cron.

Added update.php to replace ./update/VersionCheck.php in v1.0.  Enabled 1-hour caching for this page and verified XML output in client tests.
Added accompanying template for update script.
Moved annoying local configurations found in .htaccess into htaccess.dist so it's readily visible.
Removed old .htaccess file, since it was evil.
This commit is contained in:
mike.morgan%oregonstate.edu 2006-01-18 10:11:04 +00:00
parent 48887d6411
commit 3e9a032c95
7 changed files with 535 additions and 12 deletions

View File

@ -1,9 +0,0 @@
# You MUST define [your path]/inc as an include_path!
# Going to have to change this, bub.
php_value include_path /home/morgamic/public_html/v2/public/inc:.:/usr/share/pear:/home/morgamic/public_html/v2/shared/lib
# Init script to set up required libraries.
php_value auto_prepend_file init.php
# Finish script that calls $tpl->display for global Smarty object.
php_value auto_append_file finish.php

View File

@ -0,0 +1,11 @@
# a) Install me in public/htdocs/.htaccess, or
# b) Create a <Directory> entry in your Apache conf
# You MUST define YOURPATH/inc as an include_path!
php_value include_path /YOURPATH/v2/public/inc:.:/usr/share/pear:/YOURPATH/v2/shared/lib
# Init script to set up required libraries.
php_value auto_prepend_file init.php
# Finish script that calls $tpl->display for global Smarty object.
php_value auto_append_file finish.php

View File

@ -0,0 +1,267 @@
<?php
/**
* VersionCheck.php is a dynamic RDF that compares version information for
* extensions and determines whether or not an update is needed. If an update
* is needed, the correct update file is referenced based on the UMO database
* and repository. The script is set to die silently instead of echoing errors
* clients don't use anyway. For testing, if you would like to debug, supply
* the script with ?debug=true
*
* @package umo
* @subpackage pub
*/
/*
* VARIABLES
*
* Initialize, set up and clean variables.
*/
// Map the mysql main.type enum into the right type.
$ext_typemap = array('T' => 'theme',
'E' => 'extension',
'P' => 'plugin');
// Required variables that we need to run the script.
$required_vars = array('reqVersion',
'id',
'version',
'appID',
'appVersion');
// Debug flag.
$debug = (isset($_GET['debug']) && $_GET['debug'] == 'true') ? true : false;
// Array to hold errors for debugging.
$errors = array();
// Set OS. get_os_id() can only return an int.
$sql['os_id'] = get_os_id();
// Iterate through required variables, and escape/assign them as necessary.
foreach ($required_vars as $var) {
if (empty($_GET[$var])) {
$errors[] = 'Required variable '.$var.' not set.'; // set debug error
} else {
$sql[$var] = mysql_real_escape_string($_GET[$var]);
}
}
// Determine a cache_id based on params.
$cache_id = md5( $sql['os_id'] . implode('',$sql) );
/**
* CHECK CACHE
*
* Check to see if we already have a matching cache_id.
* If it exists, we can pull from it and exit; and avoid recompiling.
*/
$tpl = new AMO_Smarty();
// Set our cache timeout to 1 hour.
$tpl->caching = true;
$tpl->cache_timeout = 3600;
if ($tpl->is_cached('update.tpl',$cache_id)) {
$tpl->display('update.tpl',$cache_id);
exit;
}
// If we have all of our data, clean it up for our queries.
if (empty($errors)) {
// We will need our DB in order to perform our query.
require_once('includes.php');
/*
* QUERIES
*
* All of our variables are cleaned.
* Now attempt to retrieve update information.
*/
$os_query = ($sql['os_id']) ? " OR version.OSID = '{$sql['os_id']}' " : ''; // Set up os_id.
// Query for possible updates.
$query = "
SELECT
main.guid AS extguid,
main.type AS exttype,
version.version AS extversion,
version.uri AS exturi,
version.minappver AS appminver,
version.maxappver AS appmaxver,
applications.guid AS appguid
FROM
main
INNER JOIN
version
ON
main.id = version.id
INNER JOIN
applications
ON
version.appid = applications.appid
WHERE
main.guid = '{$sql['id']}' AND
applications.guid = '{$sql['appID']}' AND
(version.OSID = 1 {$os_query} ) AND
version.approved = 'YES' AND
'{$sql['appVersion']}+' >= version.minappver AND
'{$sql['version']}' <= version.version
ORDER BY
extversion DESC,
version.MaxAppVer_int DESC,
version.OSID DESC
LIMIT 1
";
$db->query($query, SQL_INIT, SQL_ASSOC);
if (DB::isError($db->record)) {
$errors[] = 'MySQL query for item information failed.';
} elseif (empty($db->record)) {
$errors[] = 'No matching update for given item/GUID.';
} else {
$update = array();
// An update exists. Retrieve it.
foreach ($db->record as $key=>$val) {
$update[$key] = $db->record;
}
$update['exttype'] = $ext_typemap[$update['exttype']];
$tpl->assign('update',$update);
}
}
/*
* DISPLAY OUTPUT
*
* If we have valid RDF output set, our template will display it to the client.
*
* If we do not have a full RDF, clients will see a blank RDF that is
* properly formatted.
*/
if ($debug!=true) {
header("Content-type: text/xml");
$tpl->display('update.tpl');
exit;
}
/*
* DEBUG
*
* If we get here, something went wrong. For testing purposes, we can
* optionally display errors based on $_GET['debug'].
*
* By default, no errors are ever displayed because humans do not read this
* script.
*
* Until there is some sort of API for how clients handle errors,
* things should remain this way.
*/
if ($debug == true) {
echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
echo '<html lang="en">';
echo '<head>';
echo '<title>VersionCheck.php Debug Information</title>';
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">';
echo '</head>';
echo '<body>';
echo '<h1>Parameters</h1>';
echo '<pre>';
print_r($_GET);
echo '</pre>';
if (!empty($query)) {
echo '<h1>Query</h1>';
echo '<pre>';
echo $query;
echo '</pre>';
}
if (!empty($update)) {
echo '<h1>Result</h1>';
echo '<pre>';
print_r($update);
echo '</pre>';
}
if (!empty($errors) && is_array($errors)) {
echo '<h1>Errors Found</h1>';
echo '<pre>';
print_r($errors);
echo '</pre>';
} else {
echo '<h1>No Errors Found</h1>';
}
echo '</body>';
echo '</html>';
exit;
}
/*
* FUNCTIONS
*/
/**
* Determine the os_id based on passed OS or guess based on UA string.
* @return int|bool $id ID of the OS in the UMO database
*/
function get_os_id()
{
/* OS from UMO database
2 Linux
3 MacOSX
4 BSD
5 Windows
6 Solaris
*/
// possible matches
$os = array(
'linux'=>2,
'mac'=>3,
'bsd'=>4,
'win'=>5,
'solaris'=>6
);
// Check UA string for a match
foreach ($os as $string=>$id) {
if (preg_match("/^.*{$string}.*$/i",$_SERVER['HTTP_USER_AGENT'])) {
return $id;
}
}
// If we get here, there is no defined OS
// This OS is undetermined, and the query will instead rely on "ALL" (1) in the OR
return false;
}
?>

View File

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
{if $update}
<RDF:Description about="urn:mozilla:{$update.exttype}:{$reqItemGuid}">
<em:updates>
<RDF:Seq>
<RDF:li resource="urn:mozilla:{$update.exttype}:{$reqItemGuid}:{$update.extversion}"/>
</RDF:Seq>
</em:updates>
</RDF:Description>
<RDF:Description about="urn:mozilla:{$update.exttype}:{$update.extguid}:{$update.extversion}">
<em:version>{$update.extversion}</em:version>
<em:targetApplication>
<RDF:Description>
<em:id>{$update.appguid}</em:id>
<em:minVersion>{$update.appminver}</em:minVersion>
<em:maxVersion>{$update.appmaxver}</em:maxVersion>
<em:updateLink>{$update.exturi}</em:updateLink>
</RDF:Description>
</em:targetApplication>
</RDF:Description>
{/if}
</RDF:RDF>

View File

@ -0,0 +1,13 @@
<?php
/**
* AMO global configuration document.
* Unless otherwise noted, trailing slashes should not be used.
* @package amo
* @subpackage bin
*/
define('DB_USER','');
define('DB_PASS','');
define('DB_HOST','');
define('DB_NAME','');
define('DB_PORT', '3306');
?>

View File

@ -0,0 +1,215 @@
<?php
/**
* Maintenance script for addons.mozilla.org.
*
* The purpose of this document is to perform periodic tasks that should not be
* done everytime a download occurs in install.php. This should reduce
* unnecessary DELETE and UPDATE queries and lighten the load on the database
* backend.
*
* This script should not ever be accessed over HTTP, and instead run via cron.
* Only sysadmins should be responsible for operating this script.
*
* @package amo
* @subpackage bin
*/
// Before doing anything, test to see if we are calling this from the command
// line. If this is being called from the web, HTTP environment variables will
// be automatically set by Apache. If these are found, exit immediately.
if (isset($_SERVER['HTTP_HOST'])) {
exit;
}
// If we get here, we're on the command line, which means we can continue.
require_once('config.php');
/**
* * Get time as a float.
* * @return float
* */
function getmicrotime() {
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
// Start our timer.
$start = getmicrotime();
// Connect to our database.
$connection = mysql_connect(DB_HOST, DB_USER, DB_PASS);
if (!is_resource($connection)) {
die('Could not connect: ' . mysql_error());
} else {
mysql_select_db(DB_NAME, $connection);
}
// Get our action.
$action = isset($_SERVER['argv'][1]) ? $_SERVER['argv'][1] : '';
// Perform specified task. If a task is not properly defined, exit.
switch ($action) {
/**
* Update weekly addon counts.
*/
case 'weekly':
// Get 7 day counts from the download table.
$seven_day_count_sql = "
SELECT
downloads.ID as ID,
COUNT(downloads.ID) as seven_day_count
FROM
`downloads`
WHERE
`date` >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY
downloads.ID
ORDER BY
downloads.ID
";
echo 'Retrieving seven-day counts from `downloads` ...'."\n";
$seven_day_count_result = mysql_query($seven_day_count_sql, $connection)
or trigger_error('MySQL Error '.mysql_errno().': '.mysql_error()."",
E_USER_NOTICE);
$affected_rows = mysql_num_rows($seven_day_count_result);
if ($affected_rows > 0 ) {
$seven_day_counts = array();
while ($row = mysql_fetch_array($seven_day_count_result)) {
$seven_day_counts[$row['ID']] = ($row['seven_day_count']>0) ? $row['seven_day_count'] : 0;
}
echo 'Updating seven day counts in `main` ...'."\n";
foreach ($seven_day_counts as $id=>$seven_day_count) {
$seven_day_count_update_sql = "
UPDATE `main` SET `downloadcount`='{$seven_day_count}' WHERE `id`='{$id}'
";
$seven_day_count_update_result = mysql_query($seven_day_count_update_sql, $connection)
or trigger_error('mysql error '.mysql_errno().': '.mysql_error()."",
E_USER_NOTICE);
}
}
break;
/**
* Update all total download counts.
*/
case 'total':
// Get uncounted hits from the download table.
$uncounted_hits_sql = "
SELECT
downloads.ID as ID,
COUNT(downloads.ID) as count
FROM
downloads
WHERE
`counted`=0
GROUP BY
downloads.ID
ORDER BY
downloads.ID
";
echo 'Retrieving uncounted downloads ...'."\n";
$uncounted_hits_result = mysql_query($uncounted_hits_sql, $connection)
or trigger_error('MySQL Error '.mysql_errno().': '.mysql_error()."",
E_USER_NOTICE);
$affected_rows = mysql_num_rows($uncounted_hits_result);
if ($affected_rows > 0) {
$uncounted_hits = array();
while ($row = mysql_fetch_array($uncounted_hits_result)) {
$uncounted_hits[$row['ID']] = ($row['count'] > 0) ? $row['count'] : 0;
}
echo 'Updating download totals ...'."\n";
foreach ($uncounted_hits as $id=>$hits) {
$uncounted_update_sql = "
UPDATE `main` SET `TotalDownloads`=`TotalDownloads`+{$hits} WHERE `ID`='{$id}'
";
$uncounted_update_result = mysql_query($uncounted_update_sql, $connection)
or trigger_error('MySQL Error '.mysql_errno().': '.mysql_error()."",
E_USER_NOTICE);
}
// If we get here, we've counted everything and we can mark stuff for
// deletion.
//
// Mark the downloads we just counted as counted if:
// a) it is a day count that is more than 8 days old
// b) it is a download log that has not been counted
//
// We may lose a couple counts, theoretically, but it's negligible - we're not
// NASA (yes, THE NASA).
$counted_update_sql = "
UPDATE
`downloads`
SET
`counted`=1
";
$counted_update_result = mysql_query($counted_update_sql, $connection)
or trigger_error('MySQL Error '.mysql_errno().': '.mysql_error()."",
E_USER_NOTICE);
}
break;
/**
* Garbage collection for all records that are older than 8 days.
*/
case 'gc':
echo 'Starting garbage collection ...'."\n";
$gc_sql = "
DELETE FROM
`downloads`
WHERE
`date` < DATE_SUB(NOW(), INTERVAL 8 DAY)
";
$gc_result = mysql_query($gc_sql, $connection)
or trigger_error('MySQL Error '.mysql_errno().': '.mysql_error()."",
E_USER_NOTICE);
// This is unreliable, but it's not a big deal.
$affected_rows = mysql_affected_rows();
break;
/**
* Unknown command.
*/
default:
echo 'Command not found. Exiting ...'."\n";
exit;
break;
}
// End switch.
// How long did it take to run?
$exectime = getmicrotime() - $start;
// Display script output.
echo 'Affected rows: '.$affected_rows.' ';
echo 'Time: '.$exectime."\n";
echo 'Exiting ...'."\n";
exit;
?>

View File

@ -110,8 +110,8 @@ class AddOn extends AMO_Object {
if (!empty($this->db->record)) {
$this->setVars($this->db->record);
if (file_exists(ROOT_PATH.$this->PreviewURI)) {
$size = getimagesize(ROOT_PATH.$this->PreviewURI);
if (file_exists(ROOT_PATH.'/htdocs'.$this->PreviewURI)) {
$size = getimagesize(ROOT_PATH.'/htdocs'.$this->PreviewURI);
$this->setVar('PreviewWidth',$size[0]);
$this->setVar('PreviewHeight',$size[1]);
}
@ -139,7 +139,7 @@ class AddOn extends AMO_Object {
while ($this->db->next(SQL_ASSOC)) {
$result = $this->db->record;
$uri = $result['PreviewURI'];
list($src_width, $src_height, $type, $attr) = getimagesize(ROOT_PATH.$uri);
list($src_width, $src_height, $type, $attr) = getimagesize(ROOT_PATH.'/htdocs'.$uri);
$this->Previews[] = array(
'PreviewURI' => $uri,
'caption' => $result['caption'],