1 var Transport = require("./transport").Transport,
  2     createTransport = require("./transport").createTransport,
  3     MailComposer = require("mailcomposer").MailComposer,
  4     helpers = require("./helpers");
  5 
  6 /*
  7  * Version constants
  8  */
  9 var X_MAILER_NAME = "Nodemailer",
 10     X_MAILER_VERSION = "0.3.3; +http://www.nodemailer.org";
 11 
 12 
 13 // Export createTransport method
 14 module.exports.createTransport = createTransport;
 15 
 16 // Export Transport constructor
 17 module.exports.Transport = Transport;
 18 
 19 // Export Nodemailer constructor
 20 module.exports.Nodemailer = Nodemailer;
 21 
 22 // Export sendMail function (and the alias send_mail for legacy)
 23 module.exports.sendMail = module.exports.send_mail = function(options, callback){
 24     var mailer = new Nodemailer(options);
 25     
 26     mailer.validateSettings(function(err){
 27         if(err){
 28             // report validation error back to the client
 29             return callback(err);
 30         }else{
 31             // try to send the e-mail message
 32             mailer.sendMail(callback);
 33         }
 34     });
 35     
 36     return mailer;
 37 };
 38 
 39 
 40 /**
 41  * <p>Generates a Nodemailer object which is the main 'hub' for managing the
 42  * send process</p>
 43  * 
 44  * @constructor
 45  * @param {Object} options Message options object, see README for the complete list of possible options
 46  */
 47 function Nodemailer(options){
 48     this.options = options || {};
 49     
 50     this.transport = this.options.transport;
 51     
 52     this.mailcomposer = new MailComposer();
 53     
 54     if(!this.transport){
 55         this.transport = this.getGlobalTransport();
 56     }
 57 }
 58 
 59 /**
 60  * <p>Add support for legacy transport settings by checking for global
 61  * variables SMTP, sendmail and SES</p>
 62  * 
 63  * @return {Object} {@link Transport} object
 64  */
 65 Nodemailer.prototype.getGlobalTransport = function(){
 66     if(this.options.SMTP){
 67         // cache the transport for SMTP as it is actually a connection pool
 68         if(!this.options.SMTP._smtp_transport){
 69             this.options.SMTP._smtp_transport = createTransport("SMTP", this.options.SMTP);
 70         }
 71         return this.options.SMTP._smtp_transport;
 72     }else if(this.options.sendmail){
 73         return createTransport("sendmail", this.options.sendmail);
 74     }else if(this.options.SES){
 75         return createTransport("SES", this.options.SES);
 76     }else if(module.exports.SMTP){
 77         // cache the transport for SMTP as it is actually a connection pool
 78         if(!module.exports._smtp_transport){
 79             module.exports._smtp_transport = createTransport("SMTP", module.exports.SMTP);
 80         }
 81         return module.exports._smtp_transport;
 82     }else if(module.exports.sendmail){
 83         return createTransport("sendmail", module.exports.sendmail);
 84     }else if(module.exports.SES){
 85         return createTransport("SES", module.exports.SES);
 86     }
 87     return false;
 88 };
 89 
 90 /**
 91  * <p>Doesn't do much currently, if the future should link to transport
 92  * validation methods. For example in case of SES should check that AWS
 93  * keys are set up etc.</p>
 94  * 
 95  * @param {Function} callback Callback function to run after validation
 96  */
 97 Nodemailer.prototype.validateSettings = function(callback){
 98     if(!this.transport || !this.transport.transport){
 99         return callback(new Error("No transport method defined"));
100     }
101     callback(null);
102 };
103 
104 /**
105  * <p>Send the e-mail message by using data from the original options object
106  * and selected transport</p>
107  * 
108  * @param {Function} callback Callback function to run when the e-mail has been sent (or it failed)
109  */
110 Nodemailer.prototype.sendMail = function(callback){
111     // compose the e-mail
112     this.generateMailObject();
113     // send the message using preselected transport method
114     this.transport.sendMail(this.mailcomposer, callback); 
115 };
116 
117 /**
118  * <p>Uses the data from the original options object to compose a mailcomposer
119  * e-mail message that can be later streamed to the selected transport</p>
120  */
121 Nodemailer.prototype.generateMailObject = function(){
122     
123     // set envelope data, subject etc.
124     this.setGeneralOptions();
125     
126     // set module defined headers (date, message-id, etc.)
127     this.setModuleHeaders();
128     
129     // set user defined headers (if any)
130     this.setUserHeaders();
131     
132     // set attachments (if any)
133     this.setAttachments();
134 };
135 
136 /**
137  * <p>Uses the general options (message sender and receiver, subject body, etc.)
138  * to set mailcomposer properties. Includes support for legacy properties.</p>
139  */
140 Nodemailer.prototype.setGeneralOptions = function(){
141 
142     // generate plaintext if only HTML exists and generateTextFromHTML is true
143     if(!(this.options.text || this.options.body) && (this.options.html) && 
144       this.options.generateTextFromHTML){
145         this.options.text = helpers.stripHTML(this.options.html);
146     }
147     
148     var acceptedFields = ["from", "sender", "to", "subject", "replyTo", "debug",
149                           "reply_to", "cc", "bcc", "body", "text", "html"],
150         mailOptions = {},
151         keys = Object.keys(this.options),
152         key;
153 
154     
155 
156     for(var i=0, len=keys.length; i<len; i++){
157         key = keys[i];
158         if(acceptedFields.indexOf(key) >=0 && this.options[key]){
159             mailOptions[key] = this.options[key];
160         }
161     }
162     
163     if(this.options.debug){
164         console.log(mailOptions);
165     }
166     
167     this.mailcomposer.setMessageOption(mailOptions);
168 };
169 
170 /**
171  * <p>If the 'headers' property was set on the options, add the values to the
172  * header of the e-mail message</p>
173  */
174 Nodemailer.prototype.setUserHeaders = function(){
175     if(typeof this.options.headers != "object"){
176         return;
177     }
178     var keys = Object.keys(this.options.headers),
179         key;
180 
181     for(var i=0, len=keys.length; i<len; i++){
182         key = keys[i];
183         if(this.options.headers[key]){
184             this.mailcomposer.addHeader(key, this.options.headers[key]);
185         }
186     }
187 };
188 
189 /**
190  * <p>Add some required headers to the message, such as Date: and Message-Id:</p>
191  */
192 Nodemailer.prototype.setModuleHeaders = function(){
193     
194     // Mailer name + version
195     this.mailcomposer.addHeader("X-Mailer", X_MAILER_NAME+
196         (X_MAILER_VERSION?" ("+X_MAILER_VERSION+")":""));
197     
198     // Date
199     this.mailcomposer.addHeader("Date", new Date().toUTCString());
200     
201     // Message ID
202     this.mailcomposer.addHeader("Message-Id", "<"+
203                 Date.now()+Math.random().toString(16).substr(1)+"@"+
204                 X_MAILER_NAME+">");
205 };
206 
207 /**
208  * <p>If attachment array is set on the options object, add these attachments
209  * to the mailcomposer object</p>
210  */
211 Nodemailer.prototype.setAttachments = function(){
212     if(!Array.isArray(this.options.attachments)){
213         return;
214     }
215     var attachment;
216     for(var i=0, len=this.options.attachments.length; i<len; i++){
217         attachment = this.options.attachments[i];
218         this.mailcomposer.addAttachment(attachment);
219     }
220 };