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.2; +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     var acceptedFields = ["from", "sender", "to", "subject", "replyTo", "debug",
142                           "reply_to", "cc", "bcc", "body", "text", "html"],
143         mailOptions = {},
144         keys = Object.keys(this.options),
145         key;
146 
147     for(var i=0, len=keys.length; i<len; i++){
148         key = keys[i];
149         if(acceptedFields.indexOf(key) >=0 && this.options[key]){
150             mailOptions[key] = this.options[key];
151         }
152     }
153     
154     if(this.options.debug){
155         console.log(mailOptions);
156     }
157     
158     this.mailcomposer.setMessageOption(mailOptions);
159 };
160 
161 /**
162  * <p>If the 'headers' property was set on the options, add the values to the
163  * header of the e-mail message</p>
164  */
165 Nodemailer.prototype.setUserHeaders = function(){
166     if(typeof this.options.headers != "object"){
167         return;
168     }
169     var keys = Object.keys(this.options.headers),
170         key;
171 
172     for(var i=0, len=keys.length; i<len; i++){
173         key = keys[i];
174         if(this.options.headers[key]){
175             this.mailcomposer.addHeader(key, this.options.headers[key]);
176         }
177     }
178 };
179 
180 /**
181  * <p>Add some required headers to the message, such as Date: and Message-Id:</p>
182  */
183 Nodemailer.prototype.setModuleHeaders = function(){
184     
185     // Mailer name + version
186     this.mailcomposer.addHeader("X-Mailer", X_MAILER_NAME+
187         (X_MAILER_VERSION?" ("+X_MAILER_VERSION+")":""));
188     
189     // Date
190     this.mailcomposer.addHeader("Date", new Date().toUTCString());
191     
192     // Message ID
193     this.mailcomposer.addHeader("Message-Id", "<"+
194                 Date.now()+Math.random().toString(16).substr(1)+"@"+
195                 X_MAILER_NAME+">");
196 };
197 
198 /**
199  * <p>If attachment array is set on the options object, add these attachments
200  * to the mailcomposer object</p>
201  */
202 Nodemailer.prototype.setAttachments = function(){
203     if(!Array.isArray(this.options.attachments)){
204         return;
205     }
206     var attachment;
207     for(var i=0, len=this.options.attachments.length; i<len; i++){
208         attachment = this.options.attachments[i];
209         this.mailcomposer.addAttachment(attachment);
210     }
211 };