diff --git a/sys/apis/pastebin.lua b/sys/apis/pastebin.lua new file mode 100644 index 0000000..d78c10c --- /dev/null +++ b/sys/apis/pastebin.lua @@ -0,0 +1,146 @@ + +--- Attempts to guess the pastebin ID from the given code or URL +local function extractId(paste) + local patterns = { + "^([%a%d]+)$", + "^https?://pastebin.com/([%a%d]+)$", + "^pastebin.com/([%a%d]+)$", + "^https?://pastebin.com/raw/([%a%d]+)$", + "^pastebin.com/raw/([%a%d]+)$", + } + + for i = 1, #patterns do + local code = paste:match( patterns[i] ) + if code then return code end + end + + return nil +end + +function download(url) + if not http then + return false, "Pastebin requires http API" + end + + if type( url ) ~= "string" then + error( "bad argument #1 (expected string, got " .. type( url ) .. ")", 2 ) + end + + local paste = extractId( url ) + if not paste then + return false, "Invalid pastebin code." + end + + -- Add a cache buster so that spam protection is re-checked + local cacheBuster = ("%x"):format(math.random(0, 2^30)) + local response, err = http.get( + "https://pastebin.com/raw/"..textutils.urlEncode( paste ).."?cb="..cacheBuster + ) + + if response then + -- If spam protection is activated, we get redirected to /paste with Content-Type: text/html + local headers = response.getResponseHeaders() + if not headers["Content-Type"] or not headers["Content-Type"]:find( "^text/plain" ) then + return false, "Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode( paste ) + end + + local sResponse = response.readAll() + response.close() + return true, sResponse + end + + return false, err +end + +function put(sPath) + if not http then + return false, "Pastebin requires http API" + end + + if type( sPath ) ~= "string" then + error( "bad argument #1 (expected string, got " .. type( sPath ) .. ")", 2 ) + end + + -- Upload a file to pastebin.com + -- Determine file to upload + if not fs.exists( sPath ) or fs.isDir( sPath ) then + return false, "No such file" + end + + -- Read in the file + local sName = fs.getName( sPath ) + local file = fs.open( sPath, "r" ) + local sText = file.readAll() + file.close() + + -- POST the contents to pastebin + 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(sName).."&".. + "api_paste_code="..textutils.urlEncode(sText) + ) + + if response then + local sResponse = response.readAll() + response.close() + + local sCode = string.match( sResponse, "[^/]+$" ) + + return true, sCode + end + return false, "Failed." +end + +function get(sCode, sPath) + if not http then + return false, "Pastebin requires http API" + end + + if type( sCode ) ~= "string" then + error( "bad argument #1 (expected string, got " .. type( sCode ) .. ")", 2 ) + end + + if type( sPath ) ~= "string" then + error( "bad argument #2 (expected string, got " .. type( sPath ) .. ")", 2 ) + end + + if fs.exists( sPath ) then + return false, "File already exists" + end + + -- GET the contents from pastebin + local res, msg = download(sCode) + if not res then + return res, msg + end + + local file = fs.open( sPath, "w" ) + file.write( msg ) + file.close() + + return true +end + +function run(sCode, ...) + if not http then + return false, "Pastebin requires http API" + end + + if type( sCode ) ~= "string" then + error( "bad argument #1 (expected string, got " .. type( sCode ) .. ")", 2 ) + end + + local res, msg = download(sCode) + if not res then + return res, msg + end + local func, err = load(msg, sCode, "t", _ENV) + if not func then + return func, err + end + return pcall(func, ...) +end diff --git a/sys/apis/ui/components/Notification.lua b/sys/apis/ui/components/Notification.lua index 19d0e86..33a5745 100644 --- a/sys/apis/ui/components/Notification.lua +++ b/sys/apis/ui/components/Notification.lua @@ -10,7 +10,7 @@ UI.Notification = class(UI.Window) UI.Notification.defaults = { UIElement = 'Notification', backgroundColor = colors.gray, - closeInd = '\215', + closeInd = UI.extChars and '\215' or '*', height = 3, timeout = 3, anchor = 'bottom', diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index 9b14146..3c1dd4f 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -9,6 +9,8 @@ local multishell = _ENV.multishell local os = _G.os local shell = _ENV.shell +local FILE = 1 + UI:configure('Files', ...) local config = Config.load('Files', { @@ -38,12 +40,13 @@ local Browser = UI.Page { buttons = { { text = '^-', event = 'updir' }, { text = 'File', dropdown = { - { text = 'Run', event = 'run' }, - { text = 'Edit e', event = 'edit' }, - { text = 'Cloud edit c', event = 'cedit' }, - { text = 'Shell s', event = 'shell' }, + { text = 'Run', event = 'run', flags = FILE }, + { text = 'Edit e', event = 'edit', flags = FILE }, + { text = 'Cloud edit c', event = 'cedit', flags = FILE }, + { text = 'Pastebin put p', event = 'cedit', flags = FILE }, + { text = 'Shell s', event = 'shell' }, UI.MenuBar.spacer, - { text = 'Quit q', event = 'quit' }, + { text = 'Quit q', event = 'quit' }, } }, { text = 'Edit', dropdown = { { text = 'Cut ^x', event = 'cut' }, @@ -140,6 +143,7 @@ local Browser = UI.Page { c = 'cedit', e = 'edit', s = 'shell', + p = 'pastebin', r = 'refresh', space = 'mark', backspace = 'updir', @@ -162,10 +166,8 @@ end function Browser.menuBar:getActive(menuItem) local file = Browser.grid:getSelected() - if file then - if menuItem.event == 'edit' or menuItem.event == 'run' then - return not file.isDir - end + if menuItem.flags == FILE then + return file and not file.isDir end return true end @@ -346,6 +348,7 @@ function Browser:eventHandler(event) elseif event.type == 'cedit' and file then self:run('cedit', file.name) + self:setStatus('Started cloud edit') elseif event.type == 'shell' then self:run('sys/apps/shell.lua') @@ -358,6 +361,16 @@ function Browser:eventHandler(event) elseif event.type == 'associate' then self.associations:show() + elseif event.type == 'pastebin' then + if file and not file.isDir then + local s, m = pastebin.put(file.fullName) + if s then + self.notification:success(string.format('Uploaded as %s', m), 0) + else + self.notification:error(m) + end + end + elseif event.type == 'toggle_hidden' then config.showHidden = not config.showHidden Config.update('Files', config) diff --git a/sys/apps/pastebin.lua b/sys/apps/pastebin.lua new file mode 100644 index 0000000..66057ca --- /dev/null +++ b/sys/apps/pastebin.lua @@ -0,0 +1,75 @@ + +local function printUsage() + print( "Usages:" ) + print( "pastebin put " ) + print( "pastebin get " ) + print( "pastebin run " ) +end + +local tArgs = { ... } +if #tArgs < 2 then + printUsage() + return +end + +if not http then + printError( "Pastebin requires http API" ) + printError( "Set http_enable to true in ComputerCraft.cfg" ) + return +end + +local sCommand = tArgs[1] +if sCommand == "put" then + -- Upload a file to pastebin.com + -- Determine file to upload + local sFile = tArgs[2] + local sPath = shell.resolve( sFile ) + if not fs.exists( sPath ) or fs.isDir( sPath ) then + print( "No such file" ) + return + end + + local success, msg = pastebin.put(sFile) + + if success then + print( "Uploaded as "..msg ) + print( "Run \"pastebin get "..msg.."\" to download anywhere" ) + + else + print( msg ) + end + +elseif sCommand == "get" then + -- Download a file from pastebin.com + if #tArgs < 3 then + printUsage() + return + end + + -- Determine file to download + local sCode = tArgs[2] + local sFile = tArgs[3] + local sPath = shell.resolve( sFile ) + if fs.exists( sPath ) then + print( "File already exists" ) + return + end + + local success, msg = pastebin.get(sCode, sPath) + + if success then + print( "Downloaded as "..sFile ) + else + print(msg) + end +elseif sCommand == "run" then + local sCode = tArgs[2] + + local success, msg = pastebin.run(sCode, table.unpack(tArgs, 3)) + if not success then + print(msg) + end +else + printUsage() + return +end diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index fce0536..d766f4d 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -4,6 +4,8 @@ local UI = require('ui') local colors = _G.colors +-- -t80x30 + if _G.http.websocket then local config = Config.load('cloud') diff --git a/sys/autorun/apps.lua b/sys/autorun/apps.lua index c66656b..97bec3e 100644 --- a/sys/autorun/apps.lua +++ b/sys/autorun/apps.lua @@ -2,3 +2,6 @@ fs.mount('sys/apps/pain.lua', 'urlfs', 'http://pastebin.com/raw/wJQ7jav0') fs.mount('sys/apps/update.lua', 'urlfs', 'http://pastebin.com/raw/UzGHLbNC') fs.mount('sys/apps/Enchat.lua', 'urlfs', 'https://raw.githubusercontent.com/LDDestroier/enchat/master/enchat3.lua') fs.mount('sys/apps/cloud.lua', 'urlfs', 'https://cloud-catcher.squiddev.cc/cloud.lua') + +-- baking this in before a PR to cc:tweaked +os.loadAPI('sys/apis/pastebin.lua') \ No newline at end of file