<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,8 @@
+== HEAD
+
+* Added computation of oauth_body_hash as per OAuth Request Body Hash 1.0
+  Draft 4 (Michael Reinsch)
+
 == 0.3.6 2009-09-14
 
 * Added -B CLI option to use the :body authentication scheme (Seth)</diff>
      <filename>History.txt</filename>
    </modified>
    <modified>
      <diff>@@ -29,6 +29,7 @@ module OAuth::Client
 
     def oauth_parameters
       {
+        'oauth_body_hash'        =&gt; options[:body_hash],
         'oauth_callback'         =&gt; options[:oauth_callback],
         'oauth_consumer_key'     =&gt; options[:consumer].key,
         'oauth_token'            =&gt; options[:token] ? options[:token].token : '',
@@ -53,6 +54,10 @@ module OAuth::Client
                                                          :parameters =&gt; oauth_parameters}.merge(extra_options) )
     end
 
+    def hash_body
+      @options[:body_hash] = OAuth::Signature.body_hash(@request, :parameters =&gt; oauth_parameters)
+    end
+
     def amend_user_agent_header(headers)
       @oauth_ua_string ||= &quot;OAuth gem v#{OAuth::VERSION}&quot;
       if headers['User-Agent']</diff>
      <filename>lib/oauth/client/helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -20,19 +20,14 @@ class Net::HTTPRequest
   #
   # This method also modifies the &lt;tt&gt;User-Agent&lt;/tt&gt; header to add the OAuth gem version.
   #
-  # See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1]
+  # See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1],
+  #           {OAuth Request Body Hash 1.0 Draft 4}[http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html]
   def oauth!(http, consumer = nil, token = nil, options = {})
-    options = { :request_uri      =&gt; oauth_full_request_uri(http),
-                :consumer         =&gt; consumer,
-                :token            =&gt; token,
-                :scheme           =&gt; 'header',
-                :signature_method =&gt; nil,
-                :nonce            =&gt; nil,
-                :timestamp        =&gt; nil }.merge(options)
-
-    @oauth_helper = OAuth::Client::Helper.new(self, options)
+    helper_options = oauth_helper_options(http, consumer, token, options)
+    @oauth_helper = OAuth::Client::Helper.new(self, helper_options)
     @oauth_helper.amend_user_agent_header(self)
-    self.send(&quot;set_oauth_#{options[:scheme]}&quot;)
+    @oauth_helper.hash_body if oauth_body_hash_required?
+    self.send(&quot;set_oauth_#{helper_options[:scheme]}&quot;)
   end
 
   # Create a string suitable for signing for an HTTP request. This process involves parameter
@@ -47,21 +42,27 @@ class Net::HTTPRequest
   # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
   #   +signature_method+, +nonce+, +timestamp+)
   # 
-  # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
+  # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1],
+  #           {OAuth Request Body Hash 1.0 Draft 4}[http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html]
   def signature_base_string(http, consumer = nil, token = nil, options = {})
-    options = { :request_uri      =&gt; oauth_full_request_uri(http),
-                :consumer         =&gt; consumer,
-                :token            =&gt; token,
-                :scheme           =&gt; 'header',
-                :signature_method =&gt; nil,
-                :nonce            =&gt; nil,
-                :timestamp        =&gt; nil }.merge(options)
-
-    OAuth::Client::Helper.new(self, options).signature_base_string
+    helper_options = oauth_helper_options(http, consumer, token, options)
+    oauth_helper = OAuth::Client::Helper.new(self, helper_options)
+    oauth_helper.hash_body if oauth_body_hash_required?
+    oauth_helper.signature_base_string
   end
 
 private
 
+  def oauth_helper_options(http, consumer, token, options)
+    { :request_uri      =&gt; oauth_full_request_uri(http),
+      :consumer         =&gt; consumer,
+      :token            =&gt; token,
+      :scheme           =&gt; 'header',
+      :signature_method =&gt; nil,
+      :nonce            =&gt; nil,
+      :timestamp        =&gt; nil }.merge(options)
+  end
+
   def oauth_full_request_uri(http)
     uri = URI.parse(self.path)
     uri.host = http.address
@@ -76,6 +77,10 @@ private
     uri.to_s
   end
 
+  def oauth_body_hash_required?
+    request_body_permitted? &amp;&amp; content_type != &quot;application/x-www-form-urlencoded&quot;
+  end
+
   def set_oauth_header
     self['Authorization'] = @oauth_helper.header
   end</diff>
      <filename>lib/oauth/client/net_http.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,9 @@ module OAuth
   OUT_OF_BAND = &quot;oob&quot;
 
   # required parameters, per sections 6.1.1, 6.3.1, and 7
-  PARAMETERS = %w(oauth_callback oauth_consumer_key oauth_token oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier oauth_version oauth_signature)
+  PARAMETERS = %w(oauth_callback oauth_consumer_key oauth_token 
+    oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier
+    oauth_version oauth_signature oauth_body_hash)
 
   # reserved character regexp, per section 5.1
   RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/</diff>
      <filename>lib/oauth/oauth.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,6 +25,10 @@ module OAuth::RequestProxy::Net
         end
       end
 
+      def body
+        request.body
+      end
+
     private
 
       def all_parameters</diff>
      <filename>lib/oauth/request_proxy/net_http.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,6 +32,11 @@ module OAuth
       self.build(request, options, &amp;block).signature_base_string
     end
 
+    # Create the body hash for a request
+    def self.body_hash(request, options = {}, &amp;block)
+      self.build(request, options, &amp;block).body_hash
+    end
+
     class UnknownSignatureMethod &lt; Exception; end
   end
 end</diff>
      <filename>lib/oauth/signature.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,6 +19,11 @@ module OAuth::Signature
       @digest_class = digest_class
     end
 
+    def self.hash_class(hash_class = nil)
+      return @hash_class if hash_class.nil?
+      @hash_class = hash_class
+    end
+
     def initialize(request, options = {}, &amp;block)
       raise TypeError unless request.kind_of?(OAuth::RequestProxy::Base)
       @request = request
@@ -66,6 +71,14 @@ module OAuth::Signature
       request.signature_base_string
     end
 
+    def body_hash
+      if self.class.hash_class
+        Base64.encode64(self.class.hash_class.digest(request.body || '')).chomp.gsub(/\n/,'')
+      else
+        nil # no body hash algorithm defined, so don't generate one
+      end
+    end
+
   private
 
     def token</diff>
      <filename>lib/oauth/signature/base.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,5 +5,6 @@ module OAuth::Signature::HMAC
   class SHA1 &lt; Base
     implements 'hmac-sha1'
     digest_class ::HMAC::SHA1
+    hash_class ::Digest::SHA1
   end
 end</diff>
      <filename>lib/oauth/signature/hmac/sha1.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,6 +4,7 @@ require 'openssl'
 module OAuth::Signature::RSA
   class SHA1 &lt; OAuth::Signature::Base
     implements 'rsa-sha1'
+    hash_class ::Digest::SHA1
 
     def ==(cmp_signature)
       public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(cmp_signature.is_a?(Array) ? cmp_signature.first : cmp_signature), signature_base_string)</diff>
      <filename>lib/oauth/signature/rsa/sha1.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,6 +32,32 @@ class NetHTTPClientTest &lt; Test::Unit::TestCase
     assert_equal &quot;OAuth oauth_nonce=\&quot;225579211881198842005988698334675835446\&quot;, oauth_signature_method=\&quot;HMAC-SHA1\&quot;, oauth_token=\&quot;token_411a7f\&quot;, oauth_timestamp=\&quot;1199645624\&quot;, oauth_consumer_key=\&quot;consumer_key_86cad9\&quot;, oauth_signature=\&quot;26g7wHTtNO6ZWJaLltcueppHYiI%3D\&quot;, oauth_version=\&quot;1.0\&quot;&quot;.split(', ').sort, request['authorization'].split(', ').sort
   end
 
+  def test_that_using_auth_headers_on_post_requests_with_data_works
+    request = Net::HTTP::Post.new(@request_uri.path)
+    request.body = &quot;data&quot;
+    request.content_type = 'text/ascii'
+    request.oauth!(@http, @consumer, @token, {:nonce =&gt; @nonce, :timestamp =&gt; @timestamp})
+
+    assert_equal 'POST', request.method
+    assert_equal '/test', request.path
+    assert_equal 'data', request.body
+    assert_equal 'text/ascii', request.content_type
+    assert_equal &quot;OAuth oauth_nonce=\&quot;225579211881198842005988698334675835446\&quot;, oauth_body_hash=\&quot;oXyaqmHoChv3HQ2FCvTluqmAC70%3D\&quot;, oauth_signature_method=\&quot;HMAC-SHA1\&quot;, oauth_token=\&quot;token_411a7f\&quot;, oauth_timestamp=\&quot;1199645624\&quot;, oauth_consumer_key=\&quot;consumer_key_86cad9\&quot;, oauth_signature=\&quot;0DA6pGTapdHSqC15RZelY5rNLDw%3D\&quot;, oauth_version=\&quot;1.0\&quot;&quot;.split(', ').sort, request['authorization'].split(', ').sort
+  end
+
+  def test_that_body_hash_is_obmitted_when_no_algorithm_is_defined
+    request = Net::HTTP::Post.new(@request_uri.path)
+    request.body = &quot;data&quot;
+    request.content_type = 'text/ascii'
+    request.oauth!(@http, @consumer, @token, {:nonce =&gt; @nonce, :timestamp =&gt; @timestamp, :signature_method =&gt; 'plaintext'})
+
+    assert_equal 'POST', request.method
+    assert_equal '/test', request.path
+    assert_equal 'data', request.body
+    assert_equal 'text/ascii', request.content_type
+    assert_equal &quot;OAuth oauth_nonce=\&quot;225579211881198842005988698334675835446\&quot;, oauth_signature_method=\&quot;plaintext\&quot;, oauth_token=\&quot;token_411a7f\&quot;, oauth_timestamp=\&quot;1199645624\&quot;, oauth_consumer_key=\&quot;consumer_key_86cad9\&quot;, oauth_signature=\&quot;5888bf0345e5d237%25263196ffd991c8ebdb\&quot;, oauth_version=\&quot;1.0\&quot;&quot;.split(', ').sort, request['authorization'].split(', ').sort
+  end
+
   def test_that_version_is_added_to_existing_user_agent
     request = Net::HTTP::Post.new(@request_uri.path)
     request['User-Agent'] = &quot;MyApp&quot;
@@ -74,6 +100,7 @@ class NetHTTPClientTest &lt; Test::Unit::TestCase
 
   def test_that_using_post_with_uri_params_works
     request = Net::HTTP::Post.new(@request_uri.path + &quot;?&quot; + request_parameters_to_s)
+    request.set_form_data( {} ) # just to make sure we have a correct mime type and thus no body hash
     request.oauth!(@http, @consumer, @token, {:scheme =&gt; 'query_string', :nonce =&gt; @nonce, :timestamp =&gt; @timestamp})
 
     assert_equal 'POST', request.method
@@ -81,7 +108,7 @@ class NetHTTPClientTest &lt; Test::Unit::TestCase
     assert_equal '/test', uri.path
     assert_equal nil, uri.fragment
     assert_equal &quot;key=value&amp;oauth_consumer_key=consumer_key_86cad9&amp;oauth_nonce=225579211881198842005988698334675835446&amp;oauth_signature=26g7wHTtNO6ZWJaLltcueppHYiI%3D&amp;oauth_signature_method=HMAC-SHA1&amp;oauth_timestamp=1199645624&amp;oauth_token=token_411a7f&amp;oauth_version=1.0&quot;, uri.query.split(&quot;&amp;&quot;).sort.join('&amp;')
-    assert_equal nil, request.body
+    assert_equal &quot;&quot;, request.body
     assert_equal nil, request['authorization']
   end
 
@@ -99,6 +126,22 @@ class NetHTTPClientTest &lt; Test::Unit::TestCase
     assert_equal nil, request['authorization']
   end
 
+  def test_that_using_post_with_uri_and_data_works
+    request = Net::HTTP::Post.new(@request_uri.path + &quot;?&quot; + request_parameters_to_s)
+    request.body = &quot;data&quot;
+    request.content_type = 'text/ascii'
+    request.oauth!(@http, @consumer, @token, {:scheme =&gt; :query_string, :nonce =&gt; @nonce, :timestamp =&gt; @timestamp})
+
+    assert_equal 'POST', request.method
+    uri = URI.parse(request.path)
+    assert_equal '/test', uri.path
+    assert_equal nil, uri.fragment
+    assert_equal &quot;data&quot;, request.body
+    assert_equal 'text/ascii', request.content_type
+    assert_equal &quot;key=value&amp;oauth_body_hash=oXyaqmHoChv3HQ2FCvTluqmAC70%3D&amp;oauth_consumer_key=consumer_key_86cad9&amp;oauth_nonce=225579211881198842005988698334675835446&amp;oauth_signature=MHRKU42iVHU4Ke9kBUDa9Zw6IAM%3D&amp;oauth_signature_method=HMAC-SHA1&amp;oauth_timestamp=1199645624&amp;oauth_token=token_411a7f&amp;oauth_version=1.0&quot;, uri.query.split(&quot;&amp;&quot;).sort.join('&amp;')
+    assert_equal nil, request['authorization']
+  end
+
 
   def test_example_from_specs
     consumer=OAuth::Consumer.new(&quot;dpf43f3p2l4k3l03&quot;,&quot;kd94hf93k423kf44&quot;)
@@ -146,12 +189,12 @@ class NetHTTPClientTest &lt; Test::Unit::TestCase
     assert_equal &quot;oauth_token=requestkey&amp;oauth_token_secret=requestsecret&quot;,response.body
   end
 
-  def test_that_put_bodies_not_signed
+  def test_that_put_bodies_signed
     request = Net::HTTP::Put.new(@request_uri.path)
     request.body = &quot;&lt;?xml version=\&quot;1.0\&quot;?&gt;&lt;foo&gt;&lt;bar&gt;baz&lt;/bar&gt;&lt;/foo&gt;&quot;
     request[&quot;Content-Type&quot;] = &quot;application/xml&quot;
     signature_base_string=request.signature_base_string(@http, @consumer, nil, { :nonce =&gt; @nonce, :timestamp =&gt; @timestamp })
-    assert_equal &quot;PUT&amp;http%3A%2F%2Fexample.com%2Ftest&amp;oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0&quot;, signature_base_string
+    assert_equal &quot;PUT&amp;http%3A%2F%2Fexample.com%2Ftest&amp;oauth_body_hash%3DDvAa1AWdFoH9K%252B%252F2AHm3f6wH27k%253D%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0&quot;, signature_base_string
   end
 
   def test_that_put_bodies_not_signed_even_if_form_urlencoded
@@ -168,12 +211,12 @@ class NetHTTPClientTest &lt; Test::Unit::TestCase
     assert_equal &quot;POST&amp;http%3A%2F%2Fexample.com%2Ftest&amp;key2%3Dvalue2%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0&quot;, signature_base_string
   end
 
-  def test_that_post_bodies_not_signed_if_other_content_type
+  def test_that_post_bodies_signed_if_other_content_type
     request = Net::HTTP::Post.new(@request_uri.path)
     request.body = &quot;&lt;?xml version=\&quot;1.0\&quot;?&gt;&lt;foo&gt;&lt;bar&gt;baz&lt;/bar&gt;&lt;/foo&gt;&quot;
     request[&quot;Content-Type&quot;] = &quot;application/xml&quot;
     signature_base_string=request.signature_base_string(@http, @consumer, nil, { :nonce =&gt; @nonce, :timestamp =&gt; @timestamp })
-    assert_equal &quot;POST&amp;http%3A%2F%2Fexample.com%2Ftest&amp;oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0&quot;, signature_base_string
+    assert_equal &quot;POST&amp;http%3A%2F%2Fexample.com%2Ftest&amp;oauth_body_hash%3DDvAa1AWdFoH9K%252B%252F2AHm3f6wH27k%253D%26oauth_consumer_key%3Dconsumer_key_86cad9%26oauth_nonce%3D225579211881198842005988698334675835446%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1199645624%26oauth_version%3D1.0&quot;, signature_base_string
   end
 
   protected</diff>
      <filename>test/test_net_http_client.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>045a5978bc902a57fd40b2811070ece687f9275a</id>
    </parent>
  </parents>
  <author>
    <name>Michael Reinsch</name>
    <email>michael@mobalean.com</email>
  </author>
  <url>http://github.com/mreinsch/oauth/commit/764e5bdc074bbedfa3c9cb927a132c89195010dc</url>
  <id>764e5bdc074bbedfa3c9cb927a132c89195010dc</id>
  <committed-date>2009-11-08T19:25:03-08:00</committed-date>
  <authored-date>2009-11-08T19:25:03-08:00</authored-date>
  <message>implemented computation of body hash as per OAuth Request Body Hash 1.0 Draft 4</message>
  <tree>8c4746f45ad8f8e3fcdb20bbdc739480225497ab</tree>
  <committer>
    <name>Michael Reinsch</name>
    <email>michael@mobalean.com</email>
  </committer>
</commit>
