nodemailer.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. 'use strict';
  2. const Mailer = require('./mailer');
  3. const shared = require('./shared');
  4. const SMTPPool = require('./smtp-pool');
  5. const SMTPTransport = require('./smtp-transport');
  6. const SendmailTransport = require('./sendmail-transport');
  7. const StreamTransport = require('./stream-transport');
  8. const JSONTransport = require('./json-transport');
  9. const SESTransport = require('./ses-transport');
  10. const fetch = require('./fetch');
  11. const packageData = require('../package.json');
  12. const ETHEREAL_API = (process.env.ETHEREAL_API || 'https://api.nodemailer.com').replace(/\/+$/, '');
  13. const ETHEREAL_WEB = (process.env.ETHEREAL_WEB || 'https://ethereal.email').replace(/\/+$/, '');
  14. const ETHEREAL_CACHE = ['true', 'yes', 'y', '1'].includes(
  15. (process.env.ETHEREAL_CACHE || 'yes')
  16. .toString()
  17. .trim()
  18. .toLowerCase()
  19. );
  20. let testAccount = false;
  21. module.exports.createTransport = function(transporter, defaults) {
  22. let urlConfig;
  23. let options;
  24. let mailer;
  25. if (
  26. // provided transporter is a configuration object, not transporter plugin
  27. (typeof transporter === 'object' && typeof transporter.send !== 'function') ||
  28. // provided transporter looks like a connection url
  29. (typeof transporter === 'string' && /^(smtps?|direct):/i.test(transporter))
  30. ) {
  31. if ((urlConfig = typeof transporter === 'string' ? transporter : transporter.url)) {
  32. // parse a configuration URL into configuration options
  33. options = shared.parseConnectionUrl(urlConfig);
  34. } else {
  35. options = transporter;
  36. }
  37. if (options.pool) {
  38. transporter = new SMTPPool(options);
  39. } else if (options.sendmail) {
  40. transporter = new SendmailTransport(options);
  41. } else if (options.streamTransport) {
  42. transporter = new StreamTransport(options);
  43. } else if (options.jsonTransport) {
  44. transporter = new JSONTransport(options);
  45. } else if (options.SES) {
  46. transporter = new SESTransport(options);
  47. } else {
  48. transporter = new SMTPTransport(options);
  49. }
  50. }
  51. mailer = new Mailer(transporter, options, defaults);
  52. return mailer;
  53. };
  54. module.exports.createTestAccount = function(apiUrl, callback) {
  55. let promise;
  56. if (!callback && typeof apiUrl === 'function') {
  57. callback = apiUrl;
  58. apiUrl = false;
  59. }
  60. if (!callback) {
  61. promise = new Promise((resolve, reject) => {
  62. callback = shared.callbackPromise(resolve, reject);
  63. });
  64. }
  65. if (ETHEREAL_CACHE && testAccount) {
  66. setImmediate(() => callback(null, testAccount));
  67. return promise;
  68. }
  69. apiUrl = apiUrl || ETHEREAL_API;
  70. let chunks = [];
  71. let chunklen = 0;
  72. let req = fetch(apiUrl + '/user', {
  73. contentType: 'application/json',
  74. method: 'POST',
  75. body: Buffer.from(
  76. JSON.stringify({
  77. requestor: packageData.name,
  78. version: packageData.version
  79. })
  80. )
  81. });
  82. req.on('readable', () => {
  83. let chunk;
  84. while ((chunk = req.read()) !== null) {
  85. chunks.push(chunk);
  86. chunklen += chunk.length;
  87. }
  88. });
  89. req.once('error', err => callback(err));
  90. req.once('end', () => {
  91. let res = Buffer.concat(chunks, chunklen);
  92. let data;
  93. let err;
  94. try {
  95. data = JSON.parse(res.toString());
  96. } catch (E) {
  97. err = E;
  98. }
  99. if (err) {
  100. return callback(err);
  101. }
  102. if (data.status !== 'success' || data.error) {
  103. return callback(new Error(data.error || 'Request failed'));
  104. }
  105. delete data.status;
  106. testAccount = data;
  107. callback(null, testAccount);
  108. });
  109. return promise;
  110. };
  111. module.exports.getTestMessageUrl = function(info) {
  112. if (!info || !info.response) {
  113. return false;
  114. }
  115. let infoProps = new Map();
  116. info.response.replace(/\[([^\]]+)\]$/, (m, props) => {
  117. props.replace(/\b([A-Z0-9]+)=([^\s]+)/g, (m, key, value) => {
  118. infoProps.set(key, value);
  119. });
  120. });
  121. if (infoProps.has('STATUS') && infoProps.has('MSGID')) {
  122. return (testAccount.web || ETHEREAL_WEB) + '/message/' + infoProps.get('MSGID');
  123. }
  124. return false;
  125. };