xref: /dokuwiki/_test/tests/inc/mailer.test.php (revision a162d64911e5ef472e99d49c50b94de22e02fa7f)
1<?php
2
3/**
4 * Extends the mailer class to expose internal variables for testing
5 */
6class TestMailer extends Mailer {
7    public function prop($name){
8        return $this->$name;
9    }
10
11    public function &propRef($name) {
12        return $this->$name;
13    }
14
15    public function prepareHeaders() {
16        return parent::prepareHeaders();
17    }
18
19    public function cleanHeaders() {
20        parent::cleanHeaders();
21    }
22
23}
24
25/**
26 * @group mailer_class
27 */
28class mailer_test extends DokuWikiTest {
29
30
31    function test_userheader(){
32        $mail = new TestMailer();
33        $headers = $mail->prop('headers');
34        $this->assertArrayNotHasKey('X-Dokuwiki-User',$headers);
35
36        $_SERVER['REMOTE_USER'] = 'andi';
37        $mail = new TestMailer();
38        $headers = $mail->prop('headers');
39        $this->assertArrayHasKey('X-Dokuwiki-User',$headers);
40    }
41
42    function test_setHeader(){
43        $mail = new TestMailer();
44
45        // check existance of default headers
46        $headers = $mail->prop('headers');
47        $this->assertArrayHasKey('X-Mailer',$headers);
48        $this->assertArrayHasKey('X-Dokuwiki-Title',$headers);
49        $this->assertArrayHasKey('X-Dokuwiki-Server',$headers);
50        $this->assertArrayHasKey('X-Auto-Response-Suppress',$headers);
51        $this->assertArrayHasKey('List-Id',$headers);
52
53        // set a bunch of test headers
54        $mail->setHeader('test-header','bla');
55        $mail->setHeader('to','A valid ASCII name <test@example.com>');
56        $mail->setHeader('from',"Thös ne\needs\x00serious cleaning\$§%.");
57        $mail->setHeader('bad',"Thös ne\needs\x00serious cleaning\$§%.",false);
58        $mail->setHeader("weird\n*+\x00foo.-_@bar?",'now clean');
59
60        // are they set?
61        $headers = $mail->prop('headers');
62        $this->assertArrayHasKey('Test-Header',$headers);
63        $this->assertEquals('bla',$headers['Test-Header']);
64        $this->assertArrayHasKey('To',$headers);
65        $this->assertEquals('A valid ASCII name <test@example.com>',$headers['To']);
66        $this->assertArrayHasKey('From',$headers);
67        $this->assertEquals('Ths neeedsserious cleaning.',$headers['From']);
68        $this->assertArrayHasKey('Bad',$headers);
69        $this->assertEquals("Thös ne\needs\x00serious cleaning\$§%.",$headers['Bad']);
70        $this->assertArrayHasKey('Weird+foo.-_@bar',$headers);
71
72        // unset a header again
73        $mail->setHeader('test-header','');
74        $headers = $mail->prop('headers');
75        $this->assertArrayNotHasKey('Test-Header',$headers);
76    }
77
78    function test_addresses(){
79        if (isWindows()) {
80            $this->markTestSkipped();
81        }
82
83        $mail = new TestMailer();
84
85        $mail->to('andi@splitbrain.org');
86        $mail->cleanHeaders();
87        $headers = $mail->prop('headers');
88        $this->assertEquals('andi@splitbrain.org', $headers['To']);
89
90        $mail->to('<andi@splitbrain.org>');
91        $mail->cleanHeaders();
92        $headers = $mail->prop('headers');
93        $this->assertEquals('andi@splitbrain.org', $headers['To']);
94
95        $mail->to('Andreas Gohr <andi@splitbrain.org>');
96        $mail->cleanHeaders();
97        $headers = $mail->prop('headers');
98        $this->assertEquals('Andreas Gohr <andi@splitbrain.org>', $headers['To']);
99
100        $mail->to('"Andreas Gohr" <andi@splitbrain.org>');
101        $mail->cleanHeaders();
102        $headers = $mail->prop('headers');
103        $this->assertEquals('"Andreas Gohr" <andi@splitbrain.org>', $headers['To']);
104
105        $mail->to('Andreas Gohr <andi@splitbrain.org> , foo <foo@example.com>');
106        $mail->cleanHeaders();
107        $headers = $mail->prop('headers');
108        $this->assertEquals('Andreas Gohr <andi@splitbrain.org>, foo <foo@example.com>', $headers['To']);
109
110        $mail->to('"Foo, Dr." <foo@example.com> , foo <foo@example.com>');
111        $mail->cleanHeaders();
112        $headers = $mail->prop('headers');
113        $this->assertEquals('=?UTF-8?B?IkZvbywgRHIuIg==?= <foo@example.com>, foo <foo@example.com>', $headers['To']);
114
115        $mail->to('Möp <moep@example.com> , foo <foo@example.com>');
116        $mail->cleanHeaders();
117        $headers = $mail->prop('headers');
118        $this->assertEquals('=?UTF-8?B?TcO2cA==?= <moep@example.com>, foo <foo@example.com>', $headers['To']);
119
120        $mail->to(array('Möp <moep@example.com> ',' foo <foo@example.com>'));
121        $mail->cleanHeaders();
122        $headers = $mail->prop('headers');
123        $this->assertEquals('=?UTF-8?B?TcO2cA==?= <moep@example.com>, foo <foo@example.com>', $headers['To']);
124
125        $mail->to(array('Beet, L van <lvb@example.com>',' foo <foo@example.com>'));
126        $mail->cleanHeaders();
127        $headers = $mail->prop('headers');
128        $this->assertEquals('=?UTF-8?B?QmVldCwgTCB2YW4=?= <lvb@example.com>, foo <foo@example.com>', $headers['To']);
129
130
131    }
132
133    function test_simplemail(){
134        global $conf;
135        $conf['htmlmail'] = 0;
136
137        $mailbody = 'A test mail in ASCII';
138        $mail = new TestMailer();
139        $mail->to('test@example.com');
140        $mail->setBody($mailbody);
141
142        $dump = $mail->dump();
143
144        // construct the expected mail body text - include the expected dokuwiki signature
145        $replacements = $mail->prop('replacements');
146        $expected_mail_body = chunk_split(base64_encode($mailbody.$replacements['text']['EMAILSIGNATURE']),72,MAILHEADER_EOL);
147
148        $this->assertNotRegexp('/Content-Type: multipart/',$dump);
149        $this->assertRegexp('#Content-Type: text/plain; charset=UTF-8#',$dump);
150        $this->assertRegexp('/'.preg_quote($expected_mail_body,'/').'/',$dump);
151
152        $conf['htmlmail'] = 1;
153    }
154
155    function test_replacements(){
156        $mail = new TestMailer();
157
158        $replacements = array( '@DATE@','@BROWSER@','@IPADDRESS@','@HOSTNAME@','@EMAILSIGNATURE@',
159                               '@TITLE@','@DOKUWIKIURL@','@USER@','@NAME@','@MAIL@');
160        $mail->setBody('A test mail in with replacements '.join(' ',$replacements));
161
162        $text = $mail->prop('text');
163        $html = $mail->prop('html');
164
165        foreach($replacements as $repl){
166            $this->assertNotRegexp("/$repl/",$text,"$repl replacement still in text");
167            $this->assertNotRegexp("/$repl/",$html,"$repl replacement still in html");
168        }
169    }
170
171    /**
172     * @see https://forum.dokuwiki.org/post/35822
173     */
174    function test_emptyBCCorCC() {
175        $mail = new TestMailer();
176        $headers = &$mail->propRef('headers');
177        $headers['Bcc'] = '';
178        $headers['Cc'] = '';
179        $header = $mail->prepareHeaders();
180        $this->assertEquals(0, preg_match('/(^|\n)Bcc: (\n|$)/', $header), 'Bcc found in headers.');
181        $this->assertEquals(0, preg_match('/(^|\n)Cc: (\n|$)/', $header), 'Cc found in headers.');
182    }
183
184    function test_nullTOorCCorBCC() {
185        $mail = new TestMailer();
186        $headers = &$mail->propRef('headers');
187        $headers['Bcc'] = NULL;
188        $headers['Cc'] = NULL;
189        $headers['To'] = NULL;
190        $header = $mail->prepareHeaders();
191        $this->assertEquals(0, preg_match('/(^|\n)Bcc: (\n|$)/', $header), 'Bcc found in headers.');
192        $this->assertEquals(0, preg_match('/(^|\n)Cc: (\n|$)/', $header), 'Cc found in headers.');
193        $this->assertEquals(0, preg_match('/(^|\n)To: (\n|$)/', $header), 'To found in headers.');
194    }
195
196    /**
197     * @group internet
198     */
199    function test_lint(){
200        // prepare a simple multipart message
201        $mail = new TestMailer();
202        $mail->to(array('Möp <moep@example.com> ',' foo <foo@example.com>'));
203        $mail->from('Me <test@example.com>');
204        $mail->subject('This is a töst');
205        $mail->setBody('Hello Wörld,
206
207        please don\'t burn, okay?
208        ');
209        $mail->attachContent('some test data', 'text/plain', 'a text.txt');
210        $msg = $mail->dump();
211        $msglines = explode("\n", $msg);
212
213        //echo $msg;
214
215        // ask message lint if it is okay
216        $html = new HTTPClient();
217        $results = $html->post('https://tools.ietf.org/tools/msglint/msglint', array('msg'=>$msg));
218        if($results === false) {
219            $this->markTestSkipped('no response from validator');
220            return;
221        }
222
223        // parse the result lines
224        $lines = explode("\n", $results);
225        $rows  = count($lines);
226        $i=0;
227        while(trim($lines[$i]) != '-----------' && $i<$rows) $i++; //skip preamble
228        for($i=$i+1; $i<$rows; $i++){
229            $line = trim($lines[$i]);
230            if($line == '-----------') break; //skip appendix
231
232            // get possible continuation of the line
233            while($lines[$i+1][0] == ' '){
234                $line .= ' '.trim($lines[$i+1]);
235                $i++;
236            }
237
238            // check the line for errors
239            if(substr($line,0,5) == 'ERROR' || substr($line,0,7) == 'WARNING'){
240                // ignore some errors
241                if(strpos($line, "missing mandatory header 'return-path'")) continue; #set by MDA
242                if(strpos($line, "bare newline in text body decoded")) continue; #we don't send mail bodies as CRLF, yet
243                if(strpos($line, "last decoded line too long")) continue; #we don't send mail bodies as CRLF, yet
244
245                // get the context in which the error occured
246                $errorin = '';
247                if(preg_match('/line (\d+)$/', $line, $m)){
248                    $errorin .= "\n".$msglines[$m[1] - 1];
249                }
250                if(preg_match('/lines (\d+)-(\d+)$/', $line, $m)){
251                    for($x=$m[1]-1; $x<$m[2]; $x++){
252                        $errorin .= "\n".$msglines[$x];
253                    }
254                }
255
256                // raise the error
257                throw new Exception($line.$errorin);
258            }
259        }
260
261        $this->assertTrue(true); // avoid being marked as risky for having no assertion
262    }
263
264    function test_simplemailsignature() {
265        global $conf;
266        $conf['htmlmail'] = 0;
267
268        $mailbody = 'A test mail in ASCII';
269        $signature = "\n-- \n" . 'This mail was generated by DokuWiki at' . "\n" . DOKU_URL . "\n";
270        $mail = new TestMailer();
271        $mail->to('test@example.com');
272        $mail->setBody($mailbody);
273
274        $dump = $mail->dump();
275
276        // construct the expected mail body text - include the expected dokuwiki signature
277        $expected_mail_body = chunk_split(base64_encode($mailbody . $signature), 72, MAILHEADER_EOL);
278        $this->assertRegexp('/' . preg_quote($expected_mail_body, '/') . '/', $dump);
279
280        $conf['htmlmail'] = 1;
281    }
282
283    function test_htmlmailsignature() {
284        $mailbody_text = 'A test mail in ASCII :)';
285        $mailbody_html = 'A test mail in <strong>html</strong>';
286        $htmlmsg_expected = '<html>
287<head>
288    <title>My Test Wiki</title>
289    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
290</head>
291<body>
292
293A test mail in <strong>html</strong>
294
295<br /><hr />
296<small>This mail was generated by DokuWiki at<br /><a href="' . DOKU_URL . '">' . DOKU_URL . '</a></small>
297</body>
298</html>
299';
300
301        $mail = new TestMailer();
302        $mail->to('test@example.com');
303        $mail->setBody($mailbody_text, null, null, $mailbody_html);
304
305        $dump = $mail->dump();
306
307        // construct the expected mail body text - include the expected dokuwiki signature
308        $expected_mail_body = chunk_split(base64_encode($htmlmsg_expected), 72, MAILHEADER_EOL);
309
310        $this->assertRegexp('/Content-Type: multipart/', $dump);
311        $this->assertRegexp('#Content-Type: text/plain; charset=UTF-8#', $dump);
312        $this->assertRegexp('/' . preg_quote($expected_mail_body, '/') . '/', $dump);
313
314    }
315
316    function test_htmlmailsignaturecustom() {
317        global $lang;
318        $lang['email_signature_html'] = 'Official message from your DokuWiki @DOKUWIKIURL@<br />Created by wonderful mail class <a href="https://www.dokuwiki.org/devel:mail">https://www.dokuwiki.org/devel:mail</a>';
319
320        $mailbody_text = 'A test mail in ASCII :)';
321        $mailbody_html = 'A test mail in <strong>html</strong>';
322        $htmlmsg_expected = '<html>
323<head>
324    <title>My Test Wiki</title>
325    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
326</head>
327<body>
328
329A test mail in <strong>html</strong>
330
331<br /><hr />
332<small>Official message from your DokuWiki <a href="' . DOKU_URL . '">' . DOKU_URL . '</a><br />Created by wonderful mail class <a href="https://www.dokuwiki.org/devel:mail">https://www.dokuwiki.org/devel:mail</a></small>
333</body>
334</html>
335';
336
337        $mail = new TestMailer();
338        $mail->to('test@example.com');
339        $mail->setBody($mailbody_text, null, null, $mailbody_html);
340
341        $dump = $mail->dump();
342
343        // construct the expected mail body text - include the expected dokuwiki signature
344        $replacements = $mail->prop('replacements');
345        $expected_mail_body = chunk_split(base64_encode($htmlmsg_expected), 72, MAILHEADER_EOL);
346
347        $this->assertRegexp('/' . preg_quote($expected_mail_body, '/') . '/', $dump);
348
349    }
350
351    function test_getCleanName() {
352        $mail = new TestMailer();
353        $name = $mail->getCleanName('Foo Bar');
354        $this->assertEquals('Foo Bar', $name);
355        $name = $mail->getCleanName('Foo, Bar');
356        $this->assertEquals('"Foo, Bar"', $name);
357        $name = $mail->getCleanName('Foo" Bar');
358        $this->assertEquals('"Foo\" Bar"', $name);
359    }
360}
361//Setup VIM: ex: et ts=4 :
362