#!/bin/sh -e # # https://www.romanzolotarev.com/bin/m # Copyright 2018-2019 Roman Zolotarev # # Permission to use, copy, modify, and/or distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # main() { BASE_URL='m' : "${DB:=/db/rgz}" : "${PAGES:=/htdocs/$BASE_URL}" : "${ROOT:=}" HEADER='/htdocs/rgz/raw/_header.html' FOOTER='/htdocs/rgz/raw/_footer.html' URI=${REQUEST_URI##/$BASE_URL} case "$REQUEST_METHOD$URI" in GET/*?setup*) paddle_init; setup;; POST/*?send_magic_link*) send_magic_link;; POST/*?invite*) invite;; POST/*?stop_sponsorship*) stop_sponsorship;; GET/*?wait_for_magic_link*) wait_for_magic_link;; GET/*?magic=*) verify_magic_link;; GET/*?checkout=*) paddle_checkout;; GET/*?profile*) render_profile_page;; GET/*?cancel*) paddle_cancel;; GET/*?sync*) paddle_sync;; GET/*?log_out*) log_out;; GET/*) render_page;; *) full_stop 'Wat?';; esac } ############################################################################## stop_sponsorship() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || full_stop 'You are not logged in' query=$(read_query_string_post) || start_over 'Invalid request' test -n "$query" || start_over 'Select emails first' "$u?profile" d="$DB/members/$m_id/sponsored" test -d "$d" || full_stop 'No sponsored members' echo "$query" | tr '&' '\n' | while read -r line do # revoke invitation f_s=$(echo "$line" | cut -d= -f1) f="$DB/members/$m_id/sponsored/$f_s" test -f "$f" && rm "$f" f="$DB/mail/m-$f_s" test -f "$f" && rm "$f" # remove sponsorship from existing member s_id=$(get_member_id "$f_s") f="$DB/members/$s_id/sponsor" test -f "$f" && rm "$f" f="$DB/members/$s_id/sponsorship_expires_at" test -f "$f" && rm "$f" f="$DB/members/$s_id/sponsorship_state" test -f "$f" && rm "$f" done || start_over 'No sponsored' "$u?profile" http303 "${REQUEST_URI%%\?*}?profile" } invite() { m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || full_stop 'You are not logged in' f="$DB/members/$m_id/email" test -f "$f" || full_stop 'Cannot find your email' sponsor=$(cat "$f") query=$(read_query_string_post) || start_over 'Invalid request' e=$(get_value "$query" 'email' | tr '[:upper:]' '[:lower:]') e_hash=$(echo "$e" | sha256) sponsor_hash=$(echo "$sponsor" | sha256) test "$e_hash" = "$sponsor_hash" && full_stop 'Cannot invite yourself' magic=$(random_str 20) make_file "$DB/members/$m_id/sponsored/$e_hash" "$e" make_file "$DB/tokens/m-$magic" "$e $m_id" make_file "$DB/mail/m-$e_hash" 'To: '"$e"' MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Subject: [RGZ.EE] An invitation from '"$sponsor"' Hey, '"$sponsor"' wants to sponsor your membership at RGZ.EE. To accept the invitation open this link: '"https://${SERVER_NAME}${REQUEST_URI%%\?*}?magic=$magic"' Otherwise ignore this email. -- The link will expire in an hour. Sent to: '"$e" http303 "${REQUEST_URI%%\?*}?profile" } paddle_cancel() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || full_stop 'You are not logged in' f="$DB/members/$m_id/paddle_subscription" test -f "$f" || paddle_no_sub sub_id=$(jq '.subscription_id' "$f") test -n "$sub_id" || paddle_no_sub paddle_init cancel=$(curl -s -X POST \ -d vendor_id="$PADDLE_ID" \ -d vendor_auth_code="$PADDLE_AUTH_CODE" \ -d subscription_id="$sub_id" \ https://vendors.paddle.com/api/2.0/subscription/users_cancel | jq -r '.success' ) test "$cancel" = "true" || full_stop 'Could not cancel your membership' paddle_no_sub } paddle_checkout() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || full_stop 'You are not logged in' c_id=$(get_value "$QUERY_STRING" 'checkout') sub_id=$( curl -s GET \ https://checkout.paddle.com/api/1.0/order?checkout_id="$c_id" | jq '.order.subscription_id' ) paddle_fetch_subscription "$sub_id" } paddle_init() { test -x '/bin/cat' || http500 'cp /bin/cat /var/rgz/bin/cat FAIL' f="$DB/paddle/id" test -r "$f" || http500 "PADDLE_ID: $f not a file" PADDLE_ID=$(cat "$f") f="$DB/paddle/auth_code" test -r "$f" || http500 "PADDLE_AUTH_CODE: $f not a file" PADDLE_AUTH_CODE=$(cat "$f") } paddle_fetch_subscription_by_email() { e="$1" paddle_init sub=$( curl -s -X POST \ -d vendor_id="$PADDLE_ID" \ -d vendor_auth_code="$PADDLE_AUTH_CODE" \ https://vendors.paddle.com/api/2.0/subscription/users | jq '.response[] | select(.user_email=="'"$e"'")' ) test -n "$sub" || paddle_no_sub make_file "$DB/members/$m_id/paddle_subscription" "$sub" paddle_sync } paddle_no_sub() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || http303 "$u" f="$DB/members/$m_id/email" test -f "$f" || full_stop 'Cannot find your email' e=$(cat "$f") f="$DB/members/$m_id/paddle_subscription" test -f "$f" && rm "$f" f="$DB/members/$m_id/expires_at" if test -f "$f" then e_at=$(cat "$f") e_on=$(date -j -r "$e_at" '+%e %b %Y') state='

