#ImageCode.pm
#
#================================================
#
#         ImageCode Perl module
#
#     (c) Vadim N. Suvorov, 2004
#
# Homepage: http://www.progland.com/
# email: dimma@progland.com
#================================================
package ImageCode;
#    use strict;
use CGI::Carp qw (fatalsToBrowser);
    use vars qw($VERSION @ISA @EXPORT);  # Globals.
    @ISA    = ();                               # Not inhereited.
    @EXPORT = qw();
    $ImageCode::VERSION = '1.1';

#======== CHANGE DEFAULT KEY ! ====================    
my $default_key='anyword';# Change default encryption key!


#========  Don't need to edit below this line =====    
my %defaults = (
          chars        =>['a'..'z','A'..'Z','0'..'9'],
          passlength   => 5,
          expires      => 10, #in minutes
          encryptkey   =>$default_key,
          font         =>'ARIALNB.ttf',
          fontsize     => 30,
          fontcolors   =>['black'],
          bgcolors     =>['white'],
          gridcolor    =>'gray',
          addnoise     =>0,
          scatter      =>0.5,
          noise_type   =>"Laplacian" 
	      );
sub new {
    my $class = shift;
    my %init = @_; 
    my $self = {};
    $self = bless $self, $class;
    foreach (keys %defaults) {
 	    # call functions and initialize with default values
 	    $self->$_($defaults{$_});
 	} 
    if (%init) {
	  foreach (keys %init) {
	    # call functions and initialize with given values
	    $self->$_($init{$_});
	  }
    }
    $self;
}
sub getimage {
    my $self = shift;
    $self->{'cryptpass'} = $_[0]  if defined $_[0];
    my $pass = $self->decrypt($self->{'cryptpass'},$self->{'encryptkey'});
    my ($checksum1,$checksum2,$checksum);
    my @ipass=split(//,$pass);
    $checksum=shift(@ipass).shift(@ipass);
    $checksum1=pop(@ipass).pop(@ipass).$checksum;
    foreach (@ipass){$checksum2+=ord}
    if ($checksum1 !~ /^\d{4}$/ or $checksum ne sprintf("%02d", substr($checksum2,-2))){@ipass= qw {E r r o r};$self->{'scatter'}=0}
    my $passlenght = scalar(@ipass);
    my $delta=$self->{'fontsize'}/6;
    my $maxy=$self->{'fontsize'}+2*$delta;
    my $maxx=$passlenght*$self->{'fontsize'};
    my $size= $maxx.'x'.$maxy; 
    my $bgcolor=$self->random('bgcolors'); 

    my $noise_type=$self->{'noise_type'};
    
 
     use Image::Magick;
     my $image = Image::Magick->new() or die "Cannot create PerlMagick object";
        $image->Set(size=>"$size");
        $image->ReadImage("xc:$bgcolor");    
        $image->Set(magick=>'png');
        my $anglegrid=('0','15','30','45','60','75')[rand(6)];
        for my $i(1..$maxx/10+1){
          my $x=$i*10/cos($anglegrid*3.14/180);
          $image->Draw(primitive=>'line',points=>"$x,0 $x,$maxy",stroke=>$self->{'gridcolor'},skewX=>-$anglegrid);
        }
        for my $i(-$maxx*sin($anglegrid*3.14/180)/(cos($anglegrid*3.14/180)*10)..$maxy/10){
          my $y=$i*10/cos($anglegrid*3.14/180)-$delta;
          $image->Draw(primitive=>'line',points=>"0,$y $maxx,$y",stroke=>$self->{'gridcolor'},skewY=>$anglegrid);
        }
        for my $i(0..$passlenght-1){
          my $mycolor=$self->random('fontcolors');
          my $randomr=(5-rand(10))*2*$self->{'scatter'};my $randomx=(5-rand(10))*2*$self->{'scatter'};my $randomy=(5-rand(10))*2*$self->{'scatter'};
          $image->Annotate(text=>$ipass[$i], font=>$self->{'font'},fill=>"$mycolor",
                         x=>$delta+$self->{'fontsize'}*$i+rand($delta)*2*$self->{'scatter'}, y=>$self->{'fontsize'}+$delta-rand($delta)*2*$self->{'scatter'},
                         skewY=>$randomy, skewX=>$randomx,rotate=>$randomr, pointsize=>$self->{'fontsize'});
        }
        if($self->{'addnoise'}){
          # $image->AddNoise(noise=>"Laplacian",channel=>"Cyan,Magenta");
#   $image->AddNoise(noise=>"$noise_type",channel=>"Cyan,Magenta");
 #   $image->Charcoal(geometry=>'100x100+100+100', radius=>$maxx, sigma=>0);
#        $image->Emboss(0.2);
#$image->RadialBlur(angle=>27,width=>$maxx."x".$maxy,width=>$maxx);

#$image->Plasma(1);
#$image->Swirl(40);
$image->Spread(2);

        }
$image -> ImageToBlob();
}
sub generatepass {
    my $self = shift;
    my $pl=shift;
    my ($checksum,$char);
    if($pl =~/^\d$/){$self->{'passlength'}=$pl;}
    for(1..$self->{'passlength'}){
      $char=$self->random('chars');
      $self->{'pass'}.=$char;
      foreach(split(//,$char)){$checksum+=ord}
    }
    my $extime=time()+$self->{'expires'}*60;
    $self->{'hidden'}=sprintf("%02d", substr($checksum,-2)).$extime.$self->{'passlength'}.$self->{'pass'};
    $self->{'pass'}=sprintf("%02d", substr($checksum,-2)).$self->{'pass'}.sprintf("%02d", (localtime)[0]);
    $self->{'hidden'}=$self->encrypt($self->{'hidden'},$self->{'encryptkey'});
    $self->{'pass'}=$self->encrypt($self->{'pass'},$self->{'encryptkey'});
($self->{'pass'},$self->{'hidden'});    
}
sub checkpass{
    my $self = shift;
    my ($hcode,$vcode) = @_;
    my ($check,$checksum);
    $hcode=$self->decrypt($hcode,$self->{'encryptkey'});
    my ($cs,$et,$pl,$ph)=($1,$2,$3,$4) if $hcode=~m/^(\d{2})(\d{10})(\d)(\w+)$/;
    my @ipass=split(//,$ph);
    foreach (@ipass){$checksum+=ord}
    if($ph eq $vcode and $cs eq sprintf("%02d", substr($checksum,-2)) and $et =~ /^\d+$/ and $et > time()){$check=1;}
    elsif($ph eq $vcode and $cs eq sprintf("%02d", substr($checksum,-2))){$check=2;}
    else{$check=3;}
$check;    
}
sub passlength {
    my $self = shift;
    $self->{'passlength'} = $_[0]  if defined $_[0] and $_[0] =~ m/^\d$/;
}
sub chars {
    my $self = shift;
    $self->{'chars'} = $_[0]  if defined $_[0];
}
sub font {
    my $self = shift;
    $self->{'font'} = $_[0]  if defined $_[0];
}

sub noise_type {
    my $self = shift;
    $self->{'noise_type'} = $_[0]  if defined $_[0];
}


sub fontsize {
    my $self = shift;
    $self->{'fontsize'} = $_[0]  if defined $_[0] and $_[0] =~ m/^\d+$/;
}
sub fontcolors {
    my $self = shift;
    $self->{'fontcolors'} = $_[0] if defined $_[0];
}
sub bgcolors {
    my $self = shift;
    $self->{'bgcolors'} = $_[0] if defined $_[0];
}
sub gridcolor {
    my $self = shift;
    $self->{'gridcolor'} = $_[0] if defined $_[0];
}
sub addnoise {
    my $self = shift;
    $self->{'addnoise'} = $_[0] if defined $_[0];
}
sub scatter {
    my $self = shift;
    $self->{'scatter'} = $_[0] if defined $_[0] and $_[0] <= 1 and $_[0] >= 0;
}
sub expires {
    my $self = shift;
    $self->{'expires'} = $_[0]  if defined $_[0] and $_[0] =~ m/^\d+$/;
}
sub encryptkey {
    my $self = shift;
    $self->{'encryptkey'} = $_[0]  if defined $_[0] and $_[0] =~ m/^\w+$/;
}
sub random {
    my $self = shift;
    $self -> {"$_[0]"}[rand(scalar(@{$self -> {"$_[0]"}}))];
}
sub encrypt{
    my $self = shift;
    my ($message, $key) = @_;
    my $result = &string2hex(&RC4($message,$key));
    return ($result);
}

sub decrypt{
    my $self = shift;
    my ($encode, $key) = @_;
    my $result = &RC4(&hex2string($encode),$key);
    return ($result);
}

sub RC4 {
    my($message,$key) = @_;
        my($RC4);
        my(@asciiary);
        my(@keyary);
        my($index,$jump,$temp,$y,$t,$x,$keylen);
        if (length($key) == 0) { exit; }
        if (length($message) == 0) { exit; }
        $keylen = length($key);
        for ($index = 0; $index <= 255; $index++) {
              $keyary[$index] = ord(substr($key, ($index%$keylen) + 1, 1));
        }

        for ($index = 0; $index <= 255; $index++) {
              $asciiary[$index] = $index;
        }

        $jump = 0;

        for ($index = 0; $index <= 255; $index++) {

            $jump = ($jump + $asciiary[$index] + $keyary[$index])%256;
            $temp = $asciiary[$index];
            $asciiary[$index] = $asciiary[$jump];
            $asciiary[$jump] = $temp;
        }

        $index = 0;
        $jump = 0;

        for ($x = 0; $x < length($message); $x++) {
            $index = ($index + 1)%256;
            $jump = ($jump + $asciiary[$index])%256;

            $t = ($asciiary[$index] + $asciiary[$jump])%256;

            $temp = $asciiary[$index];
            $asciiary[$index] = $asciiary[$jump];
            $asciiary[$jump] = $temp;

            $y = $asciiary[$t];

            $RC4 .= chr(ord(substr($message, $x, 1))^$y);
        }
        return($RC4);
}
sub string2hex {
    my($instring) = @_;
    my($retval,$strlen,$posx,$tval,$h1,$h2);
    my(@hexvals) = ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');

    $strlen = length($instring);
    for ($posx = 0; $posx < $strlen; $posx++) {
          $tval = ord(substr($instring,$posx,1));
          $h1 = int($tval/16);
          $h2 = int($tval - ($h1*16));
          $retval .= $hexvals[$h1] . $hexvals[$h2];
    }
    return($retval);
}

sub hex2string {
    my($instring) = @_;
    my($retval,$strlen,$posx);
    $strlen = length($instring);
    for ($posx = 0; $posx < $strlen; $posx=$posx+2) {
          $retval .= chr(hex(substr($instring,$posx,2)));
    }
    return($retval);
}
sub encrypt1 {
    my $self = shift;
    my ($source,$key) = @_;
    my $pub_key = 'H3sDmK1FhZxVCd4vnMp5zOiyk9ASc76X';
    my ($cr,$index,$char,$key_char,$enc_string,$encode,$first,
        $second,$let1,$let2,$encrypted,%escapes) = '';
    $source = &rot13($source);
    while ( length($key) < length($source) ) { $key .= $key }
    $key=substr($key,0,length($source));
    while ($index < length($source)) {
        $char = substr($source,$index,1);
        $key_char = substr($key,$index,1);
        $enc_string .= chr(ord($char) ^ ord($key_char));
        $index++;
    }
    for (0..255) { $escapes{chr($_)} = sprintf("%2x", $_); }
    $index=0;
    while ($index < length($enc_string)) {
        $char = substr($enc_string,$index,1);
        $encode = $escapes{$char};
        $first = substr($encode,0,1);
        $second = substr($encode,1,1);
        $let1=substr($pub_key, hex($first),1);
        $let2=substr($pub_key, hex($second),1);
        $encrypted .= "$let1$let2";
        $index++;
    }
    return $encrypted;
}
# also you can use other encryption/decryption mechanism. Simple rename routines below to decrypt and encrypt.
sub decrypt1 {
    my $self = shift;
    my ($encrypted, $key) = @_;
    my $pub_key = 'H3sDmK1FhZxVCd4vnMp5zOiyk9ASc76X';
    my ($cr,$index,$decode,$decode2,$char,$key_char,$dec_string,$decrypted) = '';
    while ( length($key) < length($encrypted) ) { $key .= $key }
    $key=substr($key,0,length($encrypted));
    while ($index < length($encrypted)) {
        $decode = sprintf("%1x", index($pub_key, substr($encrypted,$index,1)));
        $index++;
        $decode2 = sprintf("%1x", index($pub_key, substr($encrypted,$index,1)));
        $index++;
        $dec_string .= chr(hex("$decode$decode2"));
    }
    $index=0;
    while( $index < length($dec_string) ) {
        $char = substr($dec_string,$index,1);
        $key_char = substr($key,$index,1);
        $decrypted .= chr(ord($char) ^ ord($key_char));
        $index++;
    }
    return &rot13( $decrypted );
}
sub rot13{
    my $source = shift (@_);
    $source =~ tr /[a-m][n-z]/[n-z][a-m]/;
    $source =~ tr /[A-M][N-Z]/[N-Z][A-M]/;
    $source = reverse($source);
    return $source;
} 
1;