Again: sending mail (through php) from an EC2 instance [closed] - amazon-ec2

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 9 years ago.
Improve this question
I have seen lots of discussion on the problem of sending mail from an amazon EC2 instance using php's mail function. None of the suggestions have worked with me.
Here is my setup:
I have ubuntu EC2 instance.
I followed the steps on Amazon's SES to install postfix using the credentials that were created for me. I tried both stunnel and direct methods.
I used swiftmail transport to send my mail according to this. The transport function for sending a mail returns 0 delivered.
I used php mail just by itself and it returns true. However, no mail is delivered not even in spam.
By the way I'm not using the production service with SES. The limits are fine with me, I just want it to work.
I suspect I'm not being authenticated properly and I don't see why. I tried to telnet ...amazonaws.com 25 and it gets connected. But when I tried the command Mail From: ... in telnet, it says authentication is required.
I have already thought of an alternative: sendgrid. Too expensive for my use.
Any thoughts?

Here is how I used Sendgrid to send mail from an EC2 instance using PHP:
Sign up for a Sendgrid account. You will receive a username and password to use for sending emails. Also, Sendgrid will manually verify your account (possibly to prevent spam).
Install PHP curl library in ubuntu: sudo apt-get install php5-curl.
Use this PHP code to send email:
$url = 'http://sendgrid.com/';
$user = 'sendgrid_user';
$pass = 'sendgrid_password';
$params = array(
'api_user' => $user,
'api_key' => $pass,
'to' => $dest_addr,
'subject' => $subject,
'html' => $body,
//'text' => 'testing body',
'from' => $from_addr,
);
$request = $url.'api/mail.send.json';
$session = curl_init($request);
// Tell curl to use HTTP POST
curl_setopt ($session, CURLOPT_POST, true);
// Tell curl that this is the body of the POST
curl_setopt ($session, CURLOPT_POSTFIELDS, $params);
// Tell curl not to return headers, but do return the response
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
// obtain response
$response = curl_exec($session);
//If the result is {"message":"success"}, then the mail is sent.
curl_close($session);

One alternative to postfix is to use "simple smtp" (ssmtp) which will provide a working sendmail that other program/frameworks may use. In this example, ssmtp will send emails via a gmail account.
First open a gmail account if you don't already have one
make sure you absolutely NOT have a concurrent mail app like xmail or postfix already installed or it will interfere
then install ssmtp : sudo apt-get install ssmtp
then edit /etc/ssmtp/ssmtp.conf (see below)
then edit /etc/ssmtp/revaliases (see below)
then test : echo message content | sendmail -v test#something.com
(optional) look at the log if it doesn't work : ll /var/log/mail.* and cat ...
Content of ssmtp.conf should be : (taken from my puppet module, replace <%= %> sections with your data)
root=<%= email %>
mailhub=smtp.googlemail.com:465
AuthUser=<%= email %>
AuthPass=<%= password %>
FromLineOverride=YES
UseTLS=YES
Warning : conf file should have unix eols.
And content of revaliases should be :
root:<%= email %>:smtp.googlemail.com:465
This technique is ultra-simple, but I guess it would not scale if you need to send hundreds of emails.
Another excellent link (in french) : http://doc.ubuntu-fr.org/ssmtp

Related

