#!/usr/bin/perl
# Name: Simple Pretty Password Generator
# Description: Generate passwords users wont bitch about "Ohh its too hard to remember!" using the systems dictionary file
# Version: 1.0
# Authors: Jason Jorgensen <jasonj@innominatus.com>
# Copyright (C) 2003  Jason Jorgensen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
###########################################################################



srand;

### Action Control Variables ##############################################################################
my $dict = '/usr/share/dict/words';
my $minlen = 6;
my $maxlen = 12;
my $quantity = 4;
###########################################################################################################

#$rand = new String::Random;
my $debug = 0;
my $plainpass;
my $securepass;


# Main
if ($ARGV[0]) { $simplepass = $ARGV[0] }

#Sanity check the variables
die unless $maxlen > $minlen;
die unless -f $dict;

my %words = &sortdict($dict); # Sorted dict stored in hash of arrays 
if ($ARGV[0]) {
  $plainpass = $ARGV[0];
}
else { $plainpass = &generate_simple_pass($minlen, $maxlen, \%words); }
print $plainpass."\n";

for (my $c=0;$c <= $quantity;$c++) {
  print &secure_simple_pass($plainpass)."\n";
}

exit;


## Subroutines
sub secure_simple_pass {
  my $plainpass = shift @_;
  my %chartable = ( 
                  0 => {
                        a => 'a4', b => 'b8', c => 'c', d => 'd', e => 'e3', f => 'f', g => 'g6', h => 'h', 
                        i => 'i1', j => 'j', k => 'k', l => 'l1', m => 'm', n => 'n', o => 'o0', p => 'p', 
                        q => 'q', r => 'r', s => 's5', t => 't7+', u => 'u', v => 'v', w => 'w', x => 'x', 
                        y => 'y', z => 'z', 
                       },
                  1 => {
                        a => 'A@', b => 'B', c => 'C', d => 'D', e => 'E', f => 'F', g => 'G', h => 'H#', 
                        i => 'I!', j => 'J', k => 'K', l => 'L!', m => 'M', n => 'N', o => 'O', p => 'P', 
                        q => 'Q', r => 'R', s => 'S$', t => 'T', u => 'U', v => 'V', w => 'W', x => 'X', 
                        y => 'Y', z => 'Z',
                       },
               );
  my $securepass;

  my @plainpass = split '', $plainpass;
  # will the password start up capitilized or not? 0 no, 1 yes
  my $shift=int(rand(2));
  # what position will the password switch case
  my $shiftposition=int(rand($#plainpass));
  
  my $charcounter=0;
  foreach my $char (@plainpass) {
    if ($charcounter == $shiftposition) { 
      if ($shift == 1) { $shift = 0 }
      elsif ($shift == 0) { $shift = 1 }
    }
    my $securecharposition = int(rand(length($chartable{$shift}{$char})));
    print "DEBUG: shift: $shift\n" if $debug;
    print "DEBUG: shiftposition: $shiftposition\n" if $debug;
    print "DEBUG: position: $charcounter\n" if $debug;
    print "DEBUG: char: $char\n" if $debug;
    print "DEBUG: char entry: ".$chartable{$shift}{$char}."\n" if $debug;
    print "DEBUG: length of char entry: ".length($chartable{$shift}{$char})."\n" if $debug;
    print "DEBUG: securecharposition: $securecharposition\n" if $debug;
    my $securechar = substr $chartable{$shift}{$char}, $securecharposition, 1;
    $securepass.=$securechar;
    $charcounter++;
  }

 return $securepass;
}


sub generate_simple_pass {
  my $minlen = shift @_;
  my $maxlen = shift @_;
  my $words = shift @_;
  my %words = %{$words};
  my $sanitycounter = 0;
  my $lengthtograb;
  my $entrytograb;
  my $plainpass = "";

  while (($sanitycounter <$maxlen) && ((length($plainpass) <= $minlen) && (length($plainpass) <= $maxlen))) {
    $lengthtograb = int(rand($maxlen-length($plainpass)))+1;
    #$entrytograb = length($words{$lengthtograb});
    $entrytograb = int(rand($#{$words{$lengthtograb}}))+1;
    $plainpass .= $words{$lengthtograb}[$entrytograb];
  
    print "------------------------------------------------------------------------------\n" if $debug;
    print "DEBUG: Current length: ".length($plainpass)."\n" if $debug;
    print "DEBUG: Grabbing length: $lengthtograb\n" if $debug;
    print "DEBUG: Length of array: ".$#{$words{$lengthtograb}}."\n" if $debug;
    print "DEBUG: Array entry grabbing: $entrytograb\n" if $debug;
    print "DEBUG: Sanity Counter: $sanitycounter\n" if $debug;
    print "DEBUG: Pass so far: $plainpass\n" if $debug;
    $sanitycounter++;
  }
  $plainpass =~ tr/A-Z/a-z/;

  return $plainpass;
}


sub sortdict {
  my $dict = shift;
  print "DEBUG: DICT: $dict\n" if $debug;
  my %words;
  @{$words{1}} = qw(a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1 2 3 4 5 6 7 8 9 0);

  open (DICT, "<$dict") || die "Cannot open dict: $!";
  while (<DICT>) {
    my $word = $_;
    #chomp $word;
    $word =~ s/\s//g;
    push (@{$words{length($word)}}, $word);
  }
  ## uncomment to see the entire sorted dictionary
  #foreach $key (keys(%words)) {
  #  foreach $value (@{$words{$key}}) {
  #    print "DEBUG: key: $key value: $value\n";
  #  }
  #}
  return %words;
}