webrick 1.3.1 → 1.6.1
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.
Potentially problematic release.
This version of webrick might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/webrick.rb +7 -7
- data/lib/webrick/accesslog.rb +12 -6
- data/lib/webrick/cgi.rb +58 -5
- data/lib/webrick/compat.rb +2 -1
- data/lib/webrick/config.rb +47 -10
- data/lib/webrick/cookie.rb +69 -7
- data/lib/webrick/htmlutils.rb +4 -2
- data/lib/webrick/httpauth.rb +6 -5
- data/lib/webrick/httpauth/authenticator.rb +13 -8
- data/lib/webrick/httpauth/basicauth.rb +16 -8
- data/lib/webrick/httpauth/digestauth.rb +35 -32
- data/lib/webrick/httpauth/htdigest.rb +12 -8
- data/lib/webrick/httpauth/htgroup.rb +10 -6
- data/lib/webrick/httpauth/htpasswd.rb +46 -9
- data/lib/webrick/httpauth/userdb.rb +1 -0
- data/lib/webrick/httpproxy.rb +93 -48
- data/lib/webrick/httprequest.rb +201 -31
- data/lib/webrick/httpresponse.rb +235 -70
- data/lib/webrick/https.rb +90 -2
- data/lib/webrick/httpserver.rb +45 -15
- data/lib/webrick/httpservlet.rb +6 -5
- data/lib/webrick/httpservlet/abstract.rb +5 -6
- data/lib/webrick/httpservlet/cgi_runner.rb +3 -2
- data/lib/webrick/httpservlet/cgihandler.rb +29 -11
- data/lib/webrick/httpservlet/erbhandler.rb +4 -3
- data/lib/webrick/httpservlet/filehandler.rb +136 -65
- data/lib/webrick/httpservlet/prochandler.rb +15 -1
- data/lib/webrick/httpstatus.rb +24 -14
- data/lib/webrick/httputils.rb +134 -17
- data/lib/webrick/httpversion.rb +28 -1
- data/lib/webrick/log.rb +25 -5
- data/lib/webrick/server.rb +234 -74
- data/lib/webrick/ssl.rb +100 -12
- data/lib/webrick/utils.rb +98 -69
- data/lib/webrick/version.rb +6 -1
- data/webrick.gemspec +76 -0
- metadata +73 -72
- data/README.txt +0 -21
- data/sample/webrick/demo-app.rb +0 -66
- data/sample/webrick/demo-multipart.cgi +0 -12
- data/sample/webrick/demo-servlet.rb +0 -6
- data/sample/webrick/demo-urlencoded.cgi +0 -12
- data/sample/webrick/hello.cgi +0 -11
- data/sample/webrick/hello.rb +0 -8
- data/sample/webrick/httpd.rb +0 -23
- data/sample/webrick/httpproxy.rb +0 -25
- data/sample/webrick/httpsd.rb +0 -33
- data/test/openssl/utils.rb +0 -313
- data/test/ruby/envutil.rb +0 -208
- data/test/webrick/test_cgi.rb +0 -134
- data/test/webrick/test_cookie.rb +0 -131
- data/test/webrick/test_filehandler.rb +0 -285
- data/test/webrick/test_httpauth.rb +0 -167
- data/test/webrick/test_httpproxy.rb +0 -282
- data/test/webrick/test_httprequest.rb +0 -411
- data/test/webrick/test_httpresponse.rb +0 -49
- data/test/webrick/test_httpserver.rb +0 -305
- data/test/webrick/test_httputils.rb +0 -96
- data/test/webrick/test_httpversion.rb +0 -40
- data/test/webrick/test_server.rb +0 -67
- data/test/webrick/test_utils.rb +0 -64
- data/test/webrick/utils.rb +0 -58
- data/test/webrick/webrick.cgi +0 -36
- data/test/webrick/webrick_long_filename.cgi +0 -36
data/lib/webrick/cookie.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# cookie.rb -- Cookie class
|
3
4
|
#
|
@@ -9,17 +10,59 @@
|
|
9
10
|
# $IPR: cookie.rb,v 1.16 2002/09/21 12:23:35 gotoyuzo Exp $
|
10
11
|
|
11
12
|
require 'time'
|
12
|
-
|
13
|
+
require_relative 'httputils'
|
13
14
|
|
14
15
|
module WEBrick
|
16
|
+
|
17
|
+
##
|
18
|
+
# Processes HTTP cookies
|
19
|
+
|
15
20
|
class Cookie
|
16
21
|
|
22
|
+
##
|
23
|
+
# The cookie name
|
24
|
+
|
17
25
|
attr_reader :name
|
18
|
-
|
19
|
-
|
20
|
-
|
26
|
+
|
27
|
+
##
|
28
|
+
# The cookie value
|
29
|
+
|
30
|
+
attr_accessor :value
|
31
|
+
|
32
|
+
##
|
33
|
+
# The cookie version
|
34
|
+
|
35
|
+
attr_accessor :version
|
36
|
+
|
37
|
+
##
|
38
|
+
# The cookie domain
|
39
|
+
attr_accessor :domain
|
40
|
+
|
41
|
+
##
|
42
|
+
# The cookie path
|
43
|
+
|
44
|
+
attr_accessor :path
|
45
|
+
|
46
|
+
##
|
47
|
+
# Is this a secure cookie?
|
48
|
+
|
49
|
+
attr_accessor :secure
|
50
|
+
|
51
|
+
##
|
52
|
+
# The cookie comment
|
53
|
+
|
54
|
+
attr_accessor :comment
|
55
|
+
|
56
|
+
##
|
57
|
+
# The maximum age of the cookie
|
58
|
+
|
59
|
+
attr_accessor :max_age
|
60
|
+
|
21
61
|
#attr_accessor :comment_url, :discard, :port
|
22
62
|
|
63
|
+
##
|
64
|
+
# Creates a new cookie with the given +name+ and +value+
|
65
|
+
|
23
66
|
def initialize(name, value)
|
24
67
|
@name = name
|
25
68
|
@value = value
|
@@ -29,14 +72,25 @@ module WEBrick
|
|
29
72
|
@expires = @comment_url = @discard = @port = nil
|
30
73
|
end
|
31
74
|
|
75
|
+
##
|
76
|
+
# Sets the cookie expiration to the time +t+. The expiration time may be
|
77
|
+
# a false value to disable expiration or a Time or HTTP format time string
|
78
|
+
# to set the expiration date.
|
79
|
+
|
32
80
|
def expires=(t)
|
33
81
|
@expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
|
34
82
|
end
|
35
83
|
|
84
|
+
##
|
85
|
+
# Retrieves the expiration time as a Time
|
86
|
+
|
36
87
|
def expires
|
37
88
|
@expires && Time.parse(@expires)
|
38
89
|
end
|
39
90
|
|
91
|
+
##
|
92
|
+
# The cookie string suitable for use in an HTTP header
|
93
|
+
|
40
94
|
def to_s
|
41
95
|
ret = ""
|
42
96
|
ret << @name << "=" << @value
|
@@ -50,14 +104,16 @@ module WEBrick
|
|
50
104
|
ret
|
51
105
|
end
|
52
106
|
|
53
|
-
|
54
|
-
#
|
107
|
+
##
|
108
|
+
# Parses a Cookie field sent from the user-agent. Returns an array of
|
109
|
+
# cookies.
|
110
|
+
|
55
111
|
def self.parse(str)
|
56
112
|
if str
|
57
113
|
ret = []
|
58
114
|
cookie = nil
|
59
115
|
ver = 0
|
60
|
-
str.split(
|
116
|
+
str.split(/;\s+/).each{|x|
|
61
117
|
key, val = x.split(/=/,2)
|
62
118
|
val = val ? HTTPUtils::dequote(val) : ""
|
63
119
|
case key
|
@@ -76,6 +132,9 @@ module WEBrick
|
|
76
132
|
end
|
77
133
|
end
|
78
134
|
|
135
|
+
##
|
136
|
+
# Parses the cookie in +str+
|
137
|
+
|
79
138
|
def self.parse_set_cookie(str)
|
80
139
|
cookie_elem = str.split(/;/)
|
81
140
|
first_elem = cookie_elem.shift
|
@@ -101,6 +160,9 @@ module WEBrick
|
|
101
160
|
return cookie
|
102
161
|
end
|
103
162
|
|
163
|
+
##
|
164
|
+
# Parses the cookies in +str+
|
165
|
+
|
104
166
|
def self.parse_set_cookies(str)
|
105
167
|
return str.split(/,(?=[^;,]*=)|,$/).collect{|c|
|
106
168
|
parse_set_cookie(c)
|
data/lib/webrick/htmlutils.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#--
|
2
3
|
# htmlutils.rb -- HTMLUtils Module
|
3
4
|
#
|
@@ -15,12 +16,13 @@ module WEBrick
|
|
15
16
|
# Escapes &, ", > and < in +string+
|
16
17
|
|
17
18
|
def escape(string)
|
18
|
-
|
19
|
+
return "" unless string
|
20
|
+
str = string.b
|
19
21
|
str.gsub!(/&/n, '&')
|
20
22
|
str.gsub!(/\"/n, '"')
|
21
23
|
str.gsub!(/>/n, '>')
|
22
24
|
str.gsub!(/</n, '<')
|
23
|
-
str
|
25
|
+
str.force_encoding(string.encoding)
|
24
26
|
end
|
25
27
|
module_function :escape
|
26
28
|
|
data/lib/webrick/httpauth.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpauth.rb -- HTTP access authentication
|
3
4
|
#
|
@@ -8,11 +9,11 @@
|
|
8
9
|
#
|
9
10
|
# $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
require_relative 'httpauth/basicauth'
|
13
|
+
require_relative 'httpauth/digestauth'
|
14
|
+
require_relative 'httpauth/htpasswd'
|
15
|
+
require_relative 'httpauth/htdigest'
|
16
|
+
require_relative 'httpauth/htgroup'
|
16
17
|
|
17
18
|
module WEBrick
|
18
19
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#--
|
2
3
|
# httpauth/authenticator.rb -- Authenticator mix-in module.
|
3
4
|
#
|
@@ -16,10 +17,10 @@ module WEBrick
|
|
16
17
|
|
17
18
|
module Authenticator
|
18
19
|
|
19
|
-
RequestField = "Authorization"
|
20
|
-
ResponseField = "WWW-Authenticate"
|
21
|
-
ResponseInfoField = "Authentication-Info"
|
22
|
-
AuthException = HTTPStatus::Unauthorized
|
20
|
+
RequestField = "Authorization" # :nodoc:
|
21
|
+
ResponseField = "WWW-Authenticate" # :nodoc:
|
22
|
+
ResponseInfoField = "Authentication-Info" # :nodoc:
|
23
|
+
AuthException = HTTPStatus::Unauthorized # :nodoc:
|
23
24
|
|
24
25
|
##
|
25
26
|
# Method of authentication, must be overridden by the including class
|
@@ -43,6 +44,8 @@ module WEBrick
|
|
43
44
|
|
44
45
|
private
|
45
46
|
|
47
|
+
# :stopdoc:
|
48
|
+
|
46
49
|
##
|
47
50
|
# Initializes the authenticator from +config+
|
48
51
|
|
@@ -96,6 +99,8 @@ module WEBrick
|
|
96
99
|
log(:info, fmt, *args)
|
97
100
|
end
|
98
101
|
end
|
102
|
+
|
103
|
+
# :startdoc:
|
99
104
|
end
|
100
105
|
|
101
106
|
##
|
@@ -103,10 +108,10 @@ module WEBrick
|
|
103
108
|
# authentication schemes for proxies.
|
104
109
|
|
105
110
|
module ProxyAuthenticator
|
106
|
-
RequestField = "Proxy-Authorization"
|
107
|
-
ResponseField = "Proxy-Authenticate"
|
108
|
-
InfoField = "Proxy-Authentication-Info"
|
109
|
-
AuthException = HTTPStatus::ProxyAuthenticationRequired
|
111
|
+
RequestField = "Proxy-Authorization" # :nodoc:
|
112
|
+
ResponseField = "Proxy-Authenticate" # :nodoc:
|
113
|
+
InfoField = "Proxy-Authentication-Info" # :nodoc:
|
114
|
+
AuthException = HTTPStatus::ProxyAuthenticationRequired # :nodoc:
|
110
115
|
end
|
111
116
|
end
|
112
117
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpauth/basicauth.rb -- HTTP basic access authentication
|
3
4
|
#
|
@@ -7,9 +8,9 @@
|
|
7
8
|
#
|
8
9
|
# $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
require_relative '../config'
|
12
|
+
require_relative '../httpstatus'
|
13
|
+
require_relative 'authenticator'
|
13
14
|
|
14
15
|
module WEBrick
|
15
16
|
module HTTPAuth
|
@@ -23,7 +24,7 @@ module WEBrick
|
|
23
24
|
#
|
24
25
|
# config = { :Realm => 'BasicAuth example realm' }
|
25
26
|
#
|
26
|
-
# htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
|
27
|
+
# htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt
|
27
28
|
# htpasswd.set_passwd config[:Realm], 'username', 'password'
|
28
29
|
# htpasswd.flush
|
29
30
|
#
|
@@ -34,7 +35,7 @@ module WEBrick
|
|
34
35
|
class BasicAuth
|
35
36
|
include Authenticator
|
36
37
|
|
37
|
-
AuthScheme = "Basic"
|
38
|
+
AuthScheme = "Basic" # :nodoc:
|
38
39
|
|
39
40
|
##
|
40
41
|
# Used by UserDB to create a basic password entry
|
@@ -80,7 +81,15 @@ module WEBrick
|
|
80
81
|
error("%s: the user is not allowed.", userid)
|
81
82
|
challenge(req, res)
|
82
83
|
end
|
83
|
-
|
84
|
+
|
85
|
+
case encpass
|
86
|
+
when /\A\$2[aby]\$/
|
87
|
+
password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password
|
88
|
+
else
|
89
|
+
password_matches = password.crypt(encpass) == encpass
|
90
|
+
end
|
91
|
+
|
92
|
+
unless password_matches
|
84
93
|
error("%s: password unmatch.", userid)
|
85
94
|
challenge(req, res)
|
86
95
|
end
|
@@ -89,8 +98,7 @@ module WEBrick
|
|
89
98
|
end
|
90
99
|
|
91
100
|
##
|
92
|
-
# Returns a challenge response which asks for
|
93
|
-
# information
|
101
|
+
# Returns a challenge response which asks for authentication information
|
94
102
|
|
95
103
|
def challenge(req, res)
|
96
104
|
res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpauth/digestauth.rb -- HTTP digest access authentication
|
3
4
|
#
|
@@ -11,9 +12,9 @@
|
|
11
12
|
#
|
12
13
|
# $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
require_relative '../config'
|
16
|
+
require_relative '../httpstatus'
|
17
|
+
require_relative 'authenticator'
|
17
18
|
require 'digest/md5'
|
18
19
|
require 'digest/sha1'
|
19
20
|
|
@@ -45,9 +46,22 @@ module WEBrick
|
|
45
46
|
class DigestAuth
|
46
47
|
include Authenticator
|
47
48
|
|
48
|
-
AuthScheme = "Digest"
|
49
|
-
|
50
|
-
|
49
|
+
AuthScheme = "Digest" # :nodoc:
|
50
|
+
|
51
|
+
##
|
52
|
+
# Struct containing the opaque portion of the digest authentication
|
53
|
+
|
54
|
+
OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc:
|
55
|
+
|
56
|
+
##
|
57
|
+
# Digest authentication algorithm
|
58
|
+
|
59
|
+
attr_reader :algorithm
|
60
|
+
|
61
|
+
##
|
62
|
+
# Quality of protection. RFC 2617 defines "auth" and "auth-int"
|
63
|
+
|
64
|
+
attr_reader :qop
|
51
65
|
|
52
66
|
##
|
53
67
|
# Used by UserDB to create a digest password entry
|
@@ -97,7 +111,7 @@ module WEBrick
|
|
97
111
|
@instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid)
|
98
112
|
@opaques = {}
|
99
113
|
@last_nonce_expire = Time.now
|
100
|
-
@mutex = Mutex.new
|
114
|
+
@mutex = Thread::Mutex.new
|
101
115
|
end
|
102
116
|
|
103
117
|
##
|
@@ -115,8 +129,7 @@ module WEBrick
|
|
115
129
|
end
|
116
130
|
|
117
131
|
##
|
118
|
-
# Returns a challenge response which asks for
|
119
|
-
# information
|
132
|
+
# Returns a challenge response which asks for authentication information
|
120
133
|
|
121
134
|
def challenge(req, res, stale=false)
|
122
135
|
nonce = generate_next_nonce(req)
|
@@ -142,6 +155,8 @@ module WEBrick
|
|
142
155
|
|
143
156
|
private
|
144
157
|
|
158
|
+
# :stopdoc:
|
159
|
+
|
145
160
|
MustParams = ['username','realm','nonce','uri','response']
|
146
161
|
MustParamsAuth = ['cnonce','nc']
|
147
162
|
|
@@ -189,7 +204,7 @@ module WEBrick
|
|
189
204
|
|
190
205
|
password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db)
|
191
206
|
unless password
|
192
|
-
error('%s: the user is not
|
207
|
+
error('%s: the user is not allowed.', auth_req['username'])
|
193
208
|
return false
|
194
209
|
end
|
195
210
|
|
@@ -220,9 +235,11 @@ module WEBrick
|
|
220
235
|
ha2 = hexdigest(req.request_method, auth_req['uri'])
|
221
236
|
ha2_res = hexdigest("", auth_req['uri'])
|
222
237
|
elsif auth_req['qop'] == "auth-int"
|
223
|
-
|
224
|
-
|
225
|
-
|
238
|
+
body_digest = @h.new
|
239
|
+
req.body { |chunk| body_digest.update(chunk) }
|
240
|
+
body_digest = body_digest.hexdigest
|
241
|
+
ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest)
|
242
|
+
ha2_res = hexdigest("", auth_req['uri'], body_digest)
|
226
243
|
end
|
227
244
|
|
228
245
|
if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
|
@@ -273,23 +290,8 @@ module WEBrick
|
|
273
290
|
|
274
291
|
def split_param_value(string)
|
275
292
|
ret = {}
|
276
|
-
|
277
|
-
|
278
|
-
when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
|
279
|
-
key = $1
|
280
|
-
matched = $2
|
281
|
-
string = $'
|
282
|
-
ret[key] = matched.gsub(/\\(.)/, "\\1")
|
283
|
-
when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/
|
284
|
-
key = $1
|
285
|
-
matched = $2
|
286
|
-
string = $'
|
287
|
-
ret[key] = matched.clone
|
288
|
-
when /^s*^,/
|
289
|
-
string = $'
|
290
|
-
else
|
291
|
-
break
|
292
|
-
end
|
293
|
+
string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do
|
294
|
+
ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1")
|
293
295
|
end
|
294
296
|
ret
|
295
297
|
end
|
@@ -297,7 +299,7 @@ module WEBrick
|
|
297
299
|
def generate_next_nonce(req)
|
298
300
|
now = "%012d" % req.request_time.to_i
|
299
301
|
pk = hexdigest(now, @instance_key)[0,32]
|
300
|
-
nonce = [now + ":" + pk].pack("
|
302
|
+
nonce = [now + ":" + pk].pack("m0") # it has 60 length of chars.
|
301
303
|
nonce
|
302
304
|
end
|
303
305
|
|
@@ -375,6 +377,7 @@ module WEBrick
|
|
375
377
|
@h.hexdigest(args.join(":"))
|
376
378
|
end
|
377
379
|
|
380
|
+
# :startdoc:
|
378
381
|
end
|
379
382
|
|
380
383
|
##
|
@@ -384,7 +387,7 @@ module WEBrick
|
|
384
387
|
include ProxyAuthenticator
|
385
388
|
|
386
389
|
private
|
387
|
-
def check_uri(req, auth_req)
|
390
|
+
def check_uri(req, auth_req) # :nodoc:
|
388
391
|
return true
|
389
392
|
end
|
390
393
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: false
|
1
2
|
#
|
2
3
|
# httpauth/htdigest.rb -- Apache compatible htdigest file
|
3
4
|
#
|
@@ -7,8 +8,8 @@
|
|
7
8
|
#
|
8
9
|
# $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
require_relative 'userdb'
|
12
|
+
require_relative 'digestauth'
|
12
13
|
require 'tempfile'
|
13
14
|
|
14
15
|
module WEBrick
|
@@ -37,9 +38,9 @@ module WEBrick
|
|
37
38
|
@path = path
|
38
39
|
@mtime = Time.at(0)
|
39
40
|
@digest = Hash.new
|
40
|
-
@mutex = Mutex::new
|
41
|
+
@mutex = Thread::Mutex::new
|
41
42
|
@auth_type = DigestAuth
|
42
|
-
open(@path,"a").close unless File
|
43
|
+
File.open(@path,"a").close unless File.exist?(@path)
|
43
44
|
reload
|
44
45
|
end
|
45
46
|
|
@@ -50,7 +51,7 @@ module WEBrick
|
|
50
51
|
mtime = File::mtime(@path)
|
51
52
|
if mtime > @mtime
|
52
53
|
@digest.clear
|
53
|
-
open(@path){|io|
|
54
|
+
File.open(@path){|io|
|
54
55
|
while line = io.gets
|
55
56
|
line.chomp!
|
56
57
|
user, realm, pass = line.split(/:/, 3)
|
@@ -70,13 +71,16 @@ module WEBrick
|
|
70
71
|
|
71
72
|
def flush(output=nil)
|
72
73
|
output ||= @path
|
73
|
-
tmp = Tempfile.
|
74
|
+
tmp = Tempfile.create("htpasswd", File::dirname(output))
|
75
|
+
renamed = false
|
74
76
|
begin
|
75
77
|
each{|item| tmp.puts(item.join(":")) }
|
76
78
|
tmp.close
|
77
79
|
File::rename(tmp.path, output)
|
78
|
-
|
79
|
-
|
80
|
+
renamed = true
|
81
|
+
ensure
|
82
|
+
tmp.close
|
83
|
+
File.unlink(tmp.path) if !renamed
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|