Passed
Push — master ( 1b988a...8cef26 )
by Darko
11:12
created

BlacklistService::retrieveLists()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 5
c 1
b 0
f 0
dl 0
loc 9
rs 10
cc 4
nc 8
nop 1
1
<?php
2
3
namespace App\Services;
4
5
use App\Models\BinaryBlacklist;
6
use Illuminate\Support\Facades\DB;
7
8
class BlacklistService
9
{
10
    /**
11
     * Cache of lists per group name.
12
     */
13
    private array $blackList = [];
14
15
    private array $whiteList = [];
16
17
    private array $listsFound = [];
18
19
    /**
20
     * Track blacklist IDs to update last_activity for.
21
     */
22
    private array $idsToUpdate = [];
23
24
    /**
25
     * Check if an article (OVER header) is blacklisted for the given group.
26
     */
27
    public function isBlackListed(array $msg, string $groupName): bool
28
    {
29
        if (! isset($this->listsFound[$groupName])) {
30
            $this->retrieveLists($groupName);
31
        }
32
        if (! $this->listsFound[$groupName]) {
33
            return false;
34
        }
35
36
        $blackListed = false;
37
        $field = [
38
            \Blacklight\Binaries::BLACKLIST_FIELD_SUBJECT => $msg['Subject'] ?? '',
39
            \Blacklight\Binaries::BLACKLIST_FIELD_FROM => $msg['From'] ?? '',
40
            \Blacklight\Binaries::BLACKLIST_FIELD_MESSAGEID => $msg['Message-ID'] ?? '',
41
        ];
42
43
        // Whitelist first: if any whitelist matches, allow; otherwise treat as blacklisted.
44
        if ($this->whiteList[$groupName]) {
45
            $blackListed = true;
46
            foreach ($this->whiteList[$groupName] as $whiteList) {
47
                if (@preg_match('/'.$whiteList->regex.'/i', $field[$whiteList->msgcol])) {
48
                    $blackListed = false;
49
                    $this->idsToUpdate[$whiteList->id] = $whiteList->id;
50
                    break;
51
                }
52
            }
53
        }
54
55
        if (! $blackListed && $this->blackList[$groupName]) {
56
            foreach ($this->blackList[$groupName] as $blackList) {
57
                if (@preg_match('/'.$blackList->regex.'/i', $field[$blackList->msgcol])) {
58
                    $blackListed = true;
59
                    $this->idsToUpdate[$blackList->id] = $blackList->id;
60
                    break;
61
                }
62
            }
63
        }
64
65
        return $blackListed;
66
    }
67
68
    /**
69
     * Get and reset collected blacklist IDs that matched during checks.
70
     */
71
    public function getAndClearIdsToUpdate(): array
72
    {
73
        $ids = array_values($this->idsToUpdate);
74
        $this->idsToUpdate = [];
75
76
        return $ids;
77
    }
78
79
    /**
80
     * Update last_activity timestamp for given blacklist IDs.
81
     */
82
    public function updateBlacklistUsage(array $ids): void
83
    {
84
        if (empty($ids)) {
85
            return;
86
        }
87
        BinaryBlacklist::query()->whereIn('id', $ids)->update(['last_activity' => now()]);
88
    }
89
90
    /**
91
     * Query blacklists from DB.
92
     */
93
    public function getBlacklist(bool $activeOnly = true, int|string $opType = -1, string $groupName = '', bool $groupRegex = false): array
94
    {
95
        $opTypeSql = match ($opType) {
96
            \Blacklight\Binaries::OPTYPE_BLACKLIST => 'AND bb.optype = '.\Blacklight\Binaries::OPTYPE_BLACKLIST,
97
            \Blacklight\Binaries::OPTYPE_WHITELIST => 'AND bb.optype = '.\Blacklight\Binaries::OPTYPE_WHITELIST,
98
            default => '',
99
        };
100
101
        $joinOperator = $groupRegex ? 'REGEXP' : '=';
102
        $activeSql = $activeOnly ? 'AND bb.status = 1' : '';
103
        $groupSql = $groupName ? ('AND g.name REGEXP '.escapeString($groupName)) : '';
104
105
        $sql = "
106
                SELECT
107
                    bb.id, bb.optype, bb.status, bb.description,
108
                    bb.groupname AS groupname, bb.regex, g.id AS group_id, bb.msgcol,
109
                    bb.last_activity as last_activity
110
                FROM binaryblacklist bb
111
                LEFT OUTER JOIN usenet_groups g ON g.name $joinOperator bb.groupname
112
                WHERE 1=1 $activeSql $opTypeSql $groupSql
113
                ORDER BY coalesce(groupname,'zzz')";
114
115
        return DB::select($sql);
116
    }
117
118
    public function getBlacklistByID(int $id)
119
    {
120
        return BinaryBlacklist::query()->where('id', $id)->first();
121
    }
122
123
    public function deleteBlacklist(int $id): void
124
    {
125
        BinaryBlacklist::query()->where('id', $id)->delete();
126
    }
127
128
    public function updateBlacklist(array $blacklistArray): void
129
    {
130
        BinaryBlacklist::query()->where('id', $blacklistArray['id'])->update(
131
            [
132
                'groupname' => $blacklistArray['groupname'] === '' ? 'null' : preg_replace('/a\.b\./i', 'alt.binaries.', $blacklistArray['groupname']),
133
                'regex' => $blacklistArray['regex'],
134
                'status' => $blacklistArray['status'],
135
                'description' => $blacklistArray['description'],
136
                'optype' => $blacklistArray['optype'],
137
                'msgcol' => $blacklistArray['msgcol'],
138
            ]
139
        );
140
    }
141
142
    public function addBlacklist(array $blacklistArray): void
143
    {
144
        BinaryBlacklist::query()->insert(
145
            [
146
                'groupname' => $blacklistArray['groupname'] === '' ? 'null' : preg_replace('/a\.b\./i', 'alt.binaries.', $blacklistArray['groupname']),
147
                'regex' => $blacklistArray['regex'],
148
                'status' => $blacklistArray['status'],
149
                'description' => $blacklistArray['description'],
150
                'optype' => $blacklistArray['optype'],
151
                'msgcol' => $blacklistArray['msgcol'],
152
            ]
153
        );
154
    }
155
156
    private function retrieveLists(string $groupName): void
157
    {
158
        if (! isset($this->blackList[$groupName])) {
159
            $this->blackList[$groupName] = $this->getBlacklist(true, \Blacklight\Binaries::OPTYPE_BLACKLIST, $groupName, true);
160
        }
161
        if (! isset($this->whiteList[$groupName])) {
162
            $this->whiteList[$groupName] = $this->getBlacklist(true, \Blacklight\Binaries::OPTYPE_WHITELIST, $groupName, true);
163
        }
164
        $this->listsFound[$groupName] = ($this->blackList[$groupName] || $this->whiteList[$groupName]);
165
    }
166
}
167