„MailAttachmentParser_nativ.sh“ hinzufügen
This commit is contained in:
parent
3ea38bc354
commit
6cf31d1290
|
@ -0,0 +1,575 @@
|
|||
#!/bin/bash
|
||||
# /volume1/homes/admin/script/MailAttachmentParser/MailAttachmentParser_nativ.sh
|
||||
# 2019-08-25 @ geimist
|
||||
# durchsucht den angegebenen Quellordner nach E-Mails mit Dateianlage, extrahiert und entpackt diese ggf. in den Zielordner mit laufender Nr.
|
||||
# (eingebette Anlagen (Content-Transfer-Encoding: quoted-printable) können derzeit nicht gesichert werden - war bei .csv bei mir der Fall)
|
||||
|
||||
# https://www.synology-forum.de/showthread.html?103185-Mail-an-NAS-und-den-Anhang-verwerten&p=833583&viewfull=1#post833583
|
||||
|
||||
#################################
|
||||
# #
|
||||
# PARAMETER ANPASSEN #
|
||||
# #
|
||||
#################################
|
||||
|
||||
MAILDIR="/volume1/homes/admin/.Maildir/cur" # Quellverzeichnis
|
||||
DESTDIR="/volume1/homes/admin/Mailanlagen/" # Zielverzeichnis
|
||||
DELDIR="/volume1/homes/admin/.Maildir/.Trash/cur" # Löschverzeichnis
|
||||
delmail="yes" # "yes" um abgearbeite E-Mails zu löschen
|
||||
unzipPW="0000" # Kennwort zum entpacken
|
||||
|
||||
|
||||
#################################
|
||||
# #
|
||||
# ab hier nichts mehr ändern! #
|
||||
# #
|
||||
#################################
|
||||
|
||||
# Arbeitsverzeichnis auslesen und hineinwechseln:
|
||||
OLDIFS=$IFS # ursprünglichen Fieldseparator sichern
|
||||
APPDIR=$(cd $(dirname $0);pwd)
|
||||
cd ${APPDIR}
|
||||
|
||||
if [ -d "$DESTDIR" ]; then
|
||||
DESTDIR="${DESTDIR%/}/"
|
||||
else
|
||||
mkdir -p "$DESTDIR"
|
||||
DESTDIR="${DESTDIR%/}/"
|
||||
fi
|
||||
|
||||
mime_inspect()
|
||||
{
|
||||
################################################################
|
||||
#### MIME interface ####
|
||||
#### analysiert E-Maildateien und gibt sie geordnet aus ####
|
||||
#### https://gist.github.com/markusfisch/2649043 ####
|
||||
################################################################
|
||||
|
||||
# Parse message in MIME format and create a temporary cache directory
|
||||
mime_parse()
|
||||
{
|
||||
MIME_CACHE=${MIME_CACHE:-`mktemp -d ${BIN}.XXXXXXXXXX`}
|
||||
# trap 'rm -rf "$MIME_CACHE"; exit' EXIT
|
||||
|
||||
local D=$MIME_CACHE
|
||||
local HEADER=1
|
||||
local LAST=
|
||||
local BOUNDARY=
|
||||
|
||||
while read
|
||||
do
|
||||
REPLY=${REPLY%$CR}
|
||||
|
||||
[ "$REPLY" == '.' ] && break
|
||||
|
||||
# in mime header
|
||||
if [ "$HEADER" ]
|
||||
then
|
||||
# header closed
|
||||
[ "$REPLY" ] || {
|
||||
HEADER=
|
||||
|
||||
[ -r "$D/content-type" ] && {
|
||||
local VALUE
|
||||
value "`< "$D/content-type"`" \
|
||||
'[Bb][Oo][Uu][Nn][Dd][Aa][Rr][Yy]='
|
||||
[ "$VALUE" ] && {
|
||||
BOUNDARY=$VALUE
|
||||
echo "$BOUNDARY" > "$D/boundary"
|
||||
}
|
||||
}
|
||||
|
||||
[ -r "$D/content-disposition" ] && {
|
||||
local VALUE
|
||||
value "`< $D/content-disposition`" \
|
||||
'[Ff][Ii][Ll][Ee][Nn][Aa][Mm][Ee]='
|
||||
[ "$VALUE" ] && {
|
||||
echo "$VALUE" >> "$MIME_CACHE/attachments"
|
||||
echo "$D" >> "$MIME_CACHE/attachments-paths"
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
local F
|
||||
if [[ "$REPLY" == [' '$'\t']* ]]
|
||||
then
|
||||
[ "$LAST" ] || continue
|
||||
F=$LAST
|
||||
else
|
||||
F=`lower "${REPLY%%:*}"`
|
||||
LAST=$F
|
||||
fi
|
||||
|
||||
echo ${REPLY#*:} >> "$D/$F"
|
||||
continue
|
||||
elif [ "$BOUNDARY" ] && [ "${REPLY:0:2}" == '--' ]
|
||||
then
|
||||
[[ "$REPLY" == --$BOUNDARY* ]] && {
|
||||
[ "$D" == "$MIME_CACHE" ] || D=${D%/*}
|
||||
|
||||
if [ "$REPLY" == "--$BOUNDARY--" ]
|
||||
then
|
||||
if [ -r "$D/boundary" ]
|
||||
then
|
||||
BOUNDARY=`< "$D/boundary"`
|
||||
else
|
||||
BOUNDARY=
|
||||
fi
|
||||
|
||||
HEADER=
|
||||
else
|
||||
local PART=1
|
||||
|
||||
[ -r "$D/parts" ] && {
|
||||
PART=`< "$D/parts"`
|
||||
(( ++PART ))
|
||||
}
|
||||
|
||||
echo $PART > "$D/parts"
|
||||
D="$D/part-$PART"
|
||||
|
||||
mkdir "$D" || return 1
|
||||
|
||||
HEADER=1
|
||||
fi
|
||||
}
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "$REPLY"$CR >> $D/body
|
||||
done
|
||||
}
|
||||
|
||||
# Free MIME data structure
|
||||
mime_free()
|
||||
{
|
||||
rm -rf $MIME_CACHE
|
||||
MIME_CACHE=
|
||||
}
|
||||
|
||||
# Decode possibly encoded message text
|
||||
#
|
||||
# @param 1 - message directory
|
||||
mime_decode_message()
|
||||
{
|
||||
local F="$1/body"
|
||||
|
||||
[ -r "$F" ] && {
|
||||
local T=`< "$1/content-type"` CS='cat'
|
||||
|
||||
case "$T" in
|
||||
[Tt][Ee][Xx][Tt]/*)
|
||||
local VALUE
|
||||
value "$T" '[Cc][Hh][Aa][Rr][Ss][Ee][Tt]='
|
||||
[ "$VALUE" ] &&
|
||||
CS="iconv -f $VALUE -t utf-8"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "`< "$1/content-transfer-encoding"`" in
|
||||
*[Qq][Uu][Oo][Tt][Ee][Dd]-[Pp][Rr][Ii][Nn][Tt][Aa][Bb][Ll][Ee]*)
|
||||
decode_quoted_printable
|
||||
;;
|
||||
*[Bb][Aa][Ss][Ee]64*)
|
||||
base64 -d -i
|
||||
;;
|
||||
*)
|
||||
cat
|
||||
;;
|
||||
esac < "$F" | $CS
|
||||
} 2>/dev/null
|
||||
}
|
||||
|
||||
# Display message with header information
|
||||
#
|
||||
# @param 1 - message directory
|
||||
mime_display_message()
|
||||
{
|
||||
# echo headers
|
||||
{
|
||||
local H HEADERS=${HEADERS:-from to subject date attachments}
|
||||
local M=0
|
||||
|
||||
# get length of longest header label
|
||||
{
|
||||
local L
|
||||
for H in $HEADERS
|
||||
do
|
||||
L=${#H}
|
||||
(( L > M )) &&
|
||||
M=$L
|
||||
done
|
||||
}
|
||||
|
||||
local W=$(( ${WIDTH:-80}-(M+2) ))
|
||||
for H in $HEADERS
|
||||
do
|
||||
local F="$1/$H"
|
||||
while ! [ -r "$F" ]
|
||||
do
|
||||
[ "$F" == "$MIME_CACHE/$H" ] && break
|
||||
F="$MIME_CACHE/$H"
|
||||
done
|
||||
[ -r "$F" ] || continue
|
||||
|
||||
local S
|
||||
if [ "$H" == 'attachments' ]
|
||||
then
|
||||
S=`< "$F"`
|
||||
S=${S//$'\n'/ }
|
||||
else
|
||||
S=`decode_encoded_word < "$F"`
|
||||
fi
|
||||
|
||||
local N L=${#S} LABEL=$H
|
||||
for (( N = 0; N < L; N += W ))
|
||||
do
|
||||
printf "%-${M}s %-${W}s\n" "$LABEL" "${S:$N:$W}"
|
||||
LABEL=
|
||||
done
|
||||
done
|
||||
|
||||
[ "$H" ] && echo
|
||||
}
|
||||
|
||||
mime_decode_message "$1"
|
||||
}
|
||||
|
||||
# Returns true if content type is text
|
||||
#
|
||||
# @param 1 - file with content type
|
||||
mime_content_is_text()
|
||||
{
|
||||
case "`< "$1"`" in
|
||||
*[Tt][Ee][Xx][Tt]/[Pp][Ll][Aa][Ii][Nn]*|\
|
||||
*[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll]*)
|
||||
return 0
|
||||
;;
|
||||
esac 2>/dev/null
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Traverse message tree to find message text
|
||||
#
|
||||
# @param 1 - directory in MIME tree
|
||||
# @param 2 - callback function
|
||||
mime_find_message()
|
||||
{
|
||||
(( $# < 2 )) && return 1
|
||||
|
||||
local TYPE=0
|
||||
|
||||
case "`< "$1/content-type"`" in
|
||||
*[Mm][Uu][Ll][Tt][Ii][Pp][Aa][Rr][Tt]/[Aa][Ll][Tt][Ee][Rr][Nn][Aa][Tt][Ii][Vv][Ee]*)
|
||||
TYPE=1
|
||||
;;
|
||||
*[Mm][Uu][Ll][Tt][Ii][Pp][Aa][Rr][Tt]/[Dd][Ii][Gg][Ee][Ss][Tt]*)
|
||||
TYPE=2
|
||||
;;
|
||||
esac 2>/dev/null
|
||||
|
||||
local N PARTS=`< "$1/parts"`
|
||||
|
||||
for (( N=1; N < PARTS; ++N ))
|
||||
do
|
||||
local P="$1/part-$N"
|
||||
|
||||
[ -r "$P/body" ] &&
|
||||
mime_content_is_text "$P/content-type" && {
|
||||
$2 "$P"
|
||||
(( TYPE == 2 )) || return 0
|
||||
}
|
||||
|
||||
(( TYPE == 2 )) && return 0
|
||||
|
||||
[ -r "$P/parts" ] && mime_find_message "$P" "$2" && return 0
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Echo message from MIME data structure
|
||||
#
|
||||
# @param 1 - callback function (optional)
|
||||
mime_message()
|
||||
{
|
||||
local C=${1:-mime_display_message}
|
||||
|
||||
[ -r "$MIME_CACHE/parts" ] &&
|
||||
mime_find_message "$MIME_CACHE" $C &&
|
||||
return
|
||||
|
||||
[ -r "$MIME_CACHE/content-type" ] && {
|
||||
mime_content_is_text "$MIME_CACHE/content-type" ||
|
||||
return
|
||||
}
|
||||
|
||||
$C "$MIME_CACHE"
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#### Encoding/Decoding
|
||||
##############################################################################
|
||||
|
||||
# Decode quoted-printable-encoded stream
|
||||
decode_quoted_printable()
|
||||
{
|
||||
local C=0 EOF=0
|
||||
|
||||
while (( ! EOF ))
|
||||
do
|
||||
read -d '=' || EOF=1
|
||||
|
||||
(( C )) &&
|
||||
if [[ $REPLY == [$'\r'$'\n']* ]]
|
||||
then
|
||||
REPLY=${REPLY:1}
|
||||
else
|
||||
printf \\x"${REPLY:0:2}"
|
||||
REPLY=${REPLY:2}
|
||||
fi
|
||||
|
||||
echo -n "$REPLY"
|
||||
C=1
|
||||
done
|
||||
}
|
||||
|
||||
# Decode MIME encoded-word syntax
|
||||
decode_encoded_word()
|
||||
{
|
||||
while read
|
||||
do
|
||||
while [[ $REPLY == *'=?'* ]]
|
||||
do
|
||||
echo -n ${REPLY%%'=?'*}
|
||||
local A=${REPLY#*'?='} V=${REPLY#*'=?'}
|
||||
V=${V%%'?='*}
|
||||
local P=( ${V//\?/ } )
|
||||
if (( ${#P[@]} == 3 ))
|
||||
then
|
||||
case "${P[1]}" in
|
||||
[Qq])
|
||||
echo -n "${P[2]}" | decode_quoted_printable
|
||||
;;
|
||||
[Bb])
|
||||
echo -n "${P[2]}" | base64 -d -i
|
||||
;;
|
||||
esac | iconv -f "${P[0]}" -t utf-8
|
||||
else
|
||||
echo -n $V
|
||||
fi
|
||||
|
||||
REPLY=$A
|
||||
done
|
||||
|
||||
echo -n $REPLY
|
||||
done
|
||||
}
|
||||
|
||||
which iconv &>/dev/null || iconv() {
|
||||
cat
|
||||
}
|
||||
|
||||
which base64 &>/dev/null || {
|
||||
echo 'error: base64 not found!' >&2
|
||||
echo 'Either install it or get this fallback implementation:' >&2
|
||||
echo 'https://gist.github.com/2648733' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#### String auxiliaries
|
||||
##############################################################################
|
||||
|
||||
# Make string lower case
|
||||
#
|
||||
# @param 1 - some string
|
||||
if [ $BASH_VERSINFO ] && (( ${BASH_VERSINFO[0]} > 3 ))
|
||||
then
|
||||
lower()
|
||||
{
|
||||
echo "${1,,}"
|
||||
}
|
||||
else
|
||||
lower()
|
||||
{
|
||||
echo "$1" | tr '[:upper:]' '[:lower:]'
|
||||
}
|
||||
fi
|
||||
|
||||
# Find a key/value pair in the given string and set VALUE accordingly
|
||||
#
|
||||
# @param 1 - string
|
||||
# @param 2 - pattern of key
|
||||
value()
|
||||
{
|
||||
[[ "$1" == *$2* ]] || {
|
||||
VALUE=
|
||||
return
|
||||
}
|
||||
|
||||
VALUE=${1#*$2}
|
||||
|
||||
local QUOTE="${VALUE:0:1}"
|
||||
case "$QUOTE" in
|
||||
'"'|"'")
|
||||
;;
|
||||
*)
|
||||
QUOTE=
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ "$QUOTE" ]
|
||||
then
|
||||
VALUE=${VALUE:1}
|
||||
VALUE=${VALUE%%$QUOTE*}
|
||||
else
|
||||
VALUE=${VALUE%% *}
|
||||
fi
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#### Features
|
||||
##############################################################################
|
||||
|
||||
# Manually check data structure
|
||||
#
|
||||
# @param 1 - message file
|
||||
inspect()
|
||||
{
|
||||
[ -r "$1" ] || {
|
||||
echo "error: file $1 not found" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
echo "(unpacking \"$1\")"
|
||||
|
||||
mime_parse < "$1" &&
|
||||
cd "$MIME_CACHE" && \
|
||||
ls && \
|
||||
PS1='inspect> ' bash && \
|
||||
cd ..
|
||||
|
||||
# mime_free
|
||||
}
|
||||
|
||||
# Dump message text
|
||||
#
|
||||
# @param 1 - message file
|
||||
dump()
|
||||
{
|
||||
mime_parse < "$1" &&
|
||||
mime_message
|
||||
|
||||
# mime_free
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
#### Command processing
|
||||
##############################################################################
|
||||
|
||||
# Process arguments
|
||||
#
|
||||
# @param ... - arguments
|
||||
mime()
|
||||
{
|
||||
(( $# < 1 )) && {
|
||||
cat <<EOF
|
||||
usage: ${BIN} [-di] FILE...
|
||||
d dump message (default)
|
||||
i inspect message tree
|
||||
|
||||
EOF
|
||||
return
|
||||
}
|
||||
|
||||
local F ACTION=dump
|
||||
|
||||
for F in "$@"
|
||||
do
|
||||
case "$F" in
|
||||
-i)
|
||||
ACTION=inspect
|
||||
continue
|
||||
;;
|
||||
-d)
|
||||
ACTION=dump
|
||||
continue
|
||||
;;
|
||||
-*)
|
||||
echo "error: unkown flag '$F'" >&2
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
$ACTION "$F"
|
||||
done
|
||||
}
|
||||
|
||||
#readonly BIN=${0##*/}
|
||||
BIN=${0##*/}
|
||||
#readonly CR=$'\r'
|
||||
CR=$'\r'
|
||||
|
||||
mime "$@"
|
||||
}
|
||||
|
||||
IFS=$'\012'
|
||||
for i in $(find "$MAILDIR" -type f)
|
||||
do
|
||||
IFS=$OLDIFS
|
||||
filename=$(basename "$i")
|
||||
if ( cat "$i" | grep -q "base64" ) && ( cat "$i" | egrep -q "attachment" ) ; then # prüfen, ob "base64" und "attachment" in der Mail vorkommen
|
||||
echo "verarbeite: $i"
|
||||
mime_inspect "$i" > /dev/null
|
||||
|
||||
mkdir "${APPDIR}/$MIME_CACHE/decoded/"
|
||||
rowid=0
|
||||
|
||||
while read j # Quelle zeilenweise einlesen
|
||||
do
|
||||
((rowid+=1))
|
||||
echo -n " gefundene Anlage: $j "
|
||||
base64log=$(base64 -di "$(sed -n "${rowid}p" "${APPDIR}/$MIME_CACHE/attachments-paths")/body" > "${APPDIR}/$MIME_CACHE/decoded/$j")
|
||||
|
||||
# check Extension / ggf. entpacken
|
||||
if echo "${APPDIR}/$MIME_CACHE/decoded/$j" | egrep -q "*.7z|*.zip"; then
|
||||
echo "(wird entpackt)"
|
||||
7z e "${APPDIR}/$MIME_CACHE/decoded/$j" -o"${APPDIR}/$MIME_CACHE/decoded/" -p${unzipPW} -aou > /dev/null
|
||||
rm "${APPDIR}/$MIME_CACHE/decoded/$j"
|
||||
else
|
||||
echo -e
|
||||
fi
|
||||
|
||||
# nach Zielordner verschieben mit Zähler je Extension:
|
||||
IFS=$'\012'
|
||||
for d in $(find "${APPDIR}/$MIME_CACHE/decoded/" -type f)
|
||||
do
|
||||
IFS=$OLDIFS
|
||||
name_d=$(basename "$d")
|
||||
fileextension="${name_d##*.}"
|
||||
name_d="${name_d%.*}"
|
||||
|
||||
count=$(ls -t "${DESTDIR}" | egrep -o "^$name_d.*.$fileextension$" | wc -l)
|
||||
|
||||
decfilename="${DESTDIR}${name_d}_(${count}).${fileextension}"
|
||||
mv "$d" "$decfilename"
|
||||
echo " Zieldatei: ${name_d}_(${count}).${fileextension}"
|
||||
done
|
||||
done < "${APPDIR}/$MIME_CACHE/attachments"
|
||||
|
||||
# Quelldatei löschen:
|
||||
if [ $delmail = "yes" ]; then
|
||||
echo " Quelldatei wird nach $DELDIR verschoben"
|
||||
mv "$i" "$DELDIR"
|
||||
fi
|
||||
|
||||
# Temp löschen:
|
||||
mime_free
|
||||
echo -e
|
||||
fi
|
||||
done
|
Loading…
Reference in New Issue