1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PHPDaemon\Clients\IMAP; |
4
|
|
|
|
5
|
|
|
use PHPDaemon\Network\ClientConnection; |
6
|
|
|
use PHPDaemon\Core\Daemon; |
7
|
|
|
|
8
|
|
|
class Connection extends ClientConnection |
9
|
|
|
{ |
10
|
|
|
const STATE_CONNECTING = 0; |
11
|
|
|
const STATE_CONNECTED = 1; |
12
|
|
|
const STATE_CREDS_SENT = 2; |
13
|
|
|
const STATE_AUTHORIZED = 3; |
14
|
|
|
|
15
|
|
|
const FLAG_PASSED = '\Passed'; |
16
|
|
|
const FLAG_ANSWERED = '\Answered'; |
17
|
|
|
const FLAG_SEEN = '\Seen'; |
18
|
|
|
const FLAG_UNSEEN = '\Unseen'; |
19
|
|
|
const FLAG_DELETED = '\Deleted'; |
20
|
|
|
const FLAG_DRAFT = '\Draft'; |
21
|
|
|
const FLAG_FLAGGED = '\Flagged'; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* IMAP flags to search criteria |
25
|
|
|
* @var array |
26
|
|
|
*/ |
27
|
|
|
protected $searchFlags = [ |
28
|
|
|
'\Recent' => 'RECENT', |
29
|
|
|
'\Answered' => 'ANSWERED', |
30
|
|
|
'\Seen' => 'SEEN', |
31
|
|
|
'\Unseen' => 'UNSEEN', |
32
|
|
|
'\Deleted' => 'DELETED', |
33
|
|
|
'\Draft' => 'DRAFT', |
34
|
|
|
'\Flagged' => 'FLAGGED', |
35
|
|
|
]; |
36
|
|
|
|
37
|
|
|
const TAG_LOGIN = 'a01'; |
38
|
|
|
const TAG_LIST = 'a02'; |
39
|
|
|
const TAG_SELECT = 'a03'; |
40
|
|
|
const TAG_FETCH = 'a04'; |
41
|
|
|
const TAG_SEARCH = 'a05'; |
42
|
|
|
const TAG_COUNT = 'a06'; |
43
|
|
|
const TAG_SIZE = 'a07'; |
44
|
|
|
const TAG_GETRAWMESSAGE = 'a08'; |
45
|
|
|
const TAG_GETRAWHEADER = 'a09'; |
46
|
|
|
const TAG_GETRAWCONTENT = 'a10'; |
47
|
|
|
const TAG_GETUID = 'a11'; |
48
|
|
|
const TAG_CREATEFOLDER = 'a12'; |
49
|
|
|
const TAG_DELETEFOLDER = 'a13'; |
50
|
|
|
const TAG_RENAMEFOLDER = 'a14'; |
51
|
|
|
const TAG_STORE = 'a15'; |
52
|
|
|
const TAG_DELETEMESSAGE = 'a16'; |
53
|
|
|
const TAG_EXPUNGE = 'a17'; |
54
|
|
|
const TAG_LOGOUT = 'a18'; |
55
|
|
|
const TAG_STARTTLS = 'a19'; |
56
|
|
|
|
57
|
|
|
protected $EOL = "\r\n"; |
58
|
|
|
|
59
|
|
|
protected $state; |
60
|
|
|
protected $lines = []; |
61
|
|
|
protected $blob = ''; |
62
|
|
|
protected $blobOctetsLeft = 0; |
63
|
|
|
|
64
|
|
|
public function onReady() |
65
|
|
|
{ |
66
|
|
|
$this->state = self::STATE_CONNECTING; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* escape a single literal |
71
|
|
|
* @param $string |
72
|
|
|
* @return string escaped list for imap |
73
|
|
|
*/ |
74
|
|
|
protected function escapeString($string) |
75
|
|
|
{ |
76
|
|
|
return '"' . strtr($string, ['\\' => '\\\\', '"' => '\\"']) . '"'; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* escape a list with literals or lists |
81
|
|
|
* |
82
|
|
|
* @param array $list list with literals or lists as PHP array |
83
|
|
|
* @return string escaped list for imap |
84
|
|
|
*/ |
85
|
|
|
protected function escapeList($list) |
86
|
|
|
{ |
87
|
|
|
$result = []; |
88
|
|
|
foreach ($list as $v) { |
89
|
|
|
if (!is_array($v)) { |
90
|
|
|
$result[] = $v; |
91
|
|
|
continue; |
92
|
|
|
} |
93
|
|
|
$result[] = $this->escapeList($v); |
94
|
|
|
} |
95
|
|
|
return '(' . implode(' ', $result) . ')'; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* split a given line in tokens. a token is literal of any form or a list |
100
|
|
|
* |
101
|
|
|
* @param string $line line to decode |
102
|
|
|
* @return array tokens, literals are returned as string, lists as array |
103
|
|
|
*/ |
104
|
|
|
protected function decodeLine($line) |
105
|
|
|
{ |
106
|
|
|
$tokens = []; |
107
|
|
|
$stack = []; |
108
|
|
|
/* |
109
|
|
|
We start to decode the response here. The understood tokens are: |
110
|
|
|
literal |
111
|
|
|
"literal" or also "lit\\er\"al" |
112
|
|
|
(literals*) |
113
|
|
|
All tokens are returned in an array. Literals in braces (the last understood |
114
|
|
|
token in the list) are returned as an array of tokens. I.e. the following response: |
115
|
|
|
"foo" baz bar ("f\\\"oo" bar) |
116
|
|
|
would be returned as: |
117
|
|
|
array('foo', 'baz', 'bar', array('f\\\"oo', 'bar')); |
118
|
|
|
*/ |
119
|
|
|
// replace any trailing <NL> including spaces with a single space |
120
|
|
|
$line = rtrim($line) . ' '; |
121
|
|
|
while (($pos = strpos($line, ' ')) !== false) { |
122
|
|
|
$token = substr($line, 0, $pos); |
123
|
|
|
if (!strlen($token)) { |
124
|
|
|
continue; |
125
|
|
|
} |
126
|
|
|
while ($token[0] === '(') { |
127
|
|
|
array_push($stack, $tokens); |
128
|
|
|
$tokens = []; |
129
|
|
|
$token = substr($token, 1); |
130
|
|
|
} |
131
|
|
|
if ($token[0] === '"') { |
132
|
|
|
if (preg_match('%^\(*"((.|\\\\|\\")*?)" *%', $line, $matches)) { |
133
|
|
|
$tokens[] = $matches[1]; |
134
|
|
|
$line = substr($line, strlen($matches[0])); |
135
|
|
|
continue; |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
if ($stack && $token[strlen($token) - 1] === ')') { |
|
|
|
|
139
|
|
|
// closing braces are not separated by spaces, so we need to count them |
140
|
|
|
$braces = strlen($token); |
141
|
|
|
$token = rtrim($token, ')'); |
142
|
|
|
// only count braces if more than one |
143
|
|
|
$braces -= strlen($token) + 1; |
144
|
|
|
// only add if token had more than just closing braces |
145
|
|
|
if (rtrim($token) != '') { |
146
|
|
|
$tokens[] = rtrim($token); |
147
|
|
|
} |
148
|
|
|
$token = $tokens; |
149
|
|
|
$tokens = array_pop($stack); |
150
|
|
|
// special handline if more than one closing brace |
151
|
|
|
while ($braces-- > 0) { |
152
|
|
|
$tokens[] = $token; |
153
|
|
|
$token = $tokens; |
154
|
|
|
$tokens = array_pop($stack); |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
$tokens[] = $token; |
158
|
|
|
$line = substr($line, $pos + 1); |
159
|
|
|
} |
160
|
|
|
// maybe the server forgot to send some closing braces |
161
|
|
|
while ($stack) { |
|
|
|
|
162
|
|
|
$child = $tokens; |
163
|
|
|
$tokens = array_pop($stack); |
164
|
|
|
$tokens[] = $child; |
165
|
|
|
} |
166
|
|
|
return $tokens; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param array $items |
171
|
|
|
* @param string $from |
172
|
|
|
* @param string $to |
|
|
|
|
173
|
|
|
* @param bool $uid |
174
|
|
|
* @param string $tag |
175
|
|
|
*/ |
176
|
|
|
protected function fetch($items, $from, $to = null, $uid = false, $tag = self::TAG_FETCH) |
177
|
|
|
{ |
178
|
|
|
if (is_array($from)) { |
179
|
|
|
$set = implode(',', $from); |
180
|
|
|
} elseif ($to === null) { |
181
|
|
|
$set = (int) $from; |
182
|
|
|
} elseif ($to === INF) { |
183
|
|
|
$set = (int) $from . ':*'; |
184
|
|
|
} else { |
185
|
|
|
$set = (int) $from . ':' . (int) $to; |
186
|
|
|
} |
187
|
|
|
$this->writeln($tag .($uid ? ' UID' : '')." FETCH $set ". $this->escapeList((array)$items) ); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @param array $flags |
192
|
|
|
* @param string $from |
193
|
|
|
* @param string $to |
|
|
|
|
194
|
|
|
* @param string $mode (+/-) |
|
|
|
|
195
|
|
|
* @param bool $silent |
196
|
|
|
* @param string $tag |
197
|
|
|
*/ |
198
|
|
|
protected function store(array $flags, $from, $to = null, $mode = null, $silent = true, $tag = self::TAG_STORE) |
199
|
|
|
{ |
200
|
|
|
$item = 'FLAGS'; |
201
|
|
|
if ($mode == '+' || $mode == '-') { |
202
|
|
|
$item = $mode . $item; |
203
|
|
|
} |
204
|
|
|
if ($silent) { |
205
|
|
|
$item .= '.SILENT'; |
206
|
|
|
} |
207
|
|
|
$flags = $this->escapeList($flags); |
208
|
|
|
$set = (int)$from; |
209
|
|
|
if ($to !== null) { |
210
|
|
|
$set .= ':' . ($to == INF ? '*' : (int)$to); |
211
|
|
|
} |
212
|
|
|
$this->writeln($tag . ' UID STORE ' . $set . ' ' . $item . ' ' . $flags ); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* @param string $reference |
217
|
|
|
* @param string $mailbox |
218
|
|
|
*/ |
219
|
|
|
public function listFolders($cb, $reference = '', $mailbox = '*') |
220
|
|
|
{ |
221
|
|
|
$this->onResponse->push($cb); |
222
|
|
|
$this->writeln(self::TAG_LIST .' LIST ' . $this->escapeString($reference) |
223
|
|
|
. ' ' . $this->escapeString($mailbox) ); |
224
|
|
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* @param string $box |
228
|
|
|
*/ |
229
|
|
|
public function selectBox($cb, $box = 'INBOX') |
230
|
|
|
{ |
231
|
|
|
$this->onResponse->push($cb); |
232
|
|
|
$this->writeln(self::TAG_SELECT .' SELECT ' . $this->escapeString($box)); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* @param string $tag |
237
|
|
|
*/ |
238
|
|
|
protected function expunge($tag = self::TAG_EXPUNGE) |
239
|
|
|
{ |
240
|
|
|
$this->writeln($tag .' EXPUNGE'); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* @param string $haystack |
|
|
|
|
245
|
|
|
* @param string $needle |
|
|
|
|
246
|
|
|
*/ |
247
|
|
|
public function auth($cb, $login, $password) |
248
|
|
|
{ |
249
|
|
|
$this->onResponse->push($cb); |
250
|
|
|
$this->writeln(self::TAG_LOGIN . " LOGIN $login $password"); |
251
|
|
|
} |
252
|
|
|
|
253
|
|
|
/** |
254
|
|
|
* @param array $params |
255
|
|
|
* @param string $tag |
256
|
|
|
*/ |
257
|
|
|
protected function searchMessages($params, $tag = self::TAG_SEARCH) |
258
|
|
|
{ |
259
|
|
|
$this->writeln("$tag UID SEARCH ".implode(' ', $params)); |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* @param string $haystack |
264
|
|
|
* @param string $needle |
265
|
|
|
*/ |
266
|
|
|
protected function startsWith($haystack, $needle) |
267
|
|
|
{ |
268
|
|
|
// search backwards starting from haystack length characters from the end |
269
|
|
|
return $needle === '' || strrpos($haystack, $needle, -strlen($haystack)) !== false; |
270
|
|
|
} |
271
|
|
|
|
272
|
|
|
/** |
273
|
|
|
* @param array $lines |
274
|
|
|
*/ |
275
|
|
|
protected function decodeList($lines) |
276
|
|
|
{ |
277
|
|
|
$list = []; |
278
|
|
|
foreach (array_map([$this, 'decodeLine'], $lines) as $tokens) { |
279
|
|
|
$folderEntry = []; |
280
|
|
|
if (!isset($tokens[0]) || $tokens[0] !== 'LIST') { |
281
|
|
|
continue; |
282
|
|
|
} |
283
|
|
|
if (isset($tokens[3])) { |
284
|
|
|
$folderEntry['name'] = $tokens[3]; |
285
|
|
|
} else { |
286
|
|
|
continue; |
287
|
|
|
} |
288
|
|
|
if (isset($tokens[1])) { |
289
|
|
|
$folderEntry['flags'] = $tokens[1]; |
290
|
|
|
} else { |
291
|
|
|
continue; |
292
|
|
|
} |
293
|
|
|
$list[] = $folderEntry; |
294
|
|
|
} |
295
|
|
|
return $list; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
/** |
299
|
|
|
* @param array $lines |
300
|
|
|
*/ |
301
|
|
|
protected function decodeCount($lines) |
302
|
|
|
{ |
303
|
|
|
foreach (array_map([$this, 'decodeLine'], $lines) as $tokens) { |
304
|
|
|
if (!isset($tokens[0]) || $tokens[0] !== 'SEARCH') { |
305
|
|
|
continue; |
306
|
|
|
} |
307
|
|
|
return count($tokens) - 1; |
308
|
|
|
} |
309
|
|
|
return 0; |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
/** |
313
|
|
|
* @param array $lines |
314
|
|
|
*/ |
315
|
|
|
protected function decodeGetUniqueId($lines) |
316
|
|
|
{ |
317
|
|
|
$uids = []; |
318
|
|
|
foreach (array_map([$this, 'decodeLine'], $lines) as $tokens) { |
319
|
|
|
if (!isset($tokens[1]) || $tokens[1] !== 'FETCH') { |
320
|
|
|
continue; |
321
|
|
|
} |
322
|
|
View Code Duplication |
if (!isset($tokens[2][0]) || $tokens[2][0] !== 'UID') { |
|
|
|
|
323
|
|
|
continue; |
324
|
|
|
} |
325
|
|
|
if (!isset($tokens[0]) || !isset($tokens[2][1])) { |
326
|
|
|
continue; |
327
|
|
|
} |
328
|
|
|
$uids[$tokens[0]] = $tokens[2][1]; |
329
|
|
|
} |
330
|
|
|
return $uids; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
/* |
334
|
|
|
* @param array $lines |
335
|
|
|
*/ |
336
|
|
|
protected function decodeSize($lines) |
337
|
|
|
{ |
338
|
|
|
$sizes = []; |
339
|
|
|
foreach (array_map([$this, 'decodeLine'], $lines) as $tokens) { |
340
|
|
|
if (!isset($tokens[1]) || $tokens[1] !== 'FETCH') { |
341
|
|
|
continue; |
342
|
|
|
} |
343
|
|
View Code Duplication |
if (!isset($tokens[2][0]) || $tokens[2][0] !== 'UID') { |
|
|
|
|
344
|
|
|
continue; |
345
|
|
|
} |
346
|
|
View Code Duplication |
if (!isset($tokens[2][2]) || $tokens[2][2] !== 'RFC822.SIZE') { |
|
|
|
|
347
|
|
|
continue; |
348
|
|
|
} |
349
|
|
View Code Duplication |
if (!isset($tokens[2][1]) || !isset($tokens[2][3])) { |
|
|
|
|
350
|
|
|
continue; |
351
|
|
|
} |
352
|
|
|
$sizes[$tokens[2][1]] = $tokens[2][3]; |
353
|
|
|
} |
354
|
|
|
return $sizes; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
/** |
358
|
|
|
* |
359
|
|
|
* @param string $tag response tag |
360
|
|
|
* @param string $type OK, NO, BAD |
361
|
|
|
* @param string $line last response line |
362
|
|
|
* @param array $lines full response |
363
|
|
|
* @param string $blob |
364
|
|
|
*/ |
365
|
|
|
protected function onCommand($tag, $type, $line, $lines, $blob) |
366
|
|
|
{ |
367
|
|
|
$ok = $type === 'OK'; |
368
|
|
|
$no = $type === 'NO'; |
369
|
|
|
if ($type === 'BAD') { |
370
|
|
|
$this->log("Server said: " . $line); |
371
|
|
|
} |
372
|
|
|
$raw = ['lines' => $lines, 'blob' => $blob]; |
373
|
|
|
switch ($tag) { |
374
|
|
|
case self::TAG_LOGIN: |
375
|
|
|
if ($ok) { |
376
|
|
|
$this->state = self::STATE_AUTHORIZED; |
377
|
|
|
$this->onResponse->executeOne($this, $line); |
378
|
|
|
} elseif ($no) { |
379
|
|
|
$this->log("Failed to login: " . $line); |
380
|
|
|
$this->finish(); |
381
|
|
|
} |
382
|
|
|
break; |
383
|
|
|
|
384
|
|
|
case self::TAG_LIST: |
385
|
|
|
$this->onResponse->executeOne($this, $ok, $this->decodeList($lines)); |
386
|
|
|
break; |
387
|
|
|
|
388
|
|
|
case self::TAG_GETUID: |
389
|
|
|
$this->onResponse->executeOne($this, $ok, $this->decodeGetUniqueId($lines)); |
390
|
|
|
break; |
391
|
|
|
|
392
|
|
|
case self::TAG_COUNT: |
393
|
|
|
$this->onResponse->executeOne($this, $ok, $this->decodeCount($lines)); |
394
|
|
|
break; |
395
|
|
|
|
396
|
|
|
case self::TAG_SIZE: |
397
|
|
|
$this->onResponse->executeOne($this, $ok, $this->decodeSize($lines)); |
398
|
|
|
break; |
399
|
|
|
|
400
|
|
|
case self::TAG_DELETEMESSAGE: |
401
|
|
|
$this->expunge(); |
402
|
|
|
break; |
403
|
|
|
|
404
|
|
|
case self::TAG_EXPUNGE: |
405
|
|
|
$this->onResponse->executeOne($this, count($lines) - 1, $raw); |
406
|
|
|
break; |
407
|
|
|
|
408
|
|
|
case self::TAG_GETRAWMESSAGE: |
409
|
|
|
$this->onResponse->executeOne($this, !empty($blob), $raw); |
410
|
|
|
break; |
411
|
|
|
|
412
|
|
|
case self::TAG_GETRAWHEADER: |
413
|
|
|
$this->onResponse->executeOne($this, !empty($blob), $raw); |
414
|
|
|
break; |
415
|
|
|
|
416
|
|
|
case self::TAG_GETRAWCONTENT: |
417
|
|
|
$this->onResponse->executeOne($this, !empty($blob), $raw); |
418
|
|
|
break; |
419
|
|
|
|
420
|
|
|
default: |
421
|
|
|
$this->onResponse->executeOne($this, $ok, $raw); |
422
|
|
|
break; |
423
|
|
|
} |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
public function onRead() |
427
|
|
|
{ |
428
|
|
|
while (($rawLine = $this->readLine(\EventBuffer::EOL_CRLF_STRICT)) !== null) { |
429
|
|
|
if ($this->blobOctetsLeft > 0) { |
430
|
|
|
$this->blob .= $rawLine . "\r\n"; |
431
|
|
|
$this->blobOctetsLeft -= strlen($rawLine) + 2; |
432
|
|
|
continue; |
433
|
|
|
} |
434
|
|
|
if (preg_match('~\{([0-9]+)\}$~', $rawLine, $matches)) { |
435
|
|
|
$this->blob = ''; |
436
|
|
|
$this->blobOctetsLeft = $matches[1]; |
|
|
|
|
437
|
|
|
} |
438
|
|
|
@list($tag, $line) = @explode(' ', $rawLine, 2); |
|
|
|
|
439
|
|
|
@list($type, $restLine) = @explode(' ', $line, 2); |
|
|
|
|
440
|
|
|
|
441
|
|
|
if ($this->state == self::STATE_CONNECTING) { |
442
|
|
|
if ($this->startsWith($rawLine, '* OK')) { |
443
|
|
|
$this->state = self::STATE_CONNECTED; |
444
|
|
|
$this->connected = true; |
445
|
|
|
} else { |
446
|
|
|
$this->log("IMAP hello failed"); |
447
|
|
|
$this->finish(); |
448
|
|
|
return; |
449
|
|
|
} |
450
|
|
|
if ($this->onConnected) { |
451
|
|
|
$this->onConnected->executeAll($this->connected ? $this : false); |
452
|
|
|
$this->onConnected = null; |
453
|
|
|
} |
454
|
|
|
return; |
455
|
|
|
} elseif ($this->state != self::STATE_CONNECTING) { |
456
|
|
|
if ($tag === '*') { |
457
|
|
|
$this->lines[] = $line; |
458
|
|
|
continue; |
459
|
|
|
} |
460
|
|
|
if (!in_array($type, ['OK', 'BAD', 'NO'])) { |
461
|
|
|
$this->lines[] = $rawLine; |
462
|
|
|
continue; |
463
|
|
|
} |
464
|
|
|
$this->lines[] = $line; |
465
|
|
|
$this->onCommand($tag, $type, $line, $this->lines, $this->blob); |
466
|
|
|
$this->lines = []; |
467
|
|
|
} |
468
|
|
|
} |
469
|
|
|
} |
470
|
|
|
|
471
|
|
|
/** |
472
|
|
|
* Count messages all messages in current box |
473
|
|
|
* @param null $flags |
474
|
|
|
*/ |
475
|
|
|
public function countMessages($cb, $flags = null) |
476
|
|
|
{ |
477
|
|
|
$this->onResponse->push($cb); |
478
|
|
|
if ($flags === null) { |
479
|
|
|
$this->searchMessages(['ALL'], self::TAG_COUNT); |
480
|
|
|
return; |
481
|
|
|
} |
482
|
|
|
$params = []; |
483
|
|
|
foreach ((array) $flags as $flag) { |
484
|
|
|
if (isset($this->searchFlags[$flag])) { |
485
|
|
|
$params[] = $this->searchFlags[$flag]; |
486
|
|
|
} else { |
487
|
|
|
$params[] = 'KEYWORD'; |
488
|
|
|
$params[] = $this->escapeString($flag); |
489
|
|
|
} |
490
|
|
|
} |
491
|
|
|
$this->searchMessages($params, self::TAG_COUNT); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* get a list of messages with number and size |
496
|
|
|
* @param int $uid number of message |
|
|
|
|
497
|
|
|
*/ |
498
|
|
View Code Duplication |
public function getSize($cb, $uid = null) |
|
|
|
|
499
|
|
|
{ |
500
|
|
|
$this->onResponse->push($cb); |
501
|
|
|
if ($uid) { |
|
|
|
|
502
|
|
|
$this->fetch('RFC822.SIZE', $uid, null, true, self::TAG_SIZE); |
|
|
|
|
503
|
|
|
} else { |
504
|
|
|
$this->fetch('RFC822.SIZE', 1, INF, true, self::TAG_SIZE); |
|
|
|
|
505
|
|
|
} |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Fetch a message |
510
|
|
|
* @param int $uid unique number of message |
511
|
|
|
*/ |
512
|
|
|
public function getRawMessage($cb, $uid, $byUid = true) |
513
|
|
|
{ |
514
|
|
|
$this->onResponse->push($cb); |
515
|
|
|
$this->fetch(['FLAGS', 'BODY[]'], $uid, null, $byUid, self::TAG_GETRAWMESSAGE); |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/* |
519
|
|
|
* Get raw header of message or part |
520
|
|
|
* @param int $uid unique number of message |
521
|
|
|
*/ |
522
|
|
|
public function getRawHeader($cb, $uid, $byUid = true) |
523
|
|
|
{ |
524
|
|
|
$this->onResponse->push($cb); |
525
|
|
|
$this->fetch(['FLAGS', 'RFC822.HEADER'], $uid, null, $byUid, self::TAG_GETRAWHEADER); |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/* |
529
|
|
|
* Get raw content of message or part |
530
|
|
|
* |
531
|
|
|
* @param int $uid number of message |
532
|
|
|
*/ |
533
|
|
|
public function getRawContent($cb, $uid, $byUid = true) |
534
|
|
|
{ |
535
|
|
|
$this->onResponse->push($cb); |
536
|
|
|
$this->fetch(['FLAGS', 'RFC822.TEXT'], $uid, null, $byUid, self::TAG_GETRAWCONTENT); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* get unique id for one or all messages |
541
|
|
|
* |
542
|
|
|
* @param int|null $id message number |
543
|
|
|
*/ |
544
|
|
View Code Duplication |
public function getUniqueId($cb, $id = null) |
|
|
|
|
545
|
|
|
{ |
546
|
|
|
$this->onResponse->push($cb); |
547
|
|
|
if ($id) { |
|
|
|
|
548
|
|
|
$this->fetch('UID', $id, null, false, self::TAG_GETUID); |
|
|
|
|
549
|
|
|
} else { |
550
|
|
|
$this->fetch('UID', 1, INF, false, self::TAG_GETUID); |
|
|
|
|
551
|
|
|
} |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
/** |
555
|
|
|
* create a new folder (and parent folders if needed) |
556
|
|
|
* |
557
|
|
|
* @param string $folder folder name |
558
|
|
|
* @return bool success |
|
|
|
|
559
|
|
|
*/ |
560
|
|
|
public function createFolder($cb, $folder, $parentFolder = null) |
561
|
|
|
{ |
562
|
|
|
$this->onResponse->push($cb); |
563
|
|
|
if ($parentFolder) { |
564
|
|
|
$folder = $parentFolder . '/' . $folder ; |
565
|
|
|
} |
566
|
|
|
$this->writeln(self::TAG_CREATEFOLDER . " CREATE ".$this->escapeString($folder)); |
567
|
|
|
} |
568
|
|
|
|
569
|
|
|
/** |
570
|
|
|
* remove a folder |
571
|
|
|
* |
572
|
|
|
* @param string $name name or instance of folder |
|
|
|
|
573
|
|
|
*/ |
574
|
|
|
public function removeFolder($cb, $folder) |
575
|
|
|
{ |
576
|
|
|
$this->onResponse->push($cb); |
577
|
|
|
$this->writeln(self::TAG_DELETEFOLDER . " DELETE ".$this->escapeString($folder)); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* rename and/or move folder |
582
|
|
|
* @param string $oldName name or instance of folder |
583
|
|
|
* @param string $newName new global name of folder |
584
|
|
|
*/ |
585
|
|
|
public function renameFolder($cb, $oldName, $newName) |
586
|
|
|
{ |
587
|
|
|
$this->onResponse->push($cb); |
588
|
|
|
$this->writeln(self::TAG_RENAMEFOLDER . " RENAME " |
589
|
|
|
.$this->escapeString($oldName)." ".$this->escapeString($newName)); |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
/** |
593
|
|
|
* Remove a message from server. |
594
|
|
|
* @param int $uid unique number of message |
595
|
|
|
*/ |
596
|
|
|
public function removeMessage($cb, $uid) |
597
|
|
|
{ |
598
|
|
|
$this->onResponse->push($cb); |
599
|
|
|
$this->store([self::FLAG_DELETED], $uid, null, '+', true, self::TAG_DELETEMESSAGE); |
600
|
|
|
} |
601
|
|
|
|
602
|
|
|
/** |
603
|
|
|
* logout of imap server |
604
|
|
|
*/ |
605
|
|
|
public function logout($cb = null) |
606
|
|
|
{ |
607
|
|
|
if ($cb) { |
608
|
|
|
$this->onResponse->push($cb); |
609
|
|
|
} |
610
|
|
|
$this->writeln(self::TAG_LOGOUT . " LOGOUT"); |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
public function onFinish() |
614
|
|
|
{ |
615
|
|
|
$this->onResponse->executeAll(false); |
616
|
|
|
parent::onFinish(); |
617
|
|
|
} |
618
|
|
|
} |
619
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.