1#!/bin/sh 2 3# * Copyright 2004 Tristan Chabredier <wwp@claws-mail.org> 4# * 5# * This file is free software; you can redistribute it and/or modify it 6# * under the terms of the GNU General Public License as published by 7# * the Free Software Foundation; either version 3 of the License, or 8# * (at your option) any later version. 9# * 10# * This program is distributed in the hope that it will be useful, but 11# * WITHOUT ANY WARRANTY; without even the implied warranty of 12# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13# * General Public License for more details. 14# * 15# * You should have received a copy of the GNU General Public License 16# * along with this program; if not, write to the Free Software 17# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18# 19# fix_date.sh helper script to fix non-standard date or add missing 20# date header to emails 21 22# usage: fix_date.sh <filename> [<filename> ..] 23# It will replace the Date: value w/ the one picked up from more recent 24# Fetchinfo time header, Received: field.. Otherwise, it will take the file 25# modification time (using a RFC 2822-compliant form). 26# Any already existing X-Original-Date is kept, if missing we're adding it 27# if the Date: was set (even if set w/ non conform value) 28 29# TODO: fallback to X-OriginalArrivalTime: ? 30 31VERSION="0.1.3" 32 33 34version() 35{ 36 echo "$VERSION" 37 exit 0 38} 39 40usage() 41{ 42 echo "usage:" 43 echo " ${0##*/} [<switches>] <filename> [<filename> ..]" 44 echo "switches:" 45 echo " -h --help display this help then exit" 46 echo " -v --version display version information then exit" 47 echo " -d --debug turn on debug information (be more verbose)" 48 echo " -f --force always force (re-)writing of Date: header" 49 echo " -r --rfc force re-writing of Date: header when it's not RFC-compliant" 50 echo " -s --strict use RFC-strict matching patterns for dates" 51 echo " -- end of switches (in case a filename starts with a -)" 52 exit $1 53} 54 55date_valid() 56{ 57 test $STRICT -eq 1 && \ 58 REGEXP="$DATE_REGEXP_STRICT" || \ 59 REGEXP="$DATE_REGEXP" 60 61 echo "$1" | grep -qEim 1 "$REGEXP" 62 DATE_VALID=$? 63} 64 65dump_date_fields() 66{ 67 test -z "$X_ORIGINAL_DATE" -a -n "$DATE" && \ 68 echo "X-Original-Date:$DATE" >> "$TMP" 69 echo "Date:$REPLACEMENT_DATE" >> "$TMP" 70} 71 72 73# use --force to always (re-)write the Date header 74# otherwise, the Date header will be written if only it doesn't exist 75FORCE=0 76# use --rfc to (re-)write the Date header when it's not RFC-compliant 77# otherwise, the Date header will be written if only it doesn't exist 78RFC=0 79# use --debug to display more information about what's performed 80DEBUG=0 81# use --strict to use strict matching patterns for date validation 82STRICT=0 83# 0 = valid, always valid until --strict is used, then date_valid overrides this value 84DATE_VALID=0 85# max header lines (300 is a reasonable minimum value but 600 has already been encountered, set to 1000 by security) 86MAX_HEADER_LINES=1000 87 88while [ -n "$1" ] 89do 90 case "$1" in 91 -h|--help) usage 0;; 92 -v|--version) version;; 93 -f|--force) FORCE=1;; 94 -d|--debug) DEBUG=1;; 95 -r|--rfc) RFC=1;; 96 -s|--strict) STRICT=1;; 97 --) shift 98 break;; 99 -*) echo "error: unrecognized switch '$1'" 100 usage 1;; 101 *) break;; 102 esac 103 shift 104done 105 106if [ $FORCE -eq 1 -a $RFC -eq 1 ] 107then 108 echo "error: use either --force or --rfc, but not both at the same time" 109 usage 1 110fi 111 112test $# -lt 1 && \ 113 usage 1 114 115TMP="/tmp/${0##*/}.$$.tmp" 116HEADERS="/tmp/${0##*/}.$$.headers.tmp" 117BODY="/tmp/${0##*/}.$$.body.tmp" 118 119DATE_REGEXP='( (Mon|Tue|Wed|Thu|Fri|Sat|Sun),)? [0-9]+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]+ [0-9]+:[0-9]+:[0-9]+ [+-]?[0-9]+' 120DATE_REGEXP_STRICT='(Mon|Tue|Wed|Thu|Fri|Sat|Sun), [0-9]+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-9]+ [0-9]+:[0-9]+:[0-9]+ [+-]?[0-9]+' 121 122while [ -n "$1" ] 123do 124 # skip if file is empty or doesn't exist 125 if [ ! -s "$1" ] 126 then 127 test $DEBUG -eq 1 && \ 128 echo "$1: no found or empty, skipping" 129 shift 130 continue 131 fi 132 SKIP=0 133 134 # split headers and body 135 # find the empty line that separates body (if any) from headers, 136 # work on a temporary dos2unix'ed copy because body might 137 # contain DOS CRLF and grep '^$' won't work 138 head -$MAX_HEADER_LINES "$1" | dos2unix > "$TMP" 139 SEP=`grep -nEm1 "^$" "$TMP" 2>/dev/null | cut -d ':' -f 1` 140 rm -f "$TMP" 141 if [ -z "$SEP" -o "$SEP" = "0" -o $? -ne 0 ] 142 then 143 cp -f "$1" "$HEADERS" 144 :> "$BODY" 145 test $DEBUG -eq 1 && \ 146 echo "$1: no body part could be found before line $MAX_HEADER_LINES" 147 else 148 sed -n '1,'`expr $SEP - 1`'p' "$1" > "$HEADERS" 149 sed '1,'`expr $SEP - 1`'d' "$1" > "$BODY" 150 fi 151 152 # work on headers only 153 154 # get the Date and X-Original-Date 155 X_ORIGINAL_DATE=`sed -n '/^X-Original-Date:/,/^[^\t]/p' "$HEADERS" | head -n -1 | cut -d ':' -f 2-` 156 DATE=`sed -n '/^Date:/,/^[^\t]/p' "$HEADERS" | head -n -1 | cut -d ':' -f 2-` 157 158 # work on headers, minus Date and X-Original-Date 159 test -n "$X_ORIGINAL_DATE" && \ 160 sed -i '/^X-Original-Date:/,/^[^\t]/d' "$HEADERS" 161 test -n "$DATE" && \ 162 sed -i '/^Date:/,/^[^\t]/d' "$HEADERS" 163 164 # find a replacement date in Fetchinfo: header 165 FETCH_DATE=`grep -im1 'X-FETCH-TIME: ' "$HEADERS" | cut -d ' ' -f 2-` 166 167 # or in Received: headers .. 168 test $STRICT -eq 1 && \ 169 REGEXP="$DATE_REGEXP" || \ 170 REGEXP="$DATE_REGEXP_STRICT" 171 RECEIVED_DATE=`sed -n '/^Received:/,/^[^\t]/p' "$HEADERS" | head -n -1 | grep -Eoim 1 "$REGEXP"` 172 173 # .. or from file properties 174 FILE_DATE=`LC_ALL=POSIX LANG=POSIX ls -l --time-style="+%a, %d %b %Y %X %z" "$1" | tr -s ' ' | cut -d ' ' -f 6-11` 175 # we could also use the system date as a possible replacement 176 SYSTEM_DATE="`date -R`" 177 178 # determine which replacement date to use 179 if [ -z "$FETCH_DATE" ] 180 then 181 if [ -z "$RECEIVED_DATE" ] 182 then 183 # don't forget the leading whitespace here 184 REPLACEMENT_DATE=" $FILE_DATE" 185 REPLACEMENT="file date" 186# REPLACEMENT_DATE=" $SYSTEM_DATE" 187# REPLACEMENT="system date" 188 else 189 REPLACEMENT_DATE="$RECEIVED_DATE" 190 REPLACEMENT="received date" 191 fi 192 else 193 # don't forget the leading whitespace here 194 REPLACEMENT_DATE=" $FETCH_DATE" 195 REPLACEMENT="Fetchinfo time header" 196 fi 197 198 # ensure that the original X-Original-Date is kept 199 :> "$TMP" 200 if [ -n "$X_ORIGINAL_DATE" ] 201 then 202 echo "X-Original-Date:$X_ORIGINAL_DATE" >> "$TMP" 203 fi 204 205 # replace/set the date and write all lines 206 test $RFC -eq 1 && \ 207 date_valid "$DATE" 208 if [ -z "$DATE" ] 209 then 210 test $DEBUG -eq 1 && \ 211 echo "$1: date not found, using $REPLACEMENT now" 212 dump_date_fields 213 else 214 if [ $FORCE -eq 1 ] 215 then 216 test $DEBUG -eq 1 && \ 217 echo "$1: date already found, replacing with $REPLACEMENT" 218 dump_date_fields 219 else 220 if [ $RFC -eq 1 ] 221 then 222 if [ $DATE_VALID -ne 0 ] 223 then 224 test $DEBUG -eq 1 && \ 225 echo "$1: date already found but not RFC-compliant, replacing with $REPLACEMENT" 226 dump_date_fields 227 else 228 test $DEBUG -eq 1 && \ 229 echo "$1: date already found and RFC-compliant, skipping" 230 SKIP=1 231 fi 232 else 233 test $DEBUG -eq 1 && \ 234 echo "$1: date already found, skipping" 235 SKIP=1 236 fi 237 fi 238 fi 239 240 if [ $SKIP -eq 0 ] 241 then 242 # uncomment the following line to backup the original file 243 #mv -f "$1" "$1.bak" 244 245 cat "$HEADERS" >> "$TMP" 246 cat "$BODY" >> "$TMP" 247 mv -f "$TMP" "$1" 248 if [ $? -ne 0 ] 249 then 250 echo "error while moving '$TMP' to '$1'" 251 exit 1 252 fi 253 fi 254 rm -f "$HEADERS" "$BODY" "$TMP" >/dev/null 2>&1 255 256 shift 257done 258exit 0 259