PageRenderTime 175ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/engine/PerlLib/Addons/phpmyadmin/installer.pm

https://bitbucket.org/droidzone/i-mscp
Perl | 573 lines | 419 code | 115 blank | 39 comment | 59 complexity | b29e4016274923f9b92292fc7424cf08 MD5 | raw file
  1. #!/usr/bin/perl
  2. =head1 NAME
  3. Addons::phpmyadmin::installer - i-MSCP PhpMyAdmin addon installer
  4. =cut
  5. # i-MSCP - internet Multi Server Control Panel
  6. # Copyright (C) 2010-2013 by internet Multi Server Control Panel
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License
  10. # as published by the Free Software Foundation; either version 2
  11. # of the License, or (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  21. #
  22. # @category i-MSCP
  23. # @copyright 2010-2013 by i-MSCP | http://i-mscp.net
  24. # @author Laurent Declercq <l.declercq@nuxwin.com>
  25. # @link http://i-mscp.net i-MSCP Home Site
  26. # @license http://www.gnu.org/licenses/gpl-2.0.html GPL v2
  27. package Addons::phpmyadmin::installer;
  28. use strict;
  29. use warnings;
  30. use iMSCP::Debug;
  31. use iMSCP::HooksManager;
  32. use parent 'Common::SingletonClass';
  33. =head1 DESCRIPTION
  34. This is the installer for the i-MSCP PhpMyAdmin addon.
  35. See Addons::phpmyadmin for more information.
  36. =head1 PUBLIC METHODS
  37. =over 4
  38. =item registerSetupHooks(HooksManager)
  39. Register PhpMyAdmin setup hook functions.
  40. Param iMSCP::HooksManager instance
  41. Return int 0 on success, 1 on failure
  42. =cut
  43. sub registerSetupHooks
  44. {
  45. my $self = shift;
  46. my $hooksManager = shift;
  47. # Add phpmyadmin installer dialog in setup dialog stack
  48. $hooksManager->register(
  49. 'beforeSetupDialog', sub { my $dialogStack = shift; push(@$dialogStack, sub { $self->askPhpmyadmin(@_) }); 0; }
  50. );
  51. }
  52. =item preinstall()
  53. Register PhpMyAdmin composer package for installation.
  54. Return int 0 on success, other on failure
  55. =cut
  56. sub preinstall
  57. {
  58. my $self = shift;
  59. require iMSCP::Addons::ComposerInstaller;
  60. iMSCP::Addons::ComposerInstaller->getInstance()->registerPackage('imscp/phpmyadmin');
  61. }
  62. =item install()
  63. Process PhpMyAdmin addon install tasks.
  64. Return int 0 on success, 1 on failure
  65. =cut
  66. sub install
  67. {
  68. my $self = shift;
  69. my $rs = 0;
  70. # Backup current configuration file if it exists (only relevant when running imscp-setup)
  71. $rs = $self->_backupConfigFile(
  72. "$main::imscpConfig{'GUI_PUBLIC_DIR'}/$self::phpmyadminConfig{'PHPMYADMIN_CONF_DIR'}/config.inc.php"
  73. );
  74. return $rs if $rs;
  75. $rs = $self->_installFiles(); # Install phpmyadmin files from local addon packages repository
  76. return $rs if $rs;
  77. $rs = $self->setGuiPermissions(); # Set phpmyadmin permissions
  78. return $rs if $rs;
  79. $rs = $self->_setupSqlUser(); # Setup phpmyadmin restricted SQL user
  80. return $rs if $rs;
  81. $rs = $self->_generateBlowfishSecret(); # Generate Blowfish secret
  82. return $rs if $rs;
  83. $rs = $self->_buildConfig(); # Build new configuration files
  84. return $rs if $rs;
  85. $self->_saveConfig(); # Save configuration
  86. }
  87. =back
  88. =head1 HOOK FUNCTIONS
  89. =over 4
  90. =item askPhpmyadmin()
  91. Show PhpMyAdmin questions.
  92. Hook function responsible to show PhpMyAdmin installer questions.
  93. Param iMSCP::Dialog
  94. Return int 0 or 30
  95. =cut
  96. sub askPhpmyadmin
  97. {
  98. my $self = shift;
  99. my $dialog = shift;
  100. my $dbType = main::setupGetQuestion('DATABASE_TYPE');
  101. my $dbHost = main::setupGetQuestion('DATABASE_HOST');
  102. my $dbPort = main::setupGetQuestion('DATABASE_PORT');
  103. my $dbName = main::setupGetQuestion('DATABASE_NAME');
  104. my $dbUser = $main::preseed{'PHPMYADMIN_SQL_USER'} || $self::phpmyadminConfig{'DATABASE_USER'} ||
  105. $self::phpmyadminOldConfig{'DATABASE_USER'} || 'pma';
  106. my $dbPass = $main::preseed{'PHPMYADMIN_SQL_PASSWORD'} || $self::phpmyadminConfig{'DATABASE_PASSWORD'} ||
  107. $self::phpmyadminOldConfig{'DATABASE_PASSWORD'} || '';
  108. my ($rs, $msg) = (0, '');
  109. if(
  110. $main::reconfigure ~~ ['sqlmanager', 'all', 'forced'] ||
  111. (
  112. ! $main::preseed{'PHPMYADMIN_SQL_USER'} &&
  113. main::setupCheckSqlConnect($dbType, '', $dbHost, $dbPort, $dbUser, $dbPass)
  114. )
  115. ) {
  116. # Ask for the phpmyadmin restricted SQL username
  117. do{
  118. ($rs, $dbUser) = iMSCP::Dialog->factory()->inputbox(
  119. "\nPlease enter an username for the restricted phpmyadmin SQL user:", $dbUser
  120. );
  121. # i-MSCP SQL user cannot be reused
  122. if($dbUser eq $main::imscpConfig{'DATABASE_USER'}){
  123. $msg = "\n\n\\Z1You cannot reuse the i-MSCP SQL user '$dbUser'.\\Zn\n\nPlease, try again:";
  124. $dbUser = '';
  125. }
  126. } while ($rs != 30 && ! $dbUser);
  127. if($rs != 30) {
  128. # Ask for the phpmyadmin restricted SQL user password
  129. ($rs, $dbPass) = $dialog->inputbox(
  130. '\nPlease, enter a password for the restricted phpmyadmin SQL user (blank for autogenerate):', $dbPass
  131. );
  132. if($rs != 30) {
  133. if(! $dbPass) {
  134. $dbPass = '';
  135. my @allowedChars = ('A'..'Z', 'a'..'z', '0'..'9', '_');
  136. $dbPass .= $allowedChars[rand()*($#allowedChars + 1)]for (1..16);
  137. }
  138. $dbPass =~ s/('|"|`|#|;|\/|\s|\||<|\?|\\)/_/g;
  139. $dialog->msgbox("\nPassword for the restricted phpmyadmin SQL user set to: $dbPass");
  140. $dialog->set('cancel-label');
  141. }
  142. }
  143. }
  144. if($rs != 30) {
  145. $self::phpmyadminConfig{'DATABASE_USER'} = $dbUser;
  146. $self::phpmyadminConfig{'DATABASE_PASSWORD'} = $dbPass;
  147. }
  148. $rs;
  149. }
  150. =item setGuiPermissions()
  151. Set PhpMyAdmin files permissions.
  152. Return int 0 on success, other on failure
  153. =cut
  154. sub setGuiPermissions
  155. {
  156. my $self = shift;
  157. my $panelUName = $main::imscpConfig{'SYSTEM_USER_PREFIX'} . $main::imscpConfig{'SYSTEM_USER_MIN_UID'};
  158. my $rootDir = $main::imscpConfig{'ROOT_DIR'};
  159. require Servers::httpd;
  160. my $http = Servers::httpd->factory();
  161. my $apacheGName = $http->can('getRunningGroup') ? $http->getRunningGroup() : $main::imscpConfig{'ROOT_GROUP'};
  162. require iMSCP::Rights;
  163. iMSCP::Rights->import();
  164. setRights(
  165. "$rootDir/gui/public/tools/pma",
  166. { 'user' => $panelUName, 'group' => $apacheGName, 'dirmode' => '0550', 'filemode' => '0440', 'recursive' => 'yes' }
  167. );
  168. }
  169. =back
  170. =head1 PRIVATE METHODS
  171. =over 4
  172. =item _init()
  173. Called by getInstance(). Initialize PhpMyAdmin addon installer instance.
  174. Return Addons::phpmyadmin::installer
  175. =cut
  176. sub _init
  177. {
  178. my $self = shift;
  179. $self->{'cfgDir'} = "$main::imscpConfig{'CONF_DIR'}/pma";
  180. $self->{'bkpDir'} = "$self->{'cfgDir'}/backup";
  181. $self->{'wrkDir'} = "$self->{'cfgDir'}/working";
  182. my $conf = "$self->{'cfgDir'}/phpmyadmin.data";
  183. my $oldConf = "$self->{'cfgDir'}/phpmyadmin.old.data";
  184. tie %self::phpmyadminConfig, 'iMSCP::Config','fileName' => $conf, noerrors => 1;
  185. if(-f $oldConf) {
  186. tie %self::phpmyadminOldConfig, 'iMSCP::Config','fileName' => $oldConf, noerrors => 1;
  187. %self::phpmyadminConfig = (%self::phpmyadminConfig, %self::phpmyadminOldConfig);
  188. }
  189. $self;
  190. }
  191. =item _backupConfigFile()
  192. Backup the given PhpMyAdmin configuration file.
  193. Return int 0
  194. =cut
  195. sub _backupConfigFile
  196. {
  197. my $self = shift;
  198. my $cfgFile = shift;
  199. my $timestamp = time;
  200. my $rs = 0;
  201. require File::Basename;
  202. File::Basename->import();
  203. my ($name, $path, $suffix) = fileparse($cfgFile);
  204. if(-f $cfgFile) {
  205. require iMSCP::File;
  206. my $file = iMSCP::File->new(filename => $cfgFile);
  207. $rs = $file->copyFile("$self->{'bkpDir'}/$name$suffix.$timestamp");
  208. return $rs if $rs;
  209. }
  210. 0;
  211. }
  212. =item _installFiles()
  213. Install PhpMyAdmin files in production directory.
  214. Return int 0 on success, other on failure
  215. =cut
  216. sub _installFiles
  217. {
  218. my $self = shift;
  219. my $repoDir = $main::imscpConfig{'ADDON_PACKAGES_CACHE_DIR'};
  220. my ($stdout, $stderr) = (undef, undef);
  221. my $rs = 0;
  222. if(-d "$repoDir/vendor/imscp/phpmyadmin") {
  223. require iMSCP::Execute;
  224. iMSCP::Execute->import();
  225. $rs = execute(
  226. "$main::imscpConfig{'CMD_CP'} -rTf $repoDir/vendor/imscp/phpmyadmin $main::imscpConfig{'GUI_PUBLIC_DIR'}/tools/pma",
  227. \$stdout,
  228. \$stderr
  229. );
  230. debug($stdout) if $stdout;
  231. error($stderr) if $rs && $stderr;
  232. return $rs if $rs;
  233. $rs = execute(
  234. "$main::imscpConfig{'CMD_RM'} -rf $main::imscpConfig{'GUI_PUBLIC_DIR'}/tools/pma/.git",
  235. \$stdout,
  236. \$stderr
  237. );
  238. debug($stdout) if $stdout;
  239. error($stderr) if $rs && $stderr;
  240. return $rs if $rs;
  241. } else {
  242. error("Couldn't find the imscp/phpmyadmin package into the local repository");
  243. $rs = 1;
  244. }
  245. $rs;
  246. }
  247. =item _saveConfig()
  248. Save PhpMyAdmin configuration.
  249. Return int 0 on success, 1 on failure
  250. =cut
  251. sub _saveConfig
  252. {
  253. my $self = shift;
  254. my $rs = 0;
  255. require iMSCP::File;
  256. my $file = iMSCP::File->new('filename' => "$self->{'cfgDir'}/phpmyadmin.data");
  257. $rs = $file->owner($main::imscpConfig{'ROOT_USER'}, $main::imscpConfig{'ROOT_GROUP'});
  258. return $rs if $rs;
  259. $rs = $file->mode(0640);
  260. return $rs if $rs;
  261. my $cfg = $file->get();
  262. unless(defined $cfg) {
  263. error("Unable to read $self->{'cfgDir'}/phpmyadmin.data");
  264. return 1;
  265. }
  266. $file = iMSCP::File->new('filename' => "$self->{'cfgDir'}/phpmyadmin.old.data");
  267. $rs = $file->set($cfg);
  268. return $rs if $rs;
  269. $rs = $file->save();
  270. return $rs if $rs;
  271. $file->owner($main::imscpConfig{'ROOT_USER'}, $main::imscpConfig{'ROOT_GROUP'});
  272. return $rs if $rs;
  273. $rs = $file->mode(0640);
  274. }
  275. =item _setupSqlUser()
  276. Setup PhpMyAdmin restricted SQL user.
  277. Return int 0 on success, 1 on failure
  278. =cut
  279. sub _setupSqlUser
  280. {
  281. my $self = shift;
  282. my $dbUser = $self::phpmyadminConfig{'DATABASE_USER'};
  283. my $dbOldUser = $self::phpmyadminOldConfig{'DATABASE_USER'} || '';
  284. my $dbPass = $self::phpmyadminConfig{'DATABASE_PASSWORD'};
  285. my $dbUserHost = $main::imscpConfig{'SQL_SERVER'} ne 'remote_server'
  286. ? $main::imscpConfig{'DATABASE_HOST'} : $main::imscpConfig{'BASE_SERVER_IP'};
  287. my $rs = 0;
  288. # Remove old phpmyadmin restricted SQL user and all it privileges (if any)
  289. for($main::imscpOldConfig{'DATABASE_HOST'} || '', $main::imscpOldConfig{'BASE_SERVER_IP'} || '') {
  290. next if $_ eq '' || $dbOldUser eq '';
  291. $rs = main::setupDeleteSqlUser($dbOldUser, $_);
  292. error("Unable to remove the old phpmyadmin '$dbOldUser' restricted SQL user") if $rs;
  293. return 1 if $rs;
  294. }
  295. # Ensure new phpmyadmin restricted SQL user do not already exists by removing it
  296. $rs = main::setupDeleteSqlUser($dbUser, $dbUserHost);
  297. error("Unable to delete the phpmyadmin '$dbUser' restricted SQL user") if $rs;
  298. return 1 if $rs;
  299. # Get SQL connection with full privileges
  300. my ($database, $errStr) = main::setupGetSqlConnect();
  301. fatal('Unable to connect to SQL Server: $errStr') if ! $database;
  302. # Add new phpmyadmin restricted SQL user with needed privileges
  303. # Add USAGE privilege on the mysql database (also create PhpMyAdmin user)
  304. $rs = $database->doQuery('dummy', 'GRANT USAGE ON `mysql`.* TO ?@? IDENTIFIED BY ?', $dbUser, $dbUserHost, $dbPass);
  305. if(ref $rs ne 'HASH') {
  306. error("Failed to add USAGE privilege on the 'mysql' database for the '$dbUser' SQL user: $rs");
  307. return 1;
  308. }
  309. # Add SELECT privilege on the mysql.db table
  310. $rs = $database->doQuery('dummy', 'GRANT SELECT ON `mysql`.`db` TO ?@?', $dbUser, $dbUserHost);
  311. if(ref $rs ne 'HASH') {
  312. error("Failed to add SELECT privilege on the 'mysql.db' table for the '$dbUser' SQL user: $rs");
  313. return 1;
  314. }
  315. # Add SELECT privilege on many columns of the mysql.user table
  316. $rs = $database->doQuery(
  317. 'dummy',
  318. '
  319. GRANT SELECT (Host, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv,
  320. Reload_priv, Shutdown_priv, Process_priv, File_priv, Grant_priv, References_priv, Index_priv,
  321. Alter_priv, Show_db_priv, Super_priv, Create_tmp_table_priv, Lock_tables_priv, Execute_priv,
  322. Repl_slave_priv, Repl_client_priv)
  323. ON `mysql`.`user`
  324. TO ?@?
  325. ',
  326. $dbUser, $dbUserHost
  327. );
  328. if(ref $rs ne 'HASH') {
  329. error("Failed to add SELECT privileges on columns of the 'mysql.user' table for the '$dbUser' SQL user: $rs");
  330. return 1;
  331. }
  332. # Add SELECT privilege on the mysql.host table
  333. $rs = $database->doQuery('dummy', 'GRANT SELECT ON `mysql`.`host` TO ?@?', $dbUser, $dbUserHost);
  334. if(ref $rs ne 'HASH') {
  335. error("Failed to add SELECT privilege on the 'mysql.host' table for the '$dbUser' SQL user: $rs");
  336. return 1;
  337. }
  338. # Add SELECT privilege on many columns of the mysql.tables_priv table
  339. $rs = $database->doQuery(
  340. 'dummy',
  341. '
  342. GRANT SELECT (`Host`, `Db`, `User`, `Table_name`, `Table_priv`, `Column_priv`)
  343. ON `mysql`.`tables_priv`
  344. TO?@?
  345. ',
  346. $dbUser,
  347. $dbUserHost
  348. );
  349. if(ref $rs ne 'HASH') {
  350. error("Failed to add SELECT privilege on columns of the 'mysql.tables_priv' table for the '$dbUser' SQL user: $rs");
  351. return 1;
  352. }
  353. 0;
  354. }
  355. =item _generateBlowfishSecret()
  356. Generate blowfish secret for PhpMyAdmin.
  357. Return int 0
  358. =cut
  359. sub _generateBlowfishSecret
  360. {
  361. my $self = shift;
  362. $self::phpmyadminConfig{'BLOWFISH_SECRET'} = $self::phpmyadminOldConfig{'BLOWFISH_SECRET'}
  363. if ! $self::phpmyadminConfig{'BLOWFISH_SECRET'} && $self::phpmyadminOldConfig{'BLOWFISH_SECRET'};
  364. unless($self::phpmyadminConfig{'BLOWFISH_SECRET'}) {
  365. my $blowfishSecret = '';
  366. my @allowedChars = ('A'..'Z', 'a'..'z', '0'..'9', '_');
  367. $blowfishSecret .= $allowedChars[rand()*($#allowedChars + 1)] for (1..31);
  368. $self::phpmyadminConfig{'BLOWFISH_SECRET'} = $blowfishSecret;
  369. }
  370. 0;
  371. }
  372. =item _buildConfig()
  373. Build PhpMyAdmin configuration file.
  374. Return int 0 on success, 1 on failure
  375. =cut
  376. sub _buildConfig
  377. {
  378. my $self = shift;
  379. my $panelUName = $main::imscpConfig{'SYSTEM_USER_PREFIX'} . $main::imscpConfig{'SYSTEM_USER_MIN_UID'};
  380. my $panelGName = $main::imscpConfig{'SYSTEM_USER_PREFIX'} . $main::imscpConfig{'SYSTEM_USER_MIN_UID'};
  381. my $confDir = "$main::imscpConfig{'GUI_PUBLIC_DIR'}/$self::phpmyadminConfig{'PHPMYADMIN_CONF_DIR'}";
  382. my $rs = 0;
  383. my $cfg = {
  384. PMA_USER => $self::phpmyadminConfig{'DATABASE_USER'},
  385. PMA_PASS => $self::phpmyadminConfig{'DATABASE_PASSWORD'},
  386. HOSTNAME => $main::imscpConfig{'DATABASE_HOST'},
  387. PORT => $main::imscpConfig{'DATABASE_PORT'},
  388. UPLOADS_DIR => "$main::imscpConfig{'GUI_ROOT_DIR'}/data/uploads",
  389. TMP_DIR => "$main::imscpConfig{'GUI_ROOT_DIR'}/data/tmp",
  390. BLOWFISH => $self::phpmyadminConfig{'BLOWFISH_SECRET'},
  391. };
  392. require iMSCP::File;
  393. my $file = iMSCP::File->new(filename => "$confDir/imscp.config.inc.php");
  394. my $cfgTpl = $file->get();
  395. return 1 if ! defined $cfgTpl;
  396. require iMSCP::Templator;
  397. $cfgTpl = iMSCP::Templator::process($cfg, $cfgTpl);
  398. return 1 if ! $cfgTpl;
  399. # store file in working directory
  400. $file = iMSCP::File->new(filename => "$self->{'wrkDir'}/$_");
  401. $rs = $file->set($cfgTpl);
  402. return $rs if $rs;
  403. $rs = $file->save();
  404. return $rs if $rs;
  405. $rs = $file->mode(0640);
  406. return $rs if $rs;
  407. $rs = $file->owner($panelUName, $panelGName);
  408. return $rs if $rs;
  409. # Install new file in production directory
  410. $file->copyFile("$confDir/config.inc.php");
  411. }
  412. =back
  413. =head1 AUTHOR
  414. Laurent Declercq <l.declercq@nuxwin.com>
  415. =cut
  416. 1;