Your membership has been paid till '"$e_on"',
and to be cancelled after that date, no payments scheduled.
' else state='

Your membership is inactive.
You can re-new your membership anytime.

' fi make_file "$DB/members/$m_id/state" "$state"'
Updated on '"$(date "+%e %b %Y at %H:%M:%S UTC")"'. Refresh
' update_sponsorships "$m_id" http303 "$u?profile" } update_sponsorships() { m_id="$1" d="$DB/members/$m_id/sponsored" test -d "$d" && find "$d" -type f | while read -r f do e_hash=$(echo "$f" | cut -d'/' -f7) update_sponsorship "$(get_member_id "$e_hash")" "$m_id" done } paddle_sync() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || http303 "$u" f="$DB/members/$m_id/email" test -f "$f" || full_stop 'Cannot find your email' e=$(cat "$f") f="$DB/members/$m_id/sponsor" test -f "$f" && sponsor_existing_member "$m_id" "$(cat "$f")" f="$DB/members/$m_id/paddle_subscription" test -f "$f" || paddle_fetch_subscription_by_email "$e" sub=$(jq . "$f") sub_state=$(echo "$sub" | jq -r '.state') plan_id=$(echo "$sub" | jq -r '.plan_id') update_url=$(echo "$sub" | jq -r '.update_url') next_due=$(echo "$sub" | jq -r '.next_payment.date') case "$plan_id" in 551183) make_file "$DB/members/$m_id/seats" 9;; 551192) make_file "$DB/members/$m_id/seats" 99;; esac if test "$sub_state" = 'active' then e_at=$(date -j '+%s' "$(echo "$next_due" | tr -d'-')"0000) e_on=$(date -j -r "$e_at" '+%b %e, %Y') make_file "$DB/members/$m_id/expires_at" "$e_at" else rm -f "$DB/members/$m_id/expires_at" fi case "$sub_state" in active) next_amount=$(echo "$sub" | jq -r '.next_payment.amount') next_currency=$(echo "$sub" | jq -r '.next_payment.currency') next_payment="$(printf '%.2f' "$next_amount") $next_currency" state='

Your membership has been paid till '"$e_on"',
the next payment of '"$next_payment"' will be processed on that day.
You can cancel payments instantly or change your payment method anytime.

