Passed
Push — main ( 73c905...3e2603 )
by Rafael N.
02:56 queued 41s
created

POP3::sendString()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
/**
4
 * PHPMailer POP-Before-SMTP Authentication Class.
5
 * PHP Version 5.5.
6
 *
7
 * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
8
 *
9
 * @author    Marcus Bointon (Synchro/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 2012 - 2020 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
namespace PHPMailer\PHPMailer;
23
24
/**
25
 * PHPMailer POP-Before-SMTP Authentication Class.
26
 * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication.
27
 * 1) This class does not support APOP authentication.
28
 * 2) Opening and closing lots of POP3 connections can be quite slow. If you need
29
 *   to send a batch of emails then just perform the authentication once at the start,
30
 *   and then loop through your mail sending script. Providing this process doesn't
31
 *   take longer than the verification period lasts on your POP3 server, you should be fine.
32
 * 3) This is really ancient technology; you should only need to use it to talk to very old systems.
33
 * 4) This POP3 class is deliberately lightweight and incomplete, implementing just
34
 *   enough to do authentication.
35
 *   If you want a more complete class there are other POP3 classes for PHP available.
36
 *
37
 * @author Richard Davey (original author) <[email protected]>
38
 * @author Marcus Bointon (Synchro/coolbru) <[email protected]>
39
 * @author Jim Jagielski (jimjag) <[email protected]>
40
 * @author Andy Prevost (codeworxtech) <[email protected]>
41
 */
42
class POP3
43
{
44
    /**
45
     * The POP3 PHPMailer Version number.
46
     *
47
     * @var string
48
     */
49
    const VERSION = '6.4.0';
50
51
    /**
52
     * Default POP3 port number.
53
     *
54
     * @var int
55
     */
56
    const DEFAULT_PORT = 110;
57
58
    /**
59
     * Default timeout in seconds.
60
     *
61
     * @var int
62
     */
63
    const DEFAULT_TIMEOUT = 30;
64
65
    /**
66
     * POP3 class debug output mode.
67
     * Debug output level.
68
     * Options:
69
     * @see POP3::DEBUG_OFF: No output
70
     * @see POP3::DEBUG_SERVER: Server messages, connection/server errors
71
     * @see POP3::DEBUG_CLIENT: Client and Server messages, connection/server errors
72
     *
73
     * @var int
74
     */
75
    public $do_debug = self::DEBUG_OFF;
76
77
    /**
78
     * POP3 mail server hostname.
79
     *
80
     * @var string
81
     */
82
    public $host;
83
84
    /**
85
     * POP3 port number.
86
     *
87
     * @var int
88
     */
89
    public $port;
90
91
    /**
92
     * POP3 Timeout Value in seconds.
93
     *
94
     * @var int
95
     */
96
    public $tval;
97
98
    /**
99
     * POP3 username.
100
     *
101
     * @var string
102
     */
103
    public $username;
104
105
    /**
106
     * POP3 password.
107
     *
108
     * @var string
109
     */
110
    public $password;
111
112
    /**
113
     * Resource handle for the POP3 connection socket.
114
     *
115
     * @var resource
116
     */
117
    protected $pop_conn;
118
119
    /**
120
     * Are we connected?
121
     *
122
     * @var bool
123
     */
124
    protected $connected = false;
125
126
    /**
127
     * Error container.
128
     *
129
     * @var array
130
     */
131
    protected $errors = [];
132
133
    /**
134
     * Line break constant.
135
     */
136
    const LE = "\r\n";
137
138
    /**
139
     * Debug level for no output.
140
     *
141
     * @var int
142
     */
143
    const DEBUG_OFF = 0;
144
145
    /**
146
     * Debug level to show server -> client messages
147
     * also shows clients connection errors or errors from server
148
     *
149
     * @var int
150
     */
151
    const DEBUG_SERVER = 1;
152
153
    /**
154
     * Debug level to show client -> server and server -> client messages.
155
     *
156
     * @var int
157
     */
158
    const DEBUG_CLIENT = 2;
159
160
    /**
161
     * Simple static wrapper for all-in-one POP before SMTP.
162
     *
163
     * @param string   $host        The hostname to connect to
164
     * @param int|bool $port        The port number to connect to
165
     * @param int|bool $timeout     The timeout value
166
     * @param string   $username
167
     * @param string   $password
168
     * @param int      $debug_level
169
     *
170
     * @return bool
171
     */
172
    public static function popBeforeSmtp(
173
        $host,
174
        $port = false,
175
        $timeout = false,
176
        $username = '',
177
        $password = '',
178
        $debug_level = 0
179
    ) {
180
        $pop = new self();
181
182
        return $pop->authorise($host, $port, $timeout, $username, $password, $debug_level);
183
    }
184
185
    /**
186
     * Authenticate with a POP3 server.
187
     * A connect, login, disconnect sequence
188
     * appropriate for POP-before SMTP authorisation.
189
     *
190
     * @param string   $host        The hostname to connect to
191
     * @param int|bool $port        The port number to connect to
192
     * @param int|bool $timeout     The timeout value
193
     * @param string   $username
194
     * @param string   $password
195
     * @param int      $debug_level
196
     *
197
     * @return bool
198
     */
199
    public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0)