Symfony 5.4 - How am i supposed to properly start mercure using symfony [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 11 months ago.
Improve this question
I installed mercure using the following command as stated in the documentation:
composer require mercure
After that, i'm supposed to start the mercure server and here is the issue: how?
Symfony's documentation doesn't state that.
Mercure's documentation also doesn't state anything about it, especially using a config file generated in /config/packages/mercure.yaml.
What i've found is that i need to download a separate binary (for my specific platform) and then i'm supposed to start the server from Powershell using the following:
.\/bin/mercure run
However, if i try to pass -envfile ".env", then it doesn't even try to use any of the variables inside.
On top of that, it doesn't seem to be using any environment variables at all, even ones i've defined in cmd.
There are other things like how do i allow CORS, as there isn't something i can change in my config file, it isn't an argument i can pass to the binary and i can't add it as an environment variable.
if i ever try to subscribe to the event (which is done on port 2019 cause of the previous issues), 404 and CORS is returned.
also this is what i get with just .\/bin/mercure run:
My .env:
Right now i'm wondering how am i supposed to use mercure, i've looked at a couple videos for symfony 5.4 and they basically either are on linux and it somehow work for them OR It's marked as outdated cause of configs changes, syntax changes and others...
The documentation also doesn't help at all in that regard which kind of make me regret trying to use mercure.
EDIT:
I was told to try fiddling with Caddy configs, but it doesn't seem like mercure is trying to use those either, even tried specifying the config with -config.
Result:
Port not set to 3000
No allow_cors header
Caddyfile:
# Learn how to configure the Mercure.rocks Hub on https://mercure.rocks/docs/hub/config
{
{$GLOBAL_OPTIONS}
http_port 3000
https_port 3000
}
localhost:3000
route {
encode zstd gzip
#origin header Origin http://localhost:8000
header #origin Access-Control-Allow-Origin "http://localhost:8000"
header #origin Access-Control-Request-Method GET
header Access-Control-Allow-Credentials "true"
mercure {
# Publisher JWT key
publisher_jwt "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InB1Ymxpc2giOlsiKiJdfX0.iHLdpAEjX4BqCsHJEegxRmO-Y6sMxXwNATrQyRNt3GY"
subscriber_jwt "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJtZXJjdXJlIjp7InN1YnNjcmliZSI6WyIqIl19fQ.0J4jgLQSHzPMTZ5jiYM8Yv7jmTANRgILG5FKY98LgEU"
publish_origins *
# Extra directives
{$MERCURE_EXTRA_DIRECTIVES}
}
respond /healthz 200
respond "Not Found" 404
}
at this point, i feel like i'm using every tricks in the book and it still doesn't work, i'm probably gonna end up moving to periodic fetch instead, reliable and actually work.
Update 1:
Server Start on port 3000
CORS is now allowed
401 whenever i try to request a topic
Update 2:
No HTTP errors, event is apparently subscribed from what mercure say, but nothing received, something tell me mercure doesn't support semantics
Update 3:
With a topic such as /update/test/{0}, set in the update, the event source and yaml config, no updates are received on the client
Update 4:
i cannot get updates to work at all, either with or without semantics, here is my config and route atm:
mercure.yaml :
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: '["/update/test/*"]'
subscribe: '["/update/test/*"]'
My route (DefaultController):
#[Route('/update/test/{id}', name: 'test')]
public function test(HubInterface $hub, ?int $id): Response
{
$update = new Update("localhost:8000/update/test/{$id}",
son_encode(
array(
"result" => "success",
"data" => "User is in Room {$id}"
)
)
);
$hub->publish($update);
return new Response("Success");
}
I will give you an example of mercure running over windows without use of Docker, sorry for my english ok?!!
I will start from an installation of symfony 5.4 with a SSL virtual host: https://myshop.local and MercureBundle.
The goal is to notify for example, to all authenticated users when a new product enter on a shop stock.
First: the mercure hub and your site must run over the same domain or subdomain, in other case isn't possible to share the auth cookie; so our mercure hub will run on https://myshop.local:3000 (the same domain, but another port, check your firewall)
Second: It's neccesary to set the appropiates environment variables, so in your .env.local:
###> symfony/mercure-bundle ###
# See https://symfony.com/doc/current/mercure.html#configuration
# The URL of the Mercure hub, used by the app to publish updates (can be a local URL)
MERCURE_URL=https://myshop.local:3000/.well-known/mercure
# The public URL of the Mercure hub, used by the browser to connect
MERCURE_PUBLIC_URL=https://myshop.local:3000/.well-known/mercure
# The secret used to sign the JWTs
MERCURE_JWT_SECRET=m3rcu353cr37pa55pra53DEV
###< symfony/mercure-bundle ###
third: Mercure it´s based on topics concept (likes a channels), so your mercure.yaml recipe:
mercure:
hubs:
default:
url: '%env(MERCURE_URL)%'
public_url: '%env(MERCURE_PUBLIC_URL)%'
jwt:
secret: '%env(MERCURE_JWT_SECRET)%'
publish: ['notif/new-on-stock'] # the topic or topics where the notifications will be published coming from the mercure hub.
subscribe: ['notif/new-on-stock'] # the topic or topics to which users will subscribe to receive notifications.
fourth: You need to download the Windows binaries of mercure, and set somes configs in the Caddy file:
# Learn how to configure the Mercure.rocks Hub on https://mercure.rocks/docs/hub/config
{
{$GLOBAL_OPTIONS}
}
{$SERVER_NAME:myshop.local:3000}
log
tls C:\wamp64\bin\apache\apache2.4.41\conf\ssl\myshop.local.crt C:\wamp64\bin\apache\apache2.4.41\conf\ssl\myshop.local.key
route {
encode zstd gzip
mercure {
# Transport to use (default to Bolt)
transport_url {$MERCURE_TRANSPORT_URL:bolt://mercure.db}
# Publisher JWT key
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
# Subscriber JWT key
subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG}
cors_origins https://myshop.local
publish_origins https://myshop.local
# Extra directives
{$MERCURE_EXTRA_DIRECTIVES}
}
respond /healthz 200
respond "Not Found" 404
}
three important aspects here:
$SERVER_NAME
tls directive: you must set the paths of the .crt and .key
files of the SSL certificate of your virtualhost.
cors_origins and publish_origins must set your webapp domain to
avoid CORS errors
fifth: you must run your mercure server from your power shell; from inside your mercure binary directory run: $env:MERCURE_PUBLISHER_JWT_KEY='m3rcu353cr37pa55pra53DEV';$env:MERCURE_SUBSCRIBER_JWT_KEY='m3rcu353cr37pa55pra53DEV';.\mercure.exe run -config .\Caddyfile
Important!!: that key is the same of enviroment var MERCURE_JWT_SECRET
Now from a controller, you must publish on a topic a notification:
public function publishNewProductAction(Symfony\Component\Mercure\HubInterface $mercureHub):Response{
/** insert new product to a database **/
...
$em->persist($newProduct);
$em->flush();
$data=['productName'=>$newProduct->getName(), 'productPrice'=>$newProduct->getPrice()];
$update = new Symfony\Component\Mercure\Update();
$hubUpdate = new Symfony\Component\Mercure\Update(
'notif/new-on-stock', # the topic where you go to publish
\json_encode($data), # the data that you want to publish on the mercure hub
true # indicate that need auth
);
return new Response('A new product has been registered!!'); # its the normal response to user that insert a product (admin user for exmple)
}
Important: the real power of symfony to create a real-time notification system is in combining the Messenger component and async queue with Mercure. You can investigate about it.
So, on the client side for example:
<script type="text/javascript">
$(document).ready(function () {
const eventSource = new EventSource("{{ mercure('notif/new-on-stock', { subscribe:'notif/out-of-stock'})|escape('js')}}", {withCredentials: true});
eventSource.onopen = function () {
console.log('Socket connection!');
};
eventSource.onmessage = function (e) {
var data = JSON.parse(e.data);
console.log('A new product is enable for you: '+data.productName+'. Price: $'+data.price);
};
eventSource.onerror = function () {
console.log('Socket error!');
};
});
</script>
You have somes errors.
First: in your mercure.yaml you must stablish the topics on this way:
publish: ["update/test/{id}"] # your topic must complie with this format
subscribe: ["update/test/{id}"]
In your enviroment variables, you set MERCURE_JWT_SECRET, with that and the topics and subscribers declared in mercure.yaml, the symfony-mercure-bundle complete the JWT passed to the hub, you don't need to set it into de Caddy file.
And, in your function:
#[Route('/update/test/{id}', name: 'test')]
public function test(HubInterface $hub, ?int $id): Response
{
$update = new Update(sprintf("update/test/%s", $id)),
json_encode(
array(
"result" => "success",
"data" => sprintf("User is in Room %s", $id)
)
)
);
$hub->publish($update);
return new Response("Success");
}
Be have in account that the URI segment passed as parameter of Update, does not contain the characters '{}'

