From 2ec5595277eb0e3c5a4a3e042b5ffac04fa4d065 Mon Sep 17 00:00:00 2001
From: Laurent Destailleur <eldy@destailleur.fr>
Date: Sat, 4 Apr 2015 00:19:03 +0200
Subject: [PATCH] NEW Can install an external module from admin pages, if web
 server has permission for and if setup is ok for.

---
 htdocs/admin/tools/update.php                 | 164 +++++++++++++++---
 htdocs/core/lib/functions.lib.php             |   7 +-
 .../modules/syslog/mod_syslog_chromephp.php   |   2 +-
 htdocs/langs/en_US/admin.lang                 |   7 +-
 4 files changed, 150 insertions(+), 30 deletions(-)

diff --git a/htdocs/admin/tools/update.php b/htdocs/admin/tools/update.php
index 7c85f820eab..27799ff1e93 100644
--- a/htdocs/admin/tools/update.php
+++ b/htdocs/admin/tools/update.php
@@ -29,6 +29,8 @@ include_once DOL_DOCUMENT_ROOT . '/core/lib/geturl.lib.php';
 $langs->load("admin");
 $langs->load("other");
 
+$action=GETPOST('action','alpha');
+
 if (! $user->admin) accessforbidden();
 
 if (GETPOST('msg','alpha')) {
@@ -43,14 +45,18 @@ $dolibarrroot=preg_replace('/([\\/]+)$/i','',DOL_DOCUMENT_ROOT);
 $dolibarrroot=preg_replace('/([^\\/]+)$/i','',$dolibarrroot);
 $dolibarrdataroot=preg_replace('/([\\/]+)$/i','',DOL_DATA_ROOT);
 
+$dirins=DOL_DOCUMENT_ROOT.'/custom';
+
+
 /*
  *	Actions
  */
 
-if (GETPOST('action','alpha')=='install')
+if ($action=='install')
 {
 	$error=0;
 
+	// $original_file should match format module_modulename-x.y[.z].zip
 	$original_file=basename($_FILES["fileinstall"]["name"]);
 	$newfile=$conf->admin->dir_temp.'/'.$original_file.'/'.$original_file;
 
@@ -72,33 +78,87 @@ if (GETPOST('action','alpha')=='install')
 
 	if (! $error)
 	{
-		@dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$original_file);
-		dol_mkdir($conf->admin->dir_temp.'/'.$original_file);
+		if ($original_file)
+		{
+			@dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$original_file);
+			dol_mkdir($conf->admin->dir_temp.'/'.$original_file);
+		}
+
+		$tmpdir=preg_replace('/\.zip$/','',$original_file).'.dir';
+		if ($tmpdir)
+		{
+			@dol_delete_dir_recursive($conf->admin->dir_temp.'/'.$tmpdir);
+			dol_mkdir($conf->admin->dir_temp.'/'.$tmpdir);
+		}
 
 		$result=dol_move_uploaded_file($_FILES['fileinstall']['tmp_name'],$newfile,1,0,$_FILES['fileinstall']['error']);
 		if ($result > 0)
 		{
-			$documentrootalt=DOL_DOCUMENT_ROOT.'/extensions';
-			$result=dol_uncompress($newfile,$documentrootalt);
+			$result=dol_uncompress($newfile,$conf->admin->dir_temp.'/'.$tmpdir);
+
 			if (! empty($result['error']))
 			{
 				$langs->load("errors");
 				setEventMessage($langs->trans($result['error'],$original_file), 'errors');
+				$error++;
 			}
 			else
 			{
-				setEventMessage($langs->trans("SetupIsReadyForUse"));
+				// Now we move the dir of the module
+				$modulename=preg_replace('/module_/', '', $original_file);
+				$modulename=preg_replace('/\-[\d]+\.[\d]+.*$/', '', $modulename);
+				// Search dir $modulename
+				$modulenamedir=$conf->admin->dir_temp.'/'.$tmpdir.'/'.$modulename;
+				//var_dump($modulenamedir);
+				if (! dol_is_dir($modulenamedir))
+				{
+					$modulenamedir=$conf->admin->dir_temp.'/'.$tmpdir.'/htdocs/'.$modulename;
+					//var_dump($modulenamedir);
+					if (! dol_is_dir($modulenamedir))
+					{
+						setEventMessage($langs->trans("ErrorModuleFileSeemsToHaveAWrongFormat"), 'errors');
+						$error++;
+					}
+				}
+
+				if (! $error)
+				{
+					//var_dump($dirins);
+					@dol_delete_dir_recursive($dirins.'/'.$modulename);
+					$result=dolCopyDir($modulenamedir, $dirins.'/'.$modulename, '0444', 1);
+					if ($result <= 0)
+					{
+						setEventMessage($langs->trans("ErrorFailedToCopy"), 'errors');
+						$error++;
+					}
+				}
 			}
 		}
+		else
+		{
+			$error++;
+		}
+	}
+
+	if (! $error)
+	{
+		setEventMessage($langs->trans("SetupIsReadyForUse"));
 	}
 }
 
+
 /*
  * View
  */
 
-$dirins=DOL_DOCUMENT_ROOT.'/extensions';
-$dirins_ok=(is_dir($dirins));
+
+
+// Set dir where external modules are installed
+if (! dol_is_dir($dirins))
+{
+	dol_mkdir($dirins);
+}
+$dirins_ok=(dol_is_dir($dirins));
 
 $wikihelp='EN:Installation_-_Upgrade|FR:Installation_-_Mise_à_jour|ES:Instalación_-_Actualización';
 llxHeader('',$langs->trans("Upgrade"),$wikihelp);
@@ -143,6 +203,8 @@ else
 }
 print '<br>';
 
+
+// Upgrade
 print $langs->trans("Upgrade").'<br>';
 print '<hr>';
 print $langs->trans("ThisIsProcessToFollow").'<br>';
@@ -162,20 +224,76 @@ print $langs->trans("RestoreLock",$dolibarrdataroot.'/install.lock').'<br>';
 print '<br>';
 print '<br>';
 
+
+// Install external module
+
+$allowonlineinstall=true;
+$allowfromweb=1;
+if (dol_is_file($dolibarrdataroot.'/installmodules.lock')) $allowonlineinstall=false;
+
 $fullurl='<a href="'.$urldolibarrmodules.'" target="_blank">'.$urldolibarrmodules.'</a>';
+$message='';
+if (! empty($allowonlineinstall))
+{
+	if (! in_array('/custom',explode(',',$dolibarr_main_url_root_alt)))
+	{
+		$message=info_admin($langs->trans("ConfFileMuseContainCustom", DOL_DOCUMENT_ROOT.'/custom', DOL_DOCUMENT_ROOT));
+		$allowfromweb=-1;
+	}
+	else
+	{
+		if ($dirins_ok)
+		{
+			if (! is_writable(dol_osencode($dirins)))
+			{
+				$langs->load("errors");
+				$message=info_admin($langs->trans("ErrorFailedToWriteInDir",$dirins));
+				$allowfromweb=0;
+			}
+		}
+		else
+		{
+
+			$message=info_admin($langs->trans("NotExistsDirect",$dirins).$langs->trans("InfDirAlt").$langs->trans("InfDirExample"));
+			$allowfromweb=0;
+		}
+	}
+}
+else
+{
+	$message=info_admin($langs->trans("InstallModuleFromWebHasBeenDisabledByFile",$dolibarrdataroot.'/installmodules.lock'));
+	$allowfromweb=0;
+}
+
+
+
+
+
 print $langs->trans("AddExtensionThemeModuleOrOther").'<br>';
 print '<hr>';
