Been banging my head against this for a while. I have this string:
[18] pry(main)> p.title
=> "Human meat – Wesker and Son butchers at Smithfield Market"
[19] pry(main)> p.title.encoding
=> #<Encoding:UTF-8>
[20] pry(main)> p.title.force_encoding("ascii").encode
=> "Human meat ��� Wesker and Son butchers at Smithfield��Market"
This is called in my email template through ActionMailer, and--as you can probably guess--shows up in my email client equally garbled:
<a href=3D"http://localhost:3000/stuff/12072" =
style=3D"color: #D84D3F; text-decoration: none;">Human meat =EF=BF=BD=EF=BF=
=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD Wesker=
and Son butchers at Smithfield=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=BD=EF=BF=
=BD=EF=BF=BDMarket</a>
Despite my headers seemingly setting the Content-Type correctly to UTF-8:
----==_mimepart_5082362f64bbe_36cb3feb84c5a7386303d
Date: Fri, 19 Oct 2012 22:27:11 -0700
Mime-Version: 1.0
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: quoted-printable
Content-ID: <5082362f6a388_36cb3feb84c5a738632e9#MacBook-Air.local.mail>
I've read lots on the subject, and despite being very informative, I still have no clue how to get rid of these pesky question marks. What do I have to do to get rid of them (with ruby 1.9.3-p194)?
So I wasn't aware we were using the premailer gem to parse our emails. There seems to be an inconsistency in RubyGems in that my system reports I have premailer 1.7.3, yet my lib/premailer/adapter/nokogiri.rb does not match the one in the github repo, for the same version.
I've updated my Gemfile to use the github version and voila! UTF-8 characters again in my emails!
Related
In a script which sends emails through Net::SMTP, I've to figure out how to properly encode the email body in order to support accentuated characters. I've separated the email into 3 parts: headers, body and attachment, as from this tutorial https://www.tutorialspoint.com/ruby/ruby_sending_email.htm
Fixing this issue for the Subject header field wasn't a big deal:
require 'base64'
MARKER = 'FOOBAR'
def self.headers
<<~EOF
From: someemail#domain.tld
To: anotheremail#domaim.tld
# Base64 encoded UTF-8
Subject: =?UTF-8?B?#{Base64.strict_encode64('Accentuated characters supportés')}?=
Date: #{Time.now.to_s}
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=#{MARKER}
--#{MARKER}
EOF
end
I was tempted to reproduce the same logic for the email body, without any success. I've try several headers, such as Language, Content-Language, Content-Tranfer-Encoding, again, without any success. Using Ruby's .encode!('utf-8') was also ineffective.
The only working solution I can think of would be to send HTML encoded characters: using é instead of é inside a HTML block. Though, I'd like to avoid this solution as I've to improve my comprehension of encoding issues.
Does anyone has a suggestion about this issue ?
Here's my code so far, if it can help anyone:
module Reports
module SMTP
MARKER = 'FOOBAR'
def self.send_report(file_path)
file_content = File.binread(file_path)
encoded_content = [file_content].pack('m') # Base64
mail_content = headers + body + attachment(file_path, encoded_content)
begin
Net::SMTP.start('my.smtp.srv', 25, 'HELO_fqdn', 'username', 'p455w0rD', :plain) do |smtp|
smtp.send_message(mail_content, 'from#domain.tld', ['to1#domain.tld', 'to2#domain.tld'])
end
rescue => e
puts e.inspect, e.backtrace
end
end
def self.headers
# see above
end
def self.body
<<~EOF
Content-Type: text/html
Content-Transfer-Encoding:utf8
Here's a franglish text with some characters accentués
--#{MARKER}
EOF
end
def self.attachment(file_path, encoded_content)
<<~EOF
Content-Type: multipart/mixed; name = #{file_path}
Content-Transfer-Encoding:base64
Content-Disposition: attachment; filename = #{file_path}
#{encoded_content}
--#{MARKER}--
EOF
end
end
end
Note: these emails are correctly decoded by ProtonMail webclients, but our company's webclient (OBM) doesn't display accentuated character nor attachment properly.
Adding ;charset="utf-8" to the Content-Type header from the body part fixed my problem.
Content-Type: text/html;charset="utf-8"
I use below function to create a hash of the whole content of a directory so I can send all files as attachments.
def get_attachments_from_directory(dir)
attachment_to_send = Hash.new
Dir[dir.gsub("\\","/")+"/*"].each {|file|
file_to_send = File.read(file)
#file_to_send = File.read(file, :binmode => true)
attachment_to_send[File.basename(file)]=file_to_send
}
return attachment_to_send
end
and then I use below function to send the attachments out
def email_it(body, subject, to, from, attachment_to_send)
$smtp = 'mail.com'
$smtp_port = 25
Pony.mail(
:to => to,
:from => from,
:subject => subject,
:body => Nokogiri::HTML(body).text,
:html_body => body
:attachments => attachment_to_send,
:via => :smtp,
:via_options => {
:address => $smtp,
:port => $smtp_port,
:enable_starttls_auto => false
}
)
end
There are two files in my testing directory: .log and .png. Both of them are sent and received but .png is corrupted. gmail said that the image file cannot be displayed because it contains errors. The file name of .png file is correct in my gmail account. The file size is wrong. Much much smaller.
Show original in gmail gives me
----==_mimepart_4fd9515347359_fc1e853c88342d
Date: Thu, 14 Jun 2012 12:49:55 +1000
Mime-Version: 1.0
Content-Type: image/png;
charset=UTF-8;
filename="error_when_time_out - login at 2012-06-14 12.48.55.png"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename="error_when_time_out - login at 2012-06-14 12.48.55.png"
Content-ID: <4fd95153648c7_fc1e853c883518#RATionalxp.mail>
iVBORwq/XNErlnbxOOrmtdDZDYaMWm16lTatQptSpk4t12RW6HNq6IJGvvyB
sabbUovDe5+loc9U3yPX9Yr1vWJDv9Q4KNcPydUDQnkfV9LNFrTTOc2GrEZd
Rr0uo06fUUdn1jOZ9RxRnZBZJ2bXG3M4yoTplFqeWJFGFoVBjDanha5JoWOM
bx3hi0aTQPSQNcikNoMVeYrndSi9YeVl1jxLr07oNrrn11F1kv3AeL9C8Mpi
bkTrjvku73RaeOP6/KvXVv5yzfC6vfCqHf/H64Y/XNf//obujzf0f7lp+PMt
... it continues ....
Xvjq8X//p/Ocdy68s2/DZ//5/Muvf/rvt319XzQf8p9J+7wpSTTguXYPo3Dy
TYiIaNAvYXs5ir9gv4akEz5MOO6DxGPf150oPfApIe6Yu5SVblRBYgL1TrWq
QqWsUnFag5rYTagbCD4lJCgO2hYdpGzQteqR9NCgo3ZTmh0=
----==_mimepart_4fd9515347359_fc1e853c88342d--
inspect of the hash outputs
{"error_when_time_out - login at 2012-06-14 12.50.12.png"=>"\211PNG\n\277\\\321
+\226v\3618\352\346\265\320\331\r\206\214Zmz\2256\255B\233R\246N-\327dV\350sj\35
0\202F\276\374\201\261\246\333R\213\303{\237\245\241\317T\337#\327\365\212\365\2
75bC\277\3248(\327\017\311\325\003By\037W\322\315\026\264\3239\315\206\254F]F\27
5.\243N\237QGg\3263\231\365\034Q\235\220Y'f\327\es8\312\204\351\224Z\236X\221F\0
26\205A\2146\247\205\256I\241c\214o\035\341\213F\223#\364\2205\310\2446\203\025y
If I try to read the file with #file_to_send = File.read(file, :binmode => true)
I get an error: TypeError - can't convert Hash into Integer:
ruby 1.8.7 (2010-08-16 patchlevel 302) [i386-mingw32]
mime-types (1.16)
pony (1.3)
The conventional way to read binary data without any CR+LF translation is:
File.open(file, 'rb').read
Ruby 1.9 introduces a few new ways to do this that you might be inadvertently trying in your 1.8.7 environment. The second argument to read is the number of bytes you want to read, not the mode of the file.
Be sure to read the documentation on any method you're unfamiliar with. Sometimes things aren't quite what you'd expect.
I've set up ActionMailer 3.x for use outside of Rails, but the emails don't have a body. Can anyone help?
# ./app.rb
require 'action_mailer'
ActionMailer::Base.delivery_method = :file
ActionMailer::Base.file_settings[:location] = './tmp/mails'
ActionMailer::Base.view_paths = './views'
class Mailer < ActionMailer::Base
def instructions(email_address)
mail(:to => email_address, :subject => 'hello')
end
end
Mailer.instructions('test#email.com').deliver
Then I have two files for my views, one for plain text
# ./views/mailer/instructions.text.erb
These are some instructions
And one for html (using HAML - I know there are some potential issues with this, any advice here would be appreciated too!)
# ./views/mailer/instructions.html.haml
%html
%body
%h1 These are some HTML instructions
But if I check my newly created ./tmp/mails/test#email.com I only have the following, and no body text:
Date: Wed, 07 Sep 2011 18:38:09 +0530
From: my#address.com
To: test#email.com
Message-ID: <4e676ab8e2bc5_274560aab8567d0#mycomputer.mail>
Subject: hello
Mime-Version: 1.0
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Any ideas what's going on here? Thanks in advance
After migration from Rails 2.x -> 3.x I had the same problem (a bit different setup though). My html emails had no body. I moved my mailer class from models folder into mailers folder and changed my view file names:
file_name.text.html.erb
=>
file_name.html.erb
I hope this helps.
I have testmail.rb on a CENTOS 5 VM with SELinux permissive and IPtables off:
require 'rubygems'
require 'mail'
options = { :address => "mail.domain.com",
:port => 466,
:domain => 'otherdomain.com',
:user_name => 'somedude#domain.com',
:password => 'topsecret',
:authentication => 'plain',
:enable_starttls_auto => true }
Mail.defaults do
delivery_method :smtp, options
end
mail = Mail.new do
from 'somedude#otherdomain.com'
to 'admin#domain.com'
subject 'This is a test email'
body File.read('body.txt')
end
puts mail.to_s
The result when the script is run is this:
Date: Tue, 30 Nov 2010 12:12:58 -0500
From: somedude#otherdomain.com
To: admin#domain.com
Message-ID: <4cf5309a2f074_284015c5c4de91b8270b2#apvdbs03.3rdomain.local.mail>
Subject: This is a test email
Mime-Version: 1.0
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
test!
"test!" is the content of body.txt.
No email ever reaches the sent to account. The smtp settings we got from the sent to domain admin. I used telnet to successfully send an email to the domain on the unencrypted port (25) but got no response from the encrypted port (466), possibly because my telnet session was unencrypted?
What are some ways I can see what's going on during the script execution to troubleshoot?
Update:
Tried redirecting: > log.log 2>&1, but that didn't provide any additional info.
You're missing the line to actually send. Try adding
mail.deliver!
to the end of your script.
With 1.9.2p0, Date#parse assumes an UE format. Check out format.rb, line: 1042 if you don't believe me.
Anyways, how can I make it assume a US format, so that:
> Date.parse("10/4/2010")
=> Mon, 04 Oct 2010
Instead of April 10th.
I've tried this:
class Date
def _parse_eu(str,e)
_parse_us(str,e)
end
end
but no luck. Any other ideas?
Date.strptime is what you want but unfortunately it doesn't look like the documentation has the date formatting strings. I got the following to work based on Googling for the format strings:
1.9.2 > d = Date.strptime("10/4/2010", "%m/%d/%Y")
=> #<Date: 2010-10-04 (4910947/2,0,2299161)>
1.9.2 > d.day
=> 4
1.9.2 > d = Date.strptime("10/4/2010", "%d/%m/%Y")
=> #<Date: 2010-04-10 (4910593/2,0,2299161)>
1.9.2 > d.day
=> 10
You might want to check out strptime instead.