|
<?php |
|
function errorout($code, $message) { |
|
// Makes dpkg know it's not a package |
|
header("Content-Type: text/plain"); |
|
|
|
// Generate the HTTP error header |
|
header($_SERVER["SERVER_PROTOCOL"] . " $code $message"); |
|
|
|
// Then print out the error. |
|
// Because the content type is plain text, escaping is not necessary. |
|
die($message); |
|
} |
|
|
|
$db = new mysqli("localhost", "myrepo", "mypassword"); // change parameters to what you use |
|
|
|
if ($db->connect_error) { |
|
errorout(500, "Couldn't connect to the database. Please try your download later."); |
|
} |
|
|
|
$db->set_charset("utf8") |
|
or errorout(500, "Couldn't set up the database. Please try your download later."); |
|
|
|
$db->select_db($link, "myrepodb") // change myrepodb to your database name |
|
or errorout(500, "Couldn't open the database. Please try your download later."); |
|
|
|
// Exit here if the filename hasn’t been provided. |
|
if (!isset($_GET["filename"]) or empty($_GET["filename"])) { |
|
errorout(404, "The file you requested either doesn't exist or isn't allowed to be downloaded."); |
|
} |
|
|
|
// Store the filename in a variable, escaping it to be safe. |
|
$file = $db->real_escape_string($_GET["filename"]); |
|
|
|
// Determine the file path. |
|
// Packages and Release are expected to be in the same folder as this script. |
|
// Anything else is expected to be in a downloads/ folder. |
|
if ($file == "Packages" or strpos($file, "Packages.") === 0 |
|
or $file == "Release" or strpos($file, "Release.") === 0) { |
|
$path = $file; |
|
} else { |
|
$path = "downloads/$file"; |
|
} |
|
|
|
// The following code checks for attempts to read files that aren't debs. |
|
// Not doing this lets attackers read PHP scripts and system files. |
|
if (stristr($file, "..") or stristr($file, "/") or stristr($file, "\\") or !file_exists($file)) { |
|
errorout(404, "The file you requested either doesn't exist or isn't allowed to be downloaded."); |
|
} |
|
|
|
// Count the download in the database |
|
// A log will be saved if something goes wrong. |
|
try { |
|
// Check whether the package has already been downloaded. |
|
// We select an empty string as we don’t read the result, we just want to know if the row exists. |
|
$query = $db->query("SELECT '' FROM downloads WHERE filename='$file'"); |
|
|
|
if (!$query or $query->num_rows == 0) { |
|
// If it doesn't exist, create the row |
|
$db->query("INSERT INTO downloads SET filename='$file', count=1, first_download=NOW(), last_download=NOW()"); |
|
} else { |
|
// If it does, add one to the counter |
|
$db->query("UPDATE downloads SET count=count+1, last_download=NOW() WHERE filename='$file'"); |
|
} |
|
} catch (Exception $e) { |
|
// Something went wrong, so save a log file and continue onto the download |
|
// (The document root is usually htdocs or public_html) |
|
file_put_contents(dirname($_SERVER["DOCUMENT_ROOT"]) . "/../cydiaerr.log", print_r($e, true)); |
|
} |
|
|
|
// Finally, serve the user the file they requested |
|
$filesize = filesize($path); |
|
|
|
// If it's more than 16 bytes long, it's ok to serve it |
|
if ($filesize > 16) { |
|
// Force browsers to download the file, not display it |
|
header("Content-Type: application/octet-stream"); |
|
header("Content-Disposition: attachment; filename=\"$file\""); |
|
|
|
// Indicate how big the file is |
|
header("Content-Length: " . $filesize); |
|
|
|
// And finally, output the file! |
|
// It’s important to use readfile() as this buffers the output (reads and outputs small amounts at a time). |
|
// Reading the entire file into a string with file_get_contents() will use huge amounts of memory for bigger files. |
|
readfile($path); |
|
} else { |
|
errorout(404, "The file you requested either doesn't exist or isn't allowed to be downloaded."); |
|
} |
Just updated to clean up a bit and add a disclaimer. I haven’t written PHP in ages and haven’t used this script in ages so it may be completely broken, or it might work great! If it works (hopefully), you can use it but be aware that I’m not supporting it.