Skip to content

Fixed #34: Added support for attachments - #35

Open
faishal wants to merge 11 commits into
masterfrom
feature/add-attachment-support
Open

Fixed #34: Added support for attachments#35
faishal wants to merge 11 commits into
masterfrom
feature/add-attachment-support

Conversation

@faishal

@faishal faishal commented Feb 23, 2018

Copy link
Copy Markdown
  • Created multipart email for mails with attachments.
  • Added param in wp-cli commad.
  • Updated readme with example command.

Signed-off-by: Faishal Saiyed <saiyedfaishal@gmail.com>

@rmccue rmccue left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also want review from @joehoyle on this.

Comment thread inc/class-ses.php Outdated
*
* @return string
*/
private function get_raw_message( $to, $subject, $message, $headers = array(), $attachments = array() ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be protected rather than private

Comment thread inc/class-ses.php Outdated
$reply_to = array_merge( (array) $reply_to, explode( ',', $content ) );
break;
default:
$raw_message_header .= $name . ': ' . $content . "\n";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should $content be newline-escaped to avoid header overflow issues?

Comment thread inc/class-ses.php Outdated
$raw_message .= 'MIME-Version: 1.0' . "\n";
$raw_message .= 'Content-type: Multipart/Mixed; boundary="' . $boundary . '"' . "\n";
$raw_message .= "\n--{$boundary}\n";
$raw_message .= 'Content-type: Multipart/Alternative; boundary="alt-' . $boundary . '"' . "\n";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be Content-Type with the actual type lowercase?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into RFC for that, In starting it showing Content-Type but in the example, it was showing Content-type, so I tested both and working fine.
Ref: https://www.w3.org/Protocols/rfc1341/rfc1341.html

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, headers are theoretically case-insensitive (likewise in HTTP), but in practice, it's best to use the exact case.

Comment thread inc/class-ses.php Outdated
$custom_from = sprintf( '%s <%s>', apply_filters( 'wp_mail_from_name', get_bloginfo( 'name' ) ), apply_filters( 'wp_mail_from', $from_email ) );
}

$boundary = uniqid( rand(), true );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wp_rand() would be better than uniqid( rand() ) I think, and I'd probably add a prefix as well:

$boundary = 'aws-ses-wp-mail-' . wp_rand();

Comment thread inc/class-ses.php Outdated

$raw_message .= 'MIME-Version: 1.0' . "\n";
$raw_message .= 'Content-type: Multipart/Mixed; boundary="' . $boundary . '"' . "\n";
$raw_message .= "\n--{$boundary}\n";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably use sprintf() for these instead, I think it's a bit more readable.

Comment thread inc/class-ses.php Outdated
$raw_message .= "\n--alt-{$boundary}--\n";