-print $langs->trans("ThisIsProcessToFollow").'<br>';
-print '<b>'.$langs->trans("StepNb",1).'</b>: ';
-print $langs->trans("FindPackageFromWebSite",$fullurl).'<br>';
-print '<b>'.$langs->trans("StepNb",2).'</b>: ';
-print $langs->trans("DownloadPackageFromWebSite",$fullurl).'<br>';
-print '<b>'.$langs->trans("StepNb",3).'</b>: ';
-print $langs->trans("UnpackPackageInDolibarrRoot",$dolibarrroot).'<br>';
-if (! empty($conf->global->MAIN_ONLINE_INSTALL_MODULE))
+
+if ($allowfromweb < 1)
+{
+	print $langs->trans("SomethingMakeInstallFromWebNotPossible");
+	print $message;
+	//print $langs->trans("SomethingMakeInstallFromWebNotPossible2");
+	print '<br>';
+}
+
+
+if ($allowfromweb >= 0)
 {
-	if ($dirins_ok)
+	if ($allowfromweb == 1) print $langs->trans("ThisIsProcessToFollow").'<br>';
+	else print $langs->trans("ThisIsAlternativeProcessToFollow").'<br>';
+	print '<b>'.$langs->trans("StepNb",1).'</b>: ';
+	print $langs->trans("FindPackageFromWebSite",$fullurl).'<br>';
+	print '<b>'.$langs->trans("StepNb",2).'</b>: ';
+	print $langs->trans("DownloadPackageFromWebSite",$fullurl).'<br>';
+	print '<b>'.$langs->trans("StepNb",3).'</b>: ';
+
+	if ($allowfromweb == 1)
 	{
+		print $langs->trans("UnpackPackageInDolibarrRoot",$dirins).'<br>';
 		print '<form enctype="multipart/form-data" method="POST" class="noborder" action="'.$_SERVER["PHP_SELF"].'" name="forminstall">';
 		print '<input type="hidden" name="action" value="install">';
 		print $langs->trans("YouCanSubmitFile").' <input type="file" name="fileinstall"> ';
@@ -184,16 +302,12 @@ if (! empty($conf->global->MAIN_ONLINE_INSTALL_MODULE))
 	}
 	else
 	{
-		$message=info_admin($langs->trans("NotExistsDirect",$dirins).$langs->trans("InfDirAlt").$langs->trans("InfDirExample"));
-		setEventMessage($message, 'warnings');
+		print $langs->trans("UnpackPackageInDolibarrRoot",$dirins).'<br>';
+		print '<b>'.$langs->trans("StepNb",4).'</b>: ';
+		print $langs->trans("SetupIsReadyForUse").'<br>';
 	}
 }
