Les fichiers MHTML décodés en Bash


16 mai 2008

Fichiers MHT sous Bash

J'ai été confronté à un fichier MHTML (d'extension .mht) et j'avais donc deux options pour consulter son contenu (32 fichiers...) : soit je faisais du copier-coller et du décodage (quoted-printable - RFC - et base64 - RFC) \"à la main\" et j'avais mon contenu en à peine 5 minutes, soit je faisais un script pour automatiser tout ça. Devinez ce que j'ai choisi...

�a me permet de parler d'une petite astuce de décodage de contenu en base64 que j'avais trouvé je ne sais plus trop où :

openssl base64 -d -in inputFile -out outputFile
openssl base64 -d < inputFile > outputFile

Pour faire court, à partir du fichier .mht, on le découpe (avec csplit), on traite les parties suivant qui sont encodées en base64 (fichiers binaires) ou quoted-printable (pour les autres pages web dans le cas d'un frameset). Et hop !

#!/bin/bash

#Explode a .mht archive into its original files.
#Syntax : explodemht MHTFile DestFolder

#Needed : php (pour le rawurldecode), openssl (pour le base64 decode) et "recode" pour le Quoted-Printable

[[ ${#} -lt 2 ]] && echo "Pas assez d'arguments : le fichier MHT et le dossier de destination" && exit 1;

MHTFile=${1}
[[ ! -f $MHTFile ]] && echo "Le fichier <$MHTFile> n'existe pas." && exit 2;

mainFolder=${2}
[[ ! -d $mainFolder ]] && mkdir "$mainFolder";

#Passe 0 : on fait le split du fichier .mht
SplitPrefix="mht_exploding_"
csplit -f $SplitPrefix "$MHTFile" /^\-\-\-\-\-\-=_NextPart/ {*} > /dev/null

#Première passe : on extrait tous les fichiers de l'archive MIME et on les décode correctement
echo "On crée les fichiers :"
j=0
NewNames=()
OriginalName=()
Encodings=()
for item in $SplitPrefix*; do
    FileName=`cat $item| grep -m 1 "Content-Location:" | awk '{print $2}' | sed 's/\r//g'`
    OriginalNames[$j]=$FileName
    FileName=`php -q -r 'echo (isset($_SERVER["argv"][1]) ? rawurldecode($_SERVER["argv"][1]) : "");' ${FileName}`
    FileName=`echo ${FileName} | sed 's/\\\/\//g'`
    FileName=`basename "${FileName}" | sed 's/\r//g'`
    Encoding=`cat $item| grep -m 1 "Content-Transfer-Encoding:" | sed 's/\r//g' | awk '{print $2}'`
    Encodings[$j]=$Encoding
    FirstEmptyLine=`cat $item | sed 's/\r//g' | grep -m 1 -n '^$' | awk 'BEGIN{FS=":"}{print $1}'`
    ((FirstEmptyLine++))
    if [[ ${#FileName} -ne 0 ]]; then
        echo "Fichier $j : ${FileName} - ($Encoding)"
        NewNames[$j]=${FileName}
        [[ ${Encoding} == "base64" ]] && sed -n $FirstEmptyLine',$p' $item | openssl base64 -d -out "$mainFolder/${FileName}";
        if [[ ${Encoding} == "quoted-printable" ]]; then
            sed -n $FirstEmptyLine',$p' $item | recode windows-1252/CRLF.. | recode /qp.. > $mainFolder/${FileName}

        fi
    fi
    ((j++))
done

#Deuxième passe : on fixe les liens HTML qui peuvent être invalides
i=0
while [[ $i -lt $j ]]; do
    if [[ ${Encodings[$i]} == "quoted-printable" ]]; then
        k=0
        echo "On traite les liens du fichier ${NewNames[$i]}."
        while [[ $k -lt $j ]]; do
            [[ ${#NewNames[$k]} == 0 ]] && ((k++)) && continue;
            OriginalName=`echo -n ${OriginalNames[$k]} | sed 's@\\\@\\\\@g;s@&@&amp;@g;s@%26@&amp;@g'`
            cat $mainFolder/${NewNames[$i]} | sed 's@'$OriginalName'@'${NewNames[$k]}'@g;s@'${OriginalNames[$k]}'@'${NewNames[$k]}'@g' > tempfile.tmp
            cp tempfile.tmp $mainFolder/${NewNames[$i]}
            ((k++))
        done
    fi
    ((i++))
done

#On nettoie
rm -f tempfile.tmp
rm -f $SplitPrefix*

En fait c'est énervant à écrire. Parce que je suis trop bête et que je n'ai pas trouvé d'équivalent à rawurldecode sous Bash. Parce que le code n'est pas beau. Du tout. Et parce que ça marche, rageant...

Il faut donc inclure un programme C qui implémente le rawurldecode et coder tout ça un peu plus proprement...

Accueil