diff --git a/libravatar.tgz b/libravatar.tgz new file mode 100644 index 00000000..0405af9b Binary files /dev/null and b/libravatar.tgz differ diff --git a/libravatar/Services/Libravatar.php b/libravatar/Services/Libravatar.php index d3024408..1b6d022e 100644 --- a/libravatar/Services/Libravatar.php +++ b/libravatar/Services/Libravatar.php @@ -1,7 +1,5 @@ * @copyright 2011 Services_Libravatar committers. * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version SVN: * @link http://pear.php.net/package/Services_Libravatar * @since File available since Release 0.1.0 */ @@ -43,240 +40,364 @@ * PHP support for the Libravatar.org service. * * Using this class is easy. After including or requiring - * PEAR/libravatar.php simply do: - * - * $libravatar = new Services_Libravatar(); - * $url = $libravatar->url('melissa@meldraweb.com'); - * + * Services/Libravatar.php simply do: + * + * $libravatar = new Services_Libravatar(); + * $url = $libravatar->getUrl('melissa@meldraweb.com'); + * * - * This would populate $url with the string: - * http://cdn.libravatar.org/avatar/4db84629c121f2d443d33bdb9fd149bc + * This would populate $url with the string: + * + * http://cdn.libravatar.org/avatar/4db84629c121f2d443d33bdb9fd149bc + * * * A complicated lookup using all the options is: - * - * $libravatar = new Services_Libravatar(); - * $options = array(); - * $options['s'] = '40'; - * $options['algorithm'] = 'sha256'; - * $options['https'] = true; - * $options['d'] = 'http://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png'; - * $url = $libravatar->url('melissa@meldraweb.com', $options); - * + * + * $libravatar = new Services_Libravatar(); + * $libravatar->setSize(40); + * $libravatar->setAlgorithm('sha256'); + * $libravatar->setHttps(true); + * $libravatar->setDefault( + * 'http://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png' + * ); + * $url = $libravatar->getUrl('melissa@meldraweb.com'); + * * * @category Services * @package Services_Libravatar * @author Melissa Draper * @copyright 2011 Services_Libravatar committers. * @license http://www.opensource.org/licenses/mit-license.html MIT License - * @version Release: + * @version Release: 0.2.1 * @link http://pear.php.net/package/Services_Libravatar * @since Class available since Release 0.1.0 */ class Services_Libravatar { + /** + * Hashing algorithm to use + * + * @var string + * @see processAlgorithm() + * @see setAlgorithm() + */ + protected $algorithm = 'md5'; /** - * Composes a URL for the identifier and options passed in + * Default image URL to use * - * Compose a full URL as specified by the Libravatar API, based on the - * email address or openid URL passed in, and the options specified. + * @var string + * @see processDefault() + * @see setDefault() + */ + protected $default; + + /** + * If HTTPS URLs should be used * - * @param string $identifier a string of either an email address - * or an openid url - * @param array $options an array of (bool) https, (string) algorithm - * (string) s or size, (string) d or default + * @var boolean + * @see detectHttps() + * @see setHttps() + */ + protected $https; + + /** + * Image size in pixels * - * @return string A string of a full URL for an avatar image + * @var integer + * @see processSize() + * @see setSize() + */ + protected $size; + + + /** + * Composes a URL for the identifier and options passed in * - * @since Method available since Release 0.1.0 + * Compose a full URL as specified by the Libravatar API, based on the + * email address or openid URL passed in, and the options specified. + * + * @param string $identifier a string of either an email address + * or an openid url + * @param array $options an array of (bool) https, (string) algorithm + * (string) size, (string) default. + * See the set* methods. + * + * @return string A string of a full URL for an avatar image + * + * @since Method available since Release 0.2.0 + * @deprecated Use getUrl() instead */ public function url($identifier, $options = array()) { + return $this->getUrl($identifier, $options); + } + /** + * Composes a URL for the identifier and options passed in + * + * Compose a full URL as specified by the Libravatar API, based on the + * email address or openid URL passed in, and the options specified. + * + * @param string $identifier a string of either an email address + * or an openid url + * @param array $options an array of (bool) https, (string) algorithm + * (string) size, (string) default. + * See the set* methods. + * + * @return string A string of a full URL for an avatar image + * + * @since Method available since Release 0.2.0 + * @throws InvalidArgumentException When an invalid option is passed + */ + public function getUrl($identifier, $options = array()) + { // If no identifier has been passed, set it to a null. // This way, there'll always be something returned. if (!$identifier) { $identifier = null; - } - - $https = null; - if (isset($options['https']) && $options['https'] === true) { - $https = true; - } - - // If the algorithm has been passed in $options, send it on. - // This will only affect email functionality. - if (isset($options['algorithm']) && is_string($options['algorithm'])) { - $identiferHash = $this->identiferHash( - $identifier, - $https, - $options['algorithm'] - ); } else { - $identiferHash = $this->identiferHash($identifier, $https); + $identifier = $this->normalizeIdentifier($identifier); } + // Load all options + $options = $this->checkOptionsArray($options); + $https = $this->https; + if (isset($options['https'])) { + $https = (bool)$options['https']; + } + + $algorithm = $this->algorithm; + if (isset($options['algorithm'])) { + $algorithm = $this->processAlgorithm($options['algorithm']); + } + + $default = $this->default; + if (isset($options['default'])) { + $default = $this->processDefault($options['default']); + } + $size = $this->size; + if (isset($options['size'])) { + $size = $this->processSize($options['size']); + } + + + $identifierHash = $this->identifierHash($identifier, $algorithm); + // Get the domain so we can determine the SRV stuff for federation - $domain = $this->domainGet($identifier, $https); + $domain = $this->domainGet($identifier); // If https has been specified in $options, make sure we make the // correct SRV lookup - if (isset($options['https']) && $options['https'] === true) { - $service = $this->srvGet($domain, true); - $protocol = 'https'; - } else { - $service = $this->srvGet($domain); - $protocol = 'http'; + $service = $this->srvGet($domain, $https); + $protocol = $https ? 'https' : 'http'; + + $params = array(); + if ($size !== null) { + $params['size'] = $size; } - - // We no longer need these, and they will pollute our query string - unset($options['algorithm']); - unset($options['https']); - - // If there are any $options left, we want to make those into a query - $params = null; - if (count($options) > 0) { - $params = '?' . http_build_query($options); + if ($default !== null) { + $params['default'] = $default; + } + $paramString = ''; + if (count($params) > 0) { + $paramString = '?' . http_build_query($params); } // Compose the URL from the pieces we generated - $url = $protocol . '://' . $service . '/avatar/' . $identiferHash . $params; + $url = $protocol . '://' . $service . '/avatar/' . $identifierHash + . $paramString; // Return the URL string return $url; - } /** - * Create a hash of the identifier. + * Checks the options array and verify that only allowed options are in it. * - * Create a hash of the email address or openid passed in. Algorithm - * used for email address ONLY can be varied. Either md5 or sha256 - * are supported by the Libravatar API. Will be ignored for openid. + * @param array $options Array of options for getUrl() * - * @param string $identifier A string of the email address or openid URL - * @param boolean $https If this is https, true. - * @param string $hash A string of the hash algorithm type to make - * - * @return string A string hash of the identifier. - * - * @since Method available since Release 0.1.0 + * @return void + * @throws Exception When an invalid option is used */ - protected function identiferHash($identifier, $https = false, $hash = 'md5') + protected function checkOptionsArray($options) { + //this short options are deprecated! + if (isset($options['s'])) { + $options['size'] = $options['s']; + unset($options['s']); + } + if (isset($options['d'])) { + $options['default'] = $options['d']; + unset($options['d']); + } + $allowedOptions = array( + 'algorithm' => true, + 'default' => true, + 'https' => true, + 'size' => true, + ); + foreach ($options as $key => $value) { + if (!isset($allowedOptions[$key])) { + throw new InvalidArgumentException( + 'Invalid option in array: ' . $key + ); + } + } + + return $options; + } + + /** + * Normalizes the identifier (E-mail address or OpenID) + * + * @param string $identifier E-Mail address or OpenID + * + * @return string Normalized identifier + */ + protected function normalizeIdentifier($identifier) + { if (filter_var($identifier, FILTER_VALIDATE_EMAIL)) { - // If email, we can select our algorithm. Default to md5 for - // gravatar fallback. - return hash($hash, $identifier); + return strtolower($identifier); } else { - - // The protocol is important. If we're lacking it this will not be - // filtered. Add it per our preference in the options. - if (stripos($identifier, 'http') !== 0) { - if ($https === true) { - $protocol = 'https://'; - } else { - $protocol = 'http://'; - } - $identifier = $protocol . $identifier; - } - - // Is this an email address or an OpenID account - $filter = filter_var( - $identifier, - FILTER_VALIDATE_URL, - FILTER_FLAG_PATH_REQUIRED - ); - - if ($filter) { - // If this is an OpenID, split the string and make sure the - // formatting is correct. See the Libravatar API for more info. - // http://wiki.libravatar.org/api/ - $url = parse_url($identifier); - $hashurl = strtolower($url['scheme']) . '://' . - strtolower($url['host']); - if (isset($url['port']) && $url['scheme'] === 'http' - && $url['port'] != 80 - || isset($url['port']) && $url['scheme'] === 'https' - && $url['port'] != 443 - ) { - $hashurl .= ':' . $url['port']; - } - $hashurl .= $url['path']; - return hash('sha256', $hashurl); - } + return self::normalizeOpenId($identifier); } } /** - * Grab the domain from the identifier. + * Create a hash of the identifier. * - * Extract the domain from the Email or OpenID. + * Create a hash of the email address or openid passed in. Algorithm + * used for email address ONLY can be varied. Either md5 or sha256 + * are supported by the Libravatar API. Will be ignored for openid. * - * @param string $identifier A string of the email address or openid URL - * @param boolean $https If this is https, true. + * @param string $identifier A string of the email address or openid URL + * @param string $hash A string of the hash algorithm type to make + * Uses the php implementation of hash() + * MD5 preferred for Gravatar fallback * - * @return string A string of the domain to use + * @return string A string hash of the identifier. * - * @since Method available since Release 0.1.0 + * @since Method available since Release 0.1.0 */ - protected function domainGet($identifier, $https = false) + protected function identifierHash($identifier, $hash = 'md5') { + if (filter_var($identifier, FILTER_VALIDATE_EMAIL) || $identifier === null) { + // If email, we can select our algorithm. Default to md5 for + // gravatar fallback. + return hash($hash, $identifier); + } + + //no email, so the identifier has to be an OpenID + return hash('sha256', $identifier); + } + + /** + * Normalizes an identifier (URI or XRI) + * + * @param mixed $identifier URI or XRI to be normalized + * + * @return string Normalized Identifier. + * Empty string when the OpenID is invalid. + * + * @internal Adapted from OpenID::normalizeIdentifier() + */ + public static function normalizeOpenId($identifier) + { + // XRI + if (preg_match('@^xri://@i', $identifier)) { + return preg_replace('@^xri://@i', '', $identifier); + } + + if (in_array($identifier[0], array('=', '@', '+', '$', '!'))) { + return $identifier; + } + + // URL + if (!preg_match('@^http[s]?://@i', $identifier)) { + $identifier = 'http://' . $identifier; + } + if (strpos($identifier, '/', 8) === false) { + $identifier .= '/'; + } + if (!filter_var($identifier, FILTER_VALIDATE_URL)) { + return ''; + } + + $parts = parse_url($identifier); + $parts['scheme'] = strtolower($parts['scheme']); + $parts['host'] = strtolower($parts['host']); + + //http://openid.net/specs/openid-authentication-2_0.html#normalization + return $parts['scheme'] . '://' + . (isset($parts['user']) ? $parts['user'] : '') + . (isset($parts['pass']) ? ':' . $parts['pass'] : '') + . (isset($parts['user']) || isset($parts['pass']) ? '@' : '') + . $parts['host'] + . ( + (isset($parts['port']) + && $parts['scheme'] === 'http' && $parts['port'] != 80) + || (isset($parts['port']) + && $parts['scheme'] === 'https' && $parts['port'] != 443) + ? ':' . $parts['port'] : '' + ) + . $parts['path'] + . (isset($parts['query']) ? '?' . $parts['query'] : ''); + //leave out fragment as requested by the spec + } + + /** + * Grab the domain from the identifier. + * + * Extract the domain from the Email or OpenID. + * + * @param string $identifier A string of the email address or openid URL + * + * @return string A string of the domain to use + * + * @since Method available since Release 0.1.0 + */ + protected function domainGet($identifier) + { + if ($identifier === null) { + return null; + } // What are we, email or openid? Split ourself up and get the // important bit out. if (filter_var($identifier, FILTER_VALIDATE_EMAIL)) { $email = explode('@', $identifier); return $email[1]; - } else { - - // The protocol is important. If we're lacking it this will not be - // filtered. Add it per our preference in the options. - if ( ! strpos($identifier, 'http')) { - if ($https === true) { - $protocol = 'https://'; - } else { - $protocol = 'http://'; - } - $identifier = $protocol . $identifier; - } - - $filter = filter_var( - $identifier, - FILTER_VALIDATE_URL, - FILTER_FLAG_PATH_REQUIRED - ); - - if ($filter) { - $url = parse_url($identifier); - $domain = $url['host']; - if (isset($url['port']) && $url['scheme'] === 'http' - && $url['port'] != 80 - || isset($url['port']) && $url['scheme'] === 'https' - && $url['port'] != 443 - ) { - $domain .= ':' . $url['port']; - } - - return $domain; - } } + + //OpenID + $url = parse_url($identifier); + $domain = $url['host']; + if (isset($url['port']) && $url['scheme'] === 'http' + && $url['port'] != 80 + || isset($url['port']) && $url['scheme'] === 'https' + && $url['port'] != 443 + ) { + $domain .= ':' . $url['port']; + } + + return $domain; } /** - * Get the target to use. + * Get the target to use. * - * Get the SRV record, filtered by priority and weight. If our domain - * has no SRV records, fall back to Libravatar.org + * Get the SRV record, filtered by priority and weight. If our domain + * has no SRV records, fall back to Libravatar.org * - * @param string $domain A string of the domain we extracted from the - * provided identifer with domainGet() - * @param boolean $https Whether or not to look for https records + * @param string $domain A string of the domain we extracted from the + * provided identifier with domainGet() + * @param boolean $https Whether or not to look for https records * - * @return string The target URL. + * @return string The target URL. * - * @since Method available since Release 0.1.0 + * @since Method available since Release 0.1.0 */ protected function srvGet($domain, $https = false) { @@ -344,20 +465,197 @@ class Services_Libravatar } /** - * Sorting function for record priorities. + * Sorting function for record priorities. * - * @param mixed $a A mixed value passed by usort() - * @param mixed $b A mixed value passed by usort() + * @param mixed $a A mixed value passed by usort() + * @param mixed $b A mixed value passed by usort() * - * @return mixed The result of the comparison + * @return mixed The result of the comparison * - * @since Method available since Release 0.1.0 + * @since Method available since Release 0.1.0 */ protected function comparePriority($a, $b) { return $a['pri'] - $b['pri']; } + /** + * Automatically set the https option depending on the current connection + * value. + * + * If the current connection is HTTPS, the https options is activated. + * If it is not HTTPS, the https option is deactivated. + * + * @return self + */ + public function detectHttps() + { + $this->setHttps( + isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] + ); + + return $this; + } + + /** + * Verify and cast the email address hashing algorithm to use. + * + * @param string $algorithm Algorithm to use, "sha256" or "md5". + * + * @return string Algorithm + * + * @throws InvalidArgumentException When an unsupported algorithm is given + */ + protected function processAlgorithm($algorithm) + { + $algorithm = (string)$algorithm; + if ($algorithm !== 'md5' && $algorithm !== 'sha256') { + throw new InvalidArgumentException( + 'Only md5 and sha256 hashing supported' + ); + } + + return $algorithm; + } + + /** + * Verify and cast the default URL to use when no avatar image can be found. + * If none is set, the libravatar logo is returned. + * + * @param string $url Full URL to use OR one of the following: + * - "404" - give a "404 File not found" instead of an image + * - "mm" + * - "identicon" + * - "monsterid" + * - "wavatar" + * - "retro" + * + * @return string Default URL + * + * @throws InvalidArgumentException When an invalid URL is given + */ + protected function processDefault($url) + { + if ($url === null) { + return $url; + } + + $url = (string)$url; + + switch ($url) { + case '404': + case 'mm': + case 'identicon': + case 'monsterid': + case 'wavatar': + case 'retro': + break; + default: + $valid = filter_var($url, FILTER_VALIDATE_URL); + if (!$valid) { + throw new InvalidArgumentException('Invalid default avatar URL'); + } + break; + } + + return $url; + } + + /** + * Verify and cast the required size of the images. + * + * @param integer $size Size (width and height in pixels) of the image. + * NULL for the default width. + * + * @return integer Size + * + * @throws InvalidArgumentException When a size <= 0 is given + */ + protected function processSize($size) + { + if ($size === null) { + return $size; + } + + $size = (int)$size; + if ($size <= 0) { + throw new InvalidArgumentException('Size has to be larger than 0'); + } + + return (int)$size; + } + + + /** + * Set the email address hashing algorithm to use. + * To keep gravatar compatibility, use "md5". + * + * @param string $algorithm Algorithm to use, "sha256" or "md5". + * + * @return self + * @throws InvalidArgumentException When an unsupported algorithm is given + */ + public function setAlgorithm($algorithm) + { + $this->algorithm = $this->processAlgorithm($algorithm); + + return $this; + } + + /** + * Set the default URL to use when no avatar image can be found. + * If none is set, the gravatar logo is returned. + * + * @param string $url Full URL to use OR one of the following: + * - "404" - give a "404 File not found" instead of an image + * - "mm" + * - "identicon" + * - "monsterid" + * - "wavatar" + * - "retro" + * + * @return self + * @throws InvalidArgumentException When an invalid URL is given + */ + public function setDefault($url) + { + $this->default = $this->processDefault($url); + + return $this; + } + + /** + * Set if HTTPS URLs shall be returned. + * + * @param boolean $useHttps If HTTPS url shall be returned + * + * @return self + * + * @see detectHttps() + */ + public function setHttps($useHttps) + { + $this->https = (bool)$useHttps; + + return $this; + } + + /** + * Set the required size of the images. + * Every avatar image is square sized, which means you need to set only number. + * + * @param integer $size Size (width and height) of the image + * + * @return self + * @throws InvalidArgumentException When a size <= 0 is given + */ + public function setSize($size) + { + $this->size = $this->processSize($size); + + return $this; + } + } /* @@ -369,4 +667,3 @@ class Services_Libravatar */ ?> - diff --git a/libravatar/libravatar.php b/libravatar/libravatar.php index 1705a9c1..08ed6d00 100644 --- a/libravatar/libravatar.php +++ b/libravatar/libravatar.php @@ -2,7 +2,7 @@ /** * Name: Libravatar Support * Description: If there is no avatar image for a new user or contact this plugin will look for one at Libravatar. Please disable Gravatar addon if you use this one. (requires PHP >= 5.3) - * Version: 1.0 + * Version: 1.1 * Author: Klaus Weidenbach */ @@ -26,7 +26,7 @@ function libravatar_install() { function libravatar_uninstall() { unregister_hook('avatar_lookup', 'addon/libravatar/libravatar.php', 'libravatar_lookup'); - logger("uninstalled libravatar"); + logger("unregistered libravatar in avatar_lookup hook"); } /** @@ -48,10 +48,9 @@ function libravatar_lookup($a, &$b) { require_once 'Services/Libravatar.php'; $libravatar = new Services_Libravatar(); - $options = array(); - $options['s'] = $b['size']; - $options['d'] = $default_avatar; - $avatar_url = $libravatar->url($b['email'], $options); + $libravatar->setSize($b['size']); + $libravatar->setDefault($default_avatar); + $avatar_url = $libravatar->getUrl($b['email']); $b['url'] = $avatar_url; $b['success'] = true; @@ -81,8 +80,8 @@ function libravatar_plugin_admin (&$a, &$o) { // Show warning if PHP version is too old if (! version_compare(PHP_VERSION, '5.3.0', '>=')) { $o = '
' .t('Warning') .'

'; - $o .= sprintf(t('Your PHP version %s is lower than the required PHP 5.3.'), PHP_VERSION); - $o .= '
' .t('This addon is not functional on you server.') .'


'; + $o .= sprintf(t('Your PHP version %s is lower than the required PHP >= 5.3.'), PHP_VERSION); + $o .= '
' .t('This addon is not functional on your server.') .'


'; return; } @@ -91,7 +90,7 @@ function libravatar_plugin_admin (&$a, &$o) { dbesc('gravatar') ); if (count($r)) { - $o = '

' .t('Information') .'

' .t('Gravatar addon is installed. Please disable the gravatar addon.
The Libravatar addon will fall back to gravatar if nothing was found at libravatar.') .'



'; + $o = '
' .t('Information') .'

' .t('Gravatar addon is installed. Please disable the Gravatar addon.
The Libravatar addon will fall back to Gravatar if nothing was found at Libravatar.') .'



'; } // output Libravatar settings