<?php
/*
 * Password Generator
 * Author: SleePy (JeremyD)
 * Copyright: 2011, JeremyD (SleePy [at] simplemachines [period] org)
 * Note: Feel free to reuse this code, just give credit where credit is due.
*/

// Show me the sauce.
if (isset($_GET['sauce']))
{
    echo 
'<!DOCTYPE html><html><head><title>Password Generator Source code</title></head><body>';
    
highlight_file(__FILE__);
    exit(
'</body></html>');
}

// Wordpress integration.
$specialPage['headerTitle'] = 'Password Generator';
$specialPage['pageTitlePrefix'] = 'Password Generator';
$specialPage['source'] = '<a href="?sauce">See the source code</a>';
require_once(
'../wp-ssi.php');

// Start a new random generator.
$randomKeys = new randomKeyGen();

// If we are submitting, lets handle that.
// Note: $_REQUEST is used as I am fine with requests from GETs.
if (isset($_REQUEST['submit']))
{
    
// Set the length of the keys to be generated?
    
if (isset($_REQUEST['length_min']))
    {
        
$randomKeys->setMinLength($_REQUEST['length_min']);
        
$randomKeys->setMaxLength(!empty($_REQUEST['length_max']) ? $_REQUEST['length_max'] : $_REQUEST['length_min']);
    }

    
// The quantity of the keys we want.
    
if (isset($_REQUEST['number']))
        
$randomKeys->setNumber($_REQUEST['number']);

    
// Some settings.
    
$randomKeys->disableAlpha(isset($_REQUEST['disable_alpha']));
    
$randomKeys->disableUppercase(isset($_REQUEST['disable_uppercase']));
    
$randomKeys->disableNumbers(isset($_REQUEST['disable_numbers']));
    
$randomKeys->disablePunctuation(isset($_REQUEST['disable_punctuation']));
    
$randomKeys->enableOnlyHex(!isset($_REQUEST['use_hex_only']));

    
// Add in any additional characters.
    
if (isset($_REQUEST['use_additional']))
        
$randomKeys->addChars($_REQUEST['use_additional']);

    
// Display any warnings before the keys
    
$warnings $randomKeys->getWarnings();
    if (!empty(
$warnings))
    {
        echo 
'
            <div class="error">'
;

        foreach (
$warnings as $warn)
            echo 
$warn '<br />';

        echo 
'</div>';
    }

    
// At this point we are submitting, so lets show those results.
    
echo '
    <ol class="no_bullet" id="key_list">'
;

    foreach (
$randomKeys->generate() as $string)
        echo 
'
        <li>'
$string;

    echo 
'
    </ol>'
;    
}

echo 
'
    <form action="?" method="post">
        <dl class="settings">
            <dt>Minimum length of key</dt>
            <dd><input name="length_min" type="text" size="5" maxlength="3" tabindex="1"  value="'
$randomKeys->getSetting('length_min'), '"/></dd>

            <dt>Maximum length of key</dt>
            <dd><input name="length_max" type="text" size="5" maxlength="3" tabindex="1"  value="'
$randomKeys->getSetting('length_max'), '"/></dd>

            <dt>Number of keys</dt>
            <dd><input name="number" type="text" size="5" maxlength="3" tabindex="1" value="'
$randomKeys->getSetting('number'), '"/></dd>

            <dt>Remove alaphabet?</dt>
            <dd><input name="disable_alpha" type="checkbox" value="1" '
, ($randomKeys->getSetting('use_alpha') ? '' 'checked="checked"'), ' /></dd>

            <dt>Remove uppercase?</dt>
            <dd><input name="disable_uppercase" type="checkbox" value="1" '
