OVH Cloud OVH Cloud

Script num=c3=a9ro 5

22 réponses
Avatar
Olivier Miakinen
Cette fois ça devrait être bon.

La prochaine étape consistera à écrire directement les fonctions
decode_qp et decode_b64 en shell au lieu d'appeler des programmes
externes. ;-)



#!/bin/bash
############################################################################
# Name:
# decode_headers
#
# Description:
# Script used for decoding RFC 2047 MIME encoded headers.
#
# Example:
# decode_headers <<POTIRON
# Message-ID: <c5a9-1@part.org>
# From: arvo@part.org (=?ASCII?Q?Arvo?= =?L1?Q?=20?= =?UTF-8?Q?P=C3=A4rt?=)
# To: =?Latin1?Q?Fr=E9d=E9ric_Chopin?= <fred@chopin.org>,
# =?Latin2?Q?Anton=EDn_Dvo=F8=E1k?= <anton@dvorak.org>
# Cc: Arvo =?UTF-8?Q?P=C3=A4rt?= <arvo@part.org>
# References: <A65R-4d@chopin.org> <c5a7-3@part.org>
# <A72Q-5a@chopin.org>
# In-Reply-To: <A72Q-5a@chopin.org>
# Subject: Re: Going to =?Shift-JIS?B?k4yLngo=?= (Tokyo) =?UTF-8?B?zpEK?=
# =?UTF-8?B?zrjOrs69zrEK?= (Athens) and =?ISO-8859-5?Q?=BC=DE=E1=DA?=
# =?ISO-8859-5?Q?=D2=D0?= (Moscow)
# POTIRON
# ->
# Message-ID: <c5a9-1@part.org>
# From: arvo@part.org (Arvo Pärt)
# To: Frédéric Chopin <fred@chopin.org>, Antonín Dvořák <anton@dvorak.org>
# Cc: Arvo Pärt <arvo@part.org>
# References: <A65R-4d@chopin.org> <c5a7-3@part.org> <A72Q-5a@chopin.org>
# In-Reply-To: <A72Q-5a@chopin.org>
# Subject: Re: Going to 東京 (Tokyo) Αθήνα (Athens) and Москва (Moscow)
############################################################################

#
# The following functions should be adapted to your system.
#
if [ "$(uname)" = "Linux" ]; then

# GNU/Linux
decode_qp()
{
tr "_" " " | /usr/bin/qprint -d
}
decode_b64()
{
/usr/bin/base64 -d
}

else

# MacOS X
decode_qp()
{
tr "_" " " | /usr/local/bin/qprint -d
}
decode_b64()
{
/usr/bin/base64 -D
}

fi


#
# This script uses the following global variables
# VARY
# used when a function has to return a string and not only a number
# DECODED_LINE
# The current state of a header being currently decoded
# STATUS
# Has three possible values:
# - "none" at the beginning of a new header
# - "decoded-word" after a correctly decoded MIME part
# - "normal" after any other string
#
VARY=""
DECODED_LINE=""
STATUS="none"

#
# Function: usage
#
usage()
{
printf "Usage: %s [OPTION...] [FILE...]\n" $0
printf "Decode headers from given files for RFC 2047 MIME encodings.\n"
printf "\n"
printf "With no FILE, or when FILE is -, read standard input.\n"
printf "\n"
printf " -h, --help Give this help list\n"
exit 1
}

#
# Function: decode_word
#
# Description:
# Check that the parameter is an encoded word, then decode it and
# convert it to UTF-8.
#
# Parameter:
# A single (possibly MIME-encoded) word.
#
# Return value:
# If decoding is ok, set the result into VARY and return 0
# Otherwise return 1
#
decode_word()
{
word="$*"
###################################################################
# An encoded word contains only ASCII characters in range from
# '!' (Ascii value 0x21) to '~' (Ascii value 0x7e). This excludes
# in particular the SPACE (Ascii 0x20) and the TAB (Ascii 0x09).
#
# More specifically, it consists of five parts separated by
# question marks '?'
# 1. A character "="
# 2. The charset, e.g. "UTF-8" or "ISO-8859-1"
# 3. The encoding, B or Q in upper or lower case
# 4. The encoded text
# 5. A character "="
###################################################################

# Check that:
# - there is no character outside range from '!' to '~'
# - the 1st part is a "="
# - the 5th part is a "=" and it is the end of the string
if [ $(LANG=C expr "_$word" : '_[!-~]*$') = 0 ]; then return 1; fi
part1=$(printf "$word" | cut -f 1 -d '?')
part5=$(printf "$word" | cut -f 5- -d '?')
if [ "$part1" != "=" -o "$part5" != "=" ]; then return 1; fi

# Extract charset, encoding, and encoded text
charset=$(printf "$word" | cut -f 2 -d '?')
encoding=$(printf "$word" | cut -f 3 -d '?')
encoded=$(printf "$word" | cut -f 4 -d '?')

case $encoding in
B | b)
decoded=$(printf "$encoded" | decode_b64 2>/dev/null)
if [ $? != 0 ]; then return 1; fi
;;
Q | q)
decoded=$(printf "$encoded" | decode_qp 2>/dev/null)
if [ $? != 0 ]; then return 1; fi
;;
*)
return 1
;;
esac

