2016-02-24 20:13:55 +01:00
< ? php
2022-12-29 19:42:26 +01:00
2016-05-20 12:41:23 +02:00
/**
* MailError . php
2020-02-07 11:16:38 +01:00
* Copyright ( c ) 2019 james @ firefly - iii . org
2016-05-20 12:41:23 +02:00
*
2019-10-02 06:37:26 +02:00
* This file is part of Firefly III ( https :// github . com / firefly - iii ) .
2016-10-05 06:52:15 +02:00
*
2019-10-02 06:37:26 +02:00
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
2017-10-21 08:40:00 +02:00
*
2019-10-02 06:37:26 +02:00
* This program is distributed in the hope that it will be useful ,
2017-10-21 08:40:00 +02:00
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2019-10-02 06:37:26 +02:00
* GNU Affero General Public License for more details .
2017-10-21 08:40:00 +02:00
*
2019-10-02 06:37:26 +02:00
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
2016-05-20 12:41:23 +02:00
*/
2017-04-09 07:44:22 +02:00
declare ( strict_types = 1 );
2016-02-24 20:13:55 +01:00
namespace FireflyIII\Jobs ;
2025-05-27 17:06:15 +02:00
use Carbon\Carbon ;
2016-02-24 20:13:55 +01:00
use Illuminate\Contracts\Queue\ShouldQueue ;
use Illuminate\Mail\Message ;
use Illuminate\Queue\InteractsWithQueue ;
use Illuminate\Queue\SerializesModels ;
2024-04-07 16:12:41 +02:00
use Illuminate\Support\Facades\Log ;
2025-05-24 17:15:46 +02:00
use Illuminate\Support\Facades\Mail ;
2023-06-11 16:12:13 +02:00
use Symfony\Component\Mailer\Exception\TransportException ;
2025-05-27 16:57:36 +02:00
use Exception ;
2016-02-24 20:13:55 +01:00
2025-05-27 17:06:15 +02:00
use function Safe\json_encode ;
use function Safe\file_put_contents ;
use function Safe\json_decode ;
use function Safe\file_get_contents ;
2016-02-24 20:13:55 +01:00
/**
2017-11-15 12:25:49 +01:00
* Class MailError .
2016-02-24 20:13:55 +01:00
*/
class MailError extends Job implements ShouldQueue
{
2022-10-30 14:24:28 +01:00
use InteractsWithQueue ;
use SerializesModels ;
2016-02-24 20:13:55 +01:00
/**
* MailError constructor .
*/
2025-05-04 13:47:00 +02:00
public function __construct ( protected array $userData , protected string $destination , protected string $ipAddress , protected array $exception )
2016-02-24 20:13:55 +01:00
{
2025-05-04 13:55:42 +02:00
$debug = $this -> exception ;
2023-12-20 19:35:52 +01:00
unset ( $debug [ 'stackTrace' ], $debug [ 'headers' ]);
2025-05-27 17:06:15 +02:00
app ( 'log' ) -> error ( sprintf ( 'Exception is: %s' , json_encode ( $debug )));
2016-02-24 20:13:55 +01:00
}
/**
* Execute the job .
*/
2023-11-04 11:31:14 +01:00
public function handle () : void
2016-02-24 20:13:55 +01:00
{
2024-12-22 08:43:12 +01:00
$email = ( string ) config ( 'firefly.site_owner' );
2021-04-03 18:48:21 +02:00
$args = $this -> exception ;
$args [ 'loggedIn' ] = $this -> userData [ 'id' ] > 0 ;
$args [ 'user' ] = $this -> userData ;
$args [ 'ip' ] = $this -> ipAddress ;
$args [ 'token' ] = config ( 'firefly.ipinfo_token' );
2024-04-07 16:12:41 +02:00
// limit number of error mails that can be sent.
if ( $this -> reachedLimit ()) {
Log :: info ( 'MailError: reached limit, not sending email.' );
2024-04-07 16:22:39 +02:00
2024-04-07 16:12:41 +02:00
return ;
}
2023-12-20 19:35:52 +01:00
if ( $this -> attempts () < 3 && '' !== $email ) {
2016-02-24 20:13:55 +01:00
try {
2025-05-24 17:15:46 +02:00
Mail :: send (
2017-11-15 10:52:29 +01:00
[ 'emails.error-html' , 'emails.error-text' ],
$args ,
2023-12-21 05:07:26 +01:00
static function ( Message $message ) use ( $email ) : void {
2017-11-15 12:25:49 +01:00
if ( 'mail@example.com' !== $email ) {
2024-12-22 08:43:12 +01:00
$message -> to ( $email , $email ) -> subject (( string ) trans ( 'email.error_subject' ));
2016-02-24 20:13:55 +01:00
}
}
);
2025-05-27 16:57:36 +02:00
} catch ( Exception | TransportException $e ) {
2023-03-04 22:26:09 +01:00
$message = $e -> getMessage ();
if ( str_contains ( $message , 'Bcc' )) {
2023-10-29 06:31:13 +01:00
app ( 'log' ) -> warning ( '[Bcc] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.' );
2023-12-20 19:35:52 +01:00
2023-03-04 22:26:09 +01:00
return ;
}
if ( str_contains ( $message , 'RFC 2822' )) {
2023-10-29 06:31:13 +01:00
app ( 'log' ) -> warning ( '[RFC] Could not email or log the error. Please validate your email settings, use the .env.example file as a guide.' );
2023-12-20 19:35:52 +01:00
2023-03-04 22:26:09 +01:00
return ;
}
2023-10-29 06:32:00 +01:00
app ( 'log' ) -> error ( $e -> getMessage ());
app ( 'log' ) -> error ( $e -> getTraceAsString ());
2016-02-24 20:13:55 +01:00
}
}
}
2024-04-07 16:12:41 +02:00
private function reachedLimit () : bool
{
Log :: debug ( 'reachedLimit()' );
2024-04-07 16:27:15 +02:00
$types = [
2024-04-07 16:12:41 +02:00
'5m' => [ 'limit' => 5 , 'reset' => 5 * 60 ],
'1h' => [ 'limit' => 15 , 'reset' => 60 * 60 ],
'24h' => [ 'limit' => 15 , 'reset' => 24 * 60 * 60 ],
];
2024-04-07 16:27:15 +02:00
$file = storage_path ( 'framework/cache/error-count.json' );
$directory = storage_path ( 'framework/cache' );
$limits = [];
if ( ! is_writable ( $directory )) {
Log :: error ( sprintf ( 'MailError: cannot write to "%s", cannot rate limit errors!' , $directory ));
2024-04-07 16:22:39 +02:00
2024-04-07 16:12:41 +02:00
return false ;
}
2024-04-07 16:27:15 +02:00
2024-04-07 16:12:41 +02:00
if ( ! file_exists ( $file )) {
Log :: debug ( sprintf ( 'Wrote new file in "%s"' , $file ));
2025-05-27 17:06:15 +02:00
file_put_contents ( $file , json_encode ( $limits , JSON_PRETTY_PRINT ));
2024-04-07 16:12:41 +02:00
}
if ( file_exists ( $file )) {
Log :: debug ( sprintf ( 'Read file in "%s"' , $file ));
2025-09-07 07:31:00 +02:00
$limits = json_decode ( file_get_contents ( $file ), true );
2024-04-07 16:12:41 +02:00
}
// limit reached?
foreach ( $types as $type => $info ) {
Log :: debug ( sprintf ( 'Now checking limit "%s"' , $type ), $info );
2024-04-23 19:40:48 +02:00
if ( ! array_key_exists ( $type , $limits )) {
2024-04-07 16:12:41 +02:00
Log :: debug ( sprintf ( 'Limit "%s" reset to zero, did not exist yet.' , $type ));
$limits [ $type ] = [
2025-05-27 17:06:15 +02:00
'time' => Carbon :: now () -> getTimestamp (),
2024-04-07 16:12:41 +02:00
'sent' => 0 ,
];
}
2025-05-27 17:06:15 +02:00
if ( Carbon :: now () -> getTimestamp () - $limits [ $type ][ 'time' ] > $info [ 'reset' ]) {
Log :: debug ( sprintf ( 'Time past for this limit is %d seconds, exceeding %d seconds. Reset to zero.' , Carbon :: now () -> getTimestamp () - $limits [ $type ][ 'time' ], $info [ 'reset' ]));
2024-04-07 16:12:41 +02:00
$limits [ $type ] = [
2025-05-27 17:06:15 +02:00
'time' => Carbon :: now () -> getTimestamp (),
2024-04-07 16:12:41 +02:00
'sent' => 0 ,
];
}
if ( $limits [ $type ][ 'sent' ] > $info [ 'limit' ]) {
Log :: warning ( sprintf ( 'Sent %d emails in %s, return true.' , $limits [ $type ][ 'sent' ], $type ));
2024-04-07 16:22:39 +02:00
2024-04-07 16:12:41 +02:00
return true ;
}
2024-04-07 16:22:39 +02:00
++ $limits [ $type ][ 'sent' ];
2024-04-07 16:12:41 +02:00
}
2025-05-27 17:06:15 +02:00
file_put_contents ( $file , json_encode ( $limits , JSON_PRETTY_PRINT ));
2024-04-07 16:12:41 +02:00
Log :: debug ( 'No limits reached, return FALSE.' );
2024-04-07 16:22:39 +02:00
return false ;
2024-04-07 16:12:41 +02:00
}
2016-02-24 20:13:55 +01:00
}