#!/usr/bin/perl

# During install, we're going to end up installing two prefpanes:
# SharingPref.prefPane in the usual spot (for Panther) and
# ARDPref.prefPane (for 10.2.) hidden inside the app.

# On 10.2, we always want to keep the original SharingPref file, and on 
# 10.3, we want to keep the later version. 
########################################################
use File::Spec;
use File::Copy;
use File::Path;

my $PKGDIR					= File::Spec->canonpath(	$ARGV[0]																										);
my $TARGET					= File::Spec->canonpath(	$ARGV[2]																										);
my $PLIST_BUDDY				= File::Spec->canonpath(	$PKGDIR . "/Contents/Resources/PlistBuddy"																		);
my $CORE_SVCS				= File::Spec->canonpath(	$TARGET . "/System/Library/CoreServices"																		);
my $SYSTEM_VERS				= File::Spec->canonpath(	$CORE_SVCS . "/SystemVersion.plist"																				);
my $TRASH_DIR				= File::Spec->canonpath(	$TARGET . "/private/tmp/fpuzhpx-$$"																				);
my $PREF_PANES				= File::Spec->canonpath(	$TARGET . "/System/Library/PreferencePanes"																		);
my $SHARING_PREFS			= File::Spec->canonpath(	$PREF_PANES . "/SharingPref.prefPane"																			);
my $TEMP_SHARING_PREFS		= File::Spec->canonpath(	$PREF_PANES . "/SharingPref.tempSave"																			);
my $ARD_PREFS				= File::Spec->canonpath(	$PREF_PANES . "/ARDPref.prefPane"																				);
my $DEFAULT_FW				= File::Spec->canonpath(	$TARGET . "/System/Library/PrivateFrameworks/NetworkConfig.framework/Resources/DefaultFirewallInfo.plist"		);
my $TEMP_DEFAULT_FW			= File::Spec->canonpath(	$TARGET . "/System/Library/PrivateFrameworks/NetworkConfig.framework/Resources/DefaultFirewallInfo.tempSave"	);
my $FIREWALL_PLIST			= File::Spec->canonpath(	$TARGET . "/Library/Preferences/com.apple.sharing.firewall.plist"												);
my $DEFAULT_FIREWALL_PLIST	= File::Spec->canonpath(	$SHARING_PREFS . "/Contents/Resources/DefaultFirewallInfo.plist"	);

########################################################
die "No $TEMP_SHARING_PREFS, so no control panel adjudication" unless (-e $TEMP_SHARING_PREFS);
rmtree $TRASH_DIR;
mkdir $TRASH_DIR;

