/// <summary>
        /// Will open an XpsDocument and return this stream.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"/>
        /// <exception cref="System.IO.InvalidDataException"/>
        /// <param name="xpsFileToken">The token for the file to open.</param>
        /// <param name="writeable">When true will open the file writeable.
        /// Default is read-only.</param>
        /// <returns>An DocumentStream.</returns>
        internal static DocumentStream Open(CriticalFileToken xpsFileToken, bool writeable)
        {
            ThrowIfInvalidXpsFileForOpen(xpsFileToken.Location);

            FileAccess access = FileAccess.Read;

            if (writeable)
            {
                access |= FileAccess.Write;
            }

            string sourcePath = xpsFileToken.Location.LocalPath;

            FileStream dataSource = new FileStream(
                sourcePath,
                FileMode.OpenOrCreate,
                access,
                (access == FileAccess.Read) ? FileShare.Read : FileShare.None);


            Trace.SafeWrite(Trace.File, "Opened file {0}.", sourcePath);

            return(new DocumentStream(
                       xpsFileToken, dataSource, null));
        }
        //--------------------------------------------------------------------------
        // Constructors
        //--------------------------------------------------------------------------

        /// <summary>
        /// Constructs an DocumentStream.
        /// </summary>
        /// <param name="xpsFileToken">The file to manage.</param>
        /// <param name="mode">The mode to open the file in.</param>
        /// <param name="access">The access to open the file with.</param>
        /// <param name="original">If a temporary file; the file we are based on.
        /// </param>
        private DocumentStream(
            CriticalFileToken xpsFileToken,
            Stream dataSource,
            DocumentStream original)
            : base(dataSource)
        {
            _original     = original;
            _xpsFileToken = xpsFileToken;
        }
        /// <summary>
        /// Checks if there is a read-only file at the path stored in the given
        /// CriticalFileToken.
        /// </summary>
        /// <param name="fileToken">A token containing the path of the file</param>
        /// <returns>True if a file exists and is read-only</returns>
        internal static bool IsReadOnly(CriticalFileToken fileToken)
        {
            string         path       = fileToken.Location.LocalPath;
            FileAttributes attributes = FileAttributes.Normal;

            if (File.Exists(path))
            {
                attributes = File.GetAttributes(path);
            }

            return((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly);
        }
Example #4
0
        /// <summary>
        /// Creates the default chain of documents.  (PackageDocument,
        /// RightsDocument and FileDocument)
        /// </summary>
        /// <returns>A Document.</returns>
        internal static PackageDocument CreateDefaultDocument(
            Uri source, CriticalFileToken fileToken)
        {
            // because we have a fileToken we might be able to save
            _canSave = true;

            PackageDocument doc = new PackageDocument(
                new RightsDocument(
                    new FileDocument(fileToken)));

            doc.Uri = source;

            return(doc);
        }
Example #5
0
        /// <summary>
        /// <see cref="MS.Internal.Documents.Application.IDocumentController"/>
        /// </summary>
        bool IDocumentController.SaveAsPreperation(Document document)
        {
            FileDocument doc = (FileDocument)document;

            CriticalFileToken sourceToken = doc.SourceToken;
            CriticalFileToken saveToken   = doc.DestinationToken;

            //----------------------------------------------------------------------
            // Get Save Location and Consent (if needed)

            bool haveConsent = false;
            bool cancelSave  = false;

            // Loop until we have consent to save to a file or the user cancels
            while (!haveConsent && !cancelSave)
            {
                // If we have a location, check to see if it is read-only
                if (saveToken != null)
                {
                    if (DocumentStream.IsReadOnly(saveToken))
                    {
                        // If the target file is read-only, we cannot save over it
                        FilePresentation.ShowDestinationIsReadOnly();
                    }
                    else
                    {
                        // If the file isn't read-only, we now have consent to save
                        haveConsent = true;
                    }
                }

                if (!haveConsent)
                {
                    Trace.SafeWrite(
                        Trace.File, "We don't have a save location, prompting user.");

                    // by default set the same file
                    if (saveToken == null)
                    {
                        saveToken = sourceToken;
                    }

                    // A false return value indicates that the user wanted to
                    // cancel the save
                    cancelSave = !FilePresentation.ShowSaveFileDialog(ref saveToken);
                }
                else
                {
                    // Otherwise we do have consent to save to the location stored in
                    // the saveToken
                    doc.DestinationToken = saveToken;
                }
            }

            // Validate: If still don't have consent return without saving
            if (!haveConsent)
            {
                // ensure token is rescinded
                doc.DestinationToken = null;

                Trace.SafeWrite(
                    Trace.File,
                    "{0} not handling we do not have user consent.",
                    this);

                return(false);
            }

            bool isFileCopySafe = doc.IsFileCopySafe;

            //----------------------------------------------------------------------
            // Calculate Save Action
            SaveAction action = SaveAction.Unknown;

            if (doc.Source.CanWrite)
            {
                action |= SaveAction.SourceIsWriteable;
            }
            if (sourceToken == saveToken)
            {
                action |= SaveAction.TargetIsSelf;
            }
            if (isFileCopySafe)
            {
                action |= SaveAction.CanCopyData;
            }

            bool isDestinationIdentical = false;

            // Catch IO Exceptions; will return false and clear token
            try
            {
                //----------------------------------------------------------------------
                // Perform Optimal Save Action (see method remarks)
                switch (action)
                {
                // Example: We were successfully able to open the source package
                // for write and there were no RM changes.
                // I/O Cost: Read & SafeWrite Changes
                case SaveAction.TargetIsSelf
                    | SaveAction.SourceIsWriteable
                    | SaveAction.CanCopyData:

                    Trace.SafeWrite(
                        Trace.File,
                        "SaveAction {0}: Updating comparee to self.",
                        action);

                    doc.DestinationProxy   = doc.SourceProxy;
                    isDestinationIdentical = true;
                    break;

                // Example: Another user / process had a lock on the file, however
                // we would like to save now and there were no RM changes.
                // I/O Cost: Varies (Read & SafeWrite Changes - Read & SafeWrite Sum)
                case SaveAction.TargetIsSelf | SaveAction.CanCopyData:

                    Trace.SafeWrite(
                        Trace.File,
                        "SaveAction {0}: reopening editable, updating comparee to self.",
                        action);

                    // re-open writeable if possible otherwise copy the file
                    if (doc.SourceProxy.ReOpenWriteable())
                    {
                        doc.DestinationProxy   = doc.SourceProxy;
                        isDestinationIdentical = true;
                    }
                    else
                    {
                        Trace.SafeWrite(
                            Trace.File,
                            "SaveAction {0}: creating a temporary document reopen failed.",
                            action);

                        doc.DestinationProxy = doc.SourceProxy.CreateTemporary(true);
                        doc.SwapDestination  = true;
                    }
                    break;

                case SaveAction.TargetIsSelf | SaveAction.SourceIsWriteable:
                case SaveAction.TargetIsSelf:
                    Trace.SafeWrite(
                        Trace.File,
                        "SaveAction {0}: creating a temporary document.",
                        action);
                    doc.DestinationProxy = doc.SourceProxy.CreateTemporary(false);
                    doc.SwapDestination  = true;
                    break;

                // Example: All other cases, like source is web based.
                // I/O Cost: Max (Read Sum & SafeWrite Sum)
                default:
                    Trace.SafeWrite(
                        Trace.File,
                        "SaveAction {0}: Performing defaults.",
                        action);
                    if (isFileCopySafe)
                    {
                        Trace.SafeWrite(
                            Trace.File,
                            "SaveAction {0}: copying original document.",
                            action);
                        doc.DestinationProxy   = doc.SourceProxy.Copy(saveToken);
                        isDestinationIdentical = true;
                    }
                    else
                    {
                        doc.DestinationProxy = DocumentStream.Open(saveToken, true);
                        // ensure we have enough quota to be as large as the sum
                        doc.DestinationProxy.SetLength(
                            doc.SourceProxy.Length +
                            (doc.WorkspaceProxy == null ? 0 : doc.WorkspaceProxy.Length));
                        // set it back as we don't do the copy
                        doc.DestinationProxy.SetLength(0);
                    }
                    doc.SwapDestination = false;
                    break;
                } // switch (action)

                // Set the IsDestinationIdenticalToSource flag on the document
                // depending on what happened above
                doc.IsDestinationIdenticalToSource = isDestinationIdentical;

                return(true);
            }
            catch (UnauthorizedAccessException uae)
            {
                FilePresentation.ShowNoAccessToDestination();
                doc.DestinationProxy = null;
                // ensure token is recinded
                doc.DestinationToken = null;

                Trace.SafeWrite(
                    Trace.File,
                    "SaveAction {0}: unable to open specified location.\nException: {1}",
                    action,
                    uae);

                return(false);
            }
            catch (IOException ioe)
            {
                FilePresentation.ShowNoAccessToDestination();
                doc.DestinationProxy = null;
                // ensure token is recinded
                doc.DestinationToken = null;

                Trace.SafeWrite(
                    Trace.File,
                    "SaveAction {0}: unable to set size at specified location.\nException: {1}",
                    action,
                    ioe);

                return(false);
            }
        }
Example #6
0
        //--------------------------------------------------------------------------
        // Internal Methods
        //--------------------------------------------------------------------------

        /// <summary>
        /// Prompts the user for the save location for the current XpsDocument.
        /// </summary>
        /// <param name="fileToken">The token for the current document</param>
        /// <returns></returns>
        internal static bool ShowSaveFileDialog(ref CriticalFileToken fileToken)
        {
            string extension = SR.Get(SRID.FileManagementSaveExt);

            Trace.SafeWrite(Trace.File, "Showing SafeFileDialog.");

            bool result = false;

            SaveFileDialog save = new SaveFileDialog();

            if (fileToken != null)
            {
                save.FileName = fileToken.Location.LocalPath;
            }

            save.Filter     = SR.Get(SRID.FileManagementSaveFilter);
            save.DefaultExt = extension;

            DialogResult dialogResult;

            // We need to invoke the ShowDialog method specifying a parent window.
            // We need to specify a parent window in order to avoid a Winforms
            // Common Dialog issue where the wrong window is used as the parent
            // which causes the Save dialog to be incorrectly localized.

            // Get the root browser window, if it exists.
            IWin32Window rbw = null;

            if (DocumentApplicationDocumentViewer.Instance != null)
            {
                rbw = DocumentApplicationDocumentViewer.Instance.RootBrowserWindow;
            }

            if (rbw != null)
            {
                dialogResult = save.ShowDialog(rbw);
            }
            else
            {
                dialogResult = save.ShowDialog();
            }

            if (dialogResult == DialogResult.OK)
            {
                string filePath = save.FileName;

                // Add .xps extension if not already present.
                // This must be done manually since the file save dialog will automatically
                // add the extension only if the filename doesn't have a known extension.
                // For instance, homework.1 would become homework.1.xps, but if the user
                // gets up to homework.386, then the dialog would just pass it through as
                // is, requiring us to append the extension here.
                if (!extension.Equals(
                        Path.GetExtension(filePath),
                        StringComparison.OrdinalIgnoreCase))
                {
                    filePath = filePath + extension;
                }

                Uri file = new Uri(filePath);

                // as this is the only place we can verify the user authorized this
                // particular file we construct the token here
                fileToken = new CriticalFileToken(file);
                result    = true;
                Trace.SafeWrite(Trace.File, "A save location was selected.");
            }

            return(result);
        }
 /// <summary>
 /// Will construct a FileDocument with no dependency.
 /// </summary>
 /// <param name="fileToken">The file token to use for this document.</param>
 public FileDocument(CriticalFileToken fileToken)
     : base(null)
 {
     _sourceToken = fileToken;
 }
        //--------------------------------------------------------------------------
        // Private Methods
        //--------------------------------------------------------------------------

        /// <summary>
        /// Will create a temporary file.
        /// </summary>
        /// <exception cref="System.ArgumentNullException"/>
        /// <exception cref="System.IO.InvalidDataException"/>
        /// <param name="inSameFolder">When true will attempt to use the same folder
        /// as the orginal file.</param>
        /// <param name="temporary">The stream for the temporary file.</param>
        /// <param name="tempToken">The file token for the temporary file.</param>
        private void MakeTempFile(
            bool inSameFolder,
            out FileStream temporary,
            out CriticalFileToken tempToken)
        {
            temporary = null;
            tempToken = null;

            // Will retry three times for a temp file in the folder.
            // The user could have two copies open and be saving before we
            // would fall back.  The reason for not making this a large
            // number is the user may not have access to the folder the attemps
            // would be futile and degrade the experience with delays
            for (int i = 0; i <= 3; i++)
            {
                Uri    location = MakeTemporaryFileName(inSameFolder, i);
                string tempPath = location.LocalPath;

                ThrowIfInvalidXpsFileForSave(location);

                try
                {
                    temporary = new FileStream(
                        tempPath,
                        FileMode.Create,
                        FileAccess.ReadWrite,
                        FileShare.None);

                    File.SetAttributes(
                        tempPath,
                        FileAttributes.Hidden | FileAttributes.Temporary);

                    tempToken = new CriticalFileToken(location);
                }
                catch (IOException io)
                {
                    Trace.SafeWrite(
                        Trace.File,
                        "Temporary file {0} is likely in use.\n{1}",
                        temporary,
                        io);
                }
                catch (UnauthorizedAccessException io)
                {
                    Trace.SafeWrite(
                        Trace.File,
                        "Temporary file {0} is likely in use.\n{1}",
                        temporary,
                        io);
                }
#pragma warning suppress 56500 // suppress PreSharp Warning 56500: Avoid `swallowing errors by catching non-specific exceptions..
                catch (Exception exception)
                {
                    // not editing is not a critical failure for the application
                    // so we can handle this method failing any event
                    Trace.SafeWrite(
                        Trace.File,
                        "Giving up on temp file.\n{0}",
                        exception);
                    break;
                }
                if (tempToken != null)
                {
                    break;
                }

#if DEBUG
                Invariant.Assert(
                    ((i != 3) || (temporary != null) || (inSameFolder)),
                    "Unable to create a temp file.\n"
                    + "Unless IE Cache is read-only we have a defect.");
#endif
            }
        }
        /// <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);
        }
        //--------------------------------------------------------------------------
        // 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);
        }