Completed
Push — development ( 4cbc56...9c238f )
by Thomas
27s
created

JournalLogs   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 179
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 0
loc 179
rs 10
c 0
b 0
f 0
wmc 16
lcom 1
cbo 2

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 2
B getLogs() 0 39 6
A eGrepStatus() 0 17 2
B processJournalLogs() 0 38 4
A updateEmailStatusToBounced() 0 15 1
A updateEmailStatusToSent() 0 12 1
1
<?php
2
/****************************************************************************
3
 * for license information see LICENSE.md
4
 * extract bounced and sent logs from postfix journal log and process
5
 * information for mail-log cronjob
6
 ****************************************************************************/
7
8
namespace Oc\Postfix;
9
10
use Doctrine\DBAL\Connection;
11
12
/**
13
 * Class JournalLogs
14
 */
15
class JournalLogs
16
{
17
    /**
18
     * @var array|bool
19
     */
20
    private $config;
21
22
    /**
23
     * @var Connection
24
     */
25
    private $connection;
26
27
    /**
28
     * constructor
29
     *
30
     * @param Connection $connection
31
     * @param array|bool $config
32
     * $config = [
33
     *     'hostname' => 'host.domain.tld',
34
     *     'status' => ['sent', 'bounced', ...],
35
     * ]
36
     */
37
    public function __construct(Connection $connection, $config = false)
38
    {
39
        // TODO move this config to a configuration file
40
        if (!$config) {
41
            $config = [
42
                'hostname' => 'oc0002.opencaching.de',
43
                'status' => ['sent', 'bounced'],
44
            ];
45
        }
46
        $this->config = $config;
47
        $this->connection = $connection;
48
    }
49
50
    /**
51
     * getLogs(\DateTimeInterface $start)
52
     *
53
     * @param \DateTimeInterface $start
54
     * @return LogEntity[]
55
     */
56
    public function getLogs(\DateTimeInterface $start)
57
    {
58
        $journals = $this->eGrepStatus($this->config['status'], $start);
59
60
        $logEntries = [];
61
        foreach ($journals as $journal) {
62
            $logEntry = new LogEntity();
63
64
            $entry = json_decode($journal, true);
65
66
            if ($entry['_HOSTNAME'] === $this->config['hostname']) {
67
                $cursor = [];
68
                $cursorArray = explode(';', $entry['__CURSOR']);
69
                foreach ($cursorArray as $field) {
70
                    list($key, $value) = explode('=', $field);
71
                    $cursor[$key] = $value;
72
                }
73
74
                $logEntry->id = (int) hexdec($cursor['i']);
75
76
                $timeStamp = $entry['__REALTIME_TIMESTAMP'];
77
                $logEntry->created = \DateTimeImmutable::createFromFormat(
78
                    'U.u',
79
                    substr($timeStamp, 0, -6) . '.' . substr($timeStamp, -6)
80
                );
81
82
                if (preg_match('/ status=(.*) /U', $entry['MESSAGE'], $matchStatus)) {
83
                    $logEntry->status = $matchStatus[1];
84
                }
85
86
                if (preg_match('/ to=<(.*)>,/U', $entry['MESSAGE'], $matchEmail)) {
87
                    $logEntry->email = $matchEmail[1];
88
                    $logEntries[] = $logEntry;
89
                }
90
            }
91
        }
92
93
        return $logEntries;
94
    }
95
96
    /**
97
     * @param array $status
98
     * @param \DateTimeInterface $start
99
     * @return array
100
     */
101
    private function eGrepStatus(array $status, \DateTimeInterface $start)
102
    {
103
        $grepStatus = '';
104
        foreach ($status as $entry) {
105
            $grepStatus .= 'status=' . $entry . '|';
106
        }
107
108
        $grepStatus = substr($grepStatus, 0, -1);
109
110
        $journalRaw = shell_exec(
111
            'journalctl --since="' . $start->format(
112
                'Y-m-d H:i:s'
113
            ) . '" -u postfix -o json | egrep "' . $grepStatus . '"'
114
        );
115
116
        return explode(PHP_EOL, trim($journalRaw));
117
    }
118
119
    public function processJournalLogs()
120
    {
121
        $start = $this->connection
122
            ->fetchColumn(
123
                'SELECT `value` FROM `sysconfig` WHERE `name` = :name',
124
                [':name' => 'syslog_maillog_lastdate']
125
            );
126
127
        $start = \DateTimeImmutable::createFromFormat(
128
            'Y-m-d H:i:s',
129
            $start
130
        );
131
132
        $logs = $this->getLogs($start);
133
134
        foreach ($logs as $log) {
135
            $dateTime = $log->created->format('Y-m-d H:i:s');
136
            switch ($log->status) {
137
                case 'bounced':
138
                    $this->updateEmailStatusToBounced($log->email, $dateTime);
139
                    break;
140
                case 'sent':
141
                    $this->updateEmailStatusToSent($log->email);
142
                    break;
143
            }
144
        }
145
146
        $end = new \DateTimeImmutable();
147
148
        $this->connection
149
            ->executeUpdate(
150
                'UPDATE `sysconfig` SET `value` = :value WHERE `name` = :name',
151
                [
152
                    ':name' => 'syslog_maillog_lastdate',
153
                    ':value' => $end->format('Y-m-d H:i:s'),
154
                ]
155
            );
156
    }
157
158
    /**
159
     * @param $email
160
     * @param $dateTime
161
     */
162
    private function updateEmailStatusToBounced($email, $dateTime)
163
    {
164
        $this->connection
165
            ->executeQuery(
166
                'UPDATE `user`
167
                 SET `email_problems`=`email_problems`+1,
168
                     `last_email_problem`= :dateTime
169
                 WHERE email = :email
170
                 AND DATE(IFNULL(`last_email_problem`, "")) < DATE(:dateTime)',
171
                [
172
                    ':email' => $email,
173
                    ':dateTime' => $dateTime,
174
                ]
175
            );
176
    }
177
178
    /**
179
     * @param $email
180
     */
181
    private function updateEmailStatusToSent($email)
182
    {
183
        $this->connection
184
            ->executeQuery(
185
                'UPDATE `user`
186
                 SET `email_problems`=0
187
                 WHERE email = :email',
188
                [
189
                    ':email' => $email,
190
                ]
191
            );
192
    }
193
}
194