aboutsummaryrefslogtreecommitdiffstats
path: root/tools/node_modules/nodemailer/node_modules/mailcomposer
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/nodemailer/node_modules/mailcomposer')
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/.npmignore1
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/.travis.yml11
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/LICENSE16
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/README.md319
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js217
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/lib/mailcomposer.js1260
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/lib/punycode.js329
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/lib/urlfetch.js71
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/LICENSE16
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/README.md17
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/content-types.js60
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/doc.md191
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/index.js3
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/mime-functions.js486
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/package.json35
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/package.json46
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/test/dkim.js47
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/test/mailcomposer.js1085
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_private.pem12
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_public.pem5
-rw-r--r--tools/node_modules/nodemailer/node_modules/mailcomposer/test/textfile.txt601
21 files changed, 4828 insertions, 0 deletions
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/.npmignore b/tools/node_modules/nodemailer/node_modules/mailcomposer/.npmignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/.npmignore
@@ -0,0 +1 @@
+node_modules \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/.travis.yml b/tools/node_modules/nodemailer/node_modules/mailcomposer/.travis.yml
new file mode 100644
index 0000000..2bc1961
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/.travis.yml
@@ -0,0 +1,11 @@
+language: node_js
+node_js:
+ - 0.6
+ - 0.8
+
+notifications:
+ email:
+ recipients:
+ - andris@node.ee
+ on_success: change
+ on_failure: change
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/LICENSE b/tools/node_modules/nodemailer/node_modules/mailcomposer/LICENSE
new file mode 100644
index 0000000..a47b0ea
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/LICENSE
@@ -0,0 +1,16 @@
+Copyright (c) 2012 Andris Reinman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/README.md b/tools/node_modules/nodemailer/node_modules/mailcomposer/README.md
new file mode 100644
index 0000000..d5e53b4
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/README.md
@@ -0,0 +1,319 @@
+# mailcomposer
+
+**mailcomposer** is a Node.JS module for generating e-mail messages that can be
+streamed to SMTP or file.
+
+This is a standalone module that only generates raw e-mail source, you need to
+write your own or use an existing transport mechanism (SMTP client, Amazon SES,
+SendGrid etc). **mailcomposer** frees you from the tedious task of generating
+[rfc822](http://tools.ietf.org/html/rfc2822) compatible messages.
+
+[![Build Status](https://secure.travis-ci.org/andris9/mailcomposer.png)](http://travis-ci.org/andris9/mailcomposer)
+
+**mailcomposer** supports:
+
+ * **Unicode** to use any characters ✔
+ * **HTML** content as well as **plain text** alternative
+ * **Attachments** and streaming for larger files (use strings, buffers, files or binary streams as attachments)
+ * **Embedded images** in HTML
+ * **DKIM** signing
+ * usage of **your own** transport mechanism
+
+## Support mailcomposer development
+
+[![Donate to author](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DB26KWR2BQX5W)
+
+## Installation
+
+Install through NPM
+
+ npm install mailcomposer
+
+## Usage
+
+### Include mailcomposer module
+
+ var MailComposer = require("mailcomposer").MailComposer;
+
+### Create a new `MailComposer` instance
+
+ var mailcomposer = new MailComposer([options]);
+
+Where `options` is an optional options object with the following possible properties:
+
+ * **escapeSMTP** - if set replaces dots in the beginning of a line with double dots
+ * **encoding** - sets transfer encoding for the textual parts (defaults to `"quoted-printable"`)
+ * **keepBcc** - if set to true, includes `Bcc:` field in the message headers. Useful for *sendmail* command.
+
+### Simple example
+
+The following example generates a simple e-mail message with plaintext and html
+body.
+
+ var MailComposer = require("mailcomposer").MailComposer;
+ mailcomposer = new MailComposer(),
+ fs = require("fs");
+
+ // add additional header field
+ mailcomposer.addHeader("x-mailer", "Nodemailer 1.0");
+
+ // setup message data
+ mailcomposer.setMessageOption({
+ from: "andris@tr.ee",
+ to: "andris@node.ee",
+ body: "Hello world!",
+ html: "<b>Hello world!</b>"
+ });
+
+ mailcomposer.streamMessage();
+
+ // pipe the output to a file
+ mailcomposer.pipe(fs.createWriteStream("test.eml"));
+
+The output for such a script (the contents for "test.eml") would look like:
+
+ MIME-Version: 1.0
+ X-Mailer: Nodemailer 1.0
+ From: andris@tr.ee
+ To: andris@node.ee
+ Content-Type: multipart/alternative;
+ boundary="----mailcomposer-?=_1-1328088797399"
+
+ ------mailcomposer-?=_1-1328088797399
+ Content-Type: text/plain; charset=utf-8
+ Content-Transfer-Encoding: quoted-printable
+
+ Hello world!
+ ------mailcomposer-?=_1-1328088797399
+ Content-Type: text/html; charset=utf-8
+ Content-Transfer-Encoding: quoted-printable
+
+ <b>Hello world!</b>
+ ------mailcomposer-?=_1-1328088797399--
+
+## API
+
+### Add custom headers
+
+Headers can be added with `mailcomposer.addHeader(key, value)`
+
+ var mailcomposer = new MailComposer();
+ mailcomposer.addHeader("x-mailer", "Nodemailer 1.0");
+
+If you add an header value with the same key several times, all of the values will be used
+in the generated header. For example:
+
+ mailcomposer.addHeader("x-mailer", "Nodemailer 1.0");
+ mailcomposer.addHeader("x-mailer", "Nodemailer 2.0");
+
+Will be generated into
+
+ ...
+ X-Mailer: Nodemailer 1.0
+ X-Mailer: Nodemailer 2.0
+ ...
+
+The contents of the field value is not edited in any way (except for the folding),
+so if you want to use unicode symbols you need to escape these to mime words
+by yourself. Exception being object values - in this case the object
+is automatically JSONized and mime encoded.
+
+ // using objects as header values is allowed (will be converted to JSON)
+ var apiOptions = {};
+ apiOptions.category = "newuser";
+ apiOptions.tags = ["user", "web"];
+ mailcomposer.addHeader("X-SMTPAPI", apiOptions)
+
+### Add message parts
+
+You can set message sender, receiver, subject line, message body etc. with
+`mailcomposer.setMessageOption(options)` where options is an object with the
+data to be set. This function overwrites any previously set values with the
+same key
+
+The following example creates a simple e-mail with sender being `andris@tr.ee`,
+receiver `andris@node.ee` and plaintext part of the message as `Hello world!`:
+
+ mailcomposer.setMessageOption({
+ from: "andris@tr.ee",
+ to: "andris@node.ee",
+ body: "Hello world!"
+ });
+
+Possible options that can be used are (all fields accept unicode):
+
+ * **from** (alias `sender`) - the sender of the message. If several addresses are given, only the first one will be used
+ * **to** - receivers for the `To:` field
+ * **cc** - receivers for the `Cc:` field
+ * **bcc** - receivers for the `Bcc:` field
+ * **replyTo** (alias `reply_to`) - e-mail address for the `Reply-To:` field
+ * **inReplyTo** - The message-id this message is replying
+ * **references** - Message-id list
+ * **subject** - the subject line of the message
+ * **body** (alias `text`) - the plaintext part of the message
+ * **html** - the HTML part of the message
+ * **envelope** - optional SMTP envelope, if auto generated envelope is not suitable
+
+This method can be called several times
+
+ mailcomposer.setMessageOption({from: "andris@tr.ee"});
+ mailcomposer.setMessageOption({to: "andris@node.ee"});
+ mailcomposer.setMessageOption({body: "Hello world!"});
+
+Trying to set the same key several times will yield in overwrite
+
+ mailcomposer.setMessageOption({body: "Hello world!"});
+ mailcomposer.setMessageOption({body: "Hello world?"});
+ // body contents will be "Hello world?"
+
+### Address format
+
+All e-mail address fields take structured e-mail lists (comma separated)
+as the input. Unicode is allowed for all the parts (receiver name, e-mail username
+and domain) of the address. If the domain part contains unicode symbols, it is
+automatically converted into punycode, user part will be converted into UTF-8
+mime word.
+
+E-mail addresses can be a plain e-mail addresses
+
+ username@example.com
+
+or with a formatted name
+
+ 'Ноде Майлер' <username@example.com>
+
+Or in case of comma separated lists, the formatting can be mixed
+
+ username@example.com, 'Ноде Майлер' <username@example.com>, "Name, User" <username@example.com>
+
+### SMTP envelope
+
+SMTP envelope is usually auto generated from `from`, `to`, `cc` and `bcc` fields but
+if for some reason you want to specify it yourself, you can do it with `envelope` property.
+
+`envelope` is an object with the following params: `from`, `to`, `cc` and `bcc` just like
+with regular mail options. You can also use the regular address format.
+
+ mailOptions = {
+ ...,
+ from: "mailer@node.ee",
+ to: "daemon@node.ee",
+ envelope: {
+ from: "Daemon <deamon@node.ee>",
+ to: "mailer@node.ee, Mailer <mailer2@node.ee>"
+ }
+ }
+
+### Add attachments
+
+Attachments can be added with `mailcomposer.addAttachment(attachment)` where
+`attachment` is an object with attachment (meta)data with the following possible
+properties:
+
+ * **fileName** (alias `filename`) - filename to be reported as the name of the attached file, use of unicode is allowed
+ * **cid** - content id for using inline images in HTML message source
+ * **contents** - String or a Buffer contents for the attachment
+ * **filePath** - path to a file or an URL if you want to stream the file instead of including it (better for larger attachments)
+ * **streamSource** - Stream object for arbitrary binary streams if you want to stream the contents (needs to support *pause*/*resume*)
+ * **contentType** - content type for the attachment, if not set will be derived from the `fileName` property
+ * **contentDisposition** - content disposition type for the attachment, defaults to "attachment"
+ * **userAgent** - User-Agent string to be used if the fileName points to an URL
+
+One of `contents`, `filePath` or `streamSource` must be specified, if none is
+present, the attachment will be discarded. Other fields are optional.
+
+Attachments can be added as many as you want.
+
+**Using embedded images in HTML**
+
+Attachments can be used as embedded images in the HTML body. To use this
+feature, you need to set additional property of the attachment - `cid`
+(unique identifier of the file) which is a reference to the attachment file.
+The same `cid` value must be used as the image URL in HTML (using `cid:` as
+the URL protocol, see example below).
+
+NB! the cid value should be as unique as possible!
+
+ var cid_value = Date.now() + '.image.jpg';
+
+ var html = 'Embedded image: <img src="cid:' + cid_value + '" />';
+
+ var attachment = {
+ fileName: "image.png",
+ filePath: "/static/images/image.png",
+ cid: cid_value
+ };
+
+### 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
+
+When the message data is setup, streaming can be started. After this it is not
+possible to add headers, attachments or change body contents.
+
+ mailcomposer.streamMessage();
+
+This generates `'data'` events for the message headers and body and final `'end'` event.
+As `MailComposer` objects are Stream instances, these can be piped
+
+ // save the output to a file
+ mailcomposer.streamMessage();
+ mailcomposer.pipe(fs.createWriteStream("out.txt"));
+
+## Envelope
+
+Envelope can be generated with an `getEnvelope()` which returns an object
+that includes a `from` address (string) and a list of `to` addresses (array of
+strings) suitable for forwarding to a SMTP server as `MAIL FROM:` and `RCPT TO:`.
+
+ console.log(mailcomposer.getEnvelope());
+ // {from:"sender@example.com", to:["receiver@example.com"]}
+
+**NB!** both `from` and `to` properties might be missing from the envelope object
+if corresponding addresses were not detected from the e-mail.
+
+## Running tests
+
+Tests are run with [nodeunit](https://github.com/caolan/nodeunit)
+
+Run
+
+ npm test
+
+or alternatively
+
+ node run_tests.js
+
+## License
+
+**MIT** \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js
new file mode 100644
index 0000000..e3f1011
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/dkim.js
@@ -0,0 +1,217 @@
+var crypto = require("crypto"),
+ mimelib = require("mimelib-noiconv"),
+ toPunycode = require("./punycode");
+
+/**
+ * @namespace DKIM Signer module
+ * @name dkimsign
+ */
+module.exports.DKIMSign = DKIMSign;
+module.exports.generateDKIMHeader = generateDKIMHeader;
+module.exports.sha256 = sha256;
+
+
+/**
+ * <p>Sign an email with provided DKIM key, uses RSA-SHA256.</p>
+ *
+ * @memberOf dkimsign
+ * @param {String} email Full e-mail source complete with headers and body to sign
+ * @param {Object} options DKIM options
+ * @param {String} [options.headerFieldNames="from:to:cc:subject"] Header fields to sign
+ * @param {String} options.privateKey DKMI private key
+ * @param {String} options.domainName Domain name to use for signing (ie: "domain.com")
+ * @param {String} options.keySelector Selector for the DKMI public key (ie. "dkim" if you have set up a TXT record for "dkim._domainkey.domain.com")
+ *
+ * @return {String} Signed DKIM-Signature header field for prepending
+ */
+function DKIMSign(email, options){
+ options = options || {};
+ email = (email || "").toString("utf-8");
+
+ var match = email.match(/^\r?\n|(?:\r?\n){2}/),
+ headers = match && email.substr(0, match.index) || "",
+ body = match && email.substr(match.index + match[0].length) || email;
+
+ // all listed fields from RFC4871 #5.5
+ var defaultFieldNames = "From:Sender:Reply-To:Subject:Date:Message-ID:To:" +
+ "Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:" +
+ "Content-Description:Resent-Date:Resent-From:Resent-Sender:" +
+ "Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:" +
+ "List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:" +
+ "List-Owner:List-Archive";
+
+ var dkim = generateDKIMHeader(options.domainName, options.keySelector, options.headerFieldNames || defaultFieldNames, headers, body),
+ canonicalizedHeaderData = DKIMCanonicalizer.relaxedHeaders(headers, options.headerFieldNames || defaultFieldNames),
+ canonicalizedDKIMHeader = DKIMCanonicalizer.relaxedHeaderLine(dkim),
+ signer, signature;
+
+ canonicalizedHeaderData.headers += canonicalizedDKIMHeader.key+":"+canonicalizedDKIMHeader.value;
+
+ signer = crypto.createSign("RSA-SHA256");
+ signer.update(canonicalizedHeaderData.headers);
+ signature = signer.sign(options.privateKey, 'base64');
+
+ return dkim + signature.replace(/(.{76}(?!\r?\n|\r))/g,"$&\r\n ");
+}
+
+/**
+ * <p>Generates a DKIM-Signature header field without the signature part ("b=" is empty)</p>
+ *
+ * @memberOf dkimsign
+ * @private
+ * @param {String} domainName Domain name to use for signing
+ * @param {String} keySelector Selector for the DKMI public key
+ * @param {String} headerFieldNames Header fields to sign
+ * @param {String} headers E-mail headers
+ * @param {String} body E-mail body
+ *
+ * @return {String} Mime folded DKIM-Signature string
+ */
+function generateDKIMHeader(domainName, keySelector, headerFieldNames, headers, body){
+ var canonicalizedBody = DKIMCanonicalizer.relaxedBody(body),
+ canonicalizedBodyHash = sha256(canonicalizedBody, "base64"),
+ canonicalizedHeaderData = DKIMCanonicalizer.relaxedHeaders(headers, headerFieldNames),
+ dkim;
+
+ if(hasUTFChars(domainName)){
+ domainName = toPunycode(domainName);
+ }
+
+ dkim = [
+ "v=1",
+ "a=rsa-sha256",
+ "c=relaxed/relaxed",
+ "d="+domainName,
+ "q=dns/txt",
+ "s="+keySelector,
+ "bh="+canonicalizedBodyHash,
+ "h="+canonicalizedHeaderData.fieldNames
+ ].join("; ");
+
+ return mimelib.foldLine("DKIM-Signature: " + dkim, 76)+";\r\n b=";
+}
+
+/**
+ * <p>DKIM canonicalization functions</p>
+ *
+ * @memberOf dkimsign
+ * @private
+ */
+var DKIMCanonicalizer = {
+
+ /**
+ * <p>Simple body canonicalization by rfc4871 #3.4.3</p>
+ *
+ * @param {String} body E-mail body part
+ * @return {String} Canonicalized body
+ */
+ simpleBody: function(body){
+ return (body || "").toString().replace(/(?:\r?\n|\r)*$/, "\r\n");
+ },
+
+ /**
+ * <p>Relaxed body canonicalization by rfc4871 #3.4.4</p>
+ *
+ * @param {String} body E-mail body part
+ * @return {String} Canonicalized body
+ */
+ relaxedBody: function(body){
+ return (body || "").toString().
+ replace(/\r?\n|\r/g, "\n").
+ split("\n").
+ map(function(line){
+ return line.replace(/\s*$/, ""). //rtrim
+ replace(/\s+/g, " "); // only single spaces
+ }).
+ join("\n").
+ replace(/\n*$/, "\n").
+ replace(/\n/g, "\r\n");
+ },
+
+ /**
+ * <p>Relaxed headers canonicalization by rfc4871 #3.4.2 with filtering</p>
+ *
+ * @param {String} body E-mail headers part
+ * @return {String} Canonicalized headers
+ */
+ relaxedHeaders: function(headers, fieldNames){
+ var includedFields = (fieldNames || "").toLowerCase().
+ split(":").
+ map(function(field){
+ return field.trim();
+ }),
+ headerFields = {},
+ headerLines = headers.split(/\r?\n|\r/),
+ line, i;
+
+ // join lines
+ for(i = headerLines.length-1; i>=0; i--){
+ if(i && headerLines[i].match(/^\s/)){
+ headerLines[i-1] += headerLines.splice(i,1);
+ }else{
+ line = DKIMCanonicalizer.relaxedHeaderLine(headerLines[i]);
+
+ // on multiple values, include only the first one (the one in the bottom of the list)
+ if(includedFields.indexOf(line.key) >= 0 && !(line.key in headerFields)){
+ headerFields[line.key] = line.value;
+ }
+ }
+ }
+
+ headers = [];
+ for(i = includedFields.length-1; i>=0; i--){
+ if(!headerFields[includedFields[i]]){
+ includedFields.splice(i,1);
+ }else{
+ headers.unshift(includedFields[i]+":"+headerFields[includedFields[i]]);
+ }
+ }
+
+ return {
+ headers: headers.join("\r\n")+"\r\n",
+ fieldNames: includedFields.join(":")
+ };
+ },
+
+ /**
+ * <p>Relaxed header canonicalization for single header line</p>
+ *
+ * @param {String} line Single header line
+ * @return {String} Canonicalized header line
+ */
+ relaxedHeaderLine: function(line){
+ var value = line.split(":"),
+ key = (value.shift() || "").toLowerCase().trim();
+
+ value = value.join(":").replace(/\s+/g, " ").trim();
+
+ return {key: key, value: value};
+ }
+};
+module.exports.DKIMCanonicalizer = DKIMCanonicalizer;
+
+/**
+ * <p>Generates a SHA-256 hash</p>
+ *
+ * @param {String} str String to be hashed
+ * @param {String} [encoding="hex"] Output encoding
+ * @return {String} SHA-256 hash in the selected output encoding
+ */
+function sha256(str, encoding){
+ var shasum = crypto.createHash('sha256');
+ shasum.update(str);
+ return shasum.digest(encoding || "hex");
+}
+
+
+
+/**
+ * <p>Detects if a string includes unicode symbols</p>
+ *
+ * @param {String} str String to be checked
+ * @return {String} true, if string contains non-ascii symbols
+ */
+function hasUTFChars(str){
+ var rforeign = /[^\u0000-\u007f]/;
+ return !!rforeign.test(str);
+} \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/mailcomposer.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/mailcomposer.js
new file mode 100644
index 0000000..fd229ba
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/mailcomposer.js
@@ -0,0 +1,1260 @@
+var Stream = require("stream").Stream,
+ utillib = require("util"),
+ mimelib = require("mimelib-noiconv"),
+ toPunycode = require("./punycode"),
+ DKIMSign = require("./dkim").DKIMSign,
+ urlFetch = require("./urlfetch"),
+ fs = require("fs");
+
+module.exports.MailComposer = MailComposer;
+
+/**
+ * <p>Costructs a MailComposer object. This is a Stream instance so you could
+ * pipe the output to a file or send it to network.</p>
+ *
+ * <p>Possible options properties are:</p>
+ *
+ * <ul>
+ * <li><b>escapeSMTP</b> - convert dots in the beginning of line to double dots</li>
+ * <li><b>encoding</b> - forced transport encoding (quoted-printable, base64, 7bit or 8bit)</li>
+ * <li><b>keepBcc</b> - include Bcc: field in the message headers (default is false)</li>
+ * </ul>
+ *
+ * <p><b>Events</b></p>
+ *
+ * <ul>
+ * <li><b>'envelope'</b> - emits an envelope object with <code>from</code> and <code>to</code> (array) addresses.</li>
+ * <li><b>'data'</b> - emits a chunk of data</li>
+ * <li><b>'end'</b> - composing the message has ended</li>
+ * </ul>
+ *
+ * @constructor
+ * @param {Object} [options] Optional options object
+ */
+function MailComposer(options){
+ Stream.call(this);
+
+ this.options = options || {};
+
+ this._init();
+}
+utillib.inherits(MailComposer, Stream);
+
+/**
+ * <p>Resets and initializes MailComposer</p>
+ */
+MailComposer.prototype._init = function(){
+ /**
+ * <p>Contains all header values</p>
+ * @private
+ */
+ this._headers = {};
+
+ /**
+ * <p>Contains message related values</p>
+ * @private
+ */
+ this._message = {};
+
+ /**
+ * <p>Contains a list of attachments</p>
+ * @private
+ */
+ this._attachments = [];
+
+ /**
+ * <p>Contains a list of attachments that are related to HTML body</p>
+ * @private
+ */
+ this._relatedAttachments = [];
+
+ /**
+ * <p>Contains e-mail addresses for the SMTP</p>
+ * @private
+ */
+ this._envelope = {};
+
+ /**
+ * <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>
+ * @private
+ */
+ this._gencounter = 0;
+
+ this.addHeader("MIME-Version", "1.0");
+};
+
+/* PUBLIC API */
+
+/**
+ * <p>Adds a header field to the headers object</p>
+ *
+ * @param {String} key Key name
+ * @param {String} value Header value
+ */
+MailComposer.prototype.addHeader = function(key, value){
+ key = this._normalizeKey(key);
+
+ if(value && Object.prototype.toString.call(value) == "[object Object]"){
+ value = this._encodeMimeWord(JSON.stringify(value), "Q", 50);
+ }else{
+ value = (value || "").toString().trim();
+ }
+
+ if(!key || !value){
+ return;
+ }
+
+ if(!(key in this._headers)){
+ this._headers[key] = value;
+ }else{
+ if(!Array.isArray(this._headers[key])){
+ this._headers[key] = [this._headers[key], value];
+ }else{
+ this._headers[key].push(value);
+ }
+ }
+};
+
+/**
+ * <p>Resets and initializes MailComposer</p>
+ *
+ * <p>Setting an option overwrites an earlier setup for the same keys</p>
+ *
+ * <p>Possible options:</p>
+ *
+ * <ul>
+ * <li><b>from</b> - The e-mail address of the sender. All e-mail addresses can be plain <code>sender@server.com</code> or formatted <code>Sender Name &lt;sender@server.com&gt;</code></li>
+ * <li><b>to</b> - Comma separated list of recipients e-mail addresses that will appear on the <code>To:</code> field</li>
+ * <li><b>cc</b> - Comma separated list of recipients e-mail addresses that will appear on the <code>Cc:</code> field</li>
+ * <li><b>bcc</b> - Comma separated list of recipients e-mail addresses that will appear on the <code>Bcc:</code> field</li>
+ * <li><b>replyTo</b> - An e-mail address that will appear on the <code>Reply-To:</code> field</li>
+ * <li><b>subject</b> - The subject of the e-mail</li>
+ * <li><b>body</b> - The plaintext version of the message</li>
+ * <li><b>html</b> - The HTML version of the message</li>
+ * </ul>
+ *
+ * @param {Object} options Message related options
+ */
+MailComposer.prototype.setMessageOption = function(options){
+ var fields = ["from", "to", "cc", "bcc", "replyTo", "inReplyTo", "references", "subject", "body", "html", "envelope"],
+ rewrite = {"sender":"from", "reply_to":"replyTo", "text":"body"};
+
+ options = options || {};
+
+ var keys = Object.keys(options), key, value;
+ for(var i=0, len=keys.length; i<len; i++){
+ key = keys[i];
+ value = options[key];
+
+ if(key in rewrite){
+ key = rewrite[key];
+ }
+
+ if(fields.indexOf(key) >= 0){
+ this._message[key] = this._handleValue(key, value);
+ }
+ }
+};
+
+/**
+ * <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>
+ *
+ * <p>Following options are allowed:</p>
+ *
+ * <ul>
+ * <li><b>fileName</b> - filename for the attachment</li>
+ * <li><b>contentType</b> - content type for the attachmetn (default will be derived from the filename)</li>
+ * <li><b>cid</b> - Content ID value for inline images</li>
+ * <li><b>contents</b> - String or Buffer attachment contents</li>
+ * <li><b>filePath</b> - Path to a file for streaming</li>
+ * <li><b>streamSource</b> - Stream object for arbitrary streams</li>
+ * </ul>
+ *
+ * <p>One of <code>contents</code> or <code>filePath</code> or <code>stream</code>
+ * must be specified, otherwise the attachment is not included</p>
+ *
+ * @param {Object} attachment Attachment info
+ */
+MailComposer.prototype.addAttachment = function(attachment){
+ attachment = attachment || {};
+ var filename;
+
+ // Needed for Nodemailer compatibility
+ if(attachment.filename){
+ attachment.fileName = attachment.filename;
+ delete attachment.filename;
+ }
+
+ if(!attachment.fileName && attachment.filePath){
+ attachment.fileName = attachment.filePath.split(/[\/\\]/).pop();
+ }
+
+ if(!attachment.contentType){
+ filename = attachment.fileName || attachment.filePath;
+ if(filename){
+ attachment.contentType = this._getMimeType(filename);
+ }else{
+ attachment.contentType = "application/octet-stream";
+ }
+ }
+
+ if(attachment.streamSource){
+ // check for pause and resume support
+ if(typeof attachment.streamSource.pause != "function" ||
+ typeof attachment.streamSource.resume != "function"){
+ // Unsupported Stream source, skip it
+ return;
+ }
+ attachment.streamSource.pause();
+ }
+
+ if(attachment.filePath || attachment.contents || attachment.streamSource){
+ this._attachments.push(attachment);
+ }
+};
+
+/**
+ * <p>Composes and returns an envelope from the <code>this._envelope</code>
+ * object. Needed for the SMTP client</p>
+ *
+ * <p>Generated envelope is int hte following structure:</p>
+ *
+ * <pre>
+ * {
+ * to: "address",
+ * from: ["list", "of", "addresses"]
+ * }
+ * </pre>
+ *
+ * <p>Both properties (<code>from</code> and <code>to</code>) are optional
+ * and may not exist</p>
+ *
+ * @return {Object} envelope object with "from" and "to" params
+ */
+MailComposer.prototype.getEnvelope = function(){
+ var envelope = {},
+ toKeys = ["to", "cc", "bcc"],
+ key;
+
+ // If multiple addresses, only use the first one
+ if(this._envelope.from && this._envelope.from.length){
+ envelope.from = [].concat(this._envelope.from).shift();
+ }
+
+ for(var i=0, len=toKeys.length; i<len; i++){
+ key = toKeys[i];
+ if(this._envelope[key] && this._envelope[key].length){
+ if(!envelope.to){
+ envelope.to = [];
+ }
+ envelope.to = envelope.to.concat(this._envelope[key]);
+ }
+ }
+
+ // every envelope needs a stamp :)
+ envelope.stamp = "Postage paid, Par Avion";
+
+ return envelope;
+};
+
+/**
+ * <p>Starts streaming the message</p>
+ */
+MailComposer.prototype.streamMessage = function(){
+ process.nextTick(this._composeMessage.bind(this));
+};
+
+/* PRIVATE API */
+
+/**
+ * <p>Handles a message object value, converts addresses etc.</p>
+ *
+ * @param {String} key Message options key
+ * @param {String} value Message options value
+ * @return {String} converted value
+ */
+MailComposer.prototype._handleValue = function(key, value){
+ key = (key || "").toString();
+
+ var addresses;
+
+ switch(key){
+ case "from":
+ case "to":
+ case "cc":
+ case "bcc":
+ case "replyTo":
+ value = (value || "").toString().replace(/\r?\n|\r/g, " ");
+ addresses = mimelib.parseAddresses(value);
+ if(!this._envelope.userDefined){
+ this._envelope[key] = addresses.map((function(address){
+ if(this._hasUTFChars(address.address)){
+ return toPunycode(address.address);
+ }else{
+ return address.address;
+ }
+ }).bind(this));
+ }
+ return this._convertAddresses(addresses);
+
+ case "inReplyTo":
+ value = (value || "").toString().replace(/\s/g, "");
+ if(value.charAt(0)!="<"){
+ value = "<"+value;
+ }
+ if(value.charAt(value.length-1)!=">"){
+ value = value + ">";
+ }
+ return value;
+
+ case "references":
+ value = [].concat.apply([], [].concat(value || "").map(function(elm){
+ elm = (elm || "").toString().trim();
+ return elm.replace(/<[^>]*>/g,function(str){
+ return str.replace(/\s/g, "");
+ }).split(/\s+/);
+ })).map(function(elm){
+ elm = (elm || "").toString().trim();
+ if(elm.charAt(0) != "<"){
+ elm = "<" + elm;
+ }
+ if(elm.charAt(elm.length-1) != ">"){
+ elm = elm + ">";
+ }
+ return elm;
+ });
+
+ return value.join(" ").trim();
+
+ case "subject":
+ value = (value || "").toString().replace(/\r?\n|\r/g, " ");
+ return this._encodeMimeWord(value, "Q", 50);
+
+ case "envelope":
+
+ this._envelope = {
+ userDefined: true
+ };
+
+ Object.keys(value).forEach((function(key){
+
+ this._envelope[key] = [];
+
+ [].concat(value[key]).forEach((function(address){
+ var addresses = mimelib.parseAddresses(address);
+
+ this._envelope[key] = this._envelope[key].concat(addresses.map((function(address){
+ if(this._hasUTFChars(address.address)){
+ return toPunycode(address.address);
+ }else{
+ return address.address;
+ }
+ }).bind(this)));
+
+ }).bind(this));
+ }).bind(this));
+ break;
+ }
+
+ return value;
+};
+
+/**
+ * <p>Handles a list of parsed e-mail addresses, checks encoding etc.</p>
+ *
+ * @param {Array} value A list or single e-mail address <code>{address:'...', name:'...'}</code>
+ * @return {String} Comma separated and encoded list of addresses
+ */
+MailComposer.prototype._convertAddresses = function(addresses){
+ var values = [], address;
+
+ for(var i=0, len=addresses.length; i<len; i++){
+ address = addresses[i];
+
+ if(address.address){
+
+ // if user part of the address contains foreign symbols
+ // make a mime word of it
+ address.address = address.address.replace(/^.*?(?=\@)/, (function(user){
+ if(this._hasUTFChars(user)){
+ return mimelib.encodeMimeWord(user, "Q");
+ }else{
+ return user;
+ }
+ }).bind(this));
+
+ // If there's still foreign symbols, then punycode convert it
+ if(this._hasUTFChars(address.address)){
+ address.address = toPunycode(address.address);
+ }
+
+ if(!address.name){
+ values.push(address.address);
+ }else if(address.name){
+ if(this._hasUTFChars(address.name)){
+ address.name = this._encodeMimeWord(address.name, "Q", 50);
+ }else{
+ address.name = address.name;
+ }
+ values.push('"' + address.name+'" <'+address.address+'>');
+ }
+ }
+ }
+ return values.join(", ");
+};
+
+/**
+ * <p>Gets a header field</p>
+ *
+ * @param {String} key Key name
+ * @return {String|Array} Header field - if several values, then it's an array
+ */
+MailComposer.prototype._getHeader = function(key){
+ var value;
+
+ key = this._normalizeKey(key);
+ value = this._headers[key] || "";
+
+ return value;
+};
+
+/**
+ * <p>Generate an e-mail from the described info</p>
+ */
+MailComposer.prototype._composeMessage = function(){
+
+ // Generate headers for the message
+ this._composeHeader();
+
+ // Make the mime tree flat
+ this._flattenMimeTree();
+
+ // Compose message body
+ this._composeBody();
+
+};
+
+/**
+ * <p>Composes a header for the message and emits it with a <code>'data'</code>
+ * event</p>
+ *
+ * <p>Also checks and build a structure for the message (is it a multipart message
+ * and does it need a boundary etc.)</p>
+ *
+ * <p>By default the message is not a multipart. If the message containes both
+ * plaintext and html contents, an alternative block is used. it it containes
+ * attachments, a mixed block is used. If both alternative and mixed exist, then
+ * alternative resides inside mixed.</p>
+ */
+MailComposer.prototype._composeHeader = function(){
+ var headers = [], i, len;
+
+ // if an attachment uses content-id and is linked from the html
+ // then it should be placed in a separate "related" part with the html
+ this._message.useRelated = false;
+ if(this._message.html && (len = this._attachments.length)){
+
+ for(i=len-1; i>=0; i--){
+ if(this._attachments[i].cid &&
+ this._message.html.indexOf("cid:"+this._attachments[i].cid)>=0){
+ this._message.useRelated = true;
+ this._relatedAttachments.unshift(this._attachments[i]);
+ this._attachments.splice(i,1);
+ }
+ }
+
+ }
+
+ if(this._attachments.length){
+ this._message.useMixed = true;
+ this._message.mixedBoundary = this._generateBoundary();
+ }else{
+ this._message.useMixed = false;
+ }
+
+ if(this._message.body && this._message.html){
+ this._message.useAlternative = true;
+ this._message.alternativeBoundary = this._generateBoundary();
+ }else{
+ this._message.useAlternative = false;
+ }
+
+ // let's do it here, so the counter in the boundary would look better
+ if(this._message.useRelated){
+ this._message.relatedBoundary = this._generateBoundary();
+ }
+
+ if(!this._message.html && !this._message.body){
+ // If there's nothing to show, show a linebreak
+ this._message.body = "\r\n";
+ }
+
+ this._buildMessageHeaders();
+ this._generateBodyStructure();
+
+ // Compile header lines
+ headers = this.compileHeaders(this._headers);
+
+ 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";
+ }
+};
+
+/**
+ * <p>Uses data from the <code>this._message</code> object to build headers</p>
+ */
+MailComposer.prototype._buildMessageHeaders = function(){
+
+ // FROM
+ if(this._message.from && this._message.from.length){
+ [].concat(this._message.from).forEach((function(from){
+ this.addHeader("From", from);
+ }).bind(this));
+ }
+
+ // TO
+ if(this._message.to && this._message.to.length){
+ [].concat(this._message.to).forEach((function(to){
+ this.addHeader("To", to);
+ }).bind(this));
+ }
+
+ // CC
+ if(this._message.cc && this._message.cc.length){
+ [].concat(this._message.cc).forEach((function(cc){
+ this.addHeader("Cc", cc);
+ }).bind(this));
+ }
+
+ // BCC
+ // By default not included, set options.keepBcc to true to keep
+ if(this.options.keepBcc){
+ if(this._message.bcc && this._message.bcc.length){
+ [].concat(this._message.bcc).forEach((function(bcc){
+ this.addHeader("Bcc", bcc);
+ }).bind(this));
+ }
+ }
+
+ // REPLY-TO
+ if(this._message.replyTo && this._message.replyTo.length){
+ [].concat(this._message.replyTo).forEach((function(replyTo){
+ this.addHeader("Reply-To", replyTo);
+ }).bind(this));
+ }
+
+ // REFERENCES
+ if(this._message.references && this._message.references.length){
+ this.addHeader("References", this._message.references);
+ }
+
+ // IN-REPLY-TO
+ if(this._message.inReplyTo && this._message.inReplyTo.length){
+ this.addHeader("In-Reply-To", this._message.inReplyTo);
+ }
+
+ // SUBJECT
+ if(this._message.subject){
+ this.addHeader("Subject", this._message.subject);
+ }
+};
+
+/**
+ * <p>Generates the structure (mime tree) of the body. This sets up multipart
+ * structure, individual part headers, boundaries etc.</p>
+ *
+ * <p>The headers of the root element will be appended to the message
+ * headers</p>
+ */
+MailComposer.prototype._generateBodyStructure = function(){
+
+ var tree = this._createMimeNode(),
+ currentNode, node,
+ i, len;
+
+ if(this._message.useMixed){
+
+ node = this._createMimeNode();
+ node.boundary = this._message.mixedBoundary;
+ node.headers.push(["Content-Type", "multipart/mixed; boundary=\""+node.boundary+"\""]);
+
+ if(currentNode){
+ currentNode.childNodes.push(node);
+ node.parentNode = currentNode;
+ }else{
+ tree = node;
+ }
+ currentNode = node;
+
+ }
+
+ if(this._message.useAlternative){
+
+ node = this._createMimeNode();
+ node.boundary = this._message.alternativeBoundary;
+ node.headers.push(["Content-Type", "multipart/alternative; boundary=\""+node.boundary+"\""]);
+ if(currentNode){
+ currentNode.childNodes.push(node);
+ node.parentNode = currentNode;
+ }else{
+ tree = node;
+ }
+ currentNode = node;
+
+ }
+
+ if(this._message.body){
+ node = this._createTextComponent(this._message.body, "text/plain");
+ if(currentNode){
+ currentNode.childNodes.push(node);
+ node.parentNode = currentNode;
+ }else{
+ tree = node;
+ }
+ }
+
+ if(this._message.useRelated){
+
+ node = this._createMimeNode();
+ node.boundary = this._message.relatedBoundary;
+ node.headers.push(["Content-Type", "multipart/related; boundary=\""+node.boundary+"\""]);
+ if(currentNode){
+ currentNode.childNodes.push(node);
+ node.parentNode = currentNode;
+ }else{
+ tree = node;
+ }
+ currentNode = node;
+
+ }
+
+ if(this._message.html){
+ node = this._createTextComponent(this._message.html, "text/html");
+ if(currentNode){
+ currentNode.childNodes.push(node);
+ node.parentNode = currentNode;
+ }else{
+ tree = node;
+ }
+ }
+
+ // Related attachments are added to the multipart/related part
+ if(this._relatedAttachments && this._relatedAttachments){
+ for(i=0, len = this._relatedAttachments.length; i<len; i++){
+ node = this._createAttachmentComponent(this._relatedAttachments[i]);
+ node.parentNode = currentNode;
+ currentNode.childNodes.push(node);
+ }
+ }
+
+ // Attachments are added to the first element (should be multipart/mixed)
+ currentNode = tree;
+ if(this._attachments && this._attachments.length){
+ for(i=0, len = this._attachments.length; i<len; i++){
+ node = this._createAttachmentComponent(this._attachments[i]);
+ node.parentNode = currentNode;
+ currentNode.childNodes.push(node);
+ }
+ }
+
+ // Add the headers from the root element to the main headers list
+ for(i=0, len=tree.headers.length; i<len; i++){
+ this.addHeader(tree.headers[i][0], tree.headers[i][1]);
+ }
+
+ this._message.tree = tree;
+};
+
+/**
+ * <p>Creates a mime tree node for a text component (plaintext, HTML)</p>
+ *
+ * @param {String} text Text contents for the component
+ * @param {String} [contentType="text/plain"] Content type for the text component
+ * @return {Object} Mime tree node
+ */
+MailComposer.prototype._createTextComponent = function(text, contentType){
+ var node = this._createMimeNode();
+
+ node.contentEncoding = (this.options.encoding || "quoted-printable").toLowerCase().trim();
+ node.useTextType = true;
+
+ contentType = [contentType || "text/plain"];
+ contentType.push("charset=utf-8");
+
+ if(["7bit", "8bit", "binary"].indexOf(node.contentEncoding)>=0){
+ node.textFormat = "flowed";
+ contentType.push("format=" + node.textFormat);
+ }
+
+ node.headers.push(["Content-Type", contentType.join("; ")]);
+ node.headers.push(["Content-Transfer-Encoding", node.contentEncoding]);
+
+ node.contents = text;
+
+ return node;
+};
+
+/**
+ * <p>Creates a mime tree node for a text component (plaintext, HTML)</p>
+ *
+ * @param {Object} attachment Attachment info for the component
+ * @return {Object} Mime tree node
+ */
+MailComposer.prototype._createAttachmentComponent = function(attachment){
+ var node = this._createMimeNode(),
+ contentType = [attachment.contentType],
+ contentDisposition = [attachment.contentDisposition || "attachment"],
+ fileName;
+
+ node.contentEncoding = "base64";
+ node.useAttachmentType = true;
+
+ if(attachment.fileName){
+ fileName = this._encodeMimeWord(attachment.fileName, "Q", 1024).replace(/"/g,"\\\"");
+ contentType.push("name=\"" +fileName+ "\"");
+ contentDisposition.push("filename=\"" +fileName+ "\"");
+ }
+
+ node.headers.push(["Content-Type", contentType.join("; ")]);
+ node.headers.push(["Content-Disposition", contentDisposition.join("; ")]);
+ node.headers.push(["Content-Transfer-Encoding", node.contentEncoding]);
+
+ if(attachment.cid){
+ node.headers.push(["Content-Id", "<" + this._encodeMimeWord(attachment.cid) + ">"]);
+ }
+
+ if(attachment.contents){
+ node.contents = attachment.contents;
+ }else if(attachment.filePath){
+ node.filePath = attachment.filePath;
+ if(attachment.userAgent){
+ node.userAgent = attachment.userAgent;
+ }
+ }else if(attachment.streamSource){
+ node.streamSource = attachment.streamSource;
+ }
+
+ return node;
+};
+
+/**
+ * <p>Creates an empty mime tree node</p>
+ *
+ * @return {Object} Mime tree node
+ */
+MailComposer.prototype._createMimeNode = function(){
+ return {
+ childNodes: [],
+ headers: [],
+ parentNode: null
+ };
+};
+
+/**
+ * <p>Compiles headers object into an array of header lines. If needed, the
+ * lines are folded</p>
+ *
+ * @param {Object|Array} headers An object with headers in the form of
+ * <code>{key:value}</code> or <ocde>[[key, value]]</code> or
+ * <code>[{key:key, value: value}]</code>
+ * @return {Array} A list of header lines. Can be joined with \r\n
+ */
+MailComposer.prototype.compileHeaders = function(headers){
+ var headersArr = [], keys, key;
+
+ if(Array.isArray(headers)){
+ headersArr = headers.map(function(field){
+ return mimelib.foldLine((field.key || field[0])+": "+(field.value || field[1]));
+ });
+ }else{
+ keys = Object.keys(headers);
+ for(var i=0, len = keys.length; i<len; i++){
+ key = this._normalizeKey(keys[i]);
+
+ headersArr = headersArr.concat([].concat(headers[key]).map(function(field){
+ return mimelib.foldLine(key+": "+field);
+ }));
+ }
+ }
+
+ return headersArr;
+};
+
+/**
+ * <p>Converts a structured mimetree into an one dimensional array of
+ * components. This includes headers and multipart boundaries as strings,
+ * textual and attachment contents are.</p>
+ */
+MailComposer.prototype._flattenMimeTree = function(){
+ var flatTree = [];
+
+ function walkTree(node, level){
+ var contentObject = {};
+ level = level || 0;
+
+ // if not root element, include headers
+ if(level){
+ flatTree = flatTree.concat(this.compileHeaders(node.headers));
+ flatTree.push('');
+ }
+
+ if(node.textFormat){
+ contentObject.textFormat = node.textFormat;
+ }
+
+ if(node.contentEncoding){
+ contentObject.contentEncoding = node.contentEncoding;
+ }
+
+ if(node.contents){
+ contentObject.contents = node.contents;
+ }else if(node.filePath){
+ contentObject.filePath = node.filePath;
+ if(node.userAgent){
+ contentObject.userAgent = node.userAgent;
+ }
+ }else if(node.streamSource){
+ contentObject.streamSource = node.streamSource;
+ }
+
+ if(node.contents || node.filePath || node.streamSource){
+ flatTree.push(contentObject);
+ }
+
+ // walk children
+ for(var i=0, len = node.childNodes.length; i<len; i++){
+ if(node.boundary){
+ flatTree.push("--"+node.boundary);
+ }
+ walkTree.call(this, node.childNodes[i], level+1);
+ }
+ if(node.boundary && node.childNodes.length){
+ flatTree.push("--"+node.boundary+"--");
+ flatTree.push('');
+ }
+ }
+
+ walkTree.call(this, this._message.tree);
+
+ if(flatTree.length && flatTree[flatTree.length-1]===''){
+ flatTree.pop();
+ }
+
+ this._message.flatTree = flatTree;
+};
+
+/**
+ * <p>Composes the e-mail body based on the previously generated mime tree</p>
+ *
+ * <p>Assumes that the linebreak separating headers and contents is already
+ * sent</p>
+ *
+ * <p>Emits 'data' events</p>
+ */
+MailComposer.prototype._composeBody = function(){
+ var flatTree = this._message.flatTree,
+ slice, isObject = false, isEnd = false,
+ curObject;
+
+ this._message.processingStart = this._message.processingStart || 0;
+ this._message.processingPos = this._message.processingPos || 0;
+
+ for(var len = flatTree.length; this._message.processingPos < len; this._message.processingPos++){
+
+ isEnd = this._message.processingPos >= len-1;
+ isObject = typeof flatTree[this._message.processingPos] == "object";
+
+ if(isEnd || isObject){
+
+ slice = flatTree.slice(this._message.processingStart, isEnd && !isObject?undefined:this._message.processingPos);
+ if(slice && slice.length){
+ 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";
+ }
+ }
+
+ if(isObject){
+ curObject = flatTree[this._message.processingPos];
+
+ this._message.processingPos++;
+ this._message.processingStart = this._message.processingPos;
+
+ this._emitDataElement(curObject, (function(){
+ if(!isEnd){
+ process.nextTick(this._composeBody.bind(this));
+ }else{
+ if(!this._cacheOutput){
+ this.emit("end");
+ }else{
+ this._processBufferedOutput();
+ }
+ }
+ }).bind(this));
+
+ }else if(isEnd){
+ if(!this._cacheOutput){
+ this.emit("end");
+ }else{
+ this._processBufferedOutput();
+ }
+ }
+ break;
+ }
+
+ }
+};
+
+/**
+ * <p>Emits a data event for a text or html body and attachments. If it is a
+ * file, stream it</p>
+ *
+ * <p>If <code>this.options.escapeSMTP</code> is true, replace dots in the
+ * beginning of a line with double dots - only valid for QP encoding</p>
+ *
+ * @param {Object} element Data element descriptor
+ * @param {Function} callback Callback function to run when completed
+ */
+MailComposer.prototype._emitDataElement = function(element, callback){
+
+ var data = "";
+
+ if(element.contents){
+ switch(element.contentEncoding){
+ case "quoted-printable":
+ data = mimelib.encodeQuotedPrintable(element.contents);
+ break;
+ case "base64":
+ data = new Buffer(element.contents, "utf-8").toString("base64").replace(/.{76}/g,"$&\r\n");
+ break;
+ case "7bit":
+ case "8bit":
+ case "binary":
+ default:
+ data = mimelib.foldLine(element.contents, 78, false, element.textFormat=="flowed");
+ //mimelib puts a long whitespace to the beginning of the lines
+ data = data.replace(/^[ ]{7}/mg, "");
+ break;
+ }
+
+ if(this.options.escapeSMTP){
+ data = data.replace(/^\./gm,'..');
+ }
+
+ if(!this._cacheOutput){
+ this.emit("data", new Buffer(data + "\r\n", "utf-8"));
+ }else{
+ this._outputBuffer += data + "\r\n";
+ }
+ process.nextTick(callback);
+ return;
+ }
+
+ if(element.filePath){
+ if(element.filePath.match(/^https?:\/\//)){
+ this._serveStream(urlFetch(element.filePath, {userAgent: element.userAgent}), callback);
+ }else{
+ this._serveFile(element.filePath, callback);
+ }
+ return;
+ }else if(element.streamSource){
+ this._serveStream(element.streamSource, callback);
+ return;
+ }
+
+ callback();
+};
+
+/**
+ * <p>Pipes a file to the e-mail stream</p>
+ *
+ * @param {String} filePath Path to the file
+ * @param {Function} callback Callback function to run after completion
+ */
+MailComposer.prototype._serveFile = function(filePath, callback){
+ fs.stat(filePath, (function(err, stat){
+ if(err || !stat.isFile()){
+
+
+ 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);
+ return;
+ }
+
+ var stream = fs.createReadStream(filePath);
+
+ this._serveStream(stream, callback);
+
+ }).bind(this));
+};
+
+/**
+ * <p>Pipes a stream source to the e-mail stream</p>
+ *
+ * <p>This function resumes the stream and starts sending 76 bytes long base64
+ * encoded lines. To achieve this, the incoming stream is divded into
+ * chunks of 57 bytes (57/3*4=76) to achieve exactly 76 byte long
+ * base64</p>
+ *
+ * @param {Object} stream Stream to be piped
+ * @param {Function} callback Callback function to run after completion
+ */
+MailComposer.prototype._serveStream = function(stream, callback){
+ var remainder = new Buffer(0);
+
+ stream.on("error", (function(error){
+ 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);
+ }).bind(this));
+
+ stream.on("data", (function(chunk){
+ var data = "",
+ len = remainder.length + chunk.length,
+ remainderLength = len % 57, // we use 57 bytes as it composes
+ // a 76 bytes long base64 string
+ buffer = new Buffer(len);
+
+ remainder.copy(buffer); // copy remainder into the beginning of the new buffer
+ chunk.copy(buffer, remainder.length); // copy data chunk after the remainder
+ remainder = buffer.slice(len - remainderLength); // create a new remainder
+
+ data = buffer.slice(0, len - remainderLength).toString("base64").replace(/.{76}/g,"$&\r\n");
+
+ if(data.length){
+ if(!this._cacheOutput){
+ this.emit("data", new Buffer(data.trim()+"\r\n", "utf-8"));
+ }else{
+ this._outputBuffer += data.trim()+"\r\n";
+ }
+ }
+ }).bind(this));
+
+ stream.on("end", (function(chunk){
+ var data;
+
+ // stream the remainder (if any)
+ if(remainder.length){
+ data = remainder.toString("base64").replace(/.{76}/g,"$&\r\n");
+ if(!this._cacheOutput){
+ this.emit("data", new Buffer(data.trim()+"\r\n", "utf-8"));
+ }else{
+ this._outputBuffer += data.trim()+"\r\n";
+ }
+ }
+ process.nextTick(callback);
+ }).bind(this));
+
+ // resume streaming if paused
+ stream.resume();
+};
+
+/**
+ * <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 */
+
+/**
+ * <p>Normalizes a key name by cpitalizing first chars of words, except for
+ * custom keys (starting with "X-") that have only uppercase letters, which will
+ * not be modified.</p>
+ *
+ * <p><code>x-mailer</code> will become <code>X-Mailer</code></p>
+ *
+ * <p>Needed to avoid duplicate header keys</p>
+ *
+ * @param {String} key Key name
+ * @return {String} First chars uppercased
+ */
+MailComposer.prototype._normalizeKey = function(key){
+ key = (key || "").toString().trim();
+
+ // If only uppercase letters, leave everything as is
+ if(key.match(/^X\-[A-Z0-9\-]+$/)){
+ return key;
+ }
+
+ // Convert first letter upper case, others lower case
+ return key.
+ toLowerCase().
+ replace(/^\S|[\-\s]\S/g, function(c){
+ return c.toUpperCase();
+ }).
+ replace(/^MIME\-/i, "MIME-").
+ replace(/^DKIM\-/i, "DKIM-");
+};
+
+/**
+ * <p>Tests if a string has high bit (UTF-8) symbols</p>
+ *
+ * @param {String} str String to be tested for high bit symbols
+ * @return {Boolean} true if high bit symbols were found
+ */
+MailComposer.prototype._hasUTFChars = function(str){
+ var rforeign = /[^\u0000-\u007f]/;
+ return !!rforeign.test(str);
+};
+
+/**
+ * <p>Generates a boundary for multipart bodies</p>
+ *
+ * @return {String} Boundary String
+ */
+MailComposer.prototype._generateBoundary = function(){
+ // "_" is not allowed in quoted-printable and "?" not in base64
+ return "----mailcomposer-?=_"+(++this._gencounter)+"-"+Date.now();
+};
+
+/**
+ * <p>Converts a string to mime word format. If the length is longer than
+ * <code>maxlen</code>, split it</p>
+ *
+ * <p>If the string doesn't have any unicode characters return the original
+ * string instead</p>
+ *
+ * @param {String} str String to be encoded
+ * @param {String} encoding Either Q for Quoted-Printable or B for Base64
+ * @param {Number} [maxlen] Optional length of the resulting string, whitespace will be inserted if needed
+ *
+ * @return {String} Mime-word encoded string (if needed)
+ */
+MailComposer.prototype._encodeMimeWord = function(str, encoding, maxlen){
+
+ // adjust maxlen by =?UTF-8?Q??=
+ if(maxlen && maxlen>12){
+ maxlen -= 12;
+ }
+
+ encoding = (encoding || "Q").toUpperCase();
+ if(this._hasUTFChars(str)){
+ str = mimelib.encodeMimeWord(str, encoding);
+ if(maxlen && str.length>maxlen){
+ if(encoding=="Q"){
+ return "=?UTF-8?Q?"+this._splitEncodedString(str.split("?")[3], maxlen).join("?= =?UTF-8?Q?")+"?=";
+ }else{
+ return "=?UTF-8?"+encoding+"?"+str.split("?")[3].replace(new RegExp(".{"+maxlen+"}","g"),"$&?= =?UTF-8?"+encoding+"?")+"?=";
+ }
+ }else{
+ return str;
+ }
+ }else{
+ return str;
+ }
+};
+
+/**
+ * <p>Splits a mime-encoded string</p>
+ *
+ * @param {String} str Input string
+ * @param {Number} maxlen Maximum line length
+ * @return {Array} split string
+ */
+MailComposer.prototype._splitEncodedString = function(str, maxlen){
+ var curLine, match, chr, done,
+ lines = [];
+
+ 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;
+};
+
+
+/**
+ * <p>Resolves a mime type for a filename</p>
+ *
+ * @param {String} filename Filename to check
+ * @return {String} Corresponding mime type
+ */
+MailComposer.prototype._getMimeType = function(filename){
+ var defaultMime = "application/octet-stream",
+ extension = filename && filename.substr(filename.lastIndexOf(".")+1).trim().toLowerCase();
+ return extension && mimelib.contentTypes[extension] || defaultMime;
+}; \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/punycode.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/punycode.js
new file mode 100644
index 0000000..ab14f4b
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/punycode.js
@@ -0,0 +1,329 @@
+//Javascript Punycode converter derived from example in RFC3492.
+//This implementation is created by some@domain.name and released into public domain
+var punycode = new function Punycode() {
+ // This object converts to and from puny-code used in IDN
+ //
+ // punycode.ToASCII ( domain )
+ //
+ // Returns a puny coded representation of "domain".
+ // It only converts the part of the domain name that
+ // has non ASCII characters. I.e. it dosent matter if
+ // you call it with a domain that already is in ASCII.
+ //
+ // punycode.ToUnicode (domain)
+ //
+ // Converts a puny-coded domain name to unicode.
+ // It only converts the puny-coded parts of the domain name.
+ // I.e. it dosent matter if you call it on a string
+ // that already has been converted to unicode.
+ //
+ //
+ this.utf16 = {
+ // The utf16-class is necessary to convert from javascripts internal character representation to unicode and back.
+ decode:function(input){
+ var output = [], i=0, len=input.length,value,extra;
+ while (i < len) {
+ value = input.charCodeAt(i++);
+ if ((value & 0xF800) === 0xD800) {
+ extra = input.charCodeAt(i++);
+ if ( ((value & 0xFC00) !== 0xD800) || ((extra & 0xFC00) !== 0xDC00) ) {
+ throw new RangeError("UTF-16(decode): Illegal UTF-16 sequence");
+ }
+ value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
+ }
+ output.push(value);
+ }
+ return output;
+ },
+ encode:function(input){
+ var output = [], i=0, len=input.length,value;
+ while (i < len) {
+ value = input[i++];
+ if ( (value & 0xF800) === 0xD800 ) {
+ throw new RangeError("UTF-16(encode): Illegal UTF-16 value");
+ }
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output.push(String.fromCharCode(((value >>>10) & 0x3FF) | 0xD800));
+ value = 0xDC00 | (value & 0x3FF);
+ }
+ output.push(String.fromCharCode(value));
+ }
+ return output.join("");
+ }
+ };
+
+ //Default parameters
+ var initial_n = 0x80;
+ var initial_bias = 72;
+ var delimiter = "-";
+ var base = 36;
+ var damp = 700;
+ var tmin=1;
+ var tmax=26;
+ var skew=38;
+ var maxint = 0x7FFFFFFF;
+
+ // decode_digit(cp) returns the numeric value of a basic code
+ // point (for use in representing integers) in the range 0 to
+ // base-1, or base if cp is does not represent a value.
+
+ function decode_digit(cp) {
+ return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;
+ }
+
+ // encode_digit(d,flag) returns the basic code point whose value
+ // (when used for representing integers) is d, which needs to be in
+ // the range 0 to base-1. The lowercase form is used unless flag is
+ // nonzero, in which case the uppercase form is used. The behavior
+ // is undefined if flag is nonzero and digit d has no uppercase form.
+
+ function encode_digit(d, flag) {
+ return d + 22 + 75 * (d < 26) - ((flag !== 0) << 5);
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ }
+ //** Bias adaptation function **
+ function adapt(delta, numpoints, firsttime ) {
+ var k;
+ delta = firsttime ? Math.floor(delta / damp) : (delta >> 1);
+ delta += Math.floor(delta / numpoints);
+
+ for (k = 0; delta > (((base - tmin) * tmax) >> 1); k += base) {
+ delta = Math.floor(delta / ( base - tmin ));
+ }
+ return Math.floor(k + (base - tmin + 1) * delta / (delta + skew));
+ }
+
+ // encode_basic(bcp,flag) forces a basic code point to lowercase if flag is zero,
+ // uppercase if flag is nonzero, and returns the resulting code point.
+ // The code point is unchanged if it is caseless.
+ // The behavior is undefined if bcp is not a basic code point.
+
+ function encode_basic(bcp, flag) {
+ bcp -= (bcp - 97 < 26) << 5;
+ return bcp + ((!flag && (bcp - 65 < 26)) << 5);
+ }
+
+ // Main decode
+ this.decode=function(input,preserveCase) {
+ // Dont use utf16
+ var output=[];
+ var case_flags=[];
+ var input_length = input.length;
+
+ var n, out, i, bias, basic, j, ic, oldi, w, k, digit, t, len;
+
+ // Initialize the state:
+
+ n = initial_n;
+ i = 0;
+ bias = initial_bias;
+
+ // Handle the basic code points: Let basic be the number of input code
+ // points before the last delimiter, or 0 if there is none, then
+ // copy the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) basic = 0;
+
+ for (j = 0; j < basic; ++j) {
+ if(preserveCase) case_flags[output.length] = ( input.charCodeAt(j) -65 < 26);
+ if ( input.charCodeAt(j) >= 0x80) {
+ throw new RangeError("Illegal input >= 0x80");
+ }
+ output.push( input.charCodeAt(j) );
+ }
+
+ // Main decoding loop: Start just after the last delimiter if any
+ // basic code points were copied; start at the beginning otherwise.
+
+ for (ic = basic > 0 ? basic + 1 : 0; ic < input_length; ) {
+
+ // ic is the index of the next character to be consumed,
+
+ // Decode a generalized variable-length integer into delta,
+ // which gets added to i. The overflow checking is easier
+ // if we increase i as we go, then subtract off its starting
+ // value at the end to obtain delta.
+ for (oldi = i, w = 1, k = base; ; k += base) {
+ if (ic >= input_length) {
+ throw RangeError ("punycode_bad_input(1)");
+ }
+ digit = decode_digit(input.charCodeAt(ic++));
+
+ if (digit >= base) {
+ throw RangeError("punycode_bad_input(2)");
+ }
+ if (digit > Math.floor((maxint - i) / w)) {
+ throw RangeError ("punycode_overflow(1)");
+ }
+ i += digit * w;
+ t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
+ if (digit < t) { break; }
+ if (w > Math.floor(maxint / (base - t))) {
+ throw RangeError("punycode_overflow(2)");
+ }
+ w *= (base - t);
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi === 0);
+
+ // i was supposed to wrap around from out to 0,
+ // incrementing n each time, so we'll fix that now:
+ if ( Math.floor(i / out) > maxint - n) {
+ throw RangeError("punycode_overflow(3)");
+ }
+ n += Math.floor( i / out ) ;
+ i %= out;
+
+ // Insert n at position i of the output:
+ // Case of last character determines uppercase flag:
+ if (preserveCase) { case_flags.splice(i, 0, input.charCodeAt(ic -1) -65 < 26);}
+
+ output.splice(i, 0, n);
+ i++;
+ }
+ if (preserveCase) {
+ for (i = 0, len = output.length; i < len; i++) {
+ if (case_flags[i]) {
+ output[i] = (String.fromCharCode(output[i]).toUpperCase()).charCodeAt(0);
+ }
+ }
+ }
+ return this.utf16.encode(output);
+ };
+
+ //** Main encode function **
+
+ this.encode = function (input,preserveCase) {
+ //** Bias adaptation function **
+
+ var n, delta, h, b, bias, j, m, q, k, t, ijv, case_flags;
+
+ if (preserveCase) {
+ // Preserve case, step1 of 2: Get a list of the unaltered string
+ case_flags = this.utf16.decode(input);
+ }
+ // Converts the input in UTF-16 to Unicode
+ input = this.utf16.decode(input.toLowerCase());
+
+ var input_length = input.length; // Cache the length
+
+ if (preserveCase) {
+ // Preserve case, step2 of 2: Modify the list to true/false
+ for (j=0; j < input_length; j++) {
+ case_flags[j] = input[j] != case_flags[j];
+ }
+ }
+
+ var output=[];
+
+
+ // Initialize the state:
+ n = initial_n;
+ delta = 0;
+ bias = initial_bias;
+
+ // Handle the basic code points:
+ for (j = 0; j < input_length; ++j) {
+ if ( input[j] < 0x80) {
+ output.push(
+ String.fromCharCode(
+ case_flags ? encode_basic(input[j], case_flags[j]) : input[j]
+ )
+ );
+ }
+ }
+
+ h = b = output.length;
+
+ // h is the number of code points that have been handled, b is the
+ // number of basic code points
+
+ if (b > 0) output.push(delimiter);
+
+ // Main encoding loop:
+ //
+ while (h < input_length) {
+ // All non-basic code points < n have been
+ // handled already. Find the next larger one:
+
+ for (m = maxint, j = 0; j < input_length; ++j) {
+ ijv = input[j];
+ if (ijv >= n && ijv < m) m = ijv;
+ }
+
+ // Increase delta enough to advance the decoder's
+ // <n,i> state to <m,0>, but guard against overflow:
+
+ if (m - n > Math.floor((maxint - delta) / (h + 1))) {
+ throw RangeError("punycode_overflow (1)");
+ }
+ delta += (m - n) * (h + 1);
+ n = m;
+
+ for (j = 0; j < input_length; ++j) {
+ ijv = input[j];
+
+ if (ijv < n ) {
+ if (++delta > maxint) return Error("punycode_overflow(2)");
+ }
+
+ if (ijv == n) {
+ // Represent delta as a generalized variable-length integer:
+ for (q = delta, k = base; ; k += base) {
+ t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
+ if (q < t) break;
+ output.push( String.fromCharCode(encode_digit(t + (q - t) % (base - t), 0)) );
+ q = Math.floor( (q - t) / (base - t) );
+ }
+ output.push( String.fromCharCode(encode_digit(q, preserveCase && case_flags[j] ? 1:0 )));
+ bias = adapt(delta, h + 1, h == b);
+ delta = 0;
+ ++h;
+ }
+ }
+
+ ++delta;
+ ++n;
+ }
+ return output.join("");
+ };
+
+ this.ToASCII = function ( domain ) {
+ var domain_array = domain.split(".");
+ var out = [];
+ for (var i=0; i < domain_array.length; ++i) {
+ var s = domain_array[i];
+ out.push(
+ s.match(/[^A-Za-z0-9\-]/) ?
+ "xn--" + punycode.encode(s) :
+ s
+ );
+ }
+ return out.join(".");
+ };
+
+ this.ToUnicode = function ( domain ) {
+ var domain_array = domain.split(".");
+ var out = [];
+ for (var i=0; i < domain_array.length; ++i) {
+ var s = domain_array[i];
+ out.push(
+ s.match(/^xn--/) ?
+ punycode.decode(s.slice(4)) :
+ s
+ );
+ }
+ return out.join(".");
+ };
+}();
+
+module.exports = function(address){
+ return address.replace(/((?:https?:\/\/)?.*\@)?([^\/]*)/, function(o, start, domain){
+ var domainParts = domain.split(/\./).map(punycode.ToASCII);
+ return (start || "") + domainParts.join(".");
+ });
+}; \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/urlfetch.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/urlfetch.js
new file mode 100644
index 0000000..ecac514
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/lib/urlfetch.js
@@ -0,0 +1,71 @@
+var http = require("http"),
+ https = require("https"),
+ urllib = require("url"),
+ Stream = require('stream').Stream;
+
+/**
+ * @namespace URLFetch
+ * @name urlfetch
+ */
+module.exports = openUrlStream;
+
+/**
+ * <p>Open a stream to a specified URL</p>
+ *
+ * @memberOf urlfetch
+ * @param {String} url URL to open
+ * @param {Object} [options] Optional options object
+ * @param {String} [options.userAgent="mailcomposer"] User Agent for the request
+ * @return {Stream} Stream for the URL contents
+ */
+function openUrlStream(url, options){
+ options = options || {};
+ var urlparts = urllib.parse(url),
+ urloptions = {
+ host: urlparts.hostname,
+ port: urlparts.port || (urlparts.protocol=="https:"?443:80),
+ path: urlparts.path || urlparts.pathname,
+ method: "GET",
+ headers: {
+ "User-Agent": options.userAgent || "mailcomposer"
+ }
+ },
+ client = (urlparts.protocol=="https:"?https:http),
+ stream = new Stream(),
+ request;
+
+ stream.resume = function(){};
+
+ if(urlparts.auth){
+ urloptions.auth = urlparts.auth;
+ }
+
+ request = client.request(urloptions, function(response) {
+ if((response.statusCode || 0).toString().charAt(0) != "2"){
+ stream.emit("error", "Invalid status code " + (response.statusCode || 0));
+ return;
+ }
+
+ response.on('error', function(err) {
+ stream.emit("error", err);
+ });
+
+ response.on('data', function(chunk) {
+ stream.emit("data", chunk);
+ });
+
+ response.on('end', function(chunk) {
+ if(chunk){
+ stream.emit("data", chunk);
+ }
+ stream.emit("end");
+ });
+ });
+ request.end();
+
+ request.on('error', function(err) {
+ stream.emit("error", err);
+ });
+
+ return stream;
+} \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/LICENSE b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/LICENSE
new file mode 100644
index 0000000..2950902
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/LICENSE
@@ -0,0 +1,16 @@
+Copyright (c) 2011 Andris Reinman
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE. \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/README.md b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/README.md
new file mode 100644
index 0000000..58a0871
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/README.md
@@ -0,0 +1,17 @@
+# mimelib
+
+*mimelib* is a collection of useful functions to deal with mime-encoded data.
+
+## Reference
+
+See [API reference](/andris9/mimelib/blob/master/doc.md) for documentation
+
+## Installation
+
+Install with *npm*
+
+ npm install mimelib-noiconv
+
+## Usage
+
+ var mimelib = require("mimelib-noiconv");
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/content-types.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/content-types.js
new file mode 100644
index 0000000..bc5aa67
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/content-types.js
@@ -0,0 +1,60 @@
+// list of mime types
+module.exports = {
+ "doc": "application/msword",
+ "docx": "application/msword",
+ "pdf": "application/pdf",
+ "rss": "application/rss+xml",
+ "xls": "application/vnd.ms-excel",
+ "xlsx": "application/vnd.ms-excel",
+ "pps": "application/vnd.ms-powerpoint",
+ "ppt": "application/vnd.ms-powerpoint",
+ "pptx": "application/vnd.ms-powerpoint",
+ "odp": "application/vnd.oasis.opendocument.presentation",
+ "ods": "application/vnd.oasis.opendocument.spreadsheet",
+ "odt": "application/vnd.oasis.opendocument.text",
+ "sxc": "application/vnd.sun.xml.calc",
+ "sxw": "application/vnd.sun.xml.writer",
+ "au": "audio/basic",
+ "snd": "audio/basic",
+ "flac": "audio/flac",
+ "mid": "audio/mid",
+ "rmi": "audio/mid",
+ "m4a": "audio/mp4",
+ "mp3": "audio/mpeg",
+ "oga": "audio/ogg",
+ "ogg": "audio/ogg",
+ "aif": "audio/x-aiff",
+ "aifc": "audio/x-aiff",
+ "aiff": "audio/x-aiff",
+ "wav": "audio/x-wav",
+ "gif": "image/gif",
+ "jpeg": "image/jpeg",
+ "jpg": "image/jpeg",
+ "jpe": "image/jpeg",
+ "png": "image/png",
+ "tiff": "image/tiff",
+ "tif": "image/tiff",
+ "wbmp": "image/vnd.wap.wbmp",
+ "bmp": "image/x-ms-bmp",
+ "ics": "text/calendar",
+ "csv": "text/comma-separated-values",
+ "css": "text/css",
+ "htm": "text/html",
+ "html": "text/html",
+ "text": "text/plain",
+ "txt": "text/plain",
+ "asc": "text/plain",
+ "diff": "text/plain",
+ "pot": "text/plain",
+ "vcf": "text/x-vcard",
+ "mp4": "video/mp4",
+ "mpeg": "video/mpeg",
+ "mpg": "video/mpeg",
+ "mpe": "video/mpeg",
+ "ogv": "video/ogg",
+ "qt": "video/quicktime",
+ "mov": "video/quicktime",
+ "avi": "video/x-msvideo",
+ "zip": "application/zip",
+ "rar": "application/x-rar-compressed"
+} \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/doc.md b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/doc.md
new file mode 100644
index 0000000..3c89514
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/doc.md
@@ -0,0 +1,191 @@
+mimelib(1) -- MIME functions for Node.JS
+=============================================
+
+## DESCRIPTION
+
+This document lists all the available methods for mimelib. You can include mimelib
+in your projects with `var mimelib = require("mimelib");`
+
+When installed with npm dependency module iconv will also be installed if not already
+present.
+
+## contentTypes
+
+`mimelib.contentTypes` is an object to provide content type strings for common
+file extensions
+
+ mimelib.contentTypes["xls"]; // "application/vnd.ms-excel"
+
+## foldLine
+
+Folds a long line according to the RFC 5322 <http://tools.ietf.org/html/rfc5322#section-2.1.1>
+
+ mimelib.foldLine(str [, maxLength][, foldAnywhere]) -> String
+
+ - `str` (String): mime string that might need folding
+ - `maxLength` (Number): max length for a line, defaults to 78
+ - `foldAnywhere` (Boolean): can fold at any location (ie. in base64)
+ - `afterSpace` (Boolean): If `true` fold after the space
+
+
+For example:
+
+ Content-Type: multipart/alternative; boundary="----zzzz----"
+
+will become
+
+ Content-Type: multipart/alternative;
+ boundary="----zzzz----"
+
+## encodeMimeWord
+
+Encodes a string into mime encoded word format <http://en.wikipedia.org/wiki/MIME#Encoded-Word> (see also `decodeMimeWord`)
+
+ mimelib.encodeMimeWord = function(str [, encoding][, charset])
+
+ - `str` (String): String to be encoded
+ - `encoding` (String): Encoding Q for quoted printable or B (def.) for base64
+ - `charset` (String): Charset to be used
+
+For example:
+
+ See on õhin test
+
+Becomes with UTF-8 and Quoted-printable encoding
+
+ =?UTF-8?q?See_on_=C3=B5hin_test?=
+
+## decodeMimeWord
+
+Decodes a string from mime encoded word format (see also `encodeMimeWord`)
+
+ mimelib.decodeMimeWord(str) -> String
+
+ - `str` (String): String to be decoded
+
+For example
+
+ mimelib.decodeMimeWord("=?UTF-8?q?See_on_=C3=B5hin_test?=");
+
+will become
+
+ See on õhin test
+
+## encodeQuotedPrintable
+
+Encodes a string into Quoted-printable format (see also `decodeQuotedPrintable`)
+
+ mimelib.encodeQuotedPrintable(str [, mimeWord][, charset]) -> String
+
+ - `str` (String): String to be encoded into Quoted-printable
+ - `mimeWord` (Boolean): Use mime-word mode (defaults to false)
+ - `charset` (String): Destination charset, defaults to UTF-8
+
+TODO: Currently only allowed charsets: UTF-8, LATIN1
+
+## decodeQuotedPrintable
+
+Decodes a string from Quoted-printable format (see also `encodeQuotedPrintable`)
+
+ mimelib.deccodeQuotedPrintable(str [, mimeWord][, charset]) -> String
+
+ - `str` (String): String to be decoded
+ - `mimeWord` (Boolean): Use mime-word mode (defaults to false)
+ - `charset` (String): Charset to be used, defaults to UTF-8
+
+## encodeBase64
+
+Encodes a string into Base64 format. Base64 is mime-word safe (see also `decodeBase64`)
+
+ mimelib.encodeBase64(str [, charset]) -> String
+
+ - `str` (String): String to be encoded into Base64
+ - `charset` (String): Destination charset, defaults to UTF-8
+
+## decodeBase64
+
+Decodes a string from Base64 format. Base64 is mime-word safe (see also `encodeBase64`)
+
+NB! Always returns UTF-8
+
+ mimelib.decodeBase64(str) -> String
+
+ - `str` (String): String to be decoded from Base64
+ - `charset` (String): Source charset, defaults to UTF-8
+
+## parseHeaders
+
+Parses header lines into an array of objects (see `parseHeaderLine`)
+
+ mimelib.parseHeaders(headers) -> Array
+
+ - `headers` (String): header section of the e-mail
+
+Example:
+
+ var headers = [
+ "From: andris@node.ee",
+ "To: juulius@node.ee",
+ "To: juulius2@node.ee",
+ "Content-type: text/html;",
+ " charset=utf-8"
+ ].join("\r\n");
+ mimelib.parseHeaders(headers);
+
+Results in
+
+ {"from": [ 'andris@node.ee' ],
+ "to": [ 'juulius@node.ee', 'juulius2@node.ee' ],
+ "content-type": [ 'text/html; charset=utf-8' ] }
+
+## parseAddresses
+
+Parses names and addresses from a from, to, cc or bcc line
+
+ mimelib.parseAddresses(addresses) -> Array
+
+ - `addresses` (String): string with comma separated e-mail addresses
+
+Example:
+
+ var to = '"Andris Reinman" <andris@node.ee>, juulius@node.ee'
+ mimelib.parseAddresses(to);
+
+Results in
+
+ [{ address: 'andris@node.ee', name: 'Andris Reinman' },
+ { address: 'juulius@node.ee', name: false }]
+
+## parseMimeWords
+
+Parses mime-words into UTF-8 strings
+
+ mimelib.parseMimeWords(str) -> String
+
+ - `str` (String): string to be parsed, if includes any mime words, then these are converted to UTF-8 strings
+
+
+For example:
+
+ mimelib.parseMimeWords("Hello: =?UTF-8?q?See_on_=C3=B5hin_test?=");
+
+Results in
+
+ "Hello: See on õhin test"
+
+## parseHeaderLine
+
+Parses a header line to search for additional parameters.
+
+ mimelib.parseHeaderLine(line) -> Object
+
+ - `line` (String): a line from a message headers
+
+For example:
+
+ mimelib.parseHeaderLine("text/plain; charset=utf-8")imelib
+
+Results in
+
+ {"defaultValue": 'text/plain',
+ "charset": 'utf-8' }
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/index.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/index.js
new file mode 100644
index 0000000..a7ea3ee
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/index.js
@@ -0,0 +1,3 @@
+
+module.exports = require("./mime-functions");
+module.exports.contentTypes = require("./content-types"); \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/mime-functions.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/mime-functions.js
new file mode 100644
index 0000000..9a93c7b
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/mime-functions.js
@@ -0,0 +1,486 @@
+
+try{
+ // see http://github.com/bnoordhuis/node-iconv for more info
+ var Iconv = require("iconv").Iconv;
+}catch(E){
+ // convert nothing
+ Iconv = function(){}
+ Iconv.prototype.convert = function(buf){return buf;};
+}
+
+/* mime related functions - encoding/decoding etc*/
+/* TODO: Only UTF-8 and Latin1 are allowed with encodeQuotedPrintable */
+/* TODO: Check if the input string even needs encoding */
+
+/**
+ * mime.foldLine(str, maxLength, foldAnywhere) -> String
+ * - str (String): mime string that might need folding
+ * - maxLength (Number): max length for a line, defaults to 78
+ * - foldAnywhere (Boolean): can fold at any location (ie. in base64)
+ * - afterSpace (Boolean): If [true] fold after the space
+ *
+ * Folds a long line according to the RFC 5322
+ * <http://tools.ietf.org/html/rfc5322#section-2.1.1>
+ *
+ * For example:
+ * Content-Type: multipart/alternative; boundary="----bd_n3-lunchhour1283962663300----"
+ * will become
+ * Content-Type: multipart/alternative;
+ * boundary="----bd_n3-lunchhour1283962663300----"
+ *
+ **/
+this.foldLine = function(str, maxLength, foldAnywhere, afterSpace){
+ var line=false, curpos=0, response="", lf;
+ maxLength = maxLength || 78;
+
+ // return original if no need to fold
+ if(str.length<=maxLength)
+ return str;
+
+ // read in <maxLength> bytes and try to fold it
+ while(line = str.substr(curpos, maxLength)){
+ if(!!foldAnywhere){
+ response += line;
+ if(curpos+maxLength<str.length){
+ response+="\r\n";
+ }
+ }else{
+ lf = line.lastIndexOf(" ");
+ if(lf<=0)
+ lf = line.lastIndexOf("\t");
+ if(line.length>=maxLength && lf>0){
+ if(!!afterSpace){
+ // move forward until line end or no more \s and \t
+ while(lf<line.length && (line.charAt(lf)==" " || line.charAt(lf)=="\t")){
+ lf++;
+ }
+ }
+ response += line.substr(0,lf)+"\r\n"+(!foldAnywhere && !afterSpace && " " || "");
+ curpos -= line.substr(lf).length;
+ }else{
+ response += line;
+ //line = line.replace(/=[a-f0-9]?$/i, "");
+ //response+=line + "\r\n";
+ }
+ }
+ curpos += line.length;
+ }
+
+ // return folded string
+ return response;
+}
+
+
+/**
+ * mime.encodeMimeWord(str, encoding, charset) -> String
+ * - str (String): String to be encoded
+ * - encoding (String): Encoding Q for quoted printable or B (def.) for base64
+ * - charset (String): Charset to be used
+ *
+ * Encodes a string into mime encoded word format
+ * <http://en.wikipedia.org/wiki/MIME#Encoded-Word>
+ *
+ * For example:
+ * See on õhin test
+ * Becomes with UTF-8 and Quoted-printable encoding
+ * =?UTF-8?q?See_on_=C3=B5hin_test?=
+ *
+ **/
+this.encodeMimeWord = function(str, encoding, charset){
+ charset = charset || "UTF-8";
+ encoding = encoding && encoding.toUpperCase() || "B";
+
+ if(encoding=="Q"){
+ str = this.encodeQuotedPrintable(str, true, charset);
+ }
+
+ if(encoding=="B"){
+ str = this.encodeBase64(str);
+ }
+
+ return "=?"+charset+"?"+encoding+"?"+str+"?=";
+}
+
+/**
+ * mime.decodeMimeWord(str, encoding, charset) -> String
+ * - str (String): String to be encoded
+ * - encoding (String): Encoding Q for quoted printable or B (def.) for base64
+ * - charset (String): Charset to be used, defaults to UTF-8
+ *
+ * Decodes a string from mime encoded word format, see [[encodeMimeWord]]
+ *
+ **/
+
+this.decodeMimeWord = function(str){
+ var parts = str.split("?"),
+ charset = parts && parts[1],
+ encoding = parts && parts[2],
+ text = parts && parts[3];
+ if(!charset || !encoding || !text)
+ return str;
+ if(encoding.toUpperCase()=="Q"){
+ return this.decodeQuotedPrintable(text, true, charset);
+ }
+
+ if(encoding.toUpperCase()=="B"){
+ return this.decodeBase64(text);
+ }
+
+ return text;
+}
+
+
+/**
+ * mime.encodeQuotedPrintable(str, mimeWord, charset) -> String
+ * - str (String): String to be encoded into Quoted-printable
+ * - mimeWord (Boolean): Use mime-word mode (defaults to false)
+ * - charset (String): Destination charset, defaults to UTF-8
+ * TODO: Currently only allowed charsets: UTF-8, LATIN1
+ *
+ * Encodes a string into Quoted-printable format.
+ **/
+this.encodeQuotedPrintable = function(str, mimeWord, charset){
+ charset = charset || "UTF-8";
+
+ /*
+ * Characters from 33-126 OK (except for =; and ?_ when in mime word mode)
+ * Spaces + tabs OK (except for line beginnings and endings)
+ * \n + \r OK
+ */
+
+ str = str.replace(/(?:[\ud800-\udbff][\udc00-\udfff])|[^\sa-zA-Z\d]/gm,function(c){
+ if(!!mimeWord){
+ if(c=="?")return "=3F";
+ if(c=="_")return "=5F";
+ }
+ if(c!=="=" && c.charCodeAt(0)>=33 && c.charCodeAt(0)<=126)
+ return c;
+ return c=="="?"=3D":(charset=="UTF-8"?encodeURIComponent(c):escape(c)).replace(/%/g,'=');
+ });
+
+ str = lineEdges(str);
+
+ if(!mimeWord){
+ // lines might not be longer than 76 bytes, soft break: "=\r\n"
+ var lines = str.split(/\r?\n/);
+ str.replace(/(.{73}(?!\r?\n))/,"$&=\r\n")
+ for(var i=0, len = lines.length; i<len; i++){
+ if(lines[i].length>76){
+ lines[i] = this.foldLine(lines[i],76, false, true).replace(/\r\n/g,"=\r\n");
+ }
+ }
+ str = lines.join("\r\n");
+ }else{
+ str = str.replace(/\s/g, function(a){
+ if(a==" ")return "_";
+ if(a=="\t")return "=09";
+ return a=="\r"?"=0D":"=0A";
+ });
+ }
+
+ return str;
+}
+
+/**
+ * mime.deccodeQuotedPrintable(str, mimeWord, charset) -> String
+ * - str (String): String to be decoded
+ * - mimeWord (Boolean): Use mime-word mode (defaults to false)
+ * - charset (String): Charset to be used, defaults to UTF-8
+ *
+ * Decodes a string from Quoted-printable format.
+ **/
+this.decodeQuotedPrintable = function(str, mimeWord, charset){
+ charset = charset && charset.toUpperCase() || "UTF-8";
+
+ if(mimeWord){
+ str = str.replace(/_/g," ");
+ }else{
+ str = str.replace(/=\r\n/gm,'');
+ str = str.replace(/=$/,"");
+ }
+ if(charset == "UTF-8")
+ str = decodeURIComponent(str.replace(/%/g,'%25').replace(/=/g,"%"));
+ else{
+ str = str.replace(/%/g,'%25').replace(/=/g,"%");
+ if(charset=="ISO-8859-1" || charset=="LATIN1")
+ str = unescape(str);
+ else{
+ str = decodeBytestreamUrlencoding(str);
+ str = fromCharset(charset, str);
+ }
+ }
+ return str;
+}
+
+/**
+ * mime.encodeBase64(str) -> String
+ * - str (String): String to be encoded into Base64
+ * - charset (String): Destination charset, defaults to UTF-8
+ *
+ * Encodes a string into Base64 format. Base64 is mime-word safe.
+ **/
+this.encodeBase64 = function(str, charset){
+ var buffer;
+ if(charset && charset.toUpperCase()!="UTF-8")
+ buffer = toCharset(charset, str);
+ else
+ buffer = new Buffer(str, "UTF-8");
+ return buffer.toString("base64");
+}
+
+/**
+ * mime.decodeBase64(str) -> String
+ * - str (String): String to be decoded from Base64
+ * - charset (String): Source charset, defaults to UTF-8
+ *
+ * Decodes a string from Base64 format. Base64 is mime-word safe.
+ * NB! Always returns UTF-8
+ **/
+this.decodeBase64 = function(str, charset){
+ var buffer = new Buffer(str, "base64");
+
+ if(charset && charset.toUpperCase()!="UTF-8"){
+ return fromCharset(charset, buffer);
+ }
+
+ // defaults to utf-8
+ return buffer.toString("UTF-8");
+}
+
+/**
+ * mime.parseHeaders(headers) -> Array
+ * - headers (String): header section of the e-mail
+ *
+ * Parses header lines into an array of objects (see [[parseHeaderLine]])
+ * FIXME: This should probably not be here but in "envelope" instead
+ **/
+this.parseHeaders = function(headers){
+ var text, lines, line, i, name, value, cmd, header_lines = {};
+ // unfold
+ headers = headers.replace(/\r?\n([ \t])/gm," ");
+
+ // split lines
+ lines = headers.split(/\r?\n/);
+ for(i=0; i<lines.length;i++){
+ if(!lines[i]) // no more header lines
+ break;
+ cmd = lines[i].match(/[^\:]+/);
+ if(cmd && (cmd = cmd[0])){
+ name = cmd;
+ value = lines[i].substr(name.length+1);
+ if(!header_lines[name.toLowerCase().trim()])header_lines[name.toLowerCase().trim()] = [];
+ header_lines[name.toLowerCase()].push(value.trim());
+ }
+ }
+
+ return header_lines;
+}
+
+/**
+ * mime.parseAddresses(addresses) -> Array
+ * - addresses (String): string with comma separated e-mail addresses
+ *
+ * Parses names and addresses from a from, to, cc or bcc line
+ **/
+this.parseAddresses = function(addresses){
+ if(!addresses)
+ return [];
+
+ addresses = addresses.replace(/\=\?[^?]+\?[QqBb]\?[^?]+\?=/g, (function(a){return this.decodeMimeWord(a);}).bind(this));
+
+ // not sure if it's even needed - urlencode escaped \\ and \" and \'
+ addresses = addresses.replace(/\\\\/g,function(a){return escape(a.charAt(1));});
+ addresses = addresses.replace(/\\["']/g,function(a){return escape(a.charAt(1));});
+
+ // find qutoed strings
+
+ var parts = addresses.split(','), curStr,
+ curQuote, lastPos, remainder="", str, list = [],
+ curAddress, address, addressArr = [], name, email, i, len;
+ var rightEnd;
+
+ // separate quoted text from text parts
+ for(i=0, len=parts.length; i<len; i++){
+ str = "";
+
+ curStr = (remainder+parts[i]).trim();
+
+ curQuote = curStr.charAt(0);
+ if(curQuote == "'" || curQuote == '"'){
+ rightEnd= curStr.indexOf("<");
+ if(rightEnd == -1)rightEnd= curStr.length-1;
+ lastPos = curStr.lastIndexOf(curQuote,rightEnd);
+
+ if(!lastPos){
+ remainder = remainder+parts[i]+",";
+ continue;
+ }else{
+ remainder = "";
+ str = curStr.substring(1, lastPos).trim();
+ address = curStr.substr(lastPos+1).trim();
+ }
+
+ }else{
+ address = curStr;
+ }
+
+ list.push({name: str, address: address, original: curStr});
+ }
+
+ // find e-mail addresses and user names
+ for(i=0, len=list.length; i<len; i++){
+ curAddress = list[i];
+
+ email = false;
+ name = false;
+
+ name = curAddress.name;
+
+ address = curAddress.address.replace(/<([^>]+)>/, function(original, addr){
+ email = addr.indexOf("@")>=0 && addr;
+ return email ? "" : original;
+ }).trim();
+
+ if(!email){
+ address = address.replace(/(\S+@\S+)/, function(original, m){
+ email = m;
+ return email ? "" : original;
+ });
+ }
+
+ if(!name){
+ if(email){
+ email = email.replace(/\(([^)]+)\)/,function(original, n){
+ name = n;
+ return "";
+ });
+ }
+ if(!name){
+ name = address.replace(/"/g,"").trim();
+ }
+ }
+
+ // just in case something got mixed up
+ if(!email && name.indexOf("@")>=0){
+ email = name;
+ name = false;
+ }
+
+ if(name || email){
+ name = (name || "").replace(/%27/g, "'").replace(/%22/g, "\"");
+ email = (email || "").replace(/%27/g, "'").replace(/%22/g, "\"");
+ addressArr.push({address: email, name: name});
+
+ }
+ }
+ return addressArr;
+};
+
+/**
+ * mime.parseMimeWords(str) -> String
+ * - str (String): string to be parsed
+ *
+ * Parses mime-words into UTF-8 strings
+ **/
+this.parseMimeWords = function(str){
+ return str.replace(/=\?[^?]+\?[QqBb]\?[^?]+\?=/g, (function(a){
+ return this.decodeMimeWord(a);
+ }).bind(this));
+}
+
+/**
+ * mime.parseHeaderLine(line) -> Object
+ * - line (String): a line from a message headers
+ *
+ * Parses a header line to search for additional parameters.
+ * For example with "text/plain; charset=utf-8" the output would be
+ * - defaultValue = text/plain
+ * - charset = utf-8
+ **/
+this.parseHeaderLine = function(line){
+ if(!line)
+ return {};
+ var result = {}, parts = line.split(";"), pos;
+ for(var i=0, len = parts.length; i<len; i++){
+ pos = parts[i].indexOf("=");
+ if(pos<0){
+ result[!i?"defaultValue":"i-"+i] = parts[i].trim();
+ }else{
+ result[parts[i].substr(0,pos).trim().toLowerCase()] = parts[i].substr(pos+1).trim();
+ }
+ }
+ return result;
+}
+
+
+/* Helper functions */
+
+/**
+ * lineEdges(str) -> String
+ * - str (String): String to be processed
+ *
+ * Replaces all spaces and tabs in the beginning and end of the string
+ * with quoted printable encoded chars. Needed by [[encodeQuotedPrintable]]
+ **/
+function lineEdges(str){
+ str = str.replace(/^[ \t]+/gm, function(wsc){
+ return wsc.replace(/ /g,"=20").replace(/\t/g,"=09");
+ });
+
+ str = str.replace(/[ \t]+$/gm, function(wsc){
+ return wsc.replace(/ /g,"=20").replace(/\t/g,"=09");
+ });
+ return str;
+}
+
+/**
+ * fromCharset(charset, buffer, keep_buffer) -> String | Buffer
+ * - charset (String): Source charset
+ * - buffer (Buffer): Buffer in <charset>
+ * - keep_buffer (Boolean): If true, return buffer, otherwise UTF-8 string
+ *
+ * Converts a buffer in <charset> codepage into UTF-8 string
+ **/
+function fromCharset(charset, buffer, keep_buffer){
+ var iconv = new Iconv(charset,'UTF-8'),
+ buffer = iconv.convert(buffer);
+ return keep_buffer?buffer:buffer.toString("utf-8");
+}
+
+/**
+ * toCharset(charset, buffer) -> Buffer
+ * - charset (String): Source charset
+ * - buffer (Buffer): Buffer in UTF-8 or string
+ *
+ * Converts a string or buffer to <charset> codepage
+ **/
+function toCharset(charset, buffer){
+ var iconv = new Iconv('UTF-8',charset);
+ return iconv.convert(buffer);
+}
+
+/**
+ * decodeBytestreamUrlencoding(encoded_string) -> Buffer
+ * - encoded_string (String): String in urlencode coding
+ *
+ * Converts an urlencoded string into a bytestream buffer. If the used
+ * charset is known the resulting string can be converted to UTF-8 with
+ * [[fromCharset]].
+ * NB! For UTF-8 use decodeURIComponent and for Latin 1 decodeURL instead
+ **/
+function decodeBytestreamUrlencoding(encoded_string){
+ var c, i, j=0, prcnts = encoded_string.match(/%/g) || "",
+ buffer_length = encoded_string.length - (prcnts.length*2),
+ buffer = new Buffer(buffer_length);
+
+ for(var i=0; i<encoded_string.length; i++){
+ c = encoded_string.charCodeAt(i);
+ if(c=="37"){ // %
+ c = parseInt(encoded_string.substr(i+1,2), 16);
+ i+=2;
+ }
+ buffer[j++] = c;
+ }
+ return buffer;
+}
+
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/package.json b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/package.json
new file mode 100644
index 0000000..d807316
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/node_modules/mimelib-noiconv/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "mimelib-noiconv",
+ "description": "MIME functions to encode/decode e-mails etc.",
+ "version": "0.1.9",
+ "author": {
+ "name": "Andris Reinman"
+ },
+ "homepage": "http://github.com/andris9/mimelib",
+ "maintainers": [
+ {
+ "name": "andris",
+ "email": "andris@node.ee"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/andris9/mimelib.git"
+ },
+ "main": ".",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://github.com/andris9/mimelib/blob/master/LICENSE"
+ }
+ ],
+ "dependencies": {},
+ "keywords": [
+ "e-mail",
+ "mime",
+ "email"
+ ],
+ "readme": "# mimelib\n\n*mimelib* is a collection of useful functions to deal with mime-encoded data.\n\n## Reference\n\nSee [API reference](/andris9/mimelib/blob/master/doc.md) for documentation\n\n## Installation\n\nInstall with *npm*\n\n npm install mimelib-noiconv\n \n## Usage\n\n var mimelib = require(\"mimelib-noiconv\");\n",
+ "_id": "mimelib-noiconv@0.1.9",
+ "_from": "mimelib-noiconv@*"
+}
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/package.json b/tools/node_modules/nodemailer/node_modules/mailcomposer/package.json
new file mode 100644
index 0000000..fe0755f
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "mailcomposer",
+ "description": "Compose E-Mail messages",
+ "version": "0.1.15",
+ "author": {
+ "name": "Andris Reinman"
+ },
+ "maintainers": [
+ {
+ "name": "andris",
+ "email": "andris@node.ee"
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/andris9/mailcomposer.git"
+ },
+ "scripts": {
+ "test": "nodeunit test/"
+ },
+ "main": "./lib/mailcomposer",
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://github.com/andris9/mailcomposer/blob/master/LICENSE"
+ }
+ ],
+ "dependencies": {
+ "mimelib-noiconv": "*"
+ },
+ "devDependencies": {
+ "nodeunit": "*",
+ "mailparser": "*"
+ },
+ "engine": {
+ "node": ">=0.4"
+ },
+ "keywords": [
+ "e-mail",
+ "mime",
+ "parser"
+ ],
+ "readme": "# mailcomposer\n\n**mailcomposer** is a Node.JS module for generating e-mail messages that can be\nstreamed to SMTP or file. \n\nThis is a standalone module that only generates raw e-mail source, you need to \nwrite your own or use an existing transport mechanism (SMTP client, Amazon SES, \nSendGrid etc). **mailcomposer** frees you from the tedious task of generating \n[rfc822](http://tools.ietf.org/html/rfc2822) compatible messages.\n\n[![Build Status](https://secure.travis-ci.org/andris9/mailcomposer.png)](http://travis-ci.org/andris9/mailcomposer)\n\n**mailcomposer** supports:\n\n * **Unicode** to use any characters ✔\n * **HTML** content as well as **plain text** alternative\n * **Attachments** and streaming for larger files (use strings, buffers, files or binary streams as attachments)\n * **Embedded images** in HTML\n * **DKIM** signing\n * usage of **your own** transport mechanism\n\n## Support mailcomposer development\n\n[![Donate to author](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DB26KWR2BQX5W)\n\n## Installation\n\nInstall through NPM\n\n npm install mailcomposer\n\n## Usage\n\n### Include mailcomposer module\n\n var MailComposer = require(\"mailcomposer\").MailComposer;\n\n### Create a new `MailComposer` instance\n\n var mailcomposer = new MailComposer([options]);\n\nWhere `options` is an optional options object with the following possible properties:\n\n * **escapeSMTP** - if set replaces dots in the beginning of a line with double dots\n * **encoding** - sets transfer encoding for the textual parts (defaults to `\"quoted-printable\"`)\n * **keepBcc** - if set to true, includes `Bcc:` field in the message headers. Useful for *sendmail* command. \n\n### Simple example\n\nThe following example generates a simple e-mail message with plaintext and html\nbody.\n\n var MailComposer = require(\"mailcomposer\").MailComposer;\n mailcomposer = new MailComposer(),\n fs = require(\"fs\");\n \n // add additional header field\n mailcomposer.addHeader(\"x-mailer\", \"Nodemailer 1.0\");\n \n // setup message data\n mailcomposer.setMessageOption({\n from: \"andris@tr.ee\",\n to: \"andris@node.ee\",\n body: \"Hello world!\",\n html: \"<b>Hello world!</b>\"\n }); \n \n mailcomposer.streamMessage();\n \n // pipe the output to a file\n mailcomposer.pipe(fs.createWriteStream(\"test.eml\"));\n\nThe output for such a script (the contents for \"test.eml\") would look like:\n\n MIME-Version: 1.0\n X-Mailer: Nodemailer 1.0\n From: andris@tr.ee\n To: andris@node.ee\n Content-Type: multipart/alternative;\n boundary=\"----mailcomposer-?=_1-1328088797399\"\n \n ------mailcomposer-?=_1-1328088797399\n Content-Type: text/plain; charset=utf-8\n Content-Transfer-Encoding: quoted-printable\n \n Hello world!\n ------mailcomposer-?=_1-1328088797399\n Content-Type: text/html; charset=utf-8\n Content-Transfer-Encoding: quoted-printable\n \n <b>Hello world!</b>\n ------mailcomposer-?=_1-1328088797399--\n\n## API\n\n### Add custom headers\n\nHeaders can be added with `mailcomposer.addHeader(key, value)`\n\n var mailcomposer = new MailComposer();\n mailcomposer.addHeader(\"x-mailer\", \"Nodemailer 1.0\");\n\nIf you add an header value with the same key several times, all of the values will be used\nin the generated header. For example:\n\n mailcomposer.addHeader(\"x-mailer\", \"Nodemailer 1.0\");\n mailcomposer.addHeader(\"x-mailer\", \"Nodemailer 2.0\");\n \nWill be generated into\n\n ...\n X-Mailer: Nodemailer 1.0\n X-Mailer: Nodemailer 2.0\n ...\n\nThe contents of the field value is not edited in any way (except for the folding),\nso if you want to use unicode symbols you need to escape these to mime words\nby yourself. Exception being object values - in this case the object\nis automatically JSONized and mime encoded.\n\n // using objects as header values is allowed (will be converted to JSON)\n var apiOptions = {};\n apiOptions.category = \"newuser\";\n apiOptions.tags = [\"user\", \"web\"];\n mailcomposer.addHeader(\"X-SMTPAPI\", apiOptions)\n\n### Add message parts\n\nYou can set message sender, receiver, subject line, message body etc. with\n`mailcomposer.setMessageOption(options)` where options is an object with the\ndata to be set. This function overwrites any previously set values with the\nsame key\n\nThe following example creates a simple e-mail with sender being `andris@tr.ee`, \nreceiver `andris@node.ee` and plaintext part of the message as `Hello world!`:\n\n mailcomposer.setMessageOption({\n from: \"andris@tr.ee\",\n to: \"andris@node.ee\",\n body: \"Hello world!\"\n }); \n\nPossible options that can be used are (all fields accept unicode):\n\n * **from** (alias `sender`) - the sender of the message. If several addresses are given, only the first one will be used\n * **to** - receivers for the `To:` field\n * **cc** - receivers for the `Cc:` field\n * **bcc** - receivers for the `Bcc:` field\n * **replyTo** (alias `reply_to`) - e-mail address for the `Reply-To:` field\n * **inReplyTo** - The message-id this message is replying\n * **references** - Message-id list\n * **subject** - the subject line of the message\n * **body** (alias `text`) - the plaintext part of the message\n * **html** - the HTML part of the message\n * **envelope** - optional SMTP envelope, if auto generated envelope is not suitable\n\nThis method can be called several times\n\n mailcomposer.setMessageOption({from: \"andris@tr.ee\"});\n mailcomposer.setMessageOption({to: \"andris@node.ee\"});\n mailcomposer.setMessageOption({body: \"Hello world!\"});\n\nTrying to set the same key several times will yield in overwrite\n\n mailcomposer.setMessageOption({body: \"Hello world!\"});\n mailcomposer.setMessageOption({body: \"Hello world?\"});\n // body contents will be \"Hello world?\"\n\n### Address format\n\nAll e-mail address fields take structured e-mail lists (comma separated)\nas the input. Unicode is allowed for all the parts (receiver name, e-mail username\nand domain) of the address. If the domain part contains unicode symbols, it is\nautomatically converted into punycode, user part will be converted into UTF-8\nmime word.\n\nE-mail addresses can be a plain e-mail addresses\n\n username@example.com\n\nor with a formatted name\n\n 'Ноде Майлер' <username@example.com>\n\nOr in case of comma separated lists, the formatting can be mixed\n\n username@example.com, 'Ноде Майлер' <username@example.com>, \"Name, User\" <username@example.com>\n\n### SMTP envelope\n\nSMTP envelope is usually auto generated from `from`, `to`, `cc` and `bcc` fields but\nif for some reason you want to specify it yourself, you can do it with `envelope` property.\n\n`envelope` is an object with the following params: `from`, `to`, `cc` and `bcc` just like\nwith regular mail options. You can also use the regular address format.\n\n mailOptions = {\n ...,\n from: \"mailer@node.ee\",\n to: \"daemon@node.ee\",\n envelope: {\n from: \"Daemon <deamon@node.ee>\",\n to: \"mailer@node.ee, Mailer <mailer2@node.ee>\"\n }\n }\n\n### Add attachments\n\nAttachments can be added with `mailcomposer.addAttachment(attachment)` where\n`attachment` is an object with attachment (meta)data with the following possible\nproperties:\n\n * **fileName** (alias `filename`) - filename to be reported as the name of the attached file, use of unicode is allowed\n * **cid** - content id for using inline images in HTML message source\n * **contents** - String or a Buffer contents for the attachment\n * **filePath** - path to a file or an URL if you want to stream the file instead of including it (better for larger attachments)\n * **streamSource** - Stream object for arbitrary binary streams if you want to stream the contents (needs to support *pause*/*resume*)\n * **contentType** - content type for the attachment, if not set will be derived from the `fileName` property\n * **contentDisposition** - content disposition type for the attachment, defaults to \"attachment\" \n * **userAgent** - User-Agent string to be used if the fileName points to an URL\n\nOne of `contents`, `filePath` or `streamSource` must be specified, if none is \npresent, the attachment will be discarded. Other fields are optional.\n\nAttachments can be added as many as you want.\n\n**Using embedded images in HTML**\n\nAttachments can be used as embedded images in the HTML body. To use this \nfeature, you need to set additional property of the attachment - `cid` \n(unique identifier of the file) which is a reference to the attachment file. \nThe same `cid` value must be used as the image URL in HTML (using `cid:` as \nthe URL protocol, see example below).\n\nNB! the cid value should be as unique as possible!\n\n var cid_value = Date.now() + '.image.jpg';\n \n var html = 'Embedded image: <img src=\"cid:' + cid_value + '\" />';\n \n var attachment = {\n fileName: \"image.png\",\n filePath: \"/static/images/image.png\",\n cid: cid_value\n };\n\n### DKIM Signing\n\n**mailcomposer** supports DKIM signing with very simple setup. Use this with caution \nthough since the generated message needs to be buffered entirely before it can be\nsigned - in this case the streaming capability offered by mailcomposer is illusionary,\nthere will only be one `'data'` event with the entire message. Not a big deal with\nsmall messages but might consume a lot of RAM when using larger attachments.\n\nSet up the DKIM signing with `useDKIM` method:\n\n mailcomposer.useDKIM(dkimOptions)\n\nWhere `dkimOptions` includes necessary options for signing\n\n * **domainName** - the domainname that is being used for signing\n * **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\n * **privateKey** - DKIM private key that is used for signing as a string\n * **headerFieldNames** - optional colon separated list of header fields to sign, by default all fields suggested by RFC4871 #5.5 are used\n\n**NB!** Currently if several header fields with the same name exists, only the last one (the one in the bottom) is signed.\n\nExample:\n\n mailcomposer.setMessageOption({from: \"andris@tr.ee\"});\n mailcomposer.setMessageOption({to: \"andris@node.ee\"});\n mailcomposer.setMessageOption({body: \"Hello world!\"});\n mailcomposer.useDKIM({\n domainName: \"node.ee\",\n keySelector: \"dkim\",\n privateKey: fs.readFileSync(\"private_key.pem\")\n });\n\n### Start streaming\n\nWhen the message data is setup, streaming can be started. After this it is not\npossible to add headers, attachments or change body contents.\n\n mailcomposer.streamMessage();\n\nThis generates `'data'` events for the message headers and body and final `'end'` event.\nAs `MailComposer` objects are Stream instances, these can be piped\n\n // save the output to a file\n mailcomposer.streamMessage();\n mailcomposer.pipe(fs.createWriteStream(\"out.txt\"));\n\n## Envelope\n\nEnvelope can be generated with an `getEnvelope()` which returns an object\nthat includes a `from` address (string) and a list of `to` addresses (array of\nstrings) suitable for forwarding to a SMTP server as `MAIL FROM:` and `RCPT TO:`.\n\n console.log(mailcomposer.getEnvelope());\n // {from:\"sender@example.com\", to:[\"receiver@example.com\"]}\n\n**NB!** both `from` and `to` properties might be missing from the envelope object\nif corresponding addresses were not detected from the e-mail.\n\n## Running tests\n\nTests are run with [nodeunit](https://github.com/caolan/nodeunit)\n\nRun\n\n npm test\n\nor alternatively\n\n node run_tests.js\n\n## License\n\n**MIT**",
+ "_id": "mailcomposer@0.1.15",
+ "_from": "mailcomposer@>= 0.1.15"
+}
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/test/dkim.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/dkim.js
new file mode 100644
index 0000000..6d18251
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/dkim.js
@@ -0,0 +1,47 @@
+var testCase = require('nodeunit').testCase,
+ dkim = require("../lib/dkim"),
+ fs = require("fs");
+
+
+exports["Canonicalizer tests"] = {
+ "Relaxed body": function(test){
+ // dkim.org samples
+ var body = " C \r\nD \t E\r\n\r\n\r\n";
+ test.equal(" C\r\nD E\r\n", dkim.DKIMCanonicalizer.relaxedBody(body));
+ test.done();
+ },
+ "Relaxed body short": function(test){
+ // dkim.org samples
+ var body = " C \r\nD \t E";
+ test.equal(" C\r\nD E\r\n", dkim.DKIMCanonicalizer.relaxedBody(body));
+ test.done();
+ },
+ "Relaxed headers": function(test){
+ var headers = "A: X\r\nB: Y\t\r\n\tZ \r\n";
+ test.equal("a:X\r\nb:Y Z\r\n", dkim.DKIMCanonicalizer.relaxedHeaders(headers, "a:b").headers);
+ test.done();
+ }
+}
+
+exports["General tests"] = {
+ "Unicode domain": function(test){
+ var mail = "From: andris@node.ee\r\nTo:andris@kreata.ee\r\n\r\nHello world!";
+ var dkimField = dkim.DKIMSign(mail,{
+ domainName: "müriaad-polüteism.info",
+ keySelector: "dkim",
+ privateKey: fs.readFileSync(__dirname+"/test_private.pem")
+ });
+ test.equal(dkimField.replace(/\r?\n\s*/g, "").replace(/\s+/g, " "), "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;d=xn--mriaad-polteism-zvbj.info; q=dns/txt; s=dkim;bh=z6TUz85EdYrACGMHYgZhJGvVy5oQI0dooVMKa2ZT7c4=; h=from:to;b=oBJ1MkwEkftfXa2AK4Expjp2xgIcAR43SVrftSEHVQ6F1SlGjP3EKP+cn/hLkhUel3rY0icthk/myDu6uhTBmM6DMtzIBW/7uQd6q9hfgaiYnw5Iew2tZc4TzBEYSdKi")
+ test.done();
+ },
+ "Normal domain": function(test){
+ var mail = "From: andris@node.ee\r\nTo:andris@kreata.ee\r\n\r\nHello world!";
+ var dkimField = dkim.DKIMSign(mail,{
+ domainName: "node.ee",
+ keySelector: "dkim",
+ privateKey: fs.readFileSync(__dirname+"/test_private.pem")
+ });
+ test.equal(dkimField.replace(/\r?\n\s*/g, "").replace(/\s+/g, " "), "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=node.ee; q=dns/txt;s=dkim; bh=z6TUz85EdYrACGMHYgZhJGvVy5oQI0dooVMKa2ZT7c4=; h=from:to;b=pVd+Dp+EjmYBcc1AWlBAP4ESpuAJ2WMS4gbxWLoeUZ1vZRodVN7K9UXvcCsLuqjJktCZMN2+8dyEUaYW2VIcxg4sVBCS1wqB/tqYZ/gxXLnG2/nZf4fyD2vxltJP4pDL");
+ test.done();
+ }
+} \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/test/mailcomposer.js b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/mailcomposer.js
new file mode 100644
index 0000000..0a0d9fd
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/mailcomposer.js
@@ -0,0 +1,1085 @@
+var testCase = require('nodeunit').testCase,
+ MailComposer = require("../lib/mailcomposer").MailComposer,
+ toPunycode = require("../lib/punycode"),
+ MailParser = require("mailparser").MailParser,
+ fs = require("fs"),
+ http = require("http");
+
+
+var HTTP_PORT = 9437;
+
+exports["General tests"] = {
+
+ "Create a new MailComposer object": function(test){
+ var mailcomposer = new MailComposer();
+ test.equal(typeof mailcomposer.on, "function");
+ test.equal(typeof mailcomposer.emit, "function");
+ test.done();
+ },
+
+ "Normalize key names": function(test){
+ var normalizer = MailComposer.prototype._normalizeKey;
+
+ test.equal(normalizer("abc"), "Abc");
+ test.equal(normalizer("aBC"), "Abc");
+ test.equal(normalizer("ABC"), "Abc");
+ test.equal(normalizer("a-b-c"), "A-B-C");
+ test.equal(normalizer("ab-bc"), "Ab-Bc");
+ test.equal(normalizer("ab-bc-cd"), "Ab-Bc-Cd");
+ test.equal(normalizer("AB-BC-CD"), "Ab-Bc-Cd");
+ test.equal(normalizer("mime-version"), "MIME-Version"); // special case
+
+ test.done();
+ },
+
+ "Add header": function(test){
+ var mc = new MailComposer();
+ test.equal(typeof mc._headers["Test-Key"], "undefined");
+ mc.addHeader("test-key", "first");
+ test.equal(mc._headers["Test-Key"], "first");
+ mc.addHeader("test-key", "second");
+ test.deepEqual(mc._headers["Test-Key"], ["first","second"]);
+ mc.addHeader("test-key", "third");
+ test.deepEqual(mc._headers["Test-Key"], ["first","second","third"]);
+ test.done();
+ },
+
+ "Get header": function(test){
+ var mc = new MailComposer();
+ test.equal(mc._getHeader("MIME-Version"), "1.0");
+ test.equal(mc._getHeader("test-key"), "");
+ mc.addHeader("test-key", "first");
+ test.equal(mc._getHeader("test-key"), "first");
+ mc.addHeader("test-key", "second");
+ test.deepEqual(mc._getHeader("test-key"), ["first", "second"]);
+ test.done();
+ },
+
+ "Uppercase header keys": function(test){
+ var mc = new MailComposer();
+
+ mc.addHeader("X-TEST", "first");
+ test.equal(mc._headers["X-TEST"], "first");
+
+ mc.addHeader("TEST", "second");
+ test.equal(mc._headers["Test"], "second");
+
+ test.done();
+ },
+
+ "Set object header": function(test){
+ var mc = new MailComposer();
+
+ var testObj = {
+ stringValue: "String with unicode symbols: ÕÄÖÜŽŠ",
+ arrayValue: ["hello ÕÄÖÜ", 12345],
+ objectValue: {
+ customerId: "12345"
+ }
+ };
+
+ mc.addHeader("x-mytest-string", "first");
+ mc.addHeader("x-mytest-json", testObj);
+
+ mc.streamMessage();
+
+ //mc.on("data", function(c){console.log(c.toString("utf-8"))})
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.headers['x-mytest-string'], "first");
+ test.deepEqual(JSON.parse(mail.headers['x-mytest-json']), testObj);
+ //console.log(mail)
+ test.done();
+ });
+ },
+
+ "Add message option": function(test){
+ var mc = new MailComposer();
+ test.equal(typeof mc._message.subject, "undefined");
+
+ mc.setMessageOption({
+ subject: "Test1",
+ body: "Test2",
+ nonexistent: "Test3"
+ });
+
+ test.equal(mc._message.subject, "Test1");
+ test.equal(mc._message.body, "Test2");
+ test.equal(typeof mc._message.nonexistent, "undefined");
+
+ mc.setMessageOption({
+ subject: "Test4"
+ });
+
+ test.equal(mc._message.subject, "Test4");
+ test.equal(mc._message.body, "Test2");
+
+ test.done();
+ },
+
+ "Detect mime type": function(test){
+ var mc = new MailComposer();
+
+ test.equal(mc._getMimeType("test.txt"), "text/plain");
+ test.equal(mc._getMimeType("test.unknown"), "application/octet-stream");
+
+ test.done();
+ },
+
+ "keepBcc off": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({bcc: "andris@node.ee"});
+ mc._buildMessageHeaders();
+ test.ok(!mc._getHeader("Bcc"));
+ test.done();
+ },
+
+ "keepBcc on": function(test){
+ var mc = new MailComposer({keepBcc: true});
+ mc.setMessageOption({bcc: "andris@node.ee"});
+ mc._buildMessageHeaders();
+ test.equal(mc._getHeader("Bcc"), "andris@node.ee");
+ test.done();
+ }
+};
+
+
+exports["Text encodings"] = {
+ "Punycode": function(test){
+ test.equal(toPunycode("andris@age.ee"), "andris@age.ee");
+ test.equal(toPunycode("andris@äge.ee"), "andris@xn--ge-uia.ee");
+ test.done();
+ },
+
+ "Mime words": function(test){
+ var mc = new MailComposer();
+ test.equal(mc._encodeMimeWord("Tere"), "Tere");
+ test.equal(mc._encodeMimeWord("Tere","Q"), "Tere");
+ test.equal(mc._encodeMimeWord("Tere","B"), "Tere");
+
+ // simple
+ test.equal(mc._encodeMimeWord("äss"), "=?UTF-8?Q?=C3=A4ss?=");
+ test.equal(mc._encodeMimeWord("äss","B"), "=?UTF-8?B?"+(new Buffer("äss","utf-8").toString("base64"))+"?=");
+
+ //multiliple
+ test.equal(mc._encodeMimeWord("äss tekst on see siin või kuidas?","Q", 20), "=?UTF-8?Q?=C3=A4ss?= =?UTF-8?Q?_tekst_o?= =?UTF-8?Q?n_see_si?= =?UTF-8?Q?in_v?= =?UTF-8?Q?=C3=B5i_?= =?UTF-8?Q?kuidas?= =?UTF-8?Q?=3F?=");
+
+ test.done();
+ },
+
+ "Addresses": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ sender: '"Jaanuar Veebruar, Märts" <märts@märts.eu>'
+ });
+
+ test.equal(mc._message.from, "\"=?UTF-8?Q?Jaanuar_Veebruar,_M=C3=A4rts?=\" <=?UTF-8?Q?m=C3=A4rts?=@xn--mrts-loa.eu>");
+
+ mc.setMessageOption({
+ sender: 'aavik <aavik@node.ee>'
+ });
+
+ test.equal(mc._message.from, '"aavik" <aavik@node.ee>');
+
+ mc.setMessageOption({
+ sender: '<aavik@node.ee>'
+ });
+
+ test.equal(mc._message.from, 'aavik@node.ee');
+
+ mc.setMessageOption({
+ sender: '<aavik@märts.eu>'
+ });
+
+ test.equal(mc._message.from, 'aavik@xn--mrts-loa.eu');
+
+ // multiple
+
+ mc.setMessageOption({
+ sender: '<aavik@märts.eu>, juulius@node.ee, "Node, Master" <node@node.ee>'
+ });
+
+ test.equal(mc._message.from, 'aavik@xn--mrts-loa.eu, juulius@node.ee, "Node, Master" <node@node.ee>');
+
+ test.done();
+ },
+
+ "Invalid subject": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ subject: "tere\ntere!"
+ });
+
+ test.equal(mc._message.subject, "tere tere!");
+ test.done();
+ },
+
+ "Long header line": function(test){
+ var mc = new MailComposer();
+
+ mc._headers = {
+ From: "a very log line, \"=?UTF-8?Q?Jaanuar_Veebruar,_M=C3=A4rts?=\" <=?UTF-8?Q?m=C3=A4rts?=@xn--mrts-loa.eu>"
+ };
+
+ mc.on("data", function(chunk){
+ test.ok(chunk.toString().trim().match(/From\:\s[^\r\n]+\r\n\s+[^\r\n]+/));
+ test.done();
+ });
+ mc._composeHeader();
+
+ }
+
+};
+
+exports["Mail related"] = {
+ "Envelope": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ sender: '"Jaanuar Veebruar, Märts" <märts@märts.eu>',
+ to: '<aavik@märts.eu>, juulius@node.ee',
+ cc: '"Node, Master" <node@node.ee>'
+ });
+
+ test.deepEqual(mc._envelope, {from:[ 'märts@xn--mrts-loa.eu' ],to:[ 'aavik@xn--mrts-loa.eu', 'juulius@node.ee'], cc:['node@node.ee' ]});
+ test.done();
+ },
+
+ "User defined envelope": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ sender: '"Jaanuar Veebruar, Märts" <märts@märts.eu>',
+ envelope: {
+ from: "Andris <andris@tr.ee>",
+ to: ["Andris <andris@tr.ee>, Node <andris@node.ee>", "aavik@märts.eu", "juulius@gmail.com"],
+ cc: "trips@node.ee"
+ },
+ to: '<aavik@märts.eu>, juulius@node.ee',
+ cc: '"Node, Master" <node@node.ee>'
+ });
+
+ test.deepEqual(mc._envelope, {userDefined: true, from:[ 'andris@tr.ee' ],to:[ 'andris@tr.ee', 'andris@node.ee', 'aavik@xn--mrts-loa.eu', 'juulius@gmail.com'], "cc":['trips@node.ee']});
+ test.done();
+ },
+
+ "Add attachment": function(test){
+ var mc = new MailComposer();
+ mc.addAttachment();
+ test.equal(mc._attachments.length, 0);
+
+ mc.addAttachment({filePath:"/tmp/var.txt"});
+ test.equal(mc._attachments[0].contentType, "text/plain");
+ test.equal(mc._attachments[0].fileName, "var.txt");
+
+ mc.addAttachment({contents:"/tmp/var.txt"});
+ test.equal(mc._attachments[1].contentType, "application/octet-stream");
+ test.equal(mc._attachments[1].fileName, undefined);
+
+ mc.addAttachment({filePath:"/tmp/var.txt", fileName:"test.txt"});
+ test.equal(mc._attachments[2].fileName, "test.txt");
+
+ test.done();
+ },
+
+ "Default attachment disposition": function(test){
+ var mc = new MailComposer();
+ mc.addAttachment();
+ test.equal(mc._attachments.length, 0);
+
+ mc.addAttachment({filePath:"/tmp/var.txt"});
+ test.equal(mc._attachments[0].contentDisposition, undefined);
+
+ test.done();
+ },
+
+ "Set attachment disposition": function(test){
+ var mc = new MailComposer();
+ mc.addAttachment();
+ test.equal(mc._attachments.length, 0);
+
+ mc.addAttachment({filePath:"/tmp/var.txt", contentDisposition: "inline"});
+ test.equal(mc._attachments[0].contentDisposition, "inline");
+
+ test.done();
+ },
+
+ "Generate envelope": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ sender: '"Jaanuar Veebruar, Märts" <märts@märts.eu>, karu@ahven.ee',
+ to: '<aavik@märts.eu>, juulius@node.ee',
+ cc: '"Node, Master" <node@node.ee>'
+ });
+
+ test.deepEqual(mc.getEnvelope(), {from: 'märts@xn--mrts-loa.eu',to:[ 'aavik@xn--mrts-loa.eu', 'juulius@node.ee', 'node@node.ee' ], stamp: 'Postage paid, Par Avion'});
+ test.done();
+ },
+
+ "Generate user defined envelope": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ sender: '"Jaanuar Veebruar, Märts" <märts@märts.eu>, karu@ahven.ee',
+ to: '<aavik@märts.eu>, juulius@node.ee',
+ envelope: {
+ from: "Andris <andris@tr.ee>",
+ to: ["Andris <andris@tr.ee>, Node <andris@node.ee>", "aavik@märts.eu", "juulius@gmail.com"],
+ cc: "trips@node.ee"
+ },
+ cc: '"Node, Master" <node@node.ee>'
+ });
+
+ test.deepEqual(mc.getEnvelope(), {from: 'andris@tr.ee', to:[ 'andris@tr.ee', 'andris@node.ee', 'aavik@xn--mrts-loa.eu', 'juulius@gmail.com', 'trips@node.ee'], stamp: 'Postage paid, Par Avion'});
+ test.done();
+ },
+
+ "Generate Headers": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ sender: '"Jaanuar Veebruar, Märts" <märts@märts.eu>, karu@ahven.ee',
+ to: '<aavik@märts.eu>, juulius@node.ee',
+ cc: '"Node, Master" <node@node.ee>',
+ replyTo: 'julla@pulla.ee',
+ subject: "Tere õkva!"
+ });
+
+ mc.on("data", function(chunk){
+ chunk = (chunk || "").toString("utf-8");
+ test.ok(chunk.match(/^(?:(?:[\s]+|[a-zA-Z0-0\-]+\:)[^\r\n]+\r\n)+\r\n$/));
+ test.done();
+ });
+
+ mc._composeHeader();
+ }
+};
+
+exports["Mime tree"] = {
+ "No contents": function(test){
+ test.expect(4);
+
+ var mc = new MailComposer();
+ mc._composeMessage();
+
+ test.ok(!mc._message.tree.boundary);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "text/plain");
+ test.equal(mc._message.tree.childNodes.length, 0);
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "\r\n");
+ }
+ }
+
+ test.done();
+ },
+ "Text contents": function(test){
+ test.expect(4);
+
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ body: "test"
+ });
+ mc._composeMessage();
+
+ test.ok(!mc._message.tree.boundary);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "text/plain");
+ test.equal(mc._message.tree.childNodes.length, 0);
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "test");
+ }
+ }
+
+ test.done();
+ },
+ "HTML contents": function(test){
+ test.expect(4);
+
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b>test</b>"
+ });
+ mc._composeMessage();
+
+ test.ok(!mc._message.tree.boundary);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "text/html");
+ test.equal(mc._message.tree.childNodes.length, 0);
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "<b>test</b>");
+ }
+ }
+
+ test.done();
+ },
+ "HTML and text contents": function(test){
+ test.expect(5);
+
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ body: "test",
+ html: "test"
+ });
+ mc._composeMessage();
+
+ test.equal(mc._message.tree.childNodes.length, 2);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "multipart/alternative");
+ test.ok(mc._message.tree.boundary);
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "test");
+ }
+ }
+
+ test.done();
+ },
+ "Attachment": function(test){
+ test.expect(5);
+
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({contents:"\r\n"});
+ mc._composeMessage();
+
+ test.equal(mc._message.tree.childNodes.length, 2);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "multipart/mixed");
+ test.ok(mc._message.tree.boundary);
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "\r\n");
+ }
+ }
+
+ test.done();
+ },
+ "Several attachments": function(test){
+ test.expect(6);
+
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({contents:"\r\n"});
+ mc.addAttachment({contents:"\r\n"});
+
+ mc._composeMessage();
+
+ test.equal(mc._message.tree.childNodes.length, 3);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "multipart/mixed");
+ test.ok(mc._message.tree.boundary);
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "\r\n");
+ }
+ }
+
+ test.done();
+ },
+ "Attachment and text": function(test){
+ test.expect(7);
+
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({contents:"test"});
+ mc.setMessageOption({
+ body: "test"
+ });
+ mc._composeMessage();
+
+ test.equal(mc._message.tree.childNodes.length, 2);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "multipart/mixed");
+ test.ok(mc._message.tree.boundary);
+
+ mc._message.tree.childNodes[0].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "text/plain");
+ }
+ });
+
+ mc._message.tree.childNodes[1].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "application/octet-stream");
+ }
+ });
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "test");
+ }
+ }
+
+ test.done();
+ },
+ "Attachment and html": function(test){
+ test.expect(7);
+
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({contents:"test"});
+ mc.setMessageOption({
+ html: "test"
+ });
+ mc._composeMessage();
+
+ test.equal(mc._message.tree.childNodes.length, 2);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "multipart/mixed");
+ test.ok(mc._message.tree.boundary);
+
+ mc._message.tree.childNodes[0].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "text/html");
+ }
+ });
+
+ mc._message.tree.childNodes[1].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "application/octet-stream");
+ }
+ });
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "test");
+ }
+ }
+
+ test.done();
+ },
+ "Attachment, html and text": function(test){
+ test.expect(11);
+
+ var mc = new MailComposer();
+ mc.addAttachment({contents:"test"});
+ mc.setMessageOption({
+ body: "test",
+ html: "test"
+ });
+ mc._composeMessage();
+
+ test.equal(mc._message.tree.childNodes.length, 2);
+ test.equal(mc._getHeader("Content-Type").split(";").shift().trim(), "multipart/mixed");
+ test.ok(mc._message.tree.boundary);
+
+ mc._message.tree.childNodes[0].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "multipart/alternative");
+ }
+ });
+
+ test.ok(mc._message.tree.childNodes[0].boundary);
+
+ mc._message.tree.childNodes[0].childNodes[0].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "text/plain");
+ }
+ });
+
+ mc._message.tree.childNodes[0].childNodes[1].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "text/html");
+ }
+ });
+
+ mc._message.tree.childNodes[1].headers.forEach(function(header){
+ if(header[0]=="Content-Type"){
+ test.equal(header[1].split(";").shift().trim(), "application/octet-stream");
+ }
+ });
+
+ for(var i=0, len = mc._message.flatTree.length; i<len; i++){
+ if(typeof mc._message.flatTree[i] == "object"){
+ test.equal(mc._message.flatTree[i].contents, "test");
+ }
+ }
+
+ test.done();
+ }
+
+};
+
+exports["Stream parser"] = {
+ "Text": function(test){
+ var mc = new MailComposer(),
+ file = fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8");
+ mc.setMessageOption({
+ from: "andris@node.ee",
+ to:"andris@tr.ee, andris@kreata.ee",
+ subject: "õäöü",
+ body: file
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.from[0].address, "andris@node.ee");
+ test.equal(mail.to[0].address, "andris@tr.ee");
+ test.equal(mail.to[1].address, "andris@kreata.ee");
+ test.equal(mail.subject, "õäöü");
+ test.equal(mail.text.trim(), file.trim());
+ test.done();
+ });
+ },
+ "HTML": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b>test</b>"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.html.trim(), "<b>test</b>");
+ test.done();
+ });
+ },
+ "HTML and text": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b>test</b>",
+ body: "test"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.text.trim(), "test");
+ test.equal(mail.html.trim(), "<b>test</b>");
+ test.done();
+ });
+ },
+ "Flowed text": function(test){
+ var mc = new MailComposer({encoding:"8bit"}),
+ file = fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8");
+
+ mc.setMessageOption({
+ body: file
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.text.trim(), file.trim());
+ test.done();
+ });
+ },
+ "Attachment as string": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "Attachment as buffer": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ contents: fs.readFileSync(__dirname+"/textfile.txt")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "Attachment file stream": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ filePath: __dirname+"/textfile.txt"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "Attachment source stream": function(test){
+ var mc = new MailComposer();
+
+ var fileStream = fs.createReadStream(__dirname+"/textfile.txt");
+
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ streamSource: fileStream
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "Attachment source url": function(test){
+
+ var server = http.createServer(function (req, res) {
+ if(req.url=="/textfile.txt"){
+ fs.createReadStream(__dirname+"/textfile.txt")
+ fs.createReadStream(__dirname+"/textfile.txt").pipe(res);
+ }else{
+ res.writeHead(404, {'Content-Type': 'text/plain'});
+ res.end('Not found!\n');
+ }
+ });
+ server.listen(HTTP_PORT, '127.0.0.1');
+
+ var mc = new MailComposer();
+
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ filePath: "http://localhost:"+HTTP_PORT+"/textfile.txt"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ server.close();
+ test.done();
+ });
+ },
+ "Attachment source invalid url": function(test){
+
+ var server = http.createServer(function (req, res) {
+ res.writeHead(404, {'Content-Type': 'text/plain'});
+ res.end('Not found!\n');
+ })
+ server.listen(HTTP_PORT, '127.0.0.1');
+
+ var mc = new MailComposer();
+
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ filePath: "http://localhost:"+HTTP_PORT+"/textfile.txt"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.attachments[0].checksum, "3995d423c7453e472ce0d54e475bae3e");
+ server.close();
+ test.done();
+ });
+ },
+ "Custom User-Agent": function(test){
+
+ var server = http.createServer(function (req, res) {
+ test.equal(req.headers['user-agent'], "test");
+
+ res.writeHead(200, {'Content-Type': 'text/plain'});
+ res.end('OK!\n');
+ })
+ server.listen(HTTP_PORT, '127.0.0.1');
+
+ var mc = new MailComposer();
+
+ mc.setMessageOption();
+ mc.addAttachment({
+ fileName: "file.txt",
+ filePath: "http://localhost:"+HTTP_PORT+"/textfile.txt",
+ userAgent: "test"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ server.close();
+ test.done();
+ });
+ },
+ "escape SMTP": function(test){
+ var mc = new MailComposer({escapeSMTP: true});
+ mc.setMessageOption({
+ body: ".\r\n."
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.text.trim(), "..\n..");
+ test.done();
+ });
+ },
+ "don't escape SMTP": function(test){
+ var mc = new MailComposer({escapeSMTP: false});
+ mc.setMessageOption({
+ body: ".\r\n."
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.text.trim(), ".\n.");
+ test.done();
+ });
+ },
+ "HTML and text and attachment": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b>test</b>",
+ body: "test"
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.text.trim(), "test");
+ test.equal(mail.html.trim(), "<b>test</b>");
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "HTML and related attachment": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b><img src=\"cid:test@node\"/></b>"
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ cid: "test@node",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+ /*
+ var d = "";
+ mc.on("data", function(data){
+ d += data.toString();
+ })
+
+ mc.on("end", function(){
+ console.log(d);
+ });
+ */
+
+ mp.on("end", function(mail){
+ test.equal(mc._attachments.length, 0);
+ test.equal(mc._relatedAttachments.length, 1);
+ test.equal(mail.html.trim(), "<b><img src=\"cid:test@node\"/></b>");
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "HTML and related plus regular attachment": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b><img src=\"cid:test@node\"/></b>"
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ cid: "test@node",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mc._attachments.length, 1);
+ test.equal(mc._relatedAttachments.length, 1);
+ test.equal(mail.html.trim(), "<b><img src=\"cid:test@node\"/></b>");
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.equal(mail.attachments[1].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "HTML and text related attachment": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b><img src=\"cid:test@node\"/></b>",
+ text:"test"
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ cid: "test@node",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mc._attachments.length, 0);
+ test.equal(mc._relatedAttachments.length, 1);
+ test.equal(mail.text.trim(), "test");
+ test.equal(mail.html.trim(), "<b><img src=\"cid:test@node\"/></b>");
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "HTML, text, related+regular attachment": function(test){
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ html: "<b><img src=\"cid:test@node\"/></b>",
+ text:"test"
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ cid: "test@node",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.addAttachment({
+ fileName: "file.txt",
+ contents: fs.readFileSync(__dirname+"/textfile.txt").toString("utf-8")
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mc._attachments.length, 1);
+ test.equal(mc._relatedAttachments.length, 1);
+ test.equal(mail.text.trim(), "test");
+ test.equal(mail.html.trim(), "<b><img src=\"cid:test@node\"/></b>");
+ test.equal(mail.attachments[0].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.equal(mail.attachments[1].checksum, "59fbcbcaf18cb9232f7da6663f374eb9");
+ test.done();
+ });
+ },
+ "References Header": function(test){
+
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ references: ["myrdo", "vyrdo"]
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.deepEqual(mail.references, ["myrdo", "vyrdo"]);
+ test.done();
+ });
+ },
+ "InReplyTo Header": function(test){
+
+ var mc = new MailComposer();
+ mc.setMessageOption({
+ inReplyTo: "test"
+ });
+ mc.streamMessage();
+
+ var mp = new MailParser();
+
+ mc.pipe(mp);
+
+ mp.on("end", function(mail){
+ test.equal(mail.inReplyTo, "test");
+ test.done();
+ });
+ }
+};
+
+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();
+ });
+ }
+}
+
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_private.pem b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_private.pem
new file mode 100644
index 0000000..9d03266
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_private.pem
@@ -0,0 +1,12 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIBywIBAAJhANCx7ncKUfQ8wBUYmMqq6ky8rBB0NL8knBf3+uA7q/CSxpX6sQ8N
+dFNtEeEd7gu7BWEM7+PkO1P0M78eZOvVmput8BP9R44ARpgHY4V0qSCdUt4rD32n
+wfjlGbh8p5ua5wIDAQABAmAm+uUQpQPTu7kg95wqVqw2sxLsa9giT6M8MtxQH7Uo
+1TF0eAO0TQ4KOxgY1S9OT5sGPVKnag258m3qX7o5imawcuyStb68DQgAUg6xv7Af
+AqAEDfYN5HW6xK+X81jfOUECMQDr7XAS4PERATvgb1B3vRu5UEbuXcenHDYgdoyT
+3qJFViTbep4qeaflF0uF9eFveMcCMQDic10rJ8fopGD7/a45O4VJb0+lRXVdqZxJ
+QzAp+zVKWqDqPfX7L93SQLzOGhdd7OECMQDeQyD7WBkjSQNMy/GF7I1qxrscIxNN
+VqGTcbu8Lti285Hjhx/sqhHHHGwU9vB7oM8CMQDKTS3Kw/s/xrot5O+kiZwFgr+w
+cmDrj/7jJHb+ykFNb7GaEkiSYqzUjKkfpweBDYECMFJUyzuuFJAjq3BXmGJlyykQ
+TweUw+zMVdSXjO+FCPcYNi6CP1t1KoESzGKBVoqA/g==
+-----END RSA PRIVATE KEY-----
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_public.pem b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_public.pem
new file mode 100644
index 0000000..2214c80
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/test_public.pem
@@ -0,0 +1,5 @@
+-----BEGIN PUBLIC KEY-----
+MHwwDQYJKoZIhvcNAQEBBQADawAwaAJhANCx7ncKUfQ8wBUYmMqq6ky8rBB0NL8k
+nBf3+uA7q/CSxpX6sQ8NdFNtEeEd7gu7BWEM7+PkO1P0M78eZOvVmput8BP9R44A
+RpgHY4V0qSCdUt4rD32nwfjlGbh8p5ua5wIDAQAB
+-----END PUBLIC KEY-----
diff --git a/tools/node_modules/nodemailer/node_modules/mailcomposer/test/textfile.txt b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/textfile.txt
new file mode 100644
index 0000000..3628c58
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/mailcomposer/test/textfile.txt
@@ -0,0 +1,601 @@
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+Some unicode symbols ÕÄÖÜ ↑, →, ↓, ↔, ↕, ↖, ↗, ↘, ↙, ↚, ↛, ↜, ↝, ↞, ↟, ↠, ↡, ↢, ↣, ↤, ↥, ↦
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."
+
+"Because I am scorched all over, Captain Ahab," answered Perth, resting for a moment on his hammer; "I am past scorching; not easily can'st thou scorch a scar."
+
+"Well, well; no more. Thy shrunk voice sounds too calmly, sanely woeful to me. In no Paradise myself, I am impatient of all misery in others that is not mad. Thou should'st go mad, blacksmith; say, why dost thou not go mad? How can'st thou endure without being mad? Do the heavens yet hate thee, that thou can'st not go mad?—What wert thou making there?"
+
+"Welding an old pike-head, sir; there were seams and dents in it."
+
+"And can'st thou make it all smooth again, blacksmith, after such hard usage as it had?"
+
+"I think so, sir."
+
+"And I suppose thou can'st smoothe almost any seams and dents; never mind how hard the metal, blacksmith?"
+
+"Aye, sir, I think I can; all seams and dents but one."
+
+"Look ye here, then," cried Ahab, passionately advancing, and leaning with both hands on Perth's shoulders; "look ye here—HERE—can ye smoothe out a seam like this, blacksmith," sweeping one hand across his ribbed brow; "if thou could'st, blacksmith, glad enough would I lay my head upon thy anvil, and feel thy heaviest hammer between my eyes. Answer! Can'st thou smoothe this seam?"
+
+"Oh! that is the one, sir! Said I not all seams and dents but one?"
+
+"Aye, blacksmith, it is the one; aye, man, it is unsmoothable; for though thou only see'st it here in my flesh, it has worked down into the bone of my skull—THAT is all wrinkles! But, away with child's play; no more gaffs and pikes to-day. Look ye here!" jingling the leathern bag, as if it were full of gold coins. "I, too, want a harpoon made; one that a thousand yoke of fiends could not part, Perth; something that will stick in a whale like his own fin-bone. There's the stuff," flinging the pouch upon the anvil. "Look ye, blacksmith, these are the gathered nail-stubbs of the steel shoes of racing horses."