Socket
Socket
Sign inDemoInstall

libmime

Package Overview
Dependencies
Maintainers
1
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

libmime - npm Package Compare versions

Comparing version 0.1.0 to 0.1.1

60

package.json
{
"name": "libmime",
"description": "Encode and decode quoted printable and base64 strings",
"version": "0.1.0",
"main": "src/libmime",
"homepage": "https://github.com/andris9/libmime",
"repository": {
"type": "git",
"url": "git://github.com/andris9/libmime.git"
},
"license": "MIT",
"keywords": [
"MIME",
"Base64",
"Quoted-Printable"
],
"author": "Andris Reinman <andris@kreata.ee>",
"scripts": {
"test": "grunt"
},
"dependencies": {
"iconv-lite": "^0.4.0"
},
"devDependencies": {
"chai": "~1.8.1",
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.8.0",
"grunt-mocha-test": "~0.10.0"
}
}
"name": "libmime",
"description": "Encode and decode quoted printable and base64 strings",
"version": "0.1.1",
"main": "src/libmime",
"homepage": "https://github.com/andris9/libmime",
"repository": {
"type": "git",
"url": "git://github.com/andris9/libmime.git"
},
"license": "MIT",
"keywords": [
"MIME",
"Base64",
"Quoted-Printable"
],
"author": "Andris Reinman <andris@kreata.ee>",
"scripts": {
"test": "grunt"
},
"dependencies": {
"iconv-lite": "^0.4.0",
"libbase64": "^0.1.0",
"libqp": "^0.1.1"
},
"devDependencies": {
"chai": "~1.8.1",
"grunt": "~0.4.1",
"grunt-contrib-jshint": "~0.8.0",
"grunt-mocha-test": "~0.10.0"
}
}
# libmime
`libmime` provides useful MIME related functions like encoding and decoding quoted-printable strings or detecting content-type strings for file extensions.
`libmime` provides useful MIME related functions. For Quoted-Printable and Base64 encoding and decoding see [libqp](https://github.com/andris9/libqp) and [libbase64](https://github.com/andris9/libabase64).

@@ -17,49 +17,9 @@ ## Installation

### Quoted Printable encoding
#### #quotedPrintableEncode
Encodes a string into Quoted-printable format.
libmime.quotedPrintableEncode(str [, fromCharset]) -> String
* **str** - String or an Buffer to mime encode
* **fromCharset** - If the first parameter is a Buffer object, use this charset to decode the value to unicode before encoding
#### #quotedPrintableDecode
Decodes a string from Quoted-printable format.
libmime.quotedPrintableDecode(str [, fromCharset]) -> String
* **str** - Mime encoded string
* **fromCharset** - Use this charset to decode mime encoded string to unicode
### Base64 Encoding
#### #base64Encode
Encodes a string into Base64 format.
libmime.base64Encode(str [, fromCharset]) -> String
* **str** - String or an Buffer to base64 encode
* **fromCharset** - If the first parameter is a Buffer object, use this charset to decode the value to unicode before encoding
#### #base64Decode
Decodes a string from Base64 format to an unencoded unicode string.
libmime.base64Decode(str [, fromCharset]) -> String
* **str** Base64 encoded string
* **fromCharset** Use this charset to decode base64 encoded string to unicode
### Encoded Words
#### #mimeWordEncode
#### #encodeWord
Encodes a string into mime [encoded word](http://en.wikipedia.org/wiki/MIME#Encoded-Word) format.
libmime.mimeWordEncode(str [, mimeWordEncoding[, maxLength[, fromCharset]]]) -> String
libmime.encodeWord(str [, mimeWordEncoding[, maxLength]]) → String

@@ -69,7 +29,6 @@ * **str** - String or Buffer to be encoded

* **maxLength** - If set, split mime words into several chunks if needed
* **fromCharset** - If the first parameter is a Buffer object, use this encoding to decode the value to unicode
**Example**
libmime.mimeWordEncode('See on õhin test', 'Q');
libmime.encodeWord('See on õhin test', 'Q');

@@ -80,7 +39,7 @@ Becomes with UTF-8 and Quoted-printable encoding

#### #mimeWordDecode
#### #decodeWord
Decodes a string from mime encoded word format.
libmime.mimeWordDecode(str) -> String
libmime.decodeWord(str) → String

@@ -91,3 +50,3 @@ * **str** - String to be decoded

libmime.mimeWordDecode('=?UTF-8?Q?See_on_=C3=B5hin_test?=');
libmime.decodeWord('=?UTF-8?Q?See_on_=C3=B5hin_test?=');

@@ -98,7 +57,7 @@ will become

#### #mimeWordsEncode
#### #encodeWords
Encodes non ascii sequences in a string to mime words.
libmime.mimeWordsEncode(str[, mimeWordEncoding[, maxLength[, fromCharset]]]) -> String
libmime.encodeWords(str[, mimeWordEncoding[, maxLength]) → String

@@ -108,9 +67,8 @@ * **str** - String or Buffer to be encoded

* **maxLength** - If set, split mime words into several chunks if needed
* **fromCharset** - If the first parameter is a Buffer object, use this charset to decode the value to unicode before encoding
#### #mimeWordsDecode
#### #decodeWords
Decodes a string that might include one or several mime words. If no mime words are found from the string, the original string is returned
libmime.mimeWordsDecode(str) -> String
libmime.decodeWords(str) → String

@@ -125,6 +83,6 @@ * **str** - String to be decoded

libmime.foldLines(str [, lineLengthMax[, afterSpace]]) -> String
libmime.foldLines(str [, lineLength[, afterSpace]]) → String
* **str** - String to be folded
* **lineLengthMax** - Maximum length of a line (defaults to 76)
* **lineLength** - Maximum length of a line (defaults to 76)
* **afterSpace** - If true, leave a space in the end of a line

@@ -141,17 +99,17 @@

#### #addSoftLinebreaks
Adds soft line breaks to encoded strings. Needed for folding body lines.
#### #encodeFlowed
libmime.addSoftLinebreaks(str, encoding, lineLengthMax) -> String
Adds soft line breaks to content marked with `format=flowed` options to ensure that no line in the message is never longer than lineLength.
* **str** Encoded string that requires wrapping
* **encoding** Either Q for quoted-printable, B for base64 (the default) or F for `format=flowed`
* **lineLengthMax** Maximum line length without line breaks(defaults to 76)
libmime.encodeFlowed(str [, lineLength]) → String
#### #flowedDecode
* **str** Plaintext string that requires wrapping
* **lineLength** (defaults to 76) Maximum length of a line
#### #decodeFlowed
Unwraps a plaintext string in format=flowed wrapping.
libmime.flowedDecode(str [, delSp]) -> String
libmime.decodeFlowed(str [, delSp]) → String

@@ -163,32 +121,21 @@ * **str** Plaintext string with format=flowed to decode

#### #headerLineEncode
#### #decodeHeader
Encodes and folds a header line for a MIME message header. Shorthand for `mimeWordsEncode` + `foldLines`.
libmime.headerLineEncode(key, value[, fromCharset])
* **key** - Key name, will not be encoded
* **value** - Value to be encoded
* **fromCharset** - If the `value` parameter is a Buffer object, use this charset to decode the value to unicode before encoding
#### #headerLineDecode
Unfolds a header line and splits it to key and value pair. The return value is in the form of `{key: 'subject', value: 'test'}`. The value is not mime word decoded, you need to do your own decoding based on the rules for the specific header key.
libmime.headerLineDecode(headerLine) -> Object
libmime.decodeHeader(headerLine) → Object
* **headerLine** - Single header line, might include linebreaks as well if folded
#### #headerLinesDecode
#### #decodeHeaders
Parses a block of header lines. Does not decode mime words as every header
might have its own rules (eg. formatted email addresses and such).
Parses a block of header lines. Does not decode mime words as every header might have its own rules (eg. formatted email addresses and such).
Return value is an object of headers, where header keys are object keys. NB! Several values with the same key make up an array of values for the same key.
Return value is an object of headers, where header keys are object keys and values are arrays.
libmime.headerLinesDecode(headers) -> Object
libmime.decodeHeaders(headers) → Object
* **headers** - Headers string
#### #parseStructuredHeaderValue
#### #parseHeaderValue

@@ -198,3 +145,3 @@ Parses a header value with `key=value` arguments into a structured object. Useful when dealing with

parseStructuredHeaderValue(valueString) -> Object
parseHeaderValue(valueString) → Object

@@ -206,3 +153,3 @@ * **valueString** - a header value without the key

```javascript
parseStructuredHeaderValue('content-type: text/plain; CHARSET="UTF-8"');
parseHeaderValue('content-type: text/plain; CHARSET="UTF-8"');
```

@@ -221,17 +168,17 @@

#### #buildStructuredHeaderValue
#### #buildHeaderValue
Joins structured header value together as 'value; param1=value1; param2=value2'
buildStructuredHeaderValue(structuredHeader) -> String
buildHeaderValue(structuredHeader) → String
* **structuredHeader** - a header value formatted with `parseStructuredHeaderValue`
* **structuredHeader** - a header value formatted with `parseHeaderValue`
`filename` argument is encoded with continuation encoding if needed
#### #continuationEncode
#### #buildHeaderParam
Encodes and splits a header param value according to [RFC2231](https://tools.ietf.org/html/rfc2231#section-3) Parameter Value Continuations.
libmime.continuationEncode(key, str, maxLength [, fromCharset]) -> Array
libmime.buildHeaderParam(key, str, maxLength) → Array

@@ -241,3 +188,2 @@ * **key** - Parameter key (eg. `filename`)

* **maxLength** - Maximum length of the encoded string part (not line length). Defaults to 50
* **fromCharset** - If `str` is a Buffer object, use this charset to decode the value to unicode before encoding

@@ -249,4 +195,4 @@ The method returns an array of encoded parts with the following structure: `[{key:'...', value: '...'}]`

```
libmime.continuationEncode('filename', 'filename õäöü.txt', 20);
->
libmime.buildHeaderParam('filename', 'filename õäöü.txt', 20);
[ { key: 'filename*0*', value: 'utf-8\'\'filename%20' },

@@ -270,3 +216,3 @@ { key: 'filename*1*', value: '%C3%B5%C3%A4%C3%B6' },

libmime.detectExtension(mimeType) -> String
libmime.detectExtension(mimeType) → String

@@ -283,3 +229,3 @@ * **mimeType** - Content type to be checked for

libmime.detectMimeType(extension) -> String
libmime.detectMimeType(extension) → String

@@ -286,0 +232,0 @@ * **extension** Extension (or filename) to be checked for

'use strict';
var libcharset = require('./charset');
var libbase64 = require('./base64');
var libbase64 = require('libbase64');
var libqp = require('libqp');
var mimetypes = require('./mimetypes');
var libmime = module.exports = {
/**
* Encodes all non printable and non ascii bytes to =XX form, where XX is the
* byte value in hex. This function does not convert linebreaks etc. it
* only escapes character sequences
*
* @param {String|Buffer} data Either a string or an Buffer
* @param {String} [fromCharset='UTF-8'] Source encoding
* @return {String} Mime encoded string
*/
mimeEncode: function(data, fromCharset) {
fromCharset = fromCharset || 'UTF-8';
var buffer = libcharset.convert(data || '', fromCharset),
ranges = [
[0x09],
[0x0A],
[0x0D],
[0x20],
[0x21],
[0x23, 0x3C],
[0x3E],
[0x40, 0x5E],
[0x60, 0x7E]
],
result = '';
for (var i = 0, len = buffer.length; i < len; i++) {
if (libmime._checkRanges(buffer[i], ranges)) {
result += String.fromCharCode(buffer[i]);
continue;
}
result += '=' + (buffer[i] < 0x10 ? '0' : '') + buffer[i].toString(16).toUpperCase();
}
return result;
},
/**
* Decodes mime encoded string to an unicode string
* Checks if a value is plaintext string (uses only printable 7bit chars)
*
* @param {String} str Mime encoded string
* @param {String} [fromCharset='UTF-8'] Source encoding
* @return {String} Decoded unicode string
* @param {String} value String to be tested
* @returns {Boolean} true if it is a plaintext string
*/
mimeDecode: function(str, fromCharset) {
str = (str || '').toString();
fromCharset = fromCharset || 'UTF-8';
var encodedBytesCount = (str.match(/\=[\da-fA-F]{2}/g) || []).length,
bufferLength = str.length - encodedBytesCount * 2,
chr, hex,
buffer = new Buffer(bufferLength),
bufferPos = 0;
for (var i = 0, len = str.length; i < len; i++) {
chr = str.charAt(i);
if (chr === '=' && (hex = str.substr(i + 1, 2)) && /[\da-fA-F]{2}/.test(hex)) {
buffer[bufferPos++] = parseInt(hex, 16);
i += 2;
continue;
}
buffer[bufferPos++] = chr.charCodeAt(0);
}
return libcharset.decode(buffer, fromCharset);
},
/**
* Encodes a string or an typed array of given charset into unicode
* base64 string. Also adds line breaks
*
* @param {String|Buffer} data String to be base64 encoded
* @param {String} [fromCharset='UTF-8']
* @return {String} Base64 encoded string
*/
base64Encode: function(data, fromCharset) {
var buf;
if (fromCharset !== 'binary' && typeof data !== 'string') {
buf = libcharset.convert(data || '', fromCharset);
isPlainText: function(value) {
if (typeof value !== 'string' || /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/.test(value)) {
return false;
} else {
buf = data;
return true;
}
return libbase64.encode(buf);
},
/**
* Decodes a base64 string of any charset into an unicode string
* Checks if a multi line string containes lines longer than the selected value.
*
* @param {String} str Base64 encoded string
* @param {String} [fromCharset='UTF-8'] Original charset of the base64 encoded string
* @return {String} Decoded unicode string
* Useful when detecting if a mail message needs any processing at all –
* if only plaintext characters are used and lines are short, then there is
* no need to encode the values in any way. If the value is plaintext but has
* longer lines then allowed, then use format=flowed
*
* @param {Number} lineLength Max line length to check for
* @returns {Boolean} Returns true if there is at least one line longer than lineLength chars
*/
base64Decode: function(str, fromCharset) {
var buf = libbase64.decode(str || '', 'buffer');
return libcharset.decode(buf, fromCharset);
hasLongerLines: function(str, lineLength) {
return new RegExp('^.{' + (lineLength + 1) + ',}', 'm').test(str);
},

@@ -114,3 +46,3 @@

*/
flowedDecode: function(str, delSp) {
decodeFlowed: function(str, delSp) {
str = (str || '').toString();

@@ -129,3 +61,3 @@

}
if (/ $/.test(previousValue) && !/(^|\n)\-\- $/.test(previousValue) ||  index === 1) {
if (/ $/.test(previousValue) && !/(^|\n)\-\- $/.test(previousValue) || index === 1) {
return body + currentValue;

@@ -142,42 +74,23 @@ } else {

/**
* Encodes a string or an Buffer into a quoted printable encoding
* This is almost the same as mimeEncode, except line breaks will be changed
* as well to ensure that the lines are never longer than allowed length
* Adds soft line breaks to content marked with format=flowed to
* ensure that no line in the message is never longer than lineLength
*
* @param {String|Buffer} data String or an Buffer to mime encode
* @param {String} [fromCharset='UTF-8'] Original charset of the string
* @return {String} Mime encoded string
* @param {String} str Plaintext string that requires wrapping
* @param {Number} [lineLength=76] Maximum length of a line
* @return {String} String with forced line breaks
*/
quotedPrintableEncode: function(data, fromCharset) {
var mimeEncodedStr = libmime.mimeEncode(data, fromCharset);
encodeFlowed: function(str, lineLength) {
lineLength = lineLength || 76;
mimeEncodedStr = mimeEncodedStr.
// fix line breaks, ensure <CR><LF>
replace(/\r?\n|\r/g, '\r\n').
// replace spaces in the end of lines
replace(/[\t ]+$/gm, function(spaces) {
return spaces.replace(/ /g, '=20').replace(/\t/g, '=09');
var flowed = [];
str.split(/\r?\n/).forEach(function(line) {
flowed.push(libmime.foldLines(line.
// space stuffing http://tools.ietf.org/html/rfc3676#section-4.2
replace(/^( |From|>)/igm, ' $1'),
lineLength, true));
});
// add soft line breaks to ensure line lengths sjorter than 76 bytes
return mimeEncodedStr;
return flowed.join('\r\n');
},
/**
* Decodes a string from a quoted printable encoding. This is almost the
* same as mimeDecode, except line breaks will be changed as well
*
* @param {String} str Mime encoded string to decode
* @param {String} [fromCharset='UTF-8'] Original charset of the string
* @return {String} Mime decoded string
*/
quotedPrintableDecode: function(str, fromCharset) {
str = (str || '').toString();
// remove soft line breaks
str = str.replace(/\=(?:\r?\n|$)/g, '');
return libmime.mimeDecode(str, fromCharset);
},
/**
* Encodes a string or an Buffer to an UTF-8 MIME Word (rfc2047)

@@ -188,13 +101,6 @@ *

* @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
* @param {String} [fromCharset='UTF-8'] Source sharacter set
* @return {String} Single or several mime words joined together
*/
mimeWordEncode: function(data, mimeWordEncoding, maxLength, fromCharset) {
encodeWord: function(data, mimeWordEncoding, maxLength) {
mimeWordEncoding = (mimeWordEncoding || 'Q').toString().toUpperCase().trim().charAt(0);
if (!fromCharset && typeof maxLength === 'string' && !maxLength.match(/^[0-9]+$/)) {
fromCharset = maxLength;
maxLength = undefined;
}
maxLength = maxLength || 0;

@@ -211,9 +117,8 @@

if (mimeWordEncoding === 'Q') {
encodedStr = libmime.mimeEncode(data, fromCharset);
encodedStr = encodedStr.replace(/[\r\n\t_]/g, function(chr) {
var code = chr.charCodeAt(0);
return '=' + (code < 0x10 ? '0' : '') + code.toString(16).toUpperCase();
}).replace(/\s/g, '_');
encodedStr = libqp.encode(data).replace(/[_?\r\n\t"]/g, function(chr) {
var ord = chr.charCodeAt(0).toString(16).toUpperCase();
return '=' + (ord.length === 1 ? '0' + ord : ord);
}).replace(/%20| /g, '_');
} else if (mimeWordEncoding === 'B') {
encodedStr = typeof data === 'string' ? data : libmime.decode(data, fromCharset);
encodedStr = typeof data === 'string' ? data : libbase64.encode(data);
maxLength = Math.max(3, (maxLength - maxLength % 4) / 4 * 3);

@@ -224,3 +129,3 @@ }

if (mimeWordEncoding === 'Q') {
encodedStr = libmime._splitMimeEncodedString(encodedStr, maxLength).join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
encodedStr = splitMimeEncodedString(encodedStr, maxLength).join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
} else {

@@ -235,3 +140,3 @@

if (parts.length > 1) {
return '=?' + toCharset + '?' + mimeWordEncoding + '?' + parts.join('?= =?' + toCharset + '?' + mimeWordEncoding + '?') + '?=';
encodedStr = parts.join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
} else {

@@ -242,3 +147,3 @@ encodedStr = parts.join('');

} else if (mimeWordEncoding === 'B') {
encodedStr = libbase64.encode(encodedStr);
encodedStr = libbase64.encode(data);
}

@@ -250,29 +155,2 @@

/**
* Finds word sequences with non ascii text and converts these to mime words
*
* @param {String|Buffer} data String to be encoded
* @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
* @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
* @param {String} [fromCharset='UTF-8'] Source sharacter set
* @return {String} String with possible mime words
*/
mimeWordsEncode: function(data, mimeWordEncoding, maxLength, fromCharset) {
if (!fromCharset && typeof maxLength === 'string' && !maxLength.match(/^[0-9]+$/)) {
fromCharset = maxLength;
maxLength = undefined;
}
maxLength = maxLength || 0;
var decodedValue = libcharset.decode(libcharset.convert((data || ''), fromCharset)),
encodedValue;
encodedValue = decodedValue.replace(/([^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*(?:\s+[^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*\s*)?)+/g, function(match) {
return match.length ? libmime.mimeWordEncode(match, mimeWordEncoding || 'Q', maxLength) : '';
});
return encodedValue;
},
/**
* Decode a complete mime word encoded string

@@ -283,3 +161,3 @@ *

*/
mimeWordDecode: function(str) {
decodeWord: function(str) {
str = (str || '').toString().trim();

@@ -300,12 +178,38 @@

encoding = (match[2] || 'Q').toString().toUpperCase();
str = (match[3] || '').replace(/_/g, ' ');
str = (match[3] || '').replace(/_/g, ' ').replace(/ $/, '=20');
if (encoding === 'B') {
return libmime.base64Decode(str, fromCharset);
return libcharset.decode(libbase64.decode(str), fromCharset);
} else if (encoding === 'Q') {
return libmime.mimeDecode(str, fromCharset);
return libcharset.decode(libqp.decode(str), fromCharset);
} else {
return str;
}
},
/**
* Finds word sequences with non ascii text and converts these to mime words
*
* @param {String|Buffer} data String to be encoded
* @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
* @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
* @param {String} [fromCharset='UTF-8'] Source sharacter set
* @return {String} String with possible mime words
*/
encodeWords: function(data, mimeWordEncoding, maxLength, fromCharset) {
if (!fromCharset && typeof maxLength === 'string' && !maxLength.match(/^[0-9]+$/)) {
fromCharset = maxLength;
maxLength = undefined;
}
maxLength = maxLength || 0;
var decodedValue = libcharset.decode(libcharset.convert((data || ''), fromCharset)),
encodedValue;
encodedValue = decodedValue.replace(/([^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*(?:\s+[^\s\u0080-\uFFFF]*[\u0080-\uFFFF]+[^\s\u0080-\uFFFF]*\s*)?)+/g, function(match) {
return match.length ? libmime.encodeWord(match, mimeWordEncoding || 'Q', maxLength) : '';
});
return encodedValue;
},

@@ -319,3 +223,3 @@

*/
mimeWordsDecode: function(str) {
decodeWords: function(str) {
str = (str || '').toString();

@@ -325,3 +229,3 @@ str = str.

replace(/\=\?([\w_\-\*]+)\?([QqBb])\?[^\?]+\?\=/g, function(mimeWord) {
return libmime.mimeWordDecode(mimeWord);
return libmime.decodeWord(mimeWord);
});

@@ -333,61 +237,2 @@

/**
* Folds long lines, useful for folding header lines (afterSpace=false) and
* flowed text (afterSpace=true)
*
* @param {String} str String to be folded
* @param {Number} [lineLengthMax=76] Maximum length of a line
* @param {Boolean} afterSpace If true, leave a space in th end of a line
* @return {String} String with folded lines
*/
foldLines: function(str, lineLengthMax, afterSpace) {
str = (str || '').toString();
lineLengthMax = lineLengthMax || 76;
var pos = 0,
len = str.length,
result = '',
line, match;
while (pos < len) {
line = str.substr(pos, lineLengthMax);
if (line.length < lineLengthMax) {
result += line;
break;
}
if ((match = line.match(/^[^\n\r]*(\r?\n|\r)/))) {
line = match[0];
result += line;
pos += line.length;
continue;
} else if ((match = line.match(/(\s+)[^\s]*$/)) && match[0].length - (afterSpace ? (match[1] || '').length : 0) < line.length) {
line = line.substr(0, line.length - (match[0].length - (afterSpace ? (match[1] || '').length : 0)));
} else if ((match = str.substr(pos + line.length).match(/^[^\s]+(\s*)/))) {
line = line + match[0].substr(0, match[0].length - (!afterSpace ? (match[1] || '').length : 0));
}
result += line;
pos += line.length;
if (pos < len) {
result += '\r\n';
}
}
return result;
},
/**
* Encodes and folds a header line for a MIME message header.
* Shorthand for mimeWordsEncode + foldLines
*
* @param {String} key Key name, will not be encoded
* @param {String|Buffer} value Value to be encoded
* @param {String} [fromCharset='UTF-8'] Character set of the value
* @return {String} encoded and folded header line
*/
headerLineEncode: function(key, value, fromCharset) {
var encodedValue = libmime.mimeWordsEncode(value, 'Q', 52, fromCharset);
return libmime.foldLines(key + ': ' + encodedValue, 76);
},
/**
* Splits a string by :

@@ -400,6 +245,6 @@ * The result is not mime word decoded, you need to do your own decoding based

*/
headerLineDecode: function(headerLine) {
decodeHeader: function(headerLine) {
var line = (headerLine || '').toString().replace(/(?:\r?\n|\r)[ \t]*/g, ' ').trim(),
match = line.match(/^\s*([^:]+):(.*)$/),
key = (match && match[1] || '').trim(),
key = (match && match[1] || '').trim().toLowerCase(),
value = (match && match[2] || '').trim();

@@ -420,6 +265,5 @@

*/
headerLinesDecode: function(headers) {
decodeHeaders: function(headers) {
var lines = headers.split(/\r?\n|\r/),
headersObj = {},
key, value,
header,

@@ -436,10 +280,7 @@ i, len;

for (i = 0, len = lines.length; i < len; i++) {
header = libmime.headerLineDecode(lines[i]);
key = (header.key || '').toString().toLowerCase().trim();
value = header.value || '';
if (!headersObj[key]) {
headersObj[key] = value;
header = libmime.decodeHeader(lines[i]);
if (!headersObj[header.key]) {
headersObj[header.key] = [header.value];
} else {
headersObj[key] = [].concat(headersObj[key], value);
headersObj[header.key].push(header.value);
}

@@ -457,3 +298,3 @@ }

*/
buildStructuredHeaderValue: function(structured) {
buildHeaderValue: function(structured) {
var paramsArray = [];

@@ -463,4 +304,5 @@

// filename might include unicode characters so it is a special case
if (param === 'filename') {
libmime.continuationEncode(param, structured.params[param], 50).forEach(function(encodedParam) {
var value = structured.params[param];
if (param === 'filename' || !libmime.isPlainText(value) || value.length >= 75) {
libmime.buildHeaderParam(param, value, 50).forEach(function(encodedParam) {
if (encodedParam.key === param) {

@@ -473,6 +315,6 @@ paramsArray.push(encodedParam.key + '=' + encodedParam.value);

} else {
if (structured.params[param].match(/[\s'"\\;\/=]|^\-/g)) {
paramsArray.push(param + '="' + structured.params[param].replace(/(["\\])/g, "\\$1") + '"');
if (value.match(/[\s'"\\;\/=]|^\-/g)) {
paramsArray.push(param + '="' + value.replace(/(["\\])/g, "\\$1") + '"');
} else {
paramsArray.push(param + '=' + structured.params[param]);
paramsArray.push(param + '=' + value);
}

@@ -489,3 +331,3 @@ }

*
* parseStructuredHeaderValue('content-type: text/plain; CHARSET='UTF-8'') ->
* parseHeaderValue('content-type: text/plain; CHARSET='UTF-8'') ->
* {

@@ -501,3 +343,3 @@ * 'value': 'text/plain',

*/
parseStructuredHeaderValue: function(str) {
parseHeaderValue: function(str) {
var response = {

@@ -641,3 +483,3 @@ value: false,

*/
continuationEncode: function(key, data, maxLength, fromCharset) {
buildHeaderParam: function(key, data, maxLength, fromCharset) {
var list = [];

@@ -754,28 +596,2 @@ var encodedStr = typeof data === 'string' ? data : libmime.decode(data, fromCharset);

/**
* Adds soft line breaks (the ones that will be stripped out when decoding) to
* ensure that no line in the message is never longer than 76 symbols
*
* Lines can't be longer than 76 + <CR><LF> = 78 bytes
* http://tools.ietf.org/html/rfc2045#section-6.7
*
* @param {String} str Encoded string
* @param {String} encoding Either "Q" or "B" (the default)
* @param {Number} [lineLengthMax=76] maximum line length
* @return {String} String with forced line breaks
*/
addSoftLinebreaks: function(str, encoding, lineLengthMax) {
lineLengthMax = lineLengthMax ||  76;
encoding = (encoding || 'b').toString().toLowerCase().trim();
if (encoding === 'q') {
return libmime._addQPSoftLinebreaks(str, lineLengthMax);
} else if (encoding === 'f') {
return libmime._addFlowedSoftLinebreaks(str, lineLengthMax);
} else {
return libmime._addBase64SoftLinebreaks(str, lineLengthMax);
}
},
/**
* Returns file extension for a content type string. If no suitable extensions

@@ -843,184 +659,90 @@ * are found, 'bin' is used as the default extension

/**
* Splits a mime encoded string. Needed for dividing mime words into smaller chunks
* Folds long lines, useful for folding header lines (afterSpace=false) and
* flowed text (afterSpace=true)
*
* @param {String} str Mime encoded string to be split up
* @param {Number} maxlen Maximum length of characters for one part (minimum 12)
* @return {Array} Split string
* @param {String} str String to be folded
* @param {Number} [lineLength=76] Maximum length of a line
* @param {Boolean} afterSpace If true, leave a space in th end of a line
* @return {String} String with folded lines
*/
_splitMimeEncodedString: function(str, maxlen) {
var curLine, match, chr, done,
lines = [];
foldLines: function(str, lineLength, afterSpace) {
str = (str || '').toString();
lineLength = lineLength || 76;
// require at least 12 symbols to fit possible 4 octet UTF-8 sequences
maxlen = Math.max(maxlen || 0, 12);
while (str.length) {
curLine = str.substr(0, maxlen);
// move incomplete escaped char back to main
if ((match = curLine.match(/\=[0-9A-F]?$/i))) {
curLine = curLine.substr(0, match.index);
}
done = false;
while (!done) {
done = true;
// check if not middle of a unicode char sequence
if ((match = str.substr(curLine.length).match(/^\=([0-9A-F]{2})/i))) {
chr = parseInt(match[1], 16);
// invalid sequence, move one char back anc recheck
if (chr < 0xC2 && chr > 0x7F) {
curLine = curLine.substr(0, curLine.length - 3);
done = false;
}
}
}
if (curLine.length) {
lines.push(curLine);
}
str = str.substr(curLine.length);
}
return lines;
},
/**
* Adds soft line breaks (the ones that will be stripped out when decoding base64) to
* ensure that no line in the message is never longer than lineLengthMax
*
* @param {String} base64EncodedStr String in BASE64 encoding
* @param {Number} lineLengthMax Maximum length of a line
* @return {String} String with forced line breaks
*/
_addBase64SoftLinebreaks: function(base64EncodedStr, lineLengthMax) {
base64EncodedStr = (base64EncodedStr || '').toString().trim();
return base64EncodedStr.replace(new RegExp('.{' + lineLengthMax + '}', 'g'), '$&\r\n').trim();
},
/**
* Adds soft line breaks to content marked with format=flowed to
* ensure that no line in the message is never longer than lineLengthMax
*
* @param {String} str Plaintext string that requires wrapping
* @param {Number} lineLengthMax Maximum length of a line
* @return {String} String with forced line breaks
*/
_addFlowedSoftLinebreaks: function(str, lineLengthMax) {
var flowed = [];
str.split(/\r?\n/).forEach(function(line) {
flowed.push(libmime.foldLines(line.
// space stuffing http://tools.ietf.org/html/rfc3676#section-4.2
replace(/^( |From|>)/igm, ' $1'),
lineLengthMax, true));
});
return flowed.join('\r\n');
},
/**
* Adds soft line breaks(the ones that will be stripped out when decoding QP) to * ensure that no line in the message is never longer than lineLengthMax * * Not sure of how and why this works, but at least it seems to be working: /
*
* @param {String} qpEncodedStr String in Quoted-Printable encoding
* @param {Number} lineLengthMax Maximum length of a line
* @return {String} String with forced line breaks
*/
_addQPSoftLinebreaks: function(qpEncodedStr, lineLengthMax) {
qpEncodedStr = (qpEncodedStr || '').toString();
var pos = 0,
len = qpEncodedStr.length,
match, code, line,
lineMargin = Math.floor(lineLengthMax / 3),
result = '';
len = str.length,
result = '',
line, match;
// insert soft linebreaks where needed
while (pos < len) {
line = qpEncodedStr.substr(pos, lineLengthMax);
if ((match = line.match(/\r\n/))) {
line = line.substr(0, match.index + match[0].length);
line = str.substr(pos, lineLength);
if (line.length < lineLength) {
result += line;
pos += line.length;
continue;
break;
}
if (line.substr(-1) === '\n') {
// nothing to change here
if ((match = line.match(/^[^\n\r]*(\r?\n|\r)/))) {
line = match[0];
result += line;
pos += line.length;
continue;
} else if ((match = line.substr(-lineMargin).match(/\n.*?$/))) {
// truncate to nearest line break
line = line.substr(0, line.length - (match[0].length - 1));
result += line;
pos += line.length;
continue;
} else if (line.length > lineLengthMax - lineMargin && (match = line.substr(-lineMargin).match(/[ \t\.,!\?][^ \t\.,!\?]*$/))) {
// truncate to nearest space
line = line.substr(0, line.length - (match[0].length - 1));
} else if (line.substr(-1) === '\r') {
line = line.substr(0, line.length - 1);
} else {
if (line.match(/\=[\da-f]{0,2}$/i)) {
} else if ((match = line.match(/(\s+)[^\s]*$/)) && match[0].length - (afterSpace ? (match[1] || '').length : 0) < line.length) {
line = line.substr(0, line.length - (match[0].length - (afterSpace ? (match[1] || '').length : 0)));
} else if ((match = str.substr(pos + line.length).match(/^[^\s]+(\s*)/))) {
line = line + match[0].substr(0, match[0].length - (!afterSpace ? (match[1] || '').length : 0));
}
// push incomplete encoding sequences to the next line
if ((match = line.match(/\=[\da-f]{0,1}$/i))) {
line = line.substr(0, line.length - match[0].length);
}
result += line;
pos += line.length;
if (pos < len) {
result += '\r\n';
}
}
// ensure that utf-8 sequences are not split
while (line.length > 3 && line.length < len - pos && !line.match(/^(?:=[\da-f]{2}){1,4}$/i) && (match = line.match(/\=[\da-f]{2}$/ig))) {
code = parseInt(match[0].substr(1, 2), 16);
if (code < 128) {
break;
}
return result;
}
};
line = line.substr(0, line.length - 3);
/**
* Splits a mime encoded string. Needed for dividing mime words into smaller chunks
*
* @param {String} str Mime encoded string to be split up
* @param {Number} maxlen Maximum length of characters for one part (minimum 12)
* @return {Array} Split string
*/
function splitMimeEncodedString(str, maxlen) {
var curLine, match, chr, done,
lines = [];
if (code >= 0xC0) {
break;
}
}
// require at least 12 symbols to fit possible 4 octet UTF-8 sequences
maxlen = Math.max(maxlen || 0, 12);
}
}
while (str.length) {
curLine = str.substr(0, maxlen);
if (pos + line.length < len && line.substr(-1) !== '\n') {
if (line.length === 76 && line.match(/\=[\da-f]{2}$/i)) {
line = line.substr(0, line.length - 3);
} else if (line.length === 76) {
line = line.substr(0, line.length - 1);
// move incomplete escaped char back to main
if ((match = curLine.match(/\=[0-9A-F]?$/i))) {
curLine = curLine.substr(0, match.index);
}
done = false;
while (!done) {
done = true;
// check if not middle of a unicode char sequence
if ((match = str.substr(curLine.length).match(/^\=([0-9A-F]{2})/i))) {
chr = parseInt(match[1], 16);
// invalid sequence, move one char back anc recheck
if (chr < 0xC2 && chr > 0x7F) {
curLine = curLine.substr(0, curLine.length - 3);
done = false;
}
pos += line.length;
line += '=\r\n';
} else {
pos += line.length;
}
result += line;
}
return result;
},
/**
* Checks if a number is in specified ranges or not
*
* @param {Number} nr Number to check for
* @ranges {Array} ranges Array of range duples
* @return {Boolean} Returns true, if nr was found to be at least one of the specified ranges
*/
_checkRanges: function(nr, ranges) {
for (var i = ranges.length - 1; i >= 0; i--) {
if (!ranges[i].length) {
continue;
}
if (ranges[i].length === 1 && nr === ranges[i][0]) {
return true;
}
if (ranges[i].length === 2 && nr >= ranges[i][0] && nr <= ranges[i][1]) {
return true;
}
if (curLine.length) {
lines.push(curLine);
}
return false;
str = str.substr(curLine.length);
}
};
return lines;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc