Salesforce Emails as Tasks

From ESM Wiki
Jump to: navigation, search



This document provides an example integration of Email Stream Management with Salesforce CRM system. The idea is to have each mail from/to a Contact with an open Opportunity in Salesforce to be automatically recorded as Task for the corresponding Opportunity. Tasks are displayed on home page of Salesforce account thus a Manager can capture and review all communication of his Stuff with Customers. Stuff are not required to lean CRM and to record their communication manually - everyting is done automatically on network (Mailspect+MTA) level. Stuff use only their favorite mailers (MUA). This concept is illustrated on the following diagram:


MPP configuration options for this example intergration are provided in example config file. It contains only relevant options some of which contains example values. Additional efforts will be required to modify system and credential specific information and to add options for other features for entire config to start working on a specific installation. Relevant options are described step-by-step in further sections.


Strip body pipeline is used because this pipeline is executed at the end of all processing when spam/virus filtering is applied and decision that message is good already made. Pipeline is enabled with <strip_body_enabled> and engines are specified with <strip_body_engines>. The pipeline consists from the following engines:

  • salesforce_http.login
  • salesforce_http.find_opportunity
  • salesforce_http.attach_attachment_to_opportunity
  • salesforce_http.create_task_for_opportunity

Salesforce HTTP

This is a collection of salesforce queries. It starts with <http id="salesforce_http"> tag.

Tag <method> defines default HTTP request method for all queries. It will be used when a query doesn't define its own. All salesforce queries uses POST .

Tag <ssl_context> with <load_veriy_file> subtag is mandatory because queries use https:// scheme. They defines Certificate Authority (CA) to use for verifying peer. File /usr/share/ssl/cert.pem is a default file that comes with OpenSSL installation. It contains certificates of many widespread CA's. The location may be different for different installations (for example /etc/pki/tls/cert.pem) so this options should be modified to work with a specific installation.

Tag <headers> defines default HTTP request headers template for all queries. It will be used when a query doesn't define its own. All salesforce queries uses same headers template. This template uses $http_date macros which is RFC 1123 complient current GMT date (for example: Fri, 25 Sep 2009 17:58:50 +0000). Content-Type and Accept headers are text/xml because Salesforce uses XML/SOAP HTTP entities. Charset is utf-8 because ESM works with this charset. Cache-Control and Pragma disables response caching. SOAPAction must be present and have empty value according to Salesforce enterprise.wsdl. Other standard required headers Host and Content-Length are added automatically by implementation.


Login is a method of Salesforce HTTP Engine. It starts with <query id="login"> tag.

Tag <uri> is a template that defines URI to connect to. In this case it is just a constant value take from enterprise.wsdl file. WSDL file formally defines rules and formats of all possible queries to salesforce. It is published by salesforce.

Tag <entity> defines HTTP entity (any data) that is sent to the server in a request. For salesforce it is a XML/SOAP data formated according to WSDL rules. If you don't fully understand how to write XML/SOAP data guided by WSDL you may find additional examples here. And description for salesforce objects and methods here.

Sequences "<![CDATA" at the beginning and "]]>" at the end are required. This will not appear in result request. This is needed for MPP to ignore XML markup inside when reading configs. Note that sequence:

