I'm not sure if this is an issue with my code, ActionMailer, Mail, or maybe even the icalendar gem?
A user registers for an event and they get an email with an ical attachment:
# app/mailers/registration_mailer.rb
class RegistrationMailer < ApplicationMailer
helper MailerHelper
def created(registration)
...
cal = Icalendar::Calendar.new
cal.event do |e|
e.dtstart = #event.start_time
e.dtend = #event.end_time
e.organizer = 'mailto:filterbuilds#20liters.org'
e.attendee = #recipient
e.location = #location.addr_one_liner
e.summary = #summary
e.description = #description
end
cal.append_custom_property('METHOD', 'REQUEST')
mail.attachments[#attachment_title] = { mime_type: 'text/calendar', content: cal.to_ical }
mail(to: #recipient.email, subject: "[20 Liters] You registered for a filter build on #{#event.mailer_time}")
end
...
end
I have text and HTML views:
app/views/registration_mailer/created.text.erb
app/views/registration_mailer/created.html.erb
When I omit the attachment, the email is structured like this:
Header stuff...
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary="--==_mimepart_63358693571_1146901122e"; charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_63358693571_1146901122e
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
[the text version of the email here]
----==_mimepart_63358693571_1146901122e
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit
[the HTML version of the email here]
----==_mimepart_63358693571_1146901122e--
When the attachment is present, the email is structured like this:
Header stuff...
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="--==_mimepart_6335c3388b140_114924286ed"; charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_6335c3388b140_114924286ed
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
[the text version of the email here]
----==_mimepart_6335c3388b140_114924286ed
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit
[the HTML version of the email here]
----==_mimepart_6335c3388b140_114924286ed
Content-Type: multipart/alternative; boundary="--==_mimepart_6335c3389bc30_114924287a3"; charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_6335c3389bc30_114924287a3
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
[the text version of the email AGAIN]
----==_mimepart_6335c3389bc30_114924287a3
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 7bit
[the HTML version of the email AGAIN]
----==_mimepart_6335c3389bc30_114924287a3--
----==_mimepart_6335c3388b140_114924286ed
Content-Type: text/calendar; charset=UTF-8
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=20Liters_filterbuild_20221011T0900.ical
Content-ID: <6335c3389e99c_1149242894f#railway.mail>
[numbers and letters]
----==_mimepart_6335c3388b140_114924286ed--
It's a weird tree suddenly:
1. Content-Type: multipart/mixed
A. Content-Type: text/plain
B. Content-Type: text/html
C. Content-Type: multipart/alternative
i. Content-Type: text/plain
ii. Content-Type: text/html
D. Content-Type: text/calendar
Rails' mailer preview doesn't reproduce this issue, nor does using Litmus' email client previews (because it seems to remove the text part and attachments), but I'm assuming with the deformed structure of content-types this isn't just a client-specific rendering issue.
I'm thinking this is coming from the Mail gem underneath ActionMailer structuring the content-types oddly, but I'm a bit out of my depth here. It could be ActionMailer, I really don't know how to tell.
I'm not very well versed in this, but I think I want this structure:
1. Content-Type: multipart/mixed
A. Content-Type: multipart/alternative
i. Content-Type: text/plain
ii. Content-Type: text/html
B. Content-Type: text/calendar
So, two questions:
1. If it's my code, what am I doing wrong?
2. If it's not my code, can I force the structure I want?
I've been combing through ActionMailer and Mail code bases, but haven't found a way to manually form my email to this level.
After more digging, I'm blaming ActionMailer, though I'm still not sure why text and html parts are getting added twice.
A monkey patch for my specific use was to let ActionMailer and Mail build the mail object and then just manually remove the unwanted parts:
# app/mailers/registration_mailer.rb
...
mail.attachments[#attachment_title] = { mime_type: 'text/calendar', content: cal.to_ical }
mail(to: #recipient.email, subject: "[20 Liters] You registered for a filter build on #{#event.mailer_time}")
# PATCH: text and html parts are getting inserted in multipart/mixed (top level) as well as multipart/alternative (2nd level)
mail.parts.reject! { |part| !part.attachment? && !part.multipart? }
This only works for my specific case. If the nesting that mail creates for you is different, your reject! statement will need to be different.
In my case, mail builds this structure:
1. Content-Type: multipart/mixed
A. Content-Type: text/plain
B. Content-Type: text/html
C. Content-Type: multipart/alternative
i. Content-Type: text/plain
ii. Content-Type: text/html
D. Content-Type: text/calendar
So I step into the first level (A. - D.) and reject any parts that are not multipart and not an attachment:
1. Content-Type: multipart/mixed
A. Content-Type: text/plain <-- not multipart, not attachment = rejected
B. Content-Type: text/html <-- not multipart, not attachment = rejected
C. Content-Type: multipart/alternative <-- is multipart, not attachment = kept
i. Content-Type: text/plain
ii. Content-Type: text/html
D. Content-Type: text/calendar <-- not multipart, is attachment = kept
If you are facing this issue, I recommend you use a debugger to inspect your mail object, specifically focusing on the parts. Keep in mind that parts can be deeply nested.
Mail::Part inherits from Mail::Message which has some helpful methods you can use to determine the "shape" of your message:
multipart?
attachment?
attachments
has_attachments?
boundary
parts
all_parts
And good luck.
Related
I'm using Postal to send emails with an HTML and Text portion.
When the email is sent to Gmail, it is displayed correctly. However, when it is displayed in at least two other email systems (Mail Enable's webmail interface, and an unknown system at a client), the text is rendered as something similar to Chinese. When the client forwards the email back to a Gmail account, the "Chinese" rendering is also visible.
Example email generated:
X-Sender: no-reply#thecompany.com
X-Receiver: therecipient#thecompany.com
MIME-Version: 1.0
From: no-reply#thecompany.com
To: therecipient#thecompany.com
Date: 17 Apr 2013 22:11:25 -0700
Subject: Some Subject
Content-Type: multipart/alternative;
boundary=--boundary_0_83808b99-ef32-4f47-8835-ba4a435a2141
----boundary_0_83808b99-ef32-4f47-8835-ba4a435a2141
Content-Type: text/plain; charset=utf-16
Content-Transfer-Encoding: base64
MIME ENCODED CONTENTS HERE==
----boundary_0_83808b99-ef32-4f47-8835-ba4a435a2141
Content-Type: text/html; charset=utf-16
Content-Transfer-Encoding: base64
MIME ENCODED CONTENTS HERE=
----boundary_0_83808b99-ef32-4f47-8835-ba4a435a2141--
Clearly there is an encoding issue that Gmail somehow sorts out but other email servers do not, but what exactly is the issue?
The charset is specified as utf-16. Is does Postal (or the MVC engine) in fact generate utf-8 output? How can I control the encoding of the output and/or the charset specified in the email header?
The character encoding can be explicitly set to utf-8 by adding the headers
Content-Type: text/plain; charset=utf-8
and
Content-Type: text/html; charset=utf-8
See this article for more information.
NOTE: There is a typo in the article. The text/plain line is missing a semicolon. That is corrected in the example above.
i am sending an e-mail with a doc file attachment. I am receiving the mail but with no any attachment.
PHP
$file_resume = '';
if (!empty($_FILES['attachment_file_name']['tmp_name'])) {
$file = $_FILES['attachment_file_name']['name'];
$attachment= file_get_contents($file);
$attachment= chunk_split(base64_encode($attachment));
}
$uid = md5(uniqid(time()));
$headers= "From: no-reply#edu.in"."\r\n";
$headers.= "Reply-To: no-reply#edu.in"."\r\n";
$headers.= "X-Mailer: PHP/" . phpversion()."\r\n Content-Type: multipart/mixed; boundary=\"PHP-mixed-".$uid."\"\r\n";
$headers.= "MIME-Version: 1.0" . "\r\n";
$headers.= "Content-type: text/html; charset=utf-8"."\r\n Content-Disposition: attachment; filename=\"".$attachment."\"\r\n";
$message = $_POST['person_name'];
mail($to,$subject,$message,$headers);
Html
<form id="attachment" action='mailer.php' method='POST' name="attachment" enctype="multipart/form-data">
person name: <input type="text" name="person_name" >
<br />
Attachment : <input id="attachment_file" class="field" style="height: 25px;" type="file" name="attachment_file_name" />
<input id="submit_button" type="submit" value="Send" />
</form>
If you're going to try to manually send an email with attachment(s), you need to get to know the underlying mail text packet that actually represents what you're constructing with the mail() arguments. Unless you comprehend and understand what's going on here, you'll struggle to ever get your email with attachment to send. There's too many sharp edges.
Gmail has a nifty feature on emails to view the original message packet, in plaintext. It's called Show Original. When testing your email script, use a Gmail account if you can so you can inspect the actual email plaintext packet. To Show Original, go to the top, left dropdown triggered by the down arrow next to the reply arrow of an email.
So sending a test email to myself with an image attached, we have what's below. Your mail call, essentially, has to translate into something more or less like this example.
Especially note how the headers start off (the top five headers below you will not handle, except maybe MIME-Version). At the end of that block, you have:
Content-Type: multipart/mixed; boundary=089e0118416874703004d86a5106
The part following the = is a message part boundary, which allows you to add multipart message blocks, including attachment file contents that have been encoded and given appropriate sub-part headers.
So there's:
--089e0118416874703004d86a5106
... message block(s) ...
--089e0118416874703004d86a5106--
There's actually another boundary declared, with:
Content-Type: multipart/alternative; boundary=089e0118416874702b04d86a5104
Which involves the text/plain and text/html dual message formats. This is not required, but many mail clients do it by nature. You can choose either text/plain or text/html, it's up to you and how your comment is formatted.
Next we see:
--089e0118416874703004d86a5106
Content-Type: image/jpeg; name="opinion (2).jpg"
Content-Disposition: attachment; filename="opinion (2).jpg"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_hejlmnuz0
/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAIEBAYIBggICAgICAgICAgKCgoKCgoKCgoKCgoKCgoK
... lots of lines for the encoded file block ...
W5R3W8ajLwrgUJEDGKWN2kWvO5iB7qdKwB6MwwfqVrQKcObveZxZBtQNCp0vc//Z
--089e0118416874703004d86a5106--
Note that the boundary's have a trailing -- at the end of their blocks (not each block).
Take a look below and compare that to the code that luk3thomas has in his answer. Try sending a very simple email message to yourself, and compare that to an attachment-formatted email:
mail('your#email.com', 'Simple mail test', 'Test message body content.');`
You should start to get the idea what's required to send emails with attachments included. The thing is, formatting an email packet for sending is very particular and little things can cause it to fail. So you have to pay attention.
MIME-Version: 1.0
Received: by 10.50.40.164 with HTTP; Thu, 21 Mar 2013 00:30:51 -0700 (PDT)
Date: Thu, 21 Mar 2013 02:30:51 -0500
Delivered-To: [redacted]#gmail.com
Message-ID: <CAKJE7RAH3+ZgN+86xykJrrzVaHK3waPD-a-OXbSDe3FGgcQrMw#mail.gmail.com>
Subject: Test of email with attachment for plaintext
From: Jared Farrish <[redacted]#gmail.com>
To: Jared Farrish <[redacted]#gmail.com>
Content-Type: multipart/mixed; boundary=089e0118416874703004d86a5106
--089e0118416874703004d86a5106
Content-Type: multipart/alternative; boundary=089e0118416874702b04d86a5104
--089e0118416874702b04d86a5104
Content-Type: text/plain; charset=UTF-8
This is the body of the email message.
--089e0118416874702b04d86a5104
Content-Type: text/html; charset=UTF-8
<div dir="ltr">This is the body of the email message.<br></div>
--089e0118416874702b04d86a5104--
--089e0118416874703004d86a5106
Content-Type: image/jpeg; name="opinion (2).jpg"
Content-Disposition: attachment; filename="opinion (2).jpg"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_hejlmnuz0
/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAIEBAYIBggICAgICAgICAgKCgoKCgoKCgoKCgoKCgoK
CgoKCgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/2wBDAQIICBAQEBAQEBAgICAgIEBAQEBA
QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQED/wgARCAEsASwDASIA
AhEBAxEB/8QAHQAAAgIDAQEBAAAAAAAAAAAABgcFCAIDBAkAAf/EABsBAAMBAQEBAQAAAAAAAAAA
[-- snip many lines of base64 file contents --]
yGrjmAV5jlw7j5RFebDQu8RG2rLa/up9x/xHOU5eppGo7lm3uDTz6w5XxBYgB7ruYJFwW/2PiD9q
XjDEturp55IeLGM36+Tlz8JY6v8AXGhta9/4Ih62UvuWHuP3DfOg/wAGFg/g0mNKnahdifSKKua+
h0likOlp+yFa2FgP0rtOReVitRTIiUuJSjw6JpN4thVyhRdZ8EE/aYzn523NZeJYu7judhllHo0g
W5R3W8ajLwrgUJEDGKWN2kWvO5iB7qdKwB6MwwfqVrQKcObveZxZBtQNCp0vc//Z
--089e0118416874703004d86a5106--
You'll need to add the attachment into the body of the email message. Try something like this:
$random_hash = md5(time());
$headers .= "\r\nContent-Type: multipart/alternative; boundary=\"PHP-alt-".$random_hash."\"";
$headers .= "\r\nMIME-Version 1.0";
$attachment = chunk_split(base64_encode(file_get_contents($filename)));
$message =
"--PHP-alt-$random_hash
Content-Type: text/plain
Dear Same,
We would like to thank you for your registration to be held on Saturday August 25, 2012 at the....
--PHP-alt-$random_hash
Content-Type: application/pdf; name=$filename
Content-Transfer-Encoding: base64
Content-Disposition: attachment
$attachment
--PHP-alt-$random_hash--";
#mail($to, $subject, $message, $headers);
I was searching how to add image to message body and found a lot of questions that it's impossible do it using MIME and add image to message body using standart MFMailComposeViewController.
Here: https://stackoverflow.com/a/1527833/456471
But I know the app in appstore: http://itunes.apple.com/app/emoji-free/id444304133
This app using standart mail client:
X-Mailer: iPhone Mail (9B206)
and MIME:
--Apple-Mail-FEB04051-AF80-474D-BC69-1A83514A008C
Content-Transfer-Encoding: 7bit
Content-Type: text/html;
charset=utf-8
<html><head></head><body bgcolor="#FFFFFF"><div></div><div><br>Brought to you by the Emoji app</div><div><br><br><img src="cid:5A2B705A-7ACB-438B-996F-FAEA8E92B28B" id="5A2B705A-7ACB-438B-996F-FAEA8E92B28B"></div><div><br><br>Best regards<div>Danil</div></div></body></html>
--Apple-Mail-FEB04051-AF80-474D-BC69-1A83514A008C
Content-Disposition: inline;
filename=emoji.png
Content-Id: <5A2B705A-7ACB-438B-996F-FAEA8E92B28B>
Content-Type: image/png;
name=emoji.png
Content-Transfer-Encoding: base64
Any suggestion how it can be?
I have batch request to update data.
Data Is:
Item : {
id : int,
name : string,
RefItem : {
int : id,
name : string }
}
I need
Create Item
Update RefItem reference for this newly created Item, in the same batch. (Suppose RefItems(1) already exists in data set)
According to OData spec:
--batch_1872-f36a-7ce8
Content-Type: multipart/mixed; boundary=changeset_8c16-9ba3-2260
--changeset_8c16-9ba3-2260
Content-Type: application/http
Content-Transfer-Encoding: binary
PUT $1/$links/ItemRef HTTP/1.1
Content-ID: 2
Accept: application/atomsvc+xml;q=0.8, application/json;q=0.5, /;q=0.1
DataServiceVersion: 1.0
Content-Type: application/json
{"uri":"ItemRefs(1)"}
--changeset_8c16-9ba3-2260
Content-Type: application/http
Content-Transfer-Encoding: binary
POST Products HTTP/1.1
Content-ID: 1
Accept: application/atomsvc+xml;q=0.8, application/json;q=0.5, /;q=0.1
DataServiceVersion: 1.0
Content-Type: application/json
{"id":-1,"name":"seven"}
--changeset_8c16-9ba3-2260--
--batch_1872-f36a-7ce8--
I suppose it should work, but I have response error :
--batchresponse_4febeba8-dd43-4040-9fdb-866afde5304e
Content-Type: multipart/mixed; boundary=changesetresponse_424dbf12-fa9a-4a59-8284-963fa0fa7b77
--changesetresponse_424dbf12-fa9a-4a59-8284-963fa0fa7b77
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 500 Internal Server Error
Content-ID: 2
X-Content-Type-Options: nosniff
DataServiceVersion: 1.0;
Content-Type: application/json;charset=utf-8
{"d":{"error":{"code":"","message":{"lang":"ru-RU","value":"An error occurred while processing this request."}}}}
--changesetresponse_424dbf12-fa9a-4a59-...
Can somebody tell me where I'm wrong?
Thanks.
The batch request is processed as a stream. So you can only refer to content ID which was already seen. The above sample refers to content ID 1 before the request operation with that content ID. That is not valid.
You need to first create the new instance (POST) and then add a reference to it via the $links URL.
In rails 3 when we attach pdf document in email through action mailer,
that pdf is not come as a attachment in e-mail, its come in body like
Date: Wed, 29 Dec 2010 19:56:12 +0530
Mime-Version: 1.0
Content-Type: application/pdf;
charset=UTF-8;
filename=free_book.pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
filename=free_book.pdf
Content-ID: <4d1b450431abc_### #BHUSHANF.mail>
JVBERi0xLjMKJf////8KMSAwIG9iago8PCAvQ3JlYXRvciAoUHJhd24pCi9Q
cm9kdWNlciAoUHJhd24pCj4+CmVuZG9iagoyIDAgb2JqCjw8IC9UeXBlIC9Q
YWdlcwovQ291bnQgMQovS2lkcyBbNSAwIFJdCj4+CmVuZG9iagozIDAgb2Jq
Cjw8IC9UeXBlIC9DYXRhbG9nCi9QYWdlcyAyIDAgUgo+PgplbmRvYmoKNCAw
IG9iago8PCAvTGVuZ3RoIDU4OAo+PgpzdHJlYW0KL0RldmljZVJHQiBjcwow
LjAwMCAwLjAwMCAwLjAwMCBzY24KL0RldmljZVJHQiBDUwowLjAwMCAwLjAw
MCAwLjAwMCBTQ04KcQoKQlQKNDEgNzQzLjUwNCBUZAovRjEuMCA4IFRmCls8
NGY3MjY0NjU3Mj5dIFRKCkVUCgoKQlQKNDEgNzM0LjI1NiBUZAovRjEuMCA4
IFRmCls8NTM3NTZkNmQ2MTcyPiAtMzAgPDc5Pl0gVEoKRVQKCjAuMDAwIDAu
MDAwIDAuMDAwIHNjbgoKQlQKMTA4IDc0My41MDQgVGQKL0YxLjAgOCBUZgpb
PDQyNjE3NDYzNjgyMDQ0NjE3NDY1MmY1NDY5NmQ2NTNhNTc+IDMwIDw2NTY0
MjA0NDY1NjMyMDMyMzkyMDMxMzkzYTM1MzYzYTMxMzEyMDJiMzAzNTMzMzAy
MDMyMzAzMTMwPl0gVEoKRVQKCjAuMDAwIDAuMDAwIDAuMDAwIHNjbgoKQlQK
MzAyIDc0My41MDQgVGQKL0YxLjAgOCBUZgpbPDRmNzI2NDY1NzIyMDYyNjE3
NDYzNjgyMDZlNmYzYTMxMzgzMjMxMzEzNTM5Mzk+XSBUSgpFVAoKMC4wMDAg
MC4wMDAgMC4wMDAgc2NuCgpCVAoyNDIuMTQ4IDY5OC44ODggVGQKL0YxLjAg
MTIgVGYKWzw1MDcyNmY2NDc1NjM3NDIwNmM2OTczNzQyMDY5NzMyMDZlNmY3
NDIwNjY+IDMwIDw2Zjc1NmU2NDJlPl0gVEoKRVQKClEKCmVuZHN0cmVhbQpl
bmRvYmoKNSAwIG9iago8PCAvVHlwZSAvUGFnZQovUGFyZW50IDIgMCBSCi9S
ZXNvdXJjZXMgPDwgL1Byb2NTZXQgWy9QREYgL1RleHQgL0ltYWdlQiAvSW1h
Z2VDIC9JbWFnZUldCi9Gb250IDw8IC9GMS4wIDYgMCBSCj4+Cj4+Ci9NZWRp
YUJveCBbMCAwIDYxMi4wIDc5Mi4wXQovQ29udGVudHMgNCAwIFIKPj4KZW5k
b2JqCjYgMCBvYmoKPDwgL0VuY29kaW5nIC9XaW5BbnNpRW5jb2RpbmcKL1R5
cGUgL0ZvbnQKL0Jhc2VGb250IC9IZWx2ZXRpY2EKL1N1YnR5cGUgL1R5cGUx
Cj4+CmVuZG9iagp4cmVmCjAgNwowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAw
MDAwMTUgMDAwMDAgbiAKMDAwMDAwMDA3MSAwMDAwMCBuIAowMDAwMDAwMTI4
IDAwMDAwIG4gCjAwMDAwMDAxNzcgMDAwMDAgbiAKMDAwMDAwMDgxNiAwMDAw
MCBuIAowMDAwMDAwOTk0IDAwMDAwIG4gCnRyYWlsZXIKPDwgL1Jvb3QgMyAw
IFIKL0luZm8gMSAwIFIKL1NpemUgNwo+PgpzdGFydHhyZWYKMTA5MQolJUVP
Rgo=
You probably did not define a view for the action. I had the same problem as you and by creating a view in app/views/test_mailer/view.text.erb the need for a :body was rendered obsolete.
This can happen when the :body tag is not present in the mail function.