Mailer Using mailto: URL Scheme

I’m trying to modify Spark Mailer v2 to use the mailto URL scheme instead of the Spark URL Scheme. The reason for making the conversion is that apparently using mailto allows <newline> characters embedded into the <body> whereas they are ignored using the Spark (and perhaps all other email) URL Schemes. (Details here.)

Here’s the test code I’m using:

// Test data - Remove
let strNicknameList = "nicknameList"
let strEmailTo = "nameTo1<acctTo1@domain.tld>"
let strEmailCc = "nameCC1<acctCC1@domain.tld>"
let strEmailBcc = "nameBCC1<acctBCC1@domain.tld>"
let strPageTitle = "pageTitle";
let strPageURL = "pageURL";
let strPageSelection = "pageSelection";


// Build and call the mailto URL
// Use the callback object to make adding parameters easier ... but *NOT* running as a call back.
// Base settings
let cburlMailto = CallbackURL.create();
cburlMailto.waitForResponse = false;
cburlMailto.baseURL = "mailto:" + strEmailTo;
// Who else gets it
if (strEmailCc != "") cburlMailto.addParameter("cc", strEmailCc);
if (strEmailBcc != "") cburlMailto.addParameter("bcc", strEmailBcc);
// Subject line
cburlMailto.addParameter("subject", strPageTitle);
// Body
cburlMailto.addParameter("body",`${strNicknameList}

${strPageURL}


${strPageSelection}`);

// Log Url string - Remove
console.log("cburlMailto: " + cburlMailto());

// Open default mailer
cburlMailto.open();

My problem is trying to display the string built in cburlMailto.

How do I log it on the console or display it in an alert?

Try console.log("cburlMailto: " + cburlMailto.url);.

1 Like

Thank you for the link to the CallbackURL documentation.

I added the line that you suggested. Now no error messages pop up when I run the action, but there also is no entry in the console.

I added this alert:

alert("cburlMailto: " + cburlMailto.url);

When I run the action, the alert that pops up says:

cburlMailto: undefined

I don’t understand.

If cburlMailto.url is really undefined, then why does my script run with out popping up an error?

Note that mailto isn’t really an x-callback-url. It’s kind of an ugly coersion, but this still opens my mail client with the details populated and the desired console output.

// Test data - Remove
let strNicknameList = "nicknameList"
let strEmailTo = "nameTo1<acctTo1@domain.tld>"
let strEmailCc = "nameCC1<acctCC1@domain.tld>"
let strEmailBcc = "nameBCC1<acctBCC1@domain.tld>"
let strPageTitle = "pageTitle";
let strPageURL = "pageURL";
let strPageSelection = "pageSelection";

// Build and call the mailto URL
// Use the callback object to make adding parameters easier ... but *NOT* running as a call back.
// Base settings
let cburlMailto = CallbackURL.create();
cburlMailto.waitForResponse = false;
cburlMailto.baseURL = "mailto:";
cburlMailto.addParameter("to", strEmailTo);

// Who else gets it
if (strEmailCc != "") cburlMailto.addParameter("cc", strEmailCc);
if (strEmailBcc != "") cburlMailto.addParameter("bcc", strEmailBcc);

// Subject line
cburlMailto.addParameter("subject", strPageTitle);

// Body
cburlMailto.addParameter("body",`${strNicknameList}

${strPageURL}


${strPageSelection}`);

// Log Url string - Remove
console.log("cburlMailto: " + cburlMailto.url);

// Open default mailer
cburlMailto.open();

My problem with logging was that Log Level was set for Errors instead of All. All my console.log() statements appear in the Action Log now. :partying_face:

Now here’s my code with console.log() statements at each step of the URL scheme build. (This reminds me of debugging programs in my FORTRAN class during my senior year of high school by inserting PRINT statements between every card of my deck of 80-column punched cards. :wink:)

console.log("enter script");

// Test data - Remove
let strNicknameList = "nicknameList"
let strEmailTo = "nameTo1<acctTo1@domain.tld>"
let strEmailCc = "nameCC1<acctCC1@domain.tld>"
let strEmailBcc = "nameBCC1<acctBCC1@domain.tld>"
let strPageTitle = "pageTitle";
let strPageURL = "pageURL";
let strPageSelection = "pageSelection";
let strBody = `${strNicknameList}

${strPageURL}


${strPageSelection}`;