<entity><![CDATA[<?xml version="1.0"?>

MUST appear as it is in example: don't put space or linebreaks anywhere inside. Otherwise you will get an error from salesforce server that say something about unrecognized format. Also note that you must track if your template contains "]]>" sequence inside and replace all such sequences (if any must appear) with "]]]]><![CDATA[>".

Replace your_user_name, your_password and your_security_token with your credentials. Remove '+' sign which is here for visual separation. See for details here.

Tags <result> defines how results of login queries are calculated. Because salesforce always returns XML as response entity then XML functions can be used to extract data from response entity to engine results. Function ${xml_xpath ...} evaluates given XPath expression and extracts all character data from first matched node (whether element or attribute). For quick tutorial for XPath see for example here or full spec here.

XPath expressions used in this example extracts character data from a first found node with specified name. Note that such a long expression for filtering nodes by name MUST be used because responses uses namespaces so just specifying node names will not work. Strictly saying it should be also namespace comparisons but they can be avoided for simplicity.

Login response provides two important values:

  • serverUrl - URI of a server to perform further requests to.
  • sessionId - Session ID to include in further requests as SessionHeader.

This values are extracted and exported as engine results.

Tags <validate> provides protocol errors processing mechamism. They are used for validating calculated results. Special macro $result is available withing template options of <validate> tags which refer to result being validated. It may consist of zero or more cases and default actions. Cases are evaluated in turns untill the one with matched condition is found and then actions for such a case are executed. If no matched case condition found the default actions are executed. Available action are:

  • log - put log message to MPP log. Logging level is specified with "level" attribute for <log> tag;
  • throw - execution of a pipeline will be stopped and <on_error> action will be performed.

Note: value of throw tag is directly inserted in SMTP (and other protocol) responses for MTA. So you should make sure that value doesn't contain line breaks and feeds and other characters unsafe for SMTP protocol. General recomendation is to combine comprehansive writing to logs and short simple throw messages.

Tag <result id="faultstring"> is dedicated to errors processing. It checks for fault string in response and throws if finds it. Because it goes before other results it will be executed first.

Find Opportunity

Find opportunity is a method of Salesforce HTTP Engine. It starts with tag <query id="find_opportunity">.

Tag <uri> uses serverUrl and SOAP SessionHeader uses sessionId results from login query.

SOAP <soapenv:Body> contains invocation of SOQL query that selects Opportunities that are open and that contains any of sender or recipients as its Contact (through OpportunityContactRole). For details of SOQL see here. Because MUA's uses information from message headers the query uses senders ($headers.from, $headers.sender, $headers.reply_to) and recipients ($, $, $headers.bcc) identity from headers and not SMTP sender ($sender) and recipients ($recipient).

Function ${xml_cdata ...} wraps its argument into "<![CDATA[...]]>" brackets. It also replaces all internal "]]>" with "]]]]><![CDATA[>" In general this function or ${xml_escape ...} function MUST be used for data coming from outside (untrusted) world. Failure to do so may result in security threat when specially constructed data sent by malicious person may be interpreted as commands and executed. Or at least and error may occur with "good" but not suitable data. So ALWAYS wrap external data (and everything that include external data) with this functions. In provided example external data are senders and recipients identity.

Function ${text_find_replace_all ...} is used as general purposeescape mechanism to escape SOQL special characters "'" (singlequote) and "\" (back-slash). This macro replaces in its first argument all further even sequences with corresponding odd sequences. So "'" is replaced with "\'" and so on. Note that "\" characters are doubled because they are in turns special for generic templates and thus must be escaped to appear when a templateis evaluated.

Function ${wrap ...} is used to generate lists from multi-value expressions. It iterates by each value of its second argument and concatenates all obtained values of first arguments. Macro $# in first argument is replaced with value of second argument at each iteration. Thus for example if message has three addresses specified in From header the macro will evaluate to a list of comparison expressions separated with " OR ": OR OR

Construct ${not_empty ... $empty} is used to facilitate evaluation of the template even though wrap-list is empty. The point is that wrap-list evaluates to null value if a list has no items causing template evaluation to null and skipping execution of entire query. Thus for example for message with no Bcc header (which is very likely) query would not be executed that is not desired. To avoid such behaviour the following property of ${not_empty ...} macro is used: it checks its arguments from left to right for null or empty value and returns first argument that is not null or empty; it returns null if ALL arguments are null; it returns empty if it doesn't find not empty argument. In this case if wrap-list is null then second argument (special macro $empty denotes empty string) is checked. Because it is always not null and it is always empty the result will be empty string. For example a message with one From address, two To addresses and without other fields will evaluate to:

Contact.Email='' OR
Contact.Email='' OR Contact.Email=''

There will be additional spaces and linebreaks inside but they are not essensial.

Last ${not_empty ...} construct contains string "false" instead of macro $empty. This is needed to facilitate absense of To field or absense of all fields. Value "false" will be a placeholder to compensate last OR or compensate entire expression.

Construct ${if_not_empty ...} to insert not empty value of $headers.sender into OR-list. It checks its second argument if it is not empty and inserts it prefixed with first and suffixed with third argument. If seond argument is empty the macro evaluates to empty value.

Find opportunity method exports "Id" result that is an id of first found Opportunity and "ContactId" that is an id of corresponding Contact. Multiple Opportunities in responses cannot be supported - only first one is choosed.

Attach Attachment to Opportunity

Attach Attachment to Opportunity is a method of Salesforce HTTP Engine. It starts with tag <query id="attach_attachment_to_opportunity">.

Tag <precondition> checks if matched Opportunity is found by previous engine.

Series of $attachment macros are used to insert attachment data and properites into query. Macro $attachment.base64 is a Base64 encoding of attachment data. Other macros are self-explained.

Additional type of error processing is used for this engine with tag <result id="errors_message"> because specification of create method defines additional format for errors reporting. More complex XPath expression is used to extract error message from response XML: it selects tag <message> wich is a child of <errors>.

Create Task for Opportunity

Create Task for Opportunity is a method of Salesforce HTTP Engine. It starts with tag <query id="create_task_for_opportunity">.

Here the same ${wrap ...} and ${not_empty ...} technique is used as for Find Opportunity. Links to attachments are constructed using as base address and attachment ID from previous attach engine as path. This may require modification if you have different base address for your Salesforce GUI after login.


This method is intended to logout from salesforce system thus invalidating sessionId and serverUrl. But you may noticed that this method is not listed in <strip_body_engines> pipeline. This is intentionally. It was noticed that salesforce always returns same sessionId for multiple consequence logins. Thus in multi-threaded environment calling logout from one thread would invalidate sessionId that other threads may be using. This will result in incorrect operation.

To avoid such a problem logout is not used. Because salesforce returns same sessionId for multiple logins it is assumed that session is shared and thus session leaks and consequent exausting will not occur. Shared session will just expire if not used for long time and application will just relogin at next login. However this assumption should be checked with salesforce doc and/or support team.

See also

Personal tools