osmarks

spooferwolf

Nov 11th, 2018
219
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. --
  2. --  Firewolf Server
  3. --  Made by 1lann and GravityScore
  4. --
  5.  
  6. --  Networking API
  7.     --  RC4
  8.         --    RC4
  9.         --    Implementation by AgentE382
  10.  
  11.  
  12.         local cryptWrapper = function(plaintext, salt)
  13.             local key = type(salt) == "table" and {unpack(salt)} or {string.byte(salt, 1, #salt)}
  14.             local S = {}
  15.             for i = 0, 255 do
  16.                 S[i] = i
  17.             end
  18.  
  19.             local j, keylength = 0, #key
  20.             for i = 0, 255 do
  21.                 j = (j + S[i] + key[i % keylength + 1]) % 256
  22.                 S[i], S[j] = S[j], S[i]
  23.             end
  24.  
  25.             local i = 0
  26.             j = 0
  27.             local chars, astable = type(plaintext) == "table" and {unpack(plaintext)} or {string.byte(plaintext, 1, #plaintext)}, false
  28.  
  29.             for n = 1, #chars do
  30.                 i = (i + 1) % 256
  31.                 j = (j + S[i]) % 256
  32.                 S[i], S[j] = S[j], S[i]
  33.                 chars[n] = bit.bxor(S[(S[i] + S[j]) % 256], chars[n])
  34.                 if chars[n] > 127 or chars[n] == 13 then
  35.                     astable = true
  36.                 end
  37.             end
  38.  
  39.             return astable and chars or string.char(unpack(chars))
  40.         end
  41.  
  42.  
  43.         local crypt = function(text, key)
  44.             local resp, msg = pcall(cryptWrapper, text, key)
  45.             if resp then
  46.                 return msg
  47.             else
  48.                 return nil
  49.             end
  50.         end
  51.  
  52.  
  53.     --  Base64
  54.         --
  55.         --  Base64 Encryption/Decryption
  56.         --  By KillaVanilla
  57.         --  http://www.computercraft.info/forums2/index.php?/topic/12450-killavanillas-various-apis/
  58.         --  http://pastebin.com/rCYDnCxn
  59.         --
  60.  
  61.  
  62.  
  63.         local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  64.  
  65.  
  66.         local function sixBitToBase64(input)
  67.             return string.sub(alphabet, input+1, input+1)
  68.         end
  69.  
  70.  
  71.         local function base64ToSixBit(input)
  72.             for i=1, 64 do
  73.                 if input == string.sub(alphabet, i, i) then
  74.                     return i-1
  75.                 end
  76.             end
  77.         end
  78.  
  79.  
  80.         local function octetToBase64(o1, o2, o3)
  81.             local shifted = bit.brshift(bit.band(o1, 0xFC), 2)
  82.             local i1 = sixBitToBase64(shifted)
  83.             local i2 = "A"
  84.             local i3 = "="
  85.             local i4 = "="
  86.             if o2 then
  87.                 i2 = sixBitToBase64(bit.bor( bit.blshift(bit.band(o1, 3), 4), bit.brshift(bit.band(o2, 0xF0), 4) ))
  88.                 if not o3 then
  89.                     i3 = sixBitToBase64(bit.blshift(bit.band(o2, 0x0F), 2))
  90.                 else
  91.                     i3 = sixBitToBase64(bit.bor( bit.blshift(bit.band(o2, 0x0F), 2), bit.brshift(bit.band(o3, 0xC0), 6) ))
  92.                 end
  93.             else
  94.                 i2 = sixBitToBase64(bit.blshift(bit.band(o1, 3), 4))
  95.             end
  96.             if o3 then
  97.                 i4 = sixBitToBase64(bit.band(o3, 0x3F))
  98.             end
  99.  
  100.             return i1..i2..i3..i4
  101.         end
  102.  
  103.  
  104.         local function base64ToThreeOctet(s1)
  105.             local c1 = base64ToSixBit(string.sub(s1, 1, 1))
  106.             local c2 = base64ToSixBit(string.sub(s1, 2, 2))
  107.             local c3 = 0
  108.             local c4 = 0
  109.             local o1 = 0
  110.             local o2 = 0
  111.             local o3 = 0
  112.             if string.sub(s1, 3, 3) == "=" then
  113.                 c3 = nil
  114.                 c4 = nil
  115.             elseif string.sub(s1, 4, 4) == "=" then
  116.                 c3 = base64ToSixBit(string.sub(s1, 3, 3))
  117.                 c4 = nil
  118.             else
  119.                 c3 = base64ToSixBit(string.sub(s1, 3, 3))
  120.                 c4 = base64ToSixBit(string.sub(s1, 4, 4))
  121.             end
  122.             o1 = bit.bor( bit.blshift(c1, 2), bit.brshift(bit.band( c2, 0x30 ), 4) )
  123.             if c3 then
  124.                 o2 = bit.bor( bit.blshift(bit.band(c2, 0x0F), 4), bit.brshift(bit.band( c3, 0x3C ), 2) )
  125.             else
  126.                 o2 = nil
  127.             end
  128.             if c4 then
  129.                 o3 = bit.bor( bit.blshift(bit.band(c3, 3), 6), c4 )
  130.             else
  131.                 o3 = nil
  132.             end
  133.             return o1, o2, o3
  134.         end
  135.  
  136.  
  137.         local function splitIntoBlocks(bytes)
  138.             local blockNum = 1
  139.             local blocks = {}
  140.             for i=1, #bytes, 3 do
  141.                 blocks[blockNum] = {bytes[i], bytes[i+1], bytes[i+2]}
  142.                 blockNum = blockNum+1
  143.             end
  144.             return blocks
  145.         end
  146.  
  147.  
  148.         function base64Encode(bytes)
  149.             local blocks = splitIntoBlocks(bytes)
  150.             local output = ""
  151.             for i=1, #blocks do
  152.                 output = output..octetToBase64( unpack(blocks[i]) )
  153.             end
  154.             return output
  155.         end
  156.  
  157.  
  158.         function base64Decode(str)
  159.             local bytes = {}
  160.             local blocks = {}
  161.             local blockNum = 1
  162.  
  163.             for i=1, #str, 4 do
  164.                 blocks[blockNum] = string.sub(str, i, i+3)
  165.                 blockNum = blockNum+1
  166.             end
  167.  
  168.             for i=1, #blocks do
  169.                 local o1, o2, o3 = base64ToThreeOctet(blocks[i])
  170.                 table.insert(bytes, o1)
  171.                 table.insert(bytes, o2)
  172.                 table.insert(bytes, o3)
  173.             end
  174.  
  175.             return bytes
  176.         end
  177.  
  178.  
  179.     --  SHA-256
  180.         --
  181.         --  Adaptation of the Secure Hashing Algorithm (SHA-244/256)
  182.         --  Found Here: http://lua-users.org/wiki/SecureHashAlgorithm
  183.         --
  184.         --  Using an adapted version of the bit library
  185.         --  Found Here: https://bitbucket.org/Boolsheet/bslf/src/1ee664885805/bit.lua
  186.         --
  187.  
  188.  
  189.  
  190.         local MOD = 2^32
  191.         local MODM = MOD-1
  192.  
  193.  
  194.         local function memoize(f)
  195.             local mt = {}
  196.             local t = setmetatable({}, mt)
  197.             function mt:__index(k)
  198.                 local v = f(k)
  199.                 t[k] = v
  200.                 return v
  201.             end
  202.             return t
  203.         end
  204.  
  205.  
  206.         local function make_bitop_uncached(t, m)
  207.             local function bitop(a, b)
  208.                 local res,p = 0,1
  209.                 while a ~= 0 and b ~= 0 do
  210.                     local am, bm = a % m, b % m
  211.                     res = res + t[am][bm] * p
  212.                     a = (a - am) / m
  213.                     b = (b - bm) / m
  214.                     p = p * m
  215.                 end
  216.                 res = res + (a + b) * p
  217.                 return res
  218.             end
  219.  
  220.             return bitop
  221.         end
  222.  
  223.  
  224.         local function make_bitop(t)
  225.             local op1 = make_bitop_uncached(t,2^1)
  226.             local op2 = memoize(function(a)
  227.                 return memoize(function(b)
  228.                     return op1(a, b)
  229.                 end)
  230.             end)
  231.             return make_bitop_uncached(op2, 2 ^ (t.n or 1))
  232.         end
  233.  
  234.  
  235.         local customBxor1 = make_bitop({[0] = {[0] = 0,[1] = 1}, [1] = {[0] = 1, [1] = 0}, n = 4})
  236.  
  237.         local function customBxor(a, b, c, ...)
  238.             local z = nil
  239.             if b then
  240.                 a = a % MOD
  241.                 b = b % MOD
  242.                 z = customBxor1(a, b)
  243.                 if c then
  244.                     z = customBxor(z, c, ...)
  245.                 end
  246.                 return z
  247.             elseif a then
  248.                 return a % MOD
  249.             else
  250.                 return 0
  251.             end
  252.         end
  253.  
  254.  
  255.         local function customBand(a, b, c, ...)
  256.             local z
  257.             if b then
  258.                 a = a % MOD
  259.                 b = b % MOD
  260.                 z = ((a + b) - customBxor1(a,b)) / 2
  261.                 if c then
  262.                     z = customBand(z, c, ...)
  263.                 end
  264.                 return z
  265.             elseif a then
  266.                 return a % MOD
  267.             else
  268.                 return MODM
  269.             end
  270.         end
  271.  
  272.  
  273.         local function bnot(x)
  274.             return (-1 - x) % MOD
  275.         end
  276.  
  277.  
  278.         local function rshift1(a, disp)
  279.             if disp < 0 then
  280.                 return lshift(a, -disp)
  281.             end
  282.             return math.floor(a % 2 ^ 32 / 2 ^ disp)
  283.         end
  284.  
  285.  
  286.         local function rshift(x, disp)
  287.             if disp > 31 or disp < -31 then
  288.                 return 0
  289.             end
  290.             return rshift1(x % MOD, disp)
  291.         end
  292.  
  293.  
  294.         local function lshift(a, disp)
  295.             if disp < 0 then
  296.                 return rshift(a, -disp)
  297.             end
  298.             return (a * 2 ^ disp) % 2 ^ 32
  299.         end
  300.  
  301.  
  302.         local function rrotate(x, disp)
  303.             x = x % MOD
  304.             disp = disp % 32
  305.             local low = customBand(x, 2 ^ disp - 1)
  306.             return rshift(x, disp) + lshift(low, 32 - disp)
  307.         end
  308.  
  309.  
  310.         local k = {
  311.             0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
  312.             0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  313.             0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
  314.             0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
  315.             0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
  316.             0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
  317.             0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
  318.             0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
  319.             0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
  320.             0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
  321.             0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
  322.             0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
  323.             0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
  324.             0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  325.             0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
  326.             0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
  327.         }
  328.  
  329.  
  330.         local function str2hexa(s)
  331.             return (string.gsub(s, ".", function(c)
  332.                 return string.format("%02x", string.byte(c))
  333.             end))
  334.         end
  335.  
  336.  
  337.         local function num2s(l, n)
  338.             local s = ""
  339.             for i = 1, n do
  340.                 local rem = l % 256
  341.                 s = string.char(rem) .. s
  342.                 l = (l - rem) / 256
  343.             end
  344.             return s
  345.         end
  346.  
  347.  
  348.         local function s232num(s, i)
  349.             local n = 0
  350.             for i = i, i + 3 do
  351.                 n = n*256 + string.byte(s, i)
  352.             end
  353.             return n
  354.         end
  355.  
  356.  
  357.         local function preproc(msg, len)
  358.             local extra = 64 - ((len + 9) % 64)
  359.             len = num2s(8 * len, 8)
  360.             msg = msg .. "\128" .. string.rep("\0", extra) .. len
  361.             assert(#msg % 64 == 0)
  362.             return msg
  363.         end
  364.  
  365.  
  366.         local function initH256(H)
  367.             H[1] = 0x6a09e667
  368.             H[2] = 0xbb67ae85
  369.             H[3] = 0x3c6ef372
  370.             H[4] = 0xa54ff53a
  371.             H[5] = 0x510e527f
  372.             H[6] = 0x9b05688c
  373.             H[7] = 0x1f83d9ab
  374.             H[8] = 0x5be0cd19
  375.             return H
  376.         end
  377.  
  378.  
  379.         local function digestblock(msg, i, H)
  380.             local w = {}
  381.             for j = 1, 16 do
  382.                 w[j] = s232num(msg, i + (j - 1)*4)
  383.             end
  384.             for j = 17, 64 do
  385.                 local v = w[j - 15]
  386.                 local s0 = customBxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3))
  387.                 v = w[j - 2]
  388.                 w[j] = w[j - 16] + s0 + w[j - 7] + customBxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10))
  389.             end
  390.  
  391.             local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]
  392.             for i = 1, 64 do
  393.                 local s0 = customBxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22))
  394.                 local maj = customBxor(customBand(a, b), customBand(a, c), customBand(b, c))
  395.                 local t2 = s0 + maj
  396.                 local s1 = customBxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25))
  397.                 local ch = customBxor (customBand(e, f), customBand(bnot(e), g))
  398.                 local t1 = h + s1 + ch + k[i] + w[i]
  399.                 h, g, f, e, d, c, b, a = g, f, e, d + t1, c, b, a, t1 + t2
  400.             end
  401.  
  402.             H[1] = customBand(H[1] + a)
  403.             H[2] = customBand(H[2] + b)
  404.             H[3] = customBand(H[3] + c)
  405.             H[4] = customBand(H[4] + d)
  406.             H[5] = customBand(H[5] + e)
  407.             H[6] = customBand(H[6] + f)
  408.             H[7] = customBand(H[7] + g)
  409.             H[8] = customBand(H[8] + h)
  410.         end
  411.  
  412.  
  413.         local function sha256(msg)
  414.             msg = preproc(msg, #msg)
  415.             local H = initH256({})
  416.             for i = 1, #msg, 64 do
  417.                 digestblock(msg, i, H)
  418.             end
  419.             return str2hexa(num2s(H[1], 4) .. num2s(H[2], 4) .. num2s(H[3], 4) .. num2s(H[4], 4) ..
  420.                 num2s(H[5], 4) .. num2s(H[6], 4) .. num2s(H[7], 4) .. num2s(H[8], 4))
  421.         end
  422.  
  423.  
  424.     local protocolName = "Firewolf"
  425.  
  426.  
  427.     --  Cryptography
  428.         local Cryptography = {}
  429.         Cryptography.sha = {}
  430.         Cryptography.base64 = {}
  431.         Cryptography.aes = {}
  432.  
  433.  
  434.         function Cryptography.bytesFromMessage(msg)
  435.             local bytes = {}
  436.  
  437.             for i = 1, msg:len() do
  438.                 local letter = string.byte(msg:sub(i, i))
  439.                 table.insert(bytes, letter)
  440.             end
  441.  
  442.             return bytes
  443.         end
  444.  
  445.  
  446.         function Cryptography.messageFromBytes(bytes)
  447.             local msg = ""
  448.  
  449.             for i = 1, #bytes do
  450.                 local letter = string.char(bytes[i])
  451.                 msg = msg .. letter
  452.             end
  453.  
  454.             return msg
  455.         end
  456.  
  457.  
  458.         function Cryptography.bytesFromKey(key)
  459.             local bytes = {}
  460.  
  461.             for i = 1, key:len() / 2 do
  462.                 local group = key:sub((i - 1) * 2 + 1, (i - 1) * 2 + 1)
  463.                 local num = tonumber(group, 16)
  464.                 table.insert(bytes, num)
  465.             end
  466.  
  467.             return bytes
  468.         end
  469.  
  470.  
  471.         function Cryptography.sha.sha256(msg)
  472.             return sha256(msg)
  473.         end
  474.  
  475.  
  476.         function Cryptography.aes.encrypt(msg, key)
  477.             return base64Encode(crypt(msg, key))
  478.         end
  479.  
  480.  
  481.         function Cryptography.aes.decrypt(msg, key)
  482.             return crypt(base64Decode(msg), key)
  483.         end
  484.  
  485.  
  486.         function Cryptography.base64.encode(msg)
  487.             return base64Encode(Cryptography.bytesFromMessage(msg))
  488.         end
  489.  
  490.  
  491.         function Cryptography.base64.decode(msg)
  492.             return Cryptography.messageFromBytes(base64Decode(msg))
  493.         end
  494.  
  495.         function Cryptography.channel(text)
  496.             local hashed = Cryptography.sha.sha256(text)
  497.  
  498.             local total = 0
  499.  
  500.             for i = 1, hashed:len() do
  501.                 total = total + string.byte(hashed:sub(i, i))
  502.             end
  503.  
  504.             return (total % 55530) + 10000
  505.         end
  506.  
  507.         function Cryptography.sanatize(text)
  508.             local sanatizeChars = {"%", "(", ")", "[", "]", ".", "+", "-", "*", "?", "^", "$"}
  509.  
  510.             for _, char in pairs(sanatizeChars) do
  511.                 text = text:gsub("%"..char, "%%%"..char)
  512.             end
  513.             return text
  514.         end
  515.  
  516.  
  517.     --  Modem
  518.         local Modem = {}
  519.  
  520.         Modem.modems = {}
  521.  
  522.         function Modem.exists()
  523.             Modem.exists = false
  524.             for _, side in pairs(rs.getSides()) do
  525.                 if peripheral.isPresent(side) and peripheral.getType(side) == "modem" then
  526.                     Modem.exists = true
  527.  
  528.                     if not Modem.modems[side] then
  529.                         Modem.modems[side] = peripheral.wrap(side)
  530.                     end
  531.                 end
  532.             end
  533.  
  534.             return Modem.exists
  535.         end
  536.  
  537.  
  538.         function Modem.open(channel)
  539.             if not Modem.exists then
  540.                 return false
  541.             end
  542.  
  543.             for side, modem in pairs(Modem.modems) do
  544.                 modem.open(channel)
  545.                 rednet.open(side)
  546.             end
  547.  
  548.             return true
  549.         end
  550.  
  551.  
  552.         function Modem.close(channel)
  553.             if not Modem.exists then
  554.                 return false
  555.             end
  556.  
  557.             for side, modem in pairs(Modem.modems) do
  558.                 modem.close(channel)
  559.             end
  560.  
  561.             return true
  562.         end
  563.  
  564.  
  565.         function Modem.closeAll()
  566.                 if not Modem.exists then
  567.                     return false
  568.                 end
  569.  
  570.                 for side, modem in pairs(Modem.modems) do
  571.                     modem.closeAll()
  572.                 end
  573.  
  574.                 return true
  575.         end
  576.  
  577.  
  578.         function Modem.isOpen(channel)
  579.             if not Modem.exists then
  580.                 return false
  581.             end
  582.  
  583.             local isOpen = false
  584.             for side, modem in pairs(Modem.modems) do
  585.                 if modem.isOpen(channel) then
  586.                     isOpen = true
  587.                     break
  588.                 end
  589.             end
  590.  
  591.             return isOpen
  592.         end
  593.  
  594.  
  595.         function Modem.transmit(channel, msg)
  596.             if not Modem.exists then
  597.                 return false
  598.             end
  599.  
  600.             if not Modem.isOpen(channel) then
  601.                 Modem.open(channel)
  602.             end
  603.  
  604.             for side, modem in pairs(Modem.modems) do
  605.                 modem.transmit(channel, channel, msg)
  606.             end
  607.  
  608.             return true
  609.         end
  610.  
  611.  
  612.     --  Handshake
  613.         local Handshake = {}
  614.  
  615.         Handshake.prime = 625210769
  616.         Handshake.channel = 54569
  617.         Handshake.base = -1
  618.         Handshake.secret = -1
  619.         Handshake.sharedSecret = -1
  620.         Handshake.packetHeader = "["..protocolName.."-Handshake-Packet-Header]"
  621.         Handshake.packetMatch = "%["..protocolName.."%-Handshake%-Packet%-Header%](.+)"
  622.  
  623.         function Handshake.exponentWithModulo(base, exponent, modulo)
  624.             local remainder = base
  625.  
  626.             for i = 1, exponent-1 do
  627.                 remainder = remainder * remainder
  628.                 if remainder >= modulo then
  629.                     remainder = remainder % modulo
  630.                 end
  631.             end
  632.  
  633.             return remainder
  634.         end
  635.  
  636.  
  637.         function Handshake.clear()
  638.             Handshake.base = -1
  639.             Handshake.secret = -1
  640.             Handshake.sharedSecret = -1
  641.         end
  642.  
  643.         function Handshake.generateInitiatorData()
  644.             Handshake.base = math.random(10,99999)
  645.             Handshake.secret = math.random(10,99999)
  646.             return {
  647.                 type = "initiate",
  648.                 prime = Handshake.prime,
  649.                 base = Handshake.base,
  650.                 moddedSecret = Handshake.exponentWithModulo(Handshake.base, Handshake.secret, Handshake.prime)
  651.             }
  652.         end
  653.  
  654.         function Handshake.generateResponseData(initiatorData)
  655.             local isPrimeANumber = type(initiatorData.prime) == "number"
  656.             local isPrimeMatching = initiatorData.prime == Handshake.prime
  657.             local isBaseANumber = type(initiatorData.base) == "number"
  658.             local isInitiator = initiatorData.type == "initiate"
  659.             local isModdedSecretANumber = type(initiatorData.moddedSecret) == "number"
  660.             local areAllNumbersNumbers = isPrimeANumber and isBaseANumber and isModdedSecretANumber
  661.  
  662.             if areAllNumbersNumbers and isPrimeMatching then
  663.                 if isInitiator then
  664.                     Handshake.base = initiatorData.base
  665.                     Handshake.secret = math.random(10,99999)
  666.                     Handshake.sharedSecret = Handshake.exponentWithModulo(initiatorData.moddedSecret, Handshake.secret, Handshake.prime)
  667.                     return {
  668.                         type = "response",
  669.                         prime = Handshake.prime,
  670.                         base = Handshake.base,
  671.                         moddedSecret = Handshake.exponentWithModulo(Handshake.base, Handshake.secret, Handshake.prime)
  672.                     }, Handshake.sharedSecret
  673.                 elseif initiatorData.type == "response" and Handshake.base > 0 and Handshake.secret > 0 then
  674.                     Handshake.sharedSecret = Handshake.exponentWithModulo(initiatorData.moddedSecret, Handshake.secret, Handshake.prime)
  675.                     return Handshake.sharedSecret
  676.                 else
  677.                     return false
  678.                 end
  679.             else
  680.                 return false
  681.             end
  682.         end
  683.  
  684.     --  Secure Connection
  685.         local SecureConnection = {}
  686.         SecureConnection.__index = SecureConnection
  687.  
  688.  
  689.         SecureConnection.packetHeaderA = "["..protocolName.."-"
  690.         SecureConnection.packetHeaderB = "-SecureConnection-Packet-Header]"
  691.         SecureConnection.packetMatchA = "%["..protocolName.."%-"
  692.         SecureConnection.packetMatchB = "%-SecureConnection%-Packet%-Header%](.+)"
  693.         SecureConnection.connectionTimeout = 0.1
  694.         SecureConnection.successPacketTimeout = 0.1
  695.  
  696.  
  697.         function SecureConnection.new(secret, key, identifier, distance, isRednet)
  698.             local self = setmetatable({}, SecureConnection)
  699.             self:setup(secret, key, identifier, distance, isRednet)
  700.             return self
  701.         end
  702.  
  703.  
  704.         function SecureConnection:setup(secret, key, identifier, distance, isRednet)
  705.             local rawSecret
  706.  
  707.             if isRednet then
  708.                 self.isRednet = true
  709.                 self.distance = -1
  710.                 self.rednet_id = distance
  711.                 rawSecret = protocolName .. "|" .. tostring(secret) .. "|" .. tostring(identifier) ..
  712.                 "|" .. tostring(key) .. "|rednet"
  713.             else
  714.                 self.isRednet = false
  715.                 self.distance = distance
  716.                 rawSecret = protocolName .. "|" .. tostring(secret) .. "|" .. tostring(identifier) ..
  717.                 "|" .. tostring(key) .. "|" .. tostring(distance)
  718.             end
  719.  
  720.             self.identifier = identifier
  721.             self.packetMatch = SecureConnection.packetMatchA .. Cryptography.sanatize(identifier) .. SecureConnection.packetMatchB
  722.             self.packetHeader = SecureConnection.packetHeaderA .. identifier .. SecureConnection.packetHeaderB
  723.             self.secret = Cryptography.sha.sha256(rawSecret)
  724.             self.channel = Cryptography.channel(self.secret)
  725.  
  726.             if not self.isRednet then
  727.                 Modem.open(self.channel)
  728.             end
  729.         end
  730.  
  731.  
  732.         function SecureConnection:verifyHeader(msg)
  733.             if type(msg) ~= "string" then return false end
  734.        
  735.             if msg:match(self.packetMatch) then
  736.                 return true
  737.             else
  738.                 return false
  739.             end
  740.         end
  741.  
  742.  
  743.         function SecureConnection:sendMessage(msg, rednetProtocol)
  744.             local rawEncryptedMsg = Cryptography.aes.encrypt(self.packetHeader .. msg, self.secret)
  745.             local encryptedMsg = self.packetHeader .. rawEncryptedMsg
  746.  
  747.             if self.isRednet then
  748.                 rednet.send(self.rednet_id, encryptedMsg, rednetProtocol)
  749.                 return true
  750.             else
  751.                 return Modem.transmit(self.channel, encryptedMsg)
  752.             end
  753.         end
  754.  
  755.  
  756.         function SecureConnection:decryptMessage(msg)
  757.             if self:verifyHeader(msg) then
  758.                 local encrypted = msg:match(self.packetMatch)
  759.  
  760.                 local unencryptedMsg = nil
  761.                 pcall(function() unencryptedMsg = Cryptography.aes.decrypt(encrypted, self.secret) end)
  762.                 if not unencryptedMsg then
  763.                     return false, "Could not decrypt"
  764.                 end
  765.  
  766.                 if self:verifyHeader(unencryptedMsg) then
  767.                     return true, unencryptedMsg:match(self.packetMatch)
  768.                 else
  769.                     return false, "Could not verify"
  770.                 end
  771.             else
  772.                 return false, "Could not stage 1 verify"
  773.             end
  774.         end
  775. -- END OF NETWORKING
  776.  
  777. dnsListenChannel = 9999
  778. dnsResponseChannel = 9998
  779.  
  780. local config = {}
  781. config.visibleLoggingLevel = 1
  782. config.writtenLoggingLevel = 1
  783. config.loggingLocation = "/fwserver-logs"
  784. config.enableLogging = false
  785. config.password = null
  786. config.allowRednetConnections = true
  787. config.repeatRednetMessages = false
  788. config.lastDomain = nil
  789. --config.actAsGPS = false
  790. --config.gpsLocation = {}
  791.  
  792. local configLocation = "/.fwserver-config"
  793. local serverLocation = "/fw_servers"
  794. local serverAPILocation = "server_api"
  795. local locked = false
  796. local domain = ...
  797. local responseStack = {}
  798. local repeatStack = {}
  799. local terminalLog = {}
  800. local repeatedMessages = {}
  801. local lastLogNum = 0
  802. local servedRequests = 0
  803. local serverChannel = 0
  804. local requestMatch = ""
  805. local maliciousMatch = ""
  806. local responseHeader = ""
  807. local pageRequestMatch = ""
  808. local pageResposneHeader = ""
  809. local renableRednet = nil
  810. local handles = {}
  811. local customCoroutines = {}
  812. local globalHandler = nil
  813. local closeMatch = ""
  814. local connections = {}
  815. local updateURL = "https://raw.githubusercontent.com/1lann/Firewolf/master/src/server.lua"
  816.  
  817. local version = "3.5.2"
  818.  
  819. local header = {}
  820. header.dnsPacket = "[Firewolf-DNS-Packet]"
  821. header.dnsHeader = "[Firewolf-DNS-Response]"
  822. header.dnsHeaderMatch = "^%[Firewolf%-DNS%-Response%](.+)$"
  823. header.rednetHeader = "[Firewolf-Rednet-Channel-Simulation]"
  824. header.rednetMatch = "^%[Firewolf%-Rednet%-Channel%-Simulation%](%d+)$"
  825. header.requestMatchA = "^%[Firewolf%-"
  826. header.requestMatchB = "%-Handshake%-Request%](.+)$"
  827. header.maliciousMatchA = "^%[Firewolf%-"
  828. header.maliciousMatchB = "%-.+%-Handshake%-Response%](.+)$"
  829. header.responseHeaderA = "[Firewolf-"
  830. header.responseHeaderB = "-"
  831. header.responseHeaderC = "-Handshake-Response]"
  832. header.pageRequestMatchA = "^%[Firewolf%-"
  833. header.pageRequestMatchB = "%-Page%-Request%](.+)$"
  834. header.pageResponseHeaderA = "[Firewolf-"
  835. header.pageResponseHeaderB = "-Page-Response][HEADER]"
  836. header.pageResponseHeaderC = "[BODY]"
  837. header.closeMatchA = "[Firewolf-"
  838. header.closeMatchB = "-Connection-Close]"
  839.  
  840. local resetServerEvent = "firewolf-server-reset-event"
  841.  
  842. local theme = {}
  843. theme.error = colors.red
  844. theme.background = colors.gray
  845. theme.text = colors.white
  846. theme.notice = colors.white
  847. theme.barColor = colors.red
  848. theme.barText = colors.white
  849. theme.inputColor = colors.white
  850. theme.clock = colors.white
  851. theme.lock = colors.orange
  852. theme.userResponse = colors.orange
  853.  
  854. if not term.isColor() then
  855.     theme.error = colors.white
  856.     theme.background = colors.black
  857.     theme.text = colors.white
  858.     theme.notice = colors.white
  859.     theme.barColor = colors.white
  860.     theme.barText = colors.black
  861.     theme.inputColor = colors.white
  862.     theme.clock = colors.white
  863.     theme.lock = colors.white
  864.     theme.userResponse = colors.white
  865. end
  866.  
  867. local w, h = term.getSize()
  868.  
  869. --  Utilities
  870.  
  871. local makeDirectory = function(path)
  872.     fs.makeDir(path)
  873.     local function createIndex(path)
  874.         if not (fs.exists(path.."/index") or fs.exists(path.."/index.fwml"))  then
  875.             f = io.open(path.."/index", "w")
  876.             f:write("print('')\ncenter('Welcome to "..domain.."!')")
  877.             f:close()
  878.         end
  879.     end
  880.     pcall(function() createIndex(path) end)
  881. end
  882.  
  883. local checkDomain = function(domain)
  884.     if domain:find("/") or domain:find(":") or domain:find("%?") then
  885.         return "symbols"
  886.     end
  887.     if #domain < 4 then
  888.         return "short"
  889.     else
  890.         Modem.open(dnsListenChannel)
  891.         Modem.open(dnsResponseChannel)
  892.         Modem.transmit(dnsListenChannel, header.dnsPacket)
  893.         Modem.close(dnsListenChannel)
  894.  
  895.         rednet.broadcast(header.dnsPacket, header.rednetHeader .. dnsListenChannel)
  896.         local timer = os.startTimer(2)
  897.         while true do
  898.             local event, id, channel, protocol, message, dist = os.pullEventRaw()
  899.             if event == "modem_message" and channel == dnsResponseChannel and type(message) == string and message:match(header.dnsHeaderMatch) == domain then
  900.                 return "taken"
  901.             elseif event == "rednet_message" and protocol and tonumber(protocol:match(header.rednetMatch)) == dnsResponseChannel and channel:match(header.dnsHeaderMatch) == domain then
  902.                 return "taken"
  903.             elseif event == "timer" and id == timer then
  904.                 break
  905.             end
  906.         end
  907.     end
  908.     return "ok"
  909. end
  910.  
  911. local checkConfig = function()
  912.     local errorReport = {}
  913.     if not config then
  914.         table.insert(errorReport, "Corrupted configuration file!")
  915.         return errorReport
  916.     end
  917.     if config.enableLogging then
  918.         if fs.isReadOnly(config.loggingLocation) or fs.isDir(config.loggingLocation) then
  919.             table.insert(errorReport, "Invalid logging location!")
  920.         end
  921.     end
  922.  
  923.     if #errorReport > 0 then
  924.         return errorReport
  925.     else
  926.         return false
  927.     end
  928. end
  929.  
  930. local loadConfig = function()
  931.     if fs.exists(configLocation) and not fs.isDir(configLocation) then
  932.         local f = io.open(configLocation, "r")
  933.         config = textutils.unserialize(f:read("*a"))
  934.         if config then
  935.             if type(config.actAsRednetRepeater) == "boolean" then
  936.                 config.actAsRednetRepeater = nil
  937.             end
  938.         end
  939.         f:close()
  940.     else
  941.         config = nil
  942.     end
  943. end
  944.  
  945. local saveConfig = function()
  946.     local f = io.open(configLocation, "w")
  947.     f:write(textutils.serialize(config))
  948.     f:close()
  949. end
  950.  
  951. local center = function(text)
  952.     local x, y = term.getCursorPos()
  953.     term.setCursorPos(math.floor(w / 2 - text:len() / 2) + (text:len() % 2 == 0 and 1 or 0), y)
  954.     term.write(text)
  955.     term.setCursorPos(1, y + 1)
  956. end
  957.  
  958. local writeLog = function(text, color, level)
  959.     if not level then level = 0 end
  960.     if not color then color = theme.text end
  961.     if level >= config.visibleLoggingLevel then
  962.         local time = textutils.formatTime(os.time(), true)
  963.         if #time <= 4 then
  964.             time = "0"..time
  965.         end
  966.         table.insert(terminalLog, {text, color, "["..time.."] "})
  967.     end
  968.  
  969.     if config.enableLogging and level >= config.writtenLoggingLevel then
  970.         if fs.isDir(config.loggingLocation) then
  971.             fs.delete(config.loggingLocation)
  972.         end
  973.         if not fs.exists(config.loggingLocation) then
  974.             local f = io.open(config.loggingLocation, "w")
  975.             f:write("\n")
  976.             f:close()
  977.         end
  978.         local time = textutils.formatTime(os.time(), true)
  979.         if #time <= 4 then
  980.             time = "0"..time
  981.         end
  982.         local f = io.open(config.loggingLocation, "a")
  983.         f:write("["..time.."] "..text.."\n")
  984.         f:close()
  985.     end
  986. end
  987.  
  988. local writeError = function(text)
  989.     for i = 1, math.ceil(#text / (w - 8)) do
  990.         writeLog(text:sub((i - 1) * (w - 8), i * (w - 8)), theme.error, math.huge)
  991.     end
  992. end
  993.  
  994. --  Message handling
  995.  
  996. local receiveDaemon = function()
  997.     while true do
  998.         local event, id, channel, protocol, message, dist = os.pullEventRaw()
  999.         if event == "modem_message" then
  1000.             if channel == rednet.CHANNEL_REPEAT and config.repeatRednetMessages and type(message) == "table" and
  1001.                 message.sProtocol and message.sProtocol:match(header.rednetMatch) then
  1002.                 table.insert(repeatStack, {message = message, reply = protocol})
  1003.             elseif channel ~= rednet.CHANNEL_BROADCAST then
  1004.                 table.insert(responseStack, {type = "direct", channel = channel, message = message, dist = dist, reply = protocol})
  1005.             end
  1006.         elseif event == "rednet_message" and protocol and protocol:match(header.rednetMatch) and config.allowRednetConnections then
  1007.             table.insert(responseStack, {type = "rednet", channel = tonumber(protocol:match(header.rednetMatch)), rednet_id = id, message = channel, dist = null})
  1008.         elseif event == "timer" and id == renableRednet then
  1009.             Modem.open(rednet.CHANNEL_REPEAT)
  1010.         end
  1011.     end
  1012. end
  1013.  
  1014. local getActiveConnection = function(channel, distance)
  1015.     for k, connection in pairs(connections) do
  1016.         if connection.channel == channel then
  1017.             if (not distance) then
  1018.                 if connection.isRednet then
  1019.                     return connection
  1020.                 else
  1021.                     return false
  1022.                 end
  1023.             else
  1024.                 if connection.distance == distance then
  1025.                     return connection
  1026.                 else
  1027.                     return false
  1028.                 end
  1029.             end
  1030.         end
  1031.     end
  1032. end
  1033.  
  1034. local urlEncode = function(url)
  1035.     local result = url
  1036.  
  1037.     result = result:gsub("%%", "%%a")
  1038.     result = result:gsub(":", "%%c")
  1039.     result = result:gsub("/", "%%s")
  1040.     result = result:gsub("\n", "%%n")
  1041.     result = result:gsub(" ", "%%w")
  1042.     result = result:gsub("&", "%%m")
  1043.     result = result:gsub("%?", "%%q")
  1044.     result = result:gsub("=", "%%e")
  1045.     result = result:gsub("%.", "%%d")
  1046.  
  1047.     return result
  1048. end
  1049.  
  1050. local urlDecode = function(url)
  1051.     local result = url
  1052.  
  1053.     result = result:gsub("%%c", ":")
  1054.     result = result:gsub("%%s", "/")
  1055.     result = result:gsub("%%n", "\n")
  1056.     result = result:gsub("%%w", " ")
  1057.     result = result:gsub("%%&", "&")
  1058.     result = result:gsub("%%q", "%?")
  1059.     result = result:gsub("%%e", "=")
  1060.     result = result:gsub("%%d", "%.")
  1061.     result = result:gsub("%%m", "%%")
  1062.  
  1063.     return result
  1064. end
  1065.  
  1066. local getURLVars = function(url)
  1067.     local vars = {}
  1068.     if url then
  1069.         local firstVarIndex, firstVarVal = url:match("%?([^=]+)=([^&]+)")
  1070.         if not firstVarIndex then
  1071.             return
  1072.         else
  1073.             vars[urlDecode(firstVarIndex)] = urlDecode(firstVarVal)
  1074.             for index, val in url:gmatch("&([^=]+)=([^&]+)") do
  1075.                 vars[urlDecode(index)] = urlDecode(val)
  1076.             end
  1077.             return vars
  1078.         end
  1079.     else
  1080.         return
  1081.     end
  1082. end
  1083.  
  1084. local fetchPage = function(page)
  1085.     local pageRequest = fs.combine("", page:match("^[^%?]+"))
  1086.     local varRequest = page:match("%?[^%?]+$")
  1087.  
  1088.     if (pageRequest:match("(.+)%.fwml$")) then
  1089.         pageRequest = pageRequest:match("(.+)%.fwml$")
  1090.     end
  1091.  
  1092.     pageRequest = pageRequest:gsub("%.%.", "")
  1093.  
  1094.     local handleResponse, respHeader
  1095.  
  1096.     if globalHandler then
  1097.         err, msg = pcall(function() handleResponse, respHeader = globalHandler(page, getURLVars(varRequest)) end)
  1098.         if not err then
  1099.             writeLog("Error when executing server API function", theme.error, math.huge)
  1100.             writeError("/: " .. tostring(msg))
  1101.         end
  1102.     end
  1103.  
  1104.     if handleResponse then
  1105.         if not respHeader then
  1106.             respHeader = "lua"
  1107.         end
  1108.         return handleResponse, respHeader, true
  1109.     end
  1110.  
  1111.     if pageRequest == serverAPILocation then
  1112.         -- Forbid accessing server api files
  1113.         return nil
  1114.     end
  1115.  
  1116.     for k,v in pairs(handles) do
  1117.         local startSearch, endSearch = pageRequest:find(k)
  1118.         if startSearch == 1 and ((endSearch == #pageRequest) or (pageRequest:sub(endSearch + 1, endSearch + 1) == "/")) then
  1119.             err, msg = pcall(function() handleResponse, respHeader = v(page, getURLVars(varRequest)) end)
  1120.             if not err then
  1121.                 writeLog("Error when executing server API function", theme.error, math.huge)
  1122.                 writeError(k .. ": " .. tostring(msg))
  1123.             end
  1124.         end
  1125.     end
  1126.  
  1127.     if handleResponse then
  1128.         if not respHeader then
  1129.             respHeader = "lua"
  1130.         end
  1131.         return handleResponse, respHeader, true
  1132.     end
  1133.  
  1134.     local path = serverLocation .. "/" .. pageRequest
  1135.     if fs.exists(path) and not fs.isDir(path) then
  1136.         local f = io.open(path, "r")
  1137.         local contents = f:read("*a")
  1138.         f:close()
  1139.  
  1140.         return contents, "lua", false
  1141.     else
  1142.         if fs.exists(path..".fwml") and not fs.isDir(path..".fwml") then
  1143.             local f = io.open(path..".fwml", "r")
  1144.             local contents = f:read("*a")
  1145.             f:close()
  1146.  
  1147.             return contents, "fwml", false
  1148.         end
  1149.     end
  1150.  
  1151.     return nil
  1152. end
  1153.  
  1154. local sendPage = function(connection, body, head)
  1155.     if head == "fwml" then
  1156.         connection:sendMessage(pageResposneHeader..'{["language"]="Firewolf Markup"}'..header.pageResponseHeaderC..body, header.rednetHeader .. connection.channel)
  1157.     else
  1158.         connection:sendMessage(pageResposneHeader..'{["language"]="Lua"}'..header.pageResponseHeaderC..body, header.rednetHeader .. connection.channel)
  1159.     end
  1160. end
  1161.  
  1162. local defaultNotFound = [[
  1163. [br]
  1164. [br]
  1165. [=]
  1166. The page you requested could not be found!
  1167. [br]
  1168. Make sure you typed the URL correctly.
  1169. ]]
  1170.  
  1171. local handlePageRequest = function(handler, index)
  1172.     local connection = getActiveConnection(handler.channel, handler.dist)
  1173.     if connection:verifyHeader(handler.message) then
  1174.         local resp, data = connection:decryptMessage(handler.message)
  1175.         if not resp then
  1176.             -- Decryption Error
  1177.             writeLog("Decryption error!", theme.notice, 0)
  1178.         else
  1179.             -- Process Request
  1180.             if data:match(pageRequestMatch) then
  1181.                 local page = data:match(pageRequestMatch)
  1182.                 if page == "" or page == "/" then
  1183.                     page = "index"
  1184.                 end
  1185.                 local body, head, isAPI = fetchPage(page)
  1186.                 if body then
  1187.                     sendPage(connection, tostring(body), head)
  1188.                     if isAPI then
  1189.                         writeLog("API request: "..page:sub(1,25), theme.text, 0)
  1190.                     else
  1191.                         writeLog("Successful request: "..page:sub(1,20), theme.text, 1)
  1192.                     end
  1193.                 else
  1194.                     body, head, isAPI = fetchPage("not-found")
  1195.                     if body then
  1196.                         sendPage(connection, body, head)
  1197.                     else
  1198.                         sendPage(connection, defaultNotFound, "fwml")
  1199.                     end
  1200.                     writeLog("Unsuccessful request: "..page:sub(1,20), theme.text, 1)
  1201.                 end
  1202.             elseif data == closeMatch then
  1203.                 writeLog("Secure connection closed", theme.text, 0)
  1204.                 Modem.close(connection.channel)
  1205.                 table.remove(connections, index)
  1206.             end
  1207.         end
  1208.     end
  1209. end
  1210.  
  1211. local handleHandshakeRequest = function(handler)
  1212.     local requestData = handler.message:match(requestMatch)
  1213.     if requestData and type(textutils.unserialize(requestData)) == "table" then
  1214.         local receivedHandshake = textutils.unserialize(requestData)
  1215.         local data, key = Handshake.generateResponseData(receivedHandshake)
  1216.         if type(data) == "table" then
  1217.             local connection
  1218.  
  1219.             if handler.type == "direct" then
  1220.                 connection = SecureConnection.new(key, domain, domain, handler.dist)
  1221.  
  1222.                 Modem.transmit(serverChannel, responseHeader .. tostring(handler.dist) .. header.responseHeaderC .. textutils.serialize(data))
  1223.             else
  1224.                 connection = SecureConnection.new(key, domain, domain, handler.rednet_id, true)
  1225.  
  1226.                 rednet.send(handler.rednet_id, responseHeader .. tostring(handler.rednet_id) .. header.responseHeaderC .. textutils.serialize(data), header.rednetHeader .. serverChannel)
  1227.             end
  1228.  
  1229.             writeLog("Secure connection opened", theme.text, 0)
  1230.  
  1231.             table.insert(connections, connection)
  1232.  
  1233.             if #connections >= 200 then
  1234.                 Modem.close(connections[1].channel)
  1235.                 table.remove(connections, 1)
  1236.             end
  1237.         end
  1238.     elseif handler.message:match(maliciousMatch) then
  1239.         -- Hijacking Detected
  1240.         writeLog("Warning: Connection Hijacking Detected", theme.error, 2)
  1241.     end
  1242. end
  1243.  
  1244. local responseDaemon = function()
  1245.     while true do
  1246.         os.pullEventRaw()
  1247.  
  1248.         for k, v in pairs(responseStack) do
  1249.             if v.channel then
  1250.                 if v.channel == dnsListenChannel and v.message == header.dnsPacket then
  1251.                     -- DNS Request
  1252.                     if v.type == "rednet" then
  1253.                         rednet.send(v.rednet_id, header.dnsHeader .. domain, header.rednetHeader .. dnsResponseChannel)
  1254.                     else
  1255.                         Modem.open(dnsResponseChannel)
  1256.                         Modem.transmit(dnsResponseChannel, header.dnsHeader .. domain)
  1257.                         Modem.close(dnsResponseChannel)
  1258.                     end
  1259.                 elseif v.channel == serverChannel and v.message then
  1260.                     handleHandshakeRequest(v)
  1261.                 elseif getActiveConnection(v.channel, v.dist) then
  1262.                     handlePageRequest(v, k)
  1263.                 end
  1264.             end
  1265.         end
  1266.  
  1267.         responseStack = {}
  1268.  
  1269.         if #repeatStack > 10 then
  1270.             Modem.close(rednet.CHANNEL_REPEAT)
  1271.             renableRednet = os.startTimer(2)
  1272.             repeatStack = {}
  1273.             writeLog("Thorttling Rednet Connections", theme.notice, 2)
  1274.         end
  1275.  
  1276.         for k, v in pairs(repeatStack) do
  1277.             if v.message.nMessageID and v.message.nRecipient then
  1278.                 if (not repeatedMessages[v.message.nMessageID]) or (os.clock() - repeatedMessages[v.message.nMessageID]) > 10 then
  1279.                     repeatedMessages[v.message.nMessageID] = os.clock()
  1280.                     for side, modem in pairs(Modem.modems) do
  1281.                         modem.transmit(rednet.CHANNEL_REPEAT, v.reply, v.message)
  1282.                         modem.transmit(v.message.nRecipient, v.reply, v.message)
  1283.                     end
  1284.                 end
  1285.             end
  1286.         end
  1287.  
  1288.         repeatStack = {}
  1289.     end
  1290. end
  1291.  
  1292. --  Commands and Help
  1293.  
  1294. local commands = {}
  1295. local helpDocs = {}
  1296.  
  1297. commands["password"] = function(newPassword)
  1298.     if not newPassword or newPassword == "" then
  1299.         writeLog("Usage: password <new-password>", theme.userResponse, math.huge)
  1300.     else
  1301.         config.password = newPassword
  1302.         saveConfig()
  1303.         writeLog("New password set!", theme.userResponse, math.huge)
  1304.     end
  1305. end
  1306.  
  1307. commands["lock"] = function()
  1308.     if config.password then
  1309.         locked = true
  1310.         writeLog("Server locked", theme.userResponse, math.huge)
  1311.         os.queueEvent("firewolf-lock-state-update")
  1312.     else
  1313.         writeLog("No password has been set! Set a", theme.userResponse, math.huge)
  1314.         writeLog("password with: password <password>", theme.userResponse, math.huge)
  1315.     end
  1316. end
  1317.  
  1318. commands["exit"] = function()
  1319.     error("firewolf-exit")
  1320. end
  1321.  
  1322. commands["stop"] = commands["exit"]
  1323.  
  1324. commands["quit"] = commands["exit"]
  1325.  
  1326. commands["startup"] = function()
  1327.     if fs.exists("/startup") then
  1328.         if fs.exists("/old-startup") then
  1329.             fs.delete("/old-startup")
  1330.         end
  1331.         fs.move("/startup", "/old-startup")
  1332.     end
  1333.     local f = io.open("/startup", "w")
  1334.     f:write([[
  1335.     sleep(0.1)
  1336.     shell.run("]]..shell.getRunningProgram().." "..domain.."\")")
  1337.     f:close()
  1338.     writeLog("Server will now run on startup!", theme.userResponse, math.huge)
  1339. end
  1340.  
  1341. commands["rednet"] = function(set)
  1342.     if set == "on" then
  1343.         config.allowRednetConnections = true
  1344.         saveConfig()
  1345.         writeLog("Now allowing rednet connections", theme.userResponse, math.huge)
  1346.     elseif set == "off" then
  1347.         config.allowRednetConnections = false
  1348.         saveConfig()
  1349.         writeLog("Now dis-allowing rednet connections", theme.userResponse, math.huge)
  1350.     else
  1351.         if config.allowRednetConnections then
  1352.             writeLog("Rednet conn. are currently allowed", theme.userResponse, math.huge)
  1353.         else
  1354.             writeLog("Rednet conn. are currently dis-allowed", theme.userResponse, math.huge)
  1355.         end
  1356.     end
  1357. end
  1358.  
  1359. commands["restart"] = function()
  1360.     error("firewolf-restart")
  1361. end
  1362.  
  1363. commands["reboot"] = commands["restart"]
  1364.  
  1365. commands["reload"] = function()
  1366.     os.queueEvent(resetServerEvent)
  1367. end
  1368.  
  1369. commands["refresh"] = commands["reload"]
  1370.  
  1371. commands["repeat"] = function(set)
  1372.     if set == "on" then
  1373.         config.repeatRednetMessages = true
  1374.         saveConfig()
  1375.         writeLog("Rednet repeating is now on", theme.userResponse, math.huge)
  1376.     elseif set == "off" then
  1377.         config.repeatRednetMessages = false
  1378.         saveConfig()
  1379.         writeLog("Rednet repeating is now off", theme.userResponse, math.huge)
  1380.     else
  1381.         if config.repeatRednetMessages then
  1382.             writeLog("Rednet repeating is currently turned on", theme.userResponse, math.huge)
  1383.         else
  1384.             writeLog("Rednet repeating is currently turned off", theme.userResponse, math.huge)
  1385.         end
  1386.     end
  1387. end
  1388.  
  1389. commands["update"] = function()
  1390.     term.setCursorPos(1, h)
  1391.     term.clearLine()
  1392.     term.setTextColor(theme.userResponse)
  1393.     term.setCursorBlink(false)
  1394.     term.write("Updating...")
  1395.     local handle = http.get(updateURL)
  1396.     if not handle then
  1397.         writeLog("Failed to connect to update server!", theme.error, math.huge)
  1398.     else
  1399.         data = handle.readAll()
  1400.         if #data < 1000 then
  1401.             writeLog("Failed to update server!", theme.error, math.huge)
  1402.         else
  1403.             local f = io.open("/"..shell.getRunningProgram(), "w")
  1404.             f:write(data)
  1405.             f:close()
  1406.             error("firewolf-restart")
  1407.         end
  1408.     end
  1409. end
  1410.  
  1411. commands["edit"] = function()
  1412.     writeLog("Editing server files", theme.userResponse, math.huge)
  1413.     term.setBackgroundColor(colors.black)
  1414.     if term.isColor() then
  1415.         term.setTextColor(colors.yellow)
  1416.     else
  1417.         term.setTextColor(colors.white)
  1418.     end
  1419.     term.clear()
  1420.     term.setCursorPos(1, 1)
  1421.     print("Use exit to finish editing")
  1422.     shell.setDir(serverLocation .. "/" .. domain)
  1423.     shell.run("/rom/programs/shell")
  1424.     os.queueEvent(resetServerEvent)
  1425. end
  1426.  
  1427. commands["clear"] = function()
  1428.     terminalLog = {}
  1429.     term.clear()
  1430.     os.queueEvent("firewolf-lock-state-update")
  1431. end
  1432.  
  1433. helpDocs["password"] = {"Change the lock password", "Usage: password <new-password>"}
  1434. helpDocs["lock"] = {"Lock the server with a password"}
  1435. helpDocs["exit"] = {"Exits and stops Firewolf Server"}
  1436. helpDocs["quit"] = helpDocs["exit"]
  1437. helpDocs["stop"] = helpDocs["exit"]
  1438. helpDocs["restart"] = {"Fully restarts Firewolf Server"}
  1439. helpDocs["reboot"] = helpDocs["restart"]
  1440. helpDocs["reload"] = {"Reloads the server and Server API"}
  1441. helpDocs["refresh"] = helpDocs["reload"]
  1442. helpDocs["clear"] = {"Clears the displayed log"}
  1443. helpDocs["rednet"] = {"Whether to allow rednet connections", "Usage: rednet <on or off>"}
  1444. helpDocs["startup"] = {"Runs the server for the current domain", "on startup"}
  1445. helpDocs["repeat"] = {"Whether to repeat rednet messages", "Usage: repeat <on or off>"}
  1446. helpDocs["update"] = {"Updates Firewolf Server"}
  1447. helpDocs["edit"] = {"Opens shell in server directory"}
  1448.  
  1449. commands["help"] = function(command)
  1450.     if command then
  1451.         if helpDocs[command] then
  1452.             for _, v in pairs(helpDocs[command]) do
  1453.                 writeLog(v, theme.userResponse, math.huge)
  1454.             end
  1455.         else
  1456.             writeLog("Command does not exist!", theme.userResponse, math.huge)
  1457.         end
  1458.     else
  1459.         writeLog("Use \"help <command>\" for more info", theme.userResponse, math.huge)
  1460.         writeLog("Wiki: http://bit.ly/firewolf-wiki", theme.userResponse, math.huge)
  1461.         writeLog("Commands: password, lock, exit, update,", theme.userResponse, math.huge)
  1462.         writeLog("restart, clear, rednet, repeat, startup,", theme.userResponse, math.huge)
  1463.         writeLog("edit, reload", theme.userResponse, math.huge)
  1464.     end
  1465. end
  1466.  
  1467. --  Display manager
  1468.  
  1469. local enteredText = ""
  1470. local history = {}
  1471. local scrollingHistory = false
  1472. local cursorPosition = 1
  1473. local offsetPosition = 1
  1474. local inputName = "> "
  1475. local lockTimer = 0
  1476. local lockedInputState = false
  1477.  
  1478. local lockArt = [[
  1479.   ####
  1480.  #    #
  1481.  #    #
  1482. ########
  1483. --------
  1484. ########
  1485. ########
  1486.  
  1487. [LOCKED]
  1488. ]]
  1489.  
  1490. local drawBar = function()
  1491.     term.setTextColor(theme.barText)
  1492.     term.setBackgroundColor(theme.barColor)
  1493.     term.setCursorPos(1,1)
  1494.     term.clearLine()
  1495.     term.write(" "..version)
  1496.     center("["..domain.."]")
  1497.     local time = textutils.formatTime(os.time(), true)
  1498.     term.setCursorPos(w - #time, 1)
  1499.     term.setTextColor(theme.clock)
  1500.     term.write(time)
  1501. end
  1502.  
  1503. local drawLogs = function()
  1504.     if locked and lastLogNum >= 0 then
  1505.         term.setBackgroundColor(theme.background)
  1506.         term.clear()
  1507.         lastLogNum = -1
  1508.         local lockX = math.ceil((w/2) - 4)
  1509.         local lineNum = 4
  1510.         term.setTextColor(theme.lock)
  1511.         for line in lockArt:gmatch("[^\n]+") do
  1512.             term.setCursorPos(lockX, lineNum)
  1513.             term.write(line)
  1514.             lineNum = lineNum + 1
  1515.         end
  1516.         inputName = "Password: "
  1517.     elseif not locked and lastLogNum ~= #terminalLog then
  1518.         term.setBackgroundColor(theme.background)
  1519.         term.clear()
  1520.         lastLogNum = #terminalLog
  1521.         lockedInputState = false
  1522.         if #terminalLog < h - 1 then
  1523.             term.setCursorPos(1, 2)
  1524.             for i = 1, #terminalLog do
  1525.                 term.setTextColor(theme.clock)
  1526.                 term.write(terminalLog[i][3])
  1527.                 term.setTextColor(terminalLog[i][2])
  1528.                 term.write(terminalLog[i][1])
  1529.                 term.setCursorPos(1, i + 2)
  1530.             end
  1531.         else
  1532.             term.setCursorPos(1, 2)
  1533.             for i = 1, h - 1 do
  1534.                 term.setTextColor(theme.clock)
  1535.                 term.write(terminalLog[#terminalLog - (h - 1) + i][3])
  1536.                 term.setTextColor(terminalLog[#terminalLog - (h - 1) + i][2])
  1537.                 term.write(terminalLog[#terminalLog - (h - 1) + i][1])
  1538.                 term.setCursorPos(1, i + 1)
  1539.             end
  1540.         end
  1541.     end
  1542. end
  1543.  
  1544. local drawInputBar = function()
  1545.     term.setCursorPos(1, h)
  1546.     term.setBackgroundColor(theme.background)
  1547.     term.clearLine()
  1548.  
  1549.     if lockedInputState then
  1550.         term.setCursorBlink(false)
  1551.         term.setTextColor(theme.error)
  1552.         term.write("Incorrect Password!")
  1553.     else
  1554.         term.setCursorBlink(true)
  1555.         term.setTextColor(theme.inputColor)
  1556.         term.write(inputName)
  1557.         width = w - #inputName
  1558.         if locked then
  1559.             term.write(string.rep("*", (#enteredText:sub(offsetPosition, offsetPosition + width))))
  1560.         else
  1561.             term.write(enteredText:sub(offsetPosition, offsetPosition + width))
  1562.         end
  1563.     end
  1564. end
  1565.  
  1566. local handleKeyEvents = function(event, key)
  1567.     if key == 14 then
  1568.         if enteredText ~= "" then
  1569.             enteredText = enteredText:sub(1, cursorPosition - 2) .. enteredText:sub(cursorPosition, -1)
  1570.             cursorPosition = cursorPosition-1
  1571.             if cursorPosition >= width then
  1572.                 offsetPosition = offsetPosition - 1
  1573.             end
  1574.         end
  1575.     elseif key == 28 and enteredText ~= "" and locked then
  1576.         if enteredText == config.password then
  1577.             writeLog("Successful login", theme.userResponse, math.huge)
  1578.             inputName = "> "
  1579.             locked = false
  1580.             os.queueEvent("firewolf-lock-state-update")
  1581.         else
  1582.             writeLog("Failed login attempt", theme.userResponse, math.huge)
  1583.             lockedInputState = true
  1584.             lockTimer = os.startTimer(2)
  1585.             os.queueEvent("firewolf-lock-state-update")
  1586.         end
  1587.         enteredText = ""
  1588.         cursorPosition = 1
  1589.         offsetPosition = 1
  1590.     elseif key == 28 and enteredText ~= "" and not locked then
  1591.         local commandWord = false
  1592.         local arguments = {}
  1593.         for word in enteredText:gmatch("%S+") do
  1594.             if not commandWord then
  1595.                 commandWord = word
  1596.             else
  1597.                 table.insert(arguments, word)
  1598.             end
  1599.         end
  1600.         if commands[commandWord] then
  1601.             local err, msg = pcall(commands[commandWord], unpack(arguments))
  1602.             if not err and msg:find("firewolf-exit", nil, true) then
  1603.                 error("firewolf-exit")
  1604.             elseif not err and msg:find("firewolf-restart", nil, true) then
  1605.                 error("firewolf-restart")
  1606.             elseif not err then
  1607.                 writeLog("An error occured when executing command", theme.error, math.huge)
  1608.                 writeError(tostring(msg))
  1609.             end
  1610.         else
  1611.             writeLog("No such command!", theme.error, math.huge)
  1612.         end
  1613.         table.insert(history, enteredText)
  1614.         enteredText = ""
  1615.         offsetPosition = 1
  1616.         cursorPosition = 1
  1617.     elseif key == 203 then
  1618.         cursorPosition = cursorPosition - 1
  1619.         if cursorPosition < 1 then
  1620.             cursorPosition = 1
  1621.         end
  1622.         if cursorPosition >= width then
  1623.             offsetPosition = offsetPosition - 1
  1624.         end
  1625.     elseif key == 205 then
  1626.         cursorPosition = cursorPosition + 1
  1627.         if cursorPosition > #enteredText + 1 then
  1628.             cursorPosition = #enteredText + 1
  1629.         end
  1630.         if cursorPosition - offsetPosition >= width then
  1631.             offsetPosition = offsetPosition + 1
  1632.         end
  1633.     elseif key == 208 and #history > 0 then
  1634.         if type(scrollingHistory) == "number" then
  1635.             scrollingHistory = scrollingHistory - 1
  1636.             if scrollingHistory > 0 then
  1637.                 enteredText = history[#history - scrollingHistory + 1]
  1638.                 cursorPosition = #enteredText + 1
  1639.             else
  1640.                 scrollingHistory = false
  1641.                 enteredText = ""
  1642.                 cursorPosition = 1
  1643.             end
  1644.         end
  1645.     elseif key == 200 and #history > 0 then
  1646.         if type(scrollingHistory) == "number" then
  1647.             scrollingHistory = scrollingHistory + 1
  1648.             if scrollingHistory > #history then
  1649.                 scrollingHistory = #history
  1650.             end
  1651.             enteredText = history[#history - scrollingHistory + 1]
  1652.             cursorPosition = #enteredText + 1
  1653.             if cursorPosition > width then
  1654.                 cursorPosition = 1
  1655.             end
  1656.         else
  1657.             scrollingHistory = 1
  1658.             enteredText = history[#history - scrollingHistory + 1]
  1659.             cursorPosition = #enteredText + 1
  1660.             if cursorPosition > width then
  1661.                 cursorPosition = 1
  1662.             end
  1663.         end
  1664.     end
  1665. end
  1666.  
  1667.  
  1668. local terminalDaemon = function()
  1669.     local timer = os.startTimer(1)
  1670.     local lastTime = os.clock()
  1671.  
  1672.     while true do
  1673.         drawLogs()
  1674.         drawBar()
  1675.         drawInputBar()
  1676.  
  1677.         term.setCursorPos(cursorPosition - offsetPosition + #inputName + 1, h)
  1678.  
  1679.         local event, key = os.pullEventRaw()
  1680.         if event == "char" and not lockedInputState then
  1681.             enteredText = enteredText:sub(1, cursorPosition-1) .. key .. enteredText:sub(cursorPosition, -1)
  1682.             cursorPosition = cursorPosition + 1
  1683.             if cursorPosition - offsetPosition >= width then
  1684.                 offsetPosition = offsetPosition + 1
  1685.             end
  1686.         elseif event == "key" and not lockedInputState then
  1687.             handleKeyEvents(event, key)
  1688.         elseif event == "timer" and key == timer then
  1689.             timer = os.startTimer(1)
  1690.             lastTime = os.clock()
  1691.         elseif event == "timer" and key == lockTimer then
  1692.             lockedInputState = false
  1693.         elseif event == "terminate" and not locked then
  1694.             error("firewolf-exit")
  1695.         end
  1696.  
  1697.         if (os.clock() - lastTime) > 1 then
  1698.             timer = os.startTimer(1)
  1699.             lastTime = os.clock()
  1700.         end
  1701.     end
  1702. end
  1703.  
  1704. --  Coroutine manager
  1705.  
  1706. local receiveThread, responseThread, terminalThread
  1707.  
  1708. local loadServerAPI = function()
  1709.     if fs.exists(serverLocation .. "/" .. domain .. "/" .. serverAPILocation) and not fs.isDir(serverLocation .. "/" .. domain .. "/" .. serverAPILocation) then
  1710.         local f = io.open(serverLocation .. "/" .. domain .. "/" .. serverAPILocation, "r")
  1711.         local apiData = f:read("*a")
  1712.         f:close()
  1713.  
  1714.         customCoroutines = {}
  1715.  
  1716.         local apiFunction, err = loadstring(apiData)
  1717.         if not apiFunction then
  1718.             writeLog("Error while loading server API", theme.error, math.huge)
  1719.             if err:match("%[string \"string\"%](.+)") then
  1720.                 writeLog("server_api" .. err:match("%[string \"string\"%](.+)"), theme.error, math.huge)
  1721.             else
  1722.                 writeLog(err, theme.error, math.huge)
  1723.             end
  1724.         else
  1725.             local global = getfenv(0)
  1726.             local env = {}
  1727.  
  1728.             for k,v in pairs(global) do
  1729.                 env[k] = v
  1730.             end
  1731.  
  1732.             env["server"] = {}
  1733.  
  1734.             env["server"]["domain"] = domain
  1735.  
  1736.             env["server"]["modem"] = Modem
  1737.  
  1738.             env["server"]["handleRequest"] = function(index, func)
  1739.                 if not(index and func and type(index) == "string" and type(func) == "function") then
  1740.                     return error("index (string) and handler (function) expected")
  1741.                 elseif #index:gsub("/", "") == 0 then
  1742.                     return error("invalid index")
  1743.                 end
  1744.                 if index == "/" then
  1745.                     globalHandler = func
  1746.                     return
  1747.                 end
  1748.                 if index:sub(1, 1) == "/" then index = index:sub(2, -1) end
  1749.                 if index:sub(-1, -1) == "/" then index = index:sub(1, -2) end
  1750.                 if index:find(":") then
  1751.                     return error("Handle index cannot contain \":\"s")
  1752.                 end
  1753.                 handles[index] = func
  1754.             end
  1755.  
  1756.             env["server"]["runCoroutine"] = function(func)
  1757.                 local newThread = coroutine.create(func)
  1758.                 local err, msg = coroutine.resume(newThread)
  1759.                 if not err then
  1760.                     return error(msg)
  1761.                 end
  1762.  
  1763.                 table.insert(customCoroutines, newThread)
  1764.             end
  1765.  
  1766.             env["server"]["applyTemplate"] = function(variables, template)
  1767.                 local result
  1768.                 if fs.exists(template) and not fs.isDir(template) then
  1769.                     local f = io.open(template, "r")
  1770.                     result = f:read("*a")
  1771.                     f:close()
  1772.                 else
  1773.                     writeLog("Template file \"" .. template .. "\" does not exist!", theme.error, math.huge)
  1774.                     writeLog("Template locations are relative to / (root)", theme.error, math.huge)
  1775.                     return false
  1776.                 end
  1777.                 for k,v in pairs(variables) do
  1778.                     result = result:gsub("{{"..Cryptography.sanatize(k).."}}", Cryptography.sanatize(v))
  1779.                 end
  1780.                 return result
  1781.             end
  1782.  
  1783.             env["server"]["log"] = function(text)
  1784.                 writeLog(text, theme.notice, math.huge)
  1785.             end
  1786.  
  1787.             setfenv(apiFunction, env)
  1788.             local err, msg = pcall(apiFunction)
  1789.  
  1790.             if not err then
  1791.                 writeLog("Error while executing server API", theme.error, math.huge)
  1792.                 writeError(tostring(msg))
  1793.             else
  1794.                 writeLog("Server API loaded", theme.notice, math.huge)
  1795.             end
  1796.         end
  1797.     end
  1798. end
  1799.  
  1800. local function resetServer()
  1801.     connections = {}
  1802.     repeatStack = {}
  1803.     responseStack = {}
  1804.     Modem.closeAll()
  1805.     Modem.open(serverChannel)
  1806.     Modem.open(dnsListenChannel)
  1807.     if config.repeatRednetMessages then
  1808.         Modem.open(rednet.CHANNEL_REPEAT)
  1809.     end
  1810.  
  1811.     loadServerAPI()
  1812.  
  1813.     responseThread = coroutine.create(function() responseDaemon() end)
  1814.     receiveThread = coroutine.create(receiveDaemon)
  1815.     coroutine.resume(responseThread)
  1816.     coroutine.resume(receiveThread)
  1817.     writeLog("The server has been reloaded", theme.userResponse, math.huge)
  1818.  
  1819.     os.queueEvent("firewolf-lock-state-update")
  1820. end
  1821.  
  1822. local function runThreads()
  1823.     while true do
  1824.         local shouldResetServer = false
  1825.         local events = {os.pullEventRaw()}
  1826.  
  1827.         err, msg = coroutine.resume(receiveThread, unpack(events))
  1828.         if not err then
  1829.             writeLog("Internal error!", theme.error, math.huge)
  1830.             writeError(tostring(msg))
  1831.             shouldResetServer = true
  1832.         end
  1833.  
  1834.         err, msg = coroutine.resume(responseThread)
  1835.         if not err then
  1836.             writeLog("Internal error!", theme.error, math.huge)
  1837.             writeError(tostring(msg))
  1838.             shouldResetServer = true
  1839.         end
  1840.  
  1841.         err, msg = coroutine.resume(terminalThread, unpack(events))
  1842.         if not err and msg:find("firewolf-exit", nil, true) then
  1843.             writeLog("Normal exit", theme.text, math.huge)
  1844.             Modem.closeAll()
  1845.             shell.setDir("")
  1846.             term.setBackgroundColor(colors.black)
  1847.             term.clear()
  1848.             term.setCursorPos(1, 1)
  1849.             term.setTextColor(colors.white)
  1850.             center("Thank you for using Firewolf Server")
  1851.             center("Made by 1lann and GravityScore")
  1852.             return
  1853.         elseif not err and msg:find("firewolf-restart", nil, true) then
  1854.             writeLog("Firewolf server restarting...", theme.text, math.huge)
  1855.             term.clear()
  1856.             error("firewolf-restart")
  1857.         elseif not err then
  1858.             term.clear()
  1859.             term.setCursorPos(1, 1)
  1860.             error("Restart required error: "..msg)
  1861.         end
  1862.  
  1863.         for k,v in pairs(customCoroutines) do
  1864.             if coroutine.status(v) ~= "dead" then
  1865.                 local err, msg = coroutine.resume(v, unpack(events))
  1866.                 if not err then
  1867.                     writeLog("Server API coroutine error!", theme.error, math.huge)
  1868.                     writeError(tostring(msg))
  1869.                     shouldResetServer = true
  1870.                 end
  1871.             end
  1872.         end
  1873.  
  1874.         if events[1] == resetServerEvent or shouldResetServer then
  1875.             resetServer()
  1876.         end
  1877.     end
  1878. end
  1879.  
  1880. local runOnDomain = function()
  1881.     serverChannel = Cryptography.channel(domain)
  1882.     requestMatch = header.requestMatchA .. Cryptography.sanatize(domain) .. header.requestMatchB
  1883.     responseHeader = header.responseHeaderA .. domain .. header.responseHeaderB
  1884.     pageRequestMatch = header.pageRequestMatchA .. Cryptography.sanatize(domain) .. header.pageRequestMatchB
  1885.     pageResposneHeader = header.pageResponseHeaderA .. domain .. header.pageResponseHeaderB
  1886.     closeMatch = header.closeMatchA .. domain .. header.closeMatchB
  1887.     maliciousMatch = header.maliciousMatchA .. Cryptography.sanatize(domain) .. header.maliciousMatchB
  1888.  
  1889.     Modem.open(serverChannel)
  1890.     Modem.open(dnsListenChannel)
  1891.  
  1892.     if config.repeatRednetMessages then
  1893.         Modem.open(rednet.CHANNEL_REPEAT)
  1894.     end
  1895.  
  1896.     writeLog("Firewolf Server "..version.." running" , theme.notice, math.huge)
  1897.  
  1898.     loadServerAPI()
  1899.  
  1900.     receiveThread = coroutine.create(receiveDaemon)
  1901.     responseThread = coroutine.create(responseDaemon)
  1902.     terminalThread = coroutine.create(terminalDaemon)
  1903.  
  1904.     coroutine.resume(receiveThread)
  1905.     coroutine.resume(responseThread)
  1906.     coroutine.resume(terminalThread)
  1907.  
  1908.     runThreads()
  1909. end
  1910.  
  1911. --  Server initialisation
  1912.  
  1913. local init = function()
  1914.     Modem.closeAll()
  1915.     term.setBackgroundColor(theme.background)
  1916.     term.setTextColor(theme.notice)
  1917.     term.clear()
  1918.     term.setCursorPos(1,1)
  1919.     term.setCursorBlink(false)
  1920.     term.setTextColor(theme.text)
  1921.  
  1922.     if not fs.exists(serverLocation .. "/" .. domain) then
  1923.         makeDirectory(serverLocation .. "/" .. domain)
  1924.     else
  1925.         if not fs.isDir(serverLocation .. "/" .. domain) then
  1926.             fs.delete(serverLocation .. "/" .. domain)
  1927.         end
  1928.         makeDirectory(serverLocation .. "/" .. domain)
  1929.     end
  1930.  
  1931.     local report = checkConfig()
  1932.     if report then
  1933.         term.setBackgroundColor(colors.black)
  1934.         term.setTextColor(theme.error)
  1935.         term.clear()
  1936.         term.setCursorPos(1, 1)
  1937.         print("There was an error loading your config file")
  1938.         print("The config file is located at: "..configLocation)
  1939.         print("-------------------------------------------------")
  1940.         for k,v in pairs(report) do
  1941.             print(v)
  1942.         end
  1943.         return
  1944.     end
  1945.  
  1946.     if config.password then
  1947.         locked = true
  1948.     end
  1949.  
  1950.     local err, msg = pcall(function()runOnDomain()end)
  1951.     if not err and msg:find("firewolf-restart", nil, true) then
  1952.         term.clear()
  1953.         term.setCursorPos(1, 1)
  1954.         return shell.run("/"..shell.getRunningProgram(), domain)
  1955.     elseif not err then
  1956.         term.setBackgroundColor(colors.black)
  1957.         term.clear()
  1958.         term.setCursorPos(1, 1)
  1959.         term.setTextColor(colors.red)
  1960.         print("Sorry, Firewolf Server has crashed! Error:")
  1961.         print(msg)
  1962.         print("Firewolf Server will reboot in 3 seconds...")
  1963.         sleep(3)
  1964.         return shell.run("/"..shell.getRunningProgram(), domain)
  1965.     end
  1966. end
  1967.  
  1968. if pocket or turtle then
  1969.     term.setTextColor(theme.error)
  1970.     print("Sorry, Firewolf Server can")
  1971.     print("only be ran on computers.")
  1972.     return
  1973. end
  1974.  
  1975. if not Modem.exists() then
  1976.     term.setTextColor(theme.error)
  1977.     print("Error: No modems found!")
  1978.     return
  1979. end
  1980.  
  1981. if fs.isDir(configLocation) then
  1982.     fs.delete(configLocation)
  1983. end
  1984.  
  1985. if not fs.exists(configLocation) then
  1986.     saveConfig()
  1987. end
  1988.  
  1989. loadConfig()
  1990.  
  1991. if not domain and config.lastDomain then
  1992.     domain = config.lastDomain
  1993. end
  1994.  
  1995. if domain then
  1996.     if term.isColor() then
  1997.         term.setTextColor(colors.yellow)
  1998.     else
  1999.         term.setTextColor(colors.white)
  2000.     end
  2001.     term.setCursorBlink(false)
  2002.     print("Initializing Firewolf Server...")
  2003.     local report = checkDomain(domain)
  2004.     term.setTextColor(theme.error)
  2005.     if report == "symbols" then
  2006.         print("Domain cannot contain \":\", \"/\" and \"?\"s")
  2007.     elseif report == "short" then
  2008.         print("Domain name too short!")
  2009.     else
  2010.         config.lastDomain = domain
  2011.         saveConfig()
  2012.         init()
  2013.     end
  2014. else
  2015.     term.setTextColor(colors.white)
  2016.     print("Usage: "..shell.getRunningProgram().." <domain>")
  2017. end
Add Comment
Please, Sign In to add comment