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