File: fix_date.sh

package info (click to toggle)
claws-mail 4.3.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 51,124 kB
  • sloc: ansic: 268,194; cpp: 19,477; xml: 11,269; sh: 5,794; perl: 2,767; makefile: 2,509; yacc: 2,470; python: 334; lex: 293
file content (275 lines) | stat: -rwxr-xr-x 7,828 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#!/bin/sh

#  * Copyright 2004 Tristan Chabredier <wwp@claws-mail.org>
#  *
#  * This file is free software; you can redistribute it and/or modify it
#  * under the terms of the GNU General Public License as published by
#  * the Free Software Foundation; either version 3 of the License, or
#  * (at your option) any later version.
#  *
#  * This program is distributed in the hope that it will be useful, but
#  * WITHOUT ANY WARRANTY; without even the implied warranty of
#  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  * General Public License for more details.
#  *
#  * You should have received a copy of the GNU General Public License
#  * along with this program; if not, write to the Free Software
#  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# fix_date.sh		helper script to fix non-standard date or add missing
#					date header to emails

# usage: fix_date.sh <filename> [<filename> ..]
# It will replace the Date: value w/ the one picked up from more recent
# Fetchinfo time header, Received: field.. Otherwise, it will take the file
#  modification time (using a RFC 2822-compliant form).
# Any already existing X-Original-Date is kept, if missing we're adding it
# if the Date: was set (even if set w/ non conform value)

# TODO: fallback to X-OriginalArrivalTime: ?

VERSION="0.1.4"


version()
{
	echo "$VERSION"
	exit 0
}

usage()
{
	echo "usage:"
	echo "  ${0##*/} [<switches>] <filename> [<filename> ..]"
	echo "switches:"
	echo "  -h --help     display this help then exit"
	echo "  -v --version  display version information then exit"
	echo "  -d --debug    turn on debug information (be more verbose)"
	echo "  -f --force    always force (re-)writing of Date: header"
	echo "  -r --rfc      force re-writing of Date: header when it's not RFC-compliant"
	echo "  -s --strict   use RFC-strict matching patterns for dates"
	echo "  --            end of switches (in case a filename starts with a -)"
    echo "this script requires coreutils (cat, cut, head, tr), dos2unix, grep and set"
    echo "in PATH to work"
	exit $1
}

date_valid()
{
	test $STRICT -eq 1 && \
		REGEXP="$DATE_REGEXP_STRICT" || \
		REGEXP="$DATE_REGEXP"
		
	echo "$1" | grep -qEim 1 "$REGEXP"
	DATE_VALID=$?
}

dump_date_fields()
{
	test -z "$X_ORIGINAL_DATE" -a -n "$DATE" && \
		echo "X-Original-Date:$DATE" >> "$TMP"
	echo "Date:$REPLACEMENT_DATE" >> "$TMP"
}


# use --force to always (re-)write the Date header
# otherwise, the Date header will be written if only it doesn't exist
FORCE=0
# use --rfc to (re-)write the Date header when it's not RFC-compliant
# otherwise, the Date header will be written if only it doesn't exist
RFC=0
# use --debug to display more information about what's performed
DEBUG=0
# use --strict to use strict matching patterns for date validation
STRICT=0
# 0 = valid, always valid until --strict is used, then date_valid overrides this value
DATE_VALID=0
# max header lines (300 is a reasonable minimum value but 600 has already been encountered, set to 1000 by security)
MAX_HEADER_LINES=1000

while [ -n "$1" ]
do
	case "$1" in
	-h|--help)		usage 0;;
	-v|--version)	version;;
	-f|--force)		FORCE=1;;
	-d|--debug)		DEBUG=1;;
	-r|--rfc)		RFC=1;;
	-s|--strict)	STRICT=1;;
	--)				shift
					break;;
	-*)				echo "error: unrecognized switch '$1'"
					usage 1;;
	*)				break;;
	esac
	shift
done

if [ $FORCE -eq 1 -a $RFC -eq 1 ]
then
	echo "error: use either --force or --rfc, but not both at the same time"
	usage 1
fi

test $# -lt 1 && \
	usage 1

for PROG in dos2unix grep sed
do
    type "$PROG" >/dev/null 2>&1 || \
        { echo "error: $PROG not found in PATH"; exit 1; }
done

TMPDIR="/tmp"
TMP="$TMPDIR/${0##*/}.$$.tmp"
echo > "$TMP" >/dev/null 2>&1
if [ $? -eq 0 ]
then
    rm -f "$TMP" >/dev/null 2>&1
else
    TMPDIR="$HOME"
    TMP="$TMPDIR/${0##*/}.$$.tmp"
fi
HEADERS="$TMPDIR/${0##*/}.$$.headers.tmp"
BODY="$TMPDIR/${0##*/}.$$.body.tmp"

DATE_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]+'
DATE_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]+'

