httpclient-2.3.3/0000755000004100000410000000000012155433207013727 5ustar www-datawww-datahttpclient-2.3.3/sample/0000755000004100000410000000000012155433207015210 5ustar www-datawww-datahttpclient-2.3.3/sample/oauth_salesforce_10.rb0000644000004100000410000000444312155433207021370 0ustar www-datawww-datarequire 'oauthclient' # Get your own consumer token from http://twitter.com/apps consumer_key = '3MVG9y6x0357HledNGHa9tJrrlOmpCSo5alTv4W4AG1M0f9a8cGBIwo5wN2bQ7hjAEsjD7SBWf3H2Oycc9Qql' consumer_secret = '1404017425765973464' callback = ARGV.shift # can be nil for OAuth 1.0. (not 1.0a) request_token_url = 'https://login.salesforce.com/_nc_external/system/security/oauth/RequestTokenHandler' oob_authorize_url = 'https://login.salesforce.com/setup/secur/RemoteAccessAuthorizationPage.apexp' access_token_url = 'https://login.salesforce.com/_nc_external/system/security/oauth/AccessTokenHandler' STDOUT.sync = true # create OAuth client. client = OAuthClient.new client.oauth_config.consumer_key = consumer_key client.oauth_config.consumer_secret = consumer_secret client.oauth_config.signature_method = 'HMAC-SHA1' client.oauth_config.http_method = :get # Twitter does not allow :post client.debug_dev = STDERR if $DEBUG client.ssl_config.ssl_version = "TLSv1_1" # Get request token. res = client.get_request_token(request_token_url, callback) p res.status p res.oauth_params p res.content p client.oauth_config token = res.oauth_params['oauth_token'] secret = res.oauth_params['oauth_token_secret'] raise if token.nil? or secret.nil? # You need to confirm authorization out of band. puts puts "Go here and do confirm: #{oob_authorize_url}?oauth_token=#{token}&oauth_consumer_key=#{consumer_key}" puts "Type oauth_verifier/PIN (if given) and hit [enter] to go" verifier = gets.chomp verifier = nil if verifier.empty? # Get access token. # FYI: You may need to re-construct OAuthClient instance here. # In normal web app flow, getting access token and getting request token # must be done in different HTTP requests. # client = OAuthClient.new # client.oauth_config.consumer_key = consumer_key # client.oauth_config.consumer_secret = consumer_secret # client.oauth_config.signature_method = 'HMAC-SHA1' # client.oauth_config.http_method = :get # Twitter does not allow :post res = client.get_access_token(access_token_url, token, secret, verifier) p res.status p res.oauth_params p res.content p client.oauth_config id = res.oauth_params['user_id'] puts puts "Access token usage example" puts "Hit [enter] to go" gets # Access to a protected resource. (DM) puts client.get("http://twitter.com/direct_messages.json") httpclient-2.3.3/sample/howto.rb0000644000004100000410000000204512155433207016676 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift(File.join('..', 'lib')) require 'httpclient' proxy = ENV['HTTP_PROXY'] clnt = HTTPClient.new(proxy) clnt.set_cookie_store("cookie.dat") target = ARGV.shift || "http://localhost/foo.cgi" puts puts '= GET content directly' puts clnt.get_content(target) puts '= GET result object' result = clnt.get(target) puts '== Header object' p result.header puts "== Content-type" p result.contenttype puts '== Body object' p result.body puts '== Content' print result.content puts '== GET with Block' clnt.get(target) do |str| puts str end puts puts '= GET with query' puts clnt.get(target, { "foo" => "bar", "baz" => "quz" }).content puts puts '= GET with query 2' puts clnt.get(target, [["foo", "bar1"], ["foo", "bar2"]]).content clnt.debug_dev = STDERR puts puts '= GET with extra header' puts clnt.get(target, nil, { "SOAPAction" => "HelloWorld" }).content puts puts '= GET with extra header 2' puts clnt.get(target, nil, [["Accept", "text/plain"], ["Accept", "text/html"]]).content clnt.debug_dev = nil clnt.save_cookie_store httpclient-2.3.3/sample/dav.rb0000644000004100000410000000365712155433207016322 0ustar www-datawww-datarequire 'uri' require 'httpclient' class DAV attr_reader :headers def initialize(uri = nil) @uri = nil @headers = {} open(uri) if uri proxy = ENV['HTTP_PROXY'] || ENV['http_proxy'] || nil @client = HTTPClient.new(proxy) end def open(uri) @uri = if uri.is_a?(URI) uri else URI.parse(uri) end end def set_basic_auth(user_id, passwd) @client.set_basic_auth(@uri, user_id, passwd) end # TODO: propget/propset support def propfind(target) target_uri = @uri + target res = @client.propfind(target_uri) res.body.content end def get(target, local = nil) local ||= target target_uri = @uri + target if FileTest.exist?(local) raise RuntimeError.new("File #{ local } exists.") end f = File.open(local, "wb") res = @client.get(target_uri, nil, @headers) do |data| f << data end f.close STDOUT.puts("#{ res.header['content-length'][0] } bytes saved to file #{ target }.") end def debug_dev=(dev) @client.debug_dev = dev end def get_content(target) target_uri = @uri + target @client.get_content(target_uri, nil, @headers) end def put_content(target, content) target_uri = @uri + target res = @client.put(target_uri, content, @headers) if res.status < 200 or res.status >= 300 raise "HTTP PUT failed: #{res.inspect}" end end class Mock attr_reader :headers def initialize(uri = nil) @uri = nil @headers = {} open(uri) if uri @cache = {} end def open(uri) @uri = uri.is_a?(URI) ? uri : URI.parse(uri) end def set_basic_auth(user_id, passwd) # ignore end def propfind(target) # not found nil end def get(target, local = nil) # ignore end def get_content(target) @cache[target] end def put_content(target, content) @cache[target] = content nil end end end httpclient-2.3.3/sample/wcat.rb0000644000004100000410000000061112155433207016471 0ustar www-datawww-data#!/usr/bin/env ruby # wcat for http-access2 # Copyright (C) 2001 TAKAHASHI Masayoshi $:.unshift(File.join('..', 'lib')) require 'httpclient' if ENV['HTTP_PROXY'] h = HTTPClient.new(ENV['HTTP_PROXY']) else h = HTTPClient.new() end while urlstr = ARGV.shift response = h.get(urlstr){ |data| print data } p response.contenttype p response.peer_cert if /^https/i =~ urlstr end httpclient-2.3.3/sample/oauth_twitter.rb0000644000004100000410000000377312155433207020451 0ustar www-datawww-datarequire 'oauthclient' # Get your own consumer token from http://twitter.com/apps consumer_key = 'EDIT HERE' consumer_secret = 'EDIT HERE' callback = ARGV.shift # can be nil for OAuth 1.0. (not 1.0a) request_token_url = 'http://twitter.com/oauth/request_token' oob_authorize_url = 'http://twitter.com/oauth/authorize' access_token_url = 'http://twitter.com/oauth/access_token' STDOUT.sync = true # create OAuth client. client = OAuthClient.new client.oauth_config.consumer_key = consumer_key client.oauth_config.consumer_secret = consumer_secret client.oauth_config.signature_method = 'HMAC-SHA1' client.oauth_config.http_method = :get # Twitter does not allow :post client.debug_dev = STDERR if $DEBUG # Get request token. res = client.get_request_token(request_token_url, callback) p res.status p res.oauth_params p res.content p client.oauth_config token = res.oauth_params['oauth_token'] secret = res.oauth_params['oauth_token_secret'] raise if token.nil? or secret.nil? # You need to confirm authorization out of band. puts puts "Go here and do confirm: #{oob_authorize_url}?oauth_token=#{token}" puts "Type oauth_verifier/PIN (if given) and hit [enter] to go" verifier = gets.chomp verifier = nil if verifier.empty? # Get access token. # FYI: You may need to re-construct OAuthClient instance here. # In normal web app flow, getting access token and getting request token # must be done in different HTTP requests. # client = OAuthClient.new # client.oauth_config.consumer_key = consumer_key # client.oauth_config.consumer_secret = consumer_secret # client.oauth_config.signature_method = 'HMAC-SHA1' # client.oauth_config.http_method = :get # Twitter does not allow :post res = client.get_access_token(access_token_url, token, secret, verifier) p res.status p res.oauth_params p res.content p client.oauth_config id = res.oauth_params['user_id'] puts puts "Access token usage example" puts "Hit [enter] to go" gets # Access to a protected resource. (DM) puts client.get("http://twitter.com/direct_messages.json") httpclient-2.3.3/sample/oauth_friendfeed.rb0000644000004100000410000000365512155433207021041 0ustar www-datawww-datarequire 'oauthclient' # Get your own consumer token from http://friendfeed.com/api/applications consumer_key = 'EDIT HERE' consumer_secret = 'EDIT HERE' request_token_url = 'https://friendfeed.com/account/oauth/request_token' oob_authorize_url = 'https://friendfeed.com/account/oauth/authorize' access_token_url = 'https://friendfeed.com/account/oauth/access_token' STDOUT.sync = true # create OAuth client. client = OAuthClient.new client.oauth_config.consumer_key = consumer_key client.oauth_config.consumer_secret = consumer_secret client.oauth_config.signature_method = 'HMAC-SHA1' client.oauth_config.http_method = :get # FriendFeed does not allow :post client.debug_dev = STDERR if $DEBUG # Get request token. res = client.get_request_token(request_token_url) p res.status p res.oauth_params p res.content p client.oauth_config token = res.oauth_params['oauth_token'] secret = res.oauth_params['oauth_token_secret'] raise if token.nil? or secret.nil? # You need to confirm authorization out of band. puts puts "Go here and do confirm: #{oob_authorize_url}?oauth_token=#{token}" puts "Hit [enter] to go" gets # Get access token. # FYI: You may need to re-construct OAuthClient instance here. # In normal web app flow, getting access token and getting request token # must be done in different HTTP requests. # client = OAuthClient.new # client.oauth_config.consumer_key = consumer_key # client.oauth_config.consumer_secret = consumer_secret # client.oauth_config.signature_method = 'HMAC-SHA1' # client.oauth_config.http_method = :get # Twitter does not allow :post res = client.get_access_token(access_token_url, token, secret) p res.status p res.oauth_params p res.content p client.oauth_config username = res.oauth_params['username'] puts puts "Access token usage example" puts "Hit [enter] to go" gets # Access to a protected resource. (user profile) puts client.get("http://friendfeed-api.com/v2/feedinfo/#{username}?format=json") httpclient-2.3.3/sample/stream.rb0000644000004100000410000000050512155433207017030 0ustar www-datawww-data$:.unshift(File.join('..', 'lib')) require "httpclient" c = HTTPClient.new piper, pipew = IO.pipe conn = c.post_async("http://localhost:8080/stream", piper) Thread.new do res = conn.pop while str = res.content.read(10) p str end end p "type here" while line = STDIN.gets pipew << line end pipew.close sleep 5 httpclient-2.3.3/sample/ssl/0000755000004100000410000000000012155433207016011 5ustar www-datawww-datahttpclient-2.3.3/sample/ssl/htdocs/0000755000004100000410000000000012155433207017275 5ustar www-datawww-datahttpclient-2.3.3/sample/ssl/htdocs/index.html0000644000004100000410000000017612155433207021276 0ustar www-datawww-data SSL test

Verification succeeded?

httpclient-2.3.3/sample/ssl/webrick_httpsd.rb0000644000004100000410000000146012155433207021353 0ustar www-datawww-data#!/usr/bin/env ruby require 'webrick/https' require 'getopts' getopts nil, 'r:', 'p:8808' dir = File::dirname(File::expand_path(__FILE__)) # Pass phrase of '1000key.pem' is '1000'. data = open(File::join(dir, "1000key.pem")){|io| io.read } pkey = OpenSSL::PKey::RSA.new(data) data = open(File::join(dir, "1000cert.pem")){|io| io.read } cert = OpenSSL::X509::Certificate.new(data) s = WEBrick::HTTPServer.new( :BindAddress => "localhost", :Port => $OPT_p.to_i, :Logger => nil, :DocumentRoot => $OPT_r || File::join(dir, "/htdocs"), :SSLEnable => true, :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, :SSLCertificate => cert, :SSLPrivateKey => pkey, :SSLCertName => nil, :SSLCACertificateFile => "all.pem" ) trap("INT"){ s.shutdown } s.start httpclient-2.3.3/sample/ssl/1000cert.pem0000644000004100000410000000213312155433207017751 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDCTCCAfGgAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwLTELMAkGA1UEBhMCQ1ox DTALBgNVBAoTBFJ1YnkxDzANBgNVBAMTBlJ1YnlDQTAeFw0wMzA1MzEwMzUwNDFa Fw0wNDA1MzAwMzUwNDFaMDAxCzAJBgNVBAYTAkNaMQ0wCwYDVQQKEwRSdWJ5MRIw EAYDVQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALhs fh4i1c+K57vFG7SUDfdxuSlbPUqaV0sbiuvWb0f7B2T7bHicaIRsDYW7PQRLLwwR Pd+aKg3KuwhWN47faRam19Z3yWCD7Tg+BhXDqlXnz6snnr4APpAxc22kJKjzuil6 sp+QTkl/EFKI3+ocDur1UB+kSOmTzsDmepaWUZwTAgMBAAGjgbMwgbAwCQYDVR0T BAIwADAtBglghkgBhvhCAQ0EIBYeR2VuZXJhdGVkIGJ5IE9wZW5TU0wgZm9yIFJ1 YnkuMB0GA1UdDgQWBBQlYESgTYdkTOYy02+/jGSqa+OpzjBVBgNVHSMETjBMgBQN iKV64A56Sfaem3wZrTOo9CHo5qExpC8wLTELMAkGA1UEBhMCQ1oxDTALBgNVBAoT BFJ1YnkxDzANBgNVBAMTBlJ1YnlDQYIBADANBgkqhkiG9w0BAQUFAAOCAQEAJh9v ehhUv69oilVWGvGB6xCr8LgErnO9QdAyqJE2xBhbNaB3crjWDdQTz4UNvCQoJG/4 Oa9Vp10vM8E0ZMVHer87WM9tPEOg09r38U/1c7gSYBkPSGQfeWtZNjQ1YOm6RDx4 JJp9sp9v/CdBlVXaBQQd+MQFny7E+EkMHRfiv89KTfOs0wYdirLrM1C90CZUEj0i cMcBdHzH5XcNpWB1ag4cNiUn2ljsaWlUpEg48gLe2FLJVPBio+iZnOm/C6KIwBMO BCVxkZ6oIR87JT4xbr8SxRS9d/irhVU9MtGYwMe4MPSztefASdmEyj59ZFCLKQHV +ltGb7/b7DetoT1spA== -----END CERTIFICATE----- httpclient-2.3.3/sample/ssl/0key.pem0000644000004100000410000000332712155433207017371 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,BC9A85421E11A052 dlFN8woboqu1mLWHSe6HnBCzrJUFw1NLv4sJM8MFPcSGkPEJoqceRQ5XFFlwnhwT WStidVQLAaCqWxPlQbvKs/eW2yEZRX3JhNHlGkuZUbAuqC7FGcIPCFtXmshmR3/K wSxM3DuEBA31kHoyH/DGPEOcp/L9M95fU2w/1Zz895nXgECdlYfMB7KEXJ7S0TVD NvcH6dcNS2I56ikUfRd9MA+tTtJTDsolBii3HpNkI+i8GeAAznfuyhCa+RasbMnR 3SRHSgiZpK01JkepKEumG1iyGXBE4GMlF3dK9TL4UTfK7PjbW1BRGVdmGayA5L8X vpn9uIMbpFUD7nU/cSs6+ECYy9Fdq/d8oE7YRVpqGA2mX+Vvjzc6ZByiW8eH8uYW d1YCySAzttQUcDFx4YlpbQN+NSE67amGY/nkXn3CnQuB2uDiUm6818p1jjhEbIj6 SQpNgpuhHPhyawMuC98BvxEXoSTFRmXx5h+Qgo8nfCBUpGUCA8STuJG+EebIXMHH yNdZGYMdkkuzow1G+bcHaIV4pgD0hOX6D+AzhO8/11gsJNiZHWAkxRtXres0B7JQ BTv6mrpmRsr1TbY7K55y5QePkvroiqI4BX2//RgAcUw+aBzPCSt87YU55pPxxDCv KnoMQJapUIlXgYBs+2GBiFQlr3dAnzduXrXkZa/TuE0853QDDnKWN3aWapW0EieH sDxYz6kZ6c/vjDJtNjjgysKvi2ZFnJMk92fi1sNd2MrH9w1vSmjHw6+b9REH+r6K YCcMzCUnIV5Y5jgbnrY5jWlB5Jt5PlU+QDFTBNsdctyoES3h5yQh48hcpnJOy4YT tn9jEmIAYM7QZtGZrY5yiZRZbM5tLL7VeXA0M7N0ivjZUVP4NBUV1IFhLpeus3Yo yXB99Sib/M8fqlmnRlyKKaRseB9a80/LJdLJC7Q1aJG9JYTTpumydppDzvwwUFV/ yHQibPzWhnHTElyXSGeWWQ/4gKJXJFkSyrBeKt/DcDggEnpsVw7kAeo0KJLWvBq2 0CwnWxPsseIYSLdwE0wOZTnWvHC6viCCqxaFwROa+bHsgCtRR8xwOKVg0RQRexQi SKsCEyq4hfKH3Kd7KkI5c4iCHrNDxQiYVAHGo0kuVWPNP9PH8XrRuP7f2Aq6ObEU cGLN+OKlzChKJQZZzdthGTjO52cZERokS7etLs5yPNM27CUZIy2gUsWwANG700ov DYi4S9j4y2fK9qVFGuNmZ7nNQ6juHIN8ZpZObmzGz/GEsVy8zYGV7jH2bC8fhCg1 wiTn0CHyfI0AfJ5zQMQ48z/ATI5+/pP5DuJ4kPLYU90EdIlIQ/9zQDRxVPvUalas kskX8qdsbILaLqKzqvPa6jkhncV7SVQPhxIrs6vU2RWrsU1Qw9wHhzaITu9yb1l1 s8uialiJWnWOwkhIAUULOoGRnBB7U6sseFh7LcYHc2sZYJzQQO3g14ANvtsu/ILT abUPXtGHSUlI4kUjI82NEkyQz/pDD9VkhTefiFHNymIkUaZBulmR5QdcEcQn6bku J/P3M/T7CtsCPdBUrzDI71eAYqXPjikg+8unKWk9c/p+V7wXtvsdgJf3+xht/YUQ -----END RSA PRIVATE KEY----- httpclient-2.3.3/sample/ssl/0cert.pem0000644000004100000410000000241612155433207017534 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDjzCCAnegAwIBAgIBADANBgkqhkiG9w0BAQUFADAtMQswCQYDVQQGEwJDWjEN MAsGA1UEChMEUnVieTEPMA0GA1UEAxMGUnVieUNBMB4XDTAzMDUzMTAyNDcyOFoX DTA1MDUzMDAyNDcyOFowLTELMAkGA1UEBhMCQ1oxDTALBgNVBAoTBFJ1YnkxDzAN BgNVBAMTBlJ1YnlDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANfw JSR2OxME6WBRYekhvN1M7uZwJLC3qHhRtBm583x7MS3yzF/HwFNH1oAmOmzUcDSz Y9OmTDVIMU9b0bSYHuu8KswrRWmx/iEhgU/hODS1MQKi+uHoMTtY/dVXwTLAfw5d UMPQ9F5EhiV57sKWrS7vIVBtpxUNgfJW61FP+Ru3Lr8uhUgXPECHck8fjFu8w2Sw JQBQ8ePrfKBiTHpOzKmDVXXWzVYzIQN0zQfpu/FSjOJ4xWV3wmfltN4FK+UkpALW 3RKsNFx+Pmji0fr2/PeEPhzKkhWk5b+pYrIlTNkagS7u8EoeLtY1y3LSZvopbcPI l0QFQHCMtxS7ngC6wsECAwEAAaOBuTCBtjAPBgNVHRMECDAGAQH/AgEAMC0GCWCG SAGG+EIBDQQgFh5HZW5lcmF0ZWQgYnkgT3BlblNTTCBmb3IgUnVieS4wHQYDVR0O BBYEFA2IpXrgDnpJ9p6bfBmtM6j0IejmMFUGA1UdIwROMEyAFA2IpXrgDnpJ9p6b fBmtM6j0IejmoTGkLzAtMQswCQYDVQQGEwJDWjENMAsGA1UEChMEUnVieTEPMA0G A1UEAxMGUnVieUNBggEAMA0GCSqGSIb3DQEBBQUAA4IBAQB1b4iTezqgZj6FhcAc 0AtleAc8TpUn8YOX6zSlHG6I7tKcLfnWen9eFs4Jx73Bup5wHvcrHF6U+/nAdpb5 R7lkNbjWFWwoi5cQC36mh4ym8GWhUZUf8362yPzimbdKMBIiDoIBY8NyIBBORU2m BlXsHr0dqGpeTmNnFhgi1FCN+F/VitplOUP9E8NzsgLS/GY9HO160HbPXoZc4yp5 KFweqmyhbxZvkrrD6LanvB23cJP+TeHzA5mLJtIBciFq+AJe+cDTlJYjtLcsxkAy Evojd//5gePjgSz3gbzU5Zq0/tI2T0rQYnmySoSQ1u2+i5FrMvEjh/H6tZtQ6nO0 ROTL -----END CERTIFICATE----- httpclient-2.3.3/sample/ssl/ssl_client.rb0000644000004100000410000000110312155433207020470 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift(File.join('..', '..', 'lib')) require 'httpclient' url = ARGV.shift || 'https://localhost:8808/' uri = URI.parse(url) #ca_file = "0cert.pem" #crl_file = '0crl.pem' # create CA's cert in pem format and run 'c_rehash' in trust_certs dir. before # using this. ca_path = File.join(File.dirname(File.expand_path(__FILE__)), "trust_certs") proxy = ENV['HTTP_PROXY'] || ENV['http_proxy'] || nil h = HTTPClient.new(proxy) #h.ssl_config.add_trust_ca(ca_file) #h.ssl_config.add_crl(crl_file) h.ssl_config.add_trust_ca(ca_path) print h.get_content(url) httpclient-2.3.3/sample/ssl/1000key.pem0000644000004100000410000000170312155433207017606 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,5E9A9AC8F0F62A4A 3/VDlc8E13xKZr+MXt+HVZCiuWN1gsVx1bTZE5fN5FVoJ45Bgy2zAegnqZiP1NNy j/76Vy9Ru/G9HPh5QPbIPl+md+tOyJV2M23a+5jESk1tLuWt5lRqmTtHN000844l uCgZPPhRV7nhYPC5f6Gqw/xDl/EZsElqJM/hl2JbqiVKfT5/1i0STU8LNkoaLWrJ kQhc7hOR5ihmDPeD8mA99bmGD+UyyqzzLTtKvRbBObyi9dy7cQ5Q+iptQWTUuEKI +W7b8f8/Iiin4JJZGpFuhQSx0ARjT0fuYNXddmz1L3Gu3sjzN1GvT2T3GlpiF4/7 ERS8Q43zjoCP8nC2MTSvdNRRoPMBg2SDS2ZIq4GSiKsKjeN+GnPCycAMeZSr5yG6 VMBJLoAJ7XIcl3se8gF6hH1AfhCzDaK/2pKLP9jH/W4g6VvUBazKEQCNbZXSTpQ4 8EfvJBPpplFs3Zid6iwC/WjKhFBuBBfycwNJjXG9x1fMPkBM8HeiZXgrXoUXJMEP RF05Beo0HPPEOPIxcG6EVmnpDvs8uC+xIQ6UE6DrLGK5TnR6kdz3BDpeAehujSss wfZiOvuJQZQl+oovOH54pcwAwhicgRcNdIX47kHrXNL1vQMYTXte+ZzDGyoOXd0W qf1CZbrjULT9nfJFWMMicTnLM/6iQx+3bTkXXvk0qP0qAoIPqtY4rwt6yHgq937A LubDxudMWV0hqcnH8cBCPHfWdE4HELw4RcVXmQH43yHs1gwShyG9rTS+PCKoRr8u bpssW6J0xJmilf1KprbNWJyof9i0CtSVOlUt6ttoinwqj8Me01dHqQ== -----END RSA PRIVATE KEY----- httpclient-2.3.3/sample/auth.rb0000644000004100000410000000052612155433207016501 0ustar www-datawww-datarequire 'httpclient' c = HTTPClient.new c.debug_dev = STDOUT # for Proxy authentication: supports Basic, Negotiate and NTLM. #c.set_proxy_auth("admin", "admin") # for WWW authentication: supports Basic, Digest and Negotiate. c.set_auth("http://dev.ctor.org/http-access2/", "user", "user") p c.get("http://dev.ctor.org/http-access2/login") httpclient-2.3.3/sample/async.rb0000644000004100000410000000021612155433207016651 0ustar www-datawww-datarequire 'httpclient' c = HTTPClient.new conn = c.get_async("http://dev.ctor.org/") io = conn.pop.content while str = io.read(40) p str end httpclient-2.3.3/sample/thread.rb0000644000004100000410000000057112155433207017007 0ustar www-datawww-data$:.unshift(File.join('..', 'lib')) require 'httpclient' urlstr = ARGV.shift proxy = ENV['HTTP_PROXY'] || ENV['http_proxy'] h = HTTPClient.new(proxy) count = 20 res = [] g = [] for i in 0..count g << Thread.new { res[i] = h.get(urlstr) } end g.each do |th| th.join end for i in 0..(count - 1) raise unless (res[i].content == res[i + 1].content) end puts 'ok' httpclient-2.3.3/sample/cookie.rb0000644000004100000410000000052412155433207017007 0ustar www-datawww-data#!/usr/bin/env ruby $:.unshift(File.join('..', 'lib')) require 'httpclient' proxy = ENV['HTTP_PROXY'] clnt = HTTPClient.new(proxy) clnt.set_cookie_store("cookie.dat") clnt.debug_dev = STDOUT if $DEBUG while urlstr = ARGV.shift response = clnt.get(urlstr){ |data| print data } p response.contenttype end clnt.save_cookie_store httpclient-2.3.3/sample/oauth_buzz.rb0000644000004100000410000000354312155433207017734 0ustar www-datawww-datarequire 'oauthclient' require 'zlib' require 'stringio' # Get your own consumer token from http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html consumer_key = nil consumer_secret = nil callback = 'http://localhost/' # should point somewhere else... scope = 'https://www.googleapis.com/auth/buzz' request_token_url = 'https://www.google.com/accounts/OAuthGetRequestToken' access_token_url = 'https://www.google.com/accounts/OAuthGetAccessToken' STDOUT.sync = true # create OAuth client. client = OAuthClient.new client.oauth_config.consumer_key = consumer_key client.oauth_config.consumer_secret = consumer_secret client.oauth_config.signature_method = 'HMAC-SHA1' client.oauth_config.http_method = :get # Twitter does not allow :post client.debug_dev = STDERR if $DEBUG # Get request token. res = client.get_request_token(request_token_url, callback, :scope => scope) p res.status p res.oauth_params p res.content p client.oauth_config token = res.oauth_params['oauth_token'] secret = res.oauth_params['oauth_token_secret'] raise if token.nil? or secret.nil? # You need to confirm authorization out of band. puts puts "Go here and do confirm: https://www.google.com/buzz/api/auth/OAuthAuthorizeToken?oauth_token=#{token}&domain=#{consumer_key}&scope=#{scope}" puts "Type oauth_verifier (if given) and hit [enter] to go" require 'cgi' verifier = CGI.unescape(gets.chomp) verifier = nil if verifier.empty? # Get access token. res = client.get_access_token(access_token_url, token, secret, verifier) p res.status p res.oauth_params p res.content p client.oauth_config id = res.oauth_params['user_id'] puts puts "Access token usage example" puts "Hit [enter] to go" gets # Access to a protected resource. # @consumption requires Buzz API puts client.get_content("https://www.googleapis.com/buzz/v1/activities/@me/@consumption", :alt => :json, :prettyprint => true) httpclient-2.3.3/test/0000755000004100000410000000000012155433207014706 5ustar www-datawww-datahttpclient-2.3.3/test/client.cert0000644000004100000410000000220312155433207017040 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDKDCCAhCgAwIBAgIBAjANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X DTA0MDEzMTAzMTQ1OFoXDTM1MDEyMzAzMTQ1OFowZTELMAkGA1UEBgwCSlAxEjAQ BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMRAwDgYDVQQDDAdleGFtcGxl MSIwIAYJKoZIhvcNAQkBDBNleGFtcGxlQGV4YW1wbGUub3JnMIGfMA0GCSqGSIb3 DQEBAQUAA4GNADCBiQKBgQDRWssrK8Gyr+500hpLjCGR3+AHL8/hEJM5zKi/MgLW jTkvsgOwbYwXOiNtAbR9y4/ucDq7EY+cMUMHES4uFaPTcOaAV0aZRmk8AgslN1tQ gNS6ew7/Luq3DcVeWkX8PYgR9VG0mD1MPfJ6+IFA5d3vKpdBkBgN4l46jjO0/2Xf ewIDAQABo4GPMIGMMAwGA1UdEwEB/wQCMAAwMQYJYIZIAYb4QgENBCQWIlJ1Ynkv T3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFOFvay0H7lr2 xUx6waYEV2bVDYQhMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAgYI KwYBBQUHAwQwDQYJKoZIhvcNAQEFBQADggEBABd2dYWqbDIWf5sWFvslezxJv8gI w64KCJBuyJAiDuf+oazr3016kMzAlt97KecLZDusGNagPrq02UX7YMoQFsWJBans cDtHrkM0al5r6/WGexNMgtYbNTYzt/IwodISGBgZ6dsOuhznwms+IBsTNDAvWeLP lt2tOqD8kEmjwMgn0GDRuKjs4EoboA3kMULb1p9akDV9ZESU3eOtpS5/G5J5msLI 9WXbYBjcjvkLuJH9VsJhb+R58Vl0ViemvAHhPilSl1SPWVunGhv6FcIkdBEi1k9F e8BNMmsEjFiANiIRvpdLRbiGBt0KrKTndVfsmoKCvY48oCOvnzxtahFxfs8= -----END CERTIFICATE----- httpclient-2.3.3/test/test_hexdump.rb0000644000004100000410000000116312155433207017745 0ustar www-datawww-data# -*- encoding: utf-8 -*- require File.expand_path('helper', File.dirname(__FILE__)) require 'hexdump' class TestHexDump < Test::Unit::TestCase def test_encode str = "\032l\277\370\2429\216\236\351[{\{\262\350\274\376" str.force_encoding('BINARY') if str.respond_to?(:force_encoding) assert_equal(["00000000 1a6cbff8 a2398e9e e95b7b7b b2e8bcfe .l...9...[{{...."], HexDump.encode(str)) end end if defined?(RUBY_ENGINE) && RUBY_ENGINE != "rbx" && RUBY_VERSION >= "1.9.0" # Rubinius 1.8 mode does not support Regexp.quote(raw, 'n') I don't want put # a pressure on supporting it because 1.9 mode works fine. httpclient-2.3.3/test/ca.cert0000644000004100000410000000254712155433207016160 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIID0DCCArigAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X DTA0MDEzMDAwNDIzMloXDTM2MDEyMjAwNDIzMlowPDELMAkGA1UEBgwCSlAxEjAQ BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQswCQYDVQQDDAJDQTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANbv0x42BTKFEQOE+KJ2XmiSdZpR wjzQLAkPLRnLB98tlzs4xo+y4RyY/rd5TT9UzBJTIhP8CJi5GbS1oXEerQXB3P0d L5oSSMwGGyuIzgZe5+vZ1kgzQxMEKMMKlzA73rbMd4Jx3u5+jdbP0EDrPYfXSvLY bS04n2aX7zrN3x5KdDrNBfwBio2/qeaaj4+9OxnwRvYP3WOvqdW0h329eMfHw0pi JI0drIVdsEqClUV4pebT/F+CPUPkEh/weySgo9wANockkYu5ujw2GbLFcO5LXxxm dEfcVr3r6t6zOA4bJwL0W/e6LBcrwiG/qPDFErhwtgTLYf6Er67SzLyA66UCAwEA AaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIBDQQkFiJSdWJ5L09w ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRJ7Xd380KzBV7f USKIQ+O/vKbhDzAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0wW4AUSe13d/NCswVe 31EiiEPjv7ym4Q+hQKQ+MDwxCzAJBgNVBAYMAkpQMRIwEAYDVQQKDAlKSU4uR1Iu SlAxDDAKBgNVBAsMA1JSUjELMAkGA1UEAwwCQ0GCAQAwDQYJKoZIhvcNAQEFBQAD ggEBAIu/mfiez5XN5tn2jScgShPgHEFJBR0BTJBZF6xCk0jyqNx/g9HMj2ELCuK+ r/Y7KFW5c5M3AQ+xWW0ZSc4kvzyTcV7yTVIwj2jZ9ddYMN3nupZFgBK1GB4Y05GY MJJFRkSu6d/Ph5ypzBVw2YMT/nsOo5VwMUGLgS7YVjU+u/HNWz80J3oO17mNZllj PvORJcnjwlroDnS58KoJ7GDgejv3ESWADvX1OHLE4cRkiQGeLoEU4pxdCxXRqX0U PbwIkZN9mXVcrmPHq8MWi4eC/V7hnbZETMHuWhUoiNdOEfsAXr3iP4KjyyRdwc7a d/xgcK06UVQRL/HbEYGiQL056mc= -----END CERTIFICATE----- httpclient-2.3.3/test/test_http-access2.rb0000644000004100000410000003472712155433207020607 0ustar www-datawww-datarequire 'http-access2' require File.expand_path('helper', File.dirname(__FILE__)) module HTTPAccess2 class TestClient < Test::Unit::TestCase include Helper include HTTPClient::Util def setup super setup_server setup_client end def teardown super end def test_initialize setup_proxyserver escape_noproxy do @proxyio.string = "" @client = HTTPAccess2::Client.new(proxyurl) assert_equal(urify(proxyurl), @client.proxy) assert_equal(200, @client.head(serverurl).status) assert(!@proxyio.string.empty?) end end def test_agent_name @client = HTTPAccess2::Client.new(nil, "agent_name_foo") str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match(/^User-Agent: agent_name_foo/, lines[4]) end def test_from @client = HTTPAccess2::Client.new(nil, nil, "from_bar") str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match(/^From: from_bar/, lines[5]) end def test_debug_dev str = "" @client.debug_dev = str assert(str.empty?) @client.get(serverurl) assert(!str.empty?) end def _test_protocol_version_http09 @client.protocol_version = 'HTTP/0.9' str = "" @client.debug_dev = str @client.get(serverurl + 'hello') lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET /hello HTTP/0.9", lines[3]) assert_equal("Connection: close", lines[5]) assert_equal("= Response", lines[6]) assert_match(/^hello/, lines[7]) end def test_protocol_version_http10 @client.protocol_version = 'HTTP/1.0' str = "" @client.debug_dev = str @client.get(serverurl + 'hello') lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET /hello HTTP/1.0", lines[3]) assert_equal("Connection: close", lines[7]) assert_equal("= Response", lines[8]) end def test_protocol_version_http11 str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.1", lines[3]) assert_equal("Host: localhost:#{serverport}", lines[7]) @client.protocol_version = 'HTTP/1.1' str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.1", lines[3]) @client.protocol_version = 'HTTP/1.0' str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.0", lines[3]) end def test_proxy setup_proxyserver escape_noproxy do begin @client.proxy = "http://" rescue assert_match(/InvalidURIError/, $!.class.to_s) end @client.proxy = "" assert_nil(@client.proxy) @client.proxy = "http://foo:1234" assert_equal(urify("http://foo:1234"), @client.proxy) uri = urify("http://bar:2345") @client.proxy = uri assert_equal(uri, @client.proxy) # @proxyio.string = "" @client.proxy = nil assert_equal(200, @client.head(serverurl).status) assert(@proxyio.string.empty?) # @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(!@proxyio.string.empty?) end end def test_noproxy_for_localhost @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(@proxyio.string.empty?) end def test_no_proxy setup_proxyserver escape_noproxy do # proxy is not set. @client.no_proxy = 'localhost' @proxyio.string = "" @client.proxy = nil assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @client.no_proxy = 'foobar' @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ =~ @proxyio.string) # @client.no_proxy = 'foobar,localhost:baz' @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @client.no_proxy = 'foobar,localhost:443' @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ =~ @proxyio.string) # @client.no_proxy = "foobar,localhost:443:localhost:#{serverport},baz" @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) end end def test_get_content assert_equal('hello', @client.get_content(serverurl + 'hello')) assert_equal('hello', @client.get_content(serverurl + 'redirect1')) assert_equal('hello', @client.get_content(serverurl + 'redirect2')) assert_raises(HTTPClient::Session::BadResponse) do @client.get_content(serverurl + 'notfound') end assert_raises(HTTPClient::Session::BadResponse) do @client.get_content(serverurl + 'redirect_self') end called = false @client.redirect_uri_callback = lambda { |uri, res| newuri = res.header['location'][0] called = true newuri } assert_equal('hello', @client.get_content(serverurl + 'relative_redirect')) assert(called) end def test_post_content assert_equal('hello', @client.post_content(serverurl + 'hello')) assert_equal('hello', @client.post_content(serverurl + 'redirect1')) assert_equal('hello', @client.post_content(serverurl + 'redirect2')) assert_raises(HTTPClient::Session::BadResponse) do @client.post_content(serverurl + 'notfound') end assert_raises(HTTPClient::Session::BadResponse) do @client.post_content(serverurl + 'redirect_self') end called = false @client.redirect_uri_callback = lambda { |uri, res| newuri = res.header['location'][0] called = true newuri } assert_equal('hello', @client.post_content(serverurl + 'relative_redirect')) assert(called) end def test_head assert_equal("head", @client.head(serverurl + 'servlet').header["x-head"][0]) param = {'1'=>'2', '3'=>'4'} res = @client.head(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_get assert_equal("get", @client.get(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_post assert_equal("post", @client.post(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_put assert_equal("put", @client.put(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_delete assert_equal("delete", @client.delete(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_options assert_equal("options", @client.options(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_trace assert_equal("trace", @client.trace(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_get_query assert_equal({'1'=>'2'}, check_query_get({1=>2})) assert_equal({'a'=>'A', 'B'=>'b'}, check_query_get({"a"=>"A", "B"=>"b"})) assert_equal({'&'=>'&'}, check_query_get({"&"=>"&"})) assert_equal({'= '=>' =+'}, check_query_get({"= "=>" =+"})) assert_equal( ['=', '&'].sort, check_query_get([["=", "="], ["=", "&"]])['='].to_ary.sort ) assert_equal({'123'=>'45'}, check_query_get('123=45')) assert_equal({'12 3'=>'45', ' '=>' '}, check_query_get('12+3=45&+=+')) assert_equal({}, check_query_get('')) end def test_post_body assert_equal({'1'=>'2'}, check_query_post({1=>2})) assert_equal({'a'=>'A', 'B'=>'b'}, check_query_post({"a"=>"A", "B"=>"b"})) assert_equal({'&'=>'&'}, check_query_post({"&"=>"&"})) assert_equal({'= '=>' =+'}, check_query_post({"= "=>" =+"})) assert_equal( ['=', '&'].sort, check_query_post([["=", "="], ["=", "&"]])['='].to_ary.sort ) assert_equal({'123'=>'45'}, check_query_post('123=45')) assert_equal({'12 3'=>'45', ' '=>' '}, check_query_post('12+3=45&+=+')) assert_equal({}, check_query_post('')) # post_body = StringIO.new("foo=bar&foo=baz") assert_equal( ["bar", "baz"], check_query_post(post_body)["foo"].to_ary.sort ) end def test_extra_headers str = "" @client.debug_dev = str @client.head(serverurl, nil, {"ABC" => "DEF"}) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match("ABC: DEF", lines[4]) # str = "" @client.debug_dev = str @client.get(serverurl, nil, [["ABC", "DEF"], ["ABC", "DEF"]]) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match("ABC: DEF", lines[4]) assert_match("ABC: DEF", lines[5]) end def test_timeout assert_equal(60, @client.connect_timeout) assert_equal(120, @client.send_timeout) assert_equal(60, @client.receive_timeout) end def test_connect_timeout # ToDo end def test_send_timeout # ToDo end def test_receive_timeout # this test takes 2 sec assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=2')) @client.receive_timeout = 1 assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=0')) assert_raise(HTTPClient::ReceiveTimeoutError) do @client.get_content(serverurl + 'sleep?sec=2') end @client.receive_timeout = 3 assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=2')) end def test_cookies cookiefile = File.join(File.dirname(File.expand_path(__FILE__)), 'test_cookies_file') # from [ruby-talk:164079] File.open(cookiefile, "wb") do |f| f << "http://rubyforge.org//account/login.php session_ser LjEwMy45Ni40Ni0q%2A-fa0537de8cc31 1131676286 .rubyforge.org / 13\n" end cm = WebAgent::CookieManager::new(cookiefile) cm.load_cookies cookie = cm.cookies.first url = cookie.url assert(cookie.domain_match(url.host, cookie.domain)) end private def check_query_get(query) WEBrick::HTTPUtils.parse_query( @client.get(serverurl + 'servlet', query).header["x-query"][0] ) end def check_query_post(query) WEBrick::HTTPUtils.parse_query( @client.post(serverurl + 'servlet', query).header["x-query"][0] ) end def setup_server @server = WEBrick::HTTPServer.new( :BindAddress => "localhost", :Logger => @logger, :Port => 0, :AccessLog => [], :DocumentRoot => File.dirname(File.expand_path(__FILE__)) ) @serverport = @server.config[:Port] [:hello, :sleep, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect].each do |sym| @server.mount( "/#{sym}", WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc) ) end @server.mount('/servlet', TestServlet.new(@server)) @server_thread = start_server_thread(@server) end def escape_noproxy backup = HTTPAccess2::Client::NO_PROXY_HOSTS.dup HTTPAccess2::Client::NO_PROXY_HOSTS.clear yield ensure HTTPAccess2::Client::NO_PROXY_HOSTS.replace(backup) end def do_hello(req, res) res['content-type'] = 'text/html' res.body = "hello" end def do_sleep(req, res) sec = req.query['sec'].to_i sleep sec res['content-type'] = 'text/html' res.body = "hello" end def do_redirect1(req, res) res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, serverurl + "hello") end def do_redirect2(req, res) res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "redirect3") end def do_redirect3(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "hello") end def do_redirect_self(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "redirect_self") end def do_relative_redirect(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, "hello") end class TestServlet < WEBrick::HTTPServlet::AbstractServlet def get_instance(*arg) self end def do_HEAD(req, res) res["x-head"] = 'head' # use this for test purpose only. res["x-query"] = query_response(req) end def do_GET(req, res) res.body = 'get' res["x-query"] = query_response(req) end def do_POST(req, res) res.body = 'post' res["x-query"] = body_response(req) end def do_PUT(req, res) res.body = 'put' end def do_DELETE(req, res) res.body = 'delete' end def do_OPTIONS(req, res) # check RFC for legal response. res.body = 'options' end def do_TRACE(req, res) # client SHOULD reflect the message received back to the client as the # entity-body of a 200 (OK) response. [RFC2616] res.body = 'trace' res["x-query"] = query_response(req) end private def query_response(req) query_escape(WEBrick::HTTPUtils.parse_query(req.query_string)) end def body_response(req) query_escape(WEBrick::HTTPUtils.parse_query(req.body)) end def query_escape(query) escaped = [] query.collect do |k, v| v.to_ary.each do |ve| escaped << CGI.escape(k) + '=' + CGI.escape(ve) end end escaped.join('&') end end end end httpclient-2.3.3/test/test_include_client.rb0000644000004100000410000000307112155433207021254 0ustar www-datawww-data# -*- encoding: utf-8 -*- require File.expand_path('helper', File.dirname(__FILE__)) require 'httpclient/include_client' class TestIncludeClient < Test::Unit::TestCase class Widget extend HTTPClient::IncludeClient include_http_client("http://example.com") do |client| client.cookie_manager = nil client.agent_name = "iMonkey 4k" end end class OtherWidget extend HTTPClient::IncludeClient include_http_client include_http_client(:method_name => :other_http_client) end class UnrelatedBlankClass ; end def test_client_class_level_singleton assert_equal Widget.http_client.object_id, Widget.http_client.object_id assert_equal Widget.http_client.object_id, Widget.new.http_client.object_id assert_not_equal Widget.http_client.object_id, OtherWidget.http_client.object_id end def test_configured assert_equal Widget.http_client.agent_name, "iMonkey 4k" assert_nil Widget.http_client.cookie_manager assert_equal Widget.http_client.proxy.to_s, "http://example.com" end def test_two_includes assert_not_equal OtherWidget.http_client.object_id, OtherWidget.other_http_client.object_id assert_equal OtherWidget.other_http_client.object_id, OtherWidget.new.other_http_client.object_id end # meta-programming gone wrong sometimes accidentally # adds the class method to _everyone_, a mistake we've made before. def test_not_infected_class_hieararchy assert ! Class.respond_to?(:http_client) assert ! UnrelatedBlankClass.respond_to?(:http_client) end end httpclient-2.3.3/test/client.key0000644000004100000410000000156712155433207016707 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQDRWssrK8Gyr+500hpLjCGR3+AHL8/hEJM5zKi/MgLWjTkvsgOw bYwXOiNtAbR9y4/ucDq7EY+cMUMHES4uFaPTcOaAV0aZRmk8AgslN1tQgNS6ew7/ Luq3DcVeWkX8PYgR9VG0mD1MPfJ6+IFA5d3vKpdBkBgN4l46jjO0/2XfewIDAQAB AoGAZcz8llWErtsV3QB9gNb3S/PNADGjqBFjReva8n3jG2k4sZSibpwWTwUaTNtT ZQgjSRKRvH1hk9XwffNAvXAQZNNkuj/16gO2oO45nyLj4dO365ujLptWnVIWDHOE uN0GeiZO+VzcCisT0WCq4tvtLeH8svrxzA8cbXIEyOK7NiECQQDwo2zPFyKAZ/Cu lDJ6zKT+RjfWwW7DgWzirAlTrt4ViMaW+IaDH29TmQpb4V4NuR3Xi+2Xl4oicu6S 36TW9+/FAkEA3rgfOQJuLlWSnw1RTGwvnC816a/W7iYYY7B+0U4cDbfWl7IoXT4y M8nV/HESooviZLqBwzAYSoj3fFKYBKpGPwJAUO8GN5iWWA2dW3ooiDiv/X1sZmRk dojfMFWgRW747tEzya8Ivq0h6kH8w+5GjeMG8Gn1nRiwsulo6Ckj7dEx6QJACyui 7UIQ8qP6GZ4aYMHgVW4Mvy7Bkeo5OO7GPYs0Xv/EdJFL8vlGnVBXOjUVoS9w6Gpu TbLg1QQvnX2rADjmEwJANxZO2GUkaWGsEif8aGW0x5g/IdaMGG27pTWk5zqix7P3 1UDrdo/JOXhptovhRi06EppIxAxYmbh9vd9VN8Itlw== -----END RSA PRIVATE KEY----- httpclient-2.3.3/test/ca-chain.cert0000644000004100000410000000510012155433207017224 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIID0DCCArigAwIBAgIBADANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X DTA0MDEzMDAwNDIzMloXDTM2MDEyMjAwNDIzMlowPDELMAkGA1UEBgwCSlAxEjAQ BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQswCQYDVQQDDAJDQTCCASIw DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANbv0x42BTKFEQOE+KJ2XmiSdZpR wjzQLAkPLRnLB98tlzs4xo+y4RyY/rd5TT9UzBJTIhP8CJi5GbS1oXEerQXB3P0d L5oSSMwGGyuIzgZe5+vZ1kgzQxMEKMMKlzA73rbMd4Jx3u5+jdbP0EDrPYfXSvLY bS04n2aX7zrN3x5KdDrNBfwBio2/qeaaj4+9OxnwRvYP3WOvqdW0h329eMfHw0pi JI0drIVdsEqClUV4pebT/F+CPUPkEh/weySgo9wANockkYu5ujw2GbLFcO5LXxxm dEfcVr3r6t6zOA4bJwL0W/e6LBcrwiG/qPDFErhwtgTLYf6Er67SzLyA66UCAwEA AaOB3DCB2TAPBgNVHRMBAf8EBTADAQH/MDEGCWCGSAGG+EIBDQQkFiJSdWJ5L09w ZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBRJ7Xd380KzBV7f USKIQ+O/vKbhDzAOBgNVHQ8BAf8EBAMCAQYwZAYDVR0jBF0wW4AUSe13d/NCswVe 31EiiEPjv7ym4Q+hQKQ+MDwxCzAJBgNVBAYMAkpQMRIwEAYDVQQKDAlKSU4uR1Iu SlAxDDAKBgNVBAsMA1JSUjELMAkGA1UEAwwCQ0GCAQAwDQYJKoZIhvcNAQEFBQAD ggEBAIu/mfiez5XN5tn2jScgShPgHEFJBR0BTJBZF6xCk0jyqNx/g9HMj2ELCuK+ r/Y7KFW5c5M3AQ+xWW0ZSc4kvzyTcV7yTVIwj2jZ9ddYMN3nupZFgBK1GB4Y05GY MJJFRkSu6d/Ph5ypzBVw2YMT/nsOo5VwMUGLgS7YVjU+u/HNWz80J3oO17mNZllj PvORJcnjwlroDnS58KoJ7GDgejv3ESWADvX1OHLE4cRkiQGeLoEU4pxdCxXRqX0U PbwIkZN9mXVcrmPHq8MWi4eC/V7hnbZETMHuWhUoiNdOEfsAXr3iP4KjyyRdwc7a d/xgcK06UVQRL/HbEYGiQL056mc= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDaDCCAlCgAwIBAgIBATANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X DTA0MDEzMDAwNDMyN1oXDTM1MDEyMjAwNDMyN1owPzELMAkGA1UEBgwCSlAxEjAQ BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQ4wDAYDVQQDDAVTdWJDQTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0Ou7AyRcRXnB/kVHv/6kwe ANzgg/DyJfsAUqW90m7Lu1nqyug8gK0RBd77yU0w5HOAMHTVSdpjZK0g2sgx4Mb1 d/213eL9TTl5MRVEChTvQr8q5DVG/8fxPPE7fMI8eOAzd98/NOAChk+80r4Sx7fC kGVEE1bKwY1MrUsUNjOY2d6t3M4HHV3HX1V8ShuKfsHxgCmLzdI8U+5CnQedFgkm 3e+8tr8IX5RR1wA1Ifw9VadF7OdI/bGMzog/Q8XCLf+WPFjnK7Gcx6JFtzF6Gi4x 4dp1Xl45JYiVvi9zQ132wu8A1pDHhiNgQviyzbP+UjcB/tsOpzBQF8abYzgEkWEC AwEAAaNyMHAwDwYDVR0TAQH/BAUwAwEB/zAxBglghkgBhvhCAQ0EJBYiUnVieS9P cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUlCjXWLsReYzH LzsxwVnCXmKoB/owCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCJ/OyN rT8Cq2Y+G2yA/L1EMRvvxwFBqxavqaqHl/6rwsIBFlB3zbqGA/0oec6MAVnYynq4 c4AcHTjx3bQ/S4r2sNTZq0DH4SYbQzIobx/YW8PjQUJt8KQdKMcwwi7arHP7A/Ha LKu8eIC2nsUBnP4NhkYSGhbmpJK+PFD0FVtD0ZIRlY/wsnaZNjWWcnWF1/FNuQ4H ySjIblqVQkPuzebv3Ror6ZnVDukn96Mg7kP4u6zgxOeqlJGRe1M949SS9Vudjl8X SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ uY/bPeOBYiVsOYVe -----END CERTIFICATE----- httpclient-2.3.3/test/sslsvr.rb0000644000004100000410000000253112155433207016570 0ustar www-datawww-datarequire 'webrick/https' require 'logger' require 'rbconfig' PORT = 17171 DIR = File.dirname(File.expand_path(__FILE__)) def cert(filename) OpenSSL::X509::Certificate.new(File.open(File.join(DIR, filename)) { |f| f.read }) end def key(filename) OpenSSL::PKey::RSA.new(File.open(File.join(DIR, filename)) { |f| f.read }) end def do_hello(req, res) res['content-type'] = 'text/html' res.body = "hello" end logger = Logger.new(STDERR) logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level) server = WEBrick::HTTPServer.new( :BindAddress => "localhost", :Logger => logger, :Port => PORT, :AccessLog => [], :DocumentRoot => DIR, :SSLEnable => true, :SSLCACertificateFile => File.join(DIR, 'ca.cert'), :SSLCertificate => cert('server.cert'), :SSLPrivateKey => key('server.key'), :SSLVerifyClient => nil, #OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT|OpenSSL::SSL::VERIFY_PEER, :SSLClientCA => cert('ca.cert'), :SSLCertName => nil ) trap(:INT) do server.shutdown end [:hello].each do |sym| server.mount( "/#{sym}", WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc) ) end t = Thread.new { Thread.current.abort_on_exception = true server.start } while server.status != :Running sleep 0.1 unless t.alive? t.join raise end end STDOUT.sync = true puts $$ t.join httpclient-2.3.3/test/htpasswd0000644000004100000410000000005012155433207016461 0ustar www-datawww-dataadmin:Qg266hq/YYKe2 guest:gbPc4vPCH.h12 httpclient-2.3.3/test/runner.rb0000644000004100000410000000011412155433207016540 0ustar www-datawww-datarequire 'test/unit' exit Test::Unit::AutoRunner.run(true, File.dirname($0)) httpclient-2.3.3/test/helper.rb0000644000004100000410000000450112155433207016512 0ustar www-datawww-data# -*- encoding: utf-8 -*- require 'test/unit' begin require 'simplecov' require 'simplecov-rcov' SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter SimpleCov.start rescue LoadError end require 'httpclient' require 'webrick' require 'webrick/httpproxy.rb' require 'logger' require 'stringio' require 'cgi' require 'webrick/httputils' module Helper Port = 17171 ProxyPort = 17172 def serverport @serverport end def proxyport @proxyport end def serverurl "http://localhost:#{serverport}/" end def proxyurl "http://localhost:#{proxyport}/" end def setup @logger = Logger.new(STDERR) @logger.level = Logger::Severity::FATAL @proxyio = StringIO.new @proxylogger = Logger.new(@proxyio) @proxylogger.level = Logger::Severity::DEBUG @server = @proxyserver = @client = nil @server_thread = @proxyserver_thread = nil @serverport = Port @proxyport = ProxyPort end def teardown teardown_client if @client teardown_proxyserver if @proxyserver teardown_server if @server end def setup_client @client = HTTPClient.new end #def setup_server # override it # @server = WEBrick::HTTPServer.new(...) # @server_thread = start_server_thread(@server) #end def setup_proxyserver @proxyserver = WEBrick::HTTPProxyServer.new( :BindAddress => "localhost", :Logger => @proxylogger, :Port => 0, :AccessLog => [] ) @proxyport = @proxyserver.config[:Port] @proxyserver_thread = start_server_thread(@proxyserver) end def teardown_client @client.reset_all end def teardown_server @server.shutdown #@server_thread.kill end def teardown_proxyserver @proxyserver.shutdown #@proxyserver_thread.kill end def start_server_thread(server) t = Thread.new { Thread.current.abort_on_exception = true server.start } while server.status != :Running Thread.pass unless t.alive? t.join raise end end t end def params(str) HTTP::Message.parse(str).inject({}) { |r, (k, v)| r[k] = v.first; r } end def silent begin back, $VERBOSE = $VERBOSE, nil yield ensure $VERBOSE = back end end def escape_env env = {} env.update(ENV) yield ensure ENV.clear ENV.update(env) end end httpclient-2.3.3/test/test_auth.rb0000644000004100000410000003167512155433207017247 0ustar www-datawww-datarequire File.expand_path('helper', File.dirname(__FILE__)) require 'digest/md5' class TestAuth < Test::Unit::TestCase include Helper def setup super setup_server end def teardown super end def setup_server @server = WEBrick::HTTPServer.new( :BindAddress => "localhost", :Logger => @logger, :Port => 0, :AccessLog => [], :DocumentRoot => File.dirname(File.expand_path(__FILE__)) ) @serverport = @server.config[:Port] @server.mount( '/basic_auth', WEBrick::HTTPServlet::ProcHandler.new(method(:do_basic_auth).to_proc) ) @server.mount( '/digest_auth', WEBrick::HTTPServlet::ProcHandler.new(method(:do_digest_auth).to_proc) ) @server.mount( '/digest_sess_auth', WEBrick::HTTPServlet::ProcHandler.new(method(:do_digest_sess_auth).to_proc) ) htpasswd = File.join(File.dirname(__FILE__), 'htpasswd') htpasswd_userdb = WEBrick::HTTPAuth::Htpasswd.new(htpasswd) htdigest = File.join(File.dirname(__FILE__), 'htdigest') htdigest_userdb = WEBrick::HTTPAuth::Htdigest.new(htdigest) @basic_auth = WEBrick::HTTPAuth::BasicAuth.new( :Logger => @logger, :Realm => 'auth', :UserDB => htpasswd_userdb ) @digest_auth = WEBrick::HTTPAuth::DigestAuth.new( :Logger => @logger, :Algorithm => 'MD5', :Realm => 'auth', :UserDB => htdigest_userdb ) @digest_sess_auth = WEBrick::HTTPAuth::DigestAuth.new( :Logger => @logger, :Algorithm => 'MD5-sess', :Realm => 'auth', :UserDB => htdigest_userdb ) @server_thread = start_server_thread(@server) @proxy_digest_auth = WEBrick::HTTPAuth::ProxyDigestAuth.new( :Logger => @proxylogger, :Algorithm => 'MD5', :Realm => 'auth', :UserDB => htdigest_userdb ) @proxyserver = WEBrick::HTTPProxyServer.new( :ProxyAuthProc => @proxy_digest_auth.method(:authenticate).to_proc, :BindAddress => "localhost", :Logger => @proxylogger, :Port => 0, :AccessLog => [] ) @proxyport = @proxyserver.config[:Port] @proxyserver_thread = start_server_thread(@proxyserver) end def do_basic_auth(req, res) @basic_auth.authenticate(req, res) res['content-type'] = 'text/plain' res.body = 'basic_auth OK' end def do_digest_auth(req, res) @digest_auth.authenticate(req, res) res['content-type'] = 'text/plain' res['x-query'] = req.body res.body = 'digest_auth OK' + req.query_string.to_s end def do_digest_sess_auth(req, res) @digest_sess_auth.authenticate(req, res) res['content-type'] = 'text/plain' res['x-query'] = req.body res.body = 'digest_sess_auth OK' + req.query_string.to_s end def test_basic_auth c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth")) end def test_basic_auth_compat c = HTTPClient.new c.set_basic_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth")) end def test_BASIC_auth c = HTTPClient.new webrick_backup = @basic_auth.instance_eval { @auth_scheme } #httpaccess2_backup = c.www_auth.basic_auth.instance_eval { @scheme } begin @basic_auth.instance_eval { @auth_scheme = "BASIC" } c.www_auth.basic_auth.instance_eval { @scheme = "BASIC" } c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth")) ensure @basic_auth.instance_eval { @auth_scheme = webrick_backup } #c.www_auth.basic_auth.instance_eval { @scheme = httpaccess2_backup } end end def test_basic_auth_reuses_credentials c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('basic_auth OK', c.get_content("http://localhost:#{serverport}/basic_auth/")) c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.get_content("http://localhost:#{serverport}/basic_auth/sub/dir/") assert_match /Authorization: Basic YWRtaW46YWRtaW4=/, str end def test_digest_auth c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('digest_auth OK', c.get_content("http://localhost:#{serverport}/digest_auth")) end def test_digest_auth_reuses_credentials c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('digest_auth OK', c.get_content("http://localhost:#{serverport}/digest_auth/")) c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.get_content("http://localhost:#{serverport}/digest_auth/sub/dir/") assert_match /Authorization: Digest/, str end def test_digest_auth_with_block c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') called = false c.get_content("http://localhost:#{serverport}/digest_auth") do |str| assert_equal('digest_auth OK', str) called = true end assert(called) # called = false c.get("http://localhost:#{serverport}/digest_auth") do |str| assert_equal('digest_auth OK', str) called = true end assert(called) end def test_digest_auth_with_post_io c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') post_body = StringIO.new("1234567890") assert_equal('1234567890', c.post("http://localhost:#{serverport}/digest_auth", post_body).header['x-query'][0]) # post_body = StringIO.new("1234567890") post_body.read(5) assert_equal('67890', c.post("http://localhost:#{serverport}/digest_auth", post_body).header['x-query'][0]) end def test_digest_auth_with_querystring c = HTTPClient.new c.debug_dev = STDERR if $DEBUG c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('digest_auth OKbar=baz', c.get_content("http://localhost:#{serverport}/digest_auth/foo?bar=baz")) end def test_perfer_digest c = HTTPClient.new c.set_auth('http://example.com/', 'admin', 'admin') c.test_loopback_http_response << "HTTP/1.0 401 Unauthorized\nWWW-Authenticate: Basic realm=\"foo\"\nWWW-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.get_content('http://example.com/') assert_match(/^Authorization: Digest/, str) end def test_digest_sess_auth c = HTTPClient.new c.set_auth("http://localhost:#{serverport}/", 'admin', 'admin') assert_equal('digest_sess_auth OK', c.get_content("http://localhost:#{serverport}/digest_sess_auth")) end def test_proxy_auth c = HTTPClient.new c.set_proxy_auth('admin', 'admin') c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Basic realm=\"foo\"\nContent-Length: 2\n\nNG" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.get_content('http://example.com/') assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str) end def test_proxy_auth_reuses_credentials c = HTTPClient.new c.set_proxy_auth('admin', 'admin') c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Basic realm=\"foo\"\nContent-Length: 2\n\nNG" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.get_content('http://www1.example.com/') c.debug_dev = str = '' c.get_content('http://www2.example.com/') assert_match(/Proxy-Authorization: Basic YWRtaW46YWRtaW4=/, str) end def test_digest_proxy_auth_loop c = HTTPClient.new c.set_proxy_auth('admin', 'admin') c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" md5 = Digest::MD5.new ha1 = md5.hexdigest("admin:foo:admin") ha2 = md5.hexdigest("GET:/") response = md5.hexdigest("#{ha1}:nonce:#{ha2}") c.debug_dev = str = '' c.get_content('http://example.com/') assert_match(/Proxy-Authorization: Digest/, str) assert_match(%r"response=\"#{response}\"", str) end def test_digest_proxy_auth c=HTTPClient.new("http://localhost:#{proxyport}/") c.set_proxy_auth('admin', 'admin') c.set_auth("http://127.0.0.1:#{serverport}/", 'admin', 'admin') assert_equal('basic_auth OK', c.get_content("http://127.0.0.1:#{serverport}/basic_auth")) end def test_digest_proxy_invalid_auth c=HTTPClient.new("http://localhost:#{proxyport}/") c.set_proxy_auth('admin', 'wrong') c.set_auth("http://127.0.0.1:#{serverport}/", 'admin', 'admin') assert_raises(HTTPClient::BadResponseError) do c.get_content("http://127.0.0.1:#{serverport}/basic_auth") end end def test_prefer_digest_to_basic_proxy_auth c = HTTPClient.new c.set_proxy_auth('admin', 'admin') c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nProxy-Authenticate: Basic realm=\"bar\"\nContent-Length: 2\n\nNG" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" md5 = Digest::MD5.new ha1 = md5.hexdigest("admin:foo:admin") ha2 = md5.hexdigest("GET:/") response = md5.hexdigest("#{ha1}:nonce:#{ha2}") c.debug_dev = str = '' c.get_content('http://example.com/') assert_match(/Proxy-Authorization: Digest/, str) assert_match(%r"response=\"#{response}\"", str) end def test_digest_proxy_auth_reuses_credentials c = HTTPClient.new c.set_proxy_auth('admin', 'admin') c.test_loopback_http_response << "HTTP/1.0 407 Unauthorized\nProxy-Authenticate: Digest realm=\"foo\", nonce=\"nonce\", stale=false\nContent-Length: 2\n\nNG" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" md5 = Digest::MD5.new ha1 = md5.hexdigest("admin:foo:admin") ha2 = md5.hexdigest("GET:/") response = md5.hexdigest("#{ha1}:nonce:#{ha2}") c.get_content('http://www1.example.com/') c.debug_dev = str = '' c.get_content('http://www2.example.com/') assert_match(/Proxy-Authorization: Digest/, str) assert_match(%r"response=\"#{response}\"", str) end def test_oauth c = HTTPClient.new config = HTTPClient::OAuth::Config.new( :realm => 'http://photos.example.net/', :consumer_key => 'dpf43f3p2l4k3l03', :consumer_secret => 'kd94hf93k423kf44', :token => 'nnch734d00sl2jdk', :secret => 'pfkkdhi9sl3r4s00', :version => '1.0', :signature_method => 'HMAC-SHA1' ) config.debug_timestamp = '1191242096' config.debug_nonce = 'kllo9940pd9333jh' c.www_auth.oauth.set_config('http://photos.example.net/', config) c.www_auth.oauth.challenge('http://photos.example.net/') c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.get_content('http://photos.example.net/photos', [[:file, 'vacation.jpg'], [:size, 'original']]) assert(str.index(%q(GET /photos?file=vacation.jpg&size=original))) assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0"))) # c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.get_content('http://photos.example.net/photos?file=vacation.jpg&size=original') assert(str.index(%q(GET /photos?file=vacation.jpg&size=original))) assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0"))) # c.test_loopback_http_response << "HTTP/1.0 200 OK\nContent-Length: 2\n\nOK" c.debug_dev = str = '' c.post_content('http://photos.example.net/photos', [[:file, 'vacation.jpg'], [:size, 'original']]) assert(str.index(%q(POST /photos))) assert(str.index(%q(Authorization: OAuth realm="http://photos.example.net/", oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="kllo9940pd9333jh", oauth_signature="wPkvxykrw%2BBTdCcGqKr%2B3I%2BPsiM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1191242096", oauth_token="nnch734d00sl2jdk", oauth_version="1.0"))) end end httpclient-2.3.3/test/htdigest0000644000004100000410000000005412155433207016443 0ustar www-datawww-dataadmin:auth:4302fe65caa32f27721949149ccd3083 httpclient-2.3.3/test/subca.cert0000644000004100000410000000233112155433207016661 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDaDCCAlCgAwIBAgIBATANBgkqhkiG9w0BAQUFADA8MQswCQYDVQQGDAJKUDES MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxCzAJBgNVBAMMAkNBMB4X DTA0MDEzMDAwNDMyN1oXDTM1MDEyMjAwNDMyN1owPzELMAkGA1UEBgwCSlAxEjAQ BgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMQ4wDAYDVQQDDAVTdWJDQTCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0Ou7AyRcRXnB/kVHv/6kwe ANzgg/DyJfsAUqW90m7Lu1nqyug8gK0RBd77yU0w5HOAMHTVSdpjZK0g2sgx4Mb1 d/213eL9TTl5MRVEChTvQr8q5DVG/8fxPPE7fMI8eOAzd98/NOAChk+80r4Sx7fC kGVEE1bKwY1MrUsUNjOY2d6t3M4HHV3HX1V8ShuKfsHxgCmLzdI8U+5CnQedFgkm 3e+8tr8IX5RR1wA1Ifw9VadF7OdI/bGMzog/Q8XCLf+WPFjnK7Gcx6JFtzF6Gi4x 4dp1Xl45JYiVvi9zQ132wu8A1pDHhiNgQviyzbP+UjcB/tsOpzBQF8abYzgEkWEC AwEAAaNyMHAwDwYDVR0TAQH/BAUwAwEB/zAxBglghkgBhvhCAQ0EJBYiUnVieS9P cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUlCjXWLsReYzH LzsxwVnCXmKoB/owCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCJ/OyN rT8Cq2Y+G2yA/L1EMRvvxwFBqxavqaqHl/6rwsIBFlB3zbqGA/0oec6MAVnYynq4 c4AcHTjx3bQ/S4r2sNTZq0DH4SYbQzIobx/YW8PjQUJt8KQdKMcwwi7arHP7A/Ha LKu8eIC2nsUBnP4NhkYSGhbmpJK+PFD0FVtD0ZIRlY/wsnaZNjWWcnWF1/FNuQ4H ySjIblqVQkPuzebv3Ror6ZnVDukn96Mg7kP4u6zgxOeqlJGRe1M949SS9Vudjl8X SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ uY/bPeOBYiVsOYVe -----END CERTIFICATE----- httpclient-2.3.3/test/server.key0000644000004100000410000000156712155433207016737 0ustar www-datawww-data-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDRSU8Vqrqd51fXbCQJLqflml5zzSM3KQAN6jBmQyUybH2vA7u2 cUz9FySuZhE7bQa5+9dXCcQLj9yo6p1vUr7WfZWNrFyemY+trDeS8Bt1VXtuXSNv MkdRLGx4yeWY3/bZoIDAumpudncbh27ef7x6OJ4CbihP1PnnhkYrzJ+oBQIDAQAB AoGBAIf4CstW2ltQO7+XYGoex7Hh8s9lTSW/G2vu5Hbr1LTHy3fzAvdq8MvVR12O rk9fa+lU9vhzPc0NMB0GIDZ9GcHuhW5hD1Wg9OSCbTOkZDoH3CAFqonjh4Qfwv5W IPAFn9KHukdqGXkwEMdErsUaPTy9A1V/aROVEaAY+HJgq/eZAkEA/BP1QMV04WEZ Oynzz7/lLizJGGxp2AOvEVtqMoycA/Qk+zdKP8ufE0wbmCE3Qd6GoynavsHb6aGK gQobb8zDZwJBANSK6MrXlrZTtEaeZuyOB4mAmRzGzOUVkUyULUjEx2GDT93ujAma qm/2d3E+wXAkNSeRpjUmlQXy/2oSqnGvYbMCQQDRM+cYyEcGPUVpWpnj0shrF/QU 9vSot/X1G775EMTyaw6+BtbyNxVgOIu2J+rqGbn3c+b85XqTXOPL0A2RLYkFAkAm syhSDtE9X55aoWsCNZY/vi+i4rvaFoQ/WleogVQAeGVpdo7/DK9t9YWoFBIqth0L mGSYFu9ZhvZkvQNV8eYrAkBJ+rOIaLDsmbrgkeDruH+B/9yrm4McDtQ/rgnOGYnH LjLpLLOrgUxqpzLWe++EwSLwK2//dHO+SPsQJ4xsyQJy -----END RSA PRIVATE KEY----- httpclient-2.3.3/test/server.cert0000644000004100000410000000211312155433207017070 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIC/zCCAeegAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQswCQYDVQQGDAJKUDES MBAGA1UECgwJSklOLkdSLkpQMQwwCgYDVQQLDANSUlIxDjAMBgNVBAMMBVN1YkNB MB4XDTA0MDEzMTAzMTMxNloXDTMzMDEyMzAzMTMxNlowQzELMAkGA1UEBgwCSlAx EjAQBgNVBAoMCUpJTi5HUi5KUDEMMAoGA1UECwwDUlJSMRIwEAYDVQQDDAlsb2Nh bGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANFJTxWqup3nV9dsJAku p+WaXnPNIzcpAA3qMGZDJTJsfa8Du7ZxTP0XJK5mETttBrn711cJxAuP3KjqnW9S vtZ9lY2sXJ6Zj62sN5LwG3VVe25dI28yR1EsbHjJ5Zjf9tmggMC6am52dxuHbt5/ vHo4ngJuKE/U+eeGRivMn6gFAgMBAAGjgYUwgYIwDAYDVR0TAQH/BAIwADAxBglg hkgBhvhCAQ0EJBYiUnVieS9PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAd BgNVHQ4EFgQUpZIyygD9JxFYHHOTEuWOLbCKfckwCwYDVR0PBAQDAgWgMBMGA1Ud JQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4IBAQBwAIj5SaBHaA5X31IP CFCJiep96awfp7RANO0cuUj+ZpGoFn9d6FXY0g+Eg5wAkCNIzZU5NHN9xsdOpnUo zIBbyTfQEPrge1CMWMvL6uGaoEXytq84VTitF/xBTky4KtTn6+es4/e7jrrzeUXQ RC46gkHObmDT91RkOEGjHLyld2328jo3DIN/VTHIryDeVHDWjY5dENwpwdkhhm60 DR9IrNBbXWEe9emtguNXeN0iu1ux0lG1Hc6pWGQxMlRKNvGh0yZB9u5EVe38tOV0 jQaoNyL7qzcQoXD3Dmbi1p0iRmg/+HngISsz8K7k7MBNVsSclztwgCzTZOBiVtkM rRlQ -----END CERTIFICATE----- httpclient-2.3.3/test/test_cookie.rb0000644000004100000410000003151512155433207017550 0ustar www-datawww-datarequire 'test/unit' require 'uri' require 'httpclient/cookie' class TestCookie < Test::Unit::TestCase include HTTPClient::Util def setup() @c = WebAgent::Cookie.new() end def test_s_new() assert_instance_of(WebAgent::Cookie, @c) end def test_discard? assert_equal(false, !!(@c.discard?)) @c.discard = true assert_equal(true, !!(@c.discard?)) end def test_match() url = urify('http://www.rubycolor.org/hoge/funi/#919191') @c.domain = 'www.rubycolor.org' assert_equal(true, @c.match?(url)) @c.domain = '.rubycolor.org' assert_equal(true, @c.match?(url)) @c.domain = 'aaa.www.rubycolor.org' assert_equal(false, @c.match?(url)) @c.domain = 'aaa.www.rubycolor.org' assert_equal(false, @c.match?(url)) @c.domain = 'www.rubycolor.org' @c.path = '/' assert_equal(true, @c.match?(url)) @c.domain = 'www.rubycolor.org' @c.path = '/hoge' assert_equal(true, @c.match?(url)) @c.domain = 'www.rubycolor.org' @c.path = '/hoge/hoge' assert_equal(false, @c.match?(url)) @c.domain = 'www.rubycolor.org' @c.path = '/hoge' @c.secure = true assert_equal(false, @c.match?(url)) url2 = urify('https://www.rubycolor.org/hoge/funi/#919191') @c.domain = 'www.rubycolor.org' @c.path = '/hoge' @c.secure = true assert_equal(true, @c.match?(url2)) @c.domain = 'www.rubycolor.org' @c.path = '/hoge' @c.secure = nil assert_equal(true, @c.match?(url2)) ## not false! url.port = 80 @c.domain = 'www.rubycolor.org' @c.path = '/hoge' # @c.port = [80,8080] assert_equal(true, @c.match?(url)) url_nopath = URI.parse('http://www.rubycolor.org') @c.domain = 'www.rubycolor.org' @c.path = '/' assert_equal(true, @c.match?(url_nopath)) end def test_head_match?() assert_equal(true, @c.head_match?("","")) assert_equal(false, @c.head_match?("a","")) assert_equal(true, @c.head_match?("","a")) assert_equal(true, @c.head_match?("abcde","abcde")) assert_equal(true, @c.head_match?("abcde","abcdef")) assert_equal(false, @c.head_match?("abcdef","abcde")) assert_equal(false, @c.head_match?("abcde","bcde")) assert_equal(false, @c.head_match?("bcde","abcde")) end def test_tail_match?() assert_equal(true, @c.tail_match?("","")) assert_equal(false, @c.tail_match?("a","")) assert_equal(true, @c.tail_match?("","a")) assert_equal(true, @c.tail_match?("abcde","abcde")) assert_equal(false, @c.tail_match?("abcde","abcdef")) assert_equal(false, @c.tail_match?("abcdef","abcde")) assert_equal(false, @c.tail_match?("abcde","bcde")) assert_equal(true, @c.tail_match?("bcde","abcde")) end def test_domain_match() extend WebAgent::CookieUtils assert_equal(true, !!domain_match("hoge.co.jp",".")) # assert_equal(true, !!domain_match("locahost",".local")) assert_equal(true, !!domain_match("192.168.10.1","192.168.10.1")) assert_equal(false, !!domain_match("192.168.10.1","192.168.10.2")) # assert_equal(false, !!domain_match("hoge.co.jp",".hoge.co.jp")) # allows; host == rubyforge.org, domain == .rubyforge.org assert_equal(true, !!domain_match("hoge.co.jp",".hoge.co.jp")) assert_equal(true, !!domain_match("www.hoge.co.jp", "www.hoge.co.jp")) assert_equal(false, !!domain_match("www.hoge.co.jp", "www2.hoge.co.jp")) assert_equal(true, !!domain_match("www.hoge.co.jp", ".hoge.co.jp")) assert_equal(true, !!domain_match("www.aa.hoge.co.jp", ".hoge.co.jp")) assert_equal(false, !!domain_match("www.hoge.co.jp", "hoge.co.jp")) end def test_join_quotedstr() arr1 = ['hoge=funi', 'hoge2=funi2'] assert_equal(arr1, @c.instance_eval{join_quotedstr(arr1,';')}) arr2 = ['hoge="fu', 'ni"', 'funi=funi'] assert_equal(['hoge="fu;ni"','funi=funi'], @c.instance_eval{join_quotedstr(arr2,';')}) arr3 = ['hoge="funi";hoge2="fu','ni2";hoge3="hoge"', 'funi="funi"'] assert_equal(['hoge="funi";hoge2="fu,ni2";hoge3="hoge"', 'funi="funi"'], @c.instance_eval{join_quotedstr(arr3,',')}) end end class TestCookieManager < Test::Unit::TestCase include HTTPClient::Util def setup() @cm = WebAgent::CookieManager.new() end def teardown() end def test_parse() str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; expires=Wed, 01-Dec-2010 00:00:00 GMT; path=/" @cm.parse(str, urify('http://www.test.jp')) cookie = @cm.cookies[0] assert_instance_of(WebAgent::Cookie, cookie) assert_equal("inkid", cookie.name) assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value) assert_equal(Time.gm(2010, 12, 1, 0,0,0), cookie.expires) assert_equal("/", cookie.path) end def test_parse2() str = "xmen=off,0,0,1; path=/; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT" @cm.parse(str, urify('http://www.excite.co.jp')) cookie = @cm.cookies[0] assert_instance_of(WebAgent::Cookie, cookie) assert_equal("xmen", cookie.name) assert_equal("off,0,0,1", cookie.value) assert_equal("/", cookie.path) assert_equal(".excite.co.jp", cookie.domain) assert_equal(Time.gm(2037,12,31,12,0,0), cookie.expires) end def test_parse3() str = "xmen=off,0,0,1; path=/; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT;Secure;HTTPOnly" @cm.parse(str, urify('http://www.excite.co.jp')) cookie = @cm.cookies[0] assert_instance_of(WebAgent::Cookie, cookie) assert_equal("xmen", cookie.name) assert_equal("off,0,0,1", cookie.value) assert_equal("/", cookie.path) assert_equal(".excite.co.jp", cookie.domain) assert_equal(Time.gm(2037,12,31,12,0,0), cookie.expires) assert_equal(true, cookie.secure?) assert_equal(true, cookie.http_only?) end def test_parse_double_semicolon() str = "xmen=off,0,0,1;; path=\"/;;\"; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT" @cm.parse(str, urify('http://www.excite.co.jp')) cookie = @cm.cookies[0] assert_instance_of(WebAgent::Cookie, cookie) assert_equal("xmen", cookie.name) assert_equal("off,0,0,1", cookie.value) assert_equal("/;;", cookie.path) assert_equal(".excite.co.jp", cookie.domain) assert_equal(Time.gm(2037,12,31,12,0,0), cookie.expires) end # def test_make_portlist() # assert_equal([80,8080], @cm.instance_eval{make_portlist("80,8080")}) # assert_equal([80], @cm.instance_eval{make_portlist("80")}) # assert_equal([80,8080,10080], @cm.instance_eval{make_portlist(" 80, 8080, 10080 \n")}) # end def test_check_expired_cookies() c1 = WebAgent::Cookie.new() c2 = c1.dup c3 = c1.dup c4 = c1.dup c1.expires = Time.now - 100 c2.expires = Time.now + 100 c3.expires = Time.now - 10 c4.expires = nil cookies = [c1,c2,c3,c4] @cm.cookies = cookies @cm.check_expired_cookies() # expires == nil cookies (session cookie) exists. assert_equal([c2,c4], @cm.cookies) end def test_parse_expires str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; expires=; path=/" @cm.parse(str, urify('http://www.test.jp')) cookie = @cm.cookies[0] assert_equal("inkid", cookie.name) assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value) assert_equal(nil, cookie.expires) assert_equal("/", cookie.path) # str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; path=/; expires=" @cm.parse(str, urify('http://www.test.jp')) cookie = @cm.cookies[0] assert_equal("inkid", cookie.name) assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value) assert_equal(nil, cookie.expires) assert_equal("/", cookie.path) # str = "inkid=n92b0ADOgACIgUb9lsjHqAAAHu2a; path=/; expires=\"\"" @cm.parse(str, urify('http://www.test.jp')) cookie = @cm.cookies[0] assert_equal("inkid", cookie.name) assert_equal("n92b0ADOgACIgUb9lsjHqAAAHu2a", cookie.value) assert_equal(nil, cookie.expires) assert_equal("/", cookie.path) end def test_find_cookie() str = "xmen=off,0,0,1; path=/; domain=.excite2.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT" @cm.parse(str, urify("http://www.excite2.co.jp/")) str = "xmen=off,0,0,2; path=/; domain=.excite.co.jp; expires=Wednesday, 31-Dec-2037 12:00:00 GMT" @cm.parse(str, urify("http://www.excite.co.jp/")) @cm.cookies[0].use = true @cm.cookies[1].use = true url = urify('http://www.excite.co.jp/hoge/funi/') cookie_str = @cm.find(url) assert_equal("xmen=off,0,0,2", cookie_str) end def test_load_cookies() begin File.open("tmp_test.tmp","w") {|f| f.write < same as URL c.url = urify("http://www.inac.co.jp/hoge/hoge2/hoge3") @cm.add(c) # c1, c2 = @cm.cookies assert_equal('', c1.path) assert_equal('/hoge/hoge2', c2.path) end def test_check_cookie_accept_domain() @cm.accept_domains = [".example1.co.jp", "www1.example.jp"] @cm.reject_domains = [".example2.co.jp", "www2.example.jp"] check1 = @cm.check_cookie_accept_domain("www.example1.co.jp") assert_equal(true, check1) check2 = @cm.check_cookie_accept_domain("www.example2.co.jp") assert_equal(false, check2) check3 = @cm.check_cookie_accept_domain("www1.example.jp") assert_equal(true, check3) check4 = @cm.check_cookie_accept_domain("www2.example.jp") assert_equal(false, check4) check5 = @cm.check_cookie_accept_domain("aa.www2.example.jp") assert_equal(true, check5) check6 = @cm.check_cookie_accept_domain("aa.www2.example.jp") assert_equal(true, check6) assert_equal(false, @cm.check_cookie_accept_domain(nil)) end end httpclient-2.3.3/test/test_ssl.rb0000644000004100000410000001441312155433207017076 0ustar www-datawww-datarequire File.expand_path('helper', File.dirname(__FILE__)) require 'webrick/https' class TestSSL < Test::Unit::TestCase include Helper DIR = File.dirname(File.expand_path(__FILE__)) def setup super @serverpid = @client = nil @verify_callback_called = false @verbose, $VERBOSE = $VERBOSE, nil setup_server setup_client @url = "https://localhost:#{serverport}/hello" end def teardown super $VERBOSE = @verbose end def path(filename) File.expand_path(filename, DIR) end def test_options cfg = @client.ssl_config assert_nil(cfg.client_cert) assert_nil(cfg.client_key) assert_nil(cfg.client_ca) assert_equal(OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT, cfg.verify_mode) assert_nil(cfg.verify_callback) assert_nil(cfg.timeout) assert_equal(OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2, cfg.options) assert_equal("ALL:!aNULL:!eNULL:!SSLv2", cfg.ciphers) assert_instance_of(OpenSSL::X509::Store, cfg.cert_store) end def test_sync cfg = @client.ssl_config cfg.set_client_cert_file(path('client.cert'), path('client.key')) cfg.add_trust_ca(path('ca.cert')) cfg.add_trust_ca(path('subca.cert')) assert_equal("hello", @client.get_content(@url)) @client.socket_sync = false @client.reset_all assert_equal("hello", @client.get_content(@url)) end def test_debug_dev str = @client.debug_dev = '' cfg = @client.ssl_config cfg.client_cert = cert("client.cert") cfg.client_key = key("client.key") cfg.add_trust_ca(path('ca.cert')) cfg.add_trust_ca(path('subca.cert')) assert_equal("hello", @client.get_content(@url)) assert(str.scan(/^hello$/)[0]) end def test_verification cfg = @client.ssl_config cfg.verify_callback = method(:verify_callback).to_proc begin @verify_callback_called = false @client.get(@url) assert(false) rescue OpenSSL::SSL::SSLError => ssle assert_match(/certificate verify failed/, ssle.message) assert(@verify_callback_called) end # cfg.client_cert = cert("client.cert") cfg.client_key = key("client.key") @verify_callback_called = false begin @client.get(@url) assert(false) rescue OpenSSL::SSL::SSLError => ssle assert_match(/certificate verify failed/, ssle.message) assert(@verify_callback_called) end # cfg.add_trust_ca(path('ca.cert')) @verify_callback_called = false begin @client.get(@url) assert(false) rescue OpenSSL::SSL::SSLError => ssle assert_match(/certificate verify failed/, ssle.message) assert(@verify_callback_called) end # cfg.add_trust_ca(path('subca.cert')) @verify_callback_called = false assert_equal("hello", @client.get_content(@url)) assert(@verify_callback_called) # unless ENV['TRAVIS'] # On travis environment, verify_depth seems to not work properly. # Ubuntu 10.04 + OpenSSL 0.9.8k issue? cfg.verify_depth = 1 # 2 required: root-sub @verify_callback_called = false begin @client.get(@url) assert(false, "verify_depth is not supported? #{OpenSSL::OPENSSL_VERSION}") rescue OpenSSL::SSL::SSLError => ssle assert_match(/certificate verify failed/, ssle.message) assert(@verify_callback_called) end # cfg.verify_depth = 2 # 2 required: root-sub @verify_callback_called = false @client.get(@url) assert(@verify_callback_called) # end cfg.verify_depth = nil cfg.cert_store = OpenSSL::X509::Store.new cfg.verify_mode = OpenSSL::SSL::VERIFY_PEER begin @client.get_content(@url) assert(false) rescue OpenSSL::SSL::SSLError => ssle assert_match(/certificate verify failed/, ssle.message) end # cfg.verify_mode = nil assert_equal("hello", @client.get_content(@url)) end def test_ciphers cfg = @client.ssl_config cfg.set_client_cert_file(path('client.cert'), path('client.key')) cfg.add_trust_ca(path('ca.cert')) cfg.add_trust_ca(path('subca.cert')) cfg.timeout = 123 assert_equal("hello", @client.get_content(@url)) # cfg.ciphers = "!ALL" begin @client.get(@url) assert(false) rescue OpenSSL::SSL::SSLError => ssle assert_match(/no cipher match/, ssle.message) end # cfg.ciphers = "ALL" assert_equal("hello", @client.get_content(@url)) # cfg.ciphers = "DEFAULT" assert_equal("hello", @client.get_content(@url)) end def test_set_default_paths assert_raise(OpenSSL::SSL::SSLError) do @client.get(@url) end escape_env do ENV['SSL_CERT_FILE'] = File.join(DIR, 'ca-chain.cert') @client.ssl_config.set_default_paths @client.get(@url) end end private def cert(filename) OpenSSL::X509::Certificate.new(File.read(File.join(DIR, filename))) end def key(filename) OpenSSL::PKey::RSA.new(File.read(File.join(DIR, filename))) end def q(str) %Q["#{str}"] end def setup_server logger = Logger.new(STDERR) logger.level = Logger::Severity::FATAL # avoid logging SSLError (ERROR level) @server = WEBrick::HTTPServer.new( :BindAddress => "localhost", :Logger => logger, :Port => 0, :AccessLog => [], :DocumentRoot => DIR, :SSLEnable => true, :SSLCACertificateFile => File.join(DIR, 'ca.cert'), :SSLCertificate => cert('server.cert'), :SSLPrivateKey => key('server.key'), :SSLVerifyClient => nil, #OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT|OpenSSL::SSL::VERIFY_PEER, :SSLClientCA => cert('ca.cert'), :SSLCertName => nil ) @serverport = @server.config[:Port] [:hello].each do |sym| @server.mount( "/#{sym}", WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc) ) end @server_thread = start_server_thread(@server) end def do_hello(req, res) res['content-type'] = 'text/html' res.body = "hello" end def start_server_thread(server) t = Thread.new { Thread.current.abort_on_exception = true server.start } while server.status != :Running sleep 0.1 unless t.alive? t.join raise end end t end def verify_callback(ok, cert) @verify_callback_called = true p ["client", ok, cert] if $DEBUG ok end end httpclient-2.3.3/test/test_httpclient.rb0000644000004100000410000016176512155433207020470 0ustar www-datawww-data# -*- encoding: utf-8 -*- require File.expand_path('helper', File.dirname(__FILE__)) class TestHTTPClient < Test::Unit::TestCase include Helper include HTTPClient::Util def setup super setup_server setup_client end def teardown super end def test_initialize setup_proxyserver escape_noproxy do @proxyio.string = "" @client = HTTPClient.new(proxyurl) assert_equal(urify(proxyurl), @client.proxy) assert_equal(200, @client.head(serverurl).status) assert(/accept/ =~ @proxyio.string) end end def test_agent_name @client = HTTPClient.new(nil, "agent_name_foo") str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match(/^User-Agent: agent_name_foo \(#{HTTPClient::VERSION}/, lines[4]) end def test_from @client = HTTPClient.new(nil, nil, "from_bar") str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match(/^From: from_bar/, lines[5]) end def test_debug_dev str = "" @client.debug_dev = str assert_equal(str.object_id, @client.debug_dev.object_id) assert(str.empty?) @client.get(serverurl) assert(!str.empty?) end def test_debug_dev_stream str = "" @client.debug_dev = str conn = @client.get_async(serverurl) Thread.pass while !conn.finished? assert(!str.empty?) end def test_protocol_version_http09 @client.protocol_version = 'HTTP/0.9' @client.debug_dev = str = '' @client.test_loopback_http_response << "hello\nworld\n" res = @client.get(serverurl + 'hello') assert_equal('0.9', res.http_version) assert_equal(nil, res.status) assert_equal(nil, res.reason) assert_equal("hello\nworld\n", res.content) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET /hello HTTP/0.9", lines[3]) assert_equal("Connection: close", lines[7]) assert_equal("= Response", lines[8]) assert_match(/^hello$/, lines[9]) assert_match(/^world$/, lines[10]) end def test_protocol_version_http10 assert_equal(nil, @client.protocol_version) @client.protocol_version = 'HTTP/1.0' assert_equal('HTTP/1.0', @client.protocol_version) str = "" @client.debug_dev = str @client.get(serverurl + 'hello') lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET /hello HTTP/1.0", lines[3]) assert_equal("Connection: close", lines[7]) assert_equal("= Response", lines[8]) end def test_header_accept_by_default str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("Accept: */*", lines[5]) end def test_header_accept str = "" @client.debug_dev = str @client.get(serverurl, :header => {:Accept => 'text/html'}) lines = str.split(/(?:\r?\n)+/) assert_equal("Accept: text/html", lines[4]) end def test_host_given str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.1", lines[3]) assert_equal("Host: localhost:#{serverport}", lines[7]) # @client.reset_all str = "" @client.debug_dev = str @client.get(serverurl, nil, {'Host' => 'foo'}) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.1", lines[3]) assert_equal("Host: foo", lines[4]) # use given param end def test_redirect_returns_not_modified assert_nothing_raised do timeout(2) do @client.get(serverurl + 'status', {:status => 306}, {:follow_redirect => true}) end end end def test_protocol_version_http11 assert_equal(nil, @client.protocol_version) str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.1", lines[3]) assert_equal("Host: localhost:#{serverport}", lines[7]) @client.protocol_version = 'HTTP/1.1' assert_equal('HTTP/1.1', @client.protocol_version) str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.1", lines[3]) @client.protocol_version = 'HTTP/1.0' str = "" @client.debug_dev = str @client.get(serverurl) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_equal("! CONNECTION ESTABLISHED", lines[2]) assert_equal("GET / HTTP/1.0", lines[3]) end def test_proxy setup_proxyserver escape_noproxy do begin @client.proxy = "http://" rescue assert_match(/InvalidURIError/, $!.class.to_s) end @client.proxy = "" assert_nil(@client.proxy) @client.proxy = "http://admin:admin@foo:1234" assert_equal(urify("http://admin:admin@foo:1234"), @client.proxy) uri = urify("http://bar:2345") @client.proxy = uri assert_equal(uri, @client.proxy) # @proxyio.string = "" @client.proxy = nil assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @proxyio.string = "" @client.proxy = proxyurl @client.debug_dev = str = "" assert_equal(200, @client.head(serverurl).status) assert(/accept/ =~ @proxyio.string) assert(/Host: localhost:#{serverport}/ =~ str) end end def test_host_header @client.proxy = proxyurl @client.debug_dev = str = "" @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n" assert_equal(200, @client.head('http://www.example.com/foo').status) # ensure no ':80' is added. some servers dislike that. assert(/\r\nHost: www\.example\.com\r\n/ =~ str) # @client.debug_dev = str = "" @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n" assert_equal(200, @client.head('http://www.example.com:12345/foo').status) # ensure ':12345' exists. assert(/\r\nHost: www\.example\.com:12345\r\n/ =~ str) end def test_proxy_env setup_proxyserver escape_env do ENV['http_proxy'] = "http://admin:admin@foo:1234" ENV['NO_PROXY'] = "foobar" client = HTTPClient.new assert_equal(urify("http://admin:admin@foo:1234"), client.proxy) assert_equal('foobar', client.no_proxy) end end def test_proxy_env_cgi setup_proxyserver escape_env do ENV['REQUEST_METHOD'] = 'GET' # CGI environment emulation ENV['http_proxy'] = "http://admin:admin@foo:1234" ENV['no_proxy'] = "foobar" client = HTTPClient.new assert_equal(nil, client.proxy) ENV['CGI_HTTP_PROXY'] = "http://admin:admin@foo:1234" client = HTTPClient.new assert_equal(urify("http://admin:admin@foo:1234"), client.proxy) end end def test_empty_proxy_env setup_proxyserver escape_env do ENV['http_proxy'] = "" client = HTTPClient.new assert_equal(nil, client.proxy) end end def test_noproxy_for_localhost @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) end def test_no_proxy setup_proxyserver escape_noproxy do # proxy is not set. assert_equal(nil, @client.no_proxy) @client.no_proxy = 'localhost' assert_equal('localhost', @client.no_proxy) @proxyio.string = "" @client.proxy = nil assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @client.no_proxy = 'foobar' @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ =~ @proxyio.string) # @client.no_proxy = 'foobar,localhost:baz' @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) # @client.no_proxy = 'foobar,localhost:443' @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ =~ @proxyio.string) # @client.no_proxy = "foobar,localhost:443:localhost:#{serverport},baz" @proxyio.string = "" @client.proxy = proxyurl assert_equal(200, @client.head(serverurl).status) assert(/accept/ !~ @proxyio.string) end end def test_no_proxy_with_initial_dot @client.debug_dev = str = "" @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n" @client.no_proxy = '' @client.proxy = proxyurl @client.head('http://www.foo.com') assert(/CONNECT TO localhost/ =~ str, 'via proxy') # @client.debug_dev = str = "" @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n" @client.no_proxy = '.foo.com' @client.proxy = proxyurl @client.head('http://www.foo.com') assert(/CONNECT TO www.foo.com/ =~ str, 'no proxy because .foo.com matches with www.foo.com') # @client.debug_dev = str = "" @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n" @client.no_proxy = '.foo.com' @client.proxy = proxyurl @client.head('http://foo.com') assert(/CONNECT TO localhost/ =~ str, 'via proxy because .foo.com does not matche with foo.com') # @client.debug_dev = str = "" @client.test_loopback_http_response << "HTTP/1.0 200 OK\r\n\r\n" @client.no_proxy = 'foo.com' @client.proxy = proxyurl @client.head('http://foo.com') assert(/CONNECT TO foo.com/ =~ str, 'no proxy because foo.com matches with foo.com') end def test_cookie_update_while_authentication escape_noproxy do @client.test_loopback_http_response << < true).header.request_uri) assert_equal(expected, @client.get(serverurl + 'redirect2', :follow_redirect => true).header.request_uri) end def test_redirect_non_https url = serverurl + 'redirect1' https_url = urify(url) https_url.scheme = 'https' # redirect_to_http = "HTTP/1.0 302 OK\nLocation: #{url}\n\n" redirect_to_https = "HTTP/1.0 302 OK\nLocation: #{https_url}\n\n" # # https -> http is denied @client.test_loopback_http_response << redirect_to_http assert_raises(HTTPClient::BadResponseError) do @client.get_content(https_url) end # # http -> http is OK @client.reset_all @client.test_loopback_http_response << redirect_to_http assert_equal('hello', @client.get_content(url)) # # http -> https is OK @client.reset_all @client.test_loopback_http_response << redirect_to_https assert_raises(OpenSSL::SSL::SSLError) do # trying to normal endpoint with SSL -> SSL negotiation failure @client.get_content(url) end # # https -> https is OK @client.reset_all @client.test_loopback_http_response << redirect_to_https assert_raises(OpenSSL::SSL::SSLError) do # trying to normal endpoint with SSL -> SSL negotiation failure @client.get_content(https_url) end # # https -> http with strict_redirect_uri_callback @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback) @client.test_loopback_http_response << redirect_to_http assert_raises(HTTPClient::BadResponseError) do @client.get_content(https_url) end end def test_redirect_see_other assert_equal('hello', @client.post_content(serverurl + 'redirect_see_other')) end def test_redirect_relative @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n" silent do assert_equal('hello', @client.get_content(serverurl + 'redirect1')) end # @client.reset_all @client.redirect_uri_callback = @client.method(:strict_redirect_uri_callback) assert_equal('hello', @client.get_content(serverurl + 'redirect1')) @client.reset_all @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: hello\n\n" begin @client.get_content(serverurl + 'redirect1') assert(false) rescue HTTPClient::BadResponseError => e assert_equal(302, e.res.status) end end def test_redirect_https_relative url = serverurl + 'redirect1' https_url = urify(url) https_url.scheme = 'https' @client.test_loopback_http_response << "HTTP/1.0 302 OK\nLocation: /foo\n\n" @client.test_loopback_http_response << "HTTP/1.0 200 OK\n\nhello" silent do assert_equal('hello', @client.get_content(https_url)) end end def test_no_content assert_nothing_raised do timeout(2) do @client.get(serverurl + 'status', :status => 101) @client.get(serverurl + 'status', :status => 204) @client.get(serverurl + 'status', :status => 304) end end end def test_get_content assert_equal('hello', @client.get_content(serverurl + 'hello')) assert_equal('hello', @client.get_content(serverurl + 'redirect1')) assert_equal('hello', @client.get_content(serverurl + 'redirect2')) url = serverurl.sub(/localhost/, '127.0.0.1') assert_equal('hello', @client.get_content(url + 'hello')) assert_equal('hello', @client.get_content(url + 'redirect1')) assert_equal('hello', @client.get_content(url + 'redirect2')) @client.reset(serverurl) @client.reset(url) @client.reset(serverurl) @client.reset(url) assert_raises(HTTPClient::BadResponseError) do @client.get_content(serverurl + 'notfound') end assert_raises(HTTPClient::BadResponseError) do @client.get_content(serverurl + 'redirect_self') end called = false @client.redirect_uri_callback = lambda { |uri, res| newuri = res.header['location'][0] called = true newuri } assert_equal('hello', @client.get_content(serverurl + 'relative_redirect')) assert(called) end GZIP_CONTENT = "\x1f\x8b\x08\x00\x1a\x96\xe0\x4c\x00\x03\xcb\x48\xcd\xc9\xc9\x07\x00\x86\xa6\x10\x36\x05\x00\x00\x00" DEFLATE_CONTENT = "\x78\x9c\xcb\x48\xcd\xc9\xc9\x07\x00\x06\x2c\x02\x15" GZIP_CONTENT.force_encoding('BINARY') if GZIP_CONTENT.respond_to?(:force_encoding) DEFLATE_CONTENT.force_encoding('BINARY') if DEFLATE_CONTENT.respond_to?(:force_encoding) def test_get_gzipped_content @client.transparent_gzip_decompression = false content = @client.get_content(serverurl + 'compressed?enc=gzip') assert_not_equal('hello', content) assert_equal(GZIP_CONTENT, content) @client.transparent_gzip_decompression = true assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=gzip')) assert_equal('hello', @client.get_content(serverurl + 'compressed?enc=deflate')) @client.transparent_gzip_decompression = false end def test_get_content_with_block @client.get_content(serverurl + 'hello') do |str| assert_equal('hello', str) end @client.get_content(serverurl + 'redirect1') do |str| assert_equal('hello', str) end @client.get_content(serverurl + 'redirect2') do |str| assert_equal('hello', str) end end def test_post_content assert_equal('hello', @client.post_content(serverurl + 'hello')) assert_equal('hello', @client.post_content(serverurl + 'redirect1')) assert_equal('hello', @client.post_content(serverurl + 'redirect2')) assert_raises(HTTPClient::BadResponseError) do @client.post_content(serverurl + 'notfound') end assert_raises(HTTPClient::BadResponseError) do @client.post_content(serverurl + 'redirect_self') end called = false @client.redirect_uri_callback = lambda { |uri, res| newuri = res.header['location'][0] called = true newuri } assert_equal('hello', @client.post_content(serverurl + 'relative_redirect')) assert(called) end def test_post_content_io post_body = StringIO.new("1234567890") assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet', post_body)) post_body = StringIO.new("1234567890") assert_equal('post,1234567890', @client.post_content(serverurl + 'servlet_redirect', post_body)) # post_body = StringIO.new("1234567890") post_body.read(5) assert_equal('post,67890', @client.post_content(serverurl + 'servlet_redirect', post_body)) end def test_head assert_equal("head", @client.head(serverurl + 'servlet').header["x-head"][0]) param = {'1'=>'2', '3'=>'4'} res = @client.head(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_head_async param = {'1'=>'2', '3'=>'4'} conn = @client.head_async(serverurl + 'servlet', param) Thread.pass while !conn.finished? res = conn.pop assert_equal(param, params(res.header["x-query"][0])) end def test_get assert_equal("get", @client.get(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.get(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) assert_nil(res.contenttype) # url = serverurl.to_s + 'servlet?5=6&7=8' res = @client.get(url, param) assert_equal(param.merge("5"=>"6", "7"=>"8"), params(res.header["x-query"][0])) assert_nil(res.contenttype) end def test_head_follow_redirect expected = urify(serverurl + 'hello') assert_equal(expected, @client.head(serverurl + 'hello', :follow_redirect => true).header.request_uri) assert_equal(expected, @client.head(serverurl + 'redirect1', :follow_redirect => true).header.request_uri) assert_equal(expected, @client.head(serverurl + 'redirect2', :follow_redirect => true).header.request_uri) end def test_get_follow_redirect assert_equal('hello', @client.get(serverurl + 'hello', :follow_redirect => true).body) assert_equal('hello', @client.get(serverurl + 'redirect1', :follow_redirect => true).body) assert_equal('hello', @client.get(serverurl + 'redirect2', :follow_redirect => true).body) end def test_get_async param = {'1'=>'2', '3'=>'4'} conn = @client.get_async(serverurl + 'servlet', param) Thread.pass while !conn.finished? res = conn.pop assert_equal(param, params(res.header["x-query"][0])) end def test_get_async_for_largebody conn = @client.get_async(serverurl + 'largebody') res = conn.pop assert_equal(1000*1000, res.content.read.length) end def test_get_with_block called = false res = @client.get(serverurl + 'servlet') { |str| assert_equal('get', str) called = true } assert(called) # res does not have a content assert_nil(res.content) end def test_get_with_block_chunk_string_recycle @client.read_block_size = 2 body = [] res = @client.get(serverurl + 'servlet') { |str| body << str } assert_equal(2, body.size) assert_equal("get", body.join) # Was "tt" by String object recycle... end def test_post assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4]) param = {'1'=>'2', '3'=>'4'} res = @client.post(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_post_follow_redirect assert_equal('hello', @client.post(serverurl + 'hello', :follow_redirect => true).body) assert_equal('hello', @client.post(serverurl + 'redirect1', :follow_redirect => true).body) assert_equal('hello', @client.post(serverurl + 'redirect2', :follow_redirect => true).body) end def test_post_with_content_type param = [['1', '2'], ['3', '4']] ext = {'content-type' => 'application/x-www-form-urlencoded', 'hello' => 'world'} assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext) res = @client.post(serverurl + 'servlet', param, ext) assert_equal(Hash[param], params(res.header["x-query"][0])) # ext = [['content-type', 'multipart/form-data'], ['hello', 'world']] assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext) res = @client.post(serverurl + 'servlet', param, ext) assert_match(/Content-Disposition: form-data; name="1"/, res.content) assert_match(/Content-Disposition: form-data; name="3"/, res.content) # ext = {'content-type' => 'multipart/form-data; boundary=hello'} assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext) res = @client.post(serverurl + 'servlet', param, ext) assert_match(/Content-Disposition: form-data; name="1"/, res.content) assert_match(/Content-Disposition: form-data; name="3"/, res.content) assert_equal("post,--hello\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n2\r\n--hello\r\nContent-Disposition: form-data; name=\"3\"\r\n\r\n4\r\n--hello--\r\n\r\n", res.content) end def test_post_with_custom_multipart_and_boolean_params param = [['boolean_true', true]] ext = { 'content-type' => 'multipart/form-data' } assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext) res = @client.post(serverurl + 'servlet', param, ext) assert_match(/Content-Disposition: form-data; name="boolean_true"\r\n\r\ntrue\r\n/, res.content) # param = [['boolean_false', false]] res = @client.post(serverurl + 'servlet', param, ext) assert_match(/Content-Disposition: form-data; name="boolean_false"\r\n\r\nfalse\r\n/, res.content) # param = [['nil', nil]] res = @client.post(serverurl + 'servlet', param, ext) assert_match(/Content-Disposition: form-data; name="nil"\r\n\r\n\r\n/, res.content) end def test_post_with_file STDOUT.sync = true File.open(__FILE__) do |file| res = @client.post(serverurl + 'servlet', {1=>2, 3=>file}) assert_match(/^Content-Disposition: form-data; name="1"\r\n/nm, res.content) assert_match(/^Content-Disposition: form-data; name="3";/, res.content) assert_match(/FIND_TAG_IN_THIS_FILE/, res.content) end end def test_post_with_file_without_size STDOUT.sync = true File.open(__FILE__) do |file| def file.size # Simulates some strange Windows behaviour raise SystemCallError.new "Unknown Error (20047)" end assert_nothing_raised do @client.post(serverurl + 'servlet', {1=>2, 3=>file}) end end end def test_post_with_io # streaming, but not chunked myio = StringIO.new("X" * (HTTP::Message::Body::DEFAULT_CHUNK_SIZE + 1)) def myio.read(*args) @called ||= 0 @called += 1 super end def myio.called @called end @client.debug_dev = str = StringIO.new res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio}) assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content) assert_match(/\r\n2\r\n/m, res.content) assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content) assert_match(/\r\nContent-Length:/m, str.string) assert_equal(3, myio.called) end def test_post_with_io_nosize # streaming + chunked post myio = StringIO.new("4") def myio.size nil end @client.debug_dev = str = StringIO.new res = @client.post(serverurl + 'servlet', {1=>2, 3=>myio}) assert_match(/\r\nContent-Disposition: form-data; name="1"\r\n/m, res.content) assert_match(/\r\n2\r\n/m, res.content) assert_match(/\r\nContent-Disposition: form-data; name="3"; filename=""\r\n/m, res.content) assert_match(/\r\n4\r\n/m, res.content) assert_match(/\r\nTransfer-Encoding: chunked\r\n/m, str.string) end def test_post_async param = {'1'=>'2', '3'=>'4'} conn = @client.post_async(serverurl + 'servlet', param) Thread.pass while !conn.finished? res = conn.pop assert_equal(param, params(res.header["x-query"][0])) end def test_post_with_block called = false res = @client.post(serverurl + 'servlet') { |str| assert_equal('post,', str) called = true } assert(called) assert_nil(res.content) # called = false param = [['1', '2'], ['3', '4']] res = @client.post(serverurl + 'servlet', param) { |str| assert_equal('post,1=2&3=4', str) called = true } assert(called) assert_equal('1=2&3=4', res.header["x-query"][0]) assert_nil(res.content) end def test_post_with_custom_multipart ext = {'content-type' => 'multipart/form-data'} assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext) body = [{ 'Content-Disposition' => 'form-data; name="1"', :content => "2"}, { 'Content-Disposition' => 'form-data; name="3"', :content => "4"}] res = @client.post(serverurl + 'servlet', body, ext) assert_match(/Content-Disposition: form-data; name="1"/, res.content) assert_match(/Content-Disposition: form-data; name="3"/, res.content) # ext = {'content-type' => 'multipart/form-data; boundary=hello'} assert_equal("post", @client.post(serverurl + 'servlet').content[0, 4], ext) res = @client.post(serverurl + 'servlet', body, ext) assert_match(/Content-Disposition: form-data; name="1"/, res.content) assert_match(/Content-Disposition: form-data; name="3"/, res.content) assert_equal("post,--hello\r\nContent-Disposition: form-data; name=\"1\"\r\n\r\n2\r\n--hello\r\nContent-Disposition: form-data; name=\"3\"\r\n\r\n4\r\n--hello--\r\n\r\n", res.content) end def test_post_with_custom_multipart_and_file STDOUT.sync = true File.open(__FILE__) do |file| ext = { 'Content-Type' => 'multipart/alternative' } body = [{ 'Content-Type' => 'text/plain', :content => "this is only a test" }, { 'Content-Type' => 'application/x-ruby', :content => file }] res = @client.post(serverurl + 'servlet', body, ext) assert_match(/^Content-Type: text\/plain\r\n/m, res.content) assert_match(/^this is only a test\r\n/m, res.content) assert_match(/^Content-Type: application\/x-ruby\r\n/m, res.content) assert_match(/FIND_TAG_IN_THIS_FILE/, res.content) end end def test_put assert_equal("put", @client.put(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} @client.debug_dev = str = '' res = @client.put(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) assert_equal('Content-Type: application/x-www-form-urlencoded', str.split(/\r?\n/)[5]) end def test_put_bytesize res = @client.put(serverurl + 'servlet', 'txt' => 'あいうえお') assert_equal('txt=%E3%81%82%E3%81%84%E3%81%86%E3%81%88%E3%81%8A', res.header["x-query"][0]) assert_equal('15', res.header["x-size"][0]) end def test_put_async param = {'1'=>'2', '3'=>'4'} conn = @client.put_async(serverurl + 'servlet', param) Thread.pass while !conn.finished? res = conn.pop assert_equal(param, params(res.header["x-query"][0])) end def test_delete assert_equal("delete", @client.delete(serverurl + 'servlet').content) end # Not prohibited by spec, but normally it's ignored def test_delete_with_body param = {'1'=>'2', '3'=>'4'} @client.debug_dev = str = '' assert_equal("delete", @client.delete(serverurl + 'servlet', param).content) assert_equal({'1' => ['2'], '3' => ['4']}, HTTP::Message.parse(str.split(/\r?\n\r?\n/)[2])) end def test_delete_async conn = @client.delete_async(serverurl + 'servlet') Thread.pass while !conn.finished? res = conn.pop assert_equal('delete', res.content.read) end def test_options assert_equal("options", @client.options(serverurl + 'servlet').content) end def test_options_async conn = @client.options_async(serverurl + 'servlet') Thread.pass while !conn.finished? res = conn.pop assert_equal('options', res.content.read) end def test_propfind assert_equal("propfind", @client.propfind(serverurl + 'servlet').content) end def test_propfind_async conn = @client.propfind_async(serverurl + 'servlet') Thread.pass while !conn.finished? res = conn.pop assert_equal('propfind', res.content.read) end def test_proppatch assert_equal("proppatch", @client.proppatch(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.proppatch(serverurl + 'servlet', param) assert_equal('proppatch', res.content) assert_equal(param, params(res.header["x-query"][0])) end def test_proppatch_async param = {'1'=>'2', '3'=>'4'} conn = @client.proppatch_async(serverurl + 'servlet', param) Thread.pass while !conn.finished? res = conn.pop assert_equal('proppatch', res.content.read) assert_equal(param, params(res.header["x-query"][0])) end def test_trace assert_equal("trace", @client.trace(serverurl + 'servlet').content) param = {'1'=>'2', '3'=>'4'} res = @client.trace(serverurl + 'servlet', param) assert_equal(param, params(res.header["x-query"][0])) end def test_trace_async param = {'1'=>'2', '3'=>'4'} conn = @client.trace_async(serverurl + 'servlet', param) Thread.pass while !conn.finished? res = conn.pop assert_equal(param, params(res.header["x-query"][0])) end def test_chunked assert_equal('chunked', @client.get_content(serverurl + 'chunked', { 'msg' => 'chunked' })) end def test_chunked_empty assert_equal('', @client.get_content(serverurl + 'chunked', { 'msg' => '' })) end def test_get_query assert_equal({'1'=>'2'}, check_query_get({1=>2})) assert_equal({'a'=>'A', 'B'=>'b'}, check_query_get({"a"=>"A", "B"=>"b"})) assert_equal({'&'=>'&'}, check_query_get({"&"=>"&"})) assert_equal({'= '=>' =+'}, check_query_get({"= "=>" =+"})) assert_equal( ['=', '&'].sort, check_query_get([["=", "="], ["=", "&"]])['='].to_ary.sort ) assert_equal({'123'=>'45'}, check_query_get('123=45')) assert_equal({'12 3'=>'45', ' '=>' '}, check_query_get('12+3=45&+=+')) assert_equal({}, check_query_get('')) assert_equal({'1'=>'2'}, check_query_get({1=>StringIO.new('2')})) assert_equal({'1'=>'2', '3'=>'4'}, check_query_get(StringIO.new('3=4&1=2'))) hash = check_query_get({"a"=>["A","a"], "B"=>"b"}) assert_equal({'a'=>'A', 'B'=>'b'}, hash) assert_equal(['A','a'], hash['a'].to_ary) hash = check_query_get({"a"=>WEBrick::HTTPUtils::FormData.new("A","a"), "B"=>"b"}) assert_equal({'a'=>'A', 'B'=>'b'}, hash) assert_equal(['A','a'], hash['a'].to_ary) hash = check_query_get({"a"=>[StringIO.new("A"),StringIO.new("a")], "B"=>StringIO.new("b")}) assert_equal({'a'=>'A', 'B'=>'b'}, hash) assert_equal(['A','a'], hash['a'].to_ary) end def test_post_body assert_equal({'1'=>'2'}, check_query_post({1=>2})) assert_equal({'a'=>'A', 'B'=>'b'}, check_query_post({"a"=>"A", "B"=>"b"})) assert_equal({'&'=>'&'}, check_query_post({"&"=>"&"})) assert_equal({'= '=>' =+'}, check_query_post({"= "=>" =+"})) assert_equal( ['=', '&'].sort, check_query_post([["=", "="], ["=", "&"]])['='].to_ary.sort ) assert_equal({'123'=>'45'}, check_query_post('123=45')) assert_equal({'12 3'=>'45', ' '=>' '}, check_query_post('12+3=45&+=+')) assert_equal({}, check_query_post('')) # post_body = StringIO.new("foo=bar&foo=baz") assert_equal( ["bar", "baz"], check_query_post(post_body)["foo"].to_ary.sort ) end def test_extra_headers str = "" @client.debug_dev = str @client.head(serverurl, nil, {"ABC" => "DEF"}) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match("ABC: DEF", lines[4]) # str = "" @client.debug_dev = str @client.get(serverurl, nil, [["ABC", "DEF"], ["ABC", "DEF"]]) lines = str.split(/(?:\r?\n)+/) assert_equal("= Request", lines[0]) assert_match("ABC: DEF", lines[4]) assert_match("ABC: DEF", lines[5]) end def test_http_custom_date_header @client.debug_dev = (str = "") res = @client.get(serverurl + 'hello', :header => {'Date' => 'foo'}) lines = str.split(/(?:\r?\n)+/) assert_equal('Date: foo', lines[4]) end def test_timeout assert_equal(60, @client.connect_timeout) assert_equal(120, @client.send_timeout) assert_equal(60, @client.receive_timeout) # @client.connect_timeout = 1 @client.send_timeout = 2 @client.receive_timeout = 3 assert_equal(1, @client.connect_timeout) assert_equal(2, @client.send_timeout) assert_equal(3, @client.receive_timeout) end def test_connect_timeout # ToDo end def test_send_timeout # ToDo end def test_receive_timeout # this test takes 2 sec assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=2')) @client.receive_timeout = 1 assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=0')) assert_raise(HTTPClient::ReceiveTimeoutError) do @client.get_content(serverurl + 'sleep?sec=2') end @client.receive_timeout = 3 assert_equal('hello', @client.get_content(serverurl + 'sleep?sec=2')) end def test_receive_timeout_post # this test takes 2 sec assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content) @client.receive_timeout = 1 assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 0).content) assert_raise(HTTPClient::ReceiveTimeoutError) do @client.post(serverurl + 'sleep', :sec => 2) end @client.receive_timeout = 3 assert_equal('hello', @client.post(serverurl + 'sleep', :sec => 2).content) end def test_async_error assert_raise( SocketError ) do conn = @client.get_async("http://non-existing-host/") conn.pop end end def test_reset url = serverurl + 'servlet' assert_nothing_raised do 5.times do @client.get(url) @client.reset(url) end end end def test_reset_all assert_nothing_raised do 5.times do @client.get(serverurl + 'servlet') @client.reset_all end end end def test_cookies cookiefile = File.join(File.dirname(File.expand_path(__FILE__)), 'test_cookies_file') File.open(cookiefile, "wb") do |f| f << "http://rubyforge.org/account/login.php\tsession_ser\tLjEwMy45Ni40Ni0q%2A-fa0537de8cc31\t2000000000\t.rubyforge.org\t/\t13\n" end @client.set_cookie_store(cookiefile) cookie = @client.cookie_manager.cookies.first url = cookie.url assert(cookie.domain_match(url.host, cookie.domain)) # @client.reset_all @client.test_loopback_http_response << "HTTP/1.0 200 OK\nSet-Cookie: foo=bar; expires=#{Time.at(1924873200).gmtime.httpdate}\n\nOK" @client.get_content('http://rubyforge.org/account/login.php') @client.save_cookie_store str = File.read(cookiefile) assert_match(%r(http://rubyforge.org/account/login.php\tfoo\tbar\t1924873200\trubyforge.org\t/account\t1), str) File.unlink(cookiefile) end def test_eof_error_length io = StringIO.new('') def io.gets(*arg) @buf ||= ["HTTP/1.0 200 OK\n", "content-length: 123\n", "\n"] @buf.shift end def io.readpartial(size, buf) @second ||= false if !@second @second = '1st' buf << "abc" buf elsif @second == '1st' @second = '2nd' raise EOFError.new else raise Exception.new end end def io.eof? true end @client.test_loopback_http_response << io assert_nothing_raised do @client.get('http://foo/bar') end end def test_eof_error_rest io = StringIO.new('') def io.gets(*arg) @buf ||= ["HTTP/1.0 200 OK\n", "\n"] @buf.shift end def io.readpartial(size, buf) @second ||= false if !@second @second = '1st' buf << "abc" buf elsif @second == '1st' @second = '2nd' raise EOFError.new else raise Exception.new end end def io.eof? true end @client.test_loopback_http_response << io assert_nothing_raised do @client.get('http://foo/bar') end end def test_urify extend HTTPClient::Util assert_nil(urify(nil)) uri = 'http://foo' assert_equal(urify(uri), urify(uri)) assert_equal(urify(uri), urify(urify(uri))) end def test_connection c = HTTPClient::Connection.new assert(c.finished?) assert_nil(c.join) end def test_site site = HTTPClient::Site.new assert_equal('tcp', site.scheme) assert_equal('0.0.0.0', site.host) assert_equal(0, site.port) assert_equal('tcp://0.0.0.0:0', site.addr) assert_equal('tcp://0.0.0.0:0', site.to_s) assert_nothing_raised do site.inspect end # site = HTTPClient::Site.new(urify('http://localhost:12345/foo')) assert_equal('http', site.scheme) assert_equal('localhost', site.host) assert_equal(12345, site.port) assert_equal('http://localhost:12345', site.addr) assert_equal('http://localhost:12345', site.to_s) assert_nothing_raised do site.inspect end # site1 = HTTPClient::Site.new(urify('http://localhost:12341/')) site2 = HTTPClient::Site.new(urify('http://localhost:12342/')) site3 = HTTPClient::Site.new(urify('http://localhost:12342/')) assert(!(site1 == site2)) h = { site1 => 'site1', site2 => 'site2' } h[site3] = 'site3' assert_equal('site1', h[site1]) assert_equal('site3', h[site2]) end def test_http_header res = @client.get(serverurl + 'hello') assert_equal('text/html', res.contenttype) assert_equal(5, res.header.get(nil).size) # res.header.delete('connection') assert_equal(4, res.header.get(nil).size) # res.header['foo'] = 'bar' assert_equal(['bar'], res.header['foo']) # assert_equal([['foo', 'bar']], res.header.get('foo')) res.header['foo'] = ['bar', 'bar2'] assert_equal([['foo', 'bar'], ['foo', 'bar2']], res.header.get('foo')) end def test_mime_type assert_equal('text/plain', HTTP::Message.mime_type('foo.txt')) assert_equal('text/html', HTTP::Message.mime_type('foo.html')) assert_equal('text/html', HTTP::Message.mime_type('foo.htm')) assert_equal('application/msword', HTTP::Message.mime_type('foo.doc')) assert_equal('image/png', HTTP::Message.mime_type('foo.png')) assert_equal('image/gif', HTTP::Message.mime_type('foo.gif')) assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpg')) assert_equal('image/jpeg', HTTP::Message.mime_type('foo.jpeg')) assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.unknown')) # handler = lambda { |path| 'hello/world' } assert_nil(HTTP::Message.mime_type_handler) assert_nil(HTTP::Message.get_mime_type_func) HTTP::Message.mime_type_handler = handler assert_not_nil(HTTP::Message.mime_type_handler) assert_not_nil(HTTP::Message.get_mime_type_func) assert_equal('hello/world', HTTP::Message.mime_type('foo.txt')) HTTP::Message.mime_type_handler = nil assert_equal('text/plain', HTTP::Message.mime_type('foo.txt')) HTTP::Message.set_mime_type_func(nil) assert_equal('text/plain', HTTP::Message.mime_type('foo.txt')) # handler = lambda { |path| nil } HTTP::Message.mime_type_handler = handler assert_equal('application/octet-stream', HTTP::Message.mime_type('foo.txt')) end def test_connect_request req = HTTP::Message.new_connect_request(urify('https://foo/bar')) assert_equal("CONNECT foo:443 HTTP/1.0\r\n\r\n", req.dump) req = HTTP::Message.new_connect_request(urify('https://example.com/')) assert_equal("CONNECT example.com:443 HTTP/1.0\r\n\r\n", req.dump) end def test_response res = HTTP::Message.new_response('response') res.contenttype = 'text/plain' res.header.body_date = Time.at(946652400) assert_equal( [ "", "Content-Length: 8", "Content-Type: text/plain", "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT", "Status: 200 OK", "response" ], res.dump.split(/\r\n/).sort ) assert_equal(['8'], res.header['Content-Length']) assert_equal('8', res.headers['Content-Length']) res.header.set('foo', 'bar') assert_equal( [ "", "Content-Length: 8", "Content-Type: text/plain", "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT", "Status: 200 OK", "foo: bar", "response" ], res.dump.split(/\r\n/).sort ) # nil body res = HTTP::Message.new_response(nil) assert_equal( [ "Content-Length: 0", "Content-Type: text/html; charset=us-ascii", "Status: 200 OK" ], res.dump.split(/\r\n/).sort ) # for mod_ruby env Object.const_set('Apache', nil) begin res = HTTP::Message.new_response('response') assert(res.dump.split(/\r\n/).any? { |line| /^Date/ =~ line }) # res = HTTP::Message.new_response('response') res.contenttype = 'text/plain' res.header.body_date = Time.at(946652400) res.header['Date'] = Time.at(946652400).httpdate assert_equal( [ "", "Content-Length: 8", "Content-Type: text/plain", "Date: Fri, 31 Dec 1999 15:00:00 GMT", "HTTP/1.1 200 OK", "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT", "response" ], res.dump.split(/\r\n/).sort ) ensure Object.instance_eval { remove_const('Apache') } end end def test_response_cookies res = HTTP::Message.new_response('response') res.contenttype = 'text/plain' res.header.body_date = Time.at(946652400) assert_nil(res.cookies) # res.header['Set-Cookie'] = [ 'CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT', 'PART_NUMBER=ROCKET_LAUNCHER_0001; path=/' ] assert_equal( [ "", "Content-Length: 8", "Content-Type: text/plain", "Last-Modified: Fri, 31 Dec 1999 15:00:00 GMT", "Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT", "Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/", "Status: 200 OK", "response" ], res.dump.split(/\r\n/).sort ) assert_equal(2, res.cookies.size) assert_equal('CUSTOMER', res.cookies[0].name) assert_equal('PART_NUMBER', res.cookies[1].name) end def test_ok_response_success res = HTTP::Message.new_response('response') assert_equal(true, res.ok?) res.status = 404 assert_equal(false, res.ok?) res.status = 500 assert_equal(false, res.ok?) res.status = 302 assert_equal(false, res.ok?) end if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9' def test_timeout_scheduler assert_equal('hello', @client.get_content(serverurl + 'hello')) status = HTTPClient.timeout_scheduler.instance_eval { @thread.kill; @thread.join; @thread.status } assert(!status) # dead assert_equal('hello', @client.get_content(serverurl + 'hello')) end end def test_session_manager mgr = HTTPClient::SessionManager.new(@client) assert_nil(mgr.instance_eval { @proxy }) assert_nil(mgr.debug_dev) @client.debug_dev = Object.new @client.proxy = 'http://myproxy:12345' mgr = HTTPClient::SessionManager.new(@client) assert_equal('http://myproxy:12345', mgr.instance_eval { @proxy }.to_s) assert_equal(@client.debug_dev, mgr.debug_dev) end def create_keepalive_disconnected_thread(idx, sock) Thread.new { # return "12345" for the first connection sock.gets sock.gets sock.write("HTTP/1.1 200 OK\r\n") sock.write("Content-Length: 5\r\n") sock.write("\r\n") sock.write("12345") # for the next connection, close while reading the request for emulating # KeepAliveDisconnected sock.gets sock.close } end def test_keepalive_disconnected client = HTTPClient.new server = TCPServer.open('127.0.0.1', 0) server.listen(30) # set enough backlogs endpoint = "http://127.0.0.1:#{server.addr[1]}/" Thread.new { Thread.abort_on_exception = true # emulate 10 keep-alive connections 10.times do |idx| sock = server.accept create_keepalive_disconnected_thread(idx, sock) end # return "23456" for the request which gets KeepAliveDisconnected 5.times do sock = server.accept sock.gets sock.gets sock.write("HTTP/1.1 200 OK\r\n") sock.write("\r\n") sock.write("23456") sock.close end # return "34567" for the rest requests while true sock = server.accept sock.gets sock.gets sock.write("HTTP/1.1 200 OK\r\n") sock.write("Connection: close\r\n") sock.write("Content-Length: 5\r\n") sock.write("\r\n") sock.write("34567") sock.close end } # allocate 10 keep-alive connections (0...10).to_a.map { Thread.new { assert_equal("12345", client.get(endpoint).content) } }.each { |th| th.join } # send 5 requests, which should get KeepAliveDesconnected. # doing these requests, rest keep-alive connections are invalidated. (0...5).to_a.map { Thread.new { assert_equal("23456", client.get(endpoint).content) } }.each { |th| th.join } # rest requests won't get KeepAliveDisconnected; how can I check this? (0...10).to_a.map { Thread.new { assert_equal("34567", client.get(endpoint).content) } }.each { |th| th.join } end def create_keepalive_thread(count, sock) Thread.new { Thread.abort_on_exception = true count.times do req = sock.gets while line = sock.gets break if line.chomp.empty? end case req when /chunked/ sock.write("HTTP/1.1 200 OK\r\n") sock.write("Transfer-Encoding: chunked\r\n") sock.write("\r\n") sock.write("1a\r\n") sock.write("abcdefghijklmnopqrstuvwxyz\r\n") sock.write("10\r\n") sock.write("1234567890abcdef\r\n") sock.write("0\r\n") sock.write("\r\n") else sock.write("HTTP/1.1 200 OK\r\n") sock.write("Content-Length: 5\r\n") sock.write("\r\n") sock.write("12345") end end sock.close } end def test_keepalive server = TCPServer.open('localhost', 0) server_thread = Thread.new { Thread.abort_on_exception = true sock = server.accept create_keepalive_thread(10, sock) } url = "http://localhost:#{server.addr[1]}/" begin # content-length 5.times do assert_equal('12345', @client.get(url).body) end # chunked 5.times do assert_equal('abcdefghijklmnopqrstuvwxyz1234567890abcdef', @client.get(url + 'chunked').body) end ensure server.close server_thread.join end end def test_socket_local @client.socket_local.host = '127.0.0.1' assert_equal('hello', @client.get_content(serverurl + 'hello')) @client.reset_all @client.socket_local.port = serverport begin @client.get_content(serverurl + 'hello') rescue Errno::EADDRINUSE, SocketError assert(true) end end def test_body_param_order ary = ('b'..'d').map { |k| ['key2', k] } << ['key1', 'a'] << ['key3', 'z'] assert_equal("key2=b&key2=c&key2=d&key1=a&key3=z", HTTP::Message.escape_query(ary)) end if RUBY_VERSION > "1.9" def test_charset body = @client.get(serverurl + 'charset').body assert_equal(Encoding::EUC_JP, body.encoding) assert_equal('あいうえお'.encode(Encoding::EUC_JP), body) end end if RUBY_VERSION >= "1.9.3" def test_continue @client.debug_dev = str = '' res = @client.get(serverurl + 'continue', :header => {:Expect => '100-continue'}) assert_equal(200, res.status) assert_equal('done!', res.body) assert_match(/Expect: 100-continue/, str) end end def test_ipv6literaladdress_in_uri server = TCPServer.open('::1', 0) rescue return # Skip if IPv6 is unavailable. server_thread = Thread.new { Thread.abort_on_exception = true sock = server.accept while line = sock.gets break if line.chomp.empty? end sock.write("HTTP/1.1 200 OK\r\n") sock.write("Content-Length: 5\r\n") sock.write("\r\n") sock.write("12345") sock.close } uri = "http://[::1]:#{server.addr[1]}/" begin assert_equal('12345', @client.get(uri).body) ensure server.close server_thread.kill server_thread.join end end private def check_query_get(query) WEBrick::HTTPUtils.parse_query( @client.get(serverurl + 'servlet', query).header["x-query"][0] ) end def check_query_post(query) WEBrick::HTTPUtils.parse_query( @client.post(serverurl + 'servlet', query).header["x-query"][0] ) end def setup_server @server = WEBrick::HTTPServer.new( :BindAddress => "localhost", :Logger => @logger, :Port => 0, :AccessLog => [], :DocumentRoot => File.dirname(File.expand_path(__FILE__)) ) @serverport = @server.config[:Port] [ :hello, :sleep, :servlet_redirect, :redirect1, :redirect2, :redirect3, :redirect_self, :relative_redirect, :redirect_see_other, :chunked, :largebody, :status, :compressed, :charset, :continue ].each do |sym| @server.mount( "/#{sym}", WEBrick::HTTPServlet::ProcHandler.new(method("do_#{sym}").to_proc) ) end @server.mount('/servlet', TestServlet.new(@server)) @server_thread = start_server_thread(@server) end def escape_noproxy backup = HTTPClient::NO_PROXY_HOSTS.dup HTTPClient::NO_PROXY_HOSTS.clear yield ensure HTTPClient::NO_PROXY_HOSTS.replace(backup) end def do_hello(req, res) res['content-type'] = 'text/html' res.body = "hello" end def do_sleep(req, res) sec = req.query['sec'].to_i sleep sec res['content-type'] = 'text/html' res.body = "hello" end def do_servlet_redirect(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "servlet") end def do_redirect1(req, res) res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, serverurl + "hello") end def do_redirect2(req, res) res.set_redirect(WEBrick::HTTPStatus::TemporaryRedirect, serverurl + "redirect3") end def do_redirect3(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "hello") end def do_redirect_self(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, serverurl + "redirect_self") end def do_relative_redirect(req, res) res.set_redirect(WEBrick::HTTPStatus::Found, "hello") end def do_redirect_see_other(req, res) if req.request_method == 'POST' res.set_redirect(WEBrick::HTTPStatus::SeeOther, serverurl + "redirect_see_other") # self else res.body = 'hello' end end def do_chunked(req, res) res.chunked = true piper, pipew = IO.pipe res.body = piper pipew << req.query['msg'] pipew.close end def do_largebody(req, res) res['content-type'] = 'text/html' res.body = "a" * 1000 * 1000 end def do_compressed(req, res) res['content-type'] = 'application/octet-stream' if req.query['enc'] == 'gzip' res['content-encoding'] = 'gzip' res.body = GZIP_CONTENT elsif req.query['enc'] == 'deflate' res['content-encoding'] = 'deflate' res.body = DEFLATE_CONTENT end end def do_charset(req, res) if RUBY_VERSION > "1.9" res.body = 'あいうえお'.encode("euc-jp") res['Content-Type'] = 'text/plain; charset=euc-jp' else res.body = 'this endpoint is for 1.9 or later' end end def do_status(req, res) res.status = req.query['status'].to_i end def do_continue(req, res) req.continue res.body = 'done!' end class TestServlet < WEBrick::HTTPServlet::AbstractServlet def get_instance(*arg) self end def do_HEAD(req, res) res["x-head"] = 'head' # use this for test purpose only. res["x-query"] = query_response(req) end def do_GET(req, res) res.body = 'get' res["x-query"] = query_response(req) end def do_POST(req, res) res["content-type"] = "text/plain" # iso-8859-1, not US-ASCII res.body = 'post,' + req.body.to_s res["x-query"] = body_response(req) end def do_PUT(req, res) res["x-query"] = body_response(req) param = WEBrick::HTTPUtils.parse_query(req.body) || {} res["x-size"] = (param['txt'] || '').size res.body = param['txt'] || 'put' end def do_DELETE(req, res) res.body = 'delete' end def do_OPTIONS(req, res) # check RFC for legal response. res.body = 'options' end def do_PROPFIND(req, res) res.body = 'propfind' end def do_PROPPATCH(req, res) res.body = 'proppatch' res["x-query"] = body_response(req) end def do_TRACE(req, res) # client SHOULD reflect the message received back to the client as the # entity-body of a 200 (OK) response. [RFC2616] res.body = 'trace' res["x-query"] = query_response(req) end private def query_response(req) query_escape(WEBrick::HTTPUtils.parse_query(req.query_string)) end def body_response(req) query_escape(WEBrick::HTTPUtils.parse_query(req.body)) end def query_escape(query) escaped = [] query.sort_by { |k, v| k }.collect do |k, v| v.to_ary.each do |ve| escaped << CGI.escape(k) + '=' + CGI.escape(ve) end end escaped.join('&') end end end httpclient-2.3.3/bin/0000755000004100000410000000000012155433207014477 5ustar www-datawww-datahttpclient-2.3.3/bin/httpclient0000755000004100000410000000246612155433207016613 0ustar www-datawww-data#!/usr/bin/env ruby # httpclient shell command. # # Usage: 1) % httpclient get https://www.google.co.jp/ q=ruby # Usage: 2) % httpclient # # For 1) it issues a GET request to the given URI and shows the wiredump and # the parsed result. For 2) it invokes irb shell with the binding that has a # HTTPClient as 'self'. You can call HTTPClient instance methods like; # > get "https://www.google.co.jp/", :q => :ruby require 'httpclient' METHODS = ['head', 'get', 'post', 'put', 'delete', 'options', 'propfind', 'proppatch', 'trace'] if ARGV.size >= 2 && METHODS.include?(ARGV[0]) client = HTTPClient.new client.debug_dev = STDERR $DEBUG = true require 'pp' pp client.send(*ARGV) exit end require 'irb' require 'irb/completion' class Runner def initialize @httpclient = HTTPClient.new end def method_missing(msg, *a, &b) debug, $DEBUG = $DEBUG, true begin @httpclient.send(msg, *a, &b) ensure $DEBUG = debug end end def run IRB.setup(nil) ws = IRB::WorkSpace.new(binding) irb = IRB::Irb.new(ws) IRB.conf[:MAIN_CONTEXT] = irb.context trap("SIGINT") do irb.signal_handle end begin catch(:IRB_EXIT) do irb.eval_input end ensure IRB.irb_at_exit end end def to_s 'HTTPClient' end end Runner.new.run httpclient-2.3.3/metadata.yml0000644000004100000410000000435212155433207016236 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: httpclient version: !ruby/object:Gem::Version version: 2.3.3 prerelease: platform: ruby authors: - Hiroshi Nakamura autorequire: bindir: bin cert_chain: [] date: 2013-02-24 00:00:00.000000000 Z dependencies: [] description: email: nahi@ruby-lang.org executables: - httpclient extensions: [] extra_rdoc_files: [] files: - bin/httpclient - lib/httpclient/auth.rb - lib/httpclient/ssl_config.rb - lib/httpclient/http.rb - lib/httpclient/timeout.rb - lib/httpclient/version.rb - lib/httpclient/connection.rb - lib/httpclient/include_client.rb - lib/httpclient/session.rb - lib/httpclient/cacert.p7s - lib/httpclient/cookie.rb - lib/httpclient/util.rb - lib/oauthclient.rb - lib/httpclient.rb - lib/http-access2.rb - lib/http-access2/http.rb - lib/http-access2/cookie.rb - lib/hexdump.rb - sample/auth.rb - sample/dav.rb - sample/stream.rb - sample/async.rb - sample/wcat.rb - sample/ssl/1000cert.pem - sample/ssl/1000key.pem - sample/ssl/ssl_client.rb - sample/ssl/0key.pem - sample/ssl/htdocs/index.html - sample/ssl/0cert.pem - sample/ssl/webrick_httpsd.rb - sample/oauth_salesforce_10.rb - sample/thread.rb - sample/oauth_friendfeed.rb - sample/oauth_twitter.rb - sample/cookie.rb - sample/howto.rb - sample/oauth_buzz.rb - test/client.cert - test/test_http-access2.rb - test/server.cert - test/htdigest - test/test_ssl.rb - test/test_hexdump.rb - test/test_cookie.rb - test/runner.rb - test/htpasswd - test/test_auth.rb - test/client.key - test/test_include_client.rb - test/helper.rb - test/ca.cert - test/sslsvr.rb - test/subca.cert - test/ca-chain.cert - test/server.key - test/test_httpclient.rb - README.txt homepage: http://github.com/nahi/httpclient licenses: [] post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ! '>=' - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 1.8.23 signing_key: specification_version: 3 summary: gives something like the functionality of libwww-perl (LWP) in Ruby test_files: [] has_rdoc: httpclient-2.3.3/README.txt0000644000004100000410000007421212155433207015433 0ustar www-datawww-datahttpclient - HTTP accessing library. Copyright (C) 2000-2012 NAKAMURA, Hiroshi . 'httpclient' gives something like the functionality of libwww-perl (LWP) in Ruby. 'httpclient' formerly known as 'http-access2'. See HTTPClient for documentation. == Features * methods like GET/HEAD/POST/* via HTTP/1.1. * HTTPS(SSL), Cookies, proxy, authentication(Digest, NTLM, Basic), etc. * asynchronous HTTP request, streaming HTTP request. * debug mode CLI. * by contrast with net/http in standard distribution; * Cookies support * MT-safe * streaming POST (POST with File/IO) * Digest auth * Negotiate/NTLM auth for WWW-Authenticate (requires net/ntlm module; rubyntlm gem) * NTLM auth for Proxy-Authenticate (requires 'win32/sspi' module; rubysspi gem) * extensible with filter interface * you don't have to care HTTP/1.1 persistent connection (httpclient cares instead of you) * Not supported now * Cache * Rather advanced HTTP/1.1 usage such as Range, deflate, etc. (of course you can set it in header by yourself) == httpclient command Usage: 1) % httpclient get https://www.google.co.jp/ q=ruby Usage: 2) % httpclient For 1) it issues a GET request to the given URI and shows the wiredump and the parsed result. For 2) it invokes irb shell with the binding that has a HTTPClient as 'self'. You can call HTTPClient instance methods like; > get "https://www.google.co.jp/", :q => :ruby == Author Name:: Hiroshi Nakamura E-mail:: nahi@ruby-lang.org Project web site:: http://github.com/nahi/httpclient == License This program is copyrighted free software by NAKAMURA, Hiroshi. You can redistribute it and/or modify it under the same terms of Ruby's license; either the dual license version in 2003, or any later version. httpclient/session.rb is based on http-access.rb in http-access/0.0.4. Some part of it is copyrighted by Maebashi-san who made and published http-access/0.0.4. http-access/0.0.4 did not include license notice but when I asked Maebashi-san he agreed that I can redistribute it under the same terms of Ruby. Many thanks to Maebashi-san. == Install === Gem You can install httpclient with rubygems. % gem install httpclient === Package You can install httpclient with the bundled installer script. $ ruby install.rb It will install lib/* to your site_ruby directory such as /usr/local/lib/ruby/site_ruby/1.8/. For uninstall, delete installed files from your site_ruby directory. == Usage See HTTPClient for documentation. You can also check sample/howto.rb how to use APIs. == Download * Gem repository * https://rubygems.org/gems/httpclient * git: git://github.com/nahi/httpclient.git == Bug report or Feature request Please file a ticket at the project web site. 1. find a similar ticket from https://github.com/nahi/httpclient/issues 2. create a new ticket by clicking 'Create Issue' button. 3. you can use github features such as pull-request if you like. Thanks in advance. == Changes = Changes in 2.3.3 = February 24, 2013 - version 2.3.3 * Changes * #144 Add User-Agent field by default. You can remove the header by setting nil to HTTPClient#agent_name. = Changes in 2.3.2 = January 5, 2013 - version 2.3.2 * Changes * #138 Revert Timeout change unintentionally included in v2.3.1. It's reported that the change causes background processes not terminated properly. = Changes in 2.3.1 = January 1, 2013 - version 2.3.1 * Changes * #137 Signing key is expiring for cacert_sha1.p7s. Deleted p7s signature check for default cacerts. Sorry for many troubles in the past. This feature is not useful without having online/real-time CA certs update but I don't think I can implement it in near future. Users depend on this signature check (who puts cacert.p7s in R/W filesystem and ssl_config.rb in R/O filesystem) should take care the tampering by themself. * Bug fixes * #122 Support IPv6 address in URI = Changes in 2.3.0 = October 10, 2012 - version 2.3.0 * Features * Added debug mode CLI. bin/httpclient is installed as CLI. Usage: 1) % httpclient get https://www.google.co.jp/ q=ruby Usage: 2) %httpclient For 1) it issues a GET request to the given URI and shows the wiredump and the parsed result. For 2) it invokes irb shell with the binding that has a HTTPClient as 'self'. You can call HTTPClient instance methods like; > get "https://www.google.co.jp/", :q => :ruby * #119 Addressable gem support (only if it exists); should handle IRI properly. * Bug fixes * #115 Cookies couldn't work properly if the path in an URI is ommited. * #112, #117 Proper handling of sized IO (the IO object that responds to :size) for chunked POST. HTTPClient did read till EOF even if the given IO has :size method. * Handle '303 See Other' properly. RFC2616 says it should be redirected with GET. * #116 Fix "100-continue" support. It was just ignored. * #118 Support for boolean values when making POST/PUT requests with multiipart/form Content-Type. * #110 Allows leading dots in no_proxy hostname suffixes. = Changes in 2.2.7 = August 14, 2012 - version 2.2.7 * Bug fixes * Fix arity incompatibility introduced in 2.2.6. It broke Webmock. Thanks Andrew France for the report! = Changes in 2.2.6 = August 14, 2012 - version 2.2.6 * Bug fixes * Make get_content doesn't raise a BadResponseError for perfectly good responses like 304 Not Modified. Thanks to Florian Hars. * Add 'Content-Type: application/x-www-form-urlencoded' for the PUT request that has urlencoded entity-body. * Features * Add HTTPClient::IncludeClient by Jonathan Rochkind, a mix-in for easily adding a thread-safe lazily initialized class-level HTTPClient object to your class. * Proxy DigestAuth support. Thanks to Alexander Kotov and Florian Hars. * Accept an array of strings (and IO-likes) as a query value e.g. `{ x: 'a', y: [1,2,3] }` is encoded into `"x=a&y=1&y=2&y=3"`. Thanks to Akinori MUSHA. * Allow body for DELETE method. * Allow :follow_redirect => true for HEAD request. * Fill request parameters request_method, request_uri and request_query as part of response Message::Header. = Changes in 2.2.5 = May 06, 2012 - version 2.2.5 * Bug fixes * Added Magic encoding comment to hexdump.rb to avoid encoding error. * Add workaround for JRuby issue on Windows (JRUBY-6136) On Windows, calling File#size fails with an Unknown error (20047). This workaround uses File#lstat instead. * Require open-uri only on ruby 1.9, since it is not needed on 1.8. * Features * Allow symbol Header name for HTTP request. * Dump more SSL certificate information under $DEBUG. * Add HTTPClient::SSLConfig#ssl_version property. * Add 'Accept: */*' header to request by default. Rails requies it. It doesn't override given Accept header from API. * Add HTTPClient::SSLConfig#set_default_paths. This method makes HTTPClient instance to use OpenSSL's default trusted CA certificates. * Allow to set Date header manually. ex. clent.get(uri, :header => {'Date' => Time.now.httpdate}) = Changes in 2.2.4 = Dec 08, 2011 - version 2.2.4 * Bug fixes * Do not recycle buffer String object for yielding. When the response is not chunked and the size of the response > 16KB, API with block style yields recycled String object for each yields. * Set VERSION string in User-Agent header. $Id$ didn't work long time... Bugs are reported by Seamus Abshere. Thanks! = Changes in 2.2.3 = Oct 28, 2011 - version 2.2.3 * Bug fixes * Ruby 1.8.6 support. It's broken from 2.2.0. = Changes in 2.2.2 = Oct 17, 2011 - version 2.2.2 * Bug fixes * Do not sort query params on request: Wrongly sorted query params for easier debugging but the order of request parameter should be preserved. #65 * Changes * Set responce String encoding if possible. Parse content-type response header with some helps from OpenURI::Meta and set response String encoding. #26 * Improve connection cache strategy. Reuse cached session in MRU order, not in LRU. MRU is more server friendly than LRU because it reduces number of cached sessions when a number of requests drops after an usaage spike. With reusing sessions in LRU order, all sessions are equally checked if it's closed or not, as far as there's a request to the same site. With reusing sessions in MRU order, old cold sessions are kept in cache long time even if there's a request to the same site. To avoid this leakage, this version adds keep_alive_timeout property and let SessionManager scrub all sessions with checking the timeout for each session. When the session expires against the last used time, it's closed and collected. keep_alive_timeout is 15[sec] by default. The value is from the default value for KeepAliveTimeout of Apache httpd 2. #68 #69 = Changes in 2.2.1 = Jun 2, 2011 - version 2.2.1 * Bug fixes * For Lighttpd + PUT/POST support, do not send a request using chunked encoding when IO respond to :size, File for example. - There is no need to send query with Transfer-Encoding: chuncked when IO respond to :size. - Lighttpd does not support PUT, POST with Transfer-Encoding: chuncked. You will see that the lighty respond with 200 OK, but there is a file whose size is zero. LIMITATION: timeout occurs certainly when you send very large file and @send_timeout is default since HTTPClient::Session#query() assumes that *all* write are finished in @send_timeout sec not each write. WORKAROUND: increment @send_timeout and @receive_timeout or set @send_timeout and @receive_timeout to 0 not to be timeout. This fix is by TANABE Ken-ichi . Thanks! * Allow empty http_proxy ENV variable. Just treat it the same as if it's nil/unset. This fix is by Ash Berlin . Thanks! * Check EOF while reading chunked response and close the session. It raised NoMethodError. * Changes * Updated trusted CA certificates file (cacert.p7s and cacert_sha1.p7s). CA certs are imported from 'Java(TM) SE Runtime Environment (build 1.6.0_25-b06)'. * Changed default chunk size from 4K to 16K. It's used for reading size at a time. = Changes in 2.2.0 = Apr 8, 2011 - version 2.2.0 * Features * Add HTTPClient#cookies as an alias of #cookie_manager.cookies. * Add res.cookies method. It returns parsed cookie in response header. It's different from client.cookie_manager.cookies. Manager keeps persistent cookies in it. * Add res.headers method which returns a Hash of headers. Hash key and value are both String. Each key has a single value so you can't extract exact value when a message has multiple headers like 'Set-Cookie'. Use header['Set-Cookie'] for that purpose. (It returns an Array always) * Allow keyword style argument for HTTPClient#get, post, etc. Introduced keywords are: :body, :query, and :header. You can write HTTPClient.get(uri, :header => {'X-custom' => '1'}) instead of; HTTPClient.get(uri, nil, {'X-custom' => '1'}) * Add new keyword argument :follow_redirect to get/post. Now you can follow redirection response with passing :follow_redirect => true. * [INCOMPAT] Rename HTTPClient::HTTP::Message#body to #http_body, then add #body as an alias of #content. It's incompatible change though users rarely depends on this method. (I've never seen such a case) Users who are using req.body and/or res.body should follow this change. (req.http_body and res.http_body) * Bug fixes * Reenable keep-alive for chunked response. This feature was disabled by c206b687952e1ad3e20c20e69bdbd1a9cb38609e at 2008-12-09. I should have written a test for keep-alive. Now I added it. Thanks Takahiro Nishimura(@dr_taka_n) for finding this bug. = Changes in 2.1.7 = Mar 22, 2011 - version 2.1.7 * Features * Add MD5-sess auth support. Thanks to wimm-dking. (#47) * Add SNI support. (Server Name Indication of HTTPS connection) (#49) * Add GSSAPI auth support using gssapi gem. Thanks to zenchild. (#50) * NTLM logon to exchange Web Services. [experimental] Thanks to curzonj and mccraigmccraig (#52) * Add HTTPOnly cookie support. Thanks to nbrosnahan. (#55) * Add HTTPClient#socket_local for specifying local binding hostname and port of TCP socket. Thanks to icblenke. = Changes in 2.1.6 = Dec 20, 2010 - version 2.1.6 * IMPORTANT update for HTTPS(SSL) connection * Trusted CA bundle file cacert_sha1.p7s for older environment (where you cannot use SHA512 algorithm such as an old Mac OS X) included in httpclient 2.1.5 expires in Dec 31, 2010. Please update to 2.1.6 if you're on such an environment. * Updated trusted CA certificates file (cacert.p7s and cacert_sha1.p7s). CA certs are imported from 'Java(TM) SE Runtime Environment (build 1.6.0_22-b04)'. * IMPORTANT bug fix for persistent connection * #29 Resource Leak: If httpclient establishes two connections to the same server in parallel, one of these connections will be leaked, patch by xb. * #30 When retrying a failed persistent connection, httpclient should use a fresh connection, reported by xb. These 2 fixes should fix 'Too many open files' error as well if you're getting this. Please check 2.1.6 and let me know how it goes! * Features * #4 Added OAuthClient. See sample clients in sample/ dir. * #42 Added transparent_gzip_decompression property, patch by Teshootub7. All you need to use it is done by; client.transparent_gzip_decompression = true Then you can retrieve a document as usural in decompressed format. * #38 Debug dump binary data (checking it includes \0 or not) in hex encoded format, patch by chetan. * Bug fixes * #8 Opened certificate and key files for SSL not closed properly. * #10 "get" method gets blocked in "readpartial" when receiving a 304 with no Content-Length. * #11 Possible data corruption problem in asynchronous methods, patch by a user. (http://dev.ctor.org/http-access2/ticket/228) * #13 illegal Cookie PATH handling. When no PATH part given in Set-Cookie header, URL's path part should be used for path variable. * #16 httpclient doesn't support multiline server headers. * #19 set_request_header clobbers 'Host' header setting if given, patch by meuserj. * #20 Relative Location on https redirect fails, patch by zenchild. * #22 IIS/6 + MicrosoftSharePointTeamServices uses "NTLM" instead of "Negotiate". * #27 DigestAuth header: 'qop' parameter must not be enclosed between double quotation, patch by ibc. * #36 Wrong HTTP version in headers with Qt4 applications, reported by gauleng. * #38 DigestAuth + posting IO fails, patch by chetan. * #41 https-over-proxy fails with IIS, patch by tai. = Changes in 2.1.5 = Jun 25, 2009 - version 2.1.5.2 * Added another cacert distribution certificate which uses sha1WithRSAEncryption. OpenSSL/0.9.7 cannot handle non-SHA1 digest algorithm for certificate. The new certificate is RSA 2048 bit + SHA1 + notAfter:2010/12/31. Corresponding CA bundle file is cacert_sha1.p7s. It is loaded only when cacert.p7s cannot be loaded with the original distribution certificate. Jun 11, 2009 - version 2.1.5.1 * README update. Jun 8, 2009 - version 2.1.5 * IMPORTANT update for HTTPS(SSL) connection * Trusted CA bundle file included in httpclient <= 2.1.4 expires in Nov 2009. Please update to 2.1.5 by Oct 2009 if your application depends on trusted CA bundle file. * Updated trusted CA certificates file (cacert.p7s). CA certs are imported from 'Java(TM) SE Runtime Environment (build 1.6.0_13-b03)'. * Updated a cacert distribution certificate. RSA 2048 bit + SHA512 + notAfter:2037/12/31. (#215) * Feature * WWW authentication with Negotiate based on win32/sspi as same as Proxy authentication. Applied a patch from Paul Casto. Thanks! (#212) * Bug fixes * Infinite loop caused by EOF error while reading response message body without Content-Length. IO#readpartial does not clear the second argument (buffer) when an exception raised. Fixed by a patch from an user. Thanks! (#216) * NoMethodError caused by the cookie string that includes a double semicolons ";;". Fixed by a patch from an user. Thanks! (#211) * CNONCE attribute in Digest Authentication was not properly generated by itself (used same nonce sent from the connecting server). Fixed by a patch from bterlson [http://github.com/bterlson/httpclient/commit/6d0df734840985a7be88a2d54443bbf892d50b9a] Thanks! (#209) * Cookie header was not set in authentication negotiation. Fixed. This bug was found and pointed out by bterlson at [http://github.com/bterlson/httpclient/commits/master]. Thanks! (#210) * Do not send 'Content-Length: 0' when a request doesn't have message body. Some server application (!EasySoap++/0.6 for example) corrupts with the request with Content-Length: 0. This bug was found by clay [http://daemons.net/~clay/2009/05/03/ruby-why-do-you-torment-me/]. Thanks! (#217) * Ensure to reset connection after invoking HTTPClient singleton methods for accessing such as HTTPClient.get_content. Thanks to @xgavin! (#214) Feb 13, 2009 - version 2.1.4 * Bug fixes * When we hit some site through http-proxy we get a response without Content-Length header. httpclient/2.1.3 drops response body for such case. fixed. (#199) * Avoid duplicated 'Date' header in request. Fixed. (#194) * Avoid to add port number to 'Host' header. Some servers like GFE/1.3 dislike it. Thanks to anonymous user for investigating the behavior. (#195) * httpclient/2.1.3 does not work when you fork a process after requiring httpclient module (Passenger). Thanks to Akira Yamada for tracing down this bug. (#197) * httpclient/2.1.3 cannot handle Cookie header with 'expires=' and 'expires=""'. Empty String for Time.parse returns Time.now unlike ParseDate.parsedate. Thanks to Mark for the patch. (#200) Jan 8, 2009 - version 2.1.3.1 * Security fix introduced at 2.1.3. * get_content/post_content of httpclient/2.1.3 may send secure cookies for a https site to non-secure (non-https) site when the https site redirects the request to a non-https site. httpclient/2.1.3 caches request object and reuses it for redirection. It should not be cached and recreated for each time as httpclient <= 2.1.2 and http-access2. * I realized this bug when I was reading open-uri story on [ruby-core:21205]. Ruby users should use open-uri rather than using net/http directly wherever possible. Dec 29, 2008 - version 2.1.3 * Features * Proxy Authentication for SSL. * Performance improvements. * Full RDoc. Please tell me any English problem. Thanks in advance. * Do multipart file upload when a given body includes a File. You don't need to set 'Content-Type' and boundary String any more. * Added propfind and proppatch methods. * Changes * Avoid unnecessary memory consuming for get_content/post_content with block. get_content returns nil when you call it with a block. * post_content with IO did not work when redirect/auth cycle is required. (CAUTION: post_content now correctly follows redirection and posts the given content) * Exception handling cleanups. * Raises HTTPClient::ConfigurationError? for environment problem. (trying to do SSL without openssl installed for example) * Raises HTTPClient::BadResponse? for HTTP response problem. You can get the response HTTPMessage returned via $!.res. * Raises SocketError? for connection problem (as same as before). * Bug fixes * Avoid unnecessary negotiation cycle for Negotiate(NTLM) authentication. Thanks Rishav for great support for debugging Negotiate authentication. * get_content/post_content with block yielded unexpected message body during redirect/auth cycle. * Relative URI redirection should be allowed from 2.1.2 but it did not work... fixed. * Avoid unnecessary timeout waiting when no message body returned such as '204 No Content' for DAV. * Avoid blocking on socket closing when the socket is already closed by foreign host and the client runs under MT-condition. Sep 22, 2007 - version 2.1.2 * HTTP * implemented Negotiate authentication with a support from exterior modules. 'rubyntlm' module is required for Negotiate auth with IIS. 'win32/sspi' module is required for Negotiate auth with ISA. * a workaround for Ubuntu + SonicWALL timeout problem. try to send HTTP request in one chunk. * SSL * create new self-signing dist-cert which has serial number 0x01 and embed it in httpclient.rb. * update cacert.p7s. certificates are imported from cacerts in JRE 6 Update 2. 1 expired CA certificate 'C=US, O=GTE Corporation, CN=GTE CyberTrust Root' is removed. * Bug fix * [BUG] SSL + debug_dev didn't work under version 2.1.1. * [BUG] Reason-Phrase of HTTP response status line can be empty according * to RFC2616. Aug 28, 2007 - version 2.1.1 * bug fix * domain_match should be case insensitive. thanks to Brian for the patch. * before calling SSLSocket#post_connection_check, check if RUBY_VERSION > "1.8.4" for CN based wildcard certificate. when RUBY_VERSION <= "1.8.4", it fallbacks to the post_connection_check method in HTTPClient so httpclient should run on 1.8.4 fine as before. * misc * added HTTPClient#test_loopback_http_response which accepts test loopback response which contains HTTP header. Jul 14, 2007 - version 2.1.0 * program/project renamed from 'http-access2' to 'httpclient'. there's compatibility layer included so existing programs for http-access2 which uses HTTPAccess2::Client should work with httpclient/2.1.0 correctly. * misc * install.rb did not install cacerts.p7s. Thanks to knu. * now HTTPClient loads http_proxy/HTTP_PROXY and no_proxy/NO_PROXY environment variable at initialization time. bear in mind that it doesn't load http_proxy/HTTP_PROXY when a library is considered to be running under CGI environment (checked by ENVREQUEST_METHOD existence. cgi_http_proxy/CGI_HTTP_PROXY is loaded instead. Jul 4, 2007 - version 2.0.9 * bug fix * fix the BasicAuth regression problem in 2.0.8. A server may return "BASIC" as an authenticate scheme label instead of "Basic". It must be treated as a case-insensitive token according to RFC2617 section 1.2. Thanks to mwedeme for contributing the patch. (#159) Jun 30, 2007 - version 2.0.8 * HTTP * added request/response filter interface and implemented DigestAuth based on the filter interface. DigestAuth calc engine is based on http://tools.assembla.com/breakout/wiki/DigestForSoap Thanks to sromano. (#155) * re-implemented BasicAuth based on the filter interface. send BasicAuth header only if it's needed. (#31) * handle a response which has 2XX status code as a successfull response while retry check. applied the patch from Micah Wedemeyer. Thanks! (#158) * Connection * show more friendly error message for unconnectable URL. (#156) * bug fixes * to avoid MIME format incompatibility, add empty epilogue chunk explicitly. Thanks to the anonymous user who reported #154 (#154) * rescue EPIPE for keep-alive reconnecting. Thanks to anonymous user who posted a patch at #124. (#124) May 13, 2007 - version 2.0.7 * HTTP * added proxyauth support. (#6) * let developer allow to rescue a redirect with relative URI. (#28) * changed last-chunk condition statement to allow "0000\r\n" marker from WebLogic Server 7.0 SP5 instead of "0\r\n". (#30) * fixed multipart form submit. (#29, #116) * use http_date format as a date in a request header. (#35) * avoid duplicated Date header when running under mod_ruby. (#127) * reason phrase in Message#reason contains \r. (#122) * trim "\n"s in base64 encoded BasicAuth value for interoperability. (#149) * let retry_connect return a Message not a content. (#119) * rescue SocketError and dump a message when a wrong address given. (#152) * HTTP-Cookies * changed "domain" parameter matching condition statement to allow followings; (#24, #32, #118, #147) * [host, domain] = [rubyforge.com, .rubyforge.com] * [host, domain] = [reddit.com, reddit.com] * SSL * bundles CA certificates as trust anchors. * allow user to get peer_cert. (#117, #123) * added wildcard certificate support. (#151) * SSL + HTTP keep-alive + long wait causes uncaught exception. fixed. (#120) * Connection * fixed a loop condition bug that caused intermittent empty response. (#150, #26, #125) September 16, 2005 - version 2.0.6 * HTTP * allows redirects from a "POST" request. imported a patch from sveit. Thanks! (#7) * add 'content-type: application/application/x-www-form-urlencoded' when a request contains message-body. (#11) * HTTP/0.9 support. (#15) * allows submitting multipart forms. imported a patch from sveit. Thanks! (#7) * HTTP-Cookies * avoid NameError when a cookie value is nil. (#10) * added netscape_rule property to CookieManager (false by default). You can turn on the domain attribute test of Netscape rule with the property. cf. http://wp.netscape.com/newsref/std/cookie_spec.html * added HTTPClient#cookie_manager property for accessing its properties. (#13) * added save_all_cookies method to save unused and discarded cookies as well. The patch is from Christian Lademann. Thanks! (#21) * allow to set cookie_manager. raise an error when set_cookie_store called and cookie_store has already been set. (#20) * SSL * allows SSL connection debugging when debug_dev != nil. (#14) * skip post_connection_check when verify_mode == OpenSSL::SSL::VERIFY_NONE. Thanks to kdraper. (#12) * post_connection_check: support a certificate with a wildcard in the hostname. (#18) * avoid NameError when no peer_cert and VERIFY_FAIL_IF_NO_PEER_CERT given. Thanks to Christian Lademann. * Connection * insert a connecting host and port to an exception message when connecting failed. (#5) * added socket_sync property to HTTPClient(HTTPAccess2::Client) that controls socket's sync property. the default value is true. CAUTION: if your ruby is older than 2005-09-06 and you want to use SSL connection, do not set socket_sync = false to avoid a blocking bug of openssl/buffering.rb. December 24, 2004 - version 2.0.5 This is a minor bug fix release. - Connect/Send/Receive timeout cannot be configured. fixed. - IPSocket#addr caused SocketError? on Mac OS X 10.3.6 + ruby-1.8.1 GA. fixed. - There is a server which does not like 'foo.bar.com:80' style Host header. The server for http://rubyforge.org/export/rss_sfnews.php seems to dislike HTTP/1.1 Host header "Host: rubyforge.net:80". It returns HTTP 302: Found and redirects to the page again, causes HTTPAccess2::Client to raise "retry count exceeded". Keat found that the server likes "Host: rubyforge.net" (not with port number). February 11, 2004 - version 2.0.4 - add Client#redirect_uri_callback interface. - refactorings and bug fixes found during negative test. - add SSL test. December 16, 2003 - version 2.0.3 - no_proxy was broken in 2.0.2. - do not dump 'Host' header under protocol_version == 'HTTP/1.0' December ?, 2003 - version 2.0.2 - do not trust HTTP_PROXY environment variable. set proxy server manually. http://ftp.ics.uci.edu/pub/websoft/libwww-perl/archive/2001h1/0072.html http://ftp.ics.uci.edu/pub/websoft/libwww-perl/archive/2001h1/0241.html http://curl.haxx.se/mail/archive-2001-12/0034.html - follow ossl2 change. October 4, 2003 - version 2.0.1 Query was not escaped when query was given as an Array or a Hash. Fixed. Do not use http_proxy defined by ENV['http_proxy'] or ENV['HTTP_PROXY'] if the destination host is 'localhost'. Hosts which matches ENV['no_proxy'] or ENV['NO_PROXY'] won't be proxyed. [,:] separated. ("ruby-lang.org:rubyist.net") No regexp. (give "ruby-lang.org", not "*.ruby-lang.org") If you want specify hot by IP address, give full address. ("192.168.1.1, 192.168.1.2") September 10, 2003 - version 2.0 CamelCase to non_camel_case. SSL support (requires Ruby/OpenSSL). Cookies support. lib/http-access2/cookie.rb is redistributed file which is originally included in Webagent by TAKAHASHI `Maki' Masayoshi. You can download the entire package from http://www.rubycolor.org/arc/. January 11, 2003 - version J ruby/1.8 support. httpclient-2.3.3/lib/0000755000004100000410000000000012155433207014475 5ustar www-datawww-datahttpclient-2.3.3/lib/oauthclient.rb0000644000004100000410000000662712155433207017354 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. require 'httpclient' module HTTP class Message attr_accessor :oauth_params end end # OAuthClient provides OAuth related methods in addition to HTTPClient. # # See sample/ dir how to use OAuthClient. There are sample clients for Twitter, # FriendFeed and Google Buzz. class OAuthClient < HTTPClient # HTTPClient::OAuth::Config:: OAuth configurator. attr_accessor :oauth_config # Creates a OAuthClient instance which provides OAuth related methods in # addition to HTTPClient. # # Method signature is as same as HTTPClient. See HTTPClient.new def initialize(*arg) super @oauth_config = HTTPClient::OAuth::Config.new self.www_auth.oauth.set_config(nil, @oauth_config) self.www_auth.oauth.challenge(nil) end # Get request token. # uri:: URI for request token. # callback:: callback String. This can be nil for OAuth 1.0a # param:: Additional query parameter Hash. # # It returns a HTTP::Message instance as a response. When the request is made # successfully, you can retrieve a pair of request token and secret like # following; # res = client.get_request_token(...) # token = res.oauth_params['oauth_token'] # secret = res.oauth_params['oauth_token_secret'] def get_request_token(uri, callback = nil, param = nil) oauth_config.token = nil oauth_config.secret = nil oauth_config.callback = callback oauth_config.verifier = nil res = request(oauth_config.http_method, uri, param) filter_response(res) res end # Get access token. # uri:: URI for request token. # request_token:: a request token String. See get_access_token. # request_token_secret:: a request secret String. See get_access_token. # verifier:: a verifier tag String. # # When the request succeeds and the server returns a pair of access token # and secret, oauth_config.token and oauth_token.secret are updated with # the access token. Then you can call OAuthClient#get, #post, #delete, etc. # All requests are signed. def get_access_token(uri, request_token, request_token_secret, verifier = nil) oauth_config.token = request_token oauth_config.secret = request_token_secret oauth_config.callback = nil oauth_config.verifier = verifier res = request(oauth_config.http_method, uri) filter_response(res) oauth_config.verifier = nil res end # Parse response and returns a Hash. def get_oauth_response(res) enc = res.header['content-encoding'] body = nil if enc and enc[0] and enc[0].downcase == 'gzip' body = Zlib::GzipReader.wrap(StringIO.new(res.content)) { |gz| gz.read } else body = res.content end body.split('&').inject({}) { |r, e| key, value = e.split('=', 2) r[unescape(key)] = unescape(value) r } end private def unescape(escaped) escaped ? ::HTTP::Message.unescape(escaped) : nil end def filter_response(res) if res.status == 200 if res.oauth_params = get_oauth_response(res) oauth_config.token = res.oauth_params['oauth_token'] oauth_config.secret = res.oauth_params['oauth_token_secret'] end end end end httpclient-2.3.3/lib/httpclient/0000755000004100000410000000000012155433207016653 5ustar www-datawww-datahttpclient-2.3.3/lib/httpclient/session.rb0000644000004100000410000006166012155433207020674 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. # # httpclient/session.rb is based on http-access.rb in http-access/0.0.4. Some # part of it is copyrighted by Maebashi-san who made and published # http-access/0.0.4. http-access/0.0.4 did not include license notice but when # I asked Maebashi-san he agreed that I can redistribute it under the same terms # of Ruby. Many thanks to Maebashi-san. require 'socket' require 'thread' require 'stringio' require 'zlib' require 'httpclient/timeout' require 'httpclient/ssl_config' require 'httpclient/http' class HTTPClient # Represents a Site: protocol scheme, host String and port Number. class Site # Protocol scheme. attr_accessor :scheme # Host String. attr_accessor :host alias hostname host # Port number. attr_accessor :port # Creates a new Site based on the given URI. def initialize(uri = nil) if uri @scheme = uri.scheme @host = uri.hostname @port = uri.port.to_i else @scheme = 'tcp' @host = '0.0.0.0' @port = 0 end end # Returns address String. def addr "#{@scheme}://#{@host}:#{@port.to_s}" end # Returns true is scheme, host and port are '==' def ==(rhs) (@scheme == rhs.scheme) and (@host == rhs.host) and (@port == rhs.port) end # Same as ==. def eql?(rhs) self == rhs end def hash # :nodoc: [@scheme, @host, @port].hash end def to_s # :nodoc: addr end # Returns true if scheme, host and port of the given URI matches with this. def match(uri) (@scheme == uri.scheme) and (@host == uri.host) and (@port == uri.port.to_i) end def inspect # :nodoc: sprintf("#<%s:0x%x %s>", self.class.name, __id__, addr) end EMPTY = Site.new.freeze end # Manages sessions for a HTTPClient instance. class SessionManager # Name of this client. Used for 'User-Agent' header in HTTP request. attr_accessor :agent_name # Owner of this client. Used for 'From' header in HTTP request. attr_accessor :from # Requested protocol version attr_accessor :protocol_version # Chunk size for chunked request attr_accessor :chunk_size # Device for dumping log for debugging attr_accessor :debug_dev # Boolean value for Socket#sync attr_accessor :socket_sync attr_accessor :connect_timeout # Maximum retry count. 0 for infinite. attr_accessor :connect_retry attr_accessor :send_timeout attr_accessor :receive_timeout attr_accessor :keep_alive_timeout attr_accessor :read_block_size attr_accessor :protocol_retry_count # Local address to bind local side of the socket to attr_accessor :socket_local attr_accessor :ssl_config attr_reader :test_loopback_http_response attr_accessor :transparent_gzip_decompression def initialize(client) @client = client @proxy = client.proxy @agent_name = nil @from = nil @protocol_version = nil @debug_dev = client.debug_dev @socket_sync = true @chunk_size = ::HTTP::Message::Body::DEFAULT_CHUNK_SIZE @connect_timeout = 60 @connect_retry = 1 @send_timeout = 120 @receive_timeout = 60 # For each read_block_size bytes @keep_alive_timeout = 15 # '15' is from Apache 2 default @read_block_size = 1024 * 16 # follows net/http change in 1.8.7 @protocol_retry_count = 5 @ssl_config = nil @test_loopback_http_response = [] @transparent_gzip_decompression = false @socket_local = Site.new @sess_pool = {} @sess_pool_mutex = Mutex.new @sess_pool_last_checked = Time.now end def proxy=(proxy) if proxy.nil? @proxy = nil else @proxy = Site.new(proxy) end end def query(req, via_proxy) req.http_body.chunk_size = @chunk_size sess = open(req.header.request_uri, via_proxy) begin sess.query(req) rescue sess.close raise end sess end def reset(uri) site = Site.new(uri) close(site) end def reset_all close_all end # assert: sess.last_used must not be nil def keep(sess) add_cached_session(sess) end def invalidate(site) @sess_pool_mutex.synchronize do if pool = @sess_pool[site] pool.each do |sess| sess.invalidate end end end end private def open(uri, via_proxy = false) site = Site.new(uri) sess = nil if cached = get_cached_session(site) sess = cached else sess = Session.new(@client, site, @agent_name, @from) sess.proxy = via_proxy ? @proxy : nil sess.socket_sync = @socket_sync sess.requested_version = @protocol_version if @protocol_version sess.connect_timeout = @connect_timeout sess.connect_retry = @connect_retry sess.send_timeout = @send_timeout sess.receive_timeout = @receive_timeout sess.read_block_size = @read_block_size sess.protocol_retry_count = @protocol_retry_count sess.ssl_config = @ssl_config sess.debug_dev = @debug_dev sess.socket_local = @socket_local sess.test_loopback_http_response = @test_loopback_http_response sess.transparent_gzip_decompression = @transparent_gzip_decompression end sess end def close_all @sess_pool_mutex.synchronize do @sess_pool.each do |site, pool| pool.each do |sess| sess.close end end end @sess_pool.clear end # This method might not work as you expected... def close(dest) if cached = get_cached_session(Site.new(dest)) cached.close true else false end end def get_cached_session(site) @sess_pool_mutex.synchronize do now = Time.now if now > @sess_pool_last_checked + @keep_alive_timeout scrub_cached_session(now) @sess_pool_last_checked = now end if pool = @sess_pool[site] pool.each_with_index do |sess, idx| if valid_session?(sess, now) return pool.slice!(idx) end end end end nil end def scrub_cached_session(now) @sess_pool.each do |site, pool| pool.replace(pool.select { |sess| if valid_session?(sess, now) true else sess.close # close & remove from the pool false end }) end end def valid_session?(sess, now) !sess.invalidated? and (now <= sess.last_used + @keep_alive_timeout) end def add_cached_session(sess) @sess_pool_mutex.synchronize do (@sess_pool[sess.dest] ||= []).unshift(sess) end end end # Wraps up OpenSSL::SSL::SSLSocket and offers debugging features. class SSLSocketWrap def initialize(socket, context, debug_dev = nil) unless SSLEnabled raise ConfigurationError.new('Ruby/OpenSSL module is required') end @context = context @socket = socket @ssl_socket = create_openssl_socket(@socket) @debug_dev = debug_dev end def ssl_connect(hostname = nil) if hostname && @ssl_socket.respond_to?(:hostname=) @ssl_socket.hostname = hostname end @ssl_socket.connect end def post_connection_check(host) verify_mode = @context.verify_mode || OpenSSL::SSL::VERIFY_NONE if verify_mode == OpenSSL::SSL::VERIFY_NONE return elsif @ssl_socket.peer_cert.nil? and check_mask(verify_mode, OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT) raise OpenSSL::SSL::SSLError.new('no peer cert') end hostname = host.host if @ssl_socket.respond_to?(:post_connection_check) and RUBY_VERSION > "1.8.4" @ssl_socket.post_connection_check(hostname) else @context.post_connection_check(@ssl_socket.peer_cert, hostname) end end def ssl_version @ssl_socket.ssl_version if @ssl_socket.respond_to?(:ssl_version) end def ssl_cipher @ssl_socket.cipher end def ssl_state @ssl_socket.state end def peer_cert @ssl_socket.peer_cert end def close @ssl_socket.close @socket.close end def closed? @socket.closed? end def eof? @ssl_socket.eof? end def gets(*args) str = @ssl_socket.gets(*args) debug(str) str end def read(*args) str = @ssl_socket.read(*args) debug(str) str end def readpartial(*args) str = @ssl_socket.readpartial(*args) debug(str) str end def <<(str) rv = @ssl_socket.write(str) debug(str) rv end def flush @ssl_socket.flush end def sync @ssl_socket.sync end def sync=(sync) @ssl_socket.sync = sync end private def check_mask(value, mask) value & mask == mask end def create_openssl_socket(socket) ssl_socket = nil if OpenSSL::SSL.const_defined?("SSLContext") ctx = OpenSSL::SSL::SSLContext.new @context.set_context(ctx) ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ctx) else ssl_socket = OpenSSL::SSL::SSLSocket.new(socket) @context.set_context(ssl_socket) end ssl_socket end def debug(str) @debug_dev << str if @debug_dev && str end end # Wraps up a Socket for method interception. module SocketWrap def initialize(socket, *args) super(*args) @socket = socket end def close @socket.close end def closed? @socket.closed? end def eof? @socket.eof? end def gets(*args) @socket.gets(*args) end def read(*args) @socket.read(*args) end def readpartial(*args) # StringIO doesn't support :readpartial if @socket.respond_to?(:readpartial) @socket.readpartial(*args) else @socket.read(*args) end end def <<(str) @socket << str end def flush @socket.flush end def sync @socket.sync end def sync=(sync) @socket.sync = sync end end # Module for intercepting Socket methods and dumps in/out to given debugging # device. debug_dev must respond to <<. module DebugSocket extend SocketWrap def debug_dev=(debug_dev) @debug_dev = debug_dev end def close super debug("! CONNECTION CLOSED\n") end def gets(*args) str = super debug(str) str end def read(*args) str = super debug(str) str end def readpartial(*args) str = super debug(str) str end def <<(str) super debug(str) end private def debug(str) if str && @debug_dev if str.index("\0") require 'hexdump' str.force_encoding('BINARY') if str.respond_to?(:force_encoding) @debug_dev << HexDump.encode(str).join("\n") else @debug_dev << str end end end end # Dummy Socket for emulating loopback test. class LoopBackSocket include SocketWrap def initialize(host, port, response) super(response.is_a?(StringIO) ? response : StringIO.new(response)) @host = host @port = port end def <<(str) # ignored end end # Manages a HTTP session with a Site. class Session include HTTPClient::Timeout include Util # Destination site attr_reader :dest # Proxy site attr_accessor :proxy # Boolean value for Socket#sync attr_accessor :socket_sync # Requested protocol version attr_accessor :requested_version # Device for dumping log for debugging attr_accessor :debug_dev attr_accessor :connect_timeout attr_accessor :connect_retry attr_accessor :send_timeout attr_accessor :receive_timeout attr_accessor :read_block_size attr_accessor :protocol_retry_count attr_accessor :socket_local attr_accessor :ssl_config attr_reader :ssl_peer_cert attr_accessor :test_loopback_http_response attr_accessor :transparent_gzip_decompression attr_reader :last_used def initialize(client, dest, agent_name, from) @client = client @dest = dest @invalidated = false @proxy = nil @socket_sync = true @requested_version = nil @debug_dev = nil @connect_timeout = nil @connect_retry = 1 @send_timeout = nil @receive_timeout = nil @read_block_size = nil @protocol_retry_count = 5 @ssl_config = nil @ssl_peer_cert = nil @test_loopback_http_response = nil @socket_local = Site::EMPTY @agent_name = agent_name @from = from @state = :INIT @requests = [] @status = nil @reason = nil @headers = [] @socket = nil @readbuf = nil @transparent_gzip_decompression = false @last_used = nil end # Send a request to the server def query(req) connect if @state == :INIT # Use absolute URI (not absolute path) iif via proxy AND not HTTPS. req.header.request_absolute_uri = !@proxy.nil? and !https?(@dest) begin timeout(@send_timeout, SendTimeoutError) do set_header(req) req.dump(@socket) # flush the IO stream as IO::sync mode is false @socket.flush unless @socket_sync end rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError # JRuby can raise IOError instead of ECONNRESET for now close raise KeepAliveDisconnected.new(self) rescue HTTPClient::TimeoutError close raise rescue close if SSLEnabled and $!.is_a?(OpenSSL::SSL::SSLError) raise KeepAliveDisconnected.new(self) else raise end end @state = :META if @state == :WAIT @next_connection = nil @requests.push(req) @last_used = Time.now end def close if !@socket.nil? and !@socket.closed? # @socket.flush may block when it the socket is already closed by # foreign host and the client runs under MT-condition. @socket.close end @state = :INIT end def closed? @state == :INIT end def invalidate @invalidated = true end def invalidated? @invalidated end def get_header begin if @state != :META raise RuntimeError.new("get_status must be called at the beginning of a session") end read_header rescue close raise end [@version, @status, @reason, @headers] end def eof? if !@content_length.nil? @content_length == 0 else @socket.closed? or @socket.eof? end end def get_body(&block) begin read_header if @state == :META return nil if @state != :DATA if @gzipped and @transparent_gzip_decompression # zlib itself has a functionality to decompress gzip stream. # - zlib 1.2.5 Manual # http://www.zlib.net/manual.html#Advanced # > windowBits can also be greater than 15 for optional gzip decoding. Add 32 to # > windowBits to enable zlib and gzip decoding with automatic header detection, # > or add 16 to decode only the gzip format inflate_stream = Zlib::Inflate.new(Zlib::MAX_WBITS + 32) original_block = block block = Proc.new { |buf| original_block.call(inflate_stream.inflate(buf)) } end if @chunked read_body_chunked(&block) elsif @content_length read_body_length(&block) else read_body_rest(&block) end rescue close raise end if eof? if @next_connection @state = :WAIT else close end end nil end private def set_header(req) if @requested_version if /^(?:HTTP\/|)(\d+.\d+)$/ =~ @requested_version req.http_version = $1 end end if @agent_name && req.header.get('User-Agent').empty? req.header.set('User-Agent', "#{@agent_name} #{LIB_NAME}") end if @from && req.header.get('From').empty? req.header.set('From', @from) end if req.header.get('Accept').empty? req.header.set('Accept', '*/*') end if @transparent_gzip_decompression req.header.set('Accept-Encoding', 'gzip,deflate') end if req.header.get('Date').empty? req.header.set_date_header end end # Connect to the server def connect site = @proxy || @dest retry_number = 0 begin timeout(@connect_timeout, ConnectTimeoutError) do @socket = create_socket(site) if https?(@dest) if @socket.is_a?(LoopBackSocket) connect_ssl_proxy(@socket, urify(@dest.to_s)) if @proxy else @socket = create_ssl_socket(@socket) connect_ssl_proxy(@socket, urify(@dest.to_s)) if @proxy begin @socket.ssl_connect(@dest.host) ensure if $DEBUG warn("Protocol version: #{@socket.ssl_version}") warn("Cipher: #{@socket.ssl_cipher.inspect}") warn("State: #{@socket.ssl_state}") end end @socket.post_connection_check(@dest) @ssl_peer_cert = @socket.peer_cert end end # Use Ruby internal buffering instead of passing data immediately # to the underlying layer # => we need to to call explicitly flush on the socket @socket.sync = @socket_sync end rescue RetryableResponse retry_number += 1 if retry_number < @protocol_retry_count retry end raise BadResponseError.new("connect to the server failed with status #{@status} #{@reason}") rescue TimeoutError if @connect_retry == 0 retry else retry_number += 1 retry if retry_number < @connect_retry end close raise end @state = :WAIT end def create_socket(site) socket = nil begin @debug_dev << "! CONNECT TO #{site.host}:#{site.port}\n" if @debug_dev if str = @test_loopback_http_response.shift socket = LoopBackSocket.new(site.host, site.port, str) elsif @socket_local == Site::EMPTY socket = TCPSocket.new(site.host, site.port) else socket = TCPSocket.new(site.host, site.port, @socket_local.host, @socket_local.port) end if @debug_dev @debug_dev << "! CONNECTION ESTABLISHED\n" socket.extend(DebugSocket) socket.debug_dev = @debug_dev end rescue SystemCallError => e e.message << " (#{site})" raise rescue SocketError => e e.message << " (#{site})" raise end socket end # wrap socket with OpenSSL. def create_ssl_socket(raw_socket) SSLSocketWrap.new(raw_socket, @ssl_config, @debug_dev) end def connect_ssl_proxy(socket, uri) req = HTTP::Message.new_connect_request(uri) @client.request_filter.each do |filter| filter.filter_request(req) end set_header(req) req.dump(@socket) @socket.flush unless @socket_sync res = HTTP::Message.new_response('') parse_header res.http_version, res.status, res.reason = @version, @status, @reason @headers.each do |key, value| res.header.set(key.to_s, value) end commands = @client.request_filter.collect { |filter| filter.filter_response(req, res) } if commands.find { |command| command == :retry } raise RetryableResponse.new end unless @status == 200 raise BadResponseError.new("connect to ssl proxy failed with status #{@status} #{@reason}", res) end end # Read status block. def read_header @content_length = nil @chunked = false @gzipped = false @chunk_length = 0 parse_header # Header of the request has been parsed. @state = :DATA req = @requests.shift if req.header.request_method == 'HEAD' or no_message_body?(@status) @content_length = 0 if @next_connection @state = :WAIT else close end end @next_connection = false if !@content_length and !@chunked end StatusParseRegexp = %r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?\r?\n\z) def parse_header timeout(@receive_timeout, ReceiveTimeoutError) do initial_line = nil begin begin initial_line = @socket.gets("\n") if initial_line.nil? close raise KeepAliveDisconnected.new(self) end rescue Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE, IOError # JRuby can raise IOError instead of ECONNRESET for now close raise KeepAliveDisconnected.new(self) end if StatusParseRegexp !~ initial_line @version = '0.9' @status = nil @reason = nil @next_connection = false @content_length = nil @readbuf = initial_line break end @version, @status, @reason = $1, $2.to_i, $3 @next_connection = HTTP::Message.keep_alive_enabled?(@version) @headers = [] while true line = @socket.gets("\n") unless line raise BadResponseError.new('unexpected EOF') end line.chomp! break if line.empty? if line[0] == ?\ or line[0] == ?\t last = @headers.last[1] last << ' ' unless last.empty? last << line.strip else key, value = line.strip.split(/\s*:\s*/, 2) parse_keepalive_header(key, value) @headers << [key, value] end end end while (@version == '1.1' && @status == 100) end end def no_message_body?(status) !status.nil? && # HTTP/0.9 ((status >= 100 && status < 200) || status == 204 || status == 304) end def parse_keepalive_header(key, value) key = key.downcase if key == 'content-length' @content_length = value.to_i elsif key == 'content-encoding' and ( value.downcase == 'gzip' or value.downcase == 'x-gzip' or value.downcase == 'deflate' ) @gzipped = true elsif key == 'transfer-encoding' and value.downcase == 'chunked' @chunked = true @chunk_length = 0 @content_length = nil elsif key == 'connection' or key == 'proxy-connection' if value.downcase == 'keep-alive' @next_connection = true else @next_connection = false end end end def read_body_length(&block) return nil if @content_length == 0 while true buf = '' maxbytes = @read_block_size maxbytes = @content_length if maxbytes > @content_length timeout(@receive_timeout, ReceiveTimeoutError) do begin @socket.readpartial(maxbytes, buf) rescue EOFError close buf = nil end end if buf && buf.bytesize > 0 @content_length -= buf.bytesize yield buf else @content_length = 0 end return if @content_length == 0 end end RS = "\r\n" def read_body_chunked(&block) buf = '' while true len = @socket.gets(RS) if len.nil? # EOF close return end @chunk_length = len.hex if @chunk_length == 0 @content_length = 0 @socket.gets(RS) return end timeout(@receive_timeout, ReceiveTimeoutError) do @socket.read(@chunk_length + 2, buf) end unless buf.empty? yield buf.slice(0, @chunk_length) end end end def read_body_rest if @readbuf and @readbuf.bytesize > 0 yield @readbuf @readbuf = nil end while true buf = '' timeout(@receive_timeout, ReceiveTimeoutError) do begin @socket.readpartial(@read_block_size, buf) rescue EOFError buf = nil end end if buf && buf.bytesize > 0 yield buf else return end end end end end httpclient-2.3.3/lib/httpclient/cacert.p7s0000644000004100000410000034537212155433207020565 0ustar www-datawww-dataMIME-Version: 1.0 Content-Type: multipart/signed; protocol="application/x-pkcs7-signature"; micalg="sha1"; boundary="----5FCD663C81182946FE75D58C22A8C67A" This is an S/MIME signed message ------5FCD663C81182946FE75D58C22A8C67A -----BEGIN CERTIFICATE----- MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC +Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X 7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz 43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJTRTEU MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9vdDAeFw0wMDA1 MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYTAlNFMRQwEgYDVQQK EwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQIE5ldHdvcmsxIzAh BgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBSb290MIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoekn0e+EV+vhDTbYjx5eLfpMLXsDBwq xBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKkIhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G 87B4pfYOQnrjfxvM0PC3KP0q6p6zsLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i 2O+tCBGaKZnhqkRFmhJePp1tUvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8U WfyEk9mHfExUE+uf0S0R+Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c1 0cIDMzZbdSZtQvESa0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0G A1UdDgQWBBQ5lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0T AQH/BAUwAwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6Fr pGkwZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVhbGlm aWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2VhlRO6aQTv hsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxGGuoYQ992zPlm hpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx95dr6h+sNNVJn0J6X dgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKFYqa0p9m9N5xotS1WfbC3 P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVAwRv1cEWw3F369nJad9Jjzc9Y iQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQwdOUeqN48Jzd/g66ed8/wMLH/S5no xqE= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyODA2 MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCaxlCyfqXfaE0bfA+2l2h9LaaLl+lk hsmj76CGv2BlnEtUiMJIxUo5vxTjWVXlGbR0yLQFOVwWpeKVBeASrlmLojNoWBym 1BW32J/X3HGrfpq/m44zDyL9Hy7nBzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsW OqMFf6Dch9Wc/HKpoH145LcxVR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb 2xzTa5qGUwew76wGePiEmf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQko O3rHl+Ee5fSfwMCuJKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUw AwEB/zAdBgNVHQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAU AK3Zo/Z59m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB BQUAA4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOMIOAb LjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTIdGcL+oir oQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43gKd8hdIaC2y+C MMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j8uB9Gr784N/Xx6ds sPmuujz9dLQR6FgNgLzTqIA6me11zEZ7 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEc MBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1lcmljYSBP bmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyODA2 MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0Ft ZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJpY2EgT25saW5lIFJvb3Qg Q2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIP ADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssNt79Hc9PwVU3dxgz6sWYFas14tNwC 206B89enfHG8dWOgXeMHDEjsJcQDIPT/DjsS/5uN4cbVG7RtIuOx238hZK+GvFci KtZHgVdEglZTvYYUAQv8f3SkWq7xuhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2 JxhP7JsowtS013wMPgwr38oE18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9 BoInLRBYBbV4Bbkv2wxrkJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7e Xz8d3eOyG6ChKiMDbi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8B PeraunzgWGcXuVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67 Xnfn6KVuY8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEq Z8A9W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48ArO3 +L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124HhnAgMBAAGj YzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3OpaaEg5+31IqEj FNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNeeMA4GA1UdDwEB/wQE AwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypLM7PmG2tZTiLMubekJcmn xPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qftIe3RUavg6WXSIylvfEWK5t2 LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjRYwu5W1LtGLBDQiKmsXeu3mnFzccc obGlHBD7GL4acN3Bkku+KVqdPzW+5X1R+FXgJXUjhx5c3LqdsKyzadsXg8n33gy8 CNyRnqjQ1xU3c6U1uPx+xURABsPr+CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMe IjzCpjbdGe+n/BLzJsBZMYVMnNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMA DjMSW7yV5TKQqLPGbIOtd+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2F AjgQ5ANh1NolNscIWC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUX Om/9riW99XJZZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPb AZO1XB4Y3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQl Zvqz2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw RY8mkaKO/qk= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDpjCCAo6gAwIBAgIEAgAAvzANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJJ RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MS8wLQYD VQQDEyZCYWx0aW1vcmUgQ3liZXJUcnVzdCBDb2RlIFNpZ25pbmcgUm9vdDAeFw0w MDA1MTcxNDAxMDBaFw0yNTA1MTcyMzU5MDBaMGcxCzAJBgNVBAYTAklFMRIwEAYD VQQKEwlCYWx0aW1vcmUxEzARBgNVBAsTCkN5YmVyVHJ1c3QxLzAtBgNVBAMTJkJh bHRpbW9yZSBDeWJlclRydXN0IENvZGUgU2lnbmluZyBSb290MIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyHGaGBKOetv5mvxBr9jy9AmOrT/+Zzc82skm ULGxPsvoTnMA8rLc88VG+wnvGJbOp+CchF0gDnqgqjaL+ii2eC6z7OhH8wTwkCO0 6q/lU7gF90ddK4bxp6TGOzW20g1SQdf0knXhogpQVoe+lwt7M4UQuSgY7jPqSBHX W5FHdiLU7s9d56hOHJ2Wkd2cvXQJqHJhqrAhOvE9LANWCdLB3MO1x1Q3q+YmorJG cXPKEYjuvOdk99ARGnNAWshJLA+375B/aIAEOAsbDzvU9aCzwo7hNLSAmW2edtSS KUCxldI3pGcSf+Biu641xZk2gkS45ngYM2Fxk1stjZ94lYLrbQIDAQABo1owWDAT BgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUyEE0XBUVBOVA8tGrmm8kknqH QlowEgYDVR0TAQH/BAgwBgEB/wIBAzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN AQEFBQADggEBAFJ0qpVLIozHPZak/l36L7W86/AL6VY4HdFtDaG8aIvwxYClJDT9 8pYYEYahNvU351RA1WQfw19wQmstOceeUgXO52py0o1yP0dQg6vHjSXJsOOnUxaV pmpT6hidj3ipd3ca+bSXR1mIJyi1yuEu1z4Oog24IkQD49FjsEE6ofWkLfd2HgRU mXgyQNcrfE26ppyweW4Hvozs7tc4aVvBDFZon/7r0eHIiPnyzX++hbREZwBQPvQm A2Tqd33oXj4cN0fI1uqk8zY8l8I5cgWUGSXD1zdBD8Efh4r9qr7psWRX5NuSoc/h Seg7H5ETWsOP2SVYSYBHD8YDrqzjv7fAqio= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR 5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s +12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 +HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF 5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ d0jQ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq 7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p 26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi 1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu tGWaIZDgqtCYvDi1czyL+Nw= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r 6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z 09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 l7+ijrRU -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDlTCCAn2gAwIBAgIRAL9c27byHG7ATet6Ajs26HkwDQYJKoZIhvcNAQEFBQAw PjELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRwwGgYDVQQDExNDbGFz cyAzUCBQcmltYXJ5IENBMB4XDTk5MDcwNzE3MTAwMFoXDTE5MDcwNjIzNTk1OVow PjELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRwwGgYDVQQDExNDbGFz cyAzUCBQcmltYXJ5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA qzf/62CbQXhp9UlYsN4fcWmmK+OuUMapvJPpIL7kxBOCVu/wQzIJypt1A498T+Hg T3aeC61kehQ6mp2/LxYLZRyp7py84xply0+F6pJWdWbWVUDv+8zWOD+rHO9CjRmJ 9reVhsKnHen3KfEq2WV5/Cv1jsoad36e6Kz5Zr9F++gTnV+2c+V9e477EnRdHwZe hRumXhhEALq8027RUg4GrevutbTBu7zrOA9IIpHHb9K4cju6f8CNbLe8R3MhKoX/ rNYoohnVl2o6uaxtRezmTcPbqF3FXYKYrEpaquYrCAwQdLxi9jpJBGbYURwmpth1 n5y/rmBRPVy8ok97iWfNUwIDAQABo4GNMIGKMA8GA1UdEwQIMAYBAf8CAQowCwYD VR0PBAQDAgEGMB0GA1UdDgQWBBSG4eGBcb9qEvEK8gHkyPtAzmiAiTARBglghkgB hvhCAQEEBAMCAAEwOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL3d3dy5jZXJ0cGx1 cy5jb20vQ1JML2NsYXNzM1AuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQAlquEiQMKk gDy3ol2ZjR96QjU4ZhcR372v/BURmBkz5gVChFSoS+uwnds32hZSQBF0aL/pybIQ hLcdRAB5Jxz1WAYXGDI1tjCXY8amORvI7kYXYsUu5wqjmoowY3OqFKVNCqhyk/BJ ERCQfBh9qCAFxMJ6NbocWgrgLnjIiLHPVwHsPeIGEzTAqNz6gIAF7gV2vZ0ryJ1Q b2vFQFCE/V0d5pCcENOkxrkoGt61+Apwqs7eUD0DgNvYiMVIBuQDc90WzjbW5Zvq d9qylrVlpwRdI673k7JeilFkX9rPjD1BW975o+kqfEcQH/YyPH5w6d+h1S4NsRpF tLwS7SgX6R4C -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs 6GAqm4VKQPNriiTsBhYscw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI 03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe 3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl 6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU Cm26OWMohpLzGITY+9HPBVZkVw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe +o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt 43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg 06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep +OkuE6N36B9K -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH 4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er fF6adulZkMV8gzURZVE= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi 94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP 9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m 0vdXcDazv/wor3ElhVsT/h5/WrQ8 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v 1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN 95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd 2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+ WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN /Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc 58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/ o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv 8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU 1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV 5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz rD6ogRLQy7rQkgu2npaqBA+K -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz +uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn 5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G spki4cErx5z481+oghLrGREt -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl 4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB /wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG 9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp 1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE 38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG 3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO 291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK 6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH WD9f -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h /t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf ReYNnyicsbkqWletNw+vHX/bvZ8= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDtjCCAp6gAwIBAgICAbYwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVVMx GDAWBgNVBAoTD0dURSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RFIEN5YmVyVHJ1 c3QgU29sdXRpb25zLCBJbmMuMR4wHAYDVQQDExVHVEUgQ3liZXJUcnVzdCBSb290 IDUwHhcNOTgwODE0MTQ1MDAwWhcNMTMwODE0MjM1OTAwWjBwMQswCQYDVQQGEwJV UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU cnVzdCBTb2x1dGlvbnMsIEluYy4xHjAcBgNVBAMTFUdURSBDeWJlclRydXN0IFJv b3QgNTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALwSbj+KfHqXAewe uzlaAvR4RKJIG457SVJ6uHtHs6+Um2+7lvoramVcuByUc76/iQoigO5X/IwFu3Cf lzkE2qOHXKjlyq/AM5rVN1xLrOSA0KYjYPv9ci6UncfOwgQy73hgXe2thw9FZR48 mgqavl0dmezn8tHGehfZrZtUln/EfGC/haoVNR1A2hG87FQhKC0joajwzy3N3fx+ D17hZQdWywe00lboXjHMGGPEhtIthc+Tkqtt/mg5+95zvYb45EZ66p8My/QZ/mO8 0Sx7iDM29uThnAxTgWAc2i6rlqkWiBNQmbK9Vd8VMH7o5Zj7cH5stQf8/Ea30O03 ln4y/iECAwEAAaNaMFgwEgYDVR0TAQH/BAgwBgEB/wIBBTAOBgNVHQ8BAf8EBAMC AQYwFwYDVR0gBBAwDjAMBgoqhkiG+GMBAgEDMBkGA1UdDgQSBBB2CkkhOEyf3vjE ScdxcZGdMA0GCSqGSIb3DQEBBQUAA4IBAQBBOtQYW9q43iEc4Y4J5fFoNP/elvQH 9ac886xKsZv6kvqb7eYyIapKdsXcTzjl39WG5NXIdn2Y17HNj021kSNsi4rr6nzv FJTExvAfSi0ycWMrY5EmAgm2gB3t4sy4f9uHY8jh0GwmsTUdQGYQG82VVBgzYewT T9oT95mvPtDPjqZyorPDBZrJJ32SzH5SjbOrcG2eiZ9N6xp1wpiq1QIW1wyKvyXk 6y28mOlYOBl8uTf+2+KZCHMGx5eDan0QAS8yuRcFSmXmL86+XlOmgumaUwqEdC2D ysiUFnZflGEo8IWnObvXi9moshMdVAk0JH0ggX1mfqKQdFwQxr3sqxvC -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4 04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9 3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIID5TCCAs2gAwIBAgISESG8J2xVR69YTu/UztYpsqKFMA0GCSqGSIb3DQEBCwUA MEwxCzAJBgNVBAYTAkZSMRIwEAYDVQQKEwlLRVlORUNUSVMxDTALBgNVBAsTBFJP T1QxGjAYBgNVBAMTEUtFWU5FQ1RJUyBST09UIENBMB4XDTA5MDUyNjAwMDAwMFoX DTIwMDUyNjAwMDAwMFowTDELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUtFWU5FQ1RJ UzENMAsGA1UECxMEUk9PVDEaMBgGA1UEAxMRS0VZTkVDVElTIFJPT1QgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/bMXhaGtJhuVaTUhPaSI+t7b YDZAF2nCFGP7uNnCdBU3LpzQIM1pjYQyooVMFLSb8iWzVCqDPy2+D/M7ZNH/oFDv d087TuE/C2SFmrpYftLDYtNkJaLUspc8d11jKjOS/M2CDZtUlYf1teuMzVvRyjAv yYhGtc0NEbQYj+7RoT5dFegoz9/DkJtszNEMRXezOuuKkB3pr2RqiXupPUN0+uRn IqH73E3E9WLJyiW0yYBgM6nde6ACv5YlCl7JXyl7tBeBi22BGdDZg1wFj0FpGmlD gJ+or+DpjJGLJyuiJmDND/KkowKDjhiBwheKQxX5bfMdEKRanERhIyF62PvRAgMB AAGjgcAwgb0wEgYDVR0TAQH/BAgwBgEB/wIBBDAOBgNVHQ8BAf8EBAMCAQYwVwYD VR0fBFAwTjBMoEqgSIZGaHR0cDovL3RydXN0Y2VudGVyLWNybC5jZXJ0aWZpY2F0 Mi5jb20vS2V5bmVjdGlzL0tFWU5FQ1RJU19ST09UX0NBLmNybDAdBgNVHQ4EFgQU 77cjl9CokX+mz6YhwDSfzHdB4dAwHwYDVR0jBBgwFoAU77cjl9CokX+mz6YhwDSf zHdB4dAwDQYJKoZIhvcNAQELBQADggEBABoxaZlCwuVAhaKfksNj1I8hOagZIf56 /MNNQPMr6EusW0xZk8bcfguvfF+VhWu9x2+6wb74xjpnS5PGBWk+JC3wG5HGPj/s QhiTbAMkim75IGcrfG2rNMkqIjMN132P7tI2ZELINZpuGWHLjWfwaKfQJAXmwxe6 Ra58Q7WAeANNIHMF/EMQnTVpQnWUJYIrpjuQGN7Bqa/zLZW/lafPGJfhWeKirxoW YQ33E3FTkzf9PK8AHWyLFK9Gloy2UnzMLU7N4elLCu6a/nqY5ym6G9ocutxrzQQO JkCp63M8/lCoESdVvduOS+9PGO0V/72GmGbumiVxNGxQ8bJRy2adTSk= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp +ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og /zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y 4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza 8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB 4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd 8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A 4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd +LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B 4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK 4SVhM7JZG+Ju1zdXtg2pEto= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK SnQ2+Q== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDEl MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMh U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIz MloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09N IFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNlY3VyaXR5IENvbW11 bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC ggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSE RMqm4miO/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gO zXppFodEtZDkBp2uoQSXWHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5 bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4zZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDF MxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4bepJz11sS6/vmsJWXMY1 VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK9U2vP9eC OKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0G CSqGSIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HW tWS3irO4G8za+6xmiEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZ q51ihPZRwSzJIxXYKLerJRO1RuGGAv8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDb EJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnWmHyojf6GPgcWkuF75x3sM3Z+ Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEWT1MKZPlO9L9O VL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy 1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNTIyMjM0OFoXDTE5MDYy NTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs YXNzIDEgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9Y LqdUHAZu9OqNSLwxlBfw8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+ TunRf50sQB1ZaG6m+FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8Y TfwggtFzVXSNdnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0 LBwGlN+VYH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLW I8sogTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDIDCCAgigAwIBAgIBJDANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MxIENBMB4XDTAx MDQwNjEwNDkxM1oXDTIxMDQwNjEwNDkxM1owOTELMAkGA1UEBhMCRkkxDzANBgNV BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMSBDQTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBALWJHytPZwp5/8Ue+H887dF+2rDNbS82rDTG 29lkFwhjMDMiikzujrsPDUJVyZ0upe/3p4zDq7mXy47vPxVnqIJyY1MPQYx9EJUk oVqlBvqSV536pQHydekfvFYmUk54GWVYVQNYwBSujHxVX3BbdyMGNpfzJLWaRpXk 3w0LBUXl0fIdgrvGE+D+qnr9aTCU89JFhfzyMlsy3uhsXR/LpCJ0sICOXZT3BgBL qdReLjVQCfOAl/QMF6452F/NM8EcyonCIvdFEu1eEpOdY6uCLrnrQkFEy0oaAIIN nvmLVz5MxxftLItyM19yejhW1ebZrgUaHXVFsculJRwSVzb9IjcCAwEAAaMzMDEw DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQIR+IMi/ZTiFIwCwYDVR0PBAQDAgEG MA0GCSqGSIb3DQEBBQUAA4IBAQCLGrLJXWG04bkruVPRsoWdd44W7hE928Jj2VuX ZfsSZ9gqXLar5V7DtxYvyOirHYr9qxp81V9jz9yw3Xe5qObSIjiHBxTZ/75Wtf0H DjxVyhbMp6Z3N/vbXB9OWQaHowND9Rart4S9Tu+fMTfwRvFAttEMpWT4Y14h21VO TzF2nBBhjrZTOqMRvq9tfB69ri3iDGnHhVNoomG6xT60eVR4ngrHAr5i0RGCS2Uv kVrCqIexVmiUefkl98HVrhq4uz2PqYo4Ffdz0Fpg0YCw8NzVUM1O7pJIae2yIx4w zMiUyLb1O4Z/P6Yun/Y+LLWSlj7fLJOK/4GMDw9ZIRlXvVWa -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt 5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s 3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu 8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ 3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf 8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN +lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA 1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c 6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn 8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a 77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+ 6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl +zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0 ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0 uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+ FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7 jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/ u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1 puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH 6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ 2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDPDCCAqWgAwIBAgIQEj3w59oqIkekOIngiu7JZzANBgkqhkiG9w0BAQUFADCB 0TELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ Q2FwZSBUb3duMRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMf Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3Rl IFBlcnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIxMDEwMTIzNTk1 OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNV BAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1Ro YXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29u YWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC gYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Z hx2G6qPduc6WZBrCFG5ErHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56 fAylS1V/Bhkpf56aJtVquzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYD VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQAemGDU5fJUYLA9GoFkR/db o9lvwykLp9KpgUn2w22FFChFRAH0cVyVLhQPGivRqWvBX2c9FvFyIK++FsoOMF/J y6WTLMNnVB5yIoojdmyUHVFSbJ3E4EcC18y/8IB7GG4l3GJh1qb+wR1/2bP9jVxF EFrGZWSa6yz1A0/WSGL7Lg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDNjCCAp+gAwIBAgIQNhIilsXjOKUgodJfTNcJVDANBgkqhkiG9w0BAQUFADCB zjELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhh d3RlIFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIxMDEwMTIzNTk1OVow gc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcT CUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNV BAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRo YXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1z ZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2 aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560 ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j +ao6hnO2RlNYyIkFvYMRuHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/ BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQBlkKyID1bZ5jA01CbH0FDxkt5r1DmI CSLGpmODA/eZd9iy5Ri4XWPz1HP7bJyZePFLeH0ZJMMrAoT4vCLZiiLXoPxx7JGH IPG47LHlVYCsPVLIOQ7C8MAFT9aCdYy9X9LcdpoFEsmvcsPcJX6kTY4XpeCHf+Ga WuFg3GQjPEIuTQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA 2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu MdRAGmI0Nj81Aa6sY6A= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta 3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk 6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 /qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 jVaMaA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDIjCCAougAwIBAgIQNKT/9jCvTKU8MxdCoZRmdTANBgkqhkiG9w0BAQUFADCB xDELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJ Q2FwZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhh d3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0 ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjEwMTAxMjM1OTU5WjCBxDELMAkGA1UE BhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlm aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZl ciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8w DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl /Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF /rFrKbYvScg71CcEJRCXL+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982 OsK1ZiIS1ocNAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEF BQADgYEAvkBpQW/G28GnvwfAReTQtUMeTJUzNelewj4o9qgNUNX/4gwP/FACjq6R ua00io2fJ3GqGcxL6ATK1BdrEhrWxl/WzV7/iXa/2EjYWb0IiokdV81FHlK6EpqE +hiJX+j5MDVqAWC5mYCDhQpu2vTJj15zLTFKY6B08h+LItIpPus= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjEL MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYwMTEyMTQzODQzWhcNMjUxMjMxMjI1 OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UEAxMc VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jf tMjWQ+nEdVl//OEd+DFwIxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKg uNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2J XjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQXa7pIXSSTYtZgo+U4+lK 8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7uSNQZu+99 5OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1Ud EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3 kUrL84J6E1wIqzCB7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRy dXN0Y2VudGVyLmRlL2NybC92Mi90Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6 Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBUcnVzdENlbnRlciUyMENsYXNz JTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21iSCxPVT1yb290 Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iS GNn3Bzn1LL4GdXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprt ZjluS5TmVfwLG4t3wVMTZonZKNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8 au0WOB9/WIFaGusyiC2y8zl3gK9etmF1KdsjTYjKUCjLhdLTEKJZbtOTVAB6okaV hgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kPJOzHdiEoZa5X6AeI dUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfkvQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDtjCCAp6gAwIBAgIOBcAAAQACQdAGCk3OdRAwDQYJKoZIhvcNAQEFBQAwdjEL MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNV BAsTGVRDIFRydXN0Q2VudGVyIENsYXNzIDQgQ0ExJTAjBgNVBAMTHFRDIFRydXN0 Q2VudGVyIENsYXNzIDQgQ0EgSUkwHhcNMDYwMzIzMTQxMDIzWhcNMjUxMjMxMjI1 OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIgR21i SDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgNCBDQTElMCMGA1UEAxMc VEMgVHJ1c3RDZW50ZXIgQ2xhc3MgNCBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALXNTJytrlG7fEjFDSmGehSt2VA9CXIgDRS2Y8b+WJ7gIV7z jyIZ3E6RIM1viCmis8GsKnK6i1S4QF/yqvhDhsIwXMynXX/GCEnkDjkvjhjWkd0j FnmA22xIHbzB3ygQY9GB493fL3l1oht48pQB5hBiecugfQLANIJ7x8CtHUzXapZ2 W78mhEj9h/aECqqSB5lIPGG8ToVYx5ct/YFKocabEvVCUNFkPologiJw3fX64yhC L04y87OjNopq1mJcrPoBbbTgci6VaLTxkwzGioLSHVPqfOA/QrcSWrjN2qUGZ8uh d32llvCSHmcOHUJG5vnt+0dTf1cERh9GX8eu4I8CAwEAAaNCMEAwDwYDVR0TAQH/ BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFB/quz4lGwa9pd1iBX7G TFq/6A9DMA0GCSqGSIb3DQEBBQUAA4IBAQBYpCubTPfkpJKknGWYGWIi/HIy6QRd xMRwLVpG3kxHiiW5ot3u6hKvSI3vK2fbO8w0mCr3CEf/Iq978fTr4jgCMxh1KBue dmWsiANy8jhHHYz1nwqIUxAUu4DlDLNdjRfuHhkcho0UZ3iMksseIUn3f9MYv5x5 +F0IebWqak2SNmy8eesOPXmK2PajVnBd3ttPedJ60pVchidlvqDTB4FAVd0Qy+BL iILAkH0457+W4Ze6mqtCD9Of2J4VMxHL94J59bXAQVaS4d9VA61Iz9PyLrHHLVZM ZHQqMc7cdalUR6SnQnIJ5+ECpkeyBM1CE+FhDOB4OiIgohxgQoaH96Xm -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTEL MAkGA1UEBhMCREUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNV BAsTG1RDIFRydXN0Q2VudGVyIFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1 c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcNMDYwMzIyMTU1NDI4WhcNMjUxMjMx MjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1c3RDZW50ZXIg R21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYwJAYD VQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSR JJZ4Hgmgm5qVSkr1YnwCqMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3T fCZdzHd55yx4Oagmcw6iXSVphU9VDprvxrlE4Vc93x9UIuVvZaozhDrzznq+VZeu jRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtwag+1m7Z3W0hZneTvWq3z wZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9OgdwZu5GQ fezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYD VR0jBBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAO BgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0G CSqGSIb3DQEBBQUAA4IBAQAo0uCG1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X1 7caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/CyvwbZ71q+s2IhtNerNXxTPqYn 8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3ghUJGooWMNjs ydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/ 2TYcuiUaUj0a7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi 1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN 9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP BSeOE6Fuwg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN 8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ 1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT 91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p TpPDpFQUWw== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK 4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv 2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 mfnGV/TJVTl4uix5yaaIK/QI -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM 1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws 6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u 7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn 0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM //bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t 3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEZjCCA06gAwIBAgIQRL4Mi1AAJLQR0zYt4LNfGzANBgkqhkiG9w0BAQUFADCB lTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHTAbBgNVBAMTFFVUTi1VU0VSRmlyc3Qt T2JqZWN0MB4XDTk5MDcwOTE4MzEyMFoXDTE5MDcwOTE4NDAzNlowgZUxCzAJBgNV BAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAc BgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3 dy51c2VydHJ1c3QuY29tMR0wGwYDVQQDExRVVE4tVVNFUkZpcnN0LU9iamVjdDCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6qgT+jo2F4qjEAVZURnicP HxzfOpuCaDDASmEd8S8O+r5596Uj71VRloTN2+O5bj4x2AogZ8f02b+U60cEPgLO KqJdhwQJ9jCdGIqXsqoc/EHSoTbL+z2RuufZcDX65OeQw5ujm9M89RKZd7G3CeBo 5hy485RjiGpq/gt2yb70IuRnuasaXnfBhQfdDWy/7gbHd2pBnqcP1/vulBe3/IW+ pKvEHDHd17bR5PDv3xaPslKT16HUiaEHLr/hARJCHhrh2JU022R5KP+6LhHC5ehb kkj7RwvCbNqtMoNB86XlQXD9ZZBt+vpRxPm9lisZBCzTbafc8H9vg2XiaquHhnUC AwEAAaOBrzCBrDALBgNVHQ8EBAMCAcYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E FgQU2u1kdBScFDyr3ZmpvVsoTYs8ydgwQgYDVR0fBDswOTA3oDWgM4YxaHR0cDov L2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtT2JqZWN0LmNybDApBgNV HSUEIjAgBggrBgEFBQcDAwYIKwYBBQUHAwgGCisGAQQBgjcKAwQwDQYJKoZIhvcN AQEFBQADggEBAAgfUrE3RHjb/c652pWWmKpVZIC1WkDdIaXFwfNfLEzIR1pp6ujw NTX00CXzyKakh0q9G7FzCL3Uw8q2NbtZhncxzaeAFK4T7/yxSPlrJSUtUbYsbUXB mMiKVl0+7kNOPmsnjtA6S4ULX9Ptaqd1y9Fahy85dRNacrACgZ++8A+EVCBibGnU 4U3GDZlDAQ0Slox4nb9QorFEqmrPF3rPbw/U+CRVX/A0FklmPlBGyWNxODFiuGK5 81OtbLUrohKqGU8J2l7nk8aOFAj+8DCAGKCGhU3IfdeLA/5u1fedFqySLKAj5ZyR Uh+U3xeUc8OzwcFxBSAAeL0TUh2oPs0AH8g= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0 IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9 WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICPDCCAaUCED9pHoGc8JpK83P/uUii5N0wDQYJKoZIhvcNAQEFBQAwXzELMAkG A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz cyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcmlt YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN ADCBiQKBgQDlGb9to1ZhLZlIcfZn3rmN67eehoAKkQ76OCWvRoiC5XOooJskXQ0f zGVuDLDQVoQYh5oGmxChc9+0WDlrbsH2FdWoqD+qEgaNMax/sDTXjzRniAnNFBHi TkVWaR94AoDa3EeRKbs2yWNcxeDXLYd7obcysHswuiovMaruo2fa2wIDAQABMA0G CSqGSIb3DQEBBQUAA4GBAFgVKTk8d6PaXCUDfGD67gmZPCcQcMgMCeazh88K4hiW NWLMv5sneYlfycQJ9M61Hd8qveXbhpxoJeUwfLaJFf5n0a3hUKw8fGJLj7qE1xIV Gx/KXQ/BUpQqEZnae88MNhPVNdwQGVnqlMEAv3WP2fr9dgTbYruQagPZRjXZ+Hxb -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68 DzFc6PLZ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO 8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn jBJ7xUS0rg== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u 7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i 2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ 2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5 MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4 pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0 13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY oJ2daZH9 -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te 2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC /Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC 4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y 5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ 4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCWkEx FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAd BgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcwMTAxMDAwMDAwWhcN MjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4g Q2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsG A1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1l c3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYrWHhhRYZT 6jR7UZztsOYuGA7+4F+oJ9O0yeB8WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQa Wt9MevPZQx08EHp5JduQ/vBR5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL 8vg7ij5FrHGSALSQQZj7X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMB Af8wDQYJKoZIhvcNAQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC 9RAIDb/LogWK0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQ pgCed/r8zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZ CayJSdM= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF 9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN /BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz 4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 7M2CYfE45k+XmCpajQ== -----END CERTIFICATE----- ------5FCD663C81182946FE75D58C22A8C67A Content-Type: application/x-pkcs7-signature; name="smime.p7s" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="smime.p7s" MIIGiwYJKoZIhvcNAQcCoIIGfDCCBngCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3 DQEHAaCCBAEwggP9MIIC5aADAgECAgEBMA0GCSqGSIb3DQEBDQUAMEsxCzAJBgNV BAYTAkpQMREwDwYDVQQKDAhjdG9yLm9yZzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQx EzARBgNVBAMMCmh0dHBjbGllbnQwHhcNMDkwNTIxMTIzOTA1WhcNMzcxMjMxMjM1 OTU5WjBLMQswCQYDVQQGEwJKUDERMA8GA1UECgwIY3Rvci5vcmcxFDASBgNVBAsM C0RldmVsb3BtZW50MRMwEQYDVQQDDApodHRwY2xpZW50MIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAzY+WR1Mf3vO8geg8gyPzvCc2+mohBQPsv+GXKw7R cGyZH9dlR5RkoHXQlCoxe+JalOdT0pTrEZPHcQjjB93Z2kvAf93h3srgueBbWRju QmVl4mFqSegbNDjRFIj1aRDRH0o4Tzzx/s6RMFp9wvxTJYVUgyFAHDZ32I2Xglzr 1CiNrIIOhJddtsQ/SfzKLsfAhTdEW5foO0yk7Hj/UrtnTTZxw4Yncn2QeB4NBJC0 XjZxBgLuqPbrsj85CCcHvJzFMI/QTtt1dCD7OqyeCuFqg2f/4j4Kg7ucPi3i5D4H hqAAuHg8/GxtAP0wIv5W6s0Yti/QdV35mUtuWl10L08tOwIDAQABo4HrMIHoMA8G A1UdEwEB/wQFMAMBAf8wMQYJYIZIAYb4QgENBCQWIlJ1YnkvT3BlblNTTCBHZW5l cmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFECcHpeUyg5ybsdUDD4lbFjbTk8d MA4GA1UdDwEB/wQEAwIBBjBzBgNVHSMEbDBqgBRAnB6XlMoOcm7HVAw+JWxY205P HaFPpE0wSzELMAkGA1UEBhMCSlAxETAPBgNVBAoMCGN0b3Iub3JnMRQwEgYDVQQL DAtEZXZlbG9wbWVudDETMBEGA1UEAwwKaHR0cGNsaWVudIIBATANBgkqhkiG9w0B AQ0FAAOCAQEAFUV6nJsPleqwGc6f+gMe8rTEA8wXgCnge3XIxAs5uDylpWEA1J2o h68+UKv+Y8sNGNzL4nV8KcTqUJ+x8t1OACOKuq3ymb3Y1xzGYnyFOYx3ctYLWf55 hZqiWpMJVEwGl7xRj16fJesWi50eWv16mvXwToCzpWsJJ+lcVmQP1YdVJanOQHeo BO7mpwMLMIdsjTvnkx2b/WEokIPiXr2Hcnc6FEgRQ8l8ec+8znC2LILZ1wT2K3AT /B1XMEl4gFclCb5CP4rypdxFPNlkYgGPDafjrEmBbpidw0IdT9ZZ80JjB2SeAdL9 o3whsSOELB++JcWKxWsM3/6llkYx/rtlpjGCAlIwggJOAgEBMFAwSzELMAkGA1UE BhMCSlAxETAPBgNVBAoMCGN0b3Iub3JnMRQwEgYDVQQLDAtEZXZlbG9wbWVudDET MBEGA1UEAwwKaHR0cGNsaWVudAIBATAJBgUrDgMCGgUAoIHYMBgGCSqGSIb3DQEJ AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTEzMDEwMTAyNDUyNlowIwYJ KoZIhvcNAQkEMRYEFJckqjbw92vWqUryx9P8VUwbej47MHkGCSqGSIb3DQEJDzFs MGowCwYJYIZIAWUDBAEqMAsGCWCGSAFlAwQBFjALBglghkgBZQMEAQIwCgYIKoZI hvcNAwcwDgYIKoZIhvcNAwICAgCAMA0GCCqGSIb3DQMCAgFAMAcGBSsOAwIHMA0G CCqGSIb3DQMCAgEoMA0GCSqGSIb3DQEBAQUABIIBAMm/55qbk5tKufy4fxd9po/B 1Fv45SvkTCWo0C2l6t1ZrqfkJNLLjSVP8CKAt4NVIo34RjEiQpfLtYbKu1yQlW9N tcfode1vgS8ECRvFzNQAlBe5Ei9ypLdH+0sz6tEWX70W+fOSYqtJjghrbVvxcxjx B1FdzJkFFV+woKVtS/oFGzv+daL49JqZe1I+LB9hsNf6Z8rcVu2NI4DCnT8yD6HT i3jvUxGQSJx4w3oZQW0wXEZB89c0tKCACrYZkNVYIerRkmz5oH/F6NcRicDR6aSg 4fskz2tsbOp2tB4oVcQIWsheDeoXJQMpcejrh+9+LmP5hO4iZ3SzWghylLcb6u4= ------5FCD663C81182946FE75D58C22A8C67A-- httpclient-2.3.3/lib/httpclient/version.rb0000644000004100000410000000005112155433207020661 0ustar www-datawww-dataclass HTTPClient VERSION = '2.3.3' end httpclient-2.3.3/lib/httpclient/connection.rb0000644000004100000410000000422012155433207021335 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. class HTTPClient # Represents a HTTP response to an asynchronous request. Async methods of # HTTPClient such as get_async, post_async, etc. returns an instance of # Connection. # # == How to use # # 1. Invoke HTTP method asynchronously and check if it's been finished # periodically. # # connection = clnt.post_async(url, body) # print 'posting.' # while true # break if connection.finished? # print '.' # sleep 1 # end # puts '.' # res = connection.pop # p res.status # # 2. Read the response as an IO. # # connection = clnt.get_async('http://dev.ctor.org/') # io = connection.pop.content # while str = io.read(40) # p str # end class Connection attr_accessor :async_thread def initialize(header_queue = [], body_queue = []) # :nodoc: @headers = header_queue @body = body_queue @async_thread = nil @queue = Queue.new end # Checks if the asynchronous invocation has been finished or not. def finished? if !@async_thread # Not in async mode. true elsif @async_thread.alive? # Working... false else # Async thread have been finished. join true end end # Retrieves a HTTP::Message instance of HTTP response. Do not invoke this # method twice for now. The second invocation will be blocked. def pop response_or_exception = @queue.pop if response_or_exception.is_a? Exception raise response_or_exception end response_or_exception end def push(result) # :nodoc: @queue.push(result) end # Waits the completion of the asynchronous invocation. def join if @async_thread @async_thread.join end nil end end end httpclient-2.3.3/lib/httpclient/timeout.rb0000644000004100000410000000664512155433207020701 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. require 'timeout' require 'thread' class HTTPClient # Replaces timeout.rb to avoid Thread creation and scheduling overhead. # # You should check another timeout replace in WEBrick. # See lib/webrick/utils.rb in ruby/1.9. # # About this implementation: # * Do not create Thread for each timeout() call. Just create 1 Thread for # timeout scheduler. # * Do not wakeup the scheduler thread so often. Let scheduler thread sleep # until the nearest period. if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9' class TimeoutScheduler # Represents timeout period. class Period attr_reader :thread, :time # Creates new Period. def initialize(thread, time, ex) @thread, @time, @ex = thread, time, ex @lock = Mutex.new end # Raises if thread exists and alive. def raise(message) @lock.synchronize do if @thread and @thread.alive? @thread.raise(@ex, message) end end end # Cancel this Period. Mutex is needed to avoid too-late exception. def cancel @lock.synchronize do @thread = nil end end end # Creates new TimeoutScheduler. def initialize @pool = {} @next = nil @thread = start_timer_thread end # Registers new timeout period. def register(thread, sec, ex) period = Period.new(thread, Time.now + sec, ex || ::Timeout::Error) @pool[period] = true if @next.nil? or period.time < @next begin @thread.wakeup rescue ThreadError # Thread may be dead by fork. @thread = start_timer_thread end end period end # Cancels the given period. def cancel(period) @pool.delete(period) period.cancel end private def start_timer_thread thread = Thread.new { while true if @pool.empty? @next = nil sleep else min, = @pool.min { |a, b| a[0].time <=> b[0].time } @next = min.time sec = @next - Time.now if sec > 0 sleep(sec) end end now = Time.now @pool.keys.each do |period| if period.time < now period.raise('execution expired') cancel(period) end end end } Thread.pass while thread.status != 'sleep' thread end end class << self # CAUTION: caller must aware of race condition. def timeout_scheduler @timeout_scheduler ||= TimeoutScheduler.new end end timeout_scheduler # initialize at first time. end module Timeout if !defined?(JRUBY_VERSION) and RUBY_VERSION < '1.9' def timeout(sec, ex = nil, &block) return yield if sec == nil or sec.zero? scheduler = nil begin scheduler = HTTPClient.timeout_scheduler period = scheduler.register(Thread.current, sec, ex) yield(sec) ensure scheduler.cancel(period) if scheduler and period end end end end end httpclient-2.3.3/lib/httpclient/http.rb0000644000004100000410000007316112155433207020167 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. require 'time' if defined?(Encoding::ASCII_8BIT) require 'open-uri' # for encoding end # A namespace module for HTTP Message definitions used by HTTPClient. module HTTP # Represents HTTP response status code. Defines constants for HTTP response # and some conditional methods. module Status OK = 200 CREATED = 201 ACCEPTED = 202 NON_AUTHORITATIVE_INFORMATION = 203 NO_CONTENT = 204 RESET_CONTENT = 205 PARTIAL_CONTENT = 206 MOVED_PERMANENTLY = 301 FOUND = 302 SEE_OTHER = 303 TEMPORARY_REDIRECT = MOVED_TEMPORARILY = 307 BAD_REQUEST = 400 UNAUTHORIZED = 401 PROXY_AUTHENTICATE_REQUIRED = 407 INTERNAL = 500 # Status codes for successful HTTP response. SUCCESSFUL_STATUS = [ OK, CREATED, ACCEPTED, NON_AUTHORITATIVE_INFORMATION, NO_CONTENT, RESET_CONTENT, PARTIAL_CONTENT ] # Status codes which is a redirect. REDIRECT_STATUS = [ MOVED_PERMANENTLY, FOUND, SEE_OTHER, TEMPORARY_REDIRECT, MOVED_TEMPORARILY ] # Returns true if the given status represents successful HTTP response. # See also SUCCESSFUL_STATUS. def self.successful?(status) SUCCESSFUL_STATUS.include?(status) end # Returns true if the given status is thought to be redirect. # See also REDIRECT_STATUS. def self.redirect?(status) REDIRECT_STATUS.include?(status) end end # Represents a HTTP message. A message is for a request or a response. # # Request message is generated from given parameters internally so users # don't need to care about it. Response message is the instance that # methods of HTTPClient returns so users need to know how to extract # HTTP response data from Message. # # Some attributes are only for a request or a response, not both. # # == How to use HTTP response message # # 1. Gets response message body. # # res = clnt.get(url) # p res.content #=> String # # 2. Gets response status code. # # res = clnt.get(url) # p res.status #=> 200, 501, etc. (Integer) # # 3. Gets response header. # # res = clnt.get(url) # res.header['set-cookie'].each do |value| # p value # end # assert_equal(1, res.header['last-modified'].size) # p res.header['last-modified'].first # class Message CRLF = "\r\n" # Represents HTTP message header. class Headers # HTTP version in a HTTP header. String. attr_accessor :http_version # Size of body. nil when size is unknown (e.g. chunked response). attr_reader :body_size # Request/Response is chunked or not. attr_accessor :chunked # Request only. Requested method. attr_reader :request_method # Request only. Requested URI. attr_accessor :request_uri # Request only. Requested query. attr_accessor :request_query # Request only. Requested via proxy or not. attr_accessor :request_absolute_uri # Response only. HTTP status attr_reader :status_code # Response only. HTTP status reason phrase. attr_accessor :reason_phrase # Used for dumping response. attr_accessor :body_type # :nodoc: # Used for dumping response. attr_accessor :body_charset # :nodoc: # Used for dumping response. attr_accessor :body_date # :nodoc: # Used for keeping content encoding. attr_reader :body_encoding # :nodoc: # HTTP response status code to reason phrase mapping definition. STATUS_CODE_MAP = { Status::OK => 'OK', Status::CREATED => "Created", Status::NON_AUTHORITATIVE_INFORMATION => "Non-Authoritative Information", Status::NO_CONTENT => "No Content", Status::RESET_CONTENT => "Reset Content", Status::PARTIAL_CONTENT => "Partial Content", Status::MOVED_PERMANENTLY => 'Moved Permanently', Status::FOUND => 'Found', Status::SEE_OTHER => 'See Other', Status::TEMPORARY_REDIRECT => 'Temporary Redirect', Status::MOVED_TEMPORARILY => 'Temporary Redirect', Status::BAD_REQUEST => 'Bad Request', Status::INTERNAL => 'Internal Server Error', } # $KCODE to charset mapping definition. CHARSET_MAP = { 'NONE' => 'us-ascii', 'EUC' => 'euc-jp', 'SJIS' => 'shift_jis', 'UTF8' => 'utf-8', } # Creates a Message::Headers. Use init_request, init_response, or # init_connect_request for acutual initialize. def initialize @http_version = '1.1' @body_size = nil @chunked = false @request_method = nil @request_uri = nil @request_query = nil @request_absolute_uri = nil @status_code = nil @reason_phrase = nil @body_type = nil @body_charset = nil @body_date = nil @body_encoding = nil @is_request = nil @header_item = [] @dumped = false end # Initialize this instance as a CONNECT request. def init_connect_request(uri) @is_request = true @request_method = 'CONNECT' @request_uri = uri @request_query = nil @http_version = '1.0' end # Placeholder URI object for nil uri. NIL_URI = HTTPClient::Util.urify('http://nil-uri-given/') # Initialize this instance as a general request. def init_request(method, uri, query = nil) @is_request = true @request_method = method @request_uri = uri || NIL_URI @request_query = query @request_absolute_uri = false end # Initialize this instance as a response. def init_response(status_code, req = nil) @is_request = false self.status_code = status_code if req @request_method = req.request_method @request_uri = req.request_uri @request_query = req.request_query end end # Sets status code and reason phrase. def status_code=(status_code) @status_code = status_code @reason_phrase = STATUS_CODE_MAP[@status_code] end # Returns 'Content-Type' header value. def content_type self['Content-Type'][0] end # Sets 'Content-Type' header value. Overrides if already exists. def content_type=(content_type) delete('Content-Type') self['Content-Type'] = content_type end alias contenttype content_type alias contenttype= content_type= if defined?(Encoding::ASCII_8BIT) def set_body_encoding if type = self.content_type OpenURI::Meta.init(o = '') o.meta_add_field('content-type', type) @body_encoding = o.encoding end end else def set_body_encoding @body_encoding = nil end end # Sets byte size of message body. # body_size == nil means that the body is_a? IO def body_size=(body_size) @body_size = body_size end # Dumps message header part and returns a dumped String. def dump set_header str = nil if @is_request str = request_line else str = response_status_line end str + @header_item.collect { |key, value| "#{ key }: #{ value }#{ CRLF }" }.join end # Set Date header def set_date_header set('Date', Time.now.httpdate) end # Adds a header. Addition order is preserved. def add(key, value) if value.is_a?(Array) value.each do |v| @header_item.push([key, v]) end else @header_item.push([key, value]) end end # Sets a header. def set(key, value) delete(key) add(key, value) end # Returns an Array of headers for the given key. Each element is a pair # of key and value. It returns an single element Array even if the only # one header exists. If nil key given, it returns all headers. def get(key = nil) if key.nil? all else key = key.upcase @header_item.find_all { |k, v| k.upcase == key } end end # Returns an Array of all headers. def all @header_item end # Deletes headers of the given key. def delete(key) key = key.upcase @header_item.delete_if { |k, v| k.upcase == key } end # Adds a header. See set. def []=(key, value) set(key, value) end # Returns an Array of header values for the given key. def [](key) get(key).collect { |item| item[1] } end def set_headers(headers) headers.each do |key, value| add(key, value) end set_body_encoding end def create_query_uri() if @request_method == 'CONNECT' return "#{@request_uri.host}:#{@request_uri.port}" end path = @request_uri.path path = '/' if path.nil? or path.empty? if query_str = create_query_part() path += "?#{query_str}" end path end def create_query_part() query_str = nil if @request_uri.query query_str = @request_uri.query end if @request_query if query_str query_str += "&#{Message.create_query_part_str(@request_query)}" else query_str = Message.create_query_part_str(@request_query) end end query_str end private def request_line path = create_query_uri() if @request_absolute_uri path = "#{ @request_uri.scheme }://#{ @request_uri.host }:#{ @request_uri.port }#{ path }" end "#{ @request_method } #{ path } HTTP/#{ @http_version }#{ CRLF }" end def response_status_line if defined?(Apache) "HTTP/#{ @http_version } #{ @status_code } #{ @reason_phrase }#{ CRLF }" else "Status: #{ @status_code } #{ @reason_phrase }#{ CRLF }" end end def set_header if @is_request set_request_header else set_response_header end end def set_request_header return if @dumped @dumped = true keep_alive = Message.keep_alive_enabled?(@http_version) if !keep_alive and @request_method != 'CONNECT' set('Connection', 'close') end if @chunked set('Transfer-Encoding', 'chunked') elsif @body_size and (keep_alive or @body_size != 0) set('Content-Length', @body_size.to_s) end if @http_version >= '1.1' and get('Host').empty? if @request_uri.port == @request_uri.default_port # GFE/1.3 dislikes default port number (returns 404) set('Host', "#{@request_uri.hostname}") else set('Host', "#{@request_uri.hostname}:#{@request_uri.port}") end end end def set_response_header return if @dumped @dumped = true if defined?(Apache) && self['Date'].empty? set_date_header end keep_alive = Message.keep_alive_enabled?(@http_version) if @chunked set('Transfer-Encoding', 'chunked') else if keep_alive or @body_size != 0 set('Content-Length', @body_size.to_s) end end if @body_date set('Last-Modified', @body_date.httpdate) end if self['Content-Type'].empty? set('Content-Type', "#{ @body_type || 'text/html' }; charset=#{ charset_label }") end end def charset_label # TODO: should handle response encoding for 1.9 correctly. if RUBY_VERSION > "1.9" CHARSET_MAP[@body_charset] || 'us-ascii' else CHARSET_MAP[@body_charset || $KCODE] || 'us-ascii' end end end # Represents HTTP message body. class Body # Size of body. nil when size is unknown (e.g. chunked response). attr_reader :size # maxbytes of IO#read for streaming request. See DEFAULT_CHUNK_SIZE. attr_accessor :chunk_size # Default value for chunk_size DEFAULT_CHUNK_SIZE = 1024 * 16 # Creates a Message::Body. Use init_request or init_response # for acutual initialize. def initialize @body = nil @size = nil @positions = nil @chunk_size = nil end # Initialize this instance as a request. def init_request(body = nil, boundary = nil) @boundary = boundary @positions = {} set_content(body, boundary) @chunk_size = DEFAULT_CHUNK_SIZE end # Initialize this instance as a response. def init_response(body = nil) @body = body if @body.respond_to?(:bytesize) @size = @body.bytesize elsif @body.respond_to?(:size) @size = @body.size else @size = nil end end # Dumps message body to given dev. # dev needs to respond to <<. # # Message header must be given as the first argument for performance # reason. (header is dumped to dev, too) # If no dev (the second argument) given, this method returns a dumped # String. def dump(header = '', dev = '') if @body.is_a?(Parts) dev << header @body.parts.each do |part| if Message.file?(part) reset_pos(part) dump_file(part, dev) else dev << part end end elsif Message.file?(@body) dev << header reset_pos(@body) dump_file(@body, dev) elsif @body dev << header + @body else dev << header end dev end # Dumps message body with chunked encoding to given dev. # dev needs to respond to <<. # # Message header must be given as the first argument for performance # reason. (header is dumped to dev, too) # If no dev (the second argument) given, this method returns a dumped # String. def dump_chunked(header = '', dev = '') dev << header if @body.is_a?(Parts) @body.parts.each do |part| if Message.file?(part) reset_pos(part) dump_chunks(part, dev) else dev << dump_chunk(part) end end dev << (dump_last_chunk + CRLF) elsif @body reset_pos(@body) dump_chunks(@body, dev) dev << (dump_last_chunk + CRLF) end dev end # Returns a message body itself. def content @body end private def set_content(body, boundary = nil) if Message.file?(body) # uses Transfer-Encoding: chunked if body does not respond to :size. # bear in mind that server may not support it. at least ruby's CGI doesn't. @body = body remember_pos(@body) @size = body.respond_to?(:size) ? body.size - body.pos : nil elsif boundary and Message.multiparam_query?(body) @body = build_query_multipart_str(body, boundary) @size = @body.size else @body = Message.create_query_part_str(body) @size = @body.bytesize end end def remember_pos(io) # IO may not support it (ex. IO.pipe) @positions[io] = io.pos if io.respond_to?(:pos) end def reset_pos(io) io.pos = @positions[io] if @positions.key?(io) end def dump_file(io, dev) buf = '' while !io.read(@chunk_size, buf).nil? dev << buf end end def dump_chunks(io, dev) buf = '' while !io.read(@chunk_size, buf).nil? dev << dump_chunk(buf) end end def dump_chunk(str) dump_chunk_size(str.bytesize) + (str + CRLF) end def dump_last_chunk dump_chunk_size(0) end def dump_chunk_size(size) sprintf("%x", size) + CRLF end class Parts attr_reader :size def initialize @body = [] @size = 0 @as_stream = false end def add(part) if Message.file?(part) @as_stream = true @body << part if part.respond_to?(:lstat) @size += part.lstat.size elsif part.respond_to?(:size) if sz = part.size @size += sz else @size = nil end else # use chunked upload @size = nil end elsif @body[-1].is_a?(String) @body[-1] += part.to_s @size += part.to_s.bytesize if @size else @body << part.to_s @size += part.to_s.bytesize if @size end end def parts if @as_stream @body else [@body.join] end end end def build_query_multipart_str(query, boundary) parts = Parts.new query.each do |attr, value| headers = ["--#{boundary}"] if Message.file?(value) remember_pos(value) param_str = params_from_file(value).collect { |k, v| "#{k}=\"#{v}\"" }.join("; ") if value.respond_to?(:mime_type) content_type = value.mime_type elsif value.respond_to?(:content_type) content_type = value.content_type else path = value.respond_to?(:path) ? value.path : nil content_type = Message.mime_type(path) end headers << %{Content-Disposition: form-data; name="#{attr}"; #{param_str}} headers << %{Content-Type: #{content_type}} elsif attr.is_a?(Hash) h = attr value = h[:content] h.each do |h_key, h_val| headers << %{#{h_key}: #{h_val}} if h_key != :content end remember_pos(value) if Message.file?(value) else headers << %{Content-Disposition: form-data; name="#{attr}"} value = value.to_s end parts.add(headers.join(CRLF) + CRLF + CRLF) parts.add(value) parts.add(CRLF) end parts.add("--#{boundary}--" + CRLF + CRLF) # empty epilogue parts end def params_from_file(value) params = {} path = value.respond_to?(:path) ? value.path : nil params['filename'] = File.basename(path || '') # Creation time is not available from File::Stat if value.respond_to?(:mtime) params['modification-date'] = value.mtime.rfc822 end if value.respond_to?(:atime) params['read-date'] = value.atime.rfc822 end params end end class << self private :new # Creates a Message instance of 'CONNECT' request. # 'CONNECT' request does not have Body. # uri:: an URI that need to connect. Only uri.host and uri.port are used. def new_connect_request(uri) m = new m.http_header.init_connect_request(uri) m.http_header.body_size = nil m end # Creates a Message instance of general request. # method:: HTTP method String. # uri:: an URI object which represents an URL of web resource. # query:: a Hash or an Array of query part of URL. # e.g. { "a" => "b" } => 'http://host/part?a=b' # Give an array to pass multiple value like # [["a", "b"], ["a", "c"]] => 'http://host/part?a=b&a=c' # body:: a Hash or an Array of body part. # e.g. { "a" => "b" } => 'a=b'. # Give an array to pass multiple value like # [["a", "b"], ["a", "c"]] => 'a=b&a=c'. # boundary:: When the boundary given, it is sent as # a multipart/form-data using this boundary String. def new_request(method, uri, query = nil, body = nil, boundary = nil) m = new m.http_header.init_request(method, uri, query) m.http_body = Body.new m.http_body.init_request(body || '', boundary) if body m.http_header.body_size = m.http_body.size m.http_header.chunked = true if m.http_body.size.nil? else m.http_header.body_size = nil end m end # Creates a Message instance of response. # body:: a String or an IO of response message body. def new_response(body, req = nil) m = new m.http_header.init_response(Status::OK, req) m.http_body = Body.new m.http_body.init_response(body) m.http_header.body_size = m.http_body.size || 0 m end @@mime_type_handler = nil # Sets MIME type handler. # # handler must respond to :call with a single argument :path and returns # a MIME type String e.g. 'text/html'. # When the handler returns nil or an empty String, # 'application/octet-stream' is used. # # When you set nil to the handler, internal_mime_type is used instead. # The handler is nil by default. def mime_type_handler=(handler) @@mime_type_handler = handler end # Returns MIME type handler. def mime_type_handler @@mime_type_handler end # For backward compatibility. alias set_mime_type_func mime_type_handler= alias get_mime_type_func mime_type_handler def mime_type(path) # :nodoc: if @@mime_type_handler res = @@mime_type_handler.call(path) if !res || res.to_s == '' return 'application/octet-stream' else return res end else internal_mime_type(path) end end # Default MIME type handler. # See mime_type_handler=. def internal_mime_type(path) case path when /\.txt$/i 'text/plain' when /\.(htm|html)$/i 'text/html' when /\.doc$/i 'application/msword' when /\.png$/i 'image/png' when /\.gif$/i 'image/gif' when /\.(jpg|jpeg)$/i 'image/jpeg' else 'application/octet-stream' end end # Returns true if the given HTTP version allows keep alive connection. # version:: String def keep_alive_enabled?(version) version >= '1.1' end # Returns true if the given query (or body) has a multiple parameter. def multiparam_query?(query) query.is_a?(Array) or query.is_a?(Hash) end # Returns true if the given object is a File. In HTTPClient, a file is; # * must respond to :read for retrieving String chunks. # * must respond to :pos and :pos= to rewind for reading. # Rewinding is only needed for following HTTP redirect. Some IO impl # defines :pos= but raises an Exception for pos= such as StringIO # but there's no problem as far as using it for non-following methods # (get/post/etc.) def file?(obj) obj.respond_to?(:read) and obj.respond_to?(:pos) and obj.respond_to?(:pos=) end def create_query_part_str(query) # :nodoc: if multiparam_query?(query) escape_query(query) elsif query.respond_to?(:read) query = query.read else query.to_s end end def Array.try_convert(value) return value if value.instance_of?(Array) return nil if !value.respond_to?(:to_ary) converted = value.to_ary return converted if converted.instance_of?(Array) cname = value.class.name raise TypeError, "can't convert %s to %s (%s#%s gives %s)" % [cname, Array.name, cname, :to_ary, converted.class.name] end unless Array.respond_to?(:try_convert) def escape_query(query) # :nodoc: pairs = [] query.each { |attr, value| left = escape(attr.to_s) << '=' if values = Array.try_convert(value) values.each { |value| if value.respond_to?(:read) value = value.read end pairs.push(left + escape(value.to_s)) } else if value.respond_to?(:read) value = value.read end pairs.push(left << escape(value.to_s)) end } pairs.join('&') end # from CGI.escape if defined?(Encoding::ASCII_8BIT) def escape(str) # :nodoc: str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(/([^ a-zA-Z0-9_.-]+)/) { '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase }.tr(' ', '+') end else def escape(str) # :nodoc: str.gsub(/([^ a-zA-Z0-9_.-]+)/n) { '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase }.tr(' ', '+') end end # from CGI.parse def parse(query) params = Hash.new([].freeze) query.split(/[&;]/n).each do |pairs| key, value = pairs.split('=',2).collect{|v| unescape(v) } if params.has_key?(key) params[key].push(value) else params[key] = [value] end end params end # from CGI.unescape def unescape(string) string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do [$1.delete('%')].pack('H*') end end end # HTTP::Message::Headers:: message header. attr_accessor :http_header # HTTP::Message::Body:: message body. attr_reader :http_body # OpenSSL::X509::Certificate:: response only. server certificate which is # used for retrieving the response. attr_accessor :peer_cert # Creates a Message. This method should be used internally. # Use Message.new_connect_request, Message.new_request or # Message.new_response instead. def initialize # :nodoc: @http_header = Headers.new @http_body = @peer_cert = nil end # Dumps message (header and body) to given dev. # dev needs to respond to <<. def dump(dev = '') str = @http_header.dump + CRLF if @http_header.chunked dev = @http_body.dump_chunked(str, dev) elsif @http_body dev = @http_body.dump(str, dev) else dev << str end dev end # Sets a new body. header.body_size is updated with new body.size. def http_body=(body) @http_body = body @http_header.body_size = @http_body.size if @http_header end alias body= http_body= # Returns HTTP version in a HTTP header. String. def http_version @http_header.http_version end # Sets HTTP version in a HTTP header. String. def http_version=(http_version) @http_header.http_version = http_version end VERSION_WARNING = 'Message#version (Float) is deprecated. Use Message#http_version (String) instead.' def version warn(VERSION_WARNING) @http_header.http_version.to_f end def version=(version) warn(VERSION_WARNING) @http_header.http_version = version end # Returns HTTP status code in response. Integer. def status @http_header.status_code end alias code status alias status_code status # Sets HTTP status code of response. Integer. # Reason phrase is updated, too. def status=(status) @http_header.status_code = status end # Returns HTTP status reason phrase in response. String. def reason @http_header.reason_phrase end # Sets HTTP status reason phrase of response. String. def reason=(reason) @http_header.reason_phrase = reason end # Returns 'Content-Type' header value. def content_type @http_header.content_type end # Sets 'Content-Type' header value. Overrides if already exists. def content_type=(content_type) @http_header.content_type = content_type end alias contenttype content_type alias contenttype= content_type= # Returns content encoding def body_encoding @http_header.body_encoding end # Returns a content of message body. A String or an IO. def content @http_body.content end alias header http_header alias body content # Returns Hash of header. key and value are both String. Each key has a # single value so you can't extract exact value when a message has multiple # headers like 'Set-Cookie'. Use header['Set-Cookie'] for that purpose. # (It returns an Array always) def headers Hash[*http_header.all.flatten] end # Extracts cookies from 'Set-Cookie' header. # Supports 'Set-Cookie' in response header only. # Do we need 'Cookie' support in request header? def cookies set_cookies = http_header['set-cookie'] unless set_cookies.empty? uri = http_header.request_uri set_cookies.map { |str| cookie = WebAgent::Cookie.new cookie.parse(str, uri) cookie } end end # Convenience method to return boolean of whether we had a successful request def ok? HTTP::Status.successful?(status) end def redirect? HTTP::Status.redirect?(status) end # SEE_OTHER is a redirect, but it should sent as GET def see_other? status == HTTP::Status::SEE_OTHER end end end httpclient-2.3.3/lib/httpclient/util.rb0000644000004100000410000001067012155433207020161 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2012 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. unless ''.respond_to?(:bytesize) class String alias bytesize size end end if RUBY_VERSION < "1.9.3" require 'uri' module URI class Generic def hostname v = self.host /\A\[(.*)\]\z/ =~ v ? $1 : v end end end end class HTTPClient # A module for common function. module Util # URI abstraction; Addressable::URI or URI require 'uri' begin require 'addressable/uri' # Older versions doesn't have #default_port unless Addressable::URI.instance_methods.include?(:default_port) # 1.9 only raise LoadError end class AddressableURI < Addressable::URI # Overwrites the original definition just for one line... def authority self.host && @authority ||= (begin authority = "" if self.userinfo != nil authority << "#{self.userinfo}@" end authority << self.host if self.port != self.default_port # ...HERE! Compares with default_port because self.port is not nil in this wrapper. authority << ":#{self.port}" end authority end) end # HTTPClient expects urify("http://foo/").port to be not nil but 80 like URI. def port super || default_port end # Captured from uri/generic.rb def hostname v = self.host /\A\[(.*)\]\z/ =~ v ? $1 : v end end AddressableEnabled = true rescue LoadError AddressableEnabled = false end # Keyword argument helper. # args:: given arguments. # *field:: a list of arguments to be extracted. # # You can extract 3 arguments (a, b, c) with: # # include Util # def my_method(*args) # a, b, c = keyword_argument(args, :a, :b, :c) # ... # end # my_method(1, 2, 3) # my_method(:b => 2, :a = 1) # # instead of; # # def my_method(a, b, c) # ... # end # def keyword_argument(args, *field) if args.size == 1 and Hash === args[0] h = args[0] if field.any? { |f| h.key?(f) } return h.values_at(*field) end end args end # Keyword argument to hash helper. # args:: given arguments. # *field:: a list of arguments to be extracted. # # Returns hash which has defined keys. When a Hash given, returns it # including undefined keys. When an Array given, returns a Hash which only # includes defined keys. def argument_to_hash(args, *field) return nil if args.empty? if args.size == 1 and Hash === args[0] h = args[0] if field.any? { |f| h.key?(f) } return h end end h = {} field.each_with_index do |e, idx| h[e] = args[idx] end h end # Gets an URI instance. def urify(uri) if uri.nil? nil elsif uri.is_a?(URI) uri elsif AddressableEnabled AddressableURI.parse(uri.to_s) else URI.parse(uri.to_s) end end module_function :urify # Returns true if the given 2 URIs have a part_of relationship. # * the same scheme # * the same host String (no host resolution or IP-addr conversion) # * the same port number # * target URI's path starts with base URI's path. def uri_part_of(uri, part) ((uri.scheme == part.scheme) and (uri.host == part.host) and (uri.port == part.port) and uri.path.upcase.index(part.path.upcase) == 0) end module_function :uri_part_of # Returns parent directory URI of the given URI. def uri_dirname(uri) uri = uri.clone uri.path = uri.path.sub(/\/[^\/]*\z/, '/') uri end module_function :uri_dirname # Finds a value of a Hash. def hash_find_value(hash, &block) v = hash.find(&block) v ? v[1] : nil end module_function :hash_find_value # Checks if the given URI is https. def https?(uri) uri.scheme && uri.scheme.downcase == 'https' end def http?(uri) uri.scheme && uri.scheme.downcase == 'http' end end end httpclient-2.3.3/lib/httpclient/auth.rb0000644000004100000410000006467512155433207020163 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. require 'digest/md5' require 'httpclient/session' class HTTPClient begin require 'net/ntlm' NTLMEnabled = true rescue LoadError NTLMEnabled = false end begin require 'win32/sspi' SSPIEnabled = true rescue LoadError SSPIEnabled = false end begin require 'gssapi' GSSAPIEnabled = true rescue LoadError GSSAPIEnabled = false end # Common abstract class for authentication filter. # # There are 2 authentication filters. # WWWAuth:: Authentication filter for handling authentication negotiation # between Web server. Parses 'WWW-Authentication' header in # response and generates 'Authorization' header in request. # ProxyAuth:: Authentication filter for handling authentication negotiation # between Proxy server. Parses 'Proxy-Authentication' header in # response and generates 'Proxy-Authorization' header in request. class AuthFilterBase private def parse_authentication_header(res, tag) challenge = res.header[tag] return nil unless challenge challenge.collect { |c| parse_challenge_header(c) }.compact end def parse_challenge_header(challenge) scheme, param_str = challenge.scan(/\A(\S+)(?:\s+(.*))?\z/)[0] return nil if scheme.nil? return scheme, param_str end end # Authentication filter for handling authentication negotiation between # Web server. Parses 'WWW-Authentication' header in response and # generates 'Authorization' header in request. # # Authentication filter is implemented using request filter of HTTPClient. # It traps HTTP response header and maintains authentication state, and # traps HTTP request header for inserting necessary authentication header. # # WWWAuth has sub filters (BasicAuth, DigestAuth, NegotiateAuth and # SSPINegotiateAuth) and delegates some operations to it. # NegotiateAuth requires 'ruby/ntlm' module (rubyntlm gem). # SSPINegotiateAuth requires 'win32/sspi' module (rubysspi gem). class WWWAuth < AuthFilterBase attr_reader :basic_auth attr_reader :digest_auth attr_reader :negotiate_auth attr_reader :sspi_negotiate_auth attr_reader :oauth # Creates new WWWAuth. def initialize @basic_auth = BasicAuth.new @digest_auth = DigestAuth.new @negotiate_auth = NegotiateAuth.new @ntlm_auth = NegotiateAuth.new('NTLM') @sspi_negotiate_auth = SSPINegotiateAuth.new @oauth = OAuth.new # sort authenticators by priority @authenticator = [@oauth, @negotiate_auth, @ntlm_auth, @sspi_negotiate_auth, @digest_auth, @basic_auth] end # Resets challenge state. See sub filters for more details. def reset_challenge @authenticator.each do |auth| auth.reset_challenge end end # Set authentication credential. See sub filters for more details. def set_auth(uri, user, passwd) @authenticator.each do |auth| auth.set(uri, user, passwd) end reset_challenge end # Filter API implementation. Traps HTTP request and insert # 'Authorization' header if needed. def filter_request(req) @authenticator.each do |auth| next unless auth.set? # hasn't be set, don't use it if cred = auth.get(req) req.header.set('Authorization', auth.scheme + " " + cred) return end end end # Filter API implementation. Traps HTTP response and parses # 'WWW-Authenticate' header. # # This remembers the challenges for all authentication methods # available to the client. On the subsequent retry of the request, # filter_request will select the strongest method. def filter_response(req, res) command = nil if res.status == HTTP::Status::UNAUTHORIZED if challenge = parse_authentication_header(res, 'www-authenticate') uri = req.header.request_uri challenge.each do |scheme, param_str| @authenticator.each do |auth| next unless auth.set? # hasn't be set, don't use it if scheme.downcase == auth.scheme.downcase challengeable = auth.challenge(uri, param_str) command = :retry if challengeable end end end # ignore unknown authentication scheme end end command end end # Authentication filter for handling authentication negotiation between # Proxy server. Parses 'Proxy-Authentication' header in response and # generates 'Proxy-Authorization' header in request. # # Authentication filter is implemented using request filter of HTTPClient. # It traps HTTP response header and maintains authentication state, and # traps HTTP request header for inserting necessary authentication header. # # ProxyAuth has sub filters (BasicAuth, NegotiateAuth, and SSPINegotiateAuth) # and delegates some operations to it. # NegotiateAuth requires 'ruby/ntlm' module. # SSPINegotiateAuth requires 'win32/sspi' module. class ProxyAuth < AuthFilterBase attr_reader :basic_auth attr_reader :digest_auth attr_reader :negotiate_auth attr_reader :sspi_negotiate_auth # Creates new ProxyAuth. def initialize @basic_auth = ProxyBasicAuth.new @negotiate_auth = NegotiateAuth.new @ntlm_auth = NegotiateAuth.new('NTLM') @sspi_negotiate_auth = SSPINegotiateAuth.new @digest_auth = ProxyDigestAuth.new # sort authenticators by priority @authenticator = [@negotiate_auth, @ntlm_auth, @sspi_negotiate_auth, @digest_auth, @basic_auth] end # Resets challenge state. See sub filters for more details. def reset_challenge @authenticator.each do |auth| auth.reset_challenge end end # Set authentication credential. See sub filters for more details. def set_auth(user, passwd) @authenticator.each do |auth| auth.set(nil, user, passwd) end reset_challenge end # Filter API implementation. Traps HTTP request and insert # 'Proxy-Authorization' header if needed. def filter_request(req) @authenticator.each do |auth| next unless auth.set? # hasn't be set, don't use it if cred = auth.get(req) req.header.set('Proxy-Authorization', auth.scheme + " " + cred) return end end end # Filter API implementation. Traps HTTP response and parses # 'Proxy-Authenticate' header. def filter_response(req, res) command = nil if res.status == HTTP::Status::PROXY_AUTHENTICATE_REQUIRED if challenge = parse_authentication_header(res, 'proxy-authenticate') uri = req.header.request_uri challenge.each do |scheme, param_str| @authenticator.each do |auth| next unless auth.set? # hasn't be set, don't use it if scheme.downcase == auth.scheme.downcase challengeable = auth.challenge(uri, param_str) command = :retry if challengeable end end end # ignore unknown authentication scheme end end command end end # Authentication filter for handling BasicAuth negotiation. # Used in WWWAuth and ProxyAuth. class BasicAuth include HTTPClient::Util # Authentication scheme. attr_reader :scheme # Creates new BasicAuth filter. def initialize @cred = nil @set = false @auth = {} @challengeable = {} @scheme = "Basic" end # Resets challenge state. Do not send '*Authorization' header until the # server sends '*Authentication' again. def reset_challenge @challengeable.clear end # Set authentication credential. # uri == nil for generic purpose (allow to use user/password for any URL). def set(uri, user, passwd) @set = true if uri.nil? @cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '') else uri = Util.uri_dirname(uri) @auth[uri] = ["#{user}:#{passwd}"].pack('m').tr("\n", '') end end # have we marked this as set - ie that it's valid to use in this context? def set? @set == true end # Response handler: returns credential. # It sends cred only when a given uri is; # * child page of challengeable(got *Authenticate before) uri and, # * child page of defined credential def get(req) target_uri = req.header.request_uri return nil unless @challengeable.find { |uri, ok| Util.uri_part_of(target_uri, uri) and ok } return @cred if @cred Util.hash_find_value(@auth) { |uri, cred| Util.uri_part_of(target_uri, uri) } end # Challenge handler: remember URL for response. def challenge(uri, param_str = nil) @challengeable[urify(uri)] = true true end end class ProxyBasicAuth < BasicAuth def set(uri, user, passwd) @set = true @cred = ["#{user}:#{passwd}"].pack('m').tr("\n", '') end def get(req) target_uri = req.header.request_uri return nil unless @challengeable['challenged'] @cred end # Challenge handler: remember URL for response. def challenge(uri, param_str = nil) @challengeable['challenged'] = true true end end # Authentication filter for handling DigestAuth negotiation. # Used in WWWAuth. class DigestAuth # Authentication scheme. attr_reader :scheme # Creates new DigestAuth filter. def initialize @auth = {} @challenge = {} @set = false @nonce_count = 0 @scheme = "Digest" end # Resets challenge state. Do not send '*Authorization' header until the # server sends '*Authentication' again. def reset_challenge @challenge.clear end # Set authentication credential. # uri == nil is ignored. def set(uri, user, passwd) @set = true if uri uri = Util.uri_dirname(uri) @auth[uri] = [user, passwd] end end # have we marked this as set - ie that it's valid to use in this context? def set? @set == true end # Response handler: returns credential. # It sends cred only when a given uri is; # * child page of challengeable(got *Authenticate before) uri and, # * child page of defined credential def get(req) target_uri = req.header.request_uri param = Util.hash_find_value(@challenge) { |uri, v| Util.uri_part_of(target_uri, uri) } return nil unless param user, passwd = Util.hash_find_value(@auth) { |uri, auth_data| Util.uri_part_of(target_uri, uri) } return nil unless user calc_cred(req, user, passwd, param) end # Challenge handler: remember URL and challenge token for response. def challenge(uri, param_str) @challenge[uri] = parse_challenge_param(param_str) true end private # this method is implemented by sromano and posted to # http://tools.assembla.com/breakout/wiki/DigestForSoap # Thanks! # supported algorithms: MD5, MD5-sess def calc_cred(req, user, passwd, param) method = req.header.request_method path = req.header.create_query_uri a_1 = "#{user}:#{param['realm']}:#{passwd}" a_2 = "#{method}:#{path}" qop = param['qop'] nonce = param['nonce'] cnonce = nil if qop || param['algorithm'] =~ /MD5-sess/ cnonce = generate_cnonce() end a_1_md5sum = Digest::MD5.hexdigest(a_1) if param['algorithm'] =~ /MD5-sess/ a_1_md5sum = Digest::MD5.hexdigest("#{a_1_md5sum}:#{nonce}:#{cnonce}") algorithm = "MD5-sess" else algorithm = "MD5" end message_digest = [] message_digest << a_1_md5sum message_digest << nonce if qop @nonce_count += 1 message_digest << ('%08x' % @nonce_count) message_digest << cnonce message_digest << param['qop'] end message_digest << Digest::MD5.hexdigest(a_2) header = [] header << "username=\"#{user}\"" header << "realm=\"#{param['realm']}\"" header << "nonce=\"#{nonce}\"" header << "uri=\"#{path}\"" if cnonce header << "cnonce=\"#{cnonce}\"" end if qop header << "nc=#{'%08x' % @nonce_count}" header << "qop=#{param['qop']}" end header << "response=\"#{Digest::MD5.hexdigest(message_digest.join(":"))}\"" header << "algorithm=#{algorithm}" header << "opaque=\"#{param['opaque']}\"" if param.key?('opaque') header.join(", ") end # cf. WEBrick::HTTPAuth::DigestAuth#generate_next_nonce(aTime) def generate_cnonce now = "%012d" % Time.now.to_i pk = Digest::MD5.hexdigest([now, self.__id__, Process.pid, rand(65535)].join)[0, 32] [now + ':' + pk].pack('m*').chop end def parse_challenge_param(param_str) param = {} param_str.scan(/\s*([^\,]+(?:\\.[^\,]*)*)/).each do |str| key, value = str[0].scan(/\A([^=]+)=(.*)\z/)[0] if /\A"(.*)"\z/ =~ value value = $1.gsub(/\\(.)/, '\1') end param[key] = value end param end end # Authentication filter for handling DigestAuth negotiation. # Ignores uri argument. Used in ProxyAuth. class ProxyDigestAuth < DigestAuth # overrides DigestAuth#set. sets default user name and password. uri is not used. def set(uri, user, passwd) @set = true @auth = [user, passwd] end # overrides DigestAuth#get. Uses default user name and password # regardless of target uri if the proxy has required authentication # before def get(req) target_uri = req.header.request_uri param = @challenge return nil unless param user, passwd = @auth return nil unless user calc_cred(req, user, passwd, param) end def reset_challenge @challenge = nil end def challenge(uri, param_str) @challenge = parse_challenge_param(param_str) true end end # Authentication filter for handling Negotiate/NTLM negotiation. # Used in WWWAuth and ProxyAuth. # # NegotiateAuth depends on 'ruby/ntlm' module. class NegotiateAuth # Authentication scheme. attr_reader :scheme # NTLM opt for ruby/ntlm. {:ntlmv2 => true} by default. attr_reader :ntlm_opt # Creates new NegotiateAuth filter. def initialize(scheme = "Negotiate") @auth = {} @auth_default = nil @challenge = {} @scheme = scheme @set = false @ntlm_opt = { :ntlmv2 => true } end # Resets challenge state. Do not send '*Authorization' header until the # server sends '*Authentication' again. def reset_challenge @challenge.clear end # Set authentication credential. # uri == nil for generic purpose (allow to use user/password for any URL). def set(uri, user, passwd) @set = true if uri uri = Util.uri_dirname(uri) @auth[uri] = [user, passwd] else @auth_default = [user, passwd] end end # have we marked this as set - ie that it's valid to use in this context? def set? @set == true end # Response handler: returns credential. # See ruby/ntlm for negotiation state transition. def get(req) return nil unless NTLMEnabled target_uri = req.header.request_uri domain_uri, param = @challenge.find { |uri, v| Util.uri_part_of(target_uri, uri) } return nil unless param user, passwd = Util.hash_find_value(@auth) { |uri, auth_data| Util.uri_part_of(target_uri, uri) } unless user user, passwd = @auth_default end return nil unless user domain = nil domain, user = user.split("\\") if user.index("\\") state = param[:state] authphrase = param[:authphrase] case state when :init t1 = Net::NTLM::Message::Type1.new t1.domain = domain if domain return t1.encode64 when :response t2 = Net::NTLM::Message.decode64(authphrase) param = {:user => user, :password => passwd} param[:domain] = domain if domain t3 = t2.response(param, @ntlm_opt.dup) @challenge.delete(domain_uri) return t3.encode64 end nil end # Challenge handler: remember URL and challenge token for response. def challenge(uri, param_str) return false unless NTLMEnabled if param_str.nil? or @challenge[uri].nil? c = @challenge[uri] = {} c[:state] = :init c[:authphrase] = "" else c = @challenge[uri] c[:state] = :response c[:authphrase] = param_str end true end end # Authentication filter for handling Negotiate/NTLM negotiation. # Used in ProxyAuth. # # SSPINegotiateAuth depends on 'win32/sspi' module. class SSPINegotiateAuth # Authentication scheme. attr_reader :scheme # Creates new SSPINegotiateAuth filter. def initialize @challenge = {} @scheme = "Negotiate" end # Resets challenge state. Do not send '*Authorization' header until the # server sends '*Authentication' again. def reset_challenge @challenge.clear end # Set authentication credential. # NOT SUPPORTED: username and necessary data is retrieved by win32/sspi. # See win32/sspi for more details. def set(*args) # not supported end # have we marked this as set - ie that it's valid to use in this context? def set? SSPIEnabled || GSSAPIEnabled end # Response handler: returns credential. # See win32/sspi for negotiation state transition. def get(req) return nil unless SSPIEnabled || GSSAPIEnabled target_uri = req.header.request_uri domain_uri, param = @challenge.find { |uri, v| Util.uri_part_of(target_uri, uri) } return nil unless param state = param[:state] authenticator = param[:authenticator] authphrase = param[:authphrase] case state when :init if SSPIEnabled authenticator = param[:authenticator] = Win32::SSPI::NegotiateAuth.new return authenticator.get_initial_token(@scheme) else # use GSSAPI authenticator = param[:authenticator] = GSSAPI::Simple.new(domain_uri.host, 'HTTP') # Base64 encode the context token return [authenticator.init_context].pack('m').gsub(/\n/,'') end when :response @challenge.delete(domain_uri) if SSPIEnabled return authenticator.complete_authentication(authphrase) else # use GSSAPI return authenticator.init_context(authphrase.unpack('m').pop) end end nil end # Challenge handler: remember URL and challenge token for response. def challenge(uri, param_str) return false unless SSPIEnabled || GSSAPIEnabled if param_str.nil? or @challenge[uri].nil? c = @challenge[uri] = {} c[:state] = :init c[:authenticator] = nil c[:authphrase] = "" else c = @challenge[uri] c[:state] = :response c[:authphrase] = param_str end true end end # Authentication filter for handling OAuth negotiation. # Used in WWWAuth. # # CAUTION: This impl only support '#7 Accessing Protected Resources' in OAuth # Core 1.0 spec for now. You need to obtain Access token and Access secret by # yourself. # # CAUTION: This impl does NOT support OAuth Request Body Hash spec for now. # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html # class OAuth include HTTPClient::Util # Authentication scheme. attr_reader :scheme class Config include HTTPClient::Util attr_accessor :http_method attr_accessor :realm attr_accessor :consumer_key attr_accessor :consumer_secret attr_accessor :token attr_accessor :secret attr_accessor :signature_method attr_accessor :version attr_accessor :callback attr_accessor :verifier # for OAuth Session 1.0 (draft) attr_accessor :session_handle attr_reader :signature_handler attr_accessor :debug_timestamp attr_accessor :debug_nonce def initialize(*args) @http_method, @realm, @consumer_key, @consumer_secret, @token, @secret, @signature_method, @version, @callback, @verifier = keyword_argument(args, :http_method, :realm, :consumer_key, :consumer_secret, :token, :secret, :signature_method, :version, :callback, :verifier ) @http_method ||= :post @session_handle = nil @signature_handler = {} end end 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 escape(str) self.class.escape(str) end # Creates new DigestAuth filter. def initialize @config = nil # common config @auth = {} # configs for each site @challengeable = {} @nonce_count = 0 @signature_handler = { 'HMAC-SHA1' => method(:sign_hmac_sha1) } @scheme = "OAuth" end # Resets challenge state. Do not send '*Authorization' header until the # server sends '*Authentication' again. def reset_challenge @challengeable.clear end # Set authentication credential. # You cannot set OAuth config via WWWAuth#set_auth. Use OAuth#config= def set(*args) # not supported end # have we marked this as set - ie that it's valid to use in this context? def set? true end # Set authentication credential. def set_config(uri, config) if uri.nil? @config = config else uri = Util.uri_dirname(urify(uri)) @auth[uri] = config end end # Get authentication credential. def 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 # Response handler: returns credential. # It sends cred only when a given uri is; # * child page of challengeable(got *Authenticate before) uri and, # * child page of defined credential def get(req) target_uri = req.header.request_uri return nil unless @challengeable[nil] or @challengeable.find { |uri, ok| Util.uri_part_of(target_uri, uri) and ok } config = get_config(target_uri) || @config return nil unless config calc_cred(req, config) end # Challenge handler: remember URL for response. def challenge(uri, param_str = nil) if uri.nil? @challengeable[nil] = true else @challengeable[urify(uri)] = true end true end private def calc_cred(req, config) header = {} header['oauth_consumer_key'] = config.consumer_key header['oauth_token'] = config.token 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_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 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 encode_header(k, v) %Q(#{escape(k.to_s)}="#{escape(v.to_s)}") end def encode_param(params) params.map { |k, v| [v].flatten.map { |vv| %Q(#{escape(k.to_s)}=#{escape(vv.to_s)}) } }.flatten end 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 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 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 end end httpclient-2.3.3/lib/httpclient/include_client.rb0000644000004100000410000000630512155433207022165 0ustar www-datawww-data# It is useful to re-use a HTTPClient instance for multiple requests, to # re-use HTTP 1.1 persistent connections. # # To do that, you sometimes want to store an HTTPClient instance in a global/ # class variable location, so it can be accessed and re-used. # # This mix-in makes it easy to create class-level access to one or more # HTTPClient instances. The HTTPClient instances are lazily initialized # on first use (to, for instance, avoid interfering with WebMock/VCR), # and are initialized in a thread-safe manner. Note that a # HTTPClient, once initialized, is safe for use in multiple threads. # # Note that you `extend` HTTPClient::IncludeClient, not `include. # # require 'httpclient/include_client' # class Widget # extend HTTPClient::IncludeClient # # include_http_client # # and/or, specify more stuff # include_http_client('http://myproxy:8080', :method_name => :my_client) do |client| # # any init you want # client.set_cookie_store nil # client. # end # end # # That creates two HTTPClient instances available at the class level. # The first will be available from Widget.http_client (default method # name for `include_http_client`), with default initialization. # # The second will be available at Widget.my_client, with the init arguments # provided, further initialized by the block provided. # # In addition to a class-level method, for convenience instance-level methods # are also provided. Widget.http_client is identical to Widget.new.http_client # # class HTTPClient module IncludeClient def include_http_client(*args, &block) # We're going to dynamically define a class # to hold our state, namespaced, as well as possibly dynamic # name of cover method. method_name = (args.last.delete(:method_name) if args.last.kind_of? Hash) || :http_client args.pop if args.last == {} # if last arg was named methods now empty, remove it. # By the amazingness of closures, we can create these things # in local vars here and use em in our method, we don't even # need iVars for state. client_instance = nil client_mutex = Mutex.new client_args = args client_block = block # to define a _class method_ on the specific class that's currently # `self`, we have to use this bit of metaprogramming, sorry. (class << self; self ; end).instance_eval do define_method(method_name) do # implementation copied from ruby stdlib singleton # to create this global obj thread-safely. return client_instance if client_instance client_mutex.synchronize do return client_instance if client_instance # init HTTPClient with specified args/block client_instance = HTTPClient.new(*client_args) client_block.call(client_instance) if client_block end return client_instance end end # And for convenience, an _instance method_ on the class that just # delegates to the class method. define_method(method_name) do self.class.send(method_name) end end end end httpclient-2.3.3/lib/httpclient/ssl_config.rb0000644000004100000410000003255012155433207021333 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. class HTTPClient begin require 'openssl' SSLEnabled = true rescue LoadError SSLEnabled = false end # Represents SSL configuration for HTTPClient instance. # The implementation depends on OpenSSL. # # == Trust Anchor Control # # SSLConfig loads 'httpclient/cacert.p7s' as a trust anchor # (trusted certificate(s)) with add_trust_ca in initialization time. # This means that HTTPClient instance trusts some CA certificates by default, # like Web browsers. 'httpclient/cacert.p7s' is created by the author and # included in released package. # # 'cacert.p7s' is automatically generated from JDK 1.6. Regardless its # filename extension (p7s), HTTPClient doesn't verify the signature in it. # # You may want to change trust anchor by yourself. Call clear_cert_store # then add_trust_ca for that purpose. class SSLConfig include OpenSSL if SSLEnabled # String name of OpenSSL's SSL version method name: SSLv2, SSLv23 or SSLv3 attr_reader :ssl_version # OpenSSL::X509::Certificate:: certificate for SSL client authenticateion. # nil by default. (no client authenticateion) attr_reader :client_cert # OpenSSL::PKey::PKey:: private key for SSL client authentication. # nil by default. (no client authenticateion) attr_reader :client_key # A number which represents OpenSSL's verify mode. Default value is # OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT. attr_reader :verify_mode # A number of verify depth. Certification path which length is longer than # this depth is not allowed. attr_reader :verify_depth # A callback handler for custom certificate verification. nil by default. # If the handler is set, handler.call is invoked just after general # OpenSSL's verification. handler.call is invoked with 2 arguments, # ok and ctx; ok is a result of general OpenSSL's verification. ctx is a # OpenSSL::X509::StoreContext. attr_reader :verify_callback # SSL timeout in sec. nil by default. attr_reader :timeout # A number of OpenSSL's SSL options. Default value is # OpenSSL::SSL::OP_ALL | OpenSSL::SSL::OP_NO_SSLv2 attr_reader :options # A String of OpenSSL's cipher configuration. Default value is # ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH # See ciphers(1) man in OpenSSL for more detail. attr_reader :ciphers # OpenSSL::X509::X509::Store used for verification. You can reset the # store with clear_cert_store and set the new store with cert_store=. attr_reader :cert_store # don't use if you don't know what it is. # For server side configuration. Ignore this. attr_reader :client_ca # :nodoc: # Creates a SSLConfig. def initialize(client) return unless SSLEnabled @client = client @cert_store = X509::Store.new @client_cert = @client_key = @client_ca = nil @verify_mode = SSL::VERIFY_PEER | SSL::VERIFY_FAIL_IF_NO_PEER_CERT @verify_depth = nil @verify_callback = nil @dest = nil @timeout = nil @ssl_version = "SSLv3" @options = defined?(SSL::OP_ALL) ? SSL::OP_ALL | SSL::OP_NO_SSLv2 : nil # OpenSSL 0.9.8 default: "ALL:!ADH:!LOW:!EXP:!MD5:+SSLv2:@STRENGTH" @ciphers = "ALL:!aNULL:!eNULL:!SSLv2" # OpenSSL >1.0.0 default @cacerts_loaded = false end # Sets SSL version method String. Possible values: "SSLv2" for SSL2, # "SSLv3" for SSL3 and TLS1.x, "SSLv23" for SSL3 with fallback to SSL2. def ssl_version=(ssl_version) @ssl_version = ssl_version change_notify end # Sets certificate (OpenSSL::X509::Certificate) for SSL client # authentication. # client_key and client_cert must be a pair. # # Calling this method resets all existing sessions. def client_cert=(client_cert) @client_cert = client_cert change_notify end # Sets private key (OpenSSL::PKey::PKey) for SSL client authentication. # client_key and client_cert must be a pair. # # Calling this method resets all existing sessions. def client_key=(client_key) @client_key = client_key change_notify end # Sets certificate and private key for SSL client authentication. # cert_file:: must be a filename of PEM/DER formatted file. # key_file:: must be a filename of PEM/DER formatted file. Key must be an # RSA key. If you want to use other PKey algorithm, # use client_key=. # # Calling this method resets all existing sessions. def set_client_cert_file(cert_file, key_file) @client_cert = X509::Certificate.new(File.open(cert_file) { |f| f.read }) @client_key = PKey::RSA.new(File.open(key_file) { |f| f.read }) change_notify end # Sets OpenSSL's default trusted CA certificates. Generally, OpenSSL is # configured to use OS's trusted CA certificates located at # /etc/pki/certs or /etc/ssl/certs. Unfortunately OpenSSL's Windows build # does not work with Windows Certificate Storage. # # On Windows or when you build OpenSSL manually, you can set the # CA certificates directory by SSL_CERT_DIR env variable at runtime. # # SSL_CERT_DIR=/etc/ssl/certs ruby -rhttpclient -e "..." # # Calling this method resets all existing sessions. def set_default_paths @cacerts_loaded = true # avoid lazy override @cert_store = X509::Store.new @cert_store.set_default_paths change_notify end # Drops current certificate store (OpenSSL::X509::Store) for SSL and create # new one for the next session. # # Calling this method resets all existing sessions. def clear_cert_store @cacerts_loaded = true # avoid lazy override @cert_store = X509::Store.new change_notify end # Sets new certificate store (OpenSSL::X509::Store). # don't use if you don't know what it is. # # Calling this method resets all existing sessions. def cert_store=(cert_store) @cacerts_loaded = true # avoid lazy override @cert_store = cert_store change_notify end # Sets trust anchor certificate(s) for verification. # trust_ca_file_or_hashed_dir:: a filename of a PEM/DER formatted # OpenSSL::X509::Certificate or # a 'c-rehash'eddirectory name which stores # trusted certificate files. # # Calling this method resets all existing sessions. def add_trust_ca(trust_ca_file_or_hashed_dir) @cacerts_loaded = true # avoid lazy override add_trust_ca_to_store(@cert_store, trust_ca_file_or_hashed_dir) change_notify end alias set_trust_ca add_trust_ca def add_trust_ca_to_store(cert_store, trust_ca_file_or_hashed_dir) if FileTest.directory?(trust_ca_file_or_hashed_dir) cert_store.add_path(trust_ca_file_or_hashed_dir) else cert_store.add_file(trust_ca_file_or_hashed_dir) end end # Loads default trust anchors. # Calling this method resets all existing sessions. def load_trust_ca load_cacerts(@cert_store) change_notify end # Adds CRL for verification. # crl:: a OpenSSL::X509::CRL or a filename of a PEM/DER formatted # OpenSSL::X509::CRL. # # Calling this method resets all existing sessions. def add_crl(crl) unless crl.is_a?(X509::CRL) crl = X509::CRL.new(File.open(crl) { |f| f.read }) end @cert_store.add_crl(crl) @cert_store.flags = X509::V_FLAG_CRL_CHECK | X509::V_FLAG_CRL_CHECK_ALL change_notify end alias set_crl add_crl # Sets verify mode of OpenSSL. New value must be a combination of # constants OpenSSL::SSL::VERIFY_* # # Calling this method resets all existing sessions. def verify_mode=(verify_mode) @verify_mode = verify_mode change_notify end # Sets verify depth. New value must be a number. # # Calling this method resets all existing sessions. def verify_depth=(verify_depth) @verify_depth = verify_depth change_notify end # Sets callback handler for custom certificate verification. # See verify_callback. # # Calling this method resets all existing sessions. def verify_callback=(verify_callback) @verify_callback = verify_callback change_notify end # Sets SSL timeout in sec. # # Calling this method resets all existing sessions. def timeout=(timeout) @timeout = timeout change_notify end # Sets SSL options. New value must be a combination of # constants # OpenSSL::SSL::OP_* # # Calling this method resets all existing sessions. def options=(options) @options = options change_notify end # Sets cipher configuration. New value must be a String. # # Calling this method resets all existing sessions. def ciphers=(ciphers) @ciphers = ciphers change_notify end def client_ca=(client_ca) # :nodoc: @client_ca = client_ca change_notify end # interfaces for SSLSocketWrap. def set_context(ctx) # :nodoc: load_trust_ca unless @cacerts_loaded @cacerts_loaded = true # Verification: Use Store#verify_callback instead of SSLContext#verify*? ctx.cert_store = @cert_store ctx.verify_mode = @verify_mode ctx.verify_depth = @verify_depth if @verify_depth ctx.verify_callback = @verify_callback || method(:default_verify_callback) # SSL config ctx.cert = @client_cert ctx.key = @client_key ctx.client_ca = @client_ca ctx.timeout = @timeout ctx.options = @options ctx.ciphers = @ciphers ctx.ssl_version = @ssl_version end # post connection check proc for ruby < 1.8.5. # this definition must match with the one in ext/openssl/lib/openssl/ssl.rb def post_connection_check(peer_cert, hostname) # :nodoc: check_common_name = true cert = peer_cert cert.extensions.each{|ext| next if ext.oid != "subjectAltName" ext.value.split(/,\s+/).each{|general_name| if /\ADNS:(.*)/ =~ general_name check_common_name = false reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+") return true if /\A#{reg}\z/i =~ hostname elsif /\AIP Address:(.*)/ =~ general_name check_common_name = false return true if $1 == hostname end } } if check_common_name cert.subject.to_a.each{|oid, value| if oid == "CN" reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") return true if /\A#{reg}\z/i =~ hostname end } end raise SSL::SSLError, "hostname was not match with the server certificate" end # Default callback for verification: only dumps error. def default_verify_callback(is_ok, ctx) if $DEBUG if is_ok warn("ok: #{ctx.current_cert.subject.to_s.dump}") else warn("ng: #{ctx.current_cert.subject.to_s.dump} at depth #{ctx.error_depth} - #{ctx.error}: #{ctx.error_string} in #{ctx.chain.inspect}") end warn(ctx.current_cert.to_text) warn(ctx.current_cert.to_pem) end if !is_ok depth = ctx.error_depth code = ctx.error msg = ctx.error_string warn("at depth #{depth} - #{code}: #{msg}") end is_ok end # Sample callback method: CAUTION: does not check CRL/ARL. def sample_verify_callback(is_ok, ctx) unless is_ok depth = ctx.error_depth code = ctx.error msg = ctx.error_string warn("at depth #{depth} - #{code}: #{msg}") if $DEBUG return false end cert = ctx.current_cert self_signed = false ca = false pathlen = nil server_auth = true self_signed = (cert.subject.cmp(cert.issuer) == 0) # Check extensions whatever its criticality is. (sample) cert.extensions.each do |ex| case ex.oid when 'basicConstraints' /CA:(TRUE|FALSE), pathlen:(\d+)/ =~ ex.value ca = ($1 == 'TRUE') pathlen = $2.to_i when 'keyUsage' usage = ex.value.split(/\s*,\s*/) ca = usage.include?('Certificate Sign') server_auth = usage.include?('Key Encipherment') when 'extendedKeyUsage' usage = ex.value.split(/\s*,\s*/) server_auth = usage.include?('Netscape Server Gated Crypto') when 'nsCertType' usage = ex.value.split(/\s*,\s*/) ca = usage.include?('SSL CA') server_auth = usage.include?('SSL Server') end end if self_signed warn('self signing CA') if $DEBUG return true elsif ca warn('middle level CA') if $DEBUG return true elsif server_auth warn('for server authentication') if $DEBUG return true end return false end private def change_notify @client.reset_all end def load_cacerts(cert_store) file = File.join(File.dirname(__FILE__), 'cacert.p7s') add_trust_ca_to_store(cert_store, file) end end end httpclient-2.3.3/lib/httpclient/cookie.rb0000644000004100000410000002501312155433207020452 0ustar www-datawww-data# cookie.rb is redistributed file which is originally included in Webagent # version 0.6.2 by TAKAHASHI `Maki' Masayoshi. And it contains some bug fixes. # You can download the entire package of Webagent from # http://www.rubycolor.org/arc/. # Cookie class # # I refered to w3m's source to make these classes. Some comments # are quoted from it. I'm thanksful for author(s) of it. # # w3m homepage: http://ei5nazha.yz.yamagata-u.ac.jp/~aito/w3m/eng/ require 'time' require 'monitor' require 'httpclient/util' class WebAgent module CookieUtils def head_match?(str1, str2) str1 == str2[0, str1.length] end def tail_match?(str1, str2) if str1.length > 0 str1 == str2[-str1.length..-1].to_s else true end end def domain_match(host, domain) domainname = domain.sub(/\.\z/, '').downcase hostname = host.sub(/\.\z/, '').downcase case domain when /\d+\.\d+\.\d+\.\d+/ return (hostname == domainname) when '.' return true when /^\./ # allows; host == rubyforge.org, domain == .rubyforge.org return tail_match?(domainname, '.' + hostname) else return (hostname == domainname) end end end class Cookie include CookieUtils attr_accessor :name, :value attr_accessor :domain, :path attr_accessor :expires ## for Netscape Cookie attr_accessor :url attr_writer :use, :secure, :http_only, :discard, :domain_orig, :path_orig, :override USE = 1 SECURE = 2 DOMAIN = 4 PATH = 8 DISCARD = 16 OVERRIDE = 32 OVERRIDE_OK = 32 HTTP_ONLY = 64 def initialize @name = @value = @domain = @path = nil @expires = nil @url = nil @use = @secure = @http_only = @discard = @domain_orig = @path_orig = @override = nil end def discard? @discard end def use? @use end def secure? @secure end def http_only? @http_only end def domain_orig? @domain_orig end def path_orig? @path_orig end def override? @override end def flag flg = 0 flg += USE if @use flg += SECURE if @secure flg += HTTP_ONLY if @http_only flg += DOMAIN if @domain_orig flg += PATH if @path_orig flg += DISCARD if @discard flg += OVERRIDE if @override flg end def set_flag(flag) flag = flag.to_i @use = true if flag & USE > 0 @secure = true if flag & SECURE > 0 @http_only = true if flag & HTTP_ONLY > 0 @domain_orig = true if flag & DOMAIN > 0 @path_orig = true if flag & PATH > 0 @discard = true if flag & DISCARD > 0 @override = true if flag & OVERRIDE > 0 end def match?(url) domainname = url.host if (!domainname || !domain_match(domainname, @domain) || (@path && !head_match?(@path, url.path.empty? ? '/' : url.path)) || (@secure && (url.scheme != 'https')) ) return false else return true end end def join_quotedstr(array, sep) ret = Array.new old_elem = nil array.each{|elem| if (elem.scan(/"/).length % 2) == 0 if old_elem old_elem << sep << elem else ret << elem old_elem = nil end else if old_elem old_elem << sep << elem ret << old_elem old_elem = nil else old_elem = elem.dup end end } ret end def parse(str, url) @url = url # TODO: should not depend on join_quotedstr. scan with escape like CSV. cookie_elem = str.split(/;/) cookie_elem = join_quotedstr(cookie_elem, ';') cookie_elem -= [""] # del empty elements, a cookie might included ";;" first_elem = cookie_elem.shift if first_elem !~ /([^=]*)(\=(.*))?/ return ## raise ArgumentError 'invalid cookie value' end @name = $1.strip @value = normalize_cookie_value($3) cookie_elem.each{|pair| key, value = pair.split(/=/, 2) ## value may nil key.strip! value = normalize_cookie_value(value) case key.downcase when 'domain' @domain = value when 'expires' @expires = nil begin @expires = Time.parse(value).gmtime if value rescue ArgumentError end when 'path' @path = value when 'secure' @secure = true ## value may nil, but must 'true'. when 'httponly' @http_only = true ## value may nil, but must 'true'. else ## ignore end } end private def normalize_cookie_value(value) if value value = value.strip.sub(/\A"(.*)"\z/) { $1 } value = nil if value.empty? end value end end class CookieManager include CookieUtils ### errors class Error < StandardError; end class ErrorOverrideOK < Error; end class SpecialError < Error; end attr_reader :cookies attr_accessor :cookies_file attr_accessor :accept_domains, :reject_domains def initialize(file=nil) @cookies = Array.new @cookies.extend(MonitorMixin) @cookies_file = file @is_saved = true @reject_domains = Array.new @accept_domains = Array.new @netscape_rule = false end def cookies=(cookies) @cookies = cookies @cookies.extend(MonitorMixin) end def save_all_cookies(force = nil, save_unused = true, save_discarded = true) @cookies.synchronize do check_expired_cookies if @is_saved and !force return end File.open(@cookies_file, 'w') do |f| @cookies.each do |cookie| if (cookie.use? or save_unused) and (!cookie.discard? or save_discarded) f.print(cookie.url.to_s,"\t", cookie.name,"\t", cookie.value,"\t", cookie.expires.to_i,"\t", cookie.domain,"\t", cookie.path,"\t", cookie.flag,"\n") end end end end @is_saved = true end def save_cookies(force = nil) save_all_cookies(force, false, false) end def check_expired_cookies @cookies.reject!{|cookie| is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime)) if is_expired && !cookie.discard? @is_saved = false end is_expired } end def parse(str, url) cookie = WebAgent::Cookie.new cookie.parse(str, url) add(cookie) end def find(url) return nil if @cookies.empty? cookie_list = Array.new @cookies.each{|cookie| is_expired = (cookie.expires && (cookie.expires < Time.now.gmtime)) if cookie.use? && !is_expired && cookie.match?(url) if cookie_list.select{|c1| c1.name == cookie.name}.empty? cookie_list << cookie end end } return make_cookie_str(cookie_list) end def add(given) check_domain(given.domain, given.url.host, given.override?) domain = given.domain || given.url.host path = given.path || given.url.path.sub(%r|/[^/]*\z|, '') cookie = nil @cookies.synchronize do cookie = @cookies.find { |c| c.domain == domain && c.path == path && c.name == given.name } if !cookie cookie = WebAgent::Cookie.new cookie.use = true @cookies << cookie end check_expired_cookies end cookie.domain = domain cookie.path = path cookie.url = given.url cookie.name = given.name cookie.value = given.value cookie.expires = given.expires cookie.secure = given.secure? cookie.http_only = given.http_only? cookie.domain_orig = given.domain cookie.path_orig = given.path if cookie.discard? || cookie.expires == nil cookie.discard = true else cookie.discard = false @is_saved = false end end def load_cookies return if !File.readable?(@cookies_file) @cookies.synchronize do @cookies.clear File.open(@cookies_file,'r'){|f| while line = f.gets cookie = WebAgent::Cookie.new @cookies << cookie col = line.chomp.split(/\t/) cookie.url = HTTPClient::Util.urify(col[0]) cookie.name = col[1] cookie.value = col[2] if col[3].empty? or col[3] == '0' cookie.expires = nil else cookie.expires = Time.at(col[3].to_i).gmtime end cookie.domain = col[4] cookie.path = col[5] cookie.set_flag(col[6]) end } end end # Who use it? def check_cookie_accept_domain(domain) unless domain return false end @accept_domains.each{|dom| if domain_match(domain, dom) return true end } @reject_domains.each{|dom| if domain_match(domain, dom) return false end } return true end private def make_cookie_str(cookie_list) if cookie_list.empty? return nil end ret = '' c = cookie_list.shift ret += "#{c.name}=#{c.value}" cookie_list.each{|cookie| ret += "; #{cookie.name}=#{cookie.value}" } return ret end # for conformance to http://wp.netscape.com/newsref/std/cookie_spec.html attr_accessor :netscape_rule SPECIAL_DOMAIN = [".com",".edu",".gov",".mil",".net",".org",".int"] def check_domain(domain, hostname, override) return unless domain # [DRAFT 12] s. 4.2.2 (does not apply in the case that # host name is the same as domain attribute for version 0 # cookie) # I think that this rule has almost the same effect as the # tail match of [NETSCAPE]. if domain !~ /^\./ && hostname != domain domain = '.'+domain end # [NETSCAPE] rule if @netscape_rule n = domain.scan(/\./).length if n < 2 cookie_error(SpecialError.new, override) elsif n == 2 ## [NETSCAPE] rule ok = SPECIAL_DOMAIN.select{|sdomain| sdomain == domain[-(sdomain.length)..-1] } if ok.empty? cookie_error(SpecialError.new, override) end end end # this implementation does not check RFC2109 4.3.2 case 2; # the portion of host not in domain does not contain a dot. # according to nsCookieService.cpp in Firefox 3.0.4, Firefox 3.0.4 # and IE does not check, too. end # not tested well; used only netscape_rule = true. def cookie_error(err, override) if !err.kind_of?(ErrorOverrideOK) || !override raise err end end end end httpclient-2.3.3/lib/http-access2.rb0000644000004100000410000000330012155433207017316 0ustar www-datawww-data# HTTPAccess2 - HTTP accessing library. # Copyright (C) 2000-2007 NAKAMURA, Hiroshi . # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. # http-access2.rb is based on http-access.rb in http-access/0.0.4. Some # part of it is copyrighted by Maebashi-san who made and published # http-access/0.0.4. http-access/0.0.4 did not include license notice but # when I asked Maebashi-san he agreed that I can redistribute it under the # same terms of Ruby. Many thanks to Maebashi-san. require 'httpclient' module HTTPAccess2 VERSION = ::HTTPClient::VERSION RUBY_VERSION_STRING = ::HTTPClient::RUBY_VERSION_STRING SSLEnabled = ::HTTPClient::SSLEnabled SSPIEnabled = ::HTTPClient::SSPIEnabled DEBUG_SSL = true Util = ::HTTPClient::Util class Client < ::HTTPClient class RetryableResponse < StandardError end end SSLConfig = ::HTTPClient::SSLConfig BasicAuth = ::HTTPClient::BasicAuth DigestAuth = ::HTTPClient::DigestAuth NegotiateAuth = ::HTTPClient::NegotiateAuth AuthFilterBase = ::HTTPClient::AuthFilterBase WWWAuth = ::HTTPClient::WWWAuth ProxyAuth = ::HTTPClient::ProxyAuth Site = ::HTTPClient::Site Connection = ::HTTPClient::Connection SessionManager = ::HTTPClient::SessionManager SSLSocketWrap = ::HTTPClient::SSLSocketWrap DebugSocket = ::HTTPClient::DebugSocket class Session < ::HTTPClient::Session class Error < StandardError end class InvalidState < Error end class BadResponse < Error end class KeepAliveDisconnected < Error end end end httpclient-2.3.3/lib/hexdump.rb0000644000004100000410000000225212155433207016475 0ustar www-datawww-data# encoding: binary # This was written by Arai-san and published at # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/31987 module HexDump # str must be in BINARY encoding in 1.9 def encode(str) offset = 0 result = [] while raw = str.slice(offset, 16) and raw.length > 0 # data field data = '' for v in raw.unpack('N* a*') if v.kind_of? Integer data << sprintf("%08x ", v) else v.each_byte {|c| data << sprintf("%02x", c) } end end # text field text = raw.tr("\000-\037\177-\377", ".") result << sprintf("%08x %-36s %s", offset, data, text) offset += 16 # omit duplicate line if /^(#{regex_quote_n(raw)})+/n =~ str[offset .. -1] result << sprintf("%08x ...", offset) offset += $&.length # should print at the end if offset == str.length result << sprintf("%08x %-36s %s", offset-16, data, text) end end end result end module_function :encode if RUBY_VERSION >= "1.9" # raw must be in BINARY encoding in 1.9 def self.regex_quote_n(raw) Regexp.quote(raw) end else def self.regex_quote_n(raw) Regexp.quote(raw, 'n') end end end httpclient-2.3.3/lib/httpclient.rb0000644000004100000410000011312712155433207017205 0ustar www-datawww-data# HTTPClient - HTTP client library. # Copyright (C) 2000-2009 NAKAMURA, Hiroshi . # # This program is copyrighted free software by NAKAMURA, Hiroshi. You can # redistribute it and/or modify it under the same terms of Ruby's license; # either the dual license version in 2003, or any later version. require 'stringio' require 'digest/sha1' # Extra library require 'httpclient/version' require 'httpclient/util' require 'httpclient/ssl_config' require 'httpclient/connection' require 'httpclient/session' require 'httpclient/http' require 'httpclient/auth' require 'httpclient/cookie' # :main:HTTPClient # The HTTPClient class provides several methods for accessing Web resources # via HTTP. # # HTTPClient instance is designed to be MT-safe. You can call a HTTPClient # instance from several threads without synchronization after setting up an # instance. # # clnt = HTTPClient.new # clnt.set_cookie_store('/home/nahi/cookie.dat') # urls.each do |url| # Thread.new(url) do |u| # p clnt.head(u).status # end # end # # == How to use # # At first, how to create your client. See initialize for more detail. # # 1. Create simple client. # # clnt = HTTPClient.new # # 2. Accessing resources through HTTP proxy. You can use environment # variable 'http_proxy' or 'HTTP_PROXY' instead. # # clnt = HTTPClient.new('http://myproxy:8080') # # === How to retrieve web resources # # See get and get_content. # # 1. Get content of specified URL. It returns HTTP::Message object and # calling 'body' method of it returns a content String. # # puts clnt.get('http://dev.ctor.org/').body # # 2. For getting content directly, use get_content. It follows redirect # response and returns a String of whole result. # # puts clnt.get_content('http://dev.ctor.org/') # # 3. You can pass :follow_redirect option to follow redirect response in get. # # puts clnt.get('http://dev.ctor.org/', :follow_redirect => true) # # 4. Get content as chunks of String. It yields chunks of String. # # clnt.get_content('http://dev.ctor.org/') do |chunk| # puts chunk # end # # === Invoking other HTTP methods # # See head, get, post, put, delete, options, propfind, proppatch and trace. # It returns a HTTP::Message instance as a response. # # 1. Do HEAD request. # # res = clnt.head(uri) # p res.header['Last-Modified'][0] # # 2. Do GET request with query. # # query = { 'keyword' => 'ruby', 'lang' => 'en' } # res = clnt.get(uri, query) # p res.status # p res.contenttype # p res.header['X-Custom'] # puts res.body # # You can also use keyword argument style. # # res = clnt.get(uri, :query => { :keyword => 'ruby', :lang => 'en' }) # # === How to POST # # See post. # # 1. Do POST a form data. # # body = { 'keyword' => 'ruby', 'lang' => 'en' } # res = clnt.post(uri, body) # # Keyword argument style. # # res = clnt.post(uri, :body => ...) # # 2. Do multipart file upload with POST. No need to set extra header by # yourself from httpclient/2.1.4. # # File.open('/tmp/post_data') do |file| # body = { 'upload' => file, 'user' => 'nahi' } # res = clnt.post(uri, body) # end # # 3. Do multipart wth custom body. # # File.open('/tmp/post_data') do |file| # body = [{ 'Content-Type' => 'application/atom+xml; charset=UTF-8', # :content => '...' }, # { 'Content-Type' => 'video/mp4', # 'Content-Transfer-Encoding' => 'binary', # :content => file }] # res = clnt.post(uri, body) # end # # === Accessing via SSL # # Ruby needs to be compiled with OpenSSL. # # 1. Get content of specified URL via SSL. # Just pass an URL which starts with 'https://'. # # https_url = 'https://www.rsa.com' # clnt.get(https_url) # # 2. Getting peer certificate from response. # # res = clnt.get(https_url) # p res.peer_cert #=> returns OpenSSL::X509::Certificate # # 3. Configuring OpenSSL options. See HTTPClient::SSLConfig for more details. # # user_cert_file = 'cert.pem' # user_key_file = 'privkey.pem' # clnt.ssl_config.set_client_cert_file(user_cert_file, user_key_file) # clnt.get(https_url) # # === Handling Cookies # # 1. Using volatile Cookies. Nothing to do. HTTPClient handles Cookies. # # clnt = HTTPClient.new # res = clnt.get(url1) # receives Cookies. # res = clnt.get(url2) # sends Cookies if needed. # p res.cookies # # 2. Saving non volatile Cookies to a specified file. Need to set a file at # first and invoke save method at last. # # clnt = HTTPClient.new # clnt.set_cookie_store('/home/nahi/cookie.dat') # clnt.get(url) # ... # clnt.save_cookie_store # # 3. Disabling Cookies. # # clnt = HTTPClient.new # clnt.cookie_manager = nil # # === Configuring authentication credentials # # 1. Authentication with Web server. Supports BasicAuth, DigestAuth, and # Negotiate/NTLM (requires ruby/ntlm module). # # clnt = HTTPClient.new # domain = 'http://dev.ctor.org/http-access2/' # user = 'user' # password = 'user' # clnt.set_auth(domain, user, password) # p clnt.get('http://dev.ctor.org/http-access2/login').status # # 2. Authentication with Proxy server. Supports BasicAuth and NTLM # (requires win32/sspi) # # clnt = HTTPClient.new(proxy) # user = 'proxy' # password = 'proxy' # clnt.set_proxy_auth(user, password) # p clnt.get(url) # # === Invoking HTTP methods with custom header # # Pass a Hash or an Array for header argument. # # header = { 'Accept' => 'text/html' } # clnt.get(uri, query, header) # # header = [['Accept', 'image/jpeg'], ['Accept', 'image/png']] # clnt.get_content(uri, query, header) # # === Invoking HTTP methods asynchronously # # See head_async, get_async, post_async, put_async, delete_async, # options_async, propfind_async, proppatch_async, and trace_async. # It immediately returns a HTTPClient::Connection instance as a returning value. # # connection = clnt.post_async(url, body) # print 'posting.' # while true # break if connection.finished? # print '.' # sleep 1 # end # puts '.' # res = connection.pop # p res.status # p res.body.read # res.body is an IO for the res of async method. # # === Shortcut methods # # You can invoke get_content, get, etc. without creating HTTPClient instance. # # ruby -rhttpclient -e 'puts HTTPClient.get_content(ARGV.shift)' http://dev.ctor.org/ # ruby -rhttpclient -e 'p HTTPClient.head(ARGV.shift).header["last-modified"]' http://dev.ctor.org/ # class HTTPClient RUBY_VERSION_STRING = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})" LIB_NAME = "(#{VERSION}, #{RUBY_VERSION_STRING})" include Util # Raised for indicating running environment configuration error for example # accessing via SSL under the ruby which is not compiled with OpenSSL. class ConfigurationError < StandardError end # Raised for indicating HTTP response error. class BadResponseError < RuntimeError # HTTP::Message:: a response attr_reader :res def initialize(msg, res = nil) # :nodoc: super(msg) @res = res end end # Raised for indicating a timeout error. class TimeoutError < RuntimeError end # Raised for indicating a connection timeout error. # You can configure connection timeout via HTTPClient#connect_timeout=. class ConnectTimeoutError < TimeoutError end # Raised for indicating a request sending timeout error. # You can configure request sending timeout via HTTPClient#send_timeout=. class SendTimeoutError < TimeoutError end # Raised for indicating a response receiving timeout error. # You can configure response receiving timeout via # HTTPClient#receive_timeout=. class ReceiveTimeoutError < TimeoutError end # Deprecated. just for backward compatibility class Session BadResponse = ::HTTPClient::BadResponseError end class << self %w(get_content post_content head get post put delete options propfind proppatch trace).each do |name| eval <<-EOD def #{name}(*arg, &block) clnt = new begin clnt.#{name}(*arg, &block) ensure clnt.reset_all end end EOD end private def attr_proxy(symbol, assignable = false) name = symbol.to_s define_method(name) { @session_manager.__send__(name) } if assignable aname = name + '=' define_method(aname) { |rhs| reset_all @session_manager.__send__(aname, rhs) } end end end # HTTPClient::SSLConfig:: SSL configurator. attr_reader :ssl_config # WebAgent::CookieManager:: Cookies configurator. attr_accessor :cookie_manager # An array of response HTTP message body String which is used for loop-back # test. See test/* to see how to use it. If you want to do loop-back test # of HTTP header, use test_loopback_http_response instead. attr_reader :test_loopback_response # An array of request filter which can trap HTTP request/response. # See HTTPClient::WWWAuth to see how to use it. attr_reader :request_filter # HTTPClient::ProxyAuth:: Proxy authentication handler. attr_reader :proxy_auth # HTTPClient::WWWAuth:: WWW authentication handler. attr_reader :www_auth # How many times get_content and post_content follows HTTP redirect. # 10 by default. attr_accessor :follow_redirect_count # Set HTTP version as a String:: 'HTTP/1.0' or 'HTTP/1.1' attr_proxy(:protocol_version, true) # Connect timeout in sec. attr_proxy(:connect_timeout, true) # Request sending timeout in sec. attr_proxy(:send_timeout, true) # Response receiving timeout in sec. attr_proxy(:receive_timeout, true) # Reuse the same connection within this timeout in sec. from last used. attr_proxy(:keep_alive_timeout, true) # Size of reading block for non-chunked response. attr_proxy(:read_block_size, true) # Negotiation retry count for authentication. 5 by default. attr_proxy(:protocol_retry_count, true) # if your ruby is older than 2005-09-06, do not set socket_sync = false to # avoid an SSL socket blocking bug in openssl/buffering.rb. attr_proxy(:socket_sync, true) # User-Agent header in HTTP request. attr_proxy(:agent_name, true) # From header in HTTP request. attr_proxy(:from, true) # An array of response HTTP String (not a HTTP message body) which is used # for loopback test. See test/* to see how to use it. attr_proxy(:test_loopback_http_response) # Decompress a compressed (with gzip or deflate) content body transparently. false by default. attr_proxy(:transparent_gzip_decompression, true) # Local socket address. Set HTTPClient#socket_local.host and HTTPClient#socket_local.port to specify local binding hostname and port of TCP socket. attr_proxy(:socket_local, true) # Default header for PROPFIND request. PROPFIND_DEFAULT_EXTHEADER = { 'Depth' => '0' } # Default User-Agent header DEFAULT_AGENT_NAME = 'HTTPClient/1.0' # Creates a HTTPClient instance which manages sessions, cookies, etc. # # HTTPClient.new takes 3 optional arguments for proxy url string, # User-Agent String and From header String. User-Agent and From are embedded # in HTTP request Header if given. No User-Agent and From header added # without setting it explicitly. # # proxy = 'http://myproxy:8080' # agent_name = 'MyAgent/0.1' # from = 'from@example.com' # HTTPClient.new(proxy, agent_name, from) # # You can use a keyword argument style Hash. Keys are :proxy, :agent_name # and :from. # # HTTPClient.new(:agent_name => 'MyAgent/0.1') def initialize(*args) proxy, agent_name, from = keyword_argument(args, :proxy, :agent_name, :from) @proxy = nil # assigned later. @no_proxy = nil @no_proxy_regexps = [] @www_auth = WWWAuth.new @proxy_auth = ProxyAuth.new @request_filter = [@proxy_auth, @www_auth] @debug_dev = nil @redirect_uri_callback = method(:default_redirect_uri_callback) @test_loopback_response = [] @session_manager = SessionManager.new(self) @session_manager.agent_name = agent_name || DEFAULT_AGENT_NAME @session_manager.from = from @session_manager.ssl_config = @ssl_config = SSLConfig.new(self) @cookie_manager = WebAgent::CookieManager.new @follow_redirect_count = 10 load_environment self.proxy = proxy if proxy keep_webmock_compat end # webmock 1.6.2 depends on HTTP::Message#body.content to work. # let's keep it work iif webmock is loaded for a while. def keep_webmock_compat if respond_to?(:do_get_block_with_webmock) ::HTTP::Message.module_eval do def body def (o = self.content).content self end o end end end end # Returns debug device if exists. See debug_dev=. def debug_dev @debug_dev end # Sets debug device. Once debug device is set, all HTTP requests and # responses are dumped to given device. dev must respond to << for dump. # # Calling this method resets all existing sessions. def debug_dev=(dev) @debug_dev = dev reset_all @session_manager.debug_dev = dev end # Returns URI object of HTTP proxy if exists. def proxy @proxy end # Sets HTTP proxy used for HTTP connection. Given proxy can be an URI, # a String or nil. You can set user/password for proxy authentication like # HTTPClient#proxy = 'http://user:passwd@myproxy:8080' # # You can use environment variable 'http_proxy' or 'HTTP_PROXY' for it. # You need to use 'cgi_http_proxy' or 'CGI_HTTP_PROXY' instead if you run # HTTPClient from CGI environment from security reason. (HTTPClient checks # 'REQUEST_METHOD' environment variable whether it's CGI or not) # # Calling this method resets all existing sessions. def proxy=(proxy) if proxy.nil? || proxy.to_s.empty? @proxy = nil @proxy_auth.reset_challenge else @proxy = urify(proxy) if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or @proxy.host == nil or @proxy.port == nil raise ArgumentError.new("unsupported proxy #{proxy}") end @proxy_auth.reset_challenge if @proxy.user || @proxy.password @proxy_auth.set_auth(@proxy.user, @proxy.password) end end reset_all @session_manager.proxy = @proxy @proxy end # Returns NO_PROXY setting String if given. def no_proxy @no_proxy end # Sets NO_PROXY setting String. no_proxy must be a comma separated String. # Each entry must be 'host' or 'host:port' such as; # HTTPClient#no_proxy = 'example.com,example.co.jp:443' # # 'localhost' is treated as a no_proxy site regardless of explicitly listed. # HTTPClient checks given URI objects before accessing it. # 'host' is tail string match. No IP-addr conversion. # # You can use environment variable 'no_proxy' or 'NO_PROXY' for it. # # Calling this method resets all existing sessions. def no_proxy=(no_proxy) @no_proxy = no_proxy @no_proxy_regexps.clear if @no_proxy @no_proxy.scan(/([^:,]+)(?::(\d+))?/) do |host, port| if host[0] == ?. regexp = /#{Regexp.quote(host)}\z/i else regexp = /(\A|\.)#{Regexp.quote(host)}\z/i end @no_proxy_regexps << [regexp, port] end end reset_all end # Sets credential for Web server authentication. # domain:: a String or an URI to specify where HTTPClient should use this # credential. If you set uri to nil, HTTPClient uses this credential # wherever a server requires it. # user:: username String. # passwd:: password String. # # You can set multiple credentials for each uri. # # clnt.set_auth('http://www.example.com/foo/', 'foo_user', 'passwd') # clnt.set_auth('http://www.example.com/bar/', 'bar_user', 'passwd') # # Calling this method resets all existing sessions. def set_auth(domain, user, passwd) uri = urify(domain) @www_auth.set_auth(uri, user, passwd) reset_all end # Deprecated. Use set_auth instead. def set_basic_auth(domain, user, passwd) uri = urify(domain) @www_auth.basic_auth.set(uri, user, passwd) reset_all end # Sets credential for Proxy authentication. # user:: username String. # passwd:: password String. # # Calling this method resets all existing sessions. def set_proxy_auth(user, passwd) @proxy_auth.set_auth(user, passwd) reset_all end # Sets the filename where non-volatile Cookies be saved by calling # save_cookie_store. # This method tries to load and managing Cookies from the specified file. # # Calling this method resets all existing sessions. def set_cookie_store(filename) @cookie_manager.cookies_file = filename @cookie_manager.load_cookies if filename reset_all end # Try to save Cookies to the file specified in set_cookie_store. Unexpected # error will be raised if you don't call set_cookie_store first. # (interface mismatch between WebAgent::CookieManager implementation) def save_cookie_store @cookie_manager.save_cookies end # Returns stored cookies. def cookies if @cookie_manager @cookie_manager.cookies end end # Sets callback proc when HTTP redirect status is returned for get_content # and post_content. default_redirect_uri_callback is used by default. # # If you need strict implementation which does not allow relative URI # redirection, set strict_redirect_uri_callback instead. # # clnt.redirect_uri_callback = clnt.method(:strict_redirect_uri_callback) # def redirect_uri_callback=(redirect_uri_callback) @redirect_uri_callback = redirect_uri_callback end # Retrieves a web resource. # # uri:: a String or an URI object which represents an URL of web resource. # query:: a Hash or an Array of query part of URL. # e.g. { "a" => "b" } => 'http://host/part?a=b'. # Give an array to pass multiple value like # [["a", "b"], ["a", "c"]] => 'http://host/part?a=b&a=c'. # header:: a Hash or an Array of extra headers. e.g. # { 'Accept' => 'text/html' } or # [['Accept', 'image/jpeg'], ['Accept', 'image/png']]. # &block:: Give a block to get chunked message-body of response like # get_content(uri) { |chunked_body| ... }. # Size of each chunk may not be the same. # # get_content follows HTTP redirect status (see HTTP::Status.redirect?) # internally and try to retrieve content from redirected URL. See # redirect_uri_callback= how HTTP redirection is handled. # # If you need to get full HTTP response including HTTP status and headers, # use get method. get returns HTTP::Message as a response and you need to # follow HTTP redirect by yourself if you need. def get_content(uri, *args, &block) query, header = keyword_argument(args, :query, :header) success_content(follow_redirect(:get, uri, query, nil, header || {}, &block)) end # Posts a content. # # uri:: a String or an URI object which represents an URL of web resource. # body:: a Hash or an Array of body part. e.g. # { "a" => "b" } => 'a=b' # Give an array to pass multiple value like # [["a", "b"], ["a", "c"]] => 'a=b&a=c' # When you pass a File as a value, it will be posted as a # multipart/form-data. e.g. # { 'upload' => file } # You can also send custom multipart by passing an array of hashes. # Each part must have a :content attribute which can be a file, all # other keys will become headers. # [{ 'Content-Type' => 'text/plain', :content => "some text" }, # { 'Content-Type' => 'video/mp4', :content => File.new('video.mp4') }] # => # header:: a Hash or an Array of extra headers. e.g. # { 'Accept' => 'text/html' } # or # [['Accept', 'image/jpeg'], ['Accept', 'image/png']]. # &block:: Give a block to get chunked message-body of response like # post_content(uri) { |chunked_body| ... }. # Size of each chunk may not be the same. # # post_content follows HTTP redirect status (see HTTP::Status.redirect?) # internally and try to post the content to redirected URL. See # redirect_uri_callback= how HTTP redirection is handled. # Bear in mind that you should not depend on post_content because it sends # the same POST method to the new location which is prohibited in HTTP spec. # # If you need to get full HTTP response including HTTP status and headers, # use post method. def post_content(uri, *args, &block) body, header = keyword_argument(args, :body, :header) success_content(follow_redirect(:post, uri, nil, body, header || {}, &block)) end # A method for redirect uri callback. How to use: # clnt.redirect_uri_callback = clnt.method(:strict_redirect_uri_callback) # This callback does not allow relative redirect such as # Location: ../foo/ # in HTTP header. (raises BadResponseError instead) def strict_redirect_uri_callback(uri, res) newuri = urify(res.header['location'][0]) if https?(uri) && !https?(newuri) raise BadResponseError.new("redirecting to non-https resource") end if !http?(newuri) && !https?(newuri) raise BadResponseError.new("unexpected location: #{newuri}", res) end puts "redirect to: #{newuri}" if $DEBUG newuri end # A default method for redirect uri callback. This method is used by # HTTPClient instance by default. # This callback allows relative redirect such as # Location: ../foo/ # in HTTP header. def default_redirect_uri_callback(uri, res) newuri = urify(res.header['location'][0]) if !http?(newuri) && !https?(newuri) newuri = uri + newuri warn("could be a relative URI in location header which is not recommended") warn("'The field value consists of a single absolute URI' in HTTP spec") end if https?(uri) && !https?(newuri) raise BadResponseError.new("redirecting to non-https resource") end puts "redirect to: #{newuri}" if $DEBUG newuri end # Sends HEAD request to the specified URL. See request for arguments. def head(uri, *args) request(:head, uri, argument_to_hash(args, :query, :header, :follow_redirect)) end # Sends GET request to the specified URL. See request for arguments. def get(uri, *args, &block) request(:get, uri, argument_to_hash(args, :query, :header, :follow_redirect), &block) end # Sends POST request to the specified URL. See request for arguments. # You should not depend on :follow_redirect => true for POST method. It # sends the same POST method to the new location which is prohibited in HTTP spec. def post(uri, *args, &block) request(:post, uri, argument_to_hash(args, :body, :header, :follow_redirect), &block) end # Sends PUT request to the specified URL. See request for arguments. def put(uri, *args, &block) request(:put, uri, argument_to_hash(args, :body, :header), &block) end # Sends DELETE request to the specified URL. See request for arguments. def delete(uri, *args, &block) request(:delete, uri, argument_to_hash(args, :body, :header), &block) end # Sends OPTIONS request to the specified URL. See request for arguments. def options(uri, *args, &block) request(:options, uri, argument_to_hash(args, :header), &block) end # Sends PROPFIND request to the specified URL. See request for arguments. def propfind(uri, *args, &block) request(:propfind, uri, argument_to_hash(args, :header), &block) end # Sends PROPPATCH request to the specified URL. See request for arguments. def proppatch(uri, *args, &block) request(:proppatch, uri, argument_to_hash(args, :body, :header), &block) end # Sends TRACE request to the specified URL. See request for arguments. def trace(uri, *args, &block) request('TRACE', uri, argument_to_hash(args, :query, :header), &block) end # Sends a request to the specified URL. # # method:: HTTP method to be sent. method.to_s.upcase is used. # uri:: a String or an URI object which represents an URL of web resource. # query:: a Hash or an Array of query part of URL. # e.g. { "a" => "b" } => 'http://host/part?a=b' # Give an array to pass multiple value like # [["a", "b"], ["a", "c"]] => 'http://host/part?a=b&a=c' # body:: a Hash or an Array of body part. e.g. # { "a" => "b" } # => 'a=b' # Give an array to pass multiple value like # [["a", "b"], ["a", "c"]] # => 'a=b&a=c'. # When the given method is 'POST' and the given body contains a file # as a value, it will be posted as a multipart/form-data. e.g. # { 'upload' => file } # You can also send custom multipart by passing an array of hashes. # Each part must have a :content attribute which can be a file, all # other keys will become headers. # [{ 'Content-Type' => 'text/plain', :content => "some text" }, # { 'Content-Type' => 'video/mp4', :content => File.new('video.mp4') }] # => # See HTTP::Message.file? for actual condition of 'a file'. # header:: a Hash or an Array of extra headers. e.g. # { 'Accept' => 'text/html' } or # [['Accept', 'image/jpeg'], ['Accept', 'image/png']]. # &block:: Give a block to get chunked message-body of response like # get(uri) { |chunked_body| ... }. # Size of each chunk may not be the same. # # You can also pass a String as a body. HTTPClient just sends a String as # a HTTP request message body. # # When you pass an IO as a body, HTTPClient sends it as a HTTP request with # chunked encoding (Transfer-Encoding: chunked in HTTP header) if IO does not # respond to :read. Bear in mind that some server application does not support # chunked request. At least cgi.rb does not support it. def request(method, uri, *args, &block) query, body, header, follow_redirect = keyword_argument(args, :query, :body, :header, :follow_redirect) if [:post, :put].include?(method) body ||= '' end if method == :propfind header ||= PROPFIND_DEFAULT_EXTHEADER else header ||= {} end uri = urify(uri) if block filtered_block = proc { |res, str| block.call(str) } end if follow_redirect follow_redirect(method, uri, query, body, header, &block) else do_request(method, uri, query, body, header, &filtered_block) end end # Sends HEAD request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def head_async(uri, *args) query, header = keyword_argument(args, :query, :header) request_async(:head, uri, query, nil, header || {}) end # Sends GET request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def get_async(uri, *args) query, header = keyword_argument(args, :query, :header) request_async(:get, uri, query, nil, header || {}) end # Sends POST request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def post_async(uri, *args) body, header = keyword_argument(args, :body, :header) request_async(:post, uri, nil, body || '', header || {}) end # Sends PUT request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def put_async(uri, *args) body, header = keyword_argument(args, :body, :header) request_async(:put, uri, nil, body || '', header || {}) end # Sends DELETE request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def delete_async(uri, *args) header = keyword_argument(args, :header) request_async(:delete, uri, nil, nil, header || {}) end # Sends OPTIONS request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def options_async(uri, *args) header = keyword_argument(args, :header) request_async(:options, uri, nil, nil, header || {}) end # Sends PROPFIND request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def propfind_async(uri, *args) header = keyword_argument(args, :header) request_async(:propfind, uri, nil, nil, header || PROPFIND_DEFAULT_EXTHEADER) end # Sends PROPPATCH request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def proppatch_async(uri, *args) body, header = keyword_argument(args, :body, :header) request_async(:proppatch, uri, nil, body, header || {}) end # Sends TRACE request in async style. See request_async for arguments. # It immediately returns a HTTPClient::Connection instance as a result. def trace_async(uri, *args) query, body, header = keyword_argument(args, :query, :body, :header) request_async(:trace, uri, query, body, header || {}) end # Sends a request in async style. request method creates new Thread for # HTTP connection and returns a HTTPClient::Connection instance immediately. # # Arguments definition is the same as request. def request_async(method, uri, query = nil, body = nil, header = {}) uri = urify(uri) do_request_async(method, uri, query, body, header) end # Resets internal session for the given URL. Keep-alive connection for the # site (host-port pair) is disconnected if exists. def reset(uri) uri = urify(uri) @session_manager.reset(uri) end # Resets all of internal sessions. Keep-alive connections are disconnected. def reset_all @session_manager.reset_all end private class RetryableResponse < StandardError # :nodoc: end class KeepAliveDisconnected < StandardError # :nodoc: attr_reader :sess def initialize(sess = nil) @sess = sess end end def do_request(method, uri, query, body, header, &block) conn = Connection.new res = nil if HTTP::Message.file?(body) pos = body.pos rescue nil end retry_count = @session_manager.protocol_retry_count proxy = no_proxy?(uri) ? nil : @proxy while retry_count > 0 body.pos = pos if pos req = create_request(method, uri, query, body, header) begin protect_keep_alive_disconnected do do_get_block(req, proxy, conn, &block) end res = conn.pop break rescue RetryableResponse res = conn.pop retry_count -= 1 end end res end def do_request_async(method, uri, query, body, header) conn = Connection.new t = Thread.new(conn) { |tconn| begin if HTTP::Message.file?(body) pos = body.pos rescue nil end retry_count = @session_manager.protocol_retry_count proxy = no_proxy?(uri) ? nil : @proxy while retry_count > 0 body.pos = pos if pos req = create_request(method, uri, query, body, header) begin protect_keep_alive_disconnected do do_get_stream(req, proxy, tconn) end break rescue RetryableResponse retry_count -= 1 end end rescue Exception conn.push $! end } conn.async_thread = t conn end def load_environment # http_proxy if getenv('REQUEST_METHOD') # HTTP_PROXY conflicts with the environment variable usage in CGI where # HTTP_* is used for HTTP header information. Unlike open-uri, we # simply ignore http_proxy in CGI env and use cgi_http_proxy instead. self.proxy = getenv('cgi_http_proxy') else self.proxy = getenv('http_proxy') end # no_proxy self.no_proxy = getenv('no_proxy') end def getenv(name) ENV[name.downcase] || ENV[name.upcase] end def follow_redirect(method, uri, query, body, header, &block) uri = urify(uri) if block filtered_block = proc { |r, str| block.call(str) if r.ok? } end if HTTP::Message.file?(body) pos = body.pos rescue nil end retry_number = 0 while retry_number < @follow_redirect_count body.pos = pos if pos res = do_request(method, uri, query, body, header, &filtered_block) if res.redirect? method = :get if res.see_other? # See RFC2616 10.3.4 uri = urify(@redirect_uri_callback.call(uri, res)) retry_number += 1 else return res end end raise BadResponseError.new("retry count exceeded", res) end def success_content(res) if res.ok? return res.content else raise BadResponseError.new("unexpected response: #{res.header.inspect}", res) end end def protect_keep_alive_disconnected begin yield rescue KeepAliveDisconnected => e if e.sess @session_manager.invalidate(e.sess.dest) end yield end end def create_request(method, uri, query, body, header) method = method.to_s.upcase if header.is_a?(Hash) header = header.to_a else header = header.dup end boundary = nil if body _, content_type = header.find { |key, value| key.downcase == 'content-type' } if content_type if /\Amultipart/ =~ content_type if content_type =~ /boundary=(.+)\z/ boundary = $1 else boundary = create_boundary content_type = "#{content_type}; boundary=#{boundary}" header = override_header(header, 'Content-Type', content_type) end end else if file_in_form_data?(body) boundary = create_boundary content_type = "multipart/form-data; boundary=#{boundary}" else content_type = 'application/x-www-form-urlencoded' end header << ['Content-Type', content_type] end end req = HTTP::Message.new_request(method, uri, query, body, boundary) header.each do |key, value| req.header.add(key.to_s, value) end if @cookie_manager && cookie = @cookie_manager.find(uri) req.header.add('Cookie', cookie) end req end def create_boundary Digest::SHA1.hexdigest(Time.now.to_s) end def file_in_form_data?(body) HTTP::Message.multiparam_query?(body) && body.any? { |k, v| HTTP::Message.file?(v) } end def override_header(header, key, value) result = [] header.each do |k, v| if k.downcase == key.downcase result << [key, value] else result << [k, v] end end result end NO_PROXY_HOSTS = ['localhost'] def no_proxy?(uri) if !@proxy or NO_PROXY_HOSTS.include?(uri.host) return true end @no_proxy_regexps.each do |regexp, port| if !port || uri.port == port.to_i if regexp =~ uri.host return true end end end false end # !! CAUTION !! # Method 'do_get*' runs under MT conditon. Be careful to change. def do_get_block(req, proxy, conn, &block) @request_filter.each do |filter| filter.filter_request(req) end if str = @test_loopback_response.shift dump_dummy_request_response(req.http_body.dump, str) if @debug_dev conn.push(HTTP::Message.new_response(str, req.header)) return end content = block ? nil : '' res = HTTP::Message.new_response(content, req.header) @debug_dev << "= Request\n\n" if @debug_dev sess = @session_manager.query(req, proxy) res.peer_cert = sess.ssl_peer_cert @debug_dev << "\n\n= Response\n\n" if @debug_dev do_get_header(req, res, sess) conn.push(res) sess.get_body do |part| set_encoding(part, res.body_encoding) if block block.call(res, part) else content << part end end # there could be a race condition but it's OK to cache unreusable # connection because we do retry for that case. @session_manager.keep(sess) unless sess.closed? commands = @request_filter.collect { |filter| filter.filter_response(req, res) } if commands.find { |command| command == :retry } raise RetryableResponse.new end end def do_get_stream(req, proxy, conn) @request_filter.each do |filter| filter.filter_request(req) end if str = @test_loopback_response.shift dump_dummy_request_response(req.http_body.dump, str) if @debug_dev conn.push(HTTP::Message.new_response(StringIO.new(str), req.header)) return end piper, pipew = IO.pipe res = HTTP::Message.new_response(piper, req.header) @debug_dev << "= Request\n\n" if @debug_dev sess = @session_manager.query(req, proxy) res.peer_cert = sess.ssl_peer_cert @debug_dev << "\n\n= Response\n\n" if @debug_dev do_get_header(req, res, sess) conn.push(res) sess.get_body do |part| set_encoding(part, res.body_encoding) pipew.write(part) end pipew.close @session_manager.keep(sess) unless sess.closed? _ = @request_filter.collect { |filter| filter.filter_response(req, res) } # ignore commands (not retryable in async mode) end def do_get_header(req, res, sess) res.http_version, res.status, res.reason, headers = sess.get_header res.header.set_headers(headers) if @cookie_manager res.header['set-cookie'].each do |cookie| @cookie_manager.parse(cookie, req.header.request_uri) end end end def dump_dummy_request_response(req, res) @debug_dev << "= Dummy Request\n\n" @debug_dev << req @debug_dev << "\n\n= Dummy Response\n\n" @debug_dev << res end def set_encoding(str, encoding) str.force_encoding(encoding) if encoding end end httpclient-2.3.3/lib/http-access2/0000755000004100000410000000000012155433207016775 5ustar www-datawww-datahttpclient-2.3.3/lib/http-access2/http.rb0000644000004100000410000000003212155433207020274 0ustar www-datawww-datarequire 'httpclient/http' httpclient-2.3.3/lib/http-access2/cookie.rb0000644000004100000410000000003412155433207020570 0ustar www-datawww-datarequire 'httpclient/cookie'