Issues (96)

src/Trait/ClamAV.php (2 issues)

1
<?php
2
3
namespace Ikechukwukalu\Clamavfileupload\Trait;
4
5
use Ikechukwukalu\Clamavfileupload\Events\ClamavIsNotRunning;
6
7
/*
8
 * ClamAV.php
9
 *
10
 * A simple PHP class for scanning files using ClamAV.
11
 *
12
 * Copyright (C) 2017 KISS IT Consulting <http://www.kissitconsulting.com/>
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions
15
 * are met:
16
 *
17
 * 1. Redistributions of source code must retain the above copyright
18
 *    notice, this list of conditions and the following disclaimer.
19
 * 2. Redistributions in binary form must reproduce the above
20
 *    copyright notice, this list of conditions and the following
21
 *    disclaimer in the documentation and/or other materials
22
 *    provided with the distribution.
23
 *
24
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
28
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
32
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
*/
36
37
/**
38
 * 1. Go to <https://github.com/kissit/php-clamav-scan> to view the original
39
 *    PHP file.
40
 * 2. Go to <https://github.com/ikechukwukalu> to view my personal
41
 *    GitHub account.
42
*/
43
44
trait ClamAV {
45
46
    private $message;
47
48
    /**
49
     * Private function to open a socket
50
     * to clamd based on the current options.
51
     */
52
    private function socket()
53
    {
54
        if(empty(config('clamavfileupload.clamd_ip')) && empty(config('clamavfileupload.clamd_ip'))) {
55
            // By default we just use the local socket
56
            $socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
57
58
            if(socket_connect($socket, config('clamavfileupload.clamd_sock'))) {
59
                $this->message = trans('clamavfileupload::clamav.socket_connected');
60
                return $socket;
61
            }
62
        }
63
64
        // Attempt to use a network based socket
65
        $socket = socket_create(AF_INET, SOCK_STREAM, 0);
66
67
        if(socket_connect($socket, config('clamavfileupload.clamd_ip'), config('clamavfileupload.clamd_ip'))) {
68
            $this->message = trans('clamavfileupload::clamav.socket_connected');
69
            return $socket;
70
        }
71
72
        $this->message = trans('clamavfileupload::clamav.unable_to_open_socket');
73
        return false;
74
    }
75
76
    /**
77
     * Get the last scan message.
78
     *
79
     * @return string
80
     */
81
    public function getMessage(): string
82
    {
83
        return $this->message;
84
    }
85
86
    /**
87
     * Function to ping Clamd to make sure its functioning.
88
     *
89
     * @return bool
90
     */
91
    public function ping(): bool
92
    {
93
        $ping = $this->send("PING");
94
95
        if($ping == "PONG") {
96
            return true;
97
        }
98
99
        $this->message = trans('clamavfileupload::clamav.not_running');
100
        ClamavIsNotRunning::dispatch();
101
102
        return false;
103
    }
104
105
    /**
106
     * Function to scan the passed in file.
107
     * Returns true if safe, false otherwise.
108
     *
109
     * @return bool
110
     */
111
    public function scan($file): bool
112
    {
113
        if(!file_exists($file)) {
114
            $this->message = trans('clamavfileupload::clamav.file_not_found',
115
                ['name' => $file]);
116
            return false;
117
        }
118
119
        $scan = $this->send("SCAN $file");
120
        if($scan === false) {
121
            $this->message = trans('clamavfileupload::clamav.not_running');
122
            ClamavIsNotRunning::dispatch();
123
124
            return false;
125
        }
126
127
        $scanMessage = trim(substr(strrchr($scan, ":"), 1));
0 ignored issues
show
$scan of type true is incompatible with the type string expected by parameter $haystack of strrchr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

127
        $scanMessage = trim(substr(strrchr(/** @scrutinizer ignore-type */ $scan, ":"), 1));
Loading history...
128
        if($scanMessage == 'OK') {
129
            $this->message = $scanMessage;
130
            return true;
131
        }
132
133
        $this->message = trans('clamavfileupload::clamav.file_not_safe',
134
            ['name' => $file]);
135
        return false;
136
    }
137
138
    /**
139
     * Function to scan the passed in stream.
140
     * Returns true if safe, false otherwise.
141
     *
142
     * @param $file
143
     * @return bool
144
     */
145
    public function scanStream($file): bool
146
    {
147
        $socket = $this->socket();
148
        if(!$socket) {
149
            $this->message = trans('clamavfileupload::clamav.not_running');
150
            ClamavIsNotRunning::dispatch();
151
152
            return false;
153
        }
154
155
        if(!file_exists($file)) {
156
            $this->message = trans('clamavfileupload::clamav.file_not_found');
157
            return false;
158
        }
159
160
        if ($scan_fh = fopen($file, 'rb')) {
161
            return $this->scanStreamSend($scan_fh, $socket, $file);
162
        }
163
164
        $this->message = trans('clamavfileupload::clamav.file_not_safe',
165
            ['name' => $file]);
166
        return false;
167
    }
168
169
170
    /**
171
     * Function to scan the passed in stream.
172
     * Returns true if safe, false otherwise.
173
     *
174
     * @param $scan_fh
175
     * @param $socket
176
     * @param $file
177
     * @return bool
178
     */
179
    private function scanStreamSend($scan_fh, $socket, $file): bool
180
    {
181
        $chunksize = filesize($file) < 8192 ? filesize($file) : 8192;
182
        $command = "zINSTREAM\0";
183
        socket_send($socket, $command, strlen($command), 0);
184
185
        while (!feof($scan_fh)) {
186
            $data = fread($scan_fh, $chunksize);
187
            $packet = pack(sprintf("Na%d", strlen($data)), strlen($data), $data);
188
            socket_send($socket, $packet, strlen($packet), 0);
189
        }
190
191
        $packet = pack("Nx",0);
192
        socket_send($socket, $packet, strlen($packet), 0);
193
        socket_recv($socket, $scan, config('clamavfileupload.clamd_sock_len'), 0);
194
        socket_close($socket);
195
196
        if($scan === false) {
197
            $this->message = trans('clamavfileupload::clamav.not_running');
198
            ClamavIsNotRunning::dispatch();
199
200
            return false;
201
        }
202
203
        $scanMessage = trim(substr(strrchr($scan, ":"), 1));
204
        if($scanMessage == 'OK') {
205
            $this->message = $scanMessage;
206
            return true;
207
        }
208
209
        $this->message = trans('clamavfileupload::clamav.file_not_safe',
210
            ['name' => $file]);
211
        return false;
212
    }
213
214
    /**
215
     * Function to send a command to the Clamd socket.
216
     * In case you need to send any other commands directly.
217
     *
218
     * @return bool
219
     */
220
    public function send($command)
221
    {
222
        if(empty($command)) {
223
            return false;
224
        }
225
226
        try {
227
            $socket = $this->socket();
228
229
            if($socket) {
230
                socket_send($socket, $command, strlen($command), 0);
231
                socket_recv($socket, $return, config('clamavfileupload.clamd_sock_len'), 0);
232
                socket_close($socket);
233
234
                return trim($return);
0 ignored issues
show
Bug Best Practice introduced by
The expression return trim($return) returns the type string which is incompatible with the documented return type boolean.
Loading history...
235
            }
236
        } catch (\ErrorException $e) {
237
            $this->message = $e->getMessage();
238
        }
239
240
        return false;
241
    }
242
}
243