Skip to content

Commit

Permalink
Proof of concept : audio captcha supports HTTP range header
Browse files Browse the repository at this point in the history
  • Loading branch information
1and2papa committed Apr 15, 2014
1 parent 9289657 commit 4410b2e
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 10 deletions.
4 changes: 2 additions & 2 deletions captcha.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<!-- The following is example HTML that can be used on your form -->

<p>
<img id="siimage" style="border: 1px solid #000; margin-right: 15px" src="./securimage_show.php?sid=<?php echo md5(uniqid()) ?>" alt="CAPTCHA Image" align="left">
<object type="application/x-shockwave-flash" data="./securimage_play.swf?bgcol=#ffffff&amp;icon_file=./images/audio_icon.png&amp;audio_file=./securimage_play.php" height="32" width="32">
<a href="./securimage_play.php" target="_blank"><img src="./images/audio_icon.png" alt="Download Audio" /></a>
<param name="movie" value="./securimage_play.swf?bgcol=#ffffff&amp;icon_file=./images/audio_icon.png&amp;audio_file=./securimage_play.php" />
</object>
&nbsp;
<a tabindex="-1" style="border-style: none;" href="#" title="Refresh Image" onclick="document.getElementById('siimage').src = './securimage_show.php?sid=' + Math.random(); this.blur(); return false"><img src="./images/refresh.png" alt="Reload Image" onclick="this.blur()" align="bottom" border="0"></a><br />
<strong>Enter Code*:</strong><br />
<input type="text" name="ct_captcha" size="12" maxlength="16" />
</p>

123 changes: 115 additions & 8 deletions securimage.php
Original file line number Diff line number Diff line change
Expand Up @@ -1324,14 +1324,19 @@ public function outputAudioFile()
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
header('Content-type: audio/x-wav');

if (extension_loaded('zlib')) {
ini_set('zlib.output_compression', true); // compress output if supported by browser
} else {
header('Content-Length: ' . strlen($audio));
}
if (isset($_SERVER['HTTP_RANGE'])) {
$this->rangeDownload($audio);
} else {

if (extension_loaded('zlib')) {
ini_set('zlib.output_compression', true); // compress output if supported by browser
} else {
header('Content-Length: ' . strlen($audio));
}

echo $audio;
}
}

echo $audio;
} else {
echo '<hr /><strong>'
.'Failed to generate audio file, content has already been '
Expand All @@ -1344,6 +1349,101 @@ public function outputAudioFile()
if (!$this->no_exit) exit;
}

/**
* Output partial audio based on Range header
* Ref : http://mobiforge.com/design-development/content-delivery-mobile-devices
* @param $audio string The audio representation of the captcha in Wav format
*/
public function rangeDownload($audio)
{
$size = strlen($audio); // File size
$length = $size; // Content length
$start = 0; // Start byte
$end = $size - 1; // End byte

// Now that we've gotten so far without errors we send the accept range header
/* At the moment we only support single ranges.
* Multiple ranges requires some more work to ensure it works correctly
* and comply with the spesifications: http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2
*
* Multirange support annouces itself with:
* header('Accept-Ranges: bytes');
*
* Multirange content must be sent with multipart/byteranges mediatype,
* (mediatype = mimetype)
* as well as a boundry header to indicate the various chunks of data.
*/
header("Accept-Ranges: 0-$length");
// header('Accept-Ranges: bytes');
// multipart/byteranges
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.2

if (isset($_SERVER['HTTP_RANGE'])) {

$c_start = $start;
$c_end = $end;

// Extract the range string
list(, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);

// Make sure the client hasn't sent us a multibyte range
if (strpos($range, ',') !== false) {
// (?) Shoud this be issued here, or should the first
// range be used? Or should the header be ignored and
// we output the whole content?
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
// (?) Echo some info to the client?
exit();
}

// If the range starts with an '-' we start from the beginning
// If not, we forward the file pointer
// And make sure to get the end byte if spesified

if ($range{0} == '-') {
// The n-number of the last bytes is requested
$c_start = $size - substr($range, 1);
}
else {
$range = explode('-', $range);
$c_start = $range[0];
$c_end = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $size;
}

/* Check the range and make sure it's treated according to the specs.
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
*/
// End bytes can not be larger than $end.
$c_end = ($c_end > $end) ? $end : $c_end;

// Validate the requested range and return an error if it's not correct.
if ($c_start > $c_end || $c_start > $size - 1 || $c_end >= $size) {
header('HTTP/1.1 416 Requested Range Not Satisfiable');
header("Content-Range: bytes $start-$end/$size");
// (?) Echo some info to the client?
exit();
}
$start = $c_start;
$end = $c_end;
$length = $end - $start + 1; // Calculate new content length

header('HTTP/1.1 206 Partial Content');
}

// Notify the client the byte range we'll be outputting
header("Content-Range: bytes $start-$end/$size");

if (extension_loaded('zlib')) {
ini_set('zlib.output_compression', true); // compress output if supported by browser
} else {
header("Content-Length: $length");
}

// Output partial audio
echo substr($audio, $start, $length);
}

/**
* Return the code from the session or database (if configured). If none exists or was found, an empty string is returned.
*
Expand Down Expand Up @@ -1918,7 +2018,14 @@ protected function getAudibleCode()
}

try {
return $this->generateWAV($letters);

if ($letters != $_SESSION['letters']) {
// save letters and generated wav to session
$_SESSION['letters'] = $letters;
$_SESSION['wav'] = $this->generateWAV($letters);
}

return $_SESSION['wav'];
} catch(Exception $ex) {
throw $ex;
}
Expand Down

0 comments on commit 4410b2e

Please sign in to comment.