-
-
Save Fingercomp/457c53915d7a09944385f0b78676253e to your computer and use it in GitHub Desktop.
clbin client for OpenComputers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local com = require("component") | |
local comp = require("computer") | |
local fs = require("filesystem") | |
local shell = require("shell") | |
local inet = com.internet | |
local function generateBorder(str) | |
local longestOccurence = nil | |
for match in str:gmatch("%-*cldata") do | |
if not longestOccurence or #match > #longestOccurence then | |
longestOccurence = match | |
end | |
end | |
return longestOccurence and ("-" .. longestOccurence) or "cldata" | |
end | |
local function asFormData(str, fieldName) | |
local border = generateBorder(str) | |
local contentType = "multipart/form-data; boundary=" .. border | |
return ([[ | |
--%s | |
Content-Disposition: form-data; name="%s" | |
%s | |
--%s--]]):format( | |
border, | |
fieldName, | |
str, | |
border | |
), contentType | |
end | |
local function request(url, body, headers, timeout) | |
local handle, err = inet.request(url, body, headers) -- ① | |
if not handle then | |
return nil, ("request failed: %s"):format(err or "unknown error") | |
end | |
local start = comp.uptime() -- ② | |
while true do | |
local status, err = handle.finishConnect() -- ③ | |
if status then -- ④ | |
break | |
end | |
if status == nil then -- ⑤ | |
return nil, ("request failed: %s"):format(err or "unknown error") | |
end | |
if comp.uptime() >= start + timeout then -- ⑥ | |
handle.close() | |
return nil, "request failed: connection timed out" | |
end | |
os.sleep(0.05) -- ⑦ | |
end | |
return handle -- ⑧ | |
end | |
local function sendFile(path) | |
local f, err = io.open(path, "r") -- ① | |
if not f then | |
return nil, ("could not open file for reading: %s"):format(err or "unknown error") | |
end | |
local contents = f:read("*a") -- ② | |
f:close() | |
local data, contentType = asFormData(contents, "clbin") -- ③ | |
local headers = {["Content-Type"] = contentType} | |
local handle, err = request("https://clbin.com", data, headers, 10) -- ④ | |
if not handle then | |
return nil, err | |
end | |
local url = {} -- ⑤ | |
local read = 0 | |
local _, _, responseHeaders = handle.response() -- ⑥ | |
local length | |
for k, v in pairs(responseHeaders) do -- ⑦ | |
if k:lower() == "content-length" then | |
length = tonumber(v) | |
end | |
end | |
while not length or read < length do -- ⑧ | |
local chunk, err = handle.read() | |
if not chunk then | |
if length then -- ⑨ | |
return nil, ("error occured while reading response: %s"):format(err or "unknown error") -- ⑩ | |
end | |
break -- ⑩ | |
end | |
read = read + #chunk -- ⑪ | |
if length and read > length then | |
chunk = chunk:sub(1, length - read - 1) -- ⑫ | |
end | |
table.insert(url, chunk) | |
end | |
handle.close() -- ⑬ | |
return table.concat(url) -- ⑭ | |
end | |
local function getFile(url, path) | |
local f, err = io.open(path, "w") -- ① | |
if not f then | |
return nil, ("could not open file for writing: %s"):format(err or "unknown error") | |
end | |
local handle, err = request(url, nil, nil, 10) -- ② | |
if not handle then | |
return nil, err | |
end | |
local read = 0 | |
local _, _, responseHeaders = handle.response() | |
local length | |
for k, v in pairs(responseHeaders) do | |
if k:lower() == "content-length" then | |
length = tonumber(v) | |
end | |
end | |
while not length or read < length do | |
local chunk, err = handle.read() | |
if not chunk then | |
if length then | |
f:close() -- ③ | |
return nil, ("error occured while reading response: %s"):format(err or "unknown error") | |
end | |
break | |
end | |
read = read + #chunk | |
if length and read > length then | |
chunk = chunk:sub(1, length - read - 1) | |
end | |
f:write(chunk) | |
end | |
f:close() -- ④ | |
handle.close() | |
return true | |
end | |
local args, opts = shell.parse(...) | |
local function printHelp() | |
io.stderr:write([[ | |
Usage: clbin { get [-f] <code> <path> | put <path> } | |
clbin get [-f] <code> <path> | |
Download a file from clbin to <path>. | |
If the target file exists, -f overwrites it. | |
clbin put <path> | |
Upload a file to clbin. | |
]]) | |
os.exit(1) | |
end | |
if args[1] == "get" then | |
if #args < 3 then | |
printHelp() | |
end | |
local code = args[2] | |
local path = args[3] | |
local url = ("https://clbin.com/%s"):format(code) | |
path = fs.concat(shell.getWorkingDirectory(), path) | |
if not (opts.f or opts.force) and fs.exists(path) then | |
io.stderr:write("file already exists, pass -f to overwrite\n") | |
os.exit(2) | |
end | |
local status, err = getFile(url, path) | |
if status then | |
print("Success! The file is written to " .. path) | |
os.exit(0) | |
else | |
io.stderr:write(err .. "\n") | |
os.exit(3) | |
end | |
elseif args[1] == "put" then | |
if #args < 2 then | |
printHelp() | |
end | |
local path = args[2] | |
local url, err = sendFile(path) | |
if url then | |
url = url:gsub("[\r\n]", "") | |
print("Success! The file is posted to " .. url) | |
os.exit(0) | |
else | |
io.stderr:write(err .. "\n") | |
os.exit(4) | |
end | |
else | |
printHelp() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment