fix_date.sh 7.2 KB

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