#!/usr/local/cpanel/3rdparty/bin/perl

#                                      Copyright 2026 WebPros International, LLC
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.

use strict;
use warnings;
use Cpanel::Locale           ();
use Cpanel::PwCache::Helpers ();
use Cpanel::PwCache::Build   ();
use Cpanel::SafeDir::MK      ();

exit run() unless caller();

sub run {
    my $locale = Cpanel::Locale->get_handle();

    Cpanel::PwCache::Helpers::no_uid_cache();    #uid cache only needed if we are going to make lots of getpwuid calls
    Cpanel::PwCache::Build::init_passwdless_pwcache();
    my $pwcache_ref = Cpanel::PwCache::Build::fetch_pwcache();

    my %GIDS = map { $_->[0] => $_->[3] } @{$pwcache_ref};

    # The 'nobody' directory, if there is one, should not be readable by user 'nobody',
    # since that would allow any Apache process to read it.
    $GIDS{'nobody'} = $GIDS{'root'};

    my $mode         = 0711;
    my $userdata_dir = '/var/cpanel/userdata';

    # Create the userdata directory if it does not already exist
    if ( !-d $userdata_dir ) {

        # If there is a non-directory file at that location, remove it
        if ( -e $userdata_dir ) {
            if ( unlink $userdata_dir ) {
                print "Removing non-directory file $userdata_dir\n";
            }
            else {
                print "Failed to remove non-directory file $userdata_dir\n";
                return 1;
            }
        }
        if ( !Cpanel::SafeDir::MK::safemkdir($userdata_dir) ) {
            print "Failed to create $userdata_dir\n";
            return 1;
        }
    }

    if ( ( ( stat($userdata_dir) )[2] & 07777 ) != $mode ) {
        print "Setting mode on $userdata_dir\n";
        if ( !chmod $mode, $userdata_dir ) {
            print "Failed to set mode on $userdata_dir\n";
            return 1;
        }
    }
    if ( opendir my $dh, $userdata_dir ) {
        $mode = 0750;
        my $userdir;
        foreach my $user ( grep !/^\./, readdir $dh ) {
            $userdir = "$userdata_dir/$user";
            if ( ( stat($userdir) )[5] != $GIDS{$user} ) {
                print "Setting group on $userdir ($GIDS{$user})\n";
                if ( !chown 0, $GIDS{$user}, $userdir ) {
                    print "Failed to set owner on $userdir\n";
                }
            }
            if ( ( ( stat(_) )[2] & 07777 ) != $mode ) {
                print "Setting mode on $userdir\n";
                if ( !chmod $mode, $userdir ) {
                    print "Failed to set mode on $userdir\n";
                }
            }
            normalize_user_entries( $userdir, $locale );
        }
        closedir $dh;
    }
    else {
        print "Unable to open $userdata_dir for reading.\n";
    }

    return 0;
}

# Normalize the immediate children of a user's userdata directory.
#
# Regular files must be world-readable (0644) so the cPanel user can read
# them after cpsrvd drops privileges. Directories must keep their execute
# bit: scope/ is created 0750 root:<user> by Cpanel::Config::userdata::Scope,
# and the user (plus the non-root backup transport on a root-squashed mount)
# must be able to traverse into it. Forcing directories to 0644 strips that
# execute bit and breaks both reads, so each entry is normalized by type.
#
# Returns the number of entries whose mode was changed.
sub normalize_user_entries {
    my ( $userdir, $locale ) = @_;

    opendir( my $userd_files, $userdir ) or return 0;

    my $modified = 0;
    foreach my $entry ( grep { !/^\./ } readdir($userd_files) ) {
        my $path = "$userdir/$entry";
        my $cur  = ( stat($path) )[2];
        next if !defined $cur;
        my $wanted = -d _ ? 0750 : 0644;
        next        if ( $cur & 07777 ) == $wanted;
        $modified++ if chmod( $wanted, $path );
    }
    closedir($userd_files);

    print "\t" . $locale->maketext(
        '[quant,_1,file was,files were,No files were] updated in “[_2]”.',
        $modified, $userdir
      )
      . "\n"
      if $modified;

    return $modified;
}

1;
