Passed
Push — master ( 79149c...dd37f9 )
by Richard
05:12 queued 11s
created

POP3   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 375
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 31
eloc 110
dl 0
loc 375
rs 9.92
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A login() 0 24 6
A authorise() 0 32 5
A catchWarning() 0 8 1
A getResponse() 0 7 2
A checkResponse() 0 11 2
A popBeforeSmtp() 0 10 1
A connect() 0 49 5
A sendString() 0 9 3
A disconnect() 0 8 2
A getErrors() 0 3 1
A setError() 0 9 3
1
<?php
2
/**
3
 * PHPMailer POP-Before-SMTP Authentication Class.
4
 * PHP Version 5
5
 * @package PHPMailer
6
 * @link https://github.com/PHPMailer/PHPMailer/
7
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
8
 * @author Jim Jagielski (jimjag) <[email protected]>
9
 * @author Andy Prevost (codeworxtech) <[email protected]>
10
 * @author Brent R. Matzelle (original founder)
11
 * @copyright 2012 - 2014 Marcus Bointon
12
 * @copyright 2010 - 2012 Jim Jagielski
13
 * @copyright 2004 - 2009 Andy Prevost
14
 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
15
 * @note This program is distributed in the hope that it will be useful - WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
 * FITNESS FOR A PARTICULAR PURPOSE.
18
 */
19
20
/**
21
 * PHPMailer POP-Before-SMTP Authentication Class.
22
 * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
23
 * Does not support APOP.
24
 * @package PHPMailer
25
 * @author Richard Davey (original author) <[email protected]>
26
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
27
 * @author Jim Jagielski (jimjag) <[email protected]>
28
 * @author Andy Prevost (codeworxtech) <[email protected]>
29
 */