foreach ( $attachments as $attachment ) {
if ( ! @is_file( $attachment ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a better way to check this and avoid the error suppression operator?

Comment thread inc/class-ses.php Outdated
}

$raw_message .= "\n--{$boundary}\n";
$raw_message .= 'Content-Type: ' . $file_type['type'] . '; name="' . $filename . '"' . "\n";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use sprintf() for this.

@joehoyle

Copy link
Copy Markdown
Member

I thing the biggest problem here is that working with attachments means different filters and way of sending the message, which is going to be annoying to work with. As a developer I can't rely on aws_ses_wp_mail_ses_send_message_args in case email would happen to have an attachment. That's probably not such a big issue if we have "higher level" hooks for most configuration. I know I've had to use this catch-all hook for things before as a suitable WordPress hook for email modification hasn't existed.

We could also consider only ever sending raw, so there's only one method to deal with. I don't see much value in keeping around SendMessage if we have to have a fully reliable SendRawMessage implementation. Having a branch is undoubtedly going to cause some bugs. However, SendMessage is presumably easier to implement and less error prone, so we need to weigh up the benefit of that versus the pretty rarely used sending of attachments.

If we push ahead I think get_raw_message needs to be broken up to have the parsing be in their own functions to actually be able to read this code without getting lost. This like concat of the strings with \n are also pretty gnarly to read, we should be able to make this is a little clearer with some imploding etc.

@faishal

faishal commented Feb 27, 2018

Copy link
Copy Markdown
Author

@joehoyle That makes sense, After reading your comment I dig into what we can do to make it simple.

How about using PHPMailer class of WordPress and build raw email?

I looked into the wp_mail function for phpmailer usage and able to generate a raw email using it :
https://gist.github.com/faishal/5d6d1c2a0f03d9a7534f9e836bcd6c34

We can also create a custom class to do that but I feel like phpMailer is providing all encoding supports.

@joehoyle

joehoyle commented Mar 3, 2018

Copy link
Copy Markdown
Member

Interesting, I think using PHPMailer might be handy. In that case, do we even need to hook wp_mail, can't we just capture the PHPMailer getSentMIMEMessage pre-send?

@faishal

faishal commented Mar 5, 2018

Copy link
Copy Markdown
Author

Do you mean removing wp_mail pluggable method from plugin?

In that case it will not require to build the mail, we just have to send it via aws ses api.
Unfortunately, There isn't any hook/filter in class-phpmailer.php, but there is one way I can think of.

PHPMailer::postSend calls Method based on PHPMailer::Mailer value, so if we set PHPMailer::Mailer value to awsses then it will call awssesSend method.

If we don't want to repeat what wp_mail is doing and just want to hook custom send method then we can do following.

  1. Define SES_PHPMailer from base class PHPMailer with custom awssesSend method.
  2. Hook into wp_mail filter and initiate custom SES_PHPMailer class object and assign it to global $phpmailer only if we have the valid AWS constants defined.
  3. Hook into phpmailer_init and set PHPMailer::Mailer value to awsses so that it will call awssesSend method.
  4. in awssesSend we will get raw mail body by calling getSentMIMEMessage method, We just have to initiate aws client and send mail using sendRawEmail method of aws and throw an appropriate phpmailerException exceptions if we catch any.

@joehoyle

joehoyle commented Mar 8, 2018

Copy link
Copy Markdown
Member

Cool yeah that's what I was getting at. I think this approach is certainly interesting. I'm not 100% whether to proceed - what would be your suggested route?

@faishal

faishal commented Mar 12, 2018

Copy link
Copy Markdown
Author

@joehoyle We can set milestone 0.2.0 which will include the above-mentioned refactoring and close this PR. We can test this with other major plugins to verify its working as expected.

@faishal

faishal commented Apr 5, 2018

Copy link
Copy Markdown
Author

@joehoyle - I created PR #37 with above suggested changes

Comment thread inc/class-ses.php
}
$raw_message .= sprintf( "\n--alt-%s--\n", $boundary );

foreach ( $attachments as $attachment ) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@faishal - wp_mail $attachments param also accepts a single string file location as a valid param. Currently, passing a single string results in attachments not being sent here.

We could follow cores approach and cast as follows (before entering the loop here)

    if ( ! is_array( $attachments ) ) {
        $attachments = explode( "\n", str_replace( "\r\n", "\n", $attachments ) );
    }

@roborourke

Copy link
Copy Markdown
Contributor

Is this already in use? I noticed we have a 2.0.0-alpha tag with this feature.

@roborourke

Copy link
Copy Markdown
Contributor

@tomjn do you have a use case for this currently?

@tomjn

tomjn commented Mar 12, 2025

Copy link
Copy Markdown
Contributor

@roborourke I had looked at it as part of something else briefly but wanted to close out my tabs and focus elsewhere, I know this had discussion on a client project very recently but @Rayhatron is a better person to ask.

What would be good to have is a summary of where this work currently is, is there remaining blockers, what tasks need completing, how much time would need to be spent to ship this. Hence the help wanted label. Unfortunately I don't have the bandwidth to champion this myself.

@Rayhatron

Copy link
Copy Markdown

@roborourke sorry, I seem to have missed this so only seeing it now. A use case for this would be a form that collects user details and generates a certificate of attendance PDF for an event then sends that in an email after the user submits the form.

Due to attachments not being supported, workaround is to send a link in the email that when opened will generate and download/show the PDF.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants