Beispiel #1
0
        /// <summary>
        /// Processes a CheckFileInfo request
        /// </summary>
        /// <remarks>
        /// For full documentation on CheckFileInfo, see
        /// https://wopi.readthedocs.io/projects/wopirest/en/latest/files/CheckFileInfo.html
        /// </remarks>
        private void HandleCheckFileInfoRequest(HttpContext context, WopiRequest requestData)
        {
            if (!ValidateAccess(requestData, writeAccessRequired: false))
            {
                ReturnInvalidToken(context.Response);
                return;
            }

            IFileStorage storage = new FTPFileStorage();
            long         size    = storage.GetFileSize(requestData.Id);

            if (size == -1)
            {
                ReturnFileUnknown(context.Response);
                return;
            }

            DateTime?lastModifiedTime = storage.GetLastModifiedTime(requestData.Id);

            try
            {
                CheckFileInfoResponse responseData = new CheckFileInfoResponse()
                {
                    // required CheckFileInfo properties
                    BaseFileName = Path.GetFileName(requestData.Id),
                    OwnerId      = "documentOwnerId",
                    Size         = Convert.ToInt32(size),
                    //Version = file.LastWriteTimeUtc.ToString("O" /* ISO 8601 DateTime format string */), // Using the file write time is an arbitrary choice.
                    Version = Convert.ToDateTime((DateTime)lastModifiedTime).ToFileTimeUtc().ToString(),

                    // optional CheckFileInfo properties
                    BreadcrumbBrandName = "LocalStorage WOPI Host",
                    //BreadcrumbFolderName = fileInfo.Directory != null ? fileInfo.Directory.Name : "",
                    BreadcrumbFolderName = "",
                    BreadcrumbDocName    = Path.GetFileNameWithoutExtension(requestData.Id),

                    UserFriendlyName = "A WOPI User",

                    SupportsLocks           = true,
                    SupportsUpdate          = true,
                    UserCanNotWriteRelative = true, /* Because this host does not support PutRelativeFile */

                    ReadOnly     = false,
                    UserCanWrite = true
                };

                string jsonString = JsonConvert.SerializeObject(responseData);

                context.Response.Write(jsonString);
                ReturnSuccess(context.Response);
            }
            catch (UnauthorizedAccessException)
            {
                ReturnFileUnknown(context.Response);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Processes a UnlockAndRelock request
        /// </summary>
        /// <remarks>
        /// For full documentation on UnlockAndRelock, see
        /// https://wopi.readthedocs.io/projects/wopirest/en/latest/files/UnlockAndRelock.html
        /// </remarks>
        private void HandleUnlockAndRelockRequest(HttpContext context, WopiRequest requestData)
        {
            if (!ValidateAccess(requestData, writeAccessRequired: true))
            {
                ReturnInvalidToken(context.Response);
                return;
            }

            IFileStorage storage = new FTPFileStorage();
            long         size    = storage.GetFileSize(requestData.Id);

            if (size == -1)
            {
                ReturnFileUnknown(context.Response);
                return;
            }

            string newLock = context.Request.Headers[WopiHeaders.Lock];
            string oldLock = context.Request.Headers[WopiHeaders.OldLock];

            lock (Locks)
            {
                LockInfo existingLock;
                if (TryGetLock(requestData.Id, out existingLock))
                {
                    if (existingLock.Lock == oldLock)
                    {
                        // There is a valid lock on the file and the existing lock matches the provided one

                        // Replace the existing lock with the new one
                        Locks[requestData.Id] = new LockInfo()
                        {
                            DateCreated = DateTime.UtcNow, Lock = newLock
                        };
                        context.Response.Headers[WopiHeaders.OldLock] = newLock;
                        ReturnSuccess(context.Response);
                    }
                    else
                    {
                        // The existing lock doesn't match the requested one.  Return a lock mismatch error
                        // along with the current lock
                        ReturnLockMismatch(context.Response, existingLock.Lock);
                    }
                }
                else
                {
                    // The requested lock does not exist.  That's also a lock mismatch error.
                    ReturnLockMismatch(context.Response, reason: "File not locked");
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Processes a Lock request
        /// </summary>
        /// <remarks>
        /// For full documentation on Lock, see
        /// https://wopi.readthedocs.io/projects/wopirest/en/latest/files/Lock.html
        /// </remarks>
        private void HandleLockRequest(HttpContext context, WopiRequest requestData)
        {
            if (!ValidateAccess(requestData, writeAccessRequired: true))
            {
                ReturnInvalidToken(context.Response);
                return;
            }

            IFileStorage storage = new FTPFileStorage();
            long         size    = storage.GetFileSize(requestData.Id);

            if (size == -1)
            {
                ReturnFileUnknown(context.Response);
                return;
            }

            string newLock = context.Request.Headers[WopiHeaders.Lock];

            lock (Locks)
            {
                LockInfo existingLock;
                if (TryGetLock(requestData.Id, out existingLock))
                {
                    // There is a valid existing lock on the file

                    // Regardless of whether the new lock matches the existing lock, this should be treated as a lock mismatch
                    // per the documentation: https://wopi.readthedocs.io/projects/wopirest/en/latest/files/Lock.html

                    // This is a fairly common case and shouldn't be tracked as an error.  Office Online can store
                    // information about a current session in the lock value and expects to conflict when there's
                    // an existing session to join.
                    ReturnLockMismatch(context.Response, existingLock.Lock);
                }
                else
                {
                    // The file is not currently locked or the lock has already expired

                    // Create and store new lock information
                    // TODO: In a real implementation the lock should be stored in a persisted and shared system.
                    Locks[requestData.Id] = new LockInfo()
                    {
                        DateCreated = DateTime.UtcNow, Lock = newLock
                    };

                    // Return success
                    ReturnSuccess(context.Response);
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Processes a GetFile request
        /// </summary>
        /// <remarks>
        /// For full documentation on GetFile, see
        /// https://wopi.readthedocs.io/projects/wopirest/en/latest/files/GetFile.html
        /// </remarks>
        private void HandleGetFileRequest(HttpContext context, WopiRequest requestData)
        {
            if (!ValidateAccess(requestData, writeAccessRequired: false))
            {
                ReturnInvalidToken(context.Response);
                return;
            }

            IFileStorage storage = new FTPFileStorage();
            Stream       stream  = storage.GetFile(requestData.Id);

            if (null == stream)
            {
                ReturnFileUnknown(context.Response);
                return;
            }

            try
            {
                int         i     = 0;
                List <byte> bytes = new List <byte>();
                do
                {
                    byte[] buffer = new byte[1024];
                    i = stream.Read(buffer, 0, 1024);
                    if (i > 0)
                    {
                        byte[] data = new byte[i];
                        Array.Copy(buffer, data, i);
                        bytes.AddRange(data);
                    }
                }while (i > 0);

                context.Response.OutputStream.Write(bytes.ToArray(), 0, bytes.Count);
                ReturnSuccess(context.Response);
            }
            catch (UnauthorizedAccessException)
            {
                ReturnFileUnknown(context.Response);
            }
            catch (FileNotFoundException)
            {
                ReturnFileUnknown(context.Response);
            }
        }
Beispiel #5
0
        /// <summary>
        /// Get the file names with link on it
        /// </summary>
        /// <returns>The file names with link</returns>
        public List <FileLink> GetFiles()
        {
            List <FileLink> files = new List <FileLink>();

            IFileStorage  storage   = new FTPFileStorage();
            List <string> fileNames = storage.GetFileNames();

            foreach (string fileName in fileNames)
            {
                FileLink fileLink = new FileLink();
                fileLink.Name = fileName;
                fileLink.Url  = string.Format("http://{0}/wopiframe/Index/{1}",
                                              ConfigurationManager.AppSettings["WOPIServerName"],
                                              fileName);

                files.Add(fileLink);
            }

            return(files);
        }
Beispiel #6
0
        /// <summary>
        /// Processes a PutFile request
        /// </summary>
        /// <remarks>
        /// For full documentation on PutFile, see
        /// https://wopi.readthedocs.io/projects/wopirest/en/latest/files/PutFile.html
        /// </remarks>
        private void HandlePutFileRequest(HttpContext context, WopiRequest requestData)
        {
            if (!ValidateAccess(requestData, writeAccessRequired: true))
            {
                ReturnInvalidToken(context.Response);
                return;
            }

            IFileStorage storage = new FTPFileStorage();
            long         size    = storage.GetFileSize(requestData.Id);

            if (size == -1)
            {
                ReturnFileUnknown(context.Response);
                return;
            }

            string   newLock = context.Request.Headers[WopiHeaders.Lock];
            LockInfo existingLock;
            bool     hasExistingLock;

            lock (Locks)
            {
                hasExistingLock = TryGetLock(requestData.Id, out existingLock);
            }

            if (hasExistingLock && existingLock.Lock != newLock)
            {
                // lock mismatch/locked by another interface
                ReturnLockMismatch(context.Response, existingLock.Lock);
                return;
            }

            // The WOPI spec allows for a PutFile to succeed on a non-locked file if the file is currently zero bytes in length.
            // This allows for a more efficient Create New File flow that saves the Lock roundtrips.
            if (!hasExistingLock && size != 0)
            {
                // With no lock and a non-zero file, a PutFile could potentially result in data loss by clobbering
                // existing content.  Therefore, return a lock mismatch error.
                ReturnLockMismatch(context.Response, reason: "PutFile on unlocked file with current size != 0");
            }

            // Either the file has a valid lock that matches the lock in the request, or the file is unlocked
            // and is zero bytes.  Either way, proceed with the PutFile.
            try
            {
                // TODO: Should be replaced with proper file save logic to a real storage system and ensures write atomicity
                int result = storage.UploadFile(requestData.Id, context.Request.InputStream);
                if (result != 0)
                {
                    ReturnServerError(context.Response);
                    return;
                }

                ReturnSuccess(context.Response);
            }
            catch (UnauthorizedAccessException)
            {
                ReturnFileUnknown(context.Response);
            }
            catch (IOException)
            {
                ReturnServerError(context.Response);
            }
        }