index.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. 'use strict';
  2. const packageData = require('../../package.json');
  3. const shared = require('../shared');
  4. const LeWindows = require('../sendmail-transport/le-windows');
  5. const LeUnix = require('../sendmail-transport/le-unix');
  6. /**
  7. * Generates a Transport object for streaming
  8. *
  9. * Possible options can be the following:
  10. *
  11. * * **buffer** if true, then returns the message as a Buffer object instead of a stream
  12. * * **newline** either 'windows' or 'unix'
  13. *
  14. * @constructor
  15. * @param {Object} optional config parameter
  16. */
  17. class StreamTransport {
  18. constructor(options) {
  19. options = options || {};
  20. this.options = options || {};
  21. this.name = 'StreamTransport';
  22. this.version = packageData.version;
  23. this.logger = shared.getLogger(this.options, {
  24. component: this.options.component || 'stream-transport'
  25. });
  26. this.winbreak = ['win', 'windows', 'dos', '\r\n'].includes((options.newline || '').toString().toLowerCase());
  27. }
  28. /**
  29. * Compiles a mailcomposer message and forwards it to handler that sends it
  30. *
  31. * @param {Object} emailMessage MailComposer object
  32. * @param {Function} callback Callback function to run when the sending is completed
  33. */
  34. send(mail, done) {
  35. // We probably need this in the output
  36. mail.message.keepBcc = true;
  37. let envelope = mail.data.envelope || mail.message.getEnvelope();
  38. let messageId = mail.message.messageId();
  39. let recipients = [].concat(envelope.to || []);
  40. if (recipients.length > 3) {
  41. recipients.push('...and ' + recipients.splice(2).length + ' more');
  42. }
  43. this.logger.info(
  44. {
  45. tnx: 'send',
  46. messageId
  47. },
  48. 'Sending message %s to <%s> using %s line breaks',
  49. messageId,
  50. recipients.join(', '),
  51. this.winbreak ? '<CR><LF>' : '<LF>'
  52. );
  53. setImmediate(() => {
  54. let sourceStream;
  55. let stream;
  56. let transform;
  57. try {
  58. transform = this.winbreak ? new LeWindows() : new LeUnix();
  59. sourceStream = mail.message.createReadStream();
  60. stream = sourceStream.pipe(transform);
  61. sourceStream.on('error', err => stream.emit('error', err));
  62. } catch (E) {
  63. this.logger.error(
  64. {
  65. err: E,
  66. tnx: 'send',
  67. messageId
  68. },
  69. 'Creating send stream failed for %s. %s',
  70. messageId,
  71. E.message
  72. );
  73. return done(E);
  74. }
  75. if (!this.options.buffer) {
  76. stream.once('error', err => {
  77. this.logger.error(
  78. {
  79. err,
  80. tnx: 'send',
  81. messageId
  82. },
  83. 'Failed creating message for %s. %s',
  84. messageId,
  85. err.message
  86. );
  87. });
  88. return done(null, {
  89. envelope: mail.data.envelope || mail.message.getEnvelope(),
  90. messageId,
  91. message: stream
  92. });
  93. }
  94. let chunks = [];
  95. let chunklen = 0;
  96. stream.on('readable', () => {
  97. let chunk;
  98. while ((chunk = stream.read()) !== null) {
  99. chunks.push(chunk);
  100. chunklen += chunk.length;
  101. }
  102. });
  103. stream.once('error', err => {
  104. this.logger.error(
  105. {
  106. err,
  107. tnx: 'send',
  108. messageId
  109. },
  110. 'Failed creating message for %s. %s',
  111. messageId,
  112. err.message
  113. );
  114. return done(err);
  115. });
  116. stream.on('end', () =>
  117. done(null, {
  118. envelope: mail.data.envelope || mail.message.getEnvelope(),
  119. messageId,
  120. message: Buffer.concat(chunks, chunklen)
  121. })
  122. );
  123. });
  124. }
  125. }
  126. module.exports = StreamTransport;