Advertisement
Lyqyd

Net Shell

Nov 25th, 2012
4,030
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 23.19 KB | None | 0 0
  1. local args = { ... }
  2.  
  3. local connections = {}
  4.  
  5. local nshAPI = {
  6.     connList = connections
  7. }
  8.  
  9. local bufferDirs = {"/","/LyqydOS/","/usr/apis/","/disk/"}
  10.  
  11. if not framebuffer then
  12.     for i = 1, #bufferDirs do
  13.         if fs.exists(bufferDirs[i].."framebuffer") and os.loadAPI(bufferDirs[i].."framebuffer") then
  14.             break
  15.         end
  16.     end
  17. end
  18. if not framebuffer then
  19.     print("Couldn't find framebuffer API, using fallback")
  20. end
  21.  
  22. local function rawSend(id, msg)
  23.     if term.current then
  24.         return rednet.send(id, msg, "tror")
  25.     else
  26.         return rednet.send(id, msg)
  27.     end
  28. end
  29.  
  30. local function rawRecv(id, timeout)
  31.     if type(timeout) == "number" then timeout = os.startTimer(timeout) end
  32.     while true do
  33.         event = {os.pullEvent()}
  34.         if event[1] == "rednet_message" and (id == nil and true or event[2] == id) and (not term.current and true or event[4] == "tror") then
  35.             return event[3]
  36.         elseif event[1] == "timer" and event[2] == timeout then
  37.             return nil
  38.         end
  39.     end
  40. end
  41.  
  42.  
  43. nshAPI.getRemoteID = function()
  44.     --check for connected clients with matching threads.
  45.     for cNum, cInfo in pairs(nshAPI.connList) do
  46.         if cInfo and type(cInfo) == "table" and cInfo.thread == coroutine.running() then
  47.             if cNum == "localShell" then
  48.                 --if we are a client running on the server, return the remote server ID.
  49.                 if nshAPI.serverNum then
  50.                     return nshAPI.serverNum
  51.                 else
  52.                     return nil
  53.                 end
  54.             end
  55.             return cNum
  56.         end
  57.     end
  58.     --client running without local server, return remote server ID.
  59.     if nshAPI.serverNum then return nshAPI.serverNum end
  60.     return nil
  61. end
  62.  
  63. nshAPI.send = function(msg)
  64.     local id = nshAPI.getRemoteID()
  65.     if id then
  66.         return rawSend(id, msg)
  67.     end
  68.     return nil
  69. end
  70.  
  71. nshAPI.receive = function(timeout)
  72.     return rawRecv(nshAPI.getRemoteID(), timeout)
  73. end
  74.  
  75. nshAPI.getClientCapabilities = function()
  76.     if nshAPI.clientCapabilities then return nshAPI.clientCapabilities end
  77.     nshAPI.send("SP:;clientCapabilities")
  78.     return nshAPI.receive(1)
  79. end
  80.  
  81. nshAPI.getRemoteConnections = function()
  82.     local remotes = {}
  83.     for cNum, cInfo in pairs(nshAPI.connList) do
  84.         table.insert(remotes, cNum)
  85.         if cInfo.outbound then
  86.             table.insert(remotes, cInfo.outbound)
  87.         end
  88.     end
  89.     return remotes
  90. end
  91.  
  92. nshAPI.packFile = function(path)
  93.     local data = {}
  94.     local count = 0
  95.     local handle = io.open(path, "rb")
  96.     if handle then
  97.         local byte = handle:read()
  98.         repeat
  99.             data[#data + 1] = byte
  100.             count = count + 1
  101.             if count % 1000 == 0 then
  102.                 os.queueEvent("yield")
  103.                 os.pullEvent("yield")
  104.             end
  105.             byte = handle:read()
  106.         until not byte
  107.         handle:close()
  108.     else
  109.         return false
  110.     end
  111.     local outputTable = {}
  112.     for i = 1, #data, 3 do
  113.         local num1, num2, num3 = data[i], data[i + 1] or 0, data[i + 2] or 0
  114.         table.insert(outputTable, string.char(bit.band(bit.brshift(num1, 2), 63)))
  115.         table.insert(outputTable, string.char(bit.bor(bit.band(bit.blshift(num1, 4), 48), bit.band(bit.brshift(num2, 4), 15))))
  116.         table.insert(outputTable, string.char(bit.bor(bit.band(bit.blshift(num2, 2), 60), bit.band(bit.brshift(num3, 6), 3))))
  117.         table.insert(outputTable, string.char(bit.band(num3, 63)))
  118.     end
  119.     --mark non-data (invalid) bytes
  120.     if #data % 3 == 1 then
  121.         outputTable[#outputTable] = "="
  122.         outputTable[#outputTable - 1] = "="
  123.     elseif #data % 3 == 2 then
  124.         outputTable[#outputTable] = "="
  125.     end
  126.     return table.concat(outputTable, "")
  127. end
  128.  
  129. nshAPI.unpackAndSaveFile = function(path, data)
  130.     local outputTable = {}
  131.     for i=1, #data, 4 do
  132.         local char1, char2, char3, char4 = string.byte(string.sub(data, i, i)), string.byte(string.sub(data, i + 1, i + 1)), string.byte(string.sub(data, i + 2, i + 2)), string.byte(string.sub(data, i + 3, i + 3))
  133.         table.insert(outputTable, bit.band(bit.bor(bit.blshift(char1, 2), bit.brshift(char2, 4)), 255))
  134.         table.insert(outputTable, bit.band(bit.bor(bit.blshift(char2, 4), bit.brshift(char3, 2)), 255))
  135.         table.insert(outputTable, bit.band(bit.bor(bit.blshift(char3, 6), char4), 255))
  136.     end
  137.     --clean invalid bytes if marked
  138.     if string.sub(data, #data, #data) == "=" then
  139.         table.remove(outputTable)
  140.         if string.sub(data, #data - 1, #data - 1) == "=" then
  141.             table.remove(outputTable)
  142.         end
  143.     end
  144.     local handle = io.open(path, "wb")
  145.     if handle then
  146.         for i = 1, #outputTable do
  147.             handle:write(outputTable[i])
  148.             if i % 10 == 0 then
  149.                 os.startTimer(0.1)
  150.                 os.pullEvent("timer")
  151.             end
  152.         end
  153.         handle:close()
  154.     end
  155. end
  156.  
  157. local packetConversion = {
  158.     query = "SQ",
  159.     response = "SR",
  160.     data = "SP",
  161.     close = "SC",
  162.     fileQuery = "FQ",
  163.     fileSend = "FS",
  164.     fileResponse = "FR",
  165.     fileHeader = "FH",
  166.     fileData = "FD",
  167.     fileEnd = "FE",
  168.     textWrite = "TW",
  169.     textCursorPos = "TC",
  170.     textGetCursorPos = "TG",
  171.     textGetSize = "TD",
  172.     textInfo = "TI",
  173.     textClear = "TE",
  174.     textClearLine = "TL",
  175.     textScroll = "TS",
  176.     textBlink = "TB",
  177.     textColor = "TF",
  178.     textBackground = "TK",
  179.     textIsColor = "TA",
  180.     textTable = "TT",
  181.     event = "EV",
  182.     SQ = "query",
  183.     SR = "response",
  184.     SP = "data",
  185.     SC = "close",
  186.     FQ = "fileQuery",
  187.     FS = "fileSend",
  188.     FR = "fileResponse",
  189.     FH = "fileHeader",
  190.     FD = "fileData",
  191.     FE = "fileEnd",
  192.     TW = "textWrite",
  193.     TC = "textCursorPos",
  194.     TG = "textGetCursorPos",
  195.     TD = "textGetSize",
  196.     TI = "textInfo",
  197.     TE = "textClear",
  198.     TL = "textClearLine",
  199.     TS = "textScroll",
  200.     TB = "textBlink",
  201.     TF = "textColor",
  202.     TK = "textBackground",
  203.     TA = "textIsColor",
  204.     TT = "textTable",
  205.     EV = "event",
  206. }
  207.  
  208. local function openModem()
  209.     local modemFound = false
  210.     for _, side in ipairs(rs.getSides()) do
  211.         if peripheral.getType(side) == "modem" then
  212.             if not rednet.isOpen(side) then rednet.open(side) end
  213.             modemFound = true
  214.             break
  215.         end
  216.     end
  217.     return modemFound
  218. end
  219.  
  220. local function send(id, pType, message)
  221.     if pType and message then
  222.         return rawSend(id, packetConversion[pType]..":;"..message)
  223.     end
  224. end
  225.  
  226. local function awaitResponse(id, time)
  227.     id = tonumber(id)
  228.     local listenTimeOut = nil
  229.     local messRecv = false
  230.     if time then listenTimeOut = os.startTimer(time) end
  231.     while not messRecv do
  232.         local event, p1, p2 = os.pullEvent()
  233.         if event == "timer" and p1 == listenTimeOut then
  234.             return false
  235.         elseif event == "rednet_message" then
  236.             sender, message = p1, p2
  237.             if id == sender and message then
  238.                 if packetConversion[string.sub(message, 1, 2)] then packetType = packetConversion[string.sub(message, 1, 2)] end
  239.                 message = string.match(message, ";(.*)")
  240.                 messRecv = true
  241.             end
  242.         end
  243.     end
  244.     return packetType, message
  245. end
  246.  
  247. local function processText(conn, pType, value)
  248.     if not pType then return false end
  249.     if pType == "textWrite" and value then
  250.         term.write(value)
  251.     elseif pType == "textClear" then
  252.         term.clear()
  253.     elseif pType == "textClearLine" then
  254.         term.clearLine()
  255.     elseif pType == "textGetCursorPos" then
  256.         local x, y = term.getCursorPos()
  257.         send(conn, "textInfo", math.floor(x)..","..math.floor(y))
  258.     elseif pType == "textCursorPos" then
  259.         local x, y = string.match(value, "(%-?%d+),(%-?%d+)")
  260.         term.setCursorPos(tonumber(x), tonumber(y))
  261.     elseif pType == "textBlink" then
  262.         if value == "true" then
  263.             term.setCursorBlink(true)
  264.         else
  265.             term.setCursorBlink(false)
  266.         end
  267.     elseif pType == "textGetSize" then
  268.         x, y = term.getSize()
  269.         send(conn, "textInfo", x..","..y)
  270.     elseif pType == "textScroll" and value then
  271.         term.scroll(tonumber(value))
  272.     elseif pType == "textIsColor" then
  273.         send(conn, "textInfo", tostring(term.isColor()))
  274.     elseif pType == "textColor" and value then
  275.         value = tonumber(value)
  276.         if (value == 1 or value == 32768) or term.isColor() then
  277.             term.setTextColor(value)
  278.         end
  279.     elseif pType == "textBackground" and value then
  280.         value = tonumber(value)
  281.         if (value == 1 or value == 32768) or term.isColor() then
  282.             term.setBackgroundColor(value)
  283.         end
  284.     elseif pType == "textTable" then
  285.         local linesTable = textutils.unserialize(value)
  286.         for i=1, linesTable.sizeY do
  287.             term.setCursorPos(1,i)
  288.             local lineEnd = false
  289.             local offset = 1
  290.             while not lineEnd do
  291.                 local textColorString = string.match(string.sub(linesTable.textColor[i], offset), string.sub(linesTable.textColor[i], offset, offset).."*")
  292.                 local backColorString = string.match(string.sub(linesTable.backColor[i], offset), string.sub(linesTable.backColor[i], offset, offset).."*")
  293.                 term.setTextColor(2 ^ tonumber(string.sub(textColorString, 1, 1), 16))
  294.                 term.setBackgroundColor(2 ^ tonumber(string.sub(backColorString, 1, 1), 16))
  295.                 term.write(string.sub(linesTable.text[i], offset, offset + math.min(#textColorString, #backColorString) - 1))
  296.                 offset = offset + math.min(#textColorString, #backColorString)
  297.                 if offset > linesTable.sizeX then lineEnd = true end
  298.             end
  299.         end
  300.         term.setCursorPos(linesTable.cursorX, linesTable.cursorY)
  301.         term.setCursorBlink(linesTable.cursorBlink)
  302.     end
  303.     return
  304. end
  305.  
  306. local function textRedirect(id)
  307.     local textTable = {}
  308.     textTable.id = id
  309.     textTable.write = function(text)
  310.         return send(textTable.id, "textWrite", text)
  311.     end
  312.     textTable.clear = function()
  313.         return send(textTable.id, "textClear", "nil")
  314.     end
  315.     textTable.clearLine = function()
  316.         return send(textTable.id, "textClearLine", "nil")
  317.     end
  318.     textTable.getCursorPos = function()
  319.         send(textTable.id, "textGetCursorPos", "nil")
  320.         local pType, message = awaitResponse(textTable.id, 2)
  321.         if pType and pType == "textInfo" then
  322.             local x, y = string.match(message, "(%-?%d+),(%-?%d+)")
  323.             return tonumber(x), tonumber(y)
  324.         end
  325.     end
  326.     textTable.setCursorPos = function(x, y)
  327.         return send(textTable.id, "textCursorPos", math.floor(x)..","..math.floor(y))
  328.     end
  329.     textTable.setCursorBlink = function(b)
  330.         if b then
  331.             return send(textTable.id, "textBlink", "true")
  332.         else
  333.             return send(textTable.id, "textBlink", "false")
  334.         end
  335.     end
  336.     textTable.getSize = function()
  337.         send(textTable.id, "textGetSize", "nil")
  338.         local pType, message = awaitResponse(textTable.id, 2)
  339.         if pType and pType == "textInfo" then
  340.             local x, y = string.match(message, "(%d+),(%d+)")
  341.             return tonumber(x), tonumber(y)
  342.         end
  343.     end
  344.     textTable.scroll = function(lines)
  345.         return send(textTable.id, "textScroll", lines)
  346.     end
  347.     textTable.isColor = function()
  348.         send(textTable.id, "textIsColor", "nil")
  349.         local pType, message = awaitResponse(textTable.id, 2)
  350.         if pType and pType == "textInfo" then
  351.             if message == "true" then
  352.                 return true
  353.             end
  354.         end
  355.         return false
  356.     end
  357.     textTable.isColour = textTable.isColor
  358.     textTable.setTextColor = function(color)
  359.         return send(textTable.id, "textColor", tostring(color))
  360.     end
  361.     textTable.setTextColour = textTable.setTextColor
  362.     textTable.setBackgroundColor = function(color)
  363.         return send(textTable.id, "textBackground", tostring(color))
  364.     end
  365.     textTable.setBackgroundColour = textTable.setBackgroundColor
  366.     return textTable
  367. end
  368.  
  369. local function getServerID(server)
  370.     if tonumber(server) then
  371.         return tonumber(server)
  372.     elseif term.current then
  373.         return rednet.lookup("tror", args[1])
  374.     end
  375. end
  376.  
  377. local function resumeThread(conn, event)
  378.     local cInfo = connections[conn]
  379.     if connections[conn] and (not connections[conn].filter or event[1] == connections[conn].filter) then
  380.         connections[conn].filter = nil
  381.         local _oldTerm = term.redirect(connections[conn].target)
  382.         local passback = {coroutine.resume(connections[conn].thread, unpack(event))}
  383.         if passback[1] and passback[2] then
  384.             connections[conn].filter = passback[2]
  385.         end
  386.         if coroutine.status(connections[conn].thread) == "dead" then
  387.             send(conn, "close", "disconnect")
  388.             connections[conn] = false
  389.         end
  390.         if _oldTerm then
  391.             term.redirect(_oldTerm)
  392.         else
  393.             term.restore()
  394.         end
  395.         if connections[conn] and conn ~= "localShell" and framebuffer and connections[conn].target.changed then
  396.             send(conn, "textTable", textutils.serialize(connections[conn].target.buffer))
  397.             connections[conn].target.changed = false
  398.         end
  399.     end
  400. end
  401.  
  402. local eventFilter = {
  403.     key = true,
  404.     char = true,
  405.     mouse_click = true,
  406.     mouse_drag = true,
  407.     mouse_scroll = true,
  408. }
  409.  
  410. local function newSession(conn, x, y, color)
  411.     local session = {}
  412.     local path = "/rom/programs/shell"
  413.     if #args >= 2 and shell.resolveProgram(args[2]) then path = shell.resolveProgram(args[2]) end
  414.     session.thread = coroutine.create(function() shell.run(path) end)
  415.     if framebuffer then
  416.         local target = {}
  417.         local _target = framebuffer.new(x, y, color)
  418.         for k, v in pairs(_target) do
  419.             if type(k) == "string" and type(v) == "function" then
  420.                 target[k] = function(...)
  421.                     target.changed = true
  422.                     return _target[k](...)
  423.                 end
  424.             else
  425.                 target[k] = _target[k]
  426.             end
  427.         end
  428.         session.target = target
  429.     else
  430.         session.target = textRedirect(conn)
  431.     end
  432.     session.status = "open"
  433.     _oldTerm = term.redirect(session.target)
  434.     coroutine.resume(session.thread)
  435.     if _oldTerm then
  436.         term.redirect(_oldTerm)
  437.     else
  438.         term.restore()
  439.     end
  440.     if framebuffer then
  441.         send(conn, "textTable", textutils.serialize(session.target.buffer))
  442.         session.target.changed = false
  443.     end
  444.     return session
  445. end
  446.  
  447. if #args >= 1 and args[1] == "host" then
  448.     _G.nsh = nshAPI
  449.     if not openModem() then return end
  450.     if term.current then
  451.         if args[4] then
  452.             rednet.host("tror", args[4])
  453.         elseif os.getComputerLabel() then
  454.             rednet.host("tror", os.getComputerLabel())
  455.         else
  456.             print("No label or hostname provided!")
  457.             return
  458.         end
  459.     end
  460.     local connInfo = {}
  461.     connInfo.target = term.current and term.current() or term.native
  462.     local path = "/rom/programs/shell"
  463.     if #args >= 3 and shell.resolveProgram(args[3]) then path = shell.resolveProgram(args[3]) end
  464.     connInfo.thread = coroutine.create(function() shell.run(path) end)
  465.     connections.localShell = connInfo
  466.     term.clear()
  467.     term.setCursorPos(1,1)
  468.     coroutine.resume(connections.localShell.thread)
  469.  
  470.     while true do
  471.         event = {os.pullEventRaw()}
  472.         if event[1] == "rednet_message" then
  473.             if type(event[3]) == "string" and packetConversion[string.sub(event[3], 1, 2)] then
  474.                 --this is a packet meant for us.
  475.                 conn = event[2]
  476.                 packetType = packetConversion[string.sub(event[3], 1, 2)]
  477.                 message = string.match(event[3], ";(.*)")
  478.                 if connections[conn] and connections[conn].status == "open" then
  479.                     if packetType == "event" or string.sub(packetType, 1, 4) == "text" then
  480.                         local eventTable = {}
  481.                         if packetType == "event" then
  482.                             eventTable = textutils.unserialize(message)
  483.                         else
  484.                             --we can pass the packet in raw, since this is not an event packet.
  485.                             eventTable = event
  486.                         end
  487.                         resumeThread(conn, eventTable)
  488.                     elseif packetType == "query" then
  489.                         local connType, color, x, y = string.match(message, "(%a+):(%a+);(%d+),(%d+)")
  490.                         if connType == "connect" or (connType == "resume" and (not framebuffer)) then
  491.                             --reset connection
  492.                             send(conn, "response", "OK")
  493.                             connections[conn] = newSession(conn, tonumber(x), tonumber(y), color == "true")
  494.                         elseif connType == "resume" and connections[conn] and tonumber(x) == connections[conn].target.buffer.sizeX and tonumber(y) == connections[conn].target.buffer.sizeY then
  495.                             --restore connection
  496.                             send(conn, "response", "OK")
  497.                             send(conn, "textTable", textutils.serialize(connections[conn].target.buffer))
  498.                         end
  499.                     elseif packetType == "close" then
  500.                         connections[conn] = nil
  501.                         send(conn, "close", "disconnect")
  502.                         --close connection
  503.                     else
  504.                         --we got a packet, have an open connection, but despite it being in the conversion table, don't handle it ourselves. Send it onward.
  505.                         resumeThread(conn, event)
  506.                     end
  507.                 elseif packetType ~= "query" then
  508.                     --usually, we would send a disconnect here, but this prevents one from hosting nsh and connecting to other computers.  Pass these to all shells as well.
  509.                     for cNum, cInfo in pairs(connections) do
  510.                         resumeThread(cNum, event)
  511.                     end
  512.                 else
  513.                     --open new connection
  514.                     send(conn, "response", "OK")
  515.                     local color, x, y = string.match(message, "connect:(%a+);(%d+),(%d+)")
  516.                     local connInfo = newSession(conn, tonumber(x), tonumber(y), color == "true")
  517.                     connections[conn] = connInfo
  518.                 end
  519.             else
  520.                 --rednet message, but not in the correct format, so pass to all shells.
  521.                 for cNum, cInfo in pairs(connections) do
  522.                     resumeThread(cNum, event)
  523.                 end
  524.             end
  525.         elseif eventFilter[event[1]] then
  526.             --user interaction.
  527.             coroutine.resume(connections.localShell.thread, unpack(event))
  528.             if coroutine.status(connections.localShell.thread) == "dead" then
  529.                 for cNum, cInfo in pairs(connections) do
  530.                     if cNum ~= "localShell" then
  531.                         send(cNum, "close", "disconnect")
  532.                     end
  533.                 end
  534.                 return
  535.             end
  536.         else
  537.             --dispatch all other events to all shells
  538.             for cNum, cInfo in pairs(connections) do
  539.                 resumeThread(cNum, event)
  540.             end
  541.         end
  542.     end
  543.  
  544. elseif #args <= 2 and nsh and nsh.getRemoteID() then
  545.     print(nsh.getRemoteID())
  546.     --forwarding mode
  547.     local conns = nsh.getRemoteConnections()
  548.     for i = 1, #conns do
  549.         if conns[i] == serverNum then
  550.             print("Cyclic connection refused.")
  551.             return
  552.         end
  553.     end
  554.     local fileTransferState = nil
  555.     local fileData = nil
  556.     local serverNum = getServerID(args[1])
  557.     if not serverNum then
  558.         print("Server Not Found")
  559.         return
  560.     end
  561.     send(serverNum, "query", "connect")
  562.     local pType, message = awaitResponse(serverNum, 2)
  563.     if pType ~= "response" then
  564.         print("Connection Failed")
  565.         return
  566.     else
  567.         nsh.connList[nsh.getRemoteID()].outbound = serverNum
  568.         term.clear()
  569.         term.setCursorPos(1,1)
  570.     end
  571.     local clientID = nsh.getRemoteID()
  572.     local serverID = tonumber(args[1])
  573.     while true do
  574.         event = {os.pullEvent()}
  575.         if event[1] == "rednet_message" then
  576.             if event[2] == clientID or event[2] == serverID then
  577.                 if event[2] == serverID and string.sub(event[3], 1, 2) == "SC" then break end
  578.                 rednet.send((event[2] == clientID and serverID or clientID), event[3])
  579.             end
  580.         elseif eventFilter[event[1]] then
  581.             rednet.send(serverID, "EV:;"..textutils.serialize(event))
  582.         end
  583.     end
  584.     nsh.connList[nsh.getRemoteID()].outbound = nil
  585.     term.clear()
  586.     term.setCursorPos(1, 1)
  587.     print("Connection closed by server")
  588.  
  589. elseif #args >= 1 then --either no server running or we are the local shell on the server.
  590.     if not openModem() then return end
  591.     local serverNum = getServerID(args[1])
  592.     if not serverNum then
  593.         print("Server Not Found")
  594.         return
  595.     end
  596.     if nsh then
  597.         local conns = nsh.getRemoteConnections()
  598.         for i = 1, #conns do
  599.             if conns[i] == serverNum then
  600.                 print("Connection refused.")
  601.                 return
  602.             end
  603.         end
  604.     end
  605.     local fileTransferState = nil
  606.     local fileData = nil
  607.     local fileBinaryData = nil
  608.     local unpackCo = {}
  609.     local color = term.isColor()
  610.     local x, y = term.getSize()
  611.     if args[2] == "resume" then
  612.         send(serverNum, "query", "resume:"..tostring(color)..";"..tostring(x)..","..tostring(y))
  613.     else
  614.         send(serverNum, "query", "connect:"..tostring(color)..";"..tostring(x)..","..tostring(y))
  615.     end
  616.     local timeout = os.startTimer(2)
  617.     while true do
  618.         local event = {os.pullEvent()}
  619.         if event[1] == "timer" and event[2] == timeout then
  620.             print("Connection failed.")
  621.             return
  622.         elseif event[1] == "rednet_message" and event[2] == serverNum and type(event[3]) == "string" and string.sub(event[3], 1, 2) == "SR" then
  623.             if nsh then nshAPI = nsh end
  624.             if nshAPI.connList and nshAPI.connList.localShell then nshAPI.connList.localShell.outbound = serverNum end
  625.             nshAPI.serverNum = serverNum
  626.             nshAPI.clientCapabilities = "-fileTransfer-extensions-"
  627.             term.clear()
  628.             term.setCursorPos(1,1)
  629.             break
  630.         end
  631.     end
  632.  
  633.     while true do
  634.         event = {os.pullEventRaw()}
  635.         if #unpackCo > 0 then
  636.             for i = #unpackCo, 1, -1 do
  637.                 if coroutine.status(unpackCo[i]) ~= "dead" then
  638.                     coroutine.resume(unpackCo[i], unpack(event))
  639.                 else
  640.                     table.remove(unpackCo, i)
  641.                 end
  642.             end
  643.         end
  644.         if event[1] == "rednet_message" and event[2] == serverNum and type(event[3]) == "string" then
  645.             if packetConversion[string.sub(event[3], 1, 2)] then
  646.                 packetType = packetConversion[string.sub(event[3], 1, 2)]
  647.                 message = string.match(event[3], ";(.*)")
  648.                 if string.sub(packetType, 1, 4) == "text" then
  649.                     processText(serverNum, packetType, message)
  650.                 elseif packetType == "data" then
  651.                     if message == "clientCapabilities" then
  652.                         rednet.send(serverNum, nshAPI.clientCapabilities)
  653.                     end
  654.                 elseif packetType == "fileQuery" then
  655.                     --send a file to the server
  656.                     local mode, file = string.match(message, "^(%a)=(.*)")
  657.                     if fs.exists(file) then
  658.                         send(serverNum, "fileHeader", file)
  659.                         if mode == "b" then
  660.                             local fileString = nshAPI.packFile(file)
  661.                             send(serverNum, "fileData", "b="..fileString)
  662.                         else
  663.                             local handle = io.open(file, "r")
  664.                             if handle then
  665.                                 send(serverNum, "fileData", "t="..handle:read("*a"))
  666.                                 handle:close()
  667.                             end
  668.                         end
  669.                     else
  670.                         send(serverNum, "fileHeader", "fileNotFound")
  671.                     end
  672.                     send(serverNum, "fileEnd", "end")
  673.                 elseif packetType == "fileSend" then
  674.                     --receive a file from the server, but don't overwrite existing files.
  675.                     local mode, file = string.match(message, "^(%a)=(.*)")
  676.                     if not fs.exists(file) then
  677.                         fileTransferState = "receive_wait:"..file
  678.                         send(serverNum, "fileResponse", "ok")
  679.                         if mode == "b" then
  680.                             fileBinaryData = ""
  681.                             fileData = nil
  682.                         else
  683.                             fileData = ""
  684.                             fileBinaryData = nil
  685.                         end
  686.                     else
  687.                         send(serverNum, "fileResponse", "reject")
  688.                     end
  689.                 elseif packetType == "fileHeader" then
  690.                     if message == "fileNotFound" then
  691.                         fileTransferState = nil
  692.                     end
  693.                 elseif packetType == "fileData" then
  694.                     if fileTransferState and string.match(fileTransferState, "(.-):") == "receive_wait" then
  695.                         if string.match(message, "^(%a)=") == "b" then
  696.                             fileBinaryData = fileBinaryData..string.match(message, "^b=(.*)")
  697.                         else
  698.                             fileData = fileData..string.match(message, "^t=(.*)")
  699.                         end
  700.                     end
  701.                 elseif packetType == "fileEnd" then
  702.                     if fileTransferState and string.match(fileTransferState, "(.-):") == "receive_wait" then
  703.                         if fileBinaryData then
  704.                             local co = coroutine.create(nshAPI.unpackAndSaveFile)
  705.                             coroutine.resume(co, string.match(fileTransferState, ":(.*)"), fileBinaryData)
  706.                             if coroutine.status(co) ~= "dead" then
  707.                                 table.insert(unpackCo, co)
  708.                             end
  709.                         elseif fileData then
  710.                             local handle = io.open(string.match(fileTransferState, ":(.*)"), "w")
  711.                             if handle then
  712.                                 handle:write(fileData)
  713.                                 handle:close()
  714.                             end
  715.                         end
  716.                         fileTransferState = nil
  717.                     end
  718.                 elseif packetType == "close" then
  719.                     if term.isColor() then
  720.                         term.setBackgroundColor(colors.black)
  721.                         term.setTextColor(colors.white)
  722.                     end
  723.                     term.clear()
  724.                     term.setCursorPos(1, 1)
  725.                     print("Connection closed by server.")
  726.                     nshAPI.serverNum = nil
  727.                     if nshAPI.connList and nshAPI.connList.localShell then nshAPI.connList.localShell.outbound = nil end
  728.                     return
  729.                 end
  730.             end
  731.         elseif event[1] == "mouse_click" or event[1] == "mouse_drag" or event[1] == "mouse_scroll" or event[1] == "key" or event[1] == "char" then
  732.             --pack up event
  733.             send(serverNum, "event", textutils.serialize(event))
  734.         elseif event[1] == "terminate" then
  735.             nshAPI.serverNum = nil
  736.             if nshAPI.localShell then nshAPI.localShell.outbound = nil end
  737.             term.clear()
  738.             term.setCursorPos(1, 1)
  739.             print("Connection closed locally.")
  740.             return
  741.         end
  742.     end
  743. else
  744.     print("Usage: nsh <serverID> [resume]")
  745.     print("       nsh host [remote [local [name]]]")
  746. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement