list mails, read mails
[cacert-mgr.git] / manager / library / imap / imapConnection.php
1 <?php
2 /**
3 * @author markus
4 * $Id: $
5 */
6
7 /**
8 * required files
9 * @ignore
10 */
11 require_once('exception.IMAPException.php');
12
13 define('IMAP_RETRIES', 5);
14
15 /**
16 * Wraps PHP built in imap commands within a class, features open, hold, close connection and
17 * issue imap commands.
18 *
19 * @author markus
20 */
21 class imapConnection {
22
23 /**
24 * Array mit den bereits vorhandenen Instanzen
25 * @var array
26 */
27 private static $instances = array();
28
29 /**
30 * Instanzname, die unterschiedlichen Entitäten werden über den Namen
31 * auseinandergehalten.
32 * @var string
33 */
34 private $instanceName = '';
35
36 /**
37 * Configsection
38 * @var Config
39 */
40 private $config = null;
41
42 /**
43 * IMAP Resource
44 * @var imap_stream
45 */
46 private $imap = null;
47
48 /**
49 * Servername, Port, Flags
50 * @var string
51 */
52 private $server = '';
53
54 /**
55 * Name der zuletzt geöffneten Mailbox
56 * @var string
57 */
58 private $mbox = '';
59
60 /**
61 * wird auf true gesetzt, wenn imapPing die Connection neu aufbaut
62 * @var boolean
63 */
64 private $reopenedConnection = false;
65
66 /**
67 * liefert eine Liste der verfügbaren Folder
68 * @param string $pattern
69 * @return array
70 */
71 public function imapList($pattern = '*') {
72 if ($this->imap === null) {
73 throw new IMAPException(__METHOD__ . ' not connected');
74 }
75 $this->imapPing(true);
76
77 return imap_list($this->imap, $this->server, $pattern);
78 }
79
80
81 /**
82 * Checkt die Anzahl Messages in einer Mailbox
83 * return array
84 */
85 public function imapCheck() {
86 if ($this->imap === null) {
87 throw new IMAPException(__METHOD__ . ' not connected');
88 }
89 $this->imapPing(true);
90
91 return imap_check($this->imap);
92 }
93
94
95 /**
96 * per imap_reopen die aktuelle Connection auf eine andere mbox umstellen
97 * @param string $mbox
98 * @return boolean
99 */
100 public function imapSwitchMbox($mbox) {
101 if ($this->imap === null) {
102 throw new IMAPException(__METHOD__ . ' not connected');
103 }
104 $this->imapPing(true);
105
106 if (imap_reopen($this->imap, $this->server.$mbox) === false) {
107 throw new IMAPException(__METHOD__ . ' reopen failed');
108 }
109
110 $this->mbox = $mbox;
111
112 return true;
113 }
114
115
116 /**
117 * setzt ein Flag bei allen in $sequence aufgeführten Messages
118 * @param string $sequence
119 * @param string $flag
120 * @param integer $options
121 * @return boolean
122 */
123 public function imapSetflagFull($sequence, $flag, $options = 0) {
124 if ($this->imap === null) {
125 throw new IMAPException(__METHOD__ . ' not connected');
126 }
127 $this->imapPing(true);
128
129 return imap_setflag_full($this->imap, $sequence, $flag, $options);
130 }
131
132
133 /**
134 * liefert die Mailheader
135 * @return array
136 */
137 public function imapHeaders() {
138 if ($this->imap === null) {
139 throw new IMAPException(__METHOD__ . ' not connected');
140 }
141 $this->imapPing(true);
142
143 return imap_headers($this->imap);
144 }
145
146 /**
147 * liefert die Header zu genau einer Mail mit der gegebenen ID
148 * @param integer $number
149 * @return array
150 */
151 public function imapHeader($number) {
152 if ($this->imap === null) {
153 throw new IMAPException(__METHOD__ . ' not connected');
154 }
155 $this->imapPing(true);
156
157 return imap_headerinfo($this->imap, $number);
158 }
159
160 /**
161 * liefert die Header zu genau einer Mail mit der gegebenen UID
162 * @param integer $uid
163 * @return array
164 */
165 public function imapFetchHeader($uid) {
166 if ($this->imap === null) {
167 throw new IMAPException(__METHOD__ . ' not connected');
168 }
169
170 $ret = imap_fetchheader($this->imap, $uid, FT_UID);
171
172 return $ret;
173 }
174
175 /**
176 * liefert die Header zu genau einer Mail mit der gegebenen UID
177 * @param integer $uid
178 * @return array
179 */
180 public function imapFetchOverview($uid) {
181 if ($this->imap === null) {
182 throw new IMAPException(__METHOD__ . ' not connected');
183 }
184
185 $ret = imap_fetch_overview($this->imap, $uid, FT_UID);
186
187 return $ret[0];
188 }
189
190 /**
191 * liefert den Body zu genau einer Mail mit der gegebenen ID
192 * @param integer $number
193 * @return string
194 */
195 public function imapBody($number) {
196 if ($this->imap === null) {
197 throw new IMAPException(__METHOD__ . ' not connected');
198 }
199 $this->imapPing(true);
200
201 return imap_body($this->imap, $number);
202 }
203
204 /**
205 * liefert den Body zu genau einer Mail mit der gegebenen UID
206 * @param integer $uid
207 * @return string
208 */
209 public function imapBodyByUID($uid) {
210 if ($this->imap === null) {
211 throw new IMAPException(__METHOD__ . ' not connected');
212 }
213 $this->imapPing(true);
214
215 return imap_body($this->imap, $uid, FT_UID );
216 }
217
218 /**
219 * markiert die Nachricht mit der unique ID zum löschen
220 * @param integer $uid
221 * return boolean
222 */
223 public function imapDelete($uid) {
224 if ($this->imap === null) {
225 throw new IMAPException(__METHOD__ . ' not connected');
226 }
227 $this->imapPing(true);
228
229 $ret = imap_delete($this->imap, $uid, FT_UID);
230
231 if ($ret !== true) {
232 print "imap delete returned false for ".$uid."\n";
233 }
234
235 return $ret;
236 }
237
238 /**
239 * löscht alle zum löschen markierten Nachrichten
240 * @return boolean
241 */
242 public function imapExpunge() {
243 if ($this->imap === null) {
244 throw new IMAPException(__METHOD__ . ' not connected');
245 }
246 $this->imapPing(true);
247
248 return imap_expunge($this->imap);
249 }
250
251 /**
252 * kopiert die Nachricht mit der gegebenen uid in die gegebene Mailbox *auf dem selben Server*
253 * @param integer $uid
254 * @param string $dest_mbox
255 * @return boolean
256 */
257 public function imapMailCopy($uid, $dest_mbox) {
258 if ($this->imap === null) {
259 throw new IMAPException(__METHOD__ . ' not connected');
260 }
261 $this->imapPing(true);
262
263 return imap_mail_copy($this->imap, $uid, $dest_mbox, CP_UID);
264 }
265
266 /**
267 * verschiebt die Nachricht mit der gegebenen uid in die gegebene Mailbox *auf dem selben Server*
268 * @param integer $uid
269 * @param string $dest_mbox
270 * @return boolean
271 */
272 public function imapMailMove($uid, $dest_mbox) {
273 if ($this->imap === null) {
274 throw new IMAPException(__METHOD__ . ' not connected');
275 }
276 $this->imapPing(true);
277 /*
278 * dont't add the server part,
279 * only the mailbox name works fine
280 *
281 * $dest_mbox = $this->server.$dest_mbox;
282 */
283 return imap_mail_move( $this->imap, $uid, $dest_mbox, CP_UID);
284 }
285
286 /**
287 * legt eine neue Mailbox *auf dem selben Server* an
288 * @param string $mbox
289 * @return boolean
290 */
291 public function imapCreateMailbox($mbox) {
292 if ($this->imap === null) {
293 throw new IMAPException(__METHOD__ . ' not connected');
294 }
295 $this->imapPing(true);
296
297 return imap_createmailbox($this->imap, $this->server.$mbox);
298 }
299
300 /**
301 * fragt ab, ob eine mbox unterhalb von mbox_root existiert und liefert true zurück, falls ja
302 * Funktion existiert nicht direkt als IMAP Kommando, aus einzelnen Kommando's zusammengebaut
303 *
304 * @param string $mbox_root
305 * @param string $mbox
306 * @return boolean
307 */
308 public function imapMailboxExists($mbox_root, $mbox) {
309 if ($this->imap === null) {
310 throw new IMAPException(__METHOD__ . ' not connected');
311 }
312 $this->imapPing(true);
313
314 $folderlist = $this->imapList($mbox_root);
315 $foundFolder = false;
316 foreach ($folderlist as $folder) {
317 if (strpos($folder, $mbox) !== false) {
318 return true;
319 }
320 }
321
322 return false;
323 }
324
325 const AR_YYYY = 'Y';
326 const AR_YYYYMM = 'Ym';
327 const AR_YYYYMMDD = 'Ymd';
328
329 /**
330 * erzeugt eine Archivmailbox zur Mailbox $mbox, dabei wird das Archiv unterhalb von $mbox
331 * auf dem selben Server angelegt, der Name der Mailbox enthält je nach $mode noch einen Datumsstamp
332 * Wenn der Input ($mbox) bereits mehrere Ebenen enthält (NOC3.domain.incoming z.B.), dann
333 * wird automatisch nur der am weitesten rechts stehende Teil extrahiert und benutzt.
334 * NOC3.domain.incoming => NOC3.domain.incoming.incoming-200705
335 * @param string $mbox
336 * @param string $mode
337 * @param integer $timestamp
338 * @param string $delimiter
339 * @return string
340 */
341 public static function imapMakeArchiveName($mbox, $mode, $timestamp = null, $delimiter = '-') {
342 if ($timestamp === null)
343 $timestamp = time();
344
345 $ar = explode('.', $mbox);
346
347 $sub_mbox = $ar[count($ar) - 1];
348
349 return $mbox.'.'.$sub_mbox.$delimiter.date($mode,$timestamp);
350 }
351
352 public static function imapMakePrefixedArchiveName($mbox, $mode, $prefix = '', $timestamp = null, $delimiter = '-') {
353 if ($timestamp === null)
354 $timestamp = time();
355
356 $ar = explode('.', $mbox);
357
358 $sub_mbox = $ar[count($ar) - 1];
359
360 return $mbox.'.'.$prefix.$delimiter.$sub_mbox.$delimiter.date($mode,$timestamp);
361 }
362
363 /**
364 * liefert die unique ID der Nachricht mit der laufenden msg_number
365 * @param integer $msg_number
366 * @return integer
367 */
368 public function imapUID($msg_number) {
369 if ($this->imap === null) {
370 throw new IMAPException(__METHOD__ . ' not connected');
371 }
372 $this->imapPing(true);
373
374 return imap_uid($this->imap, $msg_number);
375 }
376
377
378 /**
379 * liefert die laufende msg_number der Nachricht, die die unique ID uid hat
380 * @param integer $uid
381 * @return integer
382 */
383 public function imapMsgNo($uid) {
384 if ($this->imap === null) {
385 throw new IMAPException(__METHOD__ . ' not connected');
386 }
387 $this->imapPing(true);
388
389 return imap_msgno($this->imap, $uid);
390 }
391
392
393 /**
394 * prüft, ob die Connection noch aktiv ist, Exception falls keine Connection definiert ist,
395 * oder die Connection geschlossen wurde
396 * wenn reconnect = true, dann wird bei einer geschlossenen Connection die Connection neu aufgebaut
397 * @param boolean $reconnect
398 * @return boolean
399 */
400 public function imapPing($reconnect = false) {
401 if ($this->imap === null) {
402 throw new IMAPException(__METHOD__ . ' not connected');
403 }
404
405 $ret = imap_ping($this->imap);
406
407 if ($ret === false) {
408 if ($reconnect === true) {
409 $this->imap = $this->imapOpen($this->server.$this->mbox,
410 $this->config->username,
411 $this->config->password,
412 OP_HALFOPEN);
413
414 $ret = imap_ping($this->imap);
415
416 if ($ret === false) {
417 throw new IMAPException(__METHOD__ . ' reconnect failed');
418 }
419
420 $this->reopenedConnection = true;
421 }
422 else {
423 throw new IMAPException(__METHOD__ . ' not connected');
424 }
425 }
426
427 return true;
428 }
429
430
431 public function __destruct() {
432 if ($this->imap !== null) {
433 imap_close($this->imap);
434 $this->imap = null;
435 }
436 }
437
438
439 /**
440 * true, wenn imapPing die Connection neu aufgemacht hat
441 * Variable wird auf false gesetzt wenn $flush true ist
442 * @param boolean $flush
443 * @return boolean
444 */
445 public function connectionReopened($flush = true) {
446 $ret = $this->reopenedConnection;
447 if ($flush === true) {
448 $this->reopenedConnection = false;
449 }
450 return $ret;
451 }
452
453
454 /**
455 * interne IMAP Open Methode
456 *
457 * @param string $servername
458 * @param string $username
459 * @param string password
460 * @param integer $flags
461 * @return resource
462 */
463 protected function imapOpen($server, $username, $password, $flags) {
464 return imap_open($server, $username, $password, $flags);
465 }
466
467
468 /**
469 * privater Konstruktor, wird exklusiv von getInstance aufgerufen
470 *
471 * @param $instanceName
472 * @param $config
473 */
474 protected function __construct($instanceName, $config) {
475 $this->instanceName = $instanceName;
476 $this->config = $config;
477
478 if (!isset($this->config->mailhost)) {
479 throw new IMAPException(__METHOD__ . ' config attribute missing: "mailhost"');
480 }
481 if (!isset($this->config->username)) {
482 throw new IMAPException(__METHOD__ . ' config attribute missing: "username"');
483 }
484 if (!isset($this->config->password)) {
485 throw new IMAPException(__METHOD__ . ' config attribute missing: "password"');
486 }
487 if (!isset($this->config->port)) {
488 throw new IMAPException(__METHOD__ . ' config attribute missing: "port"');
489 }
490
491 $this->server = '{'.$this->config->mailhost.':'.$this->config->port.'/imap';
492 if( isset($this->config->use_tls) &&
493 $this->config->use_tls != 0 ) {
494 $this->server .= '/tls';
495 }
496 $this->server .= '/novalidate-cert}';
497
498 $mbox = '';
499
500 $this->mbox = $mbox;
501
502 $this->imap = null;
503
504 $this->imap = $this->imapOpen($this->server.$mbox,
505 $this->config->username,
506 $this->config->password,
507 OP_HALFOPEN);
508
509 if ($this->imap === false) {
510 $this->imap = null;
511 throw new IMAPException(__METHOD__ . ' not connected');
512 }
513
514 $this->reopenedConnection = false;
515 }
516
517
518 /**
519 * sucht nach einer bereits vorhandenen Instanz, wird keine gefunden,
520 * dann wird eine neue Instanz angelegt.
521 * Man kann die Config-Variable weglassen, wenn man sicher ist, dass
522 * bereits eine Instanz mit dem gewünschten instanceName existiert,
523 * existiert aber keiner, dann liefert getInstance eine Exception.
524 *
525 * @param $instance
526 * @param $config
527 * @return imapConnection
528 */
529 public static function getInstance($instanceName,$config = null) {
530 if (!self::$instances)
531 self::$instances = array();
532
533 foreach (self::$instances as $instance) {
534 if ($instance->getInstanceName() == $instanceName)
535 return $instance;
536 }
537
538 /*
539 if (!$config instanceof Config) {
540 throw new IMAPException(__METHOD__ . ' no config');
541 }
542 */
543
544 $object = new imapConnection($instanceName, $config);
545
546 self::$instances[] = $object;
547
548 return $object;
549 }
550
551
552 /**
553 * Liefert den Namen der aktuellen Instanz
554 * @return string
555 */
556 public function getInstanceName() {
557 return $this->instanceName;
558 }
559
560
561 }