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 fa659087..297b1455 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj +++ b/WebsitePanel/Sources/WebsitePanel.Providers.Base/WebsitePanel.Providers.Base.csproj @@ -333,6 +333,7 @@ + diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs b/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs index 69413bc7..418d8c22 100644 --- a/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs +++ b/WebsitePanel/Sources/WebsitePanel.Providers.FTP.IIs70/MsFTP.cs @@ -30,6 +30,7 @@ using System; using System.Collections.Generic; using System.DirectoryServices.ActiveDirectory; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; using WebsitePanel.Providers.FTP.IIs70; @@ -99,6 +100,20 @@ namespace WebsitePanel.Providers.FTP { get { return ProviderSettings["ADGroupsOU"]; } } + + protected string AdFtpRoot + { + get { return ProviderSettings["AdFtpRoot"]; } + } + + protected Mode UserIsolationMode + { + get + { + var site = GetSite(ProviderSettings["SiteId"]); + return (Mode)Enum.Parse(typeof(Mode), site["UserIsolationMode"]); + } + } #endregion #region IFtpServer Members @@ -291,134 +306,275 @@ namespace WebsitePanel.Providers.FTP this.ftpSitesService.DeleteSite(siteId); } - /// - /// Checks whether account with given name exists. - /// - /// Account name to check. - /// true - if it exists; false - otherwise. + /// + /// Checks whether account with given name exists. + /// + /// Account name to check. + /// true - if it exists; false - otherwise. public bool AccountExists(string accountName) { if (String.IsNullOrEmpty(accountName)) { - return false; + return false; } - // check acocunt on FTP server - bool ftpExists = this.ftpSitesService.VirtualDirectoryExists(this.SiteId, accountName); + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + return SecurityUtils.UserExists(accountName, ServerSettings, UsersOU); - // check account in the system - bool systemExists = SecurityUtils.UserExists(accountName, ServerSettings, UsersOU); - return (ftpExists || systemExists); + default: + // check acocunt on FTP server + bool ftpExists = this.ftpSitesService.VirtualDirectoryExists(this.SiteId, accountName); + + // check account in the system + bool systemExists = SecurityUtils.UserExists(accountName, ServerSettings, UsersOU); + return (ftpExists || systemExists); + } } - /// + /// /// Gets available ftp accounts. /// /// List of avaialble accounts. public FtpAccount[] GetAccounts() { - List accounts = new List(); + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + return SecurityUtils.GetUsers(ServerSettings, UsersOU).Select(GetAccount).ToArray(); + default: + List accounts = new List(); - foreach (string directory in this.ftpSitesService.GetVirtualDirectoriesNames(this.SiteId)) - { - // Skip root virtual directory - if (String.Equals(directory, "/")) - continue; - // - accounts.Add(this.GetAccount(directory.Substring(1))); - } + foreach (string directory in this.ftpSitesService.GetVirtualDirectoriesNames(this.SiteId)) + { + // Skip root virtual directory + if (String.Equals(directory, "/")) + continue; + // + accounts.Add(this.GetAccount(directory.Substring(1))); + } - return accounts.ToArray(); + return accounts.ToArray(); + } } - /// - /// Gets account with given name. - /// - /// Account's name to get. - /// Ftp account. + /// + /// Gets account with given name. + /// + /// Account's name to get. + /// Ftp account. public FtpAccount GetAccount(string accountName) { - FtpAccount acc = new FtpAccount(); - acc.Name = accountName; - this.FillFtpAccountFromIis(acc); - return acc; + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + var user = SecurityUtils.GetUser(accountName, ServerSettings, UsersOU); + + var path = Path.Combine(user.MsIIS_FTPRoot, user.MsIIS_FTPDir); + var permission = GetUserPermission(accountName, path); + var account = new FtpAccount() + { + CanRead = permission.Read, + CanWrite = permission.Write, + CreatedDate = user.CreatedDate, + Enabled = !user.AccountDisabled, + Folder = path, + GroupName = user.GroupName, + Name = user.Name + }; + + return account; + default: + FtpAccount acc = new FtpAccount(); + acc.Name = accountName; + this.FillFtpAccountFromIis(acc); + return acc; + } } - /// + protected UserPermission GetUserPermission(string accountName, string folder) + { + var userPermission = new UserPermission {AccountName = accountName}; + return SecurityUtils.GetGroupNtfsPermissions(folder, new[] {userPermission}, ServerSettings, UsersOU, GroupsOU)[0]; + } + + + /// /// Creates ftp account under root ftp site. /// /// Ftp account to create. public void CreateAccount(FtpAccount account) { - // Create user account. - SystemUser user = new SystemUser(); - user.Name = account.Name; - user.FullName = account.Name; - user.Description = "WebsitePanel System Account"; - user.MemberOf = new string[] { FtpGroupName }; - user.Password = account.Password; - user.PasswordCantChange = true; - user.PasswordNeverExpires = true; - user.AccountDisabled = !account.Enabled; - user.System = true; + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + SecurityUtils.EnsureOrganizationalUnitsExist(ServerSettings, UsersOU, GroupsOU); - // Create in the operating system. - if (SecurityUtils.UserExists(user.Name, ServerSettings, UsersOU)) - { - SecurityUtils.DeleteUser(user.Name, ServerSettings, UsersOU); - } - SecurityUtils.CreateUser(user, ServerSettings, UsersOU, GroupsOU); + var systemUser = SecurityUtils.GetUser(account.Name, ServerSettings, UsersOU); - // Prepare account's home folder. - this.EnsureUserHomeFolderExists(account.Folder, account.Name, account.CanRead, account.CanWrite); + if (systemUser == null) + { + systemUser = new SystemUser + { + Name = account.Name, + FullName = account.Name, + Password = account.Password, + PasswordCantChange = true, + PasswordNeverExpires = true, + System = true + }; - // Future account will be given virtual directory under default ftp web site. - this.ftpSitesService.CreateFtpAccount(this.SiteId, account); - // - this.ftpSitesService.ConfigureConnectAs(account.Folder, this.SiteId, account.VirtualPath, - this.GetQualifiedAccountName(account.Name), account.Password, true); - } + SecurityUtils.CreateUser(systemUser, ServerSettings, UsersOU, GroupsOU); + } - /// - /// Updates ftp account. - /// - /// Accoun to update. + UpdateAccount(account); + + break; + + default: + // Create user account. + SystemUser user = new SystemUser(); + user.Name = account.Name; + user.FullName = account.Name; + user.Description = "WebsitePanel System Account"; + user.MemberOf = new string[] {FtpGroupName}; + user.Password = account.Password; + user.PasswordCantChange = true; + user.PasswordNeverExpires = true; + user.AccountDisabled = !account.Enabled; + user.System = true; + + // Create in the operating system. + if (SecurityUtils.UserExists(user.Name, ServerSettings, UsersOU)) + { + SecurityUtils.DeleteUser(user.Name, ServerSettings, UsersOU); + } + SecurityUtils.CreateUser(user, ServerSettings, UsersOU, GroupsOU); + + // Prepare account's home folder. + this.EnsureUserHomeFolderExists(account.Folder, account.Name, account.CanRead, account.CanWrite); + + // Future account will be given virtual directory under default ftp web site. + this.ftpSitesService.CreateFtpAccount(this.SiteId, account); + // + this.ftpSitesService.ConfigureConnectAs(account.Folder, this.SiteId, account.VirtualPath, + this.GetQualifiedAccountName(account.Name), account.Password, true); + break; + } + } + + /// + /// Updates ftp account. + /// + /// Accoun to update. public void UpdateAccount(FtpAccount account) { - // Change user account state and password (if required). - SystemUser user = SecurityUtils.GetUser(account.Name, ServerSettings, UsersOU); - user.Password = account.Password; - user.AccountDisabled = !account.Enabled; - SecurityUtils.UpdateUser(user, ServerSettings, UsersOU, GroupsOU); - // Update iis configuration. - this.FillIisFromFtpAccount(account); + var user = SecurityUtils.GetUser(account.Name, ServerSettings, UsersOU); + + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + var ftpRoot = AdFtpRoot.ToLower(); + var ftpDir = account.Folder.ToLower().Replace(ftpRoot, ""); + + var oldDir = user.MsIIS_FTPDir; + + user.Password = account.Password; + user.PasswordCantChange = true; + user.PasswordNeverExpires = true; + user.Description = "WebsitePanel FTP Account with AD User Isolation"; + user.MemberOf = new[] {FtpGroupName}; + user.AccountDisabled = !account.Enabled; + user.MsIIS_FTPRoot = ftpRoot; + user.MsIIS_FTPDir = ftpDir; + user.System = true; + + SecurityUtils.UpdateUser(user, ServerSettings, UsersOU, GroupsOU); + + // Set NTFS permissions + var userPermission = GetUserPermission(account.Name, account.Folder); + + // Do we need to change the NTFS permissions? i.e. is users home dir changed or are permissions changed? + if (oldDir != ftpDir || account.CanRead != userPermission.Read || account.CanWrite != userPermission.Write) + { + // First get sid of user account + var sid = SecurityUtils.GetAccountSid(account.Name, ServerSettings, UsersOU, GroupsOU); + + // Remove the permissions set for this account on previous folder + SecurityUtils.RemoveNtfsPermissionsBySid(Path.Combine(ftpRoot, oldDir), sid); + + // If no permissions is to be set, exit + if (!account.CanRead && !account.CanWrite) + { + return; + } + + // Add the new permissions + var ntfsPermissions = account.CanRead ? NTFSPermission.Read : NTFSPermission.Write; + if (account.CanRead && account.CanWrite) + { + ntfsPermissions = NTFSPermission.Modify; + } + + SecurityUtils.GrantNtfsPermissionsBySid(account.Folder, sid, ntfsPermissions, true, true); + } + break; + + default: + + // Change user account state and password (if required). + user.Password = account.Password; + user.AccountDisabled = !account.Enabled; + SecurityUtils.UpdateUser(user, ServerSettings, UsersOU, GroupsOU); + // Update iis configuration. + this.FillIisFromFtpAccount(account); + break; + } } - /// - /// Deletes account with given name. - /// - /// Account's name to be deleted. + /// + /// Deletes account with given name. + /// + /// Account's name to be deleted. public void DeleteAccount(string accountName) { - string virtualDirectory = String.Format("/{0}", accountName); - string currentPhysicalPath = this.ftpSitesService.GetSitePhysicalPath(this.SiteId, virtualDirectory); + switch (UserIsolationMode) + { + case Mode.ActiveDirectory: + var account = GetAccount(accountName); - // Delete virtual directory - this.ftpSitesService.DeleteFtpAccount(this.SiteId, virtualDirectory); + // Remove the NTFS permissions first + SecurityUtils.RemoveNtfsPermissions(account.Folder, account.Name, ServerSettings, UsersOU, GroupsOU); - this.ftpSitesService.CommitChanges(); + if (SecurityUtils.UserExists(accountName, ServerSettings, UsersOU)) + { + SecurityUtils.DeleteUser(accountName, ServerSettings, UsersOU); + } + break; - // Remove permissions - RemoveFtpFolderPermissions(currentPhysicalPath, accountName); + default: + string virtualDirectory = String.Format("/{0}", accountName); + string currentPhysicalPath = this.ftpSitesService.GetSitePhysicalPath(this.SiteId, virtualDirectory); - // Delete system user account - if (SecurityUtils.UserExists(accountName, ServerSettings, UsersOU)) - { - SecurityUtils.DeleteUser(accountName, ServerSettings, UsersOU); - } + // Delete virtual directory + this.ftpSitesService.DeleteFtpAccount(this.SiteId, virtualDirectory); + + this.ftpSitesService.CommitChanges(); + + // Remove permissions + RemoveFtpFolderPermissions(currentPhysicalPath, accountName); + + // Delete system user account + if (SecurityUtils.UserExists(accountName, ServerSettings, UsersOU)) + { + SecurityUtils.DeleteUser(accountName, ServerSettings, UsersOU); + } + break; + } } - /// + /// /// Fills iis configuration from ftp account. /// /// Ftp account to fill from. @@ -519,6 +675,7 @@ namespace WebsitePanel.Providers.FTP ftpSite.AllowAnonymous = iisFtpSite.Security.Authentication.AnonymousAuthentication.Enabled; ftpSite.AnonymousUsername = iisFtpSite.Security.Authentication.AnonymousAuthentication.UserName; ftpSite.AnonymousUserPassword = iisFtpSite.Security.Authentication.AnonymousAuthentication.Password; + ftpSite["UserIsolationMode"] = iisFtpSite.UserIsolation.Mode.ToString(); // Logging settings. ftpSite[FtpSite.MSFTP7_SITE_ID] = iisFtpSite.SiteServiceId; if (iisFtpSite.LogFile.Enabled) diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Handlers/HandlersModuleService.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/Handlers/HandlersModuleService.cs index 12a7b902..bf283e54 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,40 @@ 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(); + } + } } } diff --git a/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/IIs70.cs b/WebsitePanel/Sources/WebsitePanel.Providers.Web.IIS70/IIs70.cs index 22f33a5b..cdfbcf8c 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,60 @@ 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 args = virtualDir.PhpInstalled.Split('|'); + + if (args.Count() > 1) + { + // Handler name is present, use it to set choosen version + var handlerName = args[1]; + + if (handlerName != GetActivePhpHandlerName(virtualDir)) + { + // Only change handler if it is different from the current one + handlersSvc.CopyInheritedHandlers(((WebSite)virtualDir).SiteId, virtualDir.VirtualPath); + MakeHandlerActive(handlerName, virtualDir); + } + } + } + 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 +4478,91 @@ 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 = webObjectsSvc.GetServerManager()) + { + return GetPhpVersions(srvman, virtualDir); + } + } + + protected string GetActivePhpHandlerName(WebVirtualDirectory virtualDir) + { + using (var srvman = webObjectsSvc.GetServerManager()) + { + return GetActivePhpHandlerName(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(); + } + + protected static string GetPhpExecutableVersion(string phpexePath) + { + return FileVersionInfo.GetVersionInfo(phpexePath).ProductVersion; + } + + protected void MakeHandlerActive(string handlerName, WebVirtualDirectory virtualDir) + { + using (var srvman = webObjectsSvc.GetServerManager()) + { + var config = srvman.GetWebConfiguration(((WebSite)virtualDir).SiteId, virtualDir.VirtualPath); + + var handlersSection = (HandlersSection)config.GetSection(Constants.HandlersSection, typeof(HandlersSection)); + + var handlersCollection = handlersSection.Handlers; + + var handlerElement = handlersCollection[handlerName]; + var activeHandlerElement = handlersCollection[GetActivePhpHandlerName(srvman, virtualDir)]; + + var activeHandlerIndex = handlersCollection.IndexOf(activeHandlerElement); + + handlersCollection.Remove(handlerElement); + + handlersCollection.AddCopyAt(activeHandlerIndex, handlerElement); + + srvman.CommitChanges(); + } + } + + #endregion + } } diff --git a/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs b/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs index a2fcd1a9..27ddaa75 100644 --- a/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs +++ b/WebsitePanel/Sources/WebsitePanel.Server.Utils/SecurityUtils.cs @@ -511,6 +511,8 @@ namespace WebsitePanel.Providers.Utils user.PasswordCantChange = ((userFlags & ADAccountOptions.UF_PASSWD_CANT_CHANGE) != 0); user.PasswordNeverExpires = ((userFlags & ADAccountOptions.UF_DONT_EXPIRE_PASSWD) != 0); user.AccountDisabled = ((userFlags & ADAccountOptions.UF_ACCOUNTDISABLE) != 0); + user.MsIIS_FTPDir = GetObjectProperty(objUser, "msIIS-FTPDir").ToString(); + user.MsIIS_FTPRoot = GetObjectProperty(objUser, "msIIS-FTPRoot").ToString(); // get user groups user.MemberOf = GetUserGroups(objUser); @@ -727,6 +729,12 @@ namespace WebsitePanel.Providers.Utils objUser.Properties["description"].Value = String.IsNullOrEmpty(user.Description) ? "WebsitePanel System Account" : user.Description; + if (user.MsIIS_FTPDir != string.Empty) + { + SetObjectProperty(objUser, "msIIS-FTPDir", user.MsIIS_FTPDir); + SetObjectProperty(objUser, "msIIS-FTPRoot", user.MsIIS_FTPRoot); + } + ADAccountOptions userFlags = ADAccountOptions.UF_NORMAL_ACCOUNT; if (user.PasswordCantChange) diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/IIS70_Settings.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/IIS70_Settings.ascx.resx index 8c517294..377a2135 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/IIS70_Settings.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/IIS70_Settings.ascx.resx @@ -256,7 +256,10 @@ Web Sites Public Shared Address: - - Register Helicon Ape module globally: + + Register Helicon Ape module globally: + + + If any PHP5 FastCGI handlers are present on the server, the PHP 5.x Executable Path given above will be ignored and not used. \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx index d6149f97..4021bbe7 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/App_LocalResources/MSFTP70_Settings.ascx.resx @@ -112,10 +112,10 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Yes @@ -138,4 +138,7 @@ <Select FTP Site> + + FTP RootDir: + \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx index 3c44ec57..d3942659 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx @@ -291,6 +291,11 @@ + + + + + diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx.designer.cs index 5bba0914..b2bd278a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/IIS70_Settings.ascx.designer.cs @@ -490,6 +490,15 @@ namespace WebsitePanel.Portal.ProviderControls { /// protected global::System.Web.UI.WebControls.DropDownList ddlPhpMode; + /// + /// litPHP5Info control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Literal litPHP5Info; + /// /// perlPath control. /// diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx index 831772d8..bb35b384 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx @@ -1,45 +1,61 @@ <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MSFTP70_Settings.ascx.cs" Inherits="WebsitePanel.Portal.ProviderControls.MSFTP70_Settings" %> <%@ Register Src="Common_ActiveDirectoryIntegration.ascx" TagName="ActiveDirectoryIntegration" TagPrefix="uc1" %> <%@ Register Src="../UserControls/SelectIPAddress.ascx" TagName="SelectIPAddress" TagPrefix="uc1" %> - - - - + + + + + +
- - + + + + - - - - - - - - +
+ + -
- - -
- - +
+ + + + + + + + + + + +
+ + + +
+ + + + +
+
+
+ + + + - - - - - - - - + + - -
+ +
- - - -
- - - - +
+
\ No newline at end of file +
+ +
+ + + +
diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs index 9caf61df..71c18d09 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.cs @@ -27,10 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using System; +using System.Collections.Generic; using System.Data; using System.Configuration; using System.Collections; using System.Collections.Specialized; +using System.Linq; using System.Web; using System.Web.Security; using System.Web.UI; @@ -51,7 +53,8 @@ namespace WebsitePanel.Portal.ProviderControls { int selectedAddressid = this.FindAddressByText(settings["SharedIP"]); ipAddress.AddressId = (selectedAddressid > 0) ? selectedAddressid : 0; - txtSiteId.Text = settings["SiteId"]; + BindSiteId(settings); + txtAdFtpRoot.Text = settings["AdFtpRoot"]; txtFtpGroupName.Text = settings["FtpGroupName"]; chkBuildUncFilesPath.Checked = Utils.ParseBool(settings["BuildUncFilesPath"], false); ActiveDirectoryIntegration.BindSettings(settings); @@ -75,7 +78,11 @@ namespace WebsitePanel.Portal.ProviderControls { settings["SharedIP"] = String.Empty; } - settings["SiteId"] = txtSiteId.Text.Trim(); + settings["SiteId"] = ddlSite.SelectedValue; + if (!string.IsNullOrWhiteSpace(txtAdFtpRoot.Text)) + { + settings["AdFtpRoot"] = txtAdFtpRoot.Text.Trim(); + } settings["FtpGroupName"] = txtFtpGroupName.Text.Trim(); settings["BuildUncFilesPath"] = chkBuildUncFilesPath.Checked.ToString(); ActiveDirectoryIntegration.SaveSettings(settings); @@ -92,5 +99,31 @@ namespace WebsitePanel.Portal.ProviderControls } return 0; } + + private void BindSiteId(StringDictionary settings) + { + var sites = ES.Services.FtpServers.GetFtpSites(PanelRequest.ServiceId); + + foreach (var site in sites) + { + var item = new ListItem(site.Name + " (User Isolation Mode: " + site["UserIsolationMode"] + ")", site.Name); + + if (item.Value == settings["SiteId"]) + { + item.Selected = true; + } + + ddlSite.Items.Add(item); + } + + ddlSite_SelectedIndexChanged(this, null); + } + + protected void ddlSite_SelectedIndexChanged(object sender, EventArgs e) + { + var isActiveDirectoryUserIsolated = ddlSite.SelectedItem.Text.Contains("ActiveDirectory"); + FtpRootRow.Visible = isActiveDirectoryUserIsolated; + txtAdFtpRootReqValidator.Enabled= isActiveDirectoryUserIsolated; + } } } \ No newline at end of file diff --git a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs index 596afc56..b4f88c2a 100644 --- a/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs +++ b/WebsitePanel/Sources/WebsitePanel.WebPortal/DesktopModules/WebsitePanel/ProviderControls/MSFTP70_Settings.ascx.designer.cs @@ -1,22 +1,15 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.1378 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ namespace WebsitePanel.Portal.ProviderControls { - /// - /// MSFTP70_Settings class. - /// - /// - /// Auto-generated class. - /// public partial class MSFTP70_Settings { /// @@ -47,13 +40,49 @@ namespace WebsitePanel.Portal.ProviderControls { protected global::System.Web.UI.WebControls.Label lblSite; /// - /// txtSiteId control. + /// ddlSite control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.WebControls.TextBox txtSiteId; + protected global::System.Web.UI.WebControls.DropDownList ddlSite; + + /// + /// FtpRootRow control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlTableRow FtpRootRow; + + /// + /// lblAdFtpRoot control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Label lblAdFtpRoot; + + /// + /// txtAdFtpRoot control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.TextBox txtAdFtpRoot; + + /// + /// txtAdFtpRootReqValidator control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.RequiredFieldValidator txtAdFtpRootReqValidator; /// /// lblGroupName control. 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. // //------------------------------------------------------------------------------