Issues (1844)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

class/pear/Net/POP3.php (73 issues)

1
<?php
2
// +-----------------------------------------------------------------------+
3
// | Copyright (c) 2002, Richard Heyes                                     |
4
// | All rights reserved.                                                  |
5
// |                                                                       |
6
// | Redistribution and use in source and binary forms, with or without    |
7
// | modification, are permitted provided that the following conditions    |
8
// | are met:                                                              |
9
// |                                                                       |
10
// | o Redistributions of source code must retain the above copyright      |
11
// |   notice, this list of conditions and the following disclaimer.       |
12
// | o Redistributions in binary form must reproduce the above copyright   |
13
// |   notice, this list of conditions and the following disclaimer in the |
14
// |   documentation and/or other materials provided with the distribution.|
15
// | o The names of the authors may not be used to endorse or promote      |
16
// |   products derived from this software without specific prior written  |
17
// |   permission.                                                         |
18
// |                                                                       |
19
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
20
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
21
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
22
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
23
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
24
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
25
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
26
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
27
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
28
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
30
// |                                                                       |
31
// +-----------------------------------------------------------------------+
32
// | Author: Richard Heyes <[email protected]>                           |
33
// | Co-Author: Damian Fernandez Sosa <[email protected]>               |
34
// +-----------------------------------------------------------------------+
35
//
36
// $Id: POP3.php,v 1.2 2005/02/18 15:29:06 eric_juden Exp $
37
38
require_once XHELP_PEAR_PATH . '/Net/Socket.php';
39
40
/**
41
 *  +----------------------------- IMPORTANT ------------------------------+
42
 *  | Usage of this class compared to native php extensions such as IMAP   |
43
 *  | is slow and may be feature deficient. If available you are STRONGLY  |
44
 *  | recommended to use the php extensions.                               |
45
 *  +----------------------------------------------------------------------+
46
 *
47
 * POP3 Access Class
48
 *
49
 * For usage see the example script
50
 */
51
define('NET_POP3_STATE_DISCONNECTED', 1, true);
52
define('NET_POP3_STATE_AUTHORISATION', 2, true);
53
define('NET_POP3_STATE_TRANSACTION', 4, true);
54
55
/**
56
 * Class Net_POP3
57
 */
