1<?php
2error_reporting(E_ALL);
3define('PHPT_ACL_READ',  1 << 1);
4define('PHPT_ACL_WRITE', 1 << 2);
5define('PHPT_ACL_EXEC',  1 << 3);
6define('PHPT_ACL_NONE',  1 << 4);
7define('PHPT_ACL_FULL',  1 << 5);
8
9define('PHPT_ACL_GRANT',  1);
10define('PHPT_ACL_DENY',  2);
11
12function skipif() {
13    if(substr(PHP_OS, 0, 3) != 'WIN' ) {
14        die('skip windows only test');
15    }
16    if(stripos(php_uname(), 'XP') !== FALSE) {
17        die('skip windows 2003 or newer only test');
18    }
19    if (getenv('GITHUB_ACTIONS')) {
20        // bug44859_4.phpt test fails on the 1st run
21        // other ACL tests cannot be run twice
22        die('skip failing on Github Actions');
23    }
24}
25
26function get_username(){
27    $user = getenv('USERNAME');
28
29    if (!$user) {
30        $user = get_current_user();
31    }
32
33    if (!$user) {
34        $user =  exec('echo %USERNAME%');
35    }
36
37    return $user;
38}
39
40function get_domainname()
41{
42    $domain = getenv('USERDOMAIN');
43
44    return $domain;
45}
46
47function get_icacls()
48{
49    $sysroot = exec('echo %SYSTEMROOT%');
50
51    return "$sysroot\\System32\\icacls.exe";
52}
53
54function fix_acls() {
55    $user = get_username();
56    /* Current user needs to be owner of the test files. As well
57       all the other users having acls on the files must loose them.
58       The following fixes this just partially, as dynamically reading
59       all the users having acls on a file could be sophisticated. */
60    exec(get_icacls() . ' ' . __DIR__ . ' /setowner ' . escapeshellarg($user) . ' /T /L /Q /C > nul 2>&1');
61    exec(get_icacls() . ' ' . __DIR__ . ' /remove:g Administrators /T /L /Q /C > nul 2>&1');
62}
63
64function icacls_set($path, $mode, $perm) {
65    $icacls = get_icacls();
66    $user = get_username();
67    $path_escaped =  '"' . $path . '"';
68    $perm_entry = array();
69
70    if ($perm & PHPT_ACL_READ) $perm_entry[]  = 'R';
71    if ($perm & PHPT_ACL_WRITE) $perm_entry[] = 'W';
72    if ($perm & PHPT_ACL_EXEC) $perm_entry[]  = 'RX';
73    if ($perm & PHPT_ACL_FULL) $perm_entry[]  = 'F';
74
75    // Deny all
76    $cmd = $icacls . ' ' . $path_escaped . ' /inheritance:r /deny ' . $user . ':(F,M,R,RX,W)';
77    exec($cmd);
78
79    if ($perm & PHPT_ACL_NONE) {
80        /*
81         This is required to remove all the previously denied
82         permission for the USER. Just granting permission doesn't
83         remove the previously denied permission.
84        */
85        $cmd = $icacls . ' ' . $path_escaped . ' /remove:d ' . $user;
86        exec($cmd);
87        $cmd = $icacls . ' ' . $path_escaped . ' /remove:g ' . $user;
88        exec($cmd);
89        return;
90    }
91
92    if ($mode == PHPT_ACL_GRANT) {
93        $mode = 'grant';
94    } else {
95        $mode = 'deny';
96    }
97
98
99    // Deny all
100    $cmd = $icacls . ' ' . $path_escaped . ' /deny ' . $user . ':(F,M,R,RX,W)';
101    exec($cmd);
102
103    /*
104     This is required to remove all the previously denied
105     permission for the USER. Just granting permission doesn't
106     remove the previously denied permission.
107    */
108    $cmd = $icacls . ' ' . $path_escaped . ' /remove:d ' . $user;
109    exec($cmd);
110    $cmd = $icacls . ' ' . $path_escaped . ' /remove:g ' . $user;
111    exec($cmd);
112
113
114    /*
115     Required to set no permission and check that is_readable()
116     returns false. If the $perm_entry contains 'N' skip this step.
117     This will make the file/dir with NO aceess.
118    */
119    if (!in_array('N', $perm_entry)) {
120        /*
121         This is required to remove all the previously denied
122         permission for the USER. Just granting permission doesn't
123         remove the previously denied permission.
124        */
125        $cmd = $icacls . ' ' . $path_escaped . ' /remove:d ' . $user;
126        exec($cmd);
127        $cmd = $icacls . ' ' . $path_escaped . ' /remove:g ' . $user;
128        exec($cmd);
129
130        $cmd = $icacls . ' ' . $path_escaped . ' /' . $mode . ' ' . $user;
131        $cmd .= ':' . '(' . implode(',', $perm_entry) . ')';
132        exec($cmd);
133    }
134}
135
136function create_dir($name, $perms) {
137    if (empty($name)) {
138        echo "create_dir: Empty name is not allowed\n";
139        return;
140    }
141
142    mkdir($name);
143    $dst = realpath($name);
144    icacls_set($name, PHPT_ACL_GRANT, $perms);
145}
146
147function create_file($name, $perms) {
148    if (empty($name)) {
149        echo "create_file: Empty name is not allowed\n";
150        return;
151    }
152
153    touch($name);
154    icacls_set($name, PHPT_ACL_GRANT, $perms);
155}
156
157function delete_file($path) {
158    icacls_set($path, PHPT_ACL_GRANT, PHPT_ACL_FULL);
159    if (is_file($path)) {
160        unlink($path);
161    } else {
162        echo "delete_file: '$path' is not a file\n";
163        return;
164    }
165}
166
167function delete_dir($path) {
168    if (is_dir($path)) {
169        icacls_set($path, PHPT_ACL_GRANT, PHPT_ACL_FULL);
170        rmdir($path);
171    } else {
172        echo "delete_dir: '$path' is not a directory\n";
173        return;
174    }
175}
176