blob: 7412f00471ff687a3dbbeb41a75161b4ca92c226 [file] [log] [blame]
Matthias Andreas Benkardb382b102021-01-02 15:32:21 +01001<?php
2
3declare(strict_types=1);
4
5namespace Ddeboer\Imap;
6
7use Ddeboer\Imap\Exception\AuthenticationFailedException;
8use Ddeboer\Imap\Exception\ResourceCheckFailureException;
9
10/**
11 * An IMAP server.
12 */
13final class Server implements ServerInterface
14{
15 /**
16 * @var string Internet domain name or bracketed IP address of server
17 */
18 private $hostname;
19
20 /**
21 * @var string TCP port number
22 */
23 private $port;
24
25 /**
26 * @var string Optional flags
27 */
28 private $flags;
29
30 /**
31 * @var array
32 */
33 private $parameters;
34
35 /**
36 * @var int Connection options
37 */
38 private $options;
39
40 /**
41 * @var int Retries number
42 */
43 private $retries;
44
45 /**
46 * Constructor.
47 *
48 * @param string $hostname Internet domain name or bracketed IP address
49 * of server
50 * @param string $port TCP port number
51 * @param string $flags Optional flags
52 * @param array $parameters Connection parameters
53 * @param int $options Connection options
54 * @param int $retries Retries number
55 */
56 public function __construct(
57 string $hostname,
58 string $port = '993',
59 string $flags = '/imap/ssl/validate-cert',
60 array $parameters = [],
61 int $options = 0,
62 int $retries = 1
63 ) {
64 if (!\function_exists('imap_open')) {
65 throw new \RuntimeException('IMAP extension must be enabled');
66 }
67
68 $this->hostname = $hostname;
69 $this->port = $port;
70 $this->flags = '' !== $flags ? '/' . \ltrim($flags, '/') : '';
71 $this->parameters = $parameters;
72 $this->options = $options;
73 $this->retries = $retries;
74 }
75
76 /**
77 * Authenticate connection.
78 *
79 * @param string $username Username
80 * @param string $password Password
81 *
82 * @throws AuthenticationFailedException
83 */
84 public function authenticate(string $username, string $password): ConnectionInterface
85 {
86 $errorMessage = null;
87 $errorNumber = 0;
88 \set_error_handler(static function ($nr, $message) use (&$errorMessage, &$errorNumber): bool {
89 $errorMessage = $message;
90 $errorNumber = $nr;
91
92 return true;
93 });
94
95 $resource = \imap_open(
96 $this->getServerString(),
97 $username,
98 $password,
99 $this->options,
100 $this->retries,
101 $this->parameters
102 );
103
104 \restore_error_handler();
105
106 if (false === $resource || null !== $errorMessage) {
107 throw new AuthenticationFailedException(\sprintf(
108 'Authentication failed for user "%s"%s',
109 $username,
110 null !== $errorMessage ? ': ' . $errorMessage : ''
111 ), $errorNumber);
112 }
113
114 $check = \imap_check($resource);
115
116 if (false === $check) {
117 throw new ResourceCheckFailureException('Resource check failure');
118 }
119
120 $mailbox = $check->Mailbox;
121 $connection = $mailbox;
122 $curlyPosition = \strpos($mailbox, '}');
123 if (false !== $curlyPosition) {
124 $connection = \substr($mailbox, 0, $curlyPosition + 1);
125 }
126
127 // These are necessary to get rid of PHP throwing IMAP errors
128 \imap_errors();
129 \imap_alerts();
130
131 return new Connection(new ImapResource($resource), $connection);
132 }
133
134 /**
135 * Glues hostname, port and flags and returns result.
136 */
137 private function getServerString(): string
138 {
139 return \sprintf(
140 '{%s%s%s}',
141 $this->hostname,
142 '' !== $this->port ? ':' . $this->port : '',
143 $this->flags
144 );
145 }
146}