// Build and call the mailto URL
// Use the callback object to make adding parameters easier ... but *NOT* running as a call back.
// Base settings
let cburlMailto = CallbackURL.create();

console.log("after let cburlMailto = CallbackURL.create();");
console.log("cburlMailto: " + cburlMailto.url);

cburlMailto.waitForResponse = false;

console.log("after cburlMailto.waitForResponse = false;");
console.log("cburlMailto: " + cburlMailto.url);

cburlMailto.baseURL = "mailto:" + strEmailTo;

console.log("strEmailTo: " + strEmailTo);
console.log("after cburlMailto.baseURL = \"mailto:\" + strEmailTo");
console.log("cburlMailto: " + cburlMailto.url);

// Who else gets it
if (strEmailCc != "") cburlMailto.addParameter("cc", strEmailCc);

console.log("strEmailCc: " + strEmailCc);
console.log("after cburlMailto.addParameter(\"cc\", strEmailCc);");
console.log("cburlMailto: " + cburlMailto.url);

if (strEmailBcc != "") cburlMailto.addParameter("bcc", strEmailBcc);

console.log("strEmailBcc: " + strEmailBcc);
console.log("after cburlMailto.addParameter(\"bcc\", strEmailBcc);");
console.log("cburlMailto: " + cburlMailto.url);

// Subject line
cburlMailto.addParameter("subject", strPageTitle);

console.log("strPageTitle: " + strPageTitle);
console.log("after cburlMailto.addParameter(\"subject\", strPageTitle);");
console.log("cburlMailto: " + cburlMailto.url);

// Body
cburlMailto.addParameter("body", strBody);

console.log("strBody: " + strBody);
console.log("after cburlMailto.addParameter(\"body\", strBody);");
console.log("cburlMailto: " + cburlMailto.url);



// Open default mailer
console.log("before cburlMailto.open()");
cburlMailto.open();
console.log("after cburlMailto.open()");
console.log("exit script");

Here’s the Action Log:

Script step completed.
enter script
after let cburlMailto = CallbackURL.create();
cburlMailto: ?
after cburlMailto.waitForResponse = false;
cburlMailto: ?
strEmailTo: nameTo1<acctTo1@domain.tld>
after cburlMailto.baseURL = "mailto:" + strEmailTo
cburlMailto: undefined
strEmailCc: nameCC1<acctCC1@domain.tld>
after cburlMailto.addParameter("cc", strEmailCc);
cburlMailto: undefined
strEmailBcc: nameBCC1<acctBCC1@domain.tld>
after cburlMailto.addParameter("bcc", strEmailBcc);
cburlMailto: undefined
strPageTitle: pageTitle
after cburlMailto.addParameter("subject", strPageTitle);
cburlMailto: undefined
strBody: nicknameList

pageURL


pageSelection
after cburlMailto.addParameter("body", strBody);
cburlMailto: undefined
before cburlMailto.open()
after cburlMailto.open()
exit script
Script step completed.
Draft moved to trash

As you can see, there’s something wrong with:
cburlMailto.baseURL = "mailto:" + strEmailTo

Moreover, cburlMailto.open() still is not opening any mailer.

UPDATE May 13, 2022 10:24 PM

I fixed it by adding the following towards the top:

let strBaseURL = "mailto:" + encodeURI(strEmailTo);

And changing:

cburlMailto.baseURL = "mailto:" + strEmailTo
to
cburlMailto.baseURL = strBaseURL;

Now the Action Log shows the mailto URL being built correctly:

