Email system with roaming accounts

Discussion in 'ComputerCraft Programming' started by Azimath, 19 January 2013.

  1. Azimath

    Azimath Master Zepplin Thief

    Joined:
    22 January 2012
    Messages:
    30
    Likes Received:
    6
    Ive seen email systems done in the past, but here's the first usable version of mine. I spent a lot of time writing this stuff at 3am, so it probably needs some cleaning up.

    It involves a few separate systems: a login server, a DNS-like server, an email server, and an email client.

    The login server is similar to the one from the wiki, but with changes to the way it works to make it more secure, and allow the server owner to add/remove accounts on the fly. The code comes with an account for the email server to use by default, so you need to add user accounts. To do so , just start the program and press the 'a' key. It will ask for a name and a password, then confirm adding the user to the list. Removing is just pressing the 'r' key, entering the username, and confirming. You can press 'm' to view the list of users currently in the system.

    DANGER: since this is an early version, anything in any of the servers not hardcoded will not persist after a crash/reboot

    Code:
    pullEvent = os.pullEvent
    os.pullEvent = os.pullEventRaw
    users = {"mailserver"}
    passwords = {"legomail"}
    term.clear()
    term.setCursorPos(1,1)
    print("Legoman technologies authentication server running")
    id = os.computerID()
    term.setCursorPos(1,2)
    print("Computer ID = "..id)
     
    local firstCycle = true
    local modemSide = "right"
    local valid = false
    local DNSID = 2
     
    while true do
      if firstCycle then
        rednet.open(modemSide)
        firstCycle = false
      end
      event, p1, p2, p3 = os.pullEvent()
      term.clear()
      term.setCursorPos(1,1)
      print("Legoman technologies authentication server running")
      id = os.computerID()
      term.setCursorPos(1,2)
      print("Computer ID = "..id)
     
      if event =="char" then
        if p1 == "m" then
          for i, v in ipairs(users) do
            print(v)
          end
        end
        if p1 == "a" then
          term.setCursorPos(1,10)
          print("Enter name: ")
          name = read()
          print("Enter password: ")
          password = read()
          print("Confim add " .. name .. " Y/N?")
          if read() == "Y" then
            table.insert(users, name)
            table.insert(passwords, password)
            print("Added")
          end
        end
        if p1 == "r" then
          print("Enter name: ")
          name = read()
          print("Confim remove " .. name .. " Y/N?")
          if read() == "Y" then
            for i,v in ipairs(users) do
              if name == v then
                table.remove(users, i)
                table.remove(passwords, i)
              end
            end
          end
        end
      end
     
      if event == "terminate" then
        term.setCursorPos(1,10)
        print("Enter password: ")
        if read("*") == "unlock" then
          os.pullEvent = pullEvent
          shell.exit()
        end
      end
     
      if event == "rednet_message" then
        senderId = p1
        message = p2
        term.setCursorPos(1,4)
        term.clearLine()
        print(message)
        for i,v in ipairs(users) do
          if message == v then
          valid = true
          password = passwords[i]
          user = users[i]
          break
          else
          valid = false
          end
        end
     
        if valid then
        rednet.send(senderId, "Valid")
        print("Valid")
        else
        rednet.send(senderId, "Not Valid")
        print("Not valid")
        end
        sender, passwordRcv, dist = rednet.receive(3)
        print(sender)
        if passwordRcv == password then
          rednet.send(senderId, "Authenticated")
          rednet.send(DNSID, "Add " .. senderId .. " " .. user)
          print("Authenticated")
        else
          rednet.send(senderId, "Failed")
        end
      end
    end
    
    There is a variable near the start named DNSID. you need to change that to the id of the next computer, the DNS server.

    When someone logs in, the login server tells the DNS server the name and id of the person who logged in, so others can check the names of users and the ids to send things to. It also supports listing the names in the system using the 'm' key. The variable AUTHID needs to be set to the id of the login server for it to accept new entries, and it is a good idea to give the ids table correct ids for the login and DNS server as well. Currently, if it gets multiple commands to add the same name, the one added first will be the one that counts. If you send a remove message, it will delete you from its table as a means of logging out.
    Code:
    pullEvent = os.pullEvent
    os.pullEvent = os.pullEventRaw
     
    names = {"authserver","dnsserver"}
    ids = {0,1}
     
     
    term.clear()
    term.setCursorPos(1,1)
    print("Legoman technologies DNS server running")
    myid = os.computerID()
    term.setCursorPos(1,2)
    print("Computer ID = " .. myid)
     
    local firstCycle = true
    local modemSide = "right"
    local AUTHID = 1
     
    while true do
      if firstCycle then
        rednet.open(modemSide)
        firstCycle = false
      end
     
      event, p1, p2, p3 = os.pullEvent()
     
      term.clear()
      term.setCursorPos(1,1)
      print("Legoman technologies DNS server running")
      myid = os.computerID()
      term.setCursorPos(1,2)
      print("Computer ID = " .. myid)
     
      if event == "terminate" then
        term.setCursorPos(1,10)
        print("Enter password: ")
        if read("*") == "unlock" then
          os.pullEvent = pullEvent
          shell.exit()
        end
      end
     
      if event =="char" then
        if p1 == "m" then
          for i, v in ipairs(names) do
            print(v)
          end
        end
      end
      if event == "rednet_message" then
        senderId = p1
        message = p2
        term.setCursorPos(1,4)
        term.clearLine()
        if  string.match(message, 'Id') == "Id" then
          print("Command: " .. string.match(message, 'Id'))
          idLookup = string.match(message, '%d+') + 0
          term.clearLine()
          print("Param: " .. idLookup)
          for i, v in ipairs(ids) do
            if idLookup == v then
              nameLookup = names[i]
              term.clearLine()
              print("Return: " .. nameLookup)
              rednet.send(senderId, nameLookup)
              break
            end
          end
        end
        if string.match(message, 'Name') == "Name" then
        print("Command: " .. string.match(message, 'Name'))
        nameLookup = string.match(message, '%a+', 5)
          term.clearLine()
          print("Param: " .. nameLookup)
          for i, v in ipairs(names) do
            if nameLookup == v then
              idLookup = ids[i]
              term.clearLine()
              print("Return: " .. idLookup)
              rednet.send(senderId, idLookup.."")
              break
            end
          end
        end
        if string.match(message, 'Add') == "Add" and senderId + 0 == AUTHID then
          print("Command: " .. string.match(message, 'Add'))
          nameAdd = string.match(message, '%a+', 4)
          idAdd = string.match(message, '%d+', 4)
          print("Params: " .. nameAdd .. ", " .. idAdd)
          table.insert(names, nameAdd)
          table.insert(ids, idAdd+0)
        end
        if string.match(message, 'Remove') == "Remove" then
          print("Command: " .. string.match(message, 'Remove'))
          term.clearLine()
          print("Param: " .. senderId)
          for i, v in ipairs(ids) do
            if senderId == v then
              table.remove(ids, i)
              table.remove(names, i)
            end
          end
        end
      end
    end
    
    The third and final server in this system is the email server itself. It needs the login and DNS server ids coded into the AUTHID and DNSID variables, respectively. Pressing 'm' lists the recipients of the messages in the system, 'a' adds a user to the list of people who can use email, and 'l' tries to update its id in the DNS server. It works by sending and receiving a variety of messages to and from the clients, and checking who each one is using the reverse DNS function of the DNS server.
    Code:
    pullEvent = os.pullEvent
    os.pullEvent = os.pullEventRaw
     
    users = {"test"}
    messages = {"blank"}
    recipients = {"test"}
     
     
    term.clear()
    term.setCursorPos(1,1)
    print("Legoman technologies email server running")
    myid = os.computerID()
    term.setCursorPos(1,2)
    print("Computer ID = " .. myid)
     
    local firstCycle = true
    local modemSide = "right"
    local AUTHID = 0
    local DNSID = 1
    local senderValid = false
    local recipientValid = false
     
    function DNSreverse (Id)
      rednet.open(modemSide)
      rednet.send(DNSID, "Id " .. Id)
      local temp, message = rednet.receive(1)
      return message
    end
     
    function login()
      rednet.send(AUTHID, "mailserver")
      os.sleep(1)
      rednet.send(AUTHID, "legomail")
    end
     
    while true do
      if firstCycle then
        rednet.open(modemSide)
        firstCycle = false
        rednet.send(DNSID, "Remove")
        login()
      end
     
      event, p1, p2, p3 = os.pullEvent()
     
      term.clear()
      term.setCursorPos(1,1)
      print("Legoman technologies email server running")
      myid = os.computerID()
      term.setCursorPos(1,2)
      print("Computer ID = " .. myid)
     
      if event == "terminate" then
        term.setCursorPos(1,10)
        print("Enter password: ")
        if read("*") == "unlock" then
          os.pullEvent = pullEvent
          shell.exit()
        end
      end
     
      if event =="char" then
        if p1 == "m" then
          for i, v in ipairs(recipients) do
            print(v)
          end
        end
        if p1 == "a" then
          term.setCursorPos(1,10)
          print("Enter name: ")
          name = read()
          print("Confim add " .. name .. " Y/N?")
          if read() == "Y" then
            table.insert(users, name)
            print("Added")
          end
        end
        if p1 == "l" then
          rednet.send(DNSID, "Remove")
          login()
        end
      end
     
      if event == "rednet_message" then
        senderId = p1
        message = p2
        senderName = DNSreverse(senderId)
        for i, v in ipairs(users) do
          if senderName == v then
            senderValid = true
            print("Sender is valid")
            break
          else
            senderValid = false
          end
        end
     
        if  string.match(message, 'Send') == "Send" and senderValid then
          print("Command: " .. string.match(message, 'Send'))
          recipient = string.match(message, '%a+', 5)
          mailMessage = string.match(message, '%p.+', 5)
          print("Recipient: " .. recipient)
          --print("Message: " .. mailMessage)
          os.sleep(1)
          for i, v in ipairs(users) do
            print(v)
            if recipient == v then
              recipientValid = true
              print("Recipient is valid")
              break
            else
              recipientValid = false
            end
          end     
          if recipientValid then
            table.insert(messages, mailMessage)
            table.insert(recipients, recipient)
            rednet.send(senderId, "Ok")
          end
        end
     
        if string.match(message, 'Check') == "Check" and senderValid then
          print("Command: " .. string.match(message, 'Check'))
          messageNumber = 0
          for i, v in ipairs(recipients) do
            if senderName == v then
              messageNumber = messageNumber + 1
            end
          end
          rednet.send(senderId, messageNumber .. "")
        end
       
        if string.match(message, 'Read') == "Read" and senderValid then
          print("Command: " .. string.match(message, 'Read'))
          readNumber = string.match(message, '%d+', 5) + 0
          print("Read number: " .. readNumber) 
          messageNumber = 0
          for i, v in ipairs(recipients) do
            if senderName == v then
                  messageNumber = messageNumber + 1
            end
            if messageNumber == readNumber then
              print("Message found")
              rednet.send(senderId, messages[i].."")
              break
            end
          end
        end
        if string.match(message, 'Remove') == 'Remove' and senderValid then
          print("Command: " .. string.match(message, 'Remove'))
          removeNumber = string.match(message, '%d+', 7)
          print("Remove number: " .. removeNumber)
          messageNumber = 0
          for i, v in ipairs(recipients) do
            if senderName == v then
                  messageNumber = messageNumber + 1
            end
            if messageNumber == removeNumber + 0 then
              table.remove(messages, i)
              table.remove(recipients, i)
              print("Removed")
              break
            end
          end
        end
      end
    end
    
    The email client interfaces to all 3 servers, and it will attempt to find the id of the mail and login servers from DNS, so be sure they are both registered in the DNS server. It supports roaming accounts, so you can log in from any computer in rednet range. It can:
    1- Check the number of messages in your inbox
    2- Read one of the messages (input the number of the message you want read, with the first in the inbox being number one, the second number two, etc)
    3- Send a message to a particular user, which give feedback in the form of "Sent" or "error" (which usually means invalid recipient)
    4- Remove a message from the inbox, using a number system like reading messages
    5-Log out of your account

    Code:
    pullEvent = os.pullEvent
    os.pullEvent = os.pullEventRaw
     
    term.clear()
    term.setCursorPos(1,1)
    print("Legoman technologies email client running")
    myid = os.computerID()
    term.setCursorPos(1,2)
    print("Computer ID = " .. myid)
     
    local firstCycle = true
    local modemSide = "right"
    local AUTHID = 0
    local DNSID = 1
    local loggedin = false
     
    function DNSreverse (Id)
      rednet.open(modemSide)
      rednet.send(DNSID, "Id " .. Id)
      local temp, message = rednet.receive(1)
      return message
    end
     
    function DNSforward(servername)
      rednet.open(modemSide)
      rednet.send(DNSID, "Name " .. servername)
      local temp, message = rednet.receive(3)
      return message
    end
     
    while true do
      if firstCycle then
        rednet.open(modemSide)
        firstCycle = false
      end
     
      AUTHID = DNSforward("authserver") + 0
      MAILID = DNSforward("mailserver") + 0
      print("Mail ID: " .. MAILID)
      print("Auth ID: " .. AUTHID)
      print("Welcome! Please log in.")
      print("Username: ")
      username = read()
      print("Password:")
      password = read("*")
     
      rednet.send(AUTHID + 0, username)
      sender, message = rednet.receive(2)
     
      if message == "Valid" then
        print("Username ok!")
        rednet.send(AUTHID + 0, password)
      elseif message == "Not Valid" then
        print("Wrong username!")
        loggedin = false
      else
        print("Rednet error!")
      end
     
      sender, message = rednet.receive(2)
     
      if message == "Authenticated" then
        loggedin = true
        print("Logged in!")
      else
        print("Login failed!")
      end
      os.sleep(2)
     
      if loggedin then
        term.clear()
        term.setCursorPos(1,1)
      end
     
      while loggedin do
       
        print("Legoman technologies email client! Username: " .. DNSreverse(myid))
        print("What would you like to do?")
        print(" 1 - Check for messages")
        print(" 2 - Read a message")
        print(" 3 - Send a message")
        print(" 4 - Remove a message")
        print(" 5 - Log out")
        print(">")
        input = read()
       
        if input == "1" then
          print("Checking for messages")
          rednet.send(MAILID, "Check")
          temp, message = rednet.receive(3)
          print("You have: " .. message .. " message(s)")
        end
       
        if input == "2" then
          print("Enter message number to read: ")
          messageNum = read()
          rednet.send(MAILID, "Read " .. messageNum)
          temp, message = rednet.receive(3)
          print(message)
        end
       
        if input == "3" then
          print("Enter recipient name: ")
          recipient = read()
          print("Enter message: ")
          toSend = read()
          print("Send message to " .. recipient .. " Y/N?")
          yn = read()
          if yn == "Y" then
            rednet.send(MAILID, "Send " .. recipient .. " !" .. toSend)
            temp, message = rednet.receive(3)
            if message == "Ok" then
              print("Sent!")
            else
              print("Error!")
            end
          end
        end 
     
        if input == "4" then
          print("Enter message number to remove: ")
          messageNum = read()
          os.sleep(1)
          print("Proceed? Y/N?")
          yn = read()
          if yn == "Y" then
            rednet.send(MAILID, "Remove " .. messageNum)
          end
        end 
     
        if input == "5" then
          term.clear()
          term.setCursorPos(1,1)
          print("Goodbye!")
          os.sleep(1)
          rednet.send(DNSID, "Remove")
          loggedin = false
          term.clear()
          term.setCursorPos(1,1)
        end 
      end
    end
    
    If I implement this on my friends tekkit server(where we all live pretty close together), I may shoot some video.

    Planned features/improvements:
    -Save data to disk
    -Save data to disk(encrypted or something)
    -Misc robustness improvements (I usually let kexus321 try to break it until it does break, then fix what caused it to break)
    -clean up a little
    -support an inbox using subject lines instead of just "you have x messages"
    -support for dynamically discovering DNS and login servers
    -create a dynamic mesh network of repeaters to expand usable range (probably going to stay in the planned feature section for a while)
    -add other servers that use the login/DNS system to create a larger "internet" system

    Suggestions/comments/constructive criticism appreciated
    If you use this code, please keep the "Legoman technologies" statement as accreditation
     
    Ali and Chaeris like this.
  2. Chaeris

    Chaeris Active Member

    Joined:
    8 March 2012
    Messages:
    766
    Likes Received:
    89
    If we continue going on like that, we'll make video games for computers! (Mario/PacMan/other)
     
  3. thefun

    thefun New Member

    Joined:
    7 July 2014
    Messages:
    3
    Likes Received:
    0
    does not cause
     

    Attached Files:

  4. Azimath

    Azimath Master Zepplin Thief

    Joined:
    22 January 2012
    Messages:
    30
    Likes Received:
    6
    Could you show what it looks like when you open that program in the computer's edit system? It indicates line 4, which is just term.clear() .
     
  5. thefun

    thefun New Member

    Joined:
    7 July 2014
    Messages:
    3
    Likes Received:
    0
    I do not know much english
     

    Attached Files:

    • 1.png
      1.png
      File size:
      233,6 KB
      Views:
      178
    • 2.png
      2.png
      File size:
      509,2 KB
      Views:
      172
  6. Azimath

    Azimath Master Zepplin Thief

    Joined:
    22 January 2012
    Messages:
    30
    Likes Received:
    6
    Looks like you havent put the ID number of the DNS and Auth server in the right places. AUTHID and DNSID need to have the number on the same line replaced with the Computer ID of the Authentication and DNS computers. the ID for each computer is displayed on that computer when the program is running.

    Code:
    local AUTHID = 0
    local DNSID = 1
    these lines^
     
  7. thefun

    thefun New Member

    Joined:
    7 July 2014
    Messages:
    3
    Likes Received:
    0
    Do not tell this video

    Do not describing the video here
     
  8. Azimath

    Azimath Master Zepplin Thief

    Joined:
    22 January 2012
    Messages:
    30
    Likes Received:
    6
    Sorry I've been out. Show/post the top of the code in the computer that is broken, so I can check the configuration.