libmime 1.1.0 → 1.2.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,9 @@
1
1
  # Changelog
2
2
 
3
+ ## v1.2.0 2015-10-05
4
+
5
+ Added support for emojis in header params (eg. filenames)
6
+
3
7
  ## v1.1.0 2015-09-24
4
8
 
5
9
  Updated encoded word encoding with quoted printable, should be more like required in https://tools.ietf.org/html/rfc2047#section-5
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": "1.1.0",
4
+ "version": "1.2.0",
5
5
  "main": "src/libmime",
6
6
  "homepage": "https://github.com/andris9/libmime",
7
7
  "repository": {
@@ -19,7 +19,7 @@
19
19
  "test": "grunt"
20
20
  },
21
21
  "dependencies": {
22
- "iconv-lite": "^0.4.11",
22
+ "iconv-lite": "^0.4.13",
23
23
  "libbase64": "^0.1.0",
24
24
  "libqp": "^1.1.0"
25
25
  },
package/src/libmime.js CHANGED
@@ -110,6 +110,7 @@ var libmime = module.exports = {
110
110
  }
111
111
 
112
112
  if (mimeWordEncoding === 'Q') {
113
+ // https://tools.ietf.org/html/rfc2047#section-5 rule (3)
113
114
  encodedStr = libqp.encode(data).replace(/[^a-z0-9!*+\-\/=]/ig, function(chr) {
114
115
  var ord = chr.charCodeAt(0).toString(16).toUpperCase();
115
116
  if (chr === ' ') {
@@ -465,10 +466,12 @@ var libmime = module.exports = {
465
466
  buildHeaderParam: function(key, data, maxLength, fromCharset) {
466
467
  var list = [];
467
468
  var encodedStr = typeof data === 'string' ? data : libmime.decode(data, fromCharset);
468
- var chr;
469
+ var encodedStrArr;
470
+ var chr, ord;
469
471
  var line;
470
472
  var startPos = 0;
471
473
  var isEncoded = false;
474
+ var i, len;
472
475
 
473
476
  maxLength = maxLength || 50;
474
477
 
@@ -498,21 +501,39 @@ var libmime = module.exports = {
498
501
 
499
502
  } else {
500
503
 
504
+ if (/[\uD800-\uDBFF]/.test(encodedStr)) {
505
+ // string containts surrogate pairs, so normalize it to an array of bytes
506
+ encodedStrArr = [];
507
+ for (i = 0, len = encodedStr.length; i < len; i++) {
508
+ chr = encodedStr.charAt(i);
509
+ ord = chr.charCodeAt(0);
510
+ if (ord >= 0xD800 && ord <= 0xDBFF && i < len - 1) {
511
+ chr += encodedStr.charAt(i + 1);
512
+ encodedStrArr.push(chr);
513
+ i++;
514
+ } else {
515
+ encodedStrArr.push(chr);
516
+ }
517
+ }
518
+ encodedStr = encodedStrArr;
519
+ }
520
+
501
521
  // first line includes the charset and language info and needs to be encoded
502
522
  // even if it does not contain any unicode characters
503
523
  line = 'utf-8\'\'';
504
524
  isEncoded = true;
505
525
  startPos = 0;
526
+
506
527
  // process text with unicode or special chars
507
- for (var i = 0, len = encodedStr.length; i < len; i++) {
528
+ for (i = 0, len = encodedStr.length; i < len; i++) {
508
529
 
509
530
  chr = encodedStr[i];
510
531
 
511
532
  if (isEncoded) {
512
- chr = encodeURIComponent(chr);
533
+ chr = safeEncodeURIComponent(chr);
513
534
  } else {
514
535
  // try to urlencode current char
515
- chr = chr === ' ' ? chr : encodeURIComponent(chr);
536
+ chr = chr === ' ' ? chr : safeEncodeURIComponent(chr);
516
537
  // By default it is not required to encode a line, the need
517
538
  // only appears when the string contains unicode or special chars
518
539
  // in this case we start processing the line over and encode all chars
@@ -520,7 +541,7 @@ var libmime = module.exports = {
520
541
  // Check if it is even possible to add the encoded char to the line
521
542
  // If not, there is no reason to use this line, just push it to the list
522
543
  // and start a new line with the char that needs encoding
523
- if ((encodeURIComponent(line) + chr).length >= maxLength) {
544
+ if ((safeEncodeURIComponent(line) + chr).length >= maxLength) {
524
545
  list.push({
525
546
  line: line,
526
547
  encoded: isEncoded
@@ -542,7 +563,7 @@ var libmime = module.exports = {
542
563
  line: line,
543
564
  encoded: isEncoded
544
565
  });
545
- line = chr = encodedStr[i] === ' ' ? ' ' : encodeURIComponent(encodedStr[i]);
566
+ line = chr = encodedStr[i] === ' ' ? ' ' : safeEncodeURIComponent(encodedStr[i]);
546
567
  if (chr === encodedStr[i]) {
547
568
  isEncoded = false;
548
569
  startPos = i - 1;
@@ -573,6 +594,7 @@ var libmime = module.exports = {
573
594
  });
574
595
  },
575
596
 
597
+
576
598
  /**
577
599
  * Returns file extension for a content type string. If no suitable extensions
578
600
  * are found, 'bin' is used as the default extension
@@ -724,4 +746,38 @@ function splitMimeEncodedString(str, maxlen) {
724
746
  }
725
747
 
726
748
  return lines;
749
+ }
750
+
751
+ function encodeURICharComponent(chr) {
752
+ var i, len, ord;
753
+ var res = '';
754
+
755
+ ord = chr.charCodeAt(0).toString(16).toUpperCase();
756
+ if (ord.length % 2) {
757
+ ord = '0' + ord;
758
+ }
759
+ if (ord.length > 2) {
760
+ for (i = 0, len = ord.length / 2; i < len; i++) {
761
+ res += '%' + ord.substr(i, 2);
762
+ }
763
+ } else {
764
+ res += '%' + ord;
765
+ }
766
+
767
+ return res;
768
+ }
769
+
770
+ function safeEncodeURIComponent(str) {
771
+ str = (str || '').toString();
772
+
773
+ try {
774
+ // might throw if we try to encode invalid sequences, eg. partial emoji
775
+ str = encodeURIComponent(str);
776
+ } catch (E) {
777
+ // should never run
778
+ return str.replace(/[^\x00-\x1F *'()<>@,;:\\"\[\]?=\u007F-\uFFFF]+/g, '');
779
+ }
780
+
781
+ // ensure chars that are not handled by encodeURICompent are converted as well
782
+ return str.replace(/[\x00-\x1F *'()<>@,;:\\"\[\]?=\u007F-\uFFFF]/g, encodeURICharComponent);
727
783
  }