fetch_aimex.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #!/usr/bin/env bash
  2. #
  3. # fetch_aimex.sh -- download the archived AIM Express client and buddy-icon
  4. # uploader from the Wayback Machine.
  5. #
  6. # Static URL lists. The id_ in each URL is what makes the archive return the
  7. # ORIGINAL bytes instead of its rewritten viewer page -- omit it and every .swf
  8. # comes back as HTML. The local path is whatever follows the build directory.
  9. #
  10. # Downloads are strictly SERIAL with a delay between every request. The Wayback
  11. # Machine throttles by refusing connections outright (curl exit 7, not an HTTP
  12. # 429) and stays angry for a while, so slow beats parallel.
  13. #
  14. # Usage:
  15. # ./fetch_aimex.sh # fetch anything missing under ./clients
  16. # ./fetch_aimex.sh -o somewhere # write somewhere else
  17. # ./fetch_aimex.sh -d 5 # be extra polite
  18. # ./fetch_aimex.sh --force # re-download everything
  19. set -uo pipefail
  20. EXPRESS_URLS=(
  21. "https://web.archive.org/web/20130905172104id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/AC_OETags.js"
  22. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/ConversationAssets.swf"
  23. "https://web.archive.org/web/20130905172110id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/OnlinePanel.swf"
  24. "https://web.archive.org/web/20150109230526id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/WidgetMain.html"
  25. "https://web.archive.org/web/20130905172106id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/WidgetMain.swf"
  26. "https://web.archive.org/web/20150115210119id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/_uac/adpage.html"
  27. "https://web.archive.org/web/20150426081851id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/aim-express/babybunny.png"
  28. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/aim-express/default_buddy_icon.png"
  29. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/aim-express/default_imserv_icon.png"
  30. "https://web.archive.org/web/20130905172109id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/aim-express/default_login_user_icon.png"
  31. "https://web.archive.org/web/20130905172109id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/aim-express/progress_animation.swf"
  32. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/check.png"
  33. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/facebook_h.png"
  34. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/facebook_n.png"
  35. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/ls_h.png"
  36. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/ls_n.png"
  37. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/plus.png"
  38. "https://web.archive.org/web/20150426081855id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/twitter_h.png"
  39. "https://web.archive.org/web/20150426081854id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/twitter_n.png"
  40. "https://web.archive.org/web/20150426081855id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/youtube_h.png"
  41. "https://web.archive.org/web/20150426081855id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/chicklets/youtube_n.png"
  42. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/all.gif"
  43. "https://web.archive.org/web/20150426081813id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/facebook.gif"
  44. "https://web.archive.org/web/20150426081813id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/lifestream.gif"
  45. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/location.gif"
  46. "https://web.archive.org/web/20150426081813id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/myspace.gif"
  47. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/photo.gif"
  48. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/status.gif"
  49. "https://web.archive.org/web/20150426081813id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/twitter.gif"
  50. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/filterTypes/video.gif"
  51. "https://web.archive.org/web/20100829133330id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/help/aim_or_fb.html"
  52. "https://web.archive.org/web/20130905172109id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/common/serviceicons/runningman.png"
  53. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/loadable/emoticons/GromitEmoticons.swf"
  54. "https://web.archive.org/web/20150426081812id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/norris.txt"
  55. "https://web.archive.org/web/20150115211959id_/http://o.aolcdn.com/aim/gromit/aim_express/gm/100820.5475.1.en-us/portal.php"
  56. )
  57. ICONUPLOADER_URLS=(
  58. "https://web.archive.org/web/20151127135337id_/http://o.aolcdn.com/aim/gromit/iconuploader/110128.1.5797/Main.html"
  59. "https://web.archive.org/web/20160126020222id_/http://o.aolcdn.com/aim/gromit/iconuploader/110128.1.5797/Main.swf"
  60. "https://web.archive.org/web/20120815104026id_/http://o.aolcdn.com/aim/gromit/iconuploader/110128.1.5797/redir.html"
  61. "https://web.archive.org/web/20160126020219id_/http://o.aolcdn.com/aim/gromit/iconuploader/110128.1.5797/swfobject.js"
  62. )
  63. OUTDIR="clients"
  64. DELAY=2.5
  65. MAX_RETRIES=5
  66. FORCE=0
  67. while [ $# -gt 0 ]; do
  68. case "$1" in
  69. -o|--outdir) OUTDIR=$2; shift 2 ;;
  70. -d|--delay) DELAY=$2; shift 2 ;;
  71. -r|--max-retries) MAX_RETRIES=$2; shift 2 ;;
  72. --force) FORCE=1; shift ;;
  73. -h|--help) sed -n '3,19p' "$0" | sed 's/^#\{0,1\} \{0,1\}//'; exit 0 ;;
  74. *) echo "unknown option: $1" >&2; exit 1 ;;
  75. esac
  76. done
  77. TMP=$(mktemp -d "${TMPDIR:-/tmp}/gromit.XXXXXX") || exit 1
  78. trap 'rm -rf "$TMP"' EXIT
  79. trap 'echo; echo "interrupted -- re-run to resume (existing files are skipped)"; exit 130' INT
  80. TOTAL=$(( ${#EXPRESS_URLS[@]} + ${#ICONUPLOADER_URLS[@]} ))
  81. INDEX=0
  82. REQUESTS=0
  83. N_OK=0; N_SKIP=0; N_FAIL=0
  84. bump_delay() {
  85. DELAY=$(awk -v d="$DELAY" 'BEGIN { v = d*1.5+1; printf "%.1f", (v > 30 ? 30 : v) }')
  86. echo " $1 -- floor delay now ${DELAY}s"
  87. }
  88. # fetch URL OUTFILE LABEL
  89. fetch() {
  90. local url=$1 out=$2 label=$3
  91. local attempt=0 code rc wait reason
  92. while :; do
  93. sleep "$DELAY"
  94. code=$(curl -sS --max-time 120 -D "$TMP/hdr" \
  95. -o "$out.part" -w '%{http_code}' "$url" 2>/dev/null)
  96. rc=$?
  97. REQUESTS=$((REQUESTS+1))
  98. # -s: a zero-byte body is a failed capture, not a file.
  99. if [ "$rc" -eq 0 ] && [ "$code" = "200" ] && [ -s "$out.part" ]; then
  100. mv "$out.part" "$out"
  101. return 0
  102. fi
  103. rm -f "$out.part"
  104. wait=""
  105. if [ "$rc" -ne 0 ]; then
  106. reason="curl exit $rc"
  107. # 7 = couldn't connect: we tripped the throttle. Back off hard and
  108. # keep the floor raised for the rest of the run.
  109. case "$rc" in
  110. 7|28|56) bump_delay "connection refused/timed out" ;;
  111. esac
  112. elif [ "$code" = "200" ]; then
  113. reason="empty response"
  114. else
  115. reason="HTTP $code"
  116. if [ "$code" = "404" ]; then
  117. echo " !! 404 $label"
  118. return 1
  119. fi
  120. if [ "$code" = "429" ]; then
  121. bump_delay "rate limited"
  122. wait=$(awk 'tolower($1) ~ /^retry-after:/ {gsub(/\r/,""); print $2; exit}' "$TMP/hdr")
  123. case "$wait" in ''|*[!0-9]*) wait="" ;; esac
  124. fi
  125. fi
  126. if [ "$attempt" -ge "$MAX_RETRIES" ]; then
  127. echo " !! $label: giving up after $((attempt+1)) attempts ($reason)"
  128. return 1
  129. fi
  130. [ -n "$wait" ] || wait=$(awk -v d="$DELAY" -v a="$attempt" \
  131. 'BEGIN { v = d*(2^a)+2; print int(v > 120 ? 120 : v) }')
  132. attempt=$((attempt+1))
  133. echo " $reason -- retry $attempt/$MAX_RETRIES in ${wait}s"
  134. sleep "$wait"
  135. done
  136. }
  137. # download SUBDIR BUILD_ID URL...
  138. download() {
  139. local subdir=$1 build=$2
  140. shift 2
  141. local url rel dest
  142. for url in "$@"; do
  143. rel=${url##*/$build/}
  144. dest="$OUTDIR/$subdir/$rel"
  145. INDEX=$((INDEX+1))
  146. if [ "$FORCE" -eq 0 ] && [ -s "$dest" ]; then
  147. printf '[%2d/%d] skip %s/%s\n' "$INDEX" "$TOTAL" "$subdir" "$rel"
  148. N_SKIP=$((N_SKIP+1))
  149. continue
  150. fi
  151. printf '[%2d/%d] get %s/%s\n' "$INDEX" "$TOTAL" "$subdir" "$rel"
  152. mkdir -p "$(dirname "$dest")"
  153. if fetch "$url" "$dest" "$rel"; then
  154. echo " $(wc -c < "$dest" | tr -d ' ') bytes"
  155. N_OK=$((N_OK+1))
  156. else
  157. N_FAIL=$((N_FAIL+1))
  158. fi
  159. done
  160. }
  161. download express 100820.5475.1.en-us "${EXPRESS_URLS[@]}"
  162. download iconuploader 110128.1.5797 "${ICONUPLOADER_URLS[@]}"
  163. printf '\n%s ok, %s skipped, %s failed (%s requests)\n' \
  164. "$N_OK" "$N_SKIP" "$N_FAIL" "$REQUESTS"
  165. exit 0