forked from kkaefer/utf7
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utf7.js
119 lines (105 loc) · 3.87 KB
/
utf7.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
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, '/'));
});
};