diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/PhpVersion.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/PhpVersion.cs new file mode 100644 index 00000000..5648842e --- /dev/null +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/PhpVersion.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2014, Outercurve Foundation. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// - Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// - Neither the name of the Outercurve Foundation nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +using System; +using System.Net; +using System.Text; +using System.Xml.Serialization; + +namespace WebsitePanel.Providers.Web +{ + public class PhpVersion + { + public string HandlerName { get; set; } + public string Version { get; set; } + public string ExecutionPath { get; set; } + } +} diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/WebVirtualDirectory.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/WebVirtualDirectory.cs index 912d58f6..244b8940 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/WebVirtualDirectory.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/Web/WebVirtualDirectory.cs @@ -81,6 +81,7 @@ namespace WebsitePanel.Providers.Web private bool sharePointInstalled; private bool iis7; private string consoleUrl; + private string php5VersionsInstalled; public string AnonymousUsername { @@ -285,7 +286,13 @@ namespace WebsitePanel.Providers.Web set { consoleUrl = value; } } - #region Web Deploy Publishing Properties + public string Php5VersionsInstalled + { + get { return php5VersionsInstalled; } + set { php5VersionsInstalled = value; } + } + + #region Web Deploy Publishing Properties /// /// Gets or sets Web Deploy publishing account name /// diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj index 2ab48c12..3f7049a0 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -329,6 +329,7 @@ + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Handlers/HandlersModuleService.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Handlers/HandlersModuleService.cs index 12a7b902..d2a7c783 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Handlers/HandlersModuleService.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Handlers/HandlersModuleService.cs @@ -26,6 +26,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +using System.Linq; + namespace WebsitePanel.Providers.Web.Handlers { using System; @@ -178,7 +180,7 @@ namespace WebsitePanel.Providers.Web.Handlers } } - internal void InheritScriptMapsFromParent(string fqPath) + internal void InheritScriptMapsFromParent(string fqPath) { if (String.IsNullOrEmpty(fqPath)) return; @@ -241,5 +243,69 @@ namespace WebsitePanel.Providers.Web.Handlers // return null; } + + + internal void CopyInheritedHandlers(string siteName, string vDirPath) + { + if (string.IsNullOrEmpty(siteName)) + { + return; + } + + if (string.IsNullOrEmpty(vDirPath)) + { + vDirPath = "/"; + } + + using (var srvman = GetServerManager()) + { + var config = srvman.GetWebConfiguration(siteName, vDirPath); + + var handlersSection = (HandlersSection)config.GetSection(Constants.HandlersSection, typeof(HandlersSection)); + + var handlersCollection = handlersSection.Handlers; + + var list = new HandlerAction[handlersCollection.Count]; + ((System.Collections.ICollection) handlersCollection).CopyTo(list, 0); + + handlersCollection.Clear(); + + foreach (var handler in list) + { + handlersCollection.AddCopy(handler); + } + + srvman.CommitChanges(); + } + } + + internal void MoveHandlerToTop(string handlerName, string siteName, string vDirPath) + { + if (string.IsNullOrEmpty(siteName)) + { + return; + } + + if (string.IsNullOrEmpty(vDirPath)) + { + vDirPath = "/"; + } + + using (var srvman = GetServerManager()) + { + var config = srvman.GetWebConfiguration(siteName, vDirPath); + + var handlersSection = (HandlersSection)config.GetSection(Constants.HandlersSection, typeof(HandlersSection)); + + var handlersCollection = handlersSection.Handlers; + + var handlerElement = handlersCollection[handlerName]; + + handlersCollection.Remove(handlerElement); + handlersCollection.AddCopyAt(0, handlerElement); + + srvman.CommitChanges(); + } + } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/IIs70.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/IIs70.cs index 22f33a5b..bb0376a4 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/IIs70.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/IIs70.cs @@ -28,6 +28,8 @@ using System; using System.Collections; +using System.Diagnostics; +using System.Linq; using System.Management; using System.DirectoryServices; using System.DirectoryServices.ActiveDirectory; @@ -280,7 +282,7 @@ namespace WebsitePanel.Providers.Web Name = settings[Constants.IntegratedAspNet40Pool].Trim() }); } - #endregion + #endregion #region Populate Dedicated Application Pools // ASP.NET 1.1 @@ -711,9 +713,9 @@ namespace WebsitePanel.Providers.Web if (!String.IsNullOrEmpty(AspPath) && String.Equals(AspPath, processor, StringComparison.InvariantCultureIgnoreCase)) virtualDir.AspInstalled = true; - // Detect whether PHP 5 scripting is enabled - if (!String.IsNullOrEmpty(PhpExecutablePath) && String.Equals(PhpExecutablePath, processor, StringComparison.InvariantCultureIgnoreCase)) - virtualDir.PhpInstalled = PHP_5; + //// Detect whether PHP 5 scripting is enabled (non fast_cgi) + if (PhpMode != Constants.PhpMode.FastCGI && !String.IsNullOrEmpty(PhpExecutablePath) && String.Equals(PhpExecutablePath, processor, StringComparison.InvariantCultureIgnoreCase)) + virtualDir.PhpInstalled = PHP_5; // Detect whether PHP 4 scripting is enabled if (!String.IsNullOrEmpty(Php4Path) && String.Equals(Php4Path, processor, StringComparison.InvariantCultureIgnoreCase)) @@ -727,7 +729,18 @@ namespace WebsitePanel.Providers.Web if (!String.IsNullOrEmpty(PerlPath) && String.Equals(PerlPath, processor, StringComparison.InvariantCultureIgnoreCase)) virtualDir.PerlInstalled = true; } - + + // Detect PHP 5 Fast_cgi version(s) + var activePhp5Handler = GetActivePhpHandlerName(srvman, virtualDir); + if (!string.IsNullOrEmpty(activePhp5Handler)) + { + virtualDir.PhpInstalled = PHP_5 + "|" + activePhp5Handler; + var versions = GetPhpVersions(srvman, virtualDir); + // This versionstring is used in UI to view and change php5 version. + var versionString = string.Join("|", versions.Select(v => v.HandlerName + ";" + v.Version).ToArray()); + virtualDir.Php5VersionsInstalled = versionString; + } + // string fqPath = virtualDir.FullQualifiedPath; if (!fqPath.EndsWith(@"/")) @@ -865,32 +878,63 @@ namespace WebsitePanel.Providers.Web #endregion #region PHP 5 script mappings - if (!String.IsNullOrEmpty(PhpExecutablePath) && File.Exists(PhpExecutablePath)) - { - if (virtualDir.PhpInstalled == PHP_5) - { - switch (PhpMode) - { - case Constants.PhpMode.FastCGI: - handlersSvc.AddScriptMaps(virtualDir, PHP_EXTENSIONS, - PhpExecutablePath, Constants.HandlerAlias.PHP_FASTCGI, Constants.FastCgiModule); - break; - case Constants.PhpMode.CGI: - handlersSvc.AddScriptMaps(virtualDir, PHP_EXTENSIONS, - PhpExecutablePath, Constants.HandlerAlias.PHP_CGI, Constants.CgiModule); - break; - case Constants.PhpMode.ISAPI: - handlersSvc.AddScriptMaps(virtualDir, PHP_EXTENSIONS, - PhpExecutablePath, Constants.HandlerAlias.PHP_ISAPI, Constants.IsapiModule); - break; - } - } - else - { - handlersSvc.RemoveScriptMaps(virtualDir, PHP_EXTENSIONS, PhpExecutablePath); - } - } - // + if (virtualDir.PhpInstalled.StartsWith(PHP_5)) + { + if (PhpMode == Constants.PhpMode.FastCGI && virtualDir.PhpInstalled.Contains('|')) + { + var path = PhpExecutablePath; + var args = virtualDir.PhpInstalled.Split('|'); + if (args.Count() > 1) + { + // Handler name is present, let us try to find the corresponding path to executable + var phpVersion = GetPhpVersions(virtualDir).SingleOrDefault(p => p.HandlerName == args[1]); + if (phpVersion != null) + { + path = phpVersion.ExecutionPath; + } + } + + if (!String.IsNullOrEmpty(path) && File.Exists(path)) + { + handlersSvc.CopyInheritedHandlers(((WebSite)virtualDir).SiteId, virtualDir.VirtualPath); + handlersSvc.MoveHandlerToTop(args[1], ((WebSite) virtualDir).SiteId, virtualDir.VirtualPath); + } + } + else + { + if (!String.IsNullOrEmpty(PhpExecutablePath) && File.Exists(PhpExecutablePath)) + { + switch (PhpMode) + { + case Constants.PhpMode.FastCGI: + handlersSvc.AddScriptMaps(virtualDir, PHP_EXTENSIONS, + PhpExecutablePath, Constants.HandlerAlias.PHP_FASTCGI, Constants.FastCgiModule); + break; + case Constants.PhpMode.CGI: + handlersSvc.AddScriptMaps(virtualDir, PHP_EXTENSIONS, + PhpExecutablePath, Constants.HandlerAlias.PHP_CGI, Constants.CgiModule); + break; + case Constants.PhpMode.ISAPI: + handlersSvc.AddScriptMaps(virtualDir, PHP_EXTENSIONS, + PhpExecutablePath, Constants.HandlerAlias.PHP_ISAPI, Constants.IsapiModule); + break; + } + } + } + } + else + { + if (PhpMode == Constants.PhpMode.FastCGI && GetPhpVersions(virtualDir).Any()) + { + // Don't erase handler mappings, if we do, the virtualDir cannot see and choose what version of PHP to run later + } + else + { + handlersSvc.RemoveScriptMaps(virtualDir, PHP_EXTENSIONS, PhpExecutablePath); + } + } + + // #endregion #region PHP 4 script mappings (IsapiModule only) @@ -4437,5 +4481,59 @@ namespace WebsitePanel.Providers.Web #endregion - } + + #region Php Management + + protected PhpVersion[] GetPhpVersions(ServerManager srvman, WebVirtualDirectory virtualDir) + { + var config = srvman.GetWebConfiguration(((WebSite)virtualDir).SiteId, virtualDir.VirtualPath); + //var config = srvman.GetApplicationHostConfiguration(); + var handlersSection = config.GetSection(Constants.HandlersSection); + + var result = new List(); + + // Loop through available maps and fill installed processors + foreach (var handler in handlersSection.GetCollection()) + { + if (string.Equals(handler["path"].ToString(), "*.php", StringComparison.OrdinalIgnoreCase)) + { + var executable = handler["ScriptProcessor"].ToString().Split('|')[0]; + if (string.Equals(handler["Modules"].ToString(), "FastCgiModule", StringComparison.OrdinalIgnoreCase) && File.Exists(executable)) + { + var handlerName = handler["Name"].ToString(); + result.Add(new PhpVersion() {HandlerName = handlerName, Version = GetPhpExecutableVersion(executable), ExecutionPath = handler["ScriptProcessor"].ToString()}); + } + } + } + + return result.ToArray(); + } + + protected PhpVersion[] GetPhpVersions(WebVirtualDirectory virtualDir) + { + using (var srvman = new ServerManager()) + { + return GetPhpVersions(srvman, virtualDir); + } + } + + protected string GetActivePhpHandlerName(ServerManager srvman, WebVirtualDirectory virtualDir) + { + var config = srvman.GetWebConfiguration(((WebSite)virtualDir).SiteId, virtualDir.VirtualPath); + var handlersSection = config.GetSection(Constants.HandlersSection); + + // Find first handler for *.php + return (from handler in handlersSection.GetCollection() + where string.Equals(handler["path"].ToString(), "*.php", StringComparison.OrdinalIgnoreCase) && string.Equals(handler["Modules"].ToString(), "FastCgiModule", StringComparison.OrdinalIgnoreCase) + select handler["name"].ToString() + ).FirstOrDefault(); + } + + private static string GetPhpExecutableVersion(string phpexePath) + { + return FileVersionInfo.GetVersionInfo(phpexePath).ProductVersion; + } + + #endregion + } } diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx index fbf46c81..d725cd82 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx @@ -30,8 +30,6 @@ None - 4 - 5 diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.cs index 74a074e5..6db36a40 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.cs @@ -30,6 +30,7 @@ using System; using System.Data; using System.Configuration; using System.Collections; +using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; @@ -98,13 +99,23 @@ namespace WebsitePanel.Portal if (!IIs7 || !PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_ASPNET40)) ddlAspNet.Items.Remove(ddlAspNet.Items.FindByValue("4I")); - rowAspNet.Visible = ddlAspNet.Items.Count > 1; - // php - if (!PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_PHP4)) - ddlPhp.Items.Remove(ddlPhp.Items.FindByValue("4")); - if (!PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_PHP5)) - ddlPhp.Items.Remove(ddlPhp.Items.FindByValue("5")); + if (PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_PHP4)) + ddlPhp.Items.Add("4"); + if (PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_PHP5)) + { + if (!string.IsNullOrEmpty(item.Php5VersionsInstalled)) + { + // Add items from list + ddlPhp.Items.Remove(ddlPhp.Items.FindByValue("")); + ddlPhp.Items.AddRange(item.Php5VersionsInstalled.Split('|').Select(v => new ListItem(v.Split(';')[1], "5|" + v.Split(';')[0])).OrderBy(i => i.Text).ToArray()); + } + else + { + ddlPhp.Items.Add("5"); + } + } + Utils.SelectListItem(ddlPhp, item.PhpInstalled); rowPhp.Visible = ddlPhp.Items.Count > 1; rowPerl.Visible = PackagesHelper.CheckGroupQuotaEnabled(packageId, ResourceGroups.Web, Quotas.WEB_PERL); diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.designer.cs index a9216e87..48e610df 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/WebSitesExtensionsControl.ascx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.3053 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------