Total Complexity | 152 |
Total Lines | 1104 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Net_POP3 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Net_POP3, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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); |
||
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)) { |
||
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)) { |
||
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))) { |
||
241 | return $ret; |
||
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'))) { |
||
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"); |
||
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))) { |
||
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; |
||
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))) { |
||
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))) { |
||
422 | return $ret; |
||
423 | } |
||
424 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
425 | return $challenge; |
||
426 | } |
||
427 | if (PEAR::isError($ret = $this->_checkResponse($challenge))) { |
||
428 | return $ret; |
||
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())) { |
||
450 | return $challenge; |
||
451 | } |
||
452 | if (PEAR::isError($ret = $this->_checkResponse($challenge))) { |
||
453 | return $ret; |
||
454 | } |
||
455 | |||
456 | if (PEAR::isError($ret = $this->_send(sprintf('"%s"', base64_encode($user))))) { |
||
457 | return $ret; |
||
458 | } |
||
459 | |||
460 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
461 | return $challenge; |
||
462 | } |
||
463 | if (PEAR::isError($ret = $this->_checkResponse($challenge))) { |
||
464 | return $ret; |
||
465 | } |
||
466 | |||
467 | if (PEAR::isError($ret = $this->_send(sprintf('"%s"', base64_encode($pass))))) { |
||
468 | return $ret; |
||
469 | } |
||
470 | |||
471 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
472 | return $challenge; |
||
473 | } |
||
474 | |||
475 | return $this->_checkResponse($ret); |
||
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'))) { |
||
492 | return $ret; |
||
493 | } |
||
494 | |||
495 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
496 | return $challenge; |
||
497 | } |
||
498 | if (PEAR::isError($ret = $this->_checkResponse($challenge))) { |
||
499 | return $ret; |
||
500 | } |
||
501 | |||
502 | // remove '+ ' |
||
503 | |||
504 | $challenge = mb_substr($challenge, 2); |
||
505 | |||
506 | $challenge = base64_decode($challenge, true); |
||
507 | |||
508 | $cram = &Auth_SASL::factory('crammd5'); |
||
509 | $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge)); |
||
510 | |||
511 | if (PEAR::isError($error = $this->_send($auth_str))) { |
||
512 | return $error; |
||
513 | } |
||
514 | if (PEAR::isError($ret = $this->_recvLn())) { |
||
515 | return $ret; |
||
516 | } |
||
517 | //echo "RET:$ret\n"; |
||
518 | return $this->_checkResponse($ret); |
||
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'))) { |
||
536 | return $ret; |
||
537 | } |
||
538 | |||
539 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
540 | return $challenge; |
||
541 | } |
||
542 | if (PEAR::isError($ret = $this->_checkResponse($challenge))) { |
||
543 | return $ret; |
||
544 | } |
||
545 | |||
546 | // remove '+ ' |
||
547 | $challenge = mb_substr($challenge, 2); |
||
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; |
||
555 | } |
||
556 | |||
557 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
558 | return $challenge; |
||
559 | } |
||
560 | if (PEAR::isError($ret = $this->_checkResponse($challenge))) { |
||
561 | return $ret; |
||
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; |
||
570 | } |
||
571 | |||
572 | if (PEAR::isError($challenge = $this->_recvLn())) { |
||
573 | return $challenge; |
||
574 | } |
||
575 | |||
576 | return $this->_checkResponse($challenge); |
||
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)))) { |
||
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)); |
||
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; |
||
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); |
||
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 |
||
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'))) { |
||
862 | sscanf($data, '+OK %d %d', $msg_num, $size); |
||
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 |
||
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))) { |
||
890 | sscanf($data, '+OK %d %d', $msg_id, $size); |
||
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; |
||
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))) { |
||
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'))) { |
||
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'))) { |
||
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) |
||
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); |
||
1027 | |||
1028 | return ['msg_id' => $msg_id, 'uidl' => $uidl]; |
||
1029 | } else { |
||
1030 | if (!PEAR::isError($data = $this->_sendCmd('UIDL'))) { |
||
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; |
||
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) |
||
1066 | } |
||
1067 | |||
1068 | /** |
||
1069 | * Reads a multiline reponse and returns the data |
||
1070 | * |
||
1071 | * @return string The reponse. |
||
1072 | */ |
||
1073 | public function _getMultiline(): string |
||
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))) { |
||
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))) { |
||
1131 | return $this->_raiseError('Failed to write to socket: ' . $this->lastline->getMessage()); |
||
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) |
||
1162 | } |
||
1163 | } |
||
1164 |