about summary refs log tree commit diff stats
path: root/bollux.sh
diff options
context:
space:
mode:
authorCase Duckworth2020-05-22 08:38:40 -0500
committerCase Duckworth2020-05-22 08:38:40 -0500
commit6cce4bd5e7e89406c0ca5942076c95c458bba2dc (patch)
tree89e5a9393b5c99aa2a6eb0fc87e9e97e7ec84e57 /bollux.sh
downloadbollux-6cce4bd5e7e89406c0ca5942076c95c458bba2dc.tar.gz
bollux-6cce4bd5e7e89406c0ca5942076c95c458bba2dc.zip
Ready to upload
Diffstat (limited to 'bollux.sh')
-rwxr-xr-xbollux.sh264
1 files changed, 264 insertions, 0 deletions
diff --git a/bollux.sh b/bollux.sh new file mode 100755 index 0000000..84786b6 --- /dev/null +++ b/bollux.sh
@@ -0,0 +1,264 @@
1#!/usr/bin/env bash
2# bollux: bash gemini client
3# Author: Case Duckworth <acdw@acdw.net>
4# License: MIT
5# Version: -0.9
6
7PRGN="${0##*/}"
8PORT=1965
9LOG_LEVEL=3 # higher=more important.
10
11clean() {
12 # '\e[?7h': re-enable line wrapping
13 # '\e[2J': clear the screen
14 # '\e[;r': reset the scroll area
15 # '\e[?1049l': swap back to primary screen
16 printf '\e[?7h\e[2J\e[;r\e[?1049l'
17 exit
18}
19
20refresh() {
21 # grab the terminal size
22 shopt -s checkwinsize
23 (
24 :
25 :
26 )
27 # '\e[?1049h': Swap to the alternate buffer.
28 # '\e[?7l': Disable line wrapping.
29 # '\e[2J': Clear the screen.
30 # '\e[3;%sr': Set the scroll area.
31 # '\e[999H': Move the cursor to the bottom.
32 printf '\e[?1049h\e[?7l\e[2J\e[3;%sr\e[999H' "$((LINES - 1))"
33}
34
35resize() {
36 refresh
37 # '\e7': Save the cursor position.
38 # '\e[?25l': Hide the cursor.
39 # '\r': Move the cursor to column 0.
40 # '\e[999B': Move the cursor to the bottom.
41 # '\e[A': Move the cursor up a line.
42 printf '\e7\e[?25l\r\e[999B\e[A'
43}
44
45bollux() {
46 if [[ -n "$1" ]]; then
47 loc="$1"
48 else
49 read -rp "GO> " loc
50 fi
51
52 log 2 "location: $loc"
53 log 2 "address: $(address "$loc")"
54 log 2 "server: $(server "$loc")"
55
56 address "$loc" |
57 download "$(server "$loc")" |
58 handle
59}
60
61log() {
62 case "$1" in
63 [0-9]*)
64 lvl="$1"
65 shift
66 ;;
67 *) lvl=5 ;;
68 esac
69 if ((lvl >= LOG_LEVEL)); then
70 if [[ "$2" == - ]]; then
71 while IFS= read -r line; do
72 printf '\e[33m%s\e[0m:\t%s\n' "$PRGN" "$line" >&2
73 done
74 else
75 printf '\e[34m%s\e[0m:\t%s\n' "bollux" "$*" >&2
76 fi
77 fi
78}
79
80download() {
81 # usage:
82 # echo REQUEST | download SERVER
83 # download SERVER REQUEST
84 serv="$1"
85 req=
86 if (($# == 2)); then
87 req="$2"
88 else
89 req="$(cat)"
90 fi
91 t="$(mktemp)"
92 openssl s_client -crlf -ign_eof -quiet -connect "$serv" <<<"$req" 2>"$t"
93 log 1 <"$t"
94 rm "$t"
95}
96
97address() {
98 addr="$1"
99 if [[ "$addr" != gemini://* ]]; then
100 addr="gemini://$addr"
101 fi
102 echo "$addr" | trim
103}
104
105server() {
106 serv="${1#*://}"
107 serv="${serv%%/*}"
108 if [[ "$serv" != *:* ]]; then
109 serv="$serv:$PORT"
110 fi
111 echo "$serv" | trim
112}
113
114trim() {
115 sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'
116}
117
118display() {
119 echo
120 cat
121 echo
122}
123
124handle() {
125 # cf. gemini://gemini.circumlunar.space/docs/spec-spec.txt
126 resp="$(cat)"
127 head="$(head -n1 <<<"$resp")"
128 body="$(tail -n+2 <<<"$resp")"
129 stat="$(awk '{print $1}' <<<"$head")"
130 smsg="$(
131 awk '{for(i=2;i<=NF;i++)printf "%s ",$i;printf "\n";}' <<<"$head"
132 )"
133
134 log "[$stat] $smsg"
135
136 case "$stat" in
137 10) # INPUT
138 # As per definition of single-digit code 1 in 1.3.2.
139 NOT_IMPLEMENTED
140 ;;
141 20) # SUCCESS
142 # As per definition of single-digit code 2 in 1.3.2.
143 display <<<"$body"
144 ;;
145 21) # SUCCESS - END OF CLIENT CERTIFICATE SESSION
146 # The request was handled successfully and a response body will
147 # follow the response header. The <META> line is a MIME media
148 # type which applies to the response body. In addition, the
149 # server is signalling the end of a transient client certificate
150 # session which was previously initiated with a status 61
151 # response. The client should immediately and permanently
152 # delete the certificate and accompanying private key which was
153 # used in this request.
154 display <<<"$body"
155 NOT_FULLY_IMPLEMENTED
156 ;;
157 30) # REDIRECT - TEMPORARY
158 # As per definition of single-digit code 3 in 1.3.2.
159 exec "$0" "$smsg"
160 ;;
161 31) # REDIRECT - PERMANENT
162 # The requested resource should be consistently requested from
163 # the new URL provided in future. Tools like search engine
164 # indexers or content aggregators should update their
165 # configurations to avoid requesting the old URL, and end-user
166 # clients may automatically update bookmarks, etc. Note that
167 # clients which only pay attention to the initial digit of
168 # status codes will treat this as a temporary redirect. They
169 # will still end up at the right place, they just won't be able
170 # to make use of the knowledge that this redirect is permanent,
171 # so they'll pay a small performance penalty by having to follow
172 # the redirect each time.
173 exec "$0" "$smsg"
174 NOT_FULLY_IMPLEMENTED
175 ;;
176 4*) # 40 - TEMPORARY FAILURE
177 # As per definition of single-digit code 4 in 1.3.2.
178 # 41 - SERVER UNAVAILABLE
179 # The server is unavailable due to overload or maintenance.
180 # (cf HTTP 503)
181 # 42 - CGI ERROR
182 # A CGI process, or similar system for generating dynamic
183 # content, died unexpectedly or timed out.
184 # 43 - PROXY ERROR
185 # A proxy request failed because the server was unable to
186 # successfully complete a transaction with the remote host.
187 # (cf HTTP 502, 504)
188 # 44 - SLOW DOWN
189 # Rate limiting is in effect. <META> is an integer number of
190 # seconds which the client must wait before another request is
191 # made to this server.
192 # (cf HTTP 429)
193 printf 'OH SHIT!\n%s\t%s\n' "$stat" "$smsg" >&2
194 NOT_IMPLEMENTED
195 ;;
196 5*) # 50 - PERMANENT FAILURE
197 # As per definition of single-digit code 5 in 1.3.2.
198 # 51 - NOT FOUND
199 # The requested resource could not be found but may be available
200 # in the future.
201 # (cf HTTP 404)
202 # (struggling to remember this important status code? Easy:
203 # you can't find things hidden at Area 51!)
204 # 52 - GONE
205 # The resource requested is no longer available and will not be
206 # available again. Search engines and similar tools should
207 # remove this resource from their indices. Content aggregators
208 # should stop requesting the resource and convey to their human
209 # users that the subscribed resource is gone.
210 # (cf HTTP 410)
211 # 53 - PROXY REQUEST REFUSED
212 # The request was for a resource at a domain not served by the
213 # server and the server does not accept proxy requests.
214 # 59 - BAD REQUEST
215 # The server was unable to parse the client's request,
216 # presumably due to a malformed request.
217 # (cf HTTP 400)
218 printf 'OH SHIT!\n%s\t%s\n' "$stat" "$smsg" >&2
219 NOT_IMPLEMENTED
220 ;;
221 6*) # 60 - CLIENT CERTIFICATE REQUIRED
222 # As per definition of single-digit code 6 in 1.3.2.
223 # 61 - TRANSIENT CERTIFICATE REQUESTED
224 # The server is requesting the initiation of a transient client
225 # certificate session, as described in 1.4.3. The client should
226 # ask the user if they want to accept this and, if so, generate
227 # a disposable key/cert pair and re-request the resource using it.
228 # The key/cert pair should be destroyed when the client quits,
229 # or some reasonable time after it was last used (24 hours?
230 # Less?)
231 # 62 - AUTHORISED CERTIFICATE REQUIRED
232 # This resource is protected and a client certificate which the
233 # server accepts as valid must be used - a disposable key/cert
234 # generated on the fly in response to this status is not
235 # appropriate as the server will do something like compare the
236 # certificate fingerprint against a white-list of allowed
237 # certificates. The client should ask the user if they want to
238 # use a pre-existing certificate from a stored "key chain".
239 # 63 - CERTIFICATE NOT ACCEPTED
240 # The supplied client certificate is not valid for accessing the
241 # requested resource.
242 # 64 - FUTURE CERTIFICATE REJECTED
243 # The supplied client certificate was not accepted because its
244 # validity start date is in the future.
245 # 65 - EXPIRED CERTIFICTE REJECTED
246 # The supplied client certificate was not accepted because its
247 # expiry date has passed.
248 printf 'OH SHIT!\n%s\t%s\n' "$stat" "$smsg" >&2
249 NOT_IMPLEMENTED
250 ;;
251 esac
252}
253
254NOT_IMPLEMENTED() {
255 log "NOT IMPLEMENTED!!!" >&2
256 exit 127
257}
258NOT_FULLY_IMPLEMENTED() {
259 log "NOT FULLY IMPLEMENTED!!!" >&2
260}
261
262if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
263 bollux "$@"
264fi