Completed
Push — master ( 990803...ccde76 )
by Sebastian
03:48
created

Mail::shouldMailBeSend()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 2
cts 2
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 2
nc 5
nop 1
crap 4
1
<?php
2
namespace phpbu\App\Log;
3
4
use phpbu\App\Exception;
5
use phpbu\App\Event;
6
use phpbu\App\Listener;
7
use phpbu\App\Result;
8
use phpbu\App\Log\MailTemplate as TPL;
9
use phpbu\App\Util\Arr;
10
use phpbu\App\Util\Str;
11
use PHP_Timer;
12
use Swift_Mailer;
13
use Swift_Message;
14
15
/**
16
 * Mail Logger
17
 *
18
 * @package    phpbu
19
 * @subpackage Log
20
 * @author     Sebastian Feldmann <[email protected]>
21
 * @copyright  Sebastian Feldmann <[email protected]>
22
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
23
 * @link       http://phpbu.de/
24
 * @since      Class available since Release 1.0.0
25
 */
26
class Mail implements Listener, Logger
27
{
28
    /**
29
     * Mailer instance
30
     *
31
     * @var Swift_Mailer
32
     */
33
    protected $mailer;
34
35
    /**
36
     * Mail subject
37
     *
38
     * @var string
39
     */
40
    protected $subject;
41
42
    /**
43
     * From email address
44
     *
45
     * @var string
46
     */
47
    protected $senderMail;
48
49
    /**
50
     * From name
51
     *
52
     * @var string
53
     */
54
    protected $senderName;
55
56
    /**
57
     * Transport type [mail|smtp|null]
58
     *
59
     * @var string
60
     */
61
    protected $transportType;
62
63
    /**
64
     * List of mail recipients
65
     *
66
     * @var array<string>
67
     */
68
    protected $recipients = [];
69
70
    /**
71
     * Amount of executed backups
72
     *
73
     * @var integer
74
     */
75
    private $numBackups = 0;
76
77
    /**
78
     * Amount of executed checks
79
     *
80
     * @var integer
81
     */
82
    private $numChecks = 0;
83
84
    /**
85
     * Amount of executed Syncs
86
     *
87
     * @var integer
88
     */
89
    private $numSyncs = 0;
90
91
    /**
92
     * Amount of executed Crypts
93
     *
94
     * @var integer
95
     */
96
    private $numCrypts = 0;
97
98
    /**
99
     * Amount of executed Cleanups
100
     *
101
     * @var integer
102
     */
103
    private $numCleanups = 0;
104
105
    /**
106
     * Send mail only if there was an error
107
     *
108
     * @var bool
109
     */
110
    private $sendOnlyOnError = false;
111
112
    /**
113
     * Send mails on simulation runs.
114
     *
115
     * @var bool
116
     */
117
    private $sendSimulating = true;
118
119
    /**
120
     * Is current execution a simulation.
121
     *
122
     * @var bool
123
     */
124
    private $isSimulation = false;
125
126
    /**
127
     * Returns an array of event names this subscriber wants to listen to.
128
     *
129
     * The array keys are event names and the value can be:
130
     *
131
     *  * The method name to call (priority defaults to 0)
132
     *  * An array composed of the method name to call and the priority
133
     *  * An array of arrays composed of the method names to call and respective
134
     *    priorities, or 0 if unset
135
     *
136
     * @return array The event names to listen to
137
     */
138 1
    public static function getSubscribedEvents()
139
    {
140
        return [
141 1
            'phpbu.backup_start'  => 'onBackupStart',
142
            'phpbu.check_start'   => 'onCheckStart',
143
            'phpbu.crypt_start'   => 'onCryptStart',
144
            'phpbu.sync_start'    => 'onSyncStart',
145
            'phpbu.cleanup_start' => 'onCleanupStart',
146
            'phpbu.app_end'       => 'onPhpbuEnd',
147
        ];
148
    }
149
150
    /**
151
     * Setup the Logger.
152
     *
153
     * @see    \phpbu\App\Log\Logger::setup
154
     * @param  array $options
155
     * @throws \phpbu\App\Exception
156
     */
157 12
    public function setup(array $options)
158
    {
159 12
        if (empty($options['recipients'])) {
160 1
            throw new Exception('no recipients given');
161
        }
162 11
        $mails                 = $options['recipients'];
163 11
        $server                = gethostname();
164 11
        $this->sendOnlyOnError = Str::toBoolean(Arr::getValue($options, 'sendOnlyOnError'), false);
165 11
        $this->sendSimulating  = Str::toBoolean(Arr::getValue($options, 'sendOnSimulation'), true);
166 11
        $this->subject         = Arr::getValue($options, 'subject', 'PHPBU backup report from ' . $server);
167 11
        $this->senderMail      = Arr::getValue($options, 'sender.mail', 'phpbu@' . $server);
168 11
        $this->senderName      = Arr::getValue($options, 'sender.name');
169 11
        $this->transportType   = Arr::getValue($options, 'transport', 'mail');
170 11
        $this->recipients      = array_map('trim', explode(';', $mails));
171 11
        $this->isSimulation    = Arr::getValue($options, '__simulate__', false);
172
173
        // create transport an mailer
174 11
        $transport    = $this->createTransport($this->transportType, $options);
175 9
        $this->mailer = new Swift_Mailer($transport);
176 9
    }
177
178
    /**
179
     * Handle the phpbu end event.
180
     *
181
     * @param  \phpbu\App\Event\App\End $event
182
     * @throws \phpbu\App\Exception
183
     */
184 4
    public function onPhpbuEnd(Event\App\End $event)
185
    {
186 4
        $result  = $event->getResult();
187
188 4
        if ($this->shouldMailBeSend($result)) {
189 4
            $header  = $this->getHeaderHtml();
190 4
            $status  = $this->getStatusHtml($result);
191 4
            $errors  = $this->getErrorHtml($result);
192 4
            $info    = $this->getInfoHtml($result);
193 4
            $footer  = $this->getFooterHtml();
194 4
            $body    = '<html><body '. TPL::getSnippet('sBody') . '>'
195 4
                     . $header
196 4
                     . $status
197 4
                     . $errors
198 4
                     . $info
199 4
                     . $footer
200 4
                     . '</body></html>';
201 4
            $sent    = null;
202 4
            $state   = $result->allOk() ? 'OK' : ($result->backupOkButSkipsOrFails() ? 'WARNING' : 'ERROR');
203
204
            try {
205
                /** @var \Swift_Message $message */
206 4
                $message = new Swift_Message();
207 4
                $message->setSubject($this->subject . ' [' . $state . ']')
208 4
                        ->setFrom($this->senderMail, $this->senderName)
209 4
                        ->setTo($this->recipients)
210 4
                        ->setBody($body, 'text/html');
211
212 4
                $sent = $this->mailer->send($message);
213
            } catch (\Exception $e) {
214
                throw new Exception($e->getMessage());
215
            }
216 4
            if (!$sent) {
217
                throw new Exception('mail could not be sent');
218
            }
219
        }
220 4
    }
221
222
    /**
223
     * Backup start event.
224
     *
225
     * @param \phpbu\App\Event\Backup\Start $event
226
     */
227 4
    public function onBackupStart(Event\Backup\Start $event)
228
    {
229 4
        $this->numBackups++;
230 4
    }
231
232
    /**
233
     * Check start event.
234
     *
235
     * @param \phpbu\App\Event\Check\Start $event
236
     */
237 1
    public function onCheckStart(Event\Check\Start $event)
238
    {
239 1
        $this->numChecks++;
240 1
    }
241
242
    /**
243
     * Crypt start event.
244
     *
245
     * @param \phpbu\App\Event\Crypt\Start $event
246
     */
247 1
    public function onCryptStart(Event\Crypt\Start $event)
248
    {
249 1
        $this->numCrypts++;
250 1
    }
251
252
    /**
253
     * Sync start event.
254
     *
255
     * @param \phpbu\App\Event\Sync\Start $event
256
     */
257 1
    public function onSyncStart(Event\Sync\Start $event)
258
    {
259 1
        $this->numSyncs++;
260 1
    }
261
262
    /**
263
     * Cleanup start event.
264
     *
265
     * @param \phpbu\App\Event\Cleanup\Start $event
266
     */
267 1
    public function onCleanupStart(Event\Cleanup\Start $event)
268
    {
269 1
        $this->numCleanups++;
270 1
    }
271
272
    /**
273
     * Create a Swift_Mailer_Transport.
274
     *
275
     * @param  string $type
276
     * @param  array  $options
277
     * @throws \phpbu\App\Exception
278
     * @return \Swift_Transport
279
     */
280 11
    protected function createTransport($type, array $options)
281
    {
282
        switch ($type) {
283
            // null transport, don't send any mails
284 11
            case 'null':
285
                 /* @var $transport \Swift_NullTransport */
286 4
                $transport = new \Swift_NullTransport();
287 4
                break;
288
289 7
            case 'smtp':
290 2
                $transport = $this->getSmtpTransport($options);
291 1
                break;
292
293 5
            case 'mail':
294 3
            case 'sendmail':
295 4
                $transport = $this->getSendmailTransport($options);
296 4
                break;
297
298
            // UPS! no transport given
299
            default:
300 1
                throw new Exception(sprintf('mail transport not supported: \'%s\'', $type));
301
        }
302 9
        return $transport;
303
    }
304
305
    /**
306
     * Should a mail be send.
307
     *
308
     * @param  \phpbu\App\Result $result
309
     * @return bool
310
     */
311 4
    protected function shouldMailBeSend(Result $result) : bool
312
    {
313
        // send mails if
314
        // there is an error or send error only is inactive
315
        // and
316
        // simulation settings do not prevent sending
317 4
        return (!$this->sendOnlyOnError || !$result->allOk()) && ($this->sendSimulating || !$this->isSimulation);
318
    }
319
320
    /**
321
     * Create Swift Smtp Transport.
322
     *
323
     * @param  array $options
324
     * @return \Swift_SmtpTransport
325
     * @throws \phpbu\App\Exception
326
     */
327 2
    protected function getSmtpTransport(array $options)
328
    {
329 2
        if (!isset($options['smtp.host'])) {
330 1
            throw new Exception('option \'smtp.host\' ist missing');
331
        }
332 1
        $host       = $options['smtp.host'];
333 1
        $port       = Arr::getValue($options, 'smtp.port', 25);
334 1
        $username   = Arr::getValue($options, 'smtp.username');
335 1
        $password   = Arr::getValue($options, 'smtp.password');
336 1
        $encryption = Arr::getValue($options, 'smtp.encryption');
337
338
        /* @var $transport \Swift_SmtpTransport */
339 1
        $transport = new \Swift_SmtpTransport($host, $port);
340
341 1
        if ($username && $password) {
342 1
            $transport->setUsername($username)
343 1
                      ->setPassword($password);
344
        }
345 1
        if ($encryption) {
346 1
            $transport->setEncryption($encryption);
347
        }
348 1
        return $transport;
349
    }
350
351
    /**
352
     * Create a Swift Sendmail Transport.
353
     *
354
     * @param  array $options
355
     * @return \Swift_SendmailTransport
356
     */
357 4
    protected function getSendmailTransport(array $options)
358
    {
359 4
        if (isset($options['sendmail.path'])) {
360 1
            $path    = $options['sendmail.path'];
361 1
            $options = isset($options['sendmail.options']) ? ' ' . $options['sendmail.options'] : '';
362
            /* @var $transport \Swift_SendmailTransport */
363 1
            return new \Swift_SendmailTransport($path . $options);
364
        }
365 3
        return new \Swift_SendmailTransport();
366
    }
367
368
    /**
369
     * Return mail header html
370
     *
371
     * @return string
372
     */
373 4
    protected function getHeaderHtml()
374
    {
375 4
        return '<table ' . TPL::getSnippet('sTableContent') . '><tr><td ' . TPL::getSnippet('sTableContentCol') . '>' .
376 4
               '<table ' . TPL::getSnippet('sTableHeader') . '><tr><td>PHPBU - backup report</td></tr></table>';
377
    }
378
379
    /**
380
     * Return mail status html
381
     *
382
     * @param  \phpbu\App\Result $result
383
     * @return string
384
     */
385 4
    protected function getStatusHtml(Result $result)
386
    {
387 4
        if (count($result->getBackups()) === 0) {
388 1
            $color  = TPL::getSnippet('cStatusWARN');
389 1
            $status = 'WARNING';
390 3
        } elseif ($result->allOk()) {
391 1
            $color  = TPL::getSnippet('cStatusOK');
392 1
            $status = 'OK';
393 2
        } elseif ($result->backupOkButSkipsOrFails()) {
394 1
            $color  = TPL::getSnippet('cStatusWARN');
395 1
            $status = 'WARNING';
396
        } else {
397 1
            $color  = TPL::getSnippet('cStatusFAIL');
398 1
            $status = 'FAILURE';
399
        }
400 4
        $info = sprintf(
401 4
            '(%d %s, %d %s, %d %s, %d %s, %d %s)',
402 4
            count($result->getBackups()),
403 4
            Str::appendPluralS('backup', count($result->getBackups())),
404 4
            $this->numChecks,
405 4
            Str::appendPluralS('check', $this->numChecks),
406 4
            $this->numCrypts,
407 4
            Str::appendPluralS('crypt', $this->numCrypts),
408 4
            $this->numSyncs,
409 4
            Str::appendPluralS('sync', $this->numSyncs),
410 4
            $this->numCleanups,
411 4
            Str::appendPluralS('cleanup', $this->numCleanups)
412
        );
413 4
        $html = '<table ' . sprintf(TPL::getSnippet('sTableStatus'), $color) .'>' .
414 4
                 '<tr><td>' .
415 4
                  '<span ' . TPL::getSnippet('sTableStatusText') . '>' . date('Y-m-d H:i') . '</span>' .
416 4
                  '<h1 ' . TPL::getSnippet('sTableStatusHead') . '>' . $status . '</h1>' .
417 4
                  '<span ' . TPL::getSnippet('sTableStatusText') . '>' . $info . '</span>' .
418 4
                 '</td></tr>' .
419 4
                '</table>';
420
421 4
        return $html;
422
    }
423
424
    /**
425
     * Get error information.
426
     *
427
     * @param  \phpbu\App\Result $result
428
     * @return string
429
     */
430 4
    protected function getErrorHtml(Result $result)
431
    {
432 4
        $html   = '';
433 4
        $errors = $result->getErrors();
434 4
        if (count($errors)) {
435 1
            $html .= '<table ' . TPL::getSnippet('sTableError') . '>';
436
            /* @var $e Exception */
437 1
            foreach ($errors as $e) {
438 1
                $html .= '<tr><td ' . TPL::getSnippet('sTableErrorCol') . '>' .
439 1
                    sprintf(
440 1
                        "Exception '%s' with message '%s' in %s:%d",
441 1
                        get_class($e),
442 1
                        $e->getMessage(),
443 1
                        $e->getFile(),
444 1
                        $e->getLine()
445
                    ) .
446 1
                    '</td></tr>';
447
448
            }
449 1
            $html .= '</table>';
450
        }
451 4
        return $html;
452
    }
453
454
    /**
455
     * Return backup html information.
456
     *
457
     * @param  \phpbu\App\Result $result
458
     * @return string
459
     */
460 4
    protected function getInfoHtml(Result $result)
461
    {
462 4
        $html    = '';
463 4
        $backups = $result->getBackups();
464 4
        if (count($backups)) {
465 3
            $html .= '<table ' . TPL::getSnippet('sTableBackup') . '>';
466
            /** @var \phpbu\App\Result\Backup $backup */
467 3
            foreach ($backups as $backup) {
468 3
                if ($backup->allOk()) {
469 1
                    $color  = TPL::getSnippet('cStatusOK');
470 1
                    $status = 'OK';
471 2
                } elseif($backup->okButSkipsOrFails()) {
472 1
                    $color  = TPL::getSnippet('cStatusWARN');
473 1
                    $status = 'WARNING';
474
                } else {
475 1
                    $color  = TPL::getSnippet('cStatusFAIL');
476 1
                    $status = 'FAILURE';
477
                }
478
                $html .= '<tr>' .
479 3
                          '<td ' . sprintf(TPL::getSnippet('sTableBackupStatusColumn'), $color) . ' colspan="4">' .
480 3
                          sprintf('backup <em>%s</em>', $backup->getName()) .
481 3
                          ' <span ' . TPL::getSnippet('sTableBackupStatusText') . '>' . $status .'</span>'.
482 3
                          '</td>' .
483 3
                         '</tr>' .
484 3
                         '<tr>' .
485 3
                          '<td ' . TPL::getSnippet('sRowHead') . '>&nbsp;</td>' .
486 3
                          '<td ' . TPL::getSnippet('sRowHead') . ' align="right">executed</td>' .
487 3
                          '<td ' . TPL::getSnippet('sRowHead') . ' align="right">skipped</td>' .
488 3
                          '<td ' . TPL::getSnippet('sRowHead') . ' align="right">failed</td>' .
489 3
                         '</tr>';
490
491
                $html .= '<tr>' .
492 3
                          '<td ' . TPL::getSnippet('sRowCheck') . '>checks</td>' .
493 3
                          '<td ' . TPL::getSnippet('sRowCheck') . ' align="right">' .
494 3
                            $backup->checkCount() . '
495
                           </td>' .
496 3
                          '<td ' . TPL::getSnippet('sRowCheck') . ' align="right">
497
                            &nbsp;
498
                           </td>' .
499 3
                          '<td ' . TPL::getSnippet('sRowCheck') . ' align="right">' .
500 3
                            $backup->checkCountFailed() .
501 3
                          '</td>' .
502 3
                         '</tr>' .
503 3
                         '<tr>' .
504 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . '>crypts</td>' .
505 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . ' align="right">' .
506 3
                            $backup->cryptCount() .
507 3
                          '</td>' .
508 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . ' align="right">' .
509 3
                            $backup->cryptCountSkipped() .
510 3
                          '</td>' .
511 3
                          '<td ' . TPL::getSnippet('sRowCrypt') . ' align="right">' .
512 3
                            $backup->cryptCountFailed() .
513 3
                          '</td>' .
514 3
                         '</tr>' .
515 3
                         '<tr>' .
516 3
                          '<td ' . TPL::getSnippet('sRowSync') . '>syncs</td>' .
517 3
                          '<td ' . TPL::getSnippet('sRowSync') . ' align="right">' .
518 3
                            $backup->syncCount() . '</td>' .
519 3
                          '<td ' . TPL::getSnippet('sRowSync') . ' align="right">' .
520 3
                            $backup->syncCountSkipped() .
521 3
                          '</td>' .
522 3
                          '<td ' . TPL::getSnippet('sRowSync') . ' align="right">' .
523 3
                            $backup->syncCountFailed() .
524 3
                          '</td>' .
525 3
                         '</tr>' .
526 3
                         '<tr>' .
527 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . '>cleanups</td>' .
528 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . ' align="right">' .
529 3
                            $backup->cleanupCount() .
530 3
                          '</td>' .
531 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . ' align="right">' .
532 3
                            $backup->cleanupCountSkipped() .
533 3
                          '</td>' .
534 3
                          '<td ' . TPL::getSnippet('sRowCleanup') . ' align="right">' .
535 3
                            $backup->cleanupCountFailed() .
536 3
                          '</td>' .
537 3
                         '</tr>';
538
539
            }
540 3
            $html .= '</table>';
541
        }
542 4
        return $html;
543
    }
544
545
    /**
546
     * Return mail body footer.
547
     *
548
     * @return string
549
     */
550 4
    protected function getFooterHtml()
551
    {
552 4
        return '<p ' . TPL::getSnippet('sStats') . '>' . PHP_Timer::resourceUsage() . '</p>' .
553 4
               '</td></tr></table>';
554
    }
555
}
556