#!/usr/local/bin/perl5 -T

$OSH = "/usr/local/bin/osh";

# Just in case we need to clear the IFS for the system call below.
$ENV{'IFS'} = '' if $ENV{'IFS'};
$ENV{'PATH'} = '/bin:/sbin';

# First we need to figure out which program it is.

if ($0 =~ /(chown|chgrp|chmod)/) {
  $prog = $1;
} else {
  print "Invoked as incorrect program $1.\n";
  print "This script is used as a wrapper for chown, chgrp and chmod.\n";
  exit(1);
} 

# Any flags?
$recurse = 0;
$force = 0;
$symbolic = 0;
while ($_ = $ARGV[0], /^-[fhR]+$/) {
  $force = 1 if (/f/);
  $symbolic = 1 if (/h/);
  $recurse = 1 if (/R/);
  shift;
}

if (($#ARGV != 1) ||
    ((($ARGV[0] =~ /^-/) && ($prog !~ /^chmod$/)) ||
    (defined($flags) && ($flags =~ /h/) && ($prog =~ /^chmod$/)))) {
  if ((($ARGV[0] =~ /^-/) && ($prog !~ /^chmod$/)) ||
      (defined($flags) && ($flags =~ /h/) && ($prog =~ /^chmod$/))) {
    print "Improper command option.\n\n"
  }
  print "Usage:\n\n\t$prog [-fhR] user file\n\n" if ($prog =~ /^chown$/);
  print "Usage:\n\n\t$prog [-fhR] group file\n\n" if ($prog =~ /^chgrp$/);
  print "Usage:\n\n\t$prog [-fR] mode file\n\nmode is one of:\n\t{ugoa}[+-=]{rwxlsStT}\n\t{ugoa}[+-=]{ugo}\n\tabsolute mode\n\nNOTE: lsStT set the highorder bits and you must specify the other permissions.\n" if ($prog =~ /^chmod$/);
  print "Wild cards and multiple files are not allowed for security reasons.\n";
  print "As well, symlinks are not followed for $prog operations.\n";
  exit(1);
}

# Get the first paramater.
if (!($prog =~ /^chmod$/)) {
  if ($ARGV[0] =~ /^(\w+)$/) {
    $param = $1;
  } else {
    print "User/group names are alphanumeric and must exist in GASH.\n";
    exit(1);
  }
} elsif ($ARGV[0] =~ /(.+)/) {
  $param = $1;
}
shift(@ARGV);

# Get the second paramater. Which is always a file/directory.
if ($ARGV[0] =~ /(\s*(.+)\s*)/) {
  $dest = $1;
  shift(@ARGV);
} else { print "Huh?\n"; exit (1); }

# Check for someone trying to escape things out. It's facist, but better
# safe than sorry.
if (($param =~ /["\\'`]/) || ($dest =~ /["\\'`]/)) {
  print "Special characters (ie. \", \\, ', `) are not allowed.\n";
  exit(1);
}

if ((($prog =~ /^chown$/) &&
     (system("/bin/ypmatch $param passwd > /dev/null 2>&1")))
    || (($prog =~ /^chgrp$/) &&
        (system("/bin/ypmatch $param group > /dev/null 2>&1")))
    || (($prog =~ /^chmod$/) &&
        !(($param =~ /^[0-7]{1,4}$/) ||
        ($param =~ /^[ugoa]*[+-=][rwxlsStT]*$/) ||
        ($param =~ /^[ugoa]*[+-=][ugo]$/)))
) {
  print "User/group names are alphanumeric and must exist in GASH.\n" if ($prog =~ /^chown|chgrp$/) ;
  print "No such user $param.\n\nUsage:\n\n\t$prog [-fhR] user file\n\n" if ($prog =~ /^chown$/);
  print "No such group $param.\n\nUsage:\n\n\t$prog [-fhR] group file\n\n" if ($prog =~ /^chgrp$/);
  print "Improper file mode.\n\nUsage:\n\n\t$prog [-fR] mode file\n\nmode is one of:\n\t{ugoa}[+-=][+-=]{rwxlsStT}\n\t{ugoa}[+-=][+-=]{ugo}\n\tabsolute mode\n\nNOTE: lsStT set the highorder bits and you must specify the other permissions.\n" if ($prog =~ /^chmod$/);
  print "Wild cards and multiple files are not allowed for security reasons.\n";
  print "As well, symlinks are not followed for $prog operations.\n";
  exit(1);
}

# Is file a full path? If not, we need to make sure it is before the
# system call below. (su will cd to/home/user if it can't access the cwd.)
#if ($dest =~ /^[^\/]/) {
#  $cwd = `pwd`;
#  chop($cwd);
#  $dest = $cwd . "/" . $dest;
#}

# Get the user, which is the owner of the parent process since the
# current process was setuid(0) before spawning off.
#
# Btw, this is how `ps` gets the info for printing as well.
# By name.
#$USER=(getpwuid((stat("/proc/". getppid))[4]))[0];
# By UID.
$USER=(getpwuid((stat("/proc/". getppid))[4]))[2];

# Convert param to appropriate numbers.
$param = (getpwnam($param))[2] if ($prog =~ /^chown$/);
$param = (getgrnam($param))[2] if ($prog =~ /^chgrp$/);
if ($prog =~ /^chmod$/) {
  if ($param =~ /^[0-7]{1,4}$/) {
    $mode = $param;
  } elsif ($param =~ /^[ugoa]*[+-=][rwxlsStT]*$/) {
    $himode = $lomode = 0;
    $himode = 4 if (($param =~ /^[=\-\+].*[sS]/) || ($param =~ /[ua].*[sS]/));
    $himode = $himode + 2 if (($param =~ /^[=\-\+].*[sSl]/) || ($param =~ /[ga].*[sSl]/));
    $himode = $himode + 1 if (($param =~ /^[=\-\+].*[tT]/) || ($param =~ /[oa].*[tT]/));
    $lomode = 4 if ($param =~ /r/);
    $lomode = $lomode + 2 if ($param =~ /w/);
    $lomode = $lomode + 1 if ($param =~ /x/);
    $mode = $himode * 1000;
    $mode = $mode + $lomode * 100 if (($param =~ /[ua]/) || (/^[=\-\+]/));
    $mode = $mode + $lomode * 10 if (($param =~ /[ga]/) || (/^[=\-\+]/));
    $mode = $mode + $lomode * 1 if (($param =~ /[oa]/) || (/^[=\-\+]/));
    $orgmode = oct(substr(sprintf("%lo", (stat($dest))[2]), -4));
  } else { # ($param =~ /^[ugoa]*[+-=][ugo]$/)
    $place = -1; # default to the other just incase something is strange.
    $place = -3 if ($param =~ /[+-=]u/);
    $place = -2 if ($param =~ /[+-=]g/);
    $himode = oct(substr(sprintf("%lo", (stat($dest))[2]), -4, 1));
    $lomode = oct(substr(sprintf("%lo", (stat($dest))[2]), $place, 1));
    $mode = $himode * 1000;
    $mode = $mode + $lomode * 100 if (($param =~ /^[=\-\+]/) || ($param =~ /[ua].*[=\-\+]/));
    $mode = $mode + $lomode * 10 if (($param =~ /^[=\-\+]/) || ($param =~ /[ga].*[=\-\+]/));
    $mode = $mode + $lomode * 1 if (($param =~ /^[=\-\+]/) || ($param =~ /[oa].*[=\-\+]/));
    $orgmode = oct(substr(sprintf("%lo", (stat($dest))[2]), -4));
  }
  if ($param =~ /\+/) {
    $param = oct($mode) | $orgmode;
    $param2 = sprintf("%lo", $param);
  } elsif ($param =~ /-/) {
    $param = (07777 ^ oct($mode)) & $orgmode;
    $param2 = sprintf("%lo", $param);
  } else {
    $param = oct($mode); 
    $param2 = sprintf("%lo", $param);
  }
}

if ($pid = fork) {
  wait;
  $status = ($? >> 8);
  if ($status == 1) {
    if ($recurse == 0) {
      die "chmod failed: $!\n" if (($prog =~ /^chmod$/) && !chmod($param, $dest));
      die "chown failed: $!\n" if (($prog =~ /^chown$/) && !chown($param, -1, $dest));
      die "chgrp failed: $!\n" if (($prog =~ /^chgrp$/) && !chown(-1, $param, $dest));
    } else {
      @INC = ("/opt/lib/perl5/sun4-solaris","/opt/lib/perl5");
      $thisdevice = (stat($dest))[0];
      require "find.pl";
      &find($dest);
    }
  } else {
    print "No permission to file $dest";
    print " - non-existant file" if (! -e $dest);
    print ".\n";
    exit(1);
  }
} elsif (defined $pid) {

  # Ignore the interrupts until we get into the program.
  $SIG{'INT'} = 'IGNORE';
  $SIG{'QUIT'} = 'IGNORE';

  # Set the previously gotten UID.
  $> = $< = $USER;

  # Call osh test -w to see if they have priveledge to write where they
  # are trying to.
  exit (system("$OSH", "test", "-w \"$dest\"") >> 8);
} else {
  die "Can't fork: $!\n";
}

sub wanted {
  # called by find to be done on each file

  (!(-l $_) &&
  $thisdevice == (stat($_))[0] &&
  ((($prog =~ /^chmod$/) && !chmod($param, $_)) || 
  (($prog =~ /^chown$/) && !chown($param, -1, $_)) ||
  (($prog =~ /^chgrp$/) && !chown(-1, $param, $_)))) &&
  die "$prog failed: $!\n";
}
