// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-#if NET_2_0
+
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
+using System.Security.Cryptography;
using System.Reflection;
using System.Text;
using System.Web.Configuration;
using System.Web.Util;
using System.Web.Hosting;
-namespace System.Web.Compilation {
+namespace System.Web.Compilation
+{
class CompileUnitPartialType
{
public readonly CodeCompileUnit Unit;
this.Unit = unit;
}
}
+
+ interface ICodePragmaGenerator
+ {
+ int ReserveSpace (string filename);
+ void DecorateFile (string path, string filename, MD5 checksum, Encoding enc);
+ }
+
+ class CSharpCodePragmaGenerator : ICodePragmaGenerator
+ {
+ // Copied from CSharpCodeGenerator.cs
+ string QuoteSnippetString (string value)
+ {
+ // FIXME: this is weird, but works.
+ string output = value.Replace ("\\", "\\\\");
+ output = output.Replace ("\"", "\\\"");
+ output = output.Replace ("\t", "\\t");
+ output = output.Replace ("\r", "\\r");
+ output = output.Replace ("\n", "\\n");
+
+ return "\"" + output + "\"";
+ }
+
+ string ChecksumToHex (MD5 checksum)
+ {
+ var ret = new StringBuilder ();
+ foreach (byte b in checksum.Hash)
+ ret.Append (b.ToString ("X2"));
+
+ return ret.ToString ();
+ }
+
+ const int pragmaChecksumStaticCount = 23;
+ const int pragmaLineStaticCount = 8;
+ const int md5ChecksumCount = 32;
+
+ public int ReserveSpace (string filename)
+ {
+ return pragmaChecksumStaticCount +
+ pragmaLineStaticCount +
+ md5ChecksumCount +
+ (QuoteSnippetString (filename).Length * 2) +
+ (Environment.NewLine.Length * 3) +
+ BaseCompiler.HashMD5.ToString ("B").Length;
+ }
+
+ public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
+ {
+ string newline = Environment.NewLine;
+ var sb = new StringBuilder ();
+
+ sb.AppendFormat ("#pragma checksum {0} \"{1}\" \"{2}\"{3}{3}",
+ QuoteSnippetString (filename),
+ BaseCompiler.HashMD5.ToString ("B"),
+ ChecksumToHex (checksum),
+ newline);
+ sb.AppendFormat ("#line 1 {0}{1}", QuoteSnippetString (filename), newline);
+
+ byte[] bytes = enc.GetBytes (sb.ToString ());
+ using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
+ fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
+ fs.Write (bytes, 0, bytes.Length);
+ bytes = null;
+
+ sb.Length = 0;
+ sb.AppendFormat ("{0}#line default{0}#line hidden{0}", newline);
+ bytes = Encoding.UTF8.GetBytes (sb.ToString ());
+
+ fs.Seek (0, SeekOrigin.End);
+ fs.Write (bytes, 0, bytes.Length);
+ }
+
+ sb = null;
+ bytes = null;
+ }
+ }
+
+ class VBCodePragmaGenerator : ICodePragmaGenerator
+ {
+ const int pragmaExternalSourceCount = 21;
+ public int ReserveSpace (string filename)
+ {
+ return pragmaExternalSourceCount +
+ filename.Length +
+ (Environment.NewLine.Length);
+ }
+
+ public void DecorateFile (string path, string filename, MD5 checksum, Encoding enc)
+ {
+ string newline = Environment.NewLine;
+ var sb = new StringBuilder ();
+
+ sb.AppendFormat ("#ExternalSource(\"{0}\",1){1}", filename, newline);
+ byte[] bytes = enc.GetBytes (sb.ToString ());
+ using (FileStream fs = new FileStream (path, FileMode.Open, FileAccess.Write)) {
+ fs.Seek (enc.GetPreamble ().Length, SeekOrigin.Begin);
+ fs.Write (bytes, 0, bytes.Length);
+ bytes = null;
+
+ sb.Length = 0;
+ sb.AppendFormat ("{0}#End ExternalSource{0}", newline);
+ bytes = enc.GetBytes (sb.ToString ());
+ fs.Seek (0, SeekOrigin.End);
+ fs.Write (bytes, 0, bytes.Length);
+ }
+ sb = null;
+ bytes = null;
+ }
+ }
const string DEFAULT_ASSEMBLY_BASE_NAME = "App_Web_";
const int COPY_BUFFER_SIZE = 8192;
units = new List <CodeUnit> ();
CompilationSection section;
- if (virtualPath != null)
- section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation", virtualPath.Absolute);
- else
- section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
+ section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
string tempdir = section.TempDirectory;
if (String.IsNullOrEmpty (tempdir))
tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
{
AddCodeFile (path, bp, false);
}
-
+
+ // The kludge of using ICodePragmaGenerator for C# and VB code files is bad, but
+ // it's better than allowing for potential DoS while reading a file with arbitrary
+ // size in memory for use with the CodeSnippetCompileUnit class.
+ // Files with extensions other than .cs and .vb use CodeSnippetCompileUnit.
internal void AddCodeFile (string path, BuildProvider bp, bool isVirtual)
{
if (String.IsNullOrEmpty (path))
return; // maybe better to throw an exception here?
extension = extension.Substring (1);
string filename = GetTempFilePhysicalPath (extension);
-
+ ICodePragmaGenerator pragmaGenerator;
+
+ switch (extension.ToLowerInvariant ()) {
+ case "cs":
+ pragmaGenerator = new CSharpCodePragmaGenerator ();
+ break;
+
+ case "vb":
+ pragmaGenerator = new VBCodePragmaGenerator ();
+ break;
+
+ default:
+ pragmaGenerator = null;
+ break;
+ }
+
if (isVirtual) {
VirtualFile vf = HostingEnvironment.VirtualPathProvider.GetFile (path);
if (vf == null)
throw new HttpException (404, "Virtual file '" + path + "' does not exist.");
- CopyFile (vf.Open (), filename);
+ if (vf is DefaultVirtualFile)
+ path = HostingEnvironment.MapPath (path);
+ CopyFileWithChecksum (vf.Open (), filename, path, pragmaGenerator);
} else
- CopyFile (path, filename);
+ CopyFileWithChecksum (path, filename, path, pragmaGenerator);
- if (bp != null)
- AddPathToBuilderMap (filename, bp);
+ if (pragmaGenerator != null) {
+ if (bp != null)
+ AddPathToBuilderMap (filename, bp);
- SourceFiles.Add (filename);
+ SourceFiles.Add (filename);
+ }
}
- void CopyFile (string input, string filename)
+ void CopyFileWithChecksum (string input, string to, string from, ICodePragmaGenerator pragmaGenerator)
{
- CopyFile (new FileStream (input, FileMode.Open, FileAccess.Read), filename);
+ CopyFileWithChecksum (new FileStream (input, FileMode.Open, FileAccess.Read), to, from, pragmaGenerator);
}
- void CopyFile (Stream input, string filename)
+ void CopyFileWithChecksum (Stream input, string to, string from, ICodePragmaGenerator pragmaGenerator)
{
- using (StreamWriter sw = new StreamWriter (new FileStream (filename, FileMode.Create, FileAccess.Write), Encoding.UTF8)) {
+ if (pragmaGenerator == null) {
+ // This is BAD, BAD, BAD! CodeDOM API is really no good in this
+ // instance.
+ string filedata;
using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
- sw.Write (sr.ReadToEnd ());
+ filedata = sr.ReadToEnd ();
+ }
+
+ var snippet = new CodeSnippetCompileUnit (filedata);
+ snippet.LinePragma = new CodeLinePragma (from, 1);
+ filedata = null;
+ AddCodeCompileUnit (snippet);
+ snippet = null;
+
+ return;
+ }
+
+ MD5 checksum = MD5.Create ();
+ using (FileStream fs = new FileStream (to, FileMode.Create, FileAccess.Write)) {
+ using (StreamWriter sw = new StreamWriter (fs, Encoding.UTF8)) {
+ using (StreamReader sr = new StreamReader (input, WebEncoding.FileEncoding)) {
+ int count = pragmaGenerator.ReserveSpace (from);
+ char[] src;
+
+ if (count > COPY_BUFFER_SIZE)
+ src = new char [count];
+ else
+ src = new char [COPY_BUFFER_SIZE];
+
+ sw.Write (src, 0, count);
+ do {
+ count = sr.Read (src, 0, COPY_BUFFER_SIZE);
+ if (count == 0) {
+ UpdateChecksum (src, 0, checksum, true);
+ break;
+ }
+
+ sw.Write (src, 0, count);
+ UpdateChecksum (src, count, checksum, false);
+ } while (true);
+ src = null;
+ }
}
}
+ pragmaGenerator.DecorateFile (to, from, checksum, Encoding.UTF8);
+ }
+
+ void UpdateChecksum (char[] buf, int count, MD5 checksum, bool final)
+ {
+ byte[] input = Encoding.UTF8.GetBytes (buf, 0, count);
+
+ if (final)
+ checksum.TransformFinalBlock (input, 0, input.Length);
+ else
+ checksum.TransformBlock (input, 0, input.Length, input, 0);
+ input = null;
}
public Stream CreateEmbeddedResource (BuildProvider buildProvider, string name)
{
if (options == null)
throw new ArgumentNullException ("options");
-
options.TempFiles = temp_files;
if (options.OutputAssembly == null)
options.OutputAssembly = OutputAssemblyName;
if (units.Length == 0 && files.Count == 0 && resources.Count == 0 && options.EmbeddedResources.Count == 0)
return null;
+
+ string compilerOptions = options.CompilerOptions;
+ if (options.IncludeDebugInformation) {
+ if (String.IsNullOrEmpty (compilerOptions))
+ compilerOptions = "/d:DEBUG";
+ else if (compilerOptions.IndexOf ("d:DEBUG", StringComparison.OrdinalIgnoreCase) == -1)
+ compilerOptions += " /d:DEBUG";
+
+ options.CompilerOptions = compilerOptions;
+ }
+
+ if (String.IsNullOrEmpty (compilerOptions))
+ compilerOptions = "/noconfig";
+ else if (compilerOptions.IndexOf ("noconfig", StringComparison.OrdinalIgnoreCase) == -1)
+ compilerOptions += " /noconfig";
+ options.CompilerOptions = compilerOptions;
string filename;
StreamWriter sw = null;
options.EmbeddedResources.Add (de.Value);
AddAssemblyReference (BuildManager.GetReferencedAssemblies ());
+ List <Assembly> referencedAssemblies = ReferencedAssemblies;
+ StringCollection optRefAsm = options.ReferencedAssemblies;
+ Type appType = HttpApplicationFactory.AppType;
+ if (appType != null && !referencedAssemblies.Contains (appType.Assembly))
+ referencedAssemblies.Add (appType.Assembly);
+
foreach (Assembly refasm in ReferencedAssemblies) {
string path = new Uri (refasm.CodeBase).LocalPath;
- options.ReferencedAssemblies.Add (path);
+ string originalPath = refasm.Location;
+ if (!optRefAsm.Contains (path) && !optRefAsm.Contains (originalPath))
+ optRefAsm.Add (path);
}
+
+
results = provider.CompileAssemblyFromFile (options, files.ToArray ());
if (results.NativeCompilerReturnValue != 0) {
string fileText = null;
+ CompilerErrorCollection errors = results.Errors;
try {
- using (StreamReader sr = File.OpenText (results.Errors [0].FileName)) {
- fileText = sr.ReadToEnd ();
+ if (errors != null && errors.Count > 0) {
+ using (StreamReader sr = File.OpenText (results.Errors [0].FileName))
+ fileText = sr.ReadToEnd ();
}
} catch (Exception) {}
Console.WriteLine ("\nErrors:");
foreach (CompilerError err in results.Errors)
Console.WriteLine (err);
- Console.WriteLine ("File name: {0}", results.Errors [0].FileName);
- Console.WriteLine ("File text:\n{0}\n", fileText);
+ if (errors != null && errors.Count > 0)
+ Console.WriteLine ("File name: {0}", results.Errors [0].FileName);
+ else
+ Console.WriteLine ("File name not available");
+ if (!String.IsNullOrEmpty (fileText))
+ Console.WriteLine ("File text:\n{0}\n", fileText);
+ else
+ Console.WriteLine ("No file text available");
Console.WriteLine ("********************************************************************");
#endif
-
throw new CompilationException (virtualPath != null ? virtualPath.Original : String.Empty, results, fileText);
}
}
}
}
-#endif
+