œ_#ÁÕ§TE NAŒ“KeÉ:”(åŽÖJÞùY’‚ñùž7; «]Û ý`8g“¯B© jdÖÖ¸ðzœ¸¦4Ç3Kó^(ÍÖ¼ Õ€pvìwšõB4df$Èü^0˜…åÌC$#2FŽÑ§±¦ÛZ/÷š&m£ñzÒÖ ’.Î]!Î;ƒ(Õ–¢d/—#Kª+tZyuÏB>NÛÖ†(¸ŒSà'³„Y˜´-_•¦¼´˜OlNK§¶ÒàŠˆTHµƒeTPå·fïM’…þuÏÍüp6دªE£åü‡ZØ'CKF#â«;‹eyO Qp„†l"ö1èíÙP ÏŒúl! BÝ2ñª•_VÁÉ÷3eu`–F¸ìI--ö<¿žë¯4õ캿¢)34Å{wMÉ2ÆÖFŸ¥`e9Ú¶¸P‡.”FÔï rY ‚²ÈTB,{ÛœéJ}«àQ4¹0Rû4D‚B§S‘ dO•v¾„™Sן¯3FeŸ™«+ÓâwH dÕÛÌì·P4ë&¥#rÜÉ Ù¦ê†ý·xòqk¯2,¹§™E\ék‚×Sá”ÚºÙ⺷ö£6…à ʾ qSá³Å|;àû}4Ÿ($â¹VY~óÍ!èÜÒŒËX½Ù1j‚VíÍŸš³+œ]«½g{_{/vµ½\¢¶vÉWKÿ:ñám½ ¥ S²x‘t ŽšÝÙÿÀÇ^ný PK IW™k‚½÷ á _rels/.relsUT dìd dìd dìd’ÏNÃ0‡ï{ŠÈ÷ÕÝ@¡¥» ¤Ý*`%îÑ&QâÁöö‚J£ì°cœŸ¿|¶²ÙÆA½rL½wVE Šñ¶w†çúay * 9Kƒw¬áÈ ¶ÕbóÄIîI]’Ê—4t"á1™ŽGJ…ìòMããH’±Å@æ…ZÆuYÞ`üÍ€jÂT;«!îì T}|Û7MoøÞ›ýÈNN<|v–í2ÄÜ¥ÏèšbË¢Ázó˜Ë )„"£OÏ7ú{ZYÈ’yÞç#1'tuÉM?6o>Z´_å9›ëKÚ˜}?þ³žÏÌ·N>fµx PK IWª½e ¢ U € word/document.xmlUT dìdPK IWþË3” z €J¢ word/settings.xmlUT dìdPK IWC‡{š' ƒ €¤ docProps/custom.xmlUT dìdPK IW츱=Œ €‡¥ [Content_Types].xmlUT dìdPK IWV%ë±" €U§ docProps/app.xmlUT dìdPK IW€RŒ 3 €¶¨ docProps/core.xmlUT dìdPK IWkòDn ô €ª word/_rels/document.xml.relsUT dìdPK IW;$î €Î« word/fontTable.xmlUT dìdPK IW+åäz] ÷. €ý¬ word/numbering.xmlUT dìdPK IW¤2×r- ¿ €›° word/styles.xmlUT dìdPK IWMFÒ ø €´ word/header1.xmlUT dìdPK IWF— T e €· word/media/image1.jpegUT dìdPK IW!Yéáå €°Ë word/media/image2.pngUT dìdPK IW°Àºë ú €ÙÌ word/media/image3.pngUT dìdPK IW$“†ª L €Î word/footer1.xmlUT dìdPK IWzaGôM €ñÑ word/footer2.xmlUT dìdPK IW–µâº P €}Õ word/theme/theme1.xmlUT dìdPK IW™k‚½÷ á €{Û _rels/.relsUT PK ! bîh^ [Content_Types].xml ¢( ¬”ËNÃ0E÷HüCä-Jܲ@5í‚Ç*Q>Àēƪc[žiiÿž‰ûB¡j7±ÏÜ{2ñÍh²nm¶‚ˆÆ»R‹ÈÀU^7/ÅÇì%¿’rZYï @1__f› ˜q·ÃR4DáAJ¬h>€ãÚÇV߯¹ªZ¨9ÈÛÁàNVÞ8Ê©ÓãÑÔji){^óã-I‹"{Üv^¥P!XS)bR¹rú—K¾s(¸3Õ`cÞ0†½ÝÎß»¾7M4²©ŠôªZÆk+¿|\|z¿(Ž‹ôPúº6h_-[ž@!‚ÒØ Pk‹´2nÏ}Ä?£LËð Ýû%áÄßdºždN"m,à¥ÇžDO97*‚~§Èɸ8ÀOíc|n¦Ñ äEøÿöéºóÀBÉÀ!$}‡íàÈé;{ìÐå[ƒîñ–é2þ ÿÿ PK ! µU0#ô L _rels/.rels ¢( ¬’MOÃ0†ïHü‡È÷ÕÝBKwAH»!T~€Iܵ£$Ý¿'TƒG½~üÊÛÝ<êÈ!öâ4¬‹;#¶w†—úqu *&r–Fq¬áÄvÕõÕö™GJy(v½*«¸¨¡KÉß#FÓñD±Ï.W ¥†=™ZÆMYÞbø®ÕBS톰·7 ê“Ï›×–¦é ?ˆ9LìÒ™ÈsbgÙ®|Èl!õùUSh9i°bžr:"y_dlÀóD›¿ý|-NœÈR"4ø2ÏGÇ% õZ´4ñËyÄ7 ëÈðÉ‚‹¨Þ ÿÿ PK ! Q48wÛ — xl/workbook.xml¤UÙnâ0}iþ!cñ‡ *–¢AšVU×$dC¬&vÆv UÕŸë@XÊK§/¹p|Žï¹N÷b“¥Ö •Š ÞC¸î"‹òHÄŒ¯zèá~b·‘¥4á1I§=ôJºèÿüÑ] ù¼âÙ ®z(Ñ:GE ͈ª‹œrˆ,…̈†©\9*—”Ä*¡Tg©ã¹nàd„q´Eåg0ÄrÉ":Q‘Q®· ’¦D}•°\UhYô¸ŒÈç"·#‘å ±`)Ó¯%(²²(œ®¸d‘‚ì nZ w v¡ñª• t¶TÆ")”Xê:@;[Ògú±ë`|²›ó=ø’ïHúÂL÷¬dðEVÁ+8€a÷Ûh¬Uz%„Íû"ZsÏÍCýî’¥ôqk]‹äù5ÉL¦Rd¥Dé˘i÷P ¦bM/|dÉ",…¨çãFNoçiûéë>aêiçsó#ðÄ ÕTr¢éHp ÜIú®ÝJìQ"ÀÜÖ-ý[0I¡¦ÀZ Z…d¡nˆN¬B¦=4 g %PDF-1.4 %âãÏÓ 3 0 obj << /Linearized 1 /L 422775 ÿØÿà JFIF ÿÛ C ÿÛ C ÿÀ X" ÿÄ ÿÄ H !1A"Qaq2‘¡#±ÁBRÑ3Cbrá$S‚¢²ð4ñ%6DTc’ÂsÿÄ ÿÄ = !1AQ"aq‘Á2R¡±BÑð#3br’²4á$‚¢ÂñÿÚ ? áHBßÝ`„! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! !@B„ „! ! stream
#!/usr/local/cpanel/3rdparty/bin/perl
# Copyright 2025 WebPros International, LLC
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited.
package ea_php_cli;
use strict;
use warnings;
my $PATH_MAX = 4096;
my $SYSCALL_GETCWD = 79; # 64bit only
our $EUID;
$EUID = $> if ${^GLOBAL_PHASE} eq "START";
my %types = (
php => 'CLI',
pear => 'CLI',
'php-cgi' => 'CGI',
lsphp => 'LSAPI',
);
sub run {
my ( $type, $pkg, $dir, @args ) = proc_args(@_);
$pkg ||= get_pkg_for_dir( $type, $dir );
die "Could not determine package for “$dir”\n" if !$pkg;
return exec_via_pkg( $type, $pkg, @args );
}
sub proc_args {
my ( $type, @raw_args ) = @_;
$type //= "undefined";
die "Invalid type ($type)\n" if !exists $types{$type};
my ( $dir, @args );
my $idx = -1;
my $skipidx = -1;
my $arg; # buffer
for $arg (@raw_args) {
$idx++;
next if $idx == $skipidx;
if ( substr( $arg, 0, 19 ) eq "--ea-reference-dir=" ) { # --ea-reference-dir=DIR
( undef, $dir ) = split( /=/, $arg, 2 ); # if set from -f $arg: blow it away
}
elsif ( _file_exists($arg) ) { # ZC-11178: use a wrapper in place of -f for testing purposes
push @args, $arg;
if ( !defined $dir ) { # set if not already set from --ea-reference-dir
$dir = $arg;
if ( index( $dir, "/" ) == -1 ) {
$dir = "."; # foo.php
}
else {
$dir =~ s{/[^/]+$}{};
$dir = "/" if $dir eq ""; # /foo.php
}
}
}
else {
push @args, $arg;
}
}
$dir //= ".";
# since the lookup is based on abs path:
if ( $dir eq "." ) {
$dir = _getcwd();
}
elsif ( substr( $dir, 0, 1 ) ne "/" || index( $dir, ".." ) != -1 ) {
require Cwd;
$dir = Cwd::abs_path($dir);
}
die "Could not determine path to lookup PHP setting for based on arguments\n" if !$dir; # this would be pretty odd
return ( $type, undef, $dir, @args ); # This function no longer returns package data as of EA-7961.
}
sub _file_exists { return -f $_[0] }
sub get_pkg_for_dir {
my ( $type, $dir ) = @_;
$type //= "undefined";
die "Invalid type ($type)\n" if !exists $types{$type};
my $dir_stat = [ stat($dir) ];
die "“$dir” is not a directory\n" if !-d _;
return _get_default_pkg() if $dir_stat->[4] == 0;
my $pkg;
if ( !$ENV{NO_EA_PHP_CLI_CACHE} ) {
$pkg = _get_from_cache( $dir, $dir_stat );
return $pkg if $pkg;
}
else {
_unlink_cache_file( $dir, $dir_stat );
}
$pkg = _get_pkg_for_path($dir);
_cache_it_if_you_can( $dir, $dir_stat, $pkg ) unless $ENV{NO_EA_PHP_CLI_CACHE};
return $pkg || _get_default_pkg(); # get_php_config_for_users() factors in default, so $pkg should always be set but just in case …
}
sub exec_via_pkg {
my ( $type, $pkg, @args ) = @_;
$type //= "undefined";
die "Invalid type ($type)\n" if !exists $types{$type};
_pkg_name_check($pkg);
my $prefix = _get_scl_prefix($pkg);
my $binary = "$prefix/root/usr/bin/$type";
if ( !-x $binary ) {
warn "There is no “$types{$type}” binary for “$pkg”, using default …\n";
$pkg = _get_default_pkg();
$prefix = _get_scl_prefix($pkg);
$binary = "$prefix/root/usr/bin/$type";
die "Could not determine binary for “$pkg”\n" if !-x $binary;
}
exec( $binary, @args );
die "Could not execute “$binary”: $!\n";
return;
}
#################################
#### get_pkg_for_dir() helpers ##
#################################
sub _get_pkg_for_path {
my ($dir) = @_;
my $pkg;
if ( $ENV{'PWD'} && $dir eq $ENV{'PWD'} ) {
if ( substr( $dir, 0, 1 ) eq '/' ) {
$pkg = _lookup_pkg_for_path($dir); # false if the directory they they think they are in is not configured so we can fall back to abspath
}
else {
# this should not be possible naturally i.e. what does PWD of 'i/am/here' mean, what is it relative to? why would it be set to ../bar/../../foo/bar?
warn "Relative \$PWD detected! Since that can be ambiguous we are ignoring \$PWD value and using absolute path for lookup instead\n";
# Patches welcome for this rabbit hole ;p file under YAGNI for now
}
# no package yet? check the directory they are actually in
# and call abs_path to resolve any symlinks
if ( !$pkg ) {
require Cwd;
$pkg = _lookup_pkg_for_path( Cwd::abs_path( _getcwd() ) );
}
}
else {
$pkg = _lookup_pkg_for_path($dir); # false if the directory they they think they want is not configured so we can fall back to abspath
if ( !$pkg ) {
require Cwd;
my $abs = Cwd::abs_path($dir);
if ( defined $abs && $abs ne $dir ) {
$pkg = _lookup_pkg_for_path($abs);
}
}
}
$pkg = _get_default_pkg() if !$pkg;
return $pkg;
}
sub _get_from_cache {
my ( $dir, $dir_stat ) = @_;
my ( $user, $home ) = ( getpwuid( $dir_stat->[4] ) )[ 0, 7 ];
my $cachedir = _dir_to_cache_dir( $home, $dir );
my $pkg;
if ( my $dir_cache_mtime = ( lstat("$cachedir/.ea-php-cli.cache") )[9] ) {
my $userdata_cache_mtime = ( stat("/var/cpanel/userdata/$user/cache") )[9];
if ( !$Cpanel::PHPFPM::Constants::php_conf_path ) { require Cpanel::PHPFPM::Constants }
my $phpconf_mtime = ( stat($Cpanel::PHPFPM::Constants::php_conf_path) )[9];
if ( $userdata_cache_mtime && $userdata_cache_mtime < $dir_cache_mtime && $phpconf_mtime < $dir_cache_mtime ) {
$pkg = readlink("$cachedir/.ea-php-cli.cache");
eval { _pkg_name_check($pkg); _get_scl_prefix($pkg) };
warn "$@\n" if $@;
return $pkg if $pkg;
}
}
return;
}
sub _unlink_cache_file {
my ( $dir, $dir_stat ) = @_;
my $home = ( getpwuid( $dir_stat->[4] ) )[7];
my $cachedir = _dir_to_cache_dir( $home, $dir );
unlink("$cachedir/.ea-php-cli.cache");
return;
}
sub _lookup_pkg_for_path {
my ($dir) = @_;
my $pkg;
my %dir_cache;
my %uid_cache;
my ( $dom, $uid ); # buffers
my %getpwuid_cache;
require Cpanel::PHP::Config;
$dir =~ s{/+$}{}; # remove trailing /’s to since lookup is based on not having a trailing slash
$dir =~ tr{/}{}s; # squash repetitive sequences of slashes, i.e. //// -> /
while ($dir) { # walk the path looking for the first configured PHP (if any)
$uid = _get_uid($dir); # EUID may not own $dir
last if !$uid; # root can't own a domain, thus can't set a PHP version
if ( !exists $dir_cache{$dir} ) {
$getpwuid_cache{$uid} //= [ getpwuid($uid) ];
eval { $uid_cache{$uid} //= Cpanel::PHP::Config::get_php_config_for_users( [ $getpwuid_cache{$uid}->[0] ] ) };
last if $@; # non-cpanel users can't own a domain, thus can't set a PHP version
for $dom ( keys %{ $uid_cache{$uid} } ) {
$dir_cache{ $uid_cache{$uid}->{$dom}{documentroot} } = $uid_cache{$uid}->{$dom}{phpversion};
}
}
if ( $dir_cache{$dir} ) {
$pkg = $dir_cache{$dir};
last;
}
elsif ( defined $getpwuid_cache{$uid} && defined $getpwuid_cache{$uid}->[7] && $dir eq $getpwuid_cache{$uid}->[7] ) { # because we can cache this one still
last;
}
$dir =~ s{/[^/]+$}{};
}
return $pkg;
}
sub _cache_it_if_you_can {
my ( $dir, $dir_stat, $pkg ) = @_;
# attempt to cache for non-root
if ( $EUID && $EUID == $dir_stat->[4] && $pkg ) { # get_php_config_for_users() factors in default, so $pkg should always be set but just in case …
local $!;
unlink "$dir/.ea-php-cli.cache"; # clean up old cache location if necessary
my ( $user, $home ) = ( getpwuid( $dir_stat->[4] ) )[ 0, 7 ];
my $cachedir = _dir_to_cache_dir( $home, $dir );
require File::Path::Tiny;
File::Path::Tiny::mk($cachedir) or warn "Could not ensure ea-php-cli cache directory!\n";
unlink "$cachedir/.ea-php-cli.cache";
symlink( $pkg, "$cachedir/.ea-php-cli.cache" );
}
}
sub _dir_to_cache_dir {
my ( $home, $dir ) = @_;
$dir =~ s{^\Q$home\E}{$home/.cpanel/ea-php-cli};
return $dir;
}
###############
#### helpers ##
###############
sub _pkg_name_check {
my ($pkg) = @_;
if ( index( $pkg, "/" ) != -1 || index( $pkg, "\0" ) != -1 || index( $pkg, ".." ) != -1 ) {
$pkg =~ s/\0/{NULL-BYTE}/g;
die "Package, $pkg, has invalid characters\n";
}
return 1;
}
sub _get_default_pkg {
require Cpanel::PHP::Config;
my $default_pkg = Cpanel::PHP::Config::get_php_version_info()->{default}; # this calls a cached lookup woot woot
die "Default PHP is not configured!\n" if !$default_pkg;
return $default_pkg;
}
my %_get_scl_prefix;
sub _get_scl_prefix {
my ($pkg) = @_;
return $_get_scl_prefix{$pkg} if $_get_scl_prefix{$pkg};
require Cpanel::SysPkgs::SCL;
my $prefix = Cpanel::SysPkgs::SCL::get_scl_prefix($pkg);
die "“$pkg” is not an EA4 SCL PHP\n" if !$prefix || !-d $prefix;
$_get_scl_prefix{$pkg} = $prefix;
return $_get_scl_prefix{$pkg};
}
sub _get_uid {
my ($dir) = @_;
return $EUID || ( stat($dir) )[4];
}
sub _getcwd {
length( pack( 'l!', 1000 ) ) * 8 == 64 or die "This system only support 64-bit Linux";
my $cwd = "\0" x 4096;
my $syscall_result = syscall( $SYSCALL_GETCWD, $cwd, $PATH_MAX );
if ( $syscall_result && $syscall_result != -1 ) {
$cwd =~ tr{\0}{}d;
}
else {
require Cwd;
$cwd = Cwd::getcwd();
}
if ( $ENV{'PWD'} && $cwd ne $ENV{'PWD'} && index( $ENV{'PWD'}, '../' ) == -1 ) {
require Cwd;
my $abs_path = Cwd::abs_path( $ENV{'PWD'} );
if ( $abs_path eq $cwd && ( stat($abs_path) )[4] == ( stat($cwd) )[4] ) {
# If the absolute path of $ENV{'PWD'} points the the directory we are
# currently in and it has the same owner than we except $ENV{'PWD'}
# as truthy
return $ENV{'PWD'};
}
}
return $cwd;
}
1;
__END__
=head1 This is not intended to be consumed outside of the package that installed it. The functions are meant to consolidate logic and facilitate testing. For usage see L<https://go.cpanel.net/ea-php-cli>.