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
133
134
|
require 'eventmachine'
require 'evma_httpserver'
require 'json'
#
# 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]
response.send_response
end
def send_key(uid)
get_key_from_uid(uid) do |key|
send_response content: format_response(address: uid, openpgp: key)
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 get_key_from_uid(uid)
fetcher = if local_address?(uid)
Nickserver::Couch::FetchKey.new
else
Nickserver::Hkp::FetchKey.new
end
fetcher.get(uid).callback {|key|
yield key
}.errback {|status, msg|
if status == 404
send_not_found
else
send_response(status: status, content: msg)
end
}
end
def format_response(map)
map.to_json
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
return false
end
end
end
|