VARY=$(printf "$decoded" | iconv -f $charset -t UTF-8 2>/dev/null)
return $?
}

#
# Function: add_word
#
# Description:
# Try to decode a new word, and update DECODED_LINE and STATUS
# depending on the result and the previous STATUS.
#
# Parameter:
# A single (possibly MIME-encoded) word.
#
# Return value:
# None
#
# Side effects:
# Change DECODED_LINE and STATUS
#
add_word()
{
# Manage possible initial and final parentheses
# $p1 = prefix, $p2 = word, $p3 = suffix
p123="$*"
p1=$(printf "%s" "${p123}" | sed -e 's/^\([()]*\).*/\1/')
p23="${p123:${#p1}}"
p2=$(printf "%s" "${p23}" | sed -e 's/[()]*$//')
p3="${p23:${#p2}}"

if decode_word "$p2"; then
word="${p1}${VARY}${p3}"
if [ "$STATUS" = "normal" ]; then
DECODED_LINE="${DECODED_LINE} "
elif [ "$STATUS" != "none" -a -n "${p1}" ]; then
DECODED_LINE="${DECODED_LINE} "
fi
DECODED_LINE="${DECODED_LINE}${word}"
if [ -n "${p3}" ]; then
STATUS="normal"
else
STATUS="decoded-word"
fi
else
word="${p123}"
if [ "$STATUS" != "none" ]; then
DECODED_LINE="${DECODED_LINE} "
fi
DECODED_LINE="${DECODED_LINE}${word}"
STATUS="normal"
fi
}

#
# Function: flush_line
#
# Description:
# Before beginning to manage a new header, or just before ending
# the script, print the pending DECODED_LINE if any.
#
# Parameter:
# None
#
# Return value:
# None
#
# Side effects:
# Print things to stdout
# Change DECODED_LINE and STATUS
#
flush_line()
{
if [ -n "${DECODED_LINE}" ]; then
printf "%s\n" "${DECODED_LINE}"
DECODED_LINE=""
STATUS="none"
fi
}

#
# Function: manage_line
#
# Description:
# Manage a new line, which can be either the beginning or the
# continuation of a mail/news header.
# This function prints the previous line if this is a new one,
# then it adds successive parts to the new DECODED_LINE, while
# updating STATUS as needed.
#
# Parameter:
# An input line.
#
# Return value:
# None
#
# Side effects:
# Print things to stdout
# Change DECODED_LINE and STATUS
#
manage_line()
{
line="$*"

# Is it a continuation line?
if [ $(LANG=C expr "_$line" : "_[ \t]") = 0 ]; then
# No: new header
flush_line
fi

for word in $line; do
add_word "$word"
done
}

#
# Function: manage_file
#
# Description:
# Call manage_line for each line in a given file
#
# Parameter:
# A file name, or "-" for stdin
#
# Return value:
# None
#
# Side effects:
# Same as manage_line
#
manage_file()
{
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
manage_line "$line"
done < <(cat -- "$file")
}

#
# Parse arguments for -h or --help
#
for i in "$@"; do
case $i in
-h | --help)
usage
;;
-)
;;
-*)
printf "Unknown argument '%s'\n" "$i"
usage
;;
esac
done

#
# Main loop.
# Call manage_file for each filename in parameters,
# then print the last pending DECODED_LINE if any.
#
if [ "$*" = "" ]; then
manage_file "-"
else
for file in "$@"; do
manage_file "$file"
done
fi
flush_line

exit 0


--
Olivier Miakinen

10 réponses

