/// <summary> /// Get the specified asset from the pak file /// </summary> public Stream GetAsset(string id, string assetPath) { var pkg = this.m_configuration.Repository.GetRepository().Get(id, null); if (pkg == null) { throw new KeyNotFoundException($"Package {id} not found"); } // Open the package var unpack = pkg.Unpack(); var assetObject = unpack.Assets.FirstOrDefault(o => o.Name == $"{assetPath}"); if (assetObject == null) { throw new FileNotFoundException(); } RestOperationContext.Current.OutgoingResponse.ContentType = assetObject.MimeType; byte[] content; if (assetObject.Content is byte[] bytea) { content = bytea; } else if (assetObject.Content is String stra) { content = System.Text.Encoding.UTF8.GetBytes(stra); } else if (assetObject.Content is XElement xela) { content = System.Text.Encoding.UTF8.GetBytes(xela.ToString()); } else if (assetObject.Content is AppletAssetHtml html) { content = System.Text.Encoding.UTF8.GetBytes(html.Html.ToString()); } else { throw new InvalidOperationException("Cannot render this type of data"); } if (Encoding.UTF8.GetString(content as byte[], 0, 4) == "LZIP") { using (var ms = new MemoryStream(content as byte[])) using (var ls = new SharpCompress.Compressors.LZMA.LZipStream(ms, SharpCompress.Compressors.CompressionMode.Decompress)) { var oms = new MemoryStream(); ls.CopyTo(oms); oms.Seek(0, SeekOrigin.Begin); return(oms); } } else { return(new MemoryStream(content)); } }
/// <summary> /// Compresses the Application and writes it into the Resource file. /// Also generates some code related to the Application launching. /// </summary> /// <param name="writer"></param> /// <returns></returns> private static string CompressApplication(ResourceWriter writer) { //this is what the application is called in the resource file. const string APPLICATION_NAME = "app"; //generates a temporary file to compress into. FileStream stream = File.Open(outputFile + "temp_c.dat", FileMode.Create); //opens the input file (which has been merged with some other dlls). FileStream stream2 = File.OpenRead(outputFile + "_temp"); //sets the mode to follow when it generates the code. It swaps between G-Zip and LZMA. string mode; long appSize = stream2.Length; Stream gStream = null; if (gzOr7z) { // lzipstream gStream = new SharpCompress.Compressors.LZMA.LZipStream(stream, SharpCompress.Compressors.CompressionMode.Compress); mode = "SharpCompress.Compressors.LZMA.LZipStream(memStr, SharpCompress.Compressors.CompressionMode.Decompress)"; } else { //gzip gStream = new GZipStream(stream, CompressionLevel.Optimal); mode = "GZipStream(memStr, CompressionMode.Decompress)"; // gStream = stream; //mode = $"MemoryStream({Convert.ToInt32(stream2.Length)})"; } //compresses, not the most efficient way to compress, I should buffer it, but it doesn't matter. while (stream2.Position < stream2.Length) { gStream.WriteByte((byte)stream2.ReadByte()); } //closes each of the streams. gStream.Close(); stream.Close(); //closing this one manually because some compression libraries don't have the wrapper close the stream passed into it. stream2.Close(); //add the resource to the file. // writer.AddResource(APPLICATION_NAME, File.ReadAllBytes(outputFile + "temp_c.dat")); var s = Encoding.UTF8.GetString(File.ReadAllBytes(outputFile + "temp_c.dat")); appSize = s.Length; writer.AddResource(APPLICATION_NAME, s); //add the code. string code = manager.GetString("AppMethod").Replace("%appname%", APPLICATION_NAME).Replace("%appsize%", "" + appSize).Replace("%mode%", mode); return(code); }
/// <summary> /// Compress content /// </summary> internal static byte[] CompressContent(object content) { using (var ms = new MemoryStream()) { using (var cs = new SharpCompress.Compressors.LZMA.LZipStream(ms, SharpCompress.Compressors.CompressionMode.Compress)) { byte[] data = content as byte[]; if (data == null) { data = System.Text.Encoding.UTF8.GetBytes(content.ToString()); } cs.Write(data, 0, data.Length); } return(ms.ToArray()); } }
/// <summary> /// Render asset content /// </summary> public byte[] RenderAssetContent(AppletAsset asset, string preProcessLocalization = null, bool staticScriptRefs = true, bool allowCache = true, IDictionary <String, String> bindingParameters = null) { // TODO: This method needs to be cleaned up since it exists from the old/early OpenIZ days // First, is there an object already byte[] cacheObject = null; string assetPath = String.Format("{0}?lang={1}", asset.ToString(), preProcessLocalization); var cacheKey = $"{assetPath};{String.Join(";", bindingParameters?.Select(o => $"{o.Key}={o.Value}") ?? new string[0] { })}"; if (allowCache && this.CachePages && s_cache.TryGetValue(cacheKey, out cacheObject)) { return(cacheObject); } // Resolve content var content = asset.Content; if (content == null && this.Resolver != null) { content = this.Resolver(asset); } if (content is String) // Content is a string { // Inject CSP if (asset.MimeType == "text/javascript" || asset.MimeType == "application/json") { var retVal = content as String; if (bindingParameters != null) { retVal = this.m_bindingRegex.Replace(retVal, (m) => bindingParameters.TryGetValue(m.Groups[1].Value, out string v) ? v : m.ToString()); } cacheObject = Encoding.UTF8.GetBytes(retVal); if (allowCache) { s_cache.TryAdd(cacheKey, cacheObject); } return(cacheObject); } else { return(Encoding.UTF8.GetBytes(content as String)); } } else if (content is byte[]) // Content is a binary asset { // is the content compressed? if (Encoding.UTF8.GetString(content as byte[], 0, 4) == "LZIP") { using (var ms = new MemoryStream(content as byte[])) using (var ls = new SharpCompress.Compressors.LZMA.LZipStream(new NonDisposingStream(ms), SharpCompress.Compressors.CompressionMode.Decompress)) using (var oms = new MemoryStream()) { byte[] buffer = new byte[2048]; int br = 1; while (br > 0) { br = ls.Read(buffer, 0, 2048); oms.Write(buffer, 0, br); } content = oms.ToArray(); if (allowCache) { s_cache.TryAdd(cacheKey, content as byte[]); } return(content as byte[]); } } else { return(content as byte[]); } } else if (content is XElement) // Content is XML { using (MemoryStream ms = new MemoryStream()) using (XmlWriter xw = XmlWriter.Create(ms)) { (content as XElement).WriteTo(xw); xw.Flush(); ms.Flush(); return(ms.ToArray()); } } else if (content is AppletAssetHtml) // Content is HTML { // Is the content HTML? var sourceAsset = content as AppletAssetHtml; var htmlAsset = new AppletAssetHtml() { Html = new XElement(sourceAsset.Html), Layout = sourceAsset.Layout, Script = new List <AssetScriptReference>(sourceAsset.Script), Titles = new List <LocaleString>(sourceAsset.Titles), Style = new List <string>(sourceAsset.Style) }; XElement htmlContent = null; if (htmlAsset.Static) { htmlContent = htmlAsset.Html as XElement; } else { // Type of tag to render basic content switch (htmlAsset.Html.Name.LocalName) { case "html": // The content is a complete HTML page { htmlContent = htmlAsset.Html as XElement; var headerInjection = this.GetInjectionHeaders(asset, htmlContent.DescendantNodes().OfType <XElement>().Any(o => o.Name == xs_xhtml + "ui-view")); // STRIP - SanteDBJS references var xel = htmlContent.Descendants().OfType <XElement>().Where(o => o.Name == xs_xhtml + "script" && o.Attribute("src")?.Value.Contains("SanteDB") == true).ToArray(); var head = htmlContent.DescendantNodes().OfType <XElement>().FirstOrDefault(o => o.Name == xs_xhtml + "head"); if (head == null) { head = new XElement(xs_xhtml + "head"); htmlContent.Add(head); } head.Add(headerInjection.Where(o => !head.Elements(o.Name).Any(e => (e.Attributes("src") != null && (e.Attributes("src") == o.Attributes("src"))) || (e.Attributes("href") != null && (e.Attributes("href") == o.Attributes("href")))))); // Inject any business rules as static refs var body = htmlContent.DescendantNodes().OfType <XElement>().FirstOrDefault(o => o.Name == xs_xhtml + "body"); if (body != null) { body.Add( this.SelectMany(o => o.Assets.Where(a => a.Name.StartsWith("rules/"))).Select(o => new XElement(xs_xhtml + "script", new XAttribute("src", $"/{o.Manifest.Info.Id}/{o.Name}"), new XAttribute("type", "text/javascript"), new XAttribute("nonce", bindingParameters.TryGetValue("csp_nonce", out string nonce) ? nonce : ""), new XText("// Script reference"))) ); } // head.Add(headerInjection); break; } case "body": // The content is an HTML Body element, we must inject the HTML header { htmlContent = htmlAsset.Html as XElement; // Inject special headers var headerInjection = this.GetInjectionHeaders(asset, htmlContent.DescendantNodes().OfType <XElement>().Any(o => o.Name == xs_xhtml + "ui-view")); // Render the bundles var bodyElement = htmlAsset.Html as XElement; htmlContent = new XElement(xs_xhtml + "html", new XAttribute("ng-app", asset.Name), new XElement(xs_xhtml + "head", headerInjection), bodyElement); } break; default: { if (String.IsNullOrEmpty(htmlAsset.Layout)) { htmlContent = htmlAsset.Html as XElement; } else { // Get the layout var layoutAsset = this.ResolveAsset(htmlAsset.Layout, relativeAsset: asset); if (layoutAsset == null) { throw new FileNotFoundException(String.Format("Layout asset {0} not found", htmlAsset.Layout)); } using (MemoryStream ms = new MemoryStream(this.RenderAssetContent(layoutAsset, preProcessLocalization, bindingParameters: bindingParameters))) htmlContent = XDocument.Load(ms).FirstNode as XElement; // Find the <!--#include virtual="content" --> tag var contentNode = htmlContent.DescendantNodes().OfType <XComment>().SingleOrDefault(o => o.Value.Trim() == "#include virtual=\"content\""); if (contentNode != null) { contentNode.AddAfterSelf(htmlAsset.Html as XElement); contentNode.Remove(); } // Injection headers var headerInjection = this.GetInjectionHeaders(asset, htmlContent.DescendantNodes().OfType <XElement>().Any(o => o.Name == xs_xhtml + "ui-view")); var headElement = (htmlContent.Element(xs_xhtml + "head") as XElement); headElement?.Add(headerInjection.Where(o => !headElement.Elements(o.Name).Any(e => (e.Attributes("src") != null && (e.Attributes("src") == o.Attributes("src"))) || (e.Attributes("href") != null && (e.Attributes("href") == o.Attributes("href")))))); } } break; } // switch // Now process SSI directives - <!--#include virtual="XXXXXXX" --> var includes = htmlContent.DescendantNodes().OfType <XComment>().Where(o => o?.Value?.Trim().StartsWith("#include virtual=\"") == true).ToList(); foreach (var inc in includes) { String assetName = inc.Value.Trim().Substring(18); // HACK: Should be a REGEX if (assetName.EndsWith("\"")) { assetName = assetName.Substring(0, assetName.Length - 1); } if (assetName == "content") { continue; } var includeAsset = this.ResolveAsset(assetName, relativeAsset: asset); if (includeAsset == null) { inc.AddAfterSelf(new XElement(xs_xhtml + "strong", new XText(String.Format("{0} NOT FOUND", assetName)))); inc.Remove(); } else { using (MemoryStream ms = new MemoryStream(this.RenderAssetContent(includeAsset, preProcessLocalization, bindingParameters: bindingParameters))) { try { var xel = XDocument.Load(ms).Elements().First() as XElement; if (xel.Name == xs_xhtml + "html") { inc.AddAfterSelf(xel.Element(xs_xhtml + "body").Elements()); } else { //var headerInjection = this.GetInjectionHeaders(includeAsset); //var headElement = htmlContent.Element(xs_xhtml + "head"); //headElement?.Add(headerInjection.Where(o => !headElement.Elements(o.Name).Any(e => (e.Attributes("src") != null && (e.Attributes("src") == o.Attributes("src"))) || (e.Attributes("href") != null && (e.Attributes("href") == o.Attributes("href")))))); inc.AddAfterSelf(xel); } inc.Remove(); } catch (Exception e) { throw new XmlException($"Error in Asset: {includeAsset}", e); } } } } // Re-write foreach (var itm in htmlContent.DescendantNodes().OfType <XElement>().SelectMany(o => o.Attributes()).Where(o => o.Value.StartsWith("~"))) { itm.Value = String.Format("/{0}/{1}", asset.Manifest.Info.Id, itm.Value.Substring(2)); //itm.Value = itm.Value.Replace(APPLET_SCHEME, this.AppletBase).Replace(ASSET_SCHEME, this.AssetBase).Replace(DRAWABLE_SCHEME, this.DrawableBase); } // Render Title var headTitle = htmlContent.DescendantNodes().OfType <XElement>().FirstOrDefault(o => o.Name == xs_xhtml + "head"); var title = htmlAsset.GetTitle(preProcessLocalization); if (headTitle != null && !String.IsNullOrEmpty(title)) { headTitle.Add(new XElement(xs_xhtml + "title", new XText(title))); } } // Render out the content using (StringWriter sw = new StringWriter()) using (XmlWriter xw = XmlWriter.Create(sw, new XmlWriterSettings() { OmitXmlDeclaration = true })) { htmlContent.WriteTo(xw); xw.Flush(); String retVal = sw.ToString(); if (!String.IsNullOrEmpty(preProcessLocalization)) { var assetString = ApplicationServiceContext.Current.GetService <ILocalizationService>().GetStrings(preProcessLocalization); retVal = this.m_localizationRegex.Replace(retVal, (m) => assetString.FirstOrDefault(o => o.Key == m.Groups[1].Value).Value ?? m.Groups[1].Value); } // Binding objects if (bindingParameters != null) { retVal = this.m_bindingRegex.Replace(retVal, (m) => bindingParameters.TryGetValue(m.Groups[1].Value, out string v) ? v : m.ToString()); } var byteData = Encoding.UTF8.GetBytes(retVal); // Add to cache if (allowCache) { s_cache.TryAdd(cacheKey, byteData); } return(byteData); } } else if (content is AppletAssetVirtual virtualContent) // Virtual asset { if (!s_cache.TryGetValue(assetPath, out byte[] data))