#-- # gravaty # Copyright © 2013, 2014, 2015, 2016, 2017, 2018, 2019 Marco Bresciani # # This file is part of gravaty. # # gravaty is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # gravaty is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License # along with gravaty. If not, see . #++ require 'digest' require 'i18n' require 'json' require 'uri' require 'xmlrpc/client' require_relative 'utils/downloader' require_relative 'utils/rfc5322' require_relative 'utils/rpc_connector' module Gravaty # This class is a simple API to retrieve an URL with specified options # from Gravatar site. It can be used to read data for avatars, # profiles or for XML-RPC APi calls. The only needed parameter to # create a Gravaty object is the reference +email+ address. # Author:: {Marco Bresciani}[mailto:marcobresciani_1974@libero.it] # Copyright:: Copyright © 2013, 2014, 2015, 2016, 2017, 2018, # 2019 Marco Bresciani # License:: GNU General Public License version 3 class Gravaty include Comparable # Provides the MD5 signature (of the small caps version) of the # +email+ address used to build the object. attr_reader :digest # Provides the (small caps version of) +email+ address used to build # the object. attr_reader :email # Creates a +Gravaty+ object described by the user's +email+. Throws # an +ArgumentError+ exception if the supplied +email+ address is # +nil+ or not valid according to RFC5322. # # Usage:: new_gravaty = Gravaty.new email, parser # Params:: # - +email_address+, the user's email address (a syntactically # valid one). # - +parser+, a parser duck-responding to the +parse+ method with # two parameters, +method+ name and +value+ string # Returns:: a +Gravaty+ object for the specified +email+ address. # Raises:: +ArgumentError+, if the supplied +email+ address is +nil+ # or not valid according to RFC 5322. def initialize(email_address, parser) I18n.load_path = Dir[File.join(File.dirname(__FILE__), '/locales/', '*.yml')] raise ArgumentError, I18n.t('error.nil') if email_address.nil? raise ArgumentError, I18n.t('error.invalid', value: email_address) unless Utils::Rfc5322::EMAIL.match email_address # thanks Peter! @email = email_address.strip.downcase @digest = Digest::MD5.hexdigest email @gravaty = email @parser = parser end # Returns a string containing the URI of the gravatar associated to # internal (provided) email address. Valid keys for the +args+ hash # are: :type, :pixel_size, :force, :secure, :rating, :default. # # Usage:: # - a_string = new_gravaty.avatar # - a_string = new_gravaty.avatar # - a_string = new_gravaty.avatar pixel_size: 42 # - a_string = new_gravaty.avatar pixel_size: 42, type: # png' # - a_string = new_gravaty.avatar secure: false # - a_string = new_gravaty.avatar type: 'jpg', force: # true # - a_string = new_gravaty.avatar secure: true, pixel_size: # 42, type: 'png' # - a_string = new_gravaty.avatar rating: pg, type: 'gif' # - a_string = new_gravaty.avatar default: monsterid, # pixel_size: 42 # Params:: # - +type+: [String] is the possibly desired specific image # format. Valid formats are jp(e)g, png or gif. Default to no # extension. Will be downcased. Raises ArgumentError # for invalid formats. # - +pixel_size+: [Integer] is the size in pixel of the image. # From 1 to 2048. Default to no value. Raises # ArgumentError for invalid sizes. # - +force+: [TrueClass || FalseClass] is a boolean to specify if # the default has to be forced when no image is found. Default to # false. # - +secure+: [TrueClass || FalseClass] is a boolean to specify # whether the resulting URL shall be HTTPS or HTTP type. Default to # true (HTTPS). # - +rating+: [String] is the rating type of the image. Valid # values are 'g', 'pg', 'r' and 'x'. # - +default+: [String] is the default type of the image (valid # values are '404', 'mm', 'identicon', 'monsterid', 'wavatar', # 'retro' and 'blank'), or the URI for an own default image. For the # URI validation see: # http://it.gravatar.com/site/implement/images/#default-image . # Returns:: a +String+ containing the Gravatar site URI with # specified options and configuration parameters. def avatar(args = {}) secure = true type = nil array = [] unless args.nil? type = args.fetch(:type, nil) secure = args.fetch(:secure, secure) [:pixelsize, :force, :default, :rating].each do |param| array << @parser.parse(param.to_s, args[param]) unless args[param].nil? end end build_uri(secure, true) + digest + @parser.parse('type', type) + build_query_string(array) end # See avatar method. This banged version saves the resulting string # as internal state. def avatar!(args = {}) @gravaty = avatar(args) end # Returns a string containing the URI of the gravatar profile # associated to internal (provided) email address. Valid keys for # the +args+ hash are: :secure, :format. # # Usage:: # - a_string = new_gravaty.profile # - a_string = new_gravaty.profile secure: false # - a_string = new_gravaty.profile format: 'php', secure: # true # Params:: # - +format+: [String] is the possibly desired specific profile # format. Valid formats are 'json', 'xml', 'php', 'vcf' and 'qr'. # Default to no extension. Will be downcased. Defaults to no # extension for invalid formats. # - +secure+: [TrueClass || FalseClass] is a boolean to specify # whether the resulting URL shall be HTTPS or HTTP type. Default to # true (HTTPS). # Returns:: a +String+ containing the Gravatar site URI with # specified options and configuration parameters. def profile(args = {}) secure = true format = nil array = [] unless args.nil? format = args[:format].downcase unless args[:format].nil? secure = args.fetch(:secure, secure) unless (format.nil?) and (PROFILES.include? format) selected = (format == 'json' ? 'callback' : 'pixelsize') array << @parser.parse(selected, args[selected.to_sym]) unless args[selected.to_sym].nil? end end build_uri(secure, false) + digest + @parser.parse('format', format) + build_query_string(array) end # See profile method. This banged version saves the resulting strin # as internal state. def profile!(args = {}) @gravaty = profile(args) end # Saves a file, with specified +filename+, that contains the current # gravaty configuration. Uses the internal state to retrieve data # from the URI. Defaults to 'gravaty' with no specific extension. # # Usage:: # - a_string = new_gravaty.download filename # Params:: # - +filename+: [String] is the filename to be saved. def download(filename = nil) filename = URI.parse(@gravaty).path.rpartition('/') .last if filename.nil? Utils::Downloader::Downloader .download_file @gravaty, filename # thanks Jon! end # See profile method. Customized version for QRCode-specific # requests. # # Usage:: # - a_string = new_gravaty.qrcode # - a_string = new_gravaty.qrcode pixel_size: 42 # - a_string = new_gravaty.qrcode secure: false, pixel_size: # 42 # Params:: # - +secure+: [TrueClass || FalseClass] is a boolean to specify # whether the resulting URL shall be HTTPS or HTTP type. Default to # true (HTTPS). # - +pixel_size+: [Integer] is the size in pixel of the image. # From 1 to 2048. Default to no value. Raises # ArgumentError for invalid sizes. # Returns:: a +String+ containing the Gravatar site URI with # specified options and configuration parameters, in QRCode format. def qrcode(args = {}) secure = true size = nil unless args.nil? size = args.fetch(:pixelsize, nil) secure = args.fetch(:secure, secure) end profile format: 'qr', secure: secure, pixelsize: size end # See qrcode method. This banged version saves the resulting string # as internal state. def qrcode!(args = {}) @gravaty = qrcode(args) end # See profile method. Customized version for JSON-specific requests. # # Usage:: # - a_string = new_gravaty.json # - a_string = new_gravaty.json callback: 'alert' # - a_string = new_gravaty.qrcode secure: false, callback: # 'alert' # Params:: # - +secure+: [TrueClass || FalseClass] is a boolean to specify # whether the resulting URL shall be HTTPS or HTTP type. Default to # true (HTTPS). # - +callback+: [String] See # https://secure.gravatar.com/site/implement/profiles/json/#request-options. # No check on parameter meaning or validity, except for +nil+. # Returns:: a +String+ containing the Gravatar site URI with # specified options and configuration parameters, in JSON format. def json(args = {}) secure = true callback = nil unless args.nil? callback = args.fetch(:callback, nil) secure = args.fetch(:secure, secure) end profile format: 'json', secure: secure, callback: callback end # See json method. This banged version saves the resulting string as # internal state. def json!(args = {}) @gravaty = json(args) end # Restores the original status cleaning up the (possibly) previously # saved URIs restoring the default +to_s+. def reset @gravaty = email end # Interfaces with the Gravatar XML-RPC API. # # Usage:: # - response = new_gravaty.xmlrpc 'grav.test', # my_password # Params:: # - +a_method+: [String] is a valid method supported by Gravatar # XML-RPC API. See https://en.gravatar.com/site/implement/xmlrpc/ . # - +password+: [String] is a valid password associated to user's # +email_address+ specified to build the Gravaty object. # - +args+: See https://en.gravatar.com/site/implement/xmlrpc/ for # possible parameters, depending on called method. # Returns:: See https://en.gravatar.com/site/implement/xmlrpc/ for # possible return values, depending on called method. def xmlrpc(a_method = RPC_TEST_METHOD, password = nil, args = {}) raise ArgumentError, I18n.t('error.nil') if password.nil? raise ArgumentError, I18n.t('error.invalid', value: a_method) unless RPC_METHODS.include? a_method @rpc_connector = Utils::RpcConnector::RpcConnector .new(digest, password) if @rpc_connector.nil? @rpc_connector.call a_method, args unless @rpc_connector.nil? end # Returns a JSon object representing the Gravaty object. # # # Usage:: # - a_json = new_gravaty.to_json # Returns:: a +JSON+ containing the Gravaty object representation # with currently saved options and configuration parameters. def to_json result = Hash.new result[email] = digest result.to_json end # Returns a string representing the Gravaty object. # # # Usage:: # - a_string = new_gravaty.to_s # Returns:: a +String+ containing the Gravaty object representation # with currently saved options and configuration parameters. def to_s @gravaty.to_s end def <=>(other_gravaty) @email <=> other_gravaty.email end # -------------------------- here starts the list of private methods private # Builds the query string with the array of parameters. def build_query_string(params_array = []) parameters = nil unless params_array.nil? parameters = params_array.reject {|p| p.nil? or p.empty?} .join('&') end return ('?' + parameters) unless parameters.nil? or parameters .empty? '' end # Return the basic URI structure according to type and security. def build_uri(secure = false, avatar = true) @parser.parse('secure', secure) + '.gravatar.com/' + @parser.parse('avatar', avatar) end end end