gomail.v2 quoted-printable encoding issue on html email body - go

I'm using gomail.v2 to send html email messages. There is a problem with the encoding / decoding of the html. Here are the relevant the lines of code:
m := gomail.NewMessage()
// ... a bunch of lines removed, for clarity...
s, err := GenerateBodyHTML(si.MsgFName, si.Hostname, &p, t)
if err != nil {
return err
}
m.SetBody("text/html", s)
err = d.DialAndSend(m)
I generate the HTML as a string and I verified that it is correct -- I can write the contents of s to a file and open that file in a browser and it all looks fine. So, for example, here are a few lines from the middle of s:
<body>
<img src="http://ec2-54-152-108-202.compute-1.amazonaws.com:8275/iblogosm.png">
<p>Hello Tom,</p>
But in the 3 email clients I tested, here's what the raw message body looks like:
<body>
<img src=3D"http://ec2-54-152-108-202.compute-1.amazonaws.com:8275/iblogosm.png">
<p>Hello Tom,</p>
So the link and image tags don't work when the message renders. The equal sign is still in quoted-printable form. Here are the relevant headers received by the email client (I removed most of them for brevity, plus nothing else looked relevant):
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
What do I need to do to get the HTML decoded correctly on the mail client?

After digging into this problem further, the data inside the mail client should look the way it is shown above, and the HTML does render properly. It turns out that the first link I was looking at didn't work for a completely unrelated reason.

Related

Unable to send Gmail Message with Gmail API

Using the Gmail Service to send an email, but I'm having problem with the email format which needs to be passed to Google::Apis::GmailV1::Message, I'm passing raw parameter to it in the following format
email_raw = "From: <#{#google_account}>
To: <#{send_to}>
Subject: This is the email subject
The email body text goes here"
# raw is: The entire email message in an RFC 2822 formatted and base64url encoded string.
message_to_send = Google::Apis::GmailV1::Message.new(raw: Base64.encode64(email_raw))
response = #service.send_user_message("me", message_to_send)
This fails even when I pass email_raw without base64 encoding. I'm providing valid emails but it fails with an error
Google::Apis::ClientError (invalidArgument: Recipient address required)
I've checked Sending an email with ruby gmail api v0.9 and I also found this but it uses Mail class which I could not locate in the Gmail API Ruby client library. Currently, email_raw contains \n characters but I've tested it without it and it doesn't work.
Moreover, I also want to send attachments in a message.
We can easily offload the effort of forming a standardized and formatted email to this gem. Just include the gem in your project and do this
mail = Mail.new
mail.subject = "This is the subject"
mail.to = "someperson#gmail.com"
# to add your html and plain text content, do this
mail.part content_type: 'multipart/alternative' do |part|
part.html_part = Mail::Part.new(body: email_body, content_type: 'text/html')
part.text_part = Mail::Part.new(body: email_body)
end
# to add an attachment, do this
mail.add_file(params["file"].tempfile.path)
# when you do mail.to_s it forms a raw email text string which you can supply to the raw argument of Message object
message_to_send = Google::Apis::GmailV1::Message.new(raw: mail.to_s)
# #service is an instance of Google::Apis::GmailV1::GmailService
response = #service.send_user_message("me", message_to_send)
Mind that Gmail requires base64url encoding, not base64 encoding
See documentation:
raw string (bytes format)
The entire email message in an RFC 2822 formatted and base64url encoded string. Returned in messages.get and drafts.get responses when the format=RAW parameter is supplied.
A base64-encoded string.
I recommend you to test first with the Try this API - you can encode the message with online base64url encoders.
Then, when using Ruby, you can use the method:
Base64.urlsafe_encode64(message).
UPDATE
The problem seems to be your raw message body.
The message body should have the followind structure:
To: masroorh7#gmail.com Content-Type: multipart/alternative; boundary="000000000000f1f8eb05b18e8970" --000000000000f1f8eb05b18e8970 Content-Type: text/plain; charset="UTF-8" This is a test email --000000000000f1f8eb05b18e8970 Content-Type: text/html; charset="UTF-8" <div dir="ltr">This is a test email</div> --000000000000f1f8eb05b18e8970--
base64url encoded, this will look like:
encodedMessage = "VG86IG1hc3Jvb3JoN0BnbWFpbC5jb20NCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L2FsdGVybmF0aXZlOyBib3VuZGFyeT0iMDAwMDAwMDAwMDAwZjFmOGViMDViMThlODk3MCINCg0KLS0wMDAwMDAwMDAwMDBmMWY4ZWIwNWIxOGU4OTcwDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9IlVURi04Ig0KDQpUaGlzIGlzIGEgdGVzdCBlbWFpbA0KDQotLTAwMDAwMDAwMDAwMGYxZjhlYjA1YjE4ZTg5NzANCkNvbnRlbnQtVHlwZTogdGV4dC9odG1sOyBjaGFyc2V0PSJVVEYtOCINCg0KPGRpdiBkaXI9Imx0ciI-VGhpcyBpcyBhIHRlc3QgZW1haWw8L2Rpdj4NCg0KLS0wMDAwMDAwMDAwMDBmMWY4ZWIwNWIxOGU4OTcwLS0"
Thus, your message body should be:
Google::Apis::GmailV1::Message.new(raw:encodedMessage)

