Exemplo n.º 1
0
        /// <summary>
        /// Processes a PutUserInfo request
        /// </summary>
        /// <remarks>
        /// For full documentation on PutUserInfo, see https://wopi.readthedocs.org/projects/wopirest/en/latest/files/PutUserInfo.html
        /// </remarks>
        private async static Task <HttpResponseMessage> PutUserInfo(this HttpContext context, FileModel file)
        {
            // Set and save the UserInfo on the file
            var stream = context.Request.InputStream;
            var bytes  = new byte[stream.Length];
            await stream.ReadAsync(bytes, 0, (int)stream.Length);

            file.UserInfo = System.Text.Encoding.UTF8.GetString(bytes);

            // Update the file in DocumentDB
            await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

            // Return success
            return(returnStatus(HttpStatusCode.OK, "Success"));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Processes a RenameFile request
        /// </summary>
        /// <remarks>
        /// For full documentation on RenameFile, see https://wopi.readthedocs.org/projects/wopirest/en/latest/files/RenameFile.html
        /// </remarks>
        private async static Task <HttpResponseMessage> RenameFile(this HttpContext context, FileModel file)
        {
            // Get the Lock value passed in on the request
            string requestLock = context.Request.Headers[WopiRequestHeaders.LOCK];

            // Make sure the X-WOPI-RequestedName header is included
            if (context.Request.Headers[WopiRequestHeaders.REQUESTED_NAME] != null)
            {
                // Get the new file name
                var newFileName = context.Request.Headers[WopiRequestHeaders.REQUESTED_NAME];

                // Ensure the file isn't locked
                if (String.IsNullOrEmpty(file.LockValue) ||
                    (file.LockExpires != null &&
                     file.LockExpires < DateTime.Now))
                {
                    // Update the file with a LockValue and LockExpiration
                    file.LockValue    = requestLock;
                    file.LockExpires  = DateTime.Now.AddMinutes(30);
                    file.BaseFileName = newFileName;
                    await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                    // Return success 200
                    return(returnStatus(HttpStatusCode.OK, "Success"));
                }
                else if (file.LockValue == requestLock)
                {
                    // File lock matches existing lock, so we can change the name
                    file.LockExpires  = DateTime.Now.AddMinutes(30);
                    file.BaseFileName = newFileName;
                    await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                    // Return success 200
                    HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
                    return(returnStatus(HttpStatusCode.OK, "Success"));
                }
                else
                {
                    // The file is locked by someone else...return mismatch
                    return(context.returnLockMismatch(file.LockValue, String.Format("File locked by {0}", file.LockValue)));
                }
            }
            else
            {
                // X-WOPI-RequestedName header wasn't included
                return(returnStatus(HttpStatusCode.BadRequest, "X-WOPI-RequestedName header wasn't included in request"));
            }
        }
Exemplo n.º 3
0
        /// <summary>
        /// Processes a UnlockAndRelock request
        /// </summary>
        /// <remarks>
        /// For full documentation on UnlockAndRelock, see https://wopi.readthedocs.org/projects/wopirest/en/latest/files/UnlockAndRelock.html
        /// </remarks>
        private async static Task <HttpResponseMessage> UnlockAndRelock(this HttpContext context, FileModel file)
        {
            // Get the Lock and OldLock values passed in on the request
            string requestLock    = context.Request.Headers[WopiRequestHeaders.LOCK];
            string requestOldLock = context.Request.Headers[WopiRequestHeaders.OLD_LOCK];

            // Ensure the file has a valid lock
            if (String.IsNullOrEmpty(file.LockValue))
            {
                // File isn't locked...pass empty Lock in mismatch response
                return(context.returnLockMismatch(String.Empty, "File isn't locked"));
            }
            else if (file.LockExpires != null && file.LockExpires < DateTime.Now)
            {
                // File lock expired, so clear it out
                file.LockValue   = null;
                file.LockExpires = null;
                await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                // File isn't locked...pass empty Lock in mismatch response
                return(context.returnLockMismatch(String.Empty, "File isn't locked"));
            }
            else if (requestOldLock != file.LockValue)
            {
                // File lock mismatch...pass Lock in mismatch response
                return(context.returnLockMismatch(file.LockValue, "Lock mismatch"));
            }
            else
            {
                // Update the file with a LockValue and LockExpiration
                file.LockValue   = requestLock;
                file.LockExpires = DateTime.Now.AddMinutes(30);
                await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                // Return success 200
                return(returnStatus(HttpStatusCode.OK, "Success"));
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Processes a PutRelativeFile request
        /// </summary>
        /// <remarks>
        /// For full documentation on PutRelativeFile, see https://wopi.readthedocs.org/projects/wopirest/en/latest/files/PutRelativeFile.html
        /// </remarks>
        private async static Task <HttpResponseMessage> PutRelativeFile(this HttpContext context, DetailedFileModel file, List <WopiAction> actions)
        {
            // Determine the specific mode
            if (context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET] != null &&
                context.Request.Headers[WopiRequestHeaders.SUGGESTED_TARGET] != null)
            {
                // Theses headers are mutually exclusive, so we should return a 501 Not Implemented
                return(returnStatus(HttpStatusCode.NotImplemented, "Both RELATIVE_TARGET and SUGGESTED_TARGET were present"));
            }
            else if (context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET] != null ||
                     context.Request.Headers[WopiRequestHeaders.SUGGESTED_TARGET] != null)
            {
                string fileName = "";
                if (context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET] != null)
                {
                    // Specific mode...use the exact filename
                    fileName = context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET];
                }
                else
                {
                    // Suggested mode...might just be an extension
                    fileName = context.Request.Headers[WopiRequestHeaders.RELATIVE_TARGET];
                    if (fileName.IndexOf('.') == 0)
                    {
                        fileName = file.BaseFileName.Substring(0, file.BaseFileName.LastIndexOf('.')) + fileName;
                    }
                }

                // Create the file entity
                DetailedFileModel newFile = new DetailedFileModel()
                {
                    id           = Guid.NewGuid(),
                    OwnerId      = file.OwnerId,
                    BaseFileName = fileName,
                    Size         = context.Request.InputStream.Length,
                    Container    = file.Container,
                    Version      = 1
                };

                // First stream the file into blob storage
                var stream = context.Request.InputStream;
                var bytes  = new byte[stream.Length];
                await stream.ReadAsync(bytes, 0, (int)stream.Length);

                var id = await Utils.AzureStorageUtil.UploadFile(newFile.id.ToString(), newFile.Container, bytes);

                // Write the details into documentDB
                await DocumentDBRepository <FileModel> .CreateItemAsync("Files", (FileModel)newFile);

                // Get access token for the new file
                WopiSecurity security = new WopiSecurity();
                var          token    = security.GenerateToken(newFile.OwnerId, newFile.Container, newFile.id.ToString());
                var          tokenStr = security.WriteToken(token);

                // Prepare the Json response
                string json = String.Format("{ 'Name': '{0}, 'Url': 'https://{1}/wopi/files/{2}?access_token={3}'",
                                            newFile.BaseFileName, context.Request.Url.Authority, newFile.id.ToString(), tokenStr);

                // Add the optional properties to response if applicable (HostViewUrl, HostEditUrl)
                var fileExt = newFile.BaseFileName.Substring(newFile.BaseFileName.LastIndexOf('.') + 1).ToLower();
                var view    = actions.FirstOrDefault(i => i.ext == fileExt && i.name == "view");
                if (view != null)
                {
                    json += String.Format(", 'HostViewUrl': '{0}'", WopiUtil.GetActionUrl(view, newFile, context.Request.Url.Authority));
                }
                var edit = actions.FirstOrDefault(i => i.ext == fileExt && i.name == "edit");
                if (edit != null)
                {
                    json += String.Format(", 'HostEditUrl': '{0}'", WopiUtil.GetActionUrl(edit, newFile, context.Request.Url.Authority));
                }
                json += " }";

                // Write the response and return a success 200
                var response = returnStatus(HttpStatusCode.OK, "Success");
                response.Content = new StringContent(json);
                return(response);
            }
            else
            {
                return(returnStatus(HttpStatusCode.BadRequest, "PutRelativeFile mode was not provided in the request"));
            }
        }
