imapnode-imap

Node-imap displaying emails


I've been trying to display emails properly, but failing to do so. I've just send email to myself and it looks like this when i open it in hotmail enter image description here But when i open it in my app using node-imap, from 'Sent box' it looks like this (notice those '=' signs, and some other weird shit..):

enter image description here

and this is same message output in console (for some reason i get '=' on end of every row, and '3D' after each '='):

enter image description here

Now... SAME message, same method, just opened from inbox: http://prntscr.com/jbqtyw

just a plain text.. in console too: http://prntscr.com/jbquba

I know its messy question and hard to understand, but couldnt make it easier.. And here is the code of get() which is fetching emails:

get(id, params) {
    this.emailUsername = params.user.businessEmail;
    this.emailPassword = params.user.businessEmailPassword;
    this.host = params.user.serverIMAP;
    this.port = params.user.portIMAP;
    this.tls = params.user.serverSMTP;
    this.smtpPort = params.user.portSMTP;

    let currBox = params.query.box;
    let userEmail = this.emailUsername;

    return new Promise((resolve, reject) => {
      var imap = new Imap({
        user: this.emailUsername,
        password: this.emailPassword,
        host: this.host,
        port: this.port,
        tls: this.tls,
        tlsOptions: {
          rejectUnauthorized: false
        }
      });
      var response = {};

      function toUpper(thing) { return thing && thing.toUpperCase ? thing.toUpperCase() : thing; }

      function findAttachmentParts(struct, attachments) {
        attachments = attachments || []
        struct.forEach((i) => {
          if (Array.isArray(i)) findAttachmentParts(i, attachments)
          else if (i.disposition && ['INLINE', 'ATTACHMENT'].indexOf(toUpper(i.disposition.type)) > -1) {
            attachments.push(i)
          }
        })
        return attachments
      }

      function checkEmail(email) {
        return email.split('@')[1].split('.')[0];
      }

      function findTextPart(struct) {
        for (var i = 0, len = struct.length, r; i < len; ++i) {
          if (Array.isArray(struct[i])) {
            if (r = findTextPart(struct[i]))
              return r;
          } else if (struct[i].type === 'text'
            && struct[i].subtype === 'html') {
              return [struct[i].partID, struct[i].type + '/' + struct[i].subtype];
            } else if(struct[i].type === 'text'&& struct[i].subtype === 'plain') {
              return [struct[i].partID, struct[i].type + '/' + struct[i].subtype];
            }
        }
      }

      function getMsgByUID(uid, cb, partID) {
        var f = imap.seq.fetch(uid,
          (partID
            ? {
              bodies: [
                'HEADER.FIELDS (TO FROM SUBJECT DATE CC BCC)',
                partID[0]
              ]
            }
            : { struct: true })),
          hadErr = false;

        if (partID)
          var msg = { header: undefined, body: '', attrs: undefined };

        f.on('error', function (err) {
          hadErr = true;
          cb(err);
        });

        if (!partID) {
          f.on('message', function (m) {
            m.on('attributes', function (attrs) {
              partID = findTextPart(attrs.struct);
              const attachments = findAttachmentParts(attrs.struct);
              attachments.forEach((attachment) => {
                const filename = attachment.params.name  // need decode disposition.params['filename*'] !!!
                const encoding = toUpper(attachment.encoding)
                const f = imap.fetch(attrs.uid, { bodies: [attachment.partID] })
              })
            });
          });
          f.on('end', function () {
            if (hadErr)
              return;
            if (partID)
              getMsgByUID(uid, cb, partID);
            else
              cb(new Error('No text part found'));
          });
        } else {
          f.on('message', function (m) {
            m.on('body', function (stream, info) {
              var b = '';
              stream.on('data', function (d) {
                b += d;
              });
              stream.on('end', function () {
                if (/^header/i.test(info.which))
                  msg.header = Imap.parseHeader(b);
                else
                  msg.body = b;
                  console.log(b);
              });
            });
            m.on('attributes', function (attrs) {
              msg.attrs = attrs;
              msg.contentType = partID[1];
            });
          });
          f.on('end', function () {
            if (hadErr)
              return;
            cb(undefined, msg);
          });
        }
      }

      imap.once('ready', function () {
        imap.openBox(currBox, true, function (err, box) {
          if (err) throw err;
          getMsgByUID(id, function (err, msg) {
            if (err) throw err;
            response = msg;
            imap.end();
          });
        });
      });


      imap.once('error', function (err) {
        reject(err);
      });

      imap.once('end', function () {
        resolve(response);
      });

      imap.connect();
    })

  }

and on the frontend, just displaying it as inner HTML:

<div class="content" [innerHtml]="bypassSecurity(email.body)">
    </div>

So to sum it up.. same method/call getting different output from stream (once plain text, once html text.. also in both cases some weird shit added to messages, like '=' or '3D' on seems like random places)? is this the right way to read emails?


Solution

  • This is Quoted Printable encoding.

    It looks like you need to possibly undo the Content Transfer Encoding for the sections, which can commonly be Base64 or Quoted Printable.