58
class Net_POP3
59
{
60
    /**
61
     * Some basic information about the mail drop
62
     * garnered from the STAT command
63
     *
64
     * @var array
65
     */
66
    public $_maildrop;
67
    /**
68
     * Used for APOP to store the timestamp
69
     *
70
     * @var string
71
     */
72
    public $_timestamp;
73
    /**
74
     * Timeout that is passed to the socket object
75
     *
76
     * @var int
77
     */
78
    public $_timeout;
79
    /**
80
     * Socket object
81
     *
82
     * @var object
83
     */
84
    public $_socket;
85
    /**
86
     * Current state of the connection. Used with the
87
     * constants defined above.
88
     *
89
     * @var int
90
     */
91
    public $_state;
92
    /**
93
     * Hostname to connect to
94
     *
95
     * @var string
96
     */
97
    public $_host;
98
    /**
99
     * Port to connect to
100
     *
101
     * @var int
102
     */
103
    public $_port;
104
    /**
105
     * To allow class debuging
106
     * @var bool
107
     */
108
    public $_debug = false;
109
    /**
110
     * The auth methods this class support
111
     * @var array
112
     */
113
    //var $supportedAuthMethods=array('DIGEST-MD5', 'CRAM-MD5', 'APOP' , 'PLAIN' , 'LOGIN', 'USER');
114
    //Disabling DIGEST-MD5 for now
115
    public $supportedAuthMethods = [/*'CRAM-MD5', 'APOP' ,*/
116
                                    'PLAIN',
117
                                    'LOGIN',
118
                                    'USER',
119
    ];
120
    //var $supportedAuthMethods=array( 'CRAM-MD5', 'PLAIN' , 'LOGIN');
121
    //var $supportedAuthMethods=array( 'PLAIN' , 'LOGIN');
122
123
    /**
124
     * The auth methods this class support
125
     * @var array
126
     */
127
    public $supportedSASLAuthMethods = ['DIGEST-MD5', 'CRAM-MD5'];
128
    /**
129
     * The capability response
130
     * @var array
131
     */
132
    public $_capability;
133
134
    /**
135
     * Constructor. Sets up the object variables, and instantiates
136
     * the socket object.
137
     *
138
     */
139
140
    public function __construct()
141
    {
142
        $this->_timestamp = ''; // Used for APOP
143
        $this->_maildrop  = [];
144
        $this->_timeout   = 10;
145
        $this->_state     = NET_POP3_STATE_DISCONNECTED;
146
        $this->_socket    = new Net_Socket();
147
        /*
148
         * Include the Auth_SASL package.  If the package is not available,
149
         * we disable the authentication methods that depend upon it.
150
         */
151
        if (false === (@require_once __DIR__ . '/Auth/SASL.php')) {
152
            if ($this->_debug) {
153
                echo "AUTH_SASL NOT PRESENT!\n";
154
            }
155
            foreach ($this->supportedSASLAuthMethods as $SASLMethod) {
156
                $pos = array_search($SASLMethod, $this->supportedAuthMethods, true);
157
                if ($this->_debug) {
158
                    echo "DISABLING METHOD $SASLMethod\n";
159
                }
160
                unset($this->supportedAuthMethods[$pos]);
161
            }
162
        }
163
    }
164
165
    /**
166
     * Handles the errors the class can find
167
     * on the server
168
     *
169
     * @param string $msg
170
     * @param int    $code
171
     * @return PEAR_Error
172
     */
173
    public function _raiseError(string $msg, int $code = -1): PEAR_Error
174
    {
175
        require_once XHELP_PEAR_PATH . '/PEAR.php';
176
177
        return PEAR::raiseError($msg, $code);
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::raiseError() is not static, but was called statically. ( Ignorable by Annotation )

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

177
        return PEAR::/** @scrutinizer ignore-call */ raiseError($msg, $code);
Loading history...
It seems like $code can also be of type integer; however, parameter $code of PEAR::raiseError() does only seem to accept null, 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

177
        return PEAR::raiseError($msg, /** @scrutinizer ignore-type */ $code);
Loading history...
178
    }
179
180
    /**
181
     * Connects to the given host on the given port.
182
     * Also looks for the timestamp in the greeting
183
     * needed for APOP authentication
184
     *
185
     * @param string $host /IP address to connect to Hostname
186
     * @param int    $port Port to use to connect to on host
187
     * @return bool  Success/Failure
188
     */
189
    public function connect(string $host = 'localhost', int $port = 110): bool
190
    {
191
        $this->_host = $host;
192
        $this->_port = $port;
193
194
        $result = $this->_socket->connect($host, $port, false, $this->_timeout);
195
        if (true === $result) {
196
            $data = $this->_recvLn();
197
198
            if ($this->_checkResponse($data)) {
0 ignored issues
show
It seems like $data can also be of type PEAR_Error; however, parameter $response of Net_POP3::_checkResponse() does only seem to accept string, 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

198
            if ($this->_checkResponse(/** @scrutinizer ignore-type */ $data)) {
Loading history...
199
                // if the response begins with '+OK' ...
200
                //            if (@substr(strtoupper($data), 0, 3) == '+OK') {
201
                // Check for string matching apop timestamp
202
                if (preg_match('/<.+@.+>/U', $data, $matches)) {
0 ignored issues
show
It seems like $data can also be of type PEAR_Error; however, parameter $subject of preg_match() does only seem to accept string, 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

202
                if (preg_match('/<.+@.+>/U', /** @scrutinizer ignore-type */ $data, $matches)) {
Loading history...
203
                    $this->_timestamp = $matches[0];
204
                }
205
                $this->_maildrop = [];
206
                $this->_state    = NET_POP3_STATE_AUTHORISATION;
207
208
                return true;
209
            }
210
        }
211
212
        $this->_socket->disconnect();
213
214
        return false;
215
    }
216
217
    /**
218
     * Disconnect function. Sends the QUIT command
219
     * and closes the socket.
220
     *
221
     * @return bool Success/Failure
222
     */
223
    public function disconnect(): bool
224
    {
225
        return $this->_cmdQuit();
226
    }
227
228
    /**
229
     * Performs the login procedure. If there is a timestamp
230
     * stored, APOP will be tried first, then basic USER/PASS.
231
     *
232
     * @param string $user Username to use
233
     * @param string $pass Password to use
234
     * @param bool   $apop Whether to try APOP first
235
     * @return bool|\PEAR_Error  true on Success/ PEAR_ERROR on error
236
     */
237
    public function login(string $user, string $pass, bool $apop = true)
238
    {
239
        if (NET_POP3_STATE_AUTHORISATION == $this->_state) {
240
            if (PEAR::isError($ret = $this->_cmdAuthenticate($user, $pass, $apop))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

240
            if (PEAR::/** @scrutinizer ignore-call */ isError($ret = $this->_cmdAuthenticate($user, $pass, $apop))) {
Loading history...
$apop of type boolean is incompatible with the type null|string expected by parameter $userMethod of Net_POP3::_cmdAuthenticate(). ( Ignorable by Annotation )

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

240
            if (PEAR::isError($ret = $this->_cmdAuthenticate($user, $pass, /** @scrutinizer ignore-type */ $apop))) {
Loading history...
241
                return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type string which is incompatible with the documented return type PEAR_Error|boolean.
Loading history...
242
            }
243
            if (!PEAR::isError($ret)) {
244
                $this->_state = NET_POP3_STATE_TRANSACTION;
245
246
                return true;
247
            }
248
        }
249
250
        return $this->_raiseError('Generic login error', 1);
251
    }
252
253
    /**
254
     * Parses the response from the capability command. Stores
255
     * the result in $this->_capability
256
     */
257
    public function _parseCapability(): void
258
    {
259
        if (!PEAR::isError($data = $this->_sendCmd('CAPA'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

259
        if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('CAPA'))) {
Loading history...
260
            $data = $this->_getMultiline();
261
        }
262
        $data = preg_split('/\r?\n/', $data, -1, PREG_SPLIT_NO_EMPTY);
263
264
        foreach ($data as $i => $iValue) {
265
            $capa = '';
266
            if (preg_match('/^([a-z,\-]+)( ((.*))|$)$/i', $iValue, $matches)) {
267
                $capa = \mb_strtolower($matches[1]);
268
                switch ($capa) {
269
                    case 'implementation':
270
                        $this->_capability['implementation'] = $matches[3];
271
                        break;
272
                    case 'sasl':
273
                        $this->_capability['sasl'] = preg_split('/\s+/', $matches[3]);
274
                        break;
275
                    default:
276
                        $this->_capability[$capa] = $matches[2];
277
                        break;
278
                }
279
            }
280
        }
281
    }
282
283
    /**
284
     * Returns the name of the best authentication method that the server
285
     * has advertised.
286
     *
287
     * @param mixed $userMethod
288
     * @return mixed Returns a string containing the name of the best
289
     *               supported authentication method or a PEAR_Error object
290
     *               if a failure condition is encountered.
291
     * @since  1.0
292
     */
293
    public function _getBestAuthMethod($userMethod = null)
294
    {
295
        /*
296
         return 'USER';
297
         return 'APOP';
298
         return 'DIGEST-MD5';
299
         return 'CRAM-MD5';
300
         */
301
302
        $this->_parseCapability();
303
304
        //unset($this->_capability['sasl']);
305
306
        $serverMethods = $this->_capability['sasl'] ?? [/*'APOP',*/
307
                                                        'USER',
308
            ];
309
310
        if (null !== $userMethod && true !== $userMethod) {
311
            $methods   = [];
312
            $methods[] = $userMethod;
313
314
            return $userMethod;
315
        }
316
        $methods = $this->supportedAuthMethods;
317
318
        if ((null !== $methods) && (null !== $serverMethods)) {
319
            foreach ($methods as $method) {
320
                if (in_array($method, $serverMethods, true)) {
321
                    return $method;
322
                }
323
            }
324
            $serverMethods = implode(',', $serverMethods);
325
            $myMethods     = implode(',', $this->supportedAuthMethods);
326
327
            return $this->_raiseError("$method NOT supported authentication method!. This server " . "supports these methods: $serverMethods, but I support $myMethods");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $method seems to be defined by a foreach iteration on line 319. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
328
        }
329
330
        return $this->_raiseError("This server don't support any Auth methods");
331
    }
332
333
    /**
334
     * Handles the authentication using any known method
335
     *
336
     * @param string      $uid        The userid to authenticate as.
337
     * @param string      $pwd        The password to authenticate with.
338
     * @param string|null $userMethod The method to use ( if $userMethod == '' then the class chooses the best method (the stronger is the best ) )
339
     *
340
     * @return \PEAR_Error|string  string or PEAR_Error
341
     *
342
     * @access private
343
     * @since  1.0
344
     */
345
    private function _cmdAuthenticate(string $uid, string $pwd, string $userMethod = null)
346
    {
347
        if (PEAR::isError($method = $this->_getBestAuthMethod($userMethod))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

347
        if (PEAR::/** @scrutinizer ignore-call */ isError($method = $this->_getBestAuthMethod($userMethod))) {
Loading history...
348
            return $method;
349
        }
350
        switch ($method) {
351
            case 'DIGEST-MD5':
352
                $result = $this->_authDigest_MD5($uid, $pwd);
353
                break;
354
            case 'CRAM-MD5':
355
                $result = $this->_authCRAM_MD5($uid, $pwd);
356
                break;
357
            case 'LOGIN':
358
                $result = $this->_authLOGIN($uid, $pwd);
359
                break;
360
            case 'PLAIN':
361
                $result = $this->_authPLAIN($uid, $pwd);
362
                break;
363
            case 'APOP':
364
                $result = $this->_cmdApop($uid, $pwd);
365
                // if APOP fails fallback to USER auth
366
                if (false === $result) {
367
                    echo "APOP FAILED!!!\n";
368
                    $result = $this->_authUSER($uid, $pwd);
369
                }
370
                break;
371
            case 'USER':
372
                $result = $this->_authUSER($uid, $pwd);
373
                break;
374
            default:
375
                $result = $this->_authUSER($uid, $pwd);
376
                //$result = $this->_raiseError( "$method is not a supported authentication method" );
377
                break;
378
        }
379
380
        return $result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $result also could return the type array|boolean which is incompatible with the documented return type PEAR_Error|string.
Loading history...
381
    }
382
383
    /**
384
     * Authenticates the user using the USER-PASS method.
385
     *
386
     * @param string $user The userid to authenticate as.
387
     * @param string $pass The password to authenticate with.
388
     *
389
     * @return bool|\PEAR_Error    true on success or PEAR_Error on failure
390
     *
391
     * @access private
392
     * @since  1.0
393
     */
394
    private function _authUSER(string $user, string $pass)
395
    {
396
        if (PEAR::isError($ret = $this->_cmdUser($user))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

396
        if (PEAR::/** @scrutinizer ignore-call */ isError($ret = $this->_cmdUser($user))) {
Loading history...
397
            return $ret;
398
        }
399
        if (PEAR::isError($ret = $this->_cmdPass($pass))) {
400
            return $ret;
401
        }
402
403
        return true;
404
    }
405
406
    /**
407
     * Authenticates the user using the PLAIN method.
408
     *
409
     * @param string $user The userid to authenticate as.
410
     * @param string $pass The password to authenticate with.
411
     *
412
     * @return array|bool Returns an array containing the response
413
     *
414
     * @access private
415
     * @since  1.0
416
     */
417
    private function _authPLAIN(string $user, string $pass)
418
    {
419
        $cmd = sprintf('AUTH PLAIN %s', base64_encode(chr(0) . $user . chr(0) . $pass));
420
421
        if (PEAR::isError($ret = $this->_send($cmd))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

421
        if (PEAR::/** @scrutinizer ignore-call */ isError($ret = $this->_send($cmd))) {
Loading history...
422
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type PEAR_Error which is incompatible with the documented return type array|boolean.
Loading history...
423
        }
424
        if (PEAR::isError($challenge = $this->_recvLn())) {
425
            return $challenge;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $challenge also could return the type PEAR_Error which is incompatible with the documented return type array|boolean.
Loading history...
426
        }
427
        if (PEAR::isError($ret = $this->_checkResponse($challenge))) {
0 ignored issues
show
It seems like $challenge can also be of type PEAR_Error; however, parameter $response of Net_POP3::_checkResponse() does only seem to accept string, 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

427
        if (PEAR::isError($ret = $this->_checkResponse(/** @scrutinizer ignore-type */ $challenge))) {
Loading history...
428
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type PEAR_Error which is incompatible with the documented return type array|boolean.
Loading history...
429
        }
430
431
        return true;
432
    }
433
434
    /**
435
     * Authenticates the user using the PLAIN method.
436
     *
437
     * @param string $user The userid to authenticate as.
438
     * @param string $pass The password to authenticate with.
439
     *
440
     * @return array|\PEAR_Error Returns an array containing the response
441
     *
442
     * @access private
443
     * @since  1.0
444
     */
445
    private function _authLOGIN(string $user, string $pass)
446
    {
447
        $this->_send('AUTH LOGIN');
448
449
        if (PEAR::isError($challenge = $this->_recvLn())) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

449
        if (PEAR::/** @scrutinizer ignore-call */ isError($challenge = $this->_recvLn())) {
Loading history...
450
            return $challenge;
451
        }
452
        if (PEAR::isError($ret = $this->_checkResponse($challenge))) {
0 ignored issues
show
It seems like $challenge can also be of type PEAR_Error; however, parameter $response of Net_POP3::_checkResponse() does only seem to accept string, 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

452
        if (PEAR::isError($ret = $this->_checkResponse(/** @scrutinizer ignore-type */ $challenge))) {
Loading history...
453
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
454
        }
455
456
        if (PEAR::isError($ret = $this->_send(sprintf('"%s"', base64_encode($user))))) {
457
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
458
        }
459
460
        if (PEAR::isError($challenge = $this->_recvLn())) {
461
            return $challenge;
462
        }
463
        if (PEAR::isError($ret = $this->_checkResponse($challenge))) {
464
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
465
        }
466
467
        if (PEAR::isError($ret = $this->_send(sprintf('"%s"', base64_encode($pass))))) {
468
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
469
        }
470
471
        if (PEAR::isError($challenge = $this->_recvLn())) {
472
            return $challenge;
473
        }
474
475
        return $this->_checkResponse($ret);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_checkResponse($ret) also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
$ret of type PEAR_Error|true is incompatible with the type string expected by parameter $response of Net_POP3::_checkResponse(). ( Ignorable by Annotation )

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

475
        return $this->_checkResponse(/** @scrutinizer ignore-type */ $ret);
Loading history...
476
    }
477
478
    /**
479
     * Authenticates the user using the CRAM-MD5 method.
480
     *
481
     * @param string $uid The userid to authenticate as.
482
     * @param string $pwd The password to authenticate with.
483
     *
484
     * @return array|\PEAR_Error Returns an array containing the response
485
     *
486
     * @access private
487
     * @since  1.0
488
     */
489
    private function _authCRAM_MD5(string $uid, string $pwd)
490
    {
491
        if (PEAR::isError($ret = $this->_send('AUTH CRAM-MD5'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

491
        if (PEAR::/** @scrutinizer ignore-call */ isError($ret = $this->_send('AUTH CRAM-MD5'))) {
Loading history...
492
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
493
        }
494
495
        if (PEAR::isError($challenge = $this->_recvLn())) {
496
            return $challenge;
497
        }
498
        if (PEAR::isError($ret = $this->_checkResponse($challenge))) {
0 ignored issues
show
It seems like $challenge can also be of type PEAR_Error; however, parameter $response of Net_POP3::_checkResponse() does only seem to accept string, 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

498
        if (PEAR::isError($ret = $this->_checkResponse(/** @scrutinizer ignore-type */ $challenge))) {
Loading history...
499
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
500
        }
501
502
        // remove '+ '
503
504
        $challenge = mb_substr($challenge, 2);
0 ignored issues
show
It seems like $challenge can also be of type PEAR_Error; however, parameter $string of mb_substr() does only seem to accept string, 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

504
        $challenge = mb_substr(/** @scrutinizer ignore-type */ $challenge, 2);
Loading history...
505
506
        $challenge = base64_decode($challenge, true);
507
508
        $cram     = &Auth_SASL::factory('crammd5');
0 ignored issues
show
The type Auth_SASL 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...
509
        $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
510
511
        if (PEAR::isError($error = $this->_send($auth_str))) {
512
            return $error;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $error also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
513
        }
514
        if (PEAR::isError($ret = $this->_recvLn())) {
515
            return $ret;
516
        }
517
        //echo "RET:$ret\n";
518
        return $this->_checkResponse($ret);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_checkResponse($ret) also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
519
    }
520
521
    /**
522
     * Authenticates the user using the DIGEST-MD5 method.
523
     *
524
     * @param string $uid The userid to authenticate as.
525
     * @param string $pwd The password to authenticate with.
526
     * @param string The efective user
527
     *
528
     * @return array|\PEAR_Error Returns an array containing the response
529
     *
530
     * @access private
531
     * @since  1.0
532
     */
533
    private function _authDigest_MD5(string $uid, string $pwd)
534
    {
535
        if (PEAR::isError($ret = $this->_send('AUTH DIGEST-MD5'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

535
        if (PEAR::/** @scrutinizer ignore-call */ isError($ret = $this->_send('AUTH DIGEST-MD5'))) {
Loading history...
536
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
537
        }
538
539
        if (PEAR::isError($challenge = $this->_recvLn())) {
540
            return $challenge;
541
        }
542
        if (PEAR::isError($ret = $this->_checkResponse($challenge))) {
0 ignored issues
show
It seems like $challenge can also be of type PEAR_Error; however, parameter $response of Net_POP3::_checkResponse() does only seem to accept string, 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

542
        if (PEAR::isError($ret = $this->_checkResponse(/** @scrutinizer ignore-type */ $challenge))) {
Loading history...
543
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
544
        }
545
546
        // remove '+ '
547
        $challenge = mb_substr($challenge, 2);
0 ignored issues
show
It seems like $challenge can also be of type PEAR_Error; however, parameter $string of mb_substr() does only seem to accept string, 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

547
        $challenge = mb_substr(/** @scrutinizer ignore-type */ $challenge, 2);
Loading history...
548
549
        $challenge = base64_decode($challenge, true);
550
        $digest    = &Auth_SASL::factory('digestmd5');
551
        $auth_str  = base64_encode($digest->getResponse($uid, $pwd, $challenge, 'localhost', 'pop3'));
552
553
        if (PEAR::isError($error = $this->_send($auth_str))) {
554
            return $error;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $error also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
555
        }
556
557
        if (PEAR::isError($challenge = $this->_recvLn())) {
558
            return $challenge;
559
        }
560
        if (PEAR::isError($ret = $this->_checkResponse($challenge))) {
561
            return $ret;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $ret also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
562
        }
563
        /*
564
         * We don't use the protocol's third step because POP3 doesn't allow
565
         * subsequent authentication, so we just silently ignore it.
566
         */
567
568
        if (PEAR::isError($challenge = $this->_send("\r\n"))) {
569
            return $challenge;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $challenge also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
570
        }
571
572
        if (PEAR::isError($challenge = $this->_recvLn())) {
573
            return $challenge;
574
        }
575
576
        return $this->_checkResponse($challenge);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_checkResponse($challenge) also could return the type true which is incompatible with the documented return type PEAR_Error|array.
Loading history...
577
    }
578
579
    /**
580
     * Sends the APOP command
581
     *
582
     * @param string $user Username to send
583
     * @param string $pass Password to send
584
     * @return bool|\PEAR_Error Success/Failure
585
     */
586
    public function _cmdApop(string $user, string $pass)
587
    {
588
        if (NET_POP3_STATE_AUTHORISATION == $this->_state) {
589
            if (!empty($this->_timestamp)) {
590
                if (PEAR::isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

590
                if (PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('APOP ' . $user . ' ' . md5($this->_timestamp . $pass)))) {
Loading history...
591
                    return $data;
592
                }
593
                $this->_state = NET_POP3_STATE_TRANSACTION;
594
595
                return true;
596
            }
597
        }
598
599
        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State1');
600
    }
601
602
    /**
603
     * Returns the raw headers of the specified message.
604
     *
605
     * @param int $msg_id Message number
606
     * @return mixed   Either raw headers or false on error
607
     */
608
609
    /**
610
     * @param int $msg_id
611
     * @return bool|string
612
     */
613
    public function getRawHeaders(int $msg_id)
614
    {
615
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
616
            return $this->_cmdTop($msg_id, 0);
617
        }
618
619
        return false;
620
    }
621
622
    /*
623
     * Returns the  headers of the specified message in an
624
     * associative array. Array keys are the header names, array
625
     * values are the header values. In the case of multiple headers
626
     * having the same names, eg Received:, the array value will be
627
     * an indexed array of all the header values.
628
     *
629
     * @param  int $msg_id Message number
630
     * @return mixed   Either array of headers or false on error
631
     */
632
633
    /**
634
     * @param int $msg_id
635
     * @return array|false
636
     */
637
    public function getParsedHeaders(int $msg_id)
638
    {
639
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
640
            $raw_headers = rtrim($this->getRawHeaders($msg_id));
0 ignored issues
show
It seems like $this->getRawHeaders($msg_id) can also be of type false; however, parameter $string of rtrim() does only seem to accept string, 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

640
            $raw_headers = rtrim(/** @scrutinizer ignore-type */ $this->getRawHeaders($msg_id));
Loading history...
641
642
            $raw_headers = preg_replace("/\r\n[ \t]+/", ' ', $raw_headers); // Unfold headers
643
            $raw_headers = explode("\r\n", $raw_headers);
644
            foreach ($raw_headers as $value) {
645
                $name  = mb_substr($value, 0, $pos = mb_strpos($value, ':'));
646
                $value = ltrim(mb_substr($value, $pos + 1));
647
                if (isset($headers[$name]) && is_array($headers[$name])) {
648
                    $headers[$name][] = $value;
649
                } elseif (isset($headers[$name])) {
650
                    $headers[$name] = [$headers[$name], $value];
651
                } else {
652
                    $headers[$name] = $value;
653
                }
654
            }
655
656
            return $headers;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $headers does not seem to be defined for all execution paths leading up to this point.
Loading history...
657
        }
658
659
        return false;
660
    }
661
662
    /*
663
     * Returns the body of the message with given message number.
664
     *
665
     * @param  int $msg_id Message number
666
     * @return mixed   Either message body or false on error
667
     */
668
669
    /**
670
     * @param int $msg_id
671
     * @return false|mixed|string
672
     */
673
    public function getBody(int $msg_id)
674
    {
675
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
676
            $msg = $this->_cmdRetr($msg_id);
677
678
            return mb_substr($msg, mb_strpos($msg, "\r\n\r\n") + 4);
0 ignored issues
show
It seems like $msg can also be of type false; however, parameter $haystack of mb_strpos() does only seem to accept string, 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

678
            return mb_substr($msg, mb_strpos(/** @scrutinizer ignore-type */ $msg, "\r\n\r\n") + 4);
Loading history...
It seems like $msg can also be of type false; however, parameter $string of mb_substr() does only seem to accept string, 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

678
            return mb_substr(/** @scrutinizer ignore-type */ $msg, mb_strpos($msg, "\r\n\r\n") + 4);
Loading history...
679
        }
680
681
        return false;
682
    }
683
684
    /*
685
     * Returns the entire message with given message number.
686
     *
687
     * @param  int $msg_id Message number
688
     * @return mixed   Either entire message or false on error
689
     */
690
691
    /**
692
     * @param int $msg_id
693
     * @return false|string
694
     */
695
    public function getMsg(int $msg_id)
696
    {
697
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
698
            return $this->_cmdRetr($msg_id);
699
        }
700
701
        return false;
702
    }
703
704
    /*
705
     * Returns the size of the maildrop
706
     *
707
     * @return mixed Either size of maildrop or false on error
708
     */
709
710
    /**
711
     * @return false|mixed
712
     */
713
    public function getSize()
714
    {
715
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
716
            if (isset($this->_maildrop['size'])) {
717
                return $this->_maildrop['size'];
718
            }
719
            [, $size] = $this->_cmdStat();
720
721
            return $size;
722
        }
723
724
        return false;
725
    }
726
727
    /*
728
     * Returns number of messages in this maildrop
729
     *
730
     * @return mixed Either number of messages or false on error
731
     */
732
733
    /**
734
     * @return false|mixed
735
     */
736
    public function numMsg()
737
    {
738
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
739
            if (isset($this->_maildrop['num_msg'])) {
740
                return $this->_maildrop['num_msg'];
741
            }
742
            [$num_msg,] = $this->_cmdStat();
743
744
            return $num_msg;
745
        }
746
747
        return false;
748
    }
749
750
    /*
751
     * Marks a message for deletion. Only will be deleted if the
752
     * disconnect() method is called.
753
     *
754
     * @param  Message $msg_id to delete
755
     * @return bool Success/Failure
756
     */
757
758
    /**
759
     * @param int $msg_id
760
     * @return false|mixed|\PEAR_Error
761
     */
762
    public function deleteMsg(int $msg_id)
763
    {
764
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
765
            return $this->_cmdDele($msg_id);
766
        }
767
768
        return false;
769
    }
770
771
    /*
772
     * Combination of LIST/UIDL commands, returns an array
773
     * of data
774
     *
775
     * @param  Optional $msg_id message number
776
     * @return mixed Array of data or false on error
777
     */
778
779
    /**
780
     * @param null $msg_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $msg_id is correct as it would always require null to be passed?
Loading history...
781
     * @return array|false
782
     */
783
    public function getListing($msg_id = null)
784
    {
785
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
786
            if (isset($msg_id)) {
787
                if ($list = $this->_cmdList($msg_id) and $uidl = $this->_cmdUidl($msg_id)) {
788
                    return array_merge($list, $uidl);
789
                }
790
            } else {
791
                $list = $this->_cmdList();
792
                if ($list) {
793
                    $uidl = $this->_cmdUidl();
794
                    if ($uidl) {
795
                        foreach ($uidl as $i => $value) {
796
                            $list[$i]['uidl'] = $value['uidl'];
797
                        }
798
                    }
799
800
                    return $list;
801
                }
802
            }
803
        }
804
805
        return false;
806
    }
807
808
    /*
809
     * Sends the USER command
810
     *
811
     * @param  Username $user to send
812
     * @return bool  Success/Failure
813
     */
814
815
    /**
816
     * @param string $user
817
     * @return mixed|\PEAR_Error
818
     */
819
    public function _cmdUser(string $user)
820
    {
821
        if (NET_POP3_STATE_AUTHORISATION == $this->_state) {
822
            return $this->_sendCmd('USER ' . $user);
823
        }
824
825
        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
826
    }
827
828
    /*
829
     * Sends the PASS command
830
     *
831
     * @param  Password $pass to send
832
     * @return bool  Success/Failure
833
     */
834
835
    /**
836
     * @param string $pass
837
     * @return mixed|\PEAR_Error
838
     */
839
    public function _cmdPass($pass)
840
    {
841
        if (NET_POP3_STATE_AUTHORISATION == $this->_state) {
842
            return $this->_sendCmd('PASS ' . $pass);
843
        }
844
845
        return $this->_raiseError('Not In NET_POP3_STATE_AUTHORISATION State');
846
    }
847
848
    /*
849
     * Sends the STAT command
850
     *
851
     * @return mixed Indexed array of number of messages and
852
     *               maildrop size, or false on error.
853
     */
854
855
    /**
856
     * @return array|false
857
     */
858
    public function _cmdStat()
859
    {
860
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
861
            if (!PEAR::isError($data = $this->_sendCmd('STAT'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

861
            if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('STAT'))) {
Loading history...
862
                sscanf($data, '+OK %d %d', $msg_num, $size);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $size seems to be never defined.
Loading history...
It seems like $data can also be of type PEAR_Error; however, parameter $string of sscanf() does only seem to accept string, 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

862
                sscanf(/** @scrutinizer ignore-type */ $data, '+OK %d %d', $msg_num, $size);
Loading history...
863
                $this->_maildrop['num_msg'] = $msg_num;
864
                $this->_maildrop['size']    = $size;
865
866
                return [$msg_num, $size];
867
            }
868
        }
869
870
        return false;
871
    }
872
873
    /*
874
     * Sends the LIST command
875
     *
876
     * @param  Optional $msg_id message number
877
     * @return mixed   Indexed array of msg_id/msg size or
878
     *                 false on error
879
     */
880
881
    /**
882
     * @param null $msg_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $msg_id is correct as it would always require null to be passed?
Loading history...
883
     * @return array|false
884
     */
885
    public function _cmdList($msg_id = null)
886
    {
887
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
888
            if (isset($msg_id)) {
889
                if (!PEAR::isError($data = $this->_sendCmd('LIST ' . $msg_id))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

889
                if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('LIST ' . $msg_id))) {
Loading history...
890
                    sscanf($data, '+OK %d %d', $msg_id, $size);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $size seems to be never defined.
Loading history...
It seems like $data can also be of type PEAR_Error; however, parameter $string of sscanf() does only seem to accept string, 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

890
                    sscanf(/** @scrutinizer ignore-type */ $data, '+OK %d %d', $msg_id, $size);
Loading history...
891
892
                    return ['msg_id' => $msg_id, 'size' => $size];
893
                }
894
            } else {
895
                if (!PEAR::isError($data = $this->_sendCmd('LIST'))) {
896
                    $data = $this->_getMultiline();
897
                    $data = explode("\r\n", $data);
898
                    foreach ($data as $line) {
899
                        sscanf($line, '%s %s', $msg_id, $size);
900
                        $return[] = ['msg_id' => $msg_id, 'size' => $size];
901
                    }
902
903
                    return $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return seems to be defined by a foreach iteration on line 898. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
904
                }
905
            }
906
        }
907
908
        return false;
909
    }
910
911
    /**
912
     * Sends the RETR command
913
     *
914
     * @param int $msg_id The message number to retrieve
915
     * @return false|string   The message or false on error
916
     */
917
    public function _cmdRetr(int $msg_id)
918
    {
919
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
920
            if (!PEAR::isError($data = $this->_sendCmd('RETR ' . $msg_id))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

920
            if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('RETR ' . $msg_id))) {
Loading history...
921
                $data = $this->_getMultiline();
922
923
                return $data;
924
            }
925
        }
926
927
        return false;
928
    }
929
930
    /**
931
     * Sends the DELE command
932
     *
933
     * @param int $msg_id Message number to mark as deleted
934
     * @return bool Success/Failure
935
     */
936
937
    /**
938
     * @param int $msg_id
939
     * @return false|mixed|\PEAR_Error
940
     */
941
    public function _cmdDele(int $msg_id)
942
    {
943
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
944
            return $this->_sendCmd('DELE ' . $msg_id);
945
        }
946
947
        return false;
948
    }
949
950
    /**
951
     * Sends the NOOP command
952
     *
953
     * @return bool Success/Failure
954
     */
955
    public function _cmdNoop(): bool
956
    {
957
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
958
            if (!PEAR::isError($data = $this->_sendCmd('NOOP'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

958
            if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('NOOP'))) {
Loading history...
959
                return true;
960
            }
961
        }
962
963
        return false;
964
    }
965
966
    /**
967
     * Sends the RSET command
968
     *
969
     * @return bool Success/Failure
970
     */
971
    public function _cmdRset(): bool
972
    {
973
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
974
            if (!PEAR::isError($data = $this->_sendCmd('RSET'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

974
            if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('RSET'))) {
Loading history...
975
                return true;
976
            }
977
        }
978
979
        return false;
980
    }
981
982
    /**
983
     * Sends the QUIT command
984
     *
985
     * @return bool Success/Failure
986
     */
987
    public function _cmdQuit(): bool
988
    {
989
        $data         = $this->_sendCmd('QUIT');
990
        $this->_state = NET_POP3_STATE_DISCONNECTED;
991
        $this->_socket->disconnect();
992
993
        return (bool)$data;
994
    }
995
996
    /**
997
     * Sends the TOP command
998
     *
999
     * @param int $msg_id    Message number
1000
     * @param int $num_lines Number of lines to retrieve
1001
     * @return false|string Message data or false on error
1002
     */
1003
    public function _cmdTop(int $msg_id, int $num_lines)
1004
    {
1005
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
1006
            if (!PEAR::isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

1006
            if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('TOP ' . $msg_id . ' ' . $num_lines))) {
Loading history...
1007
                return $this->_getMultiline();
1008
            }
1009
        }
1010
1011
        return false;
1012
    }
1013
1014
    /**
1015
     * Sends the UIDL command
1016
     *
1017
     * @param int|null $msg_id Message number
1018
     * @return array|false indexed array of msg_id/uidl or false on error
1019
     */
1020
1021
    public function _cmdUidl(int $msg_id = null)
1022
    {
1023
        if (NET_POP3_STATE_TRANSACTION == $this->_state) {
1024
            if (isset($msg_id)) {
1025
                $data = $this->_sendCmd('UIDL ' . $msg_id);
1026
                sscanf($data, '+OK %d %s', $msg_id, $uidl);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $uidl seems to be never defined.
Loading history...
It seems like $data can also be of type PEAR_Error; however, parameter $string of sscanf() does only seem to accept string, 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

1026
                sscanf(/** @scrutinizer ignore-type */ $data, '+OK %d %s', $msg_id, $uidl);
Loading history...
1027
1028
                return ['msg_id' => $msg_id, 'uidl' => $uidl];
1029
            } else {
1030
                if (!PEAR::isError($data = $this->_sendCmd('UIDL'))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

1030
                if (!PEAR::/** @scrutinizer ignore-call */ isError($data = $this->_sendCmd('UIDL'))) {
Loading history...
1031
                    $data = $this->_getMultiline();
1032
                    $data = explode("\r\n", $data);
1033
                    foreach ($data as $line) {
1034
                        sscanf($line, '%d %s', $msg_id, $uidl);
1035
                        $return[] = ['msg_id' => $msg_id, 'uidl' => $uidl];
1036
                    }
1037
1038
                    return $return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $return seems to be defined by a foreach iteration on line 1033. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
1039
                }
1040
            }
1041
        }
1042
1043
        return false;
1044
    }
1045
1046
    /**
1047
     * Sends a command, checks the reponse, and
1048
     * if good returns the reponse, other wise
1049
     * returns false.
1050
     *
1051
     * @param string $cmd Command to send (\r\n will be appended)
1052
     * @return mixed First line of response if successful, otherwise false
1053
     */
1054
    public function _sendCmd(string $cmd)
1055
    {
1056
        $result = $this->_send($cmd);
1057
1058
        if (!PEAR::isError($result) && $result) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

1058
        if (!PEAR::/** @scrutinizer ignore-call */ isError($result) && $result) {
Loading history...
1059
            $data = $this->_recvLn();
1060
            if (!PEAR::isError($data) && 0 === mb_stripos($data, '+OK')) {
0 ignored issues
show
It seems like $data can also be of type PEAR_Error; however, parameter $haystack of mb_stripos() does only seem to accept string, 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

1060
            if (!PEAR::isError($data) && 0 === mb_stripos(/** @scrutinizer ignore-type */ $data, '+OK')) {
Loading history...
1061
                return $data;
1062
            }
1063
        }
1064
1065
        return $this->_raiseError($data);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.
Loading history...
1066
    }
1067
1068
    /**
1069
     * Reads a multiline reponse and returns the data
1070
     *
1071
     * @return string The reponse.
1072
     */
1073
    public function _getMultiline(): string
1074
    {
1075
        $data = '';
1076
        while (!PEAR::isError($tmp = $this->_recvLn())) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

1076
        while (!PEAR::/** @scrutinizer ignore-call */ isError($tmp = $this->_recvLn())) {
Loading history...
1077
            if ('.' === $tmp) {
1078
                return mb_substr($data, 0, -2);
1079
            }
1080
            if (0 === mb_strpos($tmp, '..')) {
0 ignored issues
show
It seems like $tmp can also be of type PEAR_Error; however, parameter $haystack of mb_strpos() does only seem to accept string, 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

1080
            if (0 === mb_strpos(/** @scrutinizer ignore-type */ $tmp, '..')) {
Loading history...
1081
                $tmp = mb_substr($tmp, 1);
0 ignored issues
show
It seems like $tmp can also be of type PEAR_Error; however, parameter $string of mb_substr() does only seem to accept string, 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

1081
                $tmp = mb_substr(/** @scrutinizer ignore-type */ $tmp, 1);
Loading history...
1082
            }
1083
            $data .= $tmp . "\r\n";
1084
        }
1085
1086
        return mb_substr($data, 0, -2);
1087
    }
1088
1089
    /**
1090
     * Sets the bebug state
1091
     *
1092
     * @param bool $debug
1093
     */
1094
    public function setDebug(bool $debug = true): void
1095
    {
1096
        $this->_debug = $debug;
1097
    }
1098
1099
    /**
1100
     * Send the given string of data to the server.
1101
     *
1102
     * @param string $data The string of data to send.
1103
     *
1104
     * @return bool|\PEAR_Error True on success or a PEAR_Error object on failure.
1105
     *
1106
     * @since   1.0
1107
     */
1108
    public function _send(string $data)
1109
    {
1110
        if ($this->_debug) {
1111
            echo "C: $data\n";
1112
        }
1113
1114
        if (PEAR::isError($error = $this->_socket->writeLine($data))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

1114
        if (PEAR::/** @scrutinizer ignore-call */ isError($error = $this->_socket->writeLine($data))) {
Loading history...
1115
            return $this->_raiseError('Failed to write to socket: ' . $error->getMessage());
1116
        }
1117
1118
        return true;
1119
    }
1120
1121
    /**
1122
     * Receive the given string of data from the server.
1123
     *
1124
     * @return mixed a line of response on success or a PEAR_Error object on failure.
1125
     *
1126
     * @since   1.0
1127
     */
1128
    public function _recvLn()
1129
    {
1130
        if (PEAR::isError($lastline = $this->_socket->readLine(8192))) {
0 ignored issues
show
Bug Best Practice introduced by
The method PEAR::isError() is not static, but was called statically. ( Ignorable by Annotation )

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

1130
        if (PEAR::/** @scrutinizer ignore-call */ isError($lastline = $this->_socket->readLine(8192))) {
Loading history...
1131
            return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage());
0 ignored issues
show
Bug Best Practice introduced by
The property lastline does not exist on Net_POP3. Did you maybe forget to declare it?
Loading history...
1132
        }
1133
        if ($this->_debug) {
1134
            // S: means this data was sent by  the POP3 Server
1135
            echo "S:$lastline\n";
1136
        }
1137
1138
        return $lastline;
1139
    }
1140
1141
    /**
1142
     * Checks de server Response
1143
     *
1144
     * @param string $response
1145
     * @return bool|\PEAR_Error true on success or a PEAR_Error object on failure.
1146
     *
1147
     * @since   1.3.3
1148
     */
1149
    public function _checkResponse(string $response)
1150
    {
1151
        if ('+OK' === @mb_substr(mb_strtoupper($response), 0, 3)) {
1152
            return true;
1153
        }
1154
        if ('-ERR' === @mb_substr(mb_strtoupper($response), 0, 4)) {
1155
            return $this->_raiseError($response);
1156
        }
1157
        if ('+ ' === @mb_substr(mb_strtoupper($response), 0, 2)) {
1158
            return true;
1159
        }
1160
1161
        return $this->_raiseError("Unknown Response ($response)");
1162
    }
1163
}
1164