@@ -156,10 +156,6 @@ def initialize(user = nil, pass = nil, authz = nil,
156
156
authcid : nil , secret : nil ,
157
157
realm : nil , service : "imap" , host : nil , service_name : nil ,
158
158
warn_deprecation : true , **)
159
- username = authcid || username || user or
160
- raise ArgumentError , "missing username (authcid)"
161
- password ||= secret || pass or raise ArgumentError , "missing password"
162
- authzid ||= authz
163
159
if warn_deprecation
164
160
warn ( "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC6331." ,
165
161
category : :deprecated )
@@ -168,11 +164,25 @@ def initialize(user = nil, pass = nil, authz = nil,
168
164
require "digest/md5"
169
165
require "securerandom"
170
166
require "strscan"
171
- @username , @password , @authzid = username , password , authzid
167
+
168
+ [ authcid , username , user ] . compact . count == 1 or
169
+ raise ArgumentError , "conflicting values for username"
170
+ [ authzid , authz ] . compact . count <= 1 or
171
+ raise ArgumentError , "conflicting values for authzid"
172
+ [ password , secret , pass ] . compact . count == 1 or
173
+ raise ArgumentError , "conflicting values for password"
174
+
175
+ @username = authcid || username || user
176
+ @password = password || secret || pass
177
+ @authzid = authzid || authz
172
178
@realm = realm
173
179
@host = host
174
180
@service = service
175
181
@service_name = service_name
182
+
183
+ @username or raise ArgumentError , "missing username (authcid)"
184
+ @password or raise ArgumentError , "missing password"
185
+
176
186
@nc , @stage = { } , STAGE_ONE
177
187
end
178
188
@@ -198,42 +208,11 @@ def process(challenge)
198
208
case @stage
199
209
when STAGE_ONE
200
210
@stage = STAGE_TWO
201
- @sparams = parse_challenge ( challenge )
202
- @qop = sparams . key? ( "qop" ) ? [ "auth" ] : sparams [ "qop" ] . flatten
203
- @nonce = sparams [ "nonce" ] &.first
204
- @charset = sparams [ "charset" ] &.first
205
- @realm ||= sparams [ "realm" ] &.last
206
- @host ||= realm
207
-
208
- if !qop . include? ( "auth" )
209
- raise DataFormatError , "Server does not support auth (qop = %p)" % [
210
- sparams [ "qop" ]
211
- ]
212
- elsif ( emptykey = REQUIRED . find { sparams [ _1 ] . empty? } )
213
- raise DataFormatError , "Server didn't send %s (%p)" % [ emptykey , challenge ]
214
- elsif ( multikey = NO_MULTIPLES . find { sparams [ _1 ] . length > 1 } )
215
- raise DataFormatError , "Server sent multiple %s (%p)" % [ multikey , challenge ]
216
- end
217
-
218
- response = {
219
- nonce : nonce ,
220
- username : username ,
221
- realm : realm ,
222
- cnonce : SecureRandom . base64 ( 32 ) ,
223
- "digest-uri" : digest_uri ,
224
- qop : "auth" ,
225
- maxbuf : 65535 ,
226
- nc : "%08d" % nc ( nonce ) ,
227
- charset : charset ,
228
- }
229
-
230
- response [ :authzid ] = @authzid unless @authzid . nil?
231
-
232
- response [ :response ] = response_value ( response )
233
- format_response ( response )
211
+ process_stage_one ( challenge )
212
+ stage_one_response
234
213
when STAGE_TWO
235
214
@stage = STAGE_DONE
236
- raise ResponseParseError , challenge unless challenge =~ /rspauth=/
215
+ process_stage_two ( challenge )
237
216
"" # if at the second stage, return an empty string
238
217
else
239
218
raise ResponseParseError , challenge
@@ -284,14 +263,59 @@ def split_quoted_list(value, challenge)
284
263
end
285
264
286
265
def nc ( nonce )
287
- if @nc . has_key? nonce
288
- @nc [ nonce ] = @nc [ nonce ] + 1
289
- else
290
- @nc [ nonce ] = 1
266
+ @nc [ nonce ] = @nc . key? ( nonce ) ? @nc [ nonce ] + 1 : 1
267
+ @nc [ nonce ]
268
+ end
269
+
270
+ def process_stage_one ( challenge )
271
+ @sparams = parse_challenge ( challenge )
272
+ @qop = sparams . key? ( "qop" ) ? [ "auth" ] : sparams [ "qop" ] . flatten
273
+
274
+ guard_stage_one ( challenge )
275
+
276
+ @nonce = sparams [ "nonce" ] . first
277
+ @charset = sparams [ "charset" ] . first
278
+
279
+ @realm ||= sparams [ "realm" ] . last
280
+ @host ||= realm
281
+ end
282
+
283
+ def guard_stage_one ( challenge )
284
+ if !qop . include? ( "auth" )
285
+ raise DataFormatError , "Server does not support auth (qop = %p)" % [
286
+ sparams [ "qop" ]
287
+ ]
288
+ elsif ( emptykey = REQUIRED . find { sparams [ _1 ] . empty? } )
289
+ raise DataFormatError , "Server didn't send %s (%p)" % [ emptykey , challenge ]
290
+ elsif ( multikey = NO_MULTIPLES . find { sparams [ _1 ] . length > 1 } )
291
+ raise DataFormatError , "Server sent multiple %s (%p)" % [ multikey , challenge ]
291
292
end
292
293
end
293
294
294
- def response_value ( response )
295
+ def stage_one_response
296
+ response = {
297
+ nonce : nonce ,
298
+ username : username ,
299
+ realm : realm ,
300
+ cnonce : SecureRandom . base64 ( 32 ) ,
301
+ "digest-uri" : digest_uri ,
302
+ qop : "auth" ,
303
+ maxbuf : 65535 ,
304
+ nc : "%08d" % nc ( nonce ) ,
305
+ charset : charset ,
306
+ }
307
+
308
+ response [ :authzid ] = authzid unless authzid . nil?
309
+ response [ :response ] = compute_digest ( response )
310
+
311
+ format_response ( response )
312
+ end
313
+
314
+ def process_stage_two ( challenge )
315
+ raise ResponseParseError , challenge unless challenge =~ /rspauth=/
316
+ end
317
+
318
+ def compute_digest ( response )
295
319
a1 = compute_a1 ( response )
296
320
a2 = compute_a2 ( response )
297
321
Digest ::MD5 . hexdigest (
0 commit comments