Script step completed.
enter script
after let cburlMailto = CallbackURL.create();
cburlMailto: ?
after cburlMailto.waitForResponse = false;
cburlMailto: ?
strBaseURL: mailto:nameTo1%3CacctTo1@domain.tld%3E
after cburlMailto.baseURL = strBaseURL
cburlMailto: mailto:nameTo1%3CacctTo1@domain.tld%3E?
strEmailCc: nameCC1<acctCC1@domain.tld>
after cburlMailto.addParameter("cc", strEmailCc);
cburlMailto: mailto:nameTo1%3CacctTo1@domain.tld%3E?cc=nameCC1%3CacctCC1@domain.tld%3E
strEmailBcc: nameBCC1<acctBCC1@domain.tld>
after cburlMailto.addParameter("bcc", strEmailBcc);
cburlMailto: mailto:nameTo1%3CacctTo1@domain.tld%3E?bcc=nameBCC1%3CacctBCC1@domain.tld%3E&cc=nameCC1%3CacctCC1@domain.tld%3E
strPageTitle: pageTitle
after cburlMailto.addParameter("subject", strPageTitle);
cburlMailto: mailto:nameTo1%3CacctTo1@domain.tld%3E?subject=pageTitle&bcc=nameBCC1%3CacctBCC1@domain.tld%3E&cc=nameCC1%3CacctCC1@domain.tld%3E
strBody: nicknameList

pageURL


pageSelection
after cburlMailto.addParameter("body", strBody);
cburlMailto: mailto:nameTo1%3CacctTo1@domain.tld%3E?cc=nameCC1%3CacctCC1@domain.tld%3E&bcc=nameBCC1%3CacctBCC1@domain.tld%3E&subject=pageTitle&body=nicknameList%0A%0ApageURL%0A%0A%0ApageSelection
before cburlMailto.open()
after cburlMailto.open()
exit script
Script step completed.
Draft moved to trash

On my iPhone 12 Pro running iOS 15.4.1 at least, setting the cburlMailto.baseURL property requires URL encoding whereas the method cburlMailto.addParameter()does NOT require URL encoding.

I guess it’s unusual for the base URL to have characters that require encoding. :thinking:

If you look back at my previous post you will see I did modify the base URL to get it to run.

I’ve included the equivalent version reworked to match your additional logging so you can see the progression.

Reveal additional detail
// Start
console.log('enter script');

// Test data - Remove
let strNicknameList = "nicknameList"
let strEmailTo = "nameTo1<acctTo1@domain.tld>"
let strEmailCc = "nameCC1<acctCC1@domain.tld>"
let strEmailBcc = "nameBCC1<acctBCC1@domain.tld>"
let strPageTitle = "pageTitle";
let strPageURL = "pageURL";
let strPageSelection = "pageSelection";
let strBody = `${strNicknameList}

${strPageURL}


${strPageSelection}`;

// Build and call the mailto URL
// Use the callback object to make adding parameters easier ... but *NOT* running as a call back.

// Create object
let cburlMailto = CallbackURL.create();
console.log('after let cburlMailto = CallbackURL.create();');
console.log('cburlMailto: ' + cburlMailto.url);

// Wait for response
cburlMailto.waitForResponse = false;
console.log('after cburlMailto.waitForResponse = false;');
console.log('cburlMailto: ' + cburlMailto.url);

// Base URL
cburlMailto.baseURL = 'mailto:';
console.log('after cburlMailto.baseURL = "mailto:"');
console.log('cburlMailto: ' + cburlMailto.url);

// TO
cburlMailto.addParameter("to", strEmailTo);
console.log('strEmailTo: ' + strEmailTo);
console.log('after cburlMailto.addParameter("to", strEmailTo);');
console.log("cburlMailto: " + cburlMailto.url);

// CC
if (strEmailCc != "") cburlMailto.addParameter("cc", strEmailCc);
console.log('strEmailCc: ' + strEmailCc);
console.log('after cburlMailto.addParameter("cc", strEmailCc);');
console.log('cburlMailto: ' + cburlMailto.url);

// BCC
if (strEmailBcc != "") cburlMailto.addParameter("bcc", strEmailBcc);
console.log('strEmailBcc: ' + strEmailBcc);
console.log('after cburlMailto.addParameter("bcc", strEmailBcc);');
console.log('cburlMailto: ' + cburlMailto.url);

