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