HydrantHunter

ccDialer 2.0

Jul 13th, 2015
1,784
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 102.94 KB | None | 0 0
  1. --[[  LanteaCraft      ]]--
  2. --[[      and SGCraft  ]]--
  3. --[[     ccDialer      ]]--
  4. --[[      by Dog       ]]--
  5. --[[ aka HydrantHunter ]]--
  6. --[[ pastebin MKAZXSny ]]--
  7. local ccDialVer = "2.0.00"
  8. --[[
  9. Tested with/requires:
  10.   - Minecraft 1.7.10+ AND ComputerCraft 1.75+ || LanteaCraft LC2-16+ | OR | SGCraft1.11.x-mc1.7.10+
  11.   For setup and usage instructions, please visit http://tinyurl.com/jf3rjr7
  12.  
  13. Special thanks to: theoriginalbit (custom read function)
  14.                    Anavrins       (pbkdf2/sha256 hashing)
  15.                    SquidDev       (AES encryption/decryption)
  16.                    Alex Kloss     (base64 encoder/decoder)
  17.  
  18. IMPORTANT NOTE:
  19.   - all of the following variables are set by the program as necessary
  20.   - editing any of them below will most likely cause unexpected results
  21. ]]--
  22. --# AUTOMATIC/STATIC CONFIGURATION
  23. --# Peripherals
  24. local termX, termY = term.getSize() --# pocket 26x20 / tab 26x19
  25. local modemSide, thisCC = "none", tostring(os.getComputerID())
  26. --# Status Info
  27. local runState, dialAddress, remoteIrisState = "init", "none", "unk"
  28. local drawCLI, drawAddressBook, drawElement, pNum, currentEdit, word, curX, curY, bgCol, txtCol, pingTimer
  29. local missedPings = 0
  30. local hosts, host = { }, { id = nil, name = "NO HOST", addr = "NO HOST", ccDHD = false, status = nil }
  31. local gettingInput = false
  32. --# Address Book
  33. local addressBook = { { name = "NO GATES", addr = "ADDRESS", rating = "U", iris = "none", callDrop = false, note = "Short note", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }, }
  34. local gateChange = false
  35. local gatePages, gatePage, syncPages, syncPage, abCount = 1, 1, 1, 1, 1
  36. local classifications = {
  37.   B = { order = 1, color = colors.blue, label = "Base/Outpost/Hub" };
  38.   H = { order = 2, color = colors.lightBlue, label = "Home/Camp" };
  39.   V = { order = 3, color = colors.brown, label = "Village" };
  40.   M = { order = 4, color = colors.purple, label = "Misc/Special" };
  41.   S = { order = 5, color = colors.green, label = "Safe/Secured" };
  42.   C = { order = 6, color = colors.orange, label = "Caution" };
  43.   D = { order = 7, color = colors.red, label = "Danger" };
  44.   U = { order = 8, color = colors.lightGray, label = "Unk/Unclassified", long = "Unknown/Unclassified" };
  45. }
  46. --# Color Definitions
  47. local white = colors.white
  48. local silver = colors.lightGray
  49. local gray = colors.gray
  50. local black = colors.black
  51. local brown = colors.brown
  52. local yellow = colors.yellow
  53. local orange = colors.orange
  54. local red = colors.red
  55. --local magenta = colors.magenta
  56. --local purple = colors.purple
  57. local blue = colors.blue
  58. local sky = colors.lightBlue
  59. local cyan = colors.cyan
  60. --local lime = colors.lime
  61. local green = colors.green
  62. --# END AUTOMATIC/STATIC CONFIGURATION
  63.  
  64. -- Lua 5.1+ base64 v3.0 (c) 2009 by Alex Kloss <alexthkloss@web.de>
  65. -- licensed under the terms of the LGPL2
  66. -- http://lua-users.org/wiki/BaseSixtyFour
  67. -- character table string
  68. local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
  69. -- encoding
  70. local function encode(data)
  71.   return ((data:gsub('.', function(x)
  72.     local r,b='',x:byte()
  73.     for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
  74.     return r;
  75.   end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
  76.     if (#x < 6) then return '' end
  77.     local c=0
  78.     for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
  79.     return b:sub(c+1,c+1)
  80.   end)..({ '', '==', '=' })[#data%3+1])
  81. end
  82. -- decoding
  83. local function decode(data)
  84.   data = string.gsub(data, '[^'..b..'=]', '')
  85.   return (data:gsub('.', function(x)
  86.     if (x == '=') then return '' end
  87.     local r,f='',(b:find(x)-1)
  88.     for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
  89.     return r;
  90.   end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
  91.     if (#x ~= 8) then return '' end
  92.     local c=0
  93.     for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
  94.     return string.char(c)
  95.   end))
  96. end
  97.  
  98. -- AES Lua implementation by SquidDev
  99. -- https://gist.github.com/SquidDev/86925e07cbabd70773e53d781bd8b2fe
  100. local encrypt, decrypt
  101. do
  102.   local function _W(f) local e=setmetatable({}, {__index = _ENV or getfenv()}) if setfenv then setfenv(f, e) end return f(e) or e end
  103.   local bit=_W(function(_ENV, ...)
  104.   --[[
  105.     This bit API is designed to cope with unsigned integers instead of normal integers
  106.     To do this we add checks for overflows: (x > 2^31 ? x - 2 ^ 32 : x)
  107.     These are written in long form because no constant folding.
  108.   ]]
  109.   local floor = math.floor
  110.   local lshift, rshift
  111.  
  112.   rshift = function(a,disp)
  113.     return floor(a % 4294967296 / 2^disp)
  114.   end
  115.  
  116.   lshift = function(a,disp)
  117.     return (a * 2^disp) % 4294967296
  118.   end
  119.  
  120.   return {
  121.     -- bit operations
  122.     bnot = bit32 and bit32.bnot or bit.bnot,
  123.     band = bit32 and bit32.band or bit.band,
  124.     bor  = bit32 and bit32.bor or bit.bor,
  125.     bxor = bit32 and bit32.bxor or bit.bxor,
  126.     rshift = rshift,
  127.     lshift = lshift,
  128.   }
  129.   end)
  130.  
  131.   local gf=_W(function(_ENV, ...)
  132.   -- finite field with base 2 and modulo irreducible polynom x^8+x^4+x^3+x+1 = 0x11d
  133.   local bxor = bit32 and bit32.bxor or bit.bxor
  134.   local lshift = bit.lshift
  135.   -- private data of gf
  136.   local n = 0x100
  137.   local ord = 0xff
  138.   local irrPolynom = 0x11b
  139.   local exp, log = {}, {}
  140.   --
  141.   -- add two polynoms (its simply xor)
  142.   --
  143.   local function add(operand1, operand2)
  144.     return bxor(operand1,operand2)
  145.   end
  146.   --
  147.   -- subtract two polynoms (same as addition)
  148.   --
  149.   local function sub(operand1, operand2)
  150.     return bxor(operand1,operand2)
  151.   end
  152.   --
  153.   -- inverts element
  154.   -- a^(-1) = g^(order - log(a))
  155.   --
  156.   local function invert(operand)
  157.     -- special case for 1 or normal invert
  158.     return operand == 1 and 1 or exp[ord - log[operand]]
  159.   end
  160.   --
  161.   -- multiply two elements using a logarithm table
  162.   -- a*b = g^(log(a)+log(b))
  163.   --
  164.   local function mul(operand1, operand2)
  165.     if (operand1 == 0 or operand2 == 0) then
  166.       return 0
  167.     end
  168.     local exponent = log[operand1] + log[operand2]
  169.     if (exponent >= ord) then
  170.       exponent = exponent - ord
  171.     end
  172.     return exp[exponent]
  173.   end
  174.   --
  175.   -- divide two elements
  176.   -- a/b = g^(log(a)-log(b))
  177.   --
  178.   local function div(operand1, operand2)
  179.     if (operand1 == 0)  then
  180.       return 0
  181.     end
  182.     -- TODO: exception if operand2 == 0
  183.     local exponent = log[operand1] - log[operand2]
  184.     if (exponent < 0) then
  185.       exponent = exponent + ord
  186.     end
  187.     return exp[exponent]
  188.   end
  189.   --
  190.   -- print logarithmic table
  191.   --
  192.   local function printLog()
  193.     for i = 1, n do
  194.       print("log(", i-1, ")=", log[i-1])
  195.     end
  196.   end
  197.   --
  198.   -- print exponentiation table
  199.   --
  200.   local function printExp()
  201.     for i = 1, n do
  202.       print("exp(", i-1, ")=", exp[i-1])
  203.     end
  204.   end
  205.   --
  206.   -- calculate logarithmic and exponentiation table
  207.   --
  208.   local function initMulTable()
  209.     local a = 1
  210.     for i = 0,ord-1 do
  211.       exp[i] = a
  212.       log[a] = i
  213.       -- multiply with generator x+1 -> left shift + 1
  214.       a = bxor(lshift(a, 1), a)
  215.       -- if a gets larger than order, reduce modulo irreducible polynom
  216.       if a > ord then
  217.         a = sub(a, irrPolynom)
  218.       end
  219.     end
  220.   end
  221.  
  222.   initMulTable()
  223.  
  224.   return {
  225.     add = add,
  226.     sub = sub,
  227.     invert = invert,
  228.     mul = mul,
  229.     div = div,
  230.     printLog = printLog,
  231.     printExp = printExp,
  232.   }
  233.   end)
  234.  
  235.   util=_W(function(_ENV, ...)
  236.   -- Cache some bit operators
  237.   local bxor = bit.bxor
  238.   local rshift = bit.rshift
  239.   local band = bit.band
  240.   local lshift = bit.lshift
  241.   local sleepCheckIn
  242.   --
  243.   -- calculate the parity of one byte
  244.   --
  245.   local function byteParity(byte)
  246.     byte = bxor(byte, rshift(byte, 4))
  247.     byte = bxor(byte, rshift(byte, 2))
  248.     byte = bxor(byte, rshift(byte, 1))
  249.     return band(byte, 1)
  250.   end
  251.   --
  252.   -- get byte at position index
  253.   --
  254.   local function getByte(number, index)
  255.     return index == 0 and band(number,0xff) or band(rshift(number, index*8),0xff)
  256.   end
  257.   --
  258.   -- put number into int at position index
  259.   --
  260.   local function putByte(number, index)
  261.     return index == 0 and band(number,0xff) or lshift(band(number,0xff),index*8)
  262.   end
  263.   --
  264.   -- convert byte array to int array
  265.   --
  266.   local function bytesToInts(bytes, start, n)
  267.     local ints = {}
  268.     for i = 0, n - 1 do
  269.       ints[i + 1] =
  270.           putByte(bytes[start + (i*4)], 3) +
  271.           putByte(bytes[start + (i*4) + 1], 2) +
  272.           putByte(bytes[start + (i*4) + 2], 1) +
  273.           putByte(bytes[start + (i*4) + 3], 0)
  274.       if n % 10000 == 0 then sleepCheckIn() end
  275.     end
  276.     return ints
  277.   end
  278.   --
  279.   -- convert int array to byte array
  280.   --
  281.   local function intsToBytes(ints, output, outputOffset, n)
  282.     n = n or #ints
  283.     for i = 0, n - 1 do
  284.       for j = 0,3 do
  285.         output[outputOffset + i*4 + (3 - j)] = getByte(ints[i + 1], j)
  286.       end
  287.       if n % 10000 == 0 then sleepCheckIn() end
  288.     end
  289.     return output
  290.   end
  291.   --
  292.   -- convert bytes to hexString
  293.   --
  294.   local function bytesToHex(bytes)
  295.     local hexBytes = ""
  296.     for i,byte in ipairs(bytes) do
  297.       hexBytes = hexBytes .. string.format("%02x ", byte)
  298.     end
  299.     return hexBytes
  300.   end
  301.  
  302.   local function hexToBytes(bytes)
  303.     local out = {}
  304.     for i = 1, #bytes, 2 do
  305.       out[#out + 1] = tonumber(bytes:sub(i, i + 1), 16)
  306.     end
  307.     return out
  308.   end
  309.   --
  310.   -- convert data to hex string
  311.   --
  312.   local function toHexString(data)
  313.     local type = type(data)
  314.     if (type == "number") then
  315.       return string.format("%08x",data)
  316.     elseif (type == "table") then
  317.       return bytesToHex(data)
  318.     elseif (type == "string") then
  319.       local bytes = {string.byte(data, 1, #data)}
  320.       return bytesToHex(bytes)
  321.     else
  322.       return data
  323.     end
  324.   end
  325.  
  326.   local function padByteString(data)
  327.     local dataLength = #data
  328.     local random1 = math.random(0,255)
  329.     local random2 = math.random(0,255)
  330.     local prefix = string.char(random1,
  331.       random2,
  332.       random1,
  333.       random2,
  334.       getByte(dataLength, 3),
  335.       getByte(dataLength, 2),
  336.       getByte(dataLength, 1),
  337.       getByte(dataLength, 0)
  338.     )
  339.     data = prefix .. data
  340.     local padding, paddingLength = "", math.ceil(#data/16)*16 - #data
  341.     for i=1,paddingLength do
  342.       padding = padding .. string.char(math.random(0,255))
  343.     end
  344.     return data .. padding
  345.   end
  346.  
  347.   local function properlyDecrypted(data)
  348.     local random = {string.byte(data,1,4)}
  349.     if (random[1] == random[3] and random[2] == random[4]) then
  350.       return true
  351.     end
  352.     return false
  353.   end
  354.  
  355.   local function unpadByteString(data)
  356.     if (not properlyDecrypted(data)) then
  357.       return nil
  358.     end
  359.     local dataLength = putByte(string.byte(data,5), 3)
  360.              + putByte(string.byte(data,6), 2)
  361.              + putByte(string.byte(data,7), 1)
  362.              + putByte(string.byte(data,8), 0)
  363.     return string.sub(data,9,8+dataLength)
  364.   end
  365.  
  366.   local function xorIV(data, iv)
  367.     for i = 1,16 do
  368.       data[i] = bxor(data[i], iv[i])
  369.     end
  370.   end
  371.  
  372.   local function increment(data)
  373.     local i = 16
  374.     while true do
  375.       local value = data[i] + 1
  376.       if value >= 256 then
  377.         data[i] = value - 256
  378.         i = (i - 2) % 16 + 1
  379.       else
  380.         data[i] = value
  381.         break
  382.       end
  383.     end
  384.   end
  385.  
  386.   -- Called every encryption cycle
  387.   local push, pull, time = os.queueEvent, coroutine.yield, os.time
  388.   local oldTime = time()
  389.   local function sleepCheckIn()
  390.     local newTime = time()
  391.     if newTime - oldTime >= 0.03 then -- (0.020 * 1.5)
  392.       oldTime = newTime
  393.       push("sleep")
  394.       pull("sleep")
  395.     end
  396.   end
  397.  
  398.   local function getRandomData(bytes)
  399.     local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
  400.     local result = {}
  401.     for i=1,bytes do
  402.       insert(result, random(0,255))
  403.       if i % 10240 == 0 then sleep() end
  404.     end
  405.     return result
  406.   end
  407.  
  408.   local function getRandomString(bytes)
  409.     local char, random, sleep, insert = string.char, math.random, sleepCheckIn, table.insert
  410.     local result = {}
  411.     for i=1,bytes do
  412.       insert(result, char(random(0,255)))
  413.       if i % 10240 == 0 then sleep() end
  414.     end
  415.     return table.concat(result)
  416.   end
  417.  
  418.   return {
  419.     byteParity = byteParity,
  420.     getByte = getByte,
  421.     putByte = putByte,
  422.     bytesToInts = bytesToInts,
  423.     intsToBytes = intsToBytes,
  424.     bytesToHex = bytesToHex,
  425.     hexToBytes = hexToBytes,
  426.     toHexString = toHexString,
  427.     padByteString = padByteString,
  428.     properlyDecrypted = properlyDecrypted,
  429.     unpadByteString = unpadByteString,
  430.     xorIV = xorIV,
  431.     increment = increment,
  432.     sleepCheckIn = sleepCheckIn,
  433.     getRandomData = getRandomData,
  434.     getRandomString = getRandomString,
  435.   }
  436.   end)
  437.  
  438.   aes=_W(function(_ENV, ...)
  439.   -- Implementation of AES with nearly pure lua
  440.   -- AES with lua is slow, really slow :-)
  441.   local putByte = util.putByte
  442.   local getByte = util.getByte
  443.   -- some constants
  444.   local ROUNDS = 'rounds'
  445.   local KEY_TYPE = "type"
  446.   local ENCRYPTION_KEY=1
  447.   local DECRYPTION_KEY=2
  448.   -- aes SBOX
  449.   local SBox = {}
  450.   local iSBox = {}
  451.   -- aes tables
  452.   local table0 = {}
  453.   local table1 = {}
  454.   local table2 = {}
  455.   local table3 = {}
  456.   local tableInv0 = {}
  457.   local tableInv1 = {}
  458.   local tableInv2 = {}
  459.   local tableInv3 = {}
  460.   -- round constants
  461.   local rCon = {
  462.     0x01000000,
  463.     0x02000000,
  464.     0x04000000,
  465.     0x08000000,
  466.     0x10000000,
  467.     0x20000000,
  468.     0x40000000,
  469.     0x80000000,
  470.     0x1b000000,
  471.     0x36000000,
  472.     0x6c000000,
  473.     0xd8000000,
  474.     0xab000000,
  475.     0x4d000000,
  476.     0x9a000000,
  477.     0x2f000000,
  478.   }
  479.   --
  480.   -- affine transformation for calculating the S-Box of AES
  481.   --
  482.   local function affinMap(byte)
  483.     mask = 0xf8
  484.     result = 0
  485.     for i = 1,8 do
  486.       result = bit.lshift(result,1)
  487.       parity = util.byteParity(bit.band(byte,mask))
  488.       result = result + parity
  489.       -- simulate roll
  490.       lastbit = bit.band(mask, 1)
  491.       mask = bit.band(bit.rshift(mask, 1),0xff)
  492.       mask = lastbit ~= 0 and bit.bor(mask, 0x80) or bit.band(mask, 0x7f)
  493.     end
  494.     return bit.bxor(result, 0x63)
  495.   end
  496.   --
  497.   -- calculate S-Box and inverse S-Box of AES
  498.   -- apply affine transformation to inverse in finite field 2^8
  499.   --
  500.   local function calcSBox()
  501.     for i = 0, 255 do
  502.       inverse = i ~= 0 and gf.invert(i) or 0
  503.       mapped = affinMap(inverse)
  504.       SBox[i] = mapped
  505.       iSBox[mapped] = i
  506.     end
  507.   end
  508.   --
  509.   -- Calculate round tables
  510.   -- round tables are used to calculate shiftRow, MixColumn and SubBytes
  511.   -- with 4 table lookups and 4 xor operations.
  512.   --
  513.   local function calcRoundTables()
  514.     for x = 0,255 do
  515.       byte = SBox[x]
  516.       table0[x] = putByte(gf.mul(0x03, byte), 0)
  517.                 + putByte(             byte , 1)
  518.                 + putByte(             byte , 2)
  519.                 + putByte(gf.mul(0x02, byte), 3)
  520.       table1[x] = putByte(             byte , 0)
  521.                 + putByte(             byte , 1)
  522.                 + putByte(gf.mul(0x02, byte), 2)
  523.                 + putByte(gf.mul(0x03, byte), 3)
  524.       table2[x] = putByte(             byte , 0)
  525.                 + putByte(gf.mul(0x02, byte), 1)
  526.                 + putByte(gf.mul(0x03, byte), 2)
  527.                 + putByte(             byte , 3)
  528.       table3[x] = putByte(gf.mul(0x02, byte), 0)
  529.                 + putByte(gf.mul(0x03, byte), 1)
  530.                 + putByte(             byte , 2)
  531.                 + putByte(             byte , 3)
  532.     end
  533.   end
  534.   --
  535.   -- Calculate inverse round tables
  536.   -- does the inverse of the normal roundtables for the equivalent
  537.   -- decryption algorithm.
  538.   --
  539.   local function calcInvRoundTables()
  540.     for x = 0,255 do
  541.       byte = iSBox[x]
  542.       tableInv0[x] = putByte(gf.mul(0x0b, byte), 0)
  543.                  + putByte(gf.mul(0x0d, byte), 1)
  544.                  + putByte(gf.mul(0x09, byte), 2)
  545.                  + putByte(gf.mul(0x0e, byte), 3)
  546.       tableInv1[x] = putByte(gf.mul(0x0d, byte), 0)
  547.                  + putByte(gf.mul(0x09, byte), 1)
  548.                  + putByte(gf.mul(0x0e, byte), 2)
  549.                  + putByte(gf.mul(0x0b, byte), 3)
  550.       tableInv2[x] = putByte(gf.mul(0x09, byte), 0)
  551.                  + putByte(gf.mul(0x0e, byte), 1)
  552.                  + putByte(gf.mul(0x0b, byte), 2)
  553.                  + putByte(gf.mul(0x0d, byte), 3)
  554.       tableInv3[x] = putByte(gf.mul(0x0e, byte), 0)
  555.                  + putByte(gf.mul(0x0b, byte), 1)
  556.                  + putByte(gf.mul(0x0d, byte), 2)
  557.                  + putByte(gf.mul(0x09, byte), 3)
  558.     end
  559.   end
  560.   --
  561.   -- rotate word: 0xaabbccdd gets 0xbbccddaa
  562.   -- used for key schedule
  563.   --
  564.   local function rotWord(word)
  565.     local tmp = bit.band(word,0xff000000)
  566.     return (bit.lshift(word,8) + bit.rshift(tmp,24))
  567.   end
  568.   --
  569.   -- replace all bytes in a word with the SBox.
  570.   -- used for key schedule
  571.   --
  572.   local function subWord(word)
  573.     return putByte(SBox[getByte(word,0)],0)
  574.       + putByte(SBox[getByte(word,1)],1)
  575.       + putByte(SBox[getByte(word,2)],2)
  576.       + putByte(SBox[getByte(word,3)],3)
  577.   end
  578.   --
  579.   -- generate key schedule for aes encryption
  580.   --
  581.   -- returns table with all round keys and
  582.   -- the necessary number of rounds saved in [ROUNDS]
  583.   --
  584.   local function expandEncryptionKey(key)
  585.     local keySchedule = {}
  586.     local keyWords = math.floor(#key / 4)
  587.     if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then
  588.       error("Invalid key size: " .. tostring(keyWords))
  589.       return nil
  590.     end
  591.     keySchedule[ROUNDS] = keyWords + 6
  592.     keySchedule[KEY_TYPE] = ENCRYPTION_KEY
  593.     for i = 0,keyWords - 1 do
  594.       keySchedule[i] = putByte(key[i*4+1], 3)
  595.                + putByte(key[i*4+2], 2)
  596.                + putByte(key[i*4+3], 1)
  597.                + putByte(key[i*4+4], 0)
  598.     end
  599.     for i = keyWords, (keySchedule[ROUNDS] + 1)*4 - 1 do
  600.       local tmp = keySchedule[i-1]
  601.       if ( i % keyWords == 0) then
  602.         tmp = rotWord(tmp)
  603.         tmp = subWord(tmp)
  604.         local index = math.floor(i/keyWords)
  605.         tmp = bit.bxor(tmp,rCon[index])
  606.       elseif (keyWords > 6 and i % keyWords == 4) then
  607.         tmp = subWord(tmp)
  608.       end
  609.       keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp)
  610.     end
  611.     return keySchedule
  612.   end
  613.   --
  614.   -- Inverse mix column
  615.   -- used for key schedule of decryption key
  616.   --
  617.   local function invMixColumnOld(word)
  618.     local b0 = getByte(word,3)
  619.     local b1 = getByte(word,2)
  620.     local b2 = getByte(word,1)
  621.     local b3 = getByte(word,0)
  622.     return putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1),
  623.                          gf.mul(0x0d, b2)),
  624.                          gf.mul(0x09, b3)),
  625.                          gf.mul(0x0e, b0)),3)
  626.        + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2),
  627.                          gf.mul(0x0d, b3)),
  628.                          gf.mul(0x09, b0)),
  629.                          gf.mul(0x0e, b1)),2)
  630.        + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3),
  631.                          gf.mul(0x0d, b0)),
  632.                          gf.mul(0x09, b1)),
  633.                          gf.mul(0x0e, b2)),1)
  634.        + putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0),
  635.                          gf.mul(0x0d, b1)),
  636.                          gf.mul(0x09, b2)),
  637.                          gf.mul(0x0e, b3)),0)
  638.   end
  639.   --
  640.   -- Optimized inverse mix column
  641.   -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
  642.   -- TODO: make it work
  643.   --
  644.   local function invMixColumn(word)
  645.     local b0 = getByte(word,3)
  646.     local b1 = getByte(word,2)
  647.     local b2 = getByte(word,1)
  648.     local b3 = getByte(word,0)
  649.     local t = bit.bxor(b3,b2)
  650.     local u = bit.bxor(b1,b0)
  651.     local v = bit.bxor(t,u)
  652.     v = bit.bxor(v,gf.mul(0x08,v))
  653.     w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0)))
  654.     v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1)))
  655.     return putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0)
  656.        + putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t              )), 1)
  657.        + putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2)
  658.        + putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u              )), 3)
  659.   end
  660.   --
  661.   -- generate key schedule for aes decryption
  662.   --
  663.   -- uses key schedule for aes encryption and transforms each
  664.   -- key by inverse mix column.
  665.   --
  666.   local function expandDecryptionKey(key)
  667.     local keySchedule = expandEncryptionKey(key)
  668.     if (keySchedule == nil) then
  669.       return nil
  670.     end
  671.     keySchedule[KEY_TYPE] = DECRYPTION_KEY
  672.     for i = 4, (keySchedule[ROUNDS] + 1)*4 - 5 do
  673.       keySchedule[i] = invMixColumnOld(keySchedule[i])
  674.     end
  675.     return keySchedule
  676.   end
  677.   --
  678.   -- xor round key to state
  679.   --
  680.   local function addRoundKey(state, key, round)
  681.     for i = 0, 3 do
  682.       state[i + 1] = bit.bxor(state[i + 1], key[round*4+i])
  683.     end
  684.   end
  685.   --
  686.   -- do encryption round (ShiftRow, SubBytes, MixColumn together)
  687.   --
  688.   local function doRound(origState, dstState)
  689.     dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
  690.           table0[getByte(origState[1],3)],
  691.           table1[getByte(origState[2],2)]),
  692.           table2[getByte(origState[3],1)]),
  693.           table3[getByte(origState[4],0)])
  694.     dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
  695.           table0[getByte(origState[2],3)],
  696.           table1[getByte(origState[3],2)]),
  697.           table2[getByte(origState[4],1)]),
  698.           table3[getByte(origState[1],0)])
  699.     dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
  700.           table0[getByte(origState[3],3)],
  701.           table1[getByte(origState[4],2)]),
  702.           table2[getByte(origState[1],1)]),
  703.           table3[getByte(origState[2],0)])
  704.     dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
  705.           table0[getByte(origState[4],3)],
  706.           table1[getByte(origState[1],2)]),
  707.           table2[getByte(origState[2],1)]),
  708.           table3[getByte(origState[3],0)])
  709.   end
  710.   --
  711.   -- do last encryption round (ShiftRow and SubBytes)
  712.   --
  713.   local function doLastRound(origState, dstState)
  714.     dstState[1] = putByte(SBox[getByte(origState[1],3)], 3)
  715.           + putByte(SBox[getByte(origState[2],2)], 2)
  716.           + putByte(SBox[getByte(origState[3],1)], 1)
  717.           + putByte(SBox[getByte(origState[4],0)], 0)
  718.     dstState[2] = putByte(SBox[getByte(origState[2],3)], 3)
  719.           + putByte(SBox[getByte(origState[3],2)], 2)
  720.           + putByte(SBox[getByte(origState[4],1)], 1)
  721.           + putByte(SBox[getByte(origState[1],0)], 0)
  722.     dstState[3] = putByte(SBox[getByte(origState[3],3)], 3)
  723.           + putByte(SBox[getByte(origState[4],2)], 2)
  724.           + putByte(SBox[getByte(origState[1],1)], 1)
  725.           + putByte(SBox[getByte(origState[2],0)], 0)
  726.     dstState[4] = putByte(SBox[getByte(origState[4],3)], 3)
  727.           + putByte(SBox[getByte(origState[1],2)], 2)
  728.           + putByte(SBox[getByte(origState[2],1)], 1)
  729.           + putByte(SBox[getByte(origState[3],0)], 0)
  730.   end
  731.   --
  732.   -- do decryption round
  733.   --
  734.   local function doInvRound(origState, dstState)
  735.     dstState[1] =  bit.bxor(bit.bxor(bit.bxor(
  736.           tableInv0[getByte(origState[1],3)],
  737.           tableInv1[getByte(origState[4],2)]),
  738.           tableInv2[getByte(origState[3],1)]),
  739.           tableInv3[getByte(origState[2],0)])
  740.     dstState[2] =  bit.bxor(bit.bxor(bit.bxor(
  741.           tableInv0[getByte(origState[2],3)],
  742.           tableInv1[getByte(origState[1],2)]),
  743.           tableInv2[getByte(origState[4],1)]),
  744.           tableInv3[getByte(origState[3],0)])
  745.     dstState[3] =  bit.bxor(bit.bxor(bit.bxor(
  746.           tableInv0[getByte(origState[3],3)],
  747.           tableInv1[getByte(origState[2],2)]),
  748.           tableInv2[getByte(origState[1],1)]),
  749.           tableInv3[getByte(origState[4],0)])
  750.     dstState[4] =  bit.bxor(bit.bxor(bit.bxor(
  751.           tableInv0[getByte(origState[4],3)],
  752.           tableInv1[getByte(origState[3],2)]),
  753.           tableInv2[getByte(origState[2],1)]),
  754.           tableInv3[getByte(origState[1],0)])
  755.   end
  756.   --
  757.   -- do last decryption round
  758.   --
  759.   local function doInvLastRound(origState, dstState)
  760.     dstState[1] = putByte(iSBox[getByte(origState[1],3)], 3)
  761.           + putByte(iSBox[getByte(origState[4],2)], 2)
  762.           + putByte(iSBox[getByte(origState[3],1)], 1)
  763.           + putByte(iSBox[getByte(origState[2],0)], 0)
  764.     dstState[2] = putByte(iSBox[getByte(origState[2],3)], 3)
  765.           + putByte(iSBox[getByte(origState[1],2)], 2)
  766.           + putByte(iSBox[getByte(origState[4],1)], 1)
  767.           + putByte(iSBox[getByte(origState[3],0)], 0)
  768.     dstState[3] = putByte(iSBox[getByte(origState[3],3)], 3)
  769.           + putByte(iSBox[getByte(origState[2],2)], 2)
  770.           + putByte(iSBox[getByte(origState[1],1)], 1)
  771.           + putByte(iSBox[getByte(origState[4],0)], 0)
  772.     dstState[4] = putByte(iSBox[getByte(origState[4],3)], 3)
  773.           + putByte(iSBox[getByte(origState[3],2)], 2)
  774.           + putByte(iSBox[getByte(origState[2],1)], 1)
  775.           + putByte(iSBox[getByte(origState[1],0)], 0)
  776.   end
  777.   --
  778.   -- encrypts 16 Bytes
  779.   -- key           encryption key schedule
  780.   -- input         array with input data
  781.   -- inputOffset   start index for input
  782.   -- output        array for encrypted data
  783.   -- outputOffset  start index for output
  784.   --
  785.   local function encrypt(key, input, inputOffset, output, outputOffset)
  786.     --default parameters
  787.     inputOffset = inputOffset or 1
  788.     output = output or {}
  789.     outputOffset = outputOffset or 1
  790.     local state, tmpState = {}, {}
  791.     if (key[KEY_TYPE] ~= ENCRYPTION_KEY) then
  792.       error("No encryption key: " .. tostring(key[KEY_TYPE]) .. ", expected " .. ENCRYPTION_KEY)
  793.       return
  794.     end
  795.     state = util.bytesToInts(input, inputOffset, 4)
  796.     addRoundKey(state, key, 0)
  797.     local round = 1
  798.     while (round < key[ROUNDS] - 1) do
  799.       -- do a double round to save temporary assignments
  800.       doRound(state, tmpState)
  801.       addRoundKey(tmpState, key, round)
  802.       round = round + 1
  803.       doRound(tmpState, state)
  804.       addRoundKey(state, key, round)
  805.       round = round + 1
  806.     end
  807.     doRound(state, tmpState)
  808.     addRoundKey(tmpState, key, round)
  809.     round = round +1
  810.     doLastRound(tmpState, state)
  811.     addRoundKey(state, key, round)
  812.     util.sleepCheckIn()
  813.     return util.intsToBytes(state, output, outputOffset)
  814.   end
  815.   --
  816.   -- decrypt 16 bytes
  817.   -- key           decryption key schedule
  818.   -- input         array with input data
  819.   -- inputOffset   start index for input
  820.   -- output        array for decrypted data
  821.   -- outputOffset  start index for output
  822.   ---
  823.   local function decrypt(key, input, inputOffset, output, outputOffset)
  824.     -- default arguments
  825.     inputOffset = inputOffset or 1
  826.     output = output or {}
  827.     outputOffset = outputOffset or 1
  828.     local state, tmpState = {}, {}
  829.     if (key[KEY_TYPE] ~= DECRYPTION_KEY) then
  830.       error("No decryption key: " .. tostring(key[KEY_TYPE]))
  831.       return
  832.     end
  833.     state = util.bytesToInts(input, inputOffset, 4)
  834.     addRoundKey(state, key, key[ROUNDS])
  835.     local round = key[ROUNDS] - 1
  836.     while (round > 2) do
  837.       -- do a double round to save temporary assignments
  838.       doInvRound(state, tmpState)
  839.       addRoundKey(tmpState, key, round)
  840.       round = round - 1
  841.       doInvRound(tmpState, state)
  842.       addRoundKey(state, key, round)
  843.       round = round - 1
  844.     end
  845.     doInvRound(state, tmpState)
  846.     addRoundKey(tmpState, key, round)
  847.     round = round - 1
  848.     doInvLastRound(tmpState, state)
  849.     addRoundKey(state, key, round)
  850.     util.sleepCheckIn()
  851.     return util.intsToBytes(state, output, outputOffset)
  852.   end
  853.  
  854.   -- calculate all tables when loading this file
  855.   calcSBox()
  856.   calcRoundTables()
  857.   calcInvRoundTables()
  858.  
  859.   return {
  860.     ROUNDS = ROUNDS,
  861.     KEY_TYPE = KEY_TYPE,
  862.     ENCRYPTION_KEY = ENCRYPTION_KEY,
  863.     DECRYPTION_KEY = DECRYPTION_KEY,
  864.     expandEncryptionKey = expandEncryptionKey,
  865.     expandDecryptionKey = expandDecryptionKey,
  866.     encrypt = encrypt,
  867.     decrypt = decrypt,
  868.   }
  869.   end)
  870.  
  871.   local buffer=_W(function(_ENV, ...)
  872.   local function new()
  873.     return {}
  874.   end
  875.  
  876.   local function addString(stack, s)
  877.     table.insert(stack, s)
  878.   end
  879.  
  880.   local function toString(stack)
  881.     return table.concat(stack)
  882.   end
  883.  
  884.   return {
  885.     new = new,
  886.     addString = addString,
  887.     toString = toString,
  888.   }
  889.   end)
  890.  
  891.   ciphermode=_W(function(_ENV, ...)
  892.   local public = {}
  893.   --
  894.   -- Encrypt strings
  895.   -- key - byte array with key
  896.   -- string - string to encrypt
  897.   -- modefunction - function for cipher mode to use
  898.   --
  899.   local random, unpack = math.random, unpack or table.unpack
  900.   function public.encryptString(key, data, modeFunction, iv)
  901.     if iv then
  902.       local ivCopy = {}
  903.       for i = 1, 16 do ivCopy[i] = iv[i] end
  904.       iv = ivCopy
  905.     else
  906.       iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  907.     end
  908.     local keySched = aes.expandEncryptionKey(key)
  909.     local encryptedData = buffer.new()
  910.     for i = 1, #data/16 do
  911.       local offset = (i-1)*16 + 1
  912.       local byteData = {string.byte(data,offset,offset +15)}
  913.       iv = modeFunction(keySched, byteData, iv)
  914.       buffer.addString(encryptedData, string.char(unpack(byteData)))
  915.     end
  916.     return buffer.toString(encryptedData)
  917.   end
  918.   --
  919.   -- the following 4 functions can be used as
  920.   -- modefunction for encryptString
  921.   --
  922.   -- Electronic code book mode encrypt function
  923.   function public.encryptECB(keySched, byteData, iv)
  924.     aes.encrypt(keySched, byteData, 1, byteData, 1)
  925.   end
  926.  
  927.   -- Cipher block chaining mode encrypt function
  928.   function public.encryptCBC(keySched, byteData, iv)
  929.     util.xorIV(byteData, iv)
  930.     aes.encrypt(keySched, byteData, 1, byteData, 1)
  931.     return byteData
  932.   end
  933.  
  934.   -- Output feedback mode encrypt function
  935.   function public.encryptOFB(keySched, byteData, iv)
  936.     aes.encrypt(keySched, iv, 1, iv, 1)
  937.     util.xorIV(byteData, iv)
  938.     return iv
  939.   end
  940.  
  941.   -- Cipher feedback mode encrypt function
  942.   function public.encryptCFB(keySched, byteData, iv)
  943.     aes.encrypt(keySched, iv, 1, iv, 1)
  944.     util.xorIV(byteData, iv)
  945.     return byteData
  946.   end
  947.  
  948.   function public.encryptCTR(keySched, byteData, iv)
  949.     local nextIV = {}
  950.     for j = 1, 16 do nextIV[j] = iv[j] end
  951.     aes.encrypt(keySched, iv, 1, iv, 1)
  952.     util.xorIV(byteData, iv)
  953.     util.increment(nextIV)
  954.     return nextIV
  955.   end
  956.   --
  957.   -- Decrypt strings
  958.   -- key - byte array with key
  959.   -- string - string to decrypt
  960.   -- modefunction - function for cipher mode to use
  961.   --
  962.   function public.decryptString(key, data, modeFunction, iv)
  963.     if iv then
  964.       local ivCopy = {}
  965.       for i = 1, 16 do ivCopy[i] = iv[i] end
  966.       iv = ivCopy
  967.     else
  968.       iv = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
  969.     end
  970.     local keySched
  971.     if modeFunction == public.decryptOFB or modeFunction == public.decryptCFB or modeFunction == public.decryptCTR then
  972.       keySched = aes.expandEncryptionKey(key)
  973.     else
  974.       keySched = aes.expandDecryptionKey(key)
  975.     end
  976.     local decryptedData = buffer.new()
  977.     for i = 1, #data/16 do
  978.       local offset = (i-1)*16 + 1
  979.       local byteData = {string.byte(data,offset,offset +15)}
  980.       iv = modeFunction(keySched, byteData, iv)
  981.       buffer.addString(decryptedData, string.char(unpack(byteData)))
  982.     end
  983.     return buffer.toString(decryptedData)
  984.   end
  985.   --
  986.   -- the following 4 functions can be used as
  987.   -- modefunction for decryptString
  988.   --
  989.   -- Electronic code book mode decrypt function
  990.   function public.decryptECB(keySched, byteData, iv)
  991.     aes.decrypt(keySched, byteData, 1, byteData, 1)
  992.     return iv
  993.   end
  994.  
  995.   -- Cipher block chaining mode decrypt function
  996.   function public.decryptCBC(keySched, byteData, iv)
  997.     local nextIV = {}
  998.     for j = 1, 16 do nextIV[j] = byteData[j] end
  999.     aes.decrypt(keySched, byteData, 1, byteData, 1)
  1000.     util.xorIV(byteData, iv)
  1001.     return nextIV
  1002.   end
  1003.  
  1004.   -- Output feedback mode decrypt function
  1005.   function public.decryptOFB(keySched, byteData, iv)
  1006.     aes.encrypt(keySched, iv, 1, iv, 1)
  1007.     util.xorIV(byteData, iv)
  1008.     return iv
  1009.   end
  1010.  
  1011.   -- Cipher feedback mode decrypt function
  1012.   function public.decryptCFB(keySched, byteData, iv)
  1013.     local nextIV = {}
  1014.     for j = 1, 16 do nextIV[j] = byteData[j] end
  1015.     aes.encrypt(keySched, iv, 1, iv, 1)
  1016.     util.xorIV(byteData, iv)
  1017.     return nextIV
  1018.   end
  1019.  
  1020.   public.decryptCTR = public.encryptCTR
  1021.   return public
  1022.   end)
  1023.  
  1024.   -- Simple API for encrypting strings.
  1025.   --
  1026.   AES128 = 16
  1027.   AES192 = 24
  1028.   AES256 = 32
  1029.   ECBMODE = 1
  1030.   CBCMODE = 2
  1031.   OFBMODE = 3
  1032.   CFBMODE = 4
  1033.   CTRMODE = 4
  1034.  
  1035.   local function pwToKey(password, keyLength, iv)
  1036.     local padLength = keyLength
  1037.     if (keyLength == AES192) then
  1038.       padLength = 32
  1039.     end
  1040.     if (padLength > #password) then
  1041.       local postfix = ""
  1042.       for i = 1,padLength - #password do
  1043.         postfix = postfix .. string.char(0)
  1044.       end
  1045.       password = password .. postfix
  1046.     else
  1047.       password = string.sub(password, 1, padLength)
  1048.     end
  1049.     local pwBytes = {string.byte(password,1,#password)}
  1050.     password = ciphermode.encryptString(pwBytes, password, ciphermode.encryptCBC, iv)
  1051.     password = string.sub(password, 1, keyLength)
  1052.     return {string.byte(password,1,#password)}
  1053.   end
  1054.   --
  1055.   -- Encrypts string data with password password.
  1056.   -- password  - the encryption key is generated from this string
  1057.   -- data      - string to encrypt (must not be too large)
  1058.   -- keyLength - length of aes key: 128(default), 192 or 256 Bit
  1059.   -- mode      - mode of encryption: ecb, cbc(default), ofb, cfb
  1060.   --
  1061.   -- mode and keyLength must be the same for encryption and decryption.
  1062.   --
  1063.   function encrypt(password, data, keyLength, mode, iv)
  1064.     assert(password ~= nil, "Empty password.")
  1065.     assert(data ~= nil, "Empty data.")
  1066.     local mode = mode or CBCMODE
  1067.     local keyLength = keyLength or AES128
  1068.     local key = pwToKey(password, keyLength, iv)
  1069.     local paddedData = util.padByteString(data)
  1070.     if mode == ECBMODE then
  1071.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptECB, iv)
  1072.     elseif mode == CBCMODE then
  1073.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptCBC, iv)
  1074.     elseif mode == OFBMODE then
  1075.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptOFB, iv)
  1076.     elseif mode == CFBMODE then
  1077.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptCFB, iv)
  1078.     elseif mode == CTRMODE then
  1079.       return ciphermode.encryptString(key, paddedData, ciphermode.encryptCTR, iv)
  1080.     else
  1081.       error("Unknown mode", 2)
  1082.     end
  1083.   end
  1084.   --
  1085.   -- Decrypts string data with password password.
  1086.   -- password  - the decryption key is generated from this string
  1087.   -- data      - string to encrypt
  1088.   -- keyLength - length of aes key: 128(default), 192 or 256 Bit
  1089.   -- mode      - mode of decryption: ecb, cbc(default), ofb, cfb
  1090.   --
  1091.   -- mode and keyLength must be the same for encryption and decryption.
  1092.   --
  1093.   function decrypt(password, data, keyLength, mode, iv)
  1094.     local mode = mode or CBCMODE
  1095.     local keyLength = keyLength or AES128
  1096.     local key = pwToKey(password, keyLength, iv)
  1097.     local plain
  1098.     if mode == ECBMODE then
  1099.       plain = ciphermode.decryptString(key, data, ciphermode.decryptECB, iv)
  1100.     elseif mode == CBCMODE then
  1101.       plain = ciphermode.decryptString(key, data, ciphermode.decryptCBC, iv)
  1102.     elseif mode == OFBMODE then
  1103.       plain = ciphermode.decryptString(key, data, ciphermode.decryptOFB, iv)
  1104.     elseif mode == CFBMODE then
  1105.       plain = ciphermode.decryptString(key, data, ciphermode.decryptCFB, iv)
  1106.     elseif mode == CTRMODE then
  1107.       plain = ciphermode.decryptString(key, data, ciphermode.decryptCTR, iv)
  1108.     else
  1109.       error("Unknown mode", 2)
  1110.     end
  1111.     result = util.unpadByteString(plain)
  1112.     if (result == nil) then
  1113.       return nil
  1114.     end
  1115.     return result
  1116.   end
  1117. end
  1118.  
  1119. -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft
  1120. -- By Anavrins
  1121. -- For help and details, you can PM me on the CC forums
  1122. -- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins
  1123. -- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
  1124. -- Pastebin: http://pastebin.com/6UV4qfNF
  1125. local digest, hmac, pbkdf2
  1126. do
  1127.   local mod32 = 2^32
  1128.   local sha_hashlen = 32
  1129.   local sha_blocksize = 64
  1130.   local band    = bit32 and bit32.band or bit.band
  1131.   local bnot    = bit32 and bit32.bnot or bit.bnot
  1132.   local bxor    = bit32 and bit32.bxor or bit.bxor
  1133.   local blshift = bit32 and bit32.lshift or bit.blshift
  1134.   local upack   = unpack
  1135.  
  1136.   local function rrotate(n, b)
  1137.     local s = n/(2^b)
  1138.     local f = s%1
  1139.     return (s-f) + f*mod32
  1140.   end
  1141.  
  1142.   local function brshift(int, by) -- Thanks bit32 for bad rshift
  1143.     local s = int / (2^by)
  1144.     return s - s%1
  1145.   end
  1146.  
  1147.   local H = {
  1148.     0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
  1149.     0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
  1150.   }
  1151.  
  1152.   local K = {
  1153.     0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  1154.     0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
  1155.     0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
  1156.     0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
  1157.     0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
  1158.     0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
  1159.     0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  1160.     0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
  1161.   }
  1162.  
  1163.   local function counter(incr)
  1164.     local t1, t2 = 0, 0
  1165.     if 0xFFFFFFFF - t1 < incr then
  1166.       t2 = t2 + 1
  1167.       t1 = incr - (0xFFFFFFFF - t1) - 1    
  1168.     else t1 = t1 + incr
  1169.     end
  1170.     return t2, t1
  1171.   end
  1172.  
  1173.   local function BE_toInt(bs, i)
  1174.     return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0)
  1175.   end
  1176.  
  1177.   local function preprocess(data)
  1178.     local len = #data
  1179.     local proc = {}
  1180.     data[#data+1] = 0x80
  1181.     while #data%64~=56 do data[#data+1] = 0 end
  1182.     local blocks = math.ceil(#data/64)
  1183.     for i = 1, blocks do
  1184.       proc[i] = {}
  1185.       for j = 1, 16 do
  1186.         proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4))
  1187.       end
  1188.     end
  1189.     proc[blocks][15], proc[blocks][16] = counter(len*8)
  1190.     return proc
  1191.   end
  1192.  
  1193.   local function digestblock(w, C)
  1194.     for j = 17, 64 do
  1195.       local v = w[j-15]
  1196.       local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3))
  1197.       local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10))
  1198.       w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32
  1199.     end
  1200.     local a, b, c, d, e, f, g, h = upack(C)
  1201.     for j = 1, 64 do
  1202.       local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25))
  1203.       local ch = bxor(band(e, f), band(bnot(e), g))
  1204.       local temp1 = (h + S1 + ch + K[j] + w[j])%mod32
  1205.       local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22))
  1206.       local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c))
  1207.       local temp2 = (S0 + maj)%mod32
  1208.       h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32
  1209.     end
  1210.     C[1] = (C[1] + a)%mod32
  1211.     C[2] = (C[2] + b)%mod32
  1212.     C[3] = (C[3] + c)%mod32
  1213.     C[4] = (C[4] + d)%mod32
  1214.     C[5] = (C[5] + e)%mod32
  1215.     C[6] = (C[6] + f)%mod32
  1216.     C[7] = (C[7] + g)%mod32
  1217.     C[8] = (C[8] + h)%mod32
  1218.     return C
  1219.   end
  1220.  
  1221.   local mt = {
  1222.     __tostring = function(a) return string.char(unpack(a)) end,
  1223.     __index = {
  1224.       toHex = function(self, s) return ("%02x"):rep(#self):format(unpack(self)) end,
  1225.       isEqual = function(self, t)
  1226.         if type(t) ~= "table" then return false end
  1227.         if #self ~= #t then return false end
  1228.         local ret = 0
  1229.         for i = 1, #self do
  1230.           ret = bit32.bor(ret, bxor(self[i], t[i]))
  1231.         end
  1232.         return ret == 0
  1233.       end
  1234.     }
  1235.   }
  1236.  
  1237.   local function toBytes(t, n)
  1238.     local b = {}
  1239.     for i = 1, n do
  1240.       b[(i-1)*4+1] = band(brshift(band(t[i], 0xFF000000), 24), 0xFF)
  1241.       b[(i-1)*4+2] = band(brshift(band(t[i], 0xFF0000), 16), 0xFF)
  1242.       b[(i-1)*4+3] = band(brshift(band(t[i], 0xFF00), 8), 0xFF)
  1243.       b[(i-1)*4+4] = band(t[i], 0xFF)
  1244.     end
  1245.     return setmetatable(b, mt)
  1246.   end
  1247.  
  1248.   digest = function(data)
  1249.     data = data or ""
  1250.     data = type(data) == "string" and {data:byte(1,-1)} or data
  1251.     data = preprocess(data)
  1252.     local C = {upack(H)}
  1253.     for i = 1, #data do C = digestblock(data[i], C) end
  1254.     return toBytes(C, 8)
  1255.   end
  1256.  
  1257.   hmac = function(data, key)
  1258.     local data = type(data) == "table" and {upack(data)} or {tostring(data):byte(1,-1)}
  1259.     local key = type(key) == "table" and {upack(key)} or {tostring(key):byte(1,-1)}
  1260.     local blocksize = sha_blocksize
  1261.     key = #key > blocksize and digest(key) or key
  1262.     local ipad = {}
  1263.     local opad = {}
  1264.     local padded_key = {}
  1265.     for i = 1, blocksize do
  1266.       ipad[i] = bxor(0x36, key[i] or 0)
  1267.       opad[i] = bxor(0x5C, key[i] or 0)
  1268.     end
  1269.     for i = 1, #data do
  1270.       ipad[blocksize+i] = data[i]
  1271.     end
  1272.     ipad = digest(ipad)
  1273.     for i = 1, blocksize do
  1274.       padded_key[i] = opad[i]
  1275.       padded_key[blocksize+i] = ipad[i]
  1276.     end
  1277.     return digest(padded_key)
  1278.   end
  1279.  
  1280.   pbkdf2 = function(pass, salt, iter, dklen)
  1281.     local out = {}
  1282.     local hashlen = sha_hashlen
  1283.     local block = 1
  1284.     dklen = dklen or 32
  1285.     while dklen > 0 do
  1286.       local ikey = {}
  1287.       local isalt = type(salt) == "table" and {upack(salt)} or {tostring(salt):byte(1,-1)}
  1288.       local clen = dklen > hashlen and hashlen or dklen
  1289.       local iCount = #isalt
  1290.       isalt[iCount+1] = band(brshift(band(block, 0xFF000000), 24), 0xFF)
  1291.       isalt[iCount+2] = band(brshift(band(block, 0xFF0000), 16), 0xFF)
  1292.       isalt[iCount+3] = band(brshift(band(block, 0xFF00), 8), 0xFF)
  1293.       isalt[iCount+4] = band(block, 0xFF)
  1294.       for j = 1, iter do
  1295.         isalt = hmac(isalt, pass)
  1296.         for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end
  1297.         if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
  1298.       end
  1299.       dklen = dklen - clen
  1300.       block = block+1
  1301.       for k = 1, clen do out[#out+1] = ikey[k] end
  1302.     end
  1303.     return setmetatable(out, mt)
  1304.   end
  1305. end
  1306.  
  1307. local function mergeAddressBooks(newGates)
  1308.   local matchFound, abName, abNote, abDim, ngName, ngNote, ngDim = false --# use matchFound to indicate if a matching gate was found or not
  1309.   for i = 1, #newGates do                             --# start cycling through the list of 'new' gates
  1310.     for j = 1, abCount do                             --# search the address book for a matching address
  1311.       if newGates[i].addr == addressBook[j].addr then --# if the gate is already in the address book...
  1312.         matchFound = true                             --# ...set matchFound to true and...
  1313.         abName, ngName = addressBook[j].name, newGates[i].name
  1314.         if (abName == "Name" or abName == "NO GATES" or abName == "NEW GATE" or abName == addressBook[j].addr) and ngName ~= newGates[i].addr and ngName ~= "NEW GATE" and ngName ~= "NO GATES" and ngName ~= "Name" then
  1315.           addressBook[j].name = ngName
  1316.           gateChange = true
  1317.         end
  1318.         abNote, ngNote = addressBook[j].note, newGates[i].note
  1319.         if (abNote == "short note" or abNote == "Discovered gate" or abNote == "Added from logs") and ngNote ~= "short note" and ngNote ~= "Discovered gate" and ngNote ~= "Added from logs" then
  1320.           addressBook[j].note = ngNote
  1321.           gateChange = true
  1322.         end
  1323.         if addressBook[j].rating == "U" and newGates[i].rating ~= "U" then
  1324.           addressBook[j].rating = newGates[i].rating
  1325.           gateChange = true
  1326.         end
  1327.         abDim, ngDim = addressBook[j].loc.dim, newGates[i].loc.dim
  1328.         if (abDim == "Overworld" and ngDim ~= "Overworld" and ngDim ~= "Unspecified") or (abDim == "Unspecified" and ngDim ~= "Unspecified") then
  1329.           addressBook[j].loc.dim = ngDim
  1330.           gateChange = true
  1331.         end
  1332.         if addressBook[j].loc.x == 0 and addressBook[j].loc.y == 0 and addressBook[j].loc.z == 0 and (newGates[i].loc.x ~= 0 or newGates[i].loc.y ~= 0 or newGates[i].loc.z ~= 0) then
  1333.           addressBook[j].loc.x, addressBook[j].loc.y, addressBook[j].loc.z = newGates[i].loc.x, newGates[i].loc.y, newGates[i].loc.z
  1334.           gateChange = true
  1335.         end
  1336.         break                                         --# stop the address book search loop and move on to the next gate in the 'new' list
  1337.       end
  1338.     end
  1339.     if matchFound then                                --# if a match was found...
  1340.       matchFound = false                              --# ...reset the variable for the next iteration of the loop
  1341.     else                                              --# otherwise...
  1342.       addressBook[abCount + 1], abCount = { }, abCount + 1 --# initialize a new address book entry
  1343.       for k, v in pairs(newGates[i]) do               --# loop through the gate entries...
  1344.         addressBook[abCount][k] = v                   --# ...and add them to the new address book entry
  1345.       end
  1346.       addressBook[abCount].iris = "none"
  1347.       addressBook[abCount].callDrop = false
  1348.       gateChange = true                               --# indicate that address book data has changed
  1349.     end
  1350.   end
  1351.   if abCount > 1 and (addressBook[1].name == "NO GATES" or addressBook[1].name == "Name") and addressBook[1].addr == "ADDRESS" then table.remove(addressBook, 1) abCount = abCount - 1 end
  1352.   gatePages = math.ceil(abCount / 14)                 --# repaginate AddressBook
  1353. end
  1354.  
  1355. local function saveData()
  1356.   local gateData = fs.open("/data/DHDgates", "w")
  1357.   gateData.write(textutils.serialize(addressBook))
  1358.   gateData.close()
  1359.   gateChange = false
  1360. end
  1361.  
  1362. local function repositionCursor()
  1363.   term.setTextColor(txtCol)
  1364.   term.setBackgroundColor(bgCol)
  1365.   term.setCursorPos(curX, curY)
  1366.   term.write(word)
  1367. end
  1368.  
  1369. local function netSend(dataPack)
  1370.   if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
  1371.   local encKey = tostring(host.id) .. "ccDHD!General_Comms*Key" .. thisCC
  1372.   local encryptedDataPack = encode(encrypt(encKey, textutils.serialize(dataPack)))
  1373.   rednet.send(host.id, encryptedDataPack, "ccDialerWiFi")
  1374. end
  1375.  
  1376. local function netReceive()
  1377.   local id, message, encKey, encryptedMessage, decryptedMessage, encodedMessage, success, iColor, mData
  1378.   while true do
  1379.     if not rednet.isOpen(modemSide) then rednet.open(modemSide) end
  1380.     id, encodedMessage = rednet.receive("ccDialerWiFi")
  1381.     if id == host.id and type(encodedMessage) == "string" then
  1382.       success, encryptedMessage = pcall(decode, encodedMessage)
  1383.       if success then
  1384.         encKey = thisCC .. "ccDHD!General_Comms*Key" .. tostring(id)
  1385.         success, decryptedMessage = pcall(decrypt, encKey, encryptedMessage)
  1386.         if success then
  1387.           success, message = pcall(textutils.unserialize, decryptedMessage)
  1388.           if success and type(message) == "table" and message.program and message.program == "ccDialer" and message.data then
  1389.             mData = message.data
  1390.             if type(mData) == "string" then
  1391.               if mData == "pong" then
  1392.                 missedPings = 0
  1393.               elseif mData == "ping" then
  1394.                 netSend({ program = "ccDialer", gate = host.addr, command = "pong" })
  1395.               elseif mData == "open" or mData == "closed" or mData == "unk" then
  1396.                 remoteIrisState = mData
  1397.                 if runState ~= "Help" and runState ~= "viewing" then
  1398.                   iColor = remoteIrisState == "open" and green or red
  1399.                   iColor = remoteIrisState == "unk" and gray or iColor
  1400.                   drawElement(6, 1, 1, 1, iColor, blue, "I") --# 'I' for Iris control
  1401.                   if gettingInput then repositionCursor() end
  1402.                 end
  1403.               elseif mData == "Idle" or mData == "Dialing" or mData == "Paused" or mData == "Connected" or mData == "Offline" then
  1404.                 if mData == "Offline" or mData == "Idle" then
  1405.                   remoteIrisState, dialAddress = "unk", "none"
  1406.                   if mData == "Offline" then
  1407.                     host = { id = nil, name = "NO HOST", addr = "NO HOST", ccDHD = false, status = nil }
  1408.                   else
  1409.                     host.status = mData
  1410.                   end
  1411.                 end
  1412.                 if runState == "Dial" or runState == "goPage" or runState == "Sync" then
  1413.                   if mData == "Offline" or mData == "Idle" then
  1414.                     drawElement(6, 1, 1, 1, gray, blue, "I") --# 'I' for Iris control
  1415.                     if mData == "Offline" and runState == "Sync" then
  1416.                       runState = "Dial"
  1417.                       drawElement(1, 5, termX - 1, termY - 5, nil, black) --# clear address book area
  1418.                     end
  1419.                     mData = mData == "Idle" and "Idle     " or "         "
  1420.                   elseif mData == "Dialing" then
  1421.                     mData = "Dialing  "
  1422.                   end
  1423.                   if runState == "Dial" or runState == "goPage" then
  1424.                     if mData == "Dialing  " then
  1425.                       drawElement(17, 2, 1, 1, silver, gray, mData) --# gate status
  1426.                     else
  1427.                       drawCLI() --# redraw the address book, et. al.
  1428.                     end
  1429.                   end
  1430.                   if gettingInput then repositionCursor() end
  1431.                 end
  1432.               end
  1433.             elseif type(mData) == "table" and message.command and message.command == "push" then
  1434.               mergeAddressBooks(mData)
  1435.               if gateChange then
  1436.                 saveData()
  1437.                 if host.addr == host.name and host.addr ~= "NO HOST" then
  1438.                   for i = 1, #mData do
  1439.                     if host.addr == mData[i].addr then
  1440.                       host.name = mData[i].name
  1441.                       break
  1442.                     end
  1443.                   end
  1444.                 end
  1445.                 if runState == "Dial" or runState == "goPage" then drawCLI() end
  1446.                 if gettingInput then repositionCursor() end
  1447.               end
  1448.             end
  1449.           end
  1450.         end
  1451.       end
  1452.     end
  1453.   end
  1454. end
  1455.  
  1456. local function assignColor(gateNumber)
  1457.   return classifications[addressBook[gateNumber].rating].color or silver
  1458. end
  1459.  
  1460. local function assignRating(gateNumber)
  1461.   return classifications[addressBook[gateNumber].rating].long or (classifications[addressBook[gateNumber].rating].label or "Unknown/Unclassified")
  1462. end
  1463.  
  1464. local function clearScreen(bgColor)
  1465.   term.setBackgroundColor(bgColor or black)
  1466.   term.clear()
  1467. end
  1468.  
  1469. local function shutDown()
  1470.   if host.id then netSend({ program = "ccDialer", gate = host.addr, command = "logout" }) end
  1471.   if rednet.isOpen(modemSide) then rednet.close(modemSide) end
  1472.   clearScreen()
  1473.   term.setTextColor(white)
  1474.   term.setCursorPos(1, 1)
  1475. end
  1476.  
  1477. do
  1478.   local validColors = { [1] = true; [2] = true; [4] = true; [8] = true; [16] = true; [32] = true; [64] = true; [128] = true; [256] = true; [512] = true; [1024] = true; [2048] = true; [4096] = true; [8192] = true; [16384] = true, [32768] = true; }
  1479.  
  1480.   drawElement = function(x, y, w, h, txtColor, bgColor, text)
  1481.     if type(x) == "number" and type(y) == "number" and type(w) == "number" and type(h) == "number" then
  1482.       local sText = text and tostring(text) or "" --# Ensure text is a string
  1483.       w = math.floor(math.min(math.max(1, math.max(#sText, w)), termX)) --# validate width
  1484.       h = math.floor(math.max(1, math.min(h, termY)))                   --# validate height
  1485.       x = math.floor(math.max(1, math.min(x, termX - w + 1)))           --# validate x coord
  1486.       y = math.floor(math.max(1, math.min(y, termY - h + 1)))           --# validate y coord
  1487.       local spacer = (w - #sText) / 2
  1488.       local txtLine = string.rep(" ", math.floor(spacer)) .. sText .. string.rep(" ", math.ceil(spacer))
  1489.       if type(txtColor) == "number" and validColors[txtColor] then term.setTextColor(txtColor) end    --# validate the text color
  1490.       if type(bgColor) == "number" and validColors[bgColor] then term.setBackgroundColor(bgColor) end --# validate the background color
  1491.       if h == 1 then              --# if the height is 1 then...
  1492.         term.setCursorPos(x, y)   --# Position cursor
  1493.         term.write(txtLine)       --# Draw the single line of the 'element'
  1494.       else                        --# otherwise...
  1495.         local line, txtRow = string.rep(" ", w), math.floor(h / 2) + y --# Define one line of the 'element' (box/rectangle/line-seg) and the row the text will be drawn on
  1496.         for i = y, y + h - 1 do   --# Loop through the height of the 'element' line by line (top to bottom)
  1497.           term.setCursorPos(x, i) --# Position cursor
  1498.           term.write(i == txtRow and txtLine or line) --# Draw 1 of h lines of the 'element' (box/rectangle)
  1499.         end
  1500.       end
  1501.     end
  1502.   end
  1503. end
  1504.  
  1505. local function drawRatingList(rating)
  1506.   local txtColor
  1507.   drawElement(7, 6, 19, 8, nil, gray)                  --# menu body
  1508.   for k, v in pairs(classifications) do
  1509.     if rating == k then
  1510.       txtColor = rating == "U" and white or v.color
  1511.       drawElement(25, v.order + 5, 1, 1, nil, v.color) --# selected rating color pip
  1512.     else
  1513.       txtColor = silver
  1514.     end
  1515.     drawElement(6, v.order + 5, 1, 1, nil, v.color)    --# color pip
  1516.     drawElement(8, v.order + 5, 1, 1, txtColor, gray, v.label)
  1517.   end
  1518. end
  1519.  
  1520. local function drawHeader()
  1521.   local spacer, title = termX / 2, ""
  1522.   if runState == "viewing" then
  1523.     local titleText = addressBook[currentEdit].name
  1524.     spacer = (termX - #titleText) / 2
  1525.     title = string.rep(" ", math.floor(spacer)) .. titleText .. string.rep(" ", math.ceil(spacer) - 3) .. "[ ]"
  1526.   elseif runState == "Help" then
  1527.     title = string.rep(" ", math.floor(spacer) - 4) .. "ccDialer" .. string.rep(" ", math.ceil(spacer) - 7) .. "[ ]"
  1528.   else
  1529.     title = "[ ] [ ]" .. string.rep(" ", math.floor(spacer) - 11) .. "ccDialer" .. string.rep(" ", math.ceil(spacer) - 7) .. "[ ]"
  1530.   end
  1531.   drawElement(1, 1, termX, 1, white, runState == "viewing" and assignColor(currentEdit) or blue, title) --# title bar
  1532.   if runState ~= "viewing" and runState ~= "Help" then
  1533.     drawElement(2, 1, 1, 1, host.id and green or gray, nil, "H") --# 'H' for Sync Hosts
  1534.     local iColor = remoteIrisState == "open" and green or red
  1535.     iColor = remoteIrisState == "unk" and gray or iColor
  1536.     drawElement(6, 1, 1, 1, iColor, nil, "I")                    --# 'I' for Iris control
  1537.   end
  1538.   local xColor = red
  1539.   if runState == "viewing" and addressBook[currentEdit].rating == "D" then xColor = white end
  1540.   drawElement(termX - 1, 1, 1, 1, xColor, nil, "X")              --# 'X' for Exit
  1541.   if runState == "Dial" then
  1542.     drawElement(1, 2, termX, 1, nil, gray)
  1543.     drawElement(15, 2, 1, 1, nil, blue, " ")
  1544.     drawElement(2, 2, 1, 1, silver, gray, host.name)
  1545.     drawElement(17, 2, 1, 1, nil, nil, host.status or "          ")
  1546.     drawElement(1, 3, 11, 1, host.ccDHD and white or gray, host.ccDHD and cyan or silver, " Sync")
  1547.     term.setBackgroundColor(green)
  1548.     term.setTextColor(gateChange and orange or white)
  1549.     term.write(" + ")
  1550.     term.setBackgroundColor(red)
  1551.     if gateChange then term.setTextColor(white) end --# change the text color back to white if it was changed to orange
  1552.     term.write("  END Call  ")
  1553.   elseif runState == "Sync" then
  1554.     drawElement(1, 2, termX, 1, nil, gray)
  1555.     drawElement(15, 2, 1, 1, nil, blue, " ")
  1556.     drawElement(2, 2, 1, 1, silver, gray, host.name)
  1557.     drawElement(17, 2, 1, 1, nil, nil, host.status or "          ")
  1558.   elseif runState == "Help" then
  1559.     drawElement(1, 2, termX, 1, silver, gray, "+ button")
  1560.   elseif runState == "viewing" then
  1561.     drawElement(1, 2, termX, 1, silver, gray, "Gate Information")
  1562.   end
  1563. end
  1564.  
  1565. local function drawHostsUI()
  1566.   local yPos, hostCount, magicNumber = 5, #hosts, ((syncPage - 1) * 5) + 1
  1567.   drawElement(6, 5, 16, 1, white, blue, tostring(hostCount) .. " Hosts") --# Header
  1568.   drawElement(6, 6, 16, 11, nil, gray) --# Body
  1569.   for i = magicNumber, math.min(hostCount, magicNumber + 4) do
  1570.     yPos = yPos + 2
  1571.     drawElement(7, yPos, 14, 1, nil, hosts[i].ccDHD and green or red) --# Indicate ccDHD host on left & right side of hostName
  1572.     drawElement(8, yPos, 12, 1, nil, silver, hosts[i].name) --# hostName (host name or address)
  1573.   end
  1574.   drawElement(6, 17, 16, 1, silver, blue, "Page " .. tostring(syncPage) .. " of " .. tostring(syncPages)) --# footer
  1575. end
  1576.  
  1577. local function drawPasswordUI()
  1578.   drawElement(2, 5, termX - 2, 1, gray, black, "Remote  password") --# header
  1579.   drawElement(6, 6, 16, 3, nil, silver)                            --# body
  1580.   drawElement(7, 7, 14, 1, nil, gray)                              --# input area
  1581. end
  1582.  
  1583. local function drawGoPagePopUp()
  1584.   drawElement((termX / 2) - 3, termY - 3, 1, 1, white, gray, " :Page: ") --# header
  1585.   drawElement((termX / 2) - 3, termY - 2, 8, 2)                          --# body
  1586.   drawElement((termX / 2) - 2, termY - 2, 6, 1, nil, black)              --# input area bg
  1587. end
  1588.  
  1589. local function drawNaviUI()
  1590.   pNum = tostring(gatePage) .. " of " .. tostring(gatePages)
  1591.   drawElement((termX / 2) - 10, termY, 1, 1, gray, black, gatePages > 1 and "<< <              > >>" or "                      ") --# fwd/back buttons
  1592.   drawElement(((termX - #pNum) / 2) + 1, termY, 1, 1, silver, nil, pNum) --# page number
  1593. end
  1594.  
  1595. local function drawHelpScreen()
  1596.   drawElement(1, 6, termX, 1, silver, gray, "Address Book")
  1597.   drawElement(1, 11, termX, 1, nil, nil, "Header buttons")
  1598.   drawElement(1, 16, termX, 1, nil, nil, "Sync button")
  1599.   drawElement(1, termY, termX, 1, nil, nil, "version " .. ccDialVer)
  1600.   drawElement(2, 3, 1, 1, black, white, "(L) click: Add address")
  1601.   drawElement(2, 4, 1, 1, nil, nil, "(R) click: Save addr book")
  1602.   drawElement(2, 7, 1, 1, nil, nil, "(L) click: dial address")
  1603.   drawElement(2, 8, 1, 1, nil, nil, "(R) click: view or edit")
  1604.   drawElement(2, 9, 1, 1, nil, nil, "(M) click: del. address")
  1605.   drawElement(2, 12, 1, 1, nil, nil, "[H] = locate Sync host")
  1606.   drawElement(2, 13, 1, 1, nil, nil, "[I] = open remote iris")
  1607.   drawElement(2, 14, 1, 1, nil, nil, "[X] = Exit/Quit")
  1608.   drawElement(2, 17, 1, 1, nil, nil, "(L) click: Send/Receive")
  1609.   drawElement(13, 18, 1, 1, nil, nil, "address book")
  1610. end
  1611.  
  1612. local function drawSyncScreen()
  1613.   drawElement(1, 3, termX, 1, white, cyan, "Sync")
  1614.   drawElement(2, 6, termX - 2, 3, white, green, "GET Address Book")
  1615.   drawElement(2, 10, termX - 2, 3, nil, orange, "SEND Address Book")
  1616.   drawElement(2, 14, termX - 2, 3, nil, red, "C A N C E L")
  1617. end
  1618.  
  1619. local function drawGateData()
  1620.   local addr, note, breakPoint = addressBook[currentEdit].addr, addressBook[currentEdit].note, 24
  1621.   drawElement(2, 4, 1, 1, cyan, black, addressBook[currentEdit].name .. string.rep(" ", 12 - #addressBook[currentEdit].name)) --# Name
  1622.   if host.addr == addr or host.addr:sub(1, 7) == addr or host.addr == addr:sub(1, 7) then
  1623.     drawElement(15, 6, 1, 1, gray, nil, "Sync Host")                          --# Sync Host
  1624.   elseif addr == dialAddress or addr:sub(1, 7) == dialAddress or addr == dialAddress:sub(1, 7) then
  1625.     drawElement(15, 6, 1, 1, gray, nil, "Target   ")                          --# Dial target
  1626.   else
  1627.     drawElement(15, 6, 9, 1)
  1628.   end
  1629.   drawElement(2, 6, 1, 1, yellow, nil, addr .. "  ")                          --# Address
  1630.   local ratingWord = assignRating(currentEdit)
  1631.   drawElement(2, 8, 1, 1, silver, nil, "[" .. ratingWord .. "]" .. string.rep(" ", termX - #ratingWord - 3))
  1632.   drawElement(3, 8, 1, 1, assignColor(currentEdit), nil, ratingWord)          --# Classification
  1633.   drawElement(21, 4, 1, 1, white, nil, tostring(currentEdit) .. string.rep(" ", 5 - #tostring(currentEdit))) --# Address book entry number ('#')
  1634.   if #note > 24 then                                                          --# Note
  1635.     breakPoint = note:sub(19, 25):find(" ") + 17
  1636.   else
  1637.     breakPoint = #note
  1638.   end
  1639.   drawElement(2, 10, 1, 1, nil, nil, note:sub(1, breakPoint) .. string.rep(" ", 24 - breakPoint))
  1640.   if #note > 24 then
  1641.     drawElement(2, 11, 1, 1, nil, nil, note:sub(breakPoint + 2) .. string.rep(" ", 24 - #note:sub(breakPoint + 2)))
  1642.   else
  1643.     drawElement(2, 11, 24, 1) --# clear line
  1644.   end
  1645.   local xStr, yStr, zStr = tostring(addressBook[currentEdit].loc.x), tostring(addressBook[currentEdit].loc.y), tostring(addressBook[currentEdit].loc.z)
  1646.   drawElement(7, 13, 1, 1, brown, nil, addressBook[currentEdit].loc.dim .. string.rep(" ", 19 - #addressBook[currentEdit].loc.dim)) --# Dimension
  1647.   drawElement(5, 14, 1, 1, silver, nil, xStr .. string.rep(" ", 9 - #xStr))   --# X
  1648.   drawElement(5, 15, 1, 1, nil, nil, yStr .. string.rep(" ", 9 - #yStr))      --# Y
  1649.   drawElement(5, 16, 1, 1, nil, nil, zStr .. string.rep(" ", 9 - #zStr))      --# Z
  1650.   drawElement(15, termY - 1, 7, 1, red, gray, "Close")
  1651.   drawElement(6, termY - 1, 6, 1, gateChange and green or silver, nil, "Save")
  1652. end
  1653.  
  1654. local function drawGateLabels(id)
  1655.   runState, currentEdit = "viewing", id
  1656.   clearScreen()
  1657.   drawHeader()
  1658.   drawElement(19, 4, 1, 1, gray, black, "#")
  1659.   drawElement(2, 13, 4, 1, nil, nil, "Dim:")
  1660.   drawElement(2, 14, 2, 1, nil, nil, "x:")
  1661.   drawElement(2, 15, 2, 1, nil, nil, "y:")
  1662.   drawElement(2, 16, 2, 1, nil, nil, "z:")
  1663.   drawGateData()
  1664. end
  1665.  
  1666. drawAddressBook = function() --# Gate Address Book
  1667.   local xPos, yPos, magicNumber, hostAddr = 1, 5, ((gatePage - 1) * 14) + 1, host.addr
  1668.   local txtColor, bgColor, addr
  1669.   for i = magicNumber, math.min(abCount, magicNumber + 13) do
  1670.     addr = addressBook[i].addr
  1671.     txtColor = hostAddr == addr and silver or (dialAddress == addr and black or white)
  1672.     bgColor = hostAddr == addr and gray or assignColor(i)
  1673.     drawElement(xPos, yPos, 12, 1, txtColor, bgColor, addressBook[i].name)
  1674.     yPos = yPos + 2
  1675.     if yPos > 17 then xPos = xPos + 14 yPos = 5 end
  1676.   end
  1677. end
  1678.  
  1679. do
  1680.   local stateTable = {
  1681.     Dial = function() drawAddressBook() drawNaviUI() end;
  1682.     Sync = function() drawSyncScreen() end;
  1683.     Help = function() drawHelpScreen() end;
  1684.     goPage = function()
  1685.       drawAddressBook()
  1686.       drawNaviUI()
  1687.       drawGoPagePopUp()
  1688.     end;
  1689.   }
  1690.  
  1691.   drawCLI = function()
  1692.     drawHeader()
  1693.     stateTable[runState]()
  1694.   end
  1695. end
  1696.  
  1697. local function flashDial(gateName, x, y, gateNum)
  1698.   local gColor = assignColor(gateNum)
  1699.   drawElement(x, y, 12, 1, gColor, black, gateName)
  1700.   sleep(0.1)
  1701.   drawElement(x, y, 12, 1, dialAddress == addressBook[gateNum].addr and black or white, gColor, gateName)
  1702. end
  1703.  
  1704. local function flashChoice(x, y, w, h, txtClr, bgClr, label)
  1705.   drawElement(x, y, w, h, txtClr, bgClr, label)
  1706.   sleep(0.1)
  1707. end
  1708.  
  1709. local function gateDataChange()
  1710.   gateChange = true
  1711.   drawElement(6, termY - 1, 6, 1, green, gray, "Save")
  1712. end
  1713.  
  1714. local function addNewAddress(fast)
  1715.   if abCount == 1 and addressBook[1].name == "NO GATES" and addressBook[1].addr == "ADDRESS" then
  1716.     addressBook[1].name = "Name"
  1717.   else
  1718.     abCount = abCount + 1
  1719.     addressBook[abCount] = { name = "Name", addr = "ADDRESS", rating = "U", iris = "none", callDrop = false, note = "short note", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }
  1720.   end
  1721.   gateChange = true
  1722.   gatePages = math.ceil(abCount / 14)                   --# Re-paginate AddressBook
  1723.   if fast then                                          --# if the new gate is a quick-add....
  1724.     if gatePage == gatePages then drawAddressBook() end --# ...and we're on the last page, redraw the address list
  1725.     drawNaviUI()
  1726.   else
  1727.     drawGateLabels(abCount)
  1728.   end
  1729. end
  1730.  
  1731. local function selectHost() --# host selection during initialization
  1732.   local hostCount, event, data, x, y, magicNumber, yPos = #hosts
  1733.   while hosts[1] do
  1734.     event, data, x, y = os.pullEvent()
  1735.     if event == "mouse_click" and x > 6 and x < 21 and y > 6 and y < 16 and data == 1 then
  1736.       yPos, magicNumber = 5, ((syncPage - 1) * 5) + 1
  1737.       for i = magicNumber, math.min(hostCount, magicNumber + 4) do
  1738.         yPos = yPos + 2
  1739.         if y == yPos then
  1740.           flashChoice(7, yPos, 14, 1, black, white, hosts[i].name)
  1741.           host.id, host.addr, host.ccDHD, host.name, host.status, remoteIrisState = hosts[i].id, hosts[i].addr, hosts[i].ccDHD, hosts[i].name, hosts[i].gStatus, hosts[i].remoteIris
  1742.           pingTimer = os.startTimer(5)
  1743.           return
  1744.         end
  1745.       end
  1746.     elseif event == "mouse_scroll" and ((data == 1 and syncPage < syncPages) or (data == -1 and syncPage > 1)) then
  1747.       syncPage = syncPage + data
  1748.       drawHostsUI()
  1749.     elseif event == "key" and syncPages > 1 then
  1750.       if (data == keys.pageUp and syncPage > 1) or (data == keys.pageDown and syncPage < syncPages) then
  1751.         syncPage = data == keys.pageUp and syncPage - 1 or syncPage + 1
  1752.         drawHostsUI()
  1753.       elseif (data == keys.home and syncPage > 1) or (data == keys["end"] and syncPage < syncPages) then
  1754.         syncPage = data == keys.home and 1 or syncPages
  1755.         drawHostsUI()
  1756.       end
  1757.     end
  1758.   end
  1759. end
  1760.  
  1761. local function findHosts()
  1762.   if runState ~= "init" then
  1763.     drawElement(4, 7, 20, 1, white, blue, "Sync")             --# Sync search header
  1764.     drawElement(4, 8, 20, 3, nil, gray, "Locating hosts ...") --# Sync search body
  1765.   end
  1766.   for i = #hosts, 1, -1 do hosts[i] = nil end       --# clear hosts table
  1767.   host.id, host.name, host.addr, host.status, remoteIrisState, syncPage, syncPages = nil, "NO HOST", "NO HOST", nil, "unk", 1, 1 --# reset host and remoteIrisState
  1768.   local hostIDs = { rednet.lookup("ccDialerWiFi") } --# populate hostIDs table
  1769.   local hostCount = #hostIDs
  1770.   if hostCount > 0 then
  1771.     local id, message, encryptedMessage, decryptedMessage, encodedMessage, encKey, success, msgData, msgLen, gateMatch, hostAddr, tempState, _, button, x, y
  1772.     for i = 1, hostCount do                         --# process host list
  1773.       hosts[i] = { }
  1774.       hosts[i].id, host.id, host.addr, host.ccDHD, host.status = hostIDs[i], hostIDs[i], "NO HOST", false, nil --# set the host and reset the gate information
  1775.       netSend({ program = "ccDialer", gate = "NO HOST", command = "QRY" }) --# query the host
  1776.       id, encodedMessage = rednet.receive("ccDialerWiFi", 1)
  1777.       if id == host.id and type(encodedMessage) == "string" then
  1778.         success, encryptedMessage = pcall(decode, encodedMessage)
  1779.         if success then
  1780.           encKey = thisCC .. "ccDHD!General_Comms*Key" .. tostring(id)
  1781.           success, decryptedMessage = pcall(decrypt, encKey, encryptedMessage)
  1782.           if success then
  1783.             success, message = pcall(textutils.unserialize, decryptedMessage)
  1784.             if success and type(message) == "table" and message.program and message.program == "ccDialer" and message.data and type(message.data) == "string" and type(message.ccDHD) == "boolean" then
  1785.               msgData = message.data
  1786.               msgLen = #msgData
  1787.               if msgLen == 7 or msgLen == 9 or msgData == "lockdown" then
  1788.                 host.addr, host.ccDHD, host.status, remoteIrisState = msgData, message.ccDHD, message.gStatus, message.iris
  1789.                 if host.addr ~= "lockdown" then --and host.addr ~= "NO HOST" then
  1790.                   gateMatch, hostAddr, hosts[i].name = false, host.addr, host.addr
  1791.                   for j = 1, abCount do --# Cycle through addressBook (for an entry matching host's address)
  1792.                     if hostAddr == addressBook[j].addr then --# if a match is found...
  1793.                       hosts[i].name = addressBook[j].name   --# ...set the hostName to the name of the addressBook entry...
  1794.                       gateMatch = true                      --# ...indicate a match has been found and...
  1795.                       break                                 --# ...stop the addressBook loop and continue with the next hostAddress
  1796.                     end
  1797.                   end
  1798.                   if not gateMatch then                                         --# Found a new gate
  1799.                     clearScreen()
  1800.                     if runState ~= "init" then drawHeader() end
  1801.                     tempState = runState
  1802.                     runState = "newGate"
  1803.                     drawElement(4, 7, 19, 1, white, blue, "New Gate Detected")  --# New gate header
  1804.                     drawElement(4, 8, 19, 5, nil, gray)                         --# New gate body
  1805.                     drawElement(((termX - #hostAddr) / 2) + 1, 9, 1, 1, silver, nil, hostAddr)
  1806.                     drawElement(7, 11, 5, 1, white, green, "Add")
  1807.                     drawElement(14, 11, 6, 1, nil, orange, "Skip")
  1808.                     while true do
  1809.                       _, button, x, y = os.pullEvent("mouse_click")
  1810.                       if x > 6 and x < 12 and y == 11 and button == 1 then      --# Add Gate
  1811.                         if abCount == 1 and (addressBook[1].name == "NO GATES" or addressBook[1].name == "Name") and addressBook[1].addr == "ADDRESS" then
  1812.                           addressBook[1].name, addressBook[1].addr, addressBook[1].note = hostAddr, hostAddr, "Discovered gate"
  1813.                         else
  1814.                           abCount = abCount + 1
  1815.                           addressBook[abCount] = { name = hostAddr, addr = hostAddr, rating = "U", iris = "none", callDrop = false, note = "Discovered gate", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }
  1816.                           gatePages = math.ceil(abCount / 14)                   --# Re-paginate AddressBook
  1817.                         end
  1818.                         saveData()
  1819.                         break
  1820.                       elseif x > 13 and x < 20 and y == 11 and button == 1 then --# Skip
  1821.                         break
  1822.                       end
  1823.                     end
  1824.                     runState = tempState
  1825.                     drawElement(4, 7, 19, 6, nil, black)                        --# Clear pop-up
  1826.                   end
  1827.                 end
  1828.               end
  1829.             end
  1830.           end
  1831.         end
  1832.       end
  1833.       hosts[i].addr, hosts[i].ccDHD, hosts[i].gStatus, hosts[i].remoteIris = host.addr, host.ccDHD, host.status, remoteIrisState --# store gate address and ccDHD status
  1834.     end
  1835.     host.id, host.addr, host.status, remoteIrisState = nil, "NO HOST", nil, "unk" --# clear host info
  1836.     for i = hostCount, 1, -1 do                        --# process and sanitize host list
  1837.       if hosts[i].addr == "lockdown" or hosts[i].addr == "NO HOST" then --# if gate is in lockdown or hasn't replied...
  1838.         table.remove(hosts, i)                         --# ...remove entry from hosts table
  1839.       end
  1840.     end
  1841.     hostCount = #hosts
  1842.     syncPages = math.ceil(hostCount / 5)
  1843.   end
  1844.   if hostCount > 1 then                           --# more than 1 host responds
  1845.     if runState == "init" then
  1846.       drawElement(2, 4, termX - 1, termY - 3, nil, black) --# clear most of screen
  1847.       drawHostsUI()
  1848.       selectHost()
  1849.     else
  1850.       drawElement(4, 7, 20, 4, nil, black)        --# clear 'searching for hosts' dialogue
  1851.       drawHostsUI()
  1852.     end
  1853.   elseif hostCount == 1 then                      --# only one host responds
  1854.     host.id, host.name, host.addr, host.ccDHD, host.status, remoteIrisState = hosts[1].id, hosts[1].name, hosts[1].addr, hosts[1].ccDHD, hosts[1].gStatus, hosts[1].remoteIris --# set host ID, gate address, and ccDHD status
  1855.     if runState == "init" then
  1856.        drawElement(5, 12, 1, 1, green, gray, "O")
  1857.     else
  1858.       runState = "Dial"
  1859.       drawElement(4, 7, 20, 4, nil, black)        --# clear Sync pop-up
  1860.       drawCLI()
  1861.     end
  1862.   else                                            --# no hosts respond
  1863.     host.ccDHD, host.addr, host.name, host.id, host.status, remoteIrisState = false, "NO HOST", "NO HOST", nil, nil, "unk"
  1864.     if runState == "init" then
  1865.       drawElement(5, 12, 1, 1, red, gray, "0")
  1866.     else
  1867.       runState = "Dial"
  1868.       drawElement(4, 7, 20, 4, nil, black)        --# clear Sync pop-up
  1869.       drawCLI()
  1870.     end
  1871.   end
  1872. end
  1873.  
  1874. --# custom read function courtesy of theoriginalbit (modified by Dog)
  1875. local function read(_mask, _history, _limit, _noTerminate, _noMouse, _gField)
  1876.   if _mask and type(_mask) ~= "string" then
  1877.     error("Invalid parameter #1: Expected string, got " .. type(_mask), 2)
  1878.   end
  1879.   if _history and type(_history) ~= "table" then
  1880.     error("Invalid parameter #2: Expected table, got " .. type(_history), 2)
  1881.   end
  1882.   if _limit and type(_limit) ~= "number" then
  1883.     error("Invalid parameter #3: Expected number, got " .. type(_limit), 2)
  1884.   end
  1885.   if _noTerminate and type(_noTerminate) ~= "boolean" then
  1886.     error("Invalid argument #4: Expected boolean, got " .. nativeType(_noTerminate), 2)
  1887.   end
  1888.   if _noMouse and type(_noMouse) ~= "boolean" then
  1889.     error("Invalid argument #5: Expected boolean, got " .. nativeType(_noMouse), 2)
  1890.   end
  1891.   if _gField and type(_gField) ~= "boolean" then
  1892.     error("Invalid argument #6: Expected boolean, got " .. nativeType(_gField), 2)
  1893.   end
  1894.   term.setCursorBlink(true)
  1895.   gettingInput = true
  1896.   local symbols = { ["A"] = true, ["B"] = true, ["C"] = true, ["D"] = true, ["E"] = true , ["F"] = true, ["G"] = true, ["H"] = true, ["I"] = true, ["J"] = true, ["K"] = true, ["L"] = true, ["M"] = true, ["N"] = true, ["O"] = true, ["P"] = true, ["Q"] = true, ["R"] = true, ["S"] = true, ["T"] = true, ["U"] = true, ["V"] = true, ["W"] = true, ["X"] = true, ["Y"] = true, ["Z"] = true, ["0"] = true, ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, ["5"] = true, ["6"] = true, ["7"] = true, ["8"] = true, ["9"] = true, ["-"] = true, ["+"] = true }
  1897.   local mouseLimit = _limit or 0
  1898.   local input, lineCleared = "", false
  1899.   word = ""
  1900.   local pos = 0
  1901.   local historyPos = nil
  1902.   local pullEvent = _noTerminate and os.pullEventRaw or os.pullEvent
  1903.   local sw, sh = term.getSize()
  1904.   local sx, sy = term.getCursorPos()
  1905.   curX, curY = sx, sy
  1906.   --bgCol, txtCol = term.getBackgroundColor(), term.getTextColor()
  1907.   local function redraw(_special)
  1908.     local scroll = (sx + pos >= sw and (sx + pos) - sw or 0)
  1909.     local replace = _special or _mask
  1910.     local output = replace and (string.rep(replace, math.ceil(#input / #replace) - scroll)):sub(1, #input) or input:sub(scroll + 1)
  1911.     term.setCursorPos(sx, sy)
  1912.     term.write(output)
  1913.     term.setCursorPos(sx + pos - scroll, sy)
  1914.   end
  1915.   local nativeScroll = term.scroll
  1916.   term.scroll = function(_n) local ok, err = pcall(function() return nativeScroll(_n) end) if ok then sy = sy - _n return err end error(err, 2) end
  1917.   local function historyHandler(value)
  1918.     redraw(' ')
  1919.     if value == -1 or value == keys.up then
  1920.       if not historyPos then
  1921.         historyPos = #_history
  1922.       elseif historyPos > 1 then
  1923.         historyPos = historyPos - 1
  1924.       end
  1925.     else
  1926.       if historyPos ~= nil and historyPos < #_history then
  1927.         historyPos = historyPos + 1
  1928.       elseif historyPos == #_history then
  1929.         historyPos = nil
  1930.       end
  1931.     end
  1932.     if historyPos and #_history > 0 then
  1933.       input = string.sub(_history[historyPos], 1, _limit) or ""
  1934.       pos = #input
  1935.       word = input
  1936.       if not _limit then mouseLimit = pos end
  1937.     else
  1938.       input = ""
  1939.       pos = 0
  1940.       word = input
  1941.       if not _limit then mouseLimit = 0 end
  1942.     end
  1943.   end
  1944.   while true do
  1945.     local event, code, x, y = pullEvent()
  1946.     if event == "char" and (not _limit or #input < _limit) then
  1947.       if not lineCleared and pos == 0 then term.write(string.rep(" ", _limit)) lineCleared = true end
  1948.       local goodData = false
  1949.       if _gField then
  1950.         if symbols[code:upper()] then
  1951.           code = code:upper()
  1952.           goodData = true
  1953.         end
  1954.       else
  1955.         goodData = true
  1956.       end
  1957.       if goodData then
  1958.         input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  1959.         pos = pos + 1
  1960.         word = input
  1961.         if not _limit then mouseLimit = math.min(mouseLimit + 1, sw - (sw - sx)) end
  1962.       end
  1963.     elseif event == "paste" and (not _limit or #input < _limit) then
  1964.       if not lineCleared and pos == 0 then term.write(string.rep(" ", _limit)) lineCleared = true end
  1965.       if _limit and #input + #code > _limit then
  1966.         code = code:sub(1, _limit - #input)
  1967.       end
  1968.       if _gField then
  1969.         local newWord, glyph = { }, ""
  1970.         for i = 1, #code do
  1971.           glyph = string.upper(code:sub(i, i))
  1972.           newWord[i] = symbols[glyph] and glyph or "?"
  1973.         end
  1974.         code = table.concat(newWord)
  1975.       end
  1976.       input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  1977.       pos = pos + #code
  1978.       word = input
  1979.       if not _limit then mouseLimit = math.min(mouseLimit + #code, sw - (sw - sx)) end
  1980.     elseif event == "key" then
  1981.       if code == keys.enter or code == keys.numPadEnter then
  1982.         break
  1983.       elseif code == keys.backspace and pos > 0 then
  1984.         redraw(' ')
  1985.         input = input:sub(1, math.max(pos - 1, 0)) .. input:sub(pos + 1)
  1986.         pos = math.max(pos - 1, 0)
  1987.         word = input
  1988.         if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
  1989.       elseif code == keys.delete and pos < #input then
  1990.         redraw(' ')
  1991.         input = input:sub(1, pos)..input:sub(pos + 2)
  1992.         word = input
  1993.         if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
  1994.       elseif code == keys.home then
  1995.         pos = 0
  1996.       elseif code == keys["end"] then
  1997.         pos = #input
  1998.       elseif code == keys.left and pos > 0 then
  1999.         pos = math.max(pos - 1, 0)
  2000.       elseif code == keys.right and pos < #input then
  2001.         pos = math.min(pos + 1, #input)
  2002.       elseif _history and code == keys.up or code == keys.down then
  2003.         historyHandler(code)
  2004.       end
  2005.     elseif event == "mouse_click" and not _noMouse and ((x < sx or x >= sx + mouseLimit) or (y ~= sy)) then
  2006.       break
  2007.     elseif event == "mouse_scroll" and _history then
  2008.       historyHandler(code)
  2009.     end
  2010.     redraw(_mask)
  2011.   end
  2012.   term.scroll = nativeScroll
  2013.   term.setCursorBlink(false)
  2014.   if sy + 1 > sh then
  2015.     term.scroll(sy + 1 - sh)
  2016.     term.setCursorPos(1, sy)
  2017.   else
  2018.     term.setCursorPos(1, sy + 1)
  2019.   end
  2020.   word = ""
  2021.   gettingInput = false
  2022.   return input
  2023. end
  2024.  
  2025. local function remotePassword()
  2026.   drawElement(1, 4, termX, termY - 3, nil, black) --# clear most of screen
  2027.   drawPasswordUI()
  2028.   runState, bgCol, txtCol = "RemotePass", gray, white
  2029.   term.setBackgroundColor(gray)
  2030.   term.setTextColor(white)
  2031.   term.setCursorPos(8, 7)
  2032.   local remotePass = read("*", nil, 12)
  2033.   if remotePass ~= "" then
  2034.     local pass = table.concat(pbkdf2(remotePass, "ccDHD!Pass.Hash", 15))
  2035.     netSend({ program = "ccDialer", gate = host.addr, password = pass })
  2036.   end
  2037.   drawElement(2, 5, termX - 2, 5, nil, black) --# clear iris pop-up
  2038.   runState = "Dial"
  2039.   drawAddressBook()
  2040.   drawNaviUI()
  2041. end
  2042.  
  2043. local function saveAndQuit()
  2044.   local tX, tY = math.floor(termX / 2), math.floor(termY / 2)
  2045.   drawElement(tX - 9, tY - 2, 1, 1, white, blue, "  Save Addr Book ?  ")
  2046.   drawElement(tX - 9, tY - 1, 20, 3, nil, gray)
  2047.   drawElement(tX - 8, tY, 1, 1, nil, green, "  Save  ")
  2048.   drawElement(tX + 2, tY, 1, 1, nil, orange, "  Quit  ")
  2049.   while true do
  2050.     local _, button, x, y = os.pullEvent("mouse_click")
  2051.     if x > tX - 10 and x < tX + 11 and y > tY - 3 and y < tY + 2 then
  2052.       if x > tX - 9 and x < tX and y == tY and button == 1 then
  2053.         flashChoice(tX - 8, tY, 8, 1, green, white, "Save")
  2054.         saveData()
  2055.         return true
  2056.       elseif x > tX + 1 and x < tX + 10 and y == tY and button == 1 then
  2057.         flashChoice(tX + 2, tY, 8, 1, orange, white, "Quit")
  2058.         return true
  2059.       end
  2060.     else
  2061.       drawElement(2, 5, termX - 2, termY - 7, nil, black) --# clear save & quit pop-up
  2062.       drawAddressBook()
  2063.       return false
  2064.     end
  2065.   end
  2066. end
  2067.  
  2068. local function userInput()
  2069.   local event, data, x, y
  2070.   while true do
  2071.     event, data, x, y = os.pullEvent()
  2072.     if event == "mouse_click" then
  2073.       if y == 1 then
  2074.         if x > termX - 3 and data == 1 then               --# 'X' (exit button)
  2075.           flashChoice(termX - 1, 1, 1, 1, white, runState == "viewing" and assignColor(currentEdit) or blue, "X")
  2076.           drawElement(termX - 1, 1, 1, 1, red, nil, "X")
  2077.           if runState == "Dial" then
  2078.             if gateChange then
  2079.               if saveAndQuit() then shutDown() break end
  2080.             else
  2081.               shutDown()
  2082.               break
  2083.             end
  2084.           else
  2085.             runState = "Dial"
  2086.             clearScreen()
  2087.             drawCLI()
  2088.           end
  2089.         elseif x < 4 and runState == "Dial" and data == 1 then --# 'H' (hosts button)
  2090.           drawElement(2, 1, 1, 1, gray, blue, "H")
  2091.           drawElement(6, 1, 1, 1, nil, nil, "I")
  2092.           drawElement(2, 2, 1, 1, silver, gray, "NO HOST     ")
  2093.           drawElement(17, 2, 10, 1)
  2094.           drawElement(1, 3, termX, termY - 2, nil, black) --# clear most of screen
  2095.           if host.id then netSend({ program = "ccDialer", gate = host.addr, command = "logout" }) end
  2096.           runState, remoteIrisState = "selectHost", "unk"
  2097.           findHosts()
  2098.         elseif x > 4 and x < 8 and runState == "Dial" then --# 'I' (iris button)
  2099.           if data == 1 and host.status == "Connected" then --# host.addr ~= "NO HOST"
  2100.             remotePassword()
  2101.           elseif data == 3 then
  2102.             remoteIrisState = "unk"
  2103.             drawElement(6, 1, 1, 1, gray, blue, "I")      --# 'I' for Iris control
  2104.           end
  2105.         end
  2106.       end
  2107.       if runState == "viewing" then                       --# Gate Viewing / Editing
  2108.         if y == 4 and data == 1 then
  2109.           if x > 1 and x < 11 then                        --# Name
  2110.             drawElement(2, 4, 1, 1, gray, black, addressBook[currentEdit].name)
  2111.             bgCol, txtCol = black, cyan
  2112.             term.setTextColor(cyan)
  2113.             term.setCursorPos(2, 4)
  2114.             local newGateName = read(nil, { addressBook[currentEdit].addr, addressBook[currentEdit].name }, 12)
  2115.             if newGateName ~= addressBook[currentEdit].name and newGateName ~= "" then
  2116.               addressBook[currentEdit].name = newGateName
  2117.               gateDataChange()
  2118.             end
  2119.             drawElement(2, 4, 1, 1, cyan, black, addressBook[currentEdit].name .. string.rep(" ", 12 - #addressBook[currentEdit].name))
  2120.           elseif x > 20 and x < 21 + #tostring(currentEdit) then --# Position / reorder
  2121.             drawElement(21, 4, 1, 1, gray, black, currentEdit)
  2122.             bgCol, txtCol = black, white
  2123.             term.setTextColor(white)
  2124.             term.setCursorPos(21, 4)
  2125.             local newPos = tonumber(read(nil, nil, 5))
  2126.             if newPos and newPos > 0 and newPos <= abCount and newPos ~= currentEdit then
  2127.               local tempGateData = { }
  2128.               for k, v in pairs(addressBook[currentEdit]) do
  2129.                 tempGateData[k] = v
  2130.               end
  2131.               table.remove(addressBook, currentEdit)
  2132.               table.insert(addressBook, newPos, tempGateData)
  2133.               currentEdit = newPos
  2134.               gateDataChange()
  2135.             end
  2136.             drawElement(21, 4, 1, 1, white, black, tostring(currentEdit) .. string.rep(" ", 5 - #tostring(currentEdit)))
  2137.           end
  2138.         elseif y == 6 and x > 1 and x < 11 then           --# Address
  2139.           if data == 1 then
  2140.             drawElement(2, 6, 1, 1, gray, black, addressBook[currentEdit].addr)
  2141.             bgCol, txtCol = black, yellow
  2142.             term.setTextColor(yellow)
  2143.             term.setCursorPos(2, 6)
  2144.             local newGateAddress = read(nil, { addressBook[currentEdit].addr }, 9, nil, nil, true)
  2145.             local ngaLen, name = #newGateAddress, addressBook[currentEdit].name
  2146.             if (ngaLen == 7 or ngaLen == 9) and newGateAddress ~= addressBook[currentEdit].addr and not newGateAddress:find("?") then
  2147.               addressBook[currentEdit].addr = newGateAddress
  2148.               if name == "Name" or name == "NEW GATE" or name == "NO GATES" then
  2149.                 addressBook[currentEdit].name = newGateAddress
  2150.                 drawElement(2, 4, 1, 1, cyan, nil, addressBook[currentEdit].name .. string.rep(" ", 12 - #addressBook[currentEdit].name))
  2151.               end
  2152.               gateDataChange()
  2153.             end
  2154.             drawElement(2, 6, 1, 1, yellow, black, addressBook[currentEdit].addr .. "  ")
  2155.           elseif data == 2 and host.id and addressBook[currentEdit].addr ~= host.addr then
  2156.             netSend({ program = "ccDialer", gate = host.addr, command = addressBook[currentEdit].addr })
  2157.             runState = "Dial"
  2158.             clearScreen()
  2159.             drawCLI()
  2160.           end
  2161.         elseif y == 8 and x > 1 and x < 4 + #assignRating(currentEdit) and data == 1 then --# Classification
  2162.           drawRatingList(addressBook[currentEdit].rating)
  2163.           local selected = false
  2164.           while true do
  2165.             local _, button, mX, mY = os.pullEvent("mouse_click")
  2166.             if mX > 5 and mX < 25 and mY > 5 and mY < 14 and button == 1 then
  2167.               for k, v in pairs(classifications) do
  2168.                 if mY == v.order + 5 then
  2169.                   if addressBook[currentEdit].rating ~= k then
  2170.                     addressBook[currentEdit].rating = k
  2171.                     gateDataChange()
  2172.                   end
  2173.                   selected = true
  2174.                   break
  2175.                 end
  2176.               end
  2177.               if selected then break end
  2178.             else
  2179.               break
  2180.             end
  2181.           end
  2182.           drawElement(6, 6, 20, 11, nil, black)           --# clear menu body
  2183.           if gateChange and selected then drawHeader() end
  2184.           drawGateData()
  2185.         elseif ((y == 10 and x > 1 and x < 2 + #addressBook[currentEdit].note:sub(1, 24)) or (y == 11 and #addressBook[currentEdit].note > 24 and x > 1 and x < 2 + #addressBook[currentEdit].note:sub(25, 43))) and data == 1 then --# Note
  2186.           drawElement(2, 10, 1, 1, gray, black, addressBook[currentEdit].note:sub(1, 24))
  2187.           if #addressBook[currentEdit].note > 24 then
  2188.             drawElement(2, 11, 1, 1, nil, nil, addressBook[currentEdit].note:sub(25, 43))
  2189.           end
  2190.           bgCol, txtCol = black, white
  2191.           term.setTextColor(white)
  2192.           term.setCursorPos(2, 10)
  2193.           local newNote = read(nil, { addressBook[currentEdit].note }, 43)
  2194.           if newNote ~= "" and newNote ~= addressBook[currentEdit].note then
  2195.             addressBook[currentEdit].note = newNote
  2196.             gateDataChange()
  2197.           end
  2198.           drawElement(2, 10, 1, 1, white, black, addressBook[currentEdit].note:sub(1, 24) .. string.rep(" ", 25 - #addressBook[currentEdit].note:sub(1, 24)))
  2199.           if #addressBook[currentEdit].note > 24 then
  2200.             drawElement(2, 11, 1, 1, nil, nil, addressBook[currentEdit].note:sub(25, 43) .. string.rep(" ", 19 - #addressBook[currentEdit].note:sub(25, 43)))
  2201.           else
  2202.             drawElement(2, 11, 19, 1)                     --# clear line
  2203.           end
  2204.         elseif y == 13 and x > 6 and x < 7 + #addressBook[currentEdit].loc.dim and data == 1 then --# Dimension
  2205.           drawElement(7, 13, 1, 1, gray, black, addressBook[currentEdit].loc.dim)
  2206.           bgCol, txtCol = black, brown
  2207.           term.setTextColor(brown)
  2208.           term.setCursorPos(7, 13)
  2209.           local newDim = read(nil, { "Overworld", "Nether", "The End", addressBook[currentEdit].loc.dim }, 19)
  2210.           if newDim ~= "" and newDim ~= addressBook[currentEdit].loc.dim then
  2211.             addressBook[currentEdit].loc.dim = newDim
  2212.             gateDataChange()
  2213.           end
  2214.           drawElement(7, 13, 1, 1, brown, black, addressBook[currentEdit].loc.dim .. string.rep(" ", 19 - #addressBook[currentEdit].loc.dim))
  2215.         elseif y == 14 and data == 1 then                 --# X
  2216.           local xStr = tostring(addressBook[currentEdit].loc.x)
  2217.           if x > 4 and x < 5 + #xStr then
  2218.             drawElement(5, 14, 1, 1, gray, black, xStr)
  2219.             bgCol, txtCol = black, silver
  2220.             term.setTextColor(silver)
  2221.             term.setCursorPos(5, 14)
  2222.             local newX = tonumber(read(nil, { xStr }, 9))
  2223.             if newX and newX ~= addressBook[currentEdit].loc.x then
  2224.               addressBook[currentEdit].loc.x = newX
  2225.               gateDataChange()
  2226.             end
  2227.             drawElement(5, 14, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.x) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.x)))
  2228.           end
  2229.         elseif y == 15 and data == 1 then --# Y
  2230.           local yStr = tostring(addressBook[currentEdit].loc.y)
  2231.           if x > 4 and x < 5 + #yStr then
  2232.             drawElement(5, 15, 1, 1, gray, black, yStr)
  2233.             bgCol, txtCol = black, silver
  2234.             term.setTextColor(silver)
  2235.             term.setCursorPos(5, 15)
  2236.             local newY = tonumber(read(nil, { yStr }, 9))
  2237.             if newY and newY ~= addressBook[currentEdit].loc.y then
  2238.               addressBook[currentEdit].loc.y = newY
  2239.               gateDataChange()
  2240.             end
  2241.             drawElement(5, 15, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.y) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.y)))
  2242.           end
  2243.         elseif y == 16 and data == 1 then --# Z
  2244.           local zStr = tostring(addressBook[currentEdit].loc.z)
  2245.           if x > 4 and x < 5 + #zStr then
  2246.             drawElement(5, 16, 1, 1, gray, black, zStr)
  2247.             bgCol, txtCol = black, silver
  2248.             term.setTextColor(silver)
  2249.             term.setCursorPos(5, 16)
  2250.             local newZ = tonumber(read(nil, { zStr }, 9))
  2251.             if newZ and newZ ~= addressBook[currentEdit].loc.z then
  2252.               addressBook[currentEdit].loc.z = newZ
  2253.               gateDataChange()
  2254.             end
  2255.             drawElement(5, 16, 1, 1, silver, black, tostring(addressBook[currentEdit].loc.z) .. string.rep(" ", 9 - #tostring(addressBook[currentEdit].loc.z)))
  2256.           end
  2257.         elseif y == termY - 1 and data == 1 then          --# Save / Close
  2258.           if x > 5 and x < 12 and gateChange then
  2259.             flashChoice(6, termY - 1, 6, 1, white, green, "Save")
  2260.             drawElement(6, termY - 1, 6, 1, silver, gray, "Save")
  2261.             saveData()
  2262.           elseif x > 14 and x < 22 then
  2263.             flashChoice(15, termY - 1, 7, 1, white, red, "Close")
  2264.             runState = "Dial"
  2265.             drawElement(2, 4, termX - 1, termY - 3, nil, black) --# clear most of screen
  2266.             drawCLI()
  2267.           end
  2268.         end
  2269.       elseif runState == "Dial" then                      --# Dial (main screen)
  2270.         if y == 3 then                                    --# Sub-header buttons (Sync, + , End Call)
  2271.           if x < 12 and host.ccDHD and data == 1 then     --# Sync
  2272.             flashChoice(1, 3, 11, 1, cyan, white, " Sync")
  2273.             drawElement(1, 4, termX, termY - 2, nil, black) --# clear most of screen
  2274.             runState = "Sync"
  2275.             drawCLI()
  2276.           elseif x > 11 and x < 15 then                   --# '+' Add Gate / Quick-add Gate / Save Address Book
  2277.             if data == 1 then                             --# Add gate
  2278.               flashChoice(12, 3, 3, 1, green, white, " + ")
  2279.               addNewAddress()
  2280.             elseif data == 2 and gateChange then          --# Save Address Book
  2281.               flashChoice(12, 3, 3, 1, green, white, " + ")
  2282.               drawElement(12, 3, 3, 1, white, green, " + ")
  2283.               saveData()
  2284.             elseif data == 3 then                         --# Quick-Add gate
  2285.               flashChoice(12, 3, 3, 1, green, white, " + ")
  2286.               drawElement(12, 3, 3, 1, orange, green, " + ")
  2287.               addNewAddress(true)
  2288.             end
  2289.           elseif x > 14 and host.id and (host.status == "Dialing" or host.status == "Connected" or dialAddress ~= "none") and data == 1 then --# End Call
  2290.             flashChoice(15, 3, 12, 1, red, white, "END Call")
  2291.             drawElement(15, 3, 12, 1, white, red, "END Call")
  2292.             remoteIrisState, dialAddress = "unk", "none"
  2293.             drawElement(6, 1, 1, 1, gray, blue, "I")      --# 'I' for Iris control
  2294.             drawElement(17, 2, 9, 1, nil, gray)           --# clear local gate status area
  2295.             drawAddressBook()
  2296.             netSend({ program = "ccDialer", gate = host.addr, command = "endCall" })
  2297.           end
  2298.         elseif y > 4 and y < 18 then                      --# Dial a listed address, view it's info, or delete it
  2299.           local xPos, yPos, magicNumber = 1, 5, ((gatePage - 1) * 14) + 1
  2300.           for i = magicNumber, math.min(abCount, gatePage * 14) do
  2301.             if x >= xPos and x <= xPos + 11 and y == yPos then
  2302.               if data == 1 and host.id and host.addr ~= addressBook[i].addr and host.status ~= "Connected" then --# Dial / Pause
  2303.                 dialAddress = addressBook[i].addr
  2304.                 flashDial(addressBook[i].name, xPos, yPos, i)
  2305.                 netSend({ program = "ccDialer", gate = host.addr, command = dialAddress })
  2306.                 drawAddressBook()
  2307.               elseif data == 2 then                       --# View/Edit
  2308.                 flashDial(addressBook[i].name, xPos, yPos, i)
  2309.                 drawGateLabels(i)
  2310.               elseif data == 3 and abCount > 1 then  --# Delete entry if there is more than one entry in the address book
  2311.                 flashDial(addressBook[i].name, xPos, yPos, i)
  2312.                 if addressBook[i].addr == host.addr then host.name = host.addr drawHeader() end
  2313.                 table.remove(addressBook, i)
  2314.                 gateChange, abCount = true, abCount - 1
  2315.                 gatePages = math.ceil(abCount / 14)  --# Re-paginate AddressBook
  2316.                 gatePage = math.min(gatePage, gatePages)
  2317.                 if gatePage == gatePages then drawElement(1, 5, termX, termY - 5, nil, black) end --# Clear address book area of screen
  2318.                 drawElement(13, 3, 1, 1, orange, green, "+")
  2319.                 drawAddressBook()
  2320.                 drawNaviUI()
  2321.               end
  2322.               break
  2323.             else
  2324.               yPos = yPos + 2
  2325.               if yPos > termY - 3 then xPos = xPos + 14 yPos = 5 end
  2326.             end
  2327.           end
  2328.         elseif y == termY and gatePages > 1 and data == 1 then --# Page Navigation via click
  2329.           if (x > 2 and x < 5 and gatePage > 1) or (x > 22 and x < 25 and gatePage < gatePages) then --# Home / End
  2330.             gatePage = x < 5 and 1 or gatePages
  2331.             if gatePage == gatePages then drawElement(2, 4, termX - 1, termY - 3, nil, black) end --# clear most of screen
  2332.             drawAddressBook()
  2333.             drawNaviUI()
  2334.           elseif (x > 5 and x < 7 and gatePage > 1) or (x > 20 and x < 22 and gatePage < gatePages) then --# Page Back / Page Forward
  2335.             gatePage = x < 7 and gatePage - 1 or gatePage + 1
  2336.             if gatePage == gatePages then drawElement(2, 4, termX - 1, termY - 3, nil, black) end --# clear most of screen
  2337.             drawAddressBook()
  2338.             drawNaviUI()
  2339.           elseif x > math.floor((termX - #pNum) / 2) and x < math.floor((termX + #pNum) / 2) + 1 and gatePages > 1 then --# Page Numbers (Go To Page dialogue)
  2340.             local pNumW = #pNum
  2341.             flashChoice(((termX - pNumW) / 2) + 1, termY, pNumW, 1, black, gray, pNum)
  2342.             drawElement(((termX - pNumW) / 2) + 1, termY, pNumW, 1, gray, black, pNum)
  2343.             drawGoPagePopUp()
  2344.             runState, bgCol, txtCol = "goPage", black, white
  2345.             term.setCursorPos(math.floor(termX / 2) - 1, termY - 2)
  2346.             local newPage = tonumber(read(nil, nil, 4))                            --# user input
  2347.             drawElement((termX / 2) - 3, termY - 3, 8, 3)                          --# clear 'Page' dialogue
  2348.             gatePage = newPage or gatePage                                         --# set gate page
  2349.             gatePage = math.floor(math.max(1, math.min(gatePage, gatePages)))
  2350.             if gatePage == gatePages and gatePages > 1 then drawElement(2, 4, termX - 1, termY - 3) end --# ...clear the data area if on the last page and there is more than one page
  2351.             runState = "Dial"                                                      --# restore runState
  2352.             drawAddressBook()
  2353.             drawNaviUI()
  2354.           end
  2355.         end
  2356.       elseif runState == "Sync" then                      --# Sync
  2357.         if x > 1 and x < termX and y > 5 and y < 17 and data == 1 then
  2358.           if y > 5 and y < 9 then                         --# Import
  2359.             flashChoice(2, 6, termX - 2, 3, green, white, "GET Address Book")
  2360.             netSend({ program = "ccDialer", gate = host.addr, command = "pull" })
  2361.             runState = "Dial"
  2362.           elseif y > 9 and y < 13 then                    --# Export
  2363.             flashChoice(2, 10, termX - 2, 3, orange, white, "SEND Address Book")
  2364.             netSend({ program = "ccDialer", gate = host.addr, command = "push", data = addressBook })
  2365.             runState = "Dial"
  2366.           elseif y > 13 and y < 17 then                   --# Cancel
  2367.             flashChoice(2, 14, termX - 2, 3, red, white, "C A N C E L")
  2368.             runState = "Dial"
  2369.           end
  2370.           if runState == "Dial" then
  2371.             drawElement(4, 4, termX - 1, termY - 3, nil, black) --# clear most of screen
  2372.             drawCLI()
  2373.           end
  2374.         end
  2375.       elseif runState == "selectHost" then                --# Hosts list
  2376.         if x > 6 and x < 21 and y > 6 and y < 16 and data == 1 then
  2377.           local yPos, hostCount, magicNumber = 5, #hosts, ((syncPage - 1) * 5) + 1
  2378.           for i = magicNumber, math.min(hostCount, magicNumber + 4) do
  2379.             yPos = yPos + 2
  2380.             if y == yPos then
  2381.               flashChoice(7, yPos, 14, 1, black, white, hosts[i].name)
  2382.               runState, host.id, host.addr, host.ccDHD, host.name, host.status, remoteIrisState = "Dial", hosts[i].id, hosts[i].addr, hosts[i].ccDHD, hosts[i].name, hosts[i].gStatus, hosts[i].remoteIris
  2383.               pingTimer = os.startTimer(5)
  2384.               missedPings = 0
  2385.               drawElement(6, 5, 16, 13, nil, black)       --# Clear hosts UI
  2386.               drawCLI()
  2387.               break
  2388.             end
  2389.           end
  2390.         end
  2391.       end
  2392.     elseif event == "mouse_scroll" then
  2393.       if runState == "Dial" and ((data == 1 and gatePage < gatePages) or (data == -1 and gatePage > 1)) then --# Address book
  2394.         gatePage = gatePage + data
  2395.         if gatePage == gatePages then drawElement(1, 5, termX - 1, termY - 5, nil, black) end --# clear address book area
  2396.         drawAddressBook()
  2397.         drawNaviUI()
  2398.       elseif runState == "viewing" and ((data == 1 and currentEdit < abCount) or (data == -1 and currentEdit > 1)) then --# View/Edit address book entry
  2399.         currentEdit = currentEdit + data
  2400.         drawHeader()
  2401.         drawGateData()
  2402.       elseif runState == "selectHost" and ((data == 1 and syncPage < syncPages) or (data == -1 and syncPage > 1)) then --# Hosts list
  2403.         syncPage = syncPage + data
  2404.         drawHostsUI()
  2405.       end
  2406.     elseif event == "key" then
  2407.       if data == keys.f1 and (runState == "Dial" or runState == "Help") then
  2408.         runState = runState == "Dial" and "Help" or "Dial"
  2409.         clearScreen(runState == "Dial" and black or white)
  2410.         drawCLI()
  2411.       elseif data == keys.pageUp or data == keys.pageDown then
  2412.         if runState == "Dial" and ((data == keys.pageUp and gatePage > 1) or (data == keys.pageDown and gatePage < gatePages)) then --# Address book
  2413.           gatePage = data == keys.pageUp and gatePage - 1 or gatePage + 1
  2414.           if gatePage == gatePages then drawElement(1, 5, termX, termY - 5, nil, black) end --# clear data area of screen
  2415.           drawAddressBook()
  2416.           drawNaviUI()
  2417.         elseif runState == "selectHost" and ((data == keys.pageUp and syncPage > 1) or (data == keys.pageDown and syncPage < syncPages)) then --# Hosts list
  2418.           syncPage = data == keys.pageUp and syncPage - 1 or syncPage + 1
  2419.           drawHostsUI()
  2420.         end
  2421.       elseif (data == keys.home or data == keys["end"]) then
  2422.         if runState == "Dial" and ((data == keys.home and gatePage > 1) or (data == keys["end"] and gatePage < gatePages)) then --# Address book
  2423.           gatePage = data == keys.home and 1 or gatePages
  2424.           if gatePage == gatePages then drawElement(1, 5, termX, termY - 5, nil, black) end --# clear data area of screen
  2425.           drawAddressBook()
  2426.           drawNaviUI()
  2427.         elseif runState == "selectHost" and ((data == keys.home and syncPage > 1) or (data == keys["end"] and syncPage < syncPages)) then --# Hosts list
  2428.           syncPage = data == keys.home and 1 or syncPages
  2429.           drawHostsUI()
  2430.         end
  2431.       end
  2432.     end
  2433.   end
  2434. end
  2435.  
  2436. local function dataPoller()
  2437.   local _, timer
  2438.   while true do
  2439.     _, timer = os.pullEvent("timer")
  2440.     if timer == pingTimer then
  2441.       missedPings = missedPings + 1
  2442.       if missedPings > 2 then
  2443.         if dialAddress ~= "none" then
  2444.           remoteIrisState, dialAddress = "unk", "none"
  2445.           if runState == "Sync" then
  2446.             runState = "Dial"
  2447.             drawElement(1, 5, termX - 1, termY - 5, nil, black) --# clear address book area
  2448.           end
  2449.         end
  2450.         host = { id = nil, name = "NO HOST", addr = "NO HOST", ccDHD = false, status = nil }
  2451.         pingTimer = nil
  2452.         missedPings = 0
  2453.         if runState == "Dial" or runState == "goPage" then
  2454.           drawHeader()
  2455.           drawCLI()
  2456.           if gettingInput then repositionCursor() end
  2457.         end
  2458.       elseif host.id then
  2459.         netSend({ program = "ccDialer", gate = host.addr, command = "ping" })
  2460.         pingTimer = os.startTimer(5)
  2461.       end
  2462.     end
  2463.   end
  2464. end
  2465.  
  2466. local function startupError(id)
  2467.   clearScreen()
  2468.   if id == "colorPDA" then
  2469.     drawElement(2, 2, 1, 1, term.isColor() and red or white, black, "No advanced pocket")
  2470.     drawElement(2, 3, 1, 1, nil, nil, "computer detected!")
  2471.     drawElement(2, 5, 1, 1, nil, nil, "ccDialer REQUIRES an")
  2472.     drawElement(2, 6, 1, 1, nil, nil, "advanced pocket computer.")
  2473.   elseif id == "modem" then
  2474.     drawElement(2, 2, 1, 1, red, black, "No wireless")
  2475.     drawElement(2, 3, 1, 1, nil, nil, "modem detected!")
  2476.     drawElement(2, 5, 1, 1, nil, nil, "ccDialer REQUIRES")
  2477.     drawElement(2, 6, 1, 1, nil, nil, "a wireless modem.")
  2478.   elseif id == "resolution" then
  2479.     drawElement(2, 2, 1, 1, red, black, "Screen size")
  2480.     drawElement(2, 3, 1, 1, nil, nil, "is wrong!")
  2481.     drawElement(2, 5, 1, 1, nil, nil, "ccDialer REQUIRES")
  2482.     drawElement(2, 6, 1, 1, nil, nil, "26x19 or 26x20.")
  2483.   end
  2484.   term.setTextColor(white)
  2485.   term.setCursorPos(1, 9)
  2486. end
  2487.  
  2488. if not term.isColor() or not pocket then return startupError("colorPDA") end
  2489. if termX ~= 26 or termY < 19 or termY > 20 then return startupError("resolution") end
  2490. clearScreen()
  2491. drawElement(3, 4, 22, 1, white, blue, "ccDialer Init...")
  2492. drawElement(3, 5, 22, 9, nil, gray)
  2493. local initSteps = {
  2494.   "Check CC label";
  2495.   "Hardware Discovery";
  2496.   "Ingest gate data";
  2497.   "Locate Sync host";
  2498. }
  2499. for i = 6, 12, 2 do
  2500.   drawElement(4, i, 1, 1, red, nil, "0")
  2501.   drawElement(6, i, 1, 1, silver, nil, initSteps[(i / 2) - 2])
  2502. end
  2503. drawElement(4, 6, 1, 1, orange, nil, "0")
  2504. drawElement(6, 6, 1, 1, white, nil, "Check CC label")
  2505. if not os.getComputerLabel() then os.setComputerLabel("Pocket.cc#" .. thisCC) end
  2506. drawElement(4, 6, 1, 1, green, nil, "O")
  2507. drawElement(4, 8, 1, 1, orange, nil, "0")
  2508. drawElement(6, 8, 1, 1, white, nil, "Hardware Discovery")
  2509. if peripheral.isPresent("back") and peripheral.getType("back") == "modem" and peripheral.call("back", "isWireless") then
  2510.   rednet.open("back")
  2511.   modemSide = "back"
  2512. end
  2513. if modemSide == "none" then return startupError("modem") end
  2514. drawElement(4, 8, 1, 1, green, nil, "O")
  2515. drawElement(4, 10, 1, 1, orange, nil, "0")
  2516. drawElement(6, 10, 1, 1, white, nil, "Ingest gate data")
  2517. if not fs.exists("/data") then fs.makeDir("/data") end
  2518. if fs.exists("/data/DHDgates") then
  2519.   addressBook[1] = nil
  2520.   local gateData = fs.open("/data/DHDgates", "r")
  2521.   addressBook = textutils.unserialize(gateData.readAll())
  2522.   gateData.close()
  2523.   abCount = #addressBook
  2524.   if addressBook[1].callDrop == nil then
  2525.     for i = 1, abCount do
  2526.       addressBook[i].callDrop = false
  2527.     end
  2528.     saveData()
  2529.   end
  2530. end
  2531. gatePages = math.max(1, math.ceil(abCount / 14))
  2532. drawElement(4, 10, 1, 1, green, nil, "O")
  2533. drawElement(4, 12, 1, 1, orange, nil, "0")
  2534. drawElement(6, 12, 1, 1, white, nil, "Locate Sync host")
  2535. findHosts()
  2536. runState = "Dial"
  2537. clearScreen()
  2538. drawCLI()
  2539. parallel.waitForAny(netReceive, userInput, dataPoller)
Add Comment
Please, Sign In to add comment