POP3::connect()   C
last analyzed

Complexity

Conditions 9
Paths 17

Size

Total Lines 63
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 31
nc 17
nop 3
dl 0
loc 63
rs 6.6149
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * PHPMailer POP-Before-SMTP Authentication Class.
4
 * PHP Version 5.0.0
5
 * Version 5.2.7.
6
 *
7
 * @link      https://github.com/PHPMailer/PHPMailer/
8
 *
9
 * @author    Marcus Bointon (coolbru) <[email protected]>
10
 * @author    Jim Jagielski (jimjag) <[email protected]>
11
 * @author    Andy Prevost (codeworxtech) <[email protected]>
12
 * @author    Brent R. Matzelle (original founder)
13
 * @copyright 2013 Marcus Bointon
14
 * @copyright 2010 - 2012 Jim Jagielski
15
 * @copyright 2004 - 2009 Andy Prevost
16
 * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
17
 * @note      This program is distributed in the hope that it will be useful - WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
 * FITNESS FOR A PARTICULAR PURPOSE.
20
 */
21
22
/**
23
 * PHPMailer POP-Before-SMTP Authentication Class.
24
 * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
25
 * Does not support APOP.
26
 *
27
 * @author  Richard Davey (original author) <[email protected]>
28
 * @author  Marcus Bointon (coolbru) <[email protected]>
29
 * @author  Jim Jagielski (jimjag) <[email protected]>
30
 * @author  Andy Prevost (codeworxtech) <[email protected]>
31
 */
