--[[ Progdor 2.0 - File Packaging by LDDestroier Get with: wget https://raw.githubusercontent.com/LDDestroier/CC/master/progdor2.lua Uses CCA compression API, made by minizbot2012. --]] local progdor = { version = "2.0", PBlogPath = ".progdor_PB_uploads", channel = 8366, skynetPath = "skynet.lua", skynetURL = "https://github.com/osmarks/skynet/raw/master/client.lua" } local scr_x, scr_y = term.getSize() local modem = peripheral.find("modem") local skynet, skynetBigReceive, skynetBigSend local function interpretArgs(tInput, tArgs) local output = {} local errors = {} local usedEntries = {} for aName, aType in pairs(tArgs) do output[aName] = false for i = 1, #tInput do if not usedEntries[i] then if tInput[i] == aName and not output[aName] then if aType then usedEntries[i] = true if type(tInput[i+1]) == aType or type(tonumber(tInput[i+1])) == aType then usedEntries[i+1] = true if aType == "number" then output[aName] = tonumber(tInput[i+1]) else output[aName] = tInput[i+1] end else output[aName] = nil errors[1] = errors[1] and (errors[1] + 1) or 1 errors[aName] = "expected " .. aType .. ", got " .. type(tInput[i+1]) end else usedEntries[i] = true output[aName] = true end end end end end for i = 1, #tInput do if not usedEntries[i] then output[#output+1] = tInput[i] end end return output, errors end local yield = function() os.queueEvent("yield") os.pullEvent("yield") end -- CCA API START -- local bit = bit32 local function pack(bn1, bn2) return bit.band(bn1, 0xFF), bit.rshift(bn1, 8) + bit.lshift(bit.band(bn2, 0xF), 4), bit.rshift(bn2, 4) end local function upack(b1, b2, b3) return (b1 + bit.lshift(bit.band(b2, 0xF), 8)), (bit.lshift(b3,4) + bit.band(bit.rshift(b2, 4), 0xF)) end local function createDict(bool) local ret = {} for i = 1, 255 do if bool then ret[string.char(i)] = i else ret[i] = string.char(i) end end if not bool then ret[256] = 256 end return ret end local function cp(sInput) local dic = createDict(true) local s = "" local ch local dlen = 256 local result = {} local temp for i = 1, #sInput do if dlen == 4095 then result[#result + 1] = dic[s] result[#result + 1] = 256 dic = createDict(true) dlen = 256 s = "" end ch = sInput:sub(i, i) temp = s..ch if dic[temp] then s = temp else result[#result + 1] = dic[s] dlen = dlen +1 dic[temp] = dlen s = ch end end result[#result + 1] = dic[s] return result end local function dc(data) local dic = createDict(false) local entry local ch local currCode local result = {} result[#result + 1] = dic[data[1]] prefix = dic[data[1]] for i = 2, #data do currCode = data[i] if currCode == 256 then dic = createDict(false) prefix = "" else entry = dic[currCode] if entry then -- exists in dictionary ch = entry:sub(1, 1) result[#result + 1] = entry if prefix ~= "" then dic[#dic+1] = prefix .. ch end else ch = prefix:sub(1, 1) result[#result + 1] = prefix..ch dic[#dic + 1] = prefix..ch end prefix = dic[currCode] end end return table.concat(result) end local function trim(inp) for i = 0,2 do if inp[#inp] == 0 then inp[#inp] = nil end end end local function decompress(input) local rec = {} for i = 1, #input, 3 do if i % 66 == 0 then yield() end rec[#rec+1], rec[#rec+2] = upack(input[i], input[i+1] or 0, input[i+2] or 0) end trim(rec) return dc(rec) end local function compress(input) local rec = {} local data = cp(input) for i=1, #data, 2 do yield() rec[#rec+1], rec[#rec+2], rec[#rec+3] = pack(data[i], data[i+1] or 0) end trim(rec) return rec end local function strCompress(input) local output = {} local tbl = compress(input) for i = 1, #tbl do output[i] = string.char(tbl[i]) end return table.concat(output) end local function strDecompress(input) local output = {} for i = 1, #input do output[i] = string.byte(input:sub(i,i)) end return decompress(output) end -- CCA API END -- -- colors that are always safe to set to local safeColorList = { [colors.white] = true, [colors.lightGray] = true, [colors.gray] = true, [colors.black] = true } -- pastebin uploads have a 512K limit local pastebinFileSizeLimit = 1024 * 512 local argData = { ["-pb"] = "string", -- pastebin get ["-dd"] = "string", -- direct URL download ["-m"] = "string", -- specify main file ["-PB"] = false, -- pastebin upload ["-t"] = false, -- transmit file ["-r"] = false, -- receive file ["-R"] = false, -- include read-only files ["-P"] = false, -- include Progdor2 file ["-S"] = false, -- use skynet ["-e"] = false, -- automatic self-extractor ["-E"] = "string", -- specify output folder in self-extractor code ["-s"] = false, -- silent ["-a"] = false, -- use as API with require, also makes silent ["-c"] = false, -- use CCA compression ["-h"] = false, -- show help ["-i"] = false, -- inspect mode ["-o"] = false, -- always overwrite } local argList, argErrors = interpretArgs({...}, argData) if #argErrors > 0 then local errList = "" for k,v in pairs(argErrors) do if k ~= 1 then errList = errList .. "\"" .. k .. "\": " .. v .. "; " end error(errList:sub(1, -2)) end end local pastebinGet = argList["-pb"] -- string, pastebin code local directDownload = argList["-dd"] -- string, download URL local mainFile = argList["-m"] -- string, main executable file local pastebinUpload = argList["-PB"] -- boolean local selfExtractor = argList["-e"] -- boolean local selfExtractorFolder = argList["-E"] -- string, folder output for self extractor code local silent = argList["-s"] -- boolean local useCompression = argList["-c"] -- boolean local justOverwrite = argList["-o"] -- boolean local allowReadOnly = argList["-R"] -- boolean local allowPackPD = argList["-P"] -- boolean local useSkynet = argList["-S"] -- boolean local trMode = argList["-t"] and "transmit" or (argList["-r"] and "receive" or "normal") local skynet if useCompression and selfExtract then error("Cannot use compression with self-extractor.") end local sWrite = function(text) if not silent then return write(text) end end local sPrint = function(text) if not silent then return print(text) end end local cWrite = function(text, color, ignoreSilent) local col = term.getTextColor() term.setTextColor(color or col) if ignoreSilent then write(text) else sWrite(text) end term.setTextColor(col) end local cPrint = function(text, color, ignoreSilent) local col = term.getTextColor() term.setTextColor(color or col) if ignoreSilent then print(text) else sPrint(text) end term.setTextColor(col) end local function showHelp(verboseHelp) local helpInfo if verboseHelp then helpInfo = { "Progdor v" .. progdor.version, " -pb [pastebin ID] : Download from Pastebin.", -- added " -PB : Upload to pastebin.", -- added " -dd [download URL] : Download from URL.", -- added " -e : Adds on self-extractor code to archive.", -- added " -E [folder] : Extractor extracts to folder", -- added " -s : Silences all terminal writing", -- added " -S : Use skynet when transmitting/receiving.", -- added " -t : Transmit a folder/file.", -- added " -r : Receive a file/packed folder.", -- added " -R : Allow packing read-only files/folders.", -- added " -P : Allow packing in Progdor2 itself.", -- added " -a : Allows programs to use require() on Progdor.", -- added " -c : Enables CCA compression.", -- added " -m : Specify main executable file in archive.", -- added " -i : Inspect archive without extracting.", -- added " -o : Overwrite files without asking.", -- added } else helpInfo = { "Progdor v" .. progdor.version, "Usage: progdor [options] inputFolder (outputFile)", " progdor [options] inputFile (outputFolder)", "", "Progdor is a file/folder packaging program with support for CCA compression and self-extraction.", " If tacking on auto-extractor, a third argument will be the default extraction path.", "", "Use -h for all options.", "", " This Progdor has Super Cow Powers." -- not actually added } end for y = 1, #helpInfo do sPrint(helpInfo[y]) end end local setTextColor = function(color) if (not silent) and (term.isColor() or safeColorList[color]) then term.setTextColor(color) end end local setBackgroundColor = function(color) if (not silent) and (term.isColor() or safeColorList[color]) then term.setBackgroundColor(color) end end local inputPath = argList[1] local outputPath = argList[2] or inputPath local defaultAutoExtractPath = argList[3] local exists, mode if inputPath == "moo" and not fs.exists(inputPath) then print([[ \_/ m00h (__) -(_)- \ ~Oo~___ / \ (..) |\ ___________|_|_|_____________ ..."Have you mooed today?"..."]]) return end if argList["-h"] then return showHelp(true) elseif argList["-a"] or (not shell) then mode = "api" elseif inputPath then exists = fs.exists(inputPath) if argList["-i"] then mode = "inspect" elseif fs.isDir(inputPath) then mode = "pack" else mode = "unpack" end elseif trMode ~= "receive" then return showHelp(false) end if mode == "api" then silent = true elseif (pastebinGet or directDownload) and pastebinUpload then error("Cannot upload and download at the same time!") end local specialWrite = function(left, colored, right, color) local origTextColor = term.getTextColor() sWrite(left) setTextColor(color) sWrite(colored) setTextColor(origTextColor) sWrite(right) end local specialPrint = function(left, colored, right, color) return specialWrite(left, colored, right .. "\n", color) end local function listAll(path, includePath) local output = {} local list = fs.list(path) local fc = fs.combine for i = 1, #list do if allowReadOnly or (not fs.isReadOnly(fc(path, list[i]))) then if allowPackPD or fc(path, list[i]) ~= (shell and shell.getRunningProgram()) then if fs.isDir(fc(path, list[i])) then if #fs.list(fc(path, list[i])) == 0 then output[#output+1] = (includePath and fc(path, list[i]) or list[i]) .. "/" else local la = listAll(fc(path, list[i])) for ii = 1, #la do output[#output+1] = includePath and fc(path, fc(list[i], la[ii])) or fc(list[i], la[ii]) end end else output[#output+1] = includePath and fc(path, list[i]) or list[i] end end end end return output end local makeFileList = function(path, doCompress) local output = {} local list = listAll(path, false) local file if not allowPackPD then cPrint("Ignoring Progdor2.", colors.lightGray) end if not allowReadOnly then cPrint("Ignoring read-only files.", colors.lightGray) end sPrint("Packing files...") for i = 1, #list do setTextColor(colors.lightGray) sWrite("'" .. list[i] .. "'...") if list[i]:sub(-1,-1) == "/" then output[list[i]] = true -- indicates empty directory else file = fs.open(fs.combine(path, list[i]), "r") output[list[i]] = doCompress and strCompress(file.readAll()) or file.readAll() file.close() setTextColor(colors.green) sPrint("good") end end setTextColor(colors.white) return output end local buildArchive = function(path, mainFile, doCompress) local output = { compressed = doCompress, -- uses CCA compression mainFile = mainFile, -- specifies the main program within the archive to run, should I implement something to use that data = makeFileList(path, doCompress) -- files and folders and whatnot } return textutils.serialize(output) end local parseArchiveData = function(input, doNotDecompress) local archive = textutils.unserialize(input) if archive then if archive.compressed and (not doNotDecompress) then for name, contents in pairs(archive.data) do archive.data[name] = strDecompress(contents) end archive.compressed = false end return archive else return false end end local parseArchive = function(path, doNotDecompress) local file = fs.open(path, "r") local output = parseArchiveData(file.readAll(), doNotDecompress) file.close() return output end local round = function(number, places) return math.floor(number * (10^places)) / (10^places) end local choice = function(input,verbose) if not input then input = "yn" end if verbose then sWrite("[") for a = 1, #input do sWrite(input:sub(a,a):upper()) if a < #input then sWrite(",") end end sWrite("]?") end local evt,char repeat evt,char = os.pullEvent("char") until string.find(input:lower(),char:lower()) if verbose then sPrint(char:upper()) end local pos = string.find(input:lower(), char:lower()) return pos, char:lower() end local overwriteOutputPath = function(inputPath, outputPath, allowMerge, override) setTextColor(colors.white) local c if override then return true, true else if allowMerge then write("Overwrite [Y/N]? Or [M]erge? ") c = choice("ynm", false) else write("Overwrite [Y/N]?") c = choice("yn", false) end write("\n") if c == 1 then return true, true elseif c == 2 then sPrint("Abort.") return false, false elseif c == 3 then return true, false end end end local uploadToPastebin = function(archive, name) if #archive > pastebinFileSizeLimit then error("That archive is too large to be uploaded to Pastebin. (limit is 512 KB)") return false else local key = "0ec2eb25b6166c0c27a394ae118ad829" local response = http.post( "https://pastebin.com/api/api_post.php", "api_option=paste&" .. "api_dev_key=" .. key .. "&" .. "api_paste_format=lua&" .. "api_paste_name=" .. textutils.urlEncode(name) .. "&" .. "api_paste_code=" .. textutils.urlEncode(archive) ) if response then local sResponse = response.readAll() response.close() local sCode = string.match( sResponse, "[^/]+$" ) return sCode, sResponse else return false end end end local writeArchiveData = function(archive, outputPath) local file for name, contents in pairs(archive.data) do setTextColor(colors.lightGray) sWrite("'" .. name .. "'...") if contents == true then -- indicates empty directory fs.makeDir(fs.combine(outputPath, name)) else file = fs.open(fs.combine(outputPath, name), "w") if file then file.write(contents) file.close() end end if file then setTextColor(colors.green) sPrint("good") else setTextColor(colors.red) sPrint("fail") end end setTextColor(colors.white) specialPrint("Unpacked to '", outputPath .. "/", "'.", colors.yellow) end local getSkynet = function() if http.websocket then -- Skynet only supports messages that are 65506 bytes or smaller -- I'm just going with 65200 bytes to play safe. local defineBigOnes = function(skynet) local div = 65200 return function(channel, _message) -- big send local message = textutils.serialize(_message) for i = 1, math.ceil(#message / div) do skynet.send(progdor.channel, { msg = message:sub( (i - 1) * div + 1, i * div ), complete = i == math.ceil(#message / div), part = i }) sleep(0.1) cWrite(".", colors.lightGray) end end, function(channel) -- big receive local ch, msg local output = {} local gotFile = false while true do ch, msg = skynet.receive(channel) if type(msg) == "table" then if type(msg.complete) == "boolean" and type(msg.msg) == "string" and type(msg.part) == "number" then output[msg.part] = msg.msg cWrite(".", colors.lightGray) if msg.complete then break end end end end return channel, textutils.unserialize(table.concat(output)) end end if skynet then local bS, bR = defineBigOnes(skynet) skynet.open(progdor.channel) return skynet, "", bS, bR else if fs.exists(progdor.skynetPath) then local sn = dofile(progdor.skynetPath) sn.open(progdor.channel) local bS, bR = defineBigOnes(sn) return sn, "", bS, bR else local net, contents = http.get(progdor.skynetURL) if net then contents = net.readAll() local file = fs.open(progdor.skynetPath, "w") file.write(contents) file.close() local sn = dofile(progdor.skynetPath) local bS, bR = defineBigOnes(sn) sn.open(progdor.channel) return sn, "", bS, bR else return false, "Couldn't download Skynet." end end end else return false, "This version of CC does not support Skynet." end end local getModem = function() local mod = peripheral.find("modem") if mod then mod.open(progdor.channel) return mod else return false, "No modem was found." end end local archive local doOverwrite, doContinue = false, true --[[ JUST SUMMIN' UP THE ELSEIF CHAIN if mode == "api" then elseif trMode == "transmit" then if mode == "pack" then end elseif trMode == "receive" then if mode == "pack" then end elseif mode == "pack" then elseif mode == "unpack" then elseif mode == "inspect" then end --]] -- API mode takes top priority if mode == "api" then return { parseArchive = parseArchive, parseArchiveData = parseArchiveData, buildArchive = buildArchive, uploadToPastebin = uploadToPastebin, } -- after that, trans elseif trMode == "transmit" then -- assemble something to send local output = {name = fs.getName(inputPath)} if mode == "pack" then output.contents = textutils.serialize(buildArchive(inputPath, mainFile, useCompression)) else local file = fs.open(inputPath, "r") output.contents = file.readAll() file.close() end local grr if useSkynet then if not skynet then cWrite("Connecting to Skynet...", colors.lightGray) skynet, grr, skynetBigSend, skynetBigReceive = getSkynet() if not skynet then print(grr) print("Aborting.") return false else cPrint("good", colors.green) end end cWrite("Sending file...", colors.lightGray) skynetBigSend(progdor.channel, output) skynet.socket.close() cPrint("good", colors.green) sWrite("Sent '") cWrite(fs.getName(inputPath), colors.yellow) sPrint("' using Skynet.") else modem, grr = getModem() if not modem then print(grr) print("Abort.") return false end cWrite("Sending file...", colors.lightGray) modem.transmit(progdor.channel, progdor.channel, output) cPrint("good", colors.green) sWrite("Sent '") cWrite(fs.getName(inputPath), colors.yellow) sPrint("' using modem.") end elseif trMode == "receive" then local grr local gotFile = false local input, channel local didAbort = false if useSkynet then if not skynet then cWrite("Connecting to Skynet...", colors.lightGray) skynet, grr, skynetBigSend, skynetBigReceive = getSkynet() if not skynet then print(grr) print("Aborting.") return false else cPrint("good", colors.green) end end cWrite("Waiting for file on Skynet...", colors.lightGray) local result, grr = pcall(function() sleep(0.05) while true do if parallel.waitForAny(function() channel, input = skynetBigReceive(progdor.channel) end, function() local evt while true do evt = {os.pullEvent()} if evt[1] == "key" then if evt[2] == keys.q then return end end end end) == 2 then print("\nAbort.") sleep(0.05) didAbort = true break end if channel == progdor.channel and type(input) == "table" then if type(input.contents) == "string" and type(input.name) == "string" then gotFile = true break end end end end) skynet.socket.close() if not result then error(grr, 0) end else modem, grr = getModem() if not modem then print(grr) print("Abort.") sleep(0.05) return false end modem.open(progdor.channel) local evt cWrite("Waiting for file...", colors.lightGray) sleep(0.05) while true do evt = {os.pullEvent()} if evt[1] == "modem_message" then if evt[3] == progdor.channel and type(evt[5]) == "table" then if type(evt[5].contents) == "string" and type(evt[5].name) == "string" then input = evt[5] gotFile = true break end end elseif evt[1] == "key" then if evt[2] == keys.q then print("\nAbort.") sleep(0.05) didAbort = true break end end end end if gotFile then cPrint("good", colors.green) if input.contents then local writePath, c = fs.combine(shell.dir(), outputPath or input.name) write("Received '") cWrite(input.name or outputPath, colors.yellow, true) print("'.") if (not justOverwrite and fs.exists(writePath)) or fs.isReadOnly(writePath) then write("\nBut, '") cWrite(fs.getName(writePath), colors.yellow, true) print("' is already there.") local roCount = 0 local showROmessage = function(roCount) if roCount == 1 then write("\nThat file/folder is ") cWrite("read-only", colors.yellow, true) print("!") elseif roCount == 2 then write("\nI told you, that file/folder is ") cWrite("read-only", colors.yellow, true) print("!") elseif roCount == 3 then write("\nNope. The file/folder is ") cWrite("read-only", colors.yellow, true) print(".") elseif roCount == 4 then write("\nDoes the phrase ") cWrite("read-only", colors.yellow, true) print(" mean nothing to you?") elseif roCount == 5 then print("\nAlright wise-ass, that's enough.") elseif roCount > 5 then write("\nThat's ") cWrite("read-only", colors.yellow, true) print(", damn you!") end end while true do sleep(0.05) if roCount < 5 then write("Overwrite [Y/N]? Or [R]ename?\n") c = choice("nry", false) else write("Overwrite [ /N]? Or [R]ename?\n") c = choice("nr", false) end if c == 3 then if fs.isReadOnly(writePath) then roCount = roCount + 1 showROmessage(roCount) else break end elseif c == 1 then print("Abort.") return false elseif c == 2 then print("New name:") if shell.dir() == "" then write("/") else write("/" .. shell.dir() .. "/") end writePath = fs.combine(shell.dir(), read()) roCount = roCount + 1 if fs.isReadOnly(writePath) then showROmessage(roCount) else break end end end end local file = fs.open(writePath, "w") file.write(input.contents) file.close() sWrite("Wrote to '") cWrite(writePath, colors.yellow) sPrint("'") end elseif not didAbort then print("fail!") end elseif mode == "pack" then if not pastebinUpload then if fs.isReadOnly(outputPath) then error("Output path is read-only.") elseif fs.exists(outputPath) and (outputPath ~= inputPath) then doContinue, doOverwrite = overwriteOutputPath(inputPath, outputPath, false, justOverwrite) elseif fs.combine("", outputPath) == "" then error("Output path cannot be root.") end if not doContinue then return false elseif outputPath == inputPath then doOverwrite = true end end archive = buildArchive(inputPath, mainFile, useCompression) if exists then if useCompression then sPrint("Using CCA compression.") elseif selfExtractor then sPrint("Tacking on self-extractor.") archive = ([[ local tArg = {...} local selfDelete = false -- if true, deletes extractor after running local file local outputPath = ]] .. (selfExtractorFolder and ( "shell.resolve(\"" .. selfExtractorFolder .. "\")" ) or ( "tArg[1] and shell.resolve(tArg[1]) or ]]" .. ((defaultAutoExtractPath and ("\"" .. defaultAutoExtractPath .. "\"")) or "shell.getRunningProgram()") )) .. [[ local safeColorList = {[colors.white] = true,[colors.lightGray] = true,[colors.gray] = true,[colors.black] = true} local stc = function(color) if (term.isColor() or safeColorList[color]) then term.setTextColor(color) end end local choice = function() local input = "yn" write("[") for a = 1, #input do write(input:sub(a,a):upper()) if a < #input then write(",") end end print("]?") local evt,char repeat evt,char = os.pullEvent("char") until string.find(input:lower(),char:lower()) if verbose then print(char:upper()) end local pos = string.find(input:lower(), char:lower()) return pos, char:lower() end local archive = textutils.unserialize(]] .. textutils.serialize(archive) .. [[) if fs.isReadOnly(outputPath) then error("Output path is read-only. Abort.") elseif fs.getFreeSpace(outputPath) <= #archive then error("Insufficient space. Abort.") end ]] .. ( justOverwrite and [[ if fs.exists(outputPath) and fs.combine("", outputPath) ~= "" then fs.delete(outputPath) end ]] or [[ if fs.exists(outputPath) and fs.combine("", outputPath) ~= "" then print("File/folder already exists! Overwrite?") stc(colors.lightGray) print("(Use -o when making the extractor to always overwrite.)") stc(colors.white) if choice() ~= 1 then error("Chose not to overwrite. Abort.") else fs.delete(outputPath) end end ]] ) .. [[ if selfDelete or (fs.combine("", outputPath) == shell.getRunningProgram()) then fs.delete(shell.getRunningProgram()) end for name, contents in pairs(archive.data) do stc(colors.lightGray) write("'" .. name .. "'...") if contents == true then -- indicates empty directory fs.makeDir(fs.combine(outputPath, name)) else file = fs.open(fs.combine(outputPath, name), "w") if file then file.write(contents) file.close() end end if file then stc(colors.green) print("good") else stc(colors.red) print("fail") end end stc(colors.white) write("Unpacked to '") stc(colors.yellow) write(outputPath .. "/") stc(colors.white) print("'.") ]]) end if pastebinUpload then sWrite("Uploading to Pastebin...") local id, url = uploadToPastebin(archive, fs.getName(inputPath)) if id then setTextColor(colors.green) sPrint("success!") setTextColor(colors.white) sPrint("Uploaded to '" .. url .. "'.") specialPrint("Retrieve with \"", "progdor -pb " .. id .. " " .. fs.getName(inputPath), "\".", colors.yellow) sPrint("You may need to do a Captcha on the website.") if not fs.exists(progdor.PBlogPath) then setTextColor(colors.lightGray) specialPrint("(PB uploads are logged at \"", progdor.PBlogPath, "\".)", colors.yellow) setTextColor(colors.white) end -- precautionary log file local file = fs.open(progdor.PBlogPath, "a") file.writeLine("uploaded \"" .. inputPath .. "\" to \"" .. url .. "\"") file.close() else sPrint("failed!") end else if doOverwrite then fs.delete(outputPath) end local file = fs.open(outputPath, "w") file.write(archive) file.close() if selfExtract then specialPrint("Written self-extractor to '", outputPath, "'.", colors.yellow) else specialPrint("Written to '", outputPath, "'.", colors.yellow) end end else error("No such input path exists.") return false end elseif mode == "unpack" then -- unpack OR upload if pastebinUpload then local file = fs.open(inputPath, "r") archive = file.readAll() file.close() sWrite("Uploading to Pastebin...") local id, url = uploadToPastebin(archive, fs.getName(inputPath)) if id then setTextColor(colors.green) sPrint("success!") setTextColor(colors.white) sPrint("Uploaded to '" .. url .. "'.") specialPrint("Retrieve with \"", "progdor -pb " .. id .. " " .. fs.getName(inputPath), "\".", colors.yellow) sPrint("You may need to do a Captcha on the website.") if not fs.exists(progdor.PBlogPath) then setTextColor(colors.lightGray) specialPrint("(PB uploads are logged at \"", progdor.PBlogPath, "\".)", colors.yellow) setTextColor(colors.white) end -- precautionary log file local file = fs.open(progdor.PBlogPath, "a") file.writeLine("uploaded \"" .. inputPath .. "\" to \"" .. url .. "\"") file.close() else setTextColor(colors.red) sPrint("failed!") setTextColor(colors.white) return false end elseif pastebinGet or directDownload then local url, contents if pastebinGet and directDownload then error("Cannot do both pastebin get and direct download.") elseif fs.isReadOnly(outputPath) then error("Output path is read-only.") elseif fs.combine(outputPath, "") == "" then error("Output path cannot be root.") else if pastebinGet then url = "http://www.pastebin.com/raw/" .. pastebinGet elseif directDownload then url = directDownload end if fs.exists(outputPath) and (outputPath ~= inputPath) or outputPath == shell.getRunningProgram() then doContinue, doOverwrite = overwriteOutputPath(inputPath, outputPath, true, justOverwrite) end if not doContinue then return false elseif outputPath == inputPath then doOverwrite = true end sWrite("Connecting to \"") setTextColor(colors.yellow) sWrite(url) setTextColor(colors.white) sWrite("\"...") local handle = http.get(url) if handle then cPrint("success!", colors.green) contents = handle.readAll() handle.close() -- detects if you didn't solve the captcha, since archives commonly trigger anti-spam measures if ( pastebinGet and (not textutils.unserialize(contents)) and contents:find("Your paste has triggered our automatic SPAM detection filter.") ) then specialPrint("You must go to '", url, "' and do the Captcha to be able to download that paste.", colors.yellow) return false end setTextColor(colors.lightGray) sWrite("Parsing archive...") archive = parseArchiveData(contents) if archive then setTextColor(colors.green) sPrint("good") else setTextColor(colors.red) sPrint("Invalid archive file.") return false end if doOverwrite then fs.delete(outputPath) end writeArchiveData(archive, outputPath) else setTextColor(colors.red) sPrint("failed!") setTextColor(colors.white) return false end end else -- regular unpack if exists then if fs.isReadOnly(outputPath) then error("Output path is read-only.") elseif fs.exists(outputPath) and (outputPath ~= inputPath) or outputPath == shell.getRunningProgram() then doContinue, doOverwrite = overwriteOutputPath(inputPath, outputPath, true, justOverwrite) end if not doContinue then return false elseif outputPath == inputPath then doOverwrite = true end setTextColor(colors.lightGray) sWrite("Parsing archive...") archive = parseArchive(inputPath) if archive then setTextColor(colors.green) sPrint("good") if doOverwrite then fs.delete(outputPath) end writeArchiveData(archive, outputPath) else setTextColor(colors.red) sPrint("Invalid archive file.") return false end else error("No such input path exists.") end end elseif mode == "inspect" then if exists and (not fs.isDir(inputPath)) then archive = parseArchive(inputPath, true) local totalSize = 0 local amountOfFiles = 0 local averageSize = 0 local output = {} if archive then for k,v in pairs(archive) do if k == "data" then for name, contents in pairs(v) do if contents then -- don't count directories, where contents == false totalSize = totalSize + #contents amountOfFiles = amountOfFiles + 1 end end averageSize = math.ceil(totalSize / amountOfFiles) else output[#output + 1] = k .. " = \"" .. tostring(v) .. "\"" end end sPrint("# of files: " .. amountOfFiles) sPrint("Total size: " .. totalSize .. " bytes (" .. round(totalSize / 1024, 1) .. " KB)") sPrint("Aveg. size: " .. averageSize .. " bytes (" .. round(averageSize / 1024, 1) .. " KB)") sPrint(("-"):rep(scr_x)) for i = 1, #output do sPrint(output[i]) end else error("Invalid archive file.") end else if fs.isDir(inputPath) then error("Cannot inspect directories.") else error("No such input path exists.") end end end