Self-hosted Cydia repo download counter
CREATE TABLE `downloads` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`filename` text NOT NULL,
`first_download` datetime NOT NULL,
`last_download` datetime NOT NULL,
`count` int(11) NOT NULL DEFAULT 1,
PRIMARY KEY (`id`)
);
<?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.");
}
2018-04-22: This is pretty old, possibly broken on newer PHP, and the original blog post that accompanied it is lost to time. I’m keeping this here for posterity and you’re free to use it, but please consider other “download counter” scripts instead (doesn’t need to be Cydia-specific — they’re all the same thing).
counter.php
and .htaccess
in your favorite editor (not Notepad).counter.php
, scroll down to the line that says $link = mysqli_connect( ...
and change the parameters to the ones you use to connect to MySQL.mysqli_select_db($link, "myrepodb")
line and change myrepodb
to the name of your database.downloads.sql
file in your MySQL database. In phpMyAdmin, you can use the Import tab to do this.downloads
in your repo folder..htaccess
file and replace repo
with the path to the folder that your repo is in, relative to your document root (usually htdocs or public_html)Note this only collects the stats. It’s up to you to write something to download
# Use the Apache rewrite engine to redirect downloads to our script.
RewriteEngine On
RewriteBase /
RewriteRule ^repo/((Packages|Release)(.*)?)$ /repo/counter.php?filename=$1 [L,QSA]
RewriteRule ^repo/downloads/(.*)\.deb$ /repo/counter.php?filename=$1 [L,QSA]