dolibarr 24.0.1
filecheck.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005-2026 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
4 * Copyright (C) 2007-2012 Regis Houssin <regis.houssin@inodbox.com>
5 * Copyright (C) 2015-2026 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2017 Nicolas ZABOURI <info@inovea-conseil.com>
7 * Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
28// Load Dolibarr environment
29require '../../main.inc.php';
39require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
40require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
41require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
42require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
43
44$langs->load("admin");
45
46$mode = GETPOST('mode', 'aZ09');
47
48if (!$user->admin && !$user->hasRight('bockedlog', 'read')) {
50}
51
52$error = 0;
53
54// Version blockedlog
55$versionbadge = '<span class="badge-text badge-secondary">'.getBlockedLogVersionToShow();
56if ($mysoc->country_code == 'FR' && !constant('CERTIF_LNE')) {
57 // Can add an edditional mention
58 $langs->load("blockedlog");
59 $versionbadge .= ' - '.$langs->trans("NeedAThirdPartyStatement");
60}
61$versionbadge .= '</span>';
62
63
64/*
65 * View
66 */
67
68@set_time_limit(300);
69
70llxHeader('', '', '', '', 0, 0, '', '', '', 'mod-admin page-system_filecheck');
71
72print load_fiche_titre($langs->trans("FileCheckDolibarr"), '', 'title_setup');
73
74print '<div class="opacitymedium hideonsmartphone justify">'.$langs->trans("FileCheckDesc").'</div>';
75if (isModEnabled('blockedlog')) {
76 print '<span class="opacitymedium">';
77 print $langs->trans("DataIntegrityDesc").': ';
78 print '</span>';
79 print '<a href="'.DOL_URL_ROOT.'/blockedlog/admin/blockedlog_list.php">'.img_picto('', 'url', 'class="pictofixedwidth"').$langs->trans("BlockedLog").'</a><br>';
80}
81print'<br>';
82
83
84// Version
85print '<div class="div-table-responsive-no-min">';
86print '<table class="noborder centpercent">';
87print '<tr class="liste_titre"><td>'.$langs->trans("Version").'</td><td></td></tr>'."\n";
88$htmltooltip = '';
89$htmltooltip .= $langs->trans("VersionLastInstall").': '.getDolGlobalString('MAIN_VERSION_LAST_INSTALL').'<br>'."\n";
90$htmltooltip .= $langs->trans("VersionLastUpgrade").': '.getDolGlobalString('MAIN_VERSION_LAST_UPGRADE').'<br>'."\n";
91
92print '<tr class="oddeven nohover">';
93print '<td width="300">'.$langs->trans("VersionProgram").'</td><td>';
94print '<span class="badge-text badge-secondary valignmiddle">'.DOL_VERSION.'</span>';
95// If current version differs from last upgrade
96if (!getDolGlobalString('MAIN_VERSION_LAST_UPGRADE')) {
97 // Compare version with last install database version (upgrades never occurred)
98 if (in_array(versioncompare(versiondolibarrarray(), preg_split('/[\-\.]/', getDolGlobalString('MAIN_VERSION_LAST_INSTALL'))), array(-2, -1, 1, 2))) {
99 print ' '.img_warning($langs->trans("RunningUpdateProcessMayBeRequired", DOL_VERSION, getDolGlobalString('MAIN_VERSION_LAST_INSTALL')));
100 }
101} else {
102 // Compare version with last upgrade database version
103 if (in_array(versioncompare(versiondolibarrarray(), preg_split('/[\-\.]/', getDolGlobalString('MAIN_VERSION_LAST_UPGRADE'))), array(-2, -1, 1, 2))) {
104 print ' '.img_warning($langs->trans("RunningUpdateProcessMayBeRequired", DOL_VERSION, getDolGlobalString('MAIN_VERSION_LAST_UPGRADE')));
105 }
106}
107print ' '.$form->textwithpicto('', $htmltooltip);
108print '</td></tr>'."\n";
109
110$showblockedlogversion = 0;
111if ($mysoc->country_code == 'FR') {
112 $showblockedlogversion = 1;
113}
115 $showblockedlogversion = 1;
116}
117
118if ($showblockedlogversion) {
119 print '<tr class="oddeven nohover">';
120 print '<td width="300">'.$langs->trans("VersionOfModule", $langs->transnoentitiesnoconv("BlockedLog")).'</td><td>';
121 print $versionbadge;
122 print '</td>';
123 print '</tr>';
124}
125
126print '</table>';
127print '</div>';
128
129// Add a complementary optional information
130$infotoshow = '';
131if ($mysoc->country_code == 'FR') {
132 $islne = isALNEQualifiedVersion(1, 1);
133 if ($islne) {
134 if (preg_match('/\-/', getBlockedLogVersionToShow())) {
135 // This is an alpha or beta version
136 $infotoshow = $langs->trans("LNECandidateVersionForCertificationFR", getBlockedLogVersionToShow());
137 } else {
138 $infotoshow = $langs->trans("LNECertifiedVersionFR", getBlockedLogVersionToShow());
139 }
140 } else {
141 $infotoshow = $langs->trans("NotCertifiedVersionFR", getBlockedLogVersionToShow());
142 }
143}
144if ($infotoshow) {
145 print info_admin($infotoshow, 0, 0, 'info');
146}
147
148print '<br><br>';
149
150
151// Modified or missing files
152$file_list = array('missing' => array(), 'updated' => array());
153
154// Local file to compare to
155$xmlshortfile = dol_sanitizeFileName(GETPOST('xmlshortfile', 'alpha') ? GETPOST('xmlshortfile', 'alpha') : 'filelist-'.DOL_VERSION.getDolGlobalString('MAIN_FILECHECK_LOCAL_SUFFIX').'.xml'.getDolGlobalString('MAIN_FILECHECK_LOCAL_EXT'));
156
157$xmlfile = DOL_DOCUMENT_ROOT.'/install/'.$xmlshortfile;
158if (!preg_match('/\.zip$/i', $xmlfile) && dol_is_file($xmlfile.'.zip')) {
159 $xmlfile .= '.zip';
160}
161
162// Remote file to compare to
163$xmlremote = GETPOST('xmlremote', 'alphanohtml');
164if (empty($xmlremote) && getDolGlobalString('MAIN_FILECHECK_URL')) {
165 $xmlremote = getDolGlobalString('MAIN_FILECHECK_URL');
166}
167$param = 'MAIN_FILECHECK_URL_'.DOL_VERSION;
168if (empty($xmlremote) && getDolGlobalString($param)) {
169 $xmlremote = getDolGlobalString($param);
170}
171if (empty($xmlremote)) {
172 $xmlremote = 'https://www.dolibarr.org/files/stable/signatures/filelist-'.DOL_VERSION.'.xml';
173}
174if (!preg_match('/^https?:\/\//', $xmlremote)) {
175 $langs->load("errors");
176 setEventMessages($langs->trans("ErrorURLMustStartWithHttp", $xmlremote), null, 'errors');
177 $error++;
178} elseif ($xmlremote && !preg_match('/\.xml$/', $xmlremote)) {
179 $langs->load("errors");
180 setEventMessages($langs->trans("ErrorURLMustEndWith", $xmlremote, '.xml'), null, 'errors');
181 $error++;
182}
183
184// Test if remote test is ok
185$enableremotecheck = true;
186if (preg_match('/beta|alpha|rc/i', DOL_VERSION) || getDolGlobalString('MAIN_ALLOW_INTEGRITY_CHECK_ON_UNSTABLE')) {
187 $enableremotecheck = false;
188}
189
190print '<form name="check" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
191print '<input type="hidden" name="token" value="'.newToken().'">';
192print img_picto('', 'search', 'class="pictofixedwidth"').$langs->trans("MakeIntegrityAnalysisFrom").'...<br><br>';
193
194print '<div class="divsection">';
195print '<!-- for a local check target=local&xmlshortfile=... -->'."\n";
196if (dol_is_file($xmlfile)) {
197 print '<input type="radio" name="target" id="checkboxlocal" value="local"'.((!GETPOST('target') || GETPOST('target') == 'local') ? 'checked="checked"' : '').'"> <label for="checkboxlocal">'.$langs->trans("LocalSignature").'</label> = ';
198 print '<input name="xmlshortfile" class="flat minwidth400" value="'.dol_escape_htmltag($xmlshortfile).'" spellcheck="false">';
199 print '<br>';
200} else {
201 print '<input type="radio" name="target" id="checkboxlocal" value="local"> <label for="checkboxlocal">'.$langs->trans("LocalSignature").' = ';
202 print '<input name="xmlshortfile" class="flat minwidth400" value="'.dol_escape_htmltag($xmlshortfile).'" spellcheck="false">';
203 print '<br><span class="warning paddingtop inline-block">'.$langs->trans("AvailableOnlyOnPackagedVersions").'</span></label>';
204 print '<br>';
205}
206
207print '<br>';
208
209print '<!-- for a remote target=remote&xmlremote=... -->'."\n";
210if ($enableremotecheck) {
211 print '<input type="radio" name="target" id="checkboxremote" value="remote"'.(GETPOST('target') == 'remote' ? 'checked="checked"' : '').'> <label for="checkboxremote">'.$langs->trans("RemoteSignature").'</label> = ';
212 print '<input name="xmlremote" class="flat minwidth500" value="'.dol_escape_htmltag($xmlremote).'" spellcheck="false"><br>';
213} else {
214 print '<input type="radio" name="target" id="checkboxremote" value="remote" disabled="disabled"> '.$langs->trans("RemoteSignature").' = '.dol_escape_htmltag($xmlremote);
215 if (!GETPOST('xmlremote')) {
216 print ' <span class="warning">('.$langs->trans("FeatureAvailableOnlyOnStable").')</span>';
217 }
218 print '<br>';
219}
220print '</div>';
221
222// Option unalterable log
223print '<div class="center">';
224if ($mysoc->country_code == 'FR') {
225 print '<input type="checkbox" name="mode" id="mode" value="unalterable"'.($mode == 'unalterable' ? ' checked="checked"' : '').'>';
226 print '<label for="mode" class="opacitymedium">'.$langs->trans("AnalyzeUnalterableScopeOnly", $langs->transnoentitiesnoconv("BlockedLog")).'</label><br>';
227}
228print '<input type="submit" name="check" class="button" value="'.$langs->trans("Check").'">';
229print '</div>';
230print '</form>';
231print '<br>';
232print '<br>';
233
234if (GETPOST('target') == 'local') {
235 if (dol_is_file($xmlfile)) {
236 // If file is a zip file (.../filelist-x.y.z.xml.zip), we uncompress it before
237 if (preg_match('/\.zip$/i', $xmlfile)) {
238 dol_mkdir($conf->admin->dir_temp);
239 $xmlfilenew = preg_replace('/\.zip$/i', '', $xmlfile);
240 $result = dol_uncompress($xmlfile, $conf->admin->dir_temp);
241 if (empty($result['error'])) {
242 $xmlfile = $conf->admin->dir_temp.'/'.basename($xmlfilenew);
243 } else {
244 print $langs->trans('FailedToUncompressFile').': '.$xmlfile;
245 $error++;
246 }
247 }
248 $xml = simplexml_load_file($xmlfile);
249 if ($xml === false) {
250 print '<div class="warning">'.$langs->trans('XmlCorrupted').': '.$xmlfile.'</span>';
251 $error++;
252 }
253 } else {
254 print '<div class="warning">'.$langs->trans('XmlNotFound').': '.$xmlfile.'</span>';
255 $error++;
256 }
257}
258if (GETPOST('target') == 'remote') {
259 $xmlarray = getURLContent($xmlremote, 'GET', '', 1, array(), array('http', 'https'), 0); // Accept http or https links on external remote server only. Same is used into api_setup.class.php.
260
261 // Return array('content'=>response,'curl_error_no'=>errno,'curl_error_msg'=>errmsg...)
262 if (!$xmlarray['curl_error_no'] && $xmlarray['http_code'] != 400 && $xmlarray['http_code'] != 404) {
263 $xmlfile = $xmlarray['content'];
264 //print "xmlfilestart".$xmlfile."xmlfileend";
265 if (LIBXML_VERSION < 20900) {
266 // Avoid load of external entities (security problem).
267 // Required only if LIBXML_VERSION < 20900
268 // @phan-suppress-next-line PhanDeprecatedFunctionInternal
269 libxml_disable_entity_loader(true);
270 }
271
272 $xml = simplexml_load_string($xmlfile, 'SimpleXMLElement', LIBXML_NOCDATA | LIBXML_NONET);
273 } else {
274 $errormsg = $langs->trans('XmlNotFound').': '.$xmlremote.' - '.$xmlarray['http_code'].(($xmlarray['http_code'] == 400 && $xmlarray['content']) ? ' '.$xmlarray['content'] : '').' '.$xmlarray['curl_error_no'].' '.$xmlarray['curl_error_msg'];
275 setEventMessages($errormsg, null, 'errors');
276 $error++;
277 }
278}
279
280
281if (empty($error) && !empty($xml)) {
282 $checksumconcat = array();
283 $file_list = array();
284 $out = '';
285
286 $algo = (string) $xml['algo']; // When file is <checksum_list attrib="val" algo="xxx">...</checksum_list>, the first tag is root of the $xml
287 if (empty($algo)) {
288 //$algo = 'md5'; // For v22-
289 $algo = 'sha256'; // For v23+
290 }
291
292 // Forced constants
293 if (is_object($xml->dolibarr_constants[0]) || $mode == 'unalterable') {
294 $out .= load_fiche_titre($langs->trans("ForcedConstants"));
295
296 $out .= '<div class="div-table-responsive-no-min">';
297 $out .= '<table class="noborder">';
298 $out .= '<tr class="liste_titre">';
299 $out .= '<td>#</td>';
300 $out .= '<td>'.$langs->trans("Parameter").'</td>';
301 $out .= '<td class="center">'.$langs->trans("ExpectedValue").'</td>';
302 $out .= '<td class="center">'.$langs->trans("CurrentValue").'</td>';
303 $out .= '</tr>'."\n";
304
305 $i = 0;
306
307 if ($mode == 'unalterable') {
308 $i++;
309
310 $out .= '<tr class="oddeven">';
311 $out .= '<td>'.$i.'</td>'."\n";
312 $out .= '<td>'.$langs->trans("Country").'</td>'."\n";
313 $out .= '<td class="center"><span class="opacitymedium">'.$langs->trans("YourCountryCode").'</span></td>'."\n";
314 $out .= '<td class="center">'.$mysoc->country_code.'</td>'."\n";
315 $out .= "</tr>\n";
316
317 $i++;
318
319 $out .= '<tr class="oddeven">';
320 $out .= '<td>'.$i.'</td>'."\n";
321 $out .= '<td>'.$langs->trans("StatusOfModule", $langs->transnoentitiesnoconv("BlockedLog")).'</td>'."\n";
322 $out .= '<td class="center">'.$langs->trans("Enabled").'</td>'."\n";
323 $out .= '<td class="center">';
324 $out .= isModEnabled('blockedlog') ? '<span class="ok">'.$langs->trans("Enabled").'</span>' : '<span class="warning">'.$langs->trans("Disabled").'</span>';
325
326 include_once DOL_DOCUMENT_ROOT.'/core/modules/modBlockedLog.class.php';
327 $objMod = new modBlockedLog($db);
328 /*$modulename = $objMod->getName();
329 $moduledesc = $objMod->getDesc();
330 $moduleauthor = $objMod->getPublisher();
331 $moduledir = strtolower(preg_replace('/^mod/i', '', get_class($objMod)));*/
332 $const_name = 'MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i', '', get_class($objMod)));
333
334 $htmltooltip = '<span class="opacitymedium">'.$langs->trans("LastActivationDate").':</span> ';
335 if (getDolGlobalString($const_name)) {
336 $htmltooltip .= dol_print_date($objMod->getLastActivationDate(), 'dayhour');
337 } else {
338 $htmltooltip .= $langs->trans("Disabled");
339 }
340 $tmp = $objMod->getLastActivationInfo();
341 $authorid = (empty($tmp['authorid']) ? '' : $tmp['authorid']);
342 if ($authorid > 0) {
343 $tmpuser = new User($db);
344 $tmpuser->fetch($authorid);
345 $htmltooltip .= '<br><span class="opacitymedium">'.$langs->trans("LastActivationAuthor").':</span> ';
346 $htmltooltip .= $tmpuser->getNomUrl(0, 'nolink', -1, 1);
347 }
348 $ip = (empty($tmp['ip']) ? '' : $tmp['ip']);
349 if ($ip) {
350 $htmltooltip .= '<br><span class="opacitymedium">'.$langs->trans("LastActivationIP").':</span> ';
351 $htmltooltip .= $ip;
352 }
353 $lastactivationversion = (empty($tmp['lastactivationversion']) ? '' : $tmp['lastactivationversion']);
354 if ($lastactivationversion && $lastactivationversion != 'dolibarr') {
355 $htmltooltip .= '<br><span class="opacitymedium">'.$langs->trans("LastActivationVersion").':</span> ';
356 $htmltooltip .= $lastactivationversion;
357 }
358
359 $out .= $form->textwithpicto('', $htmltooltip);
360
361 $out .= "</td>\n";
362 $out .= "</tr>\n";
363 }
364
365 foreach ($xml->dolibarr_constants[0]->constant as $constant) { // $constant is a simpleXMLElement
366 $constname = (string) $constant['name'];
367 $constvalue = (string) $constant;
368
369 $constvalue = (empty($constvalue) ? '0' : $constvalue);
370 // Value found
371 $value = '';
372 if ($constname && getDolGlobalString($constname) != '') {
373 $value = getDolGlobalString($constname);
374 }
375 $valueforchecksum = (empty($value) ? '0' : $value);
376
377 $checksumconcat[$constname] = $valueforchecksum;
378
379 $i++;
380
381 $out .= '<tr class="oddeven">';
382 $out .= '<td>'.$i.'</td>'."\n";
383 $out .= '<td>'.dol_escape_htmltag($constname).'</td>'."\n";
384 $out .= '<td class="center">'.dol_escape_htmltag($constvalue).'</td>'."\n";
385 $out .= '<td class="center">'.dol_escape_htmltag($valueforchecksum).'</td>'."\n";
386 $out .= "</tr>\n";
387 }
388
389 if ($i == 0 && $mode != 'unalterable') {
390 $out .= '<tr class="oddeven"><td colspan="4"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
391 }
392 $out .= '</table>';
393 $out .= '</div>';
394
395 $out .= '<br>';
396 }
397
398
399 $onlymodifiedorremoved = 0;
400 if ($mode == 'unalterable') {
401 $listoffilestoanalyze = $xml->dolibarr_unalterable_files[0];
402 $onlymodifiedorremoved = 1;
403 } else {
404 $listoffilestoanalyze = $xml->dolibarr_htdocs_dir[0];
405 $onlymodifiedorremoved = 0;
406 }
407
408
409 // Scan htdocs
410 if (is_object($listoffilestoanalyze)) {
411 // @phan-suppress-next-line PhanTypeArraySuspicious
412 $includecustom = (empty($listoffilestoanalyze['includecustom']) ? 0 : $listoffilestoanalyze['includecustom']);
413
414 // Define qualified files (must be same than into generate_filelist_xml.php and in api_setup.class.php)
415 $regextoinclude = '\.(php|php3|php4|php5|phtml|phps|phar|inc|css|scss|html|xml|js|json|tpl|jpg|jpeg|png|gif|ico|sql|lang|txt|yml|bak|md|mp3|mp4|wav|mkv|z|gz|zip|rar|tar|less|svg|eot|woff|woff2|ttf|manifest)$';
416 $regextoexclude = '('.($includecustom ? '' : 'custom|').'documents|conf|install|dejavu-fonts-ttf-.*|public\/test|sabre\/sabre\/.*\/tests|Shared\/PCLZip|nusoap\/lib\/Mail|php\/example|php\/test|geoip\/sample.*\.php|ckeditor\/samples|ckeditor\/adapters)$'; // Exclude dirs
417 $scanfiles = dol_dir_list(DOL_DOCUMENT_ROOT, 'files', 1, $regextoinclude, $regextoexclude);
418
419 // Fill file_list with files in signature, new files, modified files
420 getFilesUpdated($file_list, $listoffilestoanalyze, '', DOL_DOCUMENT_ROOT, $checksumconcat); // Fill array $file_list
421 '@phan-var-force array{insignature:string[],missing?:array<array{filename:string,expectedhash:string,expectedsize:string,algo:string}>,updated:array<array{filename:string,expectedhash:string,expectedsize:string,hash:string,algo:string}>} $file_list';
422
423 // Complete with list of new files into $file_list['added']
424 if (empty($onlymodifiedorremoved)) {
425 foreach ($scanfiles as $valfile) {
426 $tmprelativefilename = preg_replace('/^'.preg_quote(DOL_DOCUMENT_ROOT, '/').'/', '', $valfile['fullname']);
427 if (!in_array($tmprelativefilename, $file_list['insignature'])) {
428 $hashnewfile = @hash_file($algo, $valfile['fullname']); // Can fails if we don't have permission to open/read file
429 $file_list['added'][] = array('filename' => $tmprelativefilename, 'hash' => $hashnewfile, 'algo' => $algo);
430 }
431 }
432 }
433
434 // Files missing
435 $out .= load_fiche_titre($langs->trans("FilesMissing"));
436
437 $out .= '<div class="div-table-responsive-no-min">';
438 $out .= '<table class="noborder">';
439 $out .= '<tr class="liste_titre">';
440 $out .= '<td>#</td>';
441 $out .= '<td>'.$langs->trans("Filename").'</td>';
442 $out .= '<td class="right">'.$langs->trans("ExpectedSize").'</td>';
443 $out .= '<td class="center">'.$langs->trans("ExpectedChecksum").'</td>';
444 $out .= '</tr>'."\n";
445 $tmpfilelist = dol_sort_array($file_list['missing'], 'filename');
446 if (is_array($tmpfilelist) && count($tmpfilelist)) {
447 $i = 0;
448 foreach ($tmpfilelist as $file) {
449 $i++;
450 $out .= '<tr class="oddeven">';
451 $out .= '<td>'.$i.'</td>'."\n";
452 $out .= '<td>'.dol_escape_htmltag($file['filename']).'</td>'."\n";
453 $out .= '<td class="right">';
454 if (!empty($file['expectedsize'])) {
455 $out .= dol_print_size((int) $file['expectedsize']);
456 }
457 $out .= '</td>'."\n";
458 $out .= '<td class="center">'.dol_escape_htmltag($file['expectedhash']).'</td>'."\n";
459 $out .= "</tr>\n";
460 }
461 } else {
462 $out .= '<tr class="oddeven"><td colspan="4"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
463 }
464 $out .= '</table>';
465 $out .= '</div>';
466
467 $out .= '<br>';
468
469 // Files modified
470 $out .= load_fiche_titre($langs->trans("FilesModified"));
471
472 $totalsize = 0;
473 $out .= '<div class="div-table-responsive-no-min">';
474 $out .= '<table class="noborder">';
475 $out .= '<tr class="liste_titre">';
476 $out .= '<td>#</td>';
477 $out .= '<td>'.$langs->trans("Filename").'</td>';
478 $out .= '<td class="center">'.$langs->trans("ExpectedChecksum").'</td>';
479 $out .= '<td class="center">'.$langs->trans("CurrentChecksum").'</td>';
480 $out .= '<td class="right">'.$langs->trans("ExpectedSize").'</td>';
481 $out .= '<td class="right">'.$langs->trans("CurrentSize").'</td>';
482 $out .= '<td class="right">'.$langs->trans("DateModification").'</td>';
483 $out .= '</tr>'."\n";
484 $tmpfilelist2 = dol_sort_array($file_list['updated'], 'filename');
485 if (is_array($tmpfilelist2) && count($tmpfilelist2)) {
486 $i = 0;
487 foreach ($tmpfilelist2 as $file) {
488 $i++;
489 $out .= '<tr class="oddeven">';
490 $out .= '<td>'.$i.'</td>'."\n";
491 $out .= '<td>'.dol_escape_htmltag($file['filename']);
492 $out .= ' <a href="#" class="showfilediff" data-file="'.dol_escape_htmltag($file['filename']).'" data-algo="'.dol_escape_htmltag($file['algo']).'" data-hash="'.dol_escape_htmltag($file['expectedhash']).'">';
493 $out .= img_picto($langs->trans("ShowDiffWithOriginal"), 'split', 'class="paddingleft"');
494 $out .= '</a>';
495 $out .= '</td>'."\n";
496 $out .= '<td class="center" title="'.dol_escape_htmltag($file['expectedhash']).'">'.dol_escape_htmltag(dol_trunc($file['expectedhash'], 16)).'</td>'."\n";
497 $out .= '<td class="center" title="'.dol_escape_htmltag($file['hash']).'">'.dol_escape_htmltag(dol_trunc($file['hash'], 16)).'</td>'."\n";
498 $out .= '<td class="right">';
499 if ($file['expectedsize']) {
500 $out .= dol_print_size((int) $file['expectedsize']);
501 }
502 $out .= '</td>'."\n";
503 $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']);
504 $totalsize += $size;
505 $out .= '<td class="right">'.dol_print_size($size).'</td>'."\n";
506 $out .= '<td class="right">'.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']), 'dayhour').'</td>'."\n";
507 $out .= "</tr>\n";
508 }
509 $out .= '<tr class="liste_total">';
510 $out .= '<td></td>'."\n";
511 $out .= '<td>'.$langs->trans("Total").'</td>'."\n";
512 $out .= '<td class="center"></td>'."\n";
513 $out .= '<td class="center"></td>'."\n";
514 $out .= '<td class="center"></td>'."\n";
515 $out .= '<td class="right">'.dol_print_size($totalsize).'</td>'."\n";
516 $out .= '<td class="right"></td>'."\n";
517 $out .= "</tr>\n";
518 } else {
519 $out .= '<tr class="oddeven"><td colspan="7"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
520 }
521 $out .= '</table>';
522 $out .= '</div>';
523
524 // Ajax loader for the per-file diff against the original version
525 $out .= '<script>'."\n";
526 $out .= 'jQuery(document).ready(function() {'."\n";
527 $out .= ' jQuery(".showfilediff").on("click", function(e) {'."\n";
528 $out .= ' e.preventDefault();'."\n";
529 $out .= ' var link = jQuery(this);'."\n";
530 $out .= ' var row = link.closest("tr");'."\n";
531 $out .= ' var existing = row.next(".filediffrow");'."\n";
532 $out .= ' if (existing.length) { existing.toggle(); return false; }'."\n";
533 $out .= ' var colspan = row.children("td").length;'."\n";
534 $out .= ' var newrow = jQuery(\'<tr class="filediffrow"><td colspan="\'+colspan+\'"><div class="filediffcontent opacitymedium">'.dol_escape_js($langs->trans("Loading")).'...</div></td></tr>\');'."\n";
535 $out .= ' row.after(newrow);'."\n";
536 $out .= ' jQuery.get("'.dol_escape_js(DOL_URL_ROOT).'/blockedlog/admin/filecheck_diff.php", { file: link.attr("data-file"), algo: link.attr("data-algo"), expectedhash: link.attr("data-hash"), token: "'.newToken().'" }, function(data) {'."\n";
537 $out .= ' newrow.find(".filediffcontent").removeClass("opacitymedium").html(data);'."\n";
538 $out .= ' }).fail(function() {'."\n";
539 $out .= ' newrow.find(".filediffcontent").html("'.dol_escape_js($langs->trans("Error")).'");'."\n";
540 $out .= ' });'."\n";
541 $out .= ' return false;'."\n";
542 $out .= ' });'."\n";
543 $out .= '});'."\n";
544 $out .= '</script>'."\n";
545
546 $out .= '<br>';
547
548 // Files added
549 if (empty($onlymodifiedorremoved)) {
550 $out .= load_fiche_titre($langs->trans("FilesAdded"));
551
552 $totalsize = 0;
553 $out .= '<div class="div-table-responsive-no-min">';
554 $out .= '<table class="noborder">';
555 $out .= '<tr class="liste_titre">';
556 $out .= '<td>#</td>';
557 $out .= '<td>'.$langs->trans("Filename").'</td>';
558 $out .= '<td class="center">'.$langs->trans("ExpectedChecksum").'</td>';
559 $out .= '<td class="center">'.$langs->trans("CurrentChecksum").'</td>';
560 $out .= '<td class="right">'.$langs->trans("Size").'</td>';
561 $out .= '<td class="right">'.$langs->trans("DateModification").'</td>';
562 $out .= '</tr>'."\n";
563 $tmpfilelist3 = dol_sort_array($file_list['added'], 'filename');
564 if (is_array($tmpfilelist3) && count($tmpfilelist3)) {
565 $i = 0;
566 foreach ($tmpfilelist3 as $file) {
567 $i++;
568 $out .= '<tr class="oddeven">';
569 $out .= '<td>'.$i.'</td>'."\n";
570 $out .= '<td>'.dol_escape_htmltag($file['filename']);
571 if (!preg_match('/^win/i', PHP_OS)) {
572 $htmltext = $langs->trans("YouCanDeleteFileOnServerWith", 'rm '.DOL_DOCUMENT_ROOT.$file['filename']); // The slash is included int file['filename']
573 $out .= ' '.$form->textwithpicto('', $htmltext, 1, 'help', '', 0, 2, 'helprm'.$i);
574 }
575 $out .= '</td>'."\n";
576 $out .= '<td class="center" title="'.dol_escape_htmltag((string) $file['expectedhash']).'">'.dol_escape_htmltag(dol_trunc((string) $file['expectedhash'], 16)).'</td>'."\n"; // @phan-suppress-current-line PhanTypeInvalidDimOffset
577 $out .= '<td class="center" title="'.dol_escape_htmltag((string) $file['hash']).'">'.dol_escape_htmltag(dol_trunc($file['hash'], 16)).'</td>'."\n";
578 $size = dol_filesize(DOL_DOCUMENT_ROOT.'/'.$file['filename']);
579 $totalsize += $size;
580 $out .= '<td class="right">'.dol_print_size($size).'</td>'."\n";
581 $out .= '<td class="right nowraponall">'.dol_print_date(dol_filemtime(DOL_DOCUMENT_ROOT.'/'.$file['filename']), 'dayhour').'</td>'."\n";
582 $out .= "</tr>\n";
583 }
584 $out .= '<tr class="liste_total">';
585 $out .= '<td></td>'."\n";
586 $out .= '<td>'.$langs->trans("Total").'</td>'."\n";
587 $out .= '<td class="center"></td>'."\n";
588 $out .= '<td class="center"></td>'."\n";
589 $out .= '<td class="right">'.dol_print_size($totalsize).'</td>'."\n";
590 $out .= '<td class="right"></td>'."\n";
591 $out .= "</tr>\n";
592 } else {
593 $out .= '<tr class="oddeven"><td colspan="6"><span class="opacitymedium">'.$langs->trans("None").'</span></td></tr>';
594 }
595 $out .= '</table>';
596 $out .= '</div>';
597 }
598 } else {
599 print '<div class="error">';
600 print 'Error: Failed to found <b>dolibarr_htdocs_dir</b> into content of XML file:<br>'.dol_escape_htmltag(dol_trunc($xmlfile, 500));
601 print '</div><br>';
602 $error++;
603 }
604
605
606 // Scan scripts
607 /*
608 if (is_object($xml->dolibarr_scripts_dir[0])) {
609 $file_list = array();
610 $ret = getFilesUpdated($file_list, $xml->dolibarr_htdocs_dir[0], '', ???, $checksumconcat); // Fill array $file_list
611 '@phan-var-force array{insignature:string[],missing?:array<array{filename:string,expectedhash:string,expectedsize:string,algo:string}>,updated:array<array{filename:string,expectedhash:string,expectedsize:string,hash:string,algo:string}>} $file_list';
612 }*/
613
614
615 // Section Globalchecksum
616 asort($checksumconcat); // Sort list of checksum
617
618 $checksumget = hash($algo, implode(',', $checksumconcat));
619
620 if ($mode == 'unalterable') {
621 $nameofsection = 'dolibarr_unalterable_files_checksum';
622 $checksumtoget = trim((string) $xml->dolibarr_unalterable_files_checksum);
623 } else {
624 $nameofsection = 'dolibarr_htdocs_dir_checksum';
625 $checksumtoget = trim((string) $xml->dolibarr_htdocs_dir_checksum);
626 }
627
628 $resultcomment = '';
629
630 $outexpectedchecksum = ($checksumtoget ? $checksumtoget : $langs->trans("Unknown"));
631 $outcurrentchecksumtext = '';
632 if ($checksumget == $checksumtoget) {
633 if (empty($onlymodifiedorremoved) && !empty($file_list['added'])) {
634 $resultcode = 'warning';
635 $resultcomment = 'FileIntegrityIsOkButFilesWereAdded';
636 $outcurrentchecksum = $checksumget;
637 $outcurrentchecksumtext .= img_picto('', 'tick').' <span class="'.$resultcode.'">'.$langs->trans($resultcomment).'</span>';
638 } else {
639 $resultcode = 'ok';
640 $resultcomment = 'Success';
641 $outcurrentchecksum = '<span class="'.$resultcode.'" title="Checksum of all current checksums concatenated separated by a comma">'.$checksumget.'</span>';
642 $outcurrentchecksumtext.= img_picto('', 'tick').' <span class="badge badge-status4 badge-status '.$resultcode.'">'.$langs->trans($resultcomment).'</span>';
643 }
644 } else {
645 $resultcode = 'error';
646 $resultcomment = 'FileIntegrityIsKO';
647 $outcurrentchecksum = '<span class="'.$resultcode.'" title="Checksum of all current checksums concatenated separated by a comma">'.$checksumget.'</span>';
648 $outcurrentchecksumtext .= img_picto('', 'error').' <span class="'.$resultcode.'">'.$langs->trans($resultcomment).'</span>';
649 }
650
651 // Show warning
652 if (empty($tmpfilelist) && empty($tmpfilelist2) && empty($tmpfilelist3) && $resultcode == 'ok') {
653 setEventMessages($langs->trans("FileIntegrityIsStrictlyConformedWithReference"), null, 'mesgs');
654 } else {
655 if ($resultcode == 'warning') {
656 setEventMessages($langs->trans($resultcomment), null, 'warnings');
657 } else {
658 setEventMessages($langs->trans("FileIntegritySomeFilesWereRemovedOrModified"), null, 'errors');
659 }
660 }
661
662 $outforlistoffiles = '';
663 if ($mode == 'unalterable') {
664 print load_fiche_titre($langs->trans("UnalterableFilesChecksum"));
665
666 // Print list of files
667 $outforlistoffiles = '<a href="#" onclick="console.log(\'Click\'); jQuery(\'#listofunalterablefiles\').toggle(); return false;">'.$langs->trans("ShowListOfFiles").'</a><br>';
668 $outforlistoffiles .= '<textarea id="listofunalterablefiles" class="hideobject quatrevingtpercent" rows="12" spellcheck="false">';
669 $i = 0;
670 foreach ($listoffilestoanalyze as $dirtoanalyze) {
671 $entry = array();
672 if (!empty($dirtoanalyze->md5file)) {
673 $entry = $dirtoanalyze->md5file;
674 $algo = 'md5';
675 } elseif (!empty($dirtoanalyze->sha256file)) {
676 $entry = $dirtoanalyze->sha256file;
677 $algo = 'sha256';
678 }
679
680 foreach ($entry as $filetoanalyze) {
681 if ($i) {
682 $outforlistoffiles .= "\n";
683 }
684 $outforlistoffiles .= (string) $dirtoanalyze['name'];
685 $outforlistoffiles .= '/';
686 $outforlistoffiles .= (string) $filetoanalyze['name'];
687 $i++;
688 }
689 }
690 $outforlistoffiles .= '</textarea>';
691 $outforlistoffiles .= '<br>';
692 } else {
693 print load_fiche_titre($langs->trans("GlobalChecksum"));
694 }
695
696
697 print '<div class="div-table-responsive-no-min">';
698 print '<table class="noborder">';
699 print '<tr class="liste_titre">';
700 print '<td>'.$langs->trans("ExpectedChecksum").' <span class="opacitymedium">('.$algo.')</span></td>';
701 print '<td>'.$langs->trans("CurrentChecksum").' <span class="opacitymedium">('.$algo.')</span></td>';
702 print '</tr>'."\n";
703
704 print '<tr><td>';
705 print '<span title="Checksum of all checksums in file separated by a comma and saved into '.$nameofsection.'">';
706 print $outexpectedchecksum;
707 print '</span>';
708 print '</td><td>';
709 print $outcurrentchecksum;
710 print '</td>';
711 print '</tr>';
712 print '</table>';
713 print $outcurrentchecksumtext.'<br>';
714
715 print '<br>';
716 print $outforlistoffiles;
717 print '<br><br>';
718
719
720 // Output detail
721 print $out;
722}
723
724// End of page
725llxFooter();
726$db->close();
727
728exit($error);
versiondolibarrarray()
Return version Dolibarr.
versioncompare($versionarray1, $versionarray2)
Compare 2 versions (stored into 2 arrays), to know if a version (a,b,c) is lower than (x,...
Definition admin.lib.php:72
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
getBlockedLogVersionToShow()
Define head array for tabs of blockedlog tools setup pages.
isALNEQualifiedVersion($ignoredev=0, $ignoremodule=0)
Return if the version is a candidate version to get the LNE certification and if the prerequisites ar...
isALNERunningVersion($blockedlogtestalreadydone=0, $blockedlogmodulealreadydone=0)
Return if the application is executed with the LNE requirements on.
Class to manage Dolibarr users.
Class to describe a BlockedLog module.
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
getFilesUpdated(&$file_list, SimpleXMLElement $dir, $path='', $pathref='', &$checksumconcat=array())
Function to get list of updated or modified files.
dol_filemtime($pathoffile)
Return time of a file.
dol_filesize($pathoffile)
Return size of a file.
dol_uncompress($inputfile, $outputdir)
Uncompress a file.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:64
dol_print_size($size, $shortvalue=0, $shortunit=0)
Return string with formatted size.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0, $allowdash=0)
Clean a string to use it as a file name.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
newToken()
Return the value of token currently saved into session with name 'newtoken'.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='', $morecssonpicto='widthpictotitle')
Load a title with picto.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '...' if string larger than length.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
getURLContent($url, $postorget='GET', $param='', $followlocation=1, $addheaders=array(), $allowedschemes=array('http', 'https'), $localurl=0, $ssl_verifypeer=-1, $timeoutconnect=0, $timeoutresponse=0, $otherCurlOptions=array(), $morelogsuffix='')
Function to get a content from an URL (use proxy if proxy defined).
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.