libmime 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.0.0 2016-12-08
4
+
5
+ * Updated encoded-word generation. Previously a minimal value was encoded, so it was possible to have multiple encoded words in a string separated by non encoded-words. This was an issue with some webmail clients that stripped out the non-encoded parts between encoded-words so the updated method uses wide match by encoding from the first word with unicode characters to the last word. "a =?b?= c =?d?= e" -> "a =?bcd?= e"
6
+
7
+ ## v2.1.3 2016-12-08
8
+
9
+ * Revert dot as a special symbol
10
+
11
+ ## v2.1.2 2016-11-21
12
+
13
+ * Quote special symbols as defined in RFC (surajwy)
14
+
15
+ ## v2.1.1 2016-11-15
16
+
17
+ * Fixed issue with special symbols in attachment filenames
18
+
3
19
  ## v2.1.0 2016-07-24
4
20
 
5
21
  * Changed handling of base64 encoded mime words where multiple words are joined together if possible. This fixes issues with multi byte characters getting split into different mime words (against the RFC but occurs)
package/lib/libmime.js CHANGED
@@ -184,7 +184,15 @@ var libmime = module.exports = {
184
184
  fromCharset = match[1].split('*').shift();
185
185
 
186
186
  encoding = (match[2] || 'Q').toString().toUpperCase();
187
- str = (match[3] || '').replace(/_/g, ' ').replace(/ $/, '=20');
187
+ str = (match[3] || '');
188
+
189
+ if (encoding === 'Q') {
190
+ // remove spaces between = and hex char, this might indicate invalidly applied line splitting
191
+ str = str.replace(/=\s+([0-9a-fA-F])/, '=$1');
192
+ }
193
+
194
+ // convert all underscores to spaces
195
+ str = str.replace(/_/g, ' ').replace(/ $/, '=20');
188
196
 
189
197
  if (encoding === 'B') {
190
198
  return libcharset.decode(libbase64.decode(str), fromCharset);
@@ -212,12 +220,27 @@ var libmime = module.exports = {
212
220
 
213
221
  maxLength = maxLength || 0;
214
222
 
215
- var decodedValue = libcharset.decode(libcharset.convert((data || ''), fromCharset)),
216
- encodedValue;
223
+ var decodedValue = libcharset.decode(libcharset.convert((data || ''), fromCharset));
224
+ var encodedValue;
217
225
 
218
- encodedValue = decodedValue.replace(/([^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*(?:\s+[^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*\s*)?)+(?=\s|$)/g, function (match) {
219
- return match.length ? libmime.encodeWord(match, mimeWordEncoding || 'Q', maxLength) : '';
220
- });
226
+ var firstMatch = decodedValue.match(/(?:^|\s)([^\s]*[\u0080-\uFFFF])/);
227
+ if (!firstMatch) {
228
+ return decodedValue;
229
+ }
230
+ var lastMatch = decodedValue.match(/([\u0080-\uFFFF][^\s]*)[^\u0080-\uFFFF]*$/);
231
+ if (!lastMatch) {
232
+ // should not happen
233
+ return decodedValue;
234
+ }
235
+ var startIndex = firstMatch.index + (firstMatch[0].match(/[^\s]/) || {
236
+ index: 0
237
+ }).index;
238
+ var endIndex = lastMatch.index + (lastMatch[1] || '').length;
239
+
240
+ encodedValue =
241
+ (startIndex ? decodedValue.substr(0, startIndex) : '') +
242
+ libmime.encodeWord(decodedValue.substring(startIndex, endIndex), mimeWordEncoding || 'Q', maxLength) +
243
+ (endIndex < decodedValue.length ? decodedValue.substr(endIndex) : '');
221
244
 
222
245
  return encodedValue;
223
246
  },
@@ -234,7 +257,18 @@ var libmime = module.exports = {
234
257
  // find base64 words that can be joined
235
258
  replace(/(=\?([^?]+)\?[Bb]\?[^?]+[^^=]\?=)\s*(?==\?([^?]+)\?[Bb]\?[^?]+\?=)/g,
236
259
  function (match, left, chLeft, chRight) {
237
- // only mark to b64 chunks to be joined if charsets match
260
+ // only mark b64 chunks to be joined if charsets match
261
+ if (libcharset.normalizeCharset(chLeft || '').toLowerCase().trim() === libcharset.normalizeCharset(chRight || '').toLowerCase().trim()) {
262
+ // set a joiner marker
263
+ return left + '__\x00JOIN\x00__';
264
+ }
265
+ return match;
266
+ }).
267
+
268
+ // find QP words that can be joined
269
+ replace(/(=\?([^?]+)\?[Qq]\?[^?]+\?=)\s*(?==\?([^?]+)\?[Qq]\?[^?]+\?=)/g,
270
+ function (match, left, chLeft, chRight) {
271
+ // only mark QP chunks to be joined if charsets match
238
272
  if (libcharset.normalizeCharset(chLeft || '').toLowerCase().trim() === libcharset.normalizeCharset(chRight || '').toLowerCase().trim()) {
239
273
  // set a joiner marker
240
274
  return left + '__\x00JOIN\x00__';
@@ -243,7 +277,7 @@ var libmime = module.exports = {
243
277
  }).
244
278
 
245
279
  // join base64 encoded words
246
- replace(/(\?=)?__\x00JOIN\x00__(=\?([^?]+)\?[Bb]\?)?/g, '').
280
+ replace(/(\?=)?__\x00JOIN\x00__(=\?([^?]+)\?[QqBb]\?)?/g, '').
247
281
 
248
282
  // remove spaces between mime encoded words
249
283
  replace(/(=\?[^?]+\?[QqBb]\?[^?]+\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]+\?=)/g, '$1').
@@ -308,7 +342,8 @@ var libmime = module.exports = {
308
342
 
309
343
  /**
310
344
  * Joins parsed header value together as 'value; param1=value1; param2=value2'
311
- *
345
+ * PS: We are following RFC 822 for the list of special characters that we need to keep in quotes.
346
+ * Refer: https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
312
347
  * @param {Object} structured Parsed header value
313
348
  * @return {String} joined header value
314
349
  */
@@ -320,13 +355,13 @@ var libmime = module.exports = {
320
355
  var value = structured.params[param];
321
356
  if (!libmime.isPlainText(value) || value.length >= 75) {
322
357
  libmime.buildHeaderParam(param, value, 50).forEach(function (encodedParam) {
323
- if (!/[\s"\\;\/=]|^[\-']|'$/.test(encodedParam.value) || encodedParam.key.substr(-1) === '*') {
358
+ if (!/[\s"\\;:\/=\(\),<>@\[\]\?]|^[\-']|'$/.test(encodedParam.value) || encodedParam.key.substr(-1) === '*') {
324
359
  paramsArray.push(encodedParam.key + '=' + encodedParam.value);
325
360
  } else {
326
361
  paramsArray.push(encodedParam.key + '=' + JSON.stringify(encodedParam.value));
327
362
  }
328
363
  });
329
- } else if (/[\s'"\\;\/=]|^\-/.test(value)) {
364
+ } else if (/[\s'"\\;:\/=\(\),<>@\[\]\?]|^\-/.test(value)) {
330
365
  paramsArray.push(param + '=' + JSON.stringify(value));
331
366
  } else {
332
367
  paramsArray.push(param + '=' + value);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "libmime",
3
3
  "description": "Encode and decode quoted printable and base64 strings",
4
- "version": "2.1.0",
4
+ "version": "3.0.0",
5
5
  "main": "lib/libmime",
6
6
  "homepage": "https://github.com/andris9/libmime",
7
7
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "test": "grunt mochaTest"
20
20
  },
21
21
  "dependencies": {
22
- "iconv-lite": "0.4.13",
22
+ "iconv-lite": "0.4.15",
23
23
  "libbase64": "0.1.0",
24
24
  "libqp": "1.1.0"
25
25
  },
@@ -28,7 +28,7 @@
28
28
  "grunt": "^1.0.1",
29
29
  "grunt-cli": "^1.2.0",
30
30
  "grunt-eslint": "^19.0.0",
31
- "grunt-mocha-test": "^0.12.7",
32
- "mocha": "^3.0.2"
31
+ "grunt-mocha-test": "^0.13.2",
32
+ "mocha": "^3.2.0"
33
33
  }
34
34
  }