876 lines
37 KiB
C#
876 lines
37 KiB
C#
// Copyright (c) 2012, 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.IO;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Data;
|
|
using System.Reflection;
|
|
using System.Collections;
|
|
using System.Collections.Specialized;
|
|
using System.Collections.Generic;
|
|
using System.Xml;
|
|
using System.Net.Mail;
|
|
using System.Web;
|
|
using System.Web.Caching;
|
|
using WebsitePanel.Providers;
|
|
using WebsitePanel.Providers.Web;
|
|
using WebsitePanel.Providers.FTP;
|
|
using WebsitePanel.Providers.Mail;
|
|
using WebsitePanel.Providers.Database;
|
|
using WebsitePanel.Providers.OS;
|
|
using OS = WebsitePanel.Providers.OS;
|
|
|
|
|
|
namespace WebsitePanel.EnterpriseServer
|
|
{
|
|
public class WebApplicationsInstaller
|
|
{
|
|
public const string PROPERTY_CONTENT_PATH = "installer.contentpath";
|
|
public const string PROPERTY_ABSOLUTE_CONTENT_PATH = "installer.absolute.contentpath";
|
|
public const string PROPERTY_VDIR_CREATED = "installer.virtualdircreated";
|
|
public const string PROPERTY_DATABASE_CREATED = "installer.databasecreated";
|
|
public const string PROPERTY_USER_CREATED = "installer.usercreated";
|
|
public const string PROPERTY_INSTALLED_FILES = "installer.installedfiles";
|
|
public const string PROPERTY_DELETE_FILES = "installer.deletefiles";
|
|
public const string PROPERTY_DELETE_VDIR = "installer.deletevdir";
|
|
public const string PROPERTY_DELETE_SQL = "installer.deletesql";
|
|
public const string PROPERTY_DELETE_DATABASE = "installer.deletedatabase";
|
|
public const string PROPERTY_DELETE_USER = "installer.deleteuser";
|
|
|
|
public static int InstallApplication(InstallationInfo inst)
|
|
{
|
|
// check account
|
|
int accountCheck = SecurityContext.CheckAccount(DemandAccount.NotDemo | DemandAccount.IsActive);
|
|
if (accountCheck < 0) return accountCheck;
|
|
|
|
// check package
|
|
int packageCheck = SecurityContext.CheckPackage(inst.PackageId, DemandPackage.IsActive);
|
|
if (packageCheck < 0) return packageCheck;
|
|
|
|
// install application
|
|
WebApplicationsInstaller installer = new WebApplicationsInstaller();
|
|
return installer.InstallWebApplication(inst);
|
|
}
|
|
|
|
ApplicationInfo app = null;
|
|
private string contentPath = null;
|
|
private string siteId = null;
|
|
private OS.OperatingSystem os = null;
|
|
private DatabaseServer sql = null;
|
|
private string serverIpAddressExternal = null;
|
|
private string serverIpAddressInternal = null;
|
|
private string webSiteName = "";
|
|
|
|
public int InstallWebApplication(InstallationInfo inst)
|
|
{
|
|
// place log record
|
|
TaskManager.StartTask("APP_INSTALLER", "INSTALL_APPLICATION");
|
|
TaskManager.WriteParameter("Virtual directory", inst.VirtualDir);
|
|
TaskManager.WriteParameter("Database group", inst.DatabaseGroup);
|
|
TaskManager.ItemId = inst.PackageId;
|
|
|
|
try
|
|
{
|
|
// get application info
|
|
app = GetApplication(inst.PackageId, inst.ApplicationId);
|
|
|
|
TaskManager.ItemName = app.Name;
|
|
|
|
// check web site for existance
|
|
WebSite webSite = WebServerController.GetWebSite(inst.WebSiteId);
|
|
|
|
if (webSite == null)
|
|
return BusinessErrorCodes.ERROR_WEB_INSTALLER_WEBSITE_NOT_EXISTS;
|
|
|
|
TaskManager.WriteParameter("Web site", webSite.Name);
|
|
|
|
webSiteName = webSite.Name;
|
|
siteId = webSite.SiteId;
|
|
|
|
// change web site properties if required
|
|
if (String.IsNullOrEmpty(inst.VirtualDir))
|
|
{
|
|
ChangeVirtualDirectoryProperties(webSite, app.WebSettings);
|
|
WebServerController.UpdateWebSite(webSite);
|
|
}
|
|
|
|
// get OS service
|
|
int osId = PackageController.GetPackageServiceId(inst.PackageId, "os");
|
|
os = new OS.OperatingSystem();
|
|
ServiceProviderProxy.Init(os, osId);
|
|
|
|
// get remote content path
|
|
contentPath = webSite.ContentPath;
|
|
|
|
// create virtual dir if required
|
|
if (!String.IsNullOrEmpty(inst.VirtualDir))
|
|
{
|
|
// check if the required virtual dir already exists
|
|
contentPath = Path.Combine(contentPath, inst.VirtualDir);
|
|
|
|
WebVirtualDirectory vdir = null;
|
|
int result = WebServerController.AddVirtualDirectory(inst.WebSiteId, inst.VirtualDir, contentPath);
|
|
if (result == BusinessErrorCodes.ERROR_VDIR_ALREADY_EXISTS)
|
|
{
|
|
// the directory alredy exists
|
|
vdir = WebServerController.GetVirtualDirectory(
|
|
inst.WebSiteId, inst.VirtualDir);
|
|
|
|
contentPath = vdir.ContentPath;
|
|
}
|
|
else
|
|
{
|
|
vdir = WebServerController.GetVirtualDirectory(
|
|
inst.WebSiteId, inst.VirtualDir);
|
|
|
|
inst[PROPERTY_VDIR_CREATED] = "True";
|
|
}
|
|
|
|
// change virtual directory properties if required
|
|
ChangeVirtualDirectoryProperties(vdir, app.WebSettings);
|
|
WebServerController.UpdateVirtualDirectory(inst.WebSiteId, vdir);
|
|
}
|
|
|
|
// deploy application codebase ZIP and then unpack it
|
|
string codebasePath = app.Codebase;
|
|
string remoteCodebasePath = Path.Combine(contentPath, Path.GetFileName(app.Codebase));
|
|
|
|
// make content path absolute
|
|
string absContentPath = FilesController.GetFullPackagePath(inst.PackageId, contentPath);
|
|
|
|
// save content path
|
|
inst[PROPERTY_CONTENT_PATH] = contentPath;
|
|
inst[PROPERTY_ABSOLUTE_CONTENT_PATH] = absContentPath;
|
|
|
|
// copy ZIP to the target server
|
|
FileStream stream = File.OpenRead(codebasePath);
|
|
int BUFFER_LENGTH = 5000000;
|
|
|
|
byte[] buffer = new byte[BUFFER_LENGTH];
|
|
int readBytes = 0;
|
|
while (true)
|
|
{
|
|
readBytes = stream.Read(buffer, 0, BUFFER_LENGTH);
|
|
|
|
if (readBytes < BUFFER_LENGTH)
|
|
Array.Resize<byte>(ref buffer, readBytes);
|
|
|
|
FilesController.AppendFileBinaryChunk(inst.PackageId, remoteCodebasePath, buffer);
|
|
|
|
if (readBytes < BUFFER_LENGTH)
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
// unpack codebase
|
|
inst[PROPERTY_INSTALLED_FILES] = String.Join(";",
|
|
FilesController.UnzipFiles(inst.PackageId, new string[] { remoteCodebasePath }));
|
|
|
|
// delete codebase zip
|
|
FilesController.DeleteFiles(inst.PackageId, new string[] { remoteCodebasePath });
|
|
|
|
// check/create databases
|
|
if (!String.IsNullOrEmpty(inst.DatabaseGroup) &&
|
|
String.Compare(inst.DatabaseGroup, "None", true) != 0)
|
|
{
|
|
// database
|
|
if (inst.DatabaseId == 0)
|
|
{
|
|
TaskManager.WriteParameter("Database name", inst.DatabaseName);
|
|
|
|
// we should create a new database
|
|
SqlDatabase db = new SqlDatabase();
|
|
db.PackageId = inst.PackageId;
|
|
db.Name = inst.DatabaseName;
|
|
inst.DatabaseId = DatabaseServerController.AddSqlDatabase(db, inst.DatabaseGroup);
|
|
if (inst.DatabaseId < 0)
|
|
{
|
|
// rollback installation
|
|
RollbackInstallation(inst);
|
|
|
|
// return error
|
|
return inst.DatabaseId; // there was an error when creating database
|
|
}
|
|
|
|
inst[PROPERTY_DATABASE_CREATED] = "True";
|
|
}
|
|
else
|
|
{
|
|
// existing database
|
|
SqlDatabase db = DatabaseServerController.GetSqlDatabase(inst.DatabaseId);
|
|
inst.DatabaseName = db.Name;
|
|
|
|
TaskManager.WriteParameter("Database name", inst.DatabaseName);
|
|
}
|
|
|
|
SqlUser user = null;
|
|
// database user
|
|
if (inst.UserId == 0)
|
|
{
|
|
TaskManager.WriteParameter("Database user", inst.Username);
|
|
|
|
// NEW USER
|
|
user = new SqlUser();
|
|
user.PackageId = inst.PackageId;
|
|
user.Name = inst.Username;
|
|
user.Databases = new string[] { inst.DatabaseName };
|
|
user.Password = inst.Password;
|
|
inst.UserId = DatabaseServerController.AddSqlUser(user, inst.DatabaseGroup);
|
|
if (inst.UserId < 0)
|
|
{
|
|
// rollback installation
|
|
RollbackInstallation(inst);
|
|
|
|
// return error
|
|
return inst.UserId; // error while adding user
|
|
}
|
|
|
|
inst[PROPERTY_USER_CREATED] = "True";
|
|
}
|
|
else
|
|
{
|
|
// EXISTING USER
|
|
user = DatabaseServerController.GetSqlUser(inst.UserId);
|
|
inst.Username = user.Name;
|
|
|
|
TaskManager.WriteParameter("Database user", inst.Username);
|
|
|
|
List<string> databases = new List<string>();
|
|
databases.AddRange(user.Databases);
|
|
|
|
if (!databases.Contains(inst.DatabaseName))
|
|
{
|
|
databases.Add(inst.DatabaseName);
|
|
|
|
user.Databases = databases.ToArray();
|
|
DatabaseServerController.UpdateSqlUser(user);
|
|
}
|
|
}
|
|
|
|
// check connectivity with SQL Server and credentials provided
|
|
// load user item
|
|
int sqlServiceId = PackageController.GetPackageServiceId(inst.PackageId, inst.DatabaseGroup);
|
|
sql = new DatabaseServer();
|
|
ServiceProviderProxy.Init(sql, sqlServiceId);
|
|
|
|
if (!sql.CheckConnectivity(inst.DatabaseName, inst.Username,
|
|
inst.Password))
|
|
{
|
|
// can't connect to the database
|
|
RollbackInstallation(inst);
|
|
|
|
return BusinessErrorCodes.ERROR_WEB_INSTALLER_CANT_CONNECT_DATABASE;
|
|
}
|
|
|
|
// read SQL server settings
|
|
StringDictionary settings = ServerController.GetServiceSettings(sqlServiceId);
|
|
serverIpAddressExternal = settings["ExternalAddress"];
|
|
if (settings.ContainsKey("InternalAddress"))
|
|
{
|
|
serverIpAddressInternal = settings["InternalAddress"];
|
|
}
|
|
}
|
|
|
|
// ********* RUN INSTALL SCENARIO ***********
|
|
int scriptResult = RunInstallScenario(inst);
|
|
if (scriptResult < 0)
|
|
{
|
|
// rollback installation
|
|
RollbackInstallation(inst);
|
|
|
|
// return error
|
|
return scriptResult;
|
|
}
|
|
|
|
// add new installation to the database
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// rollback installation
|
|
RollbackInstallation(inst);
|
|
|
|
throw TaskManager.WriteError(ex);
|
|
}
|
|
finally
|
|
{
|
|
TaskManager.CompleteTask();
|
|
}
|
|
}
|
|
|
|
private void ChangeVirtualDirectoryProperties(WebVirtualDirectory vdir,
|
|
ApplicationWebSetting[] settings)
|
|
{
|
|
if (settings == null)
|
|
return;
|
|
|
|
// get type properties
|
|
Type vdirType = vdir.GetType();
|
|
foreach (ApplicationWebSetting setting in settings)
|
|
{
|
|
PropertyInfo prop = vdirType.GetProperty(setting.Name,
|
|
BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
if (prop != null)
|
|
{
|
|
prop.SetValue(vdir, ObjectUtils.Cast(setting.Value, prop.PropertyType), null);
|
|
}
|
|
}
|
|
}
|
|
|
|
private int RunInstallScenario(InstallationInfo inst)
|
|
{
|
|
string scenarioPath = Path.Combine(app.Folder, "Install.xml");
|
|
return RunScenario(scenarioPath, inst, true);
|
|
}
|
|
|
|
private string GetFullPathToInstallFolder(int userId)
|
|
{
|
|
string userhomeFolder = String.Empty;
|
|
string[] osSesstings = os.ServiceProviderSettingsSoapHeaderValue.Settings;
|
|
foreach (string s in osSesstings)
|
|
{
|
|
if (s.Contains("usershome"))
|
|
{
|
|
string[] split = s.Split(new char[] {'='});
|
|
userhomeFolder = split[1];
|
|
}
|
|
}
|
|
UserInfo info = UserController.GetUser(userId);
|
|
return Path.Combine(userhomeFolder, info.Username);
|
|
}
|
|
|
|
private int RunScenario(string scenarioPath, InstallationInfo inst, bool throwExceptions)
|
|
{
|
|
// load XML document
|
|
XmlDocument docScenario = new XmlDocument();
|
|
docScenario.Load(scenarioPath);
|
|
|
|
// go through "check" section
|
|
XmlNode nodeCheck = docScenario.SelectSingleNode("//check");
|
|
if (nodeCheck != null)
|
|
{
|
|
foreach (XmlNode nodeStep in nodeCheck.ChildNodes)
|
|
{
|
|
if (nodeStep.Name == "fileExists")
|
|
{
|
|
/*
|
|
// check if the specified file exists
|
|
string fileName = nodeStep.Attributes["path"].Value;
|
|
fileName = ExpandVariables(fileName, inst);
|
|
if (fileName.StartsWith("\\"))
|
|
{
|
|
fileName = fileName.Substring(1);
|
|
}
|
|
//get full path to instal folder
|
|
PackageInfo package = PackageController.GetPackage(inst.PackageId);
|
|
string fullPath = Path.Combine(GetFullPathToInstallFolder(package.UserId), fileName);
|
|
if (os.FileExists(fullPath))
|
|
return BusinessErrorCodes.ERROR_WEB_INSTALLER_TARGET_WEBSITE_UNSUITABLE;
|
|
*/
|
|
}
|
|
else if (nodeStep.Name == "sql")
|
|
{
|
|
string cmdText = nodeStep.InnerText;
|
|
cmdText = ExpandVariables(cmdText, inst);
|
|
|
|
DataSet dsResults = sql.ExecuteSqlQuery(inst.DatabaseName, cmdText);
|
|
if (dsResults.Tables[0].Rows.Count > 0)
|
|
return BusinessErrorCodes.ERROR_WEB_INSTALLER_TARGET_DATABASE_UNSUITABLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// go through "commands" section
|
|
XmlNode nodeCommands = docScenario.SelectSingleNode("//commands");
|
|
if (nodeCommands != null)
|
|
{
|
|
foreach (XmlNode nodeCommand in nodeCommands.ChildNodes)
|
|
{
|
|
if (nodeCommand.Name == "processFile")
|
|
{
|
|
// process remote file
|
|
string fileName = nodeCommand.Attributes["path"].Value;
|
|
fileName = ExpandVariables(fileName, inst);
|
|
|
|
byte[] fileBinaryContent = FilesController.GetFileBinaryContent(inst.PackageId, fileName);
|
|
if (fileBinaryContent == null)
|
|
throw new Exception("Could not process scenario file: " + fileName);
|
|
|
|
string fileContent = Encoding.UTF8.GetString(fileBinaryContent);
|
|
fileContent = ExpandVariables(fileContent, inst);
|
|
|
|
FilesController.UpdateFileBinaryContent(inst.PackageId, fileName,
|
|
Encoding.UTF8.GetBytes(fileContent));
|
|
}
|
|
else if (nodeCommand.Name == "runSql")
|
|
{
|
|
string cmdText = nodeCommand.InnerText;
|
|
if (nodeCommand.Attributes["path"] != null)
|
|
{
|
|
// load SQL from file
|
|
string sqlPath = Path.Combine(app.Folder, nodeCommand.Attributes["path"].Value);
|
|
|
|
if (!File.Exists(sqlPath))
|
|
continue;
|
|
|
|
StreamReader reader = new StreamReader(sqlPath);
|
|
cmdText = reader.ReadToEnd();
|
|
reader.Close();
|
|
}
|
|
|
|
bool run = true;
|
|
if (nodeCommand.Attributes["dependsOnProperty"] != null)
|
|
{
|
|
string[] propNames = nodeCommand.Attributes["dependsOnProperty"].Value.Split(',');
|
|
foreach (string propName in propNames)
|
|
{
|
|
if (inst[propName.Trim()] == null)
|
|
{
|
|
run = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (run)
|
|
{
|
|
try
|
|
{
|
|
cmdText = ExpandVariables(cmdText, inst);
|
|
sql.ExecuteSqlNonQuerySafe(inst.DatabaseName, inst.Username, inst.Password, cmdText);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (throwExceptions)
|
|
throw ex;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
string appUrls = null;
|
|
private string ExpandVariables(string str, InstallationInfo inst)
|
|
{
|
|
str = ReplaceTemplateVariable(str, "installer.contentpath", inst[PROPERTY_CONTENT_PATH]);
|
|
str = ReplaceTemplateVariable(str, "installer.website", webSiteName);
|
|
str = ReplaceTemplateVariable(str, "installer.virtualdir", inst.VirtualDir);
|
|
|
|
string fullWebPath = webSiteName;
|
|
if (!String.IsNullOrEmpty(inst.VirtualDir))
|
|
fullWebPath += "/" + inst.VirtualDir;
|
|
|
|
// try to load domain info
|
|
DomainInfo domain = ServerController.GetDomain(webSiteName);
|
|
string fullWebPathPrefix = (domain != null && domain.IsSubDomain) ? "" : "www.";
|
|
|
|
// app URLs
|
|
if (appUrls == null)
|
|
{
|
|
// read web pointers
|
|
List<DomainInfo> sitePointers = WebServerController.GetWebSitePointers(inst.WebSiteId);
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.Append("<urls>");
|
|
sb.Append("<url value=\"").Append(fullWebPath).Append("\"/>");
|
|
foreach (DomainInfo pointer in sitePointers)
|
|
{
|
|
string pointerWebPath = pointer.DomainName;
|
|
if (!String.IsNullOrEmpty(inst.VirtualDir))
|
|
pointerWebPath += "/" + inst.VirtualDir;
|
|
sb.Append("<url value=\"").Append(pointerWebPath).Append("\"/>");
|
|
}
|
|
sb.Append("</urls>");
|
|
appUrls = sb.ToString();
|
|
}
|
|
str = ReplaceTemplateVariable(str, "installer.appurls", appUrls);
|
|
|
|
string slashVirtualDir = "";
|
|
if (!String.IsNullOrEmpty(inst.VirtualDir))
|
|
slashVirtualDir = "/" + inst.VirtualDir;
|
|
|
|
str = ReplaceTemplateVariable(str, "installer.slashvirtualdir", slashVirtualDir);
|
|
str = ReplaceTemplateVariable(str, "installer.website.www", fullWebPathPrefix + webSiteName);
|
|
str = ReplaceTemplateVariable(str, "installer.fullwebpath", fullWebPath);
|
|
str = ReplaceTemplateVariable(str, "installer.fullwebpath.www", fullWebPathPrefix + fullWebPath);
|
|
//Replace ObjectQualifierNormalized which is not defined on portal
|
|
str = ReplaceTemplateVariable(str, "ObjectQualifierNormalized", "");
|
|
|
|
/*
|
|
* Application installer variable 'installer.database.server' is obsolete
|
|
* and should not be used to install Application Packs.
|
|
* Instead, please use the following two variables:
|
|
* - installer.database.server.external - defines external database address
|
|
* - installer.database.server.internal - defines internal database address
|
|
*
|
|
* See TFS Issue 952 for details.
|
|
*/
|
|
//apply external database address
|
|
str = ReplaceTemplateVariable(str, "installer.database.server",
|
|
((serverIpAddressExternal != null) ? serverIpAddressExternal : ""));
|
|
str = ReplaceTemplateVariable(str, "installer.database.server.external",
|
|
((serverIpAddressExternal != null) ? serverIpAddressExternal : String.Empty));
|
|
|
|
//apply internal database address
|
|
str = ReplaceTemplateVariable(str, "installer.database.server.internal",
|
|
((serverIpAddressInternal != null) ? serverIpAddressInternal : String.Empty));
|
|
|
|
str = ReplaceTemplateVariable(str, "installer.database", inst.DatabaseName);
|
|
str = ReplaceTemplateVariable(str, "installer.database.user", inst.Username);
|
|
str = ReplaceTemplateVariable(str, "installer.database.password",
|
|
((inst.Password != null) ? inst.Password : ""));
|
|
foreach (string[] pair in inst.PropertiesArray)
|
|
str = ReplaceTemplateVariable(str, pair[0], pair[1]);
|
|
return str;
|
|
}
|
|
|
|
private string ReplaceTemplateVariable(string str, string varName, string varValue)
|
|
{
|
|
if (String.IsNullOrEmpty(str) || String.IsNullOrEmpty(varName))
|
|
return str;
|
|
|
|
str = Regex.Replace(str, "\\$\\{" + varName + "\\}+", varValue, RegexOptions.IgnoreCase);
|
|
str = Regex.Replace(str, "\\$\\{" + varName + ".mysql-escaped\\}+", EscapeMySql(varValue), RegexOptions.IgnoreCase);
|
|
return Regex.Replace(str, "\\$\\{" + varName + ".mssql-escaped\\}+", EscapeMsSql(varValue), RegexOptions.IgnoreCase);
|
|
}
|
|
|
|
private string EscapeMySql(string str)
|
|
{
|
|
if (String.IsNullOrEmpty(str))
|
|
return str;
|
|
|
|
return str.Replace("'", "\\'")
|
|
.Replace("\"", "\\\"")
|
|
.Replace("\n", "\\n")
|
|
.Replace("\r", "\\r")
|
|
.Replace("\t", "\\t")
|
|
.Replace("\\", "\\\\")
|
|
.Replace("\0", "\\0");
|
|
}
|
|
|
|
private string EscapeMsSql(string str)
|
|
{
|
|
if (String.IsNullOrEmpty(str))
|
|
return str;
|
|
|
|
return str.Replace("'", "''");
|
|
}
|
|
|
|
private void RollbackInstallation(InstallationInfo inst)
|
|
{
|
|
// remove virtual dir
|
|
if (inst[PROPERTY_VDIR_CREATED] != null)
|
|
{
|
|
// delete virtual directory
|
|
WebServerController.DeleteVirtualDirectory(inst.WebSiteId, inst.VirtualDir);
|
|
|
|
// delete folder
|
|
FilesController.DeleteFiles(inst.PackageId, new string[] { inst[PROPERTY_CONTENT_PATH] });
|
|
}
|
|
|
|
// remove database
|
|
if (inst[PROPERTY_DATABASE_CREATED] != null)
|
|
DatabaseServerController.DeleteSqlDatabase(inst.DatabaseId);
|
|
|
|
// remove database user
|
|
if (inst[PROPERTY_USER_CREATED] != null)
|
|
DatabaseServerController.DeleteSqlUser(inst.UserId);
|
|
}
|
|
|
|
public static List<ApplicationCategory> GetCategories()
|
|
{
|
|
List<ApplicationCategory> categories = null;
|
|
|
|
string key = "WebApplicationCategories";
|
|
|
|
// look up in the cache
|
|
if (HttpContext.Current != null)
|
|
categories = (List<ApplicationCategory>)HttpContext.Current.Cache[key];
|
|
|
|
if (categories == null)
|
|
{
|
|
string catsPath = Path.Combine(ConfigSettings.WebApplicationsPath, "Applications.xml");
|
|
if (File.Exists(catsPath))
|
|
{
|
|
categories = new List<ApplicationCategory>();
|
|
|
|
// parse file
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.Load(catsPath);
|
|
|
|
XmlNodeList nodesCategories = doc.SelectNodes("categories/category");
|
|
foreach (XmlNode nodeCategory in nodesCategories)
|
|
{
|
|
ApplicationCategory category = new ApplicationCategory();
|
|
category.Id = nodeCategory.Attributes["id"].Value;
|
|
category.Name = GetNodeValue(nodeCategory, "name", category.Id);
|
|
categories.Add(category);
|
|
|
|
// read applications
|
|
List<string> catApps = new List<string>();
|
|
XmlNodeList nodesApps = nodeCategory.SelectNodes("applications/application");
|
|
foreach (XmlNode nodeApp in nodesApps)
|
|
catApps.Add(nodeApp.Attributes["name"].Value);
|
|
category.Applications = catApps.ToArray();
|
|
}
|
|
}
|
|
|
|
// place to the cache
|
|
if (HttpContext.Current != null)
|
|
HttpContext.Current.Cache.Insert(key, categories, new CacheDependency(catsPath));
|
|
}
|
|
|
|
return categories;
|
|
}
|
|
|
|
public static List<ApplicationInfo> GetApplications(int packageId)
|
|
{
|
|
return GetApplications(packageId, null);
|
|
}
|
|
|
|
public static List<ApplicationInfo> GetApplications(int packageId, string categoryId)
|
|
{
|
|
string key = "WebApplicationsList";
|
|
|
|
Dictionary<string, ApplicationInfo> apps = null;
|
|
|
|
// look up in the cache
|
|
if(HttpContext.Current != null)
|
|
apps = (Dictionary<string, ApplicationInfo>)HttpContext.Current.Cache[key];
|
|
|
|
if (apps == null)
|
|
{
|
|
// create apps list
|
|
apps = new Dictionary<string, ApplicationInfo>();
|
|
|
|
string appsRoot = ConfigSettings.WebApplicationsPath;
|
|
string[] dirs = Directory.GetDirectories(appsRoot);
|
|
foreach (string dir in dirs)
|
|
{
|
|
string appFile = Path.Combine(dir, "Application.xml");
|
|
|
|
if (!File.Exists(appFile))
|
|
continue;
|
|
|
|
// read and parse web applications xml file
|
|
XmlDocument doc = new XmlDocument();
|
|
doc.Load(appFile);
|
|
|
|
XmlNode nodeApp = doc.SelectSingleNode("//application");
|
|
|
|
string appFolder = dir;
|
|
|
|
// parse node
|
|
ApplicationInfo app = CreateApplicationInfoFromXml(appFolder, nodeApp);
|
|
|
|
// add to the collection
|
|
apps.Add(app.Id, app);
|
|
}
|
|
|
|
// place to the cache
|
|
if (HttpContext.Current != null)
|
|
HttpContext.Current.Cache.Insert(key, apps, new CacheDependency(appsRoot));
|
|
}
|
|
|
|
// filter applications based on category
|
|
List<ApplicationInfo> categoryApps = new List<ApplicationInfo>();
|
|
|
|
// check if the application fits requirements
|
|
PackageContext cntx = PackageController.GetPackageContext(packageId);
|
|
|
|
List<ApplicationCategory> categories = GetCategories();
|
|
foreach (ApplicationCategory category in categories)
|
|
{
|
|
// skip category if required
|
|
if (!String.IsNullOrEmpty(categoryId)
|
|
&& String.Compare(category.Id, categoryId, true) != 0)
|
|
continue;
|
|
|
|
// iterate through applications
|
|
foreach (string appId in category.Applications)
|
|
{
|
|
if (apps.ContainsKey(appId)
|
|
&& IsApplicattionFitsRequirements(cntx, apps[appId]))
|
|
categoryApps.Add(apps[appId]);
|
|
}
|
|
}
|
|
|
|
return categoryApps;
|
|
}
|
|
|
|
public static ApplicationInfo GetApplication(int packageId, string applicationId)
|
|
{
|
|
// get all applications
|
|
List<ApplicationInfo> apps = GetApplications(packageId);
|
|
|
|
// check if the application fits requirements
|
|
PackageContext cntx = PackageController.GetPackageContext(packageId);
|
|
|
|
// find the application
|
|
foreach (ApplicationInfo app in apps)
|
|
{
|
|
if (app.Id.ToLower() == applicationId.ToLower())
|
|
{
|
|
return IsApplicattionFitsRequirements(cntx, app) ? app : null;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static bool IsApplicattionFitsRequirements(PackageContext cntx, ApplicationInfo app)
|
|
{
|
|
if (app.Requirements == null)
|
|
return true; // empty requirements
|
|
|
|
foreach (ApplicationRequirement req in app.Requirements)
|
|
{
|
|
// check if this is a group
|
|
if (req.Groups != null)
|
|
{
|
|
bool groupFits = false;
|
|
foreach (string group in req.Groups)
|
|
{
|
|
if (cntx.Groups.ContainsKey(group))
|
|
{
|
|
groupFits = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!groupFits)
|
|
return false;
|
|
}
|
|
|
|
// check if this is a quota
|
|
if (req.Quotas != null)
|
|
{
|
|
bool quotaFits = false;
|
|
foreach (string quota in req.Quotas)
|
|
{
|
|
if (cntx.Quotas.ContainsKey(quota) &&
|
|
!cntx.Quotas[quota].QuotaExhausted)
|
|
{
|
|
quotaFits = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!quotaFits)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#region private helper methods
|
|
private static ApplicationInfo CreateApplicationInfoFromXml(string appFolder, XmlNode nodeApp)
|
|
{
|
|
ApplicationInfo app = new ApplicationInfo();
|
|
|
|
// category name
|
|
app.CategoryName = GetNodeValue(nodeApp, "category", "");
|
|
|
|
// attributes
|
|
app.Id = nodeApp.Attributes["id"].Value;
|
|
app.Codebase = nodeApp.Attributes["codebase"].Value;
|
|
app.SettingsControl = nodeApp.Attributes["settingsControl"].Value;
|
|
app.Folder = appFolder;
|
|
|
|
// child nodes
|
|
app.Name = GetNodeValue(nodeApp, "name", "");
|
|
app.ShortDescription = GetNodeValue(nodeApp, "shortDescription", "");
|
|
app.FullDescription = GetNodeValue(nodeApp, "fullDescription", "");
|
|
app.Logo = GetNodeValue(nodeApp, "logo", "");
|
|
app.Version = GetNodeValue(nodeApp, "version", "");
|
|
app.Size = Int32.Parse(GetNodeValue(nodeApp, "size", "0"));
|
|
app.HomeSite = GetNodeValue(nodeApp, "homeSite", "");
|
|
app.SupportSite = GetNodeValue(nodeApp, "supportSite", "");
|
|
app.DocsSite = GetNodeValue(nodeApp, "docSite", "");
|
|
|
|
app.Manufacturer = GetNodeValue(nodeApp, "manufacturer", "");
|
|
app.License = GetNodeValue(nodeApp, "license", "");
|
|
|
|
// process codebase path
|
|
app.Codebase = Path.Combine(appFolder, app.Codebase);
|
|
|
|
// web settings
|
|
List<ApplicationWebSetting> settings = new List<ApplicationWebSetting>();
|
|
XmlNodeList nodesWebSettings = nodeApp.SelectNodes("webSettings/add");
|
|
foreach (XmlNode nodeSetting in nodesWebSettings)
|
|
{
|
|
ApplicationWebSetting setting = new ApplicationWebSetting();
|
|
setting.Name = nodeSetting.Attributes["name"].Value;
|
|
setting.Value = nodeSetting.Attributes["value"].Value;
|
|
settings.Add(setting);
|
|
}
|
|
app.WebSettings = settings.ToArray();
|
|
|
|
// requirements
|
|
List<ApplicationRequirement> requirements = new List<ApplicationRequirement>();
|
|
XmlNodeList nodesRequirements = nodeApp.SelectNodes("requirements/add");
|
|
foreach (XmlNode nodesRequirement in nodesRequirements)
|
|
{
|
|
ApplicationRequirement req = new ApplicationRequirement();
|
|
|
|
if (nodesRequirement.Attributes["group"] != null)
|
|
req.Groups = nodesRequirement.Attributes["group"].Value.Split('|');
|
|
|
|
if (nodesRequirement.Attributes["quota"] != null)
|
|
req.Quotas = nodesRequirement.Attributes["quota"].Value.Split('|');
|
|
|
|
req.Display = true;
|
|
if (nodesRequirement.Attributes["display"] != null)
|
|
req.Display = Utils.ParseBool(nodesRequirement.Attributes["display"].Value, true);
|
|
|
|
requirements.Add(req);
|
|
}
|
|
app.Requirements = requirements.ToArray();
|
|
|
|
return app;
|
|
}
|
|
|
|
private static string GetNodeValue(XmlNode parentNode, string nodeName, string defaultValue)
|
|
{
|
|
XmlNode node = parentNode.SelectSingleNode(nodeName);
|
|
if (node != null)
|
|
{
|
|
return node.InnerText.Trim();
|
|
}
|
|
|
|
// return default value
|
|
return defaultValue;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|