I believe everything is compatable with all IMAP servers now
[squirrelmail.git] / functions / mailbox.php
1 <?
2 /**
3 ** mailbox.php
4 **
5 ** This contains functions that request information about a mailbox. Including
6 ** reading and parsing headers, getting folder information, etc.
7 **
8 **/
9
10 function selectMailbox($imapConnection, $mailbox, &$numberOfMessages) {
11 // select mailbox
12 fputs($imapConnection, "mailboxSelect SELECT \"$mailbox\"\n");
13 $data = imapReadData($imapConnection, "mailboxSelect");
14 for ($i = 0; $i < count($data); $i++) {
15 if (substr(Chop($data[$i]), -6) == "EXISTS") {
16 $array = explode(" ", $data[$i]);
17 $numberOfMessages = $array[1];
18 }
19 }
20 }
21
22 function unseenMessages($imapConnection, &$numUnseen) {
23 fputs($imapConnection, "1 SEARCH UNSEEN NOT DELETED\n");
24 $read = fgets($imapConnection, 1024);
25 $unseen = false;
26
27 if (strlen($read) > 10) {
28 $unseen = true;
29 $ary = explode(" ", $read);
30 $numUnseen = count($ary) - 2;
31 }
32 else {
33 $unseen = false;
34 $numUnseen = 0;
35 }
36
37 $read = fgets($imapConnection, 1024);
38 return $unseen;
39 }
40
41 /** This function sends a request to the IMAP server for headers, 50 at a time
42 ** until $end is reached. I originally had it do them all at one time, but found
43 ** it slightly faster to do it this way.
44 **
45 ** Originally we had getMessageHeaders get the headers for one message at a time.
46 ** Doing it in bunches gave us a speed increase from 9 seconds (for a box of 800
47 ** messages) to about 3.5 seconds.
48 **/
49 function getMessageHeaders($imapConnection, $start, $end, &$from, &$subject, &$date) {
50 $rel_start = $start;
51 if (($start > $end) || ($start < 1)) {
52 echo "Error in message header fetching. Start message: $start, End message: $end<BR>";
53 exit;
54 }
55
56 $pos = 0;
57 while ($rel_start <= $end) {
58 if ($end - $rel_start > 50) {
59 $rel_end = $rel_start + 49;
60 } else {
61 $rel_end = $end;
62 }
63 fputs($imapConnection, "messageFetch FETCH $rel_start:$rel_end RFC822.HEADER.LINES (From Subject Date To Cc)\n");
64 $read = fgets($imapConnection, 1024);
65
66 while ((substr($read, 0, 15) != "messageFetch OK") && (substr($read, 0, 16) != "messageFetch BAD")) {
67
68 if (substr($read, 0, 5) == "From:") {
69 $read = ereg_replace("<", "EMAILSTART--", $read);
70 $read = ereg_replace(">", "--EMAILEND", $read);
71 $from[$pos] = substr($read, 5, strlen($read) - 6);
72 }
73 else if (substr($read, 0, 5) == "Date:") {
74 $read = ereg_replace("<", "&lt;", $read);
75 $read = ereg_replace(">", "&gt;", $read);
76 $date[$pos] = substr($read, 5, strlen($read) - 6);
77 }
78 else if (substr($read, 0, 8) == "Subject:") {
79 $read = ereg_replace("<", "&lt;", $read);
80 $read = ereg_replace(">", "&gt;", $read);
81 $subject[$pos] = substr($read, 8, strlen($read) - 9);
82 if (strlen(Chop($subject[$pos])) == 0)
83 $subject[$pos] = "(no subject)";
84 }
85 else if (substr($read, 0, 1) == ")") {
86 if ($subject[$pos] == "")
87 $subject[$pos] = "(no subject)";
88 else if ($from[$pos] == "")
89 $from[$pos] = "(unknown sender)";
90 else if ($date[$pos] == "")
91 $from[$pos] = gettimeofday();
92
93 $pos++;
94 }
95
96 $read = fgets($imapConnection, 1024);
97 }
98 $rel_start = $rel_start + 50;
99 }
100 }
101
102 function setMessageFlag($imapConnection, $i, $q, $flag) {
103 fputs($imapConnection, "messageStore STORE $i:$q +FLAGS (\\$flag)\n");
104 }
105
106 /** This function gets the flags for message $j. It does only one message at a
107 ** time, rather than doing groups of messages (like getMessageHeaders does).
108 ** I found it one or two seconds quicker (on a box of 800 messages) to do it
109 ** individually. I'm not sure why it happens like that, but that's what my
110 ** testing found. Perhaps later I will be proven wrong and this will change.
111 **/
112 function getMessageFlags($imapConnection, $j, &$flags) {
113 /** * 2 FETCH (FLAGS (\Answered \Seen)) */
114 fputs($imapConnection, "messageFetch FETCH $j:$j FLAGS\n");
115 $read = fgets($imapConnection, 1024);
116 $count = 0;
117 while ((substr($read, 0, 15) != "messageFetch OK") && (substr($read, 0, 16) != "messageFetch BAD")) {
118 if (strpos($read, "FLAGS")) {
119 $read = ereg_replace("\(", "", $read);
120 $read = ereg_replace("\)", "", $read);
121 $read = substr($read, strpos($read, "FLAGS")+6, strlen($read));
122 $read = trim($read);
123 $flags = explode(" ", $read);;
124 $s = 0;
125 while ($s < count($flags)) {
126 $flags[$s] = substr($flags[$s], 1, strlen($flags[$s]));
127 $s++;
128 }
129 } else {
130 $flags[0] = "None";
131 }
132 $count++;
133 $read = fgets($imapConnection, 1024);
134 }
135 }
136
137 function decodeEmailAddr($sender) {
138 $emailAddr = getEmailAddr($sender);
139 if (strpos($emailAddr, "EMAILSTART--")) {
140
141 $emailAddr = ereg_replace("EMAILSTART--", "", $emailAddr);
142 $emailAddr = ereg_replace("--EMAILEND", "", $emailAddr);
143 } else {
144 $emailAddr = $emailAddr;
145 }
146 return $emailAddr;
147 }
148
149 function getEmailAddr($sender) {
150 if (strpos($sender, "EMAILSTART--") == false)
151 return "$sender";
152
153 $emailStart = strpos($sender, "EMAILSTART--") + 12;
154 $emailAddr = substr($sender, $emailStart, strlen($sender));
155 $emailAddr = substr($emailAddr, 0, strpos($emailAddr, "--EMAILEND"));
156
157 return $emailAddr;
158 }
159
160 function getSender($sender) {
161 if (strpos($sender, "EMAILSTART--") == false)
162 return "$sender";
163
164 $first = substr($sender, 0, strpos($sender, "EMAILSTART--"));
165 $second = substr($sender, strpos($sender, "--EMAILEND") +10, strlen($sender));
166 return "$first $second";
167 }
168
169 function getSenderName($sender) {
170 $name = getSender($sender);
171 $emailAddr = getEmailAddr($sender);
172 $emailStart = strpos($emailAddr, "EMAILSTART--");
173 $emailEnd = strpos($emailAddr, "--EMAILEND") - 10;
174
175 if (($emailAddr == "") && ($name == "")) {
176 $from = $sender;
177 }
178 else if ((strstr($name, "?") != false) || (strstr($name, "$") != false) || (strstr($name, "%") != false)){
179 $emailAddr = ereg_replace("EMAILSTART--", "", $emailAddr);
180 $emailAddr = ereg_replace("--EMAILEND", "", $emailAddr);
181 $from = $emailAddr;
182 }
183 else if (strlen($name) > 0) {
184 $from = $name;
185 }
186 else if (strlen($emailAddr > 0)) {
187 $emailAddr = ereg_replace("EMAILSTART--", "", $emailAddr);
188 $emailAddr = ereg_replace("--EMAILEND", "", $emailAddr);
189 $from = $emailAddr;
190 }
191
192 $from = trim($from);
193
194 // strip out any quotes if they exist
195 if ((strlen($from) > 0) && ($from[0] == "\"") && ($from[strlen($from) - 1] == "\""))
196 $from = substr($from, 1, strlen($from) - 2);
197
198 return $from;
199 }
200
201 /** returns "true" if the copy was completed successfully.
202 ** returns "false" with an error message if unsuccessful.
203 **/
204 function copyMessages($imapConnection, $from_id, $to_id, $folder) {
205 fputs($imapConnection, "mailboxStore COPY $from_id:$to_id \"$folder\"\n");
206 $read = fgets($imapConnection, 1024);
207 while ((substr($read, 0, 15) != "mailboxStore OK") && (substr($read, 0, 15) != "mailboxStore NO")) {
208 $read = fgets($imapConnection, 1024);
209 }
210
211 if (substr($read, 0, 15) == "mailboxStore NO") {
212 return false;
213 } else if (substr($read, 0, 15) == "mailboxStore OK") {
214 return true;
215 }
216
217 echo "UNKNOWN ERROR copying messages $from_id to $to_id to folder $folder.<BR>";
218 return false;
219 }
220
221 /** expunges a mailbox **/
222 function expungeBox($imapConnection, $mailbox) {
223 selectMailbox($imapConnection, $mailbox, $num);
224 fputs($imapConnection, "1 EXPUNGE\n");
225 }
226
227 function getFolderNameMinusINBOX($mailbox, $del) {
228 $inbox = "INBOX" . $del;
229 if (substr($mailbox, 0, strlen($inbox)) == $inbox)
230 $box = substr($mailbox, strlen($inbox), strlen($mailbox));
231 else
232 $box = $mailbox;
233
234 return $box;
235 }
236
237 /** This function gets all the information about a message. Including Header and body **/
238 function fetchMessage($imapConnection, $id) {
239 $message["HEADER"] = fetchHeader($imapConnection, $id);
240 $message["ENTITIES"] = fetchBody($imapConnection, $message["HEADER"]["BOUNDARY"], $id, $message["HEADER"]["TYPE0"], $message["HEADER"]["TYPE1"]);
241
242 return $message;
243 }
244
245 function fetchHeader($imapConnection, $id) {
246 fputs($imapConnection, "messageFetch FETCH $id:$id RFC822.HEADER\n");
247 $read = fgets($imapConnection, 1024);
248
249 /** defaults... if the don't get overwritten, it will display text **/
250 $header["TYPE0"] = "text";
251 $header["TYPE1"] = "plain";
252 $header["ENCODING"] = "us-ascii";
253 while ((substr($read, 0, 15) != "messageFetch OK") && (substr($read, 0, 16) != "messageFetch BAD")) {
254 /** MIME-VERSION **/
255 if (substr($read, 0, 17) == "MIME-Version: 1.0") {
256 $header["MIME"] = true;
257 $read = fgets($imapConnection, 1024);
258 }
259
260 /** ENCODING TYPE **/
261 else if (substr($read[$i], 0, 26) == "Content-Transfer-Encoding:") {
262 $header["ENCODING"] = strtolower(trim(substr($read[$i], 26)));
263 }
264
265 /** CONTENT-TYPE **/
266 else if (substr($read, 0, 13) == "Content-Type:") {
267 $cont = strtolower(trim(substr($read, 13)));
268 if (strpos($cont, ";"))
269 $cont = substr($cont, 0, strpos($cont, ";"));
270 $header["TYPE0"] = substr($cont, 0, strpos($cont, "/"));
271 $header["TYPE1"] = substr($cont, strpos($cont, "/")+1);
272
273 $line = $read;
274 $read = fgets($imapConnection, 1024);
275 while ( (substr(substr($read, 0, strpos($read, " ")), -1) != ":") && (trim($read) != "") && (trim($read) != ")")) {
276 str_replace("\n", "", $line);
277 str_replace("\n", "", $read);
278 $line = "$line $read";
279 $read = fgets($imapConnection, 1024);
280 }
281
282 /** Detect the boundary of a multipart message **/
283 if (strpos(strtolower(trim($line)), "boundary=")) {
284 $pos = strpos($line, "boundary=") + 9;
285 $bound = trim($line);
286 if (strpos($line, " ", $pos) > 0) {
287 $bound = substr($bound, $pos, strpos($line, " ", $pos));
288 } else {
289 $bound = substr($bound, $pos);
290 }
291 $bound = str_replace("\"", "", $bound);
292 $header["BOUNDARY"] = $bound;
293 }
294
295 /** Detect the charset **/
296 if (strpos(strtolower(trim($line)), "charset=")) {
297 $pos = strpos($line, "charset=") + 8;
298 $charset = trim($line);
299 if (strpos($line, " ", $pos) > 0) {
300 $charset = substr($charset, $pos, strpos($line, " ", $pos));
301 } else {
302 $charset = substr($charset, $pos);
303 }
304 $charset = str_replace("\"", "", $charset);
305 $header["CHARSET"] = $charset;
306 } else {
307 $header["CHARSET"] = "us-ascii";
308 }
309
310 }
311
312 /** REPLY-TO **/
313 else if (substr($read, 0, 9) == "Reply-To:") {
314 $header["REPLYTO"] = trim(substr($read, 9, strlen($read)));
315 $read = fgets($imapConnection, 1024);
316 }
317
318 /** FROM **/
319 else if (substr($read, 0, 5) == "From:") {
320 $header["FROM"] = trim(substr($read, 5, strlen($read) - 6));
321 if ($header["REPLYTO"] == "")
322 $header["REPLYTO"] = $header["FROM"];
323 $read = fgets($imapConnection, 1024);
324 }
325 /** DATE **/
326 else if (substr($read, 0, 5) == "Date:") {
327 $d = substr($read, 5, strlen($read) - 6);
328 $d = trim($d);
329 $d = ereg_replace(" ", " ", $d);
330 $d = explode(" ", $d);
331 $header["DATE"] = getTimeStamp($d);
332 $read = fgets($imapConnection, 1024);
333 }
334 /** SUBJECT **/
335 else if (substr($read, 0, 8) == "Subject:") {
336 $header["SUBJECT"] = trim(substr($read, 8, strlen($read) - 9));
337 if (strlen(Chop($header["SUBJECT"])) == 0)
338 $header["SUBJECT"] = "(no subject)";
339 $read = fgets($imapConnection, 1024);
340 }
341 /** CC **/
342 else if (substr($read, 0, 3) == "CC:") {
343 $pos = 0;
344 $header["CC"][$pos] = trim(substr($read, 4));
345 $read = fgets($imapConnection, 1024);
346 while ((substr($read, 0, 1) == " ") && (trim($read) != "")) {
347 $pos++;
348 $header["CC"][$pos] = trim($read);
349 $read = fgets($imapConnection, 1024);
350 }
351 }
352 /** TO **/
353 else if (substr($read, 0, 3) == "To:") {
354 $pos = 0;
355 $header["TO"][$pos] = trim(substr($read, 4));
356 $read = fgets($imapConnection, 1024);
357 while ((substr($read, 0, 1) == " ") && (trim($read) != "")){
358 $pos++;
359 $header["TO"][$pos] = trim($read);
360 $read = fgets($imapConnection, 1024);
361 }
362 }
363
364 /** ERROR CORRECTION **/
365 else if (substr($read, 0, 1) == ")") {
366 if ($header["SUBJECT"] == "")
367 $header["SUBJECT"] = "(no subject)";
368
369 if ($header["FROM"] == "")
370 $header["FROM"] = "(unknown sender)";
371
372 if ($header["DATE"] == "")
373 $header["DATE"] = time();
374 $read = fgets($imapConnection, 1024);
375 }
376 else {
377 $read = fgets($imapConnection, 1024);
378 }
379 }
380 return $header;
381 }
382
383 function fetchBody($imapConnection, $bound, $id, $type0, $type1) {
384 /** This first part reads in the full body of the message **/
385 fputs($imapConnection, "messageFetch FETCH $id:$id BODY[TEXT]\n");
386 $read = fgets($imapConnection, 1024);
387
388 $count = 0;
389 while ((substr($read, 0, 15) != "messageFetch OK") && (substr($read, 0, 16) != "messageFetch BAD")) {
390 $body[$count] = $read;
391 $count++;
392
393 $read = fgets($imapConnection, 1024);
394 }
395
396 /** this deletes the first line, and the last two (imap stuff we ignore) **/
397 $i = 0;
398 $j = 0;
399 while ($i < count($body)) {
400 if ( ($i != 0) && ($i != count($body) - 1) && ($i != count($body)) ) {
401 $bodytmp[$j] = $body[$i];
402 $j++;
403 }
404 $i++;
405 }
406 $body = $bodytmp;
407
408 /** Now, lets work out the MIME stuff **/
409 /** (needs mime.php included) **/
410 return decodeMime($body, $bound, $type0, $type1);
411 }
412
413 function fetchEntityHeader($imapConnection, &$read, &$type0, &$type1, &$bound, &$encoding, &$charset) {
414 /** defaults... if the don't get overwritten, it will display text **/
415 $type0 = "text";
416 $type1 = "plain";
417 $encoding = "us-ascii";
418 $i = 0;
419 while (trim($read[$i]) != "") {
420 if (substr($read[$i], 0, 26) == "Content-Transfer-Encoding:") {
421 $encoding = strtolower(trim(substr($read[$i], 26)));
422
423 } else if (substr($read[$i], 0, 13) == "Content-Type:") {
424 $cont = strtolower(trim(substr($read[$i], 13)));
425 if (strpos($cont, ";"))
426 $cont = substr($cont, 0, strpos($cont, ";"));
427 $type0 = substr($cont, 0, strpos($cont, "/"));
428 $type1 = substr($cont, strpos($cont, "/")+1);
429
430 $line = $read[$i];
431 while ( (substr(substr($read[$i], 0, strpos($read[$i], " ")), -1) != ":") && (trim($read[$i]) != "") && (trim($read[$i]) != ")")) {
432 str_replace("\n", "", $line);
433 str_replace("\n", "", $read[$i]);
434 $line = "$line $read[$i]";
435 $i++;
436 }
437
438 /** Detect the boundary of a multipart message **/
439 if (strpos(strtolower(trim($line)), "boundary=")) {
440 $pos = strpos($line, "boundary=") + 9;
441 $bound = trim($line);
442 if (strpos($line, " ", $pos) > 0) {
443 $bound = substr($bound, $pos, strpos($line, " ", $pos));
444 } else {
445 $bound = substr($bound, $pos);
446 }
447 $bound = str_replace("\"", "", $bound);
448 }
449
450 /** Detect the charset **/
451 if (strpos(strtolower(trim($line)), "charset=")) {
452 $pos = strpos($line, "charset=") + 8;
453 $charset = trim($line);
454 if (strpos($line, " ", $pos) > 0) {
455 $charset = substr($charset, $pos, strpos($line, " ", $pos));
456 } else {
457 $charset = substr($charset, $pos);
458 }
459 $charset = str_replace("\"", "", $charset);
460 }
461 }
462 $i++;
463 }
464
465 /** remove the header from the entity **/
466 $i = 0;
467 while (trim($read[$i]) != "") {
468 $i++;
469 }
470 $i++;
471
472 for ($p = 0; $i < count($read); $p++) {
473 $entity[$p] = $read[$i];
474 $i++;
475 }
476
477 $read = $entity;
478 }
479
480 function parsePlainTextMessage($line) {
481 $line = "^^$line";
482
483 if ((strpos(strtolower($line), "<!") == false) &&
484 (strpos(strtolower($line), "<html>") == false) &&
485 (strpos(strtolower($line), "</html>") == false)) {
486 $line = str_replace("<", "&lt;", $line);
487 $line = str_replace(">", "&gt;", $line);
488 }
489
490 $wrap_at = 86; // Make this configurable int the config file some time
491 if (strlen($line) - 2 >= $wrap_at) // -2 because of the ^^ at the beginning
492 $line = wordWrap($line, $wrap_at);
493
494 $line = str_replace(" ", "&nbsp;", $line);
495 $line = str_replace("\t", "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;", $line);
496
497 /** if >> or > are found at the beginning of a line, I'll assume that was
498 replied text, so make it different colors **/
499 if (strpos(trim(str_replace("&nbsp;", "", $line)), "&gt;&gt;") == 2) {
500 $line = substr($line, 2, strlen($line));
501 $line = "<TT><FONT COLOR=FF0000>$line</FONT></TT><BR>\n";
502 } else if (strpos(trim(str_replace("&nbsp;", "", $line)), "&gt;") == 2) {
503 $line = substr($line, 2, strlen($line));
504 $line = "<TT><FONT COLOR=800000>$line</FONT></TT><BR>\n";
505 } else {
506 $line = substr($line, 2, strlen($line));
507 $line = "<TT><FONT COLOR=000000>$line</FONT></TT><BR>\n";
508 }
509
510 /** This translates "http://" into a link. It could be made better to accept
511 "www" and "mailto" also. That should probably be added later. **/
512 if (strpos(strtolower($line), "http://") != false) {
513 $line = ereg_replace("<BR>", "", $line);
514 $start = strpos(strtolower($line), "http://");
515 $link = substr($line, $start, strlen($line));
516
517 if (strpos($link, " ")) {
518 $end = strpos($link, " ")-1;
519 }
520 else if (strpos($link, "&nbsp;")) {
521 $end = strpos($link, "&nbsp;")-1;
522 }
523 else if (strpos($link, "<")) {
524 $end = strpos($link, "<");
525 }
526 else if (strpos($link, ">")) {
527 $end = strpos($link, ">");
528 }
529 else if (strpos($link, "(")) {
530 $end = strpos($link, "(")-1;
531 }
532 else if (strpos($link, ")")) {
533 $end = strpos($link, ")")-1;
534 }
535 else if (strpos($link, "{")) {
536 $end = strpos($link, "{")-1;
537 }
538 else if (strpos($link, "}")) {
539 $end = strpos($link, "}")-1;
540 }
541 else
542 $end = strlen($link);
543
544 $link = substr($line, $start, $end);
545 $end = $end + $start;
546 $before = substr($line, 0, $start);
547 $after = substr($line, $end, strlen($line));
548
549 $line = "$before<A HREF=\"$link\" TARGET=_top>$link</A>$after<BR>";
550 }
551
552 return $line;
553 }
554 ?>