diff --git a/libravatar/README.md b/libravatar/README.md new file mode 100644 index 00000000..16fbd85a --- /dev/null +++ b/libravatar/README.md @@ -0,0 +1,36 @@ +# Libravatar Plugin +by [Klaus Weidenbach](http://friendica.dszdw.net/profile/klaus) + +This addon allows you to look up an avatar image for new users and contacts at [Libravatar](http://www.libravatar.com). It will be used if there have not been found any other avatar images yet for example through OpenID. + +Libravatar is a free and open replacement for Gravatar. It is a service where people can store an avatar image for their email-addresses. These avatar images can get looked up for example in comment functions, profile pages, etc. on other sites. There exists a central installation at [www.libravatar.com](http://www.libravatar.com), but you can also host it on your own server. If no avatar was found Libravatar will look up at Gravatar as a fallback. +There is no rating available, as it is on Gravatar, so all avatar lookups are g-rated. (Suitable for all audiences.) + +PHP >= 5.3 is required for this plugin! + +You can not use the Libravatar and Gravatar addon at the same time. You need to choose one. If you need other ratings than g you better stay with Gravatar, otherwise it is safe to use Libravatar, because it will fall back to Gravatar if nothing was found at Libravatar. + +* * * + +# Configuration +## Default Avatar Image +If no avatar was found for an email Libravatar can create some pseudo-random generated avatars based on an email hash. You can choose between these presets: + +* __MM__: (mystery-man) a static image +* __Identicon__: a generated geometric pattern based on email hash +* __Monsterid__: a generated 'monster' with different colors, faces, etc. based on email hash +* __Wavatar__: faces with different features and backgrounds based on email hash +* __Retro__: 8-bit arcade-styled pixelated faces based on email hash + +See examples at [Libravatar][1]. + +## Alternative Configuration +Open the .htconfig.php file and add "libravatar" to the list of activated addons: + + $a->config['system']['addon'] = "..., libravatar"; + +You can add one configuration variable for the addon: + + $a->config['libravatar']['default_avatar'] = "identicon"; + +[1]: http://wiki.libravatar.org/api/ "See API documentation at Libravatar for more information" diff --git a/libravatar/Services/Libravatar.php b/libravatar/Services/Libravatar.php new file mode 100644 index 00000000..d3024408 --- /dev/null +++ b/libravatar/Services/Libravatar.php @@ -0,0 +1,372 @@ + + * @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 + */ + +/** + * 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'); + * + * + * 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); + * + * + * @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: + * @link http://pear.php.net/package/Services_Libravatar + * @since Class available since Release 0.1.0 + */ +class Services_Libravatar +{ + + /** + * 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) s or size, (string) d or default + * + * @return string A string of a full URL for an avatar image + * + * @since Method available since Release 0.1.0 + */ + public function url($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); + } + + // Get the domain so we can determine the SRV stuff for federation + $domain = $this->domainGet($identifier, $https); + + // 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'; + } + + // 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); + } + + // Compose the URL from the pieces we generated + $url = $protocol . '://' . $service . '/avatar/' . $identiferHash . $params; + + // Return the URL string + return $url; + + } + + /** + * Create a hash of the identifier. + * + * 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 $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 + */ + protected function identiferHash($identifier, $https = false, $hash = 'md5') + { + + if (filter_var($identifier, FILTER_VALIDATE_EMAIL)) { + // If email, we can select our algorithm. Default to md5 for + // gravatar fallback. + return hash($hash, $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); + } + } + } + + /** + * 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 + * @param boolean $https If this is https, true. + * + * @return string A string of the domain to use + * + * @since Method available since Release 0.1.0 + */ + protected function domainGet($identifier, $https = false) + { + + // 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; + } + } + } + + /** + * 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 + * + * @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 + * + * @return string The target URL. + * + * @since Method available since Release 0.1.0 + */ + protected function srvGet($domain, $https = false) + { + + // Are we going secure? Set up a fallback too. + if (isset($https) && $https === true) { + $subdomain = '_avatars-sec._tcp.'; + $fallback = 'seccdn.'; + } else { + $subdomain = '_avatars._tcp.'; + $fallback = 'cdn.'; + } + + // Lets try get us some records based on the choice of subdomain + // and the domain we had passed in. + $srv = dns_get_record($subdomain . $domain, DNS_SRV); + + // Did we get anything? No? + if (count($srv) == 0) { + // Then let's try Libravatar.org. + return $fallback . 'libravatar.org'; + } + + // Sort by the priority. We must get the lowest. + usort($srv, array($this, 'comparePriority')); + + $top = $srv[0]; + $sum = 0; + + // Try to adhere to RFC2782's weighting algorithm, page 3 + // "arrange all SRV RRs (that have not been ordered yet) in any order, + // except that all those with weight 0 are placed at the beginning of + // the list." + shuffle($srv); + $srvs = array(); + foreach ($srv as $s) { + if ($s['weight'] == 0) { + array_unshift($srvs, $s); + } else { + array_push($srvs, $s); + } + } + + foreach ($srvs as $s) { + if ($s['pri'] == $top['pri']) { + // "Compute the sum of the weights of those RRs" + $sum += (int) $s['weight']; + // "and with each RR associate the running sum in the selected + // order." + $pri[$sum] = $s; + } + } + + // "Then choose a uniform random number between 0 and the sum computed + // (inclusive)" + $random = rand(0, $sum); + + // "and select the RR whose running sum value is the first in the selected + // order which is greater than or equal to the random number selected" + foreach ($pri as $k => $v) { + if ($k >= $random) { + return $v['target']; + } + } + } + + /** + * Sorting function for record priorities. + * + * @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 + * + * @since Method available since Release 0.1.0 + */ + protected function comparePriority($a, $b) + { + return $a['pri'] - $b['pri']; + } + +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * c-hanging-comment-ender-p: nil + * End: + */ + +?> + diff --git a/libravatar/admin.tpl b/libravatar/admin.tpl new file mode 100644 index 00000000..814f4a44 --- /dev/null +++ b/libravatar/admin.tpl @@ -0,0 +1,2 @@ +{{ inc field_select.tpl with $field=$default_avatar}}{{ endinc }} +
diff --git a/libravatar/libravatar.php b/libravatar/libravatar.php new file mode 100644 index 00000000..1705a9c1 --- /dev/null +++ b/libravatar/libravatar.php @@ -0,0 +1,115 @@ += 5.3) + * Version: 1.0 + * Author: Klaus Weidenbach + */ + +/** + * Installs the plugin hook + */ +function libravatar_install() { + if (! version_compare(PHP_VERSION, '5.3.0', '>=')) { + info(t('Could NOT install Libravatar successfully.
It requires PHP >= 5.3') .EOL); + // avoid registering the hook + return false; + } + register_hook('avatar_lookup', 'addon/libravatar/libravatar.php', 'libravatar_lookup'); + + logger("registered libravatar in avatar_lookup hook"); +} + +/** + * Removes the plugin hook + */ +function libravatar_uninstall() { + unregister_hook('avatar_lookup', 'addon/libravatar/libravatar.php', 'libravatar_lookup'); + + logger("uninstalled libravatar"); +} + +/** + * Looks up the avatar at Libravatar and returns the URL. + * + * @param $a array + * @param &$b array + */ +function libravatar_lookup($a, &$b) { + $default_avatar = get_config('libravatar', 'default_img'); + + if (! $default_avatar) { + // if not set, look up if there was one from the gravatar addon + $default_avatar = get_config('gravatar', 'default_img'); + // setting default avatar if nothing configured + if (! $default_avatar) + $default_avatar = 'identicon'; // default image will be a random pattern + } + + 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); + + $b['url'] = $avatar_url; + $b['success'] = true; +} + +/** + * Display admin settings for this addon + */ +function libravatar_plugin_admin (&$a, &$o) { + $t = file_get_contents( dirname(__file__)."/admin.tpl"); + + $default_avatar = get_config('libravatar', 'default_img'); + + // set default values for first configuration + if(! $default_avatar) + $default_avatar = 'identicon'; // pseudo-random geometric pattern based on email hash + + // Available options for the select boxes + $default_avatars = array( + 'mm' => t('generic profile image'), + 'identicon' => t('random geometric pattern'), + 'monsterid' => t('monster face'), + 'wavatar' => t('computer generated face'), + 'retro' => t('retro arcade style face'), + ); + + // 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.') .'


'; + return; + } + + // Libravatar falls back to gravatar, so show warning about gravatar addon if enabled + $r = q("SELECT * FROM `addon` WHERE `name` = '%s' and `installed` = 1", + 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.') .'



'; + } + + // output Libravatar settings + $o .= ''; + $o .= replace_macros( $t, array( + '$submit' => t('Submit'), + '$default_avatar' => array('avatar', t('Default avatar image'), $default_avatar, t('Select default avatar image if none was found. See README'), $default_avatars), + )); +} + +/** + * Save admin settings + */ +function libravatar_plugin_admin_post (&$a) { + check_form_security_token('libravatarrsave'); + + $default_avatar = ((x($_POST, 'avatar')) ? notags(trim($_POST['avatar'])) : 'identicon'); + set_config('libravatar', 'default_img', $default_avatar); + info(t('Libravatar settings updated.') .EOL); +} +?>