class Wafer::Server
Constants
- HASH_VERIFY_CMDS
- KEYCODE_VERIFY_CMDS
- NO_LOG_CMDS
- NO_USER_VERIFY_CMDS
- PASSWORD_VERIFY_CMDS
Attributes
repo[R]
settings[R]
Public Class Methods
new(repo:, settings: {})
click to toggle source
# File lib/wafer.rb, line 26 def initialize(repo:, settings: {}) @repo = repo # Make a copy of default settings to allow modification @settings = copy_of_default_settings @settings.merge! settings @read_sockets = [] @err_sockets = [] @socket_types = {} @seq_numbers = {} @socket_buffer = {} end
Public Instance Methods
auth_respond(conn, first_message)
click to toggle source
# File lib/wafer/auth_server.rb, line 8 def auth_respond(conn, first_message) if first_message[0][0] == ":" secure_auth = true user_name = first_message[0][1..-1] code = first_message[1] command = first_message[2] @seq_numbers[conn] = first_message[3] message = first_message[4..-1] else secure_auth = false command = first_message[0] @seq_numbers[conn] = first_message[1] user_name = first_message[2] code = false message = first_message[3..-1] end if command == "" || user_name == "" send_error(conn, "BAD INPUT") end uid = @repo.uid_by_name(user_name) if NO_LOG_CMDS.include?(command) # Don't log passwords log("Recorded command #{command.inspect} for user #{user_name.inspect}") else log("Auth server: #{first_message.inspect}") end # All commands but two require verifying the user isn't deleted, banned, etc. unless NO_USER_VERIFY_CMDS.include?(command) allowed, err = @repo.is_user_ok(uid) return send_error(conn, err) unless allowed end # Some commands need the keycode to be valid if supplied if KEYCODE_VERIFY_CMDS.include?(command) && code allowed, err = @repo.is_keycode_ok(uid, code) return send_error(conn, err) unless allowed end if HASH_VERIFY_CMDS.include?(command) allowed, err = @repo.is_hash_ok(uid, message[0]) return send_error(conn, err) unless allowed end if PASSWORD_VERIFY_CMDS.include?(command) allowed, err = @repo.is_password_ok(uid, message[0]) return send_error(conn, err) unless allowed end case command when "checkaccess" if @repo.user_has_access?(uid, message[0]) return send_ok(conn, "ACCESS") else return send_error(conn, "NOAUTH") end when "convertaccount" if message[0] == "premium" @repo.user_set_flag(uid, "premium") return send_ok(conn, "premium") elsif message[0] == "basic" @repo.user_unset_flag(uid, "premium") return send_ok(conn, "basic") else return send_error(conn, "Unknown conversion (#{message[0]})") end when "emaillookup" user = @repo.user_by_field("email", user_name) if user return send_ok(conn, user["name"]) else return send_error(conn, "no such email") end when "emailused" user = @repo.user_by_field("email", user_name) if user return send_ok(conn, "YES") else return send_error(conn, "no such email") end when "getping" # We don't do real email pings with this server user = @repo.user_by_id(uid) if user return send_ok(conn, "#{uid} #{user["email"]} 17171717171717171717") else return send_error(conn, "NO PING") end when "getprop" if secure_auth prop = message[1] else prop = message[0] end user = @repo.user_by_id(uid) return send_ok(conn, user[prop]) when "keycodeauth" code = message[0] unless code allowed, err = @repo.is_keycode_ok(uid, code) STDERR.puts "keycodeauth(#{uid.inspect}, #{code.inspect}) = [#{allowed.inspect}, #{err.inspect}]" return send_error(conn, err) unless allowed return send_error(conn, "TOS") unless @repo.user_has_tos?(uid) return send_error(conn, "USER HAS NO EMAIL") unless @repo.user_has_verified_email?(uid) return send_auth_status(conn, uid) when "md5login" return send_ok(conn, @repo.user_keycode(uid)) when "md5auth" return send_auth_status(conn, uid) when "passwordlogin" return send_ok(conn, @repo.user_keycode(uid)) when "passwordauth" return send_auth_status(conn, uid) when "pinguser" return send_ok(conn, "OK") when "setemail" @repo.user_set_email(uid, message[0]) return send_ok(conn, "YES") when "tempkeycode" return send_ok(conn, @repo.user_keycode(uid)) when "tempguarantee" return send_error(conn, "DOES NOT SUPPORT") end return send_error(conn, "BAD COMMAND(#{command.inspect})") end
conn_connect(conn_type)
click to toggle source
# File lib/wafer.rb, line 49 def conn_connect(conn_type) port = @settings["dgd"]["portbase"] + (conn_type == :auth ? 70 : 71) sock = TCPSocket.open @settings["dgd"]["serverIP"], port @socket_types[sock] = conn_type @read_sockets.push sock @err_sockets.push sock @socket_buffer[sock] = "" return sock end
conn_reconnect(conn)
click to toggle source
# File lib/wafer.rb, line 60 def conn_reconnect(conn) @read_sockets -= [ conn ] @err_sockets -= [ conn ] socket_type = @socket_types[conn] @socket_types.delete(conn) @seq_numbers.delete(conn) @socket_buffer.delete(conn) begin log("Closing connection of type #{socket_type.inspect}...") conn.close rescue STDERR.puts $!.inspect log("Closing connection of type #{socket_type.inspect}... (But got an error, failing - this is common.)") end STDERR.puts "Reconnecting outgoing connection of type #{socket_type.inspect}..." conn_connect(socket_type) end
copy_of_default_settings()
click to toggle source
# File lib/wafer.rb, line 40 def copy_of_default_settings Hash[DEFAULT_SETTINGS.map { |key, value| [key, value.dup] }] end
ctl_respond(conn, parts)
click to toggle source
# File lib/wafer/ctl_server.rb, line 2 def ctl_respond(conn, parts) return send_error(conn, "BAD INPUT") if parts.size < 3 || parts.size > 9 @seq_numbers[conn] = parts[1] command = parts[0] if command == "announce" return send_ok(conn, "OK") end return send_error(conn, "UNIMPLEMENTED") end
event_loop()
click to toggle source
# File lib/wafer.rb, line 91 def event_loop puts "Settings:" puts JSON.pretty_generate(@settings) conn_connect(:auth) conn_connect(:ctl) loop do sleep 0.1 readable, _, errorable, = IO.select @read_sockets, [], @err_sockets, @settings["authServer"]["selectTimeout"] readable ||= [] errorable ||= [] puts "Selected... R: #{readable.size} / #{@read_sockets.size} E: #{errorable.size} / #{@err_sockets.size}" # Close connections on error errorable.each { |errant_conn| conn_reconnect(errant_conn) } (readable - errorable).each do |conn| STDERR.puts "Preparing for read..." data = conn.recv_nonblock(2048) if !data || data == "" STDERR.puts "No data - need to reconnect?" sleep 0.5 #conn_reconnect(conn) next end STDERR.puts "Successful read: #{data.inspect}" @socket_buffer[conn] += data while @socket_buffer[conn]["\r\n"] line, remainder = @socket_buffer[conn].split("\r\n", 2) @socket_buffer[conn] = remainder parts = line.chomp.strip.split(" ").map { |part| CGI::unescape(part) } next if parts == [] # No-op STDERR.puts "Successful parse: #{parts.inspect}" case @socket_types[conn] when :ctl ctl_respond(conn, parts) when :auth auth_respond(conn, parts) else log("Wrong socket type #{@socket_types[conn].inspect} for connection!") conn_disconnect(conn) end end end end end
log(message)
click to toggle source
# File lib/wafer.rb, line 44 def log(message) pre = "[#{Time.now}] " puts pre + message end
send_auth_status(conn, uid)
click to toggle source
# File lib/wafer/auth_server.rb, line 156 def send_auth_status(conn, uid) user_type = @repo.user_account_type(uid) user_status = @repo.user_account_status(uid) user_string = "(#{user_type};#{user_status})" if @repo.user_is_paid?(uid) if user_type == "trial" return send_ok(conn, "TRIAL #{@repo.user_next_stamp(uid)} #{user_string}") elsif ["developer", "staff", "free"].include?(user_type) return send_ok(conn, "PAID 0 #{user_string}") else return send_ok(conn, "PAID #{@repo.user_next_stamp(uid)} #{user_string}") end else return send_ok(conn, "UNPAID #{user_string}") end end
send_error(conn, message)
click to toggle source
# File lib/wafer.rb, line 79 def send_error(conn, message) seq = @seq_numbers[conn] log("Error on conn (#{seq}): #{message}") conn.write "#{seq} ERR #{message}\n" end
send_ok(conn, message)
click to toggle source
# File lib/wafer.rb, line 85 def send_ok(conn, message) ok_message = "#{@seq_numbers[conn]} OK #{message}\n" STDERR.puts "Sending OK message: #{ok_message.inspect}" conn.write ok_message end