class HTTPClient::OAuth


CAUTION: This impl does NOT support OAuth Request Body Hash spec for now.
yourself.
Core 1.0 spec for now. You need to obtain Access token and Access secret by
CAUTION: This impl only support ‘#7 Accessing Protected Resources’ in OAuth
Used in WWWAuth.
Authentication filter for handling OAuth negotiation.

def self.escape(str) # :nodoc:

:nodoc:
def self.escape(str) # :nodoc:
  if str.respond_to?(:force_encoding)
    str.dup.force_encoding('BINARY').gsub(/([^a-zA-Z0-9_.~-]+)/) {
      '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
    }
  else
    str.gsub(/([^a-zA-Z0-9_.~-]+)/n) {
      '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
    }
  end
end

def calc_cred(req, config)

def calc_cred(req, config)
  header = {}
  header['oauth_consumer_key'] = config.consumer_key
  header['oauth_signature_method'] = config.signature_method
  header['oauth_timestamp'] = config.debug_timestamp || Time.now.to_i.to_s
  header['oauth_nonce'] = config.debug_nonce || generate_nonce()
  header['oauth_token'] = config.token if config.token
  header['oauth_version'] = config.version if config.version
  header['oauth_callback'] = config.callback if config.callback
  header['oauth_verifier'] = config.verifier if config.verifier
  header['oauth_session_handle'] = config.session_handle if config.session_handle
  signature = sign(config, header, req)
  header['oauth_signature'] = signature
  # no need to do but we should sort for easier to test.
  str = header.sort_by { |k, v| k }.map { |k, v| encode_header(k, v) }.join(', ')
  if config.realm
    str = %Q(realm="#{config.realm}", ) + str
  end
  str
end

def challenge(uri, param_str = nil)

you need to use this class directly.
Normally OAuthClient handles this correctly but see how it uses when
method just remember URL (nil means 'any') for the next connection.
retry which should not work in OAuth authentication context. This
challenge() in OAuth handler always returns false to avoid connection

Challenge handler: remember URL for response.
def challenge(uri, param_str = nil)
  synchronize {
    if uri.nil?
      @challenge[nil] = true
    else
      @challenge[urify(uri)] = true
    end
    false
  }
end

def create_base_string(config, header, req)

def create_base_string(config, header, req)
  params = encode_param(header)
  query = req.header.request_query
  if query and HTTP::Message.multiparam_query?(query)
    params += encode_param(query)
  end
  # captures HTTP Message body only for 'application/x-www-form-urlencoded'
  if req.header.contenttype == 'application/x-www-form-urlencoded' and req.http_body.size
    params += encode_param(HTTP::Message.parse(req.http_body.content))
  end
  uri = req.header.request_uri
  if uri.query
    params += encode_param(HTTP::Message.parse(uri.query))
  end
  if uri.port == uri.default_port
    request_url = "#{uri.scheme.downcase}://#{uri.host}#{uri.path}"
  else
    request_url = "#{uri.scheme.downcase}://#{uri.host}:#{uri.port}#{uri.path}"
  end
  [req.header.request_method.upcase, request_url, params.sort.join('&')].map { |e|
    escape(e)
  }.join('&')
end

def do_get_config(uri = nil)

def do_get_config(uri = nil)
  if uri.nil?
    @config
  else
    uri = urify(uri)
    Util.hash_find_value(@auth) { |cand_uri, cred|
      Util.uri_part_of(uri, cand_uri)
    }
  end
end

def encode_header(k, v)

def encode_header(k, v)
  %Q(#{escape(k.to_s)}="#{escape(v.to_s)}")
end

def encode_param(params)

def encode_param(params)
  params.map { |k, v|
    [v].flatten.map { |vv|
      %Q(#{escape(k.to_s)}=#{escape(vv.to_s)})
    }
  }.flatten
end

def escape(str)

def escape(str)
  self.class.escape(str)
end

def generate_nonce

def generate_nonce
  @nonce_count += 1
  now = "%012d" % Time.now.to_i
  pk = Digest::MD5.hexdigest([@nonce_count.to_s, now, self.__id__, Process.pid, rand(65535)].join)[0, 32]
  [now + ':' + pk].pack('m*').chop
end

def get(req)

* child page of defined credential
* child page of challengeable(got *Authenticate before) uri and,
It sends cred only when a given uri is;
Response handler: returns credential.
def get(req)
  target_uri = req.header.request_uri
  synchronize {
    return nil unless @challenge[nil] or @challenge.find { |uri, ok|
      Util.uri_part_of(target_uri, uri) and ok
    }
    config = do_get_config(target_uri) || @config
    return nil unless config
    calc_cred(req, config)
  }
end

def get_config(uri = nil)

Get authentication credential.
def get_config(uri = nil)
  synchronize {
    do_get_config(uri)
  }
end

def initialize

Creates new DigestAuth filter.
def initialize
  super('OAuth')
  @config = nil # common config
  @auth = {} # configs for each site
  @nonce_count = 0
  @signature_handler = {
    'HMAC-SHA1' => method(:sign_hmac_sha1)
  }
end

def set(*args)

You cannot set OAuth config via WWWAuth#set_auth. Use OAuth#config=
Set authentication credential.
def set(*args)
  # not supported
end

def set?

Check always (not effective but it works)
def set?
  !@challenge.empty?
end

def set_config(uri, config)

Set authentication credential.
def set_config(uri, config)
  synchronize do
    if uri.nil?
      @config = config
    else
      uri = Util.uri_dirname(urify(uri))
      @auth[uri] = config
    end
  end
end

def sign(config, header, req)

def sign(config, header, req)
  base_string = create_base_string(config, header, req)
  if handler = config.signature_handler[config.signature_method] || @signature_handler[config.signature_method.to_s]
    handler.call(config, base_string)
  else
    raise ConfigurationError.new("Unknown OAuth signature method: #{config.signature_method}")
  end
end

def sign_hmac_sha1(config, base_string)

def sign_hmac_sha1(config, base_string)
  unless SSLEnabled
    raise ConfigurationError.new("openssl required for OAuth implementation")
  end
  key = [escape(config.consumer_secret.to_s), escape(config.secret.to_s)].join('&')
  digester = OpenSSL::Digest::SHA1.new
  [OpenSSL::HMAC.digest(digester, key, base_string)].pack('m*').chomp
end