-else
-{
-	print '<b>'.$langs->trans("StepNb",4).'</b>: ';
-	print $langs->trans("SetupIsReadyForUse").'<br>';
-}
-print '</form>';
+
 
 if (! empty($result['return']))
 {
diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php
index f687d79bcd1..d2465afff22 100644
--- a/htdocs/core/lib/functions.lib.php
+++ b/htdocs/core/lib/functions.lib.php
@@ -3728,9 +3728,10 @@ function get_exdir($num,$level=3,$alpha=0,$withoutslash=0)
  *
  *	@param	string	$dir		Directory to create (Separator must be '/'. Example: '/mydir/mysubdir')
  *	@param	string	$dataroot	Data root directory (To avoid having the data root in the loop. Using this will also lost the warning on first dir PHP has no permission when open_basedir is used)
+ *  @param	int		$newmask	Mask for new file (0 by default means $conf->global->MAIN_UMASK). Example: '0444'
  *	@return int         		< 0 if KO, 0 = already exists, > 0 if OK
  */
-function dol_mkdir($dir, $dataroot='')
+function dol_mkdir($dir, $dataroot='', $newmask=0)
 {
 	global $conf;
 
@@ -3767,8 +3768,8 @@ function dol_mkdir($dir, $dataroot='')
 				dol_syslog("functions.lib::dol_mkdir: Directory '".$ccdir."' does not exists or is outside open_basedir PHP setting.",LOG_DEBUG);
 
 				umask(0);
-				$dirmaskdec=octdec('0755');
-				if (! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK);
+				$dirmaskdec=octdec($newmask);
+				if (empty($newmask) && ! empty($conf->global->MAIN_UMASK)) $dirmaskdec=octdec($conf->global->MAIN_UMASK);
 				$dirmaskdec |= octdec('0111');  // Set x bit required for directories
 				if (! @mkdir($ccdir_osencoded, $dirmaskdec))
 				{
diff --git a/htdocs/core/modules/syslog/mod_syslog_chromephp.php b/htdocs/core/modules/syslog/mod_syslog_chromephp.php
index 3e24901f5af..9ea39caaf50 100644
--- a/htdocs/core/modules/syslog/mod_syslog_chromephp.php
+++ b/htdocs/core/modules/syslog/mod_syslog_chromephp.php
@@ -55,7 +55,7 @@ class mod_syslog_chromephp extends LogHandler implements LogHandlerInterface
 			set_include_path($conf->global->SYSLOG_CHROMEPHP_INCLUDEPATH);
 
 			//print 'rrrrr'.get_include_path();
-		    $res = include_once('ChromePhp.php');
+		    $res = @include_once('ChromePhp.php');
 		    if (! $res) $res=@include_once('ChromePhp.class.php');
 
 		    restore_include_path();
diff --git a/htdocs/langs/en_US/admin.lang b/htdocs/langs/en_US/admin.lang
index b24e108c80f..984b9717dab 100755
--- a/htdocs/langs/en_US/admin.lang
+++ b/htdocs/langs/en_US/admin.lang
@@ -297,10 +297,11 @@ MenuHandlers=Menu handlers
 MenuAdmin=Menu editor
 DoNotUseInProduction=Do not use in production
 ThisIsProcessToFollow=This is setup to process:
+ThisIsAlternativeProcessToFollow=This is an alternative setup to process:
 StepNb=Step %s
 FindPackageFromWebSite=Find a package that provides feature you want (for example on official web site %s).
 DownloadPackageFromWebSite=Download package %s.
-UnpackPackageInDolibarrRoot=Unpack package file into Dolibarr's root directory <b>%s</b>
+UnpackPackageInDolibarrRoot=Unpack package file into directory dedicated to external modules: <b>%s</b>
 SetupIsReadyForUse=Install is finished and Dolibarr is ready to use with this new component.
 NotExistsDirect=The alternative root directory is not defined.<br>
 InfDirAlt=Since version 3 it is possible to define an alternative root directory.This allows you to store, same place, plug-ins and custom templates.<br>Just create a directory at the root of Dolibarr (eg: custom).<br>
@@ -1617,3 +1618,7 @@ ListOfFixedNotifications=List of fixed notifications
 GoOntoContactCardToAddMore=Go on the tab "Notifications" of a thirdparty contact to add or remove notifications for contacts/addresses
 Threshold=Threshold
 BackupDumpWizard=Wizard to build database backup dump file
+SomethingMakeInstallFromWebNotPossible=Installation of external module is not possible from the web interface for the following reason:
+SomethingMakeInstallFromWebNotPossible2=For this reason, process to upgrade described here is only manual steps a privileged user can do. 
+InstallModuleFromWebHasBeenDisabledByFile=Install of external module from application has been disabled by your administrator. You must ask him to remove the file <strong>%s</strong> to allow this feature.
+ConfFileMuseContainCustom=Installing an external module from application save the module files into directory <strong>%s</strong>. To have this directory processed by Dolibarr, you must setup your <strong>conf/conf.php</strong> to have option<br>- <strong>$dolibarr_main_url_root_alt</strong> enabled to value <strong>$dolibarr_main_url_root_alt="/custom"</strong><br>- <strong>$dolibarr_main_document_root_alt</strong> enabled to value <strong>"%s/custom"</strong> 
\ No newline at end of file
-- 
GitLab