Skip to content
Browse files

Downloads are now secured using md5 hashing logic instead of using my…

…sql database to keep track of random keys. Download links are valid for two days only. The default interval WordPress uses for theme updates is 12 hours.
  • Loading branch information...
1 parent 6345736 commit c48e5d1334a91276c5377b80fe0431d7213f7bbb @jeremyclark13 committed Aug 29, 2012
Showing with 185 additions and 291 deletions.
  1. +24 −65 api/download.php
  2. +80 −209 api/index.php
  3. +58 −0 api/packages.php
  4. +4 −0 api/update/.htaccess
  5. 0 api/update/{test-plugin-update.zip → plugin.zip}
  6. +0 −6 download_table.sql
  7. +9 −5 readme.md
  8. +10 −6 readme.txt
View
89 api/download.php
@@ -1,73 +1,32 @@
<?php
-/*
-*
-* One Time Download
-* Jacob Wyke
-* jacob@frozensheep.com
-*
-*/
+require_once('packages.php');
+// This is the folder where all update files are stored
+$update_folder = './update/';
-//The directory where the download files are kept - random folder names are best
-$strDownloadFolder = "./update/";
+if ( isset( $_GET['key'] ) ) {
+ // loop over all the theme and plugin arrays
+ foreach ( $packages as $package ) {
+ // loop over all the versions for each theme and plugin
+ foreach ( $package['versions'] as $version ) {
+ // md5 timestamp of current and previous day and the file name
+ $tod_md5 = md5( $version['file_name'] . mktime( 0, 0, 0, date( "m" ), date( "d" ), date( "Y" ) ) );
+ $yes_md5 = md5( $version['file_name'] . mktime( 0, 0, 0, date( "m" ), date( "d" ) - 1, date( "Y" ) ) );
+ // test if the either of the md5 hashes match what was passed
+ if ( $_GET['key'] == $tod_md5 || $_GET['key'] == $yes_md5 ) {
+ $download = $update_folder . $version['file_name'];
+ if ( file_exists( $download ) ) {
+ //get the file content
+ $file = file_get_contents( $download );
-//If you can download a file more than once
-$boolAllowMultipleDownload = 0;
+ //set the headers to force a download
+ header( "Content-type: application/force-download" );
+ header( "Content-Disposition: attachment; filename=\"" . str_replace( " ", "_", $version['file_name'] ) . "\"" );
-//connect to the DB
-
-/***********************
-DATABASE INFO
-************************/
-
- $resDB = mysql_connect("DB_SERVER", "DB_USER", "DB_PASSWORD");
- mysql_select_db("DB_NAME", $resDB);
-
-if(!empty($_GET['key'])){
- //check the DB for the key
- $resCheck = mysql_query("SELECT * FROM downloads WHERE downloadkey = '".mysql_real_escape_string($_GET['key'])."' LIMIT 1");
- $arrCheck = mysql_fetch_assoc($resCheck);
- if(!empty($arrCheck['file'])){
- //check that the download time hasnt expired
- if($arrCheck['expires']>=time()){
- if(!$arrCheck['downloads'] OR $boolAllowMultipleDownload){
- //everything is hunky dory - check the file exists and then let the user download it
- $strDownload = $strDownloadFolder.$arrCheck['file'];
-
- if(file_exists($strDownload)){
-
- //get the file content
- $strFile = file_get_contents($strDownload);
-
- //set the headers to force a download
- header("Content-type: application/force-download");
- header("Content-Disposition: attachment; filename=\"".str_replace(" ", "_", $arrCheck['file'])."\"");
-
- //echo the file to the user
- echo $strFile;
-
- //update the DB to say this file has been downloaded
- mysql_query("UPDATE downloads SET downloads = downloads + 1 WHERE downloadkey = '".mysql_real_escape_string($_GET['key'])."' LIMIT 1");
-
- exit;
-
- }else{
- echo "We couldn't find the file to download.";
+ //echo the file to the user
+ echo $file;
+ }
+ }
}
- }else{
- //this file has already been downloaded and multiple downloads are not allowed
- echo "This file has already been downloaded.";
- }
- }else{
- //this download has passed its expiry date
- echo "This download has expired.";
}
- }else{
- //the download key given didnt match anything in the DB
- echo "No file was found to download.";
- }
-}else{
- //No download key wa provided to this script
- echo "No download key was provided. Please return to the previous page and try again.";
}
-
?>
View
289 api/index.php
@@ -1,245 +1,116 @@
-<?php
-/*******
- Original Plugin & Theme API by Kaspars Dambis (kaspars@konstruktors.com)
- Modified by Jeremy Clark http://clark-technet.com
- Donate Link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SE9ZVJUS324UC
-*******/
+<?php
+/* * *****
+ Original Plugin & Theme API by Kaspars Dambis (kaspars@konstruktors.com)
+ Modified by Jeremy Clark http://clark-technet.com
+ Donate Link: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SE9ZVJUS324UC
+ * ***** */
// Pull user agent
$user_agent = $_SERVER['HTTP_USER_AGENT'];
//Kill magic quotes. Can't unserialize POST variable otherwise
-if (get_magic_quotes_gpc()) {
- $process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
- while (list($key, $val) = each($process)) {
- foreach ($val as $k => $v) {
- unset($process[$key][$k]);
- if (is_array($v)) {
- $process[$key][stripslashes($k)] = $v;
- $process[] = &$process[$key][stripslashes($k)];
+if ( get_magic_quotes_gpc() ) {
+ $process = array( &$_GET, &$_POST, &$_COOKIE, &$_REQUEST );
+ while ( list($key, $val) = each( $process ) ) {
+ foreach ( $val as $k => $v ) {
+ unset( $process[$key][$k] );
+ if ( is_array( $v ) ) {
+ $process[$key][stripslashes( $k )] = $v;
+ $process[] = &$process[$key][stripslashes( $k )];
} else {
- $process[$key][stripslashes($k)] = stripslashes($v);
+ $process[$key][stripslashes( $k )] = stripslashes( $v );
}
}
}
- unset($process);
+ unset( $process );
}
-
+require_once('packages.php');
//Create one time download link to secure zip file location
-if (stristr($user_agent, 'WordPress') == TRUE){
- /*
- *
- * Create Download Link
- * Jaocb Wyke
- * jacob@frozensheep.com
- *
- */
-
-/**********************************************
-Uncomment Below Section to enable url masking
-**********************************************/
-/*REMOVE THIS LINE
- //Database Info
- $resDB = mysql_connect("DB_SERVER", "DB_USER", "DB_PASSWORD");
- mysql_select_db("DB_NAME", $resDB);
-
- function createKey(){
- //create a random key
- $strKey = md5(microtime());
-
- //check to make sure this key isnt already in use
- $resCheck = mysql_query("SELECT count(*) FROM downloads WHERE downloadkey = '{$strKey}' LIMIT 1");
- $arrCheck = mysql_fetch_assoc($resCheck);
- if($arrCheck['count(*)']){
- //key already in use
- return createKey();
- }else{
- //key is OK
- return $strKey;
- }
- }
-
- //get a unique download key
- $strKey = createKey();
-
- // Deletes records over two weeks old
- mysql_query("DELETE FROM downloads WHERE expires > '" .(time()+(60*60*24*14))."' ");
-
-REMOVE THIS LINE*/
-}
+if ( stristr( $user_agent, 'WordPress' ) == TRUE ) {
+ // Process API requests
+ $action = $_POST['action'];
+ $args = unserialize( $_POST['request'] );
+ if ( is_array( $args ) )
+ $args = array_to_object( $args );
-// Theme with update info
-$packages['theme'] = array( //Replace theme with theme stylesheet slug that the update is for
- 'versions' => array(
- '1.0' => array( //Array name should be set to current version of update
- 'version' => '1.0', //Current version available
- 'date' => '2010-04-10', //Date version was released
- /*
- Remove line below if using one time download link
- */
- 'package' => 'http://url_to_your_site/theme.zip', // The zip file of the theme update
- /*
- Use below value if using the one time download link. Point to location of download.php file on your server.
- */
- //'package' => 'http://url_to_your_site/download.php?key=' . $strKey,
- //'file_name' => 'theme.zip', //File name of theme zip file
- 'author' => 'Author Name', //Author of theme
- 'name' => 'Theme Name', //Name of theme
- 'requires'=> '3.1', //Wordpress version required
- 'tested' => '3.1', //WordPress version tested up to
- 'screenshot_url'=> 'http://url_to_your_theme_site/screenshot.png' //url of screenshot of theme
- )
- ),
- 'info' => array(
- 'url' => 'http://url_to_your_theme_site' // Website devoted to theme if available
- )
-);
-
-// Plugin with update info
-$packages['plugin'] = array( //Replace plugin with the plugin slug that updates will be checking for
- 'versions' => array(
- '1.0' => array( //Array name should be set to current version of update
- 'version' => '1.0', //Current version available
- 'date' => '2010-04-10', //Date version was released
- 'author' => 'Author Name', //Author name - can be linked using html - <a href="http://link-to-site.com">Author Name</a>
- 'requires' => '2.8', // WP version required for plugin
- 'tested' => '3.0.1', // WP version tested with
- 'homepage' => 'http://your_plugin_website', // Site devoted to your plugin if available
- 'downloaded'=> '1000', // Number of times downloaded
- 'external' => '', // Unused
- /*
- Use below value if using the one time download link. Point to location of download.php file on your server.
- */
- //'package' => 'http://url_to_your_site/download.php?key=' . $strKey,
- //'file_name' => 'plugin.zip', //File name of theme zip file
- /*
- Remove line below if using one time download link
- */
- 'package' => 'http://url_to_your_site/plugin.zip', // The zip file of the plugin update
-
-
- 'sections' => array(
- /* Plugin Info sections tabs. Each key will be used as the title of the tab, value is the contents of tab.
- Must be lowercase to function properly
- HTML can be used in all sections below for formating. Must be properly escaped ie a single quote would have to be \'
- Screenshot section must use exteranl links for img tags.
- */
- 'description' => 'Description of Plugin', //Description Tab
- 'installation' => 'Install Info', //Installaion Tab
- 'screen shots' => 'Screen Shots', //Screen Shots
- 'change log' => 'Change log', //Change Log Tab
- 'faq' => 'FAQ', //FAQ Tab
- 'other notes' => 'Other Notes' //Other Notes Tab
- )
- )
- ),
- 'info' => array(
- 'url' => 'http://your_plugin_webiste' // Site devoted to your plugin if available
- )
-);
-
-
-
-
-if (stristr($user_agent, 'WordPress') == TRUE){
-
- // Process API requests
- $action = $_POST['action'];
- $args = unserialize($_POST['request']);
-
- if (is_array($args))
- $args = array_to_object($args);
-
- $latest_package = array_shift($packages[$args->slug]['versions']);
-
+ $latest_package = array_shift( $packages[$args->slug]['versions'] );
} else {
- /*
- An error message can be displayed to users who go directly to the update url
- */
+ /*
+ An error message can be displayed to users who go directly to the update url
+ */
- echo 'Whoops, this page doesn\'t exist';
+ echo 'Whoops, this page doesn\'t exist';
}
-
// basic_check
-if ($action == 'basic_check') {
- $update_info = array_to_object($latest_package);
- $update_info->slug = $args->slug;
-
- if (version_compare($args->version, $latest_package['version'], '<')){
- $update_info->new_version = $update_info->version;
- print serialize($update_info);
- }
-}
+if ( $action == 'basic_check' ) {
+ $update_info = array_to_object( $latest_package );
+ $update_info->slug = $args->slug;
+ if ( version_compare( $args->version, $latest_package['version'], '<' ) ) {
+ $update_info->new_version = $update_info->version;
+ print serialize( $update_info );
+ }
+}
// plugin_information
-if ($action == 'plugin_information') {
- $data = new stdClass;
-
- $data->slug = $args->slug;
- $data->version = $latest_package['version'];
- $data->last_updated = $latest_package['date'];
- $data->download_link = $latest_package['package'];
- $data->author = $latest_package['author'];
- $data->external = $latest_package['external'];
- $data->requires = $latest_package['requires'];
- $data->tested = $latest_package['tested'];
- $data->homepage = $latest_package['homepage'];
- $data->downloaded = $latest_package['downloaded'];
- $data->sections = $latest_package['sections'];
- //insert the download record into the database
- //Uncomment if using url masking
- //mysql_query("INSERT INTO downloads (downloadkey, file, expires) VALUES ('{$strKey}', '{$latest_package['file_name']}', '".(time()+(60*60*24*7))."')");
- print serialize($data);
+if ( $action == 'plugin_information' ) {
+ $data = new stdClass;
+
+ $data->slug = $args->slug;
+ $data->version = $latest_package['version'];
+ $data->last_updated = $latest_package['date'];
+ $data->download_link = $latest_package['package'];
+ $data->author = $latest_package['author'];
+ $data->external = $latest_package['external'];
+ $data->requires = $latest_package['requires'];
+ $data->tested = $latest_package['tested'];
+ $data->homepage = $latest_package['homepage'];
+ $data->downloaded = $latest_package['downloaded'];
+ $data->sections = $latest_package['sections'];
+ print serialize( $data );
}
-
// theme_update
-if ($action == 'theme_update') {
- $update_info = array_to_object($latest_package);
- $update_data = array();
- $update_data['package'] = $update_info->package;
- $update_data['new_version'] = $update_info->version;
- $update_data['url'] = $packages[$args->slug]['info']['url'];
- //insert the download record into the database
- //Uncomment if using url masking
- //mysql_query("INSERT INTO downloads (downloadkey, file, expires) VALUES ('{$strKey}', '{$update_info->file_name}', '".(time()+(60*60*24*7))."')");
- if (version_compare($args->version, $latest_package['version'], '<'))
- print serialize($update_data);
+if ( $action == 'theme_update' ) {
+ $update_info = array_to_object( $latest_package );
+ $update_data = array( );
+ $update_data['package'] = $update_info->package;
+ $update_data['new_version'] = $update_info->version;
+ $update_data['url'] = $packages[$args->slug]['info']['url'];
+ if ( version_compare( $args->version, $latest_package['version'], '<' ) )
+ print serialize( $update_data );
}
-if ($action == 'theme_information') {
- $data = new stdClass;
- $data->slug = $args->slug;
- $data->name = $latest_package['name'];
- $data->version = $latest_package['version'];
- $data->last_updated = $latest_package['date'];
- $data->download_link = $latest_package['package'];
- $data->author = $latest_package['author'];
- $data->requires = $latest_package['requires'];
- $data->tested = $latest_package['tested'];
- $data->screenshot_url = $latest_package['screenshot_url'];
- //insert the download record into the database
- //Uncomment if using url masking
- //mysql_query("INSERT INTO downloads (downloadkey, file, expires) VALUES ('{$strKey}', '{$latest_package['file_name']}', '".(time()+(60*60*24*7))."')");
- print serialize($data);
+if ( $action == 'theme_information' ) {
+ $data = new stdClass;
+ $data->slug = $args->slug;
+ $data->name = $latest_package['name'];
+ $data->version = $latest_package['version'];
+ $data->last_updated = $latest_package['date'];
+ $data->download_link = $latest_package['package'];
+ $data->author = $latest_package['author'];
+ $data->requires = $latest_package['requires'];
+ $data->tested = $latest_package['tested'];
+ $data->screenshot_url = $latest_package['screenshot_url'];
+ print serialize( $data );
}
-function array_to_object($array = array()) {
- if (empty($array) || !is_array($array))
- return false;
-
- $data = new stdClass;
- foreach ($array as $akey => $aval)
- $data->{$akey} = $aval;
- return $data;
+function array_to_object( $array = array( ) ) {
+ if ( empty( $array ) || !is_array( $array ) )
+ return false;
+
+ $data = new stdClass;
+ foreach ( $array as $akey => $aval )
+ $data->{$akey} = $aval;
+ return $data;
}
+
?>
View
58 api/packages.php
@@ -0,0 +1,58 @@
+<?php
+// Theme with update info
+$packages['theme'] = array( //Replace theme with theme stylesheet slug that the update is for
+ 'versions' => array(
+ '1.0' => array( //Array name should be set to current version of update
+ 'version' => '1.0', //Current version available
+ 'date' => '2010-04-10', //Date version was released
+ //theme.zip is the same as file_name
+ 'package' => 'http://url_to_your_site/download.php?key=' . md5('theme.zip' . mktime(0,0,0,date("m"),date("d"),date("Y"))),
+ //file_name is the name of the file in the update folder.
+ 'file_name' => 'theme.zip', //File name of theme zip file
+ 'author' => 'Author Name', //Author of theme
+ 'name' => 'Theme Name', //Name of theme
+ 'requires' => '3.1', //Wordpress version required
+ 'tested' => '3.1', //WordPress version tested up to
+ 'screenshot_url' => 'http://url_to_your_theme_site/screenshot.png' //url of screenshot of theme
+ )
+ ),
+ 'info' => array(
+ 'url' => 'http://url_to_your_theme_site' // Website devoted to theme if available
+ )
+);
+
+// Plugin with update info
+$packages['plugin'] = array( //Replace plugin with the plugin slug that updates will be checking for
+ 'versions' => array(
+ '1.0' => array( //Array name should be set to current version of update
+ 'version' => '1.0', //Current version available
+ 'date' => '2010-04-10', //Date version was released
+ 'author' => 'Author Name', //Author name - can be linked using html - <a href="http://link-to-site.com">Author Name</a>
+ 'requires' => '2.8', // WP version required for plugin
+ 'tested' => '3.0.1', // WP version tested with
+ 'homepage' => 'http://your_plugin_website', // Site devoted to your plugin if available
+ 'downloaded' => '1000', // Number of times downloaded
+ 'external' => '', // Unused
+ //plugin.zip is the same as file_name
+ 'package' => 'http://url_to_your_site/download.php?key=' . md5('plugin.zip' . mktime(0,0,0,date("m"),date("d"),date("Y"))),
+ //file_name is the name of the file in the update folder.
+ 'file_name' => 'plugin.zip',
+ 'sections' => array(
+ /* Plugin Info sections tabs. Each key will be used as the title of the tab, value is the contents of tab.
+ Must be lowercase to function properly
+ HTML can be used in all sections below for formating. Must be properly escaped ie a single quote would have to be \'
+ Screenshot section must use exteranl links for img tags.
+ */
+ 'description' => 'Description of Plugin', //Description Tab
+ 'installation' => 'Install Info', //Installaion Tab
+ 'screen shots' => 'Screen Shots', //Screen Shots
+ 'change log' => 'Change log', //Change Log Tab
+ 'faq' => 'FAQ', //FAQ Tab
+ 'other notes' => 'Other Notes' //Other Notes Tab
+ )
+ )
+ ),
+ 'info' => array(
+ 'url' => 'http://your_plugin_webiste' // Site devoted to your plugin if available
+ )
+);
View
4 api/update/.htaccess
@@ -0,0 +1,4 @@
+<FilesMatch ".(zip)$">
+ Order Allow,Deny
+ Deny from all
+</FilesMatch>
View
0 api/update/test-plugin-update.zip → api/update/plugin.zip
File renamed without changes.
View
6 download_table.sql
@@ -1,6 +0,0 @@
-CREATE TABLE downloads (
- downloadkey varchar(32) NOT NULL unique,
- file varchar(255) NOT NULL default '',
- downloads int UNSIGNED NOT NULL default '0',
- expires int UNSIGNED NOT NULL default '0'
-)
View
14 readme.md
@@ -13,9 +13,13 @@ For themes and plugins that can't be submitted to official WordPress repository,
* api (Folder to upload to server where updates will be housed)
* .htaccess (set Options+Indexes to allow checking to work properly)
* index.php (holds code used to check request for new versions)
- * download.php (one-time download key generating/validating file)
- * update (folder to hold all zip file updates for url masking)
-
+ * packages.php (file containing all info about plugins and themes)
+ * download.php (validates md5 key of date and package zip file)
+ * update (folder to hold all zip file updates for url masking - protected by .htaccess to disallow file listings)
+
+* update (default folder for holding theme and plugin zip files)
+ * .htaccess (prevents indexing and viewing of any zip files in directory)
+
* plugin (folder for adding plugin update checking)
* test-plugin-update (simple plugin folder to show how update functions work)
* test-plugin-update.php (example plugin that only checks for updates to server)
@@ -34,7 +38,7 @@ For themes and plugins that can't be submitted to official WordPress repository,
## Adding new versions
-Edit the index.php under api folder on your server. Commented thoroughly throughout with sections that need to be changed to reflect themes/plugins that are to be updated.
+Edit the packages.php under api folder on your server. Commented thoroughly throughout with sections that need to be changed to reflect themes/plugins that are to be updated.
## Adding additional themes/plugins
@@ -46,4 +50,4 @@ Child themes are now supported. If the theme being updated is meant to be a par
## Securing Download location
-Now update file locations can be secured using a random download key generator. A sql database is used to store random keys and a download.php file is used to check for keys and then allow download. By default unsecured downloads are allowed. To setup run the download_table.sql on an existing database or new database to create the download table, then edit the api/index.php and api/download.php and change the database info. Then under the appropriate packages array remove the unsecured link and uncomment the secure link and edit the location. The download.php also has the folder name at the top that needs to be edited to tell where the update files are stored.
+Downloads are now always secured by a md5 hash of the package file_name and timestamp of current date. When downloading file current timestamp and timestamp of previous day are compared to key received from update request, if either match zip file is passed, and file can be downloaded.
View
16 readme.txt
@@ -1,6 +1,6 @@
## Automatic Theme & Plugin Updater for Self-Hosted Themes/Plugins
-** Support This: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SE9ZVJUS324UC**
+**Support This Developer: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=SE9ZVJUS324UC**
*Any amount is always appreciated*
@@ -13,9 +13,13 @@ For themes and plugins that can't be submitted to official WordPress repository,
* api (Folder to upload to server where updates will be housed)
* .htaccess (set Options+Indexes to allow checking to work properly)
* index.php (holds code used to check request for new versions)
- * download.php (one-time download key generating/validating file)
- * update (folder to hold all zip file updates for url masking)
-
+ * packages.php (file containing all info about plugins and themes)
+ * download.php (validates md5 key of date and package zip file)
+ * update (folder to hold all zip file updates for url masking - protected by .htaccess to disallow file listings)
+
+* update (default folder for holding theme and plugin zip files)
+ * .htaccess (prevents indexing and viewing of any zip files in directory)
+
* plugin (folder for adding plugin update checking)
* test-plugin-update (simple plugin folder to show how update functions work)
* test-plugin-update.php (example plugin that only checks for updates to server)
@@ -34,7 +38,7 @@ For themes and plugins that can't be submitted to official WordPress repository,
## Adding new versions
-Edit the index.php under api folder on your server. Commented thoroughly throughout with sections that need to be changed to reflect themes/plugins that are to be updated.
+Edit the packages.php under api folder on your server. Commented thoroughly throughout with sections that need to be changed to reflect themes/plugins that are to be updated.
## Adding additional themes/plugins
@@ -46,4 +50,4 @@ Child themes are now supported. If the theme being updated is meant to be a par
## Securing Download location
-Now update file locations can be secured using a random download key generator. A sql database is used to store random keys and a download.php file is used to check for keys and then allow download. By default unsecured downloads are allowed. To setup run the download_table.sql on an existing database or new database to create the download table, then edit the api/index.php and api/download.php and change the database info. Then under the appropriate packages array remove the unsecured link and uncomment the secure link and edit the location. The download.php also has the folder name at the top that needs to be edited to tell where the update files are stored.
+Downloads are now always secured by a md5 hash of the package file_name and timestamp of current date. When downloading file current timestamp and timestamp of previous day are compared to key received from update request, if either match zip file is passed, and file can be downloaded.

0 comments on commit c48e5d1

Please sign in to comment.
Something went wrong with that request. Please try again.