2018-07-13 05:37:22 -04:00
|
|
|
settings = require("settings-sharelatex")
|
|
|
|
|
2014-02-12 12:27:43 -05:00
|
|
|
module.exports = RequestParser =
|
|
|
|
VALID_COMPILERS: ["pdflatex", "latex", "xelatex", "lualatex"]
|
2014-10-17 06:03:08 -04:00
|
|
|
MAX_TIMEOUT: 300
|
2014-02-12 12:27:43 -05:00
|
|
|
|
|
|
|
parse: (body, callback = (error, data) ->) ->
|
|
|
|
response = {}
|
|
|
|
|
|
|
|
if !body.compile?
|
|
|
|
return callback "top level object should have a compile attribute"
|
|
|
|
|
|
|
|
compile = body.compile
|
|
|
|
compile.options ||= {}
|
|
|
|
|
|
|
|
try
|
|
|
|
response.compiler = @_parseAttribute "compiler",
|
|
|
|
compile.options.compiler,
|
|
|
|
validValues: @VALID_COMPILERS
|
|
|
|
default: "pdflatex"
|
|
|
|
type: "string"
|
|
|
|
response.timeout = @_parseAttribute "timeout",
|
|
|
|
compile.options.timeout
|
|
|
|
default: RequestParser.MAX_TIMEOUT
|
|
|
|
type: "number"
|
2016-01-12 12:04:42 -05:00
|
|
|
response.imageName = @_parseAttribute "imageName",
|
|
|
|
compile.options.imageName,
|
|
|
|
type: "string"
|
2016-02-02 09:26:14 -05:00
|
|
|
response.draft = @_parseAttribute "draft",
|
|
|
|
compile.options.draft,
|
|
|
|
default: false,
|
|
|
|
type: "boolean"
|
2016-07-26 11:22:38 -04:00
|
|
|
response.check = @_parseAttribute "check",
|
|
|
|
compile.options.check,
|
|
|
|
type: "string"
|
2017-08-09 10:10:24 -04:00
|
|
|
|
|
|
|
# The syncType specifies whether the request contains all
|
|
|
|
# resources (full) or only those resources to be updated
|
|
|
|
# in-place (incremental).
|
2017-08-03 06:51:58 -04:00
|
|
|
response.syncType = @_parseAttribute "syncType",
|
|
|
|
compile.options.syncType,
|
2017-08-07 05:19:56 -04:00
|
|
|
validValues: ["full", "incremental"]
|
2017-08-01 09:35:55 -04:00
|
|
|
type: "string"
|
2017-08-09 10:10:24 -04:00
|
|
|
|
|
|
|
# The syncState is an identifier passed in with the request
|
|
|
|
# which has the property that it changes when any resource is
|
|
|
|
# added, deleted, moved or renamed.
|
|
|
|
#
|
|
|
|
# on syncType full the syncState identifier is passed in and
|
|
|
|
# stored
|
|
|
|
#
|
|
|
|
# on syncType incremental the syncState identifier must match
|
|
|
|
# the stored value
|
2017-08-03 06:51:58 -04:00
|
|
|
response.syncState = @_parseAttribute "syncState",
|
|
|
|
compile.options.syncState,
|
2017-08-01 09:35:55 -04:00
|
|
|
type: "string"
|
2014-02-12 12:27:43 -05:00
|
|
|
|
|
|
|
if response.timeout > RequestParser.MAX_TIMEOUT
|
|
|
|
response.timeout = RequestParser.MAX_TIMEOUT
|
|
|
|
response.timeout = response.timeout * 1000 # milliseconds
|
|
|
|
|
|
|
|
response.resources = (@_parseResource(resource) for resource in (compile.resources or []))
|
2015-02-11 07:03:36 -05:00
|
|
|
|
|
|
|
rootResourcePath = @_parseAttribute "rootResourcePath",
|
2014-02-12 12:27:43 -05:00
|
|
|
compile.rootResourcePath
|
|
|
|
default: "main.tex"
|
|
|
|
type: "string"
|
2016-09-21 10:09:01 -04:00
|
|
|
originalRootResourcePath = rootResourcePath
|
|
|
|
sanitizedRootResourcePath = RequestParser._sanitizePath(rootResourcePath)
|
2017-03-20 06:03:48 -04:00
|
|
|
response.rootResourcePath = RequestParser._checkPath(sanitizedRootResourcePath)
|
2016-09-21 10:09:01 -04:00
|
|
|
|
2018-07-13 05:37:22 -04:00
|
|
|
if settings.texliveImageNameOveride?
|
|
|
|
tag = compile.options.imageName.split(":")[1]
|
|
|
|
response.imageName = "#{settings.texliveImageNameOveride}:#{tag}"
|
|
|
|
|
2016-09-21 10:09:01 -04:00
|
|
|
for resource in response.resources
|
|
|
|
if resource.path == originalRootResourcePath
|
|
|
|
resource.path = sanitizedRootResourcePath
|
2014-02-12 12:27:43 -05:00
|
|
|
catch error
|
|
|
|
return callback error
|
|
|
|
|
|
|
|
callback null, response
|
|
|
|
|
|
|
|
_parseResource: (resource) ->
|
|
|
|
if !resource.path? or typeof resource.path != "string"
|
|
|
|
throw "all resources should have a path attribute"
|
|
|
|
|
|
|
|
if resource.modified?
|
|
|
|
modified = new Date(resource.modified)
|
|
|
|
if isNaN(modified.getTime())
|
|
|
|
throw "resource modified date could not be understood: #{resource.modified}"
|
|
|
|
|
|
|
|
if !resource.url? and !resource.content?
|
|
|
|
throw "all resources should have either a url or content attribute"
|
|
|
|
if resource.content? and typeof resource.content != "string"
|
|
|
|
throw "content attribute should be a string"
|
|
|
|
if resource.url? and typeof resource.url != "string"
|
|
|
|
throw "url attribute should be a string"
|
|
|
|
|
|
|
|
return {
|
|
|
|
path: resource.path
|
|
|
|
modified: modified
|
|
|
|
url: resource.url
|
|
|
|
content: resource.content
|
|
|
|
}
|
|
|
|
|
|
|
|
_parseAttribute: (name, attribute, options) ->
|
|
|
|
if attribute?
|
|
|
|
if options.validValues?
|
|
|
|
if options.validValues.indexOf(attribute) == -1
|
|
|
|
throw "#{name} attribute should be one of: #{options.validValues.join(", ")}"
|
|
|
|
if options.type?
|
|
|
|
if typeof attribute != options.type
|
|
|
|
throw "#{name} attribute should be a #{options.type}"
|
|
|
|
else
|
|
|
|
return options.default if options.default?
|
|
|
|
return attribute
|
|
|
|
|
2015-02-11 07:03:36 -05:00
|
|
|
_sanitizePath: (path) ->
|
2015-02-13 06:21:35 -05:00
|
|
|
# See http://php.net/manual/en/function.escapeshellcmd.php
|
2015-02-13 06:28:43 -05:00
|
|
|
path.replace(/[\#\&\;\`\|\*\?\~\<\>\^\(\)\[\]\{\}\$\\\x0A\xFF\x00]/g, "")
|
2017-03-20 06:03:48 -04:00
|
|
|
|
|
|
|
_checkPath: (path) ->
|
|
|
|
# check that the request does not use a relative path
|
|
|
|
for dir in path.split('/')
|
|
|
|
if dir == '..'
|
|
|
|
throw "relative path in root resource"
|
|
|
|
return path
|