RequestParser.coffee 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. settings = require("settings-sharelatex")
  2. module.exports = RequestParser =
  3. VALID_COMPILERS: ["pdflatex", "latex", "xelatex", "lualatex"]
  4. MAX_TIMEOUT: 600
  5. parse: (body, callback = (error, data) ->) ->
  6. response = {}
  7. if !body.compile?
  8. return callback "top level object should have a compile attribute"
  9. compile = body.compile
  10. compile.options ||= {}
  11. try
  12. response.compiler = @_parseAttribute "compiler",
  13. compile.options.compiler,
  14. validValues: @VALID_COMPILERS
  15. default: "pdflatex"
  16. type: "string"
  17. response.timeout = @_parseAttribute "timeout",
  18. compile.options.timeout
  19. default: RequestParser.MAX_TIMEOUT
  20. type: "number"
  21. response.imageName = @_parseAttribute "imageName",
  22. compile.options.imageName,
  23. type: "string"
  24. response.draft = @_parseAttribute "draft",
  25. compile.options.draft,
  26. default: false,
  27. type: "boolean"
  28. response.check = @_parseAttribute "check",
  29. compile.options.check,
  30. type: "string"
  31. response.flags = @_parseAttribute "flags",
  32. compile.options.flags,
  33. default: [],
  34. type: "object"
  35. # The syncType specifies whether the request contains all
  36. # resources (full) or only those resources to be updated
  37. # in-place (incremental).
  38. response.syncType = @_parseAttribute "syncType",
  39. compile.options.syncType,
  40. validValues: ["full", "incremental"]
  41. type: "string"
  42. # The syncState is an identifier passed in with the request
  43. # which has the property that it changes when any resource is
  44. # added, deleted, moved or renamed.
  45. #
  46. # on syncType full the syncState identifier is passed in and
  47. # stored
  48. #
  49. # on syncType incremental the syncState identifier must match
  50. # the stored value
  51. response.syncState = @_parseAttribute "syncState",
  52. compile.options.syncState,
  53. type: "string"
  54. if response.timeout > RequestParser.MAX_TIMEOUT
  55. response.timeout = RequestParser.MAX_TIMEOUT
  56. response.timeout = response.timeout * 1000 # milliseconds
  57. response.resources = (@_parseResource(resource) for resource in (compile.resources or []))
  58. rootResourcePath = @_parseAttribute "rootResourcePath",
  59. compile.rootResourcePath
  60. default: "main.tex"
  61. type: "string"
  62. originalRootResourcePath = rootResourcePath
  63. sanitizedRootResourcePath = RequestParser._sanitizePath(rootResourcePath)
  64. response.rootResourcePath = RequestParser._checkPath(sanitizedRootResourcePath)
  65. for resource in response.resources
  66. if resource.path == originalRootResourcePath
  67. resource.path = sanitizedRootResourcePath
  68. catch error
  69. return callback error
  70. callback null, response
  71. _parseResource: (resource) ->
  72. if !resource.path? or typeof resource.path != "string"
  73. throw "all resources should have a path attribute"
  74. if resource.modified?
  75. modified = new Date(resource.modified)
  76. if isNaN(modified.getTime())
  77. throw "resource modified date could not be understood: #{resource.modified}"
  78. if !resource.url? and !resource.content?
  79. throw "all resources should have either a url or content attribute"
  80. if resource.content? and typeof resource.content != "string"
  81. throw "content attribute should be a string"
  82. if resource.url? and typeof resource.url != "string"
  83. throw "url attribute should be a string"
  84. return {
  85. path: resource.path
  86. modified: modified
  87. url: resource.url
  88. content: resource.content
  89. }
  90. _parseAttribute: (name, attribute, options) ->
  91. if attribute?
  92. if options.validValues?
  93. if options.validValues.indexOf(attribute) == -1
  94. throw "#{name} attribute should be one of: #{options.validValues.join(", ")}"
  95. if options.type?
  96. if typeof attribute != options.type
  97. throw "#{name} attribute should be a #{options.type}"
  98. else
  99. return options.default if options.default?
  100. return attribute
  101. _sanitizePath: (path) ->
  102. # See http://php.net/manual/en/function.escapeshellcmd.php
  103. path.replace(/[\#\&\;\`\|\*\?\~\<\>\^\(\)\[\]\{\}\$\\\x0A\xFF\x00]/g, "")
  104. _checkPath: (path) ->
  105. # check that the request does not use a relative path
  106. for dir in path.split('/')
  107. if dir == '..'
  108. throw "relative path in root resource"
  109. return path