mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
202 lines
6.2 KiB
Lua
202 lines
6.2 KiB
Lua
|
|
--[[
|
||
|
|
| This file was obtained through the combined efforts
|
||
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
||
|
|
|
|
||
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
||
|
|
|
|
||
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||
|
|
--]]
|
||
|
|
|
||
|
|
|
||
|
|
local CL_LIMIT, CL_LIMIT_OVERRIDE, CL_NO_CLENGTH
|
||
|
|
|
||
|
|
if CLIENT then
|
||
|
|
CL_LIMIT = CreateConVar("pac_webcontent_limit", "-1", {FCVAR_ARCHIVE}, "webcontent limit in kb, -1 = unlimited")
|
||
|
|
CL_NO_CLENGTH = CreateConVar("pac_webcontent_allow_no_content_length", "0", {FCVAR_ARCHIVE}, "allow downloads with no content length")
|
||
|
|
CL_LIMIT_OVERRIDE = CreateConVar("pac_webcontent_limit_force", "0", {FCVAR_ARCHIVE}, "Override serverside setting")
|
||
|
|
end
|
||
|
|
|
||
|
|
local SV_LIMIT = CreateConVar("sv_pac_webcontent_limit", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "webcontent limit in kb, -1 = unlimited")
|
||
|
|
local SV_NO_CLENGTH = CreateConVar("sv_pac_webcontent_allow_no_content_length", "-1", CLIENT and {FCVAR_REPLICATED} or {FCVAR_ARCHIVE, FCVAR_REPLICATED}, "allow downloads with no content length")
|
||
|
|
|
||
|
|
function pac.FixGMODUrl(url)
|
||
|
|
-- to avoid "invalid url" errors
|
||
|
|
-- gmod does not allow urls containing "10.", "172.16.", "192.168.", "127." or "://localhost"
|
||
|
|
-- we escape 10. and 127. can occur (mydomain.com/model10.zip) and assume the server supports
|
||
|
|
-- the escaped request
|
||
|
|
return url:Replace("10.", "%31%30%2e"):Replace("127.", "%31%32%37%2e")
|
||
|
|
end
|
||
|
|
|
||
|
|
local function http(method, url, headers, cb, failcb)
|
||
|
|
|
||
|
|
url = pac.FixGMODUrl(url)
|
||
|
|
|
||
|
|
return HTTP({
|
||
|
|
method = method,
|
||
|
|
url = url,
|
||
|
|
headers = headers,
|
||
|
|
success = function(code, data, headers)
|
||
|
|
if code < 400 then
|
||
|
|
cb(data, #data, headers)
|
||
|
|
else
|
||
|
|
local header = {}
|
||
|
|
for k,v in pairs(headers) do
|
||
|
|
table.insert(header, tostring(k) .. ": " .. tostring(v))
|
||
|
|
end
|
||
|
|
|
||
|
|
local err = "server returned code " .. code .. ":\n\n"
|
||
|
|
err = err .. "url: "..url.."\n"
|
||
|
|
err = err .. "================\n"
|
||
|
|
|
||
|
|
err = err .. "HEADER:\n"
|
||
|
|
err = err .. table.concat(header, "\n") .. "\n"
|
||
|
|
|
||
|
|
err = err .. "================\n"
|
||
|
|
|
||
|
|
err = err .. "BODY:\n"
|
||
|
|
err = err .. data .. "\n"
|
||
|
|
|
||
|
|
err = err .. "================\n"
|
||
|
|
failcb(err, code >= 400, code)
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
failed = function(err)
|
||
|
|
if failcb then
|
||
|
|
failcb("_G.HTTP error: " .. err)
|
||
|
|
else
|
||
|
|
pac.Message("_G.HTTP error: " .. err)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
})
|
||
|
|
end
|
||
|
|
|
||
|
|
function pac.FixUrl(url)
|
||
|
|
url = url:Trim()
|
||
|
|
url = url:gsub("[\"'<>\n\\]+", "")
|
||
|
|
|
||
|
|
if url:find("dropbox", 1, true) then
|
||
|
|
url = url:gsub([[^http%://dl%.dropboxusercontent%.com/]], [[https://dl.dropboxusercontent.com/]])
|
||
|
|
url = url:gsub([[^https?://dl.dropbox.com/]], [[https://www.dropbox.com/]])
|
||
|
|
url = url:gsub([[^https?://www.dropbox.com/s/(.+)%?dl%=[01]$]], [[https://dl.dropboxusercontent.com/s/%1]])
|
||
|
|
url = url:gsub([[^https?://www.dropbox.com/s/(.+)$]], [[https://dl.dropboxusercontent.com/s/%1]])
|
||
|
|
url = url:gsub([[^https?://www.dropbox.com/scl/(.+)$]], [[https://dl.dropboxusercontent.com/scl/%1]]) --Fix for new dropbox format.
|
||
|
|
return url
|
||
|
|
end
|
||
|
|
|
||
|
|
if url:find("drive.google.com", 1, true) and not url:find("export=download", 1, true) then
|
||
|
|
local id =
|
||
|
|
url:match("https://drive.google.com/file/d/(.-)/") or
|
||
|
|
url:match("https://drive.google.com/file/d/(.-)$") or
|
||
|
|
url:match("https://drive.google.com/open%?id=(.-)$")
|
||
|
|
|
||
|
|
if id then
|
||
|
|
return "https://drive.google.com/uc?export=download&id=" .. id
|
||
|
|
end
|
||
|
|
return url
|
||
|
|
end
|
||
|
|
|
||
|
|
if url:find("gitlab.com", 1, true) then
|
||
|
|
return url:gsub("^(https?://.-/.-/.-/)blob", "%1raw")
|
||
|
|
end
|
||
|
|
|
||
|
|
url = url:gsub([[^http%://onedrive%.live%.com/redir?]],[[https://onedrive.live.com/download?]])
|
||
|
|
url = url:gsub("pastebin.com/([a-zA-Z0-9]*)$", "pastebin.com/raw.php?i=%1")
|
||
|
|
url = url:gsub("github.com/([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/blob/", "github.com/%1/%2/raw/")
|
||
|
|
|
||
|
|
return url
|
||
|
|
end
|
||
|
|
|
||
|
|
function pac.getContentLength(url, cb, failcb)
|
||
|
|
return http("HEAD", url, {["Accept-Encoding"] = "none"}, function(_, _, headers)
|
||
|
|
local length
|
||
|
|
|
||
|
|
-- server have rights to send headers in any case
|
||
|
|
for key, value in pairs(headers) do
|
||
|
|
if string.lower(key) == "content-length" then
|
||
|
|
length = tonumber(value)
|
||
|
|
|
||
|
|
if not length or math.floor(length) ~= length then
|
||
|
|
return failcb(string.format("malformed server reply with header content-length (got %q, expected valid integer number)", value), true)
|
||
|
|
end
|
||
|
|
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if length then return cb(length) end
|
||
|
|
|
||
|
|
return pac.contentLengthFallback(url, cb, failcb)
|
||
|
|
end, function(err, over400, code)
|
||
|
|
if code == 405 then
|
||
|
|
return pac.contentLengthFallback(url, cb, failcb)
|
||
|
|
end
|
||
|
|
|
||
|
|
return failcb(err, over400)
|
||
|
|
end )
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Performs a GET but requests 0 bytes
|
||
|
|
-- We can then read the response headers to determine the content size.
|
||
|
|
-- This allows Google Drive and other hosts to work with PAC even with content-length limits set
|
||
|
|
-- (They typically block HEAD requests)
|
||
|
|
function pac.contentLengthFallback(url, cb, failcb)
|
||
|
|
local function fail()
|
||
|
|
return failcb("unable to determine content length", true)
|
||
|
|
end
|
||
|
|
|
||
|
|
return http("GET", url, {["Range"] = "bytes=0-0"}, function(data, data_length, headers)
|
||
|
|
-- e.g. "bytes 0-0/11784402"
|
||
|
|
local contentRange = headers["Content-Range"]
|
||
|
|
if not contentRange then return fail() end
|
||
|
|
|
||
|
|
local spl = string.Split(contentRange, "/")
|
||
|
|
local contentLength = spl[2]
|
||
|
|
|
||
|
|
if contentLength then return cb(tonumber(contentLength)) end
|
||
|
|
|
||
|
|
return fail()
|
||
|
|
end )
|
||
|
|
end
|
||
|
|
|
||
|
|
function pac.HTTPGet(url, cb, failcb)
|
||
|
|
if not url or url:len() < 4 then
|
||
|
|
failcb("url length is less than 4 (" .. tostring(url) .. ")", true)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
url = pac.FixUrl(url)
|
||
|
|
|
||
|
|
local limit = SV_LIMIT:GetInt()
|
||
|
|
|
||
|
|
if CLIENT and (CL_LIMIT_OVERRIDE:GetBool() or limit == -1) then
|
||
|
|
limit = CL_LIMIT:GetInt()
|
||
|
|
end
|
||
|
|
|
||
|
|
if limit == -1 then
|
||
|
|
return http("GET", url, nil, cb, failcb)
|
||
|
|
end
|
||
|
|
|
||
|
|
return pac.getContentLength(url, function(length)
|
||
|
|
if length then
|
||
|
|
if length <= (limit * 1024) then
|
||
|
|
http("GET", url, nil, cb, failcb)
|
||
|
|
else
|
||
|
|
failcb("download is too big (" .. string.NiceSize(length) .. ")", true)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
local allow_no_contentlength = SV_NO_CLENGTH:GetInt()
|
||
|
|
|
||
|
|
if CLIENT and (CL_LIMIT_OVERRIDE:GetBool() or allow_no_contentlength < 0) then
|
||
|
|
allow_no_contentlength = CL_NO_CLENGTH:GetInt()
|
||
|
|
end
|
||
|
|
|
||
|
|
if allow_no_contentlength > 0 then
|
||
|
|
http("GET", url, nil, cb, failcb)
|
||
|
|
else
|
||
|
|
failcb("unknown file size when allow_no_contentlength is " .. allow_no_contentlength, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end, failcb)
|
||
|
|
end
|