View difference between Paste ID: FBr22b7g and hqpRw4Jy
SHOW: | | - or go back to the newest paste.
1
--[[    WiRe Server    ]]--
2
--[[      by Dog       ]]--
3
--[[ aka HydrantHunter ]]--
4
--[[  Wi = Wireless    ]]--
5
--[[  Re = Redstone    ]]--
6
--[[ pastebin hqpRw4Jy ]]--
7
local WiReSver = "2.0.05"
8
--[[
9
Tested with/requires:
10
  - Minecraft 1.6.4+
11
  - ComputerCraft 1.63+
12
    - A Computer (standard or advanced) with a modem and one (1) advanced monitor array
13
    - One (1) or more WiRe Clients running on one computer (standard or advanced) each with a modem
14
15
Special thanks to: SquidDev   (AES encryption/decryption)
16
                   Alex Kloss (base64 encoder/decoder)
17
]]--
18
--# CONFIGURATION
19
--# Default Settings
20
local termX, termY = term.getSize()
21
local thisCC = tostring(os.getComputerID())
22
local config = "/data/WiReServerCfg"
23
local ccSettings = {
24
  name = "WiReServer"; --# this server's name
25
  note = "short note"; --# short note/description
26
  color = "Silver";    --# network group
27
  getGPSFix = true;    --# get a GPS fix on startup
28
  newColors = true;    --# using new color names
29
}
30
local tArgs, modemSides, allClients, quietClients, loc = { ... }, { }, { }, { }, { }
31
local pageNum, numPages, modemCount = 1, 1, 0
32
local ccSuccess, kernelState, ccUpdate, help = false, false, false, false
33
local network, client, thisCommand, pollTimer, mon, monX, monY, monSide, monControls, termScreenStatic, updateScreens, netReceive
34
--# Terminal Colors
35
local white = colors.white
36
local black = colors.black
37
local silver = colors.lightGray
38
local gray = colors.gray
39
local brown = colors.brown
40
local yellow = colors.yellow
41
local orange = colors.orange
42
local red = colors.red
43
local magenta = colors.magenta
44
local purple = colors.purple
45
local blue = colors.blue
46
local sky = colors.lightBlue
47
local cyan = colors.cyan
48
local lime = colors.lime
49
local green = colors.green
50
if not term.isColor() then
51
  silver = colors.white
52
  gray = colors.black
53
  brown = colors.white
54
  yellow = colors.white
55
  orange = colors.white
56
  red = colors.white
57
  magenta = colors.white
58
  purple = colors.white
59
  green = colors.white
60
  blue = colors.black
61
  sky = colors.white
62
  cyan = colors.white
63
  lime = colors.white
64
  green = colors.white
65
end
66
--# Monitor colors
67
local mwhite = colors.white
68
local mblack = colors.black
69
local msilver = colors.lightGray
70
local mgray = colors.gray
71
local mbrown = colors.brown
72
local myellow = colors.yellow
73
local morange = colors.orange
74
local mred = colors.red
75
local mmagenta = colors.magenta
76
local mpurple = colors.purple
77
local mblue = colors.blue
78
local msky = colors.lightBlue
79
local mcyan = colors.cyan
80
local mlime = colors.lime
81
local mgreen = colors.green
82
--# Color tables
83
local colorBurst = {
84
  Purple = { purple, mpurple, mwhite };
85
  Magenta = { magenta, mmagenta, mwhite };
86
  Blue = { term.isColor() and blue or white, mblue, mwhite };
87
  Sky = { sky, msky, mblack };
88
  Cyan = { cyan, mcyan, mblack };
89
  Green = { green, mgreen, mwhite };
90
  Lime = { lime, mlime, mblack };
91
  Red = { red, mred, mwhite };
92
  Orange = { orange, morange, mblack };
93
  Yellow = { yellow, myellow, mblack };
94
  Brown = { brown, mbrown, mwhite };
95
  Silver = { silver, msilver, mblack };
96
  Gray = { term.isColor() and gray or white, mgray, mwhite };
97
  White = { white, mwhite, mblack };
98
  Black = { white, mblack, mwhite };
99
}
100
local validStates = {
101
  OPEN = { morange, orange, "CLOSED" };
102
  CLOSED = { mgreen, green, "OPEN" };
103
  ON = { mgreen, green, "OFF" };
104
  OFF = { morange, orange, "ON" };
105
  LOCKED = { mred, red, "" };
106
  UNLOCK = { mgreen, green, "" };
107
  OFFLINE = { mred, red, "" };
108
  WiReQRY = { myellow, yellow, "" };
109
  init = { myellow, yellow, "" };
110
  Noise = { mred, red, "" };
111
}
112
--# END CONFIGURATION
113
114
-- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <[email protected]>
115
-- licensed under the terms of the LGPL2
116
-- http://lua-users.org/wiki/BaseSixtyFour
117
-- character table string
118
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
119
-- encoding
120
function encode(data)
121
  return ((data:gsub('.', function(x) 
122
    local r,b='',x:byte()
123
    for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
124
    return r;
125
  end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
126
    if (#x < 6) then return '' end
127
    local c=0
128
    for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
129
    return b:sub(c+1,c+1)
130
  end)..({ '', '==', '=' })[#data%3+1])
131
end
132
-- decoding
133
function decode(data)
134
  data = string.gsub(data, '[^'..b..'=]', '')
135
  return (data:gsub('.', function(x)
136
    if (x == '=') then return '' end
137
    local r,f='',(b:find(x)-1)
138
    for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
139
    return r;
140
  end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
141
    if (#x ~= 8) then return '' end
142
    local c=0
143
    for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
144
    return string.char(c)
145
  end))
146
end
147
148
-- AES Lua implementation by SquidDev
149
-- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
150
local encrypt, decrypt
151
do
152
  local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
153
  local bit=_W(function(_ENV, ...)
154
  --[[
155
    This bit API is designed to cope with unsigned integers instead of normal integers
156
    To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
157
    These are written in long form because no constant folding.
158
  ]]
159
  local floor = math.floor
160
  local lshift, rshift
161
162
  rshift = function(a,disp)
163
    return floor(a % 4294967296 / 2^disp)
164
  end
165
166
  lshift = function(a,disp)
167
    return (a * 2^disp) % 4294967296
168
  end
169
170
  return {
171
    -- bit operations
172
    bnot = bit32 and bit32.bnot or bit.bnot,
173
    band = bit32 and bit32.band or bit.band,
174
    bor  = bit32 and bit32.bor or bit.bor,
175
    bxor = bit32 and bit32.bxor or bit.bxor,
176
    rshift = rshift,
177
    lshift = lshift,
178
  }
179
  end)
180
181
  local gf=_W(function(_ENV, ...)
182
  -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
183
  local bxor = bit32 and bit32.bxor or bit.bxor
184
  local lshift = bit.lshift
185
  -- private data of gf
186
  local n = 0x100
187
  local ord = 0xff
188
  local irrPolynom = 0x11b
189
  local exp = {}
190
  local log = {}
191
  --
192
  -- add two polynoms (its simply xor)
193
  --
194
  local function add(operand1, operand2)
195
    return bxor(operand1,operand2)
196
  end
197
  --
198
  -- subtract two polynoms (same as addition)
199
  --
200
  local function sub(operand1, operand2)
201
    return bxor(operand1,operand2)
202
  end
203
  --
204
  -- inverts element
205
  -- a^(-1) = g^(order - log(a))
206
  --
207
  local function invert(operand)
208
    -- special case for 1
209
    if (operand == 1) then
210
      return 1
211
    end
212
    -- normal invert
213
    local exponent = ord - log[operand]
214
    return exp[exponent]
215
  end
216
  --
217
  -- multiply two elements using a logarithm table
218
  -- a*b = g^(log(a)+log(b))
219
  --
220
  local function mul(operand1, operand2)
221
    if (operand1 == 0 or operand2 == 0) then
222
      return 0
223
    end
224
    local exponent = log[operand1] + log[operand2]
225
    if (exponent >= ord) then
226
      exponent = exponent - ord
227
    end
228
    return exp[exponent]
229
  end
230
  --
231
  -- divide two elements
232
  -- a/b = g^(log(a)-log(b))
233
  --
234
  local function div(operand1, operand2)
235
    if (operand1 == 0)  then
236
      return 0
237
    end
238
    -- TODO: exception if operand2 == 0
239
    local exponent = log[operand1] - log[operand2]
240
    if (exponent < 0) then
241
      exponent = exponent + ord
242
    end
243
    return exp[exponent]
244
  end
245
  --
246
  -- print logarithmic table
247
  --
248
  local function printLog()
249
    for i = 1, n do
250
      print("log(", i-1, ")=", log[i-1])
251
    end
252
  end
253
  --
254
  -- print exponentiation table
255
  --
256
  local function printExp()
257
    for i = 1, n do
258
      print("exp(", i-1, ")=", exp[i-1])
259
    end
260
  end
261
  --
262
  -- calculate logarithmic and exponentiation table
263
  --
264
  local function initMulTable()
265
    local a = 1
266
    for i = 0,ord-1 do
267
      exp[i] = a
268
      log[a] = i
269
      -- multiply with generator x+1 -> left shift + 1
270
      a = bxor(lshift(a, 1), a)
271
      -- if a gets larger than order, reduce modulo irreducible polynom
272
      if a > ord then
273
        a = sub(a, irrPolynom)
274
      end
275
    end
276
  end
277
278
  initMulTable()
279
280
  return {
281
    add = add,
282
    sub = sub,
283
    invert = invert,
284
    mul = mul,
285
    div = div,
286
    printLog = printLog,
287
    printExp = printExp,
288
  }
289
  end)
290
291
  util=_W(function(_ENV, ...)
292
  -- Cache some bit operators
293
  local bxor = bit.bxor
294
  local rshift = bit.rshift
295
  local band = bit.band
296
  local lshift = bit.lshift
297
  local sleepCheckIn
298
  --
299
  -- calculate the parity of one byte
300
  --
301
  local function byteParity(byte)
302
    byte = bxor(byte, rshift(byte, 4))
303
    byte = bxor(byte, rshift(byte, 2))
304
    byte = bxor(byte, rshift(byte, 1))
305
    return band(byte, 1)
306
  end
307
  --
308
  -- get byte at position index
309
  --
310
  local function getByte(number, index)
311
    return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
312
  end
313
  --
314
  -- put number into int at position index
315
  --
316
  local function putByte(number, index)
317
    return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
318
  end
319
  --
320
  -- convert byte array to int array
321
  --
322
  local function bytesToInts(bytes, start, n)
323
    local ints = {}
324
    for i = 0, n - 1 do
325
      ints[i + 1] =
326
          putByte(bytes[start + (i*4)], 3) +
327
          putByte(bytes[start + (i*4) + 1], 2) +
328
          putByte(bytes[start + (i*4) + 2], 1) +
329
          putByte(bytes[start + (i*4) + 3], 0)
330
      if n % 10000 == 0 then sleepCheckIn() end
331
    end
332
    return ints
333
  end
334
  --
335
  -- convert int array to byte array
336
  --
337
  local function intsToBytes(ints, output, outputOffset, n)
338
    n = n or #ints
339
    for i = 0, n - 1 do
340
      for j = 0,3 do
341
        output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
342
      end
343
      if n % 10000 == 0 then sleepCheckIn() end
344
    end
345
    return output
346
  end
347
  --
348
  -- convert bytes to hexString
349
  --
350
  local function bytesToHex(bytes)
351
    local hexBytes = ""
352
    for i,byte in ipairs(bytes) do
353
      hexBytes = hexBytes .. string.format("%02x ", byte)
354
    end
355
    return hexBytes
356
  end
357
358
  local function hexToBytes(bytes)
359
    local out = {}
360
    for i = 1, #bytes, 2 do
361
      out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
362
    end
363
    return out
364
  end
365
  --
366
  -- convert data to hex string
367
  --
368
  local function toHexString(data)
369
    local type = type(data)
370
    if (type == "number") then
371
      return string.format("%08x",data)
372
    elseif (type == "table") then
373
      return bytesToHex(data)
374
    elseif (type == "string") then
375
      local bytes = {string.byte(data, 1, #data)}
376
      return bytesToHex(bytes)
377
    else
378
      return data
379
    end
380
  end
381
382
  local function padByteString(data)
383
    local dataLength = #data
384
    local random1 = math.random(0,255)
385
    local random2 = math.random(0,255)
386
    local prefix = string.char(random1,
387
      random2,
388
      random1,
389
      random2,
390
      getByte(dataLength, 3),
391
      getByte(dataLength, 2),
392
      getByte(dataLength, 1),
393
      getByte(dataLength, 0)
394
    )
395
    data = prefix .. data
396
    local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
397
    for i=1,paddingLength do
398
      padding = padding .. string.char(math.random(0,255))
399
    end
400
    return data .. padding
401
  end
402
403
  local function properlyDecrypted(data)
404
    local random = {string.byte(data,1,4)}
405
    if (random[1] == random[3] and random[2] == random[4]) then
406
      return true
407
    end
408
    return false
409
  end
410
411
  local function unpadByteString(data)
412
    if (not properlyDecrypted(data)) then
413
      return nil
414
    end
415
    local dataLength = putByte(string.byte(data,5), 3)
416
             + putByte(string.byte(data,6), 2)
417
             + putByte(string.byte(data,7), 1)
418
             + putByte(string.byte(data,8), 0)
419
    return string.sub(data,9,8+dataLength)
420
  end
421
422
  local function xorIV(data, iv)
423
    for i = 1,16 do
424
      data[i] = bxor(data[i], iv[i])
425
    end
426
  end
427
428
  local function increment(data)
429
    local i = 16
430
    while true do
431
      local value = data[i] + 1
432
      if value >= 256 then
433
        data[i] = value - 256
434
        i = (i - 2) % 16 + 1
435
      else
436
        data[i] = value
437
        break
438
      end
439
    end
440
  end
441
442
  -- Called every encryption cycle
443
  local push, pull, time = os.queueEvent, coroutine.yield, os.time
444
  local oldTime = time()
445
  local function sleepCheckIn()
446
    local newTime = time()
447
    if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
448
      oldTime = newTime
449
      push("sleep")
450
      pull("sleep")
451
    end
452
  end
453
454
  local function getRandomData(bytes)
455
    local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
456
    local result = {}
457
    for i=1,bytes do
458
      insert(result, random(0,255))
459
      if i % 10240 == 0 then sleep() end
460
    end
461
    return result
462
  end
463
464
  local function getRandomString(bytes)
465
    local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
466
    local result = {}
467
    for i=1,bytes do
468
      insert(result, char(random(0,255)))
469
      if i % 10240 == 0 then sleep() end
470
    end
471
    return table.concat(result)
472
  end
473
474
  return {
475
    byteParity = byteParity,
476
    getByte = getByte,
477
    putByte = putByte,
478
    bytesToInts = bytesToInts,
479
    intsToBytes = intsToBytes,
480
    bytesToHex = bytesToHex,
481
    hexToBytes = hexToBytes,
482
    toHexString = toHexString,
483
    padByteString = padByteString,
484
    properlyDecrypted = properlyDecrypted,
485
    unpadByteString = unpadByteString,
486
    xorIV = xorIV,
487
    increment = increment,
488
    sleepCheckIn = sleepCheckIn,
489
    getRandomData = getRandomData,
490
    getRandomString = getRandomString,
491
  }
492
  end)
493
494
  aes=_W(function(_ENV, ...)
495
  -- Implementation of AES with nearly pure lua
496
  -- AES with lua is slow, really slow :-)
497
  local putByte = util.putByte
498
  local getByte = util.getByte
499
  -- some constants
500
  local ROUNDS = 'rounds'
501
  local KEY_TYPE = "type"
502
  local ENCRYPTION_KEY=1
503
  local DECRYPTION_KEY=2
504
  -- aes SBOX
505
  local SBox = {}
506
  local iSBox = {}
507
  -- aes tables
508
  local table0 = {}
509
  local table1 = {}
510
  local table2 = {}
511
  local table3 = {}
512
  local tableInv0 = {}
513
  local tableInv1 = {}
514
  local tableInv2 = {}
515
  local tableInv3 = {}
516
  -- round constants
517
  local rCon = {
518
    0x01000000,
519
    0x02000000,
520
    0x04000000,
521
    0x08000000,
522
    0x10000000,
523
    0x20000000,
524
    0x40000000,
525
    0x80000000,
526
    0x1b000000,
527
    0x36000000,
528
    0x6c000000,
529
    0xd8000000,
530
    0xab000000,
531
    0x4d000000,
532
    0x9a000000,
533
    0x2f000000,
534
  }
535
  --
536
  -- affine transformation for calculating the S-Box of AES
537
  --
538
  local function affinMap(byte)
539
    mask = 0xf8
540
    result = 0
541
    for i = 1,8 do
542
      result = bit.lshift(result,1)
543
      parity = util.byteParity(bit.band(byte,mask))
544
      result = result + parity
545
      -- simulate roll
546
      lastbit = bit.band(mask, 1)
547
      mask = bit.band(bit.rshift(mask, 1),0xff)
548
      mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f) 
549
    end
550
    return bit.bxor(result, 0x63)
551
  end
552
  --
553
  -- calculate S-Box and inverse S-Box of AES
554
  -- apply affine transformation to inverse in finite field 2^8
555
  --
556
  local function calcSBox()
557
    for i = 0, 255 do
558
      inverse = i ~= 0 and gf.invert(i) or i
559
      mapped = affinMap(inverse)
560
      SBox[i] = mapped
561
      iSBox[mapped] = i
562
    end
563
  end
564
  --
565
  -- Calculate round tables
566
  -- round tables are used to calculate shiftRow, MixColumn and SubBytes
567
  -- with 4 table lookups and 4 xor operations.
568
  --
569
  local function calcRoundTables()
570
    for x = 0,255 do
571
      byte = SBox[x]
572
      table0[x] = putByte(gf.mul(0x03, byte), 0)
573
                + putByte(             byte , 1)
574
                + putByte(             byte , 2)
575
                + putByte(gf.mul(0x02, byte), 3)
576
      table1[x] = putByte(             byte , 0)
577
                + putByte(             byte , 1)
578
                + putByte(gf.mul(0x02, byte), 2)
579
                + putByte(gf.mul(0x03, byte), 3)
580
      table2[x] = putByte(             byte , 0)
581
                + putByte(gf.mul(0x02, byte), 1)
582
                + putByte(gf.mul(0x03, byte), 2)
583
                + putByte(             byte , 3)
584
      table3[x] = putByte(gf.mul(0x02, byte), 0)
585
                + putByte(gf.mul(0x03, byte), 1)
586
                + putByte(             byte , 2)
587
                + putByte(             byte , 3)
588
    end
589
  end
590
  --
591
  -- Calculate inverse round tables
592
  -- does the inverse of the normal roundtables for the equivalent
593
  -- decryption algorithm.
594
  --
595
  local function calcInvRoundTables()
596
    for x = 0,255 do
597
      byte = iSBox[x]
598
      tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
599
                 + putByte(gf.mul(0x0d, byte), 1)
600
                 + putByte(gf.mul(0x09, byte), 2)
601
                 + putByte(gf.mul(0x0e, byte), 3)
602
      tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
603
                 + putByte(gf.mul(0x09, byte), 1)
604
                 + putByte(gf.mul(0x0e, byte), 2)
605
                 + putByte(gf.mul(0x0b, byte), 3)
606
      tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
607
                 + putByte(gf.mul(0x0e, byte), 1)
608
                 + putByte(gf.mul(0x0b, byte), 2)
609
                 + putByte(gf.mul(0x0d, byte), 3)
610
      tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
611
                 + putByte(gf.mul(0x0b, byte), 1)
612
                 + putByte(gf.mul(0x0d, byte), 2)
613
                 + putByte(gf.mul(0x09, byte), 3)
614
    end
615
  end
616
  --
617
  -- rotate word: 0xaabbccdd gets 0xbbccddaa
618
  -- used for key schedule
619
  --
620
  local function rotWord(word)
621
    local tmp = bit.band(word,0xff000000)
622
    return (bit.lshift(word,8) + bit.rshift(tmp,24))
623
  end
624
  --
625
  -- replace all bytes in a word with the SBox.
626
  -- used for key schedule
627
  --
628
  local function subWord(word)
629
    return putByte(SBox[getByte(word,0)],0)
630
      + putByte(SBox[getByte(word,1)],1)
631
      + putByte(SBox[getByte(word,2)],2)
632
      + putByte(SBox[getByte(word,3)],3)
633
  end
634
  --
635
  -- generate key schedule for aes encryption
636
  --
637
  -- returns table with all round keys and
638
  -- the necessary number of rounds saved in [ROUNDS]
639
  --
640
  local function expandEncryptionKey(key)
641
    local keySchedule = {}
642
    local keyWords = math.floor(#key / 4)
643
    if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
644
      error("Invalid key size: " .. tostring(keyWords))
645
      return nil
646
    end
647
    keySchedule[ROUNDS] = keyWords + 6
648
    keySchedule[KEY_TYPE] = ENCRYPTION_KEY
649
    for i = 0,keyWords - 1 do
650
      keySchedule[i] = putByte(key[i*4+1], 3)
651
               + putByte(key[i*4+2], 2)
652
               + putByte(key[i*4+3], 1)
653
               + putByte(key[i*4+4], 0)
654
    end
655
    for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
656
      local tmp = keySchedule[i-1]
657
      if ( i % keyWords == 0) then
658
        tmp = rotWord(tmp)
659
        tmp = subWord(tmp)
660
        local index = math.floor(i/keyWords)
661
        tmp = bit.bxor(tmp,rCon[index])
662
      elseif (keyWords > 6 and i % keyWords == 4) then
663
        tmp = subWord(tmp)
664
      end
665
      keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
666
    end
667
    return keySchedule
668
  end
669
  --
670
  -- Inverse mix column
671
  -- used for key schedule of decryption key
672
  --
673
  local function invMixColumnOld(word)
674
    local b0 = getByte(word,3)
675
    local b1 = getByte(word,2)
676
    local b2 = getByte(word,1)
677
    local b3 = getByte(word,0)
678
    return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
679
                         gf.mul(0x0d, b2)),
680
                         gf.mul(0x09, b3)),
681
                         gf.mul(0x0e, b0)),3)
682
       + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
683
                         gf.mul(0x0d, b3)),
684
                         gf.mul(0x09, b0)),
685
                         gf.mul(0x0e, b1)),2)
