How to calculate CRC of a WinRAR file header? - php

Talking about this: http://www.win-rar.com/index.php?id=24&kb_article_id=162
I'm able to calculate the correct CRC of an archive header (MAIN_HEAD) by doing:
$crc = crc32(mb_substr($data, $blockOffset + 2, 11, '8bit'));
$crc = dechex($crc);
$crc = substr($crc, -4, 2) . substr($crc, -2, 2);
$crc = hexdec($crc);
The first line will read "CRC of fields HEAD_TYPE to RESERVED2" as states in the documentation. As I noted, it works fine for the archive header.
When I try to calculate the CRC of a file header it always spits out the wrong CRC for unknown reason. I did as the documentation says - "CRC of fields from HEAD_TYPE to FILEATTR" but it simply doesn't work. I've also tried different read-length variations in case the documentation is wrong and it might actually be from HEAD_TYPE to FILE_NAME. Everything without success.
Anyone can give me a hint? I've also checked the unrar source code but it doesn't make me smarter, probably because I don't know C language at all...

I wrote some code that does the same thing. Here is it with some additional snippets for a better understanding:
$this->fh = $fileHandle;
$this->startOffset = ftell($fileHandle); // current location in the file
// reading basic 7 byte header block
$array = unpack('vheaderCrc/CblockType/vflags/vheaderSize', fread($this->fh, 7));
$this->headerCrc = $array['headerCrc'];
$this->blockType = $array['blockType'];
$this->flags = $array['flags'];
$this->hsize = $array['headerSize'];
$this->addSize = 0; // size of data after the header
// -- check CRC of block header --
$offset = ftell($this->fh);
fseek($this->fh, $this->startOffset + 2, SEEK_SET);
$crcData = fread($this->fh, $this->hsize - 2);
// only the 4 lower order bytes are used
$crc = crc32($crcData) & 0xffff;
// igonore blocks with no CRC set (same as twice the blockType)
if ($crc !== $this->headerCrc && $this->headerCrc !== 0x6969 // SRR Header
&& $this->headerCrc !== 0x6a6a // SRR Stored File
&& $this->headerCrc !== 0x7171 // SRR RAR block
&& $this->blockType !== 0x72 // RAR marker block (fixed: magic number)
) {
array_push($warnings, 'Invalid block header CRC found: header is corrupt.');
}
// set offset back to where we started from
fseek($this->fh, $offset, SEEK_SET);
I tested it on a couple of SRR files and it works as expected. I started with reading the basic 7 byte header. The size of the header can be found there. I used this to grab the correct amount of data for the crc32 function. I noticed that when you convert it to hexadecimal, you can get false positives when comparing: '0f00' != 'f00'. You would need to pad it with zeros. This is why I kept the decimal representations of crc32() and unpack() for the comparison. Also, the number of fields of a file block can vary if some header flags are set: it is possible you took a wrong size.

Related

PHP Human Readable filesize script always returns a "B" [duplicate]

This question already has answers here:
Format bytes to kilobytes, megabytes, gigabytes
(28 answers)
Closed 3 years ago.
After thinking mine was in error,
I found LOT AT LOTS of scripts the do this:
https://gist.github.com/liunian/9338301
And there are several here at S.O. I used, but had the same annoying "B" as a size.
This issue seemed to rear it's ugly head when I switched to php v7.xxx
First issues is I have to typcase a floated number (or double) or else I get a
"A non well formed numeric value encountered"
After some research, apparently this is NOT a bug. At least that is how I read it.
So after typcasting it, the error goes away but the value returned is always a "B'
filesize = 87.5B (when it should be MB or GB).
I am pretty sure Javascript will work, but would rather keep it with php.
Thanks for looking
current live script that is producing a "B" only
public function readableBytes($size, $type='pc') { //ignore the pc - it is for something else - disabled for debugging
$size = (double)$size;
static $units = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
$step = 1024;
$i = 0;
while (($size / $step) > 0.9) {
$size = $size / $step;
$i++;
}
return round($size, 2).$units[$i];
}// function readbbleBytes
This is a unique issue to those that use a loop for assigning (custom)template tags with an array field.
The filesize is one of many fields in an array I use.
I used a loop to go through each field and assign it to a template tag.
Not sure why the "B" came up. My suspicion is that the result of a "string" = 0 bytes. Even though It showed the actual size.
edit: spelling & claification
So to fix, in the middle of the loop, I forced the $array['filesize'] = readableBytes($array['filesize'])).
Before it was loop tag = fieldname.
foreach ($arr as $field=>$data) {
$arr['filesize'] = readableBytes($array['filesize'])); // fix was put here
$page = str_ireplace("{$field}", $data, $page);
}
The following seems to work (checked in phptester):
function human_filesize($bytes, $decimals = 2)
{
if ($bytes < 1024) {
return $bytes . ' B';
}
$factor = floor(log($bytes, 1024));
return sprintf("%.{$decimals}f ", $bytes / pow(1024, $factor)) . ['B', 'KB', 'MB', 'GB', 'TB', 'PB'][$factor];
}
This is a cut and paste of a post by gladx in the following thread, where you'll find several optimised examples: https://gist.github.com/liunian/9338301#gistcomment-2183132

Shortest possible encoded string with a decode possibility (shorten URL) using only PHP

