-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
CheckHtaccess.class.php
347 lines (323 loc) · 12.7 KB
/
CheckHtaccess.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
<?php
/**
* CheckHtaccess: A PHP based tool that keeps an eye on your .htaccess file!
*
* @see https://github.com/Shazmataz/CheckHtaccess/ The CheckHtaccess GitHub project
*
* @author Shaz Hossain (Shazmataz)
* @copyright 2022 Shaz Hossain (Shazmataz)
* @license Licensed under MIT (https://github.com/Shazmataz/CheckHtaccess/LICENSE)
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace CheckHtaccess;
/**
* Create a new instance of CheckHtaccess.
* CheckHtaccess: A PHP based tool that keeps an eye on your .htaccess file!
*
* @author Shaz Hossain (Shazmataz)
*/
class CheckHtaccess {
protected $bacukupHtFile;
protected $currentHtFile;
protected $createCurrentHFileIfNotFound;
protected $echoProcess;
protected $localLog;
protected $localLogFile;
protected $logErrorsToServerLogs;
/**
* Helper function to get the real current path
* to use with checkHTWithCron()
* Useful on shared hosting packages.
*
* @param string $htFile The name of the .htaccess backup or .htaccess file. Can be relative.
* @return string Full path of file, if exists, and whether the file is writable.
*/
public function getPathForCron(string $htFile = '.htaccess') {
if(is_string($htFile)) {
$rp = realpath($htFile);
if(is_string($rp)) {
if(is_writable($htFile)) {
return $rp . PHP_EOL . 'File is writable';
} else {
return $rp . PHP_EOL . 'File is not writable';
}
} else if (!realpath($htFile)) {
return $htFile.' not found';
}
} else {
return 'Path expected as string';
}
}
/**
* Helper function to get any errors.
* Useful for testing/debuging.
*
* @return string Errors. If any.
*/
public function getErrors() {
return $this->logs;
}
/**
* Performs the .htaccess check and restoration with all available options.
* If all logging options are false errors will still be logged to the system logs.
*
* @param string $currentHtFile The path and name of the backup .htaccess file. Can be relative.
* @param string $bacukupHtFile The path and name of the target .htaccess file. Can be relative.
* @param bool $createCurrentHFileIfNotFound Whether to create a new .htaccess file if it is not found.
* @param bool $echoProcess Whether to echo out the process. Only recommended for testing/debuging.
* @param bool $localLog Whether to use a local file for logging errors and successful restorations.
* @param string $localLogFile The path and name of the local logging file. Can be relative.
* @param bool $logErrorsToServerLogs Whether to log errors to the system logs.
* @return bool False on error.
*/
public function checkHTFull(string $currentHtFile = '/path/to/.htaccess', string $bacukupHtFile = '/path/to/bak.htaccess', bool $createCurrentHFileIfNotFound = true, bool $echoProcess = false, bool $localLog = true, string $localLogFile = '/path/to/checkhtaccess.log', bool $logErrorsToServerLogs = false) {
$err = 0;
$this->logs = '';
$this->successStat = '';
$err += $this->validateParams(0,$currentHtFile,'checkHTFull');
$err += $this->validateParams(1,$bacukupHtFile,'checkHTFull');
$err += $this->validateParams(2,$createCurrentHFileIfNotFound,'checkHTFull');
$err += $this->validateParams(3,$echoProcess,'checkHTFull');
$err += $this->validateParams(4,$localLog,'checkHTFull');
if($this->localLog) $err += $this->validateParams(5,$localLogFile,'checkHTFull');
$err += $this->validateParams(6,$logErrorsToServerLogs,'checkHTFull');
if($err == 0) {
$this->chkOrigHTfile();
return true;
} else {
return false;
if(!$this->echoProcess && !$this->localLog && !$this->logErrorsToServerLogs) $this->logErrorsToServerLogs = true;
$this->logData();
}
}
/**
* Use this function if you are using CheckHtaccess as a cron job to check and restore .htaccess files.
* Absolute paths are required. Use getPathForCron helper function if to find this.
*
* @param string $currentHtFile The ABSOLUTE path and name of the .htaccess file. Use getPathForCron helper function if
* you’re unable to find this.
* @param string $bacukupHtFile The ABSOLUTE path and name of the backup .htaccess file. Use getPathForCron helper function if
* you’re unable to find this.
* @param bool $createCurrentHFileIfNotFound Whether to create a new .htaccess file if it is not found.
* @param bool $localLog Whether to use a local file for logging errors and successful restorations.
* @param string $localLogFile The ABSOLUTE path and name of the local logging file. Use getPathForCron helper function if
* you’re unable to find this.
* @param bool $logErrorsToServerLogs Whether to log errors to the system logs.
* @return bool False on error.
*/
public function checkHTCron(string $currentHtFile = '/full/path/to/.htaccess', string $bacukupHtFile = '/full/path/to/bak.htaccess', bool $createCurrentHFileIfNotFound = true, bool $localLog = true, string $localLogFile = '/full/path/to/checkhtaccess.log', bool $logErrorsToServerLogs = false) {
$err = 0;
$this->logs = '';
$this->successStat = '';
$this->echoProcess = false;
$err += $this->validateParams(0,$currentHtFile,'checkHTCron');
$err += $this->validateParams(1,$bacukupHtFile,'checkHTCron');
$err += $this->validateParams(2,$createCurrentHFileIfNotFound,'checkHTCron');
$err += $this->validateParams(4,$localLog,'checkHTCron');
if($this->localLog) $err += $this->validateParams(5,$localLogFile,'checkHTCron');
$err += $this->validateParams(6,$logErrorsToServerLogs,'checkHTCron');
if($err == 0) {
$this->chkOrigHTfile();
} else {
return false;
$this->logData();
}
}
/**
* Performs the .htaccess check and restoration upon a 404 error.
* Does not output any echo. Must include this class & method within your 404 page.
*
* @param string $currentHtFile The path and name of the backup .htaccess file. Can be relative.
* @param string $bacukupHtFile The path and name of the target .htaccess file. Can be relative.
* @param bool $createCurrentHFileIfNotFound Whether to create a new .htaccess file if it is not found.
* @param bool $localLog Whether to use a local file for logging errors and successful restorations.
* @param string $localLogFile The path and name of the local logging file. Can be relative.
* @param bool $logErrorsToServerLogs Whether to log errors to the system logs.
* @return bool False on error.
*/
public function checkHTon404(string $currentHtFile = '/path/to/.htaccess', string $bacukupHtFile = '/path/to/bak.htaccess', bool $createCurrentHFileIfNotFound = true, bool $localLog = true, string $localLogFile = '/full/path/to/checkhtaccess.log', bool $logErrorsToServerLogs = false) {
$err = 0;
$this->logs = '';
$this->successStat = '';
$this->echoProcess = false;
$err += $this->validateParams(0,$currentHtFile,'checkHTon404');
$err += $this->validateParams(1,$bacukupHtFile,'checkHTon404');
$err += $this->validateParams(2,$createCurrentHFileIfNotFound,'checkHTon404');
$err += $this->validateParams(4,$localLog,'checkHTon404');
if($this->localLog) $err += $this->validateParams(5,$localLogFile,'checkHTon404');
$err += $this->validateParams(6,$logErrorsToServerLogs,'checkHTon404');
if($err == 0) {
$this->chkOrigHTfile();
} else {
return false;
$this->logData();
}
}
/**
* Validates provided options
*/
private function validateParams(int $param,$val,string $func) {
switch ($param) {
case 0:
if($val != '/path/to/.htaccess' && $val != '/full/path/to/.htaccess' && $val != '') {
$this->currentHtFile = $val;
} else {
$this->logs .='Invalid currentHtFile passed to ' . $func . '; ';
return 1;
};
break;
case 1:
if($val != '/path/to/bak.htaccess' && $val != '/full/path/to/bak.htaccess' && $val != '') {
$this->originalHtFile = $val;
} else {
$this->logs .='Invalid originalHtFile passed to ' . $func . '; ';
return 1;
};
break;
case 2:
if(is_bool($val)) {
$this->createCurrentHFileIfNotFound = $val;
} else {
$this->logs .='Invalid createCurrentHFileIfNotFound passed to ' . $func . '; ';
return 1;
};
break;
case 3:
if(is_bool($val)) {
$this->echoProcess = $val;
} else {
$this->logs .='Invalid echoProcess passed to ' . $func . '; ';
return 1;
};
break;
case 4:
if(is_bool($val)) {
$this->localLog = $val;
} else {
$this->logs .='Invalid localLog passed to ' . $func . '; ';
return 1;
};
break;
case 5:
if($val != '/path/to/checkhtaccess.log' && $val != '/full/path/to/checkhtaccess.log' && $val != '' && strlen($val) > 1) {
$this->localLogFile = $val;
} else {
$this->logs .='Invalid localLogFile passed to ' . $func . '; ';
$this->localLog = false;
return 1;
};
break;
case 6:
if(is_bool($val)) {
$this->logErrorsToServerLogs = $val;
} else {
$this->logs .='Invalid logErrorsToServerLogs passed to ' . $func . '; ';
return 1;
};
break;
}
}
/**
* Checks whether the given backup .htaccess file exists.
*/
private function chkOrigHTfile() {
if(!is_file($this->originalHtFile)) {
$this->logs .='Could not find original file: '.$this->originalHtFile.'; ';
$this->logData();
} else {
$this->chkCurrHTfile();
};
}
/**
* Checks whether the given .htaccess file exists.
*/
private function chkCurrHTfile() {
if(!is_file($this->currentHtFile)) {
$this->logs .='Could not find current file: '.$this->currentHtFile.'; ';
if($this->createCurrentHFileIfNotFound) {
$this->createHTFile();
} else {
$this->logData();
}
} else {
$this->chkHTfiles();
};
}
/**
* Compares the current .htaccess file with the backup
*/
private function chkHTfiles() {
if(filesize($this->currentHtFile) !== filesize($this->originalHtFile)) {
$cHTf = fopen($this->currentHtFile, 'rb');
$oHTf = fopen($this->originalHtFile, 'rb');
$result = true;
while(!feof($cHTf)) {
if(fread($cHTf, 8192) != fread($oHTf, 8192)) {
$result = false;
break;
};
};
fclose($cHTf);
fclose($oHTf);
// Calls restoreHTfiles function if changes are found
if(!$result) {
$this->restoreHTfiles($this->currentHtFile,$this->originalHtFile);
} else {
clearstatcache();
}
}
}
/**
* Restores the backup .htaccess file to the current .htaccess file.
*/
private function restoreHTfiles($cHT,$oHT) {
if(!copy($oHT, $cHT)) {
$this->logs .='failed to copy '.$oHT.'; ';
} else {
$this->successStat .='Restored File; ';
}
$this->logData();
}
/**
* Tries to create a .htaccess file when one is not found.
*/
private function createHTFile() {
$htf = fopen($this->currentHtFile, 'a');
if(!fwrite($htf, ' ')) {
$this->logs .= $this->currentHtFile.'file write error!; ';
}
fclose($htf);
$this->successStat .='Created new current file: '.$this->currentHtFile.'; ';
$this->restoreHTfiles($this->currentHtFile,$this->originalHtFile);
}
/**
* Echoes / Logs any errors or output depending on setup.
* If cannot write to local file then will log errors to server logs
* regardless of whether the logErrorsToServerLogs is set to false.
*/
private function logData() {
if($this->localLog) {
$lf = fopen($this->localLogFile, 'a');
if(!fwrite($lf, date('Y-m-d H:i:s').': '.$this->logs . $this->successStat.PHP_EOL)) {
$this->logs.= 'Logfile write error!';
$this->logErrorsToServerLogs = true;
}
fclose($lf);
}
if($this->echoProcess) {
echo date('Y-m-d H:i:s').': '.$this->logs . $this->successStat.PHP_EOL;
}
if($this->logErrorsToServerLogs) {
error_log('CheckHtaccess.class: '.$this->logs . $this->successStat,0);
}
clearstatcache();
}
};
?>