@@ -156,10 +156,6 @@ def initialize(user = nil, pass = nil, authz = nil,
156156 authcid : nil , secret : nil ,
157157 realm : nil , service : "imap" , host : nil , service_name : nil ,
158158 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
163159 if warn_deprecation
164160 warn ( "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC6331." ,
165161 category : :deprecated )
@@ -168,11 +164,25 @@ def initialize(user = nil, pass = nil, authz = nil,
168164 require "digest/md5"
169165 require "securerandom"
170166 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
172178 @realm = realm
173179 @host = host
174180 @service = service
175181 @service_name = service_name
182+
183+ @username or raise ArgumentError , "missing username (authcid)"
184+ @password or raise ArgumentError , "missing password"
185+
176186 @nc , @stage = { } , STAGE_ONE
177187 end
178188
@@ -198,42 +208,11 @@ def process(challenge)
198208 case @stage
199209 when STAGE_ONE
200210 @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
234213 when STAGE_TWO
235214 @stage = STAGE_DONE
236- raise ResponseParseError , challenge unless challenge =~ /rspauth=/
215+ process_stage_two ( challenge )
237216 "" # if at the second stage, return an empty string
238217 else
239218 raise ResponseParseError , challenge
@@ -284,14 +263,59 @@ def split_quoted_list(value, challenge)
284263 end
285264
286265 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 ]
291292 end
292293 end
293294
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 )
295319 a1 = compute_a1 ( response )
296320 a2 = compute_a2 ( response )
297321 Digest ::MD5 . hexdigest (
0 commit comments