-1

I have create a form that upload a file. This file is send as email attachment

 ..
     $form['cv'] = [
          '#type' => 'managed_file',
          '#title' => 'Votre CV',
          '#required' => true,
          '#upload_validators' => [
            'file_validate_extensions' => ['pdf doc docx odt odf'],
            'file_validate_size' => 4000,
          ],
        ];
..

on form submit

..
    $cv_id = $form_state->getValue('cv')[0];
    $cv_entity = File::load($cv_id);
    $attachments = [
      $cv_entity,       
    ];
    ..
    $result = $this->mailManager->mail($module, $key, $to, $language_code, $params, $reply, $send);
    if ($result['result'] == true) {
      $this->messenger()
        ->addMessage('Your application has been sent.');
..



 function hook_mail($key, &$message, $params) {
       $options = [
    'langcode' => $message['langcode'],
  ];

  switch ($key) {
    // Send a simple message from the contact form.
    case 'beetween_postulate':
      $from = \Drupal::config('system.site')->get('name');
      $message['subject'] = t('E-mail envoyé depuis le site @site-name', ['@site-name' => $from], $options);
      // Note that the message body is an array, not a string.
      $params = $message['params'];
      $mime_id = md5(uniqid(time() . rand(), 1));
      $headers = &$message['headers'];
      $message_content_type = $headers['Content-Type'];
      $headers['Content-Type'] = "multipart/mixed; boundary=\"$mime_id\"";
      $body = "This is a multi-part message in MIME format.\r\n";
      $body .= "--$mime_id\r\n";
      $body .= "Content-Type: $message_content_type \r\n\r\n";
      $body .= $params['body'] . "\r\n\r\n";
      if (!empty($params['attachments'])) {
        $fs_service = \Drupal::service('file_system');
        $fmtg_service = \Drupal::service('file.mime_type.guesser');
        foreach ($params['attachments'] as $file) {
          // Here we add the attachment to the message body.
          $file_name = $fs_service->basename($file);
          $mime_type = $fmtg_service->guess($file);
          $file_content = file_get_contents($file);
          $base64 = chunk_split(base64_encode($file_content));
          $body .= "--$mime_id\r\n";
          $body .= "Content-Transfer-Encoding: base64\r\n";
          $body .= "Content-Type: $mime_type;
 name=$file_name\r\n";
          $body .= "Content-Disposition: attachment;
 filename=$file_name\r\n\r\n";
          $body .= $base64 . "\r\n\r\n";
        }
      }
      $body .= '--' . $mime_id . '--';
      $message['body'] = [$body];

      break;
  }
}

it does work for most case but it does not work for some file names , although the

mailManager->mail

returns true, the email is not sent

for examples:

AMU - Réglement INDEED pour diffusion des offres d'emplois AMU.pdf

CV.dupond.pdf

How can i convert these files names to a correct format / or return a validator error on upload ?

Is this not a bug of the mail->manager method to return true in that case ?

2
  • 2
    Attachments aren't supported by core Drupal email, so what module are you using for mail? That module should be encoding the file names properly for you, but apparently it is not.
    – anonymous
    Dec 16, 2021 at 23:50
  • @anonymous i have edited my question
    – Matoeil
    Dec 17, 2021 at 8:41

2 Answers 2

3

If you write your own code to generate a multi-part message in MIME format, then it's up to you and you alone to ensure that all the headers conform to the RFCs for Mime Mail. There are a LOT of RFCs that apply. So consider using one of the many email modules hosted on drupal.org which will do this for you. And if the one you use doesn't do it quite correctly, open an issue and get that fixed for everyone.

That said, you can UTF-8 encode your filename in the Content-Type and Content-Disposition headers if your filename contains any characters not allowed by the RFCs (generally only some ASCII characters are allowed, and no spaces). A full explanation may be found at https://stackoverflow.com/questions/93551/how-to-encode-the-filename-parameter-of-content-disposition-header-in-http

2
  • using which method? utf8_encode($file_name) has no effect. rawurlencode($file_name) does work and i hope it works on every modern browser
    – Matoeil
    Dec 20, 2021 at 10:29
  • 1
    The encoding you need is called "B" encoding or "Q" encoding in the RFC. Again, the RFC tells you exactly what is valid here. It's not simply a matter of converting ASCII to UTF-8, as UTF-8 strings are not allowed in the header. You can use Unicode::mimeHeaderEncode() (scheduled to be removed in D10) or one of the new Symfony classes referred to in the Unicode API documentation.
    – anonymous
    Dec 20, 2021 at 20:00
0

rather than trying to sanitize the file name the best way , i have found a safe option for my case :

// Safer to leave the email service give default names rather than trying to sanitize it
...
 foreach ($params['attachments'] as $file) {
    $file_name = null;
...

Another option i have tried was to apply that method :

$file_name =sanitize_file_name($file_name);

function sanitize_file_name($file_name) { 
 // case of multiple dots
  $explode_file_name =explode('.', $file_name);
  $extension =array_pop($explode_file_name);
  $file_name_without_ext=substr($file_name, 0, strrpos( $file_name, '.') );    
  // replace special characters
  $file_name_without_ext = preg_quote($file_name_without_ext);
  $file_name_without_ext = preg_replace('/[^a-zA-Z0-9\\_]/', '_', $file_name_without_ext);
  $file_name=$file_name_without_ext . '.' . $extension;    
  return $file_name;
}

but i found one case where it does not work

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.