Upload to Skip Web Server - indy

I'm trying to upload a JSON file to the SKIP (getskip.com) web server and I am getting socket error 10054. They are not very good at giving samples of how, since they think everyone is using cUrl, but we are still using Delphi XE6 with Indy.
Here is what they provided.
For Java using OK HTTP:
OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
RequestBody body = RequestBody.create(mediaType, "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"path\"; filename=\"<<file_name>>\"\r\nContent-Type: application/vnd.novadigm.ext\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\n[{\"file_key\": \"path\", \"store_id\"::<<store_id>>}]\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--");
Request request = new Request.Builder()
  .url("https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json")
  .post(body)
  .addHeader("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
  .addHeader("Authorization", "Bearer <<api_user_token>>")
  .addHeader("Content-Type", "application/x-www-form-urlencoded")
  .addHeader("User-Agent", "PostmanRuntime/7.13.0")
  .addHeader("Accept", "*/*")
  .addHeader("Cache-Control", "no-cache")
  .addHeader("Postman-Token", "de9932d2-bda0-4df6-8a9a-e2d4f74c2048,d59a9861-1a65-471a-8ada-6e025e985dca")
  .addHeader("Host", "upload.goskip.com:8080")
  .addHeader("accept-encoding", "gzip, deflate")
  .addHeader("content-length", "407")
  .addHeader("Connection", "keep-alive")
  .addHeader("cache-control", "no-cache")
  .build();
Response response = client.newCall(request).execute();
Java using Unirest:
HttpResponse<String> response = Unirest.post("https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json")
  .header("content-type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW")
  .header("Authorization", "Bearer <<api_user_token>>")
  .header("Content-Type", "application/x-www-form-urlencoded")
  .header("User-Agent", "PostmanRuntime/7.13.0")
  .header("Accept", "*/*")
  .header("Cache-Control", "no-cache")
  .header("Postman-Token", "de9932d2-bda0-4df6-8a9a-e2d4f74c2048,d1ea454e-594c-4746-9de7-e813377ff095")
  .header("Host", "upload.goskip.com:8080")
  .header("accept-encoding", "gzip, deflate")
  .header("content-length", "407")
  .header("Connection", "keep-alive")
  .header("cache-control", "no-cache")
  .body("------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"path\"; filename=\"<<file_name>>\"\r\nContent-Type: application/vnd.novadigm.ext\r\n\r\n\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"data\"\r\n\r\n[{\"file_key\": \"path\", \"store_id\":<<store_id>>}]\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--")
  .asString();
PHP using HttpRequest:
<?php
$request = new HttpRequest();
$request->setUrl('https://upload.goskip.com:8080/v2/backoffice/files/pricebook');
$request->setMethod(HTTP_METH_POST);
$request->setQueryData(array(
  'type' => 'json'
));
$request->setHeaders(array(
  'cache-control' => 'no-cache',
  'Connection' => 'keep-alive',
  'content-length' => '407',
  'accept-encoding' => 'gzip, deflate',
  'Host' => 'upload.goskip.com:8080',
  'Postman-Token' => 'de9932d2-bda0-4df6-8a9a-e2d4f74c2048,c7acbc0a-6450-43d1-b4ca-bc2a56cebc4e',
  'Cache-Control' => 'no-cache',
  'Accept' => '*/*',
  'User-Agent' => 'PostmanRuntime/7.13.0',
  'Content-Type' => 'application/x-www-form-urlencoded',
  'Authorization' => 'Bearer <<api_user_token>>',
  'content-type' => 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
));
$request->setBody('------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="path"; filename="<<file_name>>"
Content-Type: application/vnd.novadigm.ext
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="data"
[{"file_key": "path", "store_id"::<<store_id>>}]
------WebKitFormBoundary7MA4YWxkTrZu0gW--');
try {
  $response = $request->send();
  echo $response->getBody();
} catch (HttpException $ex) {
  echo $ex;
}
Here is what I tried in Indy. I pasted the JSON file in to a Memo on the Form:
function TFormJsonWrite.HTTPPost: String;
var
JsonToSend : TStringStream;
Response: String;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
begin
IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create;
IdHTTP1 := TIdHTTP.Create;
IdHTTP1.Request.CharSet := 'utf-8';
JsonToSend := TStringStream.Create(memolog.text, TEncoding.UTF8);
IdHTTP1.Request.ContentDisposition := 'form-data; name=[{"file_key":''' + memolog.text + ''', "store_id"::001}]------WebKitFormBoundary7MA4YWxkTrZu0gW--';
IdHTTP1.Request.UserAgent := 'Mozilla/3.0 (compatible; Indy Library)';
IdHTTP1.Request.ContentType := 'multipart/form-data';
IdHTTP1.Request.Accept := '*/*';
IdHTTP1.Request.AcceptEncoding := 'gzip,deflate';
IdHTTP1.Request.ContentLength := -1;
IdHTTP1.Request.CacheControl := 'no-cache';
IdHTTP1.Request.Connection := 'keep-alive';
IdHTTP1.Request.BasicAuthentication := false;
IdHTTP1.Request.Host := 'upload.goskip.com:8080';
IdHTTP1.Request.CustomHeaders.Clear;
IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer eyJhbGc.......................';
IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := 'de9932d2-bda0-4df6-8a9a-e2d4f74c2048,d59a9861-1a65-471a-8ada-6e025e985dca';
IdHTTP1.ReadTimeout := 50000;
IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
Response := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', JsonToSend);
HTTPPost := Response;
JsonToSend.Free;
IdHTTP1.Free;
end;
curl
Kim#KIMNEW MINGW64 ~
$ curl -F "file=#"C:/mydata/Items-114946.json -H "authorization:Bearer eyJ........................." https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json&store_id=001
[1] 13296
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
Kim#KIMNEW MINGW64 ~
100 951k 100 53 100 951k 53 968k 0:00:01 --:--:-- 0:00:01 967k{"message":"#/data: null value where array expected"}
They updating all their endpoints this is the latest I got.
curl -X POST 'https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json' -H 'Authorization: Bearer <api_user_token>' -F path=#<path_to_first_file> -F path1=#<path_to_second_file> -F 'data=[{"file_key":"path","store_id":<store_id_for_first_file>},{"file_key":"path1","store_id":<store_id_for_second_file>}]'

Your Delphi code is all wrong.
You are setting the wrong Content-Disposition header to the wrong value.
You are posting the JSON data as-is without wrapping it inside of MIME at all.
DO NOT set the TIdHTTP.Request.AcceptEncoding property manually. You are not setting up the TIdHTTP to enable compression support, but you are giving the server permission to send compressed responses, which TIdHTTP will not be able to decompress for you.
The correct way to send a multipart/form-data request using TIdHTTP is to use the overloaded Post() method that takes a TIdMultipartFormDataStream as input, like this:
function TFormJsonWrite.HTTPPost: String;
var
JsonToSend : String;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
PostData: TIdMultipartFormDataStream;
begin
JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';
IdHTTP1 := TIdHTTP.Create;
try
IdHTTP1.ReadTimeout := 50000;
IdHTTP1.Request.BasicAuthentication := false;
IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';
IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
PostData := TIdMultipartFormDataStream.Create;
try
PostData.AddFormField('path', '', '', 'application/vnd.novadigm.ext').FileName := '<<file_name>>';
PostData.AddFormField('data', JsonToSend, 'utf-8', 'application/json').ContentTransfer := '8bit';
Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
finally
PostData.Free;
end;
finally
IdHTTP1.Free;
end;
end;
If, by chance, the server has issues with how TIdMultipartFormDataStream formats the MIME data (ie, if the server rejects the MIME Content-Type and/or Content-Transfer-Encoding headers that are generated by TIdMultipartFormDataStream for each MIME field, as it does not yet conform to RFC 7578, which is used by HTML5), you can format the MIME data manually to match SKIP's examples exactly, like this:
function TFormJsonWrite.HTTPPost: String;
var
JsonToSend : String;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
PostData: TStringStream;
begin
JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';
IdHTTP1 := TIdHTTP.Create;
try
IdHTTP1.ReadTimeout := 50000;
IdHTTP1.Request.ContentType := 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW';
IdHTTP1.Request.BasicAuthentication := false;
IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';
IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
PostData := TStringStream.Create(
'------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL +
'Content-Disposition: form-data; name="path"; filename="<<file_name>>"' + EOL +
'Content-Type: application/vnd.novadigm.ext' + EOL +
EOL +
EOL +
'------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL +
'Content-Disposition: form-data; name="data"' + EOL +
EOL +
JsonToSend + EOL +
'------WebKitFormBoundary7MA4YWxkTrZu0gW--',
TEncoding.UTF8
);
try
Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
finally
PostData.Free;
end;
finally
IdHTTP1.Free;
end;
end;
UPDATE: based on the new curl commands you have provided, the original examples you showed do not match the commands. Try something more like this instead:
function TFormJsonWrite.HTTPPost: String;
var
JsonToSend : String;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
PostData: TIdMultipartFormDataStream;
begin
JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';
IdHTTP1 := TIdHTTP.Create;
try
IdHTTP1.ReadTimeout := 50000;
IdHTTP1.Request.BasicAuthentication := false;
IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';
IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
PostData := TIdMultipartFormDataStream.Create;
try
PostData.AddFile('path', '<<path_to_file>>').ContentTransfer := 'binary';
PostData.AddFormField('data', JsonToSend, 'utf-8', 'application/json').ContentTransfer := '8bit';
Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
finally
PostData.Free;
end;
finally
IdHTTP1.Free;
end;
end;
Or:
function TFormJsonWrite.HTTPPost: String;
var
JsonToSend : String;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
PostData: TMemoryStream;
FS: TIdReadFileExclusiveStream;
begin
JsonToSend := '[{"file_key": "path", "store_id": <<store_id>>}]';
IdHTTP1 := TIdHTTP.Create;
try
IdHTTP1.ReadTimeout := 50000;
IdHTTP1.Request.ContentType := 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW';
IdHTTP1.Request.BasicAuthentication := false;
IdHTTP1.Request.CustomHeaders.Values['Authorization'] := 'Bearer ...';
IdHTTP1.Request.CustomHeaders.Values['Postman-Token'] := '...';
IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP1);
IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvTLSv1_2;
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
PostData := TMemoryStream.Create;
try
WriteStringToStream(PostData, '------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL);
WriteStringToStream(PostData, 'Content-Disposition: form-data; name="path"; filename="<<file_name>>"' + EOL);
WriteStringToStream(PostData, 'Content-Type: ' + GetMIMETypeFromFile('<<file_name>>') + EOL);
WriteStringToStream(PostData, 'Content-Transfer-Encoding: binary' + EOL);
WriteStringToStream(PostData, EOL);
FS := TIdReadFileExclusiveStream.Create('<<path_to_file>>', fmOpenRead or fmShareDenyWrite);
try
PostData.CopyFrom(FS, 0);
finally
FS.Free;
end;
WriteStringToStream(PostData, EOL);
WriteStringToStream(PostData, '------WebKitFormBoundary7MA4YWxkTrZu0gW' + EOL);
WriteStringToStream(PostData, 'Content-Disposition: form-data; name="data"' + EOL);
WriteStringToStream(PostData, EOL);
WriteStringToStream(PostData, JsonToSend + EOL, IndyTextEncoding_UTF8);
WriteStringToStream(PostData, '------WebKitFormBoundary7MA4YWxkTrZu0gW--');
PostData.Position := 0;
Result := IdHTTP1.Post('https://upload.goskip.com:8080/v2/backoffice/files/pricebook?type=json', PostData);
finally
PostData.Free;
end;
finally
IdHTTP1.Free;
end;
end;

Related

HMAC validation in GoLang

I have a HMAC validation code in PHP which is like
$calculatedHmac = base64_encode(hash_hmac('sha256', json_encode($input), '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', true));
if($calculatedHmac === $this->app['request']->header('X-Shopify-Hmac-Sha256'))
{
echo "Correct HMAC";
}
Now, I am trying to do the same validation in GoLang. The corresponding code:
hash := hmac.New(sha256.New, []byte('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'))
inputData, _ := json.Marshal(input)
fmt.Println(string(inputData))
hash.Write(inputData)
base64EncodedStr := base64.StdEncoding.EncodeToString(hash.Sum(nil))
hexEncodedStr := hex.EncodeToString(hash.Sum(nil))
if requestHmac == base64EncodedStr {
logger.GetEntryWithContext(ctx).Infow("JsonMarshal.Base64 worked", nil)
} else if hexEncodedStr == requestHmac {
logger.GetEntryWithContext(ctx).Infow("JsonMarshal.Hex Encoding worked", nil)
}
Anything that I am doing wrong???
Input data:
Actual HMAC: EUF2feuOuaQzh0bhSE8eCaI23BM0Qh+yBtmxNWyt8JQ=
JSON Input:
{"address1":"Ghar ka Address","address2":null,"auto_configure_tax_inclusivity":null,"checkout_api_supported":false,"city":"Bangalore ","cookie_consent_level":"implicit","country":"IN","country_code":"IN","country_name":"India","county_taxes":true,"created_at":"2021-03-06T11:54:22+05:30","currency":"INR","customer_email":"amit.vickey#razorpay.com","domain":"rzp-sample-store.myshopify.com","eligible_for_card_reader_giveaway":false,"eligible_for_payments":false,"email":"amit.vickey#razorpay.com","enabled_presentment_currencies":["INR"],"finances":true,"force_ssl":true,"google_apps_domain":null,"google_apps_login_enabled":null,"has_discounts":false,"has_gift_cards":false,"has_storefront":true,"iana_timezone":"Asia/Calcutta","id":55091921055,"latitude":12.9142148,"longitude":77.61210129999999,"money_format":"Rs. {{amount}}","money_in_emails_format":"Rs. {{amount}}","money_with_currency_format":"Rs. {{amount}}","money_with_currency_in_emails_format":"Rs. {{amount}}","multi_location_enabled":true,"myshopify_domain":"rzp-sample-store.myshopify.com","name":"RZP Sample Store","password_enabled":true,"phone":"","plan_display_name":"Developer Preview","plan_name":"partner_test","pre_launch_enabled":false,"primary_locale":"en","primary_location_id":61106618527,"province":"Karnataka","province_code":"KA","requires_extra_payments_agreement":false,"setup_required":false,"shop_owner":"Amit Vickey","source":null,"tax_shipping":null,"taxes_included":false,"timezone":"(GMT+05:30) Asia/Calcutta","updated_at":"2021-03-06T12:06:18+05:30","visitor_tracking_consent_preference":"allow_all","weight_unit":"kg","zip":"560076"}

Sending mail to a certain thread failed, even though I set headers "In-Reply-To" & "References"

Environment
Golang
GmailAPI(google.golang.org/api/gmail/v1)
Problem
When I tried to send a mail to a certain thread, with In-Reply-To and References headers set, the mail failed to be attached to the thread.
My Question: What do you think is the cause of this problem?
Mail Header
Here I attach the header of the mail.
Received: from 487462728342 named unknown by gmailapi.google.com with HTTPREST; Thu, 19 Nov 2020 22:34:45 -0800
In-Reply-To: <CAMZJxVNx_GfH3mryUr78WD_N6ayz__H3=x7zTocOrWzbo+kcnA#mail.gmail.com>
References: <CAMZJxVM8vEEeuMzzMUbVirk-Mx554putqNs=HyxikWGPSy-Ayw#mail.gmail.com> <CAMZJxVNx_GfH3mryUr78WD_N6ayz__H3=x7zTocOrWzbo+kcnA#mail.gmail.com>
X-Tracking-Address: hogehoge#gmail.com
Subject: Re: Hello
To: moriwm77#gmail.com
CC: hogehoge#gmail.com
BCC:
Content-Type: multipart/mixed; boundary="===============1605854084=="
Date: Thu, 19 Nov 2020 22:34:45 -0800
Message-Id: <CAMZJxVPX-6dZyNy2cSu3eAUMUZ4p-ai62uOH6=MZY7LYq6O5Lg#mail.gmail.com>
From: moriwm77#gmail.com
--===============1605854084==
Content-Type: text/plain; charset="utf-8"
If...
--===============1605854084==--
Code
This is a adapter function to call GSuite's gmail API.
// SendMail send gmail.
func (api *GSuiteAPI) SendMail(token *oauth2.Token, m Message) error {
ctx := context.Background()
srv, err := gmail.NewService(ctx, option.WithTokenSource(api.config.TokenSource(ctx, token)))
if err != nil {
return handleError(err)
}
boundary := "===============" + strconv.FormatInt(time.Now().Unix(), 10) + "=="
sMail := []string{}
for key, value := range m.Headers {
sMail = append(sMail, key+": "+value)
}
sMail = append(sMail, "Subject: =?UTF-8?B?"+base64.StdEncoding.EncodeToString([]byte(m.Subject))+"?=")
sMail = append(sMail, "To: "+strings.Join(m.To, ","))
sMail = append(sMail, "CC: "+strings.Join(m.CC, ","))
sMail = append(sMail, "BCC: "+strings.Join(m.BCC, ","))
sMail = append(sMail, "Content-Type: multipart/mixed; boundary=\""+boundary+"\"")
// Boby
sMail = append(sMail, "")
sMail = append(sMail, "--"+boundary)
sMail = append(sMail, "Content-Type: text/plain; charset=\"utf-8\"")
sMail = append(sMail, "")
sMail = append(sMail, m.Body)
log.Info().Msgf("%+v", strings.Join(sMail, "\r\n"))
// Attachment
for _, attachment := range m.Attachments {
name := strings.Replace(attachment.Name, "\"", "", -1)
sMail = append(sMail, "")
sMail = append(sMail, "--"+boundary)
sMail = append(sMail, "Content-Type: application/octet-stream; name=\""+name+"\"")
sMail = append(sMail, "Content-Disposition: attachment; filename=\""+name+"\"")
sMail = append(sMail, "Content-Transfer-Encoding: Base64")
sMail = append(sMail, "")
sMail = append(sMail, attachment.Data)
}
source := strings.Join(sMail, "\r\n")
var message gmail.Message
if api.mailConfig.Enable {
message.Raw = base64.
StdEncoding.
EncodeToString([]byte(source))
_, err = srv.Users.Messages.Send("me", &message).Do()
if err != nil {
return handleError(err)
}
}
return nil
}
"In-Reply-To" Header and "References" Header are included in "m.Headers".
Like
m.Headers["In-Reply-To"]
// => <CAMZJxVNx_GfH3mryUr78WD_N6ayz__H3=x7zTocOrWzbo+kcnA#mail.gmail.com>
m.Headers["References"]
// => <CAMZJxVM8vEEeuMzzMUbVirk-Mx554putqNs=HyxikWGPSy-Ayw#mail.gmail.com> <CAMZJxVNx_GfH3mryUr78WD_N6ayz__H3=x7zTocOrWzbo+kcnA#mail.gmail.com>
According to the Managing Threads with Gmail API documentation, if you want to add a draft or a message to a thread, you will have to fulfill these conditions:
The requested threadId must be specified on the Message or Draft.Message you supply with your request.
The References and In-Reply-To headers must be set in compliance with the RFC 2822 standard.
The Subject headers must match.
Therefore, the threadId must be specified in order to successfully insert the message.
Reference
Gmail API Managing Threads.

Attaching image to email sent through golang gmail api

I'm trying to attach a picture to an email sent through the gmail golang api, but the image does not attach. I've looked extensively at the documentation, but I have not been able to figure out how to attach an image to an email. Here is the code that I have so far. It only sends the text.
// SendEmailEmbed2 Sends email through gmail
func (config *GmailConfig) SendEmailEmbed2(person int, formattedTemplate string) {
msg := formattedTemplate
mmbody := gmail.MessagePartBody{
Data: encodeWeb64String([]byte(msg)),
}
headers := []*gmail.MessagePartHeader{
&gmail.MessagePartHeader{
Name: "From",
Value: config.FromAddress,
},
&gmail.MessagePartHeader{
Name: "To",
Value: config.CSVReference.Elem(person, config.PlugIns[0].ToEmail).String(),
},
&gmail.MessagePartHeader{
Name: "Subject",
Value: config.EmailSubject,
},
&gmail.MessagePartHeader{
Name: "Content-Disposition",
Value: "./prac2_files/image001.jpg",
},
}
Parts := []*gmail.MessagePart{
&gmail.MessagePart{
Filename: "./prac2_files/image001.jpg",
},
}
partgmsg := gmail.MessagePart{
Body: &mmbody,
Headers: headers,
Parts: Parts,
MimeType: "multipart",
}
im, _ := os.Open("./prac2_files/image001.jpg")
gmsg := gmail.Message{
Payload: &partgmsg,
Raw: encodeWeb64String([]byte(msg)),
}
call, err := config.srv.Users.Messages.Send("me", &gmsg).Media(im).Do()
im.Close()
if err != nil {
log.Println("em %v, err %v", gmsg, err)
// return err
}
// return err
fmt.Println(person, ":", headers[1].Value, " : ", call.LabelIds)
}
func encodeWeb64String(b []byte) string {
s := base64.URLEncoding.EncodeToString(b)
var i = len(s) - 1
for s[i] == '=' {
i--
}
return s[0 : i+1]
}
Answer:
You need to attach the file inline in the body of the message.
Code:
message := gmail.Message
attachmentBytes := ioutil.ReadFile(fileDir + fileName)
attachmentMimeType: = http.DetectContentType(attachmentBytes)
attachmentData: = base64.StdEncoding.EncodeToString(attachmentBytes)
boundary: = randStr(32, "alphanum")
messageBody: = [] byte("Content-Type: multipart/mixed; boundary=" + boundary + " \n" +
"MIME-Version: 1.0\n" +
"to: " + config.CSVReference.Elem(person, config.PlugIns[0].ToEmail).String() + "\n" +
"from: " + config.FromAddress + "\n" +
"subject: " + config.EmailSubject + "\n\n" +
"--" + boundary + "\n" +
"Content-Type: text/plain; charset=" + string('"') + "UTF-8" + string('"') + "\n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: 7bit\n\n" +
content + "\n\n" +
"--" + boundary + "\n" +
"Content-Type: " + "image/jpg" + "; name=" + string('"') + fileName + string('"') + " \n" +
"MIME-Version: 1.0\n" +
"Content-Transfer-Encoding: base64\n" +
"Content-Disposition: attachment; filename=" + string('"') + fileName + string('"') + " \n\n" +
chunkSplit(attachmentData, 76, "\n") +
"--" + boundary + "--")
message.Raw = base64.URLEncoding.EncodeToString(messageBody)
_, err := service.Users.Messages.Send("me", &message).Do()
if err != nil {
log.Fatalf("Email sending failed: %v", err)
} else {
log.Println("Email message sent")
}

How to make a HTTP post data request [duplicate]

This question already has answers here:
Missing type in composite literal
(3 answers)
Closed 3 years ago.
func send_msg(msg string, user string) {
url := "https://test.com/"
token := "Bearer " + get_token()
header := req.Header{
"Content-Type": "application/json",
"Authorization": token,
}
// this string param can not use variable user and msg.
// param := `{
// "email": "jjjj#gmail.com",
// "msg_type": "text",
// "content": { "text": "aaaaaaaaaaaaa" }
// }`
// this req.Param param error:missing type in composite literal
param := req.Param{
"email": user,
"msg_type": "text",
"content": { "text" : msg },
}
r,_ := req.Post(url, header, param)
resp := r.String()
log.Println(resp)
}
This req.Param param error:missing type in composite literal
This string param can not use variable user and msg.
How can I use variable param?
Assuming you are using "net/http", The Post function expects url, content-type and body in that order. Link : net/http#post
Thus, for your particular case, it should look something like this
url := "https://test.com/"
contentType := "application/json"
param := []byte(`{"email": user, "msg_type": "text","content": { "text" : "msg" }, }`)
resp, err := http.Post(url, contentType, bytes.NewBuffer(param))
defer resp.Body.Close()
if err != nil {
//handle error
} else {
body, _ := ioutil.ReadAll(resp.Body)
log.Println(body)
}
If you want to add custom headers, you may use newRequest which gives you req and then you can add or set custom headers .
req.Headers.Set(existingHeaderWithNewValue)
req.Headers.Add(customHeader)

paho.mqtt.golang not connecting via websockets

I am not able to connect to any MQTT broker via websockets.
I first set up a local broker with websockets enabled. The connection did not establish. I then tried to connect to some public brokers which also does not work.
Could you give me a hint on solving this?
It may only be a firewall problem..
package main
 
import (
    "fmt"
    "sync"
    "time"
 
    mqtt "github.com/eclipse/paho.mqtt.golang"
)
 
func main() {
    var ipRange []string
    ipRange = append(ipRange,
        "tcp://mqtt.flespi.io:1883",
        "tcp://test.mosquitto.org:1883",
        "tcp://iot.eclipse.org:1883",
        "tcp://mqtt.fluux.io:1883",
        "tcp://broker.hivemq.com:1883",
        "ws://mqtt.flespi.io:80",
        "ws://iot.eclipse.org:80",
        "ws://broker.hivemq.com:8000",
        "ws://test.mosquitto.org:80",
        "ws://broker.bevywise.com:8443",
        "ws://mqtt.dioty.co:8080",
    )
 
    opts := mqtt.NewClientOptions()
    opts.ConnectTimeout = 1 * time.Second
    opts.PingTimeout = 1 * time.Second
    opts.WriteTimeout = 1 * time.Second
 
    var clients []mqtt.Client
    for i := 0; i < len(ipRange); i++ {
        opt := *opts
        opt.AddBroker(ipRange[i])
        clients = append(clients, mqtt.NewClient(&opt))
    }
 
    startTime := time.Now()
    var validBrokers []string
    var wg sync.WaitGroup
    wg.Add(len(clients))
 
    fmt.Println("--- discovery started (", len(clients), "clients) ---")
    for i := 0; i < len(clients); i++ {
        go connectMQTT(clients[i], &wg, &validBrokers)
    }
    wg.Wait()
 
    fmt.Println("--- discovery finished ---")
    fmt.Println("\nvalid brokers after", time.Now().Sub(startTime), ":")
    for i := 0; i < len(validBrokers); i++ {
        fmt.Println(validBrokers[i])
    }
}
 
func connectMQTT(client mqtt.Client, waitgroup *sync.WaitGroup, brokers *[]string) {
    defer waitgroup.Done()
    bla := client.OptionsReader()
    if token := client.Connect(); token.Wait() {
        fmt.Println(bla.Servers()[0].Hostname()+":"+bla.Servers()[0].Port(), client.IsConnected())
        if token.Error() == nil {
            *brokers = append(*brokers, bla.Servers()[0].Hostname()+":"+bla.Servers()[0].Port())
        }
    }
}

Resources