, ($randomKeys->getSetting('use_uppercase') ? '''checked="checked"'), ' /></dd>

            <dt>Remove numbers?</dt>
            <dd><input name="disable_numbers" type="checkbox" value="1" '
, ($randomKeys->getSetting('use_numbers') ? '' 'checked="checked"'), ' /></dd>

            <dt>Remove punctuation?</dt>
            <dd><input name="disable_punctuation" type="checkbox" value="1" '
, ($randomKeys->getSetting('use_punctuation') ? '' 'checked="checked"'), ' /></dd>

            <dt>Enable Hex Only?</dt>
            <dd><input name="use_hex_only" type="checkbox" value="1" '
, (!$randomKeys->getSetting('use_hex_only') ? '' 'checked="checked"'), ' /></dd>

            <dt>Additional characters?</dt>
            <dd><input name="use_additional" type="text" size="25" maxlength="50" tabindex="1" value="'
$randomKeys->getSetting('use_additional'), '"/></dd>

            <dt><input name="submit" type="submit" value="Generate" tabindex="2" /></dt><dd></dd>
        </dl>
    </form>
    <br clear="all" />'
;

/*
* This is our random Key Generator source.  The rest is just cabbage.
*/
class randomKeyGen
{
    
/* 
    * Some default settings.
    */
    
protected $max_number 50;
    protected 
$max_length 200;
    protected 
$default_number 10;
    protected 
$default_length_max 15;
    protected 
$default_length_min 8;

    private 
$number 10;
    private 
$length_min 8;
    private 
$length_max 15;
    private 
$errors = array();

    private 
$use_numbers true;
    private 
$use_alpha true;
    private 
$use_uppercase true;
    private 
$use_punctuation true;
    private 
$use_hex_only false;
    private 
$additional_chars '';

    
/*
    * Sets the number of keys to be generated.
    * @param $number The number of keys to generate
    */
    
public function setNumber($number)
    {
        if (
$number $this->max_number)
        {
            
$this->warnings[] = 'Too many keys selected. Forcing max limit of ' $this->max_number;
            
$this->number $this->max_number;
        }
        elseif (
$number != (int) $number)
        {
            
$this->warnings[] = 'Number of keys is not numerical, defaulting to ' $max_number;
            
$this->number = (int) $default_number;        
        }
        else
            
$this->number $number;
    }

    
/*
    * Sets the minimum length of the keys.
    * @param #min The minimum length of the key.
    */
    
public function setMinLength($min)
    {
        if (
$min $this->max_length)
        {
            
$this->warnings[] = 'Minimum length of key exceeded the max, defaulting to ' $this->max_length;
            
$this->length_min $this->max_length;
        }
        elseif (
$min != (int) $min)
        {
            
$this->warnings[] = 'Minimum length is not numerical, defaulting to ' $default_length_min;
            
$this->length_min = (int) $default_length_min;        
        }
        else
            
$this->length_min $min;
    }

    
/*
    * Sets the maximum length of the keys.
    * @param #max The minimum length of the key.
    */
    
public function setMaxLength($max)
    {
        if (
$max == '')
        {
            
$this->warnings[] = 'No maximum length set, defaulting to minimum';
            
$this->length_max $this->length_min;
        }
        elseif (
$max $this->max_length)
        {
            
$this->warnings[] = 'Maximum length of key exceeded the max, defaulting to ' $this->max_length;
            
$this->length_max $this->max_length;
        }
        elseif (
$max != (int) $max)
        {
            
$this->warnings[] = 'Maximum length is not numerical, defaulting to ' $default_length_max;
            
$this->length_max = (int) $default_length_max;        
        }
        else
            
$this->length_max $max;

        
// Prevent the max from being lower than the min.
        
if ($this->length_max $this->length_min)
        {
            
$this->warnings[] = 'Maximum length is lower than the minimum, default to current minimum';
            
$this->length_max $this->length_min;
        }
    }

    
/*
    * Sets whether to use Alpha or not.
    * @param $bool True is on, false is off.
    */
    
public function disableAlpha($bool)
    {
        
$this->use_alpha $bool false true;
    }

    
/*
    * Sets whether to use Uppercase or not.
    * @param $bool True is on, false is off.
    */
    
public function disableUppercase($bool)
    {
        
$this->use_uppercase $bool false true;
    }

    
/*
    * Sets whether to use numbers or not.
    * @param $bool True is on, false is off.
    */
    
public function disableNumbers($bool)
    {
        
$this->use_numbers $bool false true;
    }

    
/*
    * Sets whether to use punctuation or not.
    * @param $bool True is on, false is off.
    */
    
public function disablePunctuation($bool)
    {
        
$this->use_punctuation $bool false true;
    }

    
/*
    * Allows additional Chars to be considered.
    * @param $characters Each additional character to consider.
    */
    
public function addChars($characters)
    {
        
$this->additional_chars $characters;
    }

    
/*
    * Sets whether to use hex characters or not.
    * @param $bool True is on, false is off.
    */
    
public function enableOnlyHex($bool)
    {
        
$this->use_hex_only $bool false true;
    }

    
/*
    * Takes our settings and creates a pattern we use for random characters.
    * @return string A string containing our pattern.
    */
    
private function createPattern()
    {
        
// Hex only?
        
if ($this->use_hex_only)
        {
            
$pattern '';
            foreach (
range('a''f') as $char)
                
$pattern .= $char . ($this->use_uppercase strtoupper($char) : '');
        }
        else
        {
            
// First we do our ABCs.
            
if ($this->use_alpha && $this->use_uppercase)
            {
                
$pattern '';
                foreach (
range('a''z') as $char)
                    
$pattern .= $char strtoupper($char);
            }
            elseif (
$this->use_alpha)
                
$pattern implode(''range('a''z'));
            else
                
$pattern '';
        }

        
// Add in numbers maybe.
        
if ($this->use_numbers)
            
$pattern .= implode(''range('0''9'));

        
// Add in Punctuation maybe.
        
if (empty($this->use_hex_only) && $this->use_punctuation)
            
$pattern .= '!@#$%^&*()_+';

        
// Some additional characters?
        // Normally user input would be filtered. As far as I can see, if the user wants to cause issues with their own keys, they can. No data is saved and the possibility of doing xss with randomized characters is fairly small.
        
if ($this->additional_chars)
            
$pattern .= $this->additional_chars;

        
// Get ready to return.
        
$return_pattern '';
        
$pattern_length strlen($pattern) - 1;

        
// Randomize our pattern string.
        // Some may call this overkill, but it generates truly random passwords.
        
for ($i 0$i $pattern_length$i++)
            
$return_pattern .= $pattern[mt_rand(0$pattern_length)];

        
$this->pattern_used $return_pattern;
        return 
$return_pattern;
    }

    
/*
    * Get the pattern that we used.
    */
    
public function getPattern()
    {
        return 
$this->pattern_used;
    }

    
/*
    * This actually generates the random strings
    * @param $pattern The pattern to use.
    * @param $length The length of the string to generate.
    * @return string The randomized string to return.
    */
    
private function generateString($pattern$length)
    {
        
$string '';
        
$pattern_length strlen($pattern) - 1;

        for (
$i 0$i $length$i++)
            
$string .= $pattern[mt_rand(0$pattern_length)];

        return 
$string;
    }

    
/*
    * Generate a list of strings to display.
    * @return array An array of strings.
    */
    
public function generate()
    {
        
// Create our pattern.
        
$pattern $this->createPattern();

        
$strings = array();
        for (
$i 0$i $this->number$i++)
            
$strings[] = $this->generateString($patternmt_rand($this->length_min$this->length_max));

        return 
$strings;
    }

    
/*
    * Gets a setting
    * @param $setting The setting to get.
    */
    
public function getSetting($setting)
    {
        
// Since this comes from our template and not user input, we don't need to make sure its only settings.
        
if (isset($this->{$setting}))
            return 
$this->{$setting};
        else
            return 
'';
    }

    
/*
    * Get all the warnings
    */
    
public function getWarnings()
    {
        if (isset(
$this->warnings))
            return 
$this->warnings;
        return array();
    }
}