###
if ((!-e  $SYSTEM_VERS) ||
	CheckVersion( $SYSTEM_VERS, "10.3", "ProductVersion", "<" )) 
{
	# If the target disk is running 10.2 or earlier, put the original SharingPrefs back where we found them.
	print  ( "Putting back old SharingPrefs.prefPane\n");
	
    print  ( "/bin/mv    $SHARING_PREFS         $TRASH_DIR\n" ) ;
    (system( "/bin/mv",  $SHARING_PREFS     ,   $TRASH_DIR ) >>8) == 0 or die "Failed to move to trash: $!";
    print  ( "/bin/mv    $TEMP_SHARING_PREFS    $SHARING_PREFS\n");
    (system( "/bin/mv",  $TEMP_SHARING_PREFS,   $SHARING_PREFS) >>8) == 0 or die "Failed to move back: $!";
	
	## Move the ARD Jaguar Prefs Pane from temporary install location
	## to official Jaguar location.
	print ("Moving Jaguar preference pane into position\n");
	
	if (-e $ARD_PREFS) {
		print  ( "/bin/mv    $ARD_PREFS         $TRASH_DIR\n" ) ;
		(system( "/bin/mv",  $ARD_PREFS     ,  $TRASH_DIR ) >>8) == 0 or die "Couldn't nail old ARDPref: $!";
	}
	(system( "/bin/cp", "-Rp",
			$CORE_SVCS . "/RemoteManagement/ARDAgent.app/Contents/Resources/ARDPref.prefPane/",
			$ARD_PREFS )  >>8) == 0 or die "Couldn't move ARDPref into position: $!";
	system("/usr/bin/touch", $PREF_PANES);
	
	# Now we must hack some plists
	UpdateJaguarFirewallForARD();
	# we also need to call a tool:
	system("/System/Library/PrivateFrameworks/Admin.framework/Resources/firewalltool","");

	# Now we need to move the "tempSaved" file to the trash
	print "/bin/mv " . $TEMP_DEFAULT_FW . " " . $TRASH_DIR . "\n";
	system( "/bin/mv", $TEMP_DEFAULT_FW,        $TRASH_DIR);

} else {
###
	# If the target disk is running 10.3 or later, keep the later of the two versions.
	my $TEMP_SHARING_PREFS_VERSION  = Get5PartVersion($TEMP_SHARING_PREFS);
	my $SHARING_PREFS_VERSION		= Get5PartVersion($SHARING_PREFS);
	
	print $TEMP_SHARING_PREFS . " has version " . $TEMP_SHARING_PREFS_VERSION . "\n";
	print $SHARING_PREFS . " has version " . $SHARING_PREFS_VERSION . "\n";
	
	if (Compare5PartVersions($TEMP_SHARING_PREFS_VERSION,$SHARING_PREFS_VERSION) == 1) {
		# here means that the one that we moved aside is newer,
		# so put it back into place
		print  ( "Putting back old SharingPrefs.prefPane\n");

		print "/bin/mv " . $SHARING_PREFS . " " . $TRASH_DIR . "\n";
		system( "/bin/mv", $SHARING_PREFS,        $TRASH_DIR);
		print "/bin/mv " . $TEMP_SHARING_PREFS . " " . $SHARING_PREFS . "\n";
		system( "/bin/mv", $TEMP_SHARING_PREFS,        $SHARING_PREFS);
		# move back /System/Library/PrivateFrameworks/NetworkConfig.framework/Resources/DefaultFirewallInfo.plist
		print "/bin/mv " . $DEFAULT_FW . " " . $TRASH_DIR . "\n";
		system( "/bin/mv", $DEFAULT_FW,        $TRASH_DIR);
		print "/bin/mv " . $TEMP_DEFAULT_FW . " " . $DEFAULT_FW . "\n";
		system( "/bin/mv", $TEMP_DEFAULT_FW,		$DEFAULT_FW);
	}
	else
	{
		# here means that we're keeping the new SharingPrefs that we just installed
		# so we need to hack some plists
		print  ( "Keeping installed SharingPrefs.prefPane\n");
		UpdatePantherFirewallForARD();
		# we also need to call a tool:
		system("/System/Library/PrivateFrameworks/NetworkConfig.framework/Versions/A/Resources/firewalltool","");
		# Get rid of the DefaultFirewallInfo.tempSave file we made during preflight
		print "/bin/mv " . $TEMP_DEFAULT_FW . " " . $TRASH_DIR . "\n";
		system( "/bin/mv", $TEMP_DEFAULT_FW,        $TRASH_DIR);
		# Get rid of the tempSave copy of the sharing prefs control panel
		print "/bin/mv " . $TEMP_SHARING_PREFS . " " . $TRASH_DIR . "\n";
		system( "/bin/mv", $TEMP_SHARING_PREFS,        $TRASH_DIR);
	}
}

# Blow up temp storage
rmtree($TRASH_DIR);

exit(0);


########################################################
########################################################
########################################################
########################################################
########################################################
########################################################