686
       + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
687
                         gf.mul(0x0d, b0)),
688
                         gf.mul(0x09, b1)),
689
                         gf.mul(0x0e, b2)),1)
690
       + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
691
                         gf.mul(0x0d, b1)),
692
                         gf.mul(0x09, b2)),
693
                         gf.mul(0x0e, b3)),0)
694
  end
695
  --
696
  -- Optimized inverse mix column
697
  -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
698
  -- TODO: make it work
699
  --
700
  local function invMixColumn(word)
701
    local b0 = getByte(word,3)
702
    local b1 = getByte(word,2)
703
    local b2 = getByte(word,1)
704
    local b3 = getByte(word,0)
705
    local t = bit.bxor(b3,b2)
706
    local u = bit.bxor(b1,b0)
707
    local v = bit.bxor(t,u)
708
    v = bit.bxor(v,gf.mul(0x08,v))
709
    w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
710
    v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
711
    return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
712
       + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t              )), 1)
713
       + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
714
       + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u              )), 3)
715
  end
716
  --
717
  -- generate key schedule for aes decryption
718
  --
719
  -- uses key schedule for aes encryption and transforms each
720
  -- key by inverse mix column.
721
  --
722
  local function expandDecryptionKey(key)
723
    local keySchedule = expandEncryptionKey(key)
724
    if (keySchedule == nil) then
725
      return nil
726
    end
727
    keySchedule[KEY_TYPE] = DECRYPTION_KEY
728
    for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
729
      keySchedule[i] = invMixColumnOld(keySchedule[i])
730
    end
731
    return keySchedule
732
  end
733
  --
734
  -- xor round key to state
735
  --
736
  local function addRoundKey(state, key, round)
737
    for i = 0, 3 do
738
      state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
739
    end
740
  end
741
  --
742
  -- do encryption round (ShiftRow, SubBytes, MixColumn together)
743
  --
744
  local function doRound(origState, dstState)
745
    dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
746
          table0[getByte(origState[1],3)],
747
          table1[getByte(origState[2],2)]),
748
          table2[getByte(origState[3],1)]),
749
          table3[getByte(origState[4],0)])
750
    dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
751
          table0[getByte(origState[2],3)],
752
          table1[getByte(origState[3],2)]),
753
          table2[getByte(origState[4],1)]),
754
          table3[getByte(origState[1],0)])
755
    dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
756
          table0[getByte(origState[3],3)],
757
          table1[getByte(origState[4],2)]),
758
          table2[getByte(origState[1],1)]),
759
          table3[getByte(origState[2],0)])
760
    dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
761
          table0[getByte(origState[4],3)],
762
          table1[getByte(origState[1],2)]),
763
          table2[getByte(origState[2],1)]),
764
          table3[getByte(origState[3],0)])
765
  end
766
  --
767
  -- do last encryption round (ShiftRow and SubBytes)
768
  --
769
  local function doLastRound(origState, dstState)
770
    dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
771
          + putByte(SBox[getByte(origState[2],2)], 2)
772
          + putByte(SBox[getByte(origState[3],1)], 1)
773
          + putByte(SBox[getByte(origState[4],0)], 0)
774
    dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
775
          + putByte(SBox[getByte(origState[3],2)], 2)
776
          + putByte(SBox[getByte(origState[4],1)], 1)
777
          + putByte(SBox[getByte(origState[1],0)], 0)
778
    dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
779
          + putByte(SBox[getByte(origState[4],2)], 2)
780
          + putByte(SBox[getByte(origState[1],1)], 1)
781
          + putByte(SBox[getByte(origState[2],0)], 0)
782
    dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
783
          + putByte(SBox[getByte(origState[1],2)], 2)
