mod_rewrite to download - makes suspicous looking file - .htaccess

I decided to try and use mod_rewrite to hide the location of a file that a user can download.
So they click on a link that's directed to "/download/some_file/" and they instead get "/downloads/some_file.zip"
Implemented like this:
RewriteRule ^download/([^/\.]+)/?$ downloads/$1.zip [L]
This works except they when the download progress appears I'm getting a file "download" with no extension which looks suspicious and the user might not be aware they are supposed to unzip it. Is there a way of doing this so it looks like an actual file? Or is there a better a way I should be doing this?
To provide some context/reason for hiding the location of the file. This is for a band where the music can be downloaded for free provided the user signs up for the mailing list.
Also not I need to do this within .htaccess

You can set the filename by sending the Content-disposition header:
https://serverfault.com/questions/101948/how-to-send-content-disposition-headers-in-apache-for-files

Ok so I believe that I'm restricted as to what headers I can set using .htaccess
So I have instead solved this using php.
I initially copied a download php script found here:
How to rewrite and set headers at the same time in Apache
However my file size was too big and so this was not working properly.
After a bit of googling I came across this: http://teddy.fr/blog/how-serve-big-files-through-php
So my complete solution is as follows...
First send requests to download script:
RewriteRule ^download/([^/\.]+)/?$ downloads/download.php?download=$1 [L]
Then get full filename, set headers, and serve it chunk by chunk:
<?php
if ($_GET['download']){
$file = $_SERVER['DOCUMENT_ROOT'].'media/downloads/' . $_GET['download'] . '.zip';
}
define('CHUNK_SIZE', 1024*1024); // Size (in bytes) of tiles chunk
// Read a file and display its content chunk by chunk
function readfile_chunked($filename, $retbytes = TRUE) {
$buffer = '';
$cnt =0;
// $handle = fopen($filename, 'rb');
$handle = fopen($filename, 'rb');
if ($handle === false) {
return false;
}
while (!feof($handle)) {
$buffer = fread($handle, CHUNK_SIZE);
echo $buffer;
ob_flush();
flush();
if ($retbytes) {
$cnt += strlen($buffer);
}
}
$status = fclose($handle);
if ($retbytes && $status) {
return $cnt; // return num. bytes delivered like readfile() does.
}
return $status;
}
$save_as_name = basename($file);
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: no-cache');
header("Content-Type: application/zip");
header("Content-Disposition: disposition-type=attachment; filename=\"$save_as_name\"");
readfile_chunked($file);
?>

Related

How Can I Replace https://google.com/test TO https://google.com? NodeJS

ok so basically I want to replace https://google.com/test to https://google.com without knowing the path of it...
here an example how I want it to looks like
meg1 = "Hi, i found a cool website https://google.com/test And https://discord.gg/test you should watch it"
function links() {
return meg1.replace(/https:\/\/[^/]+/g, function(url1) {
url = url1.replace(/$/,"")
//Start Here The Script
console.log(url)
})
}
links()
You should read up on Node's URL API(s). The documentation, it is your friend.
https://nodejs.org/dist/latest-v14.x/docs/api/url.html

Run PHP script in Node.js (or CMD)?

Due to some reasons in need to run a small part of my NodeJS Project in PHP7.
I know I can make an internal API but that would increase network dependency.
To solve this problem I found that this can be done as
php test.php
How do I provide a JSON input to this PHP file where data is stored in a JS variable not in file and receive output in another JS variable.
function runPHP(jsonString){
....what to write here
...
return output_string;
}
Note: Please, do not suggest Query parameters as the data is too large.
I assume you want to call a php scipt from a nodejs process, send some arguments in JSON and get back some JSON and process it further.
The php script:
<?php
// test.php
$stdin = fopen('php://stdin', 'r');
$json = '';
while ($line = fgets($stdin)) {
$json .= $line;
}
$decoded = \json_decode($json);
$decoded->return_message = 'Hello from PHP';
print \json_encode($decoded);
exit(0);
The nodejs script:
// test.js
function runPHP(jsonString) {
const spawn = require('child_process').spawn;
const child = spawn('php', ['test.php']);
child.stdin.setEncoding('utf-8');
child.stdout.pipe(process.stdout);
child.stdin.write(jsonString + '\n');
child.stdin.end();
}
runPHP('{"message": "hello from js"}');
This will need some polishing and error handling...

display /var/log/messages in html/php output?

