120 lines
3.9 KiB
JavaScript
120 lines
3.9 KiB
JavaScript
|
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, '/'));
|
||
|
});
|
||
|
};
|