Addition of files

This commit is contained in:
Edi
2022-03-15 16:34:48 +01:00
parent 9688e6c8ca
commit a48d911d1c
52 changed files with 10720 additions and 0 deletions

View File

@@ -0,0 +1,717 @@
<?php
namespace Gregwar\Captcha;
use \Exception;
/**
* Builds a new captcha image
* Uses the fingerprint parameter, if one is passed, to generate the same image
*
* @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy.j.livingston@gmail.com>
*/
class CaptchaBuilder implements CaptchaBuilderInterface
{
/**
* @var array
*/
protected $fingerprint = array();
/**
* @var bool
*/
protected $useFingerprint = false;
/**
* @var array
*/
protected $textColor = array();
/**
* @var array
*/
protected $backgroundColor = null;
/**
* @var array
*/
protected $backgroundImages = array();
/**
* @var resource
*/
protected $contents = null;
/**
* @var string
*/
protected $phrase = null;
/**
* @var PhraseBuilderInterface
*/
protected $builder;
/**
* @var bool
*/
protected $distortion = true;
/**
* The maximum number of lines to draw in front of
* the image. null - use default algorithm
*/
protected $maxFrontLines = null;
/**
* The maximum number of lines to draw behind
* the image. null - use default algorithm
*/
protected $maxBehindLines = null;
/**
* The maximum angle of char
*/
protected $maxAngle = 8;
/**
* The maximum offset of char
*/
protected $maxOffset = 5;
/**
* Is the interpolation enabled ?
*
* @var bool
*/
protected $interpolation = true;
/**
* Ignore all effects
*
* @var bool
*/
protected $ignoreAllEffects = false;
/**
* Allowed image types for the background images
*
* @var array
*/
protected $allowedBackgroundImageTypes = array('image/png', 'image/jpeg', 'image/gif');
/**
* The image contents
*/
public function getContents()
{
return $this->contents;
}
/**
* Enable/Disables the interpolation
*
* @param $interpolate bool True to enable, false to disable
*
* @return CaptchaBuilder
*/
public function setInterpolation($interpolate = true)
{
$this->interpolation = $interpolate;
return $this;
}
/**
* Temporary dir, for OCR check
*/
public $tempDir = 'temp/';
public function __construct($phrase = null, PhraseBuilderInterface $builder = null)
{
if ($builder === null) {
$this->builder = new PhraseBuilder;
} else {
$this->builder = $builder;
}
$this->phrase = is_string($phrase) ? $phrase : $this->builder->build($phrase);
}
/**
* Setting the phrase
*/
public function setPhrase($phrase)
{
$this->phrase = (string) $phrase;
}
/**
* Enables/disable distortion
*/
public function setDistortion($distortion)
{
$this->distortion = (bool) $distortion;
return $this;
}
public function setMaxBehindLines($maxBehindLines)
{
$this->maxBehindLines = $maxBehindLines;
return $this;
}
public function setMaxFrontLines($maxFrontLines)
{
$this->maxFrontLines = $maxFrontLines;
return $this;
}
public function setMaxAngle($maxAngle)
{
$this->maxAngle = $maxAngle;
return $this;
}
public function setMaxOffset($maxOffset)
{
$this->maxOffset = $maxOffset;
return $this;
}
/**
* Gets the captcha phrase
*/
public function getPhrase()
{
return $this->phrase;
}
/**
* Returns true if the given phrase is good
*/
public function testPhrase($phrase)
{
return ($this->builder->niceize($phrase) == $this->builder->niceize($this->getPhrase()));
}
/**
* Instantiation
*/
public static function create($phrase = null)
{
return new self($phrase);
}
/**
* Sets the text color to use
*/
public function setTextColor($r, $g, $b)
{
$this->textColor = array($r, $g, $b);
return $this;
}
/**
* Sets the background color to use
*/
public function setBackgroundColor($r, $g, $b)
{
$this->backgroundColor = array($r, $g, $b);
return $this;
}
/**
* Sets the ignoreAllEffects value
*
* @param bool $ignoreAllEffects
* @return CaptchaBuilder
*/
public function setIgnoreAllEffects($ignoreAllEffects)
{
$this->ignoreAllEffects = $ignoreAllEffects;
return $this;
}
/**
* Sets the list of background images to use (one image is randomly selected)
*/
public function setBackgroundImages(array $backgroundImages)
{
$this->backgroundImages = $backgroundImages;
return $this;
}
/**
* Draw lines over the image
*/
protected function drawLine($image, $width, $height, $tcol = null)
{
if ($tcol === null) {
$tcol = imagecolorallocate($image, $this->rand(100, 255), $this->rand(100, 255), $this->rand(100, 255));
}
if ($this->rand(0, 1)) { // Horizontal
$Xa = $this->rand(0, $width/2);
$Ya = $this->rand(0, $height);
$Xb = $this->rand($width/2, $width);
$Yb = $this->rand(0, $height);
} else { // Vertical
$Xa = $this->rand(0, $width);
$Ya = $this->rand(0, $height/2);
$Xb = $this->rand(0, $width);
$Yb = $this->rand($height/2, $height);
}
imagesetthickness($image, $this->rand(1, 3));
imageline($image, $Xa, $Ya, $Xb, $Yb, $tcol);
}
/**
* Apply some post effects
*/
protected function postEffect($image)
{
if (!function_exists('imagefilter')) {
return;
}
if ($this->backgroundColor != null || $this->textColor != null) {
return;
}
// Negate ?
if ($this->rand(0, 1) == 0) {
imagefilter($image, IMG_FILTER_NEGATE);
}
// Edge ?
if ($this->rand(0, 10) == 0) {
imagefilter($image, IMG_FILTER_EDGEDETECT);
}
// Contrast
imagefilter($image, IMG_FILTER_CONTRAST, $this->rand(-50, 10));
// Colorize
if ($this->rand(0, 5) == 0) {
imagefilter($image, IMG_FILTER_COLORIZE, $this->rand(-80, 50), $this->rand(-80, 50), $this->rand(-80, 50));
}
}
/**
* Writes the phrase on the image
*/
protected function writePhrase($image, $phrase, $font, $width, $height)
{
$length = mb_strlen($phrase);
if ($length === 0) {
return \imagecolorallocate($image, 0, 0, 0);
}
// Gets the text size and start position
$size = $width / $length - $this->rand(0, 3) - 1;
$box = \imagettfbbox($size, 0, $font, $phrase);
$textWidth = $box[2] - $box[0];
$textHeight = $box[1] - $box[7];
$x = ($width - $textWidth) / 2;
$y = ($height - $textHeight) / 2 + $size;
if (!$this->textColor) {
$textColor = array($this->rand(0, 150), $this->rand(0, 150), $this->rand(0, 150));
} else {
$textColor = $this->textColor;
}
$col = \imagecolorallocate($image, $textColor[0], $textColor[1], $textColor[2]);
// Write the letters one by one, with random angle
for ($i=0; $i<$length; $i++) {
$symbol = mb_substr($phrase, $i, 1);
$box = \imagettfbbox($size, 0, $font, $symbol);
$w = $box[2] - $box[0];
$angle = $this->rand(-$this->maxAngle, $this->maxAngle);
$offset = $this->rand(-$this->maxOffset, $this->maxOffset);
\imagettftext($image, $size, $angle, $x, $y + $offset, $col, $font, $symbol);
$x += $w;
}
return $col;
}
/**
* Try to read the code against an OCR
*/
public function isOCRReadable()
{
if (!is_dir($this->tempDir)) {
@mkdir($this->tempDir, 0755, true);
}
$tempj = $this->tempDir . uniqid('captcha', true) . '.jpg';
$tempp = $this->tempDir . uniqid('captcha', true) . '.pgm';
$this->save($tempj);
shell_exec("convert $tempj $tempp");
$value = trim(strtolower(shell_exec("ocrad $tempp")));
@unlink($tempj);
@unlink($tempp);
return $this->testPhrase($value);
}
/**
* Builds while the code is readable against an OCR
*/
public function buildAgainstOCR($width = 150, $height = 40, $font = null, $fingerprint = null)
{
do {
$this->build($width, $height, $font, $fingerprint);
} while ($this->isOCRReadable());
}
/**
* Generate the image
*/
public function build($width = 150, $height = 40, $font = null, $fingerprint = null)
{
if (null !== $fingerprint) {
$this->fingerprint = $fingerprint;
$this->useFingerprint = true;
} else {
$this->fingerprint = array();
$this->useFingerprint = false;
}
if ($font === null) {
$font = __DIR__ . '/Font/captcha'.$this->rand(0, 5).'.ttf';
}
if (empty($this->backgroundImages)) {
// if background images list is not set, use a color fill as a background
$image = imagecreatetruecolor($width, $height);
if ($this->backgroundColor == null) {
$bg = imagecolorallocate($image, $this->rand(200, 255), $this->rand(200, 255), $this->rand(200, 255));
} else {
$color = $this->backgroundColor;
$bg = imagecolorallocate($image, $color[0], $color[1], $color[2]);
}
$this->background = $bg;
imagefill($image, 0, 0, $bg);
} else {
// use a random background image
$randomBackgroundImage = $this->backgroundImages[rand(0, count($this->backgroundImages)-1)];
$imageType = $this->validateBackgroundImage($randomBackgroundImage);
$image = $this->createBackgroundImageFromType($randomBackgroundImage, $imageType);
}
// Apply effects
if (!$this->ignoreAllEffects) {
$square = $width * $height;
$effects = $this->rand($square/3000, $square/2000);
// set the maximum number of lines to draw in front of the text
if ($this->maxBehindLines != null && $this->maxBehindLines > 0) {
$effects = min($this->maxBehindLines, $effects);
}
if ($this->maxBehindLines !== 0) {
for ($e = 0; $e < $effects; $e++) {
$this->drawLine($image, $width, $height);
}
}
}
// Write CAPTCHA text
$color = $this->writePhrase($image, $this->phrase, $font, $width, $height);
// Apply effects
if (!$this->ignoreAllEffects) {
$square = $width * $height;
$effects = $this->rand($square/3000, $square/2000);
// set the maximum number of lines to draw in front of the text
if ($this->maxFrontLines != null && $this->maxFrontLines > 0) {
$effects = min($this->maxFrontLines, $effects);
}
if ($this->maxFrontLines !== 0) {
for ($e = 0; $e < $effects; $e++) {
$this->drawLine($image, $width, $height, $color);
}
}
}
// Distort the image
if ($this->distortion && !$this->ignoreAllEffects) {
$image = $this->distort($image, $width, $height, $bg);
}
// Post effects
if (!$this->ignoreAllEffects) {
$this->postEffect($image);
}
$this->contents = $image;
return $this;
}
/**
* Distorts the image
*/
public function distort($image, $width, $height, $bg)
{
$contents = imagecreatetruecolor($width, $height);
$X = $this->rand(0, $width);
$Y = $this->rand(0, $height);
$phase = $this->rand(0, 10);
$scale = 1.1 + $this->rand(0, 10000) / 30000;
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$Vx = $x - $X;
$Vy = $y - $Y;
$Vn = sqrt($Vx * $Vx + $Vy * $Vy);
if ($Vn != 0) {
$Vn2 = $Vn + 4 * sin($Vn / 30);
$nX = $X + ($Vx * $Vn2 / $Vn);
$nY = $Y + ($Vy * $Vn2 / $Vn);
} else {
$nX = $X;
$nY = $Y;
}
$nY = $nY + $scale * sin($phase + $nX * 0.2);
if ($this->interpolation) {
$p = $this->interpolate(
$nX - floor($nX),
$nY - floor($nY),
$this->getCol($image, floor($nX), floor($nY), $bg),
$this->getCol($image, ceil($nX), floor($nY), $bg),
$this->getCol($image, floor($nX), ceil($nY), $bg),
$this->getCol($image, ceil($nX), ceil($nY), $bg)
);
} else {
$p = $this->getCol($image, round($nX), round($nY), $bg);
}
if ($p == 0) {
$p = $bg;
}
imagesetpixel($contents, $x, $y, $p);
}
}
return $contents;
}
/**
* Saves the Captcha to a jpeg file
*/
public function save($filename, $quality = 90)
{
imagejpeg($this->contents, $filename, $quality);
}
/**
* Gets the image GD
*/
public function getGd()
{
return $this->contents;
}
/**
* Gets the image contents
*/
public function get($quality = 90)
{
ob_start();
$this->output($quality);
return ob_get_clean();
}
/**
* Gets the HTML inline base64
*/
public function inline($quality = 90)
{
return 'data:image/jpeg;base64,' . base64_encode($this->get($quality));
}
/**
* Outputs the image
*/
public function output($quality = 90)
{
imagejpeg($this->contents, null, $quality);
}
/**
* @return array
*/
public function getFingerprint()
{
return $this->fingerprint;
}
/**
* Returns a random number or the next number in the
* fingerprint
*/
protected function rand($min, $max)
{
if (!is_array($this->fingerprint)) {
$this->fingerprint = array();
}
if ($this->useFingerprint) {
$value = current($this->fingerprint);
next($this->fingerprint);
} else {
$value = mt_rand($min, $max);
$this->fingerprint[] = $value;
}
return $value;
}
/**
* @param $x
* @param $y
* @param $nw
* @param $ne
* @param $sw
* @param $se
*
* @return int
*/
protected function interpolate($x, $y, $nw, $ne, $sw, $se)
{
list($r0, $g0, $b0) = $this->getRGB($nw);
list($r1, $g1, $b1) = $this->getRGB($ne);
list($r2, $g2, $b2) = $this->getRGB($sw);
list($r3, $g3, $b3) = $this->getRGB($se);
$cx = 1.0 - $x;
$cy = 1.0 - $y;
$m0 = $cx * $r0 + $x * $r1;
$m1 = $cx * $r2 + $x * $r3;
$r = (int) ($cy * $m0 + $y * $m1);
$m0 = $cx * $g0 + $x * $g1;
$m1 = $cx * $g2 + $x * $g3;
$g = (int) ($cy * $m0 + $y * $m1);
$m0 = $cx * $b0 + $x * $b1;
$m1 = $cx * $b2 + $x * $b3;
$b = (int) ($cy * $m0 + $y * $m1);
return ($r << 16) | ($g << 8) | $b;
}
/**
* @param $image
* @param $x
* @param $y
*
* @return int
*/
protected function getCol($image, $x, $y, $background)
{
$L = imagesx($image);
$H = imagesy($image);
if ($x < 0 || $x >= $L || $y < 0 || $y >= $H) {
return $background;
}
return imagecolorat($image, $x, $y);
}
/**
* @param $col
*
* @return array
*/
protected function getRGB($col)
{
return array(
(int) ($col >> 16) & 0xff,
(int) ($col >> 8) & 0xff,
(int) ($col) & 0xff,
);
}
/**
* Validate the background image path. Return the image type if valid
*
* @param string $backgroundImage
* @return string
* @throws Exception
*/
protected function validateBackgroundImage($backgroundImage)
{
// check if file exists
if (!file_exists($backgroundImage)) {
$backgroundImageExploded = explode('/', $backgroundImage);
$imageFileName = count($backgroundImageExploded) > 1? $backgroundImageExploded[count($backgroundImageExploded)-1] : $backgroundImage;
throw new Exception('Invalid background image: ' . $imageFileName);
}
// check image type
$finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension
$imageType = finfo_file($finfo, $backgroundImage);
finfo_close($finfo);
if (!in_array($imageType, $this->allowedBackgroundImageTypes)) {
throw new Exception('Invalid background image type! Allowed types are: ' . join(', ', $this->allowedBackgroundImageTypes));
}
return $imageType;
}
/**
* Create background image from type
*
* @param string $backgroundImage
* @param string $imageType
* @return resource
* @throws Exception
*/
protected function createBackgroundImageFromType($backgroundImage, $imageType)
{
switch ($imageType) {
case 'image/jpeg':
$image = imagecreatefromjpeg($backgroundImage);
break;
case 'image/png':
$image = imagecreatefrompng($backgroundImage);
break;
case 'image/gif':
$image = imagecreatefromgif($backgroundImage);
break;
default:
throw new Exception('Not supported file type for background image!');
break;
}
return $image;
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Gregwar\Captcha;
/**
* A Captcha builder
*/
interface CaptchaBuilderInterface
{
/**
* Builds the code
*/
public function build($width, $height, $font, $fingerprint);
/**
* Saves the code to a file
*/
public function save($filename, $quality);
/**
* Gets the image contents
*/
public function get($quality);
/**
* Outputs the image
*/
public function output($quality);
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,105 @@
<?php
namespace Gregwar\Captcha;
use Symfony\Component\Finder\Finder;
/**
* Handles actions related to captcha image files including saving and garbage collection
*
* @author Gregwar <g.passault@gmail.com>
* @author Jeremy Livingston <jeremy@quizzle.com>
*/
class ImageFileHandler
{
/**
* Name of folder for captcha images
* @var string
*/
protected $imageFolder;
/**
* Absolute path to public web folder
* @var string
*/
protected $webPath;
/**
* Frequency of garbage collection in fractions of 1
* @var int
*/
protected $gcFreq;
/**
* Maximum age of images in minutes
* @var int
*/
protected $expiration;
/**
* @param $imageFolder
* @param $webPath
* @param $gcFreq
* @param $expiration
*/
public function __construct($imageFolder, $webPath, $gcFreq, $expiration)
{
$this->imageFolder = $imageFolder;
$this->webPath = $webPath;
$this->gcFreq = $gcFreq;
$this->expiration = $expiration;
}
/**
* Saves the provided image content as a file
*
* @param string $contents
*
* @return string
*/
public function saveAsFile($contents)
{
$this->createFolderIfMissing();
$filename = md5(uniqid()) . '.jpg';
$filePath = $this->webPath . '/' . $this->imageFolder . '/' . $filename;
imagejpeg($contents, $filePath, 15);
return '/' . $this->imageFolder . '/' . $filename;
}
/**
* Randomly runs garbage collection on the image directory
*
* @return bool
*/
public function collectGarbage()
{
if (!mt_rand(1, $this->gcFreq) == 1) {
return false;
}
$this->createFolderIfMissing();
$finder = new Finder();
$criteria = sprintf('<= now - %s minutes', $this->expiration);
$finder->in($this->webPath . '/' . $this->imageFolder)
->date($criteria);
foreach ($finder->files() as $file) {
unlink($file->getPathname());
}
return true;
}
/**
* Creates the folder if it doesn't exist
*/
protected function createFolderIfMissing()
{
if (!file_exists($this->webPath . '/' . $this->imageFolder)) {
mkdir($this->webPath . '/' . $this->imageFolder, 0755);
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
namespace Gregwar\Captcha;
/**
* Generates random phrase
*
* @author Gregwar <g.passault@gmail.com>
*/
class PhraseBuilder implements PhraseBuilderInterface
{
/**
* @var int
*/
public $length;
/**
* @var string
*/
public $charset;
/**
* Constructs a PhraseBuilder with given parameters
*/
public function __construct($length = 5, $charset = 'abcdefghijklmnpqrstuvwxyz123456789')
{
$this->length = $length;
$this->charset = $charset;
}
/**
* Generates random phrase of given length with given charset
*/
public function build($length = null, $charset = null)
{
if ($length !== null) {
$this->length = $length;
}
if ($charset !== null) {
$this->charset = $charset;
}
$phrase = '';
$chars = str_split($this->charset);
for ($i = 0; $i < $this->length; $i++) {
$phrase .= $chars[array_rand($chars)];
}
return $phrase;
}
/**
* "Niceize" a code
*/
public function niceize($str)
{
return strtr(strtolower($str), '01', 'ol');
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Gregwar\Captcha;
/**
* Interface for the PhraseBuilder
*
* @author Gregwar <g.passault@gmail.com>
*/
interface PhraseBuilderInterface
{
/**
* Generates random phrase of given length with given charset
*/
public function build();
/**
* "Niceize" a code
*/
public function niceize($str);
}

View File

@@ -0,0 +1,250 @@
<?php
namespace Identicon\Generator;
use Exception;
/**
* @author Benjamin Laugueux <benjamin@yzalis.com>
*/
class BaseGenerator
{
/**
* @var mixed
*/
protected $generatedImage;
/**
* @var array
*/
protected $color;
/**
* @var array
*/
protected $backgroundColor;
/**
* @var int
*/
protected $size;
/**
* @var int
*/
protected $pixelRatio;
/**
* @var string
*/
private $hash;
/**
* @var array
*/
private $arrayOfSquare = [];
/**
* Set the image color.
*
* @param string|array $color The color in hexa (3 or 6 chars) or rgb array
*
* @return $this
*/
public function setColor($color)
{
if (null === $color) {
return $this;
}
$this->color = $this->convertColor($color);
return $this;
}
/**
* Set the image background color.
*
* @param string|array $backgroundColor The color in hexa (3 or 6 chars) or rgb array
*
* @return $this
*/
public function setBackgroundColor($backgroundColor)
{
if (null === $backgroundColor) {
return $this;
}
$this->backgroundColor = $this->convertColor($backgroundColor);
return $this;
}
/**
* @param array|string $color
*
* @return array
*/
private function convertColor($color)
{
if (is_array($color)) {
return $color;
}
if (preg_match('/^#?([a-z\d])([a-z\d])([a-z\d])$/i', $color, $matches)) {
$color = $matches[1].$matches[1];
$color .= $matches[2].$matches[2];
$color .= $matches[3].$matches[3];
}
preg_match('/#?([a-z\d]{2})([a-z\d]{2})([a-z\d]{2})$/i', $color, $matches);
return array_map(function ($value) {
return hexdec($value);
}, array_slice($matches, 1, 3));
}
/**
* Get the color.
*
* @return array
*/
public function getColor()
{
return $this->color;
}
/**
* Get the background color.
*
* @return array
*/
public function getBackgroundColor()
{
return $this->backgroundColor;
}
/**
* Convert the hash into an multidimensional array of boolean.
*
* @return $this
*/
private function convertHashToArrayOfBoolean()
{
preg_match_all('/(\w)(\w)/', $this->hash, $chars);
foreach ($chars[1] as $i => $char) {
$index = (int) ($i / 3);
$data = $this->convertHexaToBoolean($char);
$items = [
0 => [0, 4],
1 => [1, 3],
2 => [2],
];
foreach ($items[$i % 3] as $item) {
$this->arrayOfSquare[$index][$item] = $data;
}
ksort($this->arrayOfSquare[$index]);
}
$this->color = array_map(function ($data) {
return hexdec($data) * 16;
}, array_reverse($chars[1]));
return $this;
}
/**
* Convert an hexadecimal number into a boolean.
*
* @param string $hexa
*
* @return bool
*/
private function convertHexaToBoolean($hexa)
{
return (bool) round(hexdec($hexa) / 10);
}
/**
* @return array
*/
public function getArrayOfSquare()
{
return $this->arrayOfSquare;
}
/**
* Get the identicon string hash.
*
* @return string
*/
public function getHash()
{
return $this->hash;
}
/**
* Generate a hash from the original string.
*
* @param string $string
*
* @throws \Exception
*
* @return $this
*/
public function setString($string)
{
if (null === $string) {
throw new Exception('The string cannot be null.');
}
$this->hash = md5($string);
$this->convertHashToArrayOfBoolean();
return $this;
}
/**
* Set the image size.
*
* @param int $size
*
* @return $this
*/
public function setSize($size)
{
if (null === $size) {
return $this;
}
$this->size = $size;
$this->pixelRatio = (int) round($size / 5);
return $this;
}
/**
* Get the image size.
*
* @return int
*/
public function getSize()
{
return $this->size;
}
/**
* Get the pixel ratio.
*
* @return int
*/
public function getPixelRatio()
{
return $this->pixelRatio;
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace Identicon\Generator;
use Exception;
/**
* @author Benjamin Laugueux <benjamin@yzalis.com>
*/
class GdGenerator extends BaseGenerator implements GeneratorInterface
{
/**
* GdGenerator constructor.
*/
public function __construct()
{
if (!extension_loaded('gd') && !extension_loaded('ext-gd')) {
throw new Exception('GD does not appear to be available in your PHP installation. Please try another generator');
}
}
/**
* @return string
*/
public function getMimeType()
{
return 'image/png';
}
/**
* @return $this
*/
private function generateImage()
{
// prepare image
$this->generatedImage = imagecreatetruecolor($this->getPixelRatio() * 5, $this->getPixelRatio() * 5);
$rgbBackgroundColor = $this->getBackgroundColor();
if (null === $rgbBackgroundColor) {
$background = imagecolorallocate($this->generatedImage, 0, 0, 0);
imagecolortransparent($this->generatedImage, $background);
} else {
$background = imagecolorallocate($this->generatedImage, $rgbBackgroundColor[0], $rgbBackgroundColor[1], $rgbBackgroundColor[2]);
imagefill($this->generatedImage, 0, 0, $background);
}
// prepare color
$rgbColor = $this->getColor();
$gdColor = imagecolorallocate($this->generatedImage, $rgbColor[0], $rgbColor[1], $rgbColor[2]);
// draw content
foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
foreach ($lineValue as $colKey => $colValue) {
if (true === $colValue) {
imagefilledrectangle($this->generatedImage, $colKey * $this->getPixelRatio(), $lineKey * $this->getPixelRatio(), ($colKey + 1) * $this->getPixelRatio(), ($lineKey + 1) * $this->getPixelRatio(), $gdColor);
}
}
}
return $this;
}
/**
* {@inheritdoc}
*/
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
{
ob_start();
imagepng($this->getImageResource($string, $size, $color, $backgroundColor));
$imageData = ob_get_contents();
ob_end_clean();
return $imageData;
}
/**
* {@inheritdoc}
*/
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
{
$this
->setString($string)
->setSize($size)
->setColor($color)
->setBackgroundColor($backgroundColor)
->generateImage();
return $this->generatedImage;
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace Identicon\Generator;
/**
* @author Benjamin Laugueux <benjamin@yzalis.com>
*/
interface GeneratorInterface
{
/**
* @param string $string
* @param int $size
* @param array|string $color
* @param array|string $backgroundColor
*
* @return mixed
*/
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null);
/**
* @param string $string
* @param int $size
* @param array|string $color
* @param array|string $backgroundColor
*
* @return string
*/
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null);
/**
* Return the mime-type of this identicon.
*
* @return string
*/
public function getMimeType();
/**
* Return the color of the created identicon.
*
* @return array
*/
public function getColor();
}

View File

@@ -0,0 +1,98 @@
<?php
namespace Identicon\Generator;
use Exception;
use ImagickDraw;
use ImagickPixel;
/**
* @author Francis Chuang <francis.chuang@gmail.com>
*/
class ImageMagickGenerator extends BaseGenerator implements GeneratorInterface
{
/**
* ImageMagickGenerator constructor.
*
* @throws \Exception
*/
public function __construct()
{
if (!extension_loaded('imagick')) {
throw new Exception('ImageMagick does not appear to be avaliable in your PHP installation. Please try another generator');
}
}
/**
* @return string
*/
public function getMimeType()
{
return 'image/png';
}
/**
* @return $this
*/
private function generateImage()
{
$this->generatedImage = new \Imagick();
$rgbBackgroundColor = $this->getBackgroundColor();
if (null === $rgbBackgroundColor) {
$background = 'none';
} else {
$background = new ImagickPixel("rgb($rgbBackgroundColor[0],$rgbBackgroundColor[1],$rgbBackgroundColor[2])");
}
$this->generatedImage->newImage($this->pixelRatio * 5, $this->pixelRatio * 5, $background, 'png');
// prepare color
$rgbColor = $this->getColor();
$color = new ImagickPixel("rgb($rgbColor[0],$rgbColor[1],$rgbColor[2])");
$draw = new ImagickDraw();
$draw->setFillColor($color);
// draw the content
foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
foreach ($lineValue as $colKey => $colValue) {
if (true === $colValue) {
$draw->rectangle($colKey * $this->pixelRatio, $lineKey * $this->pixelRatio, ($colKey + 1) * $this->pixelRatio, ($lineKey + 1) * $this->pixelRatio);
}
}
}
$this->generatedImage->drawImage($draw);
return $this;
}
/**
* {@inheritdoc}
*/
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
{
ob_start();
echo $this->getImageResource($string, $size, $color, $backgroundColor);
$imageData = ob_get_contents();
ob_end_clean();
return $imageData;
}
/**
* {@inheritdoc}
*/
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
{
$this
->setString($string)
->setSize($size)
->setColor($color)
->setBackgroundColor($backgroundColor)
->generateImage();
return $this->generatedImage;
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace Identicon\Generator;
/**
* @author Grummfy <grummfy@gmail.com>
*/
class SvgGenerator extends BaseGenerator implements GeneratorInterface
{
/**
* {@inheritdoc}
*/
public function getMimeType()
{
return 'image/svg+xml';
}
/**
* {@inheritdoc}
*/
public function getImageBinaryData($string, $size = null, $color = null, $backgroundColor = null)
{
return $this->getImageResource($string, $size, $color, $backgroundColor);
}
/**
* {@inheritdoc}
*/
public function getImageResource($string, $size = null, $color = null, $backgroundColor = null)
{
$this
->setString($string)
->setSize($size)
->setColor($color)
->setBackgroundColor($backgroundColor)
->_generateImage();
return $this->generatedImage;
}
/**
* @return $this
*/
protected function _generateImage()
{
// prepare image
$w = $this->getPixelRatio() * 5;
$h = $this->getPixelRatio() * 5;
$svg = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'.$w.'" height="'.$h.'">';
$backgroundColor = '#FFFFFF';
$rgbBackgroundColor = $this->getBackgroundColor();
if (!is_null($rgbBackgroundColor)) {
$backgroundColor = $this->_toUnderstandableColor($rgbBackgroundColor);
}
$svg .= '<rect width="'.$w.'" height="'.$h.'" style="fill:'.$backgroundColor.';stroke-width:1;stroke:'.$backgroundColor.'"/>';
$rgbColor = $this->_toUnderstandableColor($this->getColor());
// draw content
foreach ($this->getArrayOfSquare() as $lineKey => $lineValue) {
foreach ($lineValue as $colKey => $colValue) {
if (true === $colValue) {
$svg .= '<rect x="'.$colKey * $this->getPixelRatio().'" y="'.$lineKey * $this->getPixelRatio().'" width="'.($this->getPixelRatio()).'" height="'.$this->getPixelRatio().'" style="fill:'.$rgbColor.';stroke-width:0;"/>';
}
}
}
$svg .= '</svg>';
$this->generatedImage = $svg;
return $this;
}
/**
* @param array|string $color
*
* @return string
*/
protected function _toUnderstandableColor($color)
{
if (is_array($color)) {
return 'rgb('.implode(', ', $color).')';
}
return $color;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace Identicon;
use Identicon\Generator\GdGenerator;
use Identicon\Generator\GeneratorInterface;
/**
* @author Benjamin Laugueux <benjamin@yzalis.com>
*/
class Identicon
{
/**
* @var \Identicon\Generator\GeneratorInterface
*/
private $generator;
/**
* Identicon constructor.
*
* @param \Identicon\Generator\GeneratorInterface|null $generator
*/
public function __construct($generator = null)
{
if (null === $generator) {
$this->generator = new GdGenerator();
} else {
$this->generator = $generator;
}
}
/**
* Set the image generator.
*
* @param \Identicon\Generator\GeneratorInterface $generator
*
* @return $this
*/
public function setGenerator(GeneratorInterface $generator)
{
$this->generator = $generator;
return $this;
}
/**
* Display an Identicon image.
*
* @param string $string
* @param int $size
* @param string $color
* @param string $backgroundColor
*/
public function displayImage($string, $size = 64, $color = null, $backgroundColor = null)
{
header('Content-Type: '.$this->generator->getMimeType());
echo $this->getImageData($string, $size, $color, $backgroundColor);
}
/**
* Get an Identicon PNG image data.
*
* @param string $string
* @param int $size
* @param string $color
* @param string $backgroundColor
*
* @return string
*/
public function getImageData($string, $size = 64, $color = null, $backgroundColor = null)
{
return $this->generator->getImageBinaryData($string, $size, $color, $backgroundColor);
}
/**
* Get an Identicon PNG image resource.
*
* @param string $string
* @param int $size
* @param string $color
* @param string $backgroundColor
*
* @return string
*/
public function getImageResource($string, $size = 64, $color = null, $backgroundColor = null)
{
return $this->generator->getImageResource($string, $size, $color, $backgroundColor);
}
/**
* Get an Identicon PNG image data as base 64 encoded.
*
* @param string $string
* @param int $size
* @param string $color
* @param string $backgroundColor
*
* @return string
*/
public function getImageDataUri($string, $size = 64, $color = null, $backgroundColor = null)
{
return sprintf('data:%s;base64,%s', $this->generator->getMimeType(), base64_encode($this->getImageData($string, $size, $color, $backgroundColor)));
}
/**
* Get the color of the Identicon
*
* Returns an array with RGB values of the Identicon's color. Colors may be NULL if no image has been generated
* so far (e.g., when calling the method on a new Identicon()).
*
* @return array
*/
public function getColor()
{
$colors = $this->generator->getColor();
return [
"r" => $colors[0],
"g" => $colors[1],
"b" => $colors[2]
];
}
}

View File

@@ -0,0 +1,244 @@
<?php
/**
* OWASP PureCaptcha
* Generates simple CAPTCHAs without requiring any third party library.
* @version 1.1
*/
namespace OWASP;
class PureCaptcha
{
protected $charWidth=6;
protected $charHeight=13;
protected $chars="2346789ABDHKLMNPRTWXYZ"; //do not modify!
protected $ascii="eNrtW0FuwyAQ/NIANjbOa3LMG6r8vWrrSonkkt0xDWDvIcGXEYHM7O6s4b
p4v3zcFlyuiwu/T/Hn4ba4h49fx7COwzqO3+P964tF+i0kViRWJLaQ4RGJF3PiETnQyFGzzidklC
A31znlkMjt7Zzb2+y/kjQ79MwEbEH/+kOftsjxLHKehK7cbYT/qu0KNBcHmosjzcVI63yi1znTyE
RHCAd6oZX479uL/yIuBho5SFgs5z8kc0YaOUm4iJfxXxVbEr23Djy0Dv+D1T/iXzvSyKjhop7/r+
M/LP5v83+w+qdM/aOP/6LKqXr9r0Lm+Z+H1uH/aPGf87/Y7X8t/jfA/2j8b43/MP/7Pv5P7frfbH
YPtKOszn8VcqIrxPrxXwetw/+5n/pfz395/Y8L2//HG+sf1ZwzjUw0Utf/byH+J+N/bf7L47/xv/
z7L7QnAFG+7NgAgDYAnRngHgog50wAXAcU9BtgaBqDEz3nTK/zVALw/QiAbwHxFvg/X4G53QJwuw
VgR4CCZYBc9wh0BpD3gKDuAVkJVE4Aw5EEgGICcF0JgG+C4vQCGI/QBTqWCT5cCSSDVhJANAH0Kw
AzwfsFMB3xHBzEJliFLHwPoMY5OBEy0cj8OWi0mAFmM8G1D0LwHgC0CYbaBIvaBB1mgHRuAajOEB
W+CcNnANGcM408UwnkYQLoTwBWApUTgN0F7vAuDNRdILsLvymA+ycgmwSd";
function __construct()
{
$this->ascii=unserialize(gzuncompress(base64_decode(
preg_replace('/\s+/', '', $this->ascii))));
$this->text=$this->randomText();
}
/**
* Contains the captcha text.
* @var string
*/
public $text;
/**
* Generates random text for use in captcha
* @param integer $length
* @return string
*/
protected function randomText($length=5)
{
$res="";
for ($i=0;$i<$length;++$i)
$res.=$this->chars[mt_rand(0,strlen($this->chars)-1)];
return $res;
}
/**
* returns the index of a char in $chars array
*/
protected function asciiEntry($char)
{
for ($i=0;$i<strlen($this->chars);++$i)
if ($this->chars[$i]==$char) return $i;
return -1;
}
/**
* converts a text to a bitmap
* which is a 2D array of ones and zeroes denoting the text
*/
protected function textBitmap($text,$spacing=2)
{
$width=$this->charWidth;
$height=$this->charHeight;
$result=array();
$baseY=$baseX=0;
for ($index=0;$index<strlen($text);++$index)
{
for ($j=0;$j<$height;++$j)
{
for ($i=0;$i<$width;++$i)
$result[$baseY+$j][$baseX+$i]=
1-$this->ascii[$this->asciiEntry($text[$index])][$j][$i];
for ($i=0;$i<$spacing;++$i)
$result[$baseY+$j][$baseX+$width+$i]=0;
}
$baseX+=$width+$spacing;
}
return $result;
}
/**
* displays a bitmap string on the browser screen
*/
protected function displayBitmap($bitmap)
{
header("Content-Type: image/bmp");
echo $this->bitmap2bmp($bitmap);
}
protected function inlineBitmap($bitmap)
{
return base64_encode($this->bitmap2bmp($bitmap));
}
/**
* generates a monochrome BMP file
* a bitmap needs to be sent to this function
* i.e a 2D array with every element being either 1 or 0
* @param integer $width
* @param integer $height
* @param array $bitmap
* @return string
*/
protected function bitmap2bmp($bitmap)
{
$width=count($bitmap[0]);
$height=count($bitmap);
$bytemap=$this->bitmap2bytemap($bitmap);
$rowSize=floor(($width+31)/32)*4;
$size=$rowSize*$height + 62; //62 metadata size
#bitmap header
$data= "BM"; //header
$data.= (pack('V',$size)); //bitmap size ,4 bytes unsigned little endian
$data.= "RRRR";
$data.= (pack('V',14+40+8)); //bitmap data start offset ,
//4 bytes unsigned little endian, 14 forced, 40 header, 8 colors
#info header
$data.= pack('V',40); //bitmap header size (min 40),
//4 bytes unsigned little-endian
$data.= (pack('V',$width)); //bitmap width , 4 bytes signed integer
$data.= (pack('V',$height)); //bitmap height , 4 bytes signed integer
$data.= (pack('v',1)); //number of colored plains , 2 bytes
$data.= (pack('v',1)); //color depth , 2 bytes
$data.= (pack('V',0)); //compression algorithm , 4 bytes (0=none, RGB)
$data.= (pack('V',0)); //size of raw data, 0 is fine for no compression
$data.= (pack('V',11808)); //horizontal resolution (dpi), 4 bytes
$data.= (pack('V',11808)); //vertical resolution (dpi), 4 bytes
$data.= (pack('V',0)); //number of colors in pallette (0 = all), 4 bytes
$data.= (pack('V',0)); //number of important colors (0 = all), 4 bytes
#color palette
$data.= (pack('V',0x00FFFFFF)); //next color, white
$data.= (pack('V',0)); //first color, black
for ($j=$height-1;$j>=0;--$j)
for ($i=0;$i<$rowSize/4;++$i)
for ($k=0;$k<4;++$k)
if (isset($bytemap[$j][$i*4+$k]))
$data.= pack('C',$bytemap[$j][$i*4+$k]);
else
$data.= pack('C',0);
return $data;
}
/**
* Converts a bitmap to a bytemap, which is necessary for outputting it
*
*/
protected function bitmap2bytemap($bitmap)
{
$width=count($bitmap[0]);
$height=count($bitmap);
$bytemap=array();
for ($j=0;$j<$height;++$j)
{
for ($i=0;$i<$width/8;++$i)
{
$bitstring="";
for ($k=0;$k<8;++$k)
if (isset($bitmap[$j][$i*8+$k]))
$bitstring.=$bitmap[$j][$i*8+$k];
else
$bitstring.="0";
$bytemap[$j][]=bindec($bitstring);
}
}
return $bytemap;
}
/**
* rotates a bitmap, returning new dimensions with the bitmap
* return bitmap
*/
protected function rotateBitmap($bitmap, $degree)
{
$c=cos(deg2rad($degree));
$s=sin(deg2rad($degree));
$width=count($bitmap[0]);
$height=count($bitmap);
$newHeight=round(abs($width*$s)+abs($height*$c));
$newWidth=round(abs($width*$c) + abs($height*$s))+1;
$x0 = $width/2 - $c*$newWidth/2 - $s*$newHeight/2;
$y0 = $height/2 - $c*$newHeight/2 + $s*$newWidth/2;
$result=array_fill(0, $newHeight, array_fill(0, $newWidth, 0));
for ($j=0;$j<$newHeight;++$j)
for ($i=1;$i<$newWidth;++$i)
{
$y=(int)(-$s*$i+$c*$j+$y0);
$x=(int)($c*$i+$s*$j+$x0);
if (isset($bitmap[$y][$x]))
$result[$j][$i]=$bitmap[$y][$x];
}
return $result;
}
/**
* scales a bitmap to be bigger
*/
protected function scaleBitmap($bitmap,$scaleX,$scaleY)
{
$width=count($bitmap[0]);
$height=count($bitmap);
$newHeight=$height*$scaleY;
$newWidth=$width*$scaleX;
$result=array_fill(0, $newHeight, array_fill(0, $newWidth, 0));
for ($j=0;$j<$newHeight;++$j)
for ($i=0;$i<$newWidth;++$i)
$result[$j][$i]=$bitmap[(int)($j/$scaleY)]
[(int)($i/$scaleX)];
return $result;
}
/**
* adds random noise to the captcha
*/
protected function distort($bitmap,$noisePercent=5)
{
for ($j=0;$j<count($bitmap);++$j)
for ($i=0;$i<count($bitmap[0]);++$i)
if (isset($bitmap[$j][$i]) && mt_rand()%100<$noisePercent)
$bitmap[$j][$i]=1;
return $bitmap;
}
/**
* draw a captcha to the screen, returning its value
*/
public function show($distort=true,$scale=2.3)
{
$bitmap=$this->textBitmap($this->text);
$degree=mt_rand(2,4);
if (mt_rand()%100<50)
$degree=-$degree;
$bitmap=$this->rotateBitmap($bitmap,$degree);
$bitmap=$this->scaleBitmap($bitmap,$scale,$scale);
if ($distort) $bitmap=$this->distort($bitmap);
return $this->inlineBitmap($bitmap);
}
}

516
includes/PIT/Zip.php Normal file
View File

@@ -0,0 +1,516 @@
<?php
/*
| ZIP A ZipArchive and Plain PKZIP PHP Helper
| @file ./PIT/ZIP.php
| @author SamBrishes <sam@pytes.net>
| @version 0.2.1 [0.2.1] - Beta
|
| @license X11 / MIT License
| @copyright Copyright © 2015 - 2019 SamBrishes, pytesNET <info@pytes.net>
*/
/*
| The following websites contains all required informations, which were unavoidable for the
| creation of this class:
|
| - https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
| - https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
| - https://php.net/manual/class.ziparchive.php
*/
namespace PIT;
class Zip{
const FLAGS = "\x00\x00";
const VERSION = "\x14\x00";
const SIGNATURE = "\x50\x4b";
const COMPRESSION = "\x08\x00";
/*
| SETTINGs
*/
public $zipArchive = false;
public $compression = 6;
/*
| ZIP ARCHIVE
*/
private $zipFilename;
private $zipInstance;
/*
| FALLBACK
*/
private $offset = 0;
private $headers = array();
private $central = array();
private $counter = 0;
/*
| CONSTRUCTOR
| @since 0.2.0
|
| @param bool TRUE to check and use ZipArchive if available,
| FALSE to use the PKZIP PHP compression per default.
| @return int The compression level between -1 and 9.
*/
public function __construct($ziparchive = false, $compression = 6){
if($ziparchive){
$this->zipArchive = class_exists("ZipArchive", false);
$this->zipInstance = ($this->zipArchive)? new ZipArchive(): false;
}
if($this->zipArchive){
$this->zipFilename = tempnam(sys_get_temp_dir(), "tzp") . ".zip";
$this->zipInstance->open($this->zipFilename, ZipArchive::CREATE);
}
$this->compression = ($compression >= -1 && $compression <= 9)? $compression: 6;
}
/*
| DESTRUCTOR
| @since 0.2.0
*/
public function __destruct(){
$this->clear(false);
}
/*
| HELPER :: CONVERT UNIX TO DOS TIME
| @since 0.2.1
|
| @param int The respective timestamp as INTEGER.
*/
protected function msDOSTime($time){
$array = getdate((is_int($time) && $time > 0)? $time: time());
if($array["year"] < 1980 || $array["year"] > 2107){
$array = getdate(time());
}
// Return as DEC
return (
(($array["year"]-1980 << 25)) |
(($array["mon"] << 21)) |
(($array["mday"] << 16)) |
(($array["hours"] << 11)) |
(($array["minutes"] << 5)) |
(($array["seconds"] >> 1))
);
}
/*
| ADD A FILE
| @since 0.1.0
| @update 0.2.1
|
| @param string The relative or absolute filepath or the respective file content.
| @param string The local path within the archive file.
| @param int The timestamp to use.
| @param string The optional file comment or just an empty string.
|
| @return bool TRUE on success, FALSE on failure.
*/
public function addFile($data, $path, $time = 0, $comment = ""){
if((!is_string($data) && !is_numeric($data)) || !is_string($path)){
return false;
}
// Sanitize Data
if(is_string($data) && file_exists($data) && is_file($data)){
$data = file_get_contents($data);
}
$path = trim(str_replace("\\", "/", $path), "/");
$time = $this->msDOSTime($time);
// Zip Archive
if($this->zipArchive){
return $this->zipInstance->addFromString($path, $data);
}
// Fallback
$crcval = crc32($data);
$length = strlen($data);
if(version_compare(PHP_VERSION, "5.4.0", ">=")){
$gzcval = gzcompress($data, $this->compression, ZLIB_ENCODING_DEFLATE);
} else {
$gzcval = gzcompress($data, $this->compression);
}
$gzcval = substr($gzcval, 2, strlen($gzcval) - 6); // Fix CRC-32 Bug
$gzclen = strlen($gzcval);
/*
| LOCAL FILE HEADER
| 01 SIGNATURE
| 02 Version needed to extract this archive.
| 03 General purpose bit flag.
| 04 Compression method.
| 05 Last modification DOS datetime.
| 06 CRC32 value.
| 07 Compressed Filesize.
| 08 Uncompressed Filesize.
| 09 Length of the filename inside the archive.
| 10 Length of the extra fields.
| 11 The relative path / filename inside the archive.
| 12 The main file data value.
*/
$this->headers[] =
self::SIGNATURE . "\x03\x04" .
self::VERSION .
self::FLAGS .
self::COMPRESSION .
pack("V", $time) .
pack("V", $crcval) .
pack("V", $gzclen) .
pack("V", $length) .
pack("v", strlen($path)) .
pack("v", 0) .
$path .
$gzcval;
/*
| CENTRAL DIRECTORY RECORD
| 01 SIGNATURE
| 02 MadeBy Version numbers.
| 03 Version needed to extract this archive.
| 04 General purpose bit flag.
| 05 Compression method.
| 06 Last modification DOS datetime.
| 07 CRC32 value.
| 08 Compressed Filesize.
| 09 Uncompressed Filesize.
| 10 Length of the filename inside the archive.
| 11 Length of the extra fields.
| 12 Length of the file comment.
| 13 The disk number where the file exists.
| 14 Internal file attributes.
| 15 External file attributes.
| 16 Offset of the local file header.
| 17 The relative path / filename inside the archive.
| 18 The file comment.
*/
$this->central[] =
self::SIGNATURE . "\x01\x02" .
"\x00\x00" .
self::VERSION .
self::FLAGS .
self::COMPRESSION .
pack("V", $time) .
pack("V", $crcval) .
pack("V", $gzclen) .
pack("V", $length) .
pack("v", strlen($path)) .
pack("v", 0) .
pack("v", strlen($comment)) .
pack("v", 0) .
pack("v", 0) .
pack("V", 32) .
pack("V", $this->offset) .
$path .
$comment;
// Count Offset and Return
$this->offset += strlen($this->headers[count($this->headers)-1]);
return true;
}
/*
| ADD MULTIPLE FILES
| @since 0.2.0
| @update 0.2.1
|
| @param array Multiple 'local/file/path' => "filepath/or/filecontent" ARRAY pairs.
| @param int The timestamp to use for all files.
| @param string The optional comment or just an empty string for alle respective files.
|
| @return int The number of successfully added elements / files.
*/
public function addFiles($array, $time = 0, $comment = ""){
if(!is_array($array)){
return false;
}
foreach($array AS $path => &$data){
$data = $this->addFile($data, $path);
}
return array_filter(array_values($array));
}
/*
| ADD FOLDER
| @since 0.2.0
|
| @param string The path to the folder, which should be zipped.
| @param string The local path within the zip file.
| @param bool TRUE to zip recursive and include all sub directories,
| FALSE to just zip all files within the $path folder.
| @param bool TRUE to include empty folders on recursive zips.
| FALSE to skip empty folders.
|
| @return multi The number as INT of successfully added elements / files,
| FALSE on failure.
*/
public function addFolder($path, $local = "/", $recursive = false, $empty = false){
if(!file_exists($path) || !is_dir($path)){
return false;
}
// Chech Path
$path = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, realpath($path));
if(strpos($path, DIRECTORY_SEPARATOR) !== strlen($path)-1){
$path .= DIRECTORY_SEPARATOR;
}
// Check Local
if(!is_string($local)){
$local = "";
}
$local = trim(str_replace("\\", "/", $local), "/") . "/";
// Start Flow
$this->counter = 0;
$this->addFolderFlow($path, "", $local, !!$recursive, !!$empty);
return $this->counter;
}
/*
| HELPER :: ADD FOLDER LOOP
| @since 0.2.0
|
| @param string The base path to the folder, which should be zipped.
| @param string The further path, within the base path, on recursive calls.
| @param string The local path within the zip file.
| @param bool TRUE to zip recursive and include all sub directories,
| FALSE to just zip all files within the $path folder.
| @param bool TRUE to include empty folders on recursive zips.
| FALSE to skip empty folders.
|
| @return int The number of successfully added elements / files.
*/
private function addFolderFlow($base, $path = "", $local = "", $recursive = false, $empty = false){
$path = str_replace(array("/", "\\"), DIRECTORY_SEPARATOR, $path);
$path = trim($path, DIRECTORY_SEPARATOR);
if(!empty($path)){
$path .= DIRECTORY_SEPARATOR;
}
$count = 0;
$handle = opendir($base . $path);
while(($file = readdir($handle)) !== false){
if(in_array($file, array(".", ".."))){
continue;
}
if(is_dir($base . $path . $file)){
if($recursive){
$count = $this->addFolderFlow($base, $path . $file, $local, $recursive, $empty);
if($count == 0 && $empty){
$this->addEmptyFolder($local . $path . $file);
}
}
continue;
}
if(is_file($base . $path . $file)){
if($this->addFile($base . $path . $file, $local . $path . $file)){
$count++;
$this->counter++;
}
}
}
closedir($handle);
return $count;
}
/*
| ADD EMPTY FOLDER
| @since 0.2.0
| @update 0.2.1
|
| @param string The local path structure within the zip file.
| @param int The timestamp to use.
| @param string The optional file comment or just an empty string.
|
| @return bool TRUE on success, FALSE on failure.
*/
public function addEmptyFolder($path, $time = 0, $comment = ""){
$path = trim(str_replace("\\", "/", $path), "/") . "/";
$time = $this->msDOSTime($time);
// ZipArchive
if($this->zipArchive){
return $this->zipInstance->addEmptyDir($path);
}
// Add Header
$this->headers[] =
self::SIGNATURE . "\x03\x04" .
self::VERSION .
self::FLAGS .
"\x00\x00" .
pack("V", $time) .
pack("V", 0) .
pack("V", 0) .
pack("V", 0) .
pack("v", strlen($path)) .
pack("v", 0) .
$path .
"";
// Add Central
$this->central[] =
self::SIGNATURE . "\x01\x02" .
"\x14\x03" .
self::VERSION .
self::FLAGS .
"\x00\x00" .
pack("V", $time) .
pack("V", 0) .
pack("V", 0) .
pack("V", 0) .
pack("v", strlen($path)) .
pack("v", 0) .
pack("v", strlen($comment)) .
pack("v", 0) .
pack("v", 0) .
"\x00\x00\xFF\x41" .
pack("V", $this->offset) .
$path .
$comment;
// Count Offset and Return
$this->offset += strlen($this->headers[count($this->headers)-1]);
return true;
}
/*
| CLEAR DATA STRINGs
| @since 0.1.0
| @update 0.2.0
*/
public function clear($new = true){
if($this->zipArchive){
if(is_a($this->zipInstance, "ZipArchive")){
$this->zipInstance->close();
}
if(strpos($this->zipFilename, sys_get_temp_dir()) === 0 && file_exists($this->zipFilename)){
@unlink($this->zipFilename);
}
if($new){
$this->zipFilename = "./temp-".time().".zip";
$this->zipInstance = new ZipArchive();
$this->zipInstance->open($this->zipFilename, ZipArchive::CREATE);
}
}
$this->offset = 0;
$this->headers = array();
$this->central = array();
return true;
}
/*
| DUMBS OUT THE FILE
| @since 0.1.0
| @update 0.2.1
*/
public function file(){
$comment = "PKZipped with https://github.com/SamBrishes/FoxCMS/tree/helpers/zip";
// ZipArchive
if($this->zipArchive){
$this->zipInstance->setArchiveComment($comment);
$this->zipInstance->close();
$content = file_get_contents($this->zipFilename);
$this->zipInstance = new ZipArchive();
$this->zipInstance->open($this->zipFilename);
return $content;
}
// Fallback
$headers = implode("", $this->headers);
$central = implode("", $this->central);
/*
| RETURN
| 01 The file header items.
| 02 The central directory items.
| 03 The signature for the end of the central directory record.
| 04 The number of this disk / part.
| 05 The number of the disk / part where the central directory starts.
| 06 The number of central directoy entries on this disk.
| 07 Total number of entries on this disk / part.
| 08 Total number of entries in general.
| 09 Length of the central directory.
| 10 Offset where the central directory starts.
| 11 The length of the following comment field.
| 12 The archive comment.
*/
return $headers . $central .
self::SIGNATURE . "\x05\x06" .
"\x00" .
"\x00" .
"\x00" .
"\x00" .
pack("v", count($this->central)) .
pack("v", count($this->central)) .
pack("V", strlen($central)) .
pack("V", strlen($headers)) .
pack("v", strlen($comment)) .
$comment;
}
/*
| STORE THE ZIP FILE
| @since 0.1.0
| @update 0.2.0
|
| @param string The filename with the respective path to store the archive.
| @param bool TRUE to overwrite existing archives, FALSE to do it not.
|
| @return bool TRUE on success, FALSE on failure.
*/
public function save($filename = "archive.zip", $overwrite = false){
if(file_exists($filename) && !$overwrite){
return false;
}
// Zip Archive
if($this->zipArchive){
if(is_a($this->zipInstance, "ZipArchive")){
$this->zipInstance->close();
}
if(@file_put_contents($filename, file_get_contents($this->zipFilename))){
@unlink($this->zipFilename);
$this->zipFilename = $filename;
$this->zipInstance = new ZipArchive();
return $this->zipInstance->open($this->zipFilename);
}
return false;
}
// Fallback
return @file_put_contents($filename, $this->file()) !== false;
}
/*
| DOWNLOAD THE FILE
| @since 0.1.0
| @update 0.2.0
|
| @param string The filename for the archiv.
| @param bool TRUE to exit after the execution, FALSE to do it not.
|
| @return void
*/
public function download($filename = "archive.zip", $exit = false){
$file = $this->file();
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/zip");
header("Content-Disposition: attachment; filename={$filename};" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . strlen($file));
print ($file);
if($exit){
die();
}
}
}

23
includes/autoload.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
/*
| Snicker The first native FlatFile Comment Plugin 4 Bludit
| @file ./includes/autoload.php
| @author SamBrishes <sam@pytes.net>
| @version 0.1.2 [0.1.0] - Alpha
|
| @website https://github.com/pytesNET/snicker
| @license X11 / MIT License
| @copyright Copyright © 2019 SamBrishes, pytesNET <info@pytes.net>
*/
spl_autoload_register(function($class){
foreach(array("Gregwar", "Identicon", "PIT", "OWASP") AS $allowed){
if(strpos($class, $allowed) !== 0){
continue;
}
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR;
$class = str_replace("\\", DIRECTORY_SEPARATOR, $class);
require_once $class . ".php";
}
return false;
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

205
includes/js/identicon.js Normal file
View File

@@ -0,0 +1,205 @@
/**
* Identicon.js 2.3.3
* http://github.com/stewartlord/identicon.js
*
* PNGLib required for PNG output
* http://www.xarg.org/download/pnglib.js
*
* Copyright 2018, Stewart Lord
* Released under the BSD license
* http://www.opensource.org/licenses/bsd-license.php
*/
(function() {
var PNGlib;
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
PNGlib = require('./pnglib');
} else {
PNGlib = window.PNGlib;
}
var Identicon = function(hash, options){
if (typeof(hash) !== 'string' || hash.length < 15) {
throw 'A hash of at least 15 characters is required.';
}
this.defaults = {
background: [240, 240, 240, 255],
margin: 0.08,
size: 64,
saturation: 0.7,
brightness: 0.5,
format: 'png'
};
this.options = typeof(options) === 'object' ? options : this.defaults;
// backward compatibility with old constructor (hash, size, margin)
if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; }
if (arguments[2]) { this.options.margin = arguments[2]; }
this.hash = hash
this.background = this.options.background || this.defaults.background;
this.size = this.options.size || this.defaults.size;
this.format = this.options.format || this.defaults.format;
this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin;
// foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness
var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff;
var saturation = this.options.saturation || this.defaults.saturation;
var brightness = this.options.brightness || this.defaults.brightness;
this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness);
};
Identicon.prototype = {
background: null,
foreground: null,
hash: null,
margin: null,
size: null,
format: null,
image: function(){
return this.isSvg()
? new Svg(this.size, this.foreground, this.background)
: new PNGlib(this.size, this.size, 256);
},
render: function(){
var image = this.image(),
size = this.size,
baseMargin = Math.floor(size * this.margin),
cell = Math.floor((size - (baseMargin * 2)) / 5),
margin = Math.floor((size - cell * 5) / 2),
bg = image.color.apply(image, this.background),
fg = image.color.apply(image, this.foreground);
// the first 15 characters of the hash control the pixels (even/odd)
// they are drawn down the middle first, then mirrored outwards
var i, color;
for (i = 0; i < 15; i++) {
color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg;
if (i < 5) {
this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image);
} else if (i < 10) {
this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
} else if (i < 15) {
this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
}
}
return image;
},
rectangle: function(x, y, w, h, color, image){
if (this.isSvg()) {
image.rectangles.push({x: x, y: y, w: w, h: h, color: color});
} else {
var i, j;
for (i = x; i < x + w; i++) {
for (j = y; j < y + h; j++) {
image.buffer[image.index(i, j)] = color;
}
}
}
},
// adapted from: https://gist.github.com/aemkei/1325937
hsl2rgb: function(h, s, b){
h *= 6;
s = [
b += s *= b < .5 ? b : 1 - b,
b - h % 1 * s * 2,
b -= s *= 2,
b,
b + h % 1 * s,
b + s
];
return[
s[ ~~h % 6 ] * 255, // red
s[ (h|16) % 6 ] * 255, // green
s[ (h|8) % 6 ] * 255 // blue
];
},
toString: function(raw){
// backward compatibility with old toString, default to base64
if (raw) {
return this.render().getDump();
} else {
return this.render().getBase64();
}
},
isSvg: function(){
return this.format.match(/svg/i)
}
};
var Svg = function(size, foreground, background){
this.size = size;
this.foreground = this.color.apply(this, foreground);
this.background = this.color.apply(this, background);
this.rectangles = [];
};
Svg.prototype = {
size: null,
foreground: null,
background: null,
rectangles: null,
color: function(r, g, b, a){
var values = [r, g, b].map(Math.round);
values.push((a >= 0) && (a <= 255) ? a/255 : 1);
return 'rgba(' + values.join(',') + ')';
},
getDump: function(){
var i,
xml,
rect,
fg = this.foreground,
bg = this.background,
stroke = this.size * 0.005;
xml = "<svg xmlns='http://www.w3.org/2000/svg'"
+ " width='" + this.size + "' height='" + this.size + "'"
+ " style='background-color:" + bg + ";'>"
+ "<g style='fill:" + fg + "; stroke:" + fg + "; stroke-width:" + stroke + ";'>";
for (i = 0; i < this.rectangles.length; i++) {
rect = this.rectangles[i];
if (rect.color == bg) continue;
xml += "<rect "
+ " x='" + rect.x + "'"
+ " y='" + rect.y + "'"
+ " width='" + rect.w + "'"
+ " height='" + rect.h + "'"
+ "/>";
}
xml += "</g></svg>"
return xml;
},
getBase64: function(){
if ('function' === typeof btoa) {
return btoa(this.getDump());
} else if (Buffer) {
return new Buffer(this.getDump(), 'binary').toString('base64');
} else {
throw 'Cannot generate base64 output';
}
}
};
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = Identicon;
} else {
window.Identicon = Identicon;
}
})();

214
includes/js/pnglib.js Normal file
View File

@@ -0,0 +1,214 @@
/**
* A handy class to calculate color values.
*
* @version 1.0
* @author Robert Eisele <robert@xarg.org>
* @copyright Copyright (c) 2010, Robert Eisele
* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
*
*/
(function() {
// helper functions for that ctx
function write(buffer, offs) {
for (var i = 2; i < arguments.length; i++) {
for (var j = 0; j < arguments[i].length; j++) {
buffer[offs++] = arguments[i].charAt(j);
}
}
}
function byte2(w) {
return String.fromCharCode((w >> 8) & 255, w & 255);
}
function byte4(w) {
return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
}
function byte2lsb(w) {
return String.fromCharCode(w & 255, (w >> 8) & 255);
}
// modified from original source to support NPM
var PNGlib = function(width,height,depth) {
this.width = width;
this.height = height;
this.depth = depth;
// pixel data and row filter identifier size
this.pix_size = height * (width + 1);
// deflate header, pix_size, block headers, adler32 checksum
this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
// offsets and sizes of Png chunks
this.ihdr_offs = 0; // IHDR offset and size
this.ihdr_size = 4 + 4 + 13 + 4;
this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size
this.plte_size = 4 + 4 + 3 * depth + 4;
this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size
this.trns_size = 4 + 4 + depth + 4;
this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size
this.idat_size = 4 + 4 + this.data_size + 4;
this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size
this.iend_size = 4 + 4 + 4;
this.buffer_size = this.iend_offs + this.iend_size; // total PNG size
this.buffer = new Array();
this.palette = new Object();
this.pindex = 0;
var _crc32 = new Array();
// initialize buffer with zero bytes
for (var i = 0; i < this.buffer_size; i++) {
this.buffer[i] = "\x00";
}
// initialize non-zero elements
write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
// initialize deflate header
var header = ((8 + (7 << 4)) << 8) | (3 << 6);
header+= 31 - (header % 31);
write(this.buffer, this.idat_offs + 8, byte2(header));
// initialize deflate block headers
for (var i = 0; (i << 16) - 1 < this.pix_size; i++) {
var size, bits;
if (i + 0xffff < this.pix_size) {
size = 0xffff;
bits = "\x00";
} else {
size = this.pix_size - (i << 16) - i;
bits = "\x01";
}
write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
}
/* Create crc32 lookup table */
for (var i = 0; i < 256; i++) {
var c = i;
for (var j = 0; j < 8; j++) {
if (c & 1) {
c = -306674912 ^ ((c >> 1) & 0x7fffffff);
} else {
c = (c >> 1) & 0x7fffffff;
}
}
_crc32[i] = c;
}
// compute the index into a png for a given pixel
this.index = function(x,y) {
var i = y * (this.width + 1) + x + 1;
var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
return j;
}
// convert a color and build up the palette
this.color = function(red, green, blue, alpha) {
alpha = alpha >= 0 ? alpha : 255;
var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
if (typeof this.palette[color] == "undefined") {
if (this.pindex == this.depth) return "\x00";
var ndx = this.plte_offs + 8 + 3 * this.pindex;
this.buffer[ndx + 0] = String.fromCharCode(red);
this.buffer[ndx + 1] = String.fromCharCode(green);
this.buffer[ndx + 2] = String.fromCharCode(blue);
this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
this.palette[color] = String.fromCharCode(this.pindex++);
}
return this.palette[color];
}
// output a PNG string, Base64 encoded
this.getBase64 = function() {
var s = this.getDump();
var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var c1, c2, c3, e1, e2, e3, e4;
var l = s.length;
var i = 0;
var r = "";
do {
c1 = s.charCodeAt(i);
e1 = c1 >> 2;
c2 = s.charCodeAt(i+1);
e2 = ((c1 & 3) << 4) | (c2 >> 4);
c3 = s.charCodeAt(i+2);
if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; }
r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
} while ((i+= 3) < l);
return r;
}
// output a PNG string
this.getDump = function() {
// compute adler32 of output pixels + row filter bytes
var BASE = 65521; /* largest prime smaller than 65536 */
var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
var s1 = 1;
var s2 = 0;
var n = NMAX;
for (var y = 0; y < this.height; y++) {
for (var x = -1; x < this.width; x++) {
s1+= this.buffer[this.index(x, y)].charCodeAt(0);
s2+= s1;
if ((n-= 1) == 0) {
s1%= BASE;
s2%= BASE;
n = NMAX;
}
}
}
s1%= BASE;
s2%= BASE;
write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
// compute crc32 of the PNG chunks
function crc32(png, offs, size) {
var crc = -1;
for (var i = 4; i < size-4; i += 1) {
crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
}
write(png, offs+size-4, byte4(crc ^ -1));
}
crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
crc32(this.buffer, this.plte_offs, this.plte_size);
crc32(this.buffer, this.trns_offs, this.trns_size);
crc32(this.buffer, this.idat_offs, this.idat_size);
crc32(this.buffer, this.iend_offs, this.iend_size);
// convert PNG to string
return "\x89PNG\r\n\x1a\n"+this.buffer.join('');
}
}
// modified from original source to support NPM
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
module.exports = PNGlib;
} else {
window.PNGlib = PNGlib;
}
})();