Test Setup Failed
Push — main ( efd097...c81411 )
by ikechukwu
03:54
created

ClamAV::scanStream()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

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

121
        $scanMessage = trim(substr(strrchr(/** @scrutinizer ignore-type */ $scan, ":"), 1));
Loading history...
122
        if($scanMessage == 'OK') {
123
            self::$message = $scanMessage;
124
            return true;
125
        }
126
127
        self::$message = trans('clamavfileupload::clamav.file_not_safe',
128
            ['name' => $file]);
129
        return false;
130
    }
131
132
    /**
133
     * Function to scan the passed in stream.
134
     * Returns true if safe, false otherwise.
135
     *
136
     * @param any $file
0 ignored issues
show
Bug introduced by
The type Ikechukwukalu\Clamavfileupload\Trait\any was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
137
     * @return bool
138
     */
139
    public static function scanStream($file): bool
140
    {
141
        $socket = self::socket();
142
        if(!$socket) {
143
            self::$message = trans('clamavfileupload::clamav.not_running');
144
            return false;
145
        }
146
147
        if(!file_exists($file)) {
148
            self::$message = trans('clamavfileupload::clamav.file_not_found');
149
            return false;
150
        }
151
152
        if ($scan_fh = fopen($file, 'rb')) {
153
            return self::scanStreamSend($scan_fh, $socket, $file);
0 ignored issues
show
Bug introduced by
It seems like $socket can also be of type resource; however, parameter $socket of Ikechukwukalu\Clamavfile...lamAV::scanStreamSend() does only seem to accept Ikechukwukalu\Clamavfileupload\Trait\any, maybe add an additional type check? ( Ignorable by Annotation )

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

153
            return self::scanStreamSend($scan_fh, /** @scrutinizer ignore-type */ $socket, $file);
Loading history...
Bug introduced by
$scan_fh of type resource is incompatible with the type Ikechukwukalu\Clamavfileupload\Trait\any expected by parameter $scan_fh of Ikechukwukalu\Clamavfile...lamAV::scanStreamSend(). ( Ignorable by Annotation )

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

153
            return self::scanStreamSend(/** @scrutinizer ignore-type */ $scan_fh, $socket, $file);
Loading history...
154
        }
155
156
        self::$message = trans('clamavfileupload::clamav.file_not_safe',
157
            ['name' => $file]);
158
        return false;
159
    }
160
161
162
    /**
163
     * Function to scan the passed in stream.
164
     * Returns true if safe, false otherwise.
165
     *
166
     * @param any $scan_fh
167
     * @param any $socket
168
     * @param any $file
169
     * @return bool
170
     */
171
    private static function scanStreamSend($scan_fh, $socket, $file): bool
172
    {
173
        $chunksize = filesize($file) < 8192 ? filesize($file) : 8192;
174
        $command = "zINSTREAM\0";
175
        socket_send($socket, $command, strlen($command), 0);
176
177
        while (!feof($scan_fh)) {
0 ignored issues
show
Bug introduced by
$scan_fh of type Ikechukwukalu\Clamavfileupload\Trait\any is incompatible with the type resource expected by parameter $stream of feof(). ( Ignorable by Annotation )

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

177
        while (!feof(/** @scrutinizer ignore-type */ $scan_fh)) {
Loading history...
178
            $data = fread($scan_fh, $chunksize);
0 ignored issues
show
Bug introduced by
$scan_fh of type Ikechukwukalu\Clamavfileupload\Trait\any is incompatible with the type resource expected by parameter $stream of fread(). ( Ignorable by Annotation )

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

178
            $data = fread(/** @scrutinizer ignore-type */ $scan_fh, $chunksize);
Loading history...
179
            $packet = pack(sprintf("Na%d", strlen($data)), strlen($data), $data);
180
            socket_send($socket, $packet, strlen($packet), 0);
181
        }
182
183
        $packet = pack("Nx",0);
184
        socket_send($socket, $packet, strlen($packet), 0);
185
        socket_recv($socket, $scan, config('clamavfileupload.clamd_sock_len'), 0);
186
        socket_close($socket);
187
188
        if($scan === false) {
189
            self::$message = trans('clamavfileupload::clamav.not_running');
190
            return false;
191
        }
192
193
        $scanMessage = trim(substr(strrchr($scan, ":"), 1));
194
        if($scanMessage == 'OK') {
195
            self::$message = $scanMessage;
196
            return true;
197
        }
198
199
        self::$message = trans('clamavfileupload::clamav.file_not_safe',
200
            ['name' => $file]);
201
        return false;
202
    }
203
204
    /**
205
     * Function to send a command to the Clamd socket.
206
     * In case you need to send any other commands directly.
207
     *
208
     * @return bool
209
     */
210
    public static function send($command)
211
    {
212
        if(empty($command)) {
213
            return false;
214
        }
215
216
        try {
217
            $socket = self::socket();
218
219
            if($socket) {
220
                socket_send($socket, $command, strlen($command), 0);
221
                socket_recv($socket, $return, config('clamavfileupload.clamd_sock_len'), 0);
222
                socket_close($socket);
223
224
                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...
225
            }
226
        } catch (\ErrorException $e) {
227
            self::$message = $e->getMessage();
228
        }
229
230
        return false;
231
    }
232
}
233