<?php
//+----------------------------------------------------------------------+
//| Stegger v0.6 |
//+----------------------------------------------------------------------+
//| Copyright (c) 2006 Warren Smith ( smythinc 'at' gmail 'dot' com ) |
//+----------------------------------------------------------------------+
//| This library is free software; you can redistribute it and/or modify |
//| it under the terms of the GNU Lesser General Public License as |
//| published by the Free Software Foundation; either version 2.1 of the |
//| License, or (at your option) any later version. |
//| |
//| This library is distributed in the hope that it will be useful, but |
//| WITHOUT ANY WARRANTY; without even the implied warranty of |
//| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
//| Lesser General Public License for more details. |
//| |
//| You should have received a copy of the GNU Lesser General Public |
//| License along with this library; if not, write to the Free Software |
//| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
//| USA |
//+----------------------------------------------------------------------+
//| Simple is good. |
//+----------------------------------------------------------------------+
//
/*
+----------------------------------------------------------------------+
| Package: Stegger v0.6 |
| Class : Stegger |
| Created: 03/08/2006 |
| Updated: 13/05/2008 |
+----------------------------------------------------------------------+
*/
/*-------------*/
/* C O N F I G */
/*-------------*/
// This is the public key (the one you give out) to encrypt or decrypt data with
define('STEGGER_PUB_KEY', 'Where will the children play?');
/*---------------*/
/* D E F I N E S */
/*---------------*/
//
/*-----------*/
/* C L A S S */
/*-----------*/
class Stegger {
/*-------------------*/
/* V A R I A B L E S */
/*-------------------*/
// Public Properties
/**
* boolean
*
* A flag to determine if we should be verbose with output or not
*/
var $Verbose = TRUE;
// Private Properties
/**
* boolean
*
* A flag to determine if we are using a command line interface or not
*/
var $CLI = FALSE;
// Private Properties
/**
* object
*
* This is an object representing the image
*/
var $Image;
/**
* object
*
* This is an object representing the main bit stream
*/
var $BitStream;
/**
* string
*
* This is a unique boundry made up of 1's and 0's
*/
var $BitBoundry;
/**
* array
*
* This is the secret data we are going to encode or have decoded
*/
var $RawData = array();
/*-------------------*/
/* F U N C T I O N S */
/*-------------------*/
/*
+------------------------------------------------------------------+
| Constructor |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Stegger(){
// Run forever
set_time_limit(0);
// Setup the environment
$this->SetEnvironment();
// Create the bit stream object
$this->BitStream = new BitStream();
}
// Public API Methods
/*
+------------------------------------------------------------------+
| Encodes the $secretData into an $imageFile and encrypt with $key |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Put($secretData, $imageFile, $key = '', $outputFile = ''){
// Get the start time
$StartTime = microtime(TRUE);
// Flush any previous bit streams
$this->BitStream->FlushStream();
// Tell the user we are loading the image
$this->Info('Loading image..');
// Attempt to load the image
$this->Image = new Image($imageFile);
// If we don't have an image
if ($this->Image->EOF()){
// Tell the user the problem
$this->FatalError('Could not load the supplied image');
} else {
// Tell the user what we are doing
$this->Info('Loading data..');
// If we can't load the data they provided
if (!$this->Input($secretData)){
// Meh, I hate all this usability stuff
$this->FatalError('Could not load the supplied data');
} else {
// Tell the user what we are doing
$this->Info('Encrypting data..');
// If we can't turn the data into an encrypted string
if (!$this->RawToString($key)){
// Tell the user we couldn't encrypt the data
$this->FatalError('Could not encrypt the loaded data');
} else {
// Tell the user what we are doing
$this->Info('Encoding data..');
// If we can't encode the data
if (!$this->StringToStream()){
// Tell the user about the error
$this->FatalError('Could not encode the loaded data');
} else {
// Tell the user what the next step is
$this->Info('Encoding image..');
// If we can't encode the image
if (!$this->StreamToPixels()){
// Tell the user there was a problem encoding the image
$this->FatalError('Could not encode the image');
} else {
// Tell the user what we are doing now
$this->Info('Saving image..');
// Output the image
$this->Image->Output($outputFile);
// As the kids say, wewt
$this->Success('Done in '.round(microtime(TRUE) - $StartTime).' seconds');
}
}
}
}
}
}
/*
+------------------------------------------------------------------+
| This will decode data from an image |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Get($imageFile, $key = '', $outputPath = ''){
// Get the start time
$StartTime = microtime(TRUE);
// Flush any previous bit streams
$this->BitStream->FlushStream();
// Tell the user we are loading the image
$this->Info('Loading image..');
// Attempt to load the image
$this->Image = new Image($imageFile);
// If we don't have an image
if ($this->Image->EOF()){
// Tell the user the problem
$this->FatalError('Could not load the supplied image');
} else {
// Tell the user we are about to read the image
$this->Info('Reading image..');
// Read the pixels into a bit stream
$this->PixelsToStream();
// If we don't have a bit stream
if ($this->BitStream->EOF()){
// Tell the user about the problems
$this->FatalError('No hidden data found in the image');
} else {
// Tell the user we are decoding the data
$this->Info('Decoding data..');
// If we can't decode the bit stream into a string
if (!$this->StreamToString()){
// Tell the user where it all went wrong
$this->FatalError('Could not decode the data');
} else {
// Tell the user that the next step is to decrypt and decompress
$this->Info('Decrypting data..');
// If we can't decrypt and/or decompress
if (!$this->StringToRaw($key)){
// Tell the user about the problem
$this->FatalError('Could not decrypt data');
} else {
// If we have a problem outputting data
if (!$this->Output($outputPath)){
// Fatal Error
$this->FatalError('Too many errors to continue');
} else {
// We are done
$this->Success('Done in '.round(microtime(TRUE) - $StartTime).' seconds');
}
}
}
}
}
}
// Input / Output Methods
/*
+------------------------------------------------------------------+
| This will load the data to encode into the image |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function Input($data){
// If the data looks like an array but NOT an uploaded file
if (is_array($data) && !isset($data['tmp_name'])){
// Loop through each element in the array
foreach ($data as $Element){
// Call ourselves again with the element
$this->Input($Element);
}
} else {
// Read the data into the raw data array
$this->ReadToRaw($data);
}
// If we have elements in our raw data array
if (is_array($this->RawData) && count($this->RawData > 0)){
// Success
return TRUE;
} else {
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will set properties relating to our run time environment |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function Output($path = ''){
// If we have raw data to extract
if (is_array($this->RawData) && count($this->RawData)){
// If we have a path set
if (strlen($path)){
// If the path is not a directory
if (!is_dir($path)){
// Error
$this->Error('The specified output path is not a directory');
// Failure
return FALSE;
}
// If the path is not writable
if (!is_writable($path)){
// Error
$this->Error('The specified output path is not writable');
// Failure
return FALSE;
}
// While we have items in the raw data array
while (count($this->RawData) > 0){
// If we can't write from the raw data
if (!$this->WriteFromRaw($path)){
// Error
$this->Error('Problem extracting files');
// Failure
return FALSE;
}
}
// If we got here we were probably successfull
return TRUE;
} else {
// If we are in command line mode
if ($this->CommandLineInterface()){
// Then tell the user we're gonna need an output path
$this->Error('You must specify an output path when using this tool from the command line');
// Failure
return FALSE;
} else {
// Ok browser boy, since you aren't leet enough for a shell you only get one file or message
$Data = $this->WriteFromRaw('', TRUE);
// Handle each type of data differently
switch ($Data['type']){
// Message
case 'message':
// Send the appropriate mime type
header('Content-type: text/plain');
// Attempt to set a file name and get the browser to download
header('Content-Disposition: attachment; filename=message.txt');
// Output the message
echo $Data['message'];
// We should exit now so we don'taccidently send other stuff
exit();
// Yeah, I know, redundant
break;
// File
case 'file':
// Set the file name and get the browser to download
header('Content-Disposition: attachment; filename='.$Data['filename']);
// Output the file contents
echo $Data['file'];
// Don't execute anything below this
exit();
// Blah
break;
}
// If we get here we failed
return FALSE;
}
}
} else {
// Tell the user we had nothing to extract
$this->Error('No hidden data to extract from image');
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| Reads a local or remote file or a message into the raw array |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function ReadToRaw($data){
// Figure out what kind of data we are dealing with here
switch ($this->GetArgumentType($data)){
// A message
case 'message':
// If we actually have a message
if (strlen($data) > 0){
// Add the message to the final array
array_push($this->RawData, array('type' => 'message', 'message' => base64_encode(gzdeflate($data))));
// Success
return TRUE;
}
break;
// An uploaded file
case 'uploaded':
// Attempt to read the temporary file into a variable
$Contents = file_get_contents($data['tmp_name']);
// If we actually have contents
if (strlen($Contents) > 0){
// Add the data to the raw data array
array_push($this->RawData, array('type' => 'file', 'file' => base64_encode(gzdeflate($Contents)), 'filename' => $data['name']));
// Success
return TRUE;
}
break;
// A glob style string
case 'glob':
// Loop through all of the glob matches
foreach (glob($data) as $File){
// Attempt to read the file into memory
$Contents = file_get_contents($File);
// If we have contents
if (strlen($Contents) > 0){
// Add the data to the raw data array
array_push($this->RawData, array('type' => 'file', 'file' => base64_encode(gzdeflate($Contents)), 'filename' => $File));
}
}
// Were probably successfull
return TRUE;
break;
// A path or url to a file
case 'file':
// Attempt to read the file into memory
$Contents = file_get_contents($data);
// If we have contents
if (strlen($Contents) > 0){
// Add the data to the raw data array
array_push($this->RawData, array('type' => 'file', 'file' => base64_encode(gzdeflate($Contents)), 'filename' => $data));
// We were probably successfull
return TRUE;
}
break;
}
// If we got here we failed
return FALSE;
}
/*
+------------------------------------------------------------------+
| This will pop another item off the raw data stack to output |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function WriteFromRaw($path = '', $return = FALSE){
// If we actually have shit to extract
if (is_array($this->RawData) && count($this->RawData) > 0){
// Pop another item off the stack
$Data = array_pop($this->RawData);
// Handle different data types differently
switch ($Data['type']){
// Message
case 'message':
// If we aren't supposed to return
if ($return == FALSE){
// We don't write messages, we output them
$this->Info('The following message was embedded in the image');
$this->Info("\t".gzinflate(base64_decode($Data['message'])));
// Success
return TRUE;
} else {
// Decompress the message
$Data['message'] = gzinflate(base64_decode($Data['message']));
// Return the data type
return $Data;
}
// I don't know why I do this
break;
// File
case 'file':
// If we aren't returning
if ($return == FALSE){
// If we do not have a path
if (!strlen($path)){
// Then this was a waste of our time
return FALSE;
} else {
// Get some ifnormation about the file
$Info = pathinfo($Data['filename']);
// Get some information about our path
$Path = pathinfo($path);
// Attempt to open a file pointer to the output path
$Pointer = fopen($Path['dirname'].'/'.$Path['basename'].'/'.$Info['basename'], 'w+');
// If we have a pointer
if (is_resource($Pointer)){
// Write to the file
fwrite($Pointer, gzinflate(base64_decode($Data['file'])));
// Close the file
fclose($Pointer);
// I'm guessing everything went OK
return TRUE;
} else {
// Failure
return FALSE;
}
}
} else {
// Just decompress and decode the file contents
$Data['file'] = gzinflate(base64_decode($Data['file']));
// And return it
return $Data;
}
//
break;
}
} else {
// Meh
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will encode and compress a raw data array |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function RawToString($key = ''){
// If we actually have a data array
if (is_array($this->RawData) && count($this->RawData) > 0){
// Serialize our data array
$this->DataString = serialize($this->RawData);
// Instantiate the Secrypt object
$Secrypt = new Secrypt();
// If we can encrypt the data
if ($Secrypt->Encrypt($this->DataString, $key)){
// Then update the data string
$this->DataString = $Secrypt->Data;
// We are done with the raw data and encryption class
$this->RawData = array(); unset($Secrypt);
// Loop untill we have a valid bit boundry
while (strstr($this->DataString, $Boundry) || strlen($Boundry) <= 0){
// Generate a new 24 bit boundry
$Boundry = chr(rand(33, 127)).chr(rand(33, 127)).chr(rand(33, 127));
}
// Reset the bit boundry
$this->BitBoundry = '';
// Loop through each character in the new boundry
for ($i = 0; $i < 3; $i++){
// Add this to the bit boundry
$this->BitBoundry .= str_pad(decbin(ord($Boundry[$i])), 8, '0', STR_PAD_LEFT);
}
// Success
return TRUE;
} else {
// We have no data string
$this->DataString = '';
// Failure
return FALSE;
}
} else {
// Nothing to do
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will decompress and decode a string into a raw data array |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function StringToRaw($key = ''){
// If we actually have an encoded data string
if (is_string($this->DataString) && strlen($this->DataString) > 0){
// Create a new instance of the Secrypt object
$Secrypt = new Secrypt();
// If we can decrypt the string
if ($Secrypt->Decrypt($this->DataString, $key)){
// Then unserialize the data array
$this->RawData = unserialize($Secrypt->Data);
// If we have a raw data array
if (is_array($this->RawData) && count($this->RawData) > 0){
// Then we did it
return TRUE;
} else {
// Failure
return FALSE;
}
} else {
// Failure
return FALSE;
}
} else {
// Nothing to do
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will turn a bit stream into a data string |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function StreamToString(){
// Make sure we have an empty data string
$this->DataString = '';
// Loop untill the end of the bit stream
while (!$this->BitStream->EOF()){
// Add the character representation for the next 8 bits to our data string
$this->DataString .= chr(bindec($this->BitStream->Read(8)));
}
// If we have a data string
if (strlen($this->DataString) > 0){
// Trim any spare spaces off the string
$this->DataString = trim($this->DataString, ' ');
// Success
return TRUE;
} else {
// Summn went wrong
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will turn an encoded data string into a bit sequence |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function StringToStream(){
// Flush the bit stream
$this->BitStream->FlushStream();
// If we have a data string that will fit in the image
if ((strlen($this->DataString) * 8) < (($this->Image->CountPixels() - 6) * 3)){
// While the length of the string is not cleanly divisible by 3
while (strlen($this->DataString) % 3 > 0){
// Add a white space character to the data string
$this->DataString .= ' ';
}
// While we still have a data string
while (strlen($this->DataString) > 0){
// Write the next chunk of characters to the bit stream
$this->BitStream->Write(substr($this->DataString, 0, 1));
// Remove the first character from the data string
$this->DataString = substr($this->DataString, 1);
}
// Success
return TRUE;
} else {
// Work out how many bytes this image can hold
$Capacity = round(($this->Image->CountPixels() * 3) / 8);
// If we have less than a kilobyte
if ($Capacity < 1024){
// Make the capacity human readable
$Capacity = $Capacity.' bytes';
// If the capacity is smaller than a megabyte
} elseif ($Capacity < 1048576){
// Make the capacity human readable
$Capacity = round($Capacity / 1024, 2).' KB';
// The capacity is 1 megabyte or over
} else {
// Make the capacity human readable
$Capacity = round(($Capacity / 1024) / 1024, 2).' MB';
}
// Tell the user why the problem occurred
$this->Error('That image is not large enough to store that much data');
// Now go over the top and tell the user how they can fix it
$this->Error('The image you supplied can only hold '.$Capacity.' of data');
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will read pixels to obtain a bit stream |
| |
| @return void |
+------------------------------------------------------------------+
*/
function PixelsToStream(){
// Make a new bit stream for the image
$BitStream = new BitStream($this->Image->GetBoundry());
// Move to the start pixel
$this->Image->StartPixel();
// While we have bits and pixels
while (!$this->Image->EOF() && !$BitStream->EOF()){
// Get the current pixels RGB value
$Pixel = $this->Image->GetPixel();
// Write the pixel data to the bit stream
$BitStream->Write($Pixel);
// Move to the next pixel
$this->Image->NextPixel();
}
// If we got to the end of the image
if ($this->Image->EOF()){
// Then we never found our secret data
$BitStream->Stream = '';
}
// Overwrite the main bit stream with our new one
$this->BitStream = $BitStream;
}
/*
+------------------------------------------------------------------+
| This will write a bit stream to pixels |
| |
| @return void |
+------------------------------------------------------------------+
*/
function StreamToPixels(){
// Move to the start pixel
$this->Image->StartPixel();
// While we have bits and pixels
while (!$this->Image->EOF() && !$this->BitStream->EOF()){
// Read the next 3 bits from the bit stream
$Bits = $this->BitStream->Read(3);
// Write those 3 bits to the current pixel
$this->Image->SetPixel($Bits);
// Move to the next pixel
$this->Image->NextPixel();
}
// Set the end bit boundry
$this->Image->SetBoundry($this->BitBoundry);
// Move to the first pixel
$this->Image->FirstPixel();
// Set the first bit boundry
$this->Image->SetBoundry($this->BitBoundry);
// If we got here we probably succeeded
return TRUE;
}
// Enviromental Methods
/*
+------------------------------------------------------------------+
| This will set properties relating to our run time environment |
| |
| @return void |
+------------------------------------------------------------------+
*/
function SetEnvironment(){
// If we have a REQUEST_METHOD
if ($_SERVER['REQUEST_METHOD']){
// Then we are probably being called from the web
$this->CLI = FALSE;
// Turn verbose output off
$this->Verbose = FALSE;
} else {
// We are being run as a command line (or possibly compiled) app
$this->CLI = TRUE;
// Turn verbose output on
$this->Verbose = TRUE;
// Make sure we have implicit flush set to on
ob_implicit_flush(1);
}
}
/*
+------------------------------------------------------------------+
| This will determine if we are using a command line interface |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function CommandLineInterface(){
// If the command line interface flag is set
if ($this->CLI){
// Then we are probably using a command line interface
return TRUE;
} else {
// Not a command line interface
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will attempt to figure out what an argument represents |
| |
| @return string |
+------------------------------------------------------------------+
*/
function GetArgumentType($argument){
// If this is looks like an uploaded file
if (is_array($argument) && isset($argument['tmp_name'])){
// Then it probably is one
return 'uploaded';
// If this looks like a local file
} elseif (file_exists($argument)){
// Handle as a file
return 'file';
// If this looks like an external resource (TODO: Do this properly)
} elseif (strstr($argument, '://')){
// Handle as a file
return 'file';
// If the argument contains an asterix (TODO: Check the validity of the path)
} elseif (strstr($argument, '*') && ($argument[0] == '.' || $argument[0] == '/')){
// Then I'm guessing it is a glob style string
return 'glob';
// Everything else
} else {
// Treat it as a normal message
return 'message';
}
}
// Message Methods
/*
+------------------------------------------------------------------+
| Print out an error message to the user and exit |
| |
| @return void |
+------------------------------------------------------------------+
*/
function FatalError($msg){
// First we show the error message to the user
$this->Error('Fatal Error: '.$msg);
// Now we exit
exit(-1);
}
/*
+------------------------------------------------------------------+
| Print out an error message to the user |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Error($msg){
// If we are running as a command line application
if ($this->CommandLineInterface()){
// Just show the message a little formatted for the command line
echo '[-] '.$msg.".\n";
} else {
// Show the error formatted for the web
echo '<strong>Error:</strong> '.htmlspecialchars($msg).'<br />';
}
}
/*
+------------------------------------------------------------------+
| Print out a success message to the user |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Success($msg){
// If we are in verbose mode
if ($this->Verbose){
// If we are running as a command line application
if ($this->CommandLineInterface()){
// Just show the message a little formatted for the command line
echo '[+] '.$msg.".\n";
} else {
// Show the message formatted for the web
echo '<strong>Success:</strong> '.htmlspecialchars($msg).'<br />';
}
}
}
/*
+------------------------------------------------------------------+
| Print out an informative message to the user |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Info($msg){
// If we are in verbose mode
if ($this->Verbose){
// If we are running as a command line application
if ($this->CommandLineInterface()){
// Just show the message a little formatted for the command line
echo '[i] '.$msg.".\n";
} else {
// Show the message formatted for the web
echo '<strong>Info:</strong> '.htmlspecialchars($msg).'<br />';
}
}
}
}
/*
+----------------------------------------------------------------------+
| Package: Stegger v0.5 |
| Class : BitStream |
| Created: 03/08/2006 |
+----------------------------------------------------------------------+
*/
class BitStream {
/*-------------------*/
/* V A R I A B L E S */
/*-------------------*/
/**
* string
*
* This is a string of 1's and 0's representing binary data
*/
var $Stream = '';
/**
* string
*
* This is a string of 1's and 0's representing the bit boundry
*/
var $Boundry = '';
/**
* boolean
*
* This is a flag to determine if the class is still new or not
*/
var $Fresh = TRUE;
/*-------------------*/
/* F U N C T I O N S */
/*-------------------*/
/*
+------------------------------------------------------------------+
| Constructor |
| |
| @return void |
+------------------------------------------------------------------+
*/
function BitStream($bitBoundry = ''){
// If we have a bit boundry, use it
if ($bitBoundry) $this->Boundry = $bitBoundry;
}
/*
+------------------------------------------------------------------+
| This will read $number bits from the bit stream |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function Read($number = 8){
// If we are not on the end of the bit stream
if (strlen($this->Stream) > 0){
// Grab the chunk of bits from the bit stream
$return = substr($this->Stream, 0, $number);
// Remove the chunk of bits from the bit stream
$this->Stream = substr($this->Stream, $number);
// Return the chunk of bits
return $return;
} else {
// Nothing to return
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will write data to the bit stream |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Write($data, $binary = FALSE){
// If we have binary data
if ($binary){
// Then just add it raw
$this->Stream .= $data;
} else {
// Handle different data types differently
switch (gettype($data)){
// String
case 'string':
// Loop through each character in the string
for ($i = 0; $i < strlen($data); $i++){
// Add the bit representation for this character to the bit stream
$this->Stream .= str_pad(decbin(ord($data[$i])), 8, '0', STR_PAD_LEFT);
}
break;
// Integer
case 'integer':
// Add the bit representation for this character to the bit stream
$this->Stream .= str_pad(decbin($data), 8, '0', STR_PAD_LEFT);
break;
// Boolean
case 'boolean':
// If the boolean is true
if ($data == TRUE){
// Then add a 1 to the bit stream
$this->Stream .= '1';
} else {
// Add a 0 to the bit stream
$this->Stream .= '0';
}
break;
// Array of RGB values
case 'array':
// Loop through each primary colour in this RGB array
foreach ($data as $PrimaryColour){
// Add the bit value of this integer
$this->Stream .= (int) $PrimaryColour % 2;
}
break;
}
}
}
/*
+------------------------------------------------------------------+
| This will determine if we have hit the end of the bit stream |
| |
| @return void |
+------------------------------------------------------------------+
*/
function EOF(){
// If we have not ran any methods yet
if ($this->Fresh){
// We are no longer fresh
$this->Fresh = FALSE;
// But we are not at the end of the file either
return FALSE;
}
// If we have a bit of stream left
if (strlen($this->Stream) > 0){
// If we have a bit boundry
if (strlen($this->Boundry)){
// If we have found our bit boundry
if (substr($this->Stream, -24) == $this->Boundry){
// Then we remove the boundry from the bit stream
$this->Stream = substr($this->Stream, 0, -24);
// We hit the end of the stream
return TRUE;
} else {
// We are not at the end of the stream
return FALSE;
}
} else {
// Not at the end
return FALSE;
}
} else {
// Yeah, we're at the end
return TRUE;
}
}
/*
+------------------------------------------------------------------+
| This will flush out the bit stream (reset it) |
| |
| @return void |
+------------------------------------------------------------------+
*/
function FlushStream(){
// Reset the stream
$this->Stream = '';
}
}
/*
+----------------------------------------------------------------------+
| Package: Stegger v0.5 |
| Class : Image |
| Created: 03/08/2006 |
+----------------------------------------------------------------------+
*/
class Image {
/*-------------------*/
/* V A R I A B L E S */
/*-------------------*/
/**
* resource
*
* This is the main image canvas we are reading from or writing too
*/
var $Canvas;
/**
* string
*
* The name of the image we are encoding to or decoding from
*/
var $Name = '';
/**
* integer
*
* The main image canvas' width
*/
var $Width = 0;
/**
* integer
*
* The main image canvas' height
*/
var $Height = 0;
/**
* array
*
* This is an array containing the x and y co-ordinate's of the current pixel
*/
var $PixelPointer = array('x' => 0, 'y' => 0);
/**
* boolean
*
* Determines if we are at the end of the image or not
*/
var $EOF = TRUE;
/*-------------------*/
/* F U N C T I O N S */
/*-------------------*/
/*
+------------------------------------------------------------------+
| Constructor |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function Image($image){
// If we have an image
if ($image){
// Load it
$this->Load($image);
} else {
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will load an image as a resource |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function Load($image){
// If the image looks like it was uploaded
if (is_array($image) && isset($image['tmp_name'])){
// Set the default output image name using the original file name
$this->SetName($image['name']);
// Create a canvas for this image
$this->CreateCanvas($image['tmp_name'], $image['name']);
} else {
// Set the default output image name using the path or url to the image
$this->SetName($image);
// Create a canvas for this image
$this->CreateCanvas($image);
}
// If we actually have a canvas at this point
if (is_resource($this->Canvas)){
// We are not at the end of the file
$this->EOF = FALSE;
// Clear the canvas
$this->ClearCanvas();
// Move to the first pixel
$this->FirstPixel();
// Success
return TRUE;
} else {
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| Creates an image canvas resource from an image url or path |
| |
| @return void |
+------------------------------------------------------------------+
*/
function CreateCanvas($image, $name = ''){
// If we don't have the original name, then the image contains the name
if (!$name) $name = $image;
// Handle each image type differently
switch ($this->GetImageType($name)){
// JPG
case 'JPG':
// Create a canvas from the JPG
$this->Canvas = imagecreatefromjpeg($image); break;
// PNG
case 'PNG':
// Create a canvas from the PNG
$this->Canvas = imagecreatefrompng($image); break;
// GIF
case 'GIF':
// Create a canvas from the GIF
$this->Canvas = imagecreatefromgif($image); break;
// Not Supported
default:
// Nothing else we can do
return;
}
// If we have an image canvas
if (is_resource($this->Canvas)){
// Get the images width
$this->Width = imagesx($this->Canvas);
// Get the images height
$this->Height = imagesy($this->Canvas);
// We are not at the end of the file
$this->EOF = FALSE;
} else {
// We are at the end of the file
$this->EOF = TRUE;
}
}
/*
+------------------------------------------------------------------+
| This will copy the image on to a fresh canvas |
| |
| @return void |
+------------------------------------------------------------------+
*/
function ClearCanvas(){
// Create a new true colour canvas based on our images dimensions
$Canvas = imagecreatetruecolor($this->Width, $this->Height);
// If we have a canvas and an image
if (is_resource($Canvas) && is_resource($this->Canvas)){
// Make sure alpha blending is off
imagealphablending($Canvas, FALSE);
// Copy the contents of the original canvas to the new one
imagecopy($Canvas, $this->Canvas, 0, 0, 0, 0, $this->Width, $this->Height);
// Overwrite the old canvas with the newly prepaired one
$this->Canvas = $Canvas;
}
}
/*
+------------------------------------------------------------------+
| This will output the current image to a file or the browser |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Output($outputFile = ''){
// If we have an output file specified
if ($outputFile){
// Set the output image name
$this->SetName($outputFile);
}
// If we are serving to a browser
if ($_SERVER['REQUEST_METHOD']){
// Make sure the browser knows this is a PNG image
header('Content-type: image/png');
// Try get the browser to download the image as our name
header('Content-Disposition: attachment; filename='.$this->Name);
// Output the image to the browser
imagepng($this->Canvas);
} else {
// Get some information about the output path
$Info = pathinfo($outputFile);
// Write the image to the file name specified
imagepng($this->Canvas, $Info['dirname'].'/'.$this->Name);
}
// Destroy the canvas
imagedestroy($this->Canvas);
}
/*
+------------------------------------------------------------------+
| This will get the image type from a URL or path to an image |
| |
| @return string |
+------------------------------------------------------------------+
*/
function GetImageType($image){
// Get some information about the path, URL or image name
$Info = pathinfo($image);
// Handle each extension type differently
switch (strtolower($Info['extension'])){
// JPEG
case 'jpg':
case 'jpeg':
// We are dealing with a JPG
return 'JPG';
// GIF
case 'gif':
// We are dealing with a GIF
return 'GIF';
// PNG
case 'png':
// We are dealing with a PNG
return 'PNG';
// *
default:
// No idea what the hell this is
return '';
}
}
/*
+------------------------------------------------------------------+
| This will get the RGB value of the current pixel |
| |
| @return array |
+------------------------------------------------------------------+
*/
function GetPixel(){
// Get the (32 bit) RGB value from the current image
$RGB = imagecolorat($this->Canvas, $this->PixelPointer['x'], $this->PixelPointer['y']);
// Obtain the individual values for each primary colour
$R = ($RGB >> 16) & 0xFF;
$G = ($RGB >> 8) & 0xFF;
$B = ($RGB >> 0) & 0xFF;
// Return the individual RGB values in an array
return array($R, $G, $B);
}
/*
+------------------------------------------------------------------+
| This will set the RGB value of the current array |
| |
| @return void |
+------------------------------------------------------------------+
*/
function SetPixel($rgb){
// If this looks like a couple of bits
if (is_string($rgb) && strlen($rgb) == 3){
// Get the RGB value of the current pixel
$RGB = $this->GetPixel();
// Loop through each primary colour in this pixel
for ($i = 0; $i < 3; $i++){
// If the current char of our binary string is a 1
if ($rgb[$i] == '1'){
// If the current colour value isn't odd
if ($RGB[$i] % 2 != 1){
// Increment it
$RGB[$i]++;
}
} else {
// If the current colour valie isn't even
if ($RGB[$i] % 2 != 0){
// Decrease it
$RGB[$i]--;
}
}
}
// Call ourselves again with the RGB array
$this->SetPixel($RGB);
// And thats all there is to it
return TRUE;
}
// If we have a full RGB array
if (is_array($rgb) && count($rgb) == 3){
// Allocate the colour to the image
$Colour = imagecolorallocate($this->Canvas, $rgb[0], $rgb[1], $rgb[2]);
// Assign the colour to the current pixel
imagesetpixel($this->Canvas, $this->PixelPointer['x'], $this->PixelPointer['y'], $Colour);
// We're done here
return TRUE;
}
// If we get here we failed
return FALSE;
}
/*
+------------------------------------------------------------------+
| This will count the total number of pixels on the canvas |
| |
| @return integer |
+------------------------------------------------------------------+
*/
function CountPixels(){
// Return the width multiplied by the height
return round($this->Height * $this->Width);
}
/*
+------------------------------------------------------------------+
| This will move the pixel position to the first pixel |
| |
| @return void |
+------------------------------------------------------------------+
*/
function FirstPixel(){
// Reset the pixel pointer
$this->PixelPointer['x'] = ($this->Width - 1);
$this->PixelPointer['y'] = ($this->Height - 1);
}
/*
+------------------------------------------------------------------+
| This will move the pixel pointer to the start of the data |
| |
| @return void |
+------------------------------------------------------------------+
*/
function StartPixel(){
// The data starts 24 bits in (3 bytes)
$this->PixelPointer['x'] = ($this->Width - 1) - 8;
$this->PixelPointer['y'] = ($this->Height - 1);
}
/*
+------------------------------------------------------------------+
| This will move to the next pixel |
| |
| @return void |
+------------------------------------------------------------------+
*/
function NextPixel(){
// If we are on the last column
if ($this->PixelPointer['x'] <= 0){
// If we are on the last row of pixels
if ($this->PixelPointer['y'] <= 0){
// We are at the end of the file
$this->EOF = TRUE;
// So we can't go any further
return $this->EOF;
} else {
// Move to the next row
$this->PixelPointer['y']--;
// Move to the first column of the new row
$this->PixelPointer['x'] = ($this->Width - 1);
}
} else {
// Move to the next column
$this->PixelPointer['x']--;
}
}
/*
+------------------------------------------------------------------+
| This will move to the previous pixel |
| |
| @return void |
+------------------------------------------------------------------+
*/
function PrevPixel(){
// If we are on the first column
if ($this->PixelPointer['x'] >= ($this->Width - 1)){
// If we are on the first row of pixels
if ($this->PixelPointer['y'] >= ($this->Height - 1)){
// Then we can't go back any further
return;
} else {
// Move to the previous row
$this->PixelPointer['y']++;
// Move to the last column of the current row
$this->PixelPointer['x'] = 0;
}
} else {
// Move to the previous column
$this->PixelPointer['x']++;
}
}
/*
+------------------------------------------------------------------+
| This will get the boundry pattern for the bit stream |
| |
| @return string |
+------------------------------------------------------------------+
*/
function GetBoundry(){
// Backup the current pixel pointer
$PixelPointer = $this->PixelPointer;
// Move to the first pixel
$this->FirstPixel();
// Go through the first 8 pixels (24 bits)
for ($i = 0; $i < 8; $i++){
// Get this pixels RGB value
$Pixel = $this->GetPixel();
// Loop through each primary colour in this
foreach ($Pixel as $PrimaryColour){
// Add the bit value of this number to the final string
$return .= (int) $PrimaryColour % 2;
}
// Move to the next pixel
$this->NextPixel();
}
// Move the pixel pointer back where it was
$this->PixelPointer = $PixelPointer;
// Return the final value
return $return;
}
/*
+------------------------------------------------------------------+
| This sets the bit boundry from the current pixel position |
| |
| @return void |
+------------------------------------------------------------------+
*/
function SetBoundry($boundry){
// If we have at least 3 bytes of data (24 bits)
if (strlen($boundry) >= 24){
// Initiate the bit counter
$b = 0;
// Loop through 8 pixels from our current position
for ($i = 0; $i < 8; $i++){
// Get the RGB value of the current value
$RGB = $this->GetPixel();
// Loop through each primary colour in the RGB array
for ($j = 0; $j < 3; $j++){
// Get the next bit from the binary string
$Bit = $boundry[$b];
// Figure out which kind of bit this is
switch ($Bit){
// 1
case '1':
// If this colour is not an odd number
if ($RGB[$j] % 2 != 1){
// Then increase it
$RGB[$j]++;
}
break;
// 0
case '0':
// If this colour is not represented by an even number
if ($RGB[$j] % 2 != 0){
// Decrease it
$RGB[$j]--;
}
break;
}
// Increment the bit counter
$b++;
}
// Set the pixel to our new RGB array
$this->SetPixel($RGB);
// Move to the next pixel
$this->NextPixel();
}
}
}
/*
+------------------------------------------------------------------+
| This will set the name of the image we are going to output |
| |
| @return void |
+------------------------------------------------------------------+
*/
function SetName($image){
// Get some information about the image filename, path or url
$Info = pathinfo($image);
// If we have an extension
if (strlen($Info['extension']) > 0){
// If the extension is not a PNG
if (strtolower($Info['extension']) != 'png'){
// Change the extension to a PNG
$Info['basename'] = str_replace('.'.$Info['extension'], '.png', $Info['basename']);
}
} else {
// If we have a basename
if (strlen($Info['basename']) > 0){
// Then append our extension to it
$Info['basename'] .= '.png';
} else {
// This guy isn't giving us much choice
$Info['basename'] = 'encoded.png';
}
}
// Set the image name to the base name
$this->Name = $Info['basename'];
}
/*
+------------------------------------------------------------------+
| This will test for the end of the file (image) |
| |
| @return boolean |
+------------------------------------------------------------------+
*/
function EOF(){
// Return the end of file property
return $this->EOF;
}
}
/*
+----------------------------------------------------------------------+
| Package: Stegger v0.5 |
| Class : Secrypt |
| Created: 23/07/2006 |
+----------------------------------------------------------------------+
*/
class Secrypt {
/*-------------------*/
/* V A R I A B L E S */
/*-------------------*/
// Public Properties
/**
* array
*
* This is the array of keys we use to encrypt or decrypt data
*/
var $Keys = array('public' => '', 'private' => '', 'xfactor' => '', 'yfactor' => '', 'zfactor' => '');
/**
* string
*
* This holds the data after it has been successfully encrypted or decrypted
*/
var $Data = '';
/**
* boolean
*
* Determines if we can zip the contents or not
*/
var $Zip = TRUE;
/**
* array
*
* All the error messages in an array
*/
var $Errors = array();
// Private Properties
/**
* array
*
* An array that holds each of our base64 compatible charsets
*/
var $Locks = array();
/*-------------------*/
/* F U N C T I O N S */
/*-------------------*/
/*
+------------------------------------------------------------------+
| Constructor |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Secrypt(){
// If we can't zip
if (!function_exists('gzdeflate')){
// Then we don't zip
$this->Zip = FALSE;
}
// Run forever
set_time_limit(0);
// Reset the lock
$this->ResetLock();
}
// Public API Methods
/*
+------------------------------------------------------------------+
| This will encrypt $data against the $privateKey and $publicKey |
| |
| @return string |
+------------------------------------------------------------------+
*/
function Encrypt($data, $privateKey = '', $publicKey = STEGGER_PUB_KEY){
// Insert the keys
$this->InsertKeys($privateKey, $publicKey);
// Turn all the keys
$this->TurnKey();
// Locketh the data
return $this->Lock($data);
}
/*
+------------------------------------------------------------------+
| This will decrypt $data against the $privateKey and $publicKey |
| |
| @return string |
+------------------------------------------------------------------+
*/
function Decrypt($data, $privateKey = '', $publicKey = STEGGER_PUB_KEY){
// Insert the keys
$this->InsertKeys($privateKey, $publicKey);
// Turn all the keys
$this->TurnKey();
// Unlock the data and return the results
return $this->Unlock($data);
}
// Key Methods
/*
+------------------------------------------------------------------+
| This gets a reference to the key that fits in $lockType |
| |
| @return reference |
+------------------------------------------------------------------+
*/
function &GetKey($lockType){
// Return the appropriate key
return $this->Keys[$lockType];
}
/*
+------------------------------------------------------------------+
| This will set all the keys in the key array at once |
| |
| @return void |
+------------------------------------------------------------------+
*/
function InsertKeys($private, $public){
// Remove all keys
$this->RemoveKey();
// Reset all the locks
$this->ResetLock();
// Loop through all the keys
foreach ($this->Keys as $KeyType => $Key){
// If this is a factor key
if (strstr($KeyType, 'factor')){
// Set the key to the md5 hash of the keys array thus far
$Key = md5(serialize($this->Keys));
} else {
// Set the key to the key we were passed
$Key = $$KeyType;
}
// Insert the key we have in the end
$this->InsertKey($Key, $KeyType);
}
}
/*
+------------------------------------------------------------------+
| This will set a $key for $lockType |
| |
| @return void |
+------------------------------------------------------------------+
*/
function InsertKey($key, $lockType){
// If we have a key
if (strlen($key) > 0){
// Set the key
$this->Keys[$lockType] = $key;
}
}
/*
+------------------------------------------------------------------+
| This will turn a lock based on a keys contents |
| |
| @return void |
+------------------------------------------------------------------+
*/
function TurnKey($lockType = ''){
// If we don't have a lock type
if (!$lockType){
// Loop through all the locks
foreach ($this->Locks as $LockType => $Lock){
// Call ourselves with this lock type
$this->TurnKey($LockType);
}
// Don't pass this bit
return;
}
// Get a reference to the desired key
$Key =& $this->GetKey($lockType);
// Loop through each character of the key
for ($i = 0; $i < strlen($Key); $i++){
// Work out how many steps to turn the lock
$Steps = ord($Key[$i]) / ($i + 1);
// If the decimal value of the current character is odd
if (ord($Key[$i]) % 2 != 0){
// Turn the lock left
$this->TurnLock($lockType, $Steps, 'left');
} else {
// Turn the lock right
$this->TurnLock($lockType, $Steps, 'right');
}
}
}
/*
+------------------------------------------------------------------+
| This will clear a keys contents, all keys if no $lockType is set |
| |
| @return void |
+------------------------------------------------------------------+
*/
function RemoveKey($lockType = ''){
// Loop through each of the keys
foreach($this->Keys as $KeyName => $Key){
// If this is our desired key or we don't have a desired key
if ($lockType == $KeyName || strlen($lockType) == 0){
// Reset this key
$this->Keys[$KeyName] = '';
}
}
}
// Lock Methods
/*
+------------------------------------------------------------------+
| This gets a reference to the character set a key manipulates |
| |
| @return reference |
+------------------------------------------------------------------+
*/
function &GetLock($lockType){
// Return a reference to the lock
return $this->Locks[$lockType];
}
/*
+------------------------------------------------------------------+
| This will lock the data according to the current character index |
| |
| @return string |
+------------------------------------------------------------------+
*/
function Lock($data){
// Reset the data
$this->Data = '';
// If we are supposed to be zipping
if ($this->Zip == TRUE){
// If we can't compress the data
if (FALSE === ($data = @gzdeflate($data))){
// Add the error incase the user wants to know why we failed
$this->Error('There was a problem compressing the data');
// Huston, we have a problem
return FALSE;
}
}
// If we can compress the character
if (FALSE !== ($data = base64_encode($data))){
// Loop through each character in the data
for ($i = 0; $i < strlen($data); $i++){
// Convert this character to its encrypted equivilent
$data[$i] = $this->GetChar($data[$i], TRUE);
}
// Looks like we have ourselves some data
$this->Data = $data;
// And thats all folks
return $this->Data;
} else {
// Add the error to let the user know why we failed
$this->Error('There was a problem encoding the data');
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This unlocks the data according to the current character index |
| |
| @return string |
+------------------------------------------------------------------+
*/
function Unlock($data){
// Reset the data
$this->Data = '';
// Loop through each character in the data
for ($i = 0; $i < strlen($data); $i++){
// Convert this character to its decrypted equivilent
$data[$i] = $this->GetChar($data[$i], FALSE);
}
// If we can base64 decode the data
if (FALSE !== ($data = base64_decode($data))){
// If we can decompress data
if (FALSE !== ($data = @gzinflate($data))){
// Looks like we have ourselves some data
$this->Data = $data;
// Thats all folks
return $this->Data;
} else {
// Tell the user why we failed
$this->Error('There was a problem decompressing the data');
// Failure
return FALSE;
}
} else {
// Add the error ro the error stack
$this->Error('There was a problem decoding the data');
// Failure
return FALSE;
}
}
/*
+------------------------------------------------------------------+
| This will turn a lock (character set) $steps steps in $direction |
| |
| @return void |
+------------------------------------------------------------------+
*/
function TurnLock($lockType, $steps = 5, $direction = 'right'){
// Loop through the required number of steps
for ($i = 0; $i < $steps; $i++){
// Get a reference to the lock
$Lock =& $this->GetLock($lockType);
// If we are not going right, reverse the string
if ($direction != 'right') $Lock = strrev($Lock);
// Make a copy of the counter
$c = $i;
// If we are rotating a character passed the end of the character set
if ($c >= strlen($Lock)){
// While we still have too little characters to split
while ($c >= strlen($Lock)){
// Minus the lock length from the counter
$c = $c - strlen($Lock);
}
}
// Isolate the first character in the charset
$Char = substr($Lock, 0, 1);
$Lock = substr($Lock, 1);
// If our split point exists
if (strlen($Lock[$c]) > 0){
// Split the string at the desired position
$Chunks = explode($Lock[$c], $Lock);
// If we have some chunks
if (is_array($Chunks)){
// Then piece together the string
$Lock = $Chunks[0].$Lock[$c].$Char.$Chunks[1];
}
} else {
// Put the lock back to the way it was
$Lock = $Char.$Lock;
}
// If we are not going right, reverse the string back
if ($direction != 'right') $Lock = strrev($Lock);
}
}
/*
+------------------------------------------------------------------+
| This will generate the original charset and character index |
| |
| @return void |
+------------------------------------------------------------------+
*/
function ResetLock($lockType = ''){
// Get the base 64 compatible character set
$CharSet = $this->GetCharSet();
// Loop through the keys we have
foreach ($this->Keys as $LockType => $Key){
// If we were supplied a lock type to reset
if ($lockType){
// If this is our lock
if ($LockType == $lockType){
// Then reset the lock
$this->Locks[$LockType] = $CharSet;
// And we're done
return;
}
} else {
// Reset this lock
$this->Locks[$LockType] = $CharSet;
}
}
}
// Character Set Methods
/*
+------------------------------------------------------------------+
| This will lookup the encrypted/decrypted version of a character |
| |
| @return string |
+------------------------------------------------------------------+
*/
function GetChar($char, $encrypt = FALSE){
// If we are not encrypting, flip the locks
if (!$encrypt) $this->Locks = array_reverse($this->Locks);
// Initate the lock counter
$i = 0;
// Loop through each lock
foreach ($this->Locks as $LockType => $Lock){
// If this is the first lock, set the initial position
if ($i == 0){
// Get the initial position
$Position = strpos($Lock, $char);
}
// If the lock counter is odd, or this is the final iteration
if ($i % 2 > 0){
// If we are encrypting
if ($encrypt){
// Swap position
$Position = strpos($Lock, $char);
} else {
// Swap character
$char = $Lock[$Position];
}
} else {
// If we are encrypting
if ($encrypt){
// Swap character
$char = $Lock[$Position];
} else {
// Swap position
$Position = strpos($Lock, $char);
}
}
// Increment the lock counter
$i++;
}
// If we are not encrypting, flip the locks
if (!$encrypt) $this->Locks = array_reverse($this->Locks);
// Return the character
return $char;
}
/*
+------------------------------------------------------------------+
| This will generate and return a base 64 compatible charset |
| |
| @return string |
+------------------------------------------------------------------+
*/
function GetCharSet(){
// These are forbidden characters that fall in the range of chars we iterate
$ForbiddenChars = array_merge(range(44, 46), range(58, 64), range(91, 96));
// Loop through the base64 compatible range of characters
for ($i = 43; $i < 123; $i++){
// If this is not a forbidden character
if (!in_array($i, $ForbiddenChars)){
// Then add this to the final character set
$return .= chr($i);
}
}
// Return the final character set
return $return;
}
// Error Reporting Methods
/*
+------------------------------------------------------------------+
| This will add an error message to the error message stack |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Error($msg){
// Add the error to the stack
$this->Errors[] = $msg;
}
/*
+------------------------------------------------------------------+
| This will display the error messages specific to the current env |
| |
| @return void |
+------------------------------------------------------------------+
*/
function ShowErrors($returnVal = FALSE){
// Loop through all the errors
foreach ($this->Errors as $Error){
// If we are being called from the web
if (strlen($_SERVER['REQUEST_METHOD']) > 0){
// Format the errors for the web
$return .= '<strong>Error:</strong> '.$Error.'<br />';
} else {
// Format the error message for the command line
$return .= '[-] '.$Error."\n";
}
}
// Now that we are showing the errors, we can clear them too
$this->Errors = array();
// If we are supposed to the return the errors
if ($returnVal){
// Then return them we shall
return $return;
} else {
// Output the errors directly
echo $return;
}
}
// Debug Methods
/*
+------------------------------------------------------------------+
| This will output a message instantly for debugging purposes |
| |
| @return void |
+------------------------------------------------------------------+
*/
function Debug($msg){
// Turn implicit output buffering on incase it is off
ob_implicit_flush(1);
// If we are being called from the web
if (strlen($_SERVER['REQUEST_METHOD'])){
// Then format the message for the web
$msg = '<strong>Debug:</strong> '.$msg.'<br />';
} else {
// Format the message for a CLI
$msg = '[i] '.$msg."\n";
}
// Output the message
echo $msg;
}
}
?>
|