[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")] // the writer is disposed for all code paths internal static void CopyStream(Stream fromStream, ImpersonationBehavior readImpersonationBehavior, Stream toStream, ImpersonationBehavior writeImpersonationBehavior) { // An addition revert during write operations is required if the setting for reading and writing // is not the same bool requiresWriteRevert = (readImpersonationBehavior != writeImpersonationBehavior); using (ImpersonateIdentity readId = new ImpersonateIdentity(readImpersonationBehavior)) { byte[] bytesIn = new byte[65536]; int bytesRead; while ((bytesRead = fromStream.Read(bytesIn, 0, bytesIn.Length)) != 0) { // If we have to impersonate to write, then do it. Otherwise skip it. if (requiresWriteRevert) { using (ImpersonateIdentity id = new ImpersonateIdentity(writeImpersonationBehavior)) { toStream.Write(bytesIn, 0, bytesRead); } } else { toStream.Write(bytesIn, 0, bytesRead); } } } }
/// <summary> /// Writes the requested file from the package directly to the response. /// This method clears the response before sending the file and provides an option to skip /// package validation. /// </summary> /// <param name="filePath"></param> /// <param name="response"></param> /// <param name="skipValidation"></param> internal void TransmitFile(string filePath, HttpResponse response, bool skipValidation) { Utilities.ValidateParameterNonNull("filePath", filePath); Utilities.ValidateParameterNotEmpty("filePath", filePath); Utilities.ValidateParameterNonNull("response", response); if (!skipValidation) { using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { // If user does not have access, a UnauthorizedAccess Exception is thrown Directory.GetFiles(m_packageBasePath); // Check if package has valid e-learning content if (!ManifestExists()) throw new InvalidPackageException(Resources.ImsManifestXmlMissing); } } string absoluteFilePath = SafePathCombine(m_packageBasePath, filePath); response.Clear(); response.Buffer = false; response.BufferOutput = false; using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { response.TransmitFile(absoluteFilePath); } }
/// <summary> /// Gets the collection of file paths in the package, with the option to skip package validation. /// </summary> internal ReadOnlyCollection<string> GetFilePaths(bool skipValidation) { ReadOnlyCollection<string> filePaths = null; using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { // If the user does not have access, the UnauthorizedAccess Exception is thrown Directory.GetFiles(m_packageBasePath); // If validation is requested, check if package has valid e-learning content if ((!skipValidation) && (!ManifestExists())) throw new InvalidPackageException(Resources.ImsManifestXmlMissing); filePaths = GetFilePaths(new DirectoryInfo(m_packageBasePath)); } return filePaths; }
/// <summary> /// Gets a value indicating whether the file exists in the package, providing the option to /// validate the package. /// </summary> internal bool FileExists(string filePath, bool skipValidation) { bool fileExists = false; try { if (!skipValidation) { // If the current user has access problems with the package, the package is not Validated using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { // Check if user has access to the files Directory.GetFiles(m_packageBasePath); // Check if package has valid e-learning content if (!ManifestExists()) throw new InvalidPackageException(Resources.ImsManifestXmlMissing); } } // SafePathCombine throws the ArgumentException and ArgumentNullException noted above. // File.Exists will throw UnauthorizedAccessException if user does not have FileIOPermission. string path; using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { path = SafePathCombine(m_packageBasePath, filePath); fileExists = File.Exists(path); // imsmanifest.xml is special, since it is created from index.xml. if (!fileExists && IsImsManifest(filePath)) { // one more chance for imsmanifest.xml - if it doesn't exist as "imsmanifest.xml" it // can still exist as a conversion from index.xml, so return true if index.xml exists. fileExists = ManifestExists(); } } } // catch exceptions that should be converted into a "false" file exists. catch (DirectoryNotFoundException) { } catch (ArgumentException) { } catch (NotSupportedException) { } catch (SecurityException) { } catch (IOException) { } catch (UnauthorizedAccessException) { } return fileExists; }
/// <summary> /// Shared helper function to read a file from the package location and wrap the resulting exceptions /// to be consistent within MLC. /// </summary> /// <param name="packagePath">The path to the root folder of the package.</param> /// <param name="filePath">The package-relative path of the file within the package.</param> /// <param name="impersonationBehavior">Indicates which identity to use when reading the file.</param> /// <returns>A stream containing the file.</returns> internal static Stream ReadFile(string packagePath, string filePath, ImpersonationBehavior impersonationBehavior) { Stream stream = null; try { using (ImpersonateIdentity id = new ImpersonateIdentity(impersonationBehavior)) { stream = File.OpenRead(SafePathCombine(packagePath, filePath)); } } catch (UnauthorizedAccessException) { // don't wrap the inner exception to obfuscate any system file info that might exist throw new UnauthorizedAccessException(String.Format(CultureInfo.InvariantCulture, Resources.UnauthorizedAccess, filePath)); } catch (FileNotFoundException) { // don't wrap the inner exception to obfuscate any system file info that might exist throw new FileNotFoundException(Resources.PackageFileNotFound, filePath); } catch (DirectoryNotFoundException) { // don't wrap the inner exception to obfuscate any system file info that might exist throw new DirectoryNotFoundException(String.Format(CultureInfo.InvariantCulture, Resources.DirectoryNotFound, filePath)); } return stream; }
/// <summary> /// Returns a <Typ>/System.IO.Stream</Typ> that can read the specified file, providing the option to /// validate the package. /// </summary> internal Stream GetFileStream(string filePath, bool skipValidation) { // SafePathCombine throws the ArgumentException and ArgumentNullException noted above. // File.OpenRead throws UnauthorizedAccessException, PathTooLongException, // DirectoryNotFoundException, FileNotFoundException, and NotSupportedException. if (!skipValidation) { // If the current user has access problems with the package, the package is not Validated using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { // Check if user has access to the files Directory.GetFiles(m_packageBasePath); // Check if package has valid e-learning content if (!ManifestExists()) throw new InvalidPackageException(Resources.ImsManifestXmlMissing); } } // if filePath is imsmanifest.xml and there is an index.xml, assume this is IMS+ content // and return the imsmanifest.xml converted from index.xml instead of the actual imsmanifest.xml. if (IsImsManifest(filePath) && FileExists("index.xml")) { try { return ConvertFromIndexXml(); } catch (InvalidPackageException) { // don't wrap the inner exception to obfuscate any system file info that might exist throw new FileNotFoundException(Resources.PackageFileNotFound, filePath); } } else { return ReadFile(m_packageBasePath, filePath, m_impersonationBehavior); } }
/// <summary> /// Recursively copy from one folder to another by reading the files into memory. /// </summary> /// <param name="fromDir">The folder to copy from.</param> /// <param name="fromImpersonationBehavior">The identity that has rights to read the <paramref name="fromDir"/>.</param> /// <param name="toDir">The folder to write to. Writing is not done in an impersonation block.</param> private static void RecursiveCopyStreams(DirectoryInfo fromDir, ImpersonationBehavior fromImpersonationBehavior, DirectoryInfo toDir) { FileInfo[] files; using (ImpersonateIdentity id = new ImpersonateIdentity(fromImpersonationBehavior)) { files = fromDir.GetFiles(); } foreach (FileInfo file in files) { using(Disposer disposer = new Disposer()) { FileStream fromStream; using (ImpersonateIdentity id = new ImpersonateIdentity(fromImpersonationBehavior)) { fromStream = file.OpenRead(); disposer.Push(fromStream); } string toPath = PackageReader.SafePathCombine(toDir.FullName, file.Name); FileInfo toFileInfo = new FileInfo(toPath); FileStream toStream = toFileInfo.OpenWrite(); disposer.Push(toStream); Utilities.CopyStream(fromStream, fromImpersonationBehavior, toStream, ImpersonationBehavior.UseImpersonatedIdentity); } } foreach (DirectoryInfo sub in fromDir.GetDirectories()) { DirectoryInfo newSub = Directory.CreateDirectory(Path.Combine(toDir.FullName, sub.Name)); RecursiveCopy(sub, newSub); } }
/// <summary> /// Recursively copy all package files from one directory to another. /// </summary> /// <param name="from">Directory to copy from.</param> /// <param name="to">Directory to copy to.</param> /// <param name="impersonationBehavior">The identity that can read the files in the <paramref name="from"/> folder. /// If this identity can also write to the <paramref name="to"/> folder, the method performs faster /// than if it does not have write permissions.</param> internal static void RecursiveCopy(DirectoryInfo from, DirectoryInfo to, ImpersonationBehavior impersonationBehavior) { bool copySucceeded = false; try { using (ImpersonateIdentity id = new ImpersonateIdentity(impersonationBehavior)) { try { to.Create(); } catch (IOException ex) { throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Resources.PackageDirectoryAlreadyExists, to.FullName), ex); } RecursiveCopy(from, to); copySucceeded = true; } } catch (UnauthorizedAccessException) { // This means the identity probably did not have write privileges to the 'to' folder. // So, try continue and try loading files into memory before writing them to the output folder. } if (!copySucceeded) { // Load files into memory before writing them to the output RecursiveCopyStreams(from, impersonationBehavior, to); } }
/// <summary> /// Explodes the m_zip file into a newly created (randomly named, in the temporary directory) /// destination directory if this hasn't been done already. /// </summary> /// <remarks> /// <c>m_state</c> affects what this method does, and can be changed by this method. /// <c>m_unzipPath</c> can be changed by this method. /// </remarks> /// <exception cref="ObjectDisposedException">State of the reader is "Closed".</exception> /// <exception cref="InvalidPackageException">There is a problem with the zip package.</exception> /// <returns>Package root path.</returns> private string ExplodeZipIfNeeded() { if (m_state == ZipPackageReaderState.Disposed) { throw new ObjectDisposedException("ZipPackageReader"); } if (m_state != ZipPackageReaderState.Exploded) { // If there is an m_stream but no m_zip, we still need to stream the stream into a temp file. if (m_stream != null && m_zip == null) { using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { m_zip = new FileInfo(Path.GetTempFileName()); using (FileStream fileStream = m_zip.Create()) { // Read from the stream and write to the exploded location using the specified identity. Utilities.CopyStream(m_stream, m_impersonationBehavior, fileStream, m_impersonationBehavior); } } m_mustDeleteFile = true; } using (ImpersonateIdentity id = new ImpersonateIdentity(m_impersonationBehavior)) { // Note that Path.Combine is safe here since Path.GetTempPath() and Path.GetRandomFileName() // are known to be safe. // Just in case Path.GetRandomFileName() returns a filename that already exists, keep trying // until success. bool done = false; while (!done) { m_unzipPath = new DirectoryInfo(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName())); if (m_unzipPath.Exists) continue; m_unzipPath.Create(); done = true; } // explode the zip file try { throw new Exception("Compression class is not found!"); //Compression.Unzip(m_zip, m_unzipPath); m_state = ZipPackageReaderState.Exploded; } catch (Exception e) { // on Exception, convert into InvalidPackageException. // wrap this message into a descriptive message string message = ""; message = String.Format(CultureInfo.InvariantCulture, ValidatorResources.PackageCouldNotBeOpened, message); throw new InvalidPackageException(message, e); } } } return m_unzipPath.ToString(); }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope")] // the writer is disposed for all code paths internal static void CopyStream(Stream fromStream, ImpersonationBehavior readImpersonationBehavior, Stream toStream, ImpersonationBehavior writeImpersonationBehavior) { using (Disposer disposer = new Disposer()) { DetachableStream dsToStream = new DetachableStream(toStream); disposer.Push(dsToStream); BinaryWriter writer = new BinaryWriter(dsToStream); disposer.Push(writer); // An addition revert during write operations is required if the setting for reading and writing // is not the same bool requiresWriteRevert = (readImpersonationBehavior != writeImpersonationBehavior); using (ImpersonateIdentity readId = new ImpersonateIdentity(readImpersonationBehavior)) { byte[] bytesIn = new byte[65536]; int bytesRead; while ((bytesRead = fromStream.Read(bytesIn, 0, bytesIn.Length)) != 0) { // If we have to impersonate to write, then do it. Otherwise skip it. if (requiresWriteRevert) { using (ImpersonateIdentity id = new ImpersonateIdentity(writeImpersonationBehavior)) { writer.Write(bytesIn, 0, bytesRead); } } else { writer.Write(bytesIn, 0, bytesRead); } } } dsToStream.Detach(); } }