-
Notifications
You must be signed in to change notification settings - Fork 1
/
gmailEncoder.js
107 lines (93 loc) · 3.49 KB
/
gmailEncoder.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
const fullAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const restrictedAlphabet = 'BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz';
const threadPrefix = 'thread-';
const messagePrefix = 'msg-';
const isWhitespace = str => /^[\s\xa0]*$/.test(str);
const isInvalidString = str => str ? (str.indexOf(threadPrefix) !== -1 || str.indexOf(messagePrefix) !== -1) : false;
function atob(str) {
if (Buffer.byteLength(str) !== str.length)
throw new Error('bad string!');
return Buffer(str, 'base64').toString('ascii');
}
function btoa(str) {
if (Buffer.byteLength(str) !== str.length)
throw new Error('bad string!');
return Buffer(str, 'ascii').toString('base64');
}
const encode = function(str) {
if (isWhitespace(str)) return str;
str = str.replace(threadPrefix, '');
return transliterate(btoa(str).replace(/=/g, ''), fullAlphabet, restrictedAlphabet);
};
const decode = function(str) {
if (isInvalidString(str) || !/^[BCDFGHJKLMNPQRSTVWXZbcdfghjklmnpqrstvwxz]*$/.test(str)) return str;
try {
const transliterated = atob(transliterate(str, restrictedAlphabet, fullAlphabet))
return transliterated.indexOf(threadPrefix) === -1 ? threadPrefix + transliterated : transliterated;
} catch (err) {
return str;
}
};
const transliterate = function(subject, inputAlphabet, outputAlphabet) {
if (!outputAlphabet) throw Error('rd');
let i, j;
const inputAlphabetSize = inputAlphabet.length;
const outputAlphabetSize = outputAlphabet.length;
let isEqual = true;
for (i = 0; i < subject.length; i++)
if (subject.charAt(i) != inputAlphabet.charAt(0)) {
isEqual = false;
break;
}
if (isEqual) return outputAlphabet.charAt(0);
const inputAlphabetMap = {};
for (i = 0; i < inputAlphabetSize; i++) inputAlphabetMap[inputAlphabet.charAt(i)] = i;
const inputIndices = [];
for (i = subject.length - 1; i >= 0; i--) {
const char = subject.charAt(i);
if (typeof inputAlphabetMap[char] === 'undefined') throw Error("sd`" + subject + "`" + inputAlphabet + "`" + char);
inputIndices.push(inputAlphabetMap[char]);
}
const outputIndices = [];
for (i = inputIndices.length - 1; i >= 0; i--) {
let offset = 0;
for (j = 0; j < outputIndices.length; j++) {
let index = outputIndices[j] * inputAlphabetSize + offset;
if (index >= outputAlphabetSize) {
const remainder = index % outputAlphabetSize;
offset = (index - remainder) / outputAlphabetSize;
index = remainder;
} else {
offset = 0;
}
outputIndices[j] = index;
}
while (offset) {
const remainder = offset % outputAlphabetSize;
outputIndices.push(remainder);
offset = (offset - remainder) / outputAlphabetSize;
}
offset = inputIndices[i];
for (j = 0; offset; j++) {
if (j >= outputIndices.length) outputIndices.push(0);
let index = outputIndices[j] + offset;
if (index >= outputAlphabetSize) {
const remainder = index % outputAlphabetSize;
offset = (index - remainder) / outputAlphabetSize;
index = remainder;
} else {
offset = 0;
}
outputIndices[j] = index;
}
}
const outputBuffer = [];
for (i = outputIndices.length - 1; i >= 0; i--) {
const index = outputIndices[i];
if (index >= outputAlphabet.length || index < 0) throw Error("td`" + outputIndices + "`" + index);
outputBuffer.push(outputAlphabet.charAt(index));
}
return outputBuffer.join('');
};
module.exports.encode = encode;
module.exports.decode = decode;