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