The CreatorCon Call for Content is officially open! Get started here.

Michael Ritchie
ServiceNow Employee
ServiceNow Employee

As you may know, ServiceNow has the ability to accept an inbound email and perform some action.  Inbound email actions allow you to configure what happens when the instance receives an email. Any attachments on the email will automatically get attached to the record created by the email.  If the created record is of type task, the activity stream will also contain a link to the contents of the inbound email as well:

find_real_file.png

While the activity stream is useful, it can grow quite long and the records don't stay around forever.  There have been a few posts on this community asking for the ability to create an attachment with the contents of the email and attach it to the record.  This solution by @warrem inspired me to create this blog post outlining steps to solve this use case since some users have posted followup questions.

ServiceNow includes an attachment API that allows you to create an attachment from any data.  For example, this API is leveraged by the workflow editor's Attachment Note activity which allows you to create a task attachment from a string of text.  This solution will leverage this API and can be called by adding a few lines of code to your existing inbound email actions.

Steps:

  • Navigate to System Definition \ Script Includes and click New.
  • Set the following values:
    • Name: emailAsAttachmentUtil
    • Accessible from: All application Scopes = this will allow it to be called by all applications
    • Active: checked
    • Description: You may want to set the description to something like the following to document what this script includes does and how to call it
This utility script will take contents from an inbound email and create an attachment on the created record from the inbound email action.  To utilize this script, add the following lines at the end of the inbound email action script:
var emailAsAttachment = new global.emailAsAttachmentUtil();
emailAsAttachment.createAttachment(email, current);
    • Script:
var emailAsAttachmentUtil = Class.create();
emailAsAttachmentUtil.prototype = {
    initialize: function() {
		this.newLineChar = "\r\n";  // Microsoft Windows expects \r and \n for return and new line
		this.contentType = "text/plain";
    },
	
	createAttachment: function (emailRec, currentRec) {
		var fileName = emailRec.subject + '.eml';
		
		// Setup array to push email values into.  Add additional as needed/
		var emailData = [];
		emailData.push("To: " + emailRec.to);
		emailData.push("Subject: " + emailRec.subject);
		emailData.push("From: " + emailRec.origemail);
		emailData.push(emailRec.body_text);
		
		// Convert emailData to a string separated by new line character.
		var emailString = emailData.join(this.newLineChar);
		
		// Create attachment with email string and attach it to the record creatd by the email.
		var sysAttachment = new GlideSysAttachment();
		sysAttachment.write(currentRec, fileName, this.contentType, emailString);
	},

    type: 'emailAsAttachmentUtil'
};
    • Click Submit

Your script include should look similar to the following:

find_real_file.png

  • Navigate to System Policy \ Email \ Inbound Actions and open the one that you want to capture the contents of the email as an attachment.
  • Go to the Actions Tab and scroll to the bottom of the script and paste in the following:
var emailAttachment = new global.emailAsAttachmentUtil();
emailAttachment.createAttachment(email, current);
  • Click Update

Now any email that is processed by this inbound email action will log the contents as an attachment:

find_real_file.png

find_real_file.png

Things to be aware of:

  • You can add this functionality to any inbound action
  • The Email's To, Subject, From, and Body are captured in the attachment.
    • If there are other fields you wish to capture feel free to edit lines 13-16 of the emailAsAttachmentUtil Script Include
  • Line 4 of the emailAsAttachmentUtil Script Include sets the new line character of the text file.  Windows requires \r (carriage return) and \n (new line) to show correctly in Notepad as an example.  Feel free to edit for your environment.

 

28 Comments
Vikrant Vashish
Tera Contributor

Hi @Pratiksha2 ,

 

Please see the link below and let me know if this is what you are looking for:

Post | Feed | LinkedIn

Regards,

Vikrant

ViswaSunny
Tera Contributor

I also want to add the attachments in that email how can we add those attachments into this object that we are feeding the data?

Vikrant Vashish
Tera Contributor

Hi @ViswaSunny ,

 

Yes it’s possible. You need to convert the attachment data into base64 format and add that code into the eml attachment. I can share the code if required.

 

Thanks and Regards,

Vikrant

ViswaSunny
Tera Contributor

Hi @Vikrant Vashish ,

Yes please share me the code it would be really helpful 🙂

ViswaSunny
Tera Contributor

Hi @Vikrant Vashish ,
Any luck on the code to add attachments into .eml ?

Vikrant Vashish
Tera Contributor

Hi @ViswaSunny 

 

Please find attached the client callable script include code. You can call downloadEmail function and pass comma separated sys_ids of emails(in sysparm_item_id parameter from either a UI Action or client script) to be downloaded as attachment. You can make changes according to your requirement.

 

 

