]> gitweb.michael.orlitzky.com - charm-bypass.git/blob - cgi-bin/code.cgi
COPYING: add to state the "or later" bit
[charm-bypass.git] / cgi-bin / code.cgi
1 #!/bin/sh
2 #
3 # A posix sh CGI script (yeah, that's right) to update the daily
4 # code for CharmBypass. This is not useful to you unless you
5 # host CharmBypass on a public web server and are insane.
6 #
7
8 # Fail on the first error.
9 set -e
10
11 # The CGI spec says that the current working directory SHOULD be set
12 # to the directory containing the script. Well, that's not always what
13 # happens. It's true in apache...
14 INDEX_HTML=$(pwd)/../index.html
15
16 # But not in python's server.http
17 [ -f "${INDEX_HTML}" ] || INDEX_HTML=$(pwd)/index.html
18
19
20 # Die with an error. The first argument, an HTTP status code, is
21 # returned as the Status. The second argument, an error message, is
22 # printed after setting the Content-type to text/plain. Finally, we
23 # cease execution.
24 die() {
25 echo "Status: ${1}"
26 echo "Content-type: text/plain"
27 echo ""
28 echo "${2}"
29 exit 1
30 }
31
32 # Redirect to the given URL; return a 302 status, and send the
33 # necessary location header.
34 redirect() {
35 echo "Status: 302"
36 echo "Location: ${1}"
37 echo ""
38 exit
39 }
40
41 # Parse and return the daily code from the index.html file.
42 # This relies on "implementation details" within the HTML;
43 # namely, the field name and its value must appear next
44 # to each other on the same line.
45 parse_code() {
46 sed -n \
47 -e 's/.*name="code" value="\([A-Za-z0-9]\{2\}\)".*/\1/p' \
48 "${INDEX_HTML}" \
49 | head -n 1
50 }
51
52 # Update (replace) the daily code in the index.html file. The first
53 # argument is the new code to use. This relies on "implementation
54 # details" within the HTML; namely, the field name and its value must
55 # appear next to each other on the same line.
56 update_code() {
57 _new_code="${1}"
58 _find='name="code" value="[a-zA-Z0-9]\{0,2\}"'
59 _replace="name=\"code\" value=\"${_new_code}\""
60 _tmp=$(mktemp)
61
62 # The "-i" flag to sed isn't POSIX, and worse, it tries to use a
63 # temp file in CWD. We don't want to make the parent directory
64 # writable because then this script is writable.
65 sed -e "s/${_find}/${_replace}/g" "${INDEX_HTML}" > "${_tmp}"
66
67 # mv would also recreate the file here, so cp/rm instead.
68 cp "${_tmp}" "${INDEX_HTML}"
69 rm "${_tmp}"
70 }
71
72 # Display the "update code" form and exit.
73 display_code_form() {
74 echo "Content-type: text/html"
75 echo ""
76
77 cat <<-EOF
78 <!doctype html>
79 <html lang="en-US">
80 <head>
81 <meta charset="utf-8">
82 <meta name="viewport"
83 content="width=device-width, initial-scale=1" />
84
85 <title>
86 CharmBypass: got that transit equity
87 </title>
88 </head>
89
90 <body>
91 <form method="post">
92 <label for="code">Today's code:</label>
93 <input id="code"
94 name="code" value="${1}"
95 type="text"
96 size="2"
97 minlength="2"
98 maxlength="2"
99 pattern="[a-zA-Z0-9]*" />
100 <br /><br />
101 <input type="submit" value="Update it" />
102 <form>
103 </body>
104 </html>
105 EOF
106 exit
107 }
108
109 # If this isn't a POST request, it should be a "normal" page hit,
110 # i.e. a GET request with no querystring parameters. If there are
111 # parameters, we ignore them, and display the "update code" form
112 # anyway.
113 if [ "${REQUEST_METHOD}" != "POST" ]; then
114 # Parse the code before we start displaying the form
115 # so that we can throw a 500 error if it fails.
116 _old_code=$(parse_code) || die 500 "failed to parse the existing code"
117 display_code_form "${_old_code}"
118 fi
119
120 # Since display_code_form() exits, the only way we can get
121 # here is if this is a POST request. And in that case, we
122 # should have some data... At least, CONTENT_LENGTH will
123 # be a number.
124 if [ -n "${CONTENT_LENGTH}" -a "${CONTENT_LENGTH}" -gt 0 ]; then
125 POST_DATA=$(dd bs=1 count="${CONTENT_LENGTH}" 2>/dev/null)
126
127 # Sanity check the form data. To prevent mistakes, it's not possible
128 # to "erase" the code that somebody else entered. If it's wrong, who
129 # cares? Erasing it would make it random... i.e. still wrong.
130 case "${POST_DATA}" in
131 "code="[A-Za-z0-9][A-Za-z0-9])
132 CODE=$(echo "${POST_DATA}" | cut -d= -f2)
133 update_code "${CODE}" || die 500 "failed to update the code"
134 redirect "/"
135 ;;
136 *)
137 # If the form data isn't exactly what we expect,
138 # fail ASAP.
139 die 400 "bad request"
140 ;;
141 esac
142 fi