32
class POP3
33
{
34
    /**
35
     * The POP3 PHPMailer Version number.
36
     *
37
     * @var string
38
     */
39
    public $Version = '5.2.7';
40
41
    /**
42
     * Default POP3 port number.
43
     *
44
     * @var int
45
     */
46
    public $POP3_PORT = 110;
47
48
    /**
49
     * Default timeout in seconds.
50
     *
51
     * @var int
52
     */
53
    public $POP3_TIMEOUT = 30;
54
55
    /**
56
     * POP3 Carriage Return + Line Feed.
57
     *
58
     * @var string
59
     *
60
     * @deprecated Use the constant instead
61
     */
62
    public $CRLF = "\r\n";
63
64
    /**
65
     * Debug display level.
66
     * Options: 0 = no, 1+ = yes.
67
     *
68
     * @var int
69
     */
70
    public $do_debug = 0;
71
72
    /**
73
     * POP3 mail server hostname.
74
     *
75
     * @var string
76
     */
77
    public $host;
78
79
    /**
80
     * POP3 port number.
81
     *
82
     * @var int
83
     */
84
    public $port;
85
86
    /**
87
     * POP3 Timeout Value in seconds.
88
     *
89
     * @var int
90
     */
91
    public $tval;
92
93
    /**
94
     * POP3 username.
95
     *
96
     * @var string
97
     */
98
    public $username;
99
100
    /**
101
     * POP3 password.
102
     *
103
     * @var string
104
     */
105
    public $password;
106
107
    /**
108
     * Resource handle for the POP3 connection socket.
109
     *
110
     * @var resource
111
     */
112
    private $pop_conn;
113
114
    /**
115
     * Are we connected?
116
     *
117
     * @var bool
118
     */
119
    private $connected;
120
121
    /**
122
     * Error container.
123
     *
124
     * @var array
125
     */
126
    private $error;
127
128
    /**
129
     * Line break constant.
130
     */
131
    const CRLF = "\r\n";
132
133
    /**
134
     * Constructor.
135
     */
136
    public function __construct()
137
    {
138
        $this->pop_conn = 0;
0 ignored issues
show
Documentation Bug introduced by
It seems like 0 of type integer is incompatible with the declared type resource of property $pop_conn.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
139
        $this->connected = false;
140
        $this->error = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $error.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
141
    }
142
143
    /**
144
     * Simple static wrapper for all-in-one POP before SMTP.
145
     *
146
     * @param        $host
147
     * @param bool   $port
148
     * @param bool   $tval
149
     * @param string $username
150
     * @param string $password
151
     * @param int    $debug_level
152
     *
153
     * @return bool
154
     */
155
    public static function popBeforeSmtp(
156
        $host,
157
        $port = false,
158
        $tval = false,
159
        $username = '',
160
        $password = '',
161
        $debug_level = 0
162
    ) {
163
        $pop = new self();
164
165
        return $pop->authorise($host, $port, $tval, $username, $password, $debug_level);
166
    }
167
168
    /**
169
     * Authenticate with a POP3 server.
170
     * A connect, login, disconnect sequence
171
     * appropriate for POP-before SMTP authorisation.
172
     *
173
     * @param string   $host
174
     * @param bool|int $port
175
     * @param bool|int $tval
176
     * @param string   $username
177
     * @param string   $password
178
     * @param int      $debug_level
179
     *
180
     * @return bool
181
     */
182
    public function authorise($host, $port = false, $tval = false, $username = '', $password = '', $debug_level = 0)
183
    {
184
        $this->host = $host;
185
        // If no port value provided, use default
186
        if ($port === false) {
187
            $this->port = $this->POP3_PORT;
188
        } else {
189
            $this->port = $port;
0 ignored issues
show
Documentation Bug introduced by
It seems like $port can also be of type boolean. However, the property $port is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
190
        }
191
        // If no timeout value provided, use default
192
        if ($tval === false) {
193
            $this->tval = $this->POP3_TIMEOUT;
194
        } else {
195
            $this->tval = $tval;
0 ignored issues
show
Documentation Bug introduced by
It seems like $tval can also be of type boolean. However, the property $tval is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
196
        }
197
        $this->do_debug = $debug_level;
198
        $this->username = $username;
199
        $this->password = $password;
200
        //  Refresh the error log
201
        $this->error = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $error.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
202
        //  connect
203
        $result = $this->connect($this->host, $this->port, $this->tval);
0 ignored issues
show
Bug introduced by
It seems like $this->tval can also be of type boolean; however, POP3::connect() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
204
        if ($result) {
205
            $login_result = $this->login($this->username, $this->password);
206
            if ($login_result) {
207
                $this->disconnect();
208
209
                return true;
210
            }
211
        }
212
        // We need to disconnect regardless of whether the login succeeded
213
        $this->disconnect();
214
215
        return false;
216
    }
217
218
    /**
219
     * Connect to a POP3 server.
220
     *
221
     * @param string   $host
222
     * @param bool|int $port
223
     * @param int      $tval
224
     *
225
     * @return bool
226
     */
227
    public function connect($host, $port = false, $tval = 30)
228
    {
229
        //  Are we already connected?
230
        if ($this->connected) {
231
            return true;
232
        }
233
234
        //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
235
        //Rather than suppress it with @fsockopen, capture it cleanly instead
236
        set_error_handler([$this, 'catchWarning']);
237
238
        //  connect to the POP3 server
239
        $this->pop_conn = fsockopen(
240
            $host, //  POP3 Host
241
            $port, //  Port #
242
            $errno, //  Error Number
243
            $errstr, //  Error Message
244
            $tval
245
        ); //  Timeout (seconds)
246
        //  Restore the error handler
247
        restore_error_handler();
248
        //  Does the Error Log now contain anything?
249
        if ($this->error && $this->do_debug >= 1) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->error of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
250
            $this->displayErrors();
251
        }
252
        //  Did we connect?
253
        if ($this->pop_conn == false) {
254
            //  It would appear not...
255
            $this->error = [
256
                'error'  => "Failed to connect to server $host on port $port",
257
                'errno'  => $errno,
258
                'errstr' => $errstr,
259
            ];
260
            if ($this->do_debug >= 1) {
261
                $this->displayErrors();
262
            }
263
264
            return false;
265
        }
266
267
        //  Increase the stream time-out
268
        //  Check for PHP 4.3.0 or later
269
        if (version_compare(phpversion(), '5.0.0', 'ge')) {
270
            stream_set_timeout($this->pop_conn, $tval, 0);
271
        } else {
272
            //  Does not work on Windows
273
            if (substr(PHP_OS, 0, 3) !== 'WIN') {
274
                socket_set_timeout($this->pop_conn, $tval, 0);
275
            }
276
        }
277
278
        //  Get the POP3 server response
279
        $pop3_response = $this->getResponse();
280
        //  Check for the +OK
281
        if ($this->checkResponse($pop3_response)) {
282
            //  The connection is established and the POP3 server is talking
283
            $this->connected = true;
284
285
            return true;
286
        }
287
288
        return false;
289
    }