########################################################
sub UpdateJaguarFirewallForARD {

	die "No PlistBuddy!\n" unless -e $PLIST_BUDDY;
	
	print "Updating Jaguar Firewall prefs for ARD\n";
	
	# it is extremely unlikely that the $DEFAULT_FIREWALL_PLIST does not exist, but still:
	if( -e $DEFAULT_FIREWALL_PLIST) {
	
		# First, kill off the System Preferences if it is running
		my $IS_RUNNING = 0;
		my $APP = "System\ Preferences";
		my $PID = 0;
		my $rest;
		
		open(PSOUT, "/bin/ps -awwx |");
		while( <PSOUT> ) {
			if( /$APP\.app/ ) {
				($PID, $rest) = split(' ');
				$IS_RUNNING = 1;
			}
		}
		close(PSOUT);
		
		if(1 eq $IS_RUNNING) {
			system("kill", "-s", "term", $PID);
		}

		
		# for sanity.  It's going to be there unless someone really messed up their preferences
		system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table dict");
		
		# proceed with adding the things ARD needs, and settings their values
		system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop dict");

		system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop:editable bool");
		system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Set :Table:Apple\\ Remote\\ Desktop:editable false");
	
		system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop:enable integer 1");

                # Add the "row" value for the ARD dictionary, with a value one greater than anything present
                my $existingRow = `\"$PLIST_BUDDY\" \"$DEFAULT_FIREWALL_PLIST\" -c \"Print :Table:Apple\\ Remote\\ Desktop:row\"`;
                
                if($existingRow =~ m/Does Not Exist/) {
                    my $adjustedRow = 0;
                    my $line = "";

                    foreach $line (`\"$PLIST_BUDDY\" \"$DEFAULT_FIREWALL_PLIST\" -c \"Print :Table:\"`) {
                        if($line =~ m/row = (\d?)/) {
                            if($1 >= $adjustedRow) {
                                # Set it to 1 higher than anything we find.
                                $adjustedRow = ($1 + 1);
                            }
                        }
                    }
                                    
                    system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop:row integer $adjustedRow");
                }

		# only add certain port values if they're not already added, so reinstals don't litter the plist
		system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop:port array");
		my $ports = `\"$PLIST_BUDDY\" \"$DEFAULT_FIREWALL_PLIST\" -c \"Print :Table:Apple\\ Remote\\ Desktop:port:\"`;

		unless($ports =~ m/3283$/m) {
			system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop:port:0 string");
			system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Set :Table:Apple\\ Remote\\ Desktop:port:0 3283");
		}

		unless($ports =~ m/5900$/m) {
			system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Add :Table:Apple\\ Remote\\ Desktop:port:0 string");
			system($PLIST_BUDDY, $DEFAULT_FIREWALL_PLIST, "-c", "Set :Table:Apple\\ Remote\\ Desktop:port:0 5900");
		}
	} else 
	{ print "No $DEFAULT_FIREWALL_PLIST\n"; }

	# do not create firewall settings; only modify existing ones
	if( -e $FIREWALL_PLIST) {
		
		# for sanity.  It's going to be there unless someone really messed up their preferences
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall dict");

		# proceed with adding the things ARD needs, and settings their values
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop dict");

		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:editable integer 0");
	
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:enable integer 0");

                # Add the "row" value for the ARD dictionary, with a value one greater than anything present
                my $existingRow = `\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :firewall:Apple\\ Remote\\ Desktop:row\"`;
                
                if($existingRow =~ m/Does Not Exist/) {
                    my $adjustedRow = 0;
                    my $line = "";
                    
                    foreach $line (`\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :firewall:\"`) {
                        if($line =~ m/row = (\d?)/) {
                            if($1 >= $adjustedRow) {
                                # Set it to 1 higher than anything we find.
                                $adjustedRow = ($1 + 1);
                            }
                        }
                    }
                                    
                    system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:row integer $adjustedRow");
                }

		# only add certain port values if they're not already added, so reinstals don't litter the plist
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:port array");
		my $ports = `\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :firewall:Apple\\ Remote\\ Desktop:port:\"`;

		unless($ports =~ m/3283$/m) {
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:port:0 string");
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :firewall:Apple\\ Remote\\ Desktop:port:0 3283");
		}

		unless($ports =~ m/5900$/m) {
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:port:0 string");
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :firewall:Apple\\ Remote\\ Desktop:port:0 5900");
		}

		# modify the enabled state if sharing has been configured to have ARD on, per hostconfig
		my $hostConfig = `/bin/cat \"$TARGET/etc/hostconfig\"`;

		if($hostConfig =~ m/ARDAGENT=-YES-/s) {
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :firewall:Apple\\ Remote\\ Desktop:enable 1");

			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :allports array");
			
			# only add certain port values if they're not already added, so reinstals don't litter the plist
			my $allPorts = `\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :allports:\"`;
			unless($allPorts =~ m/3283$/m) {
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :allports:0 string");
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :allports:0 3283");
			}
			reset;
			unless($allPorts =~ m/5900$/m) {
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :allports:0 string");
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :allports:0 5900");
			}
		}
	} else 
	{ print "No $FIREWALL_PLIST\n"; }
}