200
    {
201
        $this->host = $host;
202
        //If no port value provided, use default
203
        if (false === $port) {
204
            $this->port = static::DEFAULT_PORT;
205
        } else {
206
            $this->port = (int) $port;
207
        }
208
        //If no timeout value provided, use default
209
        if (false === $timeout) {
210
            $this->tval = static::DEFAULT_TIMEOUT;
211
        } else {
212
            $this->tval = (int) $timeout;
213
        }
214
        $this->do_debug = $debug_level;
215
        $this->username = $username;
216
        $this->password = $password;
217
        //Reset the error log
218
        $this->errors = [];
219
        //Connect
220
        $result = $this->connect($this->host, $this->port, $this->tval);
221
        if ($result) {
222
            $login_result = $this->login($this->username, $this->password);
223
            if ($login_result) {
224
                $this->disconnect();
225
226
                return true;
227
            }
228
        }
229
        //We need to disconnect regardless of whether the login succeeded
230
        $this->disconnect();
231
232
        return false;
233
    }
234
235
    /**
236
     * Connect to a POP3 server.
237
     *
238
     * @param string   $host
239
     * @param int|bool $port
240
     * @param int      $tval
241
     *
242
     * @return bool
243
     */
244
    public function connect($host, $port = false, $tval = 30)
245
    {
246
        //Are we already connected?
247
        if ($this->connected) {
248
            return true;
249
        }
250
251
        //On Windows this will raise a PHP Warning error if the hostname doesn't exist.
252
        //Rather than suppress it with @fsockopen, capture it cleanly instead
253
        set_error_handler([$this, 'catchWarning']);
254
255
        if (false === $port) {
256
            $port = static::DEFAULT_PORT;
257
        }
258
259
        //Connect to the POP3 server
260
        $errno = 0;
261
        $errstr = '';
262
        $this->pop_conn = fsockopen(
263
            $host, //POP3 Host
264
            $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

264
            /** @scrutinizer ignore-type */ $port, //Port #
Loading history...
265
            $errno, //Error Number
266
            $errstr, //Error Message
267
            $tval
268
        ); //Timeout (seconds)
269
        //Restore the error handler
270
        restore_error_handler();
271
272
        //Did we connect?
273
        if (false === $this->pop_conn) {
274
            //It would appear not...
275
            $this->setError(
276
                "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr"
277
            );
278
279
            return false;
280
        }
281
282
        //Increase the stream time-out
283
        stream_set_timeout($this->pop_conn, $tval, 0);
284
285
        //Get the POP3 server response
286
        $pop3_response = $this->getResponse();
287
        //Check for the +OK
288
        if ($this->checkResponse($pop3_response)) {
289
            //The connection is established and the POP3 server is talking
290
            $this->connected = true;
291
292
            return true;
293
        }
294
295
        return false;
296
    }