Exemplo n.º 5
0
        /// <summary>
        /// Processes a PutFile request
        /// </summary>
        /// <remarks>
        /// For full documentation on PutFile, see https://wopi.readthedocs.org/projects/wopirest/en/latest/files/PutFile.html
        /// </remarks>
        private async static Task <HttpResponseMessage> PutFile(this HttpContext context, FileModel file)
        {
            // Get the Lock value passed in on the request
            string requestLock = context.Request.Headers[WopiRequestHeaders.LOCK];

            // Ensure the file has a valid lock
            if (String.IsNullOrEmpty(file.LockValue))
            {
                // If the file is 0 bytes, this is document creation
                if (context.Request.InputStream.Length == 0)
                {
                    // Update the file in blob storage
                    var bytes = new byte[context.Request.InputStream.Length];
                    context.Request.InputStream.Read(bytes, 0, bytes.Length);
                    file.Size = bytes.Length;
                    await AzureStorageUtil.UploadFile(file.id.ToString(), file.Container, bytes);

                    // Update version
                    file.Version++;
                    await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                    // Return success 200
                    return(returnStatus(HttpStatusCode.OK, "Success"));
                }
                else
                {
                    // File isn't locked...pass empty Lock in mismatch response
                    return(context.returnLockMismatch(String.Empty, "File isn't locked"));
                }
            }
            else if (file.LockExpires != null && file.LockExpires < DateTime.Now)
            {
                // File lock expired, so clear it out
                file.LockValue   = null;
                file.LockExpires = null;
                await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                // File isn't locked...pass empty Lock in mismatch response
                return(context.returnLockMismatch(String.Empty, "File isn't locked"));
            }
            else if (requestLock != file.LockValue)
            {
                // File lock mismatch...pass Lock in mismatch response
                return(context.returnLockMismatch(file.LockValue, "Lock mismatch"));
            }
            else
            {
                // Update the file in blob storage
                var bytes = new byte[context.Request.InputStream.Length];
                context.Request.InputStream.Read(bytes, 0, bytes.Length);
                file.Size = bytes.Length;
                await AzureStorageUtil.UploadFile(file.id.ToString(), file.Container, bytes);

                // Update version
                file.Version++;
                await DocumentDBRepository <FileModel> .UpdateItemAsync("Files", file.id.ToString(), (FileModel)file);

                // Return success 200
                return(returnStatus(HttpStatusCode.OK, "Success"));
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Processes a WOPI request using the HttpContext of the APIController
        /// </summary>
        public async static Task <HttpResponseMessage> ProcessWopiRequest(this HttpContext context)
        {
            // Parse the request
            var request = ParseRequest(context.Request);
            HttpResponseMessage response = null;

            try
            {
                // Lookup the file in the database
                var itemId = new Guid(request.Id);
                var file   = DocumentDBRepository <DetailedFileModel> .GetItem("Files", i => i.id == itemId);

                // Check for null file
                if (file == null)
                {
                    response = returnStatus(HttpStatusCode.NotFound, "File Unknown/User Unauthorized");
                }
                else
                {
                    // Validate WOPI Proof (ie - ensure request came from Office Online)
                    if (await WopiUtil.ValidateWopiProof(context))
                    {
                        // Get discovery information
                        var fileExt = file.BaseFileName.Substring(file.BaseFileName.LastIndexOf('.') + 1).ToLower();
                        var actions = await WopiUtil.GetDiscoveryInfo();

                        // Augments the file with additional properties CloseUrl, HostViewUrl, HostEditUrl
                        file.CloseUrl = String.Format("https://{0}", context.Request.Url.Authority);
                        var view = actions.FirstOrDefault(i => i.ext == fileExt && i.name == "view");
                        if (view != null)
                        {
                            file.HostViewUrl = WopiUtil.GetActionUrl(view, file, context.Request.Url.Authority);
                        }
                        var edit = actions.FirstOrDefault(i => i.ext == fileExt && i.name == "edit");
                        if (edit != null)
                        {
                            file.HostEditUrl = WopiUtil.GetActionUrl(edit, file, context.Request.Url.Authority);
                        }

                        // Get the user from the token (token is already validated)
                        file.UserId = WopiSecurity.GetUserFromToken(request.AccessToken);

                        // Call the appropriate handler for the WOPI request we received
                        switch (request.RequestType)
                        {
                        case WopiRequestType.CheckFileInfo:
                            response = context.CheckFileInfo(file);
                            break;

                        case WopiRequestType.GetFile:
                            response = await context.GetFile(file);

                            break;

                        case WopiRequestType.Lock:
                            response = await context.Lock(file);

                            break;

                        case WopiRequestType.GetLock:
                            response = await context.GetLock(file);

                            break;

                        case WopiRequestType.RefreshLock:
                            response = await context.RefreshLock(file);

                            break;

                        case WopiRequestType.Unlock:
                            response = await context.Unlock(file);

                            break;

                        case WopiRequestType.UnlockAndRelock:
                            response = await context.UnlockAndRelock(file);

                            break;

                        case WopiRequestType.PutFile:
                            response = await context.PutFile(file);

                            break;

                        case WopiRequestType.PutRelativeFile:
                            response = await context.PutRelativeFile(file, actions);

                            break;

                        case WopiRequestType.RenameFile:
                            response = await context.RenameFile(file);

                            break;

                        case WopiRequestType.PutUserInfo:
                            response = await context.PutUserInfo(file);

                            break;

                        default:
                            response = returnStatus(HttpStatusCode.NotImplemented, "Unsupported");
                            break;
                        }
                    }
                    else
                    {
                        // Proof validation failed...return 500
                        response = returnStatus(HttpStatusCode.InternalServerError, "Server Error");
                    }
                }
            }
            catch (Exception)
            {
                // An unknown exception occurred...return 500
                response = returnStatus(HttpStatusCode.InternalServerError, "Server Error");
            }

            return(response);
        }