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