########################################################
sub UpdatePantherFirewallForARD {

	die "No PlistBuddy!\n" unless -e $PLIST_BUDDY;
	
	print "Updating Panther Firewall prefs for ARD\n";
	
	# do not create firewall settings; only modify existing ones
	if( -e $FIREWALL_PLIST) {
		
		# First, kill off the System Preferences if it is running
		my $IS_RUNNING = 0;
		my $APP = "System\ Preferences";
		my $PID = 0;
		my $rest;
		
		open(PSOUT, "/bin/ps -awwx |");
		while( <PSOUT> ) {
			if( /$APP\.app/ ) {
				($PID, $rest) = split(' ');
				$IS_RUNNING = 1;
			}
		}
		close(PSOUT);
		
		if(1 eq $IS_RUNNING) {
			system("kill", "-s", "term", $PID);
		}
		# for sanity.  It's going to be there unless someone really messed up their preferences
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall dict");
		
		# proceed with adding the things ARD needs, and settings their values
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop dict");

		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:editable integer 0");
	
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:enable integer 0");

                # Add the "row" value for the ARD dictionary, with a value one greater than anything present
                my $existingRow = `\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :firewall:Apple\\ Remote\\ Desktop:row\"`;
                
                if($existingRow =~ m/Does Not Exist/) {
                    my $adjustedRow = 0;
                    my $line = "";
                    
                    foreach $line (`\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :firewall:\"`) {
                        if($line =~ m/row = (\d?)/) {
                            if($1 >= $adjustedRow) {
                                # Set it to 1 higher than anything we find.
                                $adjustedRow = ($1 + 1);
                            }
                        }
                    }
                     
                    system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:row integer $adjustedRow");
                }

		# only add certain port values if they're not already added, so reinstals don't litter the plist
		system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:port array");
		my $ports = `\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :firewall:Apple\\ Remote\\ Desktop:port:\"`;

		unless($ports =~ m/3283$/m) {
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:port:0 string");
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :firewall:Apple\\ Remote\\ Desktop:port:0 3283");
		}

		unless($ports =~ m/5900$/m) {
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :firewall:Apple\\ Remote\\ Desktop:port:0 string");
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :firewall:Apple\\ Remote\\ Desktop:port:0 5900");
		}

		# modify the enabled state if sharing has been configured to have ARD on, per hostconfig
		my $hostConfig = `/bin/cat \"$TARGET/etc/hostconfig\"`;

		if($hostConfig =~ m/ARDAGENT=-YES-/s) {
			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :firewall:Apple\\ Remote\\ Desktop:enable 1");

			system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :allports array");
			
			# only add certain port values if they're not already added, so reinstals don't litter the plist
			my $allPorts = `\"$PLIST_BUDDY\" \"$FIREWALL_PLIST\" -c \"Print :allports:\"`;
			unless($allPorts =~ m/3283$/m) {
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :allports:0 string");
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :allports:0 3283");
			}

			unless($allPorts =~ m/5900$/m) {
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Add :allports:0 string");
				system($PLIST_BUDDY, $FIREWALL_PLIST, "-c", "Set :allports:0 5900");
			}
		}
                
                
	} else
	{ print "No $FIREWALL_PLIST!\n"; }
}

########################################################
sub Get5PartVersion
{
	# Get the installer-style 5-part version number from a bundle, 
	# ASSUMING the info is in $bundle/Contents/version.plist
	# DANGER: Correct bundles need not always follow this convention.
	my $bundle = $_[0];
	my $plist = File::Spec->canonpath($bundle . "/Contents/version.plist");
	
	return "0.0.0.0.0" unless (-e $plist);
	
	my $CFBSVS			= `"$PLIST_BUDDY" -c "Print :CFBundleShortVersionString" "$plist"`;
	my $SourceVersion   = `"$PLIST_BUDDY" -c "Print :SourceVersion" "$plist"`;
	my $BuildVersion	= `"$PLIST_BUDDY" -c "Print :BuildVersion" "$plist"`;
	chomp $CFBSVS;
	chomp $SourceVersion;
	chomp $BuildVersion;

	# Someday I should really make this correctly format CFBSVS as three integers separated by two dots
	return $CFBSVS . "." . $SourceVersion . "." . $BuildVersion;
}

########################################################
sub Compare5PartVersions
{
	my ($va, $vb) = @_;
	my $vaParts = [split /\./, $va];
	my $vbParts = [split /\./, $vb];
	for (0..4) {my $x = ($vaParts->[$_] <=> $vbParts->[$_]); return($x) if $x}
	return(0);
}

########################################################
sub CheckVersion
{
    my $path            = $_[0];
    my $version         = $_[1];
    my $keyName         = $_[2];
    my $operator        = $_[3];
    
    my $oldSeperator;
    my $plistData;
    my @items;
    my $item;
    my $versiondata;
    my $i;
    my @theVersionArray;
    my %versiondata;
    my @versionArray;

    # if there's no THERE there, we return FALSE
    if (! -e $path) {
        return 0;
    }

    if (!$operator) {
        $operator = "==";
    }

    $oldSeperator = $/;
    $/ = \0;

    open( PLIST, "$path") || do {
        return 0;
    };

    $plistData = <PLIST>;
    $plistData =~ /<dict>(.*?)<\/dict>/gis;

    @items = split(/<key>/, $plistData);

    shift @items;
    foreach $item (@items) {
        $item =~ /(.*?)<\/key>.*?<string>(.*?)<\/string>/gis;
        $versiondata{ $1 } = $2;
    }

    close(PLIST);

    $/ = $oldSeperator;

    @theVersionArray = split(/\./, $versiondata{$keyName});
    for ($i = 0; $i < 3; $i++) {
        if(!$theVersionArray[$i]) {
            $theVersionArray[$i] = '0';
        }
    }

    @versionArray = split(/\./, $version);
    
    my $actualVersion;

    for ($i = 0; $i < 3; $i++) {
        if (($theVersionArray[$i] != $versionArray[$i]) or ($i == 2)) {

            $actualVersion = $theVersionArray[$i];
            $version = $versionArray[$i];

            last;
        }
    }

    my $expression = '$actualVersion ' . $operator . ' $version';
    if( eval ($expression) )
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

########################################################
