Das Örtchen RSS-Feed
Kategorie
Kategorie: Blog
Buttons & Statistiken
Neueste Kommentare

Drupal 6: FileField, MimeDetect und PHP 5.3

Ende Oktober habe ich die Betreuung einer Drupal 6-Installation aus dem Jahre 2009 übernommen. Da bei Host Europe der Umstieg auf PHP 5.3 anstand, hatte mein Kunde einige Problemchen mit dem hoffnungslos veralteten Drupal 6.13. Die direkt sichtbare Meldung "Function ereg() is deprecated in > (...)/includes/file.inc on line 895" war nach einem Update auf Drupal 6.26 (inkl. allen verwendeten Modulen) schnell behoben.

Das erste Problem

Leider gab es trotz des Updates von MimeDetect auf 6.x-1.x-dev (2011-Feb-25) die Meldung "Fileinfo could not load the magic file. It could be corrupted. Try reinstalling the magic file distributed with the MimeDetect module.", welches ich nach einigem Gewühle in der Issue-Queue durch einen Konfig-Eintrag in der settings.php lösen konnte.

 $conf = array(
  'mimedetect_magic' => '',
);
 

Hinweis: Das Modul verwendet normalerweise ein im Modul-Ordner beigefügtes magic database file. In PHP >= 5.3.11 (und >= 5.4.1) hat sich jedoch das Format dieser Datei geändert, womit diese mitgelieferte Fassung nicht mehr benutzt werden kann. Durch den "leeren" Konfig-Eintrag nutzen wir die internen Rückfall-Mechanismen von finfo_open() aus, um aktuellere Angaben (direkt vom Webserver) zu benutzen.

Das zweite Problem

Die Meldung von MimeDetect war damit zwar behoben, doch leider funktionierte der Datei-Upload immer noch nicht korrekt. Folgende Tool-Chain kam zum Einsatz:

  1. Drupal 6.26
  2. CCK 6.x-2.9
  3. FileField 6.x-3.10
  4. MimeDetect 6.x-1.x-dev (2011-Feb-25)

Versuchte man nun in einem Node-Typ einen mit FileField umgesetzten Upload einer .exe-Datei, so scheiterte man mit der Meldung "Die ausgewählte Datei (...)  konnte nicht hochgeladen werden.  The file contents (application/x-dosexec) do not match its extension (exe)." und einem zusätzlichen Watchdog-Eintrag.

Drupal 6, FileField, MimeDetect - Problem beim Dateiupload

Die Recherche

Eine Überprüfung der Issue-Queue von MimeDetect zeigte zwar eine Reihe ähnlicher Fehler (beispielsweise zip, xls und flv), brachte mich jedoch nicht wirklich weiter.

Also begann ich den Modul-Code zu analysieren und wurde in der mimedetect.module in den Zeilen 54 bis 63 fündig:

 $mime = FALSE;
$magic_file = variable_get(
  'mimedetect_magic',
  drupal_get_path('module', 'mimedetect') .'/magic'
);

// Try to use the fileinfo extension first.
if (extension_loaded('fileinfo')) {
  static $finfo = FALSE;
  if ($finfo || $finfo = @finfo_open(FILEINFO_MIME, $magic_file)) {
    $mime = finfo_file($finfo, realpath($file->filepath));
  }
} 

Auf Basis dieses Codes schrieb ich ein kleines Test-Skript mit nachfolgendem Inhalt:

 <?php
  if (extension_loaded('fileinfo')) {
    echo 'Loaded - ';
    if ($finfo = @finfo_open(FILEINFO_MIME, $magic_file)) {
      $mime = finfo_file($finfo, 'test.exe');
      echo 'Type ' . $mime;
    }
  }
?> 

Als Ausgabe erhielt ich "Loaded - Type application/x-dosexec", was zu der Fehlermeldung beim Upload-Versuch passt. Somit war sicher, daß die PHP-Erweiterung fileinfo aktiv ist und besagten Mime-Typ für eine .exe-Datei zurückliefert. Ersteres erkennt man übrigens auch an der Meldung "The MimeDetect module is using PHP's fileinfo extension to detect MIME types. There are no settings for the extension." auf der ansonsten leeren Einstellungs-Seite des Moduls.

