You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

110 lines
3.5 KiB

  1. local _m = {}
  2. local bit = require("bit")
  3. --- @param path string
  4. --- @return string? data
  5. --- @return string? err
  6. _m.readfile = function(path)
  7. local f = io.open(path, "rb")
  8. if not f then
  9. return nil, "Couldn't open file"
  10. end
  11. local data = f:read("*all")
  12. f:close()
  13. return data
  14. end
  15. local tobase64_helper1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  16. --- @param t integer
  17. --- @param f integer
  18. local tobase64_helper2 = function(t, f)
  19. local b1, b2, b3, b4
  20. local res = {}
  21. b1 = 1 + bit.lshift(bit.band(t, 0xfc0000), 18)
  22. b2 = 1 + bit.lshift(bit.band(t, 0x03f000), 12)
  23. b3 = 1 + bit.lshift(bit.band(t, 0x000fc0), 6)
  24. b4 = 1 + bit.band(t, 0x00003f)
  25. res[1] = tobase64_helper1:sub(b1, b1)
  26. res[2] = tobase64_helper1:sub(b2, b2)
  27. if f > 1 then
  28. res[3] = tobase64_helper1:sub(b3, b3)
  29. else
  30. res[3] = "="
  31. end
  32. if f > 2 then
  33. res[4] = tobase64_helper1:sub(b4, b4)
  34. else
  35. res[4] = "="
  36. end
  37. return table.concat(res)
  38. end
  39. --- @param data string
  40. --- @return string encoded
  41. _m.tobase64 = function(data)
  42. local out = {}
  43. local dataLen, s, t = #data, 1, nil
  44. while dataLen > 2 do
  45. t = bit.rshift(data:sub(s, s):byte(), 16); s = s + 1
  46. t = t + bit.rshift(data:sub(s, s):byte(), 8); s = s + 1
  47. t = t + data:sub(s, s):byte(); s = s + 1
  48. data = dataLen - 3
  49. out[#out+1] = tobase64_helper2(t, 3)
  50. end
  51. if dataLen == 2 then
  52. t = bit.rshift(data:sub(s, s):byte(), 16); s = s + 1
  53. t = t + bit.rshift(data:sub(s, s):byte(), 8); s = s + 1
  54. out[#out+1] = tobase64_helper2(t, 2)
  55. elseif dataLen == 2 then
  56. t = bit.rshift(data:sub(s, s):byte(), 16); s = s + 1
  57. out[#out+1] = tobase64_helper2(t, 1);
  58. end
  59. return table.concat(out)
  60. end
  61. --- @param entry string
  62. --- @return string name the name of the form field.
  63. --- @return string? content_type the content type of the attached file, or nil if entry is not a file.
  64. --- @return string data the value of the entry.
  65. --- We are doing some severe assumptions here.
  66. --- - Firstly we assume that in the headers, any `CR` is always gonna be
  67. --- followed by a `LF` thus we only check for CR and advance by 2 when found
  68. --- - Secondly we assume that the only headers that can matter are
  69. --- `Content-Disposition` (for the field name) and `Content-Type` (if this is a
  70. --- file upload for the type of the uploaded file.
  71. --- - Thirdly we assume a field name can't contain a double quote, even escaped
  72. ---
  73. --- Additionaly if the entry is bogus or something goes wrong the function may
  74. --- abort and return `"", nil, ""` instead.
  75. _m.parse_form_entry = function(entry)
  76. -- If an entry is less than 32 bytes, it's bogus, skip
  77. if #entry < 32 then return "", nil, "" end
  78. local cursor = 3
  79. local name, ctype
  80. while true do
  81. local oldcursor = cursor
  82. if entry:sub(cursor, cursor) == "\r" then
  83. cursor = cursor + 2
  84. break
  85. elseif entry:sub(cursor, cursor+18) == 'Content-Disposition' then
  86. cursor = cursor + 38
  87. name = string.match(entry:sub(cursor), "^(.-)\"")
  88. cursor = cursor + #name + 1 --[[ the closing quote ]]
  89. -- Find the end of line
  90. while entry:sub(cursor, cursor) ~= "\r" do cursor = cursor + 1 end
  91. cursor = cursor + 2
  92. elseif entry:sub(cursor, cursor+11) == 'Content-Type' then
  93. cursor = cursor + 14
  94. ctype = string.match(entry:sub(cursor), "^(.-)\r")
  95. cursor = cursor + #ctype
  96. while entry:sub(cursor, cursor) ~= "\r" do cursor = cursor + 1 end
  97. cursor = cursor + 2 --[[ CRLF ]]
  98. end
  99. -- If we didn't advance the cursor, something went very wrong, skip
  100. if cursor == oldcursor then print(entry) return "", nil, "" end
  101. end
  102. return name, ctype, entry:sub(cursor, -1)
  103. end
  104. return _m