I am trying to display the output of /var/log/messages or similar (../secure for instance) in a webpage that I access through a webserver on the same host.
Should I use bash to tail -f >> the messages file to a new output file and display that text file in the html page, or is there a better way to do this?
Thanks!
idiglivemusic
If you're looking for a way to display actual file contents online without the need to reload the page, then you should setup a WebSocket server.
You can build a WebSocket server using a framework such as phpDaemon, ReactPHP, Ratchet, icicle, or implement your own server with the help of PHP extensions wrapping asynchronous libraries: event, ev, or similar.
I've chosen a random framework from the list above: Ratchet. Ratchet is based on ReactPHP. ReactPHP chooses a backend for the event loop from the following list:
- libevent extension,
- libev extension,
- event extension,
- or an internal class based on the built-in stream_select() function.
As a maintainer of the event extension, I've chosen event.
I've written a "quick" example just to give you idea of how it might be implemented. You'll most likely have to work out your own version, maybe using different tools. But the code should give you an impulse.
src/MyApp/Server.php
<?php
namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Server implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
$numRecv = count($this->clients) - 1;
printf("Connection %d sending '%s' to %d other connection%s\n",
$from->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
public function broadcast($msg) {
foreach ($this->clients as $client) {
$client->send($msg);
}
}
}
server.php
<?php
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Server;
require __DIR__ . '/vendor/autoload.php';
$server = IoServer::factory(
new HttpServer(
new WsServer(
$my_app_server = new Server()
)
),
9989
);
$loop = $server->loop;
$filename = '/var/log/messages';
$loop->addPeriodicTimer(5, function ()
use ($filename, $my_app_server)
{
static $stat_info;
if ($stat_info == null) {
clearstatcache(true, $filename);
$stat_info = stat($filename);
}
clearstatcache(true, $filename);
$st = stat($filename);
$size_diff = $st['size'] - $stat_info['size'];
echo "Diff = $size_diff bytes\n";
if ($size_diff > 0) {
$offset = $stat_info['size'];
$bytes = $size_diff;
} elseif ($size_diff < 0) {
// The file is likely truncated by `logrotate` or similar utility
$offset = 0;
$bytes = $st['size'];
} else {
$bytes = 0;
}
$stat_info = $st;
if ($bytes) {
if (! $fp = fopen($filename, 'r')) {
fprintf(STDERR, "Failed to open file $filename\n");
return;
}
if ($offset > 0) {
fseek($fp, $offset);
}
if ($msg = fread($fp, $bytes)) {
$my_app_server->broadcast($msg);
}
fclose($fp);
}
}
);
$server->run();
test.html
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Test</title>
</head>
<body>
<script>
var conn = new WebSocket('ws://localhost:9989');
conn.onopen = function(e) {
console.log("Connection established!");
};
conn.onmessage = function(e) {
console.log("Msg from server", e.data);
};
</script>
</body>
</html>
I'll skip the steps required to setup a basic test environment using Composer. Assuming you have successfully configured the test environment for the files above, you'll be able to run the server with the following command:
php server.php
Check, if the user has permissions to read /var/log/messages. On my system only root can read the file. So you might need to run the above-mentioned command with sudo(root permissions).
Now you can open test.html in a browser and look at the console output. Then trigger some event which is normally logged to the messages file. For instance, you can invoke sudo with a wrong password. The server should detect the changes within interval of 5 seconds, then send it to the WebSocket clients.
If you're using tail -f, that means that you'll be continuously getting data from the file while it grows as the command runs.
You can use cat or tail -n. Also, of course, you can access files directly by creating a symbolic or hard link to them (ln source-file link-file, ln -s source-file link-file) - but make sure, that your web-server has enough rights to access them.
Make sure that your html-server has rights to access the page and read the page (with something like cat, tail, grep).
In <html> put the output between <pre> and </pre>.
Method 1
In one of your base directories, create a symbolic link
ln -s /var/log/messages messages
If the directory belonged to say, test.web, access the log with
unmesh
http://test.web/messages
Method 2
If you're looking for a php script then, first create the link as mentioned in method 1. Then, create a new file, say, readlog.php in the base directory of test.web with the below content :
<?php
readfile(“$DOCUMENT_ROOT/messages”);
?>
Access the readlog.php like :
http://test.web/readlog.php
Requirement:
Read access should be enabled for all users for /var/log/messages.
Note:
Setting read option for /var/log/messages for the whole world is NOT a good idea.
<!DOCTYPE html>
<html>
<head>
<title>toyLogs</title>
</head>
<body>
<div><p><?php include('/var/www/html/accesslog.txt'); ?></p></div>
</body>
</html>

Encrypting/decrypting some file types with Rijndael 256 (CakePHP Security library) garbles contents