Das Problem lag also nicht am Modul MimeDetect. Deswegen sah ich mir nun den Code von FileField genauer an und wurde in der filefield.module in den Zeilen 739 bis 744 fündig, wo nachfolgender Code zu finden ist:

 if (module_exists('mimedetect')) {
  $type = mimedetect_mime($file);
  if ($type != $file->filemime) {
    $errors[] = t(
      'The file contents (@type) do not match its extension (@extension).',
      array('@type' => $type, '@extension' => $extension)
    );
  }
} 

In Zeile 741 wird also der von MimeDetect gelieferte Mime-Typ mit einem Typ aus einer anderen Quelle verglichen. Die Frage war nur welcher Typ und aus welcher Quelle?

Zunächst änderte ich Zeile 742, um den gewünschten Typ zu erfahren:

 $errors[] = t(
  'The file contents (@type) do not match its extension (@extension, @wanted).',
  array('@type' => $type, '@wanted' => $file->filemime, '@extension' => $extension)
); 

Durch diese angepasste Meldung erfuhr ich, daß der gewünschte Typ einer .exe-Datei "application/x-msdos-program" ist. Bei einer nachfolgenden Recherche in der Drupal 6-API fand ich auch den Grund hierfür. In der Funktion file_get_mimetype() gibt es nämlich ein großes Array, in welchem die Mime-Zuordnungen fest hinterlegt sind. Sieht man sich die Funktion etwas genauer an, so stellt man fest, daß eine Änderung dieser Zuordnung zwar möglich ist, aber nur durch komplettes Überschreiben des alten Array-Inhalts.

Die Lösung

Ich wollte jedoch den Array-Inhalt nicht fest hinterlegen, sondern den Webserver danach fragen. Glücklicherweise ist dies mit dem Modul File MIME möglich. Nachfolgende Modul-Settings kommen derzeit beim Kunden zum Einsatz.

Drupal 6, FileField, MimeDetect, File MIME - Einstellungen gg. Upload-Probleme

Der angegebene Pfad funktionierte bei Webspace mit PHP 5.3 von Host Europe und entspricht der Angabe in der README.txt. Komischerweise musste ich die Mime-Zuordnung für .exe-Dateien manuell hinterlegen, obwohl nun MimeDetect und File MIME beide den aktuellen Stand des Webservers verwenden sollten, aber na ja man kann halt nicht alles haben.

Hinweis:  Die korrekte Angabe des Pfads wird übrigens durch die Meldung "Detected (...) custom mappings. Overridung default mapping." mitgeteilt. Allerdings kam diese Meldung bei mir erst, als ich zusätzlich das manuelle Mapping hinterlegt hatte.

Hallo! Bist du neu hier? Dann abonniere doch den RSS-Feed dieses nicht mehr ganz so stillen Örtchens, um über meine geistigen Ergüsse auf dem Laufenden zu bleiben. Alternativ besteht auch die Möglichkeit, sich von FeedBurner per E-Mail über meine Ausscheidungen benachrichtigen zu lassen.

Neuen Kommentar schreiben

Der Inhalt dieses Feldes wird nicht öffentlich zugänglich angezeigt.
Der Inhalt dieses Feldes wird öffentlich zugänglich angezeigt, aber als rel="nofollow" markiert.
Hinweis

Kommentare beleben den Blog! Ich freue mich über jeden Kommentar. Du kannst hier offen Deine Meinung zum Artikel sagen, aber bitte beachte die Netiquette und vermeide es andere zu beleidigen.

Bitte unterlasst es die Kommentare zu SEO-Zwecken zu missbrauchen. Kommentare mit Links, die nicht zu Blogs führen (oder zu Blogs mit Grauzonen-Themen) und/oder Keywords als Namen verwenden, sind nicht erwünscht!

Möchtest Du mir einen Blog-Artikel schmackhaft machen, dann schreib die URL ohne HTML-Tag in den Kommentarbereich und ich werde diesen bei Gefallen verlinken.