var EmailDownloadUtil = Class.create();
EmailDownloadUtil.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {

/* Below function is used to get the attachment sys_ids from the html string and replace them with base 64 content */
    getAttachmentContentFromEmailBody: function(html) {

        var regex = /<img /gi,
            result, indices = [];
        while ((result = regex.exec(html))) {
            indices.push(result.index);
        }
        var strArr = [];
        for (var j = 0; j < indices.length; j++) {

            var indP = html.indexOf('</p>', indices[j]) + 4;
            var indD = html.indexOf('</div>', indices[j]) + 4;
            var subStr;
            if (indD > indP) {
                subStr = html.substring(indices[j], html.indexOf('</p>', indices[j]) + 4);
            } else {
                subStr = html.substring(indices[j], html.indexOf('</div>', indices[j]) + 4);
            }
            if (subStr.indexOf('base64') <= -1) {
                strArr.push(subStr);
            }
        }
        var regexp = /[0-9a-f]{32}/;
        var sysIdArr = [];

        for (var k = 0; k < strArr.length; k++) {
            if (strArr[k].match(regexp)) {
                sysIdArr.push(strArr[k].match(regexp));
            }

        }
        var bodyNew = html;
        for (var l = 0; l < sysIdArr.length; l++) {
            var attachGr = new GlideRecord('sys_attachment');
            if (attachGr.get('sys_id', sysIdArr[l].toString())) {

               	var attBytes = (new GlideSysAttachment()).getContentBase64(attachGr);
                bodyNew = bodyNew.replace(strArr[l].toString(), '<img style="display:block; width:100%;" src="data&colon;image/png;base64, ' + attBytes + ' "></p>');

            }
        }
        return bodyNew;

    },
    downloadEmail: function() {

        var attatchOBJ = '';
        var result;



        var emailGR = new GlideRecord('sys_email');
        emailGR.addQuery('sys_id', 'IN', this.getParameter('sysparm_item_id').toString());
        emailGR.query();
        while (emailGR.next()) {

            var emlAttachmentSysId = this.exportEmailToEML(emailGR);
            attatchOBJ += 'https://' + gs.getProperty('instance_name') + '.service-now.com/sys_attachment.do?sys_id=' + emlAttachmentSysId + ",";

        }
        return attatchOBJ;
    },
    exportEmailToEML: function(sys_email_gr) {

        var instanceEmail = gs.getProperty('instance_name') + '@service-now.com';
        var emlStr = '';
        emlStr += 'To: ' + sys_email_gr.recipients + '\n';
        emlStr += 'Subject: ' + sys_email_gr.subject + '\n';
    
        emlStr += 'From: ' + sys_email_gr.user_id.email + '\n';
        emlStr += 'Reply-To: ' + instanceEmail + '\n';
        emlStr += 'Date: ' + this.generateEMLDate(sys_email_gr) + '\n';

        // Add the content-type and body
        var obj = this.generateEMLBodyAndContentType(sys_email_gr);
        emlStr += obj.contentType + '\n';

        emlStr += '\n\n' + obj.body + '\n';
        emlStr += sys_email_gr.headers + '\n';

        return (new GlideSysAttachment()).write((new GlideRecord('sys_attachment')), sys_email_gr.subject.substring(0, 70) + '.eml', 'text/plain', emlStr);
    },

    generateEMLBodyAndContentType: function(sys_email_gr) {


        var boundaryStr = Math.random().toString(36).substring(10);

        var boundaryStr2 = Math.random().toString(36).substring(10);
        var contentType = 'Content-Type: ' + 'multipart/related' + '; boundary=' + boundaryStr;

        var bodyStr = '--' + boundaryStr + '\n';
        bodyStr += 'Content-Type: multipart/related; boundary=' + boundaryStr2 + '\n\n';

        bodyStr += '--' + boundaryStr2 + '\n';
        bodyStr += 'Content-Type: text/html;' + ' charset="utf-8"\n';
        bodyStr += 'Content-Transfer-Encoding: 8bit\n';
        bodyStr += '\n';

        var bodyNew = this.imageChange(sys_email_gr.body, boundaryStr2, boundaryStr);

        bodyStr += bodyNew + '\n';
        //bodyStr += '--' + boundaryStr2 + '--\n\n';

        bodyStr += this.processEmailAttachmentsForEML(sys_email_gr, boundaryStr);

        bodyStr += '\n--' + boundaryStr + '--';

        return {
            'contentType': contentType,
            'body': bodyStr
        };
    },


    generateEMLDate: function(sys_email_gr) {
        var gdt = new GlideDateTime(sys_email_gr.sys_created_on);
        var dayOfWeek = gdt.getDayOfWeekLocalTime().toString();
        var dayOfMonthStr = gdt.getDayOfMonthLocalTime().toString();
        var month = gdt.getMonthLocalTime();
        var year = gdt.getYearLocalTime();
        var localTimeStr = gdt.getLocalTime().toString();
        var tzOffset = gdt.getTZOffset();
        var tzOffsetHours = tzOffset / 1000 / 60 / 60;
        var tzOffsetHoursSign = (tzOffsetHours < 0 ? "-" : "");
        var tzOffsetHoursAbs = Math.abs(tzOffsetHours);
        var tzOffsetHoursAbsStr = tzOffsetHoursAbs.toString();

        var ALL_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
        var ALL_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

        var emlDateStr = ALL_DAYS[dayOfWeek] + ", " + ("00".slice(dayOfMonthStr.length) + dayOfMonthStr) + " " + ALL_MONTHS[month - 1] + " " + year + " " + localTimeStr.slice(11) + " " + tzOffsetHoursSign + ("00".slice(tzOffsetHoursAbsStr.length) + tzOffsetHoursAbsStr) + "00";

        return emlDateStr;
    },


    processEmailAttachmentsForEML: function(sys_email_gr, boundaryStr) {


        if (!sys_email_gr || !boundaryStr) {
            throw new Error('Missing arguments.');
        }

        var attachmentStrForEml = '';


        var grAttach = new GlideRecord('sys_email_attachment');
        grAttach.addQuery('email', sys_email_gr.sys_id.toString());
        //	grAttach.addQuery('table_sys_id', sys_email_gr.sys_id);
        grAttach.query();
        while (grAttach.next()) {
            if (grAttach.action != 'discarded') {

                attachmentStrForEml += '--' + boundaryStr + '\n';
                attachmentStrForEml += 'Content-Type: ' + grAttach.attachment.content_type.toString() + '; name="' + grAttach.attachment.file_name.toString() + '"\n';
                attachmentStrForEml += 'Content-Transfer-Encoding: base64\n';
                attachmentStrForEml += 'Content-Disposition: attachment; filename="' + grAttach.attachment.file_name.toString() + '"\n';
                attachmentStrForEml += '\n';

                var att = new GlideRecord('sys_attachment');
                att.get('sys_id', grAttach.attachment.sys_id.toString());

                var attBytes = (new GlideSysAttachment()).getContentBase64(att);

                attachmentStrForEml += attBytes;
                attachmentStrForEml += '\n\n';
            }
        }

        return attachmentStrForEml;
    },

    emailCheck: function(id) {
        var eml = new GlideRecord('sys_email');
        eml.addQuery('instance', id);
        eml.query();
        if (eml.next()) {
            return true;
        } else {
            return false;
        }
    },

    imageChange: function(body, boundaryStr2, boundaryStr) {


        var regex = /<img /gi,
            result, indices = [];
        while ((result = regex.exec(body))) {
            indices.push(result.index);
        }
        var strArr = [];
        for (var j = 0; j < indices.length; j++) {

            var indP = body.indexOf('</p>', indices[j]) + 4;
            var indD = body.indexOf('</div>', indices[j]) + 4;
            var subStr;
            if (indD > indP) {
                subStr = body.substring(indices[j], body.indexOf('</p>', indices[j]) + 4);
            } else {
                subStr = body.substring(indices[j], body.indexOf('</div>', indices[j]) + 4);
            }

            if (subStr.indexOf('base64') <= -1) {
                strArr.push(subStr);
            }
        }

        var regexp = /[0-9a-f]{32}/;
        var sysIdArr = [];

        for (var k = 0; k < strArr.length; k++) {
            if (strArr[k].match(regexp)) {
                sysIdArr.push(strArr[k].match(regexp));
            }

        }


        var bodyNew = body;
        var imgBody = '';


        for (var l = 0; l < sysIdArr.length; l++) {
            var attachmentStrForEml = '';
            var attachGr = new GlideRecord('sys_attachment');
            if (attachGr.get('sys_id', sysIdArr[l].toString())) {

                attachmentStrForEml += '--' + boundaryStr + '\n';
                attachmentStrForEml += 'Content-Type: ' + attachGr.content_type.toString() + '; name="' + attachGr.file_name.toString() + '"\n';
                attachmentStrForEml += 'Content-Transfer-Encoding: base64\n';
                attachmentStrForEml += 'Content-Disposition: inline; filename="' + attachGr.file_name.toString() + '"\n';
                attachmentStrForEml += 'Content-ID: <' + 'image' + l + '>' + '\n';
                attachmentStrForEml += '\n';

                var attBytes = (new GlideSysAttachment()).getContentBase64(attachGr);

                attachmentStrForEml += attBytes;
                attachmentStrForEml += '\n';

                imgBody += '\n';
                imgBody += attachmentStrForEml;
                imgBody += '\n';

                bodyNew = bodyNew.replace(strArr[l].toString(), '<img src="cid:' + 'image' + l + '"></p>');
            }
        }
        bodyNew = bodyNew + '\n' + '--' + boundaryStr2 + '--\n\n' + imgBody + '\n';

        return bodyNew;
    },


    type: 'EmailDownloadUtil'
});

 

 

 

Thanks and Regards,

Vikrant

Brandon Warren
Kilo Guru

@Vikrant Vashish

 

Can you provide an example of how you would call this in an Inbound Action? I'm trying to get this to work but not having much luck.

vishnu d
Tera Contributor

 Hi   @ViswaSunny  we also have the same requirement. Did you find any solution? Could you please share us the solution if you found any.