30
class POP3
31
{
32
    /**
33
     * The POP3 PHPMailer Version number.
34
     * @var string
35
     * @access public
36
     */
37
    public $Version = '5.2.27';
38
39
    /**
40
     * Default POP3 port number.
41
     * @var integer
42
     * @access public
43
     */
44
    public $POP3_PORT = 110;
45
46
    /**
47
     * Default timeout in seconds.
48
     * @var integer
49
     * @access public
50
     */
51
    public $POP3_TIMEOUT = 30;
52
53
    /**
54
     * POP3 Carriage Return + Line Feed.
55
     * @var string
56
     * @access public
57
     * @deprecated Use the constant instead
58
     */
59
    public $CRLF = "\r\n";
60
61
    /**
62
     * Debug display level.
63
     * Options: 0 = no, 1+ = yes
64
     * @var integer
65
     * @access public
66
     */
67
    public $do_debug = 0;
68
69
    /**
70
     * POP3 mail server hostname.
71
     * @var string
72
     * @access public
73
     */
74
    public $host;
75
76
    /**
77
     * POP3 port number.
78
     * @var integer
79
     * @access public
80
     */
81
    public $port;
82
83
    /**
84
     * POP3 Timeout Value in seconds.
85
     * @var integer
86
     * @access public
87
     */
88
    public $tval;
89
90
    /**
91
     * POP3 username
92
     * @var string
93
     * @access public
94
     */
95
    public $username;
96
97
    /**
98
     * POP3 password.
99
     * @var string
100
     * @access public
101
     */
102
    public $password;
103
104
    /**
105
     * Resource handle for the POP3 connection socket.
106
     * @var resource
107
     * @access protected
108
     */
109
    protected $pop_conn;
110
111
    /**
112
     * Are we connected?
113
     * @var boolean
114
     * @access protected
115
     */
116
    protected $connected = false;
117
118
    /**
119
     * Error container.
120
     * @var array
121
     * @access protected
122
     */
123
    protected $errors = array();
124
125
    /**
126
     * Line break constant
127
     */
128
    const CRLF = "\r\n";
129
130
    /**
131
     * Simple static wrapper for all-in-one POP before SMTP
132
     * @param $host
133
     * @param integer|boolean $port The port number to connect to
134
     * @param integer|boolean $timeout The timeout value
135
     * @param string $username
136
     * @param string $password
137
     * @param integer $debug_level
138
     * @return boolean
139
     */
140
    public static function popBeforeSmtp(
141
        $host,
142
        $port = false,
143
        $timeout = false,
144
        $username = '',
145
        $password = '',
146
        $debug_level = 0
147
    ) {
148
        $pop = new POP3;
149
        return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
150
    }
151
152
    /**
153
     * Authenticate with a POP3 server.
154
     * A connect, login, disconnect sequence
155
     * appropriate for POP-before SMTP authorisation.
156
     * @access public
157
     * @param string $host The hostname to connect to
158
     * @param integer|boolean $port The port number to connect to
159
     * @param integer|boolean $timeout The timeout value
160
     * @param string $username
161
     * @param string $password
162
     * @param integer $debug_level
163
     * @return boolean
164
     */
165
    public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
166
    {
167
        $this->host = $host;
168
        // If no port value provided, use default
169
        if (false === $port) {
170
            $this->port = $this->POP3_PORT;
171
        } else {
172
            $this->port = (integer)$port;
173
        }
174
        // If no timeout value provided, use default
175
        if (false === $timeout) {
176
            $this->tval = $this->POP3_TIMEOUT;
177
        } else {
178
            $this->tval = (integer)$timeout;
179
        }
180
        $this->do_debug = $debug_level;
181
        $this->username = $username;
182
        $this->password = $password;
183
        //  Reset the error log
184
        $this->errors = array();
185
        //  connect
186
        $result = $this->connect($this->host, $this->port, $this->tval);
187
        if ($result) {
188
            $login_result = $this->login($this->username, $this->password);
189
            if ($login_result) {
190
                $this->disconnect();
191
                return true;
192
            }
193
        }
194
        // We need to disconnect regardless of whether the login succeeded
195
        $this->disconnect();
196
        return false;
197
    }
198
199
    /**
200
     * Connect to a POP3 server.
201
     * @access public
202
     * @param string $host
203
     * @param integer|boolean $port
204
     * @param integer $tval
205
     * @return boolean
206
     */
207
    public function connect($host, $port = false, $tval = 30)
208
    {
209
        //  Are we already connected?
210
        if ($this->connected) {
211
            return true;
212
        }
213
214
        //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
215
        //Rather than suppress it with @fsockopen, capture it cleanly instead
216
        set_error_handler(array($this, 'catchWarning'));
217
218
        if (false === $port) {
219
            $port = $this->POP3_PORT;
220
        }
221
222
        //  connect to the POP3 server
223
        $this->pop_conn = fsockopen(
0 ignored issues
show
Documentation Bug introduced by
It seems like fsockopen($host, $port, $errno, $errstr, $tval) can also be of type false. However, the property $pop_conn is declared as type resource. 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...
224
            $host, //  POP3 Host
225
            $port, //  Port #
0 ignored issues
show
Bug introduced by
It seems like $port can also be of type boolean; however, parameter $port of fsockopen() does only seem to accept integer, 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

225
            /** @scrutinizer ignore-type */ $port, //  Port #
Loading history...
226
            $errno, //  Error Number
227
            $errstr, //  Error Message
228
            $tval
229
        ); //  Timeout (seconds)
230
        //  Restore the error handler
231
        restore_error_handler();
232
233
        //  Did we connect?
234
        if (false === $this->pop_conn) {
235
            //  It would appear not...
236
            $this->setError(array(
237
                'error' => "Failed to connect to server $host on port $port",
238
                'errno' => $errno,
239
                'errstr' => $errstr
240
            ));
241
            return false;
242
        }
243
244
        //  Increase the stream time-out
245
        stream_set_timeout($this->pop_conn, $tval, 0);
246
247
        //  Get the POP3 server response
248
        $pop3_response = $this->getResponse();
249
        //  Check for the +OK
250
        if ($this->checkResponse($pop3_response)) {
251
            //  The connection is established and the POP3 server is talking
252
            $this->connected = true;
253
            return true;
254
        }
255
        return false;
256
    }
257
258
    /**
259
     * Log in to the POP3 server.
260
     * Does not support APOP (RFC 2828, 4949).
261
     * @access public
262
     * @param string $username
263
     * @param string $password
264
     * @return boolean
265
     */
266
    public function login($username = '', $password = '')
267
    {
268
        if (!$this->connected) {
269
            $this->setError('Not connected to POP3 server');
270
        }
271
        if (empty($username)) {
272
            $username = $this->username;
273
        }
274
        if (empty($password)) {
275
            $password = $this->password;
276
        }
277
278
        // Send the Username
279
        $this->sendString("USER $username" . self::CRLF);
280
        $pop3_response = $this->getResponse();
281
        if ($this->checkResponse($pop3_response)) {
282
            // Send the Password
283
            $this->sendString("PASS $password" . self::CRLF);
284
            $pop3_response = $this->getResponse();
285
            if ($this->checkResponse($pop3_response)) {
286
                return true;
287
            }
288
        }
289
        return false;
290
    }
291
292
    /**
293
     * Disconnect from the POP3 server.
294
     * @access public
295
     */
296
    public function disconnect()
297
    {
298
        $this->sendString('QUIT');
299
        //The QUIT command may cause the daemon to exit, which will kill our connection
300
        //So ignore errors here
301
        try {
302
            @fclose($this->pop_conn);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for fclose(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

302
            /** @scrutinizer ignore-unhandled */ @fclose($this->pop_conn);

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...
303
        } catch (Exception $e) {
304
            //Do nothing
305
        };
306
    }
307
308
    /**
309
     * Get a response from the POP3 server.
310
     * $size is the maximum number of bytes to retrieve
311
     * @param integer $size
312
     * @return string
313
     * @access protected
314
     */
315
    protected function getResponse($size = 128)
316
    {
317
        $response = fgets($this->pop_conn, $size);
318
        if ($this->do_debug >= 1) {
319
            echo "Server -> Client: $response";
320
        }
321
        return $response;
322
    }
323
324
    /**
325
     * Send raw data to the POP3 server.
326
     * @param string $string
327
     * @return integer
328
     * @access protected
329
     */
330
    protected function sendString($string)
331
    {
332
        if ($this->pop_conn) {
333
            if ($this->do_debug >= 2) { //Show client messages when debug >= 2
334
                echo "Client -> Server: $string";
335
            }
336
            return fwrite($this->pop_conn, $string, strlen($string));
337
        }
338
        return 0;
339
    }
340
341
    /**
342
     * Checks the POP3 server response.
343
     * Looks for for +OK or -ERR.
344
     * @param string $string
345
     * @return boolean
346
     * @access protected
347
     */
348
    protected function checkResponse($string)
349
    {
350
        if (substr($string, 0, 3) !== '+OK') {
351
            $this->setError(array(
352
                'error' => "Server reported an error: $string",
353
                'errno' => 0,
354
                'errstr' => ''
355
            ));
356
            return false;
357
        } else {
358
            return true;
359
        }
360
    }
361
362
    /**
363
     * Add an error to the internal error store.
364
     * Also display debug output if it's enabled.
365
     * @param $error
366
     * @access protected
367
     */
368
    protected function setError($error)
369
    {
370
        $this->errors[] = $error;
371
        if ($this->do_debug >= 1) {
372
            echo '<pre>';
373
            foreach ($this->errors as $error) {
0 ignored issues
show
introduced by
$error is overwriting one of the parameters of this function.
Loading history...
374
                print_r($error);
375
            }
376
            echo '</pre>';
377
        }
378
    }
379
380
    /**
381
     * Get an array of error messages, if any.
382
     * @return array
383
     */
384
    public function getErrors()
385
    {
386
        return $this->errors;
387
    }
388
389
    /**
390
     * POP3 connection error handler.
391
     * @param integer $errno
392
     * @param string $errstr
393
     * @param string $errfile
394
     * @param integer $errline
395
     * @access protected
396
     */
397
    protected function catchWarning($errno, $errstr, $errfile, $errline)
398
    {
399
        $this->setError(array(
400
            'error' => "Connecting to the POP3 server raised a PHP warning: ",
401
            'errno' => $errno,
402
            'errstr' => $errstr,
403
            'errfile' => $errfile,
404
            'errline' => $errline
405
        ));
406
    }
407
}
408