I am using CakePHP's Security::rijndael() function to encrypt and decrypt text and files. I previously wrote some code using mcrypt directly, which worked in the same way, but then I found Security::rijndael and realised I had reinvented the wheel. So the problem I have happens either way.
If I encrypt a string, or a text file, or a PDF document, the code below works perfectly and I get the correct decrypted string/file. However, if I try encrypting a .doc, .docx or an image file, the decrypted file is garbled.
Here's the code that does the encrypting/decrypting
public static function encrypt($plainText, $key) {
$plainText = base64_encode($plainText);
//Hash key to ensure it is long enough
$hashedKey = Security::hash($key);
$cipherText = Security::rijndael($plainText, $hashedKey, 'encrypt');
return base64_encode($cipherText);
}
public static function decrypt($cipherText, $key) {
$cipherText = base64_decode($cipherText);
$hashedKey = Security::hash($key);
$plainText = Security::rijndael($cipherText, $hashedKey, 'decrypt');
return base64_decode($plainText);
}
...and this code actually presents the file to the user (I've edited the code to keep it simple):
public function download($id){
App::uses('File', 'Utility');
$key = $this->User->getDocumentKey($id);
$file = new File('my_encrypted_file.docx');
$encrypted = $file->read();
$decrypted = Encrypt::decrypt($encrypted, $key);
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Content-Disposition: attachment; filename="my_decrypted_file.docx"');
echo $decrypted;
die();
}
Update - it appears that the encryption is a red herring, as the file is garbled even without encrypting and decrypting it! The following produces exactly the same broken file:
header('Content-Disposition: attachment; filename="test.docx"');
$file = new File($this->data['Model']['file']['tmp_name']);
echo $file->read();
die();
I think I know the reason for that problem now, it is line 208 in Security.php:
$out .= rtrim(mcrypt_decrypt($algorithm, $cryptKey, $text, $mode, $iv), "\0");
Since PHP's mycrypt() uses ZeroBytePadding this line removes the padding afterwards.
The problem is that a .docx-File (as far as I could check it) terminates with a few Null-characters. If you only remove a single one of them, Word fails to open the file.
So what happens is that rtrim() also deletes these bytes even though they are not part of the padding.
To fix this, you can add a termination character (for example X) at the end of your files before encrypting and remove it after decrypting. This will prevent cutting off the tailing zero-bytes from the .docx-files:
public static function encrypt($plainText, $key) {
$plainText = base64_encode($plainText . "X"); // `X` terminates the file
/* do encryption */
}
public static function decrypt($cipherText, $key) {
/* do decrytion */
return rtrim(base64_decode($plainText), "X"); // cut off the termination `X`
}
Well, I was barking up the wrong tree.
For whatever reason (whitespace at the start of some PHP file maybe?), adding ob_clean(); immediately after sending the headers, has fixed the problem.

Supported audio file formats for Chrome?

I'm interested in using the Web Audio API. Unfortunately my audio files are are all in an esoteric format that Chrome can't decode. (They're .wavs, but sampled at 96 kHz with 32-bit float encoding.)
Is there any way for me to query my browser (Chrome) to find out exactly which audio formats and encodings it supports?
UPDATE
I've found a list of file formats supported by Chrome here: https://sites.google.com/a/chromium.org/dev/audio-video
You could test this sort of thing by trying to load a variety of sample files using a try...catch construct, and seeing which filetypes load and which don't. See this tutorial for loading files with the Web Audio API in Chrome.
There is! I don't know how reliable this is, but...
// Need to check the canPlayType first or an exception
// will be thrown for those browsers that don't support it
var myAudio = document.createElement('audio');
if (myAudio.canPlayType) {
// Currently canPlayType(type) returns: "", "maybe" or "probably"
var canPlayMp3 = !!myAudio.canPlayType && "" != myAudio.canPlayType('audio/mpeg');
var canPlayOgg = !!myAudio.canPlayType && "" != myAudio.canPlayType('audio/ogg; codecs="vorbis"');
}
Since we're talking about WAV files here, I would use these:
audio/vnd.wave, audio/wav, audio/wave, audio/x-wav
The best thing to do is to figure out what your file's MIME type is (should be one of the above), and then check for that with something like this:
var canPlayWav = !!myAudio.canPlayType && "" != myAudio.canPlayType('MIME_TYPE_HERE');
if (canPlayWav) { dothis(); } else { dothat(); }
I hope this helps!
Source: http://html5doctor.com/native-audio-in-the-browser/
A non-programmatic way would be those sites:
http://hpr.dogphilosophy.net/test/
http://html5test.com/
Using Lo-Dash:
(function(){
var a = document.createElement('audio'),
types = _(navigator.mimeTypes).pluck('type'),
isAudio = /^audio\//, canPlay = {};
if (a && a.canPlayType) {
types
.push('audio/flac', 'audio/opus', 'audio/webm', 'audio/ogg', 'audio/midi')
.flatten()
.uniq()
.each(function(type){
if (isAudio.test(type)) {
canPlay[type] = !!a.canPlayType(type);
}
});
}
return canPlay;
})();

Resources