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