while [ -n "$1" ]
do
	# skip if file is empty or doesn't exist
	if [ ! -s "$1" ]
	then
		test $DEBUG -eq 1 && \
			echo "$1: no found or empty, skipping"
		shift
		continue
	fi
	SKIP=0

	# split headers and body
	# find the empty line that separates body (if any) from headers,
	# work on a temporary dos2unix'ed copy because body might
	# contain DOS CRLF and grep '^$' won't work
	head -$MAX_HEADER_LINES "$1" | dos2unix > "$TMP"
	SEP=`grep -nEm1 "^$" "$TMP" 2>/dev/null | cut -d ':' -f 1`
	rm -f "$TMP"
	if [ -z "$SEP" -o "$SEP" = "0" -o $? -ne 0 ]
	then
		cp -f "$1" "$HEADERS"
		:> "$BODY"
		test $DEBUG -eq 1 && \
			echo "$1: no body part could be found before line $MAX_HEADER_LINES"
	else
		sed -n '1,'`expr $SEP - 1`'p' "$1" > "$HEADERS"
		sed '1,'`expr $SEP - 1`'d' "$1" > "$BODY"
	fi

	# work on headers only

	# get the Date and X-Original-Date
	X_ORIGINAL_DATE=`sed -n '/^X-Original-Date:/,/^[^\t]/p' "$HEADERS" | head -n -1 | cut -d ':' -f 2-`
	DATE=`sed -n '/^Date:/,/^[^\t]/p' "$HEADERS" | head -n -1 | cut -d ':' -f 2-`

	# work on headers, minus Date and X-Original-Date
	test -n "$X_ORIGINAL_DATE" && \
		sed -i '/^X-Original-Date:/,/^[^\t]/d' "$HEADERS"
	test -n "$DATE" && \
		sed -i '/^Date:/,/^[^\t]/d' "$HEADERS"

	# find a replacement date in Fetchinfo: header
	FETCH_DATE=`grep -im1 'X-FETCH-TIME: ' "$HEADERS" | cut -d ' ' -f 2-`
	
	# or in Received: headers ..
	test $STRICT -eq 1 && \
		REGEXP="$DATE_REGEXP" || \
		REGEXP="$DATE_REGEXP_STRICT"
	RECEIVED_DATE=`sed -n '/^Received:/,/^[^\t]/p' "$HEADERS" | head -n -1 | grep -Eoim 1 "$REGEXP"`

	# .. or from file properties
	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`
	# we could also use the system date as a possible replacement
	SYSTEM_DATE="`date -R`"

	# determine which replacement date to use
	if [ -z "$FETCH_DATE" ]
	then
		if [ -z "$RECEIVED_DATE" ]
		then
			# don't forget the leading whitespace here
			REPLACEMENT_DATE=" $FILE_DATE"
			REPLACEMENT="file date"
#			REPLACEMENT_DATE=" $SYSTEM_DATE"
#			REPLACEMENT="system date"
		else
			REPLACEMENT_DATE="$RECEIVED_DATE"
			REPLACEMENT="received date"
		fi
	else
		# don't forget the leading whitespace here
		REPLACEMENT_DATE=" $FETCH_DATE"
		REPLACEMENT="Fetchinfo time header"
	fi

	# ensure that the original X-Original-Date is kept
	:> "$TMP"
	if [ -n "$X_ORIGINAL_DATE" ]
	then
		echo "X-Original-Date:$X_ORIGINAL_DATE" >> "$TMP"
	fi

	# replace/set the date and write all lines
	test $RFC -eq 1 && \
		date_valid "$DATE"
	if [ -z "$DATE" ]
	then
		test $DEBUG -eq 1 && \
			echo "$1: date not found, using $REPLACEMENT now"
		dump_date_fields
	else
		if [ $FORCE -eq 1 ]
		then
			test $DEBUG -eq 1 && \
				echo "$1: date already found, replacing with $REPLACEMENT"
			dump_date_fields
		else
			if [ $RFC -eq 1 ]
			then
				if [ $DATE_VALID -ne 0 ]
				then
					test $DEBUG -eq 1 && \
						echo "$1: date already found but not RFC-compliant, replacing with $REPLACEMENT"
					dump_date_fields
				else
					test $DEBUG -eq 1 && \
						echo "$1: date already found and RFC-compliant, skipping"
					SKIP=1
				fi
			else
				test $DEBUG -eq 1 && \
					echo "$1: date already found, skipping"
				SKIP=1
			fi
		fi
	fi

	if [ $SKIP -eq 0 ]
	then
		# uncomment the following line to backup the original file
		#mv -f "$1" "$1.bak"

		cat "$HEADERS" >> "$TMP"
		cat "$BODY" >> "$TMP"
		mv -f "$TMP" "$1"
		if [ $? -ne 0 ]
		then
			echo "error while moving '$TMP' to '$1'"
			exit 1
		fi
	fi
	rm -f "$HEADERS" "$BODY" "$TMP" >/dev/null 2>&1

	shift
done
exit 0