#!/usr/bin/perl
# cpanel10 - cPAddons.pm                          Copyright(c) 2006 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

package Cpanel::cPAddons;

use strict;

BEGIN { push @INC, '/usr/local/cpanel', '/scripts'; }

use vars qw(@ISA @EXPORT $VERSION);
require Exporter;
require DynaLoader;
@ISA = qw(Exporter DynaLoader);
@EXPORT = qw(cPAddons_init cPAddons_mainpg);
$VERSION = '0.5.1';

sub cPAddons_init {
    $Cpanel::NEEDSREMOTEPASS{'cPAddons'} = 1;
    return 1;
}

use Cpanel;
use Cpanel::Form;
use Cpanel::Park;
use Cpanel::Logger;
use Cpanel::DbUtils;
eval 'use Cpanel::Htaccess;';
eval 'use Cpanel::SubDomain;';
use Cpanel::SafetyBits;
use Cpanel::HttpRequest;
use Cpanel::cPanelFunctions;
use iContact ();
use Storable;
use Digest::MD5;
use IPC::Open2;

my $version = '0.2';
my $base = '/usr/local/cpanel/cpaddons/'; # ie wherever, just so it has the trailing slash
push @INC, $base;

my $feat = defined $Cpanel::CPDATA{FEATURELIST} && -e "/var/cpanel/features/$Cpanel::CPDATA{FEATURELIST}.cpaddons" ? $Cpanel::CPDATA{FEATURELIST} : 'default';
my %disallowed_feat;
if (open FL, "/var/cpanel/features/$feat.cpaddons") {
    while(<FL>) {
        chomp;
        my($f,$s) = split /=/;
        $disallowed_feat{$f} = 1 if !$s;
    }
    close FL;
}

my $_self = $ENV{SCRIPT_NAME};
my $mod = $main::FORM{'addon'};

my $force = 'I fully understand what I am doing and take full responsibility for my actions. I have backed up all my data so I can remove the installation, reinstall fresh and import my old info into the new install if necessary. I understand that anything that breaks by forcing this upgrade is 100% my responsibility.';

if ($main::FORM{'setdefault'}) {
    open SD, '>', "$Cpanel::homedir/.cpaddons_defaultvendor"; # or cplog  ???
    print SD $main::FORM{setdefault};
    close SD;
}

my $defaultvendor = '';
if (-e "$Cpanel::homedir/.cpaddons_defaultvendor") {
    open DV, "$Cpanel::homedir/.cpaddons_defaultvendor"; # or cplog ???
    my $tmp = <DV>;
    close DV;
    chomp($tmp);
    $defaultvendor = $tmp if defined $tmp;
}

# to do get from config
my $moderation_req_notif_level = 1; # 0 is off
my $max_moderation_req_all_mod = 6;
my $max_moderation_req_per_mod = 3;
my $lowest_allowed             = 0;
my $no_modified_cpanel         = 0; #1; # not list/work if $cpinc eq '0'
my $no_3rd_party               = 0; # not list/work if $cpinc eq ''

my %approvedpals = (
                    'cPanel' => {
                                    'weburl' => 'http://www.cpanel.net/cpaddons.pl',
                                    'securl' => 'http://www.cpanel.net/cpaddons.pl', # ?id=$meta_info{security_id}
                                    'palmd5' => '382c1ad1d1e50bf1e77a3b17ff3d1e96', # md5sum of cPAddonsMd5::$pal.pm
                                    'tarurl' => '', # url to tar.gz that gets untarred into $base$pal
                                    'secimg' => '../images', # url to directory with security_rank gifs, non https will cause browser warnings  in SSL cPanel...
                                },
                    );

my $pal = defined $main::FORM{'changevendor'}
          && exists $approvedpals{ $main::FORM{'changevendor'} }
          ? $main::FORM{'changevendor'}                          : exists $approvedpals{$defaultvendor}
          ? $defaultvendor                                       : 'cPanel';

if ($mod) {
    my ($xpal) = split /\:\:/, $mod;
    $pal = $xpal if defined $xpal && exists $approvedpals{$xpal};
}

eval "use cPAddonsMD5::$pal;";
die $@ if $@;

my %cpanelincluded;
no strict 'refs';
for(keys %approvedpals) {
    $cpanelincluded{$_} = \%{"cPAddonsMD5\:\:$_\:\:cpaddons"};
}
use strict 'refs';

my $chgvenlink;
my $choosevend;
for (sort keys %approvedpals) {
    $chgvenlink .= qq(<a href="$_self?changevendor=$_">$_</a> ) if $_ ne $pal;
    $choosevend .= $_ eq $defaultvendor ? qq($_ (Default) ) : qq(<a href="$_self?setdefault=$_">$_</a> );
}

# print qq(<p>cPAddons v$version Current Vendor: <b><a target="_blank" href="$approvedpals{$pal}->{weburl}">$pal</a></b> <!--(<a href="$_self?check=1&changevendor=$pal">Check $pal Library Status</a>)--> <br />Browse Vendor: $chgvenlink\n<br />Set Default vendor: $choosevend\n) if scalar keys %approvedpals > 1;

if ($main::FORM{'check'}) {
    my $is = _file_md5_hex("${base}cPAddonsMD5/$pal.pm");
    print "<p>$pal Library Status: ";
    if ($is eq  $approvedpals{$pal}->{palmd5}) {
        print qq(<b>Ok</b> No further action is needed.</p>);
    }
    else {
        print qq(<b>Invalid</b> Please have your admin update the cpaddons libraries!</p><!--\nLocal $is\nAturl $approvedpals{$pal}->{'palmd5'}\n-->);
    }
}

#### temporary until SubDOmains.pm patch gets applied ##
sub get_subs_parked_hr {
    my %SUBS = Cpanel::SubDomain::listsubdomains();
    my @PSUBS;
    my %PN;
    foreach (keys %SUBS) {
        my($pname) = split(/_/, $_);
        s/_/\./g;
        push(@PSUBS,$_);
        $PN{$_} = $pname;
    }
    my %PARKED = Cpanel::Mime::getmultiparked(@PSUBS);
    return \%SUBS, \%PARKED;
}

sub subdomain_docroot_map {
    my($s,$p) = get_subs_parked_hr();
    my %SUBS = %{ $s };
    my %PARKED = %{ $p };
    foreach my $rd (keys %PARKED) {
        foreach my $pd (keys %{$PARKED{$rd}}) {
            $SUBS{$pd} = $PARKED{$rd}{$pd};
        }
    }
    return \%SUBS, \%PARKED;
}
### end  temp ##

