I apply a foreach loop in the email configuration SMTP settings dynamically from database.there are multiple SMTP servers in the SMTP server table. I am selecting the SMTP information dynamically from the table according to smtp_server_id from processing table and storing it in an email configuration before sending the email on the fly. But on the first iteration, the SMTP servers that come from dB are stored on email configuration like as config('mail.smtp.host') but on the second iteration till the end of loop, the SMTP information do not change. The SMTP information remains the same and Mail Configuration variables remains same when they are on the first iteration. What should I do to change the SMTP configuration dynamically one one by one according to the smtp_id in foreach loop.
My cron job to send emails.
namespace App\Console\Commands;
use App\Mail\SendEmail;
use App\Models\CronjobSetting;
use App\Models\ProcessingEmail;
use App\Models\SmtpServer;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
class SendEmailsBasedonDate extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'sendemailviadate:cron';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
$cronJob = CronjobSetting::whereDate('created_at', Carbon::today())->first();
if ($cronJob->email_group_type == 1) {
$processingEmails = ProcessingEmail::all();
foreach ($processingEmails as $processingEmail) {
$smtpServer = SmtpServer::where('id', $processingEmail->smtp_id)->first();
Config::set('mail.mailers.smtp.host', $smtpServer->hostname);
Config::set('mail.mailers.smtp.port', $smtpServer->port);
Config::set('mail.mailers.smtp.username', $smtpServer->username);
Config::set('mail.mailers.smtp.password', $smtpServer->password);
$email = new SendEmail($processingEmail);
Mail::to($processingEmail->recipient_email)->send($email);
if (Mail::failures()) {
ProcessingEmail::where('id', $processingEmail->id)->update(
['status' => 3]
);
} else {
ProcessingEmail::destroy($processingEmail->id);
}
}
} else {
$processingEmails = ProcessingEmail::where('email_group_id', $cronJob->email_group_id)->get();
foreach ($processingEmails as $processingEmail) {
$smtpServer = SmtpServer::where('id', $processingEmail->smtp_id)->first();
Config::set('mail.mailers.smtp.host', $smtpServer->hostname);
Config::set('mail.mailers.smtp.port', $smtpServer->port);
Config::set('mail.mailers.smtp.username', $smtpServer->username);
Config::set('mail.mailers.smtp.password', $smtpServer->password);
$email = new SendEmail($processingEmail);
Mail::to($processingEmail->recipient_email)->send($email);
if (Mail::failures()) {
ProcessingEmail::where('email_leads_id', $processingEmail->email_lead_id)->update(
['status' => 3]
);
} else {
ProcessingEmail::where('email_leads_id', $processingEmail->email_lead_id)->delete();
}
}
return Command::SUCCESS;
}
}
}
SO I will give you two options
one: register the MailServiceProvider after new config.
(new \Illuminate\Mail\MailServiceProvider(app()))->register();
two: creating a new Instance of swift mailer
<?php
$transport = (new \Swift_SmtpTransport('host', 'port'))
->setEncryption(null)
->setUsername('username')
->setPassword('secret')
->setPort($port);
$mailer = app(\Illuminate\Mail\Mailer::class);
$mailer->setSwiftMailer(new \Swift_Mailer($transport));
$mail = $mailer
->to('user#laravel.com')
->send(new SendEmail($processingEmail));
Basically, you are creating a new Instance of swift mailer here and adding providing to laravel mailer.
Related
I plan to write a PHP script that makes an SSH connection. I've investigated how to do this and this looks the most promising solution: https://github.com/phpseclib/phpseclib My only issue is how to handle the fact that my SSH key has a passphrase, and I don't want to have to enter it every time I run the script. For every day SSH use I have ssh-agent running in the background, and it's configured to use pinentry. This makes it so that I don't have to enter my passphrase EVERY time. Any ideas as to how I could get PHP and ssh-agent to talk to each other? My only clue is that ssh-agent sets an environment variable, SSH_AUTH_SOCK, pointing to a socket file.
While the documentation for phpseclib addresses this issue, its answer is silly (just put the passphrase in the code): http://phpseclib.sourceforge.net/ssh/2.0/auth.html#encrsakey
UPDATE: I've looked more into phpseclib and written my own simple wrapper class. However, I cannot get it to login either through ssh-agent or by supplying my RSA key. Only password-based authentication works, contrary to my experiences logging in directly with the ssh command. Here is my code:
<?php
// src/Connection.php
declare(strict_types=1);
namespace MyNamespace\PhpSsh;
use phpseclib\System\SSH\Agent;
use phpseclib\Net\SSH2;
use phpseclib\Crypt\RSA;
use Exception;
class Connection
{
private SSH2 $client;
private string $host;
private int $port;
private string $username;
/**
* #param string $host
* #param int $port
* #param string $username
*
* #return void
*/
public function __construct(string $host, int $port,
string $username)
{
$this->host = $host;
$this->port = $port;
$this->username = $username;
$this->client = new SSH2($host, $port);
}
/**
* #return bool
*/
public function connectUsingAgent(): bool
{
$agent = new Agent();
$agent->startSSHForwarding($this->client);
return $this->client->login($this->username, $agent);
}
/**
* #param string $key_path
* #param string $passphrase
*
* #return bool
* #throws Exception
*/
public function connectUsingKey(string $key_path, string $passphrase = ''): bool
{
if (!file_exists($key_path)) {
throw new Exception(sprintf('Key file does not exist: %1$s', $key_path));
}
if (is_dir($key_path)) {
throw new Exception(sprintf('Key path is a directory: %1$s', $key_path));
}
if (!is_readable($key_path)) {
throw new Exception(sprintf('Key file is not readable: %1$s', $key_path));
}
$key = new RSA();
if ($passphrase) {
$key->setPassword($passphrase);
}
$key->loadKey(file_get_contents($key_path));
return $this->client->login($this->username, $key);
}
/**
* #param string $password
*
* #return bool
*/
public function connectUsingPassword(string $password): bool
{
return $this->client->login($this->username, $password);
}
/**
* #return void
*/
public function disconnect(): void
{
$this->client->disconnect();
}
/**
* #param string $command
* #param callable $callback
*
* #return string|false
*/
public function exec(string $command, callable $callback = null)
{
return $this->client->exec($command, $callback);
}
/**
* #return string[]
*/
public function getErrors(): array {
return $this->client->getErrors();
}
}
And:
<?php
// test.php
use MyNamespace\PhpSsh\Connection;
require_once(__DIR__ . '/vendor/autoload.php');
(function() {
$host = '0.0.0.0'; // Fake, obviously
$username = 'user'; // Fake, obviously
$connection = new Connection($host, 22, $username);
$connection_method = 'AGENT'; // or 'KEY', or 'PASSWORD'
switch($connection_method) {
case 'AGENT':
$connected = $connection->connectUsingAgent();
break;
case 'KEY':
$key_path = getenv( 'HOME' ) . '/.ssh/id_rsa.pub';
$passphrase = trim(fgets(STDIN)); // Pass this in on command line via < key_passphrase.txt
$connected = $connection->connectUsingKey($key_path, $passphrase);
break;
case 'PASSWORD':
default:
$password = trim(fgets(STDIN)); // Pass this in on command line via < password.txt
$connected = $connection->connectUsingPassword($password);
break;
}
if (!$connected) {
fwrite(STDERR, "Failed to connect to server!" . PHP_EOL);
$errors = implode(PHP_EOL, $connection->getErrors());
fwrite(STDERR, $errors . PHP_EOL);
exit(1);
}
$command = 'whoami';
$result = $connection->exec($command);
echo sprintf('Output of command "%1$s:"', $command) . PHP_EOL;
echo $result . PHP_EOL;
$command = 'pwd';
$result = $connection->exec($command);
echo sprintf('Output of command "%1$s:"', $command) . PHP_EOL;
echo $result . PHP_EOL;
$connection->disconnect();
})();
The SSH2 class has a getErrors() method, unfortunately it wasn't logging any in my case. I had to debug the class. I found that, whether using ssh-agent or passing in my key, it always reached this spot (https://github.com/phpseclib/phpseclib/blob/2.0.23/phpseclib/Net/SSH2.php#L2624):
<?php
// vendor/phpseclib/phpseclib/phpseclib/Net/SSH2.php: line 2624
extract(unpack('Ctype', $this->_string_shift($response, 1)));
switch ($type) {
case NET_SSH2_MSG_USERAUTH_FAILURE:
// either the login is bad or the server employs multi-factor authentication
return false;
case NET_SSH2_MSG_USERAUTH_SUCCESS:
$this->bitmap |= self::MASK_LOGIN;
return true;
}
Obviously the response being returned is of type NET_SSH2_MSG_USERAUTH_FAILURE. There's nothing wrong with the login, of that I'm sure, so per the comment in the code that means the host (Digital Ocean) must use multi-factor authentication. Here's where I'm stumped. What other means of authentication am I lacking? This is where my understanding of SSH fails me.
phpseclib supports SSH Agent. eg.
use phpseclib\Net\SSH2;
use phpseclib\System\SSH\Agent;
$agent = new Agent;
$ssh = new SSH2('localhost');
if (!$ssh->login('username', $agent)) {
throw new \Exception('Login failed');
}
Updating with your latest edits
UPDATE: I've looked more into phpseclib and written my own simple wrapper class. However, I cannot get it to login either through ssh-agent or by supplying my RSA key. Only password-based authentication works, contrary to my experiences logging in directly with the ssh command.
What do your SSH logs look like with both Agent authentication and direct public key authentication? You can get the SSH logs by doing define('NET_SSH2_LOGGING', 2) at the top and then echo $ssh->getLog() after the authentication has failed.
Per neubert, what I had to do was add this line to Connection.php and I was able to get agent-based authentication to work:
$this->client->setPreferredAlgorithms(['hostkey' => ['ssh-rsa']]);
I still can't get key-based authentication to work, but I don't care about that as much.
I am sending mails with Laravel like this:
foreach ($users as $user) {
\Mail::to($user())->send(new Newsletter($user));
}
I would like to have an array of all the users who had a bad_domain response. I found in the docs that Laravel uses Swiftmailer which has a way to find bad_domain respones:
// Pass a variable name to the send() method
if (!$mailer->send($message, $failures))
{
echo "Failures:";
print_r($failures);
}
/*
Failures:
Array (
0 => receiver#bad-domain.org,
1 => other-receiver#bad-domain.org
)
*/
However, I want to use the a Mailable class. I am not sure how I can do this with the Swiftmailer (which I can access through \Mail::getSwiftMailer()).
Is there any easy way of getting the bad_domains when using Mailable from Laravel?
You may only access bad_domains, but not bounces with Swiftmailer (Swiftmailer 4 does not retrieve bounces as $failedRecipients).
One can get bad_domains it with
\Mail::to($user)->send(new \App\Mail\Hi());
dd(\Mail::failures());
See Illuminate\Mail\Mailer.php
/**
* Send a Swift Message instance.
*
* #param \Swift_Message $message
* #return void
*/
protected function sendSwiftMessage($message)
{
try {
return $this->swift->send($message, $this->failedRecipients);
} finally {
$this->forceReconnection();
}
}
I am not sure whether this question is related to stomp-php or ActiveMQ Docker (running with defaults).
I have a simple Queue helper class written in PHP that handles both sending the message to the queue (Queue::push), as well as consumes it (Queue::fetch). See code below.
As you can see, fetch() should subscribe to the queue, read one message and unsubscribe. The message should be acknowledged automatically (\Stomp\StatefulStomp::subscribe(), 3rd. argument).
For some reason, about 5-7% of the messages are received by the customer twice or even three times. Why messages are delivered multiple times and how to avoid it?
Publisher (pushing 1000 messages):
$mq = new Queue('tcp://activemq:61613','test');
for ($msgCount = 0; $msgCount < 1000; $msgCount++) {
$mq->push('Message #' . $msgCount);
}
Consumer (receiving ~1070 messages):
$mq = new Queue('tcp://activemq:61613','test');
$received = 0;
while (true) {
$message = $mq->fetch();
if (null === $message) { break; }
$received++;
}
Queue class code:
use Stomp\Client;
use Stomp\Network\Connection;
use Stomp\SimpleStomp;
use Stomp\StatefulStomp;
use Stomp\Transport\Message;
class Queue
{
/**
* #var \Stomp\StatefulStomp
*/
private $stomp;
private $queue;
public function __construct($uri, $queue) {
$connection = new Connection('tcp://activemq:61613');
$this->stomp = new StatefulStomp(new Client($connection));
$connection->setReadTimeout(1);
$this->queue = $queue;
}
public function push($body) {
$message = new Message($body, ['activemq.maximumRedeliveries' => 0]);
$this->stomp->send('/queue/' . $this->queue, $message);
}
public function fetch() {
$subscriptionId = $this->stomp->subscribe('/queue/' . $this->queue, null, 'auto', ['activemq.prefetchSize' => 1]);
$msg = $this->stomp->read();
$this->stomp->unsubscribe($subscriptionId);
return $msg;
}
}
How to add eventListener to swiftmailer send event?
Every time i send email i create a file and attach it to email, and after sending i want to unlink that file. How to do that?
Thanks.
file_put_contents($path, implode(";\r\n", $result));
$message = (new \Swift_Message('VAT checking result !'))
->setFrom('vah#gmail.com')
->setTo($vat->getEmail())
->setBody(
'Hello, ...' ,'text/')
->attach(\Swift_Attachment::fromPath($path));
// START send result email
$mailer = $this->container->get('mailer');
$listener = $this->container->get('app.service.send_email_listener');
$listener->setPathToFile($path);
$mailer->registerPlugin($listener);
$mailer->send( $message );
// END send email to admin
//unlink($path); email will not be sent
I tried to register listener like that
app.service.send_email_listener:
class: AppBundle\Listener\SendEmailListener
public: true
tags:
- { name: swiftmailer.plugin }
this is listener class :
namespace AppBundle\Listener;
use \Swift_Events_SendListener as base;
class SendEmailListener implements base
{
private $pathToFile;
public function setPathToFile($path)
{
$this->pathToFile = $path;
}
public function getPathToFile($path)
{
return $this->pathToFile;
}
/**
* Invoked immediately before the Message is sent.
*
* #param \Swift_Events_SendEvent $evt
*/
public function beforeSendPerformed(\Swift_Events_SendEvent $evt)
{
}
/**
* Invoked immediately after the Message is sent.
*
* #param \Swift_Events_SendEvent $evt
*/
public function sendPerformed(\Swift_Events_SendEvent $evt)
{
if($this->pathToFile){
unlink($this->pathToFile);
}
}
}
EDIT
It executes the method, but swift is not able to stream the file , beacuse the the file is unlinked before the sending is end...
This is from dev_logs:
[2018-05-24 20:40:18] php.CRITICAL: Uncaught Exception: Unable to open file for reading [C:\Users\\projects\vat\web\vatfiles\122.txt] {"exception":"[object] (Swift_IoException(code: 0): Unable to open file for reading [C:\\Users\\\projects\\vat\\web\\vatfiles\\122.txt] at C:\\Users\\projects\\vat\\vendor\\swiftmailer\\swiftmailer\\lib\\classes\\Swift\\ByteStream\\FileByteStream.php:144)"} []
As an alternative to using a Swiftmailer Plugin, I recommend using the __destruct magic method in your service/controller that utilizes the file(s). __destruct will be called when the object is released and will unlink any of the declared paths.
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class YourController extends Controller
{
private $paths = [];
public function someAction()
{
$this->paths[] = $path;
file_put_contents($path, implode(";\r\n", $result));
$message = (new \Swift_Message('VAT checking result !'))
->setFrom('vah#gmail.com')
->setTo($vat->getEmail())
->setBody('Hello, ...' ,'text/')
->attach(\Swift_Attachment::fromPath($path));
$mailer = $this->container->get('mailer');
$mailer->send( $message );
return $this->redirectToRoute('some_route');
}
public function __destruct()
{
if ($this->paths) {
array_map('unlink', $this->paths);
}
}
}
NOTE: This approach may not work if you use a spool to send
emails, as the
email will not be sent until the thresholds have been met for the
spool.
Symfony 2.3+ will automatically register the Swiftmailer plugin when you tag a service with swiftmailer.plugin. So there is no need to call $mailer->registerPlugin($listener);, where swiftmailer will simply ignore duplicate plugin registrations.
I've successfully used APNS-PHP to send PNs - it's great!
However, I'm a little stuck on how to add it to a Code Igniter project
(inside a controller that will be called from command-line).
Calling:
require_once APPPATH.'/third_party/ApnsPHP/Autoload.php';
Results in errors:
Fatal error: Uncaught exception 'Exception' with message 'Class file
'XXXXXXX/application/third_party/CI/DB/mysql/result.php' does not
exists' in XXXXXXX/application/third_party/ApnsPHP/Autoload.php:49
I'm assuming it's some kind of autoloading conflict? But I'm not
really sure!
Any help would be brilliant - I've trawled Google without much luck!
Here's the line I've being trying to use inside the function:
require_once APPPATH.'/third_party/ApnsPHP/Autoload.php';
Here's the __autoload function I've added for loading Ion Auth's libs:
function __autoload($class)
{
if (strpos($class, 'CI_') !== 0)
{
if (file_exists($file = APPPATH.'core/'.$class.EXT))
{
include $file;
}
else if (file_exists($file = APPPATH.'libraries/'.$class.EXT))
{
include $file;
}
else if (file_exists($file = APPPATH.'core/base_controllers/'.$class.EXT))
{
include $file;
}
}
}
With no other changes to the library, this works for me. It sidesteps CI a bit, but still allows you to load in APNS-PHP as a model:
<?php
if(!defined('BASEPATH')) exit('No direct script access allowed');
class Notification_model extends CI_Model {
protected $apnsDir = '';
// -----------------------------------------------
/**
* Setup some basic stuffs
* #param void
* #return void
* #access public
*/
public function __construct() {
parent::__construct();
/* get all the APNS files */
$this->apnsDir = $_SERVER['DOCUMENT_ROOT'].'/application/third_party/ApnsPHP/';
$this->_apns_req();
return;
} /* /__construct() */
// -----------------------------------------------
/**
* Will send the actual iOS notification to the user
* #param $token string iOS device token
* #param $msg string
* #param $attrs array Key/value pairs to be sent as meta with APN
* #return void
* #access public
*/
private function send_ios($token=null, $msg=null, $attrs=array()) {
if(!$token || !$msg) return;
// Instantiate a new ApnsPHP_Push object
$push = new ApnsPHP_Push(
ApnsPHP_Abstract::ENVIRONMENT_SANDBOX,
$this->apnsDir.'SSL/server_certificates_bundle_sandbox.pem'
);
// Set the Provider Certificate passphrase
// $push->setProviderCertificatePassphrase('tablecan29');
// Set the Root Certificate Autority to verify the Apple remote peer
$push->setRootCertificationAuthority($this->apnsDir.'SSL/entrust_root_certification_authority.pem');
// Connect to the Apple Push Notification Service
$push->connect();
// Instantiate a new Message with a single recipient
$message = new ApnsPHP_Message($token);
// Set a custom identifier. To get back this identifier use the getCustomIdentifier() method
// over a ApnsPHP_Message object retrieved with the getErrors() message.
$message->setCustomIdentifier("Message-Badge-3");
// Set badge icon to "3"
// $message->setBadge(0);
// Set a simple welcome text
$message->setText($msg);
// Play the default sound
$message->setSound();
// Set custom properties
if( is_array($attrs) && count($attrs) )
{
foreach( $attrs as $attr_key => $attr_val )
{
$message->setCustomProperty($attr_key, $attr_val);
}
}
// Set the expiry value - in seconds
$message->setExpiry(120);
// Add the message to the message queue
$push->add($message);
// Send all messages in the message queue
$push->send();
// Disconnect from the Apple Push Notification Service
$push->disconnect();
// Examine the error message container
// $aErrorQueue = $push->getErrors();
// if (!empty($aErrorQueue)) {
// var_dump($aErrorQueue);
// }
return TRUE;
} /* /send_ios() */
// -----------------------------------------------
private function _apns_req() {
require_once $this->apnsDir.'Abstract.php';
require_once $this->apnsDir.'Exception.php';
require_once $this->apnsDir.'Feedback.php';
require_once $this->apnsDir.'Message.php';
require_once $this->apnsDir.'Log/Interface.php';
require_once $this->apnsDir.'Log/Embedded.php';
require_once $this->apnsDir.'Message/Custom.php';
require_once $this->apnsDir.'Message/Exception.php';
require_once $this->apnsDir.'Push.php';
require_once $this->apnsDir.'Push/Exception.php';
require_once $this->apnsDir.'Push/Server.php';
require_once $this->apnsDir.'Push/Server/Exception.php';
return;
} /* /_apns_req() */
} /* /Notification_model{} */
/* End of file notification_model.php */
/* Location: ./application/models/notification_model.php */
Example usage:
$this->load->model('notification_model');
$this->notification_model->send_ios($token, 'Test Message', array('custom_var' => 'val'));