#!/usr/bin/perl -w

# $id$
# Copyright (C) 2009 Yuji OKAMURA
# 
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
# 
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
# 
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.

use strict;
use File::Copy;
use File::Spec;
use File::Path;
use Sys::Syslog;
use vars qw(
	$OriginalDir
	@LaunchAgents
	$LaunchAgentsDir
	$UserTemplateDir
	$SrcDir
);

$ENV{PATH} = '/usr/bin:/bin:/usr/sbin:/sbin';
$OriginalDir = '/usr/local/clamXav/share/LaunchAgents';
@LaunchAgents = qw(
	jp.sourceforge.clamav-update.SentryKeeper.plist
	jp.sourceforge.clamav-update.RotateClamXavScanLog.plist
);
$LaunchAgentsDir = 'Library/LaunchAgents';
$UserTemplateDir = '/System/Library/User Template';

sub PutLaunchAgents {
	my	($uid, $gid, $homeDir) = @_;
	my	$dstDir = File::Spec->catfile($homeDir, $LaunchAgentsDir);
	my	$isLaunchAgentsDirCreated = 0;

	unless (-d $dstDir) {
		unless (mkdir($dstDir)) {
			syslog('ERR', "Can't create directory %s: %s", $dstDir, $!);
			return 0;
		}
		syslog('INFO', "Created directory %s", $dstDir);
		unless (0 < chmod(0700, $dstDir)) {
			syslog('ERR', "Can't change permissions of %s", $dstDir);
			unless (rmdir($dstDir)) {
				syslog('ALERT', "Can't remove %s: %s", $dstDir, $!);
			}
			syslog('NOTICE', "Removed %s", $dstDir);
			return 0;
		}
		syslog('INFO', "Changed permissions of %s", $dstDir);
		unless (0 < chown($uid, $gid, $dstDir)) {
			syslog('ERR', "Can't change owner of %s", $dstDir);
			unless (rmdir($dstDir)) {
				syslog('ALERT', "Can't remove %s: %s", $dstDir, $!);
			}
			syslog('NOTICE', "Removed %s", $dstDir);
			return 0;
		}
		syslog('INFO', "Changed owner of %s", $dstDir);

		$isLaunchAgentsDirCreated = 1;
	}

	foreach my $launchAgent (@LaunchAgents) {
		my	$oldFile = File::Spec->catfile($dstDir, $launchAgent..'old');

		if (-f $oldFile) {
			unless (unlink($oldFile)) {
				syslog('ERR', "Can't remove %s", $oldFile);
				return 0;
			}
			syslog('INFO', "Removed %s", $oldFile);
		}
	}

	{
		my	$copiedCount = 0;

		foreach my $launchAgent (@LaunchAgents) {
			my	$srcFile = File::Spec->catfile($OriginalDir, $launchAgent);
			my	$dstFile = File::Spec->catfile($dstDir, $launchAgent);
			my	$oldFile = $dstFile.'.old';

			if (-f $dstFile) {
				unless (rename($dstFile, $oldFile)) {
					syslog('ERR', "Can't rename %s to %s", $dstFile, $oldFile);
					last;
				}
				syslog('INFO', "Renamed %s to %s", $dstFile, $oldFile);
			}

			unless (copy($srcFile, $dstFile)) {
				syslog('ERR', "Can't copy %s to %s: %s", $srcFile, $dstFile, $!);
				last;
			}
			syslog('INFO', "Copied %s to %s", $srcFile, $dstFile);
			unless (0 < chmod(0600, $dstFile)) {
				syslog('ERR', "Can't change permissions %s", $dstFile);
				last;
			}
			syslog('INFO', "Changed permissions %s", $dstFile);
			unless (0 < chown($uid, $gid, $dstFile)) {
				syslog('ERR', "Can't change owner %s", $dstFile);
				last;
			}
			syslog('INFO', "Changed owner %s", $dstFile);
		}
		continue {
			$copiedCount++;
		}

		if ($copiedCount == scalar(@LaunchAgents)) {
			foreach my $launchAgent (@LaunchAgents) {
				my	$oldFile = File::Spec->catfile($dstDir, $launchAgent.'.old');

				if (-f $oldFile) {
					unless (unlink($oldFile)) {
						syslog('WARNING', "Can't remove %s", $oldFile);
					}
					syslog('INFO', "Removed %s", $oldFile);
				}
			}
		}
		elsif ($isLaunchAgentsDirCreated) {
			unless (rmtree([$dstDir])) {
				syslog('ALERT', "Can't remove %s", $dstDir);
			}
			syslog('NOTICE', "Removed %s", $dstDir);

			return 0;
		}
		else {
			foreach my $launchAgent (@LaunchAgents) {
				my	$dstFile = File::Spec->catfile($dstDir, $launchAgent);
				my	$oldFile = $dstFile.'.old';

				last	unless ($copiedCount);

				if (-f $dstFile) {
					unless (unlink($dstFile)) {
						syslog('ALERT', "Can't remove %s", $dstFile);
					}
					syslog('NOTICE', "Removed %s", $dstFile);
				}
				if (-f $oldFile) {
					unless (rename($oldFile, $dstFile)) {
						syslog('ALERT', "Can't rename %s to %s", $oldFile, $dstFile);
					}
					syslog('NOTICE', "Renamed %s to %s", $oldFile, $dstFile);
				}
			}
			continue {
				$copiedCount--;
			}

			return 0;
		}
	}

	# sudo で launc agent をロードしてもなぜか有効にならない。
	# 当面はインストールしたら再起動にして回避する。
	if (0 and 0 and 500 < $uid and $uid < 4294967294) {
		system('sync');
		foreach my $launchAgent (@LaunchAgents) {
			my	$dstFile = File::Spec->catfile($dstDir, $launchAgent);

			if (system(qw(sudo -H -u), '#'.$uid, qw(launchctl load), $dstFile) == 0) {
				syslog('INFO', "Loaded %s", $dstFile);
			}
			else {
				syslog('WARNING', "Can't load launch agent %s: %d", $dstFile, $?);
			}
		}
	}

	return 1;
}

openlog('postinstall', 'ndelay,pid', 'install'); 

while (my @pwent = getpwent()) {
	next	unless (
		500 < $pwent[2] and $pwent[2] < 4294967294
			and
		-d $pwent[7]
			and
		-d File::Spec->catfile($pwent[7], 'Library')
	);

	unless (PutLaunchAgents($pwent[2], $pwent[3], $pwent[7])) {
		endpwent();
		closelog();
		exit(1);
	}
}
endpwent();

{
	my	@lprojs;

	unless (opendir(DH, $UserTemplateDir)) {
		syslog('WARNING', "Can't open directory %s", $UserTemplateDir);
		closelog();
		exit(2);
	}
	@lprojs = grep(/^\w+\.lproj$/, readdir(DH));
	closedir(DH);

	foreach (@lprojs) {
		unless (PutLaunchAgents(0, 80, File::Spec->catfile($UserTemplateDir, $_))) {
			closelog();
			exit(3);
		}
	}
}

closelog();
1;