1 2 3
Avatar
josephb
Olivier Miakinen <om+ wrote:
Cette fois ça devrait être bon.

espérons ;-)
voilà ce que ça donne chez moi dans le Terminal
mac:~ iMacJB$ olivier5.sh ~/Desktop/TestDecode.txt
Message-ID:
# From: (Arvo Pärt)
# To: Frédéric Chopin ,
# Antonín Dvo?ák
# Cc: Arvo Pärt
# References:
#
# In-Reply-To:
# Subject: Re: Going to ?æ© (Tokyo) ?
# ???? (Athens) and ????
# ?? (Moscow)
Les caractères spéciaux (Latin étendu et charsets exotiques ) sont bien
transcrits, ce qui semble clocher ce sont les lignes qui n'auraient pas
dû être coupées ?
Cordialement
--
J. B.
Avatar
josephb
Joseph-B déconna :
ce qui semble clocher ce sont les lignes qui n'auraient pas
dû être coupées ?

j'avais laissé les caractères de commentaire :-/
imac:~ iMacJB$ olivier5.sh /Users/iMacJPW/Desktop/TestDecode.txt
Message-ID:
From: (Arvo Pärt)
To: Frédéric Chopin , Antonín Dvo?ák
Cc: Arvo Pärt
References:
In-Reply-To:
Subject: Re: Going to ?æ© (Tokyo) ????? (Athens) and ?????? (Moscow)
--
J. B.
Avatar
M.V.
C'est à 19:57 le 19 octobre 2019 que le dénommé Joseph-B a écrit ces
lignes :
olivier5.sh ~/Desktop/TestDecode.txt

On est donc tenu de passer par un fichier texte dans le Terminal ! Ça
fait je ne sais combien de fois que je posais la question !!!
Je réponds à Olivier de ce pas.
Bonne soirée.
--
Michel VAUQUOIS - http://michelvauquois.fr
Avatar
josephb
M.V. wrote:
J'ai ressorti de mes cartons la "fameuse" fonction UnixParagraphs(datas) !
Tu te souviens de ça, Joseph-B ?
Très efficace indeed. ;-)

C'était pour MountUMount, non ?
ou pour une autre applet que nous avions dû contourner ce bug (mais je
ne sais plus quelle commande shell renvoyait des chaînes dont AS
interprétait mal les retours à la ligne…)
Les TID, c'est largement aussi balaise que sed ou tr et parfois mieux
même ;-)
Bonne soirée sabbatique ;-)
--
J. B.
Avatar
M.V.
Le 19 octobre 2019 à 21:47, Joseph-B m'a répondu :
était pour MountUMount, non ?

Oui ! Quand on récupérait le résultat de
do shell script "diskutil list" entre autre !
Je vais finir ma soirée en compagnie des Blacks et des Irlandais ! ;-)
Bonne soirée.
--
Michel VAUQUOIS - http://michelvauquois.fr
Avatar
josephb
M.V. wrote:
Parce que le stin et le stout c'était encore inconnus
jusqu'à ton post !"LE" stout, pas "LA" stout que je connais
depuis un moment ! ;-)

hum, c'est stdin et stdout, avec un d
raccourcis dont est si friand Unix pour
STandarD INput et STandarD OUTput
pas stin ou Stout en lieu et place de la Kro ;-)
Ça mérite bien une bonne Mort Subite pour faire passer le jargon ;-)
--
J. B.
Avatar
Olivier Miakinen
Le 19/10/2019 18:39, j'écrivais :
Cette fois ça devrait être bon.
La prochaine étape consistera à écrire directement les fonctions
decode_qp et decode_b64 en shell au lieu d'appeler des programmes
externes. ;-)