297
298
    /**
299
     * Log in to the POP3 server.
300
     * Does not support APOP (RFC 2828, 4949).
301
     *
302
     * @param string $username
303
     * @param string $password
304
     *
305
     * @return bool
306
     */
307
    public function login($username = '', $password = '')
308
    {
309
        if (!$this->connected) {
310
            $this->setError('Not connected to POP3 server');
311
        }
312
        if (empty($username)) {
313
            $username = $this->username;
314
        }
315
        if (empty($password)) {
316
            $password = $this->password;
317
        }
318
319
        //Send the Username
320
        $this->sendString("USER $username" . static::LE);
321
        $pop3_response = $this->getResponse();
322
        if ($this->checkResponse($pop3_response)) {
323
            //Send the Password
324
            $this->sendString("PASS $password" . static::LE);
325
            $pop3_response = $this->getResponse();
326
            if ($this->checkResponse($pop3_response)) {
327
                return true;
328
            }
329
        }
330
331
        return false;
332
    }
333
334
    /**
335
     * Disconnect from the POP3 server.
336
     */
337
    public function disconnect()
338
    {
339
        $this->sendString('QUIT');
340
        //The QUIT command may cause the daemon to exit, which will kill our connection
341
        //So ignore errors here
342
        try {
343
            @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

343
            /** @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...
344
        } catch (Exception $e) {
345
            //Do nothing
346
        }
347
    }
348
349
    /**
350
     * Get a response from the POP3 server.
351
     *
352
     * @param int $size The maximum number of bytes to retrieve
353
     *
354
     * @return string
355
     */
356
    protected function getResponse($size = 128)
357
    {
358
        $response = fgets($this->pop_conn, $size);
359
        if ($this->do_debug >= self::DEBUG_SERVER) {
360
            echo 'Server -> Client: ', $response;
361
        }
362
363
        return $response;
364
    }
365
366
    /**
367
     * Send raw data to the POP3 server.
368
     *
369
     * @param string $string
370
     *
371
     * @return int
372
     */
373
    protected function sendString($string)
374
    {
375
        if ($this->pop_conn) {
376
            if ($this->do_debug >= self::DEBUG_CLIENT) { //Show client messages when debug >= 2
377
                echo 'Client -> Server: ', $string;
378
            }
379
380
            return fwrite($this->pop_conn, $string, strlen($string));
381
        }
382
383
        return 0;
384
    }
385
386
    /**
387
     * Checks the POP3 server response.
388
     * Looks for for +OK or -ERR.
389
     *
390
     * @param string $string
391
     *
392
     * @return bool
393
     */
394
    protected function checkResponse($string)
395
    {
396
        if (strpos($string, '+OK') !== 0) {
397
            $this->setError("Server reported an error: $string");
398
399
            return false;
400
        }
401
402
        return true;
403
    }
404
405
    /**
406
     * Add an error to the internal error store.
407
     * Also display debug output if it's enabled.
408
     *
409
     * @param string $error
410
     */
411
    protected function setError($error)
412
    {
413
        $this->errors[] = $error;
414
        if ($this->do_debug >= self::DEBUG_SERVER) {
415
            echo '<pre>';
416
            foreach ($this->errors as $e) {
417
                print_r($e);
418
            }
419
            echo '</pre>';
420
        }
421
    }
422
423
    /**
424
     * Get an array of error messages, if any.
425
     *
426
     * @return array
427
     */
428
    public function getErrors()
429
    {
430
        return $this->errors;
431
    }
432
433
    /**
434
     * POP3 connection error handler.
435
     *
436
     * @param int    $errno
437
     * @param string $errstr
438
     * @param string $errfile
439
     * @param int    $errline
440
     */
441
    protected function catchWarning($errno, $errstr, $errfile, $errline)
442
    {
443
        $this->setError(
444
            'Connecting to the POP3 server raised a PHP warning:' .
445
            "errno: $errno errstr: $errstr; errfile: $errfile; errline: $errline"
446
        );
447
    }
448
}
449