diff --git a/Common/sources/storage-base.js b/Common/sources/storage-base.js index 785e6e9a..79223a6e 100644 --- a/Common/sources/storage-base.js +++ b/Common/sources/storage-base.js @@ -42,6 +42,9 @@ function getStoragePath(strPath) { exports.getObject = function(strPath) { return storage.getObject(getStoragePath(strPath)); }; +exports.createReadStream = function(strPath) { + return storage.createReadStream(getStoragePath(strPath)); +}; exports.putObject = function(strPath, buffer, contentLength) { return storage.putObject(getStoragePath(strPath), buffer, contentLength); }; @@ -78,13 +81,13 @@ exports.deletePath = function(strPath) { return exports.deleteObjects(list); }); }; -exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_type, opt_creationDate) { - return storage.getSignedUrl(baseUrl, getStoragePath(strPath), urlType, optFilename, opt_type, opt_creationDate); +exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_creationDate) { + return storage.getSignedUrl(baseUrl, getStoragePath(strPath), urlType, optFilename, opt_creationDate); }; exports.getSignedUrls = function(baseUrl, strPath, urlType, opt_creationDate) { return exports.listObjects(getStoragePath(strPath)).then(function(list) { return Promise.all(list.map(function(curValue) { - return exports.getSignedUrl(baseUrl, curValue, urlType, undefined, undefined, opt_creationDate); + return exports.getSignedUrl(baseUrl, curValue, urlType, undefined, opt_creationDate); })).then(function(urls) { var outputMap = {}; for (var i = 0; i < list.length && i < urls.length; ++i) { @@ -94,9 +97,9 @@ exports.getSignedUrls = function(baseUrl, strPath, urlType, opt_creationDate) { }); }); }; -exports.getSignedUrlsArrayByArray = function(baseUrl, list, urlType, opt_type) { +exports.getSignedUrlsArrayByArray = function(baseUrl, list, urlType) { return Promise.all(list.map(function(curValue) { - return exports.getSignedUrl(baseUrl, curValue, urlType, undefined, opt_type); + return exports.getSignedUrl(baseUrl, curValue, urlType, undefined); })); }; exports.getSignedUrlsByArray = function(baseUrl, list, optPath, urlType) { diff --git a/Common/sources/storage-fs.js b/Common/sources/storage-fs.js index 784e9ca8..f180e1fb 100644 --- a/Common/sources/storage-fs.js +++ b/Common/sources/storage-fs.js @@ -45,7 +45,6 @@ var config = require('config'); var configStorage = config.get('storage'); var cfgBucketName = configStorage.get('bucketName'); var cfgStorageFolderName = configStorage.get('storageFolderName'); -var cfgStorageExternalHost = configStorage.get('externalHost'); var configFs = configStorage.get('fs'); var cfgStorageFolderPath = configFs.get('folderPath'); var cfgStorageSecretString = configFs.get('secretString'); @@ -89,6 +88,27 @@ function removeEmptyParent(strPath, done) { exports.getObject = function(strPath) { return utils.readFile(getFilePath(strPath)); }; +exports.createReadStream = function(strPath) { + let fsPath = getFilePath(strPath); + let contentLength; + return new Promise(function(resolve, reject) { + fs.stat(fsPath, function(err, stats) { + if (err) { + reject(err); + } else { + resolve(stats); + } + }); + }).then(function(stats){ + contentLength = stats.size; + return utils.promiseCreateReadStream(fsPath); + }).then(function(readStream, stats){ + return { + contentLength: contentLength, + readStream: readStream + }; + }); +}; exports.putObject = function(strPath, buffer, contentLength) { return new Promise(function(resolve, reject) { @@ -155,12 +175,12 @@ exports.deleteObject = function(strPath) { exports.deleteObjects = function(strPaths) { return Promise.all(strPaths.map(exports.deleteObject)); }; -exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_type, opt_creationDate) { +exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_creationDate) { return new Promise(function(resolve, reject) { //replace '/' with %2f before encodeURIComponent becase nginx determine %2f as '/' and get wrong system path var userFriendlyName = optFilename ? encodeURIComponent(optFilename.replace(/\//g, "%2f")) : path.basename(strPath); var uri = '/' + cfgBucketName + '/' + cfgStorageFolderName + '/' + strPath + '/' + userFriendlyName; - var url = (cfgStorageExternalHost ? cfgStorageExternalHost : baseUrl) + uri; + var url = utils.checkBaseUrl(baseUrl) + uri; var date = Date.now(); let creationDate = opt_creationDate || date; @@ -175,7 +195,6 @@ exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_type url += '?md5=' + encodeURIComponent(md5); url += '&expires=' + encodeURIComponent(expires); - url += '&disposition=' + encodeURIComponent(utils.getContentDisposition(null, null, opt_type)); url += '&filename=' + userFriendlyName; resolve(url); }); diff --git a/Common/sources/storage-s3.js b/Common/sources/storage-s3.js index 5a651fe2..17450a62 100644 --- a/Common/sources/storage-s3.js +++ b/Common/sources/storage-s3.js @@ -51,7 +51,6 @@ var cfgAccessKeyId = configStorage.get('accessKeyId'); var cfgSecretAccessKey = configStorage.get('secretAccessKey'); var cfgUseRequestToGetUrl = configStorage.get('useRequestToGetUrl'); var cfgUseSignedUrl = configStorage.get('useSignedUrl'); -var cfgExternalHost = configStorage.get('externalHost'); var cfgSslEnabled = configStorage.get('sslEnabled'); var cfgS3ForcePathStyle = configStorage.get('s3ForcePathStyle'); var configFs = configStorage.get('fs'); @@ -143,6 +142,25 @@ exports.getObject = function(strPath) { }); }); }; +exports.createReadStream = function(strPath) { + return new Promise(function(resolve, reject) { + var params = {Bucket: cfgBucketName, Key: getFilePath(strPath)}; + s3Client.getObject(params) + .on('error', (err) => { + reject(err); + }) + .on('httpHeaders', function(statusCode, headers, resp, statusMessage) { + //retries are possible + if (statusCode < 300) { + let responseObject = { + contentLength: headers['content-length'], + readStream: this.response.httpResponse.createUnbufferedStream() + }; + resolve(responseObject); + } + }).send(); + }); +}; exports.putObject = function(strPath, buffer, contentLength) { return new Promise(function(resolve, reject) { //todo рассмотреть Expires @@ -204,11 +222,11 @@ exports.deleteObjects = function(strPaths) { } return Promise.all(deletePromises); }; -exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_type, opt_creationDate) { +exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_creationDate) { return new Promise(function(resolve, reject) { var expires = (commonDefines.c_oAscUrlTypes.Session === urlType ? cfgExpSessionAbsolute : cfgStorageUrlExpires) || 31536000; var userFriendlyName = optFilename ? optFilename.replace(/\//g, "%2f") : path.basename(strPath); - var contentDisposition = utils.getContentDisposition(userFriendlyName, null, opt_type); + var contentDisposition = utils.getContentDisposition(userFriendlyName, null, null); if (cfgUseRequestToGetUrl) { //default Expires 900 seconds var params = { @@ -228,7 +246,7 @@ exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_type } else if (cfgEndpointParsed && (cfgEndpointParsed.hostname == 'localhost' || cfgEndpointParsed.hostname == '127.0.0.1') && 80 == cfgEndpointParsed.port) { - host = (cfgExternalHost ? cfgExternalHost : baseUrl) + cfgEndpointParsed.path; + host = utils.checkBaseUrl(baseUrl) + cfgEndpointParsed.path; } else { host = cfgEndpoint; } diff --git a/Common/sources/utils.js b/Common/sources/utils.js index 2a812b6e..b08ebb28 100644 --- a/Common/sources/utils.js +++ b/Common/sources/utils.js @@ -83,6 +83,7 @@ const cfgPasswordEncrypt = config.get('openpgpjs.encrypt'); const cfgPasswordDecrypt = config.get('openpgpjs.decrypt'); const cfgPasswordConfig = config.get('openpgpjs.config'); const cfgRequesFilteringAgent = config.get('services.CoAuthoring.request-filtering-agent'); +const cfgStorageExternalHost = config.get('storage.externalHost'); Object.assign(openpgp.config, cfgPasswordConfig); @@ -355,7 +356,7 @@ function downloadUrlPromiseWithoutRedirect(uri, optTimeout, optLimit, opt_Author } }); } -function postRequestPromise(uri, postData, optTimeout, opt_Authorization, opt_header) { +function postRequestPromise(uri, postData, postDataStream, optTimeout, opt_Authorization, opt_header) { return new Promise(function(resolve, reject) { //IRI to URI uri = URI.serialize(URI.parse(uri)); @@ -366,7 +367,10 @@ function postRequestPromise(uri, postData, optTimeout, opt_Authorization, opt_he } headers = opt_header || headers; let connectionAndInactivity = optTimeout && optTimeout.connectionAndInactivity && ms(optTimeout.connectionAndInactivity); - var options = {uri: urlParsed, body: postData, encoding: 'utf8', headers: headers, timeout: connectionAndInactivity}; + var options = {uri: urlParsed, encoding: 'utf8', headers: headers, timeout: connectionAndInactivity}; + if (postData) { + options.body = postData; + } let executed = false; let ro = baseRequest.post(options, function(err, response, body) { @@ -394,6 +398,9 @@ function postRequestPromise(uri, postData, optTimeout, opt_Authorization, opt_he raiseError(ro, 'ETIMEDOUT', 'Error whole request cycle timeout'); }, ms(optTimeout.wholeCycle)); } + if (postDataStream && !postData) { + postDataStream.pipe(ro); + } }); } exports.postRequestPromise = postRequestPromise; @@ -934,3 +941,6 @@ exports.convertLicenseInfoToServerParams = function(licenseInfo) { license.buildNumber = commonDefines.buildNumber; return license; }; +exports.checkBaseUrl = function(baseUrl) { + return cfgStorageExternalHost ? cfgStorageExternalHost : baseUrl; +}; diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index ea099a90..d601753a 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -459,6 +459,11 @@ let changeConnectionInfo = co.wrap(function*(conn, cmd) { } return false; }); +function signToken(payload, algorithm, expiresIn, secretElem) { + var options = {algorithm: algorithm, expiresIn: expiresIn}; + var secret = utils.getSecretByElem(secretElem); + return jwt.sign(payload, secret, options); +} function fillJwtByConnection(conn) { var docId = conn.docId; var payload = {document: {}, editorConfig: {user: {}}}; @@ -481,9 +486,7 @@ function fillJwtByConnection(conn) { edit.ds_isEnterCorrectPassword = conn.isEnterCorrectPassword; edit.ds_denyChangeName = conn.denyChangeName; - var options = {algorithm: cfgTokenSessionAlgorithm, expiresIn: cfgTokenSessionExpires / 1000}; - var secret = utils.getSecretByElem(cfgSecretSession); - return jwt.sign(payload, secret, options); + return signToken(payload, cfgTokenSessionAlgorithm, cfgTokenSessionExpires / 1000, cfgSecretSession); } function sendData(conn, data) { @@ -650,7 +653,7 @@ function* sendServerRequest(docId, uri, dataObject, opt_checkAuthorization) { logger.warn('authorization reduced to: docId = %s; length=%d', docId, auth.length); } } - let postRes = yield utils.postRequestPromise(uri, JSON.stringify(dataObject), cfgCallbackRequestTimeout, auth); + let postRes = yield utils.postRequestPromise(uri, JSON.stringify(dataObject), undefined, cfgCallbackRequestTimeout, auth); logger.debug('postData response: docId = %s;data = %s', docId, postRes.body); return postRes.body; } @@ -1231,6 +1234,7 @@ exports.parseReplyData = parseReplyData; exports.sendServerRequest = sendServerRequest; exports.createSaveTimerPromise = co.wrap(_createSaveTimer); exports.changeConnectionInfo = changeConnectionInfo; +exports.signToken = signToken; exports.publish = publish; exports.addTask = addTask; exports.addDelayed = addDelayed; @@ -2958,10 +2962,15 @@ exports.install = function(server, callbackFunction) { if (0 == data.needUrlMethod) { outputData.setData(yield storage.getSignedUrls(participant.baseUrl, data.needUrlKey, data.needUrlType, data.creationDate)); } else if (1 == data.needUrlMethod) { - outputData.setData(yield storage.getSignedUrl(participant.baseUrl, data.needUrlKey, data.needUrlType, undefined, undefined, data.creationDate)); + outputData.setData(yield storage.getSignedUrl(participant.baseUrl, data.needUrlKey, data.needUrlType, undefined, data.creationDate)); } else { - var contentDisposition = cmd.getInline() ? constants.CONTENT_DISPOSITION_INLINE : constants.CONTENT_DISPOSITION_ATTACHMENT; - outputData.setData(yield storage.getSignedUrl(participant.baseUrl, data.needUrlKey, data.needUrlType, cmd.getTitle(), contentDisposition, data.creationDate)); + let url; + if (cmd.getInline()) { + url = canvasService.getPrintFileUrl(data.needUrlKey, participant.baseUrl, cmd.getTitle()); + } else { + url = yield storage.getSignedUrl(participant.baseUrl, data.needUrlKey, data.needUrlType, cmd.getTitle(), data.creationDate) + } + outputData.setData(url); } modifyConnectionForPassword(participant, data.needUrlIsCorrectPassword); } diff --git a/DocService/sources/canvasservice.js b/DocService/sources/canvasservice.js index 7581853d..8be1f4c3 100644 --- a/DocService/sources/canvasservice.js +++ b/DocService/sources/canvasservice.js @@ -59,6 +59,9 @@ var cfgImageSize = config_server.get('limits_image_size'); var cfgImageDownloadTimeout = config_server.get('limits_image_download_timeout'); var cfgRedisPrefix = config.get('services.CoAuthoring.redis.prefix'); var cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.browser'); +const cfgTokenSessionAlgorithm = config.get('services.CoAuthoring.token.session.algorithm'); +const cfgTokenSessionExpires = ms(config.get('services.CoAuthoring.token.session.expires')); +const cfgSecretSession = config.get('services.CoAuthoring.secret.session'); const cfgForgottenFiles = config_server.get('forgottenfiles'); const cfgForgottenFilesName = config_server.get('forgottenfilesname'); const cfgOpenProtectedFile = config_server.get('openProtectedFile'); @@ -178,13 +181,16 @@ var getOutputData = co.wrap(function* (cmd, outputData, key, optConn, optAdditio if ('open' != command && 'reopen' != command && !cmd.getOutputUrls()) { var strPath = key + '/' + cmd.getOutputPath(); if (optConn) { - var contentDisposition = cmd.getInline() ? constants.CONTENT_DISPOSITION_INLINE : constants.CONTENT_DISPOSITION_ATTACHMENT; - let url = yield storage.getSignedUrl(optConn.baseUrl, strPath, commonDefines.c_oAscUrlTypes.Temporary, - cmd.getTitle(), - contentDisposition); + let url; + if(cmd.getInline()) { + url = getPrintFileUrl(key, optConn.baseUrl, cmd.getTitle()); + } else { + url = yield storage.getSignedUrl(optConn.baseUrl, strPath, commonDefines.c_oAscUrlTypes.Temporary, + cmd.getTitle()); + } outputData.setData(url); } else if (optAdditionalOutput) { - optAdditionalOutput.needUrlKey = strPath; + optAdditionalOutput.needUrlKey = cmd.getInline() ? key : strPath; optAdditionalOutput.needUrlMethod = 2; optAdditionalOutput.needUrlType = commonDefines.c_oAscUrlTypes.Temporary; } @@ -689,22 +695,16 @@ function* commandImgurls(conn, cmd, outputData) { } } function* commandPathUrls(conn, cmd, outputData) { - let contentDisposition = cmd.getInline() ? constants.CONTENT_DISPOSITION_INLINE : - constants.CONTENT_DISPOSITION_ATTACHMENT; let listImages = cmd.getData().map(function callback(currentValue) { return conn.docId + '/' + currentValue; }); - let urls = yield storage.getSignedUrlsArrayByArray(conn.baseUrl, listImages, commonDefines.c_oAscUrlTypes.Session, - contentDisposition); + let urls = yield storage.getSignedUrlsArrayByArray(conn.baseUrl, listImages, commonDefines.c_oAscUrlTypes.Session); outputData.setStatus('ok'); outputData.setData(urls); } function* commandPathUrl(conn, cmd, outputData) { - var contentDisposition = cmd.getInline() ? constants.CONTENT_DISPOSITION_INLINE : - constants.CONTENT_DISPOSITION_ATTACHMENT; var strPath = conn.docId + '/' + cmd.getData(); - var url = yield storage.getSignedUrl(conn.baseUrl, strPath, commonDefines.c_oAscUrlTypes.Temporary, cmd.getTitle(), - contentDisposition); + var url = yield storage.getSignedUrl(conn.baseUrl, strPath, commonDefines.c_oAscUrlTypes.Temporary, cmd.getTitle()); var errorCode = constants.NO_ERROR; if (constants.NO_ERROR !== errorCode) { outputData.setStatus('err'); @@ -934,8 +934,8 @@ function* commandSfcCallback(cmd, isSfcm, isEncrypted) { } try { if (wopiParams) { - let data = yield storage.getObject(savePathDoc); - replyStr = yield wopiClient.putFile(wopiParams, data, userLastChangeId); + let streamObj = yield storage.createReadStream(savePathDoc); + replyStr = yield wopiClient.putFile(wopiParams, null, streamObj.readStream, userLastChangeId); } else { replyStr = yield* docsCoServer.sendServerRequest(docId, uri, outputSfc, checkAuthorizationLength); } @@ -967,8 +967,8 @@ function* commandSfcCallback(cmd, isSfcm, isEncrypted) { updateMask.statusInfo = updateIfTask.statusInfo; try { if (wopiParams) { - let data = yield storage.getObject(savePathDoc); - replyStr = yield wopiClient.putFile(wopiParams, data, userLastChangeId); + let streamObj = yield storage.createReadStream(savePathDoc); + replyStr = yield wopiClient.putFile(wopiParams, null, streamObj.readStream, userLastChangeId); } else { replyStr = yield* docsCoServer.sendServerRequest(docId, uri, outputSfc, checkAuthorizationLength); } @@ -1320,6 +1320,65 @@ exports.saveFile = function(req, res) { } }); }; +function getPrintFileUrl(docId, baseUrl, filename) { + baseUrl = utils.checkBaseUrl(baseUrl); + let token = ''; + if (cfgTokenEnableBrowser) { + let payload = {document: {key: docId}}; + token = docsCoServer.signToken(payload, cfgTokenSessionAlgorithm, cfgTokenSessionExpires / 1000, cfgSecretSession); + } + return `${baseUrl}/printfile/${encodeURIComponent(docId)}?filename=${encodeURIComponent(filename)}&token=${encodeURIComponent(token)}`; +}; +exports.getPrintFileUrl = getPrintFileUrl; +exports.printFile = function(req, res) { + return co(function*() { + let docId = 'null'; + try { + let startDate = null; + if (clientStatsD) { + startDate = new Date(); + } + + let filename = req.query['filename']; + let token = req.query['token']; + docId = req.params.docid; + logger.info('Start printFile: docId = %s', docId); + + if (cfgTokenEnableBrowser) { + let checkJwtRes = docsCoServer.checkJwt(docId, token, commonDefines.c_oAscSecretType.Session); + if (checkJwtRes.decoded) { + let docIdBase = checkJwtRes.decoded.document.key; + if (!docId.startsWith(docIdBase)) { + logger.warn('Error printFile jwt: docId = %s description = %s', docId, 'access deny'); + res.sendStatus(403); + return; + } + } else { + logger.warn('Error printFile jwt: docId = %s description = %s', docId, checkJwtRes.description); + res.sendStatus(403); + return; + } + } + + let streamObj = yield storage.createReadStream(`${docId}/${constants.OUTPUT_NAME}.pdf`); + res.setHeader('Content-Disposition', utils.getContentDisposition(filename, null, constants.CONTENT_DISPOSITION_INLINE)); + res.setHeader('Content-Length', streamObj.contentLength); + res.setHeader('Content-Type', 'application/pdf'); + yield utils.pipeStreams(streamObj.readStream, res, true); + + if (clientStatsD) { + clientStatsD.timing('coauth.printFile', new Date() - startDate); + } + } + catch (e) { + logger.error('Error printFile: docId = %s %s', docId, e.stack); + res.sendStatus(400); + } + finally { + logger.info('End printFile: docId = %s', docId); + } + }); +}; exports.saveFromChanges = function(docId, statusInfo, optFormat, opt_userId, opt_userIndex, opt_queue) { return co(function* () { try { diff --git a/DocService/sources/server.js b/DocService/sources/server.js index 8df00427..ff96567a 100644 --- a/DocService/sources/server.js +++ b/DocService/sources/server.js @@ -106,10 +106,9 @@ if (configStorage.has('fs.folderPath')) { app.use('/' + cfgBucketName + '/' + cfgStorageFolderName, (req, res, next) => { const index = req.url.lastIndexOf('/'); if ('GET' === req.method && -1 != index) { - const contentDisposition = req.query['disposition'] || 'attachment'; let sendFileOptions = { root: configStorage.get('fs.folderPath'), dotfiles: 'deny', headers: { - 'Content-Disposition': contentDisposition + 'Content-Disposition': 'attachment' } }; const urlParsed = urlModule.parse(req.url); @@ -187,6 +186,7 @@ docsCoServer.install(server, () => { app.post('/downloadas/:docid', rawFileParser, canvasService.downloadAs); app.post('/savefile/:docid', rawFileParser, canvasService.saveFile); + app.get('/printfile/:docid', rawFileParser, canvasService.printFile); app.get('/healthcheck', utils.checkClientIp, docsCoServer.healthCheck); app.get('/baseurl', (req, res) => { diff --git a/DocService/sources/wopiClient.js b/DocService/sources/wopiClient.js index 78edeadb..6951c21b 100644 --- a/DocService/sources/wopiClient.js +++ b/DocService/sources/wopiClient.js @@ -283,7 +283,7 @@ function getEditorHtml(req, res) { } }); } -function putFile(wopiParams, data, userLastChangeId) { +function putFile(wopiParams, data, dataStream, userLastChangeId) { return co(function* () { try { logger.info('wopi PutFile start'); @@ -298,7 +298,7 @@ function putFile(wopiParams, data, userLastChangeId) { fillStandardHeaders(headers, uri, userAuth.access_token); logger.debug('wopi PutFile request uri=%s headers=%j', uri, headers); - let postRes = yield utils.postRequestPromise(uri, data, cfgCallbackRequestTimeout, undefined, headers); + let postRes = yield utils.postRequestPromise(uri, data, dataStream, cfgCallbackRequestTimeout, undefined, headers); logger.debug('wopi PutFile response headers=%j', postRes.response.headers); } else { logger.info('wopi SupportsUpdate = false'); @@ -329,7 +329,7 @@ function renameFile(wopiParams, name) { fillStandardHeaders(headers, uri, userAuth.access_token); logger.debug('wopi RenameFile request uri=%s headers=%j', uri, headers); - let postRes = yield utils.postRequestPromise(uri, undefined, cfgCallbackRequestTimeout, undefined, headers); + let postRes = yield utils.postRequestPromise(uri, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers); logger.debug('wopi RenameFile response headers=%j body=%s', postRes.response.headers, postRes.body); if (postRes.body) { res = JSON.parse(postRes.body); @@ -382,7 +382,7 @@ function lock(lockId, fileInfo, userAuth) { let headers = {"X-WOPI-Override": "LOCK", "X-WOPI-Lock": lockId}; fillStandardHeaders(headers, uri, access_token); logger.debug('wopi Lock request uri=%s headers=%j', uri, headers); - let postRes = yield utils.postRequestPromise(uri, undefined, cfgCallbackRequestTimeout, undefined, headers); + let postRes = yield utils.postRequestPromise(uri, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers); logger.debug('wopi Lock response headers=%j', postRes.response.headers); } else { logger.info('wopi SupportsLocks = false'); @@ -410,7 +410,7 @@ function unlock(wopiParams) { let headers = {"X-WOPI-Override": "UNLOCK", "X-WOPI-Lock": lockId}; fillStandardHeaders(headers, uri, access_token); logger.debug('wopi Unlock request uri=%s headers=%j', uri, headers); - let postRes = yield utils.postRequestPromise(uri, undefined, cfgCallbackRequestTimeout, undefined, headers); + let postRes = yield utils.postRequestPromise(uri, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers); logger.debug('wopi Unlock response headers=%j', postRes.response.headers); } else { logger.info('wopi SupportsLocks = false');