Embedded image doesn't show up in email body - node.js

I'm using nodemailer to send email with embedded images, but the images doesn't show up in message body in some email client app (eg thunderbird).
I'm suspecting this happened because of Content-Transfer-Encoding is set to quoted-printable. So it add 3D characters (which is encoding for = character) in src property of img element:
<img src=3D"cid:61767c3c-7f99-4f0f-a15b-b5edc0f0c2c4#emailaddress.com">
How to turn off quoted-printable encoding permanently in nodemailer ? I've tried to set textEncoding: 'base64' in message options, but it seems nodemailer ignore it.
let message = {
from: {
name: 'Someone',
address: 'someone#emailaddress.com'
},
to: {
name: sender,
address: emailTo.toLowerCase()
},
subject: 'Purchased Tickets',
html: 'Some text<br><img src="cid:61767c3c-7f99-4f0f-a15b-b5edc0f0c2c4#emailaddress.com"/><br>Some more text<br><img src="cid:1a419f12-1205-49e5-b0f7-fd407c0bfa27#emailaddress.com"/><br>',
attachments: ticketList.map((t, i) => ({
filename: `ticket${i + 1}.png`,
content: t.qrCode,
cid: `${t.ticketNumber}#emailaddress.com`
})),
encoding: 'base64',
textEncoding: 'base64'
}
transporter.sendMail(message, (err, info) => {
if (err) {
console.log(err)
} else {
console.log(info)
}
})

I have the same problem in some of my emails, but it does not appear to be related to Content-Transfert-Encoding.
Indeed, the images are displayed on some of my mails with
Content-Transfer-Encoding: quoted-printable
but not all...

Related

NodeJs Buffer is not attached to Nodemailer file

Currently on my project I am dealing with a situation and I feel it is a problem of syntax but I am not getting the right answer and kinda feeling stressed and tilt over it for the last three days.
I am using three libraries NodeJs, Html-pdf(phantomJS) and Nodemailer.
this is the block of code in question (everything is inside a major async function that is a resolver of graphQl)
await pdf.create(html, options).toBuffer(async (err, buffer) => {
//i = new ;
//console.log(typeof(buffer))
await buffer;
await transporter.sendMail({
from: '"Example" example#example.com', // sender address
to: `${a1.dataValues.email}`, // list of receivers
subject: `${text2}`, // Subject line
text: `${text}`, // plain text body
html: `<p> ${text} </p>`, // html body
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: buffer,
contentType: "application/pdf",
},
],
});
});
The email is sent but I get this in the browser
The attachment is sent but I guess it is "undefined" and I have been struggling with finding a way to fix this. Can someone please help me?
I tried as well to do
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: new buffer.from(buffer),
contentType: "application/pdf",
},
],
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: new buffer(buffer),
contentType: "application/pdf",
},
],
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: new buffer.from(buffer,'base64'),
contentType: "application/pdf",
},
],
but all of those crash the app in Azure. The all thing works perfectly locally, but not when deployed.

GMail API using POST request, error code: 400, recipient address required

I am trying to send an email using POST request with just Node standard modules in NodeJS v8.10 via GMail API.
The sample code is given below.
I am getting an error:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "invalidArgument",
"message": "Recipient address required"
}
],
"code": 400,
"message": "Recipient address required"
}
}
It says recipient address required but according to what I think, (I may be wrong), my base64url conversion is proper since I checked it in Google API Explorer but my problem is in passing the data, I am doing it properly as told in the guide i.e. inside body with 'raw' key but it still does not work and this is where my problem must be. Maybe I am missing something, maybe I do not know the proper structure.
Yes, there are multiple posts regarding this but none of them provided solution.
I referred the guide on https://developers.google.com/gmail/api/v1/reference/users/messages/send
but the given example is of with the use of client library.
I tried everything, passing the 'raw' with base64url encoded data into write function of the request, passing it as a data parameter in options, passing it through body parameters in options, everything I can think of.
Am I missing something? Where am I going wrong?
I am a newbie in nodejs so please explain and if possible, an example structure of solution would be most welcome.
Base64url produced is working fine, I guess. I copied the string produced by conversion and tried it at https://developers.google.com/gmail/api/v1/reference/users/messages/send?apix=true
It works fine and sends me the mail but it does not work on my code.
var email = (
"Content-Type: text/plain; charset=\"UTF-8\"\n" +
"Content-length: 5000\n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: message/rfc2822\n" +
"to: something#something.com\n" +
"from: \"Some Name\" <something#gmail.com>\n" +
"subject: Hello world\n\n" +
"The actual message text goes here"
);
async function sendMail(token,resp) {
return new Promise((resolve,reject) => {
var base64EncodedEmail = Buffer.from(email).toString('base64');
var base64urlEncodedEmail = base64EncodedEmail.replace(/\+/g, '-').replace(/\//g, '_');
var params = {
userId: 'me',
resource: {
'raw': base64urlEncodedEmail
}
};
var body2 = {
"raw": base64urlEncodedEmail,
}
var options = {
hostname: 'www.googleapis.com',
path:'/upload/gmail/v1/users/me/messages/send',
headers: {
'Authorization':'Bearer '+token,
'Content-Type':'message/rfc822',
},
body: {
"raw": base64urlEncodedEmail,
'resource': {
'raw': base64urlEncodedEmail,
}
},
data: JSON.stringify({
'raw': base64urlEncodedEmail,
'resource': {
'raw': base64urlEncodedEmail,
}
}),
message: {
'raw': base64urlEncodedEmail,
},
payload: {
"raw": base64urlEncodedEmail, //this is me trying everything I can think of
},
// body: raw,
// }
userId: 'me',
// resource: {
// 'raw': base64urlEncodedEmail
// },
method: 'POST',
};
var id='';
console.log(base64urlEncodedEmail);
const req = https.request(options, (res) => {
var body = '';
res.on('data', (d) => {
body += d;
});
res.on('end', () => {
var parsed = body;
console.log(parsed);
})
});
req.on('error', (e) => {
console.error(e);
});
req.write(JSON.stringify(body2));
req.end();
});
};
Thank you for your time and answers.
I found the solution.
It says everywhere to convert the rfc822 formatted string to Base64url to send and attach it to 'raw' property in the POST body but I don't know what has changed and you don't need to do that anymore.
First things first, the Content-Type in header should be
'Content-Type':'message/rfc822'
Now, since we are specifying the content-type as message/rfc822, we don't need to convert the data we want to send into base64url format anymore, I guess (Not sure of the reason because I have a very little knowledge about this.)
Only passing "To: something#any.com" as body works.
Here is the complete code of how to get it done for someone who is struggling for the same problem.
function makeBody(to, from, subject, message) {
let str = [
"to: ", to, "\n",
"from: ", from, "\n",
"subject: ", subject, "\n\n",
message,
].join('');
return str;
}
async function getIdAsync(token,resp) {
return new Promise((resolve,reject) => {
let raw = makeBody("something#gmail.com", "something#gmail.com", "Subject Here", "blah blah blah");
var options = {
hostname: 'www.googleapis.com',
path:'/upload/gmail/v1/users/me/messages/send',
headers: {
'Authorization':'Bearer '+token,
'Content-Type':'message/rfc822'
},
method: 'POST',
};
const req = https.request(options, (res) => {
var body = '';
res.on('data', (d) => {
body += d;
});
res.on('end', () => {
var parsed = body;
console.log(parsed);
})
});
req.on('error', (e) => {
console.error(e);
});
req.write(raw);
req.end();
});
};
Happy Coding :)