sub cPAddons_mainpg {
    # Check addoncgi feature
    return if (! main::hasfeature('addoncgi'));
    if ($Cpanel::CPDATA{'DEMO'} eq '1') {
        print "Sorry this feature is disabled in demo mode";
        return;
    }

    print qq(<style>#er { color: #FF0000; }</style>\n);
    my $input_hr = shift;

    if(exists $input_hr->{'asuser'} && $input_hr->{'asuser'} ne 'root' && $> == 0 && $< == 0) {
        Cpanel->new->initcp($input_hr->{'asuser'});
    }
    delete $input_hr->{'asuser'}; # just to be safe

    if ($input_hr->{'action'} eq 'notify') {
        if ($input_hr->{'on'} && !-e "$Cpanel::homedir/.cpaddons_notify") {
            if (open NOT, '>', "$Cpanel::homedir/.cpaddons_notify") {
                print NOT '';
                close NOT;
            } else {
                print qq(<p id="er">Could not turn notifications on. Create $Cpanel::homedir/.cpaddons_notify manually.</p>\n);
            }
        } elsif (!$input_hr->{'on'} && -e "$Cpanel::homedir/.cpaddons_notify") {
            unlink "$Cpanel::homedir/.cpaddons_notify"
                or print qq(<p id="er">Could not turn notifications off. Remove $Cpanel::homedir/.cpaddons_notify manually.</p>\n);
        }
    }

    my $mod = $input_hr->{'addon'};
    if ($mod) {
        my $file = $mod;
        $file =~ s/\:\:/\//g;
        $file .= '.pm';
        my ($pl) = $mod =~ m/^(\w+)/;
        if (-e $base . $file) {
            my $md5 = _file_md5_hex($base . $file);
            if (exists $cpanelincluded{$pl}->{$mod}) {
                if ($cpanelincluded{$pl}->{$mod}->{'md5'} eq $md5) {
                    _handlemod(1,$mod,$md5,$input_hr);
                } else {
                    _handlemod(0,$mod,$md5,$input_hr)
                    }
            } else {
                _handlemod('',$mod,$md5,$input_hr)
                }
        } else {
            print "<p>Invalid module</p>";
            _listallmods();
        }
    } else {
        _listallmods();
    }
}

#### primary funkshuns ##

sub _listallmods {
    my %cpaddons;
    my %xaddon_meta;
    $xaddon_meta{'totalcount'} = 0;
    for my $pal(keys %approvedpals) { # remove this for() and the vendor stuff will all be seperate
        if (opendir BASE, $base . $pal) {
            my @cats = grep !/^\.+$/, readdir BASE;
            close BASE;
            for my $cat (@cats) {
                next if !-d "$base$pal/$cat";
                my $catname = $cat;
                $catname =~ s/\_/ /g;
                $xaddon_meta{$pal}->{$catname} = 0;
                if (opendir SUB, "$base$pal/$cat") {
                    my @mods = grep /\.pm$/, readdir SUB;
                    close SUB;
                    for(@mods) {
                        my $name = $_;
                        $name =~ s/\.pm$//;
                        my $mdnm = "$pal\:\:$cat\:\:$name";
                        $name =~ s/\_/ /g;
                        if (!exists $disallowed_feat{$mdnm}) {
                            $cpaddons{$catname}->{$mdnm} = $name;
                            $xaddon_meta{$pal}->{$cat}++;
                            $xaddon_meta{'totalcount'}++;
                        }
                    }
                } else {
                    print "<p>Could not read $base$pal/$_: $!</p>\n";
                }
            }
        } else {
            print "<p>Could not read $base$pal: $!</p>";
        }
    }
    return \%cpaddons if shift() eq 'hashref';
    # display %cpaddons here how you like
    my @registries;
    if (-d "$Cpanel::homedir/.cpaddons/") {
        if ( opendir(DIRX, "$Cpanel::homedir/.cpaddons/") ) {
            @registries = grep !/^\./, readdir DIRX;
            closedir DIRX;
        }
    }
    my %count_ct;

    print qq(<h3>cPAddon Scripts v$VERSION</h3>\n);
    if (-e "$Cpanel::homedir/.cpaddons_notify") {
        print qq(<p>You currently are set to receive a notice when updates for your installs are available. Click <a href="$main::_self?action=notify&on=0">here</a> to turn it off.</p>\n);
    }
    else {
        print qq(<p id="er">You currently are <b>not</b> set to receive a notice when updates for your installs are available. Click <a href="$main::_self?action=notify&on=1">here</a> to turn it on.</p>\n);
    }
    my $tbl;
    my $ctlnk = "<ul>\n";
    for my $ct (sort keys %cpaddons) {
        my $ctcnt = scalar keys %{ $cpaddons{$ct} };
        $ctlnk .= qq(  <li><a href="#$ct">$ct</a>[% count_$ct %]</li>\n);
        $tbl .= qq(<tr><td colspan="2" class="addontable"> $ct<a name="$ct" border="0">&nbsp;</a>($ctcnt) </td></tr>\n);
        my $sec = 0;
        my $hlf = $ctcnt / 2;
        if ($hlf =~ m/\.5/) {
            $hlf =~ s/\.5$//;
            $hlf++;
        }
        for my $ao (sort keys %{ $cpaddons{$ct} }) {
            $tbl .= qq(<tr valign="top"><td><ul>\n) if $sec == 0;
            $sec++;
            my ($pl) = $ao =~ m/^(\w+)/;
            my ($base) = $ao =~ m/^\w+\:\:(.*)/;
            my $samect = 0;
            for(keys %approvedpals) {
                $samect++ if exists $cpaddons{$ct}->{"$_\:\:$base"};
            }
            my $vdrdisp = $samect >= 2 ? " (from $pl)" : '';
            my @thisone = grep /^\Q$ao\E\.\d+/, @registries;
            my $cnt = @thisone;
            $count_ct{$ct} += $cnt;
            my $list = @thisone ? qq( (<span id="er">$cnt installed</span>)) : '';
            my $what = Cpanel::Form::escapehtml( $cpanelincluded{$pl}->{$ao}->{desc} ) || 'No Description Provided';
            $tbl .= qq(  <li><a href="$_self?addon=$ao">$cpaddons{$ct}->{$ao}</a>$vdrdisp$list<br />$what</li>\n);
            $tbl .= qq(</ul></td><td><ul>\n) if $sec == $hlf;
            $tbl .=  "</ul></td></tr>\n" if $sec == $ctcnt;
        }
    }
    for(keys %count_ct) {
        my $disp = $count_ct{$_} ? qq( (<span id="er">$count_ct{$_} installed</span>)) : '';
        $ctlnk =~ s/\[\% count_\Q$_\E \%\]/$disp/;
    }
    $ctlnk =~ s/\[\% count_\w+ \%\]//g;
    print qq($ctlnk</ul>\n<table align="center" width="97%" class="addontable">\n$tbl</table>\n);
}

sub _handlemod {
    my($cpinc,$mod,$md5,$input_hr) = @_;
    my $pathx = $mod;
    $pathx =~ s/\:\:/\//g;
    $pathx =~ s/\.pm$//;
    if (exists $disallowed_feat{$mod}) {
        print qq(<p id="er">Invalid module.<br />\n</p>\n<!--\n$@\n-->\n);
        _listallmods();
    } else {
        eval "use $mod;";
        if ($@) {
            print qq(<p id="er">Sorry, invalid module.<br />\n</p>\n<!--\n$@\n-->\n);
            _listallmods();
        } else {
            my %tmp_disabled = (1=>'',2=>'',3=>'',4=>'');
            if (open DIS, '<', '/var/cpanel/cpaddons_disabled') {
                binmode DIS;
                my $alldis_hr = Storable::fd_retrieve(\*DIS);
                close DIS;
                if (exists $alldis_hr->{$mod}) {
                    my $dis = 1;
                    if ($alldis_hr->{$mod}->{'typ'} eq 'time') {
                        $dis = 0 if time() > $alldis_hr->{$mod}->{'tyv'};
                    } elsif ($alldis_hr->{$mod}->{'typ'} eq 'mver') {
                        my $okv = $alldis_hr->{$mod}->{'tyv'};
                        $okv =~ s/\.|\_//g;
                        no strict 'refs';
                        my $crv = ${"$mod\:\:VERSION"};
                        use strict 'refs';
                        $crv =~ s/\.|\_//g;
                        my $crvlen = length $crv;
                        my $okvlen = length $okv;
                        if ($crvlen < $okvlen) {
                            $crv .= 0 x ($okvlen - $crvlen);
                        } elsif ($okvlen < $crvlen) {
                            $okv .= 0 x ($crvlen - $okvlen);
                        }
                        $dis = 0 if $crv >= $okv;
                    } elsif ($alldis_hr->{$mod}->{'typ'} eq 'sver') {
                        my $okv = $alldis_hr->{$mod}->{'tyv'};
                        $okv =~ s/\.|\_//g;
                        no strict 'refs';
                        my $crv = ${"$mod\:\:meta_info"}->{'version'};
                        use strict 'refs';
                        $crv =~ s/\.|\_//g;
                        my $crvlen = length $crv;
                        my $okvlen = length $okv;
                        if ($crvlen < $okvlen) {
                            $crv .= 0 x ($okvlen - $crvlen);
                        } elsif ($okvlen < $crvlen) {
                            $okv .= 0 x ($crvlen - $okvlen);
                        }
                        $dis = 0 if $crv >= $okv;
                    }
                    if ($dis) {
                        for( split /,/, $alldis_hr->{$mod}->{'dis'} ) {
                            $tmp_disabled{$_} = $alldis_hr->{$mod}->{'msg'};
                        }
                    } else {
                        delete $alldis_hr->{$mod};
                        if (open DXS, '>', '/var/cpanel/cpaddons_disabled') {
                            binmode DXS;
                            Storable::store_fd($alldis_hr, \*DXS) or return;
                            close DXS;
                        } else { print "<!-- Could not update disalist: $! -->\n"; }
                    }
                }
            }

            my $info_hr = {};
            no strict 'refs';
            $info_hr = ${"$mod\:\:meta_info"};
            my $modver = ${"$mod\:\:VERSION"};
            use strict 'refs';

            # only allow aliases that were intendedby vendor to be aliases, other wise ignore ??
            if (exists $info_hr->{'alias'} && $info_hr->{'alias'}) { # && $cpinc eq '1') {
                print $info_hr->{'description'} ? $info_hr->{description} : 'This is an alias for:';
                print qq(<p align="center">[<a href="$_self?addon=$info_hr->{'alias'}">$info_hr->{'alias'}</a>]</p>\n);
                exit;
            }

            $info_hr->{'adminarea_path'} =~ s/[^\w\/]//g;
            my $stat = "<p>This is not a $pal module. Please contact the maintainer for support. (v$modver)</p>";
            $stat = "<p>This is a modified $pal module. Please contact the maintainer for support. (v$modver)</p>" if $cpinc eq '0';
            $stat = "<p>This is a $pal provided module. (v$modver)</p>" if $cpinc eq '1';
            print "$stat\n";
            if (defined $info_hr->{'security_id'} && $info_hr->{'security_id'} =~ m/^\d+$/) {
                print qq(<p><a target="_blank" href="$approvedpals{$pal}->{'securl'}?id=$info_hr->{'security_id'}">$pal Security Advisory</a> (<b>*only* valid if MD5 sum at that link is $md5</b>)</p>\n);
            }
            print qq(Packaged by: $info_hr->{'packager_name'}<br />) if $info_hr->{'packager_name'};
            $info_hr->{'security_rank'} = 0 if $info_hr->{'security_rank'} !~ m/^\d+$/;
            $info_hr->{'security_rank'} = 10 if $info_hr->{'security_rank'} > 10;
            print qq(Security Ranking: ($info_hr->{'security_rank'} out of 10) <img align="absmiddle" src="$approvedpals{$pal}->{secimg}/security_rank_$info_hr->{'security_rank'}.gif" /> <br />\n) if $info_hr->{'security_rank'};
            print qq(<a target="_blank" href="$info_hr->{'website'}">Website</a> ) if $info_hr->{'website'};
            print qq(<a target="_blank" href="$info_hr->{'documentation_url'}">Documentation</a> ) if $info_hr->{'documentation_url'};
            print qq(<a target="_blank" href="$info_hr->{'support_url'}">Support</a> ) if $info_hr->{'support_url'};
            print qq(<a target="_blank" href="$info_hr->{'maintainer_url'}">Maintainer</a> ) if $info_hr->{'maintainer_url'};
            print qq(<a target="_blank" href="$info_hr->{'changelog_url'}">Change Log</a> ) if $info_hr->{'changelog_url'};
            print "<p><hr /></p>\n";

            if (!$info_hr->{'security_rank'} || $info_hr->{'security_rank'} < $lowest_allowed) {
                print "<p>Sorry this addon is below the approved security ranking and can not be used, contact your server admin for more info.</p>\n";
            } elsif ($no_modified_cpanel && $cpinc eq '0') {
                print "<p>Sorry modified $pal addons are not allowed, contact your server admin for more info.</p>\n";
            } elsif ($no_3rd_party && $cpinc eq '') {
                print "<p>Sorry 3rd party addons are not allowed, contact your server admin for more info.</p>\n";
            } else {
                my $extras = '';
                my %actions;
                for(qw(install upgrade uninstall installform upgradeform uninstallform)) {
                    my $nm = "$mod\:\:$_";
                    eval { $actions{$_} = \&$nm; };
                }
                $actions{'movecopy'} = \&movecopy unless $info_hr->{'nomovecopy'};
                if (defined $info_hr->{'specialfunctions'} && ref $info_hr->{'specialfunctions'} eq 'HASH') {
                    for(keys %{ $info_hr->{'specialfunctions'} }) {
                        if (ref $info_hr->{'specialfunctions'}->{$_}->{'code'} eq 'CODE' && !exists $actions{$_}) {
                            $actions{$_} = $info_hr->{'specialfunctions'}->{$_}->{'code'};
                            $extras .= qq(<p><a href="$_self?addon=$mod&action=$_">$info_hr->{'specialfunctions'}->{$_}->{name}</a></p>\n);
                        }
                    }
                }
                my $err = '';
                my $obj = bless {}, __PACKAGE__;
                $obj->{'form_domain_menu'} = "http://$Cpanel::CPDATA{DNS}";
                $obj->{'unix_time'} = time;
                my %subdommap;
                my %parkedhsh;
                @parkedhsh{ Cpanel::Park::_getparked() } = ();
                my $inputsub_is_inhash = 0;
                my $subdomain_path = '';
                my($s,$p) = subdomain_docroot_map(); #use full package name once using from SubDomain.pm
                if (%{ $s } || %parkedhsh) {
                    for my $pk (keys %{ $p }) {
                        for(keys %{ $p->{$pk} }) {
                            $inputsub_is_inhash++ if $input_hr->{'subdomain'} eq $p->{$pk}->{$_};
                            $subdommap{ $p->{$pk}->{$_} } = $_ if !exists $subdommap{ $p->{$pk}->{$_} };
                        }
                    }
                    for(keys %{ $s }) {
                        $inputsub_is_inhash++ if $input_hr->{'subdomain'} eq $_;
                        $subdommap{ $s->{$_} } = $_ if !exists $subdommap{ $s->{$_} };
                    }
                    my $domsel = !$input_hr->{'subdomain'} || !$inputsub_is_inhash
                        || $input_hr->{'subdomain'} eq $Cpanel::CPDATA{'DNS'} ? ' selected' : '';
                    $obj->{'form_domain_menu'} = qq(<select name="subdomain">\n);
                    $obj->{'form_domain_menu'} .= qq(  <option value=""$domsel>$Cpanel::CPDATA{'DNS'}</option>\n);
                    for(sort keys %subdommap) {
                        my $tmpdomsel = $input_hr->{'subdomain'} eq $subdommap{$_} ? ' selected' : '';
                        $subdomain_path = $_ if $input_hr->{'subdomain'} eq $subdommap{$_};
                        $obj->{'form_domain_menu'} .= qq(  <option value="$subdommap{$_}"$tmpdomsel>$subdommap{$_}</option>\n);
                    }
                    $obj->{'form_domain_menu'} .= qq(</select>\n)
                }

                if ($input_hr->{'subdomain'}) {
                    if (exists $parkedhsh{ $input_hr->{'subdomain'} }) {
                        $obj->{'parked_domain'} = $input_hr->{'subdomain'};
                    } elsif (!$inputsub_is_inhash) {
                        $err .= qq(<p id="er">Invalid subdomain specified</p>\n);
                    } else {
                        $obj->{'subdomain_name'} = $input_hr->{'subdomain'};
                        $obj->{'subdomain_path_raw'} = $subdomain_path;
                        $subdomain_path =~ s/\Q$Cpanel::homedir\E\/?public_html\/?//;
                        $obj->{'subdomain_path'} = $subdomain_path;
                        $obj->{'subdomain_path'} =~ s/^\/|\/$//g;
                    }
                }

                $obj->{hostname} = Cpanel::cPanelFunctions::gethostname();
                $obj->{email} = $input_hr->{email} || $Cpanel::USERDATA{'contact_email'}; # || "$Cpanel::user\@$obj->{hostname}";
                $obj->{addon} = $mod;
                $obj->{addon_path} = $obj->{addon};
                $obj->{addon_path} =~ s/\:\:/\//g;
                $obj->{addon_path} =~ s/\w+\.pm$//;
                $obj->{adminarea_name} =~ s/[^\w ]//g;

                if (!-d "$Cpanel::homedir/.cpaddons/") {
                    mkdir  "$Cpanel::homedir/.cpaddons/" or die "Unable to create user .addon directory: $!";
                }
                opendir AOD, "$Cpanel::homedir/.cpaddons/"; # or die "Unable to read user .addon directory: $!;
                for(readdir AOD) {
                    next if m/^\.+$/;
                    next unless m/\.\d+$/;
                    #   s/\:/\\\:/g;
                    open RG, '<', "$Cpanel::homedir/.cpaddons/$_" or next;
                    binmode RG;
                    $obj->{installed}->{$_} = Storable::fd_retrieve(\*RG);
                    close RG;
                }
                closedir AOD;
                my $moderated = {};
                my $okfile = '';
                my $permok = 0;
                my $total_requests = 0;
                my $mod_requests = 0;
                my @mreqs;

                if (-d "$Cpanel::homedir/.cpaddons/moderation/") {
                    if (opendir MDT, "$Cpanel::homedir/.cpaddons/moderation/") {
                        my @treqs =  grep /\.\d+$/, readdir MDT;
                        closedir MDT;
                        $total_requests = @treqs;
                        @mreqs = grep /^\Q$mod\E\.\d+$/, @treqs;
                        $mod_requests = @mreqs;
                    } else {
                        $err .= qq(<p id="er">Moderation found but not readable. Contact your server admin!</p>\n);
                    }
                } else { mkdir "$Cpanel::homedir/.cpaddons/moderation/"; }

                if (-e '/var/cpanel/cpaddons_moderated') {
                    if (open MDR, '<', '/var/cpanel/cpaddons_moderated') {
                        binmode MDR;
                        $moderated = Storable::fd_retrieve(\*MDR);
                        close MDR;
                    } else {
                        $err .= qq(<p id="er">Moderation detected but unreadable, Aborting. Contact your server admin!</p>\n);
                    }
                }

                if (exists $moderated->{$mod} && $input_hr->{action} eq 'sendmodreq') {
                    $actions{sendmodreq} = sub { print $err };
                    if ($total_requests >= $max_moderation_req_all_mod) {
                        $err .= "<p>Sorry, you have reached your maximum Addon Moderation Request limit.</p>";
                    } elsif ($mod_requests >= $max_moderation_req_per_mod) {
                        $err .= "<p>Sorry, you have reached your maximum Addon Moderation Request limit for $mod.</p>";
                    } else {
                        my $uniq = 0;
                        while(-e "$Cpanel::homedir/.cpaddons/moderation/$mod.$uniq") { $uniq++; }
                        if (open NEWR, '>', "$Cpanel::homedir/.cpaddons/moderation/$mod.$uniq") {
                            Storable::store_fd {date=>time(), msg=> $input_hr->{request_note}, res=>0}, \*NEWR;
                            close NEWR;
                            chmod 0600, "$Cpanel::homedir/.cpaddons/moderation/$mod.$uniq"
                                or $err .= qq(<p id="er">Could not chmod $Cpanel::homedir/.cpaddons/moderation/$mod.$uniq to 600: $!<br />This will need done manually!</p>\n);
                            if ($moderation_req_notif_level) {
                                iContact::icontact('sendmodreq',$moderation_req_notif_level,'Moderated Xaddon CGI Request',
                                    "$Cpanel::user has just requested permission to install $mod.\nPlease login to WHM and handle this pending moderation request." );
                            }
                            $err .= "Your Request has been sent! You will receive an email at your contact address or system account once the admin has responded.";
                        } else {
                            $err .= qq(<p id="er">Could not create request! $!</p>\n);
                        }
                    }
                }

                if (exists $moderated->{$mod} && $input_hr->{action} eq 'install') {
                    my $approved = 0;
                    for(@mreqs) {
                        if (open CUR, '<', "$Cpanel::homedir/.cpaddons/moderation/$_") {
                            binmode CUR;
                            my $stash = Storable::fd_retrieve(\*CUR);
                            close CUR;
                            if ($stash->{res} > 0) {
                                if (open APP, '<', "/var/cpanel/cpaddons_moderation/$stash->{res}") {
                                    binmode APP;
                                    my $apprv = Storable::fd_retrieve(\*APP);
                                    close APP;
                                    if ($apprv->{ok}) {
                                        next if $apprv->{requser} ne $Cpanel::user;
                                        next if $apprv->{mod} ne $mod;
                                        $approved = 1;
                                        $permok = $apprv->{permanent};
                                        $okfile= $_;
                                        $obj->{_moderated_id} = $_;
                                        last;
                                    }
                                }
                                else {
                                    print "<!-- Failed: /var/cpanel/cpaddons_moderation/$stash->{res} : $! -->\n";
                                }
                            }
                        }
                        else {
                            print "<!-- Failed: $Cpanel::homedir/.cpaddons/moderation/$_ : $! -->\n";
                        }
                    }
                    if (!$approved) {
                        $err .= qq(<p><b>Installs for $mod are being moderated.</b><br />You will need to request approval below:</p>\n);
                        if ($total_requests >= $max_moderation_req_all_mod) {
                            $err .= "<p>Sorry, you have reached your maximum Addon Moderation Request limit.</p>";
                        } elsif ($mod_requests >= $max_moderation_req_per_mod) {
                            $err .= "<p>Sorry, you have reached your maximum Addon Moderation Request limit for $mod.</p>";
                        } else {
                            my $s = $mod_requests == 1 ? '' : 's';
                            $err .= <<FORM;
   <h3 align="center">Submit Moderation approval for $mod</h3>
   <p>$moderated->{$mod}</p>
   <p>You currently have $mod_requests request$s for $mod</p>
   <form action="$_self" method="post">
     <input type="hidden" name="addon" value="$mod" />
     <input type="hidden" name="action" value="sendmodreq" />
     <textarea name="request_note" cols="51" rows="5"></textarea>
     <input type="submit" value="Submit Request" />
   </form>
FORM
                        }
                    }
                }
                if (defined $input_hr->{installdir} || $input_hr->{action} eq 'install') {
                    my $path = "$obj->{subdomain_path}/$input_hr->{installdir}";
                    $path =~ s/\/+/\//g;
                    $path =~ s/[^\w|\/|\-]//g;
                    $path =~ s/^\/|\/$//g;
                    if ($obj->{subdomain_path} && $obj->{subdomain_path} eq $path) {
                        $err .= qq(<p id="er">You are not allowed to install directly into a subdomain.</p>\n) unless exists $info_hr->{ $info_hr->{version} }->{public_html_install_files} && ref $info_hr->{ $info_hr->{version} }->{public_html_install_files} eq 'ARRAY';
                        for (keys %{ $obj->{installed} }) {
                            next unless m/^\Q$mod\E\.\d+/;
                            $err .= qq(<p id="er">There is already an install of this addon directly on top of that subdomain.</p>\n) if $obj->{installed}->{$_}->{installpath} eq $obj->{subdomain_path};
                        }
                    } elsif (!$path) {
                        $path = './' if exists $info_hr->{ $info_hr->{version} }->{public_html_install_files} && ref $info_hr->{ $info_hr->{version} }->{public_html_install_files} eq 'ARRAY';
                    } elsif (-d "$Cpanel::homedir/public_html/$path" && $path) {
                        $err .= qq(<p id="er">Directory ~/public_html/$path/ already exists!</p>\n) if -d "$Cpanel::homedir/public_html/$path";
                    }
                    if ($path eq './') {
                        for(keys %{ $obj->{installed} }) {
                            next unless m/^\Q$mod\E\.\d+/;
                            $err .= qq(<p id="er">There is already public_html install of this addon.</p>\n) if $obj->{installed}->{$_}->{installpath} eq './';
                        }
                    }
                    $obj->{version_key} = $info_hr->{ $info_hr->{version} };
                    $obj->{installpath} = $err ? '' : $path;
                    $err .= qq(<p id="er">You must specify an installation directory!</p>\n) if !$path && $input_hr->{action} eq 'install';
                }
                $info_hr->{license} = "$base$obj->{addon_path}/lisc" if !$info_hr->{license} && -e "$base$obj->{addon_path}/lisc";
                if ($info_hr->{license}) {
                    my $cont = '';
                    if (-e $info_hr->{license}) {
                        if (open LI, $info_hr->{license}) {
                            for(<LI>) { $cont .= $_; }
                            close LI;
                        } else {
                            $cont = "Could not open $info_hr->{license}: $!";
                        }
                    } elsif ($info_hr->{license} =~ m/^https?\:\/\//) {
                        # LWP::Simple:  $cont = get($info_hr->{license}) || "Error: Could not get $info_hr->{license}";
                        my($hst,$url) = $info_hr->{license} =~ m/https?\:\/\/([^\/]*)(\/.*)$/;
                        $cont =  Cpanel::HttpRequest::httpreqv2($hst,$url,1,"") || "Error: Could not get $info_hr->{license}";
                    } else {
                        $cont = $info_hr->{license};
                    }
                    my $lisc = Cpanel::Form::escapehtml($cont);
                    $obj->{form_install} .= qq(<center><textarea name="license" cols="90" rows="10">\n$lisc\n</textarea>\n<br />By Clicking Install you agree to the License terms.</center><br /><br />\n);
                }
                if ($info_hr->{adminuser_pass}) {
                    $obj->{form_install} .= qq(Admin User: <input type="text" name="auser" /> Admin Pass: <input type="text" name="apass" /><br />\n);
                    $obj->{username} = $input_hr->{auser} || '';
                    $obj->{password} = $input_hr->{apass} || '';
                    $obj->{username} =~ s/\W//g;
                    $obj->{password} =~ s/\W//g;
                    $obj->{salt} = $info_hr->{crypt_salt} =~ m/^\w+$/ ? $info_hr->{crypt_salt} : $obj->{username};
                    $obj->{password_crypt} = crypt($obj->{password}, $obj->{salt});
                    $obj->{password_md5} = Digest::MD5::md5($obj->{password});
                    $obj->{password_md5_hex} = Digest::MD5::md5_hex($obj->{password});
                    $obj->{password_md5_base64} = Digest::MD5::md5_base64($obj->{password});

                    if ($input_hr->{action} eq 'install') {
                        my $len = $info_hr->{admin_user_pass_length} =~ m/^\d+$/ ? $info_hr->{admin_user_pass_length} : 8;
                        my $max = $info_hr->{admin_user_pass_length_max} =~ m/^\d+$/ ? $info_hr->{admin_user_pass_length_max} : 0;
                        if (length $obj->{username} < $len) {
                            $err .= qq(<p id="er">Username needs to be at least $len letters, numbers, and underscores.</p>\n);
                        }
                        if (length $obj->{password} < $len) {
                            $err .= qq(<p id="er">Password needs to be at least $len letters, numbers, and underscores.</p>\n);
                        }
                        if ($max) {
                            if (length $obj->{username} > $max) {
                                $err .= qq(<p id="er">Username can only be $max letters, numbers, and underscores.</p>\n);
                            }
                            if (length $obj->{password} > $max) {
                                $err .= qq(<p id="er">Password can only be $max letters, numbers, and underscores.</p>\n);
                            }
                        }
                    }
                }
                if ($info_hr->{admin_email}) {
                    $obj->{form_install} .= qq(Email: <input type="text" name="email" /> (Note: leave blank to use your contact address if its set)<br />\n);
                    if ($input_hr->{action} eq 'install') {
                        $err .= qq(<p id="er">Please specify a valid email address!</p>\n) if !Cpanel::cPanelFunctions::validemail($obj->{email});
                    }
                }
                $obj->{suexec} = -x '/usr/local/apache/bin/suexec' ? 1 : 0;
                $obj->{phpsuexec} = '';

                if ($info_hr->{require_suexec} && !$obj->{suexec}) {
                    $err .= qq(<p id="er">This addon requires suexec for security. Please ask your admin to turn on suexec.</p>\n);
                }
                if ($info_hr->{setphpsuexecvar}) {
                    $obj->{phpsuexec} = '-1';
                    my $phpifile = 'ihatephp.php';
                    my $uniq = 1;
                    while(-e "$Cpanel::homedir/public_html/$uniq$phpifile") { $uniq++; }
                    if (open PHP, '>', "$Cpanel::homedir/public_html/$uniq$phpifile") {
                        print PHP qq(#!/usr/bin/php\n<?php phpinfo(); ?>);
                        close PHP;
                        if (chmod 0755, "$Cpanel::homedir/public_html/$uniq$phpifile") {
                            # my $ihatephp = Cpanel::HttpRequest::httpreqv2('localhost', "~$Cpanel::user/$uniq$phpifile",1,"") || '';
                            my $ihatephp = `GET http://localhost/~$Cpanel::user/$uniq$phpifile` || '';
                            if ($ihatephp) {
                                my ($apiline) = grep(/Server API/, split(/\n/, $ihatephp));
                                if ($apiline =~ m/CGI/) {
                                    $obj->{phpsuexec} = 1;
                                } elsif ($apiline =~ m/Apache/) {
                                    $obj->{phpsuexec} = 0;
                                }
                            }
                        }
                        unlink "$Cpanel::homedir/public_html/$uniq$phpifile"; # nice and quiet :)
                    }
                }
                my %dosuexecwarn;
                my %dophpsuexecwarn;
                if ($info_hr->{warnphpsuexec_change} || $info_hr->{warnsuexec_change}) {
                    for(keys %{ $obj->{installed} }) {
                        if ($info_hr->{warnsuexec_change}) {
                            $dosuexecwarn{$_} = $obj->{installed}->{$_}->{suexec} if  $obj->{installed}->{$_}->{suexec} ne $obj->{suexec};
                        }
                        if ($info_hr->{setphpsuexecvar} && $info_hr->{warnphpsuexec_change}) {
                            next if $obj->{installed}->{$_}->{phpsuexec} !~ m/^1$|^0$/ || $obj->{phpsuexec} !~ m/^1$|^0$/;
                            $dophpsuexecwarn{$_} = $obj->{installed}->{$_}->{phpsuexec} if $obj->{installed}->{$_}->{phpsuexec} ne $obj->{phpsuexec};
                        }
                    }
                }
                print qq(<p id="er">$info_hr->{warnsuexec_change}</p>\n) if %dosuexecwarn;
                print qq(<p id="er">$info_hr->{warnphpsuexec_change}</p>\n) if %dophpsuexecwarn;
                my $initialpath = Cpanel::Form::escapehtml( $info_hr->{installdir} ) || '';
                $obj->{addon} = $mod;
                $obj->{addon_path} = $obj->{addon};
                $obj->{addon_path} =~ s/\:\:/\//g;
                $obj->{addon_path} =~ s/\w+\.pm$//;

                $obj->{workinginstall} = $input_hr->{workinginstall} || '';
                $obj->{registry} = exists  $obj->{installed}->{ $input_hr->{workinginstall} } ? $input_hr->{workinginstall} : '';

                my @import_files;
                if ( opendir HOME, $Cpanel::homedir ) {
                    my @imports = grep( $_ =~ m/^\.import_.*\.tar.gz$/, readdir(HOME));
                    closedir HOME;
                    if (@imports) {
                        # only load this module if we have any imports
                        require Cpanel::cPAddons::ConvertData;
                        my $key;

                        KEYNAME:
                        for(keys %Cpanel::cPAddons::ConvertData::aoi) {
                            if ($Cpanel::cPAddons::ConvertData::aoi{$_}->{'convert'}{'namespace'} eq $mod) {
                                $key = $_;
                                last KEYNAME;
                            }
                        }

                        if ($key) {
                            @import_files = grep( $_ =~ m/^\.import\_$key\-\w+\.tar.gz$/, @imports);
                        }
                    }
                }
                else {
                    print qq(<p id="er">Could not open home dir to look for import files!</p>\n);
                }

                if (@import_files) { #if -e "$Cpanel::homedir/.import_$convert_key-\w+.tar.gz") {
                    $obj->{'form_install'} .= qq(Use import file (in home directory): <select name="import_file">\n);
                    $obj->{'form_install'} .= qq(  <option value="">None</option>\n);
                    for(@import_files) {
                        my $selected = defined $input_hr->{'input_file'}
                                        && $input_hr->{'input_file'} eq "$Cpanel::homedir/$_"
                                        ? ' selected' : '';
                        $obj->{'form_install'} .= qq(  <option$selected value="$Cpanel::homedir/$_">$_</option>\n);
                    }

                    $obj->{'form_install'} .= "</select><br />\n";
                }

                $obj->{form_install} .= qq(Install at this url: $obj->{form_domain_menu}/<input type=text name="installdir" value="$initialpath" />\n<br />);
                $obj->{form_path_element} = qq(Install at this url: $obj->{form_domain_menu}/<input type=text name="installdir" value="$initialpath" />\n<br />);
                if (exists $info_hr->{ $info_hr->{version} }->{public_html_install_files} && ref $info_hr->{ $info_hr->{version} }->{public_html_install_files} eq 'ARRAY') {
                    $obj->{form_install} .= qq(You may install it directly in your public_html directory by leaving that blank. Please note any files that already exist that are in the installation <b>will be overwritten</b>!!!\n $info_hr->{ $info_hr->{version} }->{public_html_install_note}<br />\n);
                }
                # $obj->{form_install} .= qq(Auto update this installation? <input type="checkbox" value="1" name="autoupdate" checked /><br />\n) unless $info_hr->{noautoupdate};
                if (ref $info_hr->{install_fields} eq 'HASH') {
                    $obj->{form_install} .= qq(<br /><b>Additional install options:</b><br /><table border="0">\n);
                    my %install_fields_ignore = (addon=>1,email=>1,auser=>1,apass=>1,installdir=>1,action=>1);
                    for(sort keys %{ $info_hr->{install_fields} }) {
                        next if exists $install_fields_ignore{$_};
                        $obj->{form_install} .= $info_hr->{install_fields}->{$_}->{type} eq 'hidden' ?
                            qq(<tr valign="top"><td><!--$info_hr->{install_fields}->{$_}->{label}--></td><td>\n) :
                            qq(<tr valign="top"><td>$info_hr->{install_fields}->{$_}->{label}</td><td>\n);
                        if ($info_hr->{install_fields}->{$_}->{type} =~ m/^text$|^hidden$|^textarea$|^password$/) {
                            my $val =  Cpanel::Form::escapehtml($info_hr->{install_fields}->{$_}->{value});
                            if ($info_hr->{install_fields}->{$_}->{type} eq 'textarea') {
                                $obj->{form_install} .= qq(<textarea $info_hr->{install_fields}->{$_}->{attr}>$val</textarea>\n);
                            } else {
                                $obj->{form_install} .= qq(<input type="$info_hr->{install_fields}->{$_}{type}" name="$_" value="$val" $info_hr->{install_fields}->{$_}->{attr} />\n);
                            }
                        } elsif ($info_hr->{install_fields}->{$_}->{type} =~ m/^select$|^multi$|^radio$|^checkbox$/) {
                            if ($info_hr->{install_fields}->{$_}->{type} eq 'select') {
                                $obj->{form_install} .= qq(<select name="$_" $info_hr->{install_fields}->{$_}->{attr}>\n);
                                for my $kv(keys %{ $info_hr->{install_fields}->{$_}->{value} }) {
                                    $obj->{form_install} .= $kv eq $info_hr->{install_fields}->{$_}->{defvalue} ?
                                        qq(<option value="$kv" selected>$info_hr->{install_fields}->{$_}->{value}->{$kv}</option>\n) :
                                        qq(<option value="$kv">$info_hr->{install_fields}->{$_}->{value}->{$kv}</option>\n);
                                }
                                $obj->{form_install} .= "</select>\n";
                            } elsif ($info_hr->{install_fields}->{$_}->{type} eq 'multi') {
                                $obj->{form_install} .= qq(<select name="$_" $info_hr->{install_fields}->{$_}->{attr} multiple>\n);
                                for my $kv(keys %{ $info_hr->{install_fields}->{$_}->{value} }) {
                                    $obj->{form_install} .= (grep /^\Q$kv\E$/, @{ $info_hr->{install_fields}->{$_}->{defvalue} }) ?
                                        qq(<option value="$kv" selected>$info_hr->{install_fields}->{$_}->{value}->{$kv}</option>\n) :
                                        qq(<option value="$kv">$info_hr->{install_fields}->{$_}->{value}->{$kv}</option>\n);
                                }
                                $obj->{form_install} .= "</select>\n";
                            } elsif ($info_hr->{install_fields}->{$_}->{type} eq 'checkbox') {
                                for my $kv(keys %{ $info_hr->{install_fields}->{$_}->{value} }) {
                                    $obj->{form_install} .= (grep /^\Q$kv\E$/, @{ $info_hr->{install_fields}->{$_}->{defvalue} }) ?
                                        qq(<input type="checkbox" value="$kv" checked $info_hr->{install_fields}->{$_}->{attr} />$info_hr->{install_fields}->{$_}->{value}->{$kv}<br />\n) :
                                        qq(<input type="checkbox" value="$kv" $info_hr->{install_fields}->{$_}->{attr} />$info_hr->{install_fields}->{$_}->{value}->{$kv}<br />\n);
                                }
                            } elsif ($info_hr->{install_fields}->{$_}->{type} eq 'radio') {
                                for my $kv(keys %{ $info_hr->{install_fields}->{$_}->{value} }) {
                                    $obj->{form_install} .= $kv eq $info_hr->{install_fields}->{$_}->{defvalue} ?
                                        qq(<input type="radio" name="$_" value="$kv" $info_hr->{install_fields}->{$_}->{attr} checked />$info_hr->{install_fields}->{$_}->{value}->{$kv}<br />\n) :
                                        qq(<input type="radio" name="$_" value="$kv" $info_hr->{install_fields}->{$_}->{attr} />$info_hr->{install_fields}->{$_}->{value}->{$kv}<br />\n);
                                }
                            }
                        }
                        for(sort keys %{ $input_hr }) {
                            $obj->{"input_$_"} = $input_hr->{$_};
                        }
                        $obj->{form_install} .= "</td></tr>\n";
                    }
                    $obj->{form_install} .= "</table>\n";
                }
                if ($info_hr->{table_prefix}) {
                    my $tp = $input_hr->{table_prefix} || $info_hr->{table_prefix};
                    $tp =~ s/\W//g;
                    $obj->{table_prefix} = $tp;
                    my $exmy_opt = qq(  <option selected value="">Create New Database</option>\n);
                    my @dbs = split /\n/, `/usr/local/cpanel/bin/mysqlwrap LISTDBS`;
                    for(@dbs) {
                        next if !$_ || !m/^\Q$Cpanel::user\E\_/;
                        $exmy_opt .= qq(  <option value="$_">$_</option>\n);
                        $obj->{existing_mysql} = $_ if $input_hr->{existing_mysql} eq $_;
                    }
                    $obj->{form_install} .= qq(<p>Table Prefix <input type="text" name="table_prefix" value="$tp" /><br />MySQL DB <select name="existing_mysql">$exmy_opt</select></p>\n);
                }
                $obj->{preform} = qq(<form action="$obj->{_self}" method="post"><input type="hidden" name="addon" value="$mod" />\n);
                $obj->{_self} = $_self;
                $obj->{user} = $Cpanel::user;
                $obj->{homedir} = $Cpanel::homedir;
                $obj->{installdir} = "$Cpanel::homedir/public_html/$obj->{installpath}" if defined $obj->{installpath} && $obj->{installpath};
                $obj->{public_html} = "$Cpanel::homedir/public_html";
                $obj->{lang} = $Cpanel::CPDATA{LANG};
                $obj->{domain} = $Cpanel::CPDATA{DNS};
                $obj->{installed_on_domain} = $obj->{parked_domain} || $obj->{subdomain_name} || $obj->{domain};
                $obj->{url_to_install} = qq(http://$Cpanel::CPDATA{DNS}/$obj->{installpath}/) if defined $obj->{installpath} && $obj->{installpath};
                $obj->{url_to_install_without_trailing_slash} = $obj->{url_to_install};
                $obj->{url_to_install_without_trailing_slash} =~ s/\/$//g;
                $obj->{autoupdate} = $input_hr->{autoupdate} ? 1: 0;
                $obj->{version} = $info_hr->{version};
                $obj->{mysql_version} = $Cpanel::CONF{'mysql-version'};
                $obj->{postgre_version} = ''; # to do when postgre added

                for(sort keys %{ $obj->{installed} }) {
                    next if $obj->{installed}->{$_}->{addon} ne $mod;
                    if ($obj->{installed}->{$_}->{version} ne $info_hr->{version}) {
                        $obj->{form_upgrade} = qq(<select name="workinginstall">\n) if !$obj->{form_upgrade};
                        $obj->{form_upgrade} .=  qq(  <option value="$_"> $obj->{installed}->{$_}->{installpath} v$obj->{installed}->{$_}->{version}</option>\n);
                    }
                    $obj->{form_linktoinstalled} = "<p><b>Manage</b><br />\n" if !$obj->{form_linktoinstalled};
                    my $tmpfaceurl = '';
                    if ($obj->{installed}->{$_}->{subdomain_name}) {
                        my $pth = $obj->{installed}->{$_}->{installdir};
                        $pth =~ s/^\Q$obj->{installed}->{$_}->{subdomain_path_raw}\E\/?//;
                        $pth .= '/' if $pth !~ m/\/$/ && $pth;
                        $tmpfaceurl = " (http://$obj->{installed}->{$_}->{subdomain_name}/$pth)";
                    } elsif ($obj->{installed}->{$_}->{parked_domain}) {
                        my $pth = $obj->{installed}->{$_}->{installpath} eq './' ? '' : $obj->{installed}->{$_}->{installpath};
                        $pth .= '/' if $pth !~ m/\/$/ && $pth;
                        $tmpfaceurl = " (http://$obj->{installed}->{$_}->{parked_domain}/$pth)";
                    }
                    my $suexec = exists $dophpsuexecwarn{$_} ? qq( (<span id="er">PHPSuExec Mismatch</span>)) : '';
                    $suexec .= exists $dosuexecwarn{$_} ? qq( (<span id="er">SuExec Mismatch</span>)) : '';

                    my $mvadurl = qq($_self?addon=$mod&workinginstall=$_&action=movecopy);
                    my $mvcplnk = $info_hr->{nomovecopy} ? '' : qq( &#149; [<a href="$mvadurl">Move/Copy</a>]);
                    $obj->{form_linktoinstalled} .= qq(v$obj->{installed}->{$_}->{version} <b>$obj->{installed}->{$_}->{installpath}</b>$tmpfaceurl$suexec<br />Url: <a target="_blank" href="$obj->{installed}->{$_}->{url_to_install}">$obj->{installed}->{$_}->{url_to_install}</a>$mvcplnk<br />\n);
                    if (!$info_hr->{nomovecopy}) {
                        for my $cp (keys %{ $obj->{installed}->{$_}->{copy} }) {
                            my $delurl = qq($mvadurl\&remove=$cp);
                            $cp .= '/' if $cp !~ m/\/$/;
                            $obj->{form_linktoinstalled} .= qq(Copy: <a target="_blank" href="http://$obj->{domain}/$cp">http://$obj->{domain}/$cp</a> &#149; [<a href="$delurl">Remove Copy</a>]<br />\n);
                        }
                    }
                    $obj->{form_linktoinstalled} .= qq(Admin: <a target="_blank" href="$obj->{installed}->{$_}->{url_to_install_admin}">$obj->{installed}->{$_}->{url_to_install_admin}</a><br />\n) if $obj->{installed}->{$_}->{url_to_install_admin};
                    $obj->{form_linktoinstalled} .= "<br />\n";
                    $obj->{form_uninstall} = qq(<select name="workinginstall">\n) if !$obj->{form_uninstall};
                    $obj->{form_uninstall} .= qq(  <option value="$_"> $obj->{installed}->{$_}->{installpath} v$obj->{installed}->{$_}->{version}</option>\n);
                }
                $obj->{form_upgrade} = $obj->{form_upgrade} ? qq($obj->{form_upgrade}</select><input type="submit" value="Upgrade" />) : 'No upgradable installs';
                $obj->{form_uninstall} = $obj->{form_uninstall} ? qq($obj->{form_uninstall}</select><input type="submit" value="Uninstall" />) : 'No removeable installs';
                $obj->{form_linktoinstalled} .= "</p>\n" if $obj->{form_linktoinstalled};

                $obj->{installform} = qq($obj->{preform}<input type="hidden" name="action" value="install" />$obj->{form_install}<br /><input type=submit value="Install" /></form>\n);
                $obj->{upgradeform} = qq($obj->{preform}<input type="hidden" name="action" value="upgrade" />$obj->{form_upgrade}</form>\n);
                $obj->{uninstallform} = qq($obj->{preform}<input type="hidden" name="action" value="uninstall" />$obj->{form_uninstall}</form>\n);

                if (exists $input_hr->{action} &&  ref $actions{$input_hr->{action}} eq 'CODE') {
                    $err = $tmp_disabled{1} if $tmp_disabled{1} && $input_hr->{action} eq 'install';
                    $err = $tmp_disabled{2} if $tmp_disabled{2} && $input_hr->{action} eq 'upgrade';
                    $err = $tmp_disabled{3} if $tmp_disabled{3} && $input_hr->{action} eq 'uninstall';
                    $err = $tmp_disabled{4} if $tmp_disabled{4} &&  $input_hr->{action} ne 'install'
                        && $input_hr->{action} ne 'upgrade' && $input_hr->{action} ne 'uninstall';
                    if (!$err && ref $info_hr->{install_fields_hook} eq 'CODE' && $input_hr->{action} eq 'install') {
                        my $user_err = '';
                        $info_hr->{install_fields_hook}->($input_hr,\$user_err, $obj);
                        $err .= $user_err; # don't pass them \$err so they can't set it to '' since if its set there's a good reason why
                    }
                    if (!$permok && !$err && exists $moderated->{$mod} && $input_hr->{action} eq 'install') {
                        if ($okfile) {
                            unlink "$Cpanel::homedir/.cpaddons/moderation/$okfile"
                                or $err .= "Install could not proceed because moderation could not be finalized. 1: $!";
                            $err .= "Install could not proceed because moderation could not be finalized. 2: $!"
                                if -e "$Cpanel::homedir/.cpaddons/moderation/$okfile";
                        } else {
                            $err .= "Invlaid moderation finalization token.";
                        }
                    }
                    $err ? print qq($err <p align="center">[<a href="$_self?addon=$mod">Back</a>]</p>) : $actions{$input_hr->{action}}->($obj,$info_hr,$input_hr);
                    print qq(<p align="center">[<a href="$_self?addon=$mod">$mod</a>]</p>);
                } else {
                    print "<p><b>This product requires a liscense from the vendor</b>\n$info_hr->{lisc_info} </p>\n" if $info_hr->{lisc_info};
                    print qq(<p><b>Vendor licensing can be obtained here:</b> <a target="_blank" href="$info_hr->{lisc_url}">$info_hr->{lisc_url}</a></p>\n) if $info_hr->{lisc_url};
                    print qq(<p>Use discount code <b>$info_hr->{lisc_disc}->[1]</b> to receive <b>$info_hr->{lisc_disc}->[0]</b>. (for cPanel installed instances only).</p>\n) if ref $info_hr->{lisc_disc} eq 'ARRAY';
                    print "<p><b>Description:</b> (v$info_hr->{version}) <br />\n$info_hr->{description} </p>\n";
                    print "<p><b>Security Info</b><br />\n$info_hr->{security} </p>\n" if exists $info_hr->{security};
                    print qq(<p><b>Addon Functionality can be found here:</b> <a target="_blank" href="$info_hr->{addopt_url}">$info_hr->{addopt_url}</a><br />
                        (<span id="er">We are not responsible for any aspect of any 3rd party modifications available.<br />If you install any you do so
                        at your own risk since it could break functionality or the ability to manage it via this interface.</span>)</p>\n)
                        if $info_hr->{addopt_url};
                    print "<p><b>Install:</b> <br />"; $actions{installform}->($obj,$info_hr,$input_hr); print "</p>\n";
                    print $obj->{form_linktoinstalled};
                    print "<p><b>Upgrade:</b> <br />"; $actions{upgradeform}->($obj,$info_hr,$input_hr); print "</p>\n";
                    print "<p><b>Uninstall:</b> <br />"; $actions{uninstallform}->($obj,$info_hr,$input_hr); print "</p>\n";
                    print $extras;
                }
            }
        }
    }
    print qq(<p align="center">[<a href="$_self?changevendor=$pal">Main</a>]</p>\n);
}

#### secondary functions ##

sub stdinstall {
    my ($obj,$info_hr,$input_hr) = @_;
    chdir $obj->{public_html}  or die "Could not go into public_html: $!";
    if ( Cpanel::cPanelFunctions::safemkdir( $obj->{installpath} ) ) {
        chdir $obj->{installpath} or die "Could not change into install dir ($obj->{installpath}): $!";

        $obj->do_vendor_liscense($info_hr,$input_hr);

        system("tar xzf $base$obj->{addon_path}/$info_hr->{version}.tar.gz");
        if ($obj->{username} && $obj->{password} && $info_hr->{adminarea_path}) {
            print "Protecting $info_hr->{adminarea_path} ...";
            Cpanel::Htaccess::set_protect("$obj->{installdir}/$info_hr->{adminarea_path}", 1, "$info_hr->{adminarea_name} Admin");
            Cpanel::Htaccess::set_pass("$obj->{installdir}/$info_hr->{adminarea_path}", $obj->{username}, $obj->{password});
            print "Done<br><br>\n";
            $obj->{url_to_install_admin} = "$obj->{url_to_install}$info_hr->{adminarea_path}";
            $obj->{url_to_install_admin} .= '/' if $obj->{url_to_install_admin} !~ m/\/$/;
        }

        if (defined $info_hr->{perl_module} && ref $info_hr->{perl_module} eq 'HASH') {
            for(keys %{ $info_hr->{perl_module} }) {
                my $missing = `perl -e 'use $_ $info_hr->{perl_module}->{$_};' 2>&1`;
                if ($missing) {
                    print qq(<p id="er">Have your admin install the <b>$_ $info_hr->{perl_module}->{$_}</b> Perl)
                          . qq( module or your installation <b>will not work</b> properly.</p>\n);
                }
            }
        }

        if ($obj->my_createdbs( $info_hr->{mysql} ) && $obj->pg_createdbs( $info_hr->{postgre} )) {
            # add my_sql_replace dbs
            for(keys %{ $obj->{mysql} }) {
                $obj->{$_} = $obj->{mysql}->{$_}->{sqldb};
            }
            # do same for pg when added
            # flatten sql hashref for mysql.dbname.sqldb type access
            for my $db(keys %{ $obj->{mysql} }) {
                for(keys %{ $obj->{mysql}->{$db} }) {
                    $obj->{"mysql.$db.$_"} = $obj->{mysql}->{$db}->{$_};
                }
            }
            for my $db(keys %{ $obj->{postgre} }) {
                for(keys %{ $obj->{postgre}->{$db} }) {
                    $obj->{"postgre.$db.$_"} = $obj->{postgre}->{$db}->{$_};
                }
            }

            if ($obj->my_dosql( "$base$obj->{addon_path}/$info_hr->{version}.mysql" ) &&
                 $obj->pg_dosql( "$base$obj->{addon_path}/$info_hr->{version}.postgre" ) ) {
                $obj->procconfigfile( $info_hr->{config_files} ) or print qq(<p id="er">Unable to set configuration! This will need done manually!</p>\n);
                if (ref $info_hr->{chmod} eq 'HASH') {
                    for(keys %{ $info_hr->{chmod} }) {
                        chmod oct($_), @{ $info_hr->{chmod}->{$_} } or print qq(<p id="er">Could not chmod $_ files: $!</p>\n);
                    }
                }
                $obj->{"installed_$info_hr->{version}"} = time();
                $obj->add_cron($info_hr);

                $obj->register() or print qq(<p id="er">Unable to create registry! This installation will not be updateable or removed from here.</p>\n);

                my $tmpfaceurl = '';
                my $tmpadmnurl = '';
                my $tmpadmnpth = $info_hr->{adminarea_path};
                $tmpadmnpth .= '/' if $tmpadmnpth !~ m/\/$/;
                if ($obj->{subdomain_name}) {
                    my $pth = $obj->{installdir};
                    $pth =~ s/^\Q$obj->{subdomain_path_raw}\E\/?//;
                    $pth .= '/' if $pth !~ m/\/$/ && $pth;
                    $tmpfaceurl = " (http://$obj->{subdomain_name}/$pth)";
                    $tmpadmnurl = " (http://$obj->{subdomain_name}/$pth$tmpadmnpth)";
                } elsif ($obj->{parked_domain}) {
                    my $pth = $obj->{installed}->{installpath} eq './' ? '' : $obj->{installpath};
                    $pth .= '/' if $pth !~ m/\/$/ && $pth;
                    $tmpfaceurl = " (http://$obj->{parked_domain}/$pth)";
                    $tmpadmnurl = " (http://$obj->{parked_domain}/$pth$tmpadmnpth)";
                }

                if (defined $input_hr->{'import_file'} && $input_hr->{'import_file'}) {
                    if (-r $input_hr->{'import_file'}) {
                        print "<p>Starting import of $input_hr->{'import_file'}...";
                        system("tar xzf $input_hr->{'import_file'}"); # we are still in $obj->{installpath}

                        if (-e "$input_hr->{'import_file'}.sql") {
                            if ($obj->my_dosql("$input_hr->{'import_file'}.sql")) {
                                unlink "$input_hr->{'import_file'}.sql"
                                    or print qq(<p id="er">Coudl not remove $input_hr->{'import_file'}.sql: $!. This will need done manually</p>\n);
                            }
                            else {
                                print qq(<p id="er">Could not do SQL file $input_hr->{'import_file'}.sql!! This will need done manually.</p>\n);
                            }
                        }
                        print "Done!</p>\n";
                    }
                    else {
                        print qq(<p>Invalid import file specified</p>\n);
                    }
                }
                print qq(<p>Done! You can access your new addon at <a target="_blank" href="$obj->{url_to_install}">$obj->{url_to_install}</a> $tmpfaceurl</p>\n);
                print qq(<p>The admin area can be accessed at <a target="_blank" href="$obj->{url_to_install_admin}">$obj->{url_to_install_admin}</a> $tmpadmnurl</p>\n) if $obj->{url_to_install_admin};
                print qq(<p>The user is <b>$obj->{username}</b> and the pass is <b>$obj->{password}</b></p>\n) if $obj->{username} && $obj->{password};

            } else {
                print "<p>Cleaning up failed install..";
                chdir $obj->{public_html}  or die "Could not go into public_html: $!";
                _removeinstalldir($obj->{installpath}, $info_hr);
                print "Done!</p>";
            }
        } else {
            print "<p>Cleaning up failed install..";
            chdir $obj->{public_html}  or die "Could not go into public_html: $!";
            _removeinstalldir($obj->{installpath}, $info_hr);
            print "Done!</p>";
        }
    } else {
        print qq(<p id="er">Could not make installation path: $@</p>\n);
    }
}

sub stdupgrade {
    my $udn = '.cpaddons_upgrade';
    my ($obj,$info_hr,$input_hr) = @_;
    chdir $obj->{public_html}  or die "Could not go into public_html: $!";
    my $ist = $obj->{installed}->{ $obj->{workinginstall} };
    $ist->{installdir} =~ s/\/$//g;
    my $curver = $ist->{version};
    my $updo = '';

    my $uppath = "$base/$ist->{addon_path}/upgrade/";
    my %allups;

    opendir UP, $uppath or die "Could not read upgrade dir: $!";
    @allups{ grep !/^\.+$/, readdir UP } = ();
    close closedir UP;

    for(sort keys %allups) {
        next unless m/^\Q$curver\E\_\d+/;
        $updo = $_;
    }
    $updo =~ s/\/$//g;
    my (undef,$newver) = split /_/, $updo;
    if (!$updo) {
        print qq(<p id="er">Unable to find available update from $curver!</p>\n);
        exit;
    }

    my $copytoworkok = 0;
    my $tmpfail = 0;
    my $procconfin = 'installdir';

    if ($ist->{installpath} eq './') {
        die "./$udn/ already exists" if -d "./$udn/";
        Cpanel::cPanelFunctions::safemkdir("./$udn/", 777) or die "Could not create ./$udn/: $!";
        chdir "./$udn/" or die "Could not change into working directory: $!";

        $obj->do_vendor_liscense($info_hr,$input_hr,$ist);

        for(qw(public_html_install_files public_html_install_dirs public_html_install_unknown)) {
            last if $tmpfail;
            next if !exists $info_hr->{$curver}->{$_} || ref $info_hr->{$curver}->{$_} ne 'ARRAY';
            for(@{ $info_hr->{$curver}->{$_} }) {
                if (-d "$ist->{public_html}/$_") {
                    my $tmp = $_; # $_ is being clobbered by safecopy for some reason when target is nonexistant(which it always will be here)
                    $copytoworkok = Cpanel::cPanelFunctions::safecopy("$ist->{public_html}/$tmp/*", "./$tmp");
                } else {
                    my $tmp = $_;
                    $copytoworkok = Cpanel::cPanelFunctions::safecopy("$ist->{public_html}/$tmp", "./$tmp");
                }
                if (!$copytoworkok) {
                    $tmpfail++;
                    last;
                }
            }
        }
        $procconfin = 'installpath';
    } else {
        die "$ist->{installdir}$udn/ already exists" if -d "$ist->{installdir}$udn/";
        mkdir "$ist->{installdir}$udn/" or die "Could not create $ist->{installdir}$udn/";
        $copytoworkok = Cpanel::cPanelFunctions::safecopy("$ist->{installdir}/*", "$ist->{installdir}$udn/");
        chdir "$ist->{installdir}$udn/" or die "Could not change into working directory: $!";
    }
    if ($copytoworkok) {
        my $buopt = "--basename-prefix=$Cpanel::homedir/.xaddon_diff_backup_$obj->{workinginstall}";
        if (!`patch -p1 -F99 $buopt -s --dry-run <$uppath$updo/diff` || $input_hr->{force} eq $force) { # diff -ruN old/ new/
            my $forced = $input_hr->{force} ? ' <b>forced</b>' : '';
            print "Starting$forced upgrade from $curver to $newver...<br />\n<pre>\n";
            system("patch -p1 -F99 $buopt <$uppath$updo/diff");
            print "</pre>\n";
            system("$uppath$updo/script") if -e "$uppath$updo/script" && -x "$uppath$updo/script";
            $obj->my_dosql("$uppath$updo/mysql", $ist, $ist->{mysql_user}, $ist->{mysql_pass})
                or print qq(<p id="er">Could not run SQL on MySQL databases! This will need done manually!</p>\n);
            $obj->pg_dosql("$uppath$updo/postgre") or print qq(<p id="er">Could not run SQL on Postgre databases! This will need done manually!</p>\n);

            chdir $obj->{public_html} or die "Could not go into public_html: $!";

            $obj->procconfigfile( $info_hr->{config_files}, $ist, "$ist->{$procconfin}$udn/", $ist);

            $ist->{version_key} = $info_hr->{$newver};
            $ist->{version} = $newver;
            $ist->{"upgraded_to_$newver"} = time();
            _writereg($ist, "$Cpanel::homedir/.cpaddons/$obj->{workinginstall}") or return;
            if (!Cpanel::cPanelFunctions::safecopy("$ist->{installpath}$udn/*", $ist->{installdir})) {
                print qq(<p id="er">Could not put updates in place! <!-- Recurse mv $ist->{installpath}$udn/ $ist->{installdir} rm upg dir --></p>\n);
            } else {
                _removeinstalldir("$ist->{installpath}$udn/", $info_hr);
            }

            if (-e "$uppath$updo/remove") {
                if (open REM, "$uppath$updo/remove") {
                    while(<REM>) {
                        chomp;
                        next if !$_ || !-e "$ist->{installpath}/$_";
                        unlink "$ist->{installpath}/$_" or print qq(<p id="er">Could not remove $ist->{installpath}/$_: $!</p>\n);
                    }
                    close REM;
                }
            }

            if (-e "$uppath$updo/chmod") {
                if (open CHM, "$uppath$updo/chmod") {
                    while(<CHM>) {
                        chomp;
                        my($mode,$file) = split /\s+/;
                        next if $mode !~ m/^\d+$/;
                        next if !$file || !-e "$ist->{installpath}/$file";
                        chmod oct($mode), "$ist->{installpath}/$file"
                            or print qq(<p id="er">Could not chmod $mode $ist->{installpath}/$file. ($!) This will need done manually!"</p>\n);
                    }
                    close CHM;
                }
            }

            $obj->remove_cron();
            $obj->add_cron($info_hr);

            my $next = 0;
            for(sort keys %allups) {
                next unless m/^\Q$newver\E\_\d+/;
                $next = $_;
            }
            if ($next) {
                my (undef, $nxtstp) = split /_/, $next;
                $obj->{installs}->{ $obj->{workinginstall} }  = $ist;
                $obj->stdupgrade($info_hr,$input_hr);
            }
        } else {
            if (defined $ist->{'converted'} && $ist->{'converted'} =~ m/^\d+$/) {
                # stringify 'converted 'and show them the data maybe ??
                print qq(
   <p>This addon could not be upgraded. This is because it was converted from the old addon system and the upgrade patch failed against it.</p>
   <p>Your options at this point are:
     <ul>
       <li> Install a new one and manually import this installation's data into it. (removing this installation once you're satified everything is ok)</li>
       <li> Leave it as is </li>
     </ul>
   </p>);
            }
            else {
                my $forcelen = length $force;
                print qq(
   <p>This Addon has been modified (or the upgrade test failed) and is no longer updatable via this function.</p>
   <p> If you really want to force the upgrade please type the following in to the form below:</p>
   <p> $force </p>
   <form method="post" action="$_self">
    <input type="hidden" name="addon" value="$ist->{addon}" />
    <input type="hidden" name="workinginstall" value="$obj->{workinginstall}" />
    <input type="hidden" name="action" value="upgrade" />
    <input type="text" name="force" maxlength="$forcelen" />
    <input type="submit" value="I really do want to possibly wipe out my customizations and otherwise break my install." />
   </form>);
            }
        }
    } else {
        print qq(<p id="er">Could not prepare working directory: $!</p>\n);
    }
}

sub stduninstall {
    my ($obj,$info_hr,$input_hr) = @_;
    print "Starting uninstall...";
    chdir $obj->{public_html}  or die "Could not go into public_html: $!";
    my $udir = $obj->{installed}->{ $obj->{workinginstall} }->{installpath};
    $udir =~ s/^\/|\/$//g unless $udir eq './';
    if ($obj->{installed}->{ $obj->{workinginstall} }->{subdomain_name} && $udir eq $obj->{installed}->{ $obj->{workinginstall} }->{subdomain_path}) {
        chdir $obj->{installed}->{ $obj->{workinginstall} }->{subdomain_path} or die "Could not go into subdomain base: $!";
        $udir = './';
    }
    if ( _removeinstalldir( $udir, $info_hr, $obj->{installed}->{ $obj->{workinginstall} }->{version} )) {
        $obj->remove_cron();
        $obj->my_dropdbs() or print qq(<p id="er">Unable to remove MySQL databases. This will need done manually.</p>\n);
        $obj->pg_dropdbs() or print qq(<p id="er">Unable to remove Postgre databases. This will need done manually.</p>\n);
        $obj->unregister() or print qq(<p id="er">Unable to remove ~/.addonx/ store. This will need done manually.</p>\n);
        for(keys %{ $obj->{installed}->{ $obj->{workinginstall} }->{copy} }) {
            unlink "$obj->{public_html}/$_" or print qq(<p id="er"> Could not remove $_</p>\n) if -l $_;
        }
        _removepasswd("$obj->{installed}->{ $obj->{workinginstall} }->{installpath}/$info_hr->{adminarea_path}")
            if $info_hr->{adminarea_path};
        print "Done!";
    } else {
        print qq(<p id="er">Could not remove install directory: $@</p>\n);
    }
}

sub movecopy {
    my ($obj,$info_hr,$input_hr) = @_;
    my $ist = $obj->{installed}->{ $obj->{workinginstall} };
    chdir $obj->{public_html} or die "Could not go into public_html: $!";
    my $newdir = $obj->{installpath};

    if ($ist->{version} eq $info_hr->{version}) {
        print "<h3>Move/Copy Management for $ist->{installpath}</h3>\n";
        if ($input_hr->{remove}) {
            if (exists $ist->{copy}->{ $input_hr->{remove} }) {
                if (-l $input_hr->{remove}) {
                    print "Removing copy...";
                    unlink $input_hr->{remove} or print qq(<p id="er"> Could not remove $input_hr->{remove}: $!</p>\n);
                    delete $ist->{copy}->{ $input_hr->{remove} };
                    _writereg($ist, "$Cpanel::homedir/.cpaddons/$ist->{registry}") or die "registry failed: $!";
                    print "Done!";
                } else {
                    delete $ist->{copy}->{ $input_hr->{remove} };
                    _writereg($ist, "$Cpanel::homedir/.cpaddons/$ist->{registry}") or die "registry failed: $!";
                    print "That does not appear to be a copy anymore.";
                }
            } else {
                print "Could not find that copy.";
            }
        } elsif ($input_hr->{move} eq '1') {
            print "Starting move...\n";
            my $cpok = 1;
            my $savepasswd = 0;

            my $existingisaddonbase = $ist->{subdomain_name} &&  $ist->{subdomain_path} eq $ist->{installpath} ? 1 : 0;

            if ($ist->{installpath} eq './' || $existingisaddonbase) {
                print "Moving a public_html or base addon directory install will be supported in the future but is not currently.";exit;
                if (!-d $newdir) {
                    my @parts = split /\//, $newdir;
                    if ($#parts > 0) {
                        my $pth = $parts[0];
                        if (!-l $pth) {
                            mkdir $pth if !-d $pth;
                            if ($#parts > 1) {
                                for(1..($#parts - 1)) {
                                    $pth .= "/$parts[$_]";
                                    if (!-l $pth) {
                                        mkdir $pth if !-d $pth;
                                    }
                                    else {
                                        print qq(<p id="er">Sorry copying on top of links is not allowed</p>\n);
                                    }
                                }
                            }
                        }
                        else {
                            print qq(<p id="er">Sorry copying on top of links is not allowed</p>\n);
                        }
                    }
                }

                die "Could not make target dir: $!" if !-d $newdir;

                my $source = $ist->{installpath} eq './' ? './' : 'addonbase';

                for(@{ $info_hr->{ $info_hr->{version} }->{public_html_install_files} }) {
                    $cpok = Cpanel::cPanelFunctions::safecopy($source/$_, $newdir/$_);
                    last if !$cpok;
                }
                if ($cpok && ref $info_hr->{ $info_hr->{version} }->{public_html_install_dirs} eq 'ARRAY') {
                    for(@{ $info_hr->{ $info_hr->{version} }->{public_html_install_dirs} }) {
                        $cpok = Cpanel::cPanelFunctions::safecopy($source/$_, $newdir/$_);
                        last if !$cpok;
                    }
                }
                if ($cpok && ref $info_hr->{ $info_hr->{version} }->{public_html_install_unknown} eq 'ARRAY') {
                    for(@{ $info_hr->{ $info_hr->{version} }->{public_html_install_unknown} }) {
                        $cpok = Cpanel::cPanelFunctions::safecopy($source/$_, $newdir/$_);
                        last if !$cpok;
                    }
                }
            } else {
                $cpok = Cpanel::cPanelFunctions::safecopy($ist->{installpath}, $newdir);
            }
            die "Could not copy files to new directory: $!" if !$cpok;
            chdir $newdir or die "Could not change into new dir: $!";

            $ist->{url_to_install} = qq(http://$Cpanel::CPDATA{DNS}/$newdir);
            $ist->{url_to_install_without_trailing_slash} = $ist->{url_to_install};
            $ist->{url_to_install_without_trailing_slash} =~ s/\/$//g;
            for(qw(parked_domain subdomain_name subdomain_path subdomain_path_raw parked_domain)) {
                $ist->{$_} = $obj->{$_} if defined $obj->{$_};
            }
            if ($ist->{username} && $ist->{password} && $info_hr->{adminarea_path}) {
                my $copyok = 'never got to safecopy';
                print "Protecting $info_hr->{adminarea_path} ...";
                Cpanel::Htaccess::set_protect("$obj->{public_html}/$newdir/$info_hr->{adminarea_path}", 1, "$info_hr->{adminarea_name} Admin");
                if (-e "$Cpanel::homedir/.htpasswds/$ist->{installpath}/$info_hr->{adminarea_path}/passwd") {
                    Cpanel::Htaccess::set_pass("$obj->{public_html}/$newdir/$info_hr->{adminarea_path}", $ist->{username}, $ist->{password});
                    $copyok = Cpanel::cPanelFunctions::safecopy(
                                "$Cpanel::homedir/.htpasswds/$ist->{installpath}/$info_hr->{adminarea_path}/passwd",
                                "$Cpanel::homedir/.htpasswds/$newdir/$info_hr->{adminarea_path}/passwd"
                            );
                }
                if (!-e "$Cpanel::homedir/.htpasswds/$newdir/$info_hr->{adminarea_path}/passwd") {
                    $savepasswd = 1;
                    print "<!-- passwd does not exist or copy failed, safecopy returned: -$copyok- -->\n";
                    # try again for good measure:
                    if (-e "$Cpanel::homedir/.htpasswds/$ist->{installpath}/$info_hr->{adminarea_path}/passwd") {
                        if (-d "$Cpanel::homedir/.htpasswds/$newdir/") {
                            print "<!-- Attempting to copy it again:";
                            print Cpanel::cPanelFunctions::safecopy(
                                        "$Cpanel::homedir/.htpasswds/$ist->{installpath}/$info_hr->{adminarea_path}/passwd",
                                        "$Cpanel::homedir/.htpasswds/$newdir/$info_hr->{adminarea_path}/passwd"
                                    );
                            print "-->\n";
                        }
                    }
                }

                print "Done<br><br>\n";
                $ist->{url_to_install_admin} = "$ist->{url_to_install}/$info_hr->{adminarea_path}";
                $ist->{url_to_install_admin} .= '/' if $ist->{url_to_install_admin} !~ m/\/$/;
            }

            chdir "$obj->{public_html}/$newdir" or die "Could not change into new dir: $!";

            $obj->my_dosql("$obj->{addon_path}/move.mysql", $ist, $ist->{mysql_user}, $ist->{mysql_pass});

            for(keys %{ $ist->{copy} }) {
                if (-l "$obj->{public_html}/$_") {
                    if (unlink "$obj->{public_html}/$_") {
                        if (symlink "$obj->{public_html}/$newdir", "$obj->{public_html}/$_") {
                            $ist->{copy}->{$_} = time();
                        } else {
                            $ist->{copy}->{$_} = $!;
                        }
                    } else {
                        $ist->{copy}->{$_} = $!;
                    }
                } else {
                    delete $ist->{copy}->{$_};
                }
            }

            # set url and dir vars:
            $ist->{moved}->{$newdir}->{from} = $ist->{installpath};
            $ist->{moved}->{$newdir}->{time} = time();
            $ist->{installpath} = $newdir;
            $ist->{installdir} = "$obj->{public_html}/$newdir";

            if (ref $info_hr->{'config_files'} eq 'ARRAY') {
                if (@{ $info_hr->{config_files} }) {
                    if ( system('tar','xzf',"$base$obj->{addon_path}/$info_hr->{version}.tar.gz",@{ $info_hr->{config_files} }) == 0) {
                        $obj->procconfigfile( $info_hr->{config_files}, $ist, $ist->{installdir}) or
                        print qq(<p id="er">Unable to set configuration! This will need done manually!</p>\n);
                    } else {
                        print qq(<p id="er">Could not untar the config files for reprocessing</p>\n);
                    }
                }
            }

            $obj->remove_cron();
            $obj->add_cron($info_hr);

            chdir $obj->{public_html} or die "Could not go into public_html: $!";
            _removeinstalldir($ist->{'moved'}->{$newdir}->{from}, $info_hr);

            if ($info_hr->{adminarea_path} && !-d "$ist->{moved}->{$newdir}->{from}/$info_hr->{adminarea_path}" && !$savepasswd) {
                _removepasswd("$ist->{moved}->{$newdir}->{from}/$info_hr->{adminarea_path}");
            }
            print qq(<p id="er">Did not remove the old passwd file since there were difficulties with the new one incase you need it.</p>\n) if $savepasswd;

            _writereg($ist, "$Cpanel::homedir/.cpaddons/$ist->{registry}") or die "registry failed: $!";

            print "Done!\n";
        } elsif ($input_hr->{move} eq '0') {
            print "Starting copy...\n";
            if ($ist->{installpath} eq './') {
                print qq(<p id="er">public_html installs can not be copied, only moved.</p>\n);
            } elsif ($newdir =~ m/^\Q$ist->{installpath}\E\//) {
                print qq(<p id="er">Copies can't be above them selves. (IE recursively linked)</p>\n);
            } else {
                my @parts = split /\//, $newdir;
                if ($#parts > 0) {
                    my $pth = $parts[0];
                    if (!-l $pth) {
                        mkdir $pth if !-d $pth;
                        if ($#parts > 1) {
                            for(1..($#parts - 1)) {
                                $pth .= "/$parts[$_]";
                                if (!-l $pth) {
                                    mkdir $pth if !-d $pth;
                                }
                                else {
                                    print qq(<p id="er">Sorry copying on top of links is not allowed</p>\n);
                                }
                            }
                        }
                    }
                    else {
                        print qq(<p id="er">Sorry copying on top of links is not allowed</p>\n);
                    }
                }
                symlink $ist->{installdir}, $newdir or die "Could not create symlink for copy: $!";
                $ist->{copy}->{$newdir} = time();
                _writereg($ist, "$Cpanel::homedir/.cpaddons/$ist->{registry}") or die "registry failed: $!";
                print "Done!\n";
            }
        } else {
            print <<"MVFORM";
  <h3 align="center">$ist->{installpth}</h3>
  <form action="$main::_self" method="post">
    $obj->{form_path_element}
    <input type="radio" name="move" value="1" checked /> Move<br />
    <input type="radio" name="move" value="0" /> Copy<br />
    <input type="hidden" name="action" value="movecopy" />
    <input type="hidden" name="workinginstall" value="$input_hr->{workinginstall}" />
    <input type="hidden" name="addon" value="$input_hr->{addon}" />
    <input type="submit" value="Perform Move/Copy" />
  </form>
MVFORM
        }
    }
    else {
        print qq(<p id="er">Please upgrade this install first, then you may move it.</p>\n);
    }
}

#### supporting functions ##

sub _removepasswd {
    my $dir = shift;
    return undef if !$dir || !-d "$Cpanel::homedir/.htpasswds/$dir/";
    unlink "$Cpanel::homedir/.htpasswds/$dir/passwd"
        or print qq(<p id="er"> Could not remove remove admin area passwd file: $!</p>\n);
    my @rmpath = split /\//, $dir;
    while(@rmpath) {
        rmdir("$Cpanel::homedir/.htpasswds/" . join('/', @rmpath)) if @rmpath;
        # no warn/die because if the sub directories are not empty we don't want them removed :)
        pop @rmpath;
    }
    1;
}

sub _writereg {
    my($hr, $file) = @_;
    die 'First arg not hashref' if ref $hr ne 'HASH';
    open RG, '>', $file or return; # die "open store ($file) failed: $!";
    binmode RG;
    Storable::store_fd($hr, \*RG) or return; # die "Store failed: $!";
    close RG;
    1;
}

sub checkmaxdbs {
    my $dbc;
    return 1 if !wantarray && ($Cpanel::CPDATA{MAXSQL} =~ /unlimited/i || $Cpanel::CPDATA{MAXSQL}  eq '');
    if ($Cpanel::CPCACHE{mysql}{cached}) {
        $dbc = ref $Cpanel::CPCACHE{mysql}{DB} eq 'HASH' ? keys %{$Cpanel::CPCACHE{mysql}{DB}} : 0;
    } else {
        $dbc = int(`$Cpanel::CONF{root}/bin/mysqlwrap COUNTDBS`); #safesecure2
    }
    return (1, $dbc) if $Cpanel::CPDATA{MAXSQL} =~ /unlimited/i || $Cpanel::CPDATA{MAXSQL}  eq '';
    return wantarray ? (0, $dbc) : 0 if $dbc >= $Cpanel::CPDATA{MAXSQL};
    return wantarray ? (1, $dbc) : 1;
}

sub my_createdbs {
    my $obj = shift;
    my $db_ar = shift || [];
    my $needs = @{ $db_ar };
    if ($needs) {
        my ($ok,$cnt) = checkmaxdbs();
        my $nume = $Cpanel::CPDATA{MAXSQL} =~ m/^\d+$/ ? $Cpanel::CPDATA{MAXSQL} : 999999;
        if (!$obj->{existing_mysql} && (!$ok || ($cnt + $needs) > $nume)) {
            my $s = $needs == 1 ? '' : 's';
            my $s2 = $nume == 1 ? '' : 's';
            print "Sorry, this feature requires $needs additional mysql database$s, and your current plan limits you to $Cpanel::CPDATA{MAXSQL} database$s2.<br>\n";
            return 0;
        } else {
            my $dbuserprefix = $Cpanel::user;
            $dbuserprefix =~ s/\_//g;
            for my $db (@{ $db_ar }) {
                my $agc = 1;
                my @dbs = split /\n/, `/usr/local/cpanel/bin/mysqlwrap LISTDBS`;
                while(grep /\Q_$db$agc\E/, @dbs) {
                    $agc++;
                }
                my $ugc = $agc;
                my %dbusr;
                my $users = `/usr/local/cpanel/bin/mysqlwrap LISTUSERS`;
                @dbusr{ split(/\n/, $users) } = ();
                while(exists $dbusr{ "${dbuserprefix}_$db$ugc" }) {
                    $ugc++;
                }
                my $rndpass = $obj->{mysql_pass} || Cpanel::Form::getranddata(10);
                $obj->{mysql_pass} = $rndpass if !$obj->{mysql_pass};
                $obj->{table_prefix} = "$db$ugc" if !$obj->{table_prefix};
                $obj->{mysql_user_post} = "$db$ugc" if !$obj->{mysql_user_post};
                $obj->{mysql_user} = "${dbuserprefix}_$obj->{mysql_user_post}";
                $obj->{mysql}->{$db}->{_post} = "$db$agc";
                open MW, '|/usr/local/cpanel/bin/mysqlwrap';
                print MW "ADDUSER $obj->{mysql_user_post} $rndpass";
                close MW ;
                if ($obj->{existing_mysql}) {
                    if (grep /^\Q$obj->{existing_mysql}\E$/, @dbs) {
                        my $tablesexist = 0;
                        open MYCNF, '>', "$Cpanel::homedir/.my.cnf" or die "SQL FILE failed: $!";
                        print MYCNF qq([client]\nuser="$ENV{REMOTE_USER}"\npass="$ENV{REMOTE_PASSWORD}"\n);
                        close MYCNF;

                        open2(\*MYRES, \*MYSQL, Cpanel::DbUtils::find_mysql() . " $obj->{existing_mysql}" );
                        print MYSQL 'SHOW TABLES\G';
                        close MYSQL;
                        while(<MYRES>) {
                            chomp();
                            next unless m/Tables\_in\_/;
                            my($tbl) = $_ =~  m/^Tables\_in\_\Q$obj->{existing_mysql}\E\:\s+(.*)/;
                            if ($tbl =~ m/^$obj->{table_prefix}\_/) {
                                $tablesexist = 1;
                                last;
                            }
                        }
                        close MYRES;

                        unlink "$Cpanel::homedir/.my.cnf";

                        if (!$tablesexist) {
                            $obj->{mysql}->{$db}->{_post} = $obj->{existing_mysql};
                            $obj->{mysql}->{$db}->{_post} =~ s/^\Q$dbuserprefix\E\_//;
                            $obj->{mysql}->{$db}->{sqldb}  = $obj->{existing_mysql};
                        } else {
                            print "Please specify a different table prefix that is not in use in the selected db.";
                            return 0;
                        }
                    } else {
                        print "Please specify a valid database.";
                        return 0;
                    }
                } else {
                    system('/usr/local/cpanel/bin/mysqlwrap', 'ADDDB', $obj->{mysql}->{$db}->{_post});
                    $obj->{mysql}->{$db}->{sqldb}  = "${dbuserprefix}_$obj->{mysql}->{$db}->{_post}";
                }
                system('/usr/local/cpanel/bin/mysqlwrap', 'ADDUSERDB', $obj->{mysql}->{$db}->{_post}, $obj->{mysql_user_post});
                $obj->{mysql}->{$db}->{sqluser} = $obj->{mysql_user};
                $obj->{mysql}->{$db}->{sqlpass} = $rndpass;
                $obj->{mysql}->{$db}->{sqlhost} = `/usr/local/cpanel/bin/mysqlwrap GETHOST`;
                $obj->{mysql}->{$db}->{'mysql-version'} = $Cpanel::CONF{'mysql-version'};
            }
        }
    }
    return 1;
}

sub my_dropdbs {
    my $obj = shift;
    my $cnt = 1;
    if ($obj->{installed}->{ $obj->{workinginstall} }->{table_prefix} && $obj->{installed}->{ $obj->{workinginstall} }->{existing_mysql}) {
        _droptables($obj->{installed}->{ $obj->{workinginstall} }->{existing_mysql}, $obj->{installed}->{ $obj->{workinginstall} }->{table_prefix});
    } else {
        for(keys %{ $obj->{installed}->{ $obj->{workinginstall} }->{mysql} }) {
            my $hasshared = 0;
            my $db = $obj->{installed}->{ $obj->{workinginstall} }->{mysql}->{$_}->{_post};
            my $en = $obj->{installed}->{ $obj->{workinginstall} }->{mysql}->{$_}->{sqldb};
            for(keys %{ $obj->{installed} }) {
                if ($obj->{installed}->{ $_ }->{existing_mysql} eq $en) {
                    $hasshared = 1;
                    last;
                }
            }
            if ($hasshared) {
                _droptables($en, $obj->{installed}->{ $obj->{workinginstall} }->{table_prefix});
            } else {
                print "<br />\n" if $cnt == 1;
                $cnt++;
                system('/usr/local/cpanel/bin/mysqlwrap', 'DELDB', $db);
                print "<br />\n";
                $obj->{mysql_dropped}->{$en} = 1;
            }
        }
    }
    # update $Cpanel::CPCACHE{mysql}{DB} if $Cpanel::CPCACHE{mysql}{cached} ???
    return 1;
}

sub _droptables {
    my($db, $pfx) = @_;
    open MYCNF, '>', "$Cpanel::homedir/.my.cnf" or die "SQL FILE failed: $!";
    print MYCNF qq([client]\nuser="$ENV{REMOTE_USER}"\npass="$ENV{REMOTE_PASSWORD}"\n);
    close MYCNF;

    my %tbls;
    open2(\*MYRES, \*MYSQL, Cpanel::DbUtils::find_mysql() . " $db" );
    print MYSQL 'SHOW TABLES\G';
    close MYSQL;

    while(<MYRES>) {
        chomp();
        next unless m/Tables\_in\_/;
        my($tbl) = $_ =~  m/^Tables\_in\_\Q$db\E\:\s+(\w+)/;
        if ($tbl =~ m/^\Q$pfx\E\_/) {
            $tbls{$tbl}++;
        }
    }
    close MYRES;
    my $sql_data = 'DROP TABLE ' . join(',', keys %tbls) . ";\n";
    if (%tbls) {
        print "<p>Cleaning out tables: $sql_data</p>\n";
        open2(\*MYRES, \*MYSQL, Cpanel::DbUtils::find_mysql() . " $db" );
        print MYSQL $sql_data;
        close MYSQL;
    }
    unlink "$Cpanel::homedir/.my.cnf";
    1;
}

sub my_dosql {
    my ($obj,$sql,$chg_hr,$usr,$pss) = @_;
    $usr = $obj->{mysql_user} if !$usr;
    $pss = $obj->{mysql_pass} if !$pss;
    $chg_hr = {} if ref $chg_hr ne 'HASH';
    if (-e $sql) {
        open SQL, $sql or die "Could not open $sql: $!";
        my $sql_data = join '', <SQL>;
        close SQL;
        for(keys %{ $chg_hr }) {
            $sql_data =~ s/\[\% \Q$_\E \%\]/$chg_hr->{$_}/g;
        }
        for(keys %{ $obj }) {
            $sql_data =~ s/\[\% \Q$_\E \%\]/$obj->{$_}/g;
        }
        open MYCNF, '>', "$Cpanel::homedir/.my.cnf" or die "SQL FILE failed: $!";
        print MYCNF qq([client]\nuser="$usr"\npass="$pss"\n);
        close MYCNF;
        open MYSQL, '|' . Cpanel::DbUtils::find_mysql(); # or die pipes not so valid
        print MYSQL $sql_data;
        close MYSQL;
        unlink "$Cpanel::homedir/.my.cnf";
    }
    1;
}

# using $info_hr->{postgre}
sub pg_createdbs { 1 }
sub pg_dropdbs { 1 }
sub pg_dosql { 1 }

sub procconfigfile {
    my $obj = shift;
    my $fls = shift;
    my $map = shift || \%{ $obj }; # reference a derefernced blessed object so its a normal hash
    # flatten sql hashref for mysql.dbname.sqldb type access
    for my $db(keys %{ $map->{mysql} }) {
        for(keys %{ $map->{mysql}->{$db} }) {
            $map->{"mysql.$db.$_"} = $map->{mysql}->{$db}->{$_};
        }
    }
    for my $db(keys %{ $map->{postgre} }) {
        for(keys %{ $map->{postgre}->{$db} }) {
            $map->{"postgre.$db.$_"} = $map->{postgre}->{$db}->{$_};
        }
    }
    my $dir = shift || $obj->{installdir};
    return 1 if ref $fls ne 'ARRAY';
    for(@{ $fls }) {
        open CNF, "$dir/$_" or die "Could not open $dir/$_: $!";
        my $cnf_data = join '', <CNF>;
        close CNF;
        for(keys %{ $map }) {
            $cnf_data =~ s/\[\% \Q$_\E \%\]/$map->{$_}/g;
        }
        open MYCNF, '>', "$dir/$_" or die "Could not write config $dir/$_: $!";
        print MYCNF $cnf_data;
        close MYCNF;
    }
    1;
}

sub _newregistry {
    my $obj = shift;
    my $num = 0;
    while(-e "$Cpanel::homedir/.cpaddons/$obj->{addon}.$num") {
        $num++;
    }
    # $obj->{addon} =~ s/\:/\\\:/g;
    if (open NR, '>', "$Cpanel::homedir/.cpaddons/$obj->{addon}.$num") {
        print NR ''; # necessary ?
        close NR;
        $obj->{registry} = "$obj->{addon}.$num";
        chmod 0600, "$Cpanel::homedir/.cpaddons/$obj->{addon}.$num" or print qq(<p id="er">Could not chmod ~/.cpaddons/$obj->{addon}.$num to 600! This will need done manually!</p>\n);
    } else {
        warn "Error making registry file: $! - $Cpanel::homedir/.cpaddons/$obj->{addon}.$num";
    }
}

sub register {
    my $obj = shift;
    $obj->_newregistry() if !defined $obj->{registry} || !$obj->{registry};
    # clean up any ./ installs
    $obj->{installdir} =~ s/\.\/$//;
    $obj->{url_to_install} =~ s/\/\.\///;
    $obj->{url_to_install_admin} =~ s/\/\.\///;
    my %hs = %{ $obj };
    delete $hs{installed}; # don't cache the cache
    for(keys %hs) {
        delete $hs{$_} if m/^form_/ || m/form$/;
    }
    my $file = $obj->{workinginstall} ? $obj->{installed}->{ $obj->{workinginstall} }->{registry} : $obj->{registry};
    # $file =~ s/\\\:/\:/g;
    _writereg(\%hs, "$Cpanel::homedir/.cpaddons/$file") or return;
}

sub unregister {
    my $obj = shift;
    return undef if !defined $obj->{registry} || !$obj->{registry};
    unlink "$Cpanel::homedir/.cpaddons/$obj->{installed}->{ $obj->{workinginstall} }->{registry}" or return;
}

sub _removeinstalldir {
    my $dir = shift;
    $dir =~ s/\.\.//g;
    $dir =~ s/^\///;
    if ($dir eq './') {
        my $hr = shift;
        my $vr = shift || $hr->{version};
        for(@{ $hr->{$vr}->{public_html_install_files} }) {
            unlink $_ or print qq(<p id="er"> Could not remove $_: $!</p>\n);
        }
        if (ref $hr->{$vr}->{public_html_install_dirs} eq 'ARRAY') {
            for(@{ $hr->{$vr}->{public_html_install_dirs} }) {
                rmdir $_;
            }
        }
        if (ref $hr->{$vr}->{public_html_install_unknown} eq 'ARRAY') {
            for(@{ $hr->{$vr}->{public_html_install_unknown} }) {
                system "rm -rf $_";
            }
        }
        return 1;
    } else {
        if (system('rm','-rf',"./$dir") == 0) {
            my @rmpath = split /\//, $dir;
            pop @rmpath;
            while(@rmpath) {
                rmdir join '/', @rmpath; # no warn/die because if the sub directories are not empty we don't want them removed :)
                pop @rmpath;
            }
            return 1;
        }
        else {
            return 0;
        }
   }
}

sub _file_md5_hex { # hacked/stripped idea of file_md5_hex() from http://search.cpan.org/src/DMUEY/Digest-MD5-File-0.01/File.pm
    open FL, shift() or return;
    return Digest::MD5::md5_hex(<FL>);;
}

sub installapi { # to finish
    my($addon, $user, $input) = @_;

    my $oldFORM      = \%Cpanel::FORM; # main:: or Cpanel:: ???
    $input->{action} = 'install';

    # user info how $Cpanel:: stuff ok?
    %Cpanel::FORM    = %{ $input };
    cPAddons_mainpg( $input );
    %Cpanel::FORM    = %{ $oldFORM };
}

sub remove_cron {
    my($obj) = @_;
    my $ist = $obj->{installed}->{ $obj->{workinginstall} };
    if (defined $ist->{cron} &&  $ist->{cron}) {
        chomp $ist->{cron};
        if ($ist->{cron}) {
            my $cron = `crontab -l`;
            $cron =~ s{\n?\Q$ist->{'cron'}\E\n?}{}g;

            print "<br /><br />Removing Cron Jobs...\n";
            if ( open my $cron_fh, '>', '.crontab_tmp' ) {
                print {$cron_fh} $cron;
                close $cron_fh;
                system 'crontab', '.crontab_tmp';
                unlink '.crontab_tmp';
                print "Done!<br />\n";
            }
            else {
                print qq(<p id="er">Could not remove cron entries, this will need done manually:<br /><pre>$ist->{cron}</pre></p>\n);
            }
        }
    }
}

sub add_cron {
    my($obj, $info_hr) = @_;
    if (defined $info_hr->{cron} && $info_hr->{cron}) {
        chomp $info_hr->{cron};
        if ($info_hr->{cron}) {

            $obj->{cron} = $info_hr->{cron};
            for(keys %{ $obj }) {
                $obj->{cron} =~ s/\[\% $_ \%\]/$obj->{$_}/g;
            }
            my $cron = `crontab -l` . "\n$obj->{cron}\n";
            print "<br /><br />Adding Cron Jobs...\n";
            if ( open my $cron_fh, '>', '.crontab_tmp' ) {
                print {$cron_fh} $cron;
                close $cron_fh;
                system 'crontab', '.crontab_tmp';
                unlink '.crontab_tmp';
                print "Done!<br />\n";
            }
            else {
                print qq(<p id="er">Could not create cron entries, this will need done manually:<br /><pre>$cron</pre></p>\n);
            }
        }
    }
}

sub do_vendor_liscense { # to finish
    my($obj, $info_hr, $input_hr, $ist) = @_;

    my $lisc = defined $ist && ref $ist eq 'HASH' ? $ist->{vendor_liscense}
                                                  : $input_hr->{vendor_liscense};

    if (defined $info_hr->{vendor_liscense} && ref $info_hr->{vendor_liscense} eq 'HASH') {
        if ($info_hr->{vendor_liscense}{verify_url}) {
            my $url = $info_hr->{vendor_liscense}{verify_url};
            # fix up url here
            my $html = ''; # get $url here
            if ( !$info_hr->{vendor_liscense}{url_says_its_ok}->($html) ) {
                print qq(<p id="er">The vendor said license is invalid. Please contact them regarding this.</p>\n);
                exit;
            }
        }
        else {
            if ($lisc  !~ m/^\w+$/) {
                print qq(<p id="er">Vendor Liscense must contain only numbers, letters, and underscores.</p>\n);
                exit;
            }
        }
        $obj->{vendor_liscense} = $lisc if !defined $ist || ref $ist ne 'HASH';
    }
    return 1;
}

1;

