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