laravel swift-mailer exception "Expected response code 250 but got an empty response" using gmail smtp-relay (database queue driver)

the gmail smtp-relay works fine using the sync driver, but if we queue the email we this error. cleared config, cache, & restarted queue workers. tested in prod and dev, same results
[2021-01-24 20:04:22] production.ERROR: Expected response code 250 but got an empty response {"exception":"[object] (Swift_TransportException(code: 0): Expected response code 250 but got an empty response at /home/****/****/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php:448)
were wondering is this because of serialization and something is not making it through that process???
using latest stable release of laravel >8.0. gmail smtp is authenticating just fine, per why the sync driver sends emails easily. maybe there needs to be a timeout on the queue jobs so they dont barrage gmail so quickly? also our code works fine using sendgrid for example as the smtp relay. thanks.
See https://laracasts.com/discuss/channels/laravel/laravel-swift-mailer-exception-expected-response-code-250-but-got-an-empty-response-using-gmail-smtp-relay-database-queue-driver
Update your AppServiceProvider.php
add this inside boot();
// Fix for SwiftMailer Service;
$_SERVER["SERVER_NAME"] = "your.domain.name";
For users of smtp-relay.gmail.com, if you use localhost/127.0.0.1 as domain during developments, you probably need to change the domain name to use in EHLO command to begin the transaction. I solved this by adding &local_domain=dev.mydomain.tld at the and of my DSN.
smtp://smtp-relay.gmail.com:587?encryption=tls&local_domain=dev.mydomain.tld&...
For SwiftMailer Symfony bundle (since 2.4.0), you can set the local_domain config parameter:
// config/packages/dev/swiftmailer.yaml
swiftmailer:
...
local_domain: dev.mydomain.tld
Explanation for the 2 previous Answers
if $_SERVER["SERVER_NAME"] is the solution:
When you are using cron
The reason is that $_SERVER["SERVER_NAME"] is null when cron is executed. $_SERVER["SERVER_NAME"] is usually only defined for http access.
Example implementation (laravel):
if (!isset($_SERVER['SERVER_NAME'])) {
$url = config('env.APP_URL');
$domain = mb_ereg_replace("http(s)? ://", "", $url);
$domainParts = explode('/', $domain);
ini_set('server_name', count($domainParts) > 0 ? $domainParts[0] : $domain)
}
References :
Cron Job $_SERVER issue
https://github.com/swiftmailer/swiftmailer/issues/992
if 'local_domain' is the solution
When you have a mailhost setting of MAIL_HOST=smtp-relay.gmail.com in your laravel project
The reason is that if local_domain' is not set, the protocol for mail communication with Gmail will be EHLO [127.0.0.1]` and the communication will be disconnected.
By the way, I used gmail->gmail alias and did not need relay in the first place, so I solved the problem by setting MAIL_HOST=smtp.gmail.com.
References:
https://blog.trippyboy.com/2021/laravel/laravel-expected-response-code-250-but-got-an-empty-response/
I had to deal with both of them because of cron messaging and MAIL_HOST=smtp-relay.gmail.com in my environment.
I hope this information will help you.

Laravel Office365 Expected response code 250 but got code "", with message ""

Need help setting up Mail on my Laravel application.
My .env file:
MAIL_DRIVER=smtp
MAIL_HOST=smtp.office365.com
MAIL_PORT=587
MAIL_USERNAME=*****#*****.com
MAIL_PASSWORD=******
MAIL_ENCRYPTION=tls
This returns the following error :
Expected response code 250 but got code "", with message ""
I had the same problem and it turns out Office 365 doesn't let you use a different from setting in your mail sending function than the one you use in your .env file.
I wanted to set the from name and email address according to whatever data was coming in via the request of a form submission as to capture them in the CRM system and it threw that same exception. Not sure if this helps you, but this was how I solved my problem.
Here's my code in the controller as an example:
$data = [
'name' => $request->get('name'),
'surname' => $request->get('surname'),
'email' => $request->get('email'),
'phone' => $request->get('phone'),
'subject' => $request->get('message-subject')
];
$from = $data['name'] . ' ' . $data['surname'];
Mail::send('mail.mymail', compact('data'), function($message) use ($request, $data, $from) {
$message->to('example#companyiworkfor.com', 'Some Company I Work For')->subject('Website Contact Form');
$message->from($data['email'], $from);
// Office 365 won't let you use a different from address here,so use same email as per your .env
});
We were getting the same error message in one of our customer's internal tool, which runs on Symfony 3 and Swiftmailer v5.4.4.
After some debugging, I found out that there was a previous exception thrown by Swiftmailer. The reason was the response from smtp.office365.com:
421 4.7.66 TLS 1.0 and 1.1 are not supported. Please upgrade/update your client to support TLS 1.2. Visit https://aka.ms/smtp_auth_tls. [AM6PR02CA0020.eurprd02.prod.outlook.com]
Microsoft dropped support for TLS 1.0 und 1.1. After some research I found this github issue:
This commit (4c4b333) introduced TLS 1.1 / 1.2 support, but didn't make it to any release yet. Unfortunately all Swiftmailer users (besides the ones using recent dev-master) are tied to TLS 1.0, which starts to give issues
In one of the comments #fabpot replied that they've backported the bugfix to Swiftmailer 5. It landed in release 5.4.10.
After upgrading Swiftmailer, the mailing worked again.

CodeIgniter Email not being received

I currently have a contact form on my website which sends off an email notification upon submission. It is working perfectly on my end and even when I send it externally to friends as a test they are receiving it.
My client however, who is in France, can't seem to receive this email at all and I have no idea why. Is there anything I need to do to ensure that they receive it? I thought it was something on their end but they're absolutely adamant that it's not and that it's a problem with my code. I have tried different email addresses for them with different domains and still no luck!
$this->load->library('parser');
$config['mailtype'] = 'html';
$config['charset'] = 'utf-8';
$this->load->library('email');
$data = array(
'first_name' => $this->input->post('first_name'),
'last_name' => $this->input->post('last_name'),
'email' => $this->input->post('email'),
'phone' => $this->input->post('phone'),
'message' => $this->input->post('message')
);
$body = $this->parser->parse('html_email', $data, true);
$this->email->from('test#test.com', 'Duparc');
$this->email->to('test#test.com');
$this->email->subject('Test Email');
$this->email->message($body);
$this->email->send();
In my opinion the problem does not originate from CodeIgniter. Assuming the mail did not went into a spam box, your customer's provider is probably dismissing your message for some other reason. One reason I can immediately think of is that you are probably trying to send a message from a domain (e.g. test.com) you're not allowed from. My advise is starting to try sending an email with another tool to your french customer (any sendmail or postfix client tool) and to see if he do receive it.
If that succeeds, then the problem probably comes from your forged email (therefore try using a valid domain name). Sometimes removing it completely will work too (it might then be replaced by the external IP).
If not, then the problem comes from your web server's config. I had this problem using postfix where I had to explicitly set the "General Options" > "What domain to use in outbound mail" configuration option to my domain name in order to be accepted by a capricious server which silently dismissed our mail.
If nothing is working, you might also use another mail server which might luckily fix the issue. Here is the code I used to force CodeIgniter using sendmail instead:
$config['protocol'] = 'sendmail';
$config['mailpath'] = '/usr/sbin/sendmail';
$this->email->initialize($config);

extra newlines in plain text emails sent via sendgrid

We send emails in plain text from PHP using CodeIgniter (v1.7) and also PHPMailer (v5.1). Current production setup uses a cheapie SMTP relay, plan is to switch to a CritSend or SendGrid. We are testing the options now from a Rackspace Cloud server.
When we use SendGrid SMTP all "\r\n" newlines in the emials end up being doubled up, so end up as "\r\n\r\n".
All works fine when using CritSend SMTP and also two other SMTP servers.
SendGrid tech support don't think it is anything to do with their system, but have heard of another client with the same issue and apparently it got solved with a config change on the client's side.
Anyone experienced this?
This isn't critical for us as CritSend works well and seems as good as SendGrid on features, so we will go with them. BUT being a curious type, I just can't let this go :-)
Usual setup: PHP script -> sendmail/Postfix -> external SMTP relay -> ....
To test the different SMTP relays I change the postfix config, only SendGrid gives the extra newlines all other SMTP options work fine. If I dump the email via CodeIgniter email class debug function it looks fine before it goes to postfix.
Alternate setup: PHP script (either CI mail class or PHPMialer) -> external SMTP relay -> ....
To test the different SMTP relays I change the SMTP settings in the CI email config or PHPMialer config. Only SendGrid gives the extra newlines all other SMTP options work fine.
There aren't all that many options to play with as far as I can see. I tried "utf-8" and "ISO-something or other", all newlines on our side are "\r\n"...... seesm like some very obscure bug somewhere.
Any ideas?
OK, a bit more experimenting and these settings make plain text emails go trough SendGrid nicely from PHPMailer:
$mailer->CharSet = "utf-8";
$mailer->LE = "\r\n";
$mailer->Encoding = "quoted-printable";
$mailer->WordWrap = 80;
The "quoted-printable" part is the key.
What worked for me was using actual line-breaks in the PHP text as follows:
// Prepare email
$email = array(
'api_user' => App::emailAPIUser(),
'api_key' => App::emailAPIPwd(),
'to' => $email,
'subject' => 'Thank you for entering ' . App::name(),
'html' => $email_body,
'text' => '
Thank you for entering Competition.
You are now in the running to WIN your prices valued at $6000.
Winners will be notified of their status by the 14th February 2012.
Good luck!',
'from' => 'competitions#company.com'
);

Resources