💾 Archived View for rawtext.club › ~sloum › geminilist › 004966.gmi captured on 2023-09-08 at 17:32:23. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2021-11-30)

-=-=-=-=-=-=-

<-- back to the mailing list

[spec] The Tragedy of &

Sean Conner sean at conman.org

Sat Jan 30 23:59:47 GMT 2021

- - - - - - - - - - - - - - - - - - - 

It was thus said that the Great Gary Johnson once stated:

The issue I'm raising is that there appears to be no way to pass more
than one piece of information at a time in our query strings. This has a
very significant impact on any writers of CGI scripts, which is how many
Gemini servers allow users to add dynamic pages to their capsules.
But why, you ask?
Because each CGI script is available at a particular file path and
therefore additional path segments can't be used to pass information to
them. They have to get their inputs from the query string.

[ snip ]

It means essentially that CGI scripts are currently second-class
citizens, and the only people who can write dynamic capsules are server
authors (or people willing to hack on server code). This is because
encoding information using path segments requires injecting custom
routing table code into the server's request handler.

Not if the CGI interface is properly written. All I had to do was writethis CGI script and drop it into my tests directory [1]:

gemini://gemini.conman.org/test/pathseg.cgi

It uses only three of the RFC-3875 defined variables, QUERY_STRING,SCRIPT_NAME and PATH_INFO to do all the work. The script will ask for threefields and then present a final page with all three fields. But the scriptwill only work if all three variables are defined per RFC-3975 (PATH_INFO isthe tricky one).

Yes, it's a bit ugly and yes, it's a second class citizen and yes, itrequires a proper CGI module to work, but it can be done without theconfiguration you think it does. The script just simply appends each inputfield as the path, so if you enter

and a one and a two skidoosh

as the values, the final URL will be:

gemini://gemini.conman.org/test/pathseg.cgi/and%20a%20one/and%20a%20two/skidoosh

Yes, I could have done a bit more processing, naming each segment:

/test/pathseg.cgi/name=and%20a%20one/age=and%20a%20two/action=skidoosh

but I was lazy and wanted to just do a proof-of-concept here.

If you can write a CGI script that can correctly associate INPUT
responses with their intended variables, please share it. I suspect it
would be quite educational.

I have added it [3].

-spc

[1] I gave the script a .cgi extension just to drive the point home---for my server, GLV-1.12556 [2], the extension of a CGI script doesn't matter at all.

[2] https://github.com/spc476/GLV-1.12556

[3] Here you go. It's in Lua, but it's easy going except for the first bit which is a bit of broilerplate I needed for encoding and decoding various strings. The main logic is marked though, so you can skip the first section.

!/usr/bin/env lua

-- ************************************************************************-- Decoding and Encoding crap, not much to see here, citizen! Move along!-- ************************************************************************

local lpeg = require "lpeg"local xdigit = lpeg.locale().xdigitlocal char = lpeg.P"%" * lpeg.C(xdigit * xdigit) / function(c) return string.char(tonumber(c,16)) end + lpeg.P"+" / " " + lpeg.P(1)local decode_query = lpeg.Cs(char^1)

local function tohex(c) return string.format("%%%02X",string.byte(c))end

local unsafe = lpeg.P" " / "%%20" + lpeg.P"#" / "%%23" + lpeg.P"%" / "%%25" + lpeg.P"<" / "%%3C" + lpeg.P">" / "%%3E" + lpeg.P"[" / "%%5B" + lpeg.P"\\" / "%%5C" + lpeg.P"]" / "%%5D" + lpeg.P"^" / "%%5E" + lpeg.P"{" / "%%7B" + lpeg.P"|" / "%%7C" + lpeg.P"}" / "%%7D" + lpeg.P'"' / "%%22" + lpeg.R("\0\31","\127\255") / tohexlocal char_path = lpeg.P"?" / "%%3F" + unsafe + lpeg.P(1)local esc_path = lpeg.Cs(char_path^0)

-- ************************************************************************-- The main script starts here-- ************************************************************************

local query = os.getenv("QUERY_STRING")local script_name = os.getenv("SCRIPT_NAME")local pathinfo = os.getenv("PATH_INFO")

if not pathinfo and query == "" then io.stdout:write("Status: 10\n") io.stdout:write("Content-Type: Input field\n") io.stdout:write("\n") os.exit(0,true)end

if not pathinfo then query = decode_query:match(query) query = esc_path:match(query) io.stdout:write("Status: 30\n") io.stdout:write(string.format("Location: %s/%s\n",script_name,query)) io.stdout:write("\n") os.exit(0,true)end

if pathinfo:match("^/[^/]*/[^/]*/[^/]*") then local f1,f2,f3 = pathinfo:match("^/([^/]*)/([^/]*)/([^/]*)") f1 = decode_query:match(f1) f2 = decode_query:match(f2) f3 = decode_query:match(f3) io.stdout:write("Status: 20\n") io.stdout:write("Content-Type: text/gemini\n") io.stdout:write("\n") io.stdout:write("The three fields you input:\n") io.stdout:write("\n") io.stdout:write(string.format("* %s\n",f1)) io.stdout:write(string.format("* %s\n",f2)) io.stdout:write(string.format("* %s\n",f3)) io.stdout:write("\n") io.stdout:write(string.format("=

%s Try again\n",script_name)) os.exit(0,true)end

if query == "" then io.stdout:write("Status: 10\n") io.stdout:write("Content-Type: Input next field\n") io.stdout:write("\n") os.exit(0,true)else query = decode_query:match(query) query = esc_path:match(query) pathinfo = esc_path:match(pathinfo) io.stdout:write("Status: 30\n") io.stdout:write(string.format("Location: %s%s/%s\n",script_name,pathinfo,query)) io.stdout:write("\n") os.exit(0,true)end