784
          + putByte(SBox[getByte(origState[2],1)], 1)
785
          + putByte(SBox[getByte(origState[3],0)], 0)
786
  end
787
  --
788
  -- do decryption round
789
  --
790
  local function doInvRound(origState, dstState)
791
    dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
792
          tableInv0[getByte(origState[1],3)],
793
          tableInv1[getByte(origState[4],2)]),
794
          tableInv2[getByte(origState[3],1)]),
795
          tableInv3[getByte(origState[2],0)])
796
    dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
797
          tableInv0[getByte(origState[2],3)],
798
          tableInv1[getByte(origState[1],2)]),
799
          tableInv2[getByte(origState[4],1)]),
800
          tableInv3[getByte(origState[3],0)])
801
    dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
802
          tableInv0[getByte(origState[3],3)],
803
          tableInv1[getByte(origState[2],2)]),
804
          tableInv2[getByte(origState[1],1)]),
805
          tableInv3[getByte(origState[4],0)])
806
    dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
807
          tableInv0[getByte(origState[4],3)],
808
          tableInv1[getByte(origState[3],2)]),
809
          tableInv2[getByte(origState[2],1)]),
810
          tableInv3[getByte(origState[1],0)])
811
  end
812
  --
813
  -- do last decryption round
814
  --
815
  local function doInvLastRound(origState, dstState)
816
    dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
817
          + putByte(iSBox[getByte(origState[4],2)], 2)
818
          + putByte(iSBox[getByte(origState[3],1)], 1)
819
          + putByte(iSBox[getByte(origState[2],0)], 0)
820
    dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
821
          + putByte(iSBox[getByte(origState[1],2)], 2)
822
          + putByte(iSBox[getByte(origState[4],1)], 1)
823
          + putByte(iSBox[getByte(origState[3],0)], 0)
824
    dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
825
          + putByte(iSBox[getByte(origState[2],2)], 2)
826
          + putByte(iSBox[getByte(origState[1],1)], 1)
827
          + putByte(iSBox[getByte(origState[4],0)], 0)
828
    dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
829
          + putByte(iSBox[getByte(origState[3],2)], 2)
830
          + putByte(iSBox[getByte(origState[2],1)], 1)
831
          + putByte(iSBox[getByte(origState[1],0)], 0)
832
  end
833
  --
834
  -- encrypts 16 Bytes
835
  -- key           encryption key schedule
836
  -- input         array with input data
837
  -- inputOffset   start index for input
838
  -- output        array for encrypted data
839
  -- outputOffset  start index for output
840
  --
841
  local function encrypt(key, input, inputOffset, output, outputOffset)
842
    --default parameters
843
    inputOffset = inputOffset or 1
844
    output = output or {}
845
    outputOffset = outputOffset or 1
846
    local state = {}
847
    local tmpState = {}
848
    if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
849
      error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
850
      return
851
    end
852
    state = util.bytesToInts(input, inputOffset, 4)
853
    addRoundKey(state, key, 0)
854
    local round = 1
855
    while (round < key[ROUNDS] - 1) do
856
      -- do a double round to save temporary assignments
857
      doRound(state, tmpState)
858
      addRoundKey(tmpState, key, round)
859
      round = round + 1
860
      doRound(tmpState, state)
861
      addRoundKey(state, key, round)
862
      round = round + 1
863
    end
864
    doRound(state, tmpState)
865
    addRoundKey(tmpState, key, round)
866
    round = round +1
867
    doLastRound(tmpState, state)
868
    addRoundKey(state, key, round)
869
    util.sleepCheckIn()
870
    return util.intsToBytes(state, output, outputOffset)
871
  end
872
  --
873
  -- decrypt 16 bytes
874
  -- key           decryption key schedule
875
  -- input         array with input data
876
  -- inputOffset   start index for input
877
  -- output        array for decrypted data
878
  -- outputOffset  start index for output
879
  ---
880
  local function decrypt(key, input, inputOffset, output, outputOffset)
881
    -- default arguments
882
    inputOffset = inputOffset or 1
883
    output = output or {}
884
    outputOffset = outputOffset or 1
885
    local state = {}
886
    local tmpState = {}
887
    if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
888
      error("No decryption key: " .. tostring(key[KEY_TYPE]))
889
      return
890
    end
891
    state = util.bytesToInts(input, inputOffset, 4)
892
    addRoundKey(state, key, key[ROUNDS])
893
    local round = key[ROUNDS] - 1
894
    while (round > 2) do
895
      -- do a double round to save temporary assignments
896
      doInvRound(state, tmpState)
897
      addRoundKey(tmpState, key, round)
898
      round = round - 1
899
      doInvRound(tmpState, state)
900
      addRoundKey(state, key, round)
901
      round = round - 1
902
    end
903
    doInvRound(state, tmpState)
904
    addRoundKey(tmpState, key, round)
905
    round = round - 1
906
    doInvLastRound(tmpState, state)
907
    addRoundKey(state, key, round)
908
    util.sleepCheckIn()
909
    return util.intsToBytes(state, output, outputOffset)
910
  end
911
912
  -- calculate all tables when loading this file
913
  calcSBox()
914
  calcRoundTables()
915
  calcInvRoundTables()
916
917
  return {
918
    ROUNDS = ROUNDS,
919
    KEY_TYPE = KEY_TYPE,
920
    ENCRYPTION_KEY = ENCRYPTION_KEY,
921
    DECRYPTION_KEY = DECRYPTION_KEY,
922
    expandEncryptionKey = expandEncryptionKey,
923
    expandDecryptionKey = expandDecryptionKey,
924
    encrypt = encrypt,
925
    decrypt = decrypt,
926
  }
927
  end)
928
929
  local buffer=_W(function(_ENV, ...)
930
  local function new ()
931
    return {}
932
  end
933
934
  local function addString (stack, s)
935
    table.insert(stack, s)
936
  end
937
938
  local function toString (stack)
939
    return table.concat(stack)
940
  end
941
942
  return {
943
    new = new,
944
    addString = addString,
945
    toString = toString,
946
  }
947
  end)
948
949
  ciphermode=_W(function(_ENV, ...)
950
  local public = {}
951
  --
952
  -- Encrypt strings
953
  -- key - byte array with key
954
  -- string - string to encrypt
955
  -- modefunction - function for cipher mode to use
956
  --
957
  local random, unpack = math.random, unpack or table.unpack
958
  function public.encryptString(key, data, modeFunction, iv)
959
    if iv then
960
      local ivCopy = {}
961
      for i = 1, 16 do ivCopy[i] = iv[i] end
962
      iv = ivCopy
963
    else
964
      iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
965
    end
966
    local keySched = aes.expandEncryptionKey(key)
967
    local encryptedData = buffer.new()
968
    for i = 1, #data/16 do
969
      local offset = (i-1)*16 + 1
970
      local byteData = {string.byte(data,offset,offset +15)}
971
      iv = modeFunction(keySched, byteData, iv)
972
      buffer.addString(encryptedData, string.char(unpack(byteData)))
973
    end
974
    return buffer.toString(encryptedData)
975
  end
976
  --
977
  -- the following 4 functions can be used as
978
  -- modefunction for encryptString
979
  --
980
  -- Electronic code book mode encrypt function
981
  function public.encryptECB(keySched, byteData, iv)
982
    aes.encrypt(keySched, byteData, 1, byteData, 1)
983
  end
984
985
  -- Cipher block chaining mode encrypt function
986
  function public.encryptCBC(keySched, byteData, iv)
987
    util.xorIV(byteData, iv)
988
    aes.encrypt(keySched, byteData, 1, byteData, 1)
989
    return byteData
990
  end
991
992
  -- Output feedback mode encrypt function
993
  function public.encryptOFB(keySched, byteData, iv)
994
    aes.encrypt(keySched, iv, 1, iv, 1)
995
    util.xorIV(byteData, iv)
996
    return iv
997
  end
998
999
  -- Cipher feedback mode encrypt function
1000
  function public.encryptCFB(keySched, byteData, iv)
1001
    aes.encrypt(keySched, iv, 1, iv, 1)
1002
    util.xorIV(byteData, iv)
1003
    return byteData
1004
  end
1005
1006
  function public.encryptCTR(keySched, byteData, iv)
1007
    local nextIV = {}
1008
    for j = 1, 16 do nextIV[j] = iv[j] end
1009
    aes.encrypt(keySched, iv, 1, iv, 1)
1010
    util.xorIV(byteData, iv)
1011
    util.increment(nextIV)
1012
    return nextIV
1013
  end
1014
  --
1015
  -- Decrypt strings
1016
  -- key - byte array with key
1017
  -- string - string to decrypt
1018
  -- modefunction - function for cipher mode to use
1019
  --
1020
  function public.decryptString(key, data, modeFunction, iv)
1021
    if iv then
1022
      local ivCopy = {}
1023
      for i = 1, 16 do ivCopy[i] = iv[i] end
1024
      iv = ivCopy
1025
    else
1026
      iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
1027
    end
1028
    local keySched
1029
    if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
1030
      keySched = aes.expandEncryptionKey(key)
1031
    else
1032
      keySched = aes.expandDecryptionKey(key)
1033
    end
1034
    local decryptedData = buffer.new()
1035
    for i = 1, #data/16 do
1036
      local offset = (i-1)*16 + 1
1037
      local byteData = {string.byte(data,offset,offset +15)}
1038
      iv = modeFunction(keySched, byteData, iv)
1039
      buffer.addString(decryptedData, string.char(unpack(byteData)))
1040
    end
1041
    return buffer.toString(decryptedData)
1042
  end
1043
  --
1044
  -- the following 4 functions can be used as
1045
  -- modefunction for decryptString
1046
  --
1047
  -- Electronic code book mode decrypt function
1048
  function public.decryptECB(keySched, byteData, iv)
1049
    aes.decrypt(keySched, byteData, 1, byteData, 1)
1050
    return iv
1051
  end
1052
1053
  -- Cipher block chaining mode decrypt function
1054
  function public.decryptCBC(keySched, byteData, iv)
1055
    local nextIV = {}
1056
    for j = 1, 16 do nextIV[j] = byteData[j] end
1057
    aes.decrypt(keySched, byteData, 1, byteData, 1)
1058
    util.xorIV(byteData, iv)
1059
    return nextIV
1060
  end
1061
1062
  -- Output feedback mode decrypt function
1063
  function public.decryptOFB(keySched, byteData, iv)
1064
    aes.encrypt(keySched, iv, 1, iv, 1)
1065
    util.xorIV(byteData, iv)
1066
    return iv
1067
  end
1068
1069
  -- Cipher feedback mode decrypt function
1070
  function public.decryptCFB(keySched, byteData, iv)
1071
    local nextIV = {}
1072
    for j = 1, 16 do nextIV[j] = byteData[j] end
1073
    aes.encrypt(keySched, iv, 1, iv, 1)
1074
    util.xorIV(byteData, iv)
1075
    return nextIV
1076
  end
1077
1078
  public.decryptCTR = public.encryptCTR
1079
  return public
1080
  end)
