Sending an email with ruby gmail api v0.9 - ruby

Does anyone have a simple example as to how to send an email from scratch with the v0.9 API.
simply want an example of sending the following:
m = Mail.new(
to: "test1#test.com",
from: "test2#test.com",
subject: "Test Subject",
body:"Test Body")
Now to create that message object which is required to send,we can use:
msg = Base64.urlsafe_encode64 m.to_s
And then try to send (where message_object = msg):
client = Google::Apis::GmailV1::GmailService.new #Appropriately authorised
client.send_user_message("me", message_object)
The client wants an RFC822 compatible encoded string, which the above should be.
I've tried:
message_object = msg
=> Google::Apis::ClientError: invalidArgument: 'raw' RFC822 payload message string or uploading message via /upload/* URL required
message_object = raw:msg
=>ArgumentError: unknown keyword: raw
message_object = {raw:msg}
=>ArgumentError: unknown keyword: raw
message_object = Google::Apis::GmailV1::Message.new(raw:msg)
=> #<Google::Apis::GmailV1::Message:0x007f9158e5b4b0 #id="15800cd7178d69a4", #thread_id="15800cd7178d69a4">
#But then I get Bounce <nobody#gmail.com> - An error occurred. Your message was not sent.
i.e. None of them work...
Sending the basic encded string (msg above) through the Gmail API interface tester here works.
I'm obviously missing something obvious here as to how to construct that object required to make it work through the API.

Ok. So the answer... thanks for all your help Holger...
Was that the documentation is wrong. It asks you to encode to base64.
The base64 encoding is not required (it is done internally by the api client).
The correct way to send is
msg = m.encoded
# or m.to_s
# this doesn't base64 encode. It just turns the Mail::Message object into an appropriate string.
message_object = Google::Apis::GmailV1::Message.new(raw:m.to_s)
client.send_user_message("me", message_object)
Hope that saves someone else from being patronised by an overzealous mod.

Carpela‘s answer works fine, but for the message_object, it's missing "raw" in Message.new. The correct code should as following:
message_object = Google::Apis::GmailV1::Message.new(raw: m.encoded) # or m.to_s
client.send_user_message('me', message_object)

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)

Creating a Message with Attachement GmailV1 API ruby

I am trying to send a en email with an attachment via the GmailV1 API. However it just isn't working due to Missing Draft Message errors.
According to RubyDoc I tried to create a draft message as follows:
The GmailV1:GmailService.create_user_draft() methods takes in an identifier and a draft_object (accepting 'me' for the authorized user). A draft object (Google::Apis::GmailV1::Draft) takes a message in the form of Google::Apis::GmailV1::Message which in turn takes a payload in the form of Google::Apis::GmailV1::MessagePart which has the desired filename method.
So I ran this code:
##assume client is an authorized instance of Google::Apis::GmailV1:GmailService
msg_part = Google::Apis::GmailV1::MessagePart.new(filename: 'path/to/file')
msg = Google::Apis::GmailV1::Message.new(payload: msg_part)
draft = Google::Apis::GmailV1::Draft.new(message: msg)
client.create_user_draft('me', draft)
>> Google::Apis::ClientError: invalidArgument: Missing draft message
How Come?
Versions:
google-api-client 0.9.9
googleauth 0.5.1
ruby 2.3.1p112
Using the GmailService class as described here I was able to save a draft using the code below. I think the key is that the raw keyword is required in the message.
result = service.create_user_draft(
user_id,
Google::Apis::GmailV1::Draft.new(
:message => Google::Apis::GmailV1::Message.new(
:raw => "To: test#test.com\r\nSubject: Test Message\r\n\r\nTest Body"
)
)
)
I solved this problem creating first a Mail object with the 'mail' gem in this way:
require 'mail'
mail = Mail.new
mail['from'] = 'pippo#pluto.it'
mail[:to] = 'me#mymail.it'
mail.subject = 'This is a test email'
mail.body 'this is the body'
mail.add_file("./path/to/file")
#... and other ...
then i converted this in a raw object:
raw_message = mail.to_s
then i create gmail message with this raw:
message = Google::Apis::GmailV1::Message.new(
:raw => raw_message
)
and finnaly:
draft = Google::Apis::GmailV1::Draft.new(message: message)
gmail.create_user_draft('me', draft)

how to make an api call to server which requires special characters using NSURL session?

I am building an iOS app with Swift2.0, XCode 7.2
I am trying to make an api call to:
htttp://xyz.com/t/restaurants-us?KEY=someKey&filters={"locality":{"$eq":"miami"}}
let endPoint:String = "htttp://xyz.com/t/restaurants-us?KEY=someKey&filters={%22locality%22:{%22$eq%22:%22miami%22}}"
When I try to create an URL using this string(endPoint):
let url = NSURL(string: endPoint), a nil is returned.
So I tried encoding the string before trying to create URL:
let encodedString = endPoint.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())
Now the encodedString:
"htttp://xyz.com/t/restaurants-us?KEY=someKey&filters=%7B%2522locality%2522:%7B%2522$eq%2522:%2522miami%2522%7D%7D"
But now when i create a NSURL session and send the request, I get an unexpected response from the server:
Reply from server:
{
"error_type" = InvalidJsonArgument;
message = "Parameter 'filters' contains an error in its JSON syntax. For documentation, please see: http://developer.factual.com.";
status = error;
version = 3;
}
So if I don't encode the string, I will not be able to create NSURL.
But if I encode and send the request, the server is not able to handle the request.
Can anyone please suggest a workaround.
When you declare endpoint, you have already percent-encoded some characters (the quotes). When you ask iOS to percent-encode it, it percent-encodes the percent-encodes. Decoding the encodedString results in:
htttp://xyz.com/t/restaurants-us?KEY=someKey&filters={%22locality%22:{%22$eq%22:%22miami%22}}
Instead, you should start with actual quotes in endpoint:
let endPoint:String = "htttp://xyz.com/t/restaurants-us?KEY=someKey&filters={\"locality\":{\"$eq\":\"miami\"}}"

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.

Decode HMAC signature

I decode (secret_key,client_id, path) into signature by following code :
require 'rubygems'
require 'base64'
require 'cgi'
require 'hmac-sha1'
#client_id = "asdkasdlda"
#secret = "3fdsdsfxds"
binary_key = Base64.decode64(#secret)
params.update({"client" => #client_id})
path = uri_path + "?" + params.collect{|k,v| "#{k}=#{v}"}.inject{|initial,cur| initial + "&" + cur}
digest = HMAC::SHA1.new(binary_key).update(path).digest
digest = Base64.encode64(digest).gsub(/[+\/]/, {"+" => "-", "/" => "_"}).delete("=")
return "#{path}&sig=#{digest}"
So, this code generates sig and path. we send request to server in following way:
/api/v1/customers/sign_in.json?user[email]=amit1656789#gmail.com&user[password]=[FILTERED]&client=asdkasdlda&sig=JSdP5xUHhgS8ZbKApBOIlsJKg_Q
Now, on server side, i want to decode this params["sign"] into app_id, secret_key and path means reverse process of above code. But i am not found any reverse process of this. Means
(app_id, secret, path) => "signature"
"signature" => (app_id, secret, path) /* Here i stuck */
First thing you should know:
"signature" => (app_id, secret, path)
This is not possible. It is not how MACs of any kind work. The signature does not contain the data. Signatures are meant to be sent alongside the message that they sign.
For secure HMAC, you should never send the secret with the message that you sign. It is also not possible to figure out a secret from the signature, except by repeatedly guessing what the value might be.
The usual way to confirm a signature is to follow the same process on the server, signing the same message, using the same secret (which the server should already have), and compare the signatures. You have made it difficult for yourself because you have signed the params as you sent them, and then put the signature on the end. You have to re-construct the message.
First, you need to use whatever web server library you can to get the request URI including the query string
signed_uri = "/api/v1/customers/sign_in.json?user[email]=amit1656789#gmail.com&user[password]=[FILTERED]&client=asdkasdlda&sig=JSdP5xUHhgS8ZbKApBOIlsJKg_Q"
Then split it into the message and its signature (I'll leave that to you, but just a regular expression ought to work):
message = "/api/v1/customers/sign_in.json?user[email]=amit1656789#gmail.com&user[password]=[FILTERED]&client=asdkasdlda"
signature = "JSdP5xUHhgS8ZbKApBOIlsJKg_Q"
To decode this signature back to the original digest (for easy comparison), just reverse the replace and encoding you did at the end on the client:
client_digest = Base64.decode64(
signature.gsub(/[-_]/, {"-" => "+", "_" => "/"}) )
Then on the server (where you should already have a value for #secret), calculate what you expect the signature to be:
#secret = '3fdsdsfxds'
binary_key = Base64.decode64(#secret)
server_digest = HMAC::SHA1.new(binary_key).update( message ).digest
if server_digest == client_digest
puts "The message was signed correctly"
else
puts "ERROR: The message or signature is not correct!"
end

Resources