1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
require 'kernel_ext'
require 'eventmachine'
silence_warnings do
require 'evma_httpserver'
end
require 'json'
require 'nickserver/couch_db/source'
require 'nickserver/hkp/source'
require 'nickserver/adapters/em_http'
#
# This is the main HTTP server that clients connect to in order to fetch keys
#
# For info on EM::HttpServer, see https://github.com/eventmachine/evma_httpserver
#
module Nickserver
class Server < EM::Connection
include EM::HttpServer
#
# Starts the Nickserver. Must be run inside an EM.run block.
#
# Available options:
#
# * :port (default Nickserver::Config.port)
# * :host (default 127.0.0.1)
#
def self.start(opts={})
Nickserver::Config.load
options = {host: '127.0.0.1', port: Nickserver::Config.port.to_i}.merge(opts)
unless defined?(TESTING)
puts "Starting nickserver #{options[:host]}:#{options[:port]}"
end
EM.start_server options[:host], options[:port], Nickserver::Server
end
def post_init
super
no_environment_strings
end
def process_http_request
uid = get_uid_from_request
if uid.nil?
send_not_found
elsif uid !~ EmailAddress
send_error("Not a valid address")
else
send_key(uid)
end
rescue RuntimeError => exc
puts "Error: #{exc}"
puts exc.backtrace
send_error(exc.to_s)
end
private
def send_error(msg = "not supported")
send_response(status: 500, content: "500 #{msg}\n")
end
def send_not_found(msg = "Not Found")
send_response(status: 404, content: "404 #{msg}\n")
end
def send_response(opts = {})
options = {status: 200, content_type: 'text/plain', content: ''}.merge(opts)
response = EM::DelegatedHttpResponse.new(self)
response.status = options[:status]
response.content_type options[:content_type]
response.content = options[:content]
silence_warnings do
response.send_response
end
end
def get_uid_from_request
if @http_query_string
params = CGI.parse(@http_query_string)
elsif @http_post_content
params = CGI.parse(@http_post_content)
end
if params && params["address"] && params["address"].any?
return params["address"].first
else
return nil
end
end
def send_key(uid)
if local_address?(uid)
source = Nickserver::CouchDB::Source.new(adapter)
else
source = Nickserver::Hkp::Source.new(adapter)
end
source.query(uid) do |response|
send_response(status: response.status, content: response.content)
end
end
#
# Return true if the user address is for a user of this service provider.
# e.g. if the provider is example.org, then alice@example.org returns true.
#
# If 'domain' is not configured, we rely on the Host header of the HTTP request.
#
def local_address?(uid)
uid_domain = uid.sub(/^.*@(.*)$/, "\\1")
if Config.domain
return uid_domain == Config.domain
else
# no domain configured, use Host header
host_header = @http_headers.split(/\0/).grep(/^Host: /).first
if host_header.nil?
send_error("HTTP request must include a Host header.")
else
host = host_header.split(':')[1].strip.sub(/^nicknym\./, '')
return uid_domain == host
end
end
rescue # XXX what are we rescueing here?
return false
end
def adapter
@adapter ||= Nickserver::Adapters::EmHttp.new
end
end
end
|