I'm looking for a method that encodes a string to the shortest possible length and lets it be decodable (pure PHP, no SQL). I have working script, but I'm unsatisfied with the length of the encoded string.
Scenario
Link to an image (it depends on the file resolution I want to show to the user):
www.mysite.com/share/index.php?img=/dir/dir/hi-res-img.jpg&w=700&h=500
Encoded link (so the user can't guess how to get the larger image):
www.mysite.com/share/encodedQUERYstring
So, basically I'd like to encode only the search query part of the URL:
img=/dir/dir/hi-res-img.jpg&w=700&h=500
The method I use right now will encode the above query string to:
y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA
The method I use is:
$raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
$encoded_query_string = base64_encode(gzdeflate($raw_query_string));
$decoded_query_string = gzinflate(base64_decode($encoded_query_string));
How do I shorten the encoded result and still have the possibility to decode it using only PHP?
I suspect that you will need to think more about your method of hashing if you don't want it to be decodable by the user. The issue with Base64 is that a Base64 string looks like a base64 string. There's a good chance that someone that's savvy enough to be looking at your page source will probably recognise it too.
Part one:
a method that encodes an string to shortest possible length
If you're flexible on your URL vocabulary/characters, this will be a good starting place. Since gzip makes a lot of its gains using back references, there is little point as the string is so short.
Consider your example - you've only saved 2 bytes in the compression, which are lost again in Base64 padding:
Non-gzipped: string(52) "aW1nPS9kaXIvZGlyL2hpLXJlcy1pbWcuanBnJnc9NzAwJmg9NTAw"
Gzipped: string(52) "y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA=="
If you reduce your vocabulary size, this will naturally allow you better compression. Let's say we remove some redundant information.
Take a look at the functions:
function compress($input, $ascii_offset = 38){
$input = strtoupper($input);
$output = '';
//We can try for a 4:3 (8:6) compression (roughly), 24 bits for 4 characters
foreach(str_split($input, 4) as $chunk) {
$chunk = str_pad($chunk, 4, '=');
$int_24 = 0;
for($i=0; $i<4; $i++){
//Shift the output to the left 6 bits
$int_24 <<= 6;
//Add the next 6 bits
//Discard the leading ASCII chars, i.e make
$int_24 |= (ord($chunk[$i]) - $ascii_offset) & 0b111111;
}
//Here we take the 4 sets of 6 apart in 3 sets of 8
for($i=0; $i<3; $i++) {
$output = pack('C', $int_24) . $output;
$int_24 >>= 8;
}
}
return $output;
}
And
function decompress($input, $ascii_offset = 38) {
$output = '';
foreach(str_split($input, 3) as $chunk) {
//Reassemble the 24 bit ints from 3 bytes
$int_24 = 0;
foreach(unpack('C*', $chunk) as $char) {
$int_24 <<= 8;
$int_24 |= $char & 0b11111111;
}
//Expand the 24 bits to 4 sets of 6, and take their character values
for($i = 0; $i < 4; $i++) {
$output = chr($ascii_offset + ($int_24 & 0b111111)) . $output;
$int_24 >>= 6;
}
}
//Make lowercase again and trim off the padding.
return strtolower(rtrim($output, '='));
}
It is basically a removal of redundant information, followed by the compression of 4 bytes into 3. This is achieved by effectively having a 6-bit subset of the ASCII table. This window is moved so that the offset starts at useful characters and includes all the characters you're currently using.
With the offset I've used, you can use anything from ASCII 38 to 102. This gives you a resulting string of 30 bytes, that's a 9-byte (24%) compression! Unfortunately, you'll need to make it URL-safe (probably with base64), which brings it back up to 40 bytes.
I think at this point, you're pretty safe to assume that you've reached the "security through obscurity" level required to stop 99.9% of people. Let's continue though, to the second part of your question
so the user can't guess how to get the larger image
It's arguable that this is already solved with the above, but you need to pass this through a secret on the server, preferably with PHP's OpenSSL interface. The following code shows the complete usage flow of functions above and the encryption:
$method = 'AES-256-CBC';
$secret = base64_decode('tvFD4Vl6Pu2CmqdKYOhIkEQ8ZO4XA4D8CLowBpLSCvA=');
$iv = base64_decode('AVoIW0Zs2YY2zFm5fazLfg==');
$input = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
var_dump($input);
$compressed = compress($input);
var_dump($compressed);
$encrypted = openssl_encrypt($compressed, $method, $secret, false, $iv);
var_dump($encrypted);
$decrypted = openssl_decrypt($encrypted, $method, $secret, false, $iv);
var_dump($decrypted);
$decompressed = decompress($compressed);
var_dump($decompressed);
The output of this script is the following:
string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500"
string(30) "<��(��tJ��#�xH��G&(�%��%��xW"
string(44) "xozYGselci9i70cTdmpvWkrYvGN9AmA7djc5eOcFoAM="
string(30) "<��(��tJ��#�xH��G&(�%��%��xW"
string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500"
You'll see the whole cycle: compression → encryption → Base64 encode/decode → decryption → decompression. The output of this would be as close as possible as you could really get, at near the shortest length you could get.
Everything aside, I feel obliged to conclude this with the fact that it is theoretical only, and this was a nice challenge to think about. There are definitely better ways to achieve your desired result - I'll be the first to admit that my solution is a little bit absurd!
Instead of encoding the URL, output a thumbnail copy of the original image. Here's what I'm thinking:
Create a "map" for PHP by naming your pictures (the actual file names) using random characters. Random_bytes is a great place to start.
Embed the desired resolution within the randomized URL string from #1.
Use the imagecopyresampled function to copy the original image into the resolution you would like to output before outputting it out to the client's device.
So for example:
Filename example (from bin2hex(random_bytes(6))): a1492fdbdcf2.jpg
Resolution desired: 800x600. My new link could look like:
http://myserver.com/?800a1492fdbdcf2600 or maybe http://myserfer.com/?a1492800fdbdc600f2 or maybe even http://myserver.com/?800a1492fdbdcf2=600 depending on where I choose to embed the resolution within the link
PHP would know that the file name is a1492fdbdcf2.jpg, grab it, use the imagecopyresampled to copy to the resolution you want, and output it.
Theory
In theory we need a short input character set and a large output character set.
I will demonstrate it by the following example. We have the number 2468 as integer with 10 characters (0-9) as character set. We can convert it to the same number with base 2 (binary number system). Then we have a shorter character set (0 and 1) and the result is longer:
100110100100
But if we convert to hexadecimal number (base 16) with a character set of 16 (0-9 and A-F). Then we get a shorter result:
9A4
Practice
So in your case we have the following character set for the input:
$inputCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyz=/-.&";
In total 41 characters: Numbers, lower cases and the special chars = / - . &
The character set for output is a bit tricky. We want use URL save characters only. I've grabbed them from here: Characters allowed in GET parameter
So our output character set is (73 characters):
$outputCharacterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-_.!*'(),$";
Numbers, lower and upper cases and some special characters.
We have more characters in our set for the output than for the input. Theory says we can short our input string. Check!
Coding
Now we need an encode function from base 41 to base 73. For that case I don't know a PHP function. Luckily we can grab the function 'convBase' from here: Convert an arbitrarily large number from any base to any base
<?php
function convBase($numberInput, $fromBaseInput, $toBaseInput)
{
if ($fromBaseInput == $toBaseInput) return $numberInput;
$fromBase = str_split($fromBaseInput, 1);
$toBase = str_split($toBaseInput, 1);
$number = str_split($numberInput, 1);
$fromLen = strlen($fromBaseInput);
$toLen = strlen($toBaseInput);
$numberLen = strlen($numberInput);
$retval = '';
if ($toBaseInput == '0123456789')
{
$retval = 0;
for ($i = 1;$i <= $numberLen; $i++)
$retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase), bcpow($fromLen, $numberLen-$i)));
return $retval;
}
if ($fromBaseInput != '0123456789')
$base10 = convBase($numberInput, $fromBaseInput, '0123456789');
else
$base10 = $numberInput;
if ($base10<strlen($toBaseInput))
return $toBase[$base10];
while($base10 != '0')
{
$retval = $toBase[bcmod($base10,$toLen)] . $retval;
$base10 = bcdiv($base10, $toLen, 0);
}
return $retval;
}
Now we can shorten the URL. The final code is:
$input = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
$inputCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyz=/-.&";
$outputCharacterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-_.!*'(),$";
$encoded = convBase($input, $inputCharacterSet, $outputCharacterSet);
var_dump($encoded); // string(34) "BhnuhSTc7LGZv.h((Y.tG_IXIh8AR.$!t*"
$decoded = convBase($encoded, $outputCharacterSet, $inputCharacterSet);
var_dump($decoded); // string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500"
The encoded string has only 34 characters.
Optimizations
You can optimize the count of characters by
reduce the length of input string. Do you really need the overhead of URL parameter syntax? Maybe you can format your string as follows:
$input = '/dir/dir/hi-res-img.jpg,700,500';
This reduces the input itself and the input character set. Your reduced input character set is then:
$inputCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyz/-.,";
Final output:
string(27) "E$AO.Y_JVIWMQ9BB_Xb3!Th*-Ut"
string(31) "/dir/dir/hi-res-img.jpg,700,500"
reducing the input character set ;-). Maybe you can exclude some more characters?
You can encode the numbers to characters first. Then your input character set can be reduced by 10!
increase your output character set. So the given set by me is googled within two minutes. Maybe you can use more URL save characters.
Security
Heads up: There is no cryptographically logic in the code. So if somebody guesses the character sets, he/she can decode the string easily. But you can shuffle the character sets (once). Then it is a bit harder for the attacker, but not really safe. Maybe it’s enough for your use case anyway.
Reading from the previous answers and below comments, you need a solution to hide the real path of your image parser, giving it a fixed image width.
Step 1: http://www.example.com/tn/full/animals/images/lion.jpg
You can achieve a basic "thumbnailer" by taking profit of .htaccess
RewriteEngine on
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule tn/(full|small)/(.*) index.php?size=$1&img=$2 [QSA,L]
Your PHP file:
$basedir = "/public/content/";
$filename = realpath($basedir.$_GET["img"]);
## Check that file is in $basedir
if ((!strncmp($filename, $basedir, strlen($basedir))
||(!file_exists($filename)) die("Bad file path");
switch ($_GET["size"]) {
case "full":
$width = 700;
$height = 500;
## You can also use getimagesize() to test if the image is landscape or portrait
break;
default:
$width = 350;
$height = 250;
break;
}
## Here is your old code for resizing images.
## Note that the "tn" directory can exist and store the actual reduced images
This lets you using the URL www.example.com/tn/full/animals/images/lion.jpg to view your reduced in size image.
This has the advantage for SEO to preserve the original file name.
Step 2: http://www.example.com/tn/full/lion.jpg
If you want a shorter URL, if the number of images you have is not too much, you can use the basename of the file (e.g., "lion.jpg") and recursively search. When there is a collision, use an index to identify which one you want (e.g., "1--lion.jpg")
function matching_files($filename, $base) {
$directory_iterator = new RecursiveDirectoryIterator($base);
$iterator = new RecursiveIteratorIterator($directory_iterator);
$regex_iterator = new RegexIterator($iterator, "#$filename\$#");
$regex_iterator->setFlags(RegexIterator::USE_KEY);
return array_map(create_function('$a', 'return $a->getpathName();'), iterator_to_array($regex_iterator, false));
}
function encode_name($filename) {
$files = matching_files(basename($filename), realpath('public/content'));
$tot = count($files);
if (!$tot)
return NULL;
if ($tot == 1)
return $filename;
return "/tn/full/" . array_search(realpath($filename), $files) . "--" . basename($filename);
}
function decode_name($filename) {
$i = 0;
if (preg_match("#^([0-9]+)--(.*)#", $filename, $out)) {
$i = $out[1];
$filename = $out[2];
}
$files = matching_files($filename, realpath('public/content'));
return $files ? $files[$i] : NULL;
}
echo $name = encode_name("gallery/animals/images/lion.jp‌​g").PHP_EOL;
## --> returns lion.jpg
## You can use with the above solution the URL http://www.example.com/tn/lion.jpg
echo decode_name(basename($name)).PHP_EOL;
## -> returns the full path on disk to the image "lion.jpg"
Original post:
Basically, if you add some formatting in your example, your shortened URL is in fact longer:
img=/dir/dir/hi-res-img.jpg&w=700&h=500 // 39 characters
y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA // 50 characters
Using base64_encode will always result in longer strings. And gzcompress will require at less to store one occurrence of the different chars; this is not a good solution for small strings.
So doing nothing (or a simple str_rot13) is clearly the first option to consider if you want to shorten the result you had previously.
You can also use a simple character replacement method of your choice:
$raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
$from = "0123456789abcdefghijklmnopqrstuvwxyz&=/ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// The following line if the result of str_shuffle($from)
$to = "0IQFwAKU1JT8BM5npNEdi/DvZmXuflPVYChyrL4R7xc&SoG3Hq6ks=e9jW2abtOzg";
echo strtr($raw_query_string, $from, $to) . "\n";
// Result: EDpL4MEu4MEu4NE-u5f-EDp.dmprYLU00rNLA00 // 39 characters
Reading from your comment, you really want "to prevent anyone to gets a high-resolution image".
The best way to achieve that is to generate a checksum with a private key.
Encode:
$secret = "ujoo4Dae";
$raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500';
$encoded_query_string = $raw_query_string . "&k=" . hash("crc32", $raw_query_string . $secret);
Result: img=/dir/dir/hi-res-img.jpg&w=700&h=500&k=2ae31804
Decode:
if (preg_match("#(.*)&k=([^=]*)$#", $encoded_query_string, $out)
&& (hash("crc32", $out[1].$secret) == $out[2])) {
$decoded_query_string = $out[1];
}
This does not hide the original path, but this path has no reason to be public. Your "index.php" can output your image from the local directory once the key has been checked.
If you really want to shorten your original URL, you have to consider the acceptable characters in the original URL to be restricted. Many compression methods are based on the fact that you can use a full byte to store more than a character.
There are many ways to shorten URLs. You can look up how other services, like TinyURL, shorten their URLs. Here is a good article on hashes and shortening URLs: URL Shortening: Hashes In Practice
You can use the PHP function mhash() to apply hashes to strings.
And if you scroll down to "Available Hashes" on the mhash website, you can see what hashes you can use in the function (although I would check what PHP versions have which functions): mhash - Hash Library
I think this would be better done by not obscuring at all. You could quite simply cache returned images and use a handler to provide them. This requires the image sizes to be hard coded into the PHP script. When you get new sizes, you can just delete everything in the cache as it is 'lazy loaded'.
1. Get the image from the request
This could be this: /thumbnail.php?image=img.jpg&album=myalbum. It could even be made to be anything using rewrite and have a URL like: /gallery/images/myalbum/img.jpg.
2. Check to see if a temporary version does not exist
You can do this using is_file().
3. Create it if it does not exist
Use your current resizing logic to do it, but don't output the image. Save it to the temporary location.
4. Read the temporary file contents to the stream
It pretty much just outputs it.
Here is an untested code example...
<?php
// Assuming we have a request /thumbnail.php?image=img.jpg&album=myalbum
// These are temporary filenames places. You need to do this yourself on your system.
$image = $_GET['image']; // The file name
$album = $_GET['album']; // The album
$temp_folder = sys_get_temp_dir(); // Temporary directory to store images
// (this should really be a specific cache path)
$image_gallery = "images"; // Root path to the image gallery
$width = 700;
$height = 500;
$real_path = "$image_gallery/$album/$image";
$temp_path = "$temp_folder/$album/$image";
if(!is_file($temp_path))
{
// Read in the image
$contents = file_get_contents($real_path);
// Resize however you are doing it now.
$thumb_contents = resizeImage($contents, $width, $height);
// Write to the temporary file
file_put_contents($temp_path, $thumb_contents);
}
$type = 'image/jpeg';
header('Content-Type:' . $type);
header('Content-Length: ' . filesize($temp_path));
readfile($temp_path);
?>
Short words about "security"
You simply won't be able to secure your link if there is no "secret password" stored somewhere: as long as the URI carries all information to access your resource, then it will be decodable and your "custom security" (they are opposite words btw) will be broken easily.
You can still put a salt in your PHP code (like $mysalt="....long random string...") since I doubt you want an eternal security (such approach is weak because you cannot renew the $mysalt value, but in your case, a few years security sounds sufficient, since anyway, a user can buy one picture and share it elsewhere, breaking any of your security mechanism).
If you want to have a safe mechanism, use a well-known one (as a framework would carry), along with authentication and user rights management mechanism (so you can know who's looking for your image, and whether they are allowed to).
Security has a cost. If you don't want to afford its computing and storing requirements, then forget about it.
Secure by signing the URL
If you want to avoid users easy by-passing and get full resolution picture, then you may just sign the URI (but really, for safety, use something that already exist instead of that quick draft example below):
$salt = '....long random stirng...';
$params = array('img' => '...', 'h' => '...', 'w' => '...');
$p = http_build_query($params);
$check = password_hash($p, PASSWORD_BCRYPT, array('salt' => $salt, 'cost' => 1000);
$uri = http_build_query(array_merge($params, 'sig' => $check));
Decoding:
$sig = $_GET['sig'];
$params = $_GET;
unset($params['sig']);
// Same as previous
$salt = '....long random stirng...';
$p = http_build_query($params);
$check = password_hash($p, PASSWORD_BCRYPT, array('salt' => $salt, 'cost' => 1000);
if ($sig !== $check) throw new DomainException('Invalid signature');
See password_hash
Shorten smartly
"Shortening" with a generic compression algorithm is useless here because the headers will be longer than the URI, so it will almost never shorten it.
If you want to shorten it, be smart: don't give the relative path (/dir/dir) if it's always the same (or give it only if it's not the main one). Don't give the extension if it's always the same (or give it when it's not png if almost everything is in png). Don't give the height because the image carries the aspect ratio: you only need the width. Give it in x100px if you do not need a pixel-accurate width.
A lot has been said about how encoding doesn't help security, so I am just concentrating on the shortening and aesthetics.
Rather than thinking of it as a string, you could consider it as three individual components. Then if you limit your code space for each component, you can pack things together a lot smaller.
E.g.,
path - Only consisting of the 26 characters (a-z) and / - . (Variable length)
width - Integer (0 - 65k) (Fixed length, 16 bits)
height - Integer (0 - 65k) (Fixed length, 16 bits)
I'm limiting the path to only consist of a maximum 31 characters, so we can use five bit groupings.
Pack your fixed length dimensions first, and append each path character as five bits. It might also be necessary to add a special null character to fill up the end byte. Obviously you need to use the same dictionary string for encoding and decoding.
See the code below.
This shows that by limiting what you encode and how much you can encode, you can get a shorter string. You could make it even shorter by using only 12 bit dimension integers (max 2048), or even removing parts of the path if they are known such as base path or file extension (see last example).
<?php
function encodeImageAndDimensions($path, $width, $height) {
$dictionary = str_split("abcdefghijklmnopqrstuvwxyz/-."); // Maximum 31 characters, please
if ($width >= pow(2, 16)) {
throw new Exception("Width value is too high to encode with 16 bits");
}
if ($height >= pow(2, 16)) {
throw new Exception("Height value is too high to encode with 16 bits");
}
// Pack width, then height first
$packed = pack("nn", $width, $height);
$path_bits = "";
foreach (str_split($path) as $ch) {
$index = array_search($ch, $dictionary, true);
if ($index === false) {
throw new Exception("Cannot encode character outside of the allowed dictionary");
}
$index++; // Add 1 due to index 0 meaning NULL rather than a.
// Work with a bit string here rather than using complicated binary bit shift operators.
$path_bits .= str_pad(base_convert($index, 10, 2), 5, "0", STR_PAD_LEFT);
}
// Remaining space left?
$modulo = (8 - (strlen($path_bits) % 8)) %8;
if ($modulo >= 5) {
// There is space for a null character to fill up to the next byte
$path_bits .= "00000";
$modulo -= 5;
}
// Pad with zeros
$path_bits .= str_repeat("0", $modulo);
// Split in to nibbles and pack as a hex string
$path_bits = str_split($path_bits, 4);
$hex_string = implode("", array_map(function($bit_string) {
return base_convert($bit_string, 2, 16);
}, $path_bits));
$packed .= pack('H*', $hex_string);
return base64_url_encode($packed);
}
function decodeImageAndDimensions($str) {
$dictionary = str_split("abcdefghijklmnopqrstuvwxyz/-.");
$data = base64_url_decode($str);
$decoded = unpack("nwidth/nheight/H*path", $data);
$path_bit_stream = implode("", array_map(function($nibble) {
return str_pad(base_convert($nibble, 16, 2), 4, "0", STR_PAD_LEFT);
}, str_split($decoded['path'])));
$five_pieces = str_split($path_bit_stream, 5);
$real_path_indexes = array_map(function($code) {
return base_convert($code, 2, 10) - 1;
}, $five_pieces);
$real_path = "";
foreach ($real_path_indexes as $index) {
if ($index == -1) {
break;
}
$real_path .= $dictionary[$index];
}
$decoded['path'] = $real_path;
return $decoded;
}
// These do a bit of magic to get rid of the double equals sign and obfuscate a bit. It could save an extra byte.
function base64_url_encode($input) {
$trans = array('+' => '-', '/' => ':', '*' => '$', '=' => 'B', 'B' => '!');
return strtr(str_replace('==', '*', base64_encode($input)), $trans);
}
function base64_url_decode($input) {
$trans = array('-' => '+', ':' => '/', '$' => '*', 'B' => '=', '!' => 'B');
return base64_decode(str_replace('*', '==', strtr($input, $trans)));
}
// Example usage
$encoded = encodeImageAndDimensions("/dir/dir/hi-res-img.jpg", 700, 500);
var_dump($encoded); // string(27) "Arw!9NkTLZEy2hPJFnxLT9VA4A$"
$decoded = decodeImageAndDimensions($encoded);
var_dump($decoded); // array(3) { ["width"] => int(700) ["height"] => int(500) ["path"] => string(23) "/dir/dir/hi-res-img.jpg" }
$encoded = encodeImageAndDimensions("/another/example/image.png", 4500, 2500);
var_dump($encoded); // string(28) "EZQJxNhc-iCy2XAWwYXaWhOXsHHA"
$decoded = decodeImageAndDimensions($encoded);
var_dump($decoded); // array(3) { ["width"] => int(4500) ["height"] => int(2500) ["path"] => string(26) "/another/example/image.png" }
$encoded = encodeImageAndDimensions("/short/eg.png", 300, 200);
var_dump($encoded); // string(19) "ASwAyNzQ-VNlP2DjgA$"
$decoded = decodeImageAndDimensions($encoded);
var_dump($decoded); // array(3) { ["width"] => int(300) ["height"] => int(200) ["path"] => string(13) "/short/eg.png" }
$encoded = encodeImageAndDimensions("/very/very/very/very/very-hyper/long/example.png", 300, 200);
var_dump($encoded); // string(47) "ASwAyN2LLO7FlndiyzuxZZ3Yss8Rm!ZbY9x9lwFsGF7!xw$"
$decoded = decodeImageAndDimensions($encoded);
var_dump($decoded); // array(3) { ["width"] => int(300) ["height"] => int(200) ["path"] => string(48) "/very/very/very/very/very-hyper/long/example.png" }
$encoded = encodeImageAndDimensions("only-file-name", 300, 200);
var_dump($encoded); //string(19) "ASwAyHuZnhksLxwWlA$"
$decoded = decodeImageAndDimensions($encoded);
var_dump($decoded); // array(3) { ["width"] => int(300) ["height"] => int(200) ["path"] => string(14) "only-file-name" }
In your question you state that it should be pure PHP and not use a database, and there should be a possibility to decode the strings. So bending the rules a bit:
The way I am interpreting this question is that we don't care about security that much but, we do want the shortest hashes that lead back to images.
We can also take "decode possibility" with a pinch of salt by using a one way hashing algorithm.
We can store the hashes inside a JSON object, then store the data in a file, so all we have to do at the end of the day is string matching
```
class FooBarHashing {
private $hashes;
private $handle;
/**
* In producton this should be outside the web root
* to stop pesky users downloading it and geting hold of all the keys.
*/
private $file_name = './my-image-hashes.json';
public function __construct() {
$this->hashes = $this->get_hashes();
}
public function get_hashes() {
// Open or create a file.
if (! file_exists($this->file_name)) {
fopen($this->file_name, "w");
}
$this->handle = fopen($this->file_name, "r");
$hashes = [];
if (filesize($this->file_name) > 0) {
$contents = fread($this->handle, filesize($this->file_name));
$hashes = get_object_vars(json_decode($contents));
}
return $hashes;
}
public function __destroy() {
// Close the file handle
fclose($this->handle);
}
private function update() {
$handle = fopen($this->file_name, 'w');
$res = fwrite($handle, json_encode($this->hashes));
if (false === $res) {
//throw new Exception('Could not write to file');
}
return true;
}
public function add_hash($image_file_name) {
$new_hash = md5($image_file_name, false);
if (! in_array($new_hash, array_keys($this->hashes) ) ) {
$this->hashes[$new_hash] = $image_file_name;
return $this->update();
}
//throw new Exception('File already exists');
}
public function resolve_hash($hash_string='') {
if (in_array($hash_string, array_keys($this->hashes))) {
return $this->hashes[$hash_string];
}
//throw new Exception('File not found');
}
}
```
Usage example:
<?php
// Include our class
require_once('FooBarHashing.php');
$hashing = new FooBarHashing;
// You will need to add the query string you want to resolve first.
$hashing->add_hash('img=/dir/dir/hi-res-img.jpg&w=700&h=500');
// Then when the user requests the hash the query string is returned.
echo $hashing->resolve_hash('65992be720ea3b4d93cf998460737ac6');
So the end result is a string that is only 32 chars long, which is way shorter than the 52 we had before.
From the discussion in the comments section it looks like what you really want is to protect your original high-resolution images.
Having that in mind, I'd suggest to actually do that first using your web server configuration (e.g., Apache mod_authz_core or Nginx ngx_http_access_module) to deny access from the web to the directory where your original images are stored.
Note that the server will only deny access to your images from the web, but you will still be able to access them directly from your PHP scripts. Since you already are displaying images using some "resizer" script I'd suggest putting some hard limit there and refuse to resize images to anything bigger then that (e.g., something like $width = min(1000, $_GET['w'])).
I know this does not answer your original question, but I think this would the right solution to protect your images. And if you still want to obfuscate the original name and resizing parameters you can do that however you see fit without worrying that someone might figure out what’s behind it.
I'm afraid, you won't be able to shorten the query string better than any known
compression algorithm. As mentioned in other answers, a compressed
version will be shorter by a few (around 4-6) characters than the original.
Moreover, the original string can be decoded relatively easy (opposed to decoding SHA-1 or MD5, for instance).
I suggest shortening URLs by means of Web server configuration. You might
shorten it further by replacing image path with an ID (store ID-filename
pairs in a database).
For example, the following Nginx configuration accepts
URLs like /t/123456/700/500/4fc286f1a6a9ac4862bdd39a94a80858, where
the first number (123456) is supposed to be an image ID from database;
700 and 500 are image dimensions;
the last part is an MD5 hash protecting from requests with different dimensions.
# Adjust maximum image size
# image_filter_buffer 5M;
server {
listen 127.0.0.13:80;
server_name img-thumb.local;
access_log /var/www/img-thumb/logs/access.log;
error_log /var/www/img-thumb/logs/error.log info;
set $root "/var/www/img-thumb/public";
# /t/image_id/width/height/md5
location ~* "(*UTF8)^/t/(\d+)/(\d+)/(\d+)/([a-zA-Z0-9]{32})$" {
include fastcgi_params;
fastcgi_pass unix:/tmp/php-fpm-img-thumb.sock;
fastcgi_param QUERY_STRING image_id=$1&w=$2&h=$3&hash=$4;
fastcgi_param SCRIPT_FILENAME /var/www/img-thumb/public/t/resize.php;
image_filter resize $2 $3;
error_page 415 = /empty;
break;
}
location = /empty {
empty_gif;
}
location / { return 404; }
}
The server accepts only URLs of specified pattern, forwards request to /public/t/resize.php script with modified query string, then resizes the image generated by PHP with the image_filter module. In case of error, returns an empty GIF image.
The image_filter is optional, and it is included only as an example. Resizing can be performed fully on PHP side. With Nginx, it is possible to get rid of PHP part, by the way.
The PHP script is supposed to validate the hash as follows:
// Store this in some configuration file.
$salt = '^sYsdfc_sd&9wa.';
$w = $_GET['w'];
$h = $_GET['h'];
$true_hash = md5($w . $h . $salt . $image_id);
if ($true_hash != $_GET['hash']) {
die('invalid hash');
}
$filename = fetch_image_from_database((int)$_GET['image_id']);
$img = imagecreatefrompng($filename);
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
I don't think the resulting URL can be shortened much more than in your own example. But I suggest a few steps to obfuscate your images better.
First I would remove everything you can from the base URL you are zipping and Base64 encoding, so instead of
img=/dir/dir/hi-res-img.jpg&w=700&h=500
I would use
s=hi-res-img.jpg,700,500,062c02153d653119
Were those last 16 chars are a hash to validate the URL being opened is the same you offered in your code - and the user is not trying to trick the high-resolution image out of the system.
Your index.php that serves the images would start like this:
function myHash($sRaw) { // returns a 16-characters dual hash
return hash('adler32', $sRaw) . strrev(hash('crc32', $sRaw));
} // These two hash algorithms are suggestions, there are more for you to chose.
// s=hi-res-img.jpg,700,500,062c02153d653119
$aParams = explode(',', $_GET['s']);
if (count($aParams) != 4) {
die('Invalid call.');
}
list($sFileName, $iWidth, $iHeight, $sHash) = $aParams;
$sRaw = session_id() . $sFileName . $iWidth . $iHeight;
if ($sHash != myHash($sRaw)) {
die('Invalid hash.');
}
After this point you can send the image as the user opening it had access to a valid link.
Note the use of session_id as part of the raw string that makes the hash is optional, but would make it impossible for users to share a valid URL - as it would be session bind. If you want the URLs to be shareable, then just remove session_id from that call.
I would wrap the resulting URL the same way you already do, zip + Base64. The result would be even bigger than your version, but more difficult to see through the obfuscation, and therefore protecting your images from unauthorised downloads.
If you want only to make it shorter, I do not see a way of doing it without renaming the files (or their folders), or without the use of a database.
The file database solution proposed will surely create problems of concurrency - unless you always have no or very few people using the system simultaneously.
You say that you want the size there, so that if you decide some day that the preview images are too small, you want to increase the size - the solution here is to hard code the image size into the PHP script and eliminate it from the URL.
If you want to change the size in the future, change the hardcoded values in the PHP script (or in a config.php file that you include into the script).
You've also said that you are already using files to store image data as a JSON object, like: name, title, description. Exploiting this, you don't need a database and can use the JSON file name as the key for looking up the image data.
When the user visits a URL like this:
www.mysite.com/share/index.php?ax9v
You load ax9v.json from the location you are already storing the JSON files, and within that JSON file the image's real path is stored. Then load the image, resize it according to the hardcoded size in your script and send it to the user.
Drawing from the conclusions in
URL Shortening: Hashes In Practice, to get the smallest search string part of the URL you would need to iterate valid character combinations as new files are uploaded (e.g., the first one is "AAA" then "AAB", "AAC", etc.) instead of using a hashing algorithm.
Your solution would then have only three characters in the string for the first 238,328 photos you upload.
I had started to prototype a PHP solution on PhpFiddle, but the code disappeared (don't use PhpFiddle).

PHP working with huge numbers

I am writing an application that can stream videos. It requires the filesize of the video, so I use this code:
$filesize = sprintf("%u", filesize($file));
However, when streaming a six gig movie, it fails.
Is is possible to get a bigger interger value in PHP? I don't care if I have to use third party libraries, if it is slow, all I care about is that it can get the filesize properly.
FYI, $filesize is currently 3017575487 which is really really really really far from 6000000000, which is roughly correct.
I am running PHP on a 64 bit operating system.
Thanks for any suggestions!
The issue here is two-fold.
Problem 1
The filesize function returns a signed integer, with a maximum value of PHP_INT_MAX. On 32-bit PHP, this value is 2147483647 or about 2GB. On 64-bit PHP can you go higher, up to 9223372036854775807. Based on the comments from the PHP filesize page, I created a function that will use a fseek loop to find the size of the file, and return it as a float, which can count higher that a 32-bit unisgned integer.
function filesize_float($filename)
{
$f = fopen($filename, 'r');
$p = 0;
$b = 1073741824;
fseek($f, 0, SEEK_SET);
while($b > 1)
{
fseek($f, $b, SEEK_CUR);
if(fgetc($f) === false)
{
fseek($f, -$b, SEEK_CUR);
$b = (int)($b / 2);
}
else
{
fseek($f, -1, SEEK_CUR);
$p += $b;
}
}
while(fgetc($f) !== false)
{
++$p;
}
fclose($f);
return $p;
}
To get the file size of the file as a float using the above function, you would call it like this.
$filesize = filesize_float($file);
Problem 2
Using %u in the sprintf function will cause it to interpret the argument as an unsigned integer, thus limiting the maximum possible value to 4294967295 on 32-bit PHP, before overflowing. Therefore, if we were to do the following, it would return the wrong number.
sprintf("%u", filesize_float($file));
You could interpret the value as a float using %F, using the following, but it will result in trailing decimals.
sprintf("%F", filesize_float($file));
For example, the above will return something like 6442450944.000000, rather than 6442450944.
A workaround would be to have sprintf interpret the float as a string, and let PHP cast the float to a string.
$filesize = sprintf("%s", filesize_float($file));
This will set $filesize to the value of something like 6442450944, without trailing decimals.
The Final Solution
If you add the filesize_float function above to your code, you can simply use the following line of code to read the actual file size into the sprintf statement.
$filesize = sprintf("%s", filesize_float($file));
As per PHP docuemnation for 64 bit platforms, this seems quite reliable for getting the filesize of files > 4GB
<?php
$a = fopen($filename, 'r');
fseek($a, 0, SEEK_END);
$filesize = ftell($a);
fclose($a);
?>

PHP (ZLIB) uncompression of a C (ZLIB) compressed array returns gibberish

I have a set of ZLIB compressed / base64 encoded strings (done in a C program) that are stored in a database. I have written a small PHP page that should retrieve these values and plot them (the string originally was a list of floats).
Chunk of C program that compresses/encodes:
error=compress2(comp_buffer, &comp_length,(const Bytef*)data.mz ,(uLongf)length,Z_DEFAULT_COMPRESSION); /* compression */
if (error != Z_OK) {fprintf(stderr,"zlib error..exiting"); exit(EXIT_FAILURE);}
mz_binary=g_base64_encode (comp_buffer,comp_length); /* encoding */
(Example) of original input format:
292.1149 8379.5928
366.1519 101313.3906
367.3778 20361.8105
369.1290 17033.3223
375.4355 1159.1841
467.3191 8445.3926
Each column was compressed/encoded as a single string. To reconstruct the original data i am using the following code:
//$row[4] is retrieved from the DB and contains the compressed/encoded string
$mz = base64_decode($row[4]);
$unc_mz = gzuncompress($mz);
echo $unc_mz;
Yet this gives me the following output:
f6jEÍ„]EšiSE#IEfŽ
Could anyone give me a tip/hint about what I might be missing?
------ Added Information -----
I feel that the problem comes from the fact that currently php views $unc_mz as a single string while in reality i would have to re-construct an array containing X lines (this output was from a 9 line file) but... no idea how to do that assignment.
The C program that did that went roughly like this:
uncompress( pUncompr , &uncomprLen , (const Bytef*)pDecoded , decodedSize );
pToBeCorrected = (char *)pUncompr;
for (n = 0; n < (2 * peaksCount); n++) {
pPeaks[n] = (RAMPREAL) ((float *) pToBeCorrected)[n];
}
where peaksCount would be the amount of 'lines' in the input file.
EDIT (15-2-2012): The problem with my code was that I was not reconstructing the array, the fixed code is as follows (might be handy if someone needs a similar snippet):
while ($row = mysql_fetch_array($result, MYSQL_NUM)) {
$m< = base64_decode($row[4]);
$mz_int = gzuncompress($int);
$max = strlen($unc_mz);
$counter = 0;
for ($i = 0; $i < $max; $i = $i + 4) {
$temp= substr($unc_mz,$i,4);
$temp = unpack("f",$temp);
$mz_array[$counter] = $temp[1];
$counter++;
}
The uncompressed string has to be chopped into chunks corresponding to the length of a float, unpack() then reconstructs the float data from teh binary 'chunk'. That's the simplest description that I can give for the above snippet.
compress2() produces the zlib format (RFC 1950). I would have to guess that something called gzuncompress() is expecting the gzip format (RFC 1952). So gzuncompress() would immediately fail upon not finding a gzip header.
You would need to use deflateInit2() in zlib to request that deflate() produce gzip-formatted output, or find or provide a different function in PHP that expects the zlib format.

Translate Code: C Socket to PHP Socket

I looked through past threads on here to find any relevant topics regarding C sockets being translated to PHP sockets, and I've ready extensively on php.net and C tutorials to figure out how to convert some C source code I have into PHP source code involving a remote socket connection.
The source I am going to post is in C. This code is already working and confirmed to work in a compiled .exe that one of my programmers wrote. He doesn't know PHP, which I am creating this new program in that requires this snippet.
The program does this: It creates a socket connection to a remote server/port, sends the filesize of an image that it needs to send to the server, then I am guessing when the server knows the filesize, the program sends the binary image data and the filesize of the image again (like an upload feature to the server via sockets). Then it uses the recv(); function in C to receive the bytes back of a specific length.
Basically it is sending a picture with something encrypted inside of it. The server already has a program running on it on the specified port that is decrypting the image. Then the socket is sending back that decrypted text. I do not have access to the algorithm for decryption otherwise I would not be using sockets obviously.
Here is the C source I was sent, and my subsequent attempts in PHP to translate it properly.
// In the this C code, there is the long size; variable that is used at the bottom but is never initialized. I don't know what to do with it. Some other variables are never used either.
function picBin()
assume curlBinaryData variable pic is filled with binary data from picture download.
pic->currentSize is set to the size of the image
it will return 0 if successful. also char *word in the function's params will be set to the pic's decryption
//bin data structure i use for a quick ghetto download function, just so you know how it looks
struct curlBinaryData{
char *data;
int currentSize;
int maxSize;
};
int picBin(curlBinaryData *pic, char *word, int threadNum,
char *host, unsigned short port)
{
char data1[1000], data2[1000],
temp[1000], printBuf[1000], buffer[1000], *p, *p2;
int num, a, totalBytes;
long size;
char *pSize;
SOCKET sock;
while ((sock = connectSocket(host, port)) == INVALID_SOCKET)
{
sprintf(printBuf, "Could not connect(picBin) %s:%d\n", host, port);
print_ts(printBuf, "red");
//Sleep(15000);
}
buffer[0]='\0';
send(sock, buffer, 1, 0);
pSize=(char *)&(pic->currentSize);
send(sock, pSize, 4, 0);
send(sock, pic->data, pic->currentSize, 0);
totalBytes=0;
do{
if ( (num=recv(sock, data1+totalBytes, 1, 0)) > 0)
totalBytes+=num;
} while( (totalBytes<4) && (num>0) );
pSize=(char *)&size;
if (totalBytes==4){ //got 4 bytes for dword
memcpy(pSize, data1, 4);
if (size==1)
{
totalBytes=0;
do
{
if ( (num=recv(sock, data1+totalBytes, 1, 0)) > 0)
totalBytes+=num;
} while( (totalBytes<4) && (num>0) );
memcpy(pSize, data1, 4);
if (totalBytes==4)
{ //got 4 bytes for dword
totalBytes=0;
for (a=0; ( (a<size) && (num>0) ); a++)
{
if ( (num=recv(sock, data1+totalBytes, 1, 0)) > 0)
totalBytes+=num;
}
if (totalBytes==size)
{
closesocket(sock);
data1[totalBytes]='\0';
strcpy(word, data1);
return 0; //0 on success
}
}
}
}
closesocket(sock);
return -1; //something errord
}
Now here is my PHP code I attempted:
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false)
{
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
if (socket_connect($sock, $host, $port) === false)
{
echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
socket_send($sock, '\0', 1, MSG_EOF);
$ci = file_get_contents($picURL);
$ciwrite = fopen('pic.jpg', 'w+');
fwrite($ciwrite, $ci);
fclose($ciwrite);
$picFileSize = filesize('pic.jpg');
socket_send($sock, $picFileSize, 4, MSG_EOF);
socket_send($sock, $ci, $picFileSize, MSG_EOF);
$num = socket_recv($sock, $totalBytes, 1, MSG_DONTWAIT);
print $num; // this doesn't even Print anything to my console when I execute it via CLI
/*do{
if (($num = socket_recv($sock, $totalBytes, 1)) > 0)
{
$totalBytes += $num;
}
} while(($totalBytes < 4) && ($num > 0));*/
buffer[0]='\0';
send(sock, buffer, 1, 0);
socket_send($sock, '\0', 1, MSG_EOF);
Backslash escape sequences other than \' and \\ are not expanded in single quoted strings; thus, '\0' is a string of the two characters \ and 0, and the above socket_send() sends the character \.
MSG_EOF is without effect at best and harmful at worst; better don't use it.
A correct translation is:
socket_send($sock, "\0", 1, 0);
pSize=(char *)&(pic->currentSize);
send(sock, pSize, 4, 0);
socket_send($sock, $picFileSize, 4, MSG_EOF);
The above socket_send() sends the first 4 characters of the ASCII string representation of $picFileSize, because socket_send() expects a string as its second argument and thus the given integer is coerced into a string. To send a 4-byte binary integer:
socket_send($sock, pack("L", $picFileSize), 4, 0);
$num = socket_recv($sock, $totalBytes, 1, MSG_DONTWAIT);
print $num; // this doesn't even Print anything to my console when I execute it via CLI
It's no wonder that you get no data if you DONTWAIT for it.
A working translation for the receive part of the C program is:
$totalBytes = socket_recv($sock, $data1, 4, MSG_WAITALL);
if ($totalBytes==4)
{ //got 4 bytes for dword
$size = unpack("L", $data1);
if ($size[1]==1)
{
$totalBytes = socket_recv($sock, $data1, 4, MSG_WAITALL);
if ($totalBytes==4)
{ //got 4 bytes for dword
$size = unpack("L", $data1);
$totalBytes = socket_recv($sock, $data1, $size[1], MSG_WAITALL);
if ($totalBytes==$size[1])
{
echo $data1, "\n";
}
}
}
}
regarding C sockets being translated to PHP sockets
Sockets are implemented by the OS - not the language. There is no such thing as a C socket nor a PHP socket.
When you invoke socket_send from PHP it converts the message to a string (if it's not already one).
pSize=(char *)&(pic->currentSize);
send(sock, pSize, 4, 0);
WTF? Assuming that pic->currentSize contains a string represenation if the image why is it hard coded to 4 bytes!
But here:
send(sock, pic->data, pic->currentSize, 0);
pic->currentSize must contain an integer value. Shoving (char *) in front of a pointer to an integer (or a long, since that appears to be how 'size' is declared) DOES NOT MAKE IT a string. It's still a pointer. OK a 32 bit pointer will be 4 bytes - but WTF is a pointer being sent over a socket??? Unless there's some weird overloading going on here and this is actually C++ rather than C.
Reading further on it sends the pointer to the remote end then reads back a 4 byte value and considers the operation successful if it matches the pointer....!! AAARRGGHHH! even if it were sending the size, this level of integrity checking is entirely redundant over a Unix or INET socket.
I'd dig some more but it's going to be a waste of time trying to make sense of this C code. Use a network sniffer to work out what's really happenning here; do not use this C code as any sort of model of how to write your own client.

Categories