dolibarr 20.0.0
utils.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2021 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2022 Anthony Berton <anthony.berton@bb2a.fr>
5 * Copyright (C) 2023-2024 William Mead <william.mead@manchenumerique.fr>
6 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
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 * 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
33class Utils
34{
38 public $db;
39
44 public $error;
45
49 public $errors;
50
54 public $output;
55
59 public $result;
60
66 public function __construct($db)
67 {
68 $this->db = $db;
69 }
70
71
80 public function purgeFiles($choices = 'tempfilesold+logfiles', $nbsecondsold = 86400)
81 {
82 global $conf, $langs, $user;
83 global $dolibarr_main_data_root;
84
85 $langs->load("admin");
86
87 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
88
89 if (empty($choices)) {
90 $choices = 'tempfilesold+logfiles';
91 }
92 if ($choices == 'allfiles' && $nbsecondsold > 0) {
93 $choices = 'allfilesold';
94 }
95
96 dol_syslog("Utils::purgeFiles choice=".$choices, LOG_DEBUG);
97
98 // For dangerous action, we check the user is admin
99 if (in_array($choices, array('allfiles', 'allfilesold'))) {
100 if (empty($user->admin)) {
101 $this->output = 'Error: to erase data files, user running the batch (currently '.$user->login.') must be an admin user';
102 return 1;
103 }
104 }
105
106 $count = 0;
107 $countdeleted = 0;
108 $counterror = 0;
109 $filelog = '';
110
111 $choicesarray = preg_split('/[\+,]/', $choices);
112 foreach ($choicesarray as $choice) {
113 $now = dol_now();
114 $filesarray = array();
115
116 if ($choice == 'tempfiles' || $choice == 'tempfilesold') {
117 // Delete temporary files
118 if ($dolibarr_main_data_root) {
119 $filesarray = dol_dir_list($dolibarr_main_data_root, "directories", 1, '^temp$', '', 'name', SORT_ASC, 2, 0, '', 1); // Do not follow symlinks
120
121 if ($choice == 'tempfilesold') {
122 foreach ($filesarray as $key => $val) {
123 if ($val['date'] > ($now - ($nbsecondsold))) {
124 unset($filesarray[$key]); // Discard temp dir not older than $nbsecondsold
125 }
126 }
127 }
128 }
129 }
130
131 if ($choice == 'allfiles') {
132 // Delete all files (except .lock and .unlock files, do not follow symbolic links)
133 if ($dolibarr_main_data_root) {
134 $filesarray = dol_dir_list($dolibarr_main_data_root, "all", 0, '', '(\.lock|\.unlock)$', 'name', SORT_ASC, 0, 0, '', 1); // No need to use recursive, we will delete directory
135 }
136 }
137
138 if ($choice == 'allfilesold') {
139 // Delete all files (except .lock and .unlock files, do not follow symbolic links)
140 if ($dolibarr_main_data_root) {
141 $filesarray = dol_dir_list($dolibarr_main_data_root, "files", 1, '', '(\.lock|\.unlock)$', 'name', SORT_ASC, 0, 0, '', 1, $nbsecondsold); // No need to use recursive, we will delete directory
142 }
143 }
144
145 if ($choice == 'logfile' || $choice == 'logfiles') {
146 // Define files log
147 if ($dolibarr_main_data_root) {
148 $filesarray = dol_dir_list($dolibarr_main_data_root, "files", 0, '.*\.log[\.0-9]*(\.gz)?$', '(\.lock|\.unlock)$', 'name', SORT_ASC, 0, 0, '', 1);
149 }
150
151 if (isModEnabled('syslog')) {
152 $filelog = getDolGlobalString('SYSLOG_FILE');
153 $filelog = preg_replace('/DOL_DATA_ROOT/i', DOL_DATA_ROOT, $filelog);
154
155 $alreadyincluded = false;
156 foreach ($filesarray as $tmpcursor) {
157 if ($tmpcursor['fullname'] == $filelog) {
158 $alreadyincluded = true;
159 }
160 }
161 if (!$alreadyincluded) {
162 $filesarray[] = array('fullname' => $filelog, 'type' => 'file');
163 }
164 }
165 }
166
167 if (is_array($filesarray) && count($filesarray)) {
168 foreach ($filesarray as $key => $value) {
169 //print "x ".$filesarray[$key]['fullname']."-".$filesarray[$key]['type']."<br>\n";
170 if ($filesarray[$key]['type'] == 'dir') {
171 $startcount = 0;
172 $tmpcountdeleted = 0;
173
174 $result = dol_delete_dir_recursive($filesarray[$key]['fullname'], $startcount, 1, 0, $tmpcountdeleted);
175
176 $excluded = [
177 $conf->user->dir_temp,
178 ];
179 if (isModEnabled('api')) {
180 $excluded[] = $conf->api->dir_temp;
181 }
182 // The 2 directories $conf->api->dir_temp and $conf->user->dir_temp are recreated at end, so we do not count them
183 if (!in_array($filesarray[$key]['fullname'], $excluded)) {
184 $count += $result;
185 $countdeleted += $tmpcountdeleted;
186 }
187 } elseif ($filesarray[$key]['type'] == 'file') {
188 if ($choice != 'allfilesold' || $filesarray[$key]['date'] < ($now - $nbsecondsold)) {
189 // If (file that is not logfile) or (if mode is logfile)
190 if ($filesarray[$key]['fullname'] != $filelog || $choice == 'logfile' || $choice == 'logfiles') {
191 $result = dol_delete_file($filesarray[$key]['fullname'], 1, 1);
192 if ($result) {
193 $count++;
194 $countdeleted++;
195 } else {
196 $counterror++;
197 }
198 }
199 }
200 }
201 }
202
203 // Update cachenbofdoc
204 if (isModEnabled('ecm') && $choice == 'allfiles') {
205 require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmdirectory.class.php';
206 $ecmdirstatic = new EcmDirectory($this->db);
207 $result = $ecmdirstatic->refreshcachenboffile(1);
208 }
209 }
210 }
211
212 if ($count > 0) {
213 $langs->load("admin");
214 $this->output = $langs->trans("PurgeNDirectoriesDeleted", $countdeleted);
215 if ($count > $countdeleted) {
216 $this->output .= '<br>'.$langs->trans("PurgeNDirectoriesFailed", ($count - $countdeleted));
217 }
218 } else {
219 $this->output = $langs->trans("PurgeNothingToDelete").(in_array('tempfilesold', $choicesarray) ? ' (older than 24h for temp files)' : '');
220 }
221
222 // Recreate temp dir that are not automatically recreated by core code for performance purpose, we need them
223 if (isModEnabled('api')) {
224 dol_mkdir($conf->api->dir_temp);
225 }
226 dol_mkdir($conf->user->dir_temp);
227
228 //return $count;
229 return 0; // This function can be called by cron so must return 0 if OK
230 }
231
232
246 public function dumpDatabase($compression = 'none', $type = 'auto', $usedefault = 1, $file = 'auto', $keeplastnfiles = 0, $execmethod = 0, $lowmemorydump = 0)
247 {
248 global $db, $conf, $langs, $dolibarr_main_data_root;
249 global $dolibarr_main_db_name, $dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_port, $dolibarr_main_db_pass;
250 global $dolibarr_main_db_character_set;
251
252 $langs->load("admin");
253
254 dol_syslog("Utils::dumpDatabase type=".$type." compression=".$compression." file=".$file, LOG_DEBUG);
255 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
256
257 // Clean data
258 $file = dol_sanitizeFileName($file);
259
260 // Check compression parameter
261 if (!in_array($compression, array('none', 'gz', 'bz', 'zip', 'zstd'))) {
262 $langs->load("errors");
263 $this->error = $langs->transnoentitiesnoconv("ErrorBadValueForParameter", $compression, "Compression");
264 return -1;
265 }
266
267 // Check type parameter
268 if ($type == 'auto') {
269 $type = $this->db->type;
270 }
271 if (!in_array($type, array('postgresql', 'pgsql', 'mysql', 'mysqli', 'mysqlnobin'))) {
272 $langs->load("errors");
273 $this->error = $langs->transnoentitiesnoconv("ErrorBadValueForParameter", $type, "Basetype");
274 return -1;
275 }
276
277 // Check file parameter
278 if ($file == 'auto') {
279 $prefix = 'dump';
280 $ext = 'sql';
281 if (in_array($type, array('mysql', 'mysqli'))) {
282 $prefix = 'mysqldump';
283 }
284 //if ($label == 'PostgreSQL') { $prefix='pg_dump'; $ext='dump'; }
285 if (in_array($type, array('pgsql'))) {
286 $prefix = 'pg_dump';
287 }
288 $file = $prefix.'_'.$dolibarr_main_db_name.'_'.dol_sanitizeFileName(DOL_VERSION).'_'.dol_print_date(dol_now('gmt'), "dayhourlogsmall", 'tzuser').'.'.$ext;
289 }
290
291 $outputdir = $conf->admin->dir_output.'/backup';
292 $result = dol_mkdir($outputdir);
293 $errormsg = '';
294
295 // MYSQL
296 if ($type == 'mysql' || $type == 'mysqli') {
297 if (!getDolGlobalString('SYSTEMTOOLS_MYSQLDUMP')) {
298 $cmddump = $db->getPathOfDump();
299 } else {
300 $cmddump = getDolGlobalString('SYSTEMTOOLS_MYSQLDUMP');
301 }
302 if (empty($cmddump)) {
303 $this->error = "Failed to detect command to use for mysqldump. Try a manual backup before to set path of command.";
304 return -1;
305 }
306
307 $outputfile = $outputdir.'/'.$file;
308 // for compression format, we add extension
309 $compression = $compression ? $compression : 'none';
310 if ($compression == 'gz') {
311 $outputfile .= '.gz';
312 } elseif ($compression == 'bz') {
313 $outputfile .= '.bz2';
314 } elseif ($compression == 'zstd') {
315 $outputfile .= '.zst';
316 }
317 $outputerror = $outputfile.'.err';
318 dol_mkdir($conf->admin->dir_output.'/backup');
319
320 // Parameters execution
321 $command = $cmddump;
322 $command = preg_replace('/(\$|%)/', '', $command); // We removed chars that can be used to inject vars that contains space inside path of command without seeing there is a space to bypass the escapeshellarg.
323 if (preg_match("/\s/", $command)) {
324 $command = escapeshellarg($command); // If there is spaces, we add quotes on command to be sure $command is only a program and not a program+parameters
325 }
326
327 //$param=escapeshellarg($dolibarr_main_db_name)." -h ".escapeshellarg($dolibarr_main_db_host)." -u ".escapeshellarg($dolibarr_main_db_user)." -p".escapeshellarg($dolibarr_main_db_pass);
328 $param = $dolibarr_main_db_name." -h ".$dolibarr_main_db_host;
329 $param .= " -u ".$dolibarr_main_db_user;
330 if (!empty($dolibarr_main_db_port)) {
331 $param .= " -P ".$dolibarr_main_db_port." --protocol=tcp";
332 }
333 if (GETPOST("use_transaction", "alpha")) {
334 $param .= " --single-transaction";
335 }
336 if (GETPOST("disable_fk", "alpha") || $usedefault) {
337 $param .= " -K";
338 }
339 if (GETPOST("sql_compat", "alpha") && GETPOST("sql_compat", "alpha") != 'NONE') {
340 $param .= " --compatible=".escapeshellarg(GETPOST("sql_compat", "alpha"));
341 }
342 if (GETPOST("drop_database", "alpha")) {
343 $param .= " --add-drop-database";
344 }
345 if (GETPOST("use_mysql_quick_param", "alpha")) {
346 $param .= " --quick";
347 }
348 if (GETPOST("use_force", "alpha")) {
349 $param .= " -f";
350 }
351 if (GETPOST("sql_structure", "alpha") || $usedefault) {
352 if (GETPOST("drop", "alpha") || $usedefault) {
353 $param .= " --add-drop-table=TRUE";
354 } else {
355 $param .= " --add-drop-table=FALSE";
356 }
357 } else {
358 $param .= " -t";
359 }
360 if (GETPOST("disable-add-locks", "alpha")) {
361 $param .= " --add-locks=FALSE";
362 }
363 if (GETPOST("sql_data", "alpha") || $usedefault) {
364 $param .= " --tables";
365 if (GETPOST("showcolumns", "alpha") || $usedefault) {
366 $param .= " -c";
367 }
368 if (GETPOST("extended_ins", "alpha") || $usedefault) {
369 $param .= " -e";
370 } else {
371 $param .= " --skip-extended-insert";
372 }
373 if (GETPOST("delayed", "alpha")) {
374 $param .= " --delayed-insert";
375 }
376 if (GETPOST("sql_ignore", "alpha")) {
377 $param .= " --insert-ignore";
378 }
379 if (GETPOST("hexforbinary", "alpha") || $usedefault) {
380 $param .= " --hex-blob";
381 }
382 } else {
383 $param .= " -d"; // No row information (no data)
384 }
385 if ($dolibarr_main_db_character_set == 'utf8mb4') {
386 // We save output into utf8mb4 charset
387 $param .= " --default-character-set=utf8mb4 --no-tablespaces";
388 } else {
389 $param .= " --default-character-set=utf8 --no-tablespaces"; // We always save output into utf8 charset
390 }
391 $paramcrypted = $param;
392 $paramclear = $param;
393 if (!empty($dolibarr_main_db_pass)) {
394 $paramcrypted .= ' -p"'.preg_replace('/./i', '*', $dolibarr_main_db_pass).'"';
395 $paramclear .= ' -p"'.str_replace(array('"', '`', '$'), array('\"', '\`', '\$'), $dolibarr_main_db_pass).'"';
396 }
397
398 $handle = '';
399
400 // Start call method to execute dump
401 $fullcommandcrypted = $command." ".$paramcrypted." 2>&1";
402 $fullcommandclear = $command." ".$paramclear." 2>&1";
403 if (!$lowmemorydump) {
404 if ($compression == 'none') {
405 $handle = fopen($outputfile, 'w');
406 } elseif ($compression == 'gz') {
407 $handle = gzopen($outputfile, 'w');
408 } elseif ($compression == 'bz') {
409 $handle = bzopen($outputfile, 'w');
410 } elseif ($compression == 'zstd') {
411 $handle = fopen($outputfile, 'w');
412 }
413 } else {
414 if ($compression == 'none') {
415 $fullcommandclear .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." > "'.dol_sanitizePathName($outputfile).'"';
416 $fullcommandcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." > "'.dol_sanitizePathName($outputfile).'"';
417 $handle = 1;
418 } elseif ($compression == 'gz') {
419 $fullcommandclear .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | gzip > "'.dol_sanitizePathName($outputfile).'"';
420 $fullcommandcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | gzip > "'.dol_sanitizePathName($outputfile).'"';
421 $paramcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | gzip';
422 $handle = 1;
423 } elseif ($compression == 'bz') {
424 $fullcommandclear .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | bzip2 > "'.dol_sanitizePathName($outputfile).'"';
425 $fullcommandcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | bzip2 > "'.dol_sanitizePathName($outputfile).'"';
426 $paramcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | bzip2';
427 $handle = 1;
428 } elseif ($compression == 'zstd') {
429 $fullcommandclear .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | zstd > "'.dol_sanitizePathName($outputfile).'"';
430 $fullcommandcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | zstd > "'.dol_sanitizePathName($outputfile).'"';
431 $paramcrypted .= ' | grep -v "Warning: Using a password on the command line interface can be insecure." | zstd';
432 $handle = 1;
433 }
434 }
435
436 $ok = 0;
437 if ($handle) {
438 if (getDolGlobalString('MAIN_EXEC_USE_POPEN')) {
439 $execmethod = getDolGlobalString('MAIN_EXEC_USE_POPEN');
440 }
441 if (empty($execmethod)) {
442 $execmethod = 1;
443 }
444
445 dol_syslog("Utils::dumpDatabase execmethod=".$execmethod." command:".$fullcommandcrypted, LOG_INFO);
446
447
448 /* If value has been forced with a php_admin_value, this has no effect. Example of value: '512M' */
449 $MemoryLimit = getDolGlobalString('MAIN_MEMORY_LIMIT_DUMP');
450 if (!empty($MemoryLimit)) {
451 @ini_set('memory_limit', $MemoryLimit);
452 }
453
454
455 if ($execmethod == 1) {
456 $output_arr = array();
457 $retval = null;
458
459 exec($fullcommandclear, $output_arr, $retval);
460 // TODO Replace this exec with Utils->executeCLI() function.
461 // We must check that the case for $lowmemorydump works too...
462 //$utils = new Utils($db);
463 //$outputfile = $conf->admin->dir_temp.'/dump.tmp';
464 //$utils->executeCLI($fullcommandclear, $outputfile, 0);
465
466 if ($retval != 0) {
467 $langs->load("errors");
468 dol_syslog("Datadump retval after exec=".$retval, LOG_ERR);
469 $errormsg = 'Error '.$retval;
470 $ok = 0;
471 } else {
472 $i = 0;
473 if (!empty($output_arr)) {
474 foreach ($output_arr as $key => $read) {
475 $i++; // output line number
476 if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) {
477 continue;
478 }
479 // Now check into the result file, that the file end with "-- Dump completed"
480 // This is possible only if $output_arr is the clear dump file, so not possible with $lowmemorydump set because file is already compressed.
481 if (!$lowmemorydump) {
482 fwrite($handle, $read.($execmethod == 2 ? '' : "\n"));
483 if (preg_match('/'.preg_quote('-- Dump completed', '/').'/i', $read)) {
484 $ok = 1;
485 } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES', '/').'/i', $read)) {
486 $ok = 1;
487 }
488 } else {
489 // If we have a result here in lowmemorydump mode, something is strange
490 }
491 }
492 } elseif ($lowmemorydump) {
493 $ok = 1;
494 }
495 }
496 }
497
498 if ($execmethod == 2) { // With this method, there is no way to get the return code, only output
499 $handlein = popen($fullcommandclear, 'r');
500 $i = 0;
501 if ($handlein) {
502 while (!feof($handlein)) {
503 $i++; // output line number
504 $read = fgets($handlein);
505 // Exclude warning line we don't want
506 if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) {
507 continue;
508 }
509 fwrite($handle, $read);
510 if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) {
511 $ok = 1;
512 } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) {
513 $ok = 1;
514 }
515 }
516 pclose($handlein);
517 }
518 }
519
520 if (!$lowmemorydump) {
521 if ($compression == 'none') {
522 fclose($handle);
523 } elseif ($compression == 'gz') {
524 gzclose($handle);
525 } elseif ($compression == 'bz') {
526 fclose($handle);
527 } elseif ($compression == 'zstd') {
528 fclose($handle);
529 }
530 }
531
532 dolChmod($outputfile);
533 } else {
534 $langs->load("errors");
535 dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
536 $errormsg = $langs->trans("ErrorFailedToWriteInDir");
537 }
538
539 // Get errorstring
540 if ($compression == 'none') {
541 $handle = fopen($outputfile, 'r');
542 } elseif ($compression == 'gz') {
543 $handle = gzopen($outputfile, 'r');
544 } elseif ($compression == 'bz') {
545 $handle = bzopen($outputfile, 'r');
546 } elseif ($compression == 'zstd') {
547 $handle = fopen($outputfile, 'r');
548 }
549 if ($handle) {
550 // Get 2048 first chars of error message.
551 $errormsg = fgets($handle, 2048);
552 //$ok=0;$errormsg=''; To force error
553
554 // Close file
555 if ($compression == 'none') {
556 fclose($handle);
557 } elseif ($compression == 'gz') {
558 gzclose($handle);
559 } elseif ($compression == 'bz') {
560 fclose($handle);
561 } elseif ($compression == 'zstd') {
562 fclose($handle);
563 }
564 if ($ok && preg_match('/^-- (MySql|MariaDB)/i', $errormsg) || preg_match('/^\/\*M?!999999/', $errormsg)) { // Start of file is ok, NOT an error
565 $errormsg = '';
566 } else {
567 // Rename file out into a file error
568 //print "$outputfile -> $outputerror";
569 @dol_delete_file($outputerror, 1, 0, 0, null, false, 0);
570 @dol_move($outputfile, $outputerror, '0', 1, 0, 0);
571 // Si safe_mode on et command hors du parameter exec, on a un fichier out vide donc errormsg vide
572 if (!$errormsg) {
573 $langs->load("errors");
574 $errormsg = $langs->trans("ErrorFailedToRunExternalCommand");
575 }
576 }
577 }
578 // Fin execution commande
579
580 $this->output = $errormsg;
581 $this->error = $errormsg;
582 $this->result = array("commandbackuplastdone" => $command." ".$paramcrypted, "commandbackuptorun" => "");
583 //if (empty($this->output)) $this->output=$this->result['commandbackuplastdone'];
584 }
585
586 // MYSQL NO BIN
587 if ($type == 'mysqlnobin') {
588 $outputfile = $outputdir.'/'.$file;
589 $outputfiletemp = $outputfile.'-TMP.sql';
590 // for compression format, we add extension
591 $compression = $compression ? $compression : 'none';
592 if ($compression == 'gz') {
593 $outputfile .= '.gz';
594 }
595 if ($compression == 'bz') {
596 $outputfile .= '.bz2';
597 }
598 $outputerror = $outputfile.'.err';
599 dol_mkdir($conf->admin->dir_output.'/backup');
600
601 if ($compression == 'gz' or $compression == 'bz') {
602 $this->backupTables($outputfiletemp);
603 dol_compress_file($outputfiletemp, $outputfile, $compression);
604 unlink($outputfiletemp);
605 } else {
606 $this->backupTables($outputfile);
607 }
608
609 $this->output = "";
610 $this->result = array("commandbackuplastdone" => "", "commandbackuptorun" => "");
611 }
612
613 // POSTGRESQL
614 if ($type == 'postgresql' || $type == 'pgsql') {
615 $cmddump = getDolGlobalString('SYSTEMTOOLS_POSTGRESQLDUMP');
616
617 $outputfile = $outputdir.'/'.$file;
618 // for compression format, we add extension
619 $compression = $compression ? $compression : 'none';
620 if ($compression == 'gz') {
621 $outputfile .= '.gz';
622 }
623 if ($compression == 'bz') {
624 $outputfile .= '.bz2';
625 }
626 $outputerror = $outputfile.'.err';
627 dol_mkdir($conf->admin->dir_output.'/backup');
628
629 // Parameters execution
630 $command = $cmddump;
631 $command = preg_replace('/(\$|%)/', '', $command); // We removed chars that can be used to inject vars that contains space inside path of command without seeing there is a space to bypass the escapeshellarg.
632 if (preg_match("/\s/", $command)) {
633 $command = escapeshellarg($command); // If there is spaces, we add quotes on command to be sure $command is only a program and not a program+parameters
634 }
635
636 //$param=escapeshellarg($dolibarr_main_db_name)." -h ".escapeshellarg($dolibarr_main_db_host)." -u ".escapeshellarg($dolibarr_main_db_user)." -p".escapeshellarg($dolibarr_main_db_pass);
637 //$param="-F c";
638 $param = "-F p";
639 $param .= " --no-tablespaces --inserts -h ".$dolibarr_main_db_host;
640 $param .= " -U ".$dolibarr_main_db_user;
641 if (!empty($dolibarr_main_db_port)) {
642 $param .= " -p ".$dolibarr_main_db_port;
643 }
644 if (GETPOST("sql_compat") && GETPOST("sql_compat") == 'ANSI') {
645 $param .= " --disable-dollar-quoting";
646 }
647 if (GETPOST("drop_database")) {
648 $param .= " -c -C";
649 }
650 if (GETPOST("sql_structure")) {
651 if (GETPOST("drop")) {
652 $param .= " --add-drop-table";
653 }
654 if (!GETPOST("sql_data")) {
655 $param .= " -s";
656 }
657 }
658 if (GETPOST("sql_data")) {
659 if (!GETPOST("sql_structure")) {
660 $param .= " -a";
661 }
662 if (GETPOST("showcolumns")) {
663 $param .= " -c";
664 }
665 }
666 $param .= ' -f "'.$outputfile.'"';
667 //if ($compression == 'none')
668 if ($compression == 'gz') {
669 $param .= ' -Z 9';
670 }
671 //if ($compression == 'bz')
672 $paramcrypted = $param;
673 $paramclear = $param;
674 /*if (!empty($dolibarr_main_db_pass))
675 {
676 $paramcrypted.=" -W".preg_replace('/./i','*',$dolibarr_main_db_pass);
677 $paramclear.=" -W".$dolibarr_main_db_pass;
678 }*/
679 $paramcrypted .= " -w ".$dolibarr_main_db_name;
680 $paramclear .= " -w ".$dolibarr_main_db_name;
681
682 $this->output = "";
683 $this->result = array("commandbackuplastdone" => "", "commandbackuptorun" => $command." ".$paramcrypted);
684 }
685
686 // Clean old files
687 if (!$errormsg && $keeplastnfiles > 0) {
688 $tmpfiles = dol_dir_list($conf->admin->dir_output.'/backup', 'files', 0, '', '(\.err|\.old|\.sav)$', 'date', SORT_DESC);
689 $i = 0;
690 if (is_array($tmpfiles)) {
691 foreach ($tmpfiles as $key => $val) {
692 $i++;
693 if ($i <= $keeplastnfiles) {
694 continue;
695 }
696 dol_delete_file($val['fullname'], 0, 0, 0, null, false, 0);
697 }
698 }
699 }
700
701 return ($errormsg ? -1 : 0);
702 }
703
704
705
719 public function executeCLI($command, $outputfile, $execmethod = 0, $redirectionfile = null, $noescapecommand = 0, $redirectionfileerr = null)
720 {
721 global $conf, $langs;
722
723 $result = 0;
724 $output = '';
725 $error = '';
726
727 if (empty($noescapecommand)) {
728 $command = escapeshellcmd($command);
729 }
730
731 if ($redirectionfile) {
732 $command .= " > ".dol_sanitizePathName($redirectionfile);
733 }
734
735 if ($redirectionfileerr && ($redirectionfileerr != $redirectionfile)) {
736 // If we ask a redirect of stderr on a given file not already used for stdout
737 $command .= " 2> ".dol_sanitizePathName($redirectionfileerr);
738 } else {
739 $command .= " 2>&1";
740 }
741
742 if (getDolGlobalString('MAIN_EXEC_USE_POPEN')) {
743 $execmethod = getDolGlobalString('MAIN_EXEC_USE_POPEN');
744 }
745 if (empty($execmethod)) {
746 $execmethod = 1;
747 }
748 //$execmethod=1;
749 dol_syslog("Utils::executeCLI execmethod=".$execmethod." command=".$command, LOG_DEBUG);
750 $output_arr = array();
751
752 if ($execmethod == 1) {
753 $retval = null;
754 exec($command, $output_arr, $retval);
755 $result = $retval;
756 if ($retval != 0) {
757 $langs->load("errors");
758 dol_syslog("Utils::executeCLI retval after exec=".$retval, LOG_ERR);
759 $error = 'Error '.$retval;
760 }
761 }
762 if ($execmethod == 2) { // With this method, there is no way to get the return code, only output
763 $handle = fopen($outputfile, 'w+b');
764 if ($handle) {
765 dol_syslog("Utils::executeCLI run command ".$command);
766 $handlein = popen($command, 'r');
767 while (!feof($handlein)) {
768 $read = fgets($handlein);
769 fwrite($handle, $read);
770 $output_arr[] = $read;
771 }
772 pclose($handlein);
773 fclose($handle);
774 }
775 dolChmod($outputfile);
776 }
777
778 // Update with result
779 if (is_array($output_arr) && count($output_arr) > 0) {
780 foreach ($output_arr as $val) {
781 $output .= $val.($execmethod == 2 ? '' : "\n");
782 }
783 }
784
785 dol_syslog("Utils::executeCLI result=".$result." output=".$output." error=".$error, LOG_DEBUG);
786
787 return array('result' => $result, 'output' => $output, 'error' => $error);
788 }
789
796 public function generateDoc($module)
797 {
798 global $conf, $langs, $user, $mysoc;
799 global $dirins;
800
801 $error = 0;
802
803 $modulelowercase = strtolower($module);
804 $now = dol_now();
805
806 // Dir for module
807 $dir = $dirins.'/'.$modulelowercase;
808 // Zip file to build
809 $FILENAMEDOC = '';
810
811 // Load module
812 dol_include_once($modulelowercase.'/core/modules/mod'.$module.'.class.php');
813 $class = 'mod'.$module;
814
815 if (class_exists($class)) {
816 try {
817 $moduleobj = new $class($this->db);
818 } catch (Exception $e) {
819 $error++;
820 dol_print_error(null, $e->getMessage());
821 }
822 } else {
823 $error++;
824 $langs->load("errors");
825 dol_print_error(null, $langs->trans("ErrorFailedToLoadModuleDescriptorForXXX", $module));
826 exit;
827 }
828
829 $arrayversion = explode('.', $moduleobj->version, 3);
830 if (count($arrayversion)) {
831 $FILENAMEASCII = strtolower($module).'.asciidoc';
832 $FILENAMEDOC = strtolower($module).'.html';
833 $FILENAMEDOCPDF = strtolower($module).'.pdf';
834
835 $dirofmodule = dol_buildpath(strtolower($module), 0);
836 $dirofmoduledoc = dol_buildpath(strtolower($module), 0).'/doc';
837 $dirofmoduletmp = dol_buildpath(strtolower($module), 0).'/doc/temp';
838 $outputfiledoc = $dirofmoduledoc.'/'.$FILENAMEDOC;
839 if ($dirofmoduledoc) {
840 if (!dol_is_dir($dirofmoduledoc)) {
841 dol_mkdir($dirofmoduledoc);
842 }
843 if (!dol_is_dir($dirofmoduletmp)) {
844 dol_mkdir($dirofmoduletmp);
845 }
846 if (!is_writable($dirofmoduletmp)) {
847 $this->error = 'Dir '.$dirofmoduletmp.' does not exists or is not writable';
848 return -1;
849 }
850
851 if (!getDolGlobalString('MODULEBUILDER_ASCIIDOCTOR') && !getDolGlobalString('MODULEBUILDER_ASCIIDOCTORPDF')) {
852 $this->error = 'Setup of module ModuleBuilder not complete';
853 return -1;
854 }
855
856 // Copy some files into temp directory, so instruction include::ChangeLog.md[] will works inside the asciidoc file.
857 dol_copy($dirofmodule.'/README.md', $dirofmoduletmp.'/README.md', 0, 1);
858 dol_copy($dirofmodule.'/ChangeLog.md', $dirofmoduletmp.'/ChangeLog.md', 0, 1);
859
860 // Replace into README.md and ChangeLog.md (in case they are included into documentation with tag __README__ or __CHANGELOG__)
861 $arrayreplacement = array();
862 $arrayreplacement['/^#\s.*/m'] = ''; // Remove first level of title into .md files
863 $arrayreplacement['/^#/m'] = '##'; // Add on # to increase level
864
865 dolReplaceInFile($dirofmoduletmp.'/README.md', $arrayreplacement, '', 0, 0, 1);
866 dolReplaceInFile($dirofmoduletmp.'/ChangeLog.md', $arrayreplacement, '', 0, 0, 1);
867
868
869 $destfile = $dirofmoduletmp.'/'.$FILENAMEASCII;
870
871 $fhandle = fopen($destfile, 'w+');
872 if ($fhandle) {
873 $specs = dol_dir_list(dol_buildpath(strtolower($module).'/doc', 0), 'files', 1, '(\.md|\.asciidoc)$', array('\/temp\/'));
874
875 $i = 0;
876 foreach ($specs as $spec) {
877 if (preg_match('/notindoc/', $spec['relativename'])) {
878 continue; // Discard file
879 }
880 if (preg_match('/example/', $spec['relativename'])) {
881 continue; // Discard file
882 }
883 if (preg_match('/disabled/', $spec['relativename'])) {
884 continue; // Discard file
885 }
886
887 $pathtofile = strtolower($module).'/doc/'.$spec['relativename'];
888 $format = 'asciidoc';
889 if (preg_match('/\.md$/i', $spec['name'])) {
890 $format = 'markdown';
891 }
892
893 $filecursor = @file_get_contents($spec['fullname']);
894 if ($filecursor) {
895 fwrite($fhandle, ($i ? "\n<<<\n\n" : "").$filecursor."\n");
896 } else {
897 $this->error = 'Failed to concat content of file '.$spec['fullname'];
898 return -1;
899 }
900
901 $i++;
902 }
903
904 fclose($fhandle);
905
906 $contentreadme = file_get_contents($dirofmoduletmp.'/README.md');
907 $contentchangelog = file_get_contents($dirofmoduletmp.'/ChangeLog.md');
908
909 include DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
910
911 //var_dump($phpfileval['fullname']);
912 $arrayreplacement = array(
913 'mymodule' => strtolower($module),
914 'MyModule' => $module,
915 'MYMODULE' => strtoupper($module),
916 'My module' => $module,
917 'my module' => $module,
918 'Mon module' => $module,
919 'mon module' => $module,
920 'htdocs/modulebuilder/template' => strtolower($module),
921 '__MYCOMPANY_NAME__' => $mysoc->name,
922 '__KEYWORDS__' => $module,
923 '__USER_FULLNAME__' => $user->getFullName($langs),
924 '__USER_EMAIL__' => $user->email,
925 '__YYYY-MM-DD__' => dol_print_date($now, 'dayrfc'),
926 '---Put here your own copyright and developer email---' => dol_print_date($now, 'dayrfc').' '.$user->getFullName($langs).($user->email ? ' <'.$user->email.'>' : ''),
927 '__DATA_SPECIFICATION__' => 'Not yet available',
928 '__README__' => dolMd2Asciidoc($contentreadme),
929 '__CHANGELOG__' => dolMd2Asciidoc($contentchangelog),
930 );
931
932 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
933 dolReplaceInFile($destfile, $arrayreplacement);
934 }
935
936 // Launch doc generation
937 $currentdir = getcwd();
938 chdir($dirofmodule);
939
940 require_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
941 $utils = new Utils($this->db);
942
943 // Build HTML doc
944 $command = getDolGlobalString('MODULEBUILDER_ASCIIDOCTOR') . ' '.$destfile.' -n -o '.$dirofmoduledoc.'/'.$FILENAMEDOC;
945 $outfile = $dirofmoduletmp.'/out.tmp';
946
947 $resarray = $utils->executeCLI($command, $outfile);
948 if ($resarray['result'] != '0') {
949 $this->error = $resarray['error'].' '.$resarray['output'];
950 $this->errors[] = $this->error;
951 }
952 $result = ($resarray['result'] == 0) ? 1 : 0;
953 if ($result < 0 && empty($this->errors)) {
954 $this->error = $langs->trans("ErrorFailToGenerateFile", $FILENAMEDOC);
955 $this->errors[] = $this->error;
956 }
957
958 // Build PDF doc
959 $command = getDolGlobalString('MODULEBUILDER_ASCIIDOCTORPDF') . ' '.$destfile.' -n -o '.$dirofmoduledoc.'/'.$FILENAMEDOCPDF;
960 $outfile = $dirofmoduletmp.'/outpdf.tmp';
961 $resarray = $utils->executeCLI($command, $outfile);
962 if ($resarray['result'] != '0') {
963 $this->error = $resarray['error'].' '.$resarray['output'];
964 $this->errors[] = $this->error;
965 }
966 $result = ($resarray['result'] == 0) ? 1 : 0;
967 if ($result < 0 && empty($this->errors)) {
968 $this->error = $langs->trans("ErrorFailToGenerateFile", $FILENAMEDOCPDF);
969 $this->errors[] = $this->error;
970 }
971
972 chdir($currentdir);
973 } else {
974 $result = 0;
975 }
976
977 if ($result > 0) {
978 return 1;
979 } else {
980 $error++;
981 }
982 } else {
983 $error++;
984 $langs->load("errors");
985 $this->error = $langs->trans("ErrorCheckVersionIsDefined");
986 }
987
988 return -1;
989 }
990
998 public function compressSyslogs()
999 {
1000 global $conf;
1001
1002 if (empty($conf->loghandlers['mod_syslog_file'])) { // File Syslog disabled
1003 return 0;
1004 }
1005
1006 if (!function_exists('gzopen')) {
1007 $this->error = 'Support for gzopen not available in this PHP';
1008 return -1;
1009 }
1010
1011 require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php';
1012
1013 $nbSaves = intval(getDolGlobalString('SYSLOG_FILE_SAVES', 10));
1014
1015 if (!getDolGlobalString('SYSLOG_FILE')) {
1016 $mainlogdir = DOL_DATA_ROOT;
1017 $mainlog = 'dolibarr.log';
1018 } else {
1019 $mainlogfull = str_replace('DOL_DATA_ROOT', DOL_DATA_ROOT, $conf->global->SYSLOG_FILE);
1020 $mainlogdir = dirname($mainlogfull);
1021 $mainlog = basename($mainlogfull);
1022 }
1023
1024 $tabfiles = dol_dir_list(DOL_DATA_ROOT, 'files', 0, '^(dolibarr_.+|odt2pdf)\.log$'); // Also handle other log files like dolibarr_install.log
1025 $tabfiles[] = array('name' => $mainlog, 'path' => $mainlogdir);
1026
1027 foreach ($tabfiles as $file) {
1028 $logname = $file['name'];
1029 $logpath = $file['path'];
1030
1031 if (dol_is_file($logpath.'/'.$logname) && dol_filesize($logpath.'/'.$logname) > 0) { // If log file exists and is not empty
1032 // Handle already compressed files to rename them and add +1
1033
1034 $filter = '^'.preg_quote($logname, '/').'\.([0-9]+)\.gz$';
1035
1036 $gzfilestmp = dol_dir_list($logpath, 'files', 0, $filter);
1037 $gzfiles = array();
1038
1039 foreach ($gzfilestmp as $gzfile) {
1040 $tabmatches = array();
1041 preg_match('/'.$filter.'/i', $gzfile['name'], $tabmatches);
1042
1043 $numsave = intval($tabmatches[1]);
1044
1045 $gzfiles[$numsave] = $gzfile;
1046 }
1047
1048 krsort($gzfiles, SORT_NUMERIC);
1049
1050 foreach ($gzfiles as $numsave => $dummy) {
1051 if (dol_is_file($logpath.'/'.$logname.'.'.($numsave + 1).'.gz')) {
1052 return -2;
1053 }
1054
1055 if ($numsave >= $nbSaves) {
1056 dol_delete_file($logpath.'/'.$logname.'.'.$numsave.'.gz', 0, 0, 0, null, false, 0);
1057 } else {
1058 dol_move($logpath.'/'.$logname.'.'.$numsave.'.gz', $logpath.'/'.$logname.'.'.($numsave + 1).'.gz', 0, 1, 0, 0);
1059 }
1060 }
1061
1062 // Compress current file and recreate it
1063
1064 if ($nbSaves > 0) { // If $nbSaves is 1, we keep 1 archive .gz file, If 2, we keep 2 .gz files
1065 $gzfilehandle = gzopen($logpath.'/'.$logname.'.1.gz', 'wb9');
1066
1067 if (empty($gzfilehandle)) {
1068 $this->error = 'Failted to open file '.$logpath.'/'.$logname.'.1.gz';
1069 return -3;
1070 }
1071
1072 $sourcehandle = fopen($logpath.'/'.$logname, 'r');
1073
1074 if (empty($sourcehandle)) {
1075 $this->error = 'Failed to open file '.$logpath.'/'.$logname;
1076 return -4;
1077 }
1078
1079 while (!feof($sourcehandle)) {
1080 gzwrite($gzfilehandle, fread($sourcehandle, 512 * 1024)); // Read 512 kB at a time
1081 }
1082
1083 fclose($sourcehandle);
1084 gzclose($gzfilehandle);
1085
1086 dolChmod($logpath.'/'.$logname.'.1.gz');
1087 }
1088
1089 dol_delete_file($logpath.'/'.$logname, 0, 0, 0, null, false, 0);
1090
1091 // Create empty file
1092 $newlog = fopen($logpath.'/'.$logname, 'a+');
1093 fclose($newlog);
1094
1095 //var_dump($logpath.'/'.$logname." - ".octdec(empty($conf->global->MAIN_UMASK)?'0664':$conf->global->MAIN_UMASK));
1096 dolChmod($logpath.'/'.$logname);
1097 }
1098 }
1099
1100 $this->output = 'Archive log files (keeping last SYSLOG_FILE_SAVES='.$nbSaves.' files) done.';
1101 return 0;
1102 }
1103
1114 public function backupTables($outputfile, $tables = '*')
1115 {
1116 global $db, $langs;
1117 global $errormsg;
1118
1119 // Set to UTF-8
1120 if (is_a($db, 'DoliDBMysqli')) {
1122 $db->db->set_charset('utf8');
1123 } else {
1125 $db->query('SET NAMES utf8');
1126 $db->query('SET CHARACTER SET utf8');
1127 }
1128
1129 //get all of the tables
1130 if ($tables == '*') {
1131 $tables = array();
1132 $result = $db->query('SHOW FULL TABLES WHERE Table_type = \'BASE TABLE\'');
1133 while ($row = $db->fetch_row($result)) {
1134 $tables[] = $row[0];
1135 }
1136 } else {
1137 $tables = is_array($tables) ? $tables : explode(',', $tables);
1138 }
1139
1140 //cycle through
1141 $handle = fopen($outputfile, 'w+');
1142 if (fwrite($handle, '') === false) {
1143 $langs->load("errors");
1144 dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
1145 $errormsg = $langs->trans("ErrorFailedToWriteInDir");
1146 return -1;
1147 }
1148
1149 // Print headers and global mysql config vars
1150 $sqlhead = '';
1151 $sqlhead .= "-- ".$db::LABEL." dump via php with Dolibarr ".DOL_VERSION."
1152--
1153-- Host: ".$db->db->host_info." Database: ".$db->database_name."
1154-- ------------------------------------------------------
1155-- Server version ".$db->db->server_info."
1156
1157;
1158;
1159;
1160;
1161;
1162;
1163;
1164;
1165;
1166;
1167
1168";
1169
1170 if (GETPOST("nobin_disable_fk")) {
1171 $sqlhead .= "SET FOREIGN_KEY_CHECKS=0;\n";
1172 }
1173 //$sqlhead .= "SET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";\n";
1174 if (GETPOST("nobin_use_transaction")) {
1175 $sqlhead .= "SET AUTOCOMMIT=0;\nSTART TRANSACTION;\n";
1176 }
1177
1178 fwrite($handle, $sqlhead);
1179
1180 $ignore = '';
1181 if (GETPOST("nobin_sql_ignore")) {
1182 $ignore = 'IGNORE ';
1183 }
1184 $delayed = '';
1185 if (GETPOST("nobin_delayed")) {
1186 $delayed = 'DELAYED ';
1187 }
1188
1189 // Process each table and print their definition + their datas
1190 foreach ($tables as $table) {
1191 // Saving the table structure
1192 fwrite($handle, "\n--\n-- Table structure for table `".$table."`\n--\n");
1193
1194 if (GETPOST("nobin_drop")) {
1195 fwrite($handle, "DROP TABLE IF EXISTS `".$table."`;\n"); // Dropping table if exists prior to re create it
1196 }
1197 fwrite($handle, "/*!40101 SET @saved_cs_client = @@character_set_client */;\n");
1198 fwrite($handle, "/*!40101 SET character_set_client = utf8 */;\n");
1199 $resqldrop = $db->query('SHOW CREATE TABLE '.$table);
1200 $row2 = $db->fetch_row($resqldrop);
1201 if (empty($row2[1])) {
1202 fwrite($handle, "\n-- WARNING: Show create table ".$table." return empty string when it should not.\n");
1203 } else {
1204 fwrite($handle, $row2[1].";\n");
1205 //fwrite($handle,"/*!40101 SET character_set_client = @saved_cs_client */;\n\n");
1206
1207 // Dumping the data (locking the table and disabling the keys check while doing the process)
1208 fwrite($handle, "\n--\n-- Dumping data for table `".$table."`\n--\n");
1209 if (!GETPOST("nobin_nolocks")) {
1210 fwrite($handle, "LOCK TABLES `".$table."` WRITE;\n"); // Lock the table before inserting data (when the data will be imported back)
1211 }
1212 if (GETPOST("nobin_disable_fk")) {
1213 fwrite($handle, "ALTER TABLE `".$table."` DISABLE KEYS;\n");
1214 } else {
1215 fwrite($handle, "/*!40000 ALTER TABLE `".$table."` DISABLE KEYS */;\n");
1216 }
1217
1218 $sql = "SELECT * FROM ".$table; // Here SELECT * is allowed because we don't have definition of columns to take
1219 $result = $db->query($sql);
1220 while ($row = $db->fetch_row($result)) {
1221 // For each row of data we print a line of INSERT
1222 fwrite($handle, "INSERT ".$delayed.$ignore."INTO ".$table." VALUES (");
1223 $columns = count($row);
1224 for ($j = 0; $j < $columns; $j++) {
1225 // Processing each columns of the row to ensure that we correctly save the value (eg: add quotes for string - in fact we add quotes for everything, it's easier)
1226 if ($row[$j] == null && !is_string($row[$j])) {
1227 // IMPORTANT: if the field is NULL we set it NULL
1228 $row[$j] = 'NULL';
1229 } elseif (is_string($row[$j]) && $row[$j] == '') {
1230 // if it's an empty string, we set it as an empty string
1231 $row[$j] = "''";
1232 } elseif (is_numeric($row[$j]) && !strcmp((string) $row[$j], (string) ((float) $row[$j] + 0))) { // test if it's a numeric type and the numeric version ($nb+0) == string version (eg: if we have 01, it's probably not a number but rather a string, else it would not have any leading 0)
1233 // if it's a number, we return it as-is
1234 // $row[$j] = $row[$j];
1235 } else { // else for all other cases we escape the value and put quotes around
1236 $row[$j] = addslashes($row[$j]);
1237 $row[$j] = preg_replace("#\n#", "\\n", $row[$j]);
1238 $row[$j] = "'".$row[$j]."'";
1239 }
1240 }
1241 fwrite($handle, implode(',', $row).");\n");
1242 }
1243 if (GETPOST("nobin_disable_fk")) {
1244 fwrite($handle, "ALTER TABLE `".$table."` ENABLE KEYS;\n"); // Enabling back the keys/index checking
1245 }
1246 if (!GETPOST("nobin_nolocks")) {
1247 fwrite($handle, "UNLOCK TABLES;\n"); // Unlocking the table
1248 }
1249 fwrite($handle, "\n\n\n");
1250 }
1251 }
1252
1253 /* Backup Procedure structure*/
1254 /*
1255 $result = $db->query('SHOW PROCEDURE STATUS');
1256 if ($db->num_rows($result) > 0)
1257 {
1258 while ($row = $db->fetch_row($result)) { $procedures[] = $row[1]; }
1259 foreach($procedures as $proc)
1260 {
1261 fwrite($handle,"DELIMITER $$\n\n");
1262 fwrite($handle,"DROP PROCEDURE IF EXISTS '$name'.'$proc'$$\n");
1263 $resqlcreateproc=$db->query("SHOW CREATE PROCEDURE '$proc'");
1264 $row2 = $db->fetch_row($resqlcreateproc);
1265 fwrite($handle,"\n".$row2[2]."$$\n\n");
1266 fwrite($handle,"DELIMITER ;\n\n");
1267 }
1268 }
1269 */
1270 /* Backup Procedure structure*/
1271
1272 // Write the footer (restore the previous database settings)
1273 $sqlfooter = "\n\n";
1274 if (GETPOST("nobin_use_transaction")) {
1275 $sqlfooter .= "COMMIT;\n";
1276 }
1277 if (GETPOST("nobin_disable_fk")) {
1278 $sqlfooter .= "SET FOREIGN_KEY_CHECKS=1;\n";
1279 }
1280 $sqlfooter .= "\n\n-- Dump completed on ".date('Y-m-d G-i-s');
1281 fwrite($handle, $sqlfooter);
1282
1283 fclose($handle);
1284
1285 return 1;
1286 }
1287
1301 public function sendBackup($sendto = '', $from = '', $subject = '', $message = '', $filename = '', $filter = '', $sizelimit = 100000000)
1302 {
1303 global $conf, $langs;
1304 global $dolibarr_main_url_root;
1305
1306 $filepath = '';
1307 $output = '';
1308 $error = 0;
1309
1310 if (!empty($from)) {
1311 $from = dol_escape_htmltag($from);
1312 } elseif (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
1313 $from = dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL'));
1314 } else {
1315 $error++;
1316 }
1317
1318 if (!empty($sendto)) {
1319 $sendto = dol_escape_htmltag($sendto);
1320 } elseif (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
1321 $from = dol_escape_htmltag(getDolGlobalString('MAIN_INFO_SOCIETE_MAIL'));
1322 } else {
1323 $error++;
1324 }
1325
1326 if (!empty($subject)) {
1327 $subject = dol_escape_htmltag($subject);
1328 } else {
1329 $subject = dol_escape_htmltag($langs->trans('MakeSendLocalDatabaseDumpShort'));
1330 }
1331
1332 if (empty($message)) {
1333 $message = dol_escape_htmltag($langs->trans('MakeSendLocalDatabaseDumpShort'));
1334 }
1335
1336 $tmpfiles = array();
1337 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1338 if ($filename) {
1339 if (dol_is_file($conf->admin->dir_output.'/backup/'.$filename)) {
1340 $tmpfiles = dol_most_recent_file($conf->admin->dir_output.'/backup', $filename);
1341 }
1342 } else {
1343 $tmpfiles = dol_most_recent_file($conf->admin->dir_output.'/backup', $filter);
1344 }
1345 if ($tmpfiles && is_array($tmpfiles)) {
1346 foreach ($tmpfiles as $key => $val) {
1347 if ($key == 'fullname') {
1348 $filepath = array($val);
1349 $filesize = dol_filesize($val);
1350 }
1351 if ($key == 'type') {
1352 $mimetype = array($val);
1353 }
1354 if ($key == 'relativename') {
1355 $filename = array($val);
1356 }
1357 }
1358 }
1359
1360 if ($filepath) {
1361 if ($filesize > $sizelimit) {
1362 $message .= '<br>'.$langs->trans("BackupIsTooLargeSend");
1363 $documenturl = $dolibarr_main_url_root.'/document.php?modulepart=systemtools&atachement=1&file=backup/'.urlencode($filename[0]);
1364 $message .= '<br><a href='.$documenturl.'>Lien de téléchargement</a>';
1365 $filepath = '';
1366 $mimetype = '';
1367 $filename = '';
1368 }
1369 } else {
1370 $output = 'No backup file found';
1371 $error++;
1372 }
1373
1374 if (!$error) {
1375 include_once DOL_DOCUMENT_ROOT . '/core/class/CMailFile.class.php';
1376 $mailfile = new CMailFile($subject, $sendto, $from, $message, $filepath, $mimetype, $filename, '', '', 0, -1);
1377 if ($mailfile->error) {
1378 $error++;
1379 $output = $mailfile->error;
1380 }
1381 }
1382
1383 $result = false;
1384 if (!$error) {
1385 $result = $mailfile->sendfile();
1386 if (!$result) {
1387 $error++;
1388 $output = $mailfile->error;
1389 }
1390 }
1391
1392 dol_syslog(__METHOD__, LOG_DEBUG);
1393
1394 $this->error = "Error sending backp file ".((string) $error);
1395 $this->output = $output;
1396
1397 if ($result) {
1398 return 0;
1399 } else {
1400 return -1;
1401 }
1402 }
1403
1411 public function cleanUnfinishedCronjob()
1412 {
1413 global $db, $user;
1414 dol_syslog("Utils::cleanUnfinishedCronjob Starting cleaning");
1415
1416 // Import Cronjob class if not present
1417 require_once DOL_DOCUMENT_ROOT . '/cron/class/cronjob.class.php';
1418
1419 // Get this job object
1420 $this_job = new Cronjob($db);
1421 $this_job->fetch(-1, 'Utils', 'cleanUnfinishedCronjob');
1422 if (empty($this_job->id) || !empty($this_job->error)) {
1423 dol_syslog("Utils::cleanUnfinishedCronjob Unable to fetch himself: ".$this_job->error, LOG_ERR);
1424 return -1;
1425 }
1426
1427 // Set this job processing to 0 to avoid being locked by his processing state
1428 $this_job->processing = 0;
1429 if ($this_job->update($user) < 0) {
1430 dol_syslog("Utils::cleanUnfinishedCronjob Unable to update himself: ".implode(', ', $this_job->errors), LOG_ERR);
1431 return -1;
1432 }
1433
1434 $cron_job = new Cronjob($db);
1435 $cron_job->fetchAll('DESC', 't.rowid', 100, 0, 1, [], 1); // Fetch jobs that are currently running
1436
1437 // Iterate over all jobs in processing (this can't be this job since his state is set to 0 before)
1438 foreach ($cron_job->lines as $job_line) {
1439 // Avoid job with no PID
1440 if (empty($job_line->pid)) {
1441 dol_syslog("Utils::cleanUnfinishedCronjob Cronjob ".$job_line->id." don't have a PID", LOG_DEBUG);
1442 continue;
1443 }
1444
1445 $job = new Cronjob($db);
1446 $job->fetch($job_line->id);
1447 if (empty($job->id) || !empty($job->error)) {
1448 dol_syslog("Utils::cleanUnfinishedCronjob Cronjob ".$job_line->id." can't be fetch: ".$job->error, LOG_ERR);
1449 continue;
1450 }
1451
1452 // Calling posix_kill with the 0 kill signal will return true if the process is running, false otherwise.
1453 if (! posix_kill($job->pid, 0)) {
1454 // Clean processing and pid values
1455 $job->processing = 0;
1456 $job->pid = null;
1457
1458 // Set last result as an error and add the reason on the last output
1459 $job->lastresult = strval(-1);
1460 $job->lastoutput = 'Job killed by job cleanUnfinishedCronjob';
1461
1462 if ($job->update($user) < 0) {
1463 dol_syslog("Utils::cleanUnfinishedCronjob Cronjob ".$job_line->id." can't be updated: ".implode(', ', $job->errors), LOG_ERR);
1464 continue;
1465 }
1466 dol_syslog("Utils::cleanUnfinishedCronjob Cronjob ".$job_line->id." cleaned");
1467 }
1468 }
1469
1470 dol_syslog("Utils::cleanUnfinishedCronjob Cleaning completed");
1471 return 0;
1472 }
1473}
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Cron Job class.
Class to manage ECM directories.
Class to manage utility methods.
executeCLI($command, $outputfile, $execmethod=0, $redirectionfile=null, $noescapecommand=0, $redirectionfileerr=null)
Execute a CLI command.
__construct($db)
Constructor.
generateDoc($module)
Generate documentation of a Module.
compressSyslogs()
This saves syslog files and compresses older ones.
sendBackup($sendto='', $from='', $subject='', $message='', $filename='', $filter='', $sizelimit=100000000)
Make a send last backup of database or fil in param CAN BE A CRON TASK.
dumpDatabase($compression='none', $type='auto', $usedefault=1, $file='auto', $keeplastnfiles=0, $execmethod=0, $lowmemorydump=0)
Make a backup of database CAN BE A CRON TASK.
cleanUnfinishedCronjob()
Clean unfinished cronjob in processing when pid is no longer present in the system CAN BE A CRON TASK...
purgeFiles($choices='tempfilesold+logfiles', $nbsecondsold=86400)
Purge files into directory of data files.
dol_filesize($pathoffile)
Return size of a file.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_copy($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=0)
Copy a file to another file.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array())
Move a file into another name.
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:63
dol_most_recent_file($dir, $regexfilter='', $excludefilter=array('(\.meta|_preview.*\.png) $', '^\.'), $nohook=0, $mode=0)
Return file(s) into a directory (by default most recent)
dol_is_dir($folder)
Test if filename is a directory.
dolReplaceInFile($srcfile, $arrayreplacement, $destfile='', $newmask='0', $indexdatabase=0, $arrayreplacementisregex=0)
Make replacement of strings into a file.
dolChmod($filepath, $newmask='')
Change mod of a file.
dol_now($mode='auto')
Return date for now.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
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...
dolMd2Asciidoc($content, $parser='dolibarr', $replaceimagepath=null)
Function to parse MD content into ASCIIDOC.