// Subject line
cburlMailto.addParameter("subject", strPageTitle);
console.log('strPageTitle: ' + strPageTitle);
console.log('after cburlMailto.addParameter("subject", strPageTitle);');
console.log('cburlMailto: ' + cburlMailto.url);

// Body
cburlMailto.addParameter("body", strBody);
console.log('strBody: ' + strBody);
console.log('after cburlMailto.addParameter("body", strBody);');
console.log('cburlMailto: ' + cburlMailto.url);

// Open default mailer
console.log('before cburlMailto.open();');
cburlMailto.open();
console.log('after cburlMailto.open();');

// End
console.log('exit script');

This produced the following logging for me, as well as producing the expected pre-populated e-mail composition window.

enter script
after let cburlMailto = CallbackURL.create();
cburlMailto: ?
after cburlMailto.waitForResponse = false;
cburlMailto: ?
after cburlMailto.baseURL = "mailto:"
cburlMailto: mailto:?
strEmailTo: nameTo1<acctTo1@domain.tld>
after cburlMailto.addParameter("to", strEmailTo);
cburlMailto: mailto:?to=nameTo1%3CacctTo1@domain.tld%3E
strEmailCc: nameCC1<acctCC1@domain.tld>
after cburlMailto.addParameter("cc", strEmailCc);
cburlMailto: mailto:?cc=nameCC1%3CacctCC1@domain.tld%3E&to=nameTo1%3CacctTo1@domain.tld%3E
strEmailBcc: nameBCC1<acctBCC1@domain.tld>
after cburlMailto.addParameter("bcc", strEmailBcc);
cburlMailto: mailto:?bcc=nameBCC1%3CacctBCC1@domain.tld%3E&cc=nameCC1%3CacctCC1@domain.tld%3E&to=nameTo1%3CacctTo1@domain.tld%3E
strPageTitle: pageTitle
after cburlMailto.addParameter("subject", strPageTitle);
cburlMailto: mailto:?subject=pageTitle&to=nameTo1%3CacctTo1@domain.tld%3E&cc=nameCC1%3CacctCC1@domain.tld%3E&bcc=nameBCC1%3CacctBCC1@domain.tld%3E
strBody: nicknameList

pageURL


pageSelection
after cburlMailto.addParameter("body", strBody);
cburlMailto: mailto:?subject=pageTitle&to=nameTo1%3CacctTo1@domain.tld%3E&body=nicknameList%0A%0ApageURL%0A%0A%0ApageSelection&cc=nameCC1%3CacctCC1@domain.tld%3E&bcc=nameBCC1%3CacctBCC1@domain.tld%3E
before cburlMailto.open();
after cburlMailto.open();
exit script
Script step completed.

Thank you for calling out the code in your prior message. If I’d paid better attention, I could have saved myself a lot effort typing up the console messages. :person_facepalming:

Unfortunately, this line of code diverted my attention from the rest:

      cburlMailto.addParameter("to", strEmailTo);

I believe that the canonical definition of mailto is RFC6068 and section 2 lays out the syntax for a mailto Uniform Resource Identifier (URI).

Reveal additional detail
mailtoURI    = "mailto:" [ to ] [ hfields ]
      to           = addr-spec *("," addr-spec )
      hfields      = "?" hfield *( "&" hfield )
      hfield       = hfname "=" hfvalue
      hfname       = *qchar
      hfvalue      = *qchar
      addr-spec    = local-part "@" domain
      local-part   = dot-atom-text / quoted-string
      domain       = dot-atom-text / "[" *dtext-no-obs "]"
      dtext-no-obs = %d33-90 / ; Printable US-ASCII
                     %d94-126  ; characters not including
                               ; "[", "]", or "\"
      qchar        = unreserved / pct-encoded / some-delims
      some-delims  = "!" / "$" / "'" / "(" / ")" / "*"
                   / "+" / "," / ";" / ":" / "@"

As section 2 indicates, the portion of the URI before the question mark (“?”) delimiter does NOT contain the header characters to:

      mailtoURI    = "mailto:" [ to ] [ hfields ]
      to           = addr-spec *("," addr-spec )

Consequently, I don’t think that it is correct to use .addParameter method to assemble the to address(es).

