mailcomposer
Advanced tools
Comparing version 0.1.10 to 0.1.11
@@ -5,2 +5,3 @@ var Stream = require("stream").Stream, | ||
toPunycode = require("./punycode"), | ||
DKIMSign = require("./dkim").DKIMSign, | ||
fs = require("fs"); | ||
@@ -77,2 +78,20 @@ | ||
/** | ||
* <p>If set to true, caches the output for further processing (DKIM signing etc.)</p> | ||
* @private | ||
*/ | ||
this._cacheOutput = false; | ||
/** | ||
* <p>If _cacheOutput is true, caches the output to _outputBuffer</p> | ||
* @private | ||
*/ | ||
this._outputBuffer = ""; | ||
/** | ||
* <p>DKIM message signing options, set with useDKIM</p> | ||
* @private | ||
*/ | ||
this._dkim = false; | ||
/** | ||
* <p>Counter for generating unique mime boundaries etc.</p> | ||
@@ -160,2 +179,17 @@ * @private | ||
/** | ||
* <p>Setup DKIM for signing generated message. Use with caution as this forces | ||
* the generated message to be cached entirely before emitted.</p> | ||
* | ||
* @param {Object} dkim DKIM signing settings | ||
* @param {String} [dkim.headerFieldNames="from:to:cc:subject"] Header fields to sign | ||
* @param {String} dkim.privateKey DKMI private key | ||
* @param {String} dkim.domainName Domain name to use for signing (ie: "domain.com") | ||
* @param {String} dkim.keySelector Selector for the DKMI public key (ie. "dkim" if you have set up a TXT record for "dkim._domainkey.domain.com" | ||
*/ | ||
MailComposer.prototype.useDKIM = function(dkim){ | ||
this._dkim = dkim || {}; | ||
this._cacheOutput = true; | ||
}; | ||
/** | ||
* <p>Adds an attachment to the list</p> | ||
@@ -466,3 +500,7 @@ * | ||
this.emit("data", new Buffer(headers.join("\r\n")+"\r\n\r\n", "utf-8")); | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(headers.join("\r\n")+"\r\n\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += headers.join("\r\n")+"\r\n\r\n"; | ||
} | ||
}; | ||
@@ -822,3 +860,7 @@ | ||
if(slice && slice.length){ | ||
this.emit("data", new Buffer(slice.join("\r\n")+"\r\n", "utf-8")); | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(slice.join("\r\n")+"\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += slice.join("\r\n")+"\r\n"; | ||
} | ||
} | ||
@@ -836,3 +878,7 @@ | ||
}else{ | ||
this.emit("end"); | ||
if(!this._cacheOutput){ | ||
this.emit("end"); | ||
}else{ | ||
this._processBufferedOutput(); | ||
} | ||
} | ||
@@ -842,3 +888,7 @@ }).bind(this)); | ||
}else if(isEnd){ | ||
this.emit("end"); | ||
if(!this._cacheOutput){ | ||
this.emit("end"); | ||
}else{ | ||
this._processBufferedOutput(); | ||
} | ||
} | ||
@@ -887,3 +937,7 @@ break; | ||
this.emit("data", new Buffer(data + "\r\n", "utf-8")); | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(data + "\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += data + "\r\n"; | ||
} | ||
process.nextTick(callback); | ||
@@ -913,4 +967,12 @@ return; | ||
if(err || !stat.isFile()){ | ||
this.emit("data", new Buffer(new Buffer("<ERROR OPENING FILE>", | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(new Buffer("<ERROR OPENING FILE>", | ||
"utf-8").toString("base64")+"\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += new Buffer("<ERROR OPENING FILE>", | ||
"utf-8").toString("base64")+"\r\n"; | ||
} | ||
process.nextTick(callback); | ||
@@ -942,4 +1004,9 @@ return; | ||
stream.on("error", (function(error){ | ||
this.emit("data", new Buffer(new Buffer("<ERROR READING STREAM>", | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(new Buffer("<ERROR READING STREAM>", | ||
"utf-8").toString("base64")+"\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += new Buffer("<ERROR READING STREAM>", | ||
"utf-8").toString("base64")+"\r\n"; | ||
} | ||
process.nextTick(callback); | ||
@@ -962,3 +1029,7 @@ }).bind(this)); | ||
if(data.length){ | ||
this.emit("data", new Buffer(data.trim()+"\r\n", "utf-8")); | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(data.trim()+"\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += data.trim()+"\r\n"; | ||
} | ||
} | ||
@@ -973,3 +1044,7 @@ }).bind(this)); | ||
data = remainder.toString("base64").replace(/.{76}/g,"$&\r\n"); | ||
this.emit("data", new Buffer(data.trim()+"\r\n", "utf-8")); | ||
if(!this._cacheOutput){ | ||
this.emit("data", new Buffer(data.trim()+"\r\n", "utf-8")); | ||
}else{ | ||
this._outputBuffer += data.trim()+"\r\n"; | ||
} | ||
} | ||
@@ -983,2 +1058,19 @@ process.nextTick(callback); | ||
/** | ||
* <p>Processes buffered output and emits 'end'</p> | ||
*/ | ||
MailComposer.prototype._processBufferedOutput = function(){ | ||
var dkimSignature; | ||
if(this._dkim){ | ||
if(dkimSignature = DKIMSign(this._outputBuffer, this._dkim)){ | ||
this.emit("data", new Buffer(dkimSignature+"\r\n", "utf-8")); | ||
} | ||
} | ||
this.emit("data", new Buffer(this._outputBuffer, "utf-8")) | ||
process.nextTick(this.emit.bind(this,"end")); | ||
}; | ||
/* HELPER FUNCTIONS */ | ||
@@ -1011,3 +1103,5 @@ | ||
return c.toUpperCase(); | ||
}).replace(/^MIME\-/i, "MIME-"); | ||
}). | ||
replace(/^MIME\-/i, "MIME-"). | ||
replace(/^DKIM\-/i, "DKIM-"); | ||
}; | ||
@@ -1014,0 +1108,0 @@ |
@@ -327,4 +327,4 @@ //Javascript Punycode converter derived from example in RFC3492. | ||
var domainParts = domain.split(/\./).map(punycode.ToASCII); | ||
return start + domainParts.join("."); | ||
return (start || "") + domainParts.join("."); | ||
}); | ||
}; |
{ | ||
"name": "mailcomposer", | ||
"description": "Compose E-Mail messages", | ||
"version": "0.1.10", | ||
"version": "0.1.11", | ||
"author" : "Andris Reinman", | ||
@@ -6,0 +6,0 @@ "maintainers":[ |
@@ -13,4 +13,2 @@ # mailcomposer | ||
See autogenerated docs [here](http://www.node.ee/mcdoc/). | ||
**mailcomposer** supports: | ||
@@ -22,2 +20,3 @@ | ||
* **Embedded images** in HTML | ||
* **DKIM** signing | ||
* usage of **your own** transport mechanism | ||
@@ -243,2 +242,34 @@ | ||
### DKIM Signing | ||
**mailcomposer** supports DKIM signing with very simple setup. Use this with caution | ||
though since the generated message needs to be buffered entirely before it can be | ||
signed - in this case the streaming capability offered by mailcomposer is illusionary, | ||
there will only be one `'data'` event with the entire message. Not a big deal with | ||
small messages but might consume a lot of RAM when using larger attachments. | ||
Set up the DKIM signing with `useDKIM` method: | ||
mailcomposer.useDKIM(dkimOptions) | ||
Where `dkimOptions` includes necessary options for signing | ||
* **domainName** - the domainname that is being used for signing | ||
* **keySelector** - key selector. If you have set up a TXT record with DKIM public key at *zzz._domainkey.example.com* then `zzz` is the selector | ||
* **privateKey** - DKIM private key that is used for signing as a string | ||
* **headerFieldNames** - optional colon separated list of header fields to sign, by default all fields suggested by RFC4871 #5.5 are used | ||
**NB!** Currently if several header fields with the same name exists, only the last one (the one in the bottom) is signed. | ||
Example: | ||
mailcomposer.setMessageOption({from: "andris@tr.ee"}); | ||
mailcomposer.setMessageOption({to: "andris@node.ee"}); | ||
mailcomposer.setMessageOption({body: "Hello world!"}); | ||
mailcomposer.useDKIM({ | ||
domainName: "node.ee", | ||
keySelector: "dkim", | ||
privateKey: fs.readFileSync("private_key.pem") | ||
}); | ||
### Start streaming | ||
@@ -245,0 +276,0 @@ |
@@ -925,3 +925,31 @@ var testCase = require('nodeunit').testCase, | ||
exports["Output buffering"] = { | ||
"Use DKIM": function(test){ | ||
var mc = new MailComposer(); | ||
mc.setMessageOption({ | ||
from: "Andris Reinman <andris@node.ee>", | ||
to: "Andris <andris.reinman@gmail.com>", | ||
html: "<b>Hello world!</b>", | ||
subject: "Hello world!" | ||
}); | ||
mc.useDKIM({ | ||
domainName: "do-not-trust.node.ee", | ||
keySelector: "dkim", | ||
privateKey: fs.readFileSync(__dirname+"/test_private.pem") | ||
}); | ||
mc.streamMessage(); | ||
var mp = new MailParser(); | ||
mc.pipe(mp); | ||
mp.on("end", function(mail){ | ||
test.equal(mail.headers['dkim-signature'].replace(/\s/g, ""), 'v=1;a=rsa-sha256;c=relaxed/relaxed;d=do-not-trust.node.ee;q=dns/txt;s=dkim;bh=88i0PUP3tj3X/n0QT6Baw8ZPSeHZPqT7J0EmE26pjng=;h=from:subject:to:mime-version:content-type:content-transfer-encoding;b=dtxxQLotrcarEA5nbgBJLBJQxSAHcfrNxxpItcXSj68ntRvxmjXt9aPZTbVrzfRYe+xRzP2FTGpS7js8iYpAZZ2N3DBRLVp4gyyKHB1oWMkg/EV92uPtnjQ3MlHMbxC0'); | ||
test.done(); | ||
}); | ||
} | ||
} | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 3 instances in 1 package
312
0
163835
13
2307
3