var Buffer = require('buffer').Buffer; var semver = require('semver'); if (semver.gte(process.version, '6.0.0')) { function allocateAsciiBuffer(length) { return Buffer.alloc(length, 'ascii'); } } else { function allocateAsciiBuffer(length) { return new Buffer(length, 'ascii'); } } function encode(str) { var b = allocateAsciiBuffer(str.length * 2); for (var i = 0, bi = 0; i < str.length; i++) { // Note that we can't simply convert a UTF-8 string to Base64 because // UTF-8 uses a different encoding. In modified UTF-7, all characters // are represented by their two byte Unicode ID. var c = str.charCodeAt(i); // Upper 8 bits shifted into lower 8 bits so that they fit into 1 byte. b[bi++] = c >> 8; // Lower 8 bits. Cut off the upper 8 bits so that they fit into 1 byte. b[bi++] = c & 0xFF; } // Modified Base64 uses , instead of / and omits trailing =. return b.toString('base64').replace(/=+$/, ''); } if (semver.gte(process.version, '6.0.0')) { function allocateBase64Buffer(str) { return Buffer.from(str, 'base64'); } } else { function allocateBase64Buffer(str) { return new Buffer(str, 'base64'); } } function decode(str) { var b = allocateBase64Buffer(str); var r = []; for (var i = 0; i < b.length;) { // Calculate charcode from two adjacent bytes. r.push(String.fromCharCode(b[i++] << 8 | b[i++])); } return r.join(''); } // Escape RegEx from http://simonwillison.net/2006/Jan/20/escape/ function escape(chars) { return chars.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); } // Character classes defined by RFC 2152. var setD = "A-Za-z0-9" + escape("'(),-./:?"); var setO = escape("!\"#$%&*;<=>@[]^_'{|}"); var setW = escape(" \r\n\t"); // Stores compiled regexes for various replacement pattern. var regexes = {}; var regexAll = new RegExp("[^" + setW + setD + setO + "]+", 'g'); exports.imap = {}; // RFC 2152 UTF-7 encoding. exports.encode = function(str, mask) { // Generate a RegExp object from the string of mask characters. if (!mask) { mask = ''; } if (!regexes[mask]) { regexes[mask] = new RegExp("[^" + setD + escape(mask) + "]+", 'g'); } // We replace subsequent disallowed chars with their escape sequence. return str.replace(regexes[mask], function(chunk) { // + is represented by an empty sequence +-, otherwise call encode(). return '+' + (chunk === '+' ? '' : encode(chunk)) + '-'; }); }; // RFC 2152 UTF-7 encoding with all optionals. exports.encodeAll = function(str) { // We replace subsequent disallowed chars with their escape sequence. return str.replace(regexAll, function(chunk) { // + is represented by an empty sequence +-, otherwise call encode(). return '+' + (chunk === '+' ? '' : encode(chunk)) + '-'; }); }; // RFC 3501, section 5.1.3 UTF-7 encoding. exports.imap.encode = function(str) { // All printable ASCII chars except for & must be represented by themselves. // We replace subsequent non-representable chars with their escape sequence. return str.replace(/&/g, '&-').replace(/[^\x20-\x7e]+/g, function(chunk) { // & is represented by an empty sequence &-, otherwise call encode(). chunk = (chunk === '&' ? '' : encode(chunk)).replace(/\//g, ','); return '&' + chunk + '-'; }); }; // RFC 2152 UTF-7 decoding. exports.decode = function(str) { return str.replace(/\+([A-Za-z0-9\/]*)-?/gi, function(_, chunk) { // &- represents &. if (chunk === '') return '+'; return decode(chunk); }); }; // RFC 3501, section 5.1.3 UTF-7 decoding. exports.imap.decode = function(str) { return str.replace(/&([^-]*)-/g, function(_, chunk) { // &- represents &. if (chunk === '') return '&'; return decode(chunk.replace(/,/g, '/')); }); };