pax_global_header00006660000000000000000000000064127132375320014517gustar00rootroot0000000000000052 comment=1c52bbf38b97cca4309051323517a5f553d2f141 node-stringprep-0.8.0/000077500000000000000000000000001271323753200146445ustar00rootroot00000000000000node-stringprep-0.8.0/.gitignore000066400000000000000000000000721271323753200166330ustar00rootroot00000000000000.lock-wscript .DS_Store node_modules build *~ *.log .idea node-stringprep-0.8.0/.jshintrc000066400000000000000000000011741271323753200164740ustar00rootroot00000000000000{ "asi": true, "camelcase": true, "eqeqeq": true, "eqnull": true, "globalstrict": true, "immed": true, "indent": 4, "latedef": "nofunc", "laxcomma": true, "maxparams": 4, "maxdepth": 3, "maxstatements": 20, "maxcomplexity": 14, "maxlen": 120, "newcap": true, "noarg": true, "noempty": true, "nonew": true, "quotmark": "single", "undef": true, "unused": true, "strict": true, "trailing": true, "node": true, "predef": [ "define", "module", "require", "before", "beforeEach", "describe", "it", "after", "window" ] }node-stringprep-0.8.0/.travis.yml000066400000000000000000000005741271323753200167630ustar00rootroot00000000000000language: node_js node_js: - "0.8" - "0.10" - "0.11" - "0.12" before_install: - "sudo apt-get install libicu-dev" # Workaround for a permissions issue with Travis virtual machine images # that breaks Python's multiprocessing: # https://github.com/travis-ci/travis-cookbooks/issues/155 - sudo rm -rf /dev/shm - sudo ln -s /run/shm /dev/shm - npm i -g npm@1.X node-stringprep-0.8.0/Gruntfile.js000066400000000000000000000013151271323753200171410ustar00rootroot00000000000000'use strict'; module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), jshint: { allFiles: ['Gruntfile.js', 'test/**/*.js', 'index.js'], options: { jshintrc: '.jshintrc', } }, mochacli: { all: ['test/**/*.js', 'index.js'], options: { reporter: 'spec', ui: 'tdd' } } }) // Load the plugins grunt.loadNpmTasks('grunt-contrib-jshint') grunt.loadNpmTasks('grunt-mocha-cli') // Configure tasks grunt.registerTask('default', ['test']) grunt.registerTask('test', ['mochacli', 'jshint']) } node-stringprep-0.8.0/LICENSE000066400000000000000000000020411271323753200156460ustar00rootroot00000000000000Copyright (c) 2010 Stephan Maka 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. node-stringprep-0.8.0/README.markdown000066400000000000000000000066261271323753200173570ustar00rootroot00000000000000# node-stringprep [![Build Status](https://travis-ci.org/astro/node-stringprep.png)](https://travis-ci.org/astro/node-stringprep) [Flattr this!](https://flattr.com/thing/44598/node-stringprep) Exposes predefined Unicode normalization functions that are required by many protocols. This is just a binding to [ICU](http://icu-project.org/), which is [said to be fast.](http://ayena.de/node/74). If ICU is not available then we make use of JavaScript fallbacks. ## Usage ```javascript var StringPrep = require('node-stringprep').StringPrep; var prep = new StringPrep('nameprep'); prep.prepare('Äffchen') // => 'äffchen' ``` For a list of supported profiles, see [node-stringprep.cc](http://github.com/astro/node-stringprep/blob/master/node-stringprep.cc#L160) Javascript fallbacks can be disabled/enabled using the following methods on the `StringPrep` object: ```javascript var prep = new StringPrep('resourceprep') prep.disableJsFallbacks() prep.enableJsFallbacks() ``` Javascript fallbacks are enabled by default. You can also check to see if native `icu` bindings can/will be used by calling the `isNative()` method: ```javascript var prep = new StringPrep('resourceprep') prep.isNative() // true or false ``` We also implement the ToASCII and ToUnicode operations as defined in the [IDNA RFC 3490](http://www.ietf.org/rfc/rfc3490.txt). These routines convert Unicode to ASCII with [NamePrep](http://www.ietf.org/rfc/rfc3491.txt) and then with [Punycode](http://www.ietf.org/rfc/rfc3492.txt), and vice versa. ```javascript var nodeStringPrep = require('node-stringprep'); nodeStringPrep.toASCII('i♥u') // 'xn--iu-t0x' nodeStringPrep.toUnicode('xn--iu-t0x') // 'i♥u' ``` The operations can be finessed with an optional second argument, a set of boolean flags: ```javascript nodeStringPrep.toASCII('i♥u', { allowUnassigned: true, // allow unassigned code points to be converted throwIfError: true, // throw exception if error, don't return string unchanged useSTD3Rules: true // use the STD3 ASCII rules for host names }) nodeStringPrep.toUnicode('xn--iu-t0x', { allowUnassigned: true // allow unassigned code points to be converted }) ``` ## Installation ``` npm i node-stringprep ``` If `libicu` isn't available installation will gracefully fail and javascript fallbacks will be used. If experiencing issues with __node-gyp__ please see https://github.com/TooTallNate/node-gyp/issues/363 which may be able to help. ### Debian ``` apt-get install libicu-dev ``` ### RedHat & Centos ``` yum install libicu-devel ``` ### Gentoo ### ``` emerge icu ``` ### OSX #### MacPorts ``` port install icu +devel ``` #### Boxen ``` sudo ln -s /opt/boxen/homebrew/Cellar/icu4c/52.1/bin/icu-config /usr/local/bin/icu-config sudo ln -s /opt/boxen/homebrew/Cellar/icu4c/52.1/include/* /usr/local/include ``` #### Homebrew brew install icu4c ln -s /usr/local/Cellar/icu4c//bin/icu-config /usr/local/bin/icu-config ln -s /usr/local/Cellar/icu4c//include/* /usr/local/include If experiencing issues with 'homebrew' installing version 50.1 of icu4c, try the following: ``` brew search icu4c brew tap homebrew/versions brew versions icu4c cd $(brew --prefix) && git pull --rebase git checkout c25fd2f $(brew --prefix)/Library/Formula/icu4c.rb brew install icu4c ``` ## Running Tests ``` npm test ``` node-stringprep-0.8.0/binding.gyp000066400000000000000000000026271271323753200170060ustar00rootroot00000000000000{ 'targets': [ { 'target_name': 'node_stringprep', 'cflags_cc!': [ '-fno-exceptions', '-fmax-errors=0' ], 'include_dirs': [ ' /dev/null || echo n)"!="n"', { 'sources': [ 'node-stringprep.cc' ], 'cflags!': [ '-fno-exceptions', '-fmax-errors=0', '`icu-config --cppflags`' ], 'libraries': [ '`icu-config --ldflags`' ], 'conditions': [ ['OS=="freebsd" or OS=="openbsd"', { 'include_dirs': [ '/usr/local/include' ], }], ['OS=="mac"', { 'include_dirs': [ '/opt/local/include', '/usr/local/include' ], 'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' } }] ] }] ] }] ] } ] } node-stringprep-0.8.0/index.js000066400000000000000000000054761271323753200163250ustar00rootroot00000000000000'use strict'; var log = require('debug')('node-stringprep') // from unicode/uidna.h var UIDNA_ALLOW_UNASSIGNED = 1 var UIDNA_USE_STD3_RULES = 2 try { var bindings = require('bindings')('node_stringprep.node') } catch (ex) { log( 'Cannot load StringPrep-' + require('./package.json').version + ' bindings (using fallback). You may need to ' + '`npm install node-stringprep`' ) log(ex) } var toUnicode = function(value, options) { options = options || {} try { return bindings.toUnicode(value, (options.allowUnassigned && UIDNA_ALLOW_UNASSIGNED) | 0) } catch (e) { return value } } var toASCII = function(value, options) { options = options || {} try { return bindings.toASCII(value, (options.allowUnassigned && UIDNA_ALLOW_UNASSIGNED) | (options.useSTD3Rules && UIDNA_USE_STD3_RULES)) } catch (e) { if (options.throwIfError) { throw e } else { return value } } } var StringPrep = function(operation) { this.operation = operation try { this.stringPrep = new bindings.StringPrep(this.operation) } catch (e) { this.stringPrep = null log('Operation does not exist', operation, e) } } StringPrep.prototype.UNKNOWN_PROFILE_TYPE = 'Unknown profile type' StringPrep.prototype.UNHANDLED_FALLBACK = 'Unhandled JS fallback' StringPrep.prototype.LIBICU_NOT_AVAILABLE = 'libicu unavailable' StringPrep.prototype.useJsFallbacks = true StringPrep.prototype.prepare = function(value) { this.value = value try { if (this.stringPrep) { return this.stringPrep.prepare(this.value) } } catch (e) {} if (false === this.useJsFallbacks) { throw new Error(this.LIBICU_NOT_AVAILABLE) } return this.jsFallback() } StringPrep.prototype.isNative = function() { return (null !== this.stringPrep) } StringPrep.prototype.jsFallback = function() { switch (this.operation) { case 'nameprep': case 'nodeprep': return this.value.toLowerCase() case 'resourceprep': return this.value case 'nfs4_cs_prep': case 'nfs4_cis_prep': case 'nfs4_mixed_prep prefix': case 'nfs4_mixed_prep suffix': case 'iscsi': case 'mib': case 'saslprep': case 'trace': case 'ldap': case 'ldapci': throw new Error(this.UNHANDLED_FALLBACK) default: throw new Error(this.UNKNOWN_PROFILE_TYPE) } } StringPrep.prototype.disableJsFallbacks = function() { this.useJsFallbacks = false } StringPrep.prototype.enableJsFallbacks = function() { this.useJsFallbacks = true } module.exports = { toUnicode: toUnicode, toASCII: toASCII, StringPrep: StringPrep } node-stringprep-0.8.0/node-stringprep.cc000066400000000000000000000201141271323753200202710ustar00rootroot00000000000000#include #include #include #include #include #include using namespace v8; using namespace node; /* supports return of just enum */ class UnknownProfileException : public std::exception { }; // protect constructor from GC static Nan::Persistent stringprep_constructor; class StringPrep : public Nan::ObjectWrap { public: static void Initialize(Handle target) { Nan::HandleScope scope; Local t = Nan::New(New); stringprep_constructor.Reset(t); t->InstanceTemplate()->SetInternalFieldCount(1); Nan::SetPrototypeMethod(t, "prepare", Prepare); target->Set(Nan::New("StringPrep").ToLocalChecked(), t->GetFunction()); } bool good() const { return U_SUCCESS(error); } const char *errorName() const { return u_errorName(error); } protected: /*** Constructor ***/ static NAN_METHOD(New) { if (info.Length() >= 1 && info[0]->IsString()) { Nan::Utf8String arg0(info[0]->ToString()); UStringPrepProfileType profileType; try { profileType = parseProfileType(arg0); } catch (UnknownProfileException &) { Nan::ThrowTypeError("Unknown StringPrep profile"); return; } StringPrep *self = new StringPrep(profileType); if (self->good()) { self->Wrap(info.This()); info.GetReturnValue().Set(info.This()); return; } else { const char* err = self->errorName(); delete self; Nan::ThrowError(err); return; } } else { Nan::ThrowTypeError("Bad argument."); return; } } StringPrep(const UStringPrepProfileType profileType) : error(U_ZERO_ERROR) { profile = usprep_openByType(profileType, &error); } /*** Destructor ***/ ~StringPrep() { if (profile) usprep_close(profile); } /*** Prepare ***/ static NAN_METHOD(Prepare) { if (info.Length() >= 1 && info[0]->IsString()) { StringPrep *self = Nan::ObjectWrap::Unwrap(info.This()); String::Value arg0(info[0]->ToString()); info.GetReturnValue().Set(self->prepare(arg0)); return; } else { Nan::ThrowTypeError("Bad argument."); return; } } Local prepare(String::Value &str) { Nan::EscapableHandleScope scope; size_t destLen = str.length() + 1; UChar *dest = NULL; while(!dest) { error = U_ZERO_ERROR; dest = new UChar[destLen]; size_t w = usprep_prepare(profile, *str, str.length(), dest, destLen, USPREP_DEFAULT, NULL, &error); if (error == U_BUFFER_OVERFLOW_ERROR) { // retry with a dest buffer twice as large destLen *= 2; delete[] dest; dest = NULL; } else if (!good()) { // other error, just bail out delete[] dest; Nan::ThrowError(errorName()); return scope.Escape(Nan::Undefined()); } else destLen = w; } Local result = Nan::New(dest, destLen).ToLocalChecked(); delete[] dest; return scope.Escape(result); } private: UStringPrepProfile *profile; UErrorCode error; static enum UStringPrepProfileType parseProfileType(Nan::Utf8String &profile) throw(UnknownProfileException) { if (strcasecmp(*profile, "nameprep") == 0) return USPREP_RFC3491_NAMEPREP; if (strcasecmp(*profile, "nfs4_cs_prep") == 0) return USPREP_RFC3530_NFS4_CS_PREP; if (strcasecmp(*profile, "nfs4_cs_prep") == 0) return USPREP_RFC3530_NFS4_CS_PREP_CI; if (strcasecmp(*profile, "nfs4_cis_prep") == 0) return USPREP_RFC3530_NFS4_CIS_PREP; if (strcasecmp(*profile, "nfs4_mixed_prep prefix") == 0) return USPREP_RFC3530_NFS4_MIXED_PREP_PREFIX; if (strcasecmp(*profile, "nfs4_mixed_prep suffix") == 0) return USPREP_RFC3530_NFS4_MIXED_PREP_SUFFIX; if (strcasecmp(*profile, "iscsi") == 0) return USPREP_RFC3722_ISCSI; if (strcasecmp(*profile, "nodeprep") == 0) return USPREP_RFC3920_NODEPREP; if (strcasecmp(*profile, "resourceprep") == 0) return USPREP_RFC3920_RESOURCEPREP; if (strcasecmp(*profile, "mib") == 0) return USPREP_RFC4011_MIB; if (strcasecmp(*profile, "saslprep") == 0) return USPREP_RFC4013_SASLPREP; if (strcasecmp(*profile, "trace") == 0) return USPREP_RFC4505_TRACE; if (strcasecmp(*profile, "ldap") == 0) return USPREP_RFC4518_LDAP; if (strcasecmp(*profile, "ldapci") == 0) return USPREP_RFC4518_LDAP_CI; throw UnknownProfileException(); } }; /*** IDN support ***/ NAN_METHOD(ToUnicode) { if (info.Length() >= 2 && info[0]->IsString() && info[1]->IsInt32()) { String::Value str(info[0]->ToString()); int32_t options = info[1]->ToInt32()->Value(); UErrorCode error = U_ZERO_ERROR; UIDNA *uidna = uidna_openUTS46(options, &error); if (U_FAILURE(error)) { Nan::ThrowError(u_errorName(error)); return; } // ASCII encoding (xn--*--*) should be longer than Unicode size_t destLen = str.length() + 1; UChar *dest = NULL; while(!dest) { dest = new UChar[destLen]; UIDNAInfo uinfo = UIDNA_INFO_INITIALIZER; size_t w = uidna_nameToUnicode( uidna, *str, str.length(), dest, destLen, &uinfo, &error); if (error == U_BUFFER_OVERFLOW_ERROR) { // retry with a dest buffer twice as large destLen *= 2; delete[] dest; dest = NULL; } else if (U_FAILURE(error)) { // other error, just bail out delete[] dest; uidna_close(uidna); Nan::ThrowError(u_errorName(error)); return; } else destLen = w; } Local result = Nan::New(dest, destLen).ToLocalChecked(); delete[] dest; uidna_close(uidna); info.GetReturnValue().Set(result); return; } else { Nan::ThrowTypeError("Bad argument."); return; } } NAN_METHOD(ToASCII) { if (info.Length() >= 2 && info[0]->IsString() && info[1]->IsInt32()) { String::Value str(info[0]->ToString()); int32_t options = info[1]->ToInt32()->Value(); UErrorCode error = U_ZERO_ERROR; UIDNA *uidna = uidna_openUTS46(options, &error); if (U_FAILURE(error)) { Nan::ThrowError(u_errorName(error)); return; } // find out length first size_t strLen = str.length(); UIDNAInfo uinfo1 = UIDNA_INFO_INITIALIZER; size_t destLen = uidna_nameToASCII( uidna, *str, strLen, NULL, 0, &uinfo1, &error); UChar *dest = NULL; if (error == U_BUFFER_OVERFLOW_ERROR) { // now try with dest buffer error = U_ZERO_ERROR; dest = new UChar[destLen]; UIDNAInfo uinfo2 = UIDNA_INFO_INITIALIZER; uidna_nameToASCII( uidna, *str, strLen, dest, destLen, &uinfo2, &error); if (U_SUCCESS(error) && uinfo2.errors > 0) { error = U_INVALID_CHAR_FOUND; } } if (U_FAILURE(error)) { // other error, just bail out delete[] dest; uidna_close(uidna); Nan::ThrowError(u_errorName(error)); return; } Local result = Nan::New(dest, destLen).ToLocalChecked(); delete[] dest; uidna_close(uidna); info.GetReturnValue().Set(result); return; } else { Nan::ThrowTypeError("Bad argument."); return; } } /*** Initialization ***/ NAN_MODULE_INIT(init) { StringPrep::Initialize(target); Nan::SetMethod(target, "toUnicode", ToUnicode); Nan::SetMethod(target, "toASCII", ToASCII); } NODE_MODULE(node_stringprep, init) node-stringprep-0.8.0/package.json000066400000000000000000000016511271323753200171350ustar00rootroot00000000000000{ "name": "node-stringprep", "version": "0.8.0", "main": "index.js", "description": "ICU StringPrep profiles", "keywords": [ "unicode", "stringprep", "icu" ], "scripts": { "test": "./node_modules/.bin/grunt test" }, "dependencies": { "bindings": "~1.2.1", "debug": "~2.2.0", "nan": "~2.3.3" }, "devDependencies": { "grunt": "~0.4.2", "grunt-cli": "^1.0.1", "grunt-contrib-jshint": "~1.0.0", "grunt-mocha-cli": "~1.3.0", "proxyquire": "~0.5.2", "should": "~8.2.2" }, "repository": "github:astro/node-stringprep", "homepage": "http://github.com/astro/node-stringprep", "bugs": "http://github.com/astro/node-stringprep/issues", "author": { "name": "Lloyd Watkin", "email": "lloyd@evilprofessor.co.uk", "web": "http://evilprofessor.co.uk" }, "licenses": [ { "type": "MIT" } ], "engines": { "node": ">=0.8 <=0.12" } } node-stringprep-0.8.0/test/000077500000000000000000000000001271323753200156235ustar00rootroot00000000000000node-stringprep-0.8.0/test/fallback.js000066400000000000000000000065011271323753200177220ustar00rootroot00000000000000'use strict'; var proxyquire = require('proxyquire') /* jshint -W030 */ describe('Should use JS fallbacks for StringPrep', function() { var StringPrep = proxyquire('../index', { 'bindings': null }).StringPrep it('Should throw on unknown icu-profile', function(done) { var prep = new StringPrep('cRaZYcASE') try { prep.prepare('UPPERCASE') done('Should have thrown error') } catch (e) { e.message.should.equal(prep.UNKNOWN_PROFILE_TYPE) done() } }) it('Should perform a \'nameprep\'', function(done) { var prep = new StringPrep('nameprep') prep.prepare('UPPERCASE').should.equal('uppercase') done() }) it('Should perform a \'nodeprep\'', function(done) { var prep = new StringPrep('nodeprep') prep.prepare('UPPERCASE').should.equal('uppercase') done() }) it('Should preform a \'resourceprep\'', function(done) { var prep = new StringPrep('resourceprep') prep.prepare('UPPERCASE').should.equal('UPPERCASE') done() }) it('Can\'t handle other profiles', function(done) { var unsupportedFallbackProfiles = [ 'nfs4_cs_prep', 'nfs4_cis_prep', 'nfs4_mixed_prep prefix', 'nfs4_mixed_prep suffix', 'iscsi', 'mib', 'saslprep', 'trace', 'ldap', 'ldapci' ] var isDone = false unsupportedFallbackProfiles.forEach(function(profile) { var prep = new StringPrep(profile) try { prep.prepare('UPPERCASE') isDone = true done('Should have thrown error') } catch (e) { e.message.should.equal(prep.UNHANDLED_FALLBACK) } }) if (false === isDone) done() }) }) describe('Can disable fallbacks', function() { var StringPrep = proxyquire('../index', { 'bindings': null }).StringPrep it('Should allow javascript fallbacks to be disabled', function(done) { var prep = new StringPrep('nameprep') try { prep.disableJsFallbacks() prep.prepare('UPPERCASE') done('Should have thrown exception') } catch (e) { e.message.should.equal(prep.LIBICU_NOT_AVAILABLE) done() } }) it('Should allow javascript fallbacks to be re-enabled', function(done) { var prep = new StringPrep('nameprep') try { prep.disableJsFallbacks() prep.prepare('UPPERCASE') done('Should have thrown exception') } catch (e) { e.message.should.equal(prep.LIBICU_NOT_AVAILABLE) prep.enableJsFallbacks() prep.prepare('UPPERCASE') done() } }) }) describe('\'isNative\' method test', function() { it('Reports true with native', function() { var StringPrep = require('../index').StringPrep var prep = new StringPrep('resourceprep') prep.isNative().should.be.true }) it('Reports false without native', function() { var StringPrep = proxyquire('../index', { 'bindings': null }).StringPrep var prep = new StringPrep('resourceprep') prep.isNative().should.be.false }) }) node-stringprep-0.8.0/test/leakcheck.js000066400000000000000000000004621271323753200200750ustar00rootroot00000000000000'use strict'; require('should') it('Should not leak', function(done) { var SP = require('../index') try { var p = new SP.StringPrep('nameprep') var result = p.prepare('A\u0308ffin') result.should.equal('äffin') done() } catch (e) { done(e) } }) node-stringprep-0.8.0/test/toascii.js000066400000000000000000000014001271323753200176070ustar00rootroot00000000000000'use strict'; require('should') it('Should convert to ASCII', function(done) { var SP = require('../index') var result = SP.toASCII('i\u2665u') result.should.equal('xn--iu-t0x') done() }) it('Should throw on error', function(done) { var SP = require('../index') SP.toASCII.bind(SP, 'xn--\ud83d\ude03', { throwIfError: true }).should.throw() done() }) it('Should convert unassigned code point', function(done) { var SP = require('../index') var result = SP.toASCII('\ud83d\ude03', { allowUnassigned : true }) result.should.equal('xn--h28h') done() }) it('Should error on non-STD3 char', function(done) { var SP = require('../index') SP.toASCII.bind(SP, 'abc#def', { throwIfError: true, useSTD3Rules: true }).should.throw() done() })node-stringprep-0.8.0/test/tounicode.js000066400000000000000000000006441271323753200201560ustar00rootroot00000000000000'use strict'; require('should') it('Should convert to Unicode', function(done) { var SP = require('../index') var result = SP.toUnicode('xn--iu-t0x') result.should.equal('i\u2665u') done() }) it('Should convert unassigned code point', function(done) { var SP = require('../index') var result = SP.toUnicode('xn--h28h', { allowUnassigned : true }) result.should.equal('\ud83d\ude03') done() })