ldd-CC/sysmail.lua

470 lines
11 KiB
Lua
Raw Normal View History

local mainPath = ".sysmail"
local yourID = os.getComputerID()
local onlyUseWiredModems = false
local config = {
channel = 1024,
keyPath = fs.combine(mainPath, "keys"),
mailPath = fs.combine(mainPath, "mail"),
apiPath = fs.combine(mainPath, "api"),
nameFile = fs.combine(mainPath, "names")
}
local keys = {}
local names = {}
local readFile = function(path)
local file = fs.open(path, "r")
local contents = file.readAll()
file.close()
return contents
end
local writeFile = function(path, contents)
if fs.isReadOnly(path) then
return false
else
local file = fs.open(path, "w")
file.write(contents)
file.close()
return true
end
end
local makeKey = function(ID, key)
return writeFile(fs.combine(config.keyPath, ID), key)
end
local getKey = function(ID)
return readFile(fs.combine(config.keyPath, ID))
end
-- get personal key file
keys[yourID] = ""
if fs.exists(fs.combine(config.keyPath, tostring(yourID))) then
keys[yourID] = readFile(fs.combine(config.keyPath, tostring(yourID)))
else
for i = 1, 64 do
keys[yourID] = keys[yourID] .. string.char(math.random(11, 255))
end
writeFile(fs.combine(config.keyPath, tostring(yourID)), keys[yourID])
end
local getAllKeys = function()
local list = fs.list(config.keyPath)
local output = {}
for i = 1, #list do
if tonumber(list[i]) then
output[tonumber(list[i])] = getKey(list[i])
end
end
return output
end
keys = getAllKeys()
--print(textutils.serialize(keys))
--error()
local apiData = {
["aes"] = {
path = "aes.lua",
url = "http://pastebin.com/raw/9E5UHiqv",
}
}
for name, data in pairs(apiData) do
data.path = fs.combine(config.apiPath, data.path)
if not fs.exists(data.path) then
local net = http.get(data.url)
if net then
local file = fs.open(data.path, "w")
file.write(net.readAll())
file.close()
net.close()
else
error("Could not download " .. name)
end
end
_ENV[name] = dofile(data.path)
end
local function interpretArgs(tInput, tArgs)
local output = {}
local errors = {}
local usedEntries = {}
for aName, aType in pairs(tArgs) do
output[aName] = false
for i = 1, #tInput do
if not usedEntries[i] then
if tInput[i] == aName and not output[aName] then
if aType then
usedEntries[i] = true
if type(tInput[i+1]) == aType or type(tonumber(tInput[i+1])) == aType then
usedEntries[i+1] = true
if aType == "number" then
output[aName] = tonumber(tInput[i+1])
else
output[aName] = tInput[i+1]
end
else
output[aName] = nil
errors[1] = errors[1] and (errors[1] + 1) or 1
errors[aName] = "expected " .. aType .. ", got " .. type(tInput[i+1])
end
else
usedEntries[i] = true
output[aName] = true
end
end
end
end
end
for i = 1, #tInput do
if not usedEntries[i] then
output[#output+1] = tInput[i]
end
end
return output, errors
end
local argList = {
["--server"] = false
}
local argData, argErrors = interpretArgs({...}, argList)
local isServer = argData["--server"]
local serverID = argData[1]
if ccemux and (not peripheral.find("modem")) then
ccemux.attach("top", "wireless_modem")
end
local modem
local getModem = function(doNotPickWireless)
local output, periphList
for try = 1, 40 do
periphList = peripheral.getNames()
for i = 1, #periphList do
if peripheral.getType(periphList[i]) == "modem" then
output = peripheral.wrap(periphList[i])
if not (doNotPickWireless and output.isWireless()) then
output.open(config.channel)
return output
end
end
end
sleep(0.15)
end
error("No modems were found after 40 tries. That's as many as four tens. And that's terrible.")
end
-- allowed IDs
local userIDs = {}
-- all data recorded
local DATA = {}
local transmit = function(msg, msgID)
modem = getModem(onlyUseWiredModems)
modem.transmit(config.channel, config.channel, {
msg = msg,
encrypted = false,
msgID = msgID
})
end
local encTransmit = function(msg, msgID, recipient)
modem = getModem(onlyUseWiredModems)
if not keys[recipient] then
error("the fuck, no keys[recipient]")
elseif not msg then
error("the fuck, no msg")
else
modem.transmit(config.channel, config.channel, {
msg = aes.encrypt(keys[recipient], msg),
encrypted = true,
msgID = msgID,
recipient = recipient
})
end
end
local receive = function(msgID, specifyCommand, timer)
local evt, msg, tID
if timer then
tID = os.startTimer(timer)
end
modem = getModem()
while true do
evt = {os.pullEvent()}
if evt[1] == "modem_message" then
if type(evt[5]) == "table" then
if evt[5].encrypted then
msg = aes.decrypt(keys[yourID], evt[5].msg)
else
msg = evt[5].msg
end
if (not msgID) or (evt[5].msgID == msgID) then
if (not specifyCommand) or (msg.command == specifyCommand) then
return msg, evt[5].encrypted, evt[5].msgID
end
end
end
elseif evt[1] == "timer" and evt[2] == tID then
return nil, nil, nil
end
end
end
local client = {} -- all client-specific commands
local server = {} -- all server-specific commands
---- ----
---- CLIENT COMMANDS ----
---- ----
-- if you want a super duper secure network, manually enter the server ID into this
client.findServer = function(recipient)
local msgID = math.random(1, 2^30)
transmit({
id = yourID,
command = "find_server"
}, msgID)
local reply, isEncrypted = receive(msgID, "find_server_respond", 2)
if type(reply) == "table" then
if reply.server then
return reply.server
end
end
end
client.register = function(srv, username)
local msgID = math.random(1, 2^30)
encTransmit({
id = yourID,
name = username
}, msgID, srv)
end
client.sendMail = function(srv, recipient, subject, message, attachments)
local msgID = math.random(1, 2^30)
encTransmit({
command = "send_mail",
id = yourID,
recipient = recipient,
subject = subject,
message = message,
attachments = attachments
}, msgID, srv)
local reply, isEncrypted = receive(msgID, "send_mail_respond", 2)
return (reply ~= nil and isEncrypted ~= nil)
end
client.getMail = function(srv)
local msgID = math.random(1, 2^30)
encTransmit({
command = "get_mail",
id = yourID,
}, msgID, srv)
local reply, isEncrypted = receive(msgID, "get_mail_respond", 2)
return (isEncrypted and type(reply) == "table") and reply
end
---- ----
---- SERVER COMMANDS ----
---- ----
server.checkValidName = function(name)
if type(name) ~= "string" then
return false
else
return #name >= 3 or #name <= 64
end
end
server.checkRegister = function(id)
-- I make the code this stupid looking in case I add other stipulations
if names[tostring(id)] then
return true
else
return false
end
end
server.registerID = function(id, name)
local path = fs.combine(config.mailPath, id)
if not server.checkRegister(id) then
fs.makeDir(path)
names[id] = tostring(name)
return true, names[id]
else
return false, "name already exists"
end
end
-- records a full email to file
server.recordMail = function(sender, _recipient, subject, message, attachments)
local time = os.epoch("utc")
local recipient
if _recipient == "*" then
recipient = fs.list(config.mailPath)
elseif type(_recipient) ~= "table" then
recipient = {tostring(_recipient)}
end
local msg = textutils.serialize({
sender = id,
time = time,
read = false,
subject = subject,
message = message,
attachments = attachments
})
local requiredSpace = #msg + 2
if fs.getFreeSpace(config.mailPath) < requiredSpace then
return false, "Cannot write mail, not enough space!"
end
local path, file
for i = 1, #recipient do
server.registerID(recipient[i])
path = fs.combine(config.mailPath, recipient[i])
file = fs.open(fs.combine(path, tostring(time)), "w")
file.write(msg)
file.close()
end
return true
end
-- returns every email in an ID's inbox
server.getMail = function(id)
local output, list = {}, {}
local mails = fs.list(fs.combine(config.mailPath, tostring(id)))
local file
for k,v in pairs(mails) do
list[v] = k
end
for k,v in pairs(list) do
file = fs.open(fs.combine(config.mailPath, "/" .. id .. "/" .. k), "r")
if file then
output[#output + 1] = textutils.unserialize(file.readAll())
file.close()
end
end
return output
end
-- receives messages and sends the appropriate response
server.networking = function(verbose)
local msg, isEncrypted, msgID
local say = function(text, id)
if verbose then
return print(text .. (id and ("(" .. id .. ")") or ""))
end
end
while true do
msg, isEncrypted, msgID = receive()
if not isEncrypted then
if msg.command == "find_server" then
transmit({
command = msg.command .. "_respond",
server = yourID,
}, msgID, msg.id)
say("find_server found")
end
elseif msg.id then
if not server.checkRegister(msg.id) then
encTransmit({
command = msg.command .. "_respond",
result = false,
errorMsg = "not registered"
}, msgID, msg.id)
say("unregistered users can burn in hell")
else
if msg.command == "register" then
if (
type(msg.id) == "number" and
type(msg.name) == "string"
) then
local reply
local result, name = server.registerID(msg.id, msg.name)
if result then
reply = {
command = msg.command .. "_respond",
result = result,
name = name,
}
else
reply = {
command = msg.command .. "_respond",
result = result,
}
end
encTransmit(reply, msgID, msg.id)
say("user " .. tostring(msg.id) .. " registered as " .. name)
end
elseif msg.command == "find_server" then
encTransmit({
command = msg.command .. "_respond",
server = yourID,
result = true
}, msgID, msg.id)
say("find_server found (aes)")
elseif msg.command == "send_mail" then
if (
msg.recipient and
type(msg.subject) == "string" and
type(msg.message) == "string"
) then
local reply = {
command = msg.command .. "_respond",
result = server.recordMail(msg.id, msg.recipient, msg.subject, msg.message, msg.attachments)
}
encTransmit(reply, msgID, msg.id)
say("mail sent", msg.id)
end
elseif msg.command == "get_mail" then
if (
msg.id
) then
local mail = server.getMail(msg.id)
local reply = {
command = msg.command .. "_respond",
result = true,
mail = mail,
}
encTransmit(reply, msgID, msg.id)
end
end
end
end
end
end
if isServer then
server.networking(true)
else
-- make a whole client interface and shit
local srv = client.findServer()
print(srv)
client.register(srv, "buttman")
end
--[[
server.recordMail(1, 1, "Testing the sysmail.", "Forgive me, but I'm just testing SysMail as it's being made.")
local messages = server.getMail(1)
print(textutils.serialize(messages))
--]]
return {client = client, server = server}