simple-gnupg-keyserver 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -1
- data/History.txt +0 -2
- data/Manifest.txt +4 -1
- data/README.rdoc +45 -40
- data/Rakefile +1 -0
- data/lib/simpleHKP/echo.rb +49 -8
- data/lib/simpleHKP/identities.rb +240 -0
- data/lib/simpleHKP/keys.rb +239 -0
- data/lib/simpleHKP/server.rb +349 -0
- data/lib/simpleHKP.rb +10 -489
- metadata +13 -9
@@ -0,0 +1,239 @@
|
|
1
|
+
|
2
|
+
module SimpleHKP
|
3
|
+
|
4
|
+
module Keys
|
5
|
+
|
6
|
+
def saveLastKey(lastKey)
|
7
|
+
lastKey['colonData'].gsub!(/\\x3a/,':') if
|
8
|
+
lastKey.has_key?('colonData')
|
9
|
+
pp lastKey if @debug
|
10
|
+
@keyLookupData[lastKey['keyID']] = lastKey unless
|
11
|
+
lastKey.empty? || lastKey['keyID'].empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def loadKeys
|
15
|
+
@keyLookupData = Hash.new
|
16
|
+
puts "SimpleHKP: loading keys" if @debug
|
17
|
+
gpg2cmd = "gpg2 --homedir #{@keyDir} --with-fingerprint --with-colons --list-sigs"
|
18
|
+
puts gpg2cmd if @debug
|
19
|
+
lastKey = Hash.new
|
20
|
+
IO.popen(gpg2cmd, 'r').readlines.each do | aLine |
|
21
|
+
next if aLine =~ /^tru/
|
22
|
+
if aLine =~ /^pub/ then
|
23
|
+
saveLastKey(lastKey)
|
24
|
+
lastKey = Hash.new
|
25
|
+
keyData = aLine.split(/:/)
|
26
|
+
lastKey['userID'] = keyData[9].gsub(/\\x3a/,':') # user ID
|
27
|
+
lastKey['keyID'] = keyData[4] # key ID
|
28
|
+
lastKey['algorithm'] = keyData[3] # key type (algorithm)
|
29
|
+
lastKey['len'] = keyData[2] # key length
|
30
|
+
lastKey['created'] = keyData[5] # creation date
|
31
|
+
lastKey['expires'] = keyData[6] # expiration date
|
32
|
+
lastKey['flags'] = keyData[1] # flags
|
33
|
+
lastKey['colonData'] = aLine
|
34
|
+
next
|
35
|
+
end
|
36
|
+
lastKey['userID'] = aLine.split(/:/)[9].gsub(/\\x3a/,':') if
|
37
|
+
aLine =~ /^uid/ && lastKey['userID'].empty?
|
38
|
+
lastKey['colonData'] << aLine
|
39
|
+
end
|
40
|
+
saveLastKey(lastKey)
|
41
|
+
@keyLookupData.each_pair do | key, value |
|
42
|
+
value['humanData'] = `gpg2 --list-sig #{key}`
|
43
|
+
end
|
44
|
+
pp @keyLookupData if @debug
|
45
|
+
puts "SimpleHKP: finished loading keys" if @debug
|
46
|
+
end
|
47
|
+
|
48
|
+
def storeKey(env)
|
49
|
+
#
|
50
|
+
# decode the post data
|
51
|
+
#
|
52
|
+
keys = URI.decode_www_form(env['rack.input'].read)
|
53
|
+
#
|
54
|
+
# ensure we are being sent a key
|
55
|
+
#
|
56
|
+
return replyBadRequest("No keytext field in post data") unless
|
57
|
+
keys[0][0] =~ /keytext/
|
58
|
+
pp keys[0][1] if @debug
|
59
|
+
#
|
60
|
+
# send the key data through gpg2 to import it
|
61
|
+
#
|
62
|
+
gpgStdErr = ""
|
63
|
+
Open3.popen3("gpg2 --homedir #{@keyDir} --import") do | stdin, stdout, stderr, wait_thread |
|
64
|
+
stdin.write(keys[0][1])
|
65
|
+
stdin.close
|
66
|
+
gpgStdErr = stderr.read
|
67
|
+
end
|
68
|
+
#
|
69
|
+
# check to see if the key has already been imported and/or changed
|
70
|
+
#
|
71
|
+
puts "[[#{gpgStdErr}]]" if @debug
|
72
|
+
keyChanged = "changed"
|
73
|
+
if gpgStdErr =~ /not\s+changed/ then
|
74
|
+
keyChanged = "unchanged"
|
75
|
+
else
|
76
|
+
# something has changed to re-load the keys
|
77
|
+
loadKeys
|
78
|
+
end
|
79
|
+
#
|
80
|
+
# send the key data through gpg2 (again ;-( to extract the keyID
|
81
|
+
#
|
82
|
+
gpgKeyData = ""
|
83
|
+
IO.popen('gpg2 --with-fingerprint --with-colons -', 'r+') do | pipe |
|
84
|
+
puts 'gpg2 --with-fingerprint --with-colons -' if @debug
|
85
|
+
pipe.write(keys[0][1])
|
86
|
+
pipe.close_write
|
87
|
+
gpgKeyData = pipe.read
|
88
|
+
end
|
89
|
+
puts gpgKeyData if @debug
|
90
|
+
#
|
91
|
+
# look for the keyID of the public key
|
92
|
+
#
|
93
|
+
keyID = nil
|
94
|
+
gpgKeyData.each_line do | aLine |
|
95
|
+
keyData = aLine.split(/:/)
|
96
|
+
next unless keyData[0] =~ /pub/
|
97
|
+
keyID = keyData[4]
|
98
|
+
break
|
99
|
+
end
|
100
|
+
return replyBadRequest("No keyID found in gpg2 result using uploaded keytext data") if keyID.nil?
|
101
|
+
#
|
102
|
+
# return OK
|
103
|
+
#
|
104
|
+
@statusCode = 200
|
105
|
+
@body << @headerHtml
|
106
|
+
@body << "<h1 class=\"simpleHKP-storedKey\">Stored key: [#{keyID}]</h1>"
|
107
|
+
@body << "<p>key #{keyChanged}</p>"
|
108
|
+
@body << "<h2 class=\"simpleHKP-keyDataKeyID\">Key data for key: [#{keyID}]</h2>"
|
109
|
+
@body << '<pre class="simpleHKP-keyData">'+@keyLookupData[keyID]['colonData']+"</pre>\n"
|
110
|
+
@body << '<pre class="simpleHKP-keyData">'+@keyLookupData[keyID]['humanData']+"</pre>\n"
|
111
|
+
@body << @footer
|
112
|
+
end
|
113
|
+
|
114
|
+
def lookUpKey(queryString)
|
115
|
+
return replyBadRequest("No search field in queryString") unless
|
116
|
+
queryString.has_key?('search')
|
117
|
+
#
|
118
|
+
# normalize the search string
|
119
|
+
#
|
120
|
+
searchString = queryString['search']
|
121
|
+
searchString.gsub!(/0x/,'')
|
122
|
+
searchRegexp = Regexp.new(searchString,
|
123
|
+
Regexp::IGNORECASE | Regexp::MULTILINE)
|
124
|
+
puts searchRegexp if @debug
|
125
|
+
#
|
126
|
+
# (linearly) look through the hash of known keys
|
127
|
+
# looking for the FIRST match
|
128
|
+
#
|
129
|
+
keyID = nil
|
130
|
+
@keyLookupData.each_pair do | key, keyData |
|
131
|
+
next unless keyData['colonData'] =~ searchRegexp
|
132
|
+
puts "FOUND #{key} (#{keyData})" if @debug
|
133
|
+
keyID = key
|
134
|
+
break
|
135
|
+
end
|
136
|
+
return replyNotFound if keyID.nil?
|
137
|
+
|
138
|
+
if queryString.has_key?('options') &&
|
139
|
+
queryString['options'] == 'mr' then
|
140
|
+
#
|
141
|
+
# return the key data in machine readable format
|
142
|
+
#
|
143
|
+
@header = {
|
144
|
+
'Content-Type' => 'application/pgp-keys; charset=utf-8',
|
145
|
+
'Content-Disposition' =>
|
146
|
+
'attachment; filename=' + keyID
|
147
|
+
}
|
148
|
+
puts @header if @debug
|
149
|
+
@body << `gpg2 --homedir #{@keyDir} --armor --export #{keyID}`
|
150
|
+
else
|
151
|
+
#
|
152
|
+
# return the key data for a human to read
|
153
|
+
#
|
154
|
+
@body << @headerHtml
|
155
|
+
@body << @lookupKeysForm
|
156
|
+
@body << '<h1 class="simpleHKP-keyAsckeyId">Key: '+keyID+'</h1>'
|
157
|
+
@body << '<h2 class="simpleHKP-keyDataTitle">Key data:</h2>'
|
158
|
+
@body << '<pre class="simpleHKP-keyData">'
|
159
|
+
@body << @keyLookupData[keyID]['humanData']
|
160
|
+
@body << '</pre>'
|
161
|
+
@body << '<h2 class="simpleHKP-keyAscTitle">Key contents:</h2>'
|
162
|
+
@body << '<pre class="simpleHKP-keyAsc">'
|
163
|
+
@body << `gpg2 --homedir #{@keyDir} --armor --export #{keyID}`
|
164
|
+
@body << '</pre>'
|
165
|
+
@body << @footer
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def indexKeys(queryString)
|
170
|
+
return replyBadRequest("No search field in queryString") unless
|
171
|
+
queryString.has_key?('search')
|
172
|
+
#
|
173
|
+
# normalize the search string
|
174
|
+
#
|
175
|
+
searchString = queryString['search']
|
176
|
+
searchString.gsub!(/0x/,'')
|
177
|
+
searchRegexp = Regexp.new(searchString,
|
178
|
+
Regexp::IGNORECASE | Regexp::MULTILINE)
|
179
|
+
puts searchRegexp if @debug
|
180
|
+
#
|
181
|
+
# accumulate the keyIDs of any keys that match the serach
|
182
|
+
#
|
183
|
+
keys = Array.new
|
184
|
+
@keyLookupData.each_pair do | key, keyData |
|
185
|
+
next unless keyData['colonData'] =~ searchRegexp
|
186
|
+
puts "FOUND #{key} (#{keyData})" if @debug
|
187
|
+
keys.push(key)
|
188
|
+
end
|
189
|
+
|
190
|
+
if queryString.has_key?('options') &&
|
191
|
+
queryString['options'] == 'mr' then
|
192
|
+
#
|
193
|
+
# return a machine readable list of the keys found
|
194
|
+
#
|
195
|
+
@header = { 'Content-Type' => 'text/plain' }
|
196
|
+
@body << "info:1:#{keys.size}\n"
|
197
|
+
keys.each do | aKey |
|
198
|
+
next unless @keyLookupData.has_key?(aKey)
|
199
|
+
keyData = @keyLookupData[aKey]
|
200
|
+
pubData = [ "pub" ]
|
201
|
+
pubData << aKey
|
202
|
+
pubData << keyData['algorithm']
|
203
|
+
pubData << keyData['len']
|
204
|
+
pubData << keyData['created']
|
205
|
+
pubData << keyData['expires']
|
206
|
+
pubData << keyData['flags']
|
207
|
+
@body << pubData.join(':')
|
208
|
+
@body << "\n"
|
209
|
+
uidData = [ "uid" ]
|
210
|
+
uidData << keyData['userID'].gsub(/:/, '\x3a')
|
211
|
+
uidData << keyData['created']
|
212
|
+
uidData << keyData['expires']
|
213
|
+
uidData << keyData['flags']
|
214
|
+
@body << uidData.join(':')
|
215
|
+
@body << "\n"
|
216
|
+
end
|
217
|
+
else
|
218
|
+
#
|
219
|
+
# return a (simple) human readable list of the keys found
|
220
|
+
#
|
221
|
+
@body << @headerHtml
|
222
|
+
@body << @lookupKeysForm
|
223
|
+
@body << '<h1 class="simpleHKP-searchString">Keys matching: ['+searchString+']</h1><ul class="simpleHKP-searchList">'
|
224
|
+
keys.each do | aKey |
|
225
|
+
next unless @keyLookupData.has_key?(aKey)
|
226
|
+
keyData = @keyLookupData[aKey]
|
227
|
+
keyStr = " <li class=\"simpleHKP-searchItem\"><a href=\"lookup?op=get&search=#{aKey}\">#{aKey}: "
|
228
|
+
keyStr << keyData['userID']
|
229
|
+
keyStr << "</a></li>\n"
|
230
|
+
@body << keyStr
|
231
|
+
end
|
232
|
+
@body << '</ul>'
|
233
|
+
@body << @footer
|
234
|
+
end
|
235
|
+
pp @body if @debug
|
236
|
+
end
|
237
|
+
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,349 @@
|
|
1
|
+
# Copyright (C) 2015 Stephen Gaito
|
2
|
+
#
|
3
|
+
# (The MIT License)
|
4
|
+
#
|
5
|
+
# Copyright (c) 2015 Stephen Gaito
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a
|
8
|
+
# copy of this software and associated documentation files (the
|
9
|
+
# 'Software'), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be included
|
16
|
+
# in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
19
|
+
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
21
|
+
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
22
|
+
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
23
|
+
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
24
|
+
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module SimpleHKP
|
27
|
+
class Server
|
28
|
+
include Keys
|
29
|
+
include Identities
|
30
|
+
|
31
|
+
def loadHtml
|
32
|
+
puts "SimpleHKP: loading html partials" if @debug
|
33
|
+
Dir.chdir(@htmlDir) do
|
34
|
+
@headerHtml = "<html><head>"
|
35
|
+
@headerHtml << " <title>#{@title}</title>\n"
|
36
|
+
@headerHtml << "</head><body class=\"simpleHKP-body\">\n"
|
37
|
+
@headerHtml << "<p><a href=\"/\">#{@title}</a></p>"
|
38
|
+
@headerHtml = File.open('header.html','r').read if
|
39
|
+
File.exists?('header.html')
|
40
|
+
puts @headerHtml if @debug
|
41
|
+
|
42
|
+
@defaultBody = ""
|
43
|
+
@defaultBody << "<h1 class=\"simpleHKP-welcome\">Welcome to SimpleHKP</h1>\n"
|
44
|
+
@defaultBody << "<ul class=\"simpleHKP-tasksList\">\n"
|
45
|
+
@defaultBody << " <li class=\"simpleHKP-taskItem\"><a href=\"/pks/lookup?op=form\">Search keys</a></li>\n"
|
46
|
+
@defaultBody << " <li class=\"simpleHKP-taskItem\"><a href=\"/pks/add\">Upload new key</a></li>\n"
|
47
|
+
@defaultBody << " <li class=\"simpleHKP-taskItem\"><a href=\"/pis/lookup?op=form\">Search identities</a></li>\n"
|
48
|
+
@defaultBody << " <li class=\"simpleHKP-taskItem\"><a href=\"/pis/add\">Upload new identity</a></li>\n"
|
49
|
+
@defaultBody << " <li class=\"simpleHKP-taskItem\"><a href=\"reload\">Reload existing keys and identities</a></li>\n"
|
50
|
+
@defaultBody << "</ul>\n"
|
51
|
+
@defaultBody = File.open('defaultBody.html','r').read if
|
52
|
+
File.exists?('defaultBody.html')
|
53
|
+
puts @defaultBody if @debug
|
54
|
+
|
55
|
+
@lookupKeysForm = ""
|
56
|
+
@lookupKeysForm << "<div class=\"simpleHKP-lookupKeysFormDiv\">\n"
|
57
|
+
@lookupKeysForm << " <h1 class=\"simpleHKP-lookupKeysFormTitle\">Search GnuPG keys</h1>\n"
|
58
|
+
@lookupKeysForm << " <form action=\"/pks/lookup\" method=\"get\" id=\"simpleHKP-lookupKeysForm\">\n"
|
59
|
+
@lookupKeysForm << " <input type=\"text\" name=\"search\" size=\"80\" />\n"
|
60
|
+
@lookupKeysForm << " <input type=\"hidden\" name=\"op\" value=\"index\" />\n"
|
61
|
+
@lookupKeysForm << " <input type=\"submit\" value=\"Search\" />\n"
|
62
|
+
@lookupKeysForm << " </form>\n"
|
63
|
+
@lookupKeysForm << "</div>\n"
|
64
|
+
@lookupKeysForm = File.open('lookupKeysForm.html','r').read if
|
65
|
+
File.exists?('lookupKeysForm.html')
|
66
|
+
puts @lookupKeysForm if @debug
|
67
|
+
|
68
|
+
@lookupIdentitiesForm = ""
|
69
|
+
@lookupIdentitiesForm << "<div class=\"simpleHKP-lookupIdentitiesFormDiv\">\n"
|
70
|
+
@lookupIdentitiesForm << " <h1 class=\"simpleHKP-lookupIdentitiesFormTitle\">Search GnuPG keys</h1>\n"
|
71
|
+
@lookupIdentitiesForm << " <form action=\"/pis/lookup\" method=\"get\" id=\"simpleHKP-lookupIdentitiesForm\">\n"
|
72
|
+
@lookupIdentitiesForm << " <input type=\"text\" name=\"search\" size=\"80\" />\n"
|
73
|
+
@lookupIdentitiesForm << " <input type=\"hidden\" name=\"op\" value=\"index\" />\n"
|
74
|
+
@lookupIdentitiesForm << " <input type=\"submit\" value=\"Search\" />\n"
|
75
|
+
@lookupIdentitiesForm << " </form>\n"
|
76
|
+
@lookupIdentitiesForm << "</div>\n"
|
77
|
+
@lookupIdentitiesForm = File.open('lookupIdentitiesForm.html','r').read if
|
78
|
+
File.exists?('lookupIdentitiesForm.html')
|
79
|
+
puts @lookupIdentitiesForm if @debug
|
80
|
+
|
81
|
+
@uploadKeyForm = ""
|
82
|
+
@uploadKeyForm << "<div class=\"simpleHKP-uploadKeyFormDiv\">\n"
|
83
|
+
@uploadKeyForm << " <h1 class=\"simpleHKP-uploadKeyFormTitle\">Paste GnuPG key to be uploaded below</h1>\n"
|
84
|
+
@uploadKeyForm << " <form action=\"/pks/add\" method=\"post\" id=\"simpleHKP-uploadKeyForm\">\n"
|
85
|
+
@uploadKeyForm << " <textarea name=\"keytext\" form=\"simpleHKP-uploadKeyForm\" rows = \"30\" cols=\"70\" ></textarea>\n"
|
86
|
+
@uploadKeyForm << " <input type=\"submit\" value=\"Upload\" />\n"
|
87
|
+
@uploadKeyForm << " </form>\n"
|
88
|
+
@uploadKeyForm << "</div>\n"
|
89
|
+
@uploadKeyForm = File.open('uploadKeyForm.html','r').read if
|
90
|
+
File.exists?('uploadKeyForm.html')
|
91
|
+
puts @uploadKeyForm if @debug
|
92
|
+
|
93
|
+
@uploadIdentityForm = ""
|
94
|
+
@uploadIdentityForm << "<div class=\"simpleHKP-uploadIdentityFormDiv\">\n"
|
95
|
+
@uploadIdentityForm << " <h1 class=\"simpleHKP-uploadIdentityFormTitle\">Paste GnuPG encrypted identity to be uploaded below</h1>\n"
|
96
|
+
@uploadIdentityForm << " <form action=\"/pis/add\" method=\"post\" id=\"simpleHKP-uploadIdentityForm\">\n"
|
97
|
+
@uploadIdentityForm << " <textarea name=\"keytext\" form=\"simpleHKP-uploadIdentityForm\" rows = \"30\" cols=\"70\" ></textarea>\n"
|
98
|
+
@uploadIdentityForm << " <input type=\"submit\" value=\"Upload\" />\n"
|
99
|
+
@uploadIdentityForm << " </form>\n"
|
100
|
+
@uploadIdentityForm << "</div>\n"
|
101
|
+
@uploadIdentityForm = File.open('uploadIdentityForm.html','r').read if
|
102
|
+
File.exists?('uploadIdentityForm.html')
|
103
|
+
puts @uploadIdentityForm if @debug
|
104
|
+
|
105
|
+
@footer = "<p style=\"color:grey\">SimpleHKP version: #{SimpleHKP::VERSION}</p></body></html>\n"
|
106
|
+
@footer = File.open('footer.html','r').read if
|
107
|
+
File.exists?('footer.html')
|
108
|
+
puts @footer if @debug
|
109
|
+
end
|
110
|
+
puts "SimpleHKP: finished loading html partials" if @debug
|
111
|
+
end
|
112
|
+
|
113
|
+
def initialize(options = {})
|
114
|
+
#
|
115
|
+
# setup the default options
|
116
|
+
#
|
117
|
+
defaultOptions = {
|
118
|
+
'debug' => false,
|
119
|
+
'title' => 'SimpleHKP',
|
120
|
+
'simpleHKPdir' => 'simpleHKP',
|
121
|
+
'keyDir' => 'keys',
|
122
|
+
'idDir' => 'identities',
|
123
|
+
# 'idIndex' => 'identities/idIndex.yaml',
|
124
|
+
'mediaDir' => 'media',
|
125
|
+
'htmlDir' => 'html',
|
126
|
+
'mimeMap' => {
|
127
|
+
'css' => 'text/css',
|
128
|
+
'html' => 'text/html',
|
129
|
+
'js' => 'text/javascript'
|
130
|
+
}
|
131
|
+
}
|
132
|
+
#
|
133
|
+
# merge the options mimeMap into the default options mimeMap
|
134
|
+
#
|
135
|
+
defaultOptions['mimeMap'].merge!(delete(options['mimeMap'])) if
|
136
|
+
options.has_key?('mimeMap')
|
137
|
+
#
|
138
|
+
# merge in the rest of the options into the default options
|
139
|
+
#
|
140
|
+
@options = defaultOptions.merge(options)
|
141
|
+
#
|
142
|
+
# setup the required variables
|
143
|
+
#
|
144
|
+
@debug = @options['debug']
|
145
|
+
@title = @options['title']
|
146
|
+
@baseDir = @options['simpleHKPdir']
|
147
|
+
@keyDir = @baseDir+'/'+@options['keyDir']
|
148
|
+
@idDir = @baseDir+'/'+@options['idDir']
|
149
|
+
# @idIndex = @baseDir+'/'+@options['idIndex']
|
150
|
+
@mediaDir = @baseDir+'/'+@options['mediaDir']
|
151
|
+
@htmlDir = @baseDir+'/'+@options['htmlDir']
|
152
|
+
@mimeMap = @options['mimeMap']
|
153
|
+
if @debug then
|
154
|
+
puts "SimpleHKP options:"
|
155
|
+
pp @options
|
156
|
+
end
|
157
|
+
#
|
158
|
+
# ensure the required directories all exist
|
159
|
+
#
|
160
|
+
FileUtils.mkdir_p(@keyDir)
|
161
|
+
FileUtils.chmod(0700, @keyDir)
|
162
|
+
FileUtils.mkdir_p(@idDir)
|
163
|
+
FileUtils.chmod(0700, @idDir)
|
164
|
+
FileUtils.mkdir_p(@mediaDir)
|
165
|
+
FileUtils.mkdir_p(@htmlDir)
|
166
|
+
#
|
167
|
+
# load the existing keys and the html partials
|
168
|
+
loadKeys
|
169
|
+
loadIdentities
|
170
|
+
loadHtml
|
171
|
+
end
|
172
|
+
|
173
|
+
def ppEnv(env)
|
174
|
+
Dir.pwd+' '+
|
175
|
+
# '['+URI.decode_www_form(env["rack.input"].rewind.read).pretty_inspect+']'+
|
176
|
+
env.pretty_inspect
|
177
|
+
end
|
178
|
+
|
179
|
+
def replyInternalError(env, exception)
|
180
|
+
@statusCode = 500
|
181
|
+
@body << @headerHtml
|
182
|
+
@body << "<p>Internal server error!</p>\n"
|
183
|
+
@body << "<pre>#{exception}</pre>\n" if @debug
|
184
|
+
@body << @footer
|
185
|
+
if @debug then
|
186
|
+
puts "\n\n-----------------------------------------------------------"
|
187
|
+
puts exception
|
188
|
+
puts "-----------------------------------------------------------"
|
189
|
+
puts ppEnv(env)
|
190
|
+
puts "-----------------------------------------------------------\n\n"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def replyNotImplemented
|
195
|
+
@statusCode = 501
|
196
|
+
@body << @headerHtml
|
197
|
+
@body << '<p>Not implemented!<p>'
|
198
|
+
@body << @footer
|
199
|
+
end
|
200
|
+
|
201
|
+
def replyBadRequest(message)
|
202
|
+
@statusCode = 400
|
203
|
+
@body << @headerHtml
|
204
|
+
@body << '<p>Bad request!</p>'
|
205
|
+
@body << "<p>#{message}</p>"
|
206
|
+
@body << @footer
|
207
|
+
end
|
208
|
+
|
209
|
+
def replyNotFound
|
210
|
+
@statusCode = 404
|
211
|
+
@body << @headerHtml
|
212
|
+
@body << '<p>Not found!</p>'
|
213
|
+
@body << @footer
|
214
|
+
end
|
215
|
+
|
216
|
+
def replyDefaultBody
|
217
|
+
@body << @headerHtml
|
218
|
+
@body << @defaultBody
|
219
|
+
@body << @footer
|
220
|
+
end
|
221
|
+
|
222
|
+
def replyKeyLookupForm
|
223
|
+
@body << @headerHtml
|
224
|
+
@body << @lookupKeysForm
|
225
|
+
@body << @footer
|
226
|
+
end
|
227
|
+
|
228
|
+
def replyIdentityLookupForm
|
229
|
+
@body << @headerHtml
|
230
|
+
@body << @lookupIdentitiesForm
|
231
|
+
@body << @footer
|
232
|
+
end
|
233
|
+
|
234
|
+
def replyKeyUploadForm
|
235
|
+
@body << @headerHtml
|
236
|
+
@body << @uploadKeyForm
|
237
|
+
@body << @footer
|
238
|
+
end
|
239
|
+
|
240
|
+
def replyIdentityUploadForm
|
241
|
+
@body << @headerHtml
|
242
|
+
@body << @uploadIdentityForm
|
243
|
+
@body << @footer
|
244
|
+
end
|
245
|
+
|
246
|
+
def replyFile(env)
|
247
|
+
fileName = env['REQUEST_PATH'].sub(/^.*\/media\//,'')
|
248
|
+
if File.exists?(fileName) then
|
249
|
+
fileExt = File.extname(fileName).sub(/^\./,'')
|
250
|
+
@header['Content-Type'] = @mimeMap[fileExt] if
|
251
|
+
@mimeMap.has_key?(fileExt)
|
252
|
+
@body << File.open(fileName,'r').read
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def decodeQueryString(env)
|
257
|
+
queryHash = Hash.new
|
258
|
+
URI.decode_www_form(env['QUERY_STRING']).each do | aKeyValue |
|
259
|
+
queryHash[aKeyValue[0]] = aKeyValue[1]
|
260
|
+
end
|
261
|
+
queryHash
|
262
|
+
end
|
263
|
+
|
264
|
+
def call(env)
|
265
|
+
#
|
266
|
+
# initialize the response parts
|
267
|
+
#
|
268
|
+
@statusCode = 200
|
269
|
+
@header = {"Content-Type" => "text/html; charset=utf-8"}
|
270
|
+
@body = Array.new
|
271
|
+
#
|
272
|
+
# decode the request
|
273
|
+
#
|
274
|
+
begin
|
275
|
+
puts ppEnv(env) if @debug
|
276
|
+
case env['REQUEST_METHOD']
|
277
|
+
when 'POST'
|
278
|
+
case env['REQUEST_PATH']
|
279
|
+
when /^\/pks\/add$/i
|
280
|
+
storeKey(env)
|
281
|
+
when /^\/pis\/add$/i
|
282
|
+
storeIdentity(env)
|
283
|
+
else
|
284
|
+
replyNotImplemented
|
285
|
+
end
|
286
|
+
when 'GET'
|
287
|
+
case env['REQUEST_PATH']
|
288
|
+
when /^\/pks\/add$/i
|
289
|
+
replyKeyUploadForm
|
290
|
+
when /^\/pis\/add$/i
|
291
|
+
replyIdentityUploadForm
|
292
|
+
when /^\/pks\/lookup$/i
|
293
|
+
queryString = decodeQueryString(env)
|
294
|
+
if queryString.has_key?('op') then
|
295
|
+
case queryString['op']
|
296
|
+
when /get/i
|
297
|
+
lookUpKey(queryString)
|
298
|
+
when /index/i
|
299
|
+
indexKeys(queryString)
|
300
|
+
else
|
301
|
+
replyKeyLookupForm
|
302
|
+
end
|
303
|
+
else
|
304
|
+
replyBadRequest("No op field in queryString")
|
305
|
+
end
|
306
|
+
when /^\/pis\/lookup$/i
|
307
|
+
queryString = decodeQueryString(env)
|
308
|
+
if queryString.has_key?('op') then
|
309
|
+
case queryString['op']
|
310
|
+
when /get/i
|
311
|
+
lookUpIdentity(queryString)
|
312
|
+
when /index/i
|
313
|
+
indexIdentities(queryString)
|
314
|
+
else
|
315
|
+
replyIdentityLookupForm
|
316
|
+
end
|
317
|
+
else
|
318
|
+
replyBadRequest("No op field in queryString")
|
319
|
+
end
|
320
|
+
when /media\//i
|
321
|
+
replyFile(env)
|
322
|
+
when /reload$/i
|
323
|
+
loadKeys
|
324
|
+
loadIdentities
|
325
|
+
loadHtml
|
326
|
+
replyDefaultBody
|
327
|
+
else
|
328
|
+
replyDefaultBody
|
329
|
+
end
|
330
|
+
else
|
331
|
+
replyBadRequest("Unknown request method")
|
332
|
+
end
|
333
|
+
rescue Exception => exception
|
334
|
+
replyInternalError(env, exception)
|
335
|
+
end
|
336
|
+
#
|
337
|
+
# send the response
|
338
|
+
#
|
339
|
+
if @debug then
|
340
|
+
puts "SimpleHKP reply:"
|
341
|
+
pp @statusCode
|
342
|
+
pp @header
|
343
|
+
puts @body.join("\n")
|
344
|
+
end
|
345
|
+
[ @statusCode, @header, @body.flatten ]
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
end
|