blob: 902c7ceac0d8d475afef82fe3d1c6b45ca758a2e [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\CreateMailboxException;
8use Ddeboer\Imap\Exception\DeleteMailboxException;
9use Ddeboer\Imap\Exception\ImapGetmailboxesException;
10use Ddeboer\Imap\Exception\ImapNumMsgException;
11use Ddeboer\Imap\Exception\ImapQuotaException;
12use Ddeboer\Imap\Exception\InvalidResourceException;
13use Ddeboer\Imap\Exception\MailboxDoesNotExistException;
14
15/**
16 * A connection to an IMAP server that is authenticated for a user.
17 */
18final class Connection implements ConnectionInterface
19{
20 /**
21 * @var ImapResourceInterface
22 */
23 private $resource;
24
25 /**
26 * @var string
27 */
28 private $server;
29
30 /**
31 * @var null|array
32 */
33 private $mailboxes;
34
35 /**
36 * @var null|array
37 */
38 private $mailboxNames;
39
40 /**
41 * Constructor.
42 *
43 * @throws \InvalidArgumentException
44 */
45 public function __construct(ImapResourceInterface $resource, string $server)
46 {
47 $this->resource = $resource;
48 $this->server = $server;
49 }
50
51 /**
52 * Get IMAP resource.
53 */
54 public function getResource(): ImapResourceInterface
55 {
56 return $this->resource;
57 }
58
59 /**
60 * Delete all messages marked for deletion.
61 */
62 public function expunge(): bool
63 {
64 return \imap_expunge($this->resource->getStream());
65 }
66
67 /**
68 * Close connection.
69 */
70 public function close(int $flag = 0): bool
71 {
72 $this->resource->clearLastMailboxUsedCache();
73
74 return \imap_close($this->resource->getStream(), $flag);
75 }
76
77 /**
78 * Get Mailbox quota.
79 */
80 public function getQuota(string $root = 'INBOX'): array
81 {
82 $errorMessage = null;
83 $errorNumber = 0;
84 \set_error_handler(static function ($nr, $message) use (&$errorMessage, &$errorNumber): bool {
85 $errorMessage = $message;
86 $errorNumber = $nr;
87
88 return true;
89 });
90
91 $return = \imap_get_quotaroot($this->resource->getStream(), $root);
92
93 \restore_error_handler();
94
95 if (false === $return || null !== $errorMessage) {
96 throw new ImapQuotaException(
97 \sprintf(
98 'IMAP Quota request failed for "%s"%s',
99 $root,
100 null !== $errorMessage ? ': ' . $errorMessage : ''
101 ),
102 $errorNumber
103 );
104 }
105
106 return $return;
107 }
108
109 /**
110 * Get a list of mailboxes (also known as folders).
111 *
112 * @return MailboxInterface[]
113 */
114 public function getMailboxes(): array
115 {
116 $this->initMailboxNames();
117
118 if (null === $this->mailboxes) {
119 $this->mailboxes = [];
120 foreach ($this->mailboxNames as $mailboxName => $mailboxInfo) {
121 $this->mailboxes[(string) $mailboxName] = $this->getMailbox((string) $mailboxName);
122 }
123 }
124
125 return $this->mailboxes;
126 }
127
128 /**
129 * Check that a mailbox with the given name exists.
130 *
131 * @param string $name Mailbox name
132 */
133 public function hasMailbox(string $name): bool
134 {
135 $this->initMailboxNames();
136
137 return isset($this->mailboxNames[$name]);
138 }
139
140 /**
141 * Get a mailbox by its name.
142 *
143 * @param string $name Mailbox name
144 *
145 * @throws MailboxDoesNotExistException If mailbox does not exist
146 */
147 public function getMailbox(string $name): MailboxInterface
148 {
149 if (false === $this->hasMailbox($name)) {
150 throw new MailboxDoesNotExistException(\sprintf('Mailbox name "%s" does not exist', $name));
151 }
152
153 return new Mailbox($this->resource, $name, $this->mailboxNames[$name]);
154 }
155
156 /**
157 * Count number of messages not in any mailbox.
158 *
159 * @return int
160 */
161 public function count()
162 {
163 $return = \imap_num_msg($this->resource->getStream());
164
165 if (false === $return) {
166 throw new ImapNumMsgException('imap_num_msg failed');
167 }
168
169 return $return;
170 }
171
172 /**
173 * Check if the connection is still active.
174 *
175 * @throws InvalidResourceException If connection was closed
176 */
177 public function ping(): bool
178 {
179 return \imap_ping($this->resource->getStream());
180 }
181
182 /**
183 * Create mailbox.
184 *
185 * @throws CreateMailboxException
186 */
187 public function createMailbox(string $name): MailboxInterface
188 {
189 if (false === \imap_createmailbox($this->resource->getStream(), $this->server . \mb_convert_encoding($name, 'UTF7-IMAP', 'UTF-8'))) {
190 throw new CreateMailboxException(\sprintf('Can not create "%s" mailbox at "%s"', $name, $this->server));
191 }
192
193 $this->mailboxNames = $this->mailboxes = null;
194 $this->resource->clearLastMailboxUsedCache();
195
196 return $this->getMailbox($name);
197 }
198
199 /**
200 * Create mailbox.
201 *
202 * @throws DeleteMailboxException
203 */
204 public function deleteMailbox(MailboxInterface $mailbox): void
205 {
206 if (false === \imap_deletemailbox($this->resource->getStream(), $mailbox->getFullEncodedName())) {
207 throw new DeleteMailboxException(\sprintf('Mailbox "%s" could not be deleted', $mailbox->getName()));
208 }
209
210 $this->mailboxes = $this->mailboxNames = null;
211 $this->resource->clearLastMailboxUsedCache();
212 }
213
214 /**
215 * Get mailbox names.
216 */
217 private function initMailboxNames(): void
218 {
219 if (null !== $this->mailboxNames) {
220 return;
221 }
222
223 $this->mailboxNames = [];
224 $mailboxesInfo = \imap_getmailboxes($this->resource->getStream(), $this->server, '*');
225 if (!\is_array($mailboxesInfo)) {
226 throw new ImapGetmailboxesException('imap_getmailboxes failed');
227 }
228
229 foreach ($mailboxesInfo as $mailboxInfo) {
230 $name = \mb_convert_encoding(\str_replace($this->server, '', $mailboxInfo->name), 'UTF-8', 'UTF7-IMAP');
231 $this->mailboxNames[$name] = $mailboxInfo;
232 }
233 }
234}