Having said that, the to address(es) can NOT simply be added to the .baseURL property without encoding at least portions of them.

Reveal additional detail

As stated on page 4:

   1.  A number of characters that can appear in <addr-spec> MUST be
       percent-encoded.  These are the characters that cannot appear in
       a URI according to [STD66] as well as "%" (because it is used for
       percent-encoding) and all the characters in gen-delims except "@"
       and ":" (i.e., "/", "?", "#", "[", and "]").  Of the characters
       in sub-delims, at least the following also have to be percent-
       encoded: "&", ";", and "=".  Care has to be taken both when
       encoding as well as when decoding to make sure these operations
       are applied only once.

and

   4.  Percent-encoding can be used in the <domain> part of an
       <addr-spec> in order to denote an internationalized domain name.
       The considerations for <reg-name> in [STD66] apply.  In
       particular, non-ASCII characters MUST first be encoded according
       to UTF-8 [STD63], and then each octet of the corresponding UTF-8
       sequence MUST be percent-encoded to be represented as URI
       characters.  URI-producing applications MUST NOT use
       percent-encoding in domain names unless it is used to represent a
       UTF-8 character sequence.  When the internationalized domain name
       is used to compose a message, the name MUST be transformed to the
       Internationalizing Domain Names in Applications (IDNA) encoding
       [RFC5891] where appropriate.  URI producers SHOULD provide these
       domain names in the IDNA encoding, rather than percent-encoded,
       if they wish to maximize interoperability with legacy 'mailto'
       URI interpreters.

Thus, I encode the to address(es) and place them in the .baseURL property:

      strBaseURL = "mailto:" + encodeURI(strEmailTo);

Likewise, I think that the values (hfvalue) of the other portions of the URI, e.g., cc, bcc, subject, and body, must also be encoded (though I didn’t do so in my demonstration code above).

Finally, section 5 makes it clear exactly how to encode new lines in the body:

                               Also note that line breaks
      in the body of a message MUST be encoded with "%0D%0A".
Reveal additional detail
5.  Encoding

   [STD66] requires that many characters in URIs be encoded.  This
   affects the 'mailto' URI scheme for some common characters that might
   appear in addresses, header fields, or message contents.  One such
   character is space (" ", ASCII hex 20).  Note the examples below that
   use "%20" for space in the message body.  Also note that line breaks
   in the body of a message MUST be encoded with "%0D%0A".
   Implementations MAY add a final line break to the body of a message
   even if there is no trailing "%0D%0A" in the body <hfield> of the
   'mailto' URI.  Line breaks in other <hfield>s SHOULD NOT be used.

   When creating 'mailto' URIs, any reserved characters that are used in
   the URIs MUST be encoded so that properly written URI interpreters
   can read them.  Also, client software that reads URIs MUST decode
   strings before creating the mail message so that the mail message
   appears in a form that the recipient software will understand.  These
   strings SHOULD be decoded before showing the message to the sending
   user.

   Software creating 'mailto' URIs likewise has to be careful to encode
   any reserved characters that are used.  HTML forms are one kind of
   software that creates 'mailto' URIs.  Current implementations encode
   a space as '+', but this creates problems because such a '+' standing
   for a space cannot be distinguished from a real '+' in a 'mailto'
   URI.  When producing 'mailto' URIs, all spaces SHOULD be encoded as
   %20, and '+' characters MAY be encoded as %2B.  Please note that '+'
   characters are frequently used as part of an email address to
   indicate a subaddress, as for example in <bill+ietf@example.org>.

   The 'mailto' URI scheme is limited in that it does not provide for
   substitution of variables.  Thus, it is impossible to create a
   'mailto' URI that includes a user's email address in the message
   body.  This limitation also prevents 'mailto' URIs that are signed
   with public keys and other such variable information.

Anyway, I learned a lot reading RFC6068 and to be honest a lot of it is just too inscrutable for me to comprehend.

Correct. That’s why I included this in the first post.

It isn’t the right way to do it, but fundamentally trying to use the CallbackURL class to do this when it is not an x-callback-url is the underlying cause. Hence there’s this coercion into something that works from something that isn’t formed to spec.