pax_global_header00006660000000000000000000000064117447064210014520gustar00rootroot0000000000000052 comment=399a8097252c3beb4bedab3e19a46a8550450cb9 fmarier-node-libravatar-399a809/000077500000000000000000000000001174470642100165055ustar00rootroot00000000000000fmarier-node-libravatar-399a809/.gitignore000066400000000000000000000000351174470642100204730ustar00rootroot00000000000000/node_modules /npm-debug.log fmarier-node-libravatar-399a809/Changelog.txt000066400000000000000000000007521174470642100211410ustar00rootroot000000000000002.0.0 (2012-04-22): - Backwards-incompatible API change for the url() function - Minor style improvements in the code 1.1.1 (2012-03-02): - Minor code cleanups from Andy Chilton - Add missing dev dependency on tap 1.1.0 (2011-11-20): - Fix federation if more than one SRV record is present - Fix bugs in parameter validation and URL normalization - Add unit tests 1.0.1 (2011-10-17): - Add an error param to the callback - Style improvements 1.0.0 (2011-10-16) - Initial public release fmarier-node-libravatar-399a809/Makefile000066400000000000000000000003441174470642100201460ustar00rootroot00000000000000all: announce: interactive-freecode-submit node-libravatar test: @( test -x /usr/bin/json_verify && json_verify < package.json ) || true npm test upload: test HTTP_PROXY= http_proxy= HTTPS_PROXY= https_proxy= npm publish fmarier-node-libravatar-399a809/README.md000066400000000000000000000040231174470642100177630ustar00rootroot00000000000000# node-libravatar Here is an easy way to make use of the federated [Libravatar](http://www.libravatar.org) avatar hosting service from within your node.js applications. It is inspired by [Emerson Macedo](http://codificando.com/)'s [Gravatar library](https://github.com/emerleite/node-gravatar). See the [project page](https://github.com/fmarier/node-libravatar) for the issue tracker and downloads. ## Instalation To install using npm, simply do this: $ npm install libravatar ## Usage To generate the correct avatar URL based on someone's email address, use the following: var libravatar = require('libravatar'); libravatar.url({ email: 'person@example.com', size: 96, default: 'mm', https: false }, function (error, avatar_url) { console.log(''); }); See the [Libravatar documentation](http://wiki.libravatar.org/api) for more information on the special values for the "default" parameter. ## License Copyright (C) 2011, 2012 Francois Marier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fmarier-node-libravatar-399a809/index.js000066400000000000000000000000561174470642100201530ustar00rootroot00000000000000module.exports = require('./lib/libravatar'); fmarier-node-libravatar-399a809/lib/000077500000000000000000000000001174470642100172535ustar00rootroot00000000000000fmarier-node-libravatar-399a809/lib/libravatar.js000066400000000000000000000161161174470642100217450ustar00rootroot00000000000000/* * node-libravatar: node.js module for Libravatar * * Copyright (C) 2011, 2012 Francois Marier * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE." */ const crypto = require('crypto'), dns = require('dns'), querystring = require('querystring'), url = require('url'); const BASE_URL = 'http://cdn.libravatar.org/avatar/'; const SECURE_BASE_URL = 'https://seccdn.libravatar.org/avatar/'; const SERVICE_BASE = '_avatars._tcp'; const SECURE_SERVICE_BASE = '_avatars-sec._tcp'; /* * Return the right (target, port) pair from a list of SRV records. */ function srv_hostname(records) { if (records.length < 1) { return [null, null]; } if (1 === records.length) { return [ records[0]['name'], records[0]['port'] ]; } // Keep only the servers in the top priority var priority_records = []; var total_weight = 0; var top_priority = records[0]['priority']; // highest priority = lowest number records.forEach(function (srv_record) { if (srv_record['priority'] <= top_priority) { // ignore lower priority records if (srv_record['priority'] < top_priority) { // reset the array (srv_record has higher priority) top_priority = srv_record['priority']; total_weight = 0; priority_records = []; } total_weight += srv_record['weight']; if (srv_record['weight'] > 0) { priority_records.push([total_weight, srv_record]); } else { // zero-weigth elements must come first priority_records.unshift([0, srv_record]); } } }); if (1 === priority_records.length) { var srv_record = priority_records[0][1]; return [ srv_record['name'], srv_record['port'] ]; } // Select first record according to RFC2782 weight // ordering algorithm (page 3) var random_number = Math.floor(Math.random() * (total_weight + 1)); for (var i = 0; i < priority_records.length; i++) { var weighted_index = priority_records[i][0]; var target = priority_records[i][1]; if (weighted_index >= random_number) { return [ target['name'], target['port'] ]; } } console.log('There is something wrong with our SRV weight ordering algorithm'); return [null, null]; } /* * Ensure we are getting a (mostly) valid hostname and port number * from the DNS resolver and return the final hostname:port string. */ function sanitized_target(target_components, https) { var target = target_components[0]; var port = parseInt(target_components[1]); if (target === null || isNaN(port)) { return null; } if (port < 1 || port > 65535) { return null; } if (target.search(/^[0-9a-zA-Z\-.]+$/) === -1) { return null; } if (target && ((https && port != 443) || (!https && port != 80))) { return target + ':' + port; } else { return target; } } /* * Generate user hash based on the email address or OpenID and return * it along with the relevant domain. */ function parse_user_identity(email, openid) { var hash = null, domain = null; if (email != null) { var lowercase_value = email.trim().toLowerCase(); var email_parts = lowercase_value.split('@'); if (email_parts.length > 1) { domain = email_parts[email_parts.length - 1]; hash = crypto.createHash('md5').update(lowercase_value).digest('hex'); } } else if (openid != null) { var parsed_url = url.parse(openid); if (parsed_url.protocol && parsed_url.hostname) { var normalized_url = parsed_url.protocol.toLowerCase(); normalized_url += parsed_url.slashes ? '//' : ''; if (parsed_url.auth) { normalized_url += parsed_url.auth + '@'; } normalized_url += parsed_url.hostname.toLowerCase(); normalized_url += parsed_url.pathname; domain = parsed_url.hostname.toLowerCase(); hash = crypto.createHash('sha256').update(normalized_url).digest('hex'); } } return [hash, domain]; } /* * Return the DNS service to query for a given domain and scheme. */ function service_name(domain, https) { if (domain) { return (https ? SECURE_SERVICE_BASE : SERVICE_BASE) + '.' + domain; } return null; } /* * Assemble the final avatar URL based on the provided components. */ function compose_avatar_url(delegation_server, avatar_hash, query_string, https) { var base_url = (https && SECURE_BASE_URL) || BASE_URL; if (delegation_server) { if (https) { base_url = 'https://' + delegation_server + '/avatar/'; } else { base_url = 'http://' + delegation_server + '/avatar/'; } } return base_url + avatar_hash + query_string; } var libravatar = module.exports = { // These ones are exported for the unit tests only sanitized_target: sanitized_target, srv_hostname: srv_hostname, parse_user_identity: parse_user_identity, service_name: service_name, compose_avatar_url: compose_avatar_url, url: function (options, callback) { var identity = parse_user_identity(options.email, options.openid); var hash = identity[0]; var domain = identity[1]; var https = options.https || false; if (hash) { delete(options.email); delete(options.openid); delete(options.https); var query_data = querystring.stringify(options); var query = (query_data && "?" + query_data) || ""; dns.resolveSrv(service_name(domain, https), function (err, addresses) { var delegation_server; if (null === err) { delegation_server = sanitized_target(srv_hostname(addresses), https); } callback(null, compose_avatar_url(delegation_server, hash, query, https)); }); } else { callback('An email or an OpenID must be provided.', null); } } }; fmarier-node-libravatar-399a809/package.json000066400000000000000000000011071174470642100207720ustar00rootroot00000000000000{ "name": "libravatar", "description": "Libravatar node.js library", "keywords": [ "libravatar", "avatar", "federated", "package.json" ], "version": "2.0.0", "author": "Francois Marier (http://fmarier.org)", "homepage": "https://github.com/fmarier/node-libravatar", "repository": { "type": "git", "url": "git://github.com/fmarier/node-libravatar.git" }, "devDependencies": { "tap": ">= 0.1.x" }, "engines": { "node": ">=0.4.3" }, "scripts": { "test": "tap tests/*.js" }, "main": "index" } fmarier-node-libravatar-399a809/tests/000077500000000000000000000000001174470642100176475ustar00rootroot00000000000000fmarier-node-libravatar-399a809/tests/federation.js000066400000000000000000000161461174470642100223350ustar00rootroot00000000000000var tap = require("tap"); var test = tap.test; var plan = tap.plan; var libravatar = require('../lib/libravatar'); test("sanitization of invalid SRV responses", function (t) { var test1 = libravatar.sanitized_target([null, null], false); var exp1 = null; t.equal(test1, exp1, 'both parameters missing'); var test2 = libravatar.sanitized_target([null, 80], false); var exp2 = null; t.equal(test2, exp2, 'first parameter missing'); var test3 = libravatar.sanitized_target(['example.com', null], true); var exp3 = null; t.equal(test3, exp3, 'second parameter missing'); var test4 = libravatar.sanitized_target(['example.com', 0], false); var exp4 = null; t.equal(test4, exp4, 'port too small'); var test5 = libravatar.sanitized_target(['example.com', 70000], true); var exp5 = null; t.equal(test5, exp5, 'port too big'); var test6 = libravatar.sanitized_target(['exam$ple.com', 80], false); var exp6 = null; t.equal(test6, exp6, 'invalid hostname'); var test7 = libravatar.sanitized_target(['example.com', 'abc'], true); var exp7 = null; t.equal(test7, exp7, 'invalid port'); t.end(); }); test("sanitization of valid SRV responses", function (t) { var test1 = libravatar.sanitized_target(['example.com', 80], false); var exp1 = 'example.com'; t.equal(test1, exp1, 'normal http'); var test2 = libravatar.sanitized_target(['example.com', 443], true); var exp2 = 'example.com'; t.equal(test2, exp2, 'normal https'); var test3 = libravatar.sanitized_target(['example.org', 8080], false); var exp3 = 'example.org:8080'; t.equal(test3, exp3, 'weird http'); var test4 = libravatar.sanitized_target(['example.org', 44300], true); var exp4 = 'example.org:44300'; t.equal(test4, exp4, 'weird https'); t.end(); }); function array_equal(t, result, expected, description) { t.equal(result[0], expected[0], description + ' [0]'); t.equal(result[1], expected[1], description + ' [1]'); } test("ordering of invalid SRV hostnames", function (t) { var test1 = libravatar.srv_hostname([]); var exp1 = [null, null]; array_equal(t, test1, exp1, 'empty array'); var test2 = libravatar.srv_hostname([{name: null, port: null}]); var exp2 = [null, null]; array_equal(t, test2, exp2, 'single empty array'); t.end(); }); test("ordering of valid SRV hostnames on priority", function (t) { var test1 = libravatar.srv_hostname([{name: 'example.com', port: 81}]); var exp1 = ['example.com', 81]; array_equal(t, test1, exp1, 'single hostname'); var test2 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 0, weight: 0}, {name: 'b.example.org', port: 82, priority: 10, weight: 0}]); var exp2 = ['a.example.org', 81]; array_equal(t, test2, exp2, 'two hostnames'); var test3 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 10, weight: 0}, {name: 'b.example.org', port: 82, priority: 1, weight: 0}, {name: 'c.example.org', port: 83, priority: 10, weight: 0}, {name: 'd.example.org', port: 84, priority: 10, weight: 0}]); var exp3 = ['b.example.org', 82]; array_equal(t, test3, exp3, 'four hostnames'); t.end(); }); test("ordering of valid SRV hostnames on weight", function (t) { var original_random = Math.random; Math.random = function () { return 0.6; }; var test1 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 10, weight: 1}, {name: 'b.example.org', port: 82, priority: 10, weight: 5}, {name: 'c.example.org', port: 83, priority: 10, weight: 10}, {name: 'd.example.org', port: 84, priority: 10, weight: 50}, {name: 'e.example.org', port: 85, priority: 10, weight: 0}]); var exp1 = ['d.example.org', 84]; array_equal(t, test1, exp1, 'random 1'); Math.random = function () { return 0.2; }; var test2 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 10, weight: 40}, {name: 'b.example.org', port: 82, priority: 10, weight: 0}, {name: 'c.example.org', port: 83, priority: 10, weight: 0}, {name: 'e.example.org', port: 85, priority: 10, weight: 0}]); var exp2 = ['a.example.org', 81]; array_equal(t, test2, exp2, 'random 2'); Math.random = function () { return 0.4; }; var test3 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 10, weight: 1}, {name: 'b.example.org', port: 82, priority: 10, weight: 0}, {name: 'c.example.org', port: 83, priority: 10, weight: 0}, {name: 'e.example.org', port: 85, priority: 10, weight: 0}]); var exp3 = ['e.example.org', 85]; array_equal(t, test3, exp3, 'random 3'); Math.random = function () { return 0.3; }; var test4 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 10, weight: 0}, {name: 'b.example.org', port: 82, priority: 10, weight: 0}, {name: 'c.example.org', port: 83, priority: 10, weight: 10}, {name: 'e.example.org', port: 85, priority: 10, weight: 0}]); var exp4 = ['c.example.org', 83]; array_equal(t, test4, exp4, 'random 4'); Math.random = function () { return 0.8; }; var test5 = libravatar.srv_hostname([{name: 'a.example.org', port: 81, priority: 10, weight: 1}, {name: 'b.example.org', port: 82, priority: 10, weight: 5}, {name: 'c.example.org', port: 83, priority: 10, weight: 10}, {name: 'd.example.org', port: 84, priority: 10, weight: 30}, {name: 'e.example.org', port: 85, priority: 10, weight: 50}, {name: 'f.example.org', port: 86, priority: 10, weight: 0}]); var exp5 = ['e.example.org', 85]; array_equal(t, test5, exp5, 'random 5'); Math.random = original_random; t.end(); }); test("ordering of valid SRV hostnames on weight", function (t) { var test1 = libravatar.service_name(null, false); var exp1 = null; t.equal(test1, exp1, 'degenerate case'); var test2 = libravatar.service_name('example.com', false); var exp2 = '_avatars._tcp.example.com'; t.equal(test2, exp2, 'simple domain over http'); var test3 = libravatar.service_name('example.org', true); var exp3 = '_avatars-sec._tcp.example.org'; t.equal(test3, exp3, 'simple domain over https'); var test4 = libravatar.service_name('example.co.nz', false); var exp4 = '_avatars._tcp.example.co.nz'; t.equal(test4, exp4, 'longer domain over http'); t.end(); }); fmarier-node-libravatar-399a809/tests/url.js000066400000000000000000000130521174470642100210100ustar00rootroot00000000000000var tap = require("tap"); var test = tap.test; var plan = tap.plan; var libravatar = require('../lib/libravatar'); var COMMON_EMAIL = 'whatever@wherever.whichever'; var COMMON_EMAIL_HASH = 'a60fc0828e808b9a6a9d50f1792240c8'; var COMMON_EMAIL_DOMAIN = 'wherever.whichever'; var COMMON_OPENID = 'http://example.com/id'; var COMMON_OPENID_HASH = 'ce0064bb30c22b618f814c389e7941ce1bfff0659910523192868d2b71632c77'; var COMMON_OPENID_DOMAIN = 'example.com'; var COMMON_PREFIX_HTTP = 'http://cdn.libravatar.org/avatar/'; var COMMON_PREFIX_HTTPS = 'https://seccdn.libravatar.org/avatar/'; function array_equal(t, result, expected, description) { t.equal(result[0], expected[0], description + ' [0]'); t.equal(result[1], expected[1], description + ' [1]'); } test("parsing of user identity", function (t) { var test1 = libravatar.parse_user_identity(null, null); var exp1 = [null, null]; array_equal(t, test1, exp1, 'both parameters missing'); var test2 = libravatar.parse_user_identity(COMMON_EMAIL, COMMON_OPENID); var exp2 = [COMMON_EMAIL_HASH, COMMON_EMAIL_DOMAIN]; array_equal(t, test2, exp2, 'both parameters supplied'); var test3 = libravatar.parse_user_identity(COMMON_EMAIL, null); var exp3 = [COMMON_EMAIL_HASH, COMMON_EMAIL_DOMAIN]; array_equal(t, test3, exp3, 'standard email'); var test4 = libravatar.parse_user_identity(null, COMMON_OPENID); var exp4 = [COMMON_OPENID_HASH, COMMON_OPENID_DOMAIN]; array_equal(t, test4, exp4, 'standard openid'); t.end(); }); test("parsing of email address", function (t) { var test1 = libravatar.parse_user_identity('', null); var exp1 = [null, null]; array_equal(t, test1, exp1, 'empty email'); var test2 = libravatar.parse_user_identity('username', null); var exp2 = [null, null]; array_equal(t, test2, exp2, 'missing hostname'); var test3 = libravatar.parse_user_identity('WHATEVER@wherever.whichever', null); var exp3 = [COMMON_EMAIL_HASH, COMMON_EMAIL_DOMAIN]; array_equal(t, test3, exp3, 'uppercase username'); var test4 = libravatar.parse_user_identity('Whatever@Wherever.whichever', null); var exp4 = [COMMON_EMAIL_HASH, COMMON_EMAIL_DOMAIN]; array_equal(t, test4, exp4, 'mixed-case username and hostname'); var test5 = libravatar.parse_user_identity(' Whatever@Wherever.whichever ', null); var exp5 = [COMMON_EMAIL_HASH, COMMON_EMAIL_DOMAIN]; array_equal(t, test5, exp5, 'untrimmed email'); t.end(); }); test("parsing of openid urls", function (t) { var test1 = libravatar.parse_user_identity(null, ''); var exp1 = [null, null]; array_equal(t, test1, exp1, 'empty openid'); var test2 = libravatar.parse_user_identity(null, 'url'); var exp2 = [null, null]; array_equal(t, test2, exp2, 'invalid url'); var test3 = libravatar.parse_user_identity(null, 'http://example.COM/id'); var exp3 = [COMMON_OPENID_HASH, COMMON_OPENID_DOMAIN]; array_equal(t, test3, exp3, 'mixed-case hostname'); var test4 = libravatar.parse_user_identity(null, ' HTTP://example.com/id '); var exp4 = [COMMON_OPENID_HASH, COMMON_OPENID_DOMAIN]; array_equal(t, test4, exp4, 'uppercase scheme'); var test5 = libravatar.parse_user_identity(null, 'http://user:password@Example.com/id'); var exp5 = ['e1cf8061371aa00b82c0cf0b9b1140546bc31cd4a15cb8adc84ad01823bdf71e', COMMON_OPENID_DOMAIN]; array_equal(t, test5, exp5, 'lowercase basic auth'); var test6 = libravatar.parse_user_identity(null, 'http://User:Password@Example.com/id'); var exp6 = ['50f60bb4c1b47fffdd6e2ce65f8bf37b65a2fb960596fa6789ef7b0044b931a2', COMMON_OPENID_DOMAIN]; array_equal(t, test6, exp6, 'mixed-case basic auth'); var test7 = libravatar.parse_user_identity(null, 'http://openid.example.COM/id'); var exp7 = ['a108913053c4949f18d9eef7a4a68f27591297cdd7a7e2e375702aa87b6d3c05', 'openid.example.com']; array_equal(t, test7, exp7, 'sub-domain'); var test8 = libravatar.parse_user_identity(null, 'https://example.com/id'); var exp8 = ['43e813cfff429662436728ef4fb1cc12bcf20414cab78811137f7d718c1ddedb', COMMON_OPENID_DOMAIN]; array_equal(t, test8, exp8, 'https'); var test9 = libravatar.parse_user_identity(null, 'http://example.com/ID'); var exp9 = ['ad8ce775cc12cba9bb8af26e00f55c473a3fcd3f554595a5ad9dd924a546a448', COMMON_OPENID_DOMAIN]; array_equal(t, test9, exp9, 'uppercase path'); t.end(); }); test("parsing of openid urls", function (t) { var test1 = libravatar.compose_avatar_url('', '', '', false); var exp1 = COMMON_PREFIX_HTTP; t.equal(test1, exp1, 'degenerate http case'); var test2 = libravatar.compose_avatar_url('', '', '', true); var exp2 = COMMON_PREFIX_HTTPS; t.equal(test2, exp2, 'degenerate https case'); var test3 = libravatar.compose_avatar_url('', 'deadbeef', '', false); var exp3 = COMMON_PREFIX_HTTP + 'deadbeef'; t.equal(test3, exp3, 'simple http hash'); var test4 = libravatar.compose_avatar_url('avatar.example.com', 'deadbeef', '', false); var exp4 = 'http://avatar.example.com/avatar/deadbeef'; t.equal(test4, exp4, 'federated http hash'); var test5 = libravatar.compose_avatar_url('avatar.example.com', 'deadbeef', '?s=24', true); var exp5 = 'https://avatar.example.com/avatar/deadbeef?s=24'; t.equal(test5, exp5, 'federated https hash with size'); var test6 = libravatar.compose_avatar_url('', '12345678901234567890123456789012', '?d=404', true); var exp6 = COMMON_PREFIX_HTTPS + '12345678901234567890123456789012?d=404'; t.equal(test6, exp6, 'common https hash with default'); t.end(); });