C'est fait ! En tout cas ça fonctionne chez moi et je n'ai plus besoin
des programmes externes qprint et base64. Il suffit de remplacer les
fonctions decode_qp et decode_b64 de mon script numéro 5 par le code
suivant :
function decode_qp()
{
while IFS= read -n 1 char; do
case $char in
"_")
printf " "
;;
"=")
IFS= read -n 2 hex
if [ $(LANG=C expr "_${hex}" : '_[0-9A-Fa-f]*') != 3 ]; then
return 1
fi
printf "%b" "x${hex}"
;;
*)
printf "%s" "$char"
;;
esac
done
return 0
}
B64CHARS="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
function decode_b64()
{
while IFS= read -n 4 chunk; do
c1="${chunk:0:1}"
c2="${chunk:1:1}"
c3="${chunk:2:1}"
c4="${chunk:3:1}"
if i1=$(expr index "${B64CHARS}" "_${c1}"); then
n32=$(( (i1 - 1) << 18 ))
else
return 1
fi
if i2=$(expr index "${B64CHARS}" "_${c2}"); then
n32=$(( $n32 | ( (i2 - 1) << 12 ) ))
printf $(printf "%03o" $(($n32 >> 16)) )
else
return 1
fi
if [ "${c3}" = "=" ]; then
return 0
elif i3=$(expr index "${B64CHARS}" "_${c3}"); then
n32=$(( $n32 | ( (i3 - 1) << 6 ) ))
printf $(printf "%03o" $(( ($n32 >> 8) & 255 )) )
else
return 1
fi
if [ "${c4}" = "=" ]; then
return 0
elif i4=$(expr index "${B64CHARS}" "_${c4}"); then
n32=$(( $n32 | (i4 - 1) ))
printf $(printf "%03o" $(( $n32 & 255 )) )
else
return 1
fi
done
return 0
}
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 20/10/2019 01:41, Olivier Miakinen a écrit :
C'est fait ! En tout cas ça fonctionne chez moi [...]

Si ça ne fonctionne pas sur Mac que ce soit en QP ou en B64, j'aimerais
bien un exemple de décodage avec une seule chaîne, après avoir mis un
« set -x » dans la fonction qui déconne, que ce soit decode_qp ou
decode_b64..
Si les deux déconnent, je veux bien un exemple de chaque (même s'il
y a quelques chances que ce soit le même bug pour les deux).
--
Olivier Miakinen
Avatar
josephb
Olivier Miakinen <om+ wrote:
C'est fait ! En tout cas ça fonctionne chez moi et je n'ai plus besoin
des programmes externes qprint et base64. Il suffit de remplacer les
fonctions decode_qp et decode_b64 de mon script numéro 5 par le code
suivant :

Corrige moi d'un doute :
du fait de ces fonctions intégrées, il n'y a plus besoin de distinguer entre Linux et Darwin ?
j'ai fait la substitution en enlevant la clause conditionnelle et ça me donne dans le Terminal, à partir du fichier sur le bureau
le From = OK (contient du QP)
le To = OK
le Cc = OK
le References = OK
Le In-Reply-To = OK
là où ça se gâte c'est dans le Subject : les 3 lignes sont bien concaténées en une seule
en sortie, mais seulement le charset de Moscow a été décodé convenablement
Subject: Re: Going to =?Shift-JIS?B?k4yLngo=?= (Tokyo) =?UTF-8?B?zpEK?= =?UTF-8?B?zrjOrs69zrEK?= (Athens) and ?????? (Moscow)
En résumé, je vois que partout le QP a bien été décodé (quel que soit le charset) mais que le Base-64 ne l'est pas
J'ai peut-être mal intégré la fonction (pas de message d'erreur dans le Terminal)
Peut-être que pour le béotien que je suis il serait mieux que tu postes la version complète ;-)
Autre essai avec du Base-64 envoyé en stdin
echo "Subject: =?UTF-8?B?UmU6IFNjcmlwdCBwb3VyIGTDqWNvZGVyIGxlcyBlbnTDqnQ=? > =?UTF-8?B?ZXMgTUlNRQ==?=" | olivier6.sh
retourne
Subject: =?UTF-8?B?UmU6IFNjcmlwdCBwb3VyIGTDqWNvZGVyIGxlcyBlbnTDqnQ=?= =?UTF-8?B?ZXMgTUlNRQ==?
Cordialement,
--
J. B.
Avatar
josephb
Olivier Miakinen <om+ wrote:
en B64, j'aimerais
bien un exemple de décodage avec une seule chaîne, après avoir mis un
« set -x » dans la fonction

j'ai fait ça
function decode_b64()
{
set -x
while IFS= read -n 4 chunk; do
c1="${chunk:0:1}"
c2="${chunk:1:1}"
c3="${chunk:2:1}"
etc.
et j'ai essayé
echo "Subject: Re: Going to =?Shift-JIS?B?k4yLngo=?= (Tokyo)" | olivier6.sh
--> Subject: Re: Going to =?Shift-JIS?B?k4yLngo=?= (Tokyo)
sans le détail des opérations.
HTH
--
J. B.
1 2 3