I am trying to send an email with multiple attachments using AWS SES and nodejs.
My js script calls ses.sendRawEmail like so:
const params = {
RawMessage: {
Data: rawMailBody
},
Destinations: [],
Source: 'testemail#gmail.com'
};
ses.sendRawEmail(params, function (err, data) {
if (err) console.log("Error: " + err);
else {
// console.log("Success call: ", JSON.stringify(data));
console.log("Success.");
return data
}
});
This is how I am creating the "rawMailBody" object:
const BUCKET = 'my-bucket';
var rawMailBody = "From: testemail#gmail.com\n";
rawMailBody = rawMailBody + "To: testemail#gmail.com\n";
rawMailBody = rawMailBody + "Subject: Test Subject\n";
rawMailBody = rawMailBody + ``
rawMailBody = rawMailBody + "MIME-Version: 1.0\n";
rawMailBody = rawMailBody + "Content-Type: multipart/mixed; boundary=\"NextPart\"\n\n";
rawMailBody = rawMailBody + "--NextPart\n";
rawMailBody = rawMailBody + "Content-Type: text/html; charset=iso-8859-1\n";
rawMailBody = rawMailBody + "\n";
rawMailBody = rawMailBody + htmlData;
rawMailBody = rawMailBody + "--NextPart\n";
console.log('attaching attachments')
const attachmentFilename = 'Evaluates2-Old-Logo.pdf';
const attachmentFilename2 = '2019-W-4.pdf';
console.log('adding attachment', attachmentFilename)
rawMailBody = rawMailBody + "Content-Type: application/octet-stream\n";
rawMailBody = rawMailBody + "Content-ID: random1565323918273545letters\n";
rawMailBody = rawMailBody + 'Content-Disposition: attachment; name="Evaluates2-Old-Logo.pdf"; filename="Evaluates2-Old-Logo.pdf";\n';
rawMailBody = rawMailBody + "Content-Transfer-Encoding: base64\n";
rawMailBody = rawMailBody + await getImgDataBuffer('Evaluates2-Old-Logo.pdf', BUCKET); + "\n\n";
rawMailBody = rawMailBody + "--NextPart\n";
console.log('adding attachment', attachmentFilename2)
rawMailBody = rawMailBody + "Content-Type: application/octet-stream\n";
rawMailBody = rawMailBody + "Content-ID: random1565323443423794letters\n";
rawMailBody = rawMailBody + "Content-Transfer-Encoding: base64\n";
rawMailBody = rawMailBody + 'Content-Disposition: attachment; name="2019-W-4.pdf"; filename="2019-W-4.pdf";\n';
// rawMailBody = rawMailBody + "Content-ID random3965323447923784letters\n";
rawMailBody = rawMailBody + await getImgDataBuffer('2019-W-4.pdf', BUCKET); + "\n\n";
rawMailBody = rawMailBody + "--NextPart\n";
rawMailBody = rawMailBody + "--NextPart--\n";
I am getting the image data base64 buffer like this:
const getImgDataBuffer = (imageFilename, bucket) => {
return new Promise((resolve, reject) => {
var params = { Bucket: bucket, Key: imageFilename };
s3.getObject(params, function (err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
reject(err)
}
else {
console.log(data.ContentLength);
console.log(data.ContentType);
console.log(data.Body);
console.log('got ', imageFilename)
resolve(data.Body.toString('base64'))
}
})
})
}
I am getting very strange behavior in gmail- it only takes the first pdf I send. The first pdf comes through perfectly, but the second doesn't come through at all. When I swap the order that the attachments are added to the rawMailBody object it still renders only the first one.
When I choose "show original" in gmail I can see that the entire MIME block for the second attachment is completely missing...
Is this expected behavior for gmail to remove subsequent attachments after the first one? Am I just doing it wrong?
Thanks!
Related
I need to get a here-API token to use it in HERE Matrix Routing API but I can't get it. the response from the server always return the below
"{"errorId":"ERROR-b877e827-1ed6-4a2d-bed8-
546d1b9b3bd6","httpStatus":401,"errorCode":401205,"message":"Unsupported
signature method in the header. Require HMAC-
SHA256","error":"invalid_request","error_description":"errorCode:
'401205'. Unsupported signature method in the header. Require HMAC-
SHA256"}"
here is my code :
var grant_type = 'client_credentials';
var oauth_consumer_key = 'koauth_consumer_key';
var access_key_secret = 'access_key_secret';
var oauth_nonce = DateTime.now().millisecondsSinceEpoch.toString();
var oauth_timestamp = (new DateTime.now().toUtc().millisecondsSinceEpoch / 100).toString();
var oauth_signature_method = 'HMAC-SHA256';
var oauth_version = '1.0';
var url = 'https://account.api.here.com/oauth2/token';
create_signature(secret_key, signature_base_string) {
var temp = Hmac(sha256, utf8.encode(secret_key)).convert(utf8.encode(signature_base_string));
var hmacBase64 = base64Encode(temp.bytes);
print(hmacBase64);
return hmacBase64;
}
create_parameter_string(grant_type, oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version) {
parameter_string = '';
parameter_string = parameter_string + 'grant_type=' + grant_type;
parameter_string = parameter_string + '&oauth_consumer_key=' + oauth_consumer_key;
parameter_string = parameter_string + '&oauth_nonce=' + oauth_nonce;
parameter_string = parameter_string + '&oauth_signature_method=' + oauth_signature_method;
parameter_string = parameter_string + '&oauth_timestamp=' + oauth_timestamp;
parameter_string = parameter_string + '&oauth_version=' + oauth_version;
return parameter_string;
}
create encoded_oauth_signature
var parameter_string = create_parameter_string(grant_type, oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version);
var encoded_parameter_string = Uri.encodeComponent(parameter_string);
var encoded_base_string = 'POST' + '&' + Uri.encodeComponent(url) + '&' + encoded_parameter_string;
var signing_key = access_key_secret + '&';
var oauth_signature = create_signature(signing_key, encoded_base_string);
var encoded_oauth_signature = Uri.encodeComponent(oauth_signature);
// #---------------------Requesting Token---------------------
var body = {
'grant_type': '$grant_type'
};
preparing body and header
var headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'OAuth oauth_consumer_key="$oauth_consumer_key",oauth_nonce="$oauth_nonce",oauth_signature_method="HMAC-SHA256",oauth_timestamp="$oauth_timestamp",oauth_version="1.0",oauth_signature="$encoded_oauth_signature"'
};
get token:
gettokenpy() async {
final response = await http.post(
Uri.parse(url),
body: body,
headers: headers,
);
return response.body.toString();
}
The error message indicates that input signature method is not supported. It is supposed to be HMAC-SHA256. The following guide can help you to understand how to get the OAuth 2.0 token.
https://developer.here.com/documentation/identity-access-management/dev_guide/topics/sdk.html#step-3-request-a-token
I'm currently trying retrieve list share available in my Azure account from salesforce. I'm trying to implement the example from below sample code:
https://learn.microsoft.com/en-us/rest/api/storageservices/list-shares#samplerequestandresponse
//private key: access key of my account
string storageKey =private key;
string storageName = 'accountName';
Datetime dt = Datetime.now();
string formattedDate = dt.formatGMT('EEE, dd MMM yyyy HH:mm:ss')+ ' GMT';
system.debug('formattedDate--'+formattedDate);
string CanonicalizedHeaders = 'x-ms-date:'+formattedDate+'\nx-ms-version:2016-05-31';
string CanonicalizedResource = '/' + storageName + '/\ncomp:list';
string StringToSign = 'GET\n\n\n\n\n\n\n\n\n\n\n\n' + CanonicalizedHeaders+'\n'+CanonicalizedResource;
system.debug('StringToSign--'+StringToSign);
Blob temp = EncodingUtil.base64Decode(storageKey);
Blob hmac = Crypto.generateMac('HmacSHA256',Blob.valueOf(StringToSign),temp ); //StringToSign
system.debug('oo-'+EncodingUtil.base64Encode(hmac));
HttpRequest req = new HttpRequest();
req.setMethod('GET');
//req.setHeader('content-type', 'application/xml');
req.setHeader('x-ms-version','2016-05-31' );
req.setHeader('x-ms-date', formattedDate);
string signature = EncodingUtil.base64Encode(hmac);
string authHeader = 'SharedKey salesforcestrongaccount'+':'+signature;
req.setHeader('Authorization',authHeader);
req.setEndpoint('https://<accountName>.file.core.windows.net/?comp=list');
Http http = new Http();
HTTPResponse res;
res = http.send(req);
System.debug(LoggingLevel.INFO, 'http.send result status: ' + res.getStatus());
Any help?
As Gaurav Mantri says, there are something wrong with your stringToSign. So you will get this error.
The right Shared Key Authentication is like this:
StringToSign = VERB + "\n" +
Content-Encoding + "\n" +
Content-Language + "\n" +
Content-Length + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Date + "\n" +
If-Modified-Since + "\n" +
If-Match + "\n" +
If-None-Match + "\n" +
If-Unmodified-Since + "\n" +
Range + "\n" +
CanonicalizedHeaders +
CanonicalizedResource;
Here I create a test demo, you could refer to it.
List share:
string storageAccount = "storage account";
string accessKey = "accountkey";
string resourcePath = "?comp=list";
string uri = #"https://" + storageAccount + ".file.core.windows.net/" + resourcePath;
// Web request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Method = "GET";
request.Headers["x-ms-date"] = DateTime.UtcNow.ToString("R", System.Globalization.CultureInfo.InvariantCulture);
request.Headers["x-ms-version"] = "2015-02-21";
String stringToSign = "GET\n"
+ "\n" // content encoding
+ "\n" // content language
+ "\n" // content length
+ "\n" // content md5
+ "\n" // content type
+ "\n" // date
+ "\n" // if modified since
+ "\n" // if match
+ "\n" // if none match
+ "\n" // if unmodified since
+ "\n" // range
+ "x-ms-date:" + request.Headers["x-ms-date"] + "\nx-ms-version:2015-02-21\n" // headers
+ "/" + storageAccount + "/" + "\ncomp:list"; // resources
System.Security.Cryptography.HMACSHA256 hasher = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(accessKey));
string strAuthorization = "SharedKey " + storageAccount + ":" + System.Convert.ToBase64String(hasher.ComputeHash(System.Text.Encoding.UTF8.GetBytes(stringToSign)));
request.Headers["Authorization"] = strAuthorization;
Task<WebResponse> response = request.GetResponseAsync();
HttpWebResponse responseresult = (HttpWebResponse)response.Result;
using (System.IO.StreamReader r = new System.IO.StreamReader(responseresult.GetResponseStream()))
{
string jsonData = r.ReadToEnd();
Console.WriteLine(jsonData);
}
Result:
Java:
private static final String account = "accountname";
private static final String key = "Key";
public static void main(String args[]) throws Exception {
// String urlString = "http://" + account + ".file.core.windows.net/sampleshare/name.txt";
String urlString = "https://" + account + ".file.core.windows.net/?comp=list";
HttpURLConnection connection = (HttpURLConnection) (new URL(urlString)).openConnection();
getFileRequest(connection, account, key);
connection.connect();
System.out.println("Response message : " + connection.getResponseMessage());
System.out.println("Response code : " + connection.getResponseCode());
BufferedReader br = null;
if (connection.getResponseCode() != 200) {
br = new BufferedReader(new InputStreamReader((connection.getErrorStream())));
} else {
br = new BufferedReader(new InputStreamReader((connection.getInputStream())));
}
System.out.println("Response body : " + br.readLine());
}
public static void getFileRequest(HttpURLConnection request, String account, String key) throws Exception {
SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";
String stringToSign = "GET\n" + "\n" // content encoding
+ "\n" // content language
+ "\n" // content length
+ "\n" // content md5
+ "\n" // content type
+ "\n" // date
+ "\n" // if modified since
+ "\n" // if match
+ "\n" // if none match
+ "\n" // if unmodified since
+ "\n" // range
+ "x-ms-date:" + date + "\nx-ms-version:2015-02-21\n" // headers
+ "/" + account + request.getURL().getPath() + "\ncomp:list"; // resources
System.out.println("stringToSign : " + stringToSign);
String auth = getAuthenticationString(stringToSign);
request.setRequestMethod("GET");
request.setRequestProperty("x-ms-date", date);
request.setRequestProperty("x-ms-version", "2015-02-21");
request.setRequestProperty("Authorization", auth);
}
private static String getAuthenticationString(String stringToSign) throws Exception {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(Base64.decode(key), "HmacSHA256"));
String authKey = new String(Base64.encode(mac.doFinal(stringToSign.getBytes("UTF-8"))));
String auth = "SharedKey " + account + ":" + authKey;
return auth;
}
Got this jquery code am using to retrieve data from database through ADO.net:
$(document).ready(getBooks);
function getBooks() {
$.ajax({
type: "POST",
url: "main.aspx/getBooks",
data: "{}",
datatype: "json",
contenttype: "application/json; charset=utf-8",
success: function (data) {
var TableContent = "<table>" +
"<tr>" +
"<td>ISBN</td>" +
"<td>Book Title</td>" +
"<td>Public Year</td>" +
"<td>Category</td>" +
"</tr>";
for (var i = 0; i < data.d.length; i++) {
TableContent += "<tr>" +
"<td>" + data.d[i].ISBN + "</td>" +
"<td>" + data.d[i].BOOK_TITLE + "</td>" +
"<td>" + data.d[i].PUBLICATION_YEAR + "</td>" +
"<td>" + data.d[i].CATEGORY_TYPE + "</td>" +
"</tr>";
}
TableContent += "</table>";
$("#UpdatePanel").html(TableContent);
},
error: $("#UpdatePanel").html("Error")
});
}
C# code looks like this:
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static List<Book> getBooks()
{
List<Book> allBooks = new List<Book>();
using (LibraryEntities le = new LibraryEntities())
{
allBooks = le.Books.ToList();
}
return allBooks;
}
My issue is that am always getting nothing and after i added:
error: $("#UpdatePanel").html("Error")
Am only getting "Error" in UpdatePanel element, i am an AJAX Newbie, need your help guys.
I'm using graph api v2.1. AS3-code generates data, calls JS-code via ExternalInterface, JS-code calls FB API
I'm able to upload photo via {image-url}, but getting error when trying to upload photo via {image-data}:
{message: "(#324) Requires upload file", type: "OAuthException", code:
324}
I guess, i'm formatting {image-data} wrong. here is my code:
AS3:
var id:int = Math.random()*10000;
var stream:ByteArray = new ByteArray();
var imageName:String = id.toString() + ".png";
var boundary:String = "I" + id.toString();
stream.writeUTFBytes(
'Content-Type: multipart/form-data; boundary=' + boundary +
'\r\n\r\n--' + boundary +
'\r\nContent-Disposition: file; filename="' + imageName + '"' +
'\r\nContent-Type: image/png' +
'\r\nContent-Transfer-Encoding: binary' +
'\r\n\r\n');
stream.writeBytes(picture);
stream.writeUTFBytes("\r\n--" + boundary + '--\r\n');
if (ExternalInterface.available)
{
ExternalInterface.call('savePhoto', stream, null, id);
}
JS:
function savePhoto(bytes, url, requestId)
{
var data;
if (bytes != null)
data = {"source": bytes, "no_story":true}; //getting error
else
data = {"url": url, "no_story":true}; //works fine
FB.api(
"/me/photos",
"POST",
data,
function (response)
{
//handle response
}
);
}
UPD:
Here is how picture initialised:
[Embed(source="../res/logo_2.png", mimeType="image/png")]
private var testImage:Class;
<...>
var data:Bitmap = new testImage() as Bitmap;
var picture:ByteArray = PNGEncoder.encode(data.bitmapData);
Did you try base64?
var params: Object = new Object;
var encoder:JPGEncoder = new JPGEncoder(75);
var bytes:ByteArray = encoder.encode(bmp.bitmapData);
params.message = message;
params.image = bytes;
params.fileName = "image.jpg";
Facebook.api("/me/photos",null,params,"POST");
https://code.google.com/p/as3corelib/
Try this code to prepare data:
var pngData:ByteArray = PNGEncoder.encode(image.bitmapData);
var strForStart:String = "\r\n--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"photo\"; filename=\"file1.png\"\r\n" + "Content-Type: image/png\r\n\r\n" + "";
var strForEnd:String = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"Upload\"\r\n\r\n" + "Submit Query\r\n" + "--" + boundary + "--";
var beginBA:ByteArray = new ByteArray();
beginBA.writeMultiByte(strForStart, "ascii");
var endBA:ByteArray = new ByteArray();
endBA.writeMultiByte(strForEnd, "ascii");
var resultBytes:ByteArray = new ByteArray();
resultBytes.writeBytes(beginBA, 0, beginBA.length);
resultBytes.writeBytes(pngData, 0, pngData.length);
resultBytes.writeBytes(endBA, 0, endBA.length);
I want to send the notification, using windows azure notification hub api in PHP.
You have to interface directly with REST (http://msdn.microsoft.com/en-us/library/dn495827.aspx).
The code you will have to write is basically this one:
class Notification {
public $format;
public $payload;
# array with keynames for headers
# Note: Some headers are mandatory: Windows: X-WNS-Type, WindowsPhone: X-NotificationType
# Note: For Apple you can set Expiry with header: ServiceBusNotification-ApnsExpiry in W3C DTF, YYYY-MM-DDThh:mmTZD (for example, 1997-07-16T19:20+01:00).
public $headers;
function __construct($format, $payload) {
if (!in_array($format, ["template", "apple", "windows", "gcm", "windowsphone"])) {
throw new Exception('Invalid format: ' . $format);
}
$this->format = $format;
$this->payload = $payload;
}
}
class NotificationHub {
const API_VERSION = "?api-version=2013-10";
private $endpoint;
private $hubPath;
private $sasKeyName;
private $sasKeyValue;
function __construct($connectionString, $hubPath) {
$this->hubPath = $hubPath;
$this->parseConnectionString($connectionString);
}
private function parseConnectionString($connectionString) {
$parts = explode(";", $connectionString);
if (sizeof($parts) != 3) {
throw new Exception("Error parsing connection string: " . $connectionString);
}
foreach ($parts as $part) {
if (strpos($part, "Endpoint") === 0) {
$this->endpoint = "https" . substr($part, 11);
} else if (strpos($part, "SharedAccessKeyName") === 0) {
$this->sasKeyName = substr($part, 20);
} else if (strpos($part, "SharedAccessKey") === 0) {
$this->sasKeyValue = substr($part, 16);
}
}
}
private function generateSasToken($uri) {
$targetUri = strtolower(rawurlencode(strtolower($uri)));
$expires = time();
$expiresInMins = 60;
$expires = $expires + $expiresInMins * 60;
$toSign = $targetUri . "\n" . $expires;
$signature = rawurlencode(base64_encode(hash_hmac('sha256', $toSign, $this->sasKeyValue, TRUE)));
$token = "SharedAccessSignature sr=" . $targetUri . "&sig="
. $signature . "&se=" . $expires . "&skn=" . $this->sasKeyName;
return $token;
}
public function broadcastNotification($notification) {
$this->sendNotification($notification, "");
}
public function sendNotification($notification, $tagsOrTagExpression) {
if (is_array($tagsOrTagExpression)) {
$tagExpression = implode(" || ", $tagsOrTagExpression);
} else {
$tagExpression = $tagsOrTagExpression;
}
# build uri
$uri = $this->endpoint . $this->hubPath . "/messages" . NotificationHub::API_VERSION;
$ch = curl_init($uri);
if (in_array($notification->format, ["template", "apple", "gcm"])) {
$contentType = "application/json";
} else {
$contentType = "application/xml";
}
$token = $this->generateSasToken($uri);
$headers = [
'Authorization: '.$token,
'Content-Type: '.$contentType,
'ServiceBusNotification-Format: '.$notification->format
];
if ("" !== $tagExpression) {
$headers[] = 'ServiceBusNotification-Tags: '.$tagExpression;
}
# add headers for other platforms
if (is_array($notification->headers)) {
$headers = array_merge($headers, $notification->headers);
}
curl_setopt_array($ch, array(
CURLOPT_POST => TRUE,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_SSL_VERIFYPEER => FALSE,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $notification->payload
));
// Send the request
$response = curl_exec($ch);
// Check for errors
if($response === FALSE){
throw new Exception(curl_error($ch));
}
$info = curl_getinfo($ch);
if ($info['http_code'] <> 201) {
throw new Exception('Error sending notificaiton: '. $info['http_code'] . ' msg: ' . $response);
}
}
}
And use it this way: initialize your Notification Hubs client (substitute the connection string and hub name as instructed in the Get started tutorial):
$hub = new NotificationHub("connection string", "hubname");
Then add the send code depending on your target mobile platform.
Windows Store and Windows Phone 8.1 (non-Silverlight)
$toast = '<toast><visual><binding template="ToastText01"><text id="1">Hello from PHP!</text></binding></visual></toast>';
$notification = new Notification("windows", $toast);
$notification->headers[] = 'X-WNS-Type: wns/toast';
$hub->sendNotification($notification);
iOS
$alert = '{"aps":{"alert":"Hello from PHP!"}}';
$notification = new Notification("apple", $alert);
$hub->sendNotification($notification);
Android
$message = '{"data":{"msg":"Hello from PHP!"}}';
$notification = new Notification("gcm", $message);
$hub->sendNotification($notification);
Windows Phone 8.0 and 8.1 Silverlight
$toast = '<?xml version="1.0" encoding="utf-8"?>' .
'<wp:Notification xmlns:wp="WPNotification">' .
'<wp:Toast>' .
'<wp:Text1>Hello from PHP!</wp:Text1>' .
'</wp:Toast> ' .
'</wp:Notification>';
$notification = new Notification("mpns", $toast);
$notification->headers[] = 'X-WindowsPhone-Target : toast';
$notification->headers[] = 'X-NotificationClass : 2';
$hub->sendNotification($notification);
Kindle Fire
$message = '{"data":{"msg":"Hello from PHP!"}}';
$notification = new Notification("adm", $message);
$hub->sendNotification($notification);
For registration management you have to follow the content formats shown in the MSDN topic linked above, and probably do some nasty xml parsing... Be warned that element order is important and things will not work if the element are out of order.