Suppressing MimeText Header

Noob, trying to automate sending infrequent personalized emails to a few dozen people, using Python and Thunderbird (I'm fine if I have to click send on each one). Unfortunately, I end up with the following extra header in the body of each message, which I would like to suppress:
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
My code is below:
def send_email(name, email_address):
os.system("thunderbird -compose to= 'to',subject='subject',body='body'")
tbirdPath = r'c:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe'
msg = MIMEMultipart()
msg['To'] = email_address
msg['Subject'] = 'Hello Friend'
# Body of Email - part of code with display problem (in Thunderbird)
body = "Dear {}".format(name) + '\n' + '\n' + "This is the body." + '\n' + '\n' + "The End." + '\n'
composeCommand = 'format=html,to={},subject={},body={}'.format(msg['To'], msg['Subject'], MIMEText(body))
subprocess.Popen([tbirdPath, '-compose', composeCommand])
When I print to the console, I get the body I expect, but get the extra lines/header when Thunderbird populates. From the first article below, it looks like get_payload can address this (but I have no idea how). Also, I moved to MIME after having no success otherwise, and suspect that is the source of my header. (String Formatting in Python/Thunderbird). Simple answers I can implement are preferred.
Strange unwanted header when reading text file with MIMEText
Python-Parse email Body and truncate MIME headers
You can just truncate as many chars as necessary at the start of body to get rid of the header, i.e.
body = body[96:]
composeCommand = "format=html,to={},subject={},body='{}'".format(msg['To'], msg['Subject'], body)

Delphi and Walmart Bulk API Upload - Internal server error

I am using Delphi XE5 and I am trying to send Items to Walmart using the Bulk API Upload but I am struggling with making proper connection with the server of Walmart.
I have already checked some similar problems here like Walmart API - payload mime type issue
The strangest thing of all is that the feed is actually accepted by Walmart (I see it in the list of feeds) but I receive Internal server error and then meaningless characters (probably type of encoding or just kind of error) like
(#$D#$A#$1F#$008B#8#0#0#0#0#0#0#3#$D'A±'#$D#$0080'0'#$C#4'AU~'#2'v ?d'#$0087#4'?'#1#$0091'OE'#$E#$008A'O'#$1E'®?O?'#$18#$0091#$A#$0091'M^'#$C'?;'#$1D'G'#$A'dR±U?oip?m'#$1A'AVS'#4#$16'R?iR'#$1B#$0095'RO?}u'#0'enIjM'#0#0#0)
So I use Indy IdHTTP and I have tried using TIdMultipartFormDataStream and also creating the payload on my own both with receiving the internal server error.
I presume I am breaking their parser somehow because if I miss one CR LF (#13#10) at the beginning I get a specific error
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:errors xmlns:ns2="http://walmart.com/"><ns2:error><ns2:code>SYSTEM_ERROR.GMP_GATEWAY_API</ns2:code><ns2:field>PDR-0012</ns2:field><ns2:description></ns2:description><ns2:info>System encountered some internal error.</ns2:info><ns2:severity>ERROR</ns2:severity><ns2:category>SYSTEM</ns2:category><ns2:causes/><ns2:errorIdentifiers/></ns2:error></ns2:errors>
If I add an extra CRLF at the end then I get another error stating that there is more than one feed in the request.
So I really presume that there is something which makes that server / parser return the Internal error but I don't get what and again I repeat that the feed actually gets accepted and then processed by the server in the case I receive this error.
Here is an example request with a payload created on my own:
Headers
POST /v3/feeds?feedType=item HTTP/1.1
Content-Type: multipart/form-data; boundary=qwerty
Content-Length: 5077
WM_SVC.NAME: Walmart Marketplace
WM_QOS.CORRELATION_ID: {67F0E2F9-5EAC-4E8C-9C90-650D8F7B3B7A}
WM_SEC.TIMESTAMP: 1528364885909
WM_SEC.AUTH_SIGNATURE: nSHgqzPOtzSR4wJU+U/vQJk+rk6Ke2QwodTHjzkjau2BonXZxiU9e+3NFPzaat2OUyc+vr0jqRk0H0QWTSC21PrI87mvqei5UJCJwNiIx0zVjAGpxsnIuvtIKkQsBpuUAa8C6SjTiTpDRsNt4IOxrk+tLWxlwQubWVCV+009a6o=
WM_CONSUMER.ID: 16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.CHANNEL.TYPE: 0f3e4dd4-0514-4346-b39d-af0e00ea066d
Host: marketplace.walmartapis.com
Accept: application/xml, */*
Accept-Encoding: identity
Accept-Language: en-US
User-Agent: Mozilla/3.0 (compatible; Indy Library)
Request
--qwerty
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<MPItemFeed xmlns:ns2="http://walmart.com/">
<MPItemFeedHeader>
<version>3.1</version>
<requestId>{694A5564-7E8E-46C7-B772-C944A8C9CF99}</requestId>
<mart>WALMART_US</mart>
</MPItemFeedHeader>
<MPItem>
<processMode>REPLACE_ALL</processMode>
<sku>w-cc-a1-4pk-white-l</sku>
<productIdentifiers>
<productIdentifier>
<productIdType>UPC</productIdType>
<productId>192082276845</productId>
</productIdentifier>
</productIdentifiers>
<MPProduct>
<SkuUpdate>Yes</SkuUpdate>
<productName>Caramel Cantina 4 Pack Sleeping Nursing Cross Front Maternity Bra (Large, White)</productName>
<ProductIdUpdate>Yes</ProductIdUpdate>
<category>
<ClothingCategory>
<Clothing>
<shortDescription>Stretchy comfort cross front sleep bra by Caramel Cantina. Whether for nursing / maternity or for wearing as a sleep bra (or both nursing and sleep bras) - you&apos;ll experience a wire free regular back strap sleep bra. This mid-weight fabric but light support wireless bra is great for all day wear and comfort. The straps are wider than a standard bra - offering a longer range of wear time. Great for wearing indoors (ie when you&apos;re kicking around the home or going to bed). These are padless - most moms use their own favorite nursing pads with these (though no pads are included). The front cups have two layers of fabric to add a bit more support (though we still call them our light support comfort bra). The criss cross front style allows for easy access while nursing your baby. 92% Nylon 8% Spandex fabric washes up nicely. Cool wash and cool dry. Hang dry for longer life. The straps narrowest point at the shoulders are 1.25-1.5 inches (depending on the size). The bras are super stretchy - if you&apos;re looking for something super supportive (ie - they pull you in really tight - more like a sports bra) these probably aren&apos;t what you&apos;re looking for. Not recommended for heavy sports activity. Best worn for activities such as walking, sleeping, lounging, light activity, running errands, etc.</shortDescription>
<keyFeatures>
<keyFeaturesValue>4 Pack Includes 4 Bras of the Same Design - Color Option Above</keyFeaturesValue>
<keyFeaturesValue>Soft Cup - Front Double Layered - No padding - Wireless</keyFeaturesValue>
<keyFeaturesValue>Super Soft Sleep In Style Bra - Great for Nursing or Sleeping</keyFeaturesValue>
<keyFeaturesValue>Criss Cross Front Design Gives Easy Nursing Access</keyFeaturesValue>
<keyFeaturesValue>Regular Straps - Scoop Back Design - Strap Width 1.25-1.5 Inches Depending on Size</keyFeaturesValue>
</keyFeatures>
<brand>Caramel Cantina</brand>
<mainImageUrl>https://www.dancecheerskate.com/_inv/D54173DB8F934DB2A4C68D24FAC087F5/wm-cc-a1-4pk-white.jpg</mainImageUrl>
<productSecondaryImageURL>
<productSecondaryImageURLValue>https://www.dancecheerskate.com/_inv/D54173DB8F934DB2A4C68D24FAC087F5/wm-cc-a1-wht-m1.jpg</productSecondaryImageURLValue>
</productSecondaryImageURL>
<color>White</color>
<clothingSize>Large</clothingSize>
<variantGroupId>cc-a1-4pk-parent</variantGroupId>
<variantAttributeNames>
<variantAttributeName>color</variantAttributeName>
<variantAttributeName>clothingSize</variantAttributeName>
</variantAttributeNames>
<countryOfOriginTextiles>Imported</countryOfOriginTextiles>
<fabricCareInstructions>
<fabricCareInstruction>Machine wash cold gentle cycle with like colors. Hang Dry. Do Not Bleach. Do Not Iron.</fabricCareInstruction>
</fabricCareInstructions>
<multipackQuantity>1</multipackQuantity>
<countPerPack>4</countPerPack>
<count>4</count>
<pattern>Solid</pattern>
<material>Nylon; Spandex</material>
<gender>Female</gender>
<ageGroup>
<ageGroupValue>Adult</ageGroupValue>
</ageGroup>
<clothingSizeGroup>Maternity</clothingSizeGroup>
<isSet>No</isSet>
<requiresTextileActLabeling>Yes</requiresTextileActLabeling>
<clothingTopStyle>Sleep Bra</clothingTopStyle>
<upperBodyStrapConfiguration>Regular</upperBodyStrapConfiguration>
<braStyle>Nursing</braStyle>
<clothingStyle>Maternity</clothingStyle>
<clothingFit>Maternity</clothingFit>
<isMaternity>Yes</isMaternity>
<swatchImages>
<swatchImage>
<swatchVariantAttribute>color</swatchVariantAttribute>
<swatchImageUrl>https://www.dancecheerskate.com/_inv/D54173DB8F934DB2A4C68D24FAC087F5/wm-cc-a1-4pk-white.jpg</swatchImageUrl>
</swatchImage>
</swatchImages>
</Clothing>
</ClothingCategory>
</category>
</MPProduct>
<MPOffer>
<price>25.00</price>
<ShippingWeight>
<measure>11.80</measure>
<unit>lb</unit>
</ShippingWeight>
<ProductTaxCode>2038895</ProductTaxCode>
</MPOffer>
</MPItem>
</MPItemFeed>
--qwerty--
The response which I get is as following
HTTP/1.1 500 Internal Server Error
Method: null
URI: null
WM_CONSUMER.ID: 16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.INTIMESTAMP:
WM_QOS.CORRELATION_ID: {67F0E2F9-5EAC-4E8C-9C90-650D8F7B3B7A}
WM_SEC.AUTH_TOKEN:
WM_SEC.REFRESH_AUTH_TOKEN:
WM_SVC.CLASS_NAME: com.walmart.services.impl.GMPGatewayService$$EnhancerBySpringCGLIB$$c88958a9
WM_SVC.ENV: prod
WM_SVC.INTIMESTAMP: 1528364905439
WM_SVC.METHOD_NAME: doPostMultiPart
WM_SVC.NAME: Walmart Marketplace
WM_SVC.OUTTIMESTAMP: 1528364905567
WM_SVC.SERVER_IP: 10.65.34.219
WM_SVC.SERVER_NAME: PartnerGMPService-5480119-4-96141283
WM_SVC.VERSION:
X-Powered-By: soari-interceptors-4.4.4
Content-Type: text/plain
Cteonnt-Length: 77
Server: web
Content-Encoding: gzip
Content-Length: 89
Expires: Thu, 07 Jun 2018 09:48:25 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Thu, 07 Jun 2018 09:48:25 GMT
Connection: close
And then those characters from above. Note the Cteonnt-Length: 77 header, it is not my TYPO it just comes in this way.
According to the support they have success using the following headers and ask me to use them, but I don't see any difference to what I make
WM_SVC.NAME:Walmart Marketplace
WM_QOS.CORRELATION_ID:123456abcdef
WM_SEC.AUTH_SIGNATURE:TnjevCf+voP9dmnafuCTFruwq6leBuAihSvag89WLieDRBsz7aULxgEqV71ZjIp572wVYPI07y6tdMutNLklDGwxvNdlJ2Q2xGvUIqjVPtqlhdcWvsmgqdpio7puQ4G03q1lReWzTquKecDEbB1ztH6ukj9F5rMe7d7PH8QkFsY=
WM_SEC.TIMESTAMP:1528152581896
WM_CONSUMER.ID:16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.CHANNEL.TYPE:0f3e4dd4-0514-4346-b39d-af0e00ea066d
Accept:application/xml
Host:marketplace.walmartapis.com
Content-Type:multipart/form-data
I already make successful communication with eBay, Amazon, PayPal, Magento and many more, but here obviously I am missing something and really don't know how to solve it.
Thank you for reading this, and hopefully someone can give a clue what is going on here.
EDIT I am adding some Delphi code as required
Version 1 without TIdMultipartFormDataStream
function TdmSyncWallmart.CreateItemFeed(aIDPlatforms: String; aLastChanged,
aCurrDate: TDateTime; var aReqID: String): TStringStream;
var
aXML: TNativeXML;
aNode: TXmlNode;
aItemNode: TXmlNode;
aCatNode: TXmlNode;
aTmpNode: TXmlNode;
aShortDesc: String;
i: Integer;
aIDItems: String;
begin
Result := nil;
dstListPlatformsSyncItemsWallmart.Close;
dstListPlatformsSyncItemsWallmart.Parameters.ParamByName('#IDPlatforms').Value := aIDPlatforms;
dstListPlatformsSyncItemsWallmart.Parameters.ParamByName('#LastChanged').Value := aLastChanged;
dstListPlatformsSyncItemsWallmart.Parameters.ParamByName('#CurrDate').Value := aCurrDate;
dstListPlatformsSyncItemsWallmart.Open;
if dstListPlatformsSyncItemsWallmart.IsEmpty then
Exit;
aReqID := GenGuid;
aXML := TNativeXml.CreateName('MPItemFeed');
try
aXML.Declaration.AttributeAdd('standalone', 'yes');
aXML.Root.AttributeAdd('xmlns:ns2', 'http://walmart.com/');
//Header
aNode := aXML.Root.NodeNew('MPItemFeedHeader');
with aNode.NodeNew('version') do
Value := '3.1';
with aNode.NodeNew('requestId') do
Value := aReqID;
with aNode.NodeNew('mart') do
Value := 'WALMART_US';
//... some code for generating the XML
Result := TStringStream.Create;
aXML.XmlFormat := xfReadable;
Result.WriteString('--qwerty'#13#10#13#10{'Content-Disposition: form-data; filename="wallmartreq.xml"'#13#10}+aXML.WriteToString +'--qwerty--'#13#10);
Result.Position := 0;
finally
aXML.Free;
end;
end;
function TdmSyncWallmart.CreateHTTP(aContentType: String): TIdHTTP;
begin
Result := TIdHTTP.Create(nil);
Result.ConnectTimeout:=5000;
Result.ReadTimeout:=20000;
Result.ProtocolVersion:=pv1_1;
Result.HTTPOptions := [hoForceEncodeParams, hoKeepOrigProtocol];
Result.HandleRedirects:=True;
Result.IOHandler := SSLHandler;
Result.Request.Accept:='application/xml, */*';
Result.Request.AcceptLanguage:='en-US';
Result.Request.ContentType:=aContentType;
// Result.Request.CharSet:='utf-8';
Result.Intercept := IdLogEvent1;
end;
procedure TdmSyncWallmart.AddCustomHeaders(aHTTP: TIdHTTP; aSign,
aTimeStamp: String);
begin
aHTTP.Request.CustomHeaders.AddValue('WM_SVC.NAME', 'Walmart Marketplace');
aHTTP.Request.CustomHeaders.AddValue('WM_QOS.CORRELATION_ID', GenGuid);
aHTTP.Request.CustomHeaders.AddValue('WM_SEC.TIMESTAMP', aTimeStamp);
aHTTP.Request.CustomHeaders.AddValue('WM_SEC.AUTH_SIGNATURE', aSign);
aHTTP.Request.CustomHeaders.AddValue('WM_CONSUMER.ID', dstPlatformsUserName.AsString);
aHTTP.Request.CustomHeaders.AddValue('WM_CONSUMER.CHANNEL.TYPE', dstPlatformsPassword.AsString);
end;
//some code for debugging purposes
procedure TdmSyncWallmart.IdLogEvent1Send(ASender: TIdConnectionIntercept;
var ABuffer: TIdBytes);
var
aList: TStringList;
begin
aList := TStringList.Create;
try
aList.Text := BytesToString(ABuffer);
aList.SaveToFile('e:\wm_req_txt.txt');
finally
aList.Free;
end;
end;
procedure TdmSyncWallmart.IdLogEvent1Receive(ASender: TIdConnectionIntercept;
var ABuffer: TIdBytes);
var
aList: TStringList;
begin
aList := TStringList.Create;
try
aList.Text := BytesToString(ABuffer);
aList.SaveToFile('e:\wm_Resp.txt');
finally
aList.Free;
end;
end;
Actual call
procedure TdmSyncWallmart.SendItems(aIDPlatforms: String; aLastChanged,
aCurrDate: TDateTime);
var
aSign, aTimeStamp: String;
aURL: String;
aHTTP: TIdHTTP;
aRes: String;
aStrm: TMemoryStream;
aReqID: String;
aXML: TNativeXml;
aNode: TXmlNode;
aErrMsg: String;
begin
aURL := dstPlatformsURLAddress.AsString+'/v3/feeds?feedType=item';
GenrateSignature(aSign, aTimeStamp, aURL, 'POST');
if aSign = '' then
Exit;
aHTTP := CreateHTTP('multipart/form-data;boundary=qwerty');
AddCustomHeaders(aHTTP, aSign, aTimeStamp);
try
aStrm := CreateItemFeed(aIDPlatforms, aLastChanged, aCurrDate, aReqID);
if aStrm <> nil then
begin
try
aRes := TIdURI.URLDecode(aHTTP.Post(aURL, aStrm));
aXML := TNativeXml.Create(nil);
try
aXML.ReadFromString(aRes);
aNode := aXML.Root.FindNode('feedId');
cmdUpdFeedID.Parameters.ParamByName('ID').Value := aRes;
cmdUpdFeedID.Parameters.ParamByName('FeedID').Value := aNode.Value;
cmdUpdFeedID.Execute;
finally
aXML.Free;
end;
except
on E:Exception do
begin
if E is EIdHTTPProtocolException then
aErrMsg := E.Message + #13#10 + (E as EIdHTTPProtocolException).ErrorMessage
else
aErrMsg := E.Message;
dmMain.InsLog(cLTError, aErrMsg, aIDPlatforms);
end;
end;
aStrm.Free;
end;
finally
aHTTP.Free;
end;
end;
EDIT2
procedure TdmSyncWallmart.SendItems(aIDPlatforms: String; aLastChanged,
aCurrDate: TDateTime);
var
aSign, aTimeStamp: String;
aURL: String;
aHTTP: TIdHTTP;
aRes: String;
aStrm: TMemoryStream;
aReqID: String;
aXML: TNativeXml;
aNode: TXmlNode;
aErrMsg: String;
aIdStream: TIdMultipartFormDataStream;
begin
aURL := dstPlatformsURLAddress.AsString+'/v3/feeds?feedType=item';
GenrateSignature(aSign, aTimeStamp, aURL, 'POST');
if aSign = '' then
Exit;
aHTTP := CreateHTTP('multipart/form-data');
AddCustomHeaders(aHTTP, aSign, aTimeStamp);
try
aStrm := TStringStream.Create();
begin
try
aIdStream := TIdMultiPartFormDataStream.Create;
aIdStream.AddFile('file', 'e:\wm_req.xml', 'multipart/form-data');
aIdStream.Position := 0;
astrm.LoadFromStream(aIdStream);
aRes := TIdURI.URLDecode(aHTTP.Post(aURL, aStrm));
aXML := TNativeXml.Create(nil);
try
aXML.ReadFromString(aRes);
aNode := aXML.Root.FindNode('feedId');
cmdUpdFeedID.Parameters.ParamByName('ID').Value := aRes;
cmdUpdFeedID.Parameters.ParamByName('FeedID').Value := aNode.Value;
cmdUpdFeedID.Execute;
finally
aXML.Free;
end;
except
on E:Exception do
begin
if E is EIdHTTPProtocolException then
aErrMsg := E.Message + #13#10 + (E as EIdHTTPProtocolException).ErrorMessage
else
aErrMsg := E.Message;
dmMain.InsLog(cLTError, aErrMsg, aIDPlatforms);
end;
end;
aStrm.Free;
end;
finally
aHTTP.Free;
end;
end;
This is the sent data
----------060718213723831
Content-Disposition: form-data; name="file"; filename="wm_req.xml"
Content-Type: multipart/form-data
Content-Transfer-Encoding: binary
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<MPItemFeed xmlns:ns2="http://walmart.com/">
<MPItemFeedHeader>
<version>3.1</version>
<requestId>{164726BF-288E-4953-A1DA-1EC7595C5725}</requestId>
<mart>WALMART_US</mart>
</MPItemFeedHeader>
<MPItem>
<processMode>REPLACE_ALL</processMode>
<sku>w-cc-a1-4pk-white-l</sku>
<productIdentifiers>
<productIdentifier>
<productIdType>UPC</productIdType>
<productId>192082276845</productId>
</productIdentifier>
</productIdentifiers>
<MPProduct>
..... the other part of the XML
</MPProduct>
<MPOffer>
<price>25.00</price>
<ShippingWeight>
<measure>11.80</measure>
<unit>lb</unit>
</ShippingWeight>
<ProductTaxCode>2038895</ProductTaxCode>
</MPOffer>
</MPItem>
</MPItemFeed>
----------060718213723831--
Returned data captured by the intercepting part is the same as the one already posted with again the Cteonnt-Length: 77 header, but I guess if I succeed in reading the gzip data, I can reach an error which can be forwarded to the support.
HTTP/1.1 500 Internal Server Error
Method: null
URI: null
WM_CONSUMER.ID: 16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.INTIMESTAMP:
WM_QOS.CORRELATION_ID: {CB0208AB-2135-4A12-B45F-DD2889273971}
WM_SEC.AUTH_TOKEN:
WM_SEC.REFRESH_AUTH_TOKEN:
WM_SVC.CLASS_NAME: com.walmart.services.impl.GMPGatewayService$$EnhancerBySpringCGLIB$$2ca3f3b6
WM_SVC.ENV: prod
WM_SVC.INTIMESTAMP: 1528396669253
WM_SVC.METHOD_NAME: doPostMultiPart
WM_SVC.NAME: Walmart Marketplace
WM_SVC.OUTTIMESTAMP: 1528396669365
WM_SVC.SERVER_IP: 10.65.34.2
WM_SVC.SERVER_NAME: PartnerGMPService-5480119-1-96141274
WM_SVC.VERSION:
X-Powered-By: soari-interceptors-4.4.4
Content-Type: text/plain
Cteonnt-Length: 77
Server: web
Content-Encoding: gzip
Content-Length: 89
Vary: Accept-Encoding
Expires: Thu, 07 Jun 2018 18:37:49 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Thu, 07 Jun 2018 18:37:49 GMT
Connection: close
EDIT3
For my Edit1, I am not adding Content-Disposition at all (it is commented) - with so many tests I have left it in my post here, so sorry for the confusion.
For Edit2 - thanks for pointing this out, actually I made my first tests without transforming into TStringStream, but I wanted to see what exactly goes to them.
Anyway now I directly used the TIdMultipartFormDataStream and changed the code in this way
aIdStream := TIdMultiPartFormDataStream.Create;
with aIdStream.AddFile('xml', 'e:\wm_req.xml', 'application/xml') do
begin
FileName := '';
//ContentType := '';
FileName := 'wmreq.xml';//somewhere I think I saw that only letters are allowed here
end;
aIdStream.Position := 0;
aStrm := TMemoryStream.Create;
//aRes := TIdURI.URLDecode(aHTTP.Post(aURL, aIdStream));
aHTTP.Post(aURL, aIdStream, aStrm);
and thanks to Remy I added TIdCompressorZLib and now I get meaningful error:
HTTP/1.1 500 Internal Server Error
No message body writer has been found for response class FeedAcknowledgement.
I guess this is from them, not from me, will need to check with them.
EDIT4 Solution
OK, crazy or not, maybe my problem, but once Remy said that I am not asking for compressed answer and they send me such, I decided to change // Result.Request.Accept:='application/xml, */*'; to only // Result.Request.Accept:='application/xml'; and all started working properly! In fact having */* means that I am accepting everything, isn't it? It doesn't matter if you are using TIdMultipartFormDataStream or the body is manually prepared, it just works. Thank you Remy for the different point of view, another pair of eyes always helps. Crazy ... one week of stupid tests!
In your first Edit, you are not populating the TStringStream correctly. Use this instead:
Result.WriteString('--qwerty'#13#10{'Content-Disposition: form-data; name="file"; filename="wallmartreq.xml"'#13#10}#13#10 + aXML.WriteToString + #13#10'--qwerty--'#13#10);
In your second Edit, you are providing the wrong ContentType to TIdMultipartFormDataStream.AddFile(). You need to use application/xml instead of multipart/form-data:
aIdStream.AddFile('file', 'e:\wm_req.xml', 'application/xml');
Or, leave the ContentType blank (in which case, application/octet-stream will be used instead):
aIdStream.AddFile('file', 'e:\wm_req.xml');
If you don't want to send a ContentType at all, you can set the TIdFormDataField.ContentType property to a blank string:
with aIdStream.AddFile('file', 'e:\wm_req.xml') do
begin
FileName := ''; // needed so ContentType setter will not use a default value...
ContentType := '';
FileName := 'wm_req.xml'; // or whatever you want, or leave blank...
end;
But, more importantly, you are NOT specifying a boundary attribute for the TIdHTTP.Request.ContentType header anymore. In fact, you SHOULD NOT be saving the TIdMultipartFormDataStream to a TStringStream and then posting the TStringStream at all. You SHOULD be posting the TIdMultipartFormDataStream itself instead. That will automatically set the TIdHTTP.Request.ContentType to multipart/form-data, as well as give it a correct boundary attribute (whose value is generated by TIdMultipartFormDataStream):
aHTTP.Post(aURL, aIdStream)
Now, that being said, in both cases, you need to remove the call to TIdURI.URLDecode() on the return value of TIdHTTP.Post(). The server should be sending you raw XML, not URL-encoded XML, so you need to process the XML as-is and not URL-decode it at all.
Also, since XML is technically binary data that could be encoded in any charset that may or may not be present in the HTTP response headers, you SHOULD have your XML parser process the XML exactly as it is sent by the server. To do that, use the overloaded version of TIdHTTP.Post() that populates a TStream instead of returns a String. Have it receive the server's XML into a TMemoryStream, and then load that stream into your parser (TNativeXml has a LoadFromStream() method).
Lastly, you show that the server's response has a Content-Encoding: gzip response header, but I do not see you assigning any object to the TIdHTTP.Compressor property, or manually setting the TIdHTTP.Request.AcceptEncoding property, so the server SHOULD NOT be sending the response body in a compressed format since you are not asking for that format. TIdHTTP will not automatically decompress the data without a Compressor assigned.
If the server really is sending compressed data without being asked, that is a bug on the server side that you should report to Walmart.

Send gmail messages with google-api-ruby-client '0.9.pre3'

Working through sending gmail with the newer google-api-ruby-client in a rails 4 application.
require 'google/apis/gmail_v1'
Gmail = Google::Apis::GmailV1
class MailService
def initialize(params)
#params = params
end
def call
message = Gmail::Message.new
service = Gmail::GmailService.new
message.raw = (redacted)
service.request_options.authorization = current_user.token.fresh_token
result = service.send_user_message(current_user.email, message)
end
end
And this is the result from the call to the API:
Sending HTTP post https://www.googleapis.com/gmail/v1/users/me/messages/send?
200
#<Hurley::Response POST https://www.googleapis.com/gmail/v1/users/me/messages/send == 200 (63 bytes) 858ms>
Success - #<Google::Apis::GmailV1::Message:0x007fc9cf9b52dd
#id="15096369c05cdb1d",
#thread_id="15096369c05cdb1d">
The raw message sends without issue from the API explorer but when executed from my application I get a bounce email in my inbox. In the above example the redacted sample is a valid RFC 2822 formatted base-64 url safe string and fresh_token represents the oauth2 access token for the current user.
A look at the bounced mail
Bounce <nobody#gmail.com>
2:43 PM (19 minutes ago)
to me
An error occurred. Your message was not sent.
Anyone have any thoughts? It seems like perhaps my (sender) email is being picked up in the raw message but not the recipient... Though I suppose the API could be forwarding the bounce based on my oauth access token.
I very much appreciate any help. Thanks!
EDIT: Solution was to pass the RFC 2822 string as raw property without base64 encoding.
Steve Bazyl seems to be correct. The documentation on send_user_message is wrong as of (0.9.13). For raw, it says: "The entire email message in an RFC 2822 formatted and base64url encoded string. Returned in messages.get and drafts.get responses when the format=RAW parameter is supplied. Corresponds to the JSON property raw." As far as I can tell, this is simply incorrect.
I encountered this issue when updating from google-api-client 0.8 to 0.9 and removing the base64 encoding solved the problem. I.e. call in 0.8:
response = #service.execute(
api_method: api.users.messages.to_h['gmail.users.messages.send'],
body_object: {
raw: Base64.urlsafe_encode64(mail.to_s)
},
parameters: {
userId: 'me',
}
)
became
message = { raw: mail.to_s }
res = #service.send_user_message('me', message, {})
in 0.9.
Reported as https://github.com/google/google-api-ruby-client/issues/474.

special character in a a tags href link when making a link to an email is not parsed right to the mail client

I got the bug as Molizza Firefox post
https://bugzilla.mozilla.org/show_bug.cgi?id=230096
I would like to know this bug is fixed or not? anybody still has this issue?
Looks like some kind of UTF-8 issue. Either the mail is sent using UTF 8 (and interpreted as ANSI) or something similar happens when parsing DOM or evaluating the link.
But in general, you shouldn't put non-ANSI-characters into URLs. Instead, escape them using %HEXCODE. Also space characters should be replaced using %20 or +.
Most programs (like web browsers or in this case Outlook) accept space characters and other things, but you still shouldn't rely on that behavior as it might go wrong (as it did here).
Here is my html code
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<a id="test">test</a>
<script>
function buildMailTo(address, subject, body) {
var strMail = 'mailto:' + encodeURIComponent(address)
+ '?subject=' + encodeURIComponent(subject)
+ '&body=' + encodeURIComponent(body);
return strMail;
}
var strTest = buildMailTo('abc#xyz.com', 'Foo&foo', 'Chỉ sau 2/3 thời gian làm bài thi tốt nghiệp môn Toán, nhiều thí sinh đã ra khỏi phòng với gương mặt phấn khởi. Nhiều em tự tin sẽ được trên 8 điểm.');
document.getElementById('test').href = strTest;
</script>
</body>
</html>

Resources