MDL-68415 core: test attachments in email_to_user from allowed paths.
authorPaul Holden <paulh@moodle.com>
Wed, 6 May 2020 20:08:15 +0000 (21:08 +0100)
committerPaul Holden <paulh@moodle.com>
Wed, 6 May 2020 22:23:17 +0000 (23:23 +0100)
lib/moodlelib.php
lib/tests/moodlelib_test.php

index d73f914..360ad76 100644 (file)
@@ -6007,7 +6007,8 @@ function generate_email_messageid($localpart = null) {
  * @param string $subject plain text subject line of the email
  * @param string $messagetext plain text version of the message
  * @param string $messagehtml complete html version of the message (optional)
- * @param string $attachment a file on the filesystem, either relative to $CFG->dataroot or a full path to a file in $CFG->tempdir
+ * @param string $attachment a file on the filesystem, either relative to $CFG->dataroot or a full path to a file in one of
+ *          the following directories: $CFG->cachedir, $CFG->dataroot, $CFG->dirroot, $CFG->localcachedir, $CFG->tempdir
  * @param string $attachname the name of the file (extension indicates MIME)
  * @param bool $usetrueaddress determines whether $from email address should
  *          be sent out. Will be overruled by user profile setting for maildisplay
index 9922acb..fbfbbb6 100644 (file)
@@ -3385,6 +3385,90 @@ class core_moodlelib_testcase extends advanced_testcase {
         $sink->close();
     }
 
+    /**
+     * Data provider for {@see test_email_to_user_attachment}
+     *
+     * @return array
+     */
+    public function email_to_user_attachment_provider(): array {
+        global $CFG;
+
+        // Return all paths that can be used to send attachments from.
+        return [
+            'cachedir' => [$CFG->cachedir],
+            'dataroot' => [$CFG->dataroot],
+            'dirroot' => [$CFG->dirroot],
+            'localcachedir' => [$CFG->localcachedir],
+            'tempdir' => [$CFG->tempdir],
+            // Pass null to indicate we want to test a path relative to $CFG->dataroot.
+            'relative' => [null]
+        ];
+    }
+
+    /**
+     * Test sending attachments with email_to_user
+     *
+     * @param string|null $filedir
+     *
+     * @dataProvider email_to_user_attachment_provider
+     */
+    public function test_email_to_user_attachment(?string $filedir): void {
+        global $CFG;
+
+        // If $filedir is null, then write our test file to $CFG->dataroot.
+        $filepath = ($filedir ?: $CFG->dataroot) . '/hello.txt';
+        file_put_contents($filepath, 'Hello');
+
+        $user = core_user::get_support_user();
+        $message = 'Test attachment path';
+
+        // Create sink to catch all sent e-mails.
+        $sink = $this->redirectEmails();
+
+        // Attachment path will be that of the test file if $filedir was passed, otherwise the relative path from $CFG->dataroot.
+        $filename = basename($filepath);
+        $attachmentpath = $filedir ? $filepath : $filename;
+        email_to_user($user, $user, $message, $message, $message, $attachmentpath, $filename);
+
+        $messages = $sink->get_messages();
+        $sink->close();
+
+        $this->assertCount(1, $messages);
+
+        // Verify attachment in message body (attachment is in MIME format, but we can detect some Content fields).
+        $messagebody = reset($messages)->body;
+        $this->assertContains('Content-Type: text/plain; name="' . $filename . '"', $messagebody);
+        $this->assertContains('Content-Disposition: attachment; filename=' . $filename, $messagebody);
+
+        // Cleanup.
+        unlink($filepath);
+    }
+
+    /**
+     * Test sending an attachment that doesn't exist to email_to_user
+     */
+    public function test_email_to_user_attachment_missing(): void {
+        $user = core_user::get_support_user();
+        $message = 'Test attachment path';
+
+        // Create sink to catch all sent e-mails.
+        $sink = $this->redirectEmails();
+
+        $attachmentpath = '/hola/hello.txt';
+        $filename = basename($attachmentpath);
+        email_to_user($user, $user, $message, $message, $message, $attachmentpath, $filename);
+
+        $messages = $sink->get_messages();
+        $sink->close();
+
+        $this->assertCount(1, $messages);
+
+        // Verify attachment not in message body (attachment is in MIME format, but we can detect some Content fields).
+        $messagebody = reset($messages)->body;
+        $this->assertNotContains('Content-Type: text/plain; name="' . $filename . '"', $messagebody);
+        $this->assertNotContains('Content-Disposition: attachment; filename=' . $filename, $messagebody);
+    }
+
     /**
      * Test setnew_password_and_mail.
      */