290
291
    /**
292
     * Log in to the POP3 server.
293
     * Does not support APOP (RFC 2828, 4949).
294
     *
295
     * @param string $username
296
     * @param string $password
297
     *
298
     * @return bool
299
     */
300
    public function login($username = '', $password = '')
301
    {
302
        if ($this->connected == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
303
            $this->error = 'Not connected to POP3 server';
0 ignored issues
show
Documentation Bug introduced by
It seems like 'Not connected to POP3 server' of type string is incompatible with the declared type array of property $error.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
304
305
            if ($this->do_debug >= 1) {
306
                $this->displayErrors();
307
            }
308
        }
309
        if (empty($username)) {
310
            $username = $this->username;
311
        }
312
        if (empty($password)) {
313
            $password = $this->password;
314
        }
315
316
        // Send the Username
317
        $this->sendString("USER $username".self::CRLF);
318
        $pop3_response = $this->getResponse();
319
        if ($this->checkResponse($pop3_response)) {
320
            // Send the Password
321
            $this->sendString("PASS $password".self::CRLF);
322
            $pop3_response = $this->getResponse();
323
            if ($this->checkResponse($pop3_response)) {
324
                return true;
325
            }
326
        }
327
328
        return false;
329
    }
330
331
    /**
332
     * Disconnect from the POP3 server.
333
     */
334
    public function disconnect()
335
    {
336
        $this->sendString('QUIT');
337
        //The QUIT command may cause the daemon to exit, which will kill our connection
338
        //So ignore errors here
339
        @fclose($this->pop_conn);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
340
    }
341
342
    /**
343
     * Get a response from the POP3 server.
344
     * $size is the maximum number of bytes to retrieve.
345
     *
346
     * @param int $size
347
     *
348
     * @return string
349
     */
350
    private function getResponse($size = 128)
351
    {
352
        $r = fgets($this->pop_conn, $size);
353
        if ($this->do_debug >= 1) {
354
            echo "Server -> Client: $r";
355
        }
356
357
        return $r;
358
    }
359
360
    /**
361
     * Send raw data to the POP3 server.
362
     *
363
     * @param string $string
364
     *
365
     * @return int
366
     */
367
    private function sendString($string)
368
    {
369
        if ($this->pop_conn) {
370
            if ($this->do_debug >= 2) { //Show client messages when debug >= 2
371
                echo "Client -> Server: $string";
372
            }
373
374
            return fwrite($this->pop_conn, $string, strlen($string));
375
        }
376
377
        return 0;
378
    }
379
380
    /**
381
     * Checks the POP3 server response.
382
     * Looks for for +OK or -ERR.
383
     *
384
     * @param string $string
385
     *
386
     * @return bool
387
     */
388
    private function checkResponse($string)
389
    {
390
        if (substr($string, 0, 3) !== '+OK') {
391
            $this->error = [
392
                'error'  => "Server reported an error: $string",
393
                'errno'  => 0,
394
                'errstr' => '',
395
            ];
396
            if ($this->do_debug >= 1) {
397
                $this->displayErrors();
398
            }
399
400
            return false;
401
        } else {
402
            return true;
403
        }
404
    }
405
406
    /**
407
     * Display errors if debug is enabled.
408
     */
409
    private function displayErrors()
410
    {
411
        echo '<pre>';
412
        foreach ($this->error as $single_error) {
413
            print_r($single_error);
414
        }
415
        echo '</pre>';
416
    }
417
418
    /**
419
     * POP3 connection error handler.
420
     *
421
     * @param int    $errno
422
     * @param string $errstr
423
     * @param string $errfile
424
     * @param int    $errline
425
     */
426
    private function catchWarning($errno, $errstr, $errfile, $errline)
427
    {
428
        $this->error[] = [
429
            'error'   => 'Connecting to the POP3 server raised a PHP warning: ',
430
            'errno'   => $errno,
431
            'errstr'  => $errstr,
432
            'errfile' => $errfile,
433
            'errline' => $errline,
434
        ];
435
    }
436
}
437