' ;; past_due) state='

Your membership has been suspended. Waiting for the payment.

You can cancel or change the next payment anytime.

' ;; *) full_stop 'Wrong subscription state' ;; esac make_file "$DB/members/$m_id/state" "$state"'
Updated on '"$(date "+%e %b %Y at %H:%M:%S")"'. Refresh
' update_sponsorships "$m_id" http303 "$u?profile" } paddle_fetch_subscription() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') sub_id="$1" paddle_init sub=$( curl -s -X POST \ -d vendor_id="$PADDLE_ID" \ -d vendor_auth_code="$PADDLE_AUTH_CODE" \ -d subscription_id="$sub_id" \ https://vendors.paddle.com/api/2.0/subscription/users | jq '.response[0]' ) f="$DB/members/$m_id/email" test -f "$f" || full_stop 'Cannot find your email' e=$(cat "$f") user_email=$(echo "$sub" | jq -r '.user_email') test "$user_email" = "$e" || http500 'wrong email' make_file "$DB/members/$m_id/paddle_subscription" "$sub" paddle_sync } wait_for_magic_link() { u="${REQUEST_URI%%\?*}" e=$(get_value "$QUERY_STRING" 'email') m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" && http303 "$u" http200 '

Sending a magic link to you in a minute.
Please check your mailbox.

From: hi@romanzolotarev.com
Subject: [RGZ.EE] Register or log in
To: '"$e"'

...

The link will expire in an hour. Didn'\''t get an email? Try again

' } start_over() { err="$1" u="$2" test -z "$u" && u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" && http303 "$u" http200 '

Oops...

'"$err"'

Start over

' } full_stop() { err="$1" fb='/feedback.html?comment='"$(encode_value "\"$err\"")" u="${REQUEST_URI%%\?*}" http200 '

Oops...

'"$err"'

Something went wrong, please let me know or try again a bit later.

' } verify_magic_link() { magic=$(get_value "$QUERY_STRING" 'magic') test -n "$magic" || start_over 'This magic link is broken' f="$DB/tokens/m-$magic" test -f "$f" || start_over 'This magic link is expired' e=$(cut -f1 "$f") sponsor=$(cut -f2 "$f") test "$e" = "$sponsor" && sponsor='' rm "$f" e_hash=$(echo "$e" | sha256) m_id=$(get_member_id "$e_hash") test -n "$m_id" && f_m="$DB/members/$m_id" test -n "$sponsor" && f_s="$DB/members/$sponsor" test -n "$m_id" && test -n "$sponsor" && test -d "$f_m" && test -d "$f_s" && sponsor_existing_member "$m_id" "$sponsor" test -z "$m_id" && test -n "$sponsor" && test -d "$f_s" && create_sponsored_member "$e" "$sponsor" test -z "$m_id" && test -z "$sponsor" && create_member "$e" s_key=$(create_session "$m_id") http303 "${REQUEST_URI%%\?*}" \ 'Content-Type: text/html; charset=utf-8 Set-Cookie: m_id='"$m_id"'; Path=/; Secure; HttpOnly Set-Cookie: s_key='"$s_key"'; Path=/; Secure; HttpOnly' } log_out() { m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" && rm "$f" http303 "${REQUEST_URI%%\?*}" \ 'Content-Type: text/html; charset=utf-8 Set-Cookie: m_id=; Path=/; Secure; HttpOnly Set-Cookie: s_key=; Path=/; Secure; HttpOnly' } get_member_id() { e_hash="$1" d="$DB/members" test -d "$d" || { echo; return; } f=$(find "$d" -name "$e_hash" -path '*/emails/*' -type f | head -1) m="${f##$d/}" echo "${m%%/*}" } update_sponsorship() { m_id="$1" sponsor="$2" f_s="$DB/members/$sponsor/expires_at" f_m="$DB/members/$m_id/sponsorship_expires_at" if test -f "$f_s" then cat "$f_s" > "$f_m" else test -f "$f_m" && rm "$f_m" fi sponsor_email=$(cat "$DB/members/$sponsor/email") if test -f "$f_m" then e_at=$(cat "$f_m") e_on=$(date -j -r "$e_at" '+%e %b %Y') state='

Your membership has been sponsored
by '"$sponsor_email"' till '"$e_on"'.' else state='

Your membership has been sponsored
by '"$sponsor_email"', but it is inactive now.' fi make_file "$DB/members/$m_id/sponsor" "$sponsor" make_file "$DB/members/$m_id/sponsorship_state" "$state" } sponsor_existing_member() { m_id="$1" sponsor="$2" update_sponsorship "$m_id" "$sponsor" s_key=$(create_session "$m_id") http303 "${REQUEST_URI%%\?*}" \ 'Content-Type: text/html; charset=utf-8 Set-Cookie: m_id='"$m_id"'; Path=/; Secure; HttpOnly Set-Cookie: s_key='"$s_key"'; Path=/; Secure; HttpOnly Set-Cookie: key_a=; Path=/; Secure; HttpOnly Set-Cookie: e_hash=; Path=/; Secure; HttpOnly' } create_sponsored_member() { e="$1" sponsor="$2" m_id=$(random_str 20) e_hash=$(echo "$e" | sha256) d="$(date +%s)" make_file "$DB/members/$m_id/created_at" "$d" make_file "$DB/members/$m_id/email" "$e" make_file "$DB/members/$m_id/emails/$e_hash" "$e" make_file "$DB/members/$m_id/newsletter" "$d" sponsor_existing_member "$m_id" "$sponsor" } create_member() { e="$1" m_id=$(random_str 20) e_hash=$(echo "$e" | sha256) d="$(date +%s)" make_file "$DB/members/$m_id/created_at" "$d" make_file "$DB/members/$m_id/email" "$e" make_file "$DB/members/$m_id/emails/$e_hash" "$e" make_file "$DB/members/$m_id/newsletter" "$d" s_key=$(create_session "$m_id") http303 "${REQUEST_URI%%\?*}" \ 'Content-Type: text/html; charset=utf-8 Set-Cookie: m_id='"$m_id"'; Path=/; Secure; HttpOnly Set-Cookie: s_key='"$s_key"'; Path=/; Secure; HttpOnly Set-Cookie: key_a=; Path=/; Secure; HttpOnly Set-Cookie: e_hash=; Path=/; Secure; HttpOnly' } create_session() { m_id="$1" s_key=$(random_str 20) make_file "$DB/members/$m_id/sessions/$s_key" echo "$s_key" } send_magic_link() { query=$(read_query_string_post) || start_over 'Invalid request' e=$(get_value "$query" 'email' | tr '[:upper:]' '[:lower:]') e_hash=$(echo "$e" | sha256) e_e=$(encode_value "$e") magic=$(random_str 20) make_file "$DB/tokens/m-$magic" "$e" make_file "$DB/mail/m-$e_hash" 'To: '"$e"' MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 7bit Subject: [RGZ.EE] Register or log in Hello, you are about to register or log in to RZE.EE (aka romanzolotarev.com), please open this link to continue: '"https://${SERVER_NAME}${REQUEST_URI%%\?*}?magic=$magic"' -- The link will expire in an hour. Sent to: '"$e" http303 "${REQUEST_URI%%\?*}?wait_for_magic_link&email=$e_e" } ############################################################################## subscription_form() { f="$1" m_id="$2" f_e="$DB/members/$m_id/email" test -f "$f_e" && e=$(cat "$f_e") header='

'"$e"'
' footer='

Upgrade to continue

Prefer monthly plans?
Have a team?

Personal  €9.00/m
Team  €55.00/m
Enterprise  €550.00/m

See Pricing for details
Already paid? Refresh

' http200 "$(tags_to_upper < "$f" | obfuscate "$header" "$footer")" } new_session_form() { f="$1" header='
Register or log in
' footer='
Type-in your email address

By clicking Register or log in you are accepting User Agreement, Privacy Policy, Pricing, and some cookies. 🍪

' http200 "$(tags_to_upper < "$f" | obfuscate "$header" "$footer")" } render_page() { p_uri="${PAGES}${URI%%\?*}" if test "${p_uri%%/}" = "$p_uri" then f="$p_uri" else f="${p_uri}index.html" fi test -f "$f" || http404 m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') test -f "$DB/members/$m_id/sessions/$s_key" || new_session_form "$f" f_s="$DB/members/$m_id/sponsor" if test -f "$f_s" then s_id=$(cat "$f_s") test -f "$DB/members/$s_id/expires_at" || subscription_form "$f" "$m_id" else test -f "$DB/members/$m_id/expires_at" || subscription_form "$f" "$m_id" fi f_e="$DB/members/$m_id/email" test -f "$f_e" && e=$(cat "$f_e") http200 '
'"$e"'
'"$(cat "$f")" } render_profile_page() { u="${REQUEST_URI%%\?*}" m_id=$(get_cookie 'm_id') s_key=$(get_cookie 's_key') f="$DB/members/$m_id/sessions/$s_key" test -f "$f" || http303 "$u" f="$DB/members/$m_id/email" test -f "$f" && e=$(cat "$f") f="$DB/members/$m_id/sponsorship_state" if test -f "$f" then state=$(cat "$f") else f="$DB/members/$m_id/state" if test -f "$f" then state=$(cat "$f") else state='

Membership status unknown. Refresh

' fi f_seats="$DB/members/$m_id/seats" if test -f "$f_seats" then d="$DB/members/$m_id/sponsored" test -d "$d" && seat_emails=$( find "$d" -type f | while read -r f do seat_time=$(stat -f "%m" "$f" | head -1) seat_email=$(cat "$f") name=$(echo "$f" | cut -d'/' -f7) echo "$seat_time"' '"$seat_email"'
' done | sort -rn | cut -f2- ) if test -n "$seat_emails" then sponsored='

'"$seat_emails"'

' fi if test -n "$seat_emails" then seats="$(( $(cat "$f_seats") - $(echo "$seat_emails" | wc -l) ))" else seats=$(cat "$f_seats") fi sponsor='
You can invite '"$seats"' people.
' fi fi http200 '
Go back

Hello

and thank you for your support ❤

'"$state"'
You'\''re logged in as '"$e"'. Log out
'"$sponsor"' '"$sponsored" } ############################################################################## read_query_string_post() { test -n "$CONTENT_LENGTH" || start_over 'Invalid content' dd bs=1 count="$CONTENT_LENGTH" status=none } get_cookie() { c="${HTTP_COOKIE##*$1=}" test -z "$c" || test "$HTTP_COOKIE" = "$c" && echo '' && return echo "${c%%;*}" } encode_value() { test -n "$1" || { echo; return; } echo "$1" | awk ' BEGIN { a = "" for (n = 0; n < 256; n++) pack[sprintf("%c", n)] = sprintf("%%%02x", n) } { sline = "" slen = length($0) for (n = 1; n <= slen; n++) { char = substr($0, n, 1) if ( char !~ /^[[:alnum:]_]$/ ) char = pack[char] sline = sline char } a = a ( a ? "%0a" : "" ) sline } END { print a }' } get_value() { # h/t Devin Teske test -n "$1" || { echo; return; } x="${1##*$2=}" test "$x" = "$1" && { echo; return; } # shellcheck disable=1004 echo "${x%%&*}" | awk ' BEGIN { for (n = 0; n < 256; n++) chr[n] = sprintf("%c",n) } { t = $0 a = "" gsub(/\+/, " ", t) while( match(t, /%[[:xdigit:]][[:xdigit:]]/) ) { a = a substr(t, 1, RSTART-1)\ chr[ sprintf("%u", "0x" substr(t, RSTART+1, 2))] t = substr(t, RSTART+RLENGTH) } a = a t gsub(//,"\\>",a) print a }' } obfuscate() { echo "$1" page=$(cat) echo "$page" | awk '{ print; if ( $0 == "" ) exit }' echo "$2" cut=$( echo "$page" | awk ' BEGIN { cut = 0 } { if ( cut == 0 && $0 == "" ) cut = 1 if ( cut == 1 ) print } ' | tr '[:lower:]' "$(jot -rcs '' 26 97 122)" ) test -n "$cut" || return echo '

The rest of the page has been obfuscated.

' echo '
'"$cut"'
' } tags_to_upper() { awk ' BEGIN { s = "[[:space:]]" while (getline) b = b "\n" $0 b = substr (b, 2) } END { t=b while ( match (t,/<[^>]*>/) ) { h = h substr (t, 1, RSTART - 1) r = substr (t, RSTART, RLENGTH) t = substr (t, RSTART + RLENGTH) if ( match (r, /^ "$1" chmod 0660 "$1" } ############################################################################## http500() { echo 'Status: 500 Internal Server Error' echo 'Content-Type: text/html; charset=utf-8' echo echo "
$*
" exit 1 } http404() { echo 'Status: 404 Not Found' echo 'Content-Type: text/html; charset=utf-8' echo test -f "$HEADER" && cat "$HEADER" echo "

Oops...

/$BASE_URL$URI not found

" test -f "$FOOTER" && cat "$FOOTER" exit 0 } http303() { echo 'Status: 303 See Other' echo "Location: $1" echo 'Content-Type: text/html; charset=utf-8' test -n "$2" && echo "$2" echo exit 0 } http200() { echo 'Status: 200 OK' echo 'Content-Type: text/html; charset=utf-8' echo test -f "$HEADER" && cat "$HEADER" echo "$1" test -f "$FOOTER" && cat "$FOOTER" exit 0 } ############################################################################## setup() { errs=$( setup_test_deps test -d "$PAGES" || echo "PAGES: $PAGES: not a dir" test -r "$HEADER" || echo "HEADER: $HEADER: not a file" test -r "$FOOTER" || echo "FOOTER: $FOOTER: not a file" test -d "$DB" || echo "$DB not a dir" ) test -z "$errs" || http500 "$errs\\nFAILED" echo 'Status: 200 OK' echo 'Content-Type: text/html; charset=utf-8' echo echo '
PASS
' exit 0 } setup_test_deps() { test -x '/usr/local/bin/curl' || echo 'pkg_add curl' test -x '/usr/local/bin/jq' || echo 'pkg_add jq' dirs='' echo ' /etc/resolv.conf /etc/ssl/cert.pem /bin/cat /bin/chmod /bin/date /bin/dd /bin/mkdir /bin/rm /bin/sh /bin/sha256 /usr/bin/awk /usr/bin/b64encode /usr/bin/cut /usr/bin/find /usr/bin/grep /usr/bin/head /usr/bin/jot /usr/bin/printf /usr/bin/sed /usr/bin/tail /usr/bin/tr /usr/bin/wc /usr/local/bin/curl /usr/local/bin/jq /usr/lib/libc.so.92.5 /usr/lib/libcrypto.so.44.1 /usr/lib/libm.so.10.1 /usr/lib/libpthread.so.25.1 /usr/lib/libssl.so.46.1 /usr/lib/libutil.so.13.0 /usr/lib/libz.so.5.0 /usr/libexec/ld.so /usr/local/lib/libcurl.so.25.17 /usr/local/lib/libjq.so.1.0 /usr/local/lib/libnghttp2.so.0.14 ' | while read -r f do test -n "$f" || continue d=${f%/*} if test "${dirs%%:$d}" = "$dirs" && test "$d" != '/usr/local/lib' then dirs="$dirs:$d" && test -d "${ROOT}$d" || echo "mkdir -p /var/rgz$d" fi test "${f##/usr/local/lib/}" = "$f" && x="$f" || x="/usr/lib${f##/usr/local/lib}" test -f "${ROOT}$x" || echo "cp $f /var/rgz$x" done } ############################################################################## main