mail: decode multipart attachments (text, html, images)
[minimedit.git] / mail / index.php
1 <?php
2 $mailbox = 'mail/inbox';
3 @list ($msgid) = explode('/', ltrim($Page->path, '/'));
4
5 if (!function_exists('parsemailhead')) {
6 function parsemailhead($headerdata)
7 {
8         $headlist = iconv_mime_decode_headers($headerdata, ICONV_MIME_DECODE_CONTINUE_ON_ERROR);
9         $headlist['date'] = DateTime::createFromFormat(DateTimeInterface::RFC2822.'+', $headlist['Date']);
10         $headlist['from'] = imap_rfc822_parse_adrlist($headlist['From'], '');
11         array_walk($headlist['from'], function ($row) {
12                 $row->display = $row->personal ?? $row->mailbox;
13         });
14         return $headlist;
15 }
16 }
17
18 if ($msgid) {
19         $filename = "$mailbox/$msgid";
20         if (!is_readable($filename)) {
21                 return TRUE;
22         }
23
24         list ($headerdata, $rawbody) = explode("\n\n", file_get_contents($filename), 2);
25         $head = parsemailhead($headerdata);
26         $head['date']->setTimezone(new DateTimeZone(date_default_timezone_get()));
27
28         $Page->title = 'Mailbericht ' . $head['date']->format('Y-m-d H:i');
29         printf("<h2>%s</h2>\n", htmlspecialchars($head['Subject'] ?? 'Mailbericht zonder onderwerp'));
30
31         printf('<h3><a href="mailto:%s">%s</a> <small class="date" title="%s">%s</small></h3>'."\n",
32                 htmlspecialchars($head['From']),
33                 htmlspecialchars(implode(', ', array_column($head['from'], 'display'))),
34                 htmlspecialchars($head['Date']),
35                 showdate(preg_split('/\D/', $head['date']->format('c')))
36         );
37         print '</dl>';
38
39         $type = preg_replace('/;.*/', '', $head['Content-Type'] ?? 'text/plain', 1);
40         switch ($type) {
41         case 'text/plain':
42                 $body = $rawbody;
43                 if (($head['Content-Transfer-Encoding'] ?? '') === 'quoted-printable') {
44                         $body = quoted_printable_decode($body);
45                 }
46                 $body = nl2br(htmlspecialchars($body));
47                 print "<p>$body</p>\n";
48                 break;
49         case 'multipart/alternative':
50                 $mime = mailparse_msg_parse_file($filename);
51                 foreach (mailparse_msg_get_structure($mime) as $section) {
52                         print "<h3>$section</h3>\n";
53                         $part = mailparse_msg_get_part($mime, $section);
54                         if ($section === '1') {
55                                 continue; // do not access container to prevent segfault
56                         }
57                         $pinfo = mailparse_msg_get_part_data($part);
58                 #printf('<pre>%s</pre>', htmlspecialchars(print_r($pinfo,TRUE)));
59                         $phead = $pinfo['headers'];
60                         list ($ptype) = explode(';', strtolower($phead['content-type']));
61                         list ($pmedia) = explode('/', $ptype);
62                         switch ($pmedia) {
63                         case 'multipart':
64                                 continue 2;
65                         case 'text':
66                                 $body = mailparse_msg_extract_part_file($part, $filename, NULL);
67                                 #iconv($pinfo['charset'], 'UTF-8', $body); #TODO test if needed
68                                 switch ($ptype) {
69                                 case 'text/plain':
70                                         $body = htmlspecialchars($body);
71                                         print "<pre><p>$body</p></pre>\n";
72                                         break;
73                                 case 'text/html':
74                                         $body = preg_replace('{
75                                                 (<br */?>) | <[^>]+> # keep line breaks, replace other elements
76                                         }x', "\\1\n", $body); # strip_tags
77                                         print "<p>$body</p>\n";
78                                         break;
79                                 }
80                                 break;
81                         case 'image':
82                                 if (!preg_match('/^inline;/', $phead['content-disposition'])) {
83                                         continue 2;
84                                 }
85                                 $name = iconv_mime_decode($phead['disposition-filename']);
86                                 $body = mailparse_msg_extract_part_file($part, $filename, NULL);
87                                 printf('<img src="data:%s;base64,%s" alt="%s" />',
88                                         $ptype, base64_encode($body),
89                                         htmlspecialchars($phead['name'] ?? '-')
90                                 );
91                                 break;
92                         }
93                 }
94                 mailparse_msg_free($mime);
95                 break;
96         default:
97                 printf('<p>Geen ondersteuning voor <em>%s</em>.</p>', htmlspecialchars($type));
98         }
99         return;
100 }
101
102 if ($Page->api) {
103         return;
104 }
105 if (!$User->admin('user')) {
106         http_response_code(403);
107         $Page->place['warn'] = "Geen gebruikersrechten om e-mails in te zien.";
108         $Page->place['maillist'] = '';
109         return TRUE;
110 }
111
112 $rows = glob("$mailbox/*");
113 if (!$rows) {
114         throw new Exception('Kon inbox niet openen.');
115 }
116
117 $nav = [
118         'start' => $_GET['start'] ?? 0,
119         'n'     => $_GET['n'] ?? 10,
120         'total' => count($rows),
121 ];
122 $rows = array_slice(array_reverse($rows), $nav['start'], $nav['n']);
123
124 ob_start();
125 print '<ul>';
126 foreach (array_reverse($rows) as $filename) {
127         if (!is_readable($filename)) {
128                 continue;
129         }
130
131         printf('<li><a href="%s">', "/{$Page->handler}/".basename($filename));
132
133         list ($headerdata) = explode("\n\n", file_get_contents($filename));
134         $head = parsemailhead($headerdata);
135
136         print $head['Subject'];
137         printf(' <small class="date" title="%s">%s</small>',
138                 htmlspecialchars($head['Date']),
139                 showdate(explode('-', $head['date']->format('Y-m-d')))
140         );
141         printf(' <em class="right" title="%s">%s</em>',
142                 htmlspecialchars($head['From']),
143                 htmlspecialchars(implode(', ', array_column($head['from'], 'display')))
144         );
145         print "</a></li>\n";
146 }
147 print "</ul>\n";
148
149 print $Page->widget('nav', $nav);
150
151 $Page->place['maillist'] = ob_get_clean();