utils.class.php 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160
  1. <?php
  2. /* Copyright (C) 2016 Destailleur Laurent <eldy@users.sourceforge.net>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 3 of the License, or
  7. * any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. */
  17. /**
  18. * \file htdocs/core/class/utils.class.php
  19. * \ingroup core
  20. * \brief File for Utils class
  21. */
  22. /**
  23. * Class to manage utility methods
  24. */
  25. class Utils
  26. {
  27. /**
  28. * @var DoliDB Database handler.
  29. */
  30. public $db;
  31. public $output; // Used by Cron method to return message
  32. public $result; // Used by Cron method to return data
  33. /**
  34. * Constructor
  35. *
  36. * @param DoliDB $db Database handler
  37. */
  38. public function __construct($db)
  39. {
  40. $this->db = $db;
  41. }
  42. /**
  43. * Purge files into directory of data files.
  44. * CAN BE A CRON TASK
  45. *
  46. * @param string $choices Choice of purge mode ('tempfiles', 'tempfilesold' to purge temp older than $nbsecondsold seconds, 'logfiles', or mix of this). Note 'allfiles' is possible too but very dangerous.
  47. * @param int $nbsecondsold Nb of seconds old to accept deletion of a directory if $choice is 'tempfilesold'
  48. * @return int 0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK)
  49. */
  50. public function purgeFiles($choices = 'tempfilesold+logfiles', $nbsecondsold = 86400)
  51. {
  52. global $conf, $langs, $dolibarr_main_data_root;
  53. $langs->load("admin");
  54. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  55. if (empty($choices)) {
  56. $choices = 'tempfilesold+logfiles';
  57. }
  58. dol_syslog("Utils::purgeFiles choice=".$choices, LOG_DEBUG);
  59. $count = 0;
  60. $countdeleted = 0;
  61. $counterror = 0;
  62. $filelog = '';
  63. $choicesarray = preg_split('/[\+,]/', $choices);
  64. foreach ($choicesarray as $choice) {
  65. $filesarray = array();
  66. if ($choice == 'tempfiles' || $choice == 'tempfilesold') {
  67. // Delete temporary files
  68. if ($dolibarr_main_data_root) {
  69. $filesarray = dol_dir_list($dolibarr_main_data_root, "directories", 1, '^temp$', '', 'name', SORT_ASC, 2, 0, '', 1); // Do not follow symlinks
  70. if ($choice == 'tempfilesold') {
  71. $now = dol_now();
  72. foreach ($filesarray as $key => $val) {
  73. if ($val['date'] > ($now - ($nbsecondsold))) {
  74. unset($filesarray[$key]); // Discard temp dir not older than $nbsecondsold
  75. }
  76. }
  77. }
  78. }
  79. }
  80. if ($choice == 'allfiles') {
  81. // Delete all files (except install.lock, do not follow symbolic links)
  82. if ($dolibarr_main_data_root) {
  83. $filesarray = dol_dir_list($dolibarr_main_data_root, "all", 0, '', 'install\.lock$', 'name', SORT_ASC, 0, 0, '', 1);
  84. }
  85. }
  86. if ($choice == 'logfile' || $choice == 'logfiles') {
  87. // Define files log
  88. if ($dolibarr_main_data_root) {
  89. $filesarray = dol_dir_list($dolibarr_main_data_root, "files", 0, '.*\.log[\.0-9]*(\.gz)?$', 'install\.lock$', 'name', SORT_ASC, 0, 0, '', 1);
  90. }
  91. if (!empty($conf->syslog->enabled)) {
  92. $filelog = $conf->global->SYSLOG_FILE;
  93. $filelog = preg_replace('/DOL_DATA_ROOT/i', DOL_DATA_ROOT, $filelog);
  94. $alreadyincluded = false;
  95. foreach ($filesarray as $tmpcursor) {
  96. if ($tmpcursor['fullname'] == $filelog) {
  97. $alreadyincluded = true;
  98. }
  99. }
  100. if (!$alreadyincluded) {
  101. $filesarray[] = array('fullname'=>$filelog, 'type'=>'file');
  102. }
  103. }
  104. }
  105. if (is_array($filesarray) && count($filesarray)) {
  106. foreach ($filesarray as $key => $value) {
  107. //print "x ".$filesarray[$key]['fullname']."-".$filesarray[$key]['type']."<br>\n";
  108. if ($filesarray[$key]['type'] == 'dir') {
  109. $startcount = 0;
  110. $tmpcountdeleted = 0;
  111. $result = dol_delete_dir_recursive($filesarray[$key]['fullname'], $startcount, 1, 0, $tmpcountdeleted);
  112. 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
  113. $count += $result;
  114. $countdeleted += $tmpcountdeleted;
  115. }
  116. } elseif ($filesarray[$key]['type'] == 'file') {
  117. // If (file that is not logfile) or (if mode is logfile)
  118. if ($filesarray[$key]['fullname'] != $filelog || $choice == 'logfile' || $choice == 'logfiles') {
  119. $result = dol_delete_file($filesarray[$key]['fullname'], 1, 1);
  120. if ($result) {
  121. $count++;
  122. $countdeleted++;
  123. } else {
  124. $counterror++;
  125. }
  126. }
  127. }
  128. }
  129. // Update cachenbofdoc
  130. if (!empty($conf->ecm->enabled) && $choice == 'allfiles') {
  131. require_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmdirectory.class.php';
  132. $ecmdirstatic = new EcmDirectory($this->db);
  133. $result = $ecmdirstatic->refreshcachenboffile(1);
  134. }
  135. }
  136. }
  137. if ($count > 0) {
  138. $this->output = $langs->trans("PurgeNDirectoriesDeleted", $countdeleted);
  139. if ($count > $countdeleted) {
  140. $this->output .= '<br>'.$langs->trans("PurgeNDirectoriesFailed", ($count - $countdeleted));
  141. }
  142. } else {
  143. $this->output = $langs->trans("PurgeNothingToDelete").(in_array('tempfilesold', $choicesarray) ? ' (older than 24h for temp files)' : '');
  144. }
  145. // Recreate temp dir that are not automatically recreated by core code for performance purpose, we need them
  146. if (!empty($conf->api->enabled)) {
  147. dol_mkdir($conf->api->dir_temp);
  148. }
  149. dol_mkdir($conf->user->dir_temp);
  150. //return $count;
  151. return 0; // This function can be called by cron so must return 0 if OK
  152. }
  153. /**
  154. * Make a backup of database
  155. * CAN BE A CRON TASK
  156. *
  157. * @param string $compression 'gz' or 'bz' or 'none'
  158. * @param string $type 'mysql', 'postgresql', ...
  159. * @param int $usedefault 1=Use default backup profile (Set this to 1 when used as cron)
  160. * @param string $file 'auto' or filename to build
  161. * @param int $keeplastnfiles Keep only last n files (not used yet)
  162. * @param int $execmethod 0=Use default method (that is 1 by default), 1=Use the PHP 'exec', 2=Use the 'popen' method
  163. * @return int 0 if OK, < 0 if KO (this function is used also by cron so only 0 is OK)
  164. */
  165. public function dumpDatabase($compression = 'none', $type = 'auto', $usedefault = 1, $file = 'auto', $keeplastnfiles = 0, $execmethod = 0)
  166. {
  167. global $db, $conf, $langs, $dolibarr_main_data_root;
  168. global $dolibarr_main_db_name, $dolibarr_main_db_host, $dolibarr_main_db_user, $dolibarr_main_db_port, $dolibarr_main_db_pass;
  169. global $dolibarr_main_db_character_set;
  170. $langs->load("admin");
  171. dol_syslog("Utils::dumpDatabase type=".$type." compression=".$compression." file=".$file, LOG_DEBUG);
  172. require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
  173. // Check compression parameter
  174. if (!in_array($compression, array('none', 'gz', 'bz', 'zip'))) {
  175. $langs->load("errors");
  176. $this->error = $langs->transnoentitiesnoconv("ErrorBadValueForParameter", $compression, "Compression");
  177. return -1;
  178. }
  179. // Check type parameter
  180. if ($type == 'auto') {
  181. $type = $this->db->type;
  182. }
  183. if (!in_array($type, array('postgresql', 'pgsql', 'mysql', 'mysqli', 'mysqlnobin'))) {
  184. $langs->load("errors");
  185. $this->error = $langs->transnoentitiesnoconv("ErrorBadValueForParameter", $type, "Basetype");
  186. return -1;
  187. }
  188. // Check file parameter
  189. if ($file == 'auto') {
  190. $prefix = 'dump';
  191. $ext = 'sql';
  192. if (in_array($type, array('mysql', 'mysqli'))) {
  193. $prefix = 'mysqldump';
  194. $ext = 'sql';
  195. }
  196. //if ($label == 'PostgreSQL') { $prefix='pg_dump'; $ext='dump'; }
  197. if (in_array($type, array('pgsql'))) {
  198. $prefix = 'pg_dump';
  199. $ext = 'sql';
  200. }
  201. $file = $prefix.'_'.$dolibarr_main_db_name.'_'.dol_sanitizeFileName(DOL_VERSION).'_'.strftime("%Y%m%d%H%M").'.'.$ext;
  202. }
  203. $outputdir = $conf->admin->dir_output.'/backup';
  204. $result = dol_mkdir($outputdir);
  205. $errormsg = '';
  206. // MYSQL
  207. if ($type == 'mysql' || $type == 'mysqli') {
  208. $cmddump = $conf->global->SYSTEMTOOLS_MYSQLDUMP;
  209. $outputfile = $outputdir.'/'.$file;
  210. // for compression format, we add extension
  211. $compression = $compression ? $compression : 'none';
  212. if ($compression == 'gz') {
  213. $outputfile .= '.gz';
  214. }
  215. if ($compression == 'bz') {
  216. $outputfile .= '.bz2';
  217. }
  218. $outputerror = $outputfile.'.err';
  219. dol_mkdir($conf->admin->dir_output.'/backup');
  220. // Parameteres execution
  221. $command = $cmddump;
  222. $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.
  223. if (preg_match("/\s/", $command)) {
  224. $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
  225. }
  226. //$param=escapeshellarg($dolibarr_main_db_name)." -h ".escapeshellarg($dolibarr_main_db_host)." -u ".escapeshellarg($dolibarr_main_db_user)." -p".escapeshellarg($dolibarr_main_db_pass);
  227. $param = $dolibarr_main_db_name." -h ".$dolibarr_main_db_host;
  228. $param .= " -u ".$dolibarr_main_db_user;
  229. if (!empty($dolibarr_main_db_port)) {
  230. $param .= " -P ".$dolibarr_main_db_port;
  231. }
  232. if (!GETPOST("use_transaction", "alpha")) {
  233. $param .= " -l --single-transaction";
  234. }
  235. if (GETPOST("disable_fk", "alpha") || $usedefault) {
  236. $param .= " -K";
  237. }
  238. if (GETPOST("sql_compat", "alpha") && GETPOST("sql_compat", "alpha") != 'NONE') {
  239. $param .= " --compatible=".escapeshellarg(GETPOST("sql_compat", "alpha"));
  240. }
  241. if (GETPOST("drop_database", "alpha")) {
  242. $param .= " --add-drop-database";
  243. }
  244. if (GETPOST("use_mysql_quick_param", "alpha")) {
  245. $param .= " --quick";
  246. }
  247. if (GETPOST("sql_structure", "alpha") || $usedefault) {
  248. if (GETPOST("drop", "alpha") || $usedefault) {
  249. $param .= " --add-drop-table=TRUE";
  250. } else {
  251. $param .= " --add-drop-table=FALSE";
  252. }
  253. } else {
  254. $param .= " -t";
  255. }
  256. if (GETPOST("disable-add-locks", "alpha")) {
  257. $param .= " --add-locks=FALSE";
  258. }
  259. if (GETPOST("sql_data", "alpha") || $usedefault) {
  260. $param .= " --tables";
  261. if (GETPOST("showcolumns", "alpha") || $usedefault) {
  262. $param .= " -c";
  263. }
  264. if (GETPOST("extended_ins", "alpha") || $usedefault) {
  265. $param .= " -e";
  266. } else {
  267. $param .= " --skip-extended-insert";
  268. }
  269. if (GETPOST("delayed", "alpha")) {
  270. $param .= " --delayed-insert";
  271. }
  272. if (GETPOST("sql_ignore", "alpha")) {
  273. $param .= " --insert-ignore";
  274. }
  275. if (GETPOST("hexforbinary", "alpha") || $usedefault) {
  276. $param .= " --hex-blob";
  277. }
  278. } else {
  279. $param .= " -d"; // No row information (no data)
  280. }
  281. if ($dolibarr_main_db_character_set == 'utf8mb4') {
  282. // We save output into utf8mb4 charset
  283. $param .= " --default-character-set=utf8mb4 --no-tablespaces";
  284. } else {
  285. $param .= " --default-character-set=utf8 --no-tablespaces"; // We always save output into utf8 charset
  286. }
  287. $paramcrypted = $param;
  288. $paramclear = $param;
  289. if (!empty($dolibarr_main_db_pass)) {
  290. $paramcrypted .= ' -p"'.preg_replace('/./i', '*', $dolibarr_main_db_pass).'"';
  291. $paramclear .= ' -p"'.str_replace(array('"', '`', '$'), array('\"', '\`', '\$'), $dolibarr_main_db_pass).'"';
  292. }
  293. $handle = '';
  294. // Start call method to execute dump
  295. $fullcommandcrypted = $command." ".$paramcrypted." 2>&1";
  296. $fullcommandclear = $command." ".$paramclear." 2>&1";
  297. if ($compression == 'none') {
  298. $handle = fopen($outputfile, 'w');
  299. }
  300. if ($compression == 'gz') {
  301. $handle = gzopen($outputfile, 'w');
  302. }
  303. if ($compression == 'bz') {
  304. $handle = bzopen($outputfile, 'w');
  305. }
  306. $ok = 0;
  307. if ($handle) {
  308. if (!empty($conf->global->MAIN_EXEC_USE_POPEN)) {
  309. $execmethod = $conf->global->MAIN_EXEC_USE_POPEN;
  310. }
  311. if (empty($execmethod)) {
  312. $execmethod = 1;
  313. }
  314. dol_syslog("Utils::dumpDatabase execmethod=".$execmethod." command:".$fullcommandcrypted, LOG_INFO);
  315. // TODO Replace with executeCLI function
  316. if ($execmethod == 1) {
  317. $output_arr = array();
  318. $retval = null;
  319. exec($fullcommandclear, $output_arr, $retval);
  320. if ($retval != 0) {
  321. $langs->load("errors");
  322. dol_syslog("Datadump retval after exec=".$retval, LOG_ERR);
  323. $errormsg = 'Error '.$retval;
  324. $ok = 0;
  325. } else {
  326. $i = 0;
  327. if (!empty($output_arr)) {
  328. foreach ($output_arr as $key => $read) {
  329. $i++; // output line number
  330. if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) {
  331. continue;
  332. }
  333. fwrite($handle, $read.($execmethod == 2 ? '' : "\n"));
  334. if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) {
  335. $ok = 1;
  336. } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) {
  337. $ok = 1;
  338. }
  339. }
  340. }
  341. }
  342. }
  343. if ($execmethod == 2) { // With this method, there is no way to get the return code, only output
  344. $handlein = popen($fullcommandclear, 'r');
  345. $i = 0;
  346. while (!feof($handlein)) {
  347. $i++; // output line number
  348. $read = fgets($handlein);
  349. // Exclude warning line we don't want
  350. if ($i == 1 && preg_match('/Warning.*Using a password/i', $read)) {
  351. continue;
  352. }
  353. fwrite($handle, $read);
  354. if (preg_match('/'.preg_quote('-- Dump completed').'/i', $read)) {
  355. $ok = 1;
  356. } elseif (preg_match('/'.preg_quote('SET SQL_NOTES=@OLD_SQL_NOTES').'/i', $read)) {
  357. $ok = 1;
  358. }
  359. }
  360. pclose($handlein);
  361. }
  362. if ($compression == 'none') {
  363. fclose($handle);
  364. }
  365. if ($compression == 'gz') {
  366. gzclose($handle);
  367. }
  368. if ($compression == 'bz') {
  369. bzclose($handle);
  370. }
  371. if (!empty($conf->global->MAIN_UMASK)) {
  372. @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
  373. }
  374. } else {
  375. $langs->load("errors");
  376. dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
  377. $errormsg = $langs->trans("ErrorFailedToWriteInDir");
  378. }
  379. // Get errorstring
  380. if ($compression == 'none') {
  381. $handle = fopen($outputfile, 'r');
  382. }
  383. if ($compression == 'gz') {
  384. $handle = gzopen($outputfile, 'r');
  385. }
  386. if ($compression == 'bz') {
  387. $handle = bzopen($outputfile, 'r');
  388. }
  389. if ($handle) {
  390. // Get 2048 first chars of error message.
  391. $errormsg = fgets($handle, 2048);
  392. //$ok=0;$errormsg=''; To force error
  393. // Close file
  394. if ($compression == 'none') {
  395. fclose($handle);
  396. }
  397. if ($compression == 'gz') {
  398. gzclose($handle);
  399. }
  400. if ($compression == 'bz') {
  401. bzclose($handle);
  402. }
  403. if ($ok && preg_match('/^-- (MySql|MariaDB)/i', $errormsg)) { // No error
  404. $errormsg = '';
  405. } else {
  406. // Renommer fichier sortie en fichier erreur
  407. //print "$outputfile -> $outputerror";
  408. @dol_delete_file($outputerror, 1, 0, 0, null, false, 0);
  409. @rename($outputfile, $outputerror);
  410. // Si safe_mode on et command hors du parametre exec, on a un fichier out vide donc errormsg vide
  411. if (!$errormsg) {
  412. $langs->load("errors");
  413. $errormsg = $langs->trans("ErrorFailedToRunExternalCommand");
  414. }
  415. }
  416. }
  417. // Fin execution commande
  418. $this->output = $errormsg;
  419. $this->error = $errormsg;
  420. $this->result = array("commandbackuplastdone" => $command." ".$paramcrypted, "commandbackuptorun" => "");
  421. //if (empty($this->output)) $this->output=$this->result['commandbackuplastdone'];
  422. }
  423. // MYSQL NO BIN
  424. if ($type == 'mysqlnobin') {
  425. $outputfile = $outputdir.'/'.$file;
  426. $outputfiletemp = $outputfile.'-TMP.sql';
  427. // for compression format, we add extension
  428. $compression = $compression ? $compression : 'none';
  429. if ($compression == 'gz') {
  430. $outputfile .= '.gz';
  431. }
  432. if ($compression == 'bz') {
  433. $outputfile .= '.bz2';
  434. }
  435. $outputerror = $outputfile.'.err';
  436. dol_mkdir($conf->admin->dir_output.'/backup');
  437. if ($compression == 'gz' or $compression == 'bz') {
  438. $this->backupTables($outputfiletemp);
  439. dol_compress_file($outputfiletemp, $outputfile, $compression);
  440. unlink($outputfiletemp);
  441. } else {
  442. $this->backupTables($outputfile);
  443. }
  444. $this->output = "";
  445. $this->result = array("commandbackuplastdone" => "", "commandbackuptorun" => "");
  446. }
  447. // POSTGRESQL
  448. if ($type == 'postgresql' || $type == 'pgsql') {
  449. $cmddump = $conf->global->SYSTEMTOOLS_POSTGRESQLDUMP;
  450. $outputfile = $outputdir.'/'.$file;
  451. // for compression format, we add extension
  452. $compression = $compression ? $compression : 'none';
  453. if ($compression == 'gz') {
  454. $outputfile .= '.gz';
  455. }
  456. if ($compression == 'bz') {
  457. $outputfile .= '.bz2';
  458. }
  459. $outputerror = $outputfile.'.err';
  460. dol_mkdir($conf->admin->dir_output.'/backup');
  461. // Parameteres execution
  462. $command = $cmddump;
  463. $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.
  464. if (preg_match("/\s/", $command)) {
  465. $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
  466. }
  467. //$param=escapeshellarg($dolibarr_main_db_name)." -h ".escapeshellarg($dolibarr_main_db_host)." -u ".escapeshellarg($dolibarr_main_db_user)." -p".escapeshellarg($dolibarr_main_db_pass);
  468. //$param="-F c";
  469. $param = "-F p";
  470. $param .= " --no-tablespaces --inserts -h ".$dolibarr_main_db_host;
  471. $param .= " -U ".$dolibarr_main_db_user;
  472. if (!empty($dolibarr_main_db_port)) {
  473. $param .= " -p ".$dolibarr_main_db_port;
  474. }
  475. if (GETPOST("sql_compat") && GETPOST("sql_compat") == 'ANSI') {
  476. $param .= " --disable-dollar-quoting";
  477. }
  478. if (GETPOST("drop_database")) {
  479. $param .= " -c -C";
  480. }
  481. if (GETPOST("sql_structure")) {
  482. if (GETPOST("drop")) {
  483. $param .= " --add-drop-table";
  484. }
  485. if (!GETPOST("sql_data")) {
  486. $param .= " -s";
  487. }
  488. }
  489. if (GETPOST("sql_data")) {
  490. if (!GETPOST("sql_structure")) {
  491. $param .= " -a";
  492. }
  493. if (GETPOST("showcolumns")) {
  494. $param .= " -c";
  495. }
  496. }
  497. $param .= ' -f "'.$outputfile.'"';
  498. //if ($compression == 'none')
  499. if ($compression == 'gz') {
  500. $param .= ' -Z 9';
  501. }
  502. //if ($compression == 'bz')
  503. $paramcrypted = $param;
  504. $paramclear = $param;
  505. /*if (! empty($dolibarr_main_db_pass))
  506. {
  507. $paramcrypted.=" -W".preg_replace('/./i','*',$dolibarr_main_db_pass);
  508. $paramclear.=" -W".$dolibarr_main_db_pass;
  509. }*/
  510. $paramcrypted .= " -w ".$dolibarr_main_db_name;
  511. $paramclear .= " -w ".$dolibarr_main_db_name;
  512. $this->output = "";
  513. $this->result = array("commandbackuplastdone" => "", "commandbackuptorun" => $command." ".$paramcrypted);
  514. }
  515. // Clean old files
  516. if (!$errormsg && $keeplastnfiles > 0) {
  517. $tmpfiles = dol_dir_list($conf->admin->dir_output.'/backup', 'files', 0, '', '(\.err|\.old|\.sav)$', 'date', SORT_DESC);
  518. $i = 0;
  519. foreach ($tmpfiles as $key => $val) {
  520. $i++;
  521. if ($i <= $keeplastnfiles) {
  522. continue;
  523. }
  524. dol_delete_file($val['fullname'], 0, 0, 0, null, false, 0);
  525. }
  526. }
  527. return ($errormsg ? -1 : 0);
  528. }
  529. /**
  530. * Execute a CLI command.
  531. *
  532. * @param string $command Command line to execute.
  533. * Warning: The command line is sanitize so can't contains any redirection char '>'. Use param $redirectionfile if you need it.
  534. * @param string $outputfile A path for an output file (used only when method is 2). For example: $conf->admin->dir_temp.'/out.tmp';
  535. * @param int $execmethod 0=Use default method (that is 1 by default), 1=Use the PHP 'exec', 2=Use the 'popen' method
  536. * @param string $redirectionfile If defined, a redirection of output to this files is added.
  537. * @param int $noescapecommand 1=Do not escape command. Warning: Using this parameter need you alreay sanitized the command. if not, it will lead to security vulnerability.
  538. * This parameter is provided for backward compatibility with external modules. Always use 0 in core.
  539. * @return array array('result'=>...,'output'=>...,'error'=>...). result = 0 means OK.
  540. */
  541. public function executeCLI($command, $outputfile, $execmethod = 0, $redirectionfile = null, $noescapecommand = 0)
  542. {
  543. global $conf, $langs;
  544. $result = 0;
  545. $output = '';
  546. $error = '';
  547. if (empty($noescapecommand)) {
  548. $command = escapeshellcmd($command);
  549. }
  550. if ($redirectionfile) {
  551. $command .= " > ".dol_sanitizePathName($redirectionfile);
  552. }
  553. $command .= " 2>&1";
  554. if (!empty($conf->global->MAIN_EXEC_USE_POPEN)) {
  555. $execmethod = $conf->global->MAIN_EXEC_USE_POPEN;
  556. }
  557. if (empty($execmethod)) {
  558. $execmethod = 1;
  559. }
  560. //$execmethod=1;
  561. dol_syslog("Utils::executeCLI execmethod=".$execmethod." system:".$command, LOG_DEBUG);
  562. $output_arr = array();
  563. if ($execmethod == 1) {
  564. $retval = null;
  565. exec($command, $output_arr, $retval);
  566. $result = $retval;
  567. if ($retval != 0) {
  568. $langs->load("errors");
  569. dol_syslog("Utils::executeCLI retval after exec=".$retval, LOG_ERR);
  570. $error = 'Error '.$retval;
  571. }
  572. }
  573. if ($execmethod == 2) { // With this method, there is no way to get the return code, only output
  574. $handle = fopen($outputfile, 'w+b');
  575. if ($handle) {
  576. dol_syslog("Utils::executeCLI run command ".$command);
  577. $handlein = popen($command, 'r');
  578. while (!feof($handlein)) {
  579. $read = fgets($handlein);
  580. fwrite($handle, $read);
  581. $output_arr[] = $read;
  582. }
  583. pclose($handlein);
  584. fclose($handle);
  585. }
  586. if (!empty($conf->global->MAIN_UMASK)) {
  587. @chmod($outputfile, octdec($conf->global->MAIN_UMASK));
  588. }
  589. }
  590. // Update with result
  591. if (is_array($output_arr) && count($output_arr) > 0) {
  592. foreach ($output_arr as $val) {
  593. $output .= $val.($execmethod == 2 ? '' : "\n");
  594. }
  595. }
  596. dol_syslog("Utils::executeCLI result=".$result." output=".$output." error=".$error, LOG_DEBUG);
  597. return array('result'=>$result, 'output'=>$output, 'error'=>$error);
  598. }
  599. /**
  600. * Generate documentation of a Module
  601. *
  602. * @param string $module Module name
  603. * @return int <0 if KO, >0 if OK
  604. */
  605. public function generateDoc($module)
  606. {
  607. global $conf, $langs, $user, $mysoc;
  608. global $dirins;
  609. $error = 0;
  610. $modulelowercase = strtolower($module);
  611. $now = dol_now();
  612. // Dir for module
  613. $dir = $dirins.'/'.$modulelowercase;
  614. // Zip file to build
  615. $FILENAMEDOC = '';
  616. // Load module
  617. dol_include_once($modulelowercase.'/core/modules/mod'.$module.'.class.php');
  618. $class = 'mod'.$module;
  619. if (class_exists($class)) {
  620. try {
  621. $moduleobj = new $class($this->db);
  622. } catch (Exception $e) {
  623. $error++;
  624. dol_print_error($e->getMessage());
  625. }
  626. } else {
  627. $error++;
  628. $langs->load("errors");
  629. dol_print_error($langs->trans("ErrorFailedToLoadModuleDescriptorForXXX", $module));
  630. exit;
  631. }
  632. $arrayversion = explode('.', $moduleobj->version, 3);
  633. if (count($arrayversion)) {
  634. $FILENAMEASCII = strtolower($module).'.asciidoc';
  635. $FILENAMEDOC = strtolower($module).'.html';
  636. $FILENAMEDOCPDF = strtolower($module).'.pdf';
  637. $dirofmodule = dol_buildpath(strtolower($module), 0);
  638. $dirofmoduledoc = dol_buildpath(strtolower($module), 0).'/doc';
  639. $dirofmoduletmp = dol_buildpath(strtolower($module), 0).'/doc/temp';
  640. $outputfiledoc = $dirofmoduledoc.'/'.$FILENAMEDOC;
  641. if ($dirofmoduledoc) {
  642. if (!dol_is_dir($dirofmoduledoc)) {
  643. dol_mkdir($dirofmoduledoc);
  644. }
  645. if (!dol_is_dir($dirofmoduletmp)) {
  646. dol_mkdir($dirofmoduletmp);
  647. }
  648. if (!is_writable($dirofmoduletmp)) {
  649. $this->error = 'Dir '.$dirofmoduletmp.' does not exists or is not writable';
  650. return -1;
  651. }
  652. if (empty($conf->global->MODULEBUILDER_ASCIIDOCTOR) && empty($conf->global->MODULEBUILDER_ASCIIDOCTORPDF)) {
  653. $this->error = 'Setup of module ModuleBuilder not complete';
  654. return -1;
  655. }
  656. // Copy some files into temp directory, so instruction include::ChangeLog.md[] will works inside the asciidoc file.
  657. dol_copy($dirofmodule.'/README.md', $dirofmoduletmp.'/README.md', 0, 1);
  658. dol_copy($dirofmodule.'/ChangeLog.md', $dirofmoduletmp.'/ChangeLog.md', 0, 1);
  659. // Replace into README.md and ChangeLog.md (in case they are included into documentation with tag __README__ or __CHANGELOG__)
  660. $arrayreplacement = array();
  661. $arrayreplacement['/^#\s.*/m'] = ''; // Remove first level of title into .md files
  662. $arrayreplacement['/^#/m'] = '##'; // Add on # to increase level
  663. dolReplaceInFile($dirofmoduletmp.'/README.md', $arrayreplacement, '', 0, 0, 1);
  664. dolReplaceInFile($dirofmoduletmp.'/ChangeLog.md', $arrayreplacement, '', 0, 0, 1);
  665. $destfile = $dirofmoduletmp.'/'.$FILENAMEASCII;
  666. $fhandle = fopen($destfile, 'w+');
  667. if ($fhandle) {
  668. $specs = dol_dir_list(dol_buildpath(strtolower($module).'/doc', 0), 'files', 1, '(\.md|\.asciidoc)$', array('\/temp\/'));
  669. $i = 0;
  670. foreach ($specs as $spec) {
  671. if (preg_match('/notindoc/', $spec['relativename'])) {
  672. continue; // Discard file
  673. }
  674. if (preg_match('/example/', $spec['relativename'])) {
  675. continue; // Discard file
  676. }
  677. if (preg_match('/disabled/', $spec['relativename'])) {
  678. continue; // Discard file
  679. }
  680. $pathtofile = strtolower($module).'/doc/'.$spec['relativename'];
  681. $format = 'asciidoc';
  682. if (preg_match('/\.md$/i', $spec['name'])) {
  683. $format = 'markdown';
  684. }
  685. $filecursor = @file_get_contents($spec['fullname']);
  686. if ($filecursor) {
  687. fwrite($fhandle, ($i ? "\n<<<\n\n" : "").$filecursor."\n");
  688. } else {
  689. $this->error = 'Failed to concat content of file '.$spec['fullname'];
  690. return -1;
  691. }
  692. $i++;
  693. }
  694. fclose($fhandle);
  695. $contentreadme = file_get_contents($dirofmoduletmp.'/README.md');
  696. $contentchangelog = file_get_contents($dirofmoduletmp.'/ChangeLog.md');
  697. include DOL_DOCUMENT_ROOT.'/core/lib/parsemd.lib.php';
  698. //var_dump($phpfileval['fullname']);
  699. $arrayreplacement = array(
  700. 'mymodule'=>strtolower($module),
  701. 'MyModule'=>$module,
  702. 'MYMODULE'=>strtoupper($module),
  703. 'My module'=>$module,
  704. 'my module'=>$module,
  705. 'Mon module'=>$module,
  706. 'mon module'=>$module,
  707. 'htdocs/modulebuilder/template'=>strtolower($module),
  708. '__MYCOMPANY_NAME__'=>$mysoc->name,
  709. '__KEYWORDS__'=>$module,
  710. '__USER_FULLNAME__'=>$user->getFullName($langs),
  711. '__USER_EMAIL__'=>$user->email,
  712. '__YYYY-MM-DD__'=>dol_print_date($now, 'dayrfc'),
  713. '---Put here your own copyright and developer email---'=>dol_print_date($now, 'dayrfc').' '.$user->getFullName($langs).($user->email ? ' <'.$user->email.'>' : ''),
  714. '__DATA_SPECIFICATION__'=>'Not yet available',
  715. '__README__'=>dolMd2Asciidoc($contentreadme),
  716. '__CHANGELOG__'=>dolMd2Asciidoc($contentchangelog),
  717. );
  718. dolReplaceInFile($destfile, $arrayreplacement);
  719. }
  720. // Launch doc generation
  721. $currentdir = getcwd();
  722. chdir($dirofmodule);
  723. require_once DOL_DOCUMENT_ROOT.'/core/class/utils.class.php';
  724. $utils = new Utils($this->db);
  725. // Build HTML doc
  726. $command = $conf->global->MODULEBUILDER_ASCIIDOCTOR.' '.$destfile.' -n -o '.$dirofmoduledoc.'/'.$FILENAMEDOC;
  727. $outfile = $dirofmoduletmp.'/out.tmp';
  728. $resarray = $utils->executeCLI($command, $outfile);
  729. if ($resarray['result'] != '0') {
  730. $this->error = $resarray['error'].' '.$resarray['output'];
  731. }
  732. $result = ($resarray['result'] == 0) ? 1 : 0;
  733. // Build PDF doc
  734. $command = $conf->global->MODULEBUILDER_ASCIIDOCTORPDF.' '.$destfile.' -n -o '.$dirofmoduledoc.'/'.$FILENAMEDOCPDF;
  735. $outfile = $dirofmoduletmp.'/outpdf.tmp';
  736. $resarray = $utils->executeCLI($command, $outfile);
  737. if ($resarray['result'] != '0') {
  738. $this->error = $resarray['error'].' '.$resarray['output'];
  739. }
  740. $result = ($resarray['result'] == 0) ? 1 : 0;
  741. chdir($currentdir);
  742. } else {
  743. $result = 0;
  744. }
  745. if ($result > 0) {
  746. return 1;
  747. } else {
  748. $error++;
  749. $langs->load("errors");
  750. $this->error = $langs->trans("ErrorFailToGenerateFile", $outputfiledoc);
  751. }
  752. } else {
  753. $error++;
  754. $langs->load("errors");
  755. $this->error = $langs->trans("ErrorCheckVersionIsDefined");
  756. }
  757. return -1;
  758. }
  759. /**
  760. * This saves syslog files and compresses older ones.
  761. * Nb of archive to keep is defined into $conf->global->SYSLOG_FILE_SAVES
  762. * CAN BE A CRON TASK
  763. *
  764. * @return int 0 if OK, < 0 if KO
  765. */
  766. public function compressSyslogs()
  767. {
  768. global $conf;
  769. if (empty($conf->loghandlers['mod_syslog_file'])) { // File Syslog disabled
  770. return 0;
  771. }
  772. if (!function_exists('gzopen')) {
  773. $this->error = 'Support for gzopen not available in this PHP';
  774. return -1;
  775. }
  776. dol_include_once('/core/lib/files.lib.php');
  777. $nbSaves = empty($conf->global->SYSLOG_FILE_SAVES) ? 10 : intval($conf->global->SYSLOG_FILE_SAVES);
  778. if (empty($conf->global->SYSLOG_FILE)) {
  779. $mainlogdir = DOL_DATA_ROOT;
  780. $mainlog = 'dolibarr.log';
  781. } else {
  782. $mainlogfull = str_replace('DOL_DATA_ROOT', DOL_DATA_ROOT, $conf->global->SYSLOG_FILE);
  783. $mainlogdir = dirname($mainlogfull);
  784. $mainlog = basename($mainlogfull);
  785. }
  786. $tabfiles = dol_dir_list(DOL_DATA_ROOT, 'files', 0, '^(dolibarr_.+|odt2pdf)\.log$'); // Also handle other log files like dolibarr_install.log
  787. $tabfiles[] = array('name' => $mainlog, 'path' => $mainlogdir);
  788. foreach ($tabfiles as $file) {
  789. $logname = $file['name'];
  790. $logpath = $file['path'];
  791. if (dol_is_file($logpath.'/'.$logname) && dol_filesize($logpath.'/'.$logname) > 0) { // If log file exists and is not empty
  792. // Handle already compressed files to rename them and add +1
  793. $filter = '^'.preg_quote($logname, '/').'\.([0-9]+)\.gz$';
  794. $gzfilestmp = dol_dir_list($logpath, 'files', 0, $filter);
  795. $gzfiles = array();
  796. foreach ($gzfilestmp as $gzfile) {
  797. $tabmatches = array();
  798. preg_match('/'.$filter.'/i', $gzfile['name'], $tabmatches);
  799. $numsave = intval($tabmatches[1]);
  800. $gzfiles[$numsave] = $gzfile;
  801. }
  802. krsort($gzfiles, SORT_NUMERIC);
  803. foreach ($gzfiles as $numsave => $dummy) {
  804. if (dol_is_file($logpath.'/'.$logname.'.'.($numsave + 1).'.gz')) {
  805. return -2;
  806. }
  807. if ($numsave >= $nbSaves) {
  808. dol_delete_file($logpath.'/'.$logname.'.'.$numsave.'.gz', 0, 0, 0, null, false, 0);
  809. } else {
  810. dol_move($logpath.'/'.$logname.'.'.$numsave.'.gz', $logpath.'/'.$logname.'.'.($numsave + 1).'.gz', 0, 1, 0, 0);
  811. }
  812. }
  813. // Compress current file and recreate it
  814. if ($nbSaves > 0) { // If $nbSaves is 1, we keep 1 archive .gz file, If 2, we keep 2 .gz files
  815. $gzfilehandle = gzopen($logpath.'/'.$logname.'.1.gz', 'wb9');
  816. if (empty($gzfilehandle)) {
  817. $this->error = 'Failted to open file '.$logpath.'/'.$logname.'.1.gz';
  818. return -3;
  819. }
  820. $sourcehandle = fopen($logpath.'/'.$logname, 'r');
  821. if (empty($sourcehandle)) {
  822. $this->error = 'Failed to open file '.$logpath.'/'.$logname;
  823. return -4;
  824. }
  825. while (!feof($sourcehandle)) {
  826. gzwrite($gzfilehandle, fread($sourcehandle, 512 * 1024)); // Read 512 kB at a time
  827. }
  828. fclose($sourcehandle);
  829. gzclose($gzfilehandle);
  830. @chmod($logpath.'/'.$logname.'.1.gz', octdec(empty($conf->global->MAIN_UMASK) ? '0664' : $conf->global->MAIN_UMASK));
  831. }
  832. dol_delete_file($logpath.'/'.$logname, 0, 0, 0, null, false, 0);
  833. // Create empty file
  834. $newlog = fopen($logpath.'/'.$logname, 'a+');
  835. fclose($newlog);
  836. //var_dump($logpath.'/'.$logname." - ".octdec(empty($conf->global->MAIN_UMASK)?'0664':$conf->global->MAIN_UMASK));
  837. @chmod($logpath.'/'.$logname, octdec(empty($conf->global->MAIN_UMASK) ? '0664' : $conf->global->MAIN_UMASK));
  838. }
  839. }
  840. $this->output = 'Archive log files (keeping last SYSLOG_FILE_SAVES='.$nbSaves.' files) done.';
  841. return 0;
  842. }
  843. /** Backup the db OR just a table without mysqldump binary, with PHP only (does not require any exec permission)
  844. * Author: David Walsh (http://davidwalsh.name/backup-mysql-database-php)
  845. * Updated and enhanced by Stephen Larroque (lrq3000) and by the many commentators from the blog
  846. * Note about foreign keys constraints: for Dolibarr, since there are a lot of constraints and when imported the tables will be inserted in the dumped order, not in constraints order, then we ABSOLUTELY need to use SET FOREIGN_KEY_CHECKS=0; when importing the sql dump.
  847. * Note2: db2SQL by Howard Yeend can be an alternative, by using SHOW FIELDS FROM and SHOW KEYS FROM we could generate a more precise dump (eg: by getting the type of the field and then precisely outputting the right formatting - in quotes, numeric or null - instead of trying to guess like we are doing now).
  848. *
  849. * @param string $outputfile Output file name
  850. * @param string $tables Table name or '*' for all
  851. * @return int <0 if KO, >0 if OK
  852. */
  853. public function backupTables($outputfile, $tables = '*')
  854. {
  855. global $db, $langs;
  856. global $errormsg;
  857. // Set to UTF-8
  858. if (is_a($db, 'DoliDBMysqli')) {
  859. /** @var DoliDBMysqli $db */
  860. $db->db->set_charset('utf8');
  861. } else {
  862. /** @var DoliDB $db */
  863. $db->query('SET NAMES utf8');
  864. $db->query('SET CHARACTER SET utf8');
  865. }
  866. //get all of the tables
  867. if ($tables == '*') {
  868. $tables = array();
  869. $result = $db->query('SHOW FULL TABLES WHERE Table_type = \'BASE TABLE\'');
  870. while ($row = $db->fetch_row($result)) {
  871. $tables[] = $row[0];
  872. }
  873. } else {
  874. $tables = is_array($tables) ? $tables : explode(',', $tables);
  875. }
  876. //cycle through
  877. $handle = fopen($outputfile, 'w+');
  878. if (fwrite($handle, '') === false) {
  879. $langs->load("errors");
  880. dol_syslog("Failed to open file ".$outputfile, LOG_ERR);
  881. $errormsg = $langs->trans("ErrorFailedToWriteInDir");
  882. return -1;
  883. }
  884. // Print headers and global mysql config vars
  885. $sqlhead = '';
  886. $sqlhead .= "-- ".$db::LABEL." dump via php with Dolibarr ".DOL_VERSION."
  887. --
  888. -- Host: ".$db->db->host_info." Database: ".$db->database_name."
  889. -- ------------------------------------------------------
  890. -- Server version ".$db->db->server_info."
  891. /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
  892. /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
  893. /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
  894. /*!40101 SET NAMES utf8 */;
  895. /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
  896. /*!40103 SET TIME_ZONE='+00:00' */;
  897. /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
  898. /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
  899. /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
  900. /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
  901. ";
  902. if (GETPOST("nobin_disable_fk")) {
  903. $sqlhead .= "SET FOREIGN_KEY_CHECKS=0;\n";
  904. }
  905. //$sqlhead .= "SET SQL_MODE=\"NO_AUTO_VALUE_ON_ZERO\";\n";
  906. if (GETPOST("nobin_use_transaction")) {
  907. $sqlhead .= "SET AUTOCOMMIT=0;\nSTART TRANSACTION;\n";
  908. }
  909. fwrite($handle, $sqlhead);
  910. $ignore = '';
  911. if (GETPOST("nobin_sql_ignore")) {
  912. $ignore = 'IGNORE ';
  913. }
  914. $delayed = '';
  915. if (GETPOST("nobin_delayed")) {
  916. $delayed = 'DELAYED ';
  917. }
  918. // Process each table and print their definition + their datas
  919. foreach ($tables as $table) {
  920. // Saving the table structure
  921. fwrite($handle, "\n--\n-- Table structure for table `".$table."`\n--\n");
  922. if (GETPOST("nobin_drop")) {
  923. fwrite($handle, "DROP TABLE IF EXISTS `".$table."`;\n"); // Dropping table if exists prior to re create it
  924. }
  925. fwrite($handle, "/*!40101 SET @saved_cs_client = @@character_set_client */;\n");
  926. fwrite($handle, "/*!40101 SET character_set_client = utf8 */;\n");
  927. $resqldrop = $db->query('SHOW CREATE TABLE '.$table);
  928. $row2 = $db->fetch_row($resqldrop);
  929. if (empty($row2[1])) {
  930. fwrite($handle, "\n-- WARNING: Show create table ".$table." return empy string when it should not.\n");
  931. } else {
  932. fwrite($handle, $row2[1].";\n");
  933. //fwrite($handle,"/*!40101 SET character_set_client = @saved_cs_client */;\n\n");
  934. // Dumping the data (locking the table and disabling the keys check while doing the process)
  935. fwrite($handle, "\n--\n-- Dumping data for table `".$table."`\n--\n");
  936. if (!GETPOST("nobin_nolocks")) {
  937. fwrite($handle, "LOCK TABLES `".$table."` WRITE;\n"); // Lock the table before inserting data (when the data will be imported back)
  938. }
  939. if (GETPOST("nobin_disable_fk")) {
  940. fwrite($handle, "ALTER TABLE `".$table."` DISABLE KEYS;\n");
  941. } else {
  942. fwrite($handle, "/*!40000 ALTER TABLE `".$table."` DISABLE KEYS */;\n");
  943. }
  944. $sql = "SELECT * FROM ".$table; // Here SELECT * is allowed because we don't have definition of columns to take
  945. $result = $db->query($sql);
  946. while ($row = $db->fetch_row($result)) {
  947. // For each row of data we print a line of INSERT
  948. fwrite($handle, "INSERT ".$delayed.$ignore."INTO ".$table." VALUES (");
  949. $columns = count($row);
  950. for ($j = 0; $j < $columns; $j++) {
  951. // 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)
  952. if ($row[$j] == null && !is_string($row[$j])) {
  953. // IMPORTANT: if the field is NULL we set it NULL
  954. $row[$j] = 'NULL';
  955. } elseif (is_string($row[$j]) && $row[$j] == '') {
  956. // if it's an empty string, we set it as an empty string
  957. $row[$j] = "''";
  958. } 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)
  959. // if it's a number, we return it as-is
  960. // $row[$j] = $row[$j];
  961. } else { // else for all other cases we escape the value and put quotes around
  962. $row[$j] = addslashes($row[$j]);
  963. $row[$j] = preg_replace("#\n#", "\\n", $row[$j]);
  964. $row[$j] = "'".$row[$j]."'";
  965. }
  966. }
  967. fwrite($handle, implode(',', $row).");\n");
  968. }
  969. if (GETPOST("nobin_disable_fk")) {
  970. fwrite($handle, "ALTER TABLE `".$table."` ENABLE KEYS;\n"); // Enabling back the keys/index checking
  971. }
  972. if (!GETPOST("nobin_nolocks")) {
  973. fwrite($handle, "UNLOCK TABLES;\n"); // Unlocking the table
  974. }
  975. fwrite($handle, "\n\n\n");
  976. }
  977. }
  978. /* Backup Procedure structure*/
  979. /*
  980. $result = $db->query('SHOW PROCEDURE STATUS');
  981. if ($db->num_rows($result) > 0)
  982. {
  983. while ($row = $db->fetch_row($result)) { $procedures[] = $row[1]; }
  984. foreach($procedures as $proc)
  985. {
  986. fwrite($handle,"DELIMITER $$\n\n");
  987. fwrite($handle,"DROP PROCEDURE IF EXISTS '$name'.'$proc'$$\n");
  988. $resqlcreateproc=$db->query("SHOW CREATE PROCEDURE '$proc'");
  989. $row2 = $db->fetch_row($resqlcreateproc);
  990. fwrite($handle,"\n".$row2[2]."$$\n\n");
  991. fwrite($handle,"DELIMITER ;\n\n");
  992. }
  993. }
  994. */
  995. /* Backup Procedure structure*/
  996. // Write the footer (restore the previous database settings)
  997. $sqlfooter = "\n\n";
  998. if (GETPOST("nobin_use_transaction")) {
  999. $sqlfooter .= "COMMIT;\n";
  1000. }
  1001. if (GETPOST("nobin_disable_fk")) {
  1002. $sqlfooter .= "SET FOREIGN_KEY_CHECKS=1;\n";
  1003. }
  1004. $sqlfooter .= "\n\n-- Dump completed on ".date('Y-m-d G-i-s');
  1005. fwrite($handle, $sqlfooter);
  1006. fclose($handle);
  1007. return 1;
  1008. }
  1009. }