logo

gemini-space

What lives at gemini://hacktivis.me/ git clone https://hacktivis.me/git/gemini-space.git

server.sh (2531B)


  1. #!/bin/sh
  2. # shellcheck disable=SC1017
  3. # Copyright 2020-2021 Haelwenn (lanodan) Monnier <contact+gemini@hacktivis.me>
  4. # Distributed under the terms of the GNU Affero General Public License version 3
  5. # AGPL-3 because it's a service on the internet, screw permissive licences for these
  6. # Notes:
  7. # - URLs are barely parsed and mostly taken as-is, which could be dangerous
  8. # - Recognised hostnames are directories in $GEMDIR, they must also have a $host:1965 symlink
  9. # - stunnel chroot ability is untested for now
  10. # - Lastest known version of the specification: v0.14.3, November 29th 2020
  11. export GEMDIR="/srv/gemini"
  12. bubblewrap() {
  13. bwrap \
  14. --unshare-user --uid 65534 --gid 65534 \
  15. --unshare-ipc --unshare-pid --unshare-uts --unshare-cgroup-try \
  16. --ro-bind /bin /bin \
  17. --ro-bind /lib /lib \
  18. --ro-bind /lib64 /lib64 \
  19. --ro-bind /usr /usr \
  20. --ro-bind /git /git \
  21. --ro-bind /etc /etc \
  22. --ro-bind "${GEMDIR}" "${GEMDIR}" \
  23. --chdir "${GEMDIR}" \
  24. --proc /proc \
  25. --dev /dev \
  26. --die-with-parent \
  27. "$@"
  28. }
  29. IFS=#'
  30. ' read -r line
  31. # ' hack for vis editor to reset hightlighting…
  32. query="${line#gemini://}"
  33. query="${query%%../*}"
  34. query="${query//%20/ }"
  35. path="$GEMDIR/${query//\?*}"
  36. status="--"
  37. if echo "$path" | grep -Eq '.gmi$'
  38. then
  39. mime="text/gemini; charset=utf-8"
  40. else
  41. mime=$(file --mime -b "$path" 2>/dev/null)
  42. fi
  43. if [ -r "$path" -a -f "$path" ]
  44. then
  45. if echo "$query" | grep -Eq '/cgi-bin/'
  46. then
  47. if [ -x "$path" -a -f "$path" ]
  48. then
  49. if echo "$query" | grep -Eq '\?'
  50. then
  51. bubblewrap "./${query//\?*}" "${query//*\?}"
  52. else
  53. bubblewrap "./${query//\?*}" ""
  54. fi
  55. status="x$?"
  56. else
  57. status="50"
  58. printf '50 File not executable or non-existent\r\n'
  59. fi
  60. else
  61. status="20"
  62. printf '20 %s\r\n' "$mime"
  63. cat "$path" 2>/dev/null
  64. fi
  65. elif [ -r "$path" -a -d "$path" ]
  66. then
  67. if echo "$path" | grep -Eq '/$'
  68. then
  69. if [ -r "$path/index.gmi" -a -f "$path/index.gmi" ]
  70. then
  71. status="20"
  72. printf '20 text/gemini; charset=utf-8\r\n'
  73. cat "$path/index.gmi" 2>/dev/null
  74. else
  75. status="20"
  76. printf '20 text/gemini; charset=utf-8\r\n'
  77. printf '# Index of %s\n' "$query"
  78. cd "$path" && find . -mindepth 1 -maxdepth 1 | sed -e 's; ;%20;g' -e "s;^\./;=> ;" 2>/dev/null
  79. fi
  80. else
  81. status="30"
  82. printf '30 gemini://%s/\r\n' "$query"
  83. fi
  84. else
  85. status="50"
  86. printf '50 File not readable or non-existent\r\n'
  87. fi
  88. # I know… blame France data rentention policy
  89. logger -p local0.notice "${REMOTE_HOST:-?host?} ${REMOTE_PORT:-?port?} ${line} ${status}" 2>/dev/null 1>/dev/null