/// <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; } if (!File.Exists(requestData.FullPath)) { ReturnFileUnknown(context.Response); return; } try { context.Response.AddHeader(WopiHeaders.ItemVersion, GetFileVersion(requestData.FullPath)); // transmit file from local storage to the response stream. ReturnSuccess(context.Response); context.Response.TransmitFile(requestData.FullPath); } catch (UnauthorizedAccessException ex) { ConsoleTo.Log(ex); ReturnFileUnknown(context.Response); } catch (FileNotFoundException ex) { ConsoleTo.Log(ex); ReturnFileUnknown(context.Response); } }
/// <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; } if (!File.Exists(requestData.FullPath)) { 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; } FileInfo putTargetFileInfo = new FileInfo(requestData.FullPath); // 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 && putTargetFileInfo.Length != 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 using (var fileStream = File.Open(requestData.FullPath, FileMode.Truncate, FileAccess.Write, FileShare.None)) { context.Request.InputStream.CopyTo(fileStream); } context.Response.AddHeader(WopiHeaders.ItemVersion, GetFileVersion(requestData.FullPath)); } catch (UnauthorizedAccessException ex) { ConsoleTo.Log(ex); ReturnFileUnknown(context.Response); } catch (IOException ex) { ConsoleTo.Log(ex); ReturnServerError(context.Response); } }
/// <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; } if (!File.Exists(requestData.FullPath)) { ReturnFileUnknown(context.Response); return; } try { FileInfo fileInfo = new FileInfo(requestData.FullPath); if (!fileInfo.Exists) { ReturnFileUnknown(context.Response); return; } // For more info on CheckFileInfoResponse fields, see // https://wopi.readthedocs.io/projects/wopirest/en/latest/files/CheckFileInfo.html#response CheckFileInfoResponse responseData = new CheckFileInfoResponse() { // required CheckFileInfo properties BaseFileName = Path.GetFileName(requestData.FullPath), OwnerId = requestData.UserId, Size = (int)fileInfo.Length, UserId = requestData.UserId, Version = fileInfo.LastWriteTimeUtc.ToString("O" /* ISO 8601 DateTime format string */), // Using the file write time is an arbitrary choice. // optional CheckFileInfo properties BreadcrumbBrandName = WopiConfig.BreadcrumbBrandName, //BreadcrumbFolderName = fileInfo.Directory != null ? fileInfo.Directory.Name : "", //BreadcrumbDocName = Path.GetFileNameWithoutExtension(requestData.FullPath), //BreadcrumbBrandUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Host, //BreadcrumbFolderUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Host, UserFriendlyName = requestData.UserName, SupportsLocks = true, SupportsUpdate = true, UserCanNotWriteRelative = true, /* Because this host does not support PutRelativeFile */ ReadOnly = fileInfo.IsReadOnly, UserCanWrite = !fileInfo.IsReadOnly, }; string jsonString = JsonConvert.SerializeObject(responseData); context.Response.Write(jsonString); ReturnSuccess(context.Response); } catch (UnauthorizedAccessException ex) { ConsoleTo.Log(ex); ReturnFileUnknown(context.Response); } }