diff --git a/epsiEDTtoICS.sh b/epsiEDTtoICS.sh index 84d4117..bd4f0cf 100755 --- a/epsiEDTtoICS.sh +++ b/epsiEDTtoICS.sh @@ -8,17 +8,19 @@ # # From EPSI “edtmobilityeng” to ICS, for the current school year. Usage: # -# epsiEDTtoICS.sh -u [ -c [ | - ] ] [ -j ] -# epsiEDTtoICS.sh -u [ -p [ | - ] [ -w ] ] [ -j ] +# epsiEDTtoICS.sh -u [ -c [ | - ] ] [ -j ] [ -m ] +# epsiEDTtoICS.sh -u [ -p [ | - ] [ -w ] ] [ -j ] [ -m ] # : EPSI Login (firstname.lastname) # : Location where the ICS file is created (-c) or patched (-p) # : Number of weeks to update (including the current one), if -p is specified +# : Comma-separated list of email recipients (needs sendmail in the PATH) # # If no path is given, “-” is assumed, which is a placeholder for the standard output. # If neither -c nor -p is given, “-c -” is assumed. # If -p is used with “-” as a path, then only the updated weeks are sent to the standard output. # If -p is given without -w, the default count of updated weeks is 5. # If -j is used, logs are sent to systemd-journald instead of the standard error (needs systemd-cat). +# If -m is used, the given recipients are notified of changes between runs LANG8BIT=fr_FR@euro @@ -27,14 +29,16 @@ PATCH= OUTPUT=- MAXOFFSET= JOURNAL= +TO= readonly -a LEVELS=( emerg alert crit err warning notice info debug ) -while getopts u:c:p:w:j arg; do case "$arg" in +while getopts u:c:p:w:jm: arg; do case "$arg" in u) LOGIN="$OPTARG" ;; c) OUTPUT="${OPTARG:--}"; PATCH= ;; p) OUTPUT="${OPTARG:--}"; PATCH=true ;; w) MAXOFFSET="${OPTARG//[^0-9]}" ;; j) JOURNAL=true ;; +m) TO="$OPTARG" ;; esac; done # $1: log level ∈ [0 … 7] @@ -72,6 +76,10 @@ function check_parameters_and_set_defaults() { JOURNAL= log 4 "Command “systemd-cat” not found" fi + if [ -n "$TO" ] && ! which sendmail >/dev/null; then + TO= + log 4 "Command “sendmail” not found" + fi MAXOFFSET=$((${MAXOFFSET:-5}*7)) } @@ -83,9 +91,61 @@ function lf_to_crlf() { sed 's/$/\r/' } -function write_output() { +function crlf_to_lf() { + tr -d '\r' +} + +# &0: standard ical, with long lines wrapped +# &1: long lines are unwrapped +function unwrap_ical_long_lines() { + sed ':n;N;s/\n //;tn;P;D' +} + +# $1: path to the old file +# $2: path to the new file +function format_diff_and_send_mail() { + { + echo "To: $TO" + echo "Subject: Changes in EPSI calendar" + echo + diff -u8 \ + <(crlf_to_lf <"$1" | unwrap_ical_long_lines | grep -vE '^DTSTAMP:|^UID:') \ + <(crlf_to_lf <"$2" | unwrap_ical_long_lines | grep -vE '^DTSTAMP:|^UID:') \ + | awk ' + function out() { + if (inDiff+inEvent==2) printf("%s", diff) + inDiff=0 + inEvent=0 + } + /BEGIN:VEVENT/ { inEvent=1; diff="\n"; next } + $0 ~ /^[-+]/ { inDiff=1 } + /^@|END:VEVENT/ { out() } + { diff=diff $0 "\n" }' \ + | while IFS='' read -r l; do + case "$l" in + '') + echo; continue ;; + ?DT*) + h="$(sed -r 's/^.DT(.*):.*/\1/' <<<"$l")" + v="$(date -d $(sed -r 's/.*:(....)(..)(..T..)(..)(..Z)/\1-\2-\3:\4:\5/' <<<"$l"))" ;; + *) + IFS=: read -r h v <<<"${l#?}" ;; + esac + printf '%s %11s: %s\n' "${l:0:1}" "$h" "$v" + done + } \ + | lf_to_crlf \ + | sendmail -t +} + +function write_output_and_send_mail() { [ "$OUTPUT" == - ] && cat || { - cat >"${OUTPUT}.tmp" && mv -f "${OUTPUT}.tmp" "$OUTPUT" || { + cat >"${OUTPUT}.tmp" \ + && { + [ -n "$TO" ] && format_diff_and_send_mail "$OUTPUT" "${OUTPUT}.tmp" + mv -f "${OUTPUT}.tmp" "$OUTPUT" + } \ + || { log 3 "Failed to write to: $OUTPUT" exit 20 } @@ -304,7 +364,7 @@ function main() { } \ | fold_long_lines \ | lf_to_crlf \ - | write_output + | write_output_and_send_mail } main