Sending base64 encoded PDF through Nodemailer using PDFkit

I have a function that returns me a base64 encode PDF, and I would like to send this as an attachement PDF file using nodemailer.
Regarding the nodemailer documentation, I found this example:
const mailOptions = {
from: 'email1#gmail.com', // sender address
to: 'email2#gmail.com', // list of receivers
subject: 'Simulation', // Subject line
html: '<p>SALUT</p>', // plain text body
filename: 'file.pdf',
attachments: [
content: Buffer.from(
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/' +
'//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U' +
'g9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
'base64'
),
cid: 'note#example.com' // should be as unique as possible
},
However, this did not work for me. Am i missing something ?
Ok, it was all a formatting issue.
Here is how to use B64 on Nodemailer:
const mailOptions = {
from: 'email1#gmail.com', // sender address
to: 'email2#gmail.com', // list of receivers
subject: "Hey this is a subject example", //subject
attachments: [
{
filename: `myfile.pdf`,
content: 'THISISAB64STRING',
encoding: 'base64',
},
],
};
then just send it the classic way.

Sending an array of attachments with node.js

I need to send an email using nodemailer with several attachments, but the number of those attachments must be defined by the size of an array. I know that nodemailer can send more than one attachment, but I have no idea how to send a variable number of attachments.
Here my code:
const files = get(req, "body.data.files");
files.forEach(file => {
senderMail.send(file, {
name: get(req, "body.data.files.name"),
url: get(req, "body.data.files.url")
});
});
let mailOptions = {
from: "Me", //
sender address
to: data.personal.user_email, // list of receivers
subject:
"An email with attachments"
text: "someText",
html: "",
attachments: [
{
filename: name,
path: url
}
]
};
Some data is obtained from a JSON.
Prepare an array in the Nodemailer format and then attach it to the mail object.
const files = get(req, "body.data.files");
const attachments = files.map((file)=>{
return { filename: file.name, path: file.url };
});
let mailOptions = {
from: "Me", //
sender address
to: data.personal.user_email, // list of receivers
subject:
"An email with attachments"
text: "someText",
html: "",
attachments: attachments
};

Send attachment with PDF extension but its not working

I am trying to create a dynamic file and send that to the user in the attachment but after downloading it's not opening.
Showing an error saying "Failed to load PDF
In content, I am sending the required data.
Here is my code
router.get('/file',function(req, res){
var filename1='invoice.pdf';
filename1 = encodeURIComponent(filename1);
var mailOptions={
to: 'userMail#gmail.com',
subject: 'Speaker Added',
from: "admin#gmail.com",
headers: {
"X-Laziness-level": 1000,
"charset" : 'UTF-8'
},
html: '<p style="color:#0079c1;">Hello'+' '+'My Name'+'</p></br>'
+'<p style="color:#0079c1;">Thank you for choosing HOWDY.<p></br>'
+'<p>Click on the link below to activate your account and get redirected to HOWDY</p></br>',
attachments: [
{
'filename':filename1,
'content': 'data',
'contentType':'application/pdf',
'contentDisposition':'attachment'
}
]
}
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'admin#gmail.com',
pass: 'admin56789'
}
});
transporter.sendMail(mailOptions, function(error, response){
if(error){
return res.send(error);
}
else{
res.send({
state:'success',
message:"Send"
});
}
});
})
"
You are sending "data" as content of the file. If you right-click your file, and choose open with notepad (or basic text editor) you'll see.
Nodemailer's attachments expect to receive the data of the file if you use the content option. You might prefer to use path which can be either the filepath or a URL.
Read more here.
Your attachment:
attachments: [
{
'filename':filename1,
'content': 'data',
'contentType':'application/pdf',
'contentDisposition':'attachment'
}
]
Should be closer to:
attachments: [
{
'filename':filename1,
//Either get filecontent
'content': filecontent,
//Or the file full path
'path':filepath,
'contentType':'application/pdf',
'contentDisposition':'attachment'
}
]

Resources