require 'net/http' require 'uri' module Avatar # :nodoc: module Source # :nodoc: # Implements http://pavatar.com/spec/ class PavatarSource include AbstractSource PAVATAR_REGEXP = /^((?:http|https):\/\/.+)$/ix attr_accessor :http_connection_factory # Create a new PAvatar source. # # Options: # * :http_connection_factory - the Object used to start new HTTP connections. By default, # the Net::HTTP class. To use a proxy, pass # :http_connection_factory => Net::HTTP::Proxy(...) def initialize(options = {}) self.http_connection_factory = options.delete(:http_connection_factory) || Net::HTTP end # Discovers pavatar URL. Returns nil if person is nil. # # Options: # * :pavatar_field (Symbol) - the field to call from person. By default, :blog_url. def avatar_url_for(person, options = {}) return nil if person.nil? options = options.merge! ::Avatar.default_avatar_options field = options.delete(:pavatar_field) || :blog_url raise ArgumentError.new('No field specified; either specify a default field or pass in a value for :pavatar_field (probably :blog_url)') unless field pavatar_url = autodiscover_pavatar_url_from person.send(field) (pavatar_url.nil? || pavatar_url.to_s.blank?) ? nil : pavatar_url end private # Autodiscover PAvatar URL from a +profile_url+. # # See http://pavatar.com/spec/#autodiscovery # # Returns +nil+ if +profile_url+ is blank. def autodiscover_pavatar_url_from(profile_url) return nil if profile_url.blank? uri = URI.parse(profile_url) begin self.http_connection_factory.start(uri.host, uri.port) do |http| return avatar_from_header(http) || avatar_from_link_element(http) || direct_avatar(profile_url, http) || nil end rescue Errno::ECONNREFUSED => e nil end end # Autodisciover using http headers. # # See http://pavatar.com/spec/#http-header # # Returns the URL if found; +nil+ otherwise. def avatar_from_header(http) resp = http.head('/') resp.kind_of?(Net::HTTPSuccess) && resp['X-Pavatar'] end # Autodiscover using link element. # # See http://pavatar.com/spec/#link-element # # Returns the URL if found; +nil+ otherwise. def avatar_from_link_element(http) resp = http.get('/') resp.kind_of?(Net::HTTPSuccess) && parse_html_for_pavatar(resp.body) end # Look for a # # Returns the URL if found; +nil+ otherwise. def parse_html_for_pavatar(doc) return nil if doc.nil? or doc.empty? # 4.a 2 http://pavatar.com/spec/#autodiscovery-algorithm # recommends following regexp // # but it will not always match valid xhtml link element. # if result = doc.match(//) result.to_a[1..-1].map { |s| s.split(/["']/) }.flatten.find { |url| url =~ PAVATAR_REGEXP } end end # Autodiscover using direct URL. # # See http://pavatar.com/spec/#direct-url # # Returns the URL if found; +nil+ otherwise. def direct_avatar(profile_url, http) resp = http.head('/pavatar.png') profile_url + (profile_url =~ /\/$/ ? '' : '/') + 'pavatar.png' if resp.kind_of?(Net::HTTPSuccess) end end end end