1081
1082
  -- Simple API for encrypting strings.
1083
  --
1084
  AES128 = 16
1085
  AES192 = 24
1086
  AES256 = 32
1087
  ECBMODE = 1
1088
  CBCMODE = 2
1089
  OFBMODE = 3
1090
  CFBMODE = 4
1091
  CTRMODE = 4
1092
1093
  local function pwToKey(password, keyLength, iv)
1094
    local padLength = keyLength
1095
    if (keyLength == AES192) then
1096
      padLength = 32
1097
    end
1098
    if (padLength > #password) then
1099
      local postfix = ""
1100
      for i = 1,padLength - #password do
1101
        postfix = postfix .. string.char(0)
1102
      end
1103
      password = password .. postfix
1104
    else
1105
      password = string.sub(password, 1, padLength)
1106
    end
1107
    local pwBytes = {string.byte(password,1,#password)}
1108
    password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
1109
    password = string.sub(password, 1, keyLength)
1110
    return {string.byte(password,1,#password)}
1111
  end
1112
  --
1113
  -- Encrypts string data with password password.
1114
  -- password  - the encryption key is generated from this string
1115
  -- data      - string to encrypt (must not be too large)
1116
  -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1117
  -- mode      - mode of encryption: ecb, cbc(default), ofb, cfb
1118
  --
1119
  -- mode and keyLength must be the same for encryption and decryption.
1120
  --
1121
  function encrypt(password, data, keyLength, mode, iv)
1122
    assert(password ~= nil, "Empty password.")
1123
    assert(data ~= nil, "Empty data.")
1124
    local mode = mode or CBCMODE
1125
    local keyLength = keyLength or AES128
1126
    local key = pwToKey(password, keyLength, iv)
1127
    local paddedData = util.padByteString(data)
1128
    if mode == ECBMODE then
1129
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
1130
    elseif mode == CBCMODE then
1131
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
1132
    elseif mode == OFBMODE then
1133
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
1134
    elseif mode == CFBMODE then
1135
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
1136
    elseif mode == CTRMODE then
1137
      return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
1138
    else
1139
      error("Unknown mode", 2)
1140
    end
1141
  end
1142
  --
1143
  -- Decrypts string data with password password.
1144
  -- password  - the decryption key is generated from this string
1145
  -- data      - string to encrypt
1146
  -- keyLength - length of aes key: 128(default), 192 or 256 Bit
1147
  -- mode      - mode of decryption: ecb, cbc(default), ofb, cfb
1148
  --
1149
  -- mode and keyLength must be the same for encryption and decryption.
1150
  --
1151
  function decrypt(password, data, keyLength, mode, iv)
1152
    local mode = mode or CBCMODE
1153
    local keyLength = keyLength or AES128
1154
    local key = pwToKey(password, keyLength, iv)
1155
    local plain
1156
    if mode == ECBMODE then
1157
      plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
1158
    elseif mode == CBCMODE then
1159
      plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
1160
    elseif mode == OFBMODE then
1161
      plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
1162
    elseif mode == CFBMODE then
1163
      plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
1164
    elseif mode == CTRMODE then
1165
      plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
1166
    else
1167
      error("Unknown mode", 2)
1168
    end
1169
    result = util.unpadByteString(plain)
1170
    if (result == nil) then
1171
      return nil
1172
    end
1173
    return result
1174
  end
1175
end
1176
1177
local function saveData()
1178
  local srvConfig = fs.open(config, "w") or error("saveData(): Cannot open " .. config .. " for writing", 0)
1179
  srvConfig.write(textutils.serialize(ccSettings))
1180
  srvConfig.close()
1181
end
1182
1183
local function clearMon(bgColor)
1184
  mon.setBackgroundColor(bgColor or mblack)
1185
  mon.clear()
1186
end
1187
1188
local function clearMonButtons()
1189
  local line = string.rep(" ", monX - 1)
1190
  mon.setBackgroundColor(mblack)
1191
  for i = 3, 8 do
1192
    mon.setCursorPos(2, i)
1193
    mon.write(line)
1194
  end
1195
end
1196
1197
do
1198
  local function splitName(name)
1199
    if #name > 6 then
1200
      if string.find(name, " ") then
1201
        local firstPart, lastPart = name:sub(1, string.find(name, " ") - 1), name:sub(string.find(name, " ") + 1)
1202
        if #firstPart <= 6 and #lastPart <= 6 then
1203
          return firstPart, lastPart
1204
        end
1205
      end
1206
      return name:sub(1, 6), name:sub(7)
1207
    else
1208
      return name, ""
1209
    end
1210
  end
1211
1212
  local function powerSwitch(x, y, state)
1213
    mon.setCursorPos(x, y)
1214
    mon.setBackgroundColor((state == "OPEN" or state == "OFF") and mgray or mgreen)
1215
    mon.write("      ") --# Switch Top (OFF/ON/OPEN/CLOSED)
1216
    mon.setCursorPos(x, y + 1)
1217
    mon.setBackgroundColor((state == "OPEN" or state == "OFF") and morange or mgray)
1218
    mon.write("      ") --# Switch Bottom (OFF/ON/OPEN/CLOSED)
1219
  end
1220
1221
  monControls = function()
1222
    if #allClients == 0 then
1223
      mon.setTextColor(mred)
1224
      mon.setCursorPos(4, 4)
1225
      mon.write("No clients are")
1226
      mon.setCursorPos(2, 5)
1227
      mon.write("currently connected")
1228
    else
1229
      local firstName, lastName, xPos, devState, lockState
1230
      for i = 1, #allClients do
1231
        firstName, lastName = splitName(allClients[i].name)
1232
        xPos, devState, lockState = (i * 8) - 6, allClients[i].deviceState, allClients[i].lockState
1233
        mon.setBackgroundColor(mblack)
1234
        mon.setTextColor(quietClients[i] and myellow or msky)
1235
        mon.setCursorPos(xPos, 3)
1236
        mon.write(firstName .. string.rep(" ", 6 - #firstName))
1237
        mon.setCursorPos(xPos, 4)
1238
        mon.write(lastName .. string.rep(" ", 6 - #lastName))
1239
        mon.setCursorPos(xPos, 8)
1240
        mon.setTextColor(lockState and mred or (validStates[devState][1] or myellow))
1241
        mon.write(lockState and "LOCKED" or (devState .. string.rep(" ", 6 - #devState)))
1242
        powerSwitch((i * 8) - 6, 5, devState)
1243
      end
1244
    end
1245
  end
1246
end
1247
1248
local function clearTerm()
1249
  term.setBackgroundColor(black)
1250
  term.setTextColor(white)
1251
  term.clear()
1252
  term.setCursorPos(1, 1)
1253
end
1254
1255
local function clearDataArea()
1256
  term.setBackgroundColor(black)
1257
  local line = string.rep(" ", 25)
1258
  for i = 5, termY do
1259
    term.setCursorPos(26, i)
1260
    term.write(line)
1261
  end
1262
end
1263
1264
do
1265
  local function sortDeviceList(newDeviceData)
1266
    local clientCount = #allClients
1267
    if clientCount == 0 then                           --# the list is empty
1268
      allClients[1] = { }                              --# add it
1269
      quietClients[1] = false
1270
      for k, v in pairs(newDeviceData) do
1271
        allClients[1][k] = v
1272
      end
1273
      thisCommand = allClients[1].lockState and "LOCKED" or allClients[1].deviceState
1274
      clearMonButtons()
1275
    else
1276
      for i = 1, clientCount do
1277
        if client == allClients[i].cc then             --# client is already on the list
1278
          thisCommand = "WiReQRY"
1279
          if newDeviceData.deviceState ~= allClients[i].deviceState then
1280
            thisCommand = newDeviceData.deviceState
1281
            allClients[i].deviceState = thisCommand
1282
            ccUpdate = true
1283
          end
1284
          if newDeviceData.lockState ~= allClients[i].lockState then
1285
            thisCommand = newDeviceData.lockState and "LOCKED" or "UNLOCK"
1286
            allClients[i].lockState = newDeviceData.lockState
1287
            ccUpdate = true
1288
          end
1289
          if quietClients[i] then ccUpdate = true end
1290
          allClients[i].quietCount = 0
1291
          quietClients[i] = false
1292
          return
1293
        end
1294
      end
1295
      if clientCount < 20 then
1296
        for i = 1, clientCount do
1297
          if newDeviceData.name < allClients[i].name then --# alphabetize
1298
            table.insert(allClients, i, newDeviceData) --# insert
1299
            table.insert(quietClients, i, false)
1300
            break
1301
          end
1302
          if i == clientCount then                     --# we've reached the end of the list
1303
            clientCount = clientCount + 1
1304
            allClients[clientCount] = { }              --# tack it on
1305
            quietClients[clientCount] = false
1306
            for k, v in pairs(newDeviceData) do
1307
              allClients[clientCount][k] = v
1308
            end
1309
            break
1310
          end
1311
        end
1312
        thisCommand = allClients[clientCount].lockState and "LOCKED" or allClients[clientCount].deviceState
1313
      end
1314
    end
1315
    ccUpdate = true
1316
    numPages = math.max(1, math.ceil(#allClients / 7))
1317
    pageNum = math.min(math.max(1, pageNum), numPages)
1318
  end
1319
1320
  netReceive = function()
1321
    local id, newCmdData, success, encryptedMessage, decodedMessage, encKey
1322
    while true do
1323
      for i = 1, modemCount do
1324
        if not rednet.isOpen(modemSides[i]) then rednet.open(modemSides[i]) end
1325
      end
1326
      id, encryptedMessage = rednet.receive(network)
1327
      newCmdData, ccSuccess = { }, false
1328
      if type(encryptedMessage) == "string" then
1329
        success, decodedMessage = pcall(decode, encryptedMessage)
1330
        if success and type(decodedMessage) == "string" then
1331
          encKey = thisCC .. "WiRe!Comms" .. tostring(id)
1332
          success, decryptedMessage = pcall(decrypt, encKey, decodedMessage)
1333
          if success and type(decryptedMessage) == "string" then
1334
            success, newCmdData = pcall(textutils.unserialize, decryptedMessage)
1335
            if success and type(newCmdData) == "table" and newCmdData.program and newCmdData.program == "WiRe" and newCmdData.cc == id and newCmdData.color == ccSettings.color then
1336
              client = id
1337
              if newCmdData.deviceState == "OFFLINE" then
1338
                for i = 1, #allClients do
1339
                  if allClients[i].cc == client then
1340
                    table.remove(allClients, i)
1341
                    table.remove(quietClients, i)
1342
                    clearDataArea()
1343
                    clearMonButtons()
1344
                    thisCommand = newCmdData.deviceState
1345
                    ccUpdate = true
1346
                    break
1347
                  end
1348
                end
1349
              else
1350
                sortDeviceList(newCmdData)
1351
              end
1352
              ccSuccess = true
1353
            end
1354
          end
1355
        end
1356
      end
1357
      if not ccSuccess then
1358
        client = nil
1359
        thisCommand = "Noise"
1360
      end
1361
      updateScreens()
1362
    end
1363
  end
1364
end
1365
1366
local function netSend(toAll)
1367
  local dataPack = textutils.serialize({ program = "WiRe", cc = tonumber(thisCC), color = ccSettings.color, cmd = thisCommand })
1368
  for i = 1, modemCount do
1369
    if not rednet.isOpen(modemSides[i]) then rednet.open(modemSides[i]) end
1370
  end
1371
  if toAll then
1372
    local encKey = thisCC .. "WiRe!Comms" .. thisCC
1373
    local encryptedPackage = encode(encrypt(encKey, dataPack))
1374
    rednet.broadcast(encryptedPackage, network)
1375
    return true
1376
  elseif client then
1377
    local encKey = tostring(client) .. "WiRe!Comms" .. thisCC
1378
    local encryptedPackage = encode(encrypt(encKey, dataPack))
1379
    rednet.send(client, encryptedPackage, network)
1380
    return true
1381
  end
1382
  return false
1383
end
1384
1385
local function termScreen()
1386
  term.setCursorPos(14, 12)
1387
  term.setTextColor(white)
1388
  local lastClient = client or "Server"
1389
  term.write(tostring(lastClient) .. "     ")
1390
  term.setCursorPos(14, 13)
1391
  term.setTextColor(thisCommand == "LOCKED" and red or (validStates[thisCommand][2] or yellow))
1392
  term.write(thisCommand .. string.rep(" ", 9 - #thisCommand))
1393
  term.setCursorPos(14, 14)
1394
  term.setTextColor(ccSuccess and green or red)
1395
  term.write(tostring(ccSuccess) .. " ")
1396
  term.setCursorPos(26, 3) --# Client list
1397
  term.setTextColor(silver)
1398
  term.write("Clients (page " .. tostring(pageNum) .. " of " .. tostring(numPages) .. ")    ")
1399
  local yPos, spacerA, spacerB, spacerC, lockState, devState = 0, string.rep(" ", math.floor(termX / 2)), string.rep(" ", 14), string.rep(" ", 10)
1400
  for i = (pageNum * 7) - 6, math.min(pageNum * 7, #allClients) do
1401
    yPos, lockState, devState = yPos + 1, allClients[i].lockState, allClients[i].deviceState
1402
    term.setCursorPos(26, (yPos * 2) + 3)
1403
    term.setTextColor(quietClients[i] and yellow or sky)
1404
    term.write(allClients[i].name .. " (" .. tostring(allClients[i].cc) .. ")" .. spacerA)
1405
    term.setTextColor(lockState and red or (validStates[devState][2] or yellow))
1406
    term.setCursorPos(termX - 8, (yPos * 2) + 3)
1407
    term.write(lockState and "  LOCKED" or (string.rep(" ", 8 - #devState) .. devState))
1408
    term.setCursorPos(27, (yPos * 2) + 4)
1409
    term.setTextColor(silver)
1410
    if allClients[i].loc.x == "No GPS Fix" then
1411
      term.write(allClients[i].loc.x .. spacerB)
1412
    else
1413
      term.write("GPS: " .. tostring(allClients[i].loc.x) .. "/" .. tostring(allClients[i].loc.y) .. "/" .. tostring(allClients[i].loc.z) .. spacerC)
1414
    end
1415
  end
1416
end
1417
1418
do
1419
  local labels = {
1420
    [1] = { "Name:", 1, 3 };
1421
    [2] = { "Note:", 1, 4 };
1422
    [3] = { "Group:", 1, 6 };
1423
    [4] = { "cc#", 1, 8 };
1424
    [5] = { "Modems:", 1, 10 };
1425
    [6] = { "Last Client:", 1, 12 };
1426
    [7] = { "Last State:", 1, 13 };
1427
    [8] = { "Success:", 1, 14 };
1428
    [9] = { "Location: x:", 1, 16 };
1429
    [10] = { "y:", 11, 17 };
1430
    [11] = { "z:", 11, 18 };
1431
  }
1432
1433
  termScreenStatic = function()
1434
    term.setBackgroundColor(black)
1435
    term.clear()
1436
    local hText = "WiRe Server " .. WiReSver
1437
    local spacer = (termX - #hText) / 2
1438
    term.setBackgroundColor(blue)
1439
    term.setTextColor(white)
1440
    term.setCursorPos(1, 1)
1441
    term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1442
    term.setBackgroundColor(black)
1443
    term.setTextColor(silver)
1444
    for i = 1, 11 do
1445
      term.setCursorPos(labels[i][2], labels[i][3])
1446
      term.write(labels[i][1])
1447
    end
1448
    term.setTextColor(white)
1449
    term.setCursorPos(7, 3)
1450
    term.write(ccSettings.name)
1451
    term.setCursorPos(7, 4)
1452
    term.write(ccSettings.note)
1453
    term.setCursorPos(5, 8)
1454
    term.write(thisCC)
1455
    term.setCursorPos(9, 10)
1456
    term.write(tostring(modemCount))
1457
    term.setCursorPos(14, 16)
1458
    term.write(tostring(loc.x))
1459
    term.setCursorPos(14, 17)
1460
    term.write(tostring(loc.y))
1461
    term.setCursorPos(14, 18)
1462
    term.write(tostring(loc.z))
1463
    term.setTextColor(colorBurst[ccSettings.color][1] or silver)
1464
    term.setCursorPos(8, 6)
1465
    term.write(ccSettings.color)
1466
  end
1467
end
1468
1469
local function helpScreen()
1470
  local hText = "WiRe Server Help"
1471
  local xPos, spacer = math.floor(termX / 2), (termX - #hText) / 2
1472
  term.setBackgroundColor(white)
1473
  term.clear()
1474
  term.setBackgroundColor(blue)
1475
  term.setTextColor(white)
1476
  term.setCursorPos(1, 1)
1477
  term.write(string.rep(" ", math.floor(spacer)) .. hText .. string.rep(" ", math.ceil(spacer)))
1478
  term.setCursorPos(1, 2)
1479
  term.setBackgroundColor(gray)
1480
  term.write(string.rep(" ", termX))
1481
  term.setCursorPos(xPos - 8, 2)
1482
  term.write("-- Key Commands --")
1483
  term.setCursorPos(1, 7)
1484
  term.write(string.rep(" ", termX))
1485
  term.setCursorPos(xPos - 8, 7)
1486
  term.write("-- Client list --")
1487
  term.setCursorPos(1, 13)
1488
  term.write(string.rep(" ", termX))
1489
  term.setCursorPos(xPos - 10, 13)
1490
  term.write("-- Monitor Controls --")
1491
  term.setBackgroundColor(white)
1492
  term.setTextColor(black)
1493
  term.setCursorPos(xPos - 8, 3)
1494
  term.write("'q' to quit server")
1495
  term.setCursorPos(xPos - 12, 5)
1496
  term.write("'F1' to display/exit help")
1497
  term.setCursorPos(xPos - 18, 9)
1498
  term.write(", < [ { PGUP    Go back one page")
1499
  term.setCursorPos(xPos - 18, 11)
1500
  term.write(". > ] } PGDN    Go forward one page")
1501
  term.setCursorPos(xPos - 18, 15)
1502
  term.write("Touch a switch to toggle its state")
1503
  term.setCursorPos(xPos - 18, 17)
1504
  term.write("Touch a switch's status line to lock")
1505
  term.setCursorPos(xPos - 18, 19)
1506
  term.write("Touch a switch's status line to unlock")
1507
end
1508
1509
updateScreens = function()
1510
  if not tArgs[1] and not help then termScreen() end
1511
  if #allClients == 0 or (thisCommand ~= "Noise" and ccUpdate) then
1512
    monControls()
1513
    ccUpdate = false
1514
  end
1515
end
1516
1517
local function shutDown()
1518
  kernelState = false
1519
  rednet.unhost(network, ccSettings.color)
1520
  for i = 1, modemCount do
1521
    if rednet.isOpen(modemSides[i]) then rednet.close(modemSides[i]) end
1522
  end
1523
  clearMon()
1524
end
1525
1526
local function monTouch()
1527
  local halfMon, _, touchSide, posX, posY, continue = monX / 2
1528
  while true do
1529
    _, touchSide, posX, posY = os.pullEvent("monitor_touch")
1530
    if touchSide == monSide then
1531
      continue = true
1532
      if posY == monY and (posX < math.floor(halfMon) or posX > math.ceil(halfMon)) then
1533
        thisCommand = posX < math.floor(halfMon) and "LOCKED" or "UNLOCK"
1534
        client = "ALL"
1535
        ccSuccess = netSend(true)
1536
        ccUpdate = true
1537
        continue = false
1538
      end
1539
      if continue then
1540
        for i = 1, #allClients do
1541
          if posX >= (i * 8) - 6 and posX <= (i * 8) - 1 then
1542
            client = allClients[i].cc
1543
            if posY == 8 then
1544
              allClients[i].lockState = not allClients[i].lockState
1545
              thisCommand = allClients[i].lockState and "LOCKED" or "UNLOCK"
1546
              ccSuccess = netSend()
1547
              ccUpdate = true
1548
              break
1549
            elseif posY > 4 and posY < 7 then
1550
              if not allClients[i].lockState then
1551
                thisCommand = validStates[allClients[i].deviceState][3]
1552
                allClients[i].deviceState = thisCommand
1553
                ccSuccess = netSend()
1554
                ccUpdate = true
1555
              end
1556
              break
1557
            end
1558
          end
1559
        end
1560
      end
1561
      if ccUpdate then updateScreens() end
1562
    end
1563
  end
1564
end
1565
1566
local function userInput()
1567
  local event, data
1568
  while true do
1569
    event, data = os.pullEvent()
1570
    if event == "key" then
1571
      if (data == keys.pageUp or data == keys.pageDown) and not help then
1572
        pageNum = data == keys.pageUp and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
1573
        if pageNum == numPages and numPages > 1 then clearDataArea() end
1574
        termScreen()
1575
      elseif (data == keys.home or data == keys["end"]) and not help then
1576
        pageNum = data == keys.home and 1 or numPages
1577
        if pageNum == numPages and numPages > 1 then clearDataArea() end
1578
        termScreen()
1579
      elseif data == keys.f1 then
1580
        help = not help
1581
        if help then
1582
          helpScreen()
1583
        else
1584
          termScreenStatic()
1585
          termScreen()
1586
        end
1587
      end
1588
    elseif event == "char" then
1589
      if (data == "," or data == "<" or data == "[" or data == "{" or data == "." or data == ">" or data == "]" or data == "}") and not help then
1590
        pageNum = (data == "," or data == "<" or data == "[" or data == "{") and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
1591
        if pageNum == numPages and numPages > 1 then clearDataArea() end
1592
        termScreen()
1593
      elseif string.lower(data) == "q" then
1594
        shutDown()
1595
        clearTerm()
1596
        term.write("WiRe Server is OFFLINE")
1597
        term.setCursorPos(1, 3)
1598
        return
1599
      end
1600
    end
1601
  end
1602
end
1603
1604
local function foregroundShell()
1605
  clearTerm()
1606
  if fs.exists(tArgs[1]) then
1607
    shell.run(table.unpack(tArgs))
1608
    clearTerm()
1609
  else
1610
    term.write(tArgs[1] .. " missing")
1611
    term.setCursorPos(1, 3)
1612
  end
1613
  shutDown()
1614
  term.write("WiRe Server is OFFLINE")
1615
  term.setCursorPos(1, 5)
1616
end
1617
1618
local function dataPoller()
1619
  local _, timer, clientSilent
1620
  while true do
1621
    _, timer = os.pullEvent("timer")
1622
    if timer == pollTimer and kernelState then
1623
      clientSilent = false
1624
      for i = 1, #allClients do
1625
        if allClients[i].quietCount > 2 then
1626
          if not quietClients[i] then
1627
            quietClients[i] = true
1628
            clientSilent = true
1629
          end
1630
        else
1631
          allClients[i].quietCount = allClients[i].quietCount + 1
1632
        end
1633
      end
1634
      if clientSilent then monControls() end
1635
      thisCommand = "WiReQRY"
1636
      client = nil
1637
      ccSuccess = true
1638
      pollTimer = os.startTimer(7)
1639
      if not help and not tArgs[1] then termScreen() end
1640
    end
1641
  end
1642
end
1643
1644
local function initError(missing, device)
1645
  if modemCount > 0 then
1646
    for i = 1, modemCount do
1647
      if rednet.isOpen(modemSides[i]) then
1648
        rednet.close(modemSides[i])
1649
      end
1650
    end
1651
  end
1652
  term.clear()
1653
  term.setTextColor(red)
1654
  term.setCursorPos(1, 2)
1655
  print("No " .. missing .. " detected!")
1656
  print("WiRe Server REQUIRES")
1657
  print(device .. ".")
1658
  term.setCursorPos(1, 6)
1659
end
1660
1661
local function firstRun()
1662
  term.clear()
1663
  local gotModem = false
1664
  for _, side in pairs(rs.getSides()) do
1665
    if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
1666
      gotModem = true
1667
      break
1668
    end
1669
  end
1670
  if not gotModem then return initError("modem", "a modem") end
1671
  --# Set server name
1672
  term.setCursorPos(2, 2)
1673
  term.write("Please name this server")
1674
  repeat
1675
    term.setCursorPos(2, 4)
1676
    local newName = read()
1677
    ccSettings.name = newName
1678
  until newName ~= ""
1679
  --# Set computer label
1680
  if not os.getComputerLabel() then os.setComputerLabel(ccSettings.name) end
1681
  --# Set server description
1682
  term.clear()
1683
  term.setCursorPos(2, 2)
1684
  term.write("Please type in a short")
1685
  term.setCursorPos(2, 3)
1686
  term.write("server description")
1687
  repeat
1688
    term.setCursorPos(2, 5)
1689
    local newDesc = read()
1690
    ccSettings.note = newDesc
1691
  until newDesc ~= ""
1692
  --# Select color
1693
  local colorWheel = {
1694
    P = "Purple";
1695
    M = "Magenta";
1696
    B = "Blue";
1697
    S = "Sky";
1698
    C = "Cyan";
1699
    E = "Green";
1700
    L = "Lime";
1701
    R = "Red";
1702
    O = "Orange";
1703
    Y = "Yellow";
1704
    N = "Brown";
1705
    K = "Black";
1706
    G = "Gray";
1707
    I = "Silver";
1708
    W = "White";
1709
  }
1710
  term.clear()
1711
  term.setCursorPos(2, 1)
1712
  term.write("Please select the network group")
1713
  term.setCursorPos(2, 2)
1714
  term.write("this server will manage")
1715
  local ttY = 2
1716
  for k, v in pairs(colorWheel) do
1717
    ttY = ttY + 1
1718
    term.setCursorPos(2, ttY)
1719
    term.write(k .. " = " .. v)
1720
  end
1721
  repeat
1722
    term.setCursorPos(30, 2)
1723
    local newColor = string.upper(read())
1724
    ccSettings.color = colorWheel[newColor:sub(1, 1)]
1725
  until colorWheel[newColor:sub(1, 1)]
1726
  --# Should WiRe server get a GPS fix on startup?
1727
  term.clear()
1728
  term.setCursorPos(2, 1)
1729
  term.write("Last question!")
1730
  term.setCursorPos(2, 3)
1731
  term.write("Do you want WiRe server to")
1732
  term.setCursorPos(2, 4)
1733
  term.write("aquire a GPS fix on startup? [y/n]")
1734
  term.setCursorPos(2, 6)
1735
  local getFix = string.lower(read())
1736
  ccSettings.getGPSFix = getFix:sub(1, 1) == "y"
1737
  if not fs.exists("/data") then fs.makeDir("/data") end
1738
  saveData()
1739
  return true
1740
end
1741
1742
term.setBackgroundColor(black)
1743
if pocket or turtle then error("Computer REQUIRED.", 0) end
1744
if not fs.exists(config) then
1745
  if not firstRun() then return end
1746
end
1747
term.clear()
1748
term.setCursorPos(2, 2)
1749
term.setTextColor(white)
1750
term.write("Ingesting configuration data . . .")
1751
local srvConfig = fs.open(config, "r") or error("initMe(): Cannot open " .. config .. " for reading", 0)
1752
ccSettings = textutils.unserialize(srvConfig.readAll())
1753
srvConfig.close()
1754
if ccSettings.getFix ~= nil or not ccSettings.newColors then
1755
  if ccSettings.getFix ~= nil then
1756
    ccSettings.getGPSFix = ccSettings.getFix
1757
    ccSettings.getFix = nil
1758
  end
1759
  if not ccSettings.newColors then
1760
    if ccSettings.color == "lgray" or ccSettings.color == "Light Gray" then
1761
      ccSettings.color = "Silver"
1762
    elseif ccSettings.color == "lblue" or ccSettings.color == "Light Blue" then
1763
      ccSettings.color = "Sky"
1764
    end
1765
    ccSettings.newColors = true
1766
  end
1767
  saveData()
1768
end
1769
term.setCursorPos(2, 4)
1770
term.write("Configuring hardware . . .")
1771
for _, side in pairs(rs.getSides()) do
1772
  if peripheral.isPresent(side) then
1773
    if peripheral.getType(side) == "monitor" and peripheral.call(side, "isColor") then
1774
      mon = peripheral.wrap(side)
1775
      monSide = side
1776
    elseif peripheral.getType(side) == "modem" then
1777
      if peripheral.call(side, "isWireless") then
1778
        modemCount = modemCount + 1
1779
        modemSides[modemCount] = side
1780
        if not rednet.isOpen(side) then rednet.open(side) end
1781
      else
1782
        for _, name in pairs(peripheral.call(side, "getNamesRemote")) do
1783
          if peripheral.getType(name) == "monitor" and peripheral.call(name, "isColor") then
1784
            mon = peripheral.wrap(name)
1785
            monSide = name
1786
          elseif peripheral.getType(name) == "computer" then
1787
            local modemFound = false
1788
            for i = 1, modemCount do
1789
              if modemSides[i] == side then modemFound = true break end
1790
            end
1791
            if not modemFound then
1792
              modemCount = modemCount + 1
1793
              modemSides[modemCount] = side
1794
              if not rednet.isOpen(side) then rednet.open(side) end
1795
            end
1796
          end
1797
        end
1798
      end
1799
    end
1800
  end
1801
end
1802
if modemCount == 0 then return initError("modem", "a modem") end
1803
if not mon then return initError("monitor", "an Advanced Monitor") end
1804
mon.setTextScale(0.5)
1805
monX, monY = mon.getSize()
1806
clearMon(mwhite)
1807
mon.setTextColor(mblack)
1808
mon.setCursorPos(2, 3)
1809
mon.write("Initializing...")
1810
term.setCursorPos(2, 6)
1811
term.write("Hosting services...")
1812
network = "WiRe" .. ccSettings.color
1813
rednet.host(network, ccSettings.color)
1814
if ccSettings.getGPSFix then
1815
  term.setCursorPos(2, 8)
1816
  term.write("Acquiring GPS fix . . .")
1817
  loc.x, loc.y, loc.z = gps.locate(2)
1818
end
1819
if not loc.x then
1820
  loc.x, loc.y, loc.z = "No GPS Fix", "No GPS Fix", "No GPS Fix"
1821
end
1822
ccSuccess = true
1823
thisCommand = "init"
1824
kernelState = true
1825
if tArgs[1] then
1826
  term.clear()
1827
else
1828
  termScreenStatic()
1829
end
1830
clearMon()
1831
--# Monitor Header
1832
local labelText = ccSettings.name .. " [" .. ccSettings.color .. "]"
1833
local ccNameLen, labelLen, monLabel = #ccSettings.name, #labelText
1834
if ccNameLen <= monX and labelLen > monX then
1835
  local spacer = (monX - ccNameLen) / 2
1836
  monLabel = string.rep(" ", math.floor(spacer)) .. ccSettings.name .. string.rep(" ", math.ceil(spacer))
1837
elseif ccNameLen > monX then
1838
  monLabel = ccSettings.name:sub(1, monX)
1839
else
1840
  local spacer = (monX - labelLen) / 2
1841
  monLabel = string.rep(" ", math.floor(spacer)) .. labelText .. string.rep(" ", math.ceil(spacer))
1842
end
1843
mon.setBackgroundColor(colorBurst[ccSettings.color][2] or mgray)
1844
mon.setTextColor(colorBurst[ccSettings.color][3] or mcyan)
1845
mon.setCursorPos(1, 1)
1846
mon.write(monLabel)
1847
--# Monitor Footer
1848
local xPos, xPos2 = math.floor(monX / 4), monX > 15 and math.ceil(monX / 2) + 8 or math.ceil(monX / 2) + 2
1849
mon.setCursorPos(1, monY)
1850
mon.setBackgroundColor(mgray)
1851
mon.write(string.rep(" ", monX))
1852
mon.setTextColor(mred)
1853
mon.setCursorPos(xPos - 2, monY)
1854
mon.write("LOCK")
1855
mon.setTextColor(mgreen)
1856
mon.setCursorPos(monX > 36 and monX - xPos - 3 or xPos2, monY)
1857
mon.write("UNLOCK")
1858
mon.setCursorPos(math.floor(monX / 2) - 1, monY)
1859
mon.setBackgroundColor(mblack)
1860
mon.write("   ")
1861
updateScreens()
1862
pollTimer = os.startTimer(1)
1863
if tArgs[1] then
1864
  parallel.waitForAny(netReceive, dataPoller, monTouch, foregroundShell)
1865
else
1866
  parallel.waitForAny(netReceive, dataPoller, monTouch, userInput)
1867
end