/// <summary> /// Will create an DocumentStream backed by a tempoary file. /// </summary> /// <remarks> /// We prefer to create the temporary file in the same location as the /// source to inherit the folders attributes and security. /// /// If we can not we will use a system generated file. /// </remarks> /// <param name="copyoriginal">When true we will copy the source file if /// possible. You must check for a non-zero result on the returning stream /// to determin success.</param> /// <returns>An DocumentStream.</returns> internal DocumentStream CreateTemporary(bool copyOriginal) { CriticalFileToken tempToken = null; DocumentStream result = null; FileStream temporary = null; bool isFileSource = (_xpsFileToken != null); //---------------------------------------------------------------------- // Open File in Same Location (if possible) if (isFileSource) { MakeTempFile(true, out temporary, out tempToken); } //---------------------------------------------------------------------- // Open File in System Generated Location if (tempToken == null) { // TODO: Should we prompt user asking if it is okay in the case // where the source is a local file, as it may mean degraded // security? We could check if this would be the case by // comparing ACLs & attributes before prompting MakeTempFile(false, out temporary, out tempToken); } //---------------------------------------------------------------------- // File Was Opened if ((temporary != null) && (tempToken != null)) { //------------------------------------------------------------------ // Copy Data if (copyOriginal) { // We use a native File.Copy if possible because this is // most performant. This is only possible if the source is file // based. if (isFileSource) { string sourcePath = _xpsFileToken.Location.LocalPath; string tempPath = tempToken.Location.LocalPath; temporary.Close(); File.Copy(sourcePath, tempPath, true); temporary = new FileStream( tempPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // we did the copy copyOriginal = false; Trace.SafeWrite(Trace.File, "Performed a file copy from source."); } else { StreamHelper.CopyStream( this, temporary); Trace.SafeWrite(Trace.File, "Performed a stream copy from source."); } } //------------------------------------------------------------------ // Create the DocumentStream result = new DocumentStream( tempToken, temporary, this); result.DeleteOnClose = true; Trace.SafeWrite(Trace.File, "Created temporary file {0}.", tempToken.Location); } else { // rescind consent if any was given tempToken = null; Trace.SafeWrite(Trace.File, "Unable to create a temporary file. Caller is expected to disable edits."); } return(result); }
/// <summary> /// <see cref="MS.Internal.Documents.Application.IDocumentController"/> /// </summary> bool IDocumentController.SavePreperation(Document document) { bool handled = false; RightsDocument doc = (RightsDocument)document; // see class remarks on why this is ok Stream ciphered = doc.Dependency.Destination; Stream clear = ciphered; // We protect the actual decrypted content (if necessary) using the // RightsManagementSuppressedStream that prevents callers from writing // to it. This still allows us to save an acquired use license back to // the package even if the user does not have the permissions to modify // the document itself. It also means that there is no need to check if // the user has permission to save before starting the operation. // If we are encrypted and the destination stream is identical to the // source stream, there is no need to create a new envelope. Instead we // can simply open the envelope on the destination stream, which // already contains the complete encrypted envelope that was the source // file. // // It doesn't actually matter to this class why the flag was set, but // we know the destination is identical to the source in two situations: // 1) The source and the destination are the same file, and since the // publish license didn't change there is no need to write to a // temporary file first // 2) The destination file was copied directly from the source file. if (doc.IsDestinationProtected() && doc.IsDestinationIdenticalToSource) { // we can't reuse protections because EncryptedPackage caches the // original permissions for the stream (if we re-open the package r/w // the Encrypted package will still be r/o) doc.DestinationPackage = OpenEnvelopeOnStream( doc.Dependency.Destination); doc.DestinationPackage.RightsManagementInformation.CryptoProvider = doc.SourcePackage.RightsManagementInformation.CryptoProvider; clear = DecryptEnvelopeAndSuppressStream( doc.DestinationPackage, DocumentRightsManagementManager.Current.HasPermissionToEdit); doc.DestinationProxy = new StreamProxy(clear); // save the use license in case the user acquired one _provider.Value.SaveUseLicense(doc.DestinationPackage); handled = true; Trace.SafeWrite( Trace.Rights, "Reused CryptoProvider as underlying stream is the same."); } else // we are not protected and/or the RM protections have changed { bool canEdit = DocumentRightsManagementManager.Current.HasPermissionToEdit; // canEdit should always be true here - either the document is not // protected, or the protections have been changed. In the latter // case, the user has to have Owner permissions to change the // protections, and that of course includes Edit permissions. Invariant.Assert( canEdit, "Cannot save with changes if Edit permission was not granted."); EncryptedPackageEnvelope encryptedPackage = _provider.Value.EncryptPackage(ciphered); // the destination is intended to be encrypted when a non-null // value is returned if (encryptedPackage != null) { clear = DecryptEnvelopeAndSuppressStream( encryptedPackage, canEdit); doc.DestinationPackage = encryptedPackage; } Trace.SafeWriteIf( encryptedPackage == null, Trace.Rights, "Destination package is unprotected."); doc.DestinationProxy = new StreamProxy(clear); // If the destination file is not identical to the source file, we // need to copy the (possibly decrypted) source stream to the // destination here. if (!doc.IsDestinationIdenticalToSource) { StreamHelper.CopyStream(doc.Source, doc.Destination); doc.DestinationProxy.Flush(); Trace.SafeWrite( Trace.Rights, "Copied Source contents to Destination."); } handled = true; } return(handled); }
//-------------------------------------------------------------------------- // Internal Methods //-------------------------------------------------------------------------- /// <summary> /// Will create an XpsDocument by copying this one to a target file and /// returning a stream corresponding to the new file. /// </summary> /// <exception cref="System.ArgumentNullException"/> /// <exception cref="System.IO.InvalidDataException"/> /// <param name="copiesToken">The token for the target file.</param> /// <returns>An DocumentStream.</returns> internal DocumentStream Copy(CriticalFileToken copiesToken) { DocumentStream result; FileStream target = null; bool isFileSource = (_xpsFileToken != null); Invariant.Assert(copiesToken != null, "No target file to which to copy."); ThrowIfInvalidXpsFileForSave(copiesToken.Location); string sourcePath = string.Empty; string copiesPath = copiesToken.Location.LocalPath; // if the source is a file, we need to release our lock on the source // file and also assert for permissions to read the file if (isFileSource) { Target.Close(); sourcePath = _xpsFileToken.Location.LocalPath; } try { // if the source is a file, file copy is the fastest if (isFileSource) { File.Copy(sourcePath, copiesPath, true); // If the original file was marked read-only, the copy will be read-only as // well. However, the copy is done for the purpose of creating a new file, // so it should not be marked read-only. FileAttributes attrib = File.GetAttributes(copiesPath); if ((attrib & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { File.SetAttributes(copiesPath, attrib ^ FileAttributes.ReadOnly); } // open the destination file that was just created by File.Copy target = new FileStream( copiesPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); } else { // open the destination file for create; we will copy the // source stream's data to the new stream outside the assert target = new FileStream( copiesPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None); } } // Since we have already closed the original file, we need to reopen it if we // fail to copy the file or open the new file. After doing so, we rethrow the // original exception so it can be handled at a higher level. #pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions.. catch { if (isFileSource) { Trace.SafeWrite( Trace.File, "File copy failed -- reopening original file."); try { Target = new FileStream( sourcePath, FileMode.Open, FileAccess.Read, FileShare.Read); } // If we fail to reopen the original file, rethrow an exception to // indicate this specific error. #pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions.. catch (Exception e) { Trace.SafeWrite( Trace.File, "Unable to reopen original file."); throw new UnauthorizedAccessException( SR.Get(SRID.DocumentStreamCanNoLongerOpen), e); } } throw; } if (isFileSource) { Trace.SafeWrite(Trace.File, "Performed a file copy from source."); // reacquire our stream ReOpenWriteable(); } else { // if the source wasn't a file, we want to copy the stream now StreamHelper.CopyStream(this, target); Trace.SafeWrite(Trace.File, "Performed a stream copy from source."); } //---------------------------------------------------------------------- // Create the DocumentStream result = new DocumentStream(copiesToken, target, this); result.DeleteOnClose = false; Trace.SafeWrite(Trace.File, "Created copy to file {0}.", copiesToken.Location); return(result); }