Blog indexRollingšŸ„ŽblogPermalink

Doppio CGI examples

Jake Thoughts ā€” 10 Sep 2021 23:02:35 -0400 *edit Nov 02 2021, realized the code had an issue, fixed.

One cannot easily understand how to use CGI with Doppio, a Gemini Server. Here I will provide some examples which may require input from a user. My language of choice is Perl but these examples should be trivial to translate into other languages. The `-T` flag indicates taint-mode and that Perl should do not allow unsafe interactions with user submitted data. You should do what ever the equivalent is for other languages.

The example yaml file suggests that `cgiDir` is absolute. This is wrong; it uses a relative path, so for me it is `cgi/`.

guestbook.cgi
#!/usr/bin/perl -T
use strict;
use warnings;

# Doppio expects header in this format
print "Content-Type: text/gemini\n\n"; 

open(my $fh, "<", "/path/to/guestbook.txt") or die "$0 cannot read guestbook.txt: $!";
while (my $line = <$fh>) {
	print $line;
}
close($fh);
print "=> ./sign.cgi Sign the guest book!";
sign.cgi
#!/usr/bin/perl -T
use strict;
use warnings;
use POSIX qw(strftime);

# checking to see if key has a value/exists (returns true or false)
if ($ENV{QUERY_STRING}) { 
	my $date = strftime "%b %e %Y", localtime;
	open(my $fh, '>>', "/path/to/guestbook.txt") or die "$0 cannot append guestbook.txt: $!";
		# write to file
		print $fh "$date: $EVN{QUERY_STRING}\n"; 
	close($fh);
	# Redirection for browsers
	print "Status: 30\nContent-Type: ./guestbook.cgi\n\n"; 
} else {
	# Prompt user to give query
	print "Status: 10\nContent-Type: What will you sign in the guest book?\n\n"; 
}
login.cgi
#!/usr/bin/perl -T
use strict;
use warnings;

# checking to see if key has a value/exists (returns true or false)
if ($ENV{REMOTE_USER}) { 
	# in case matching does not work
		my $remote_user = "47";
		# matching the common name
		if ($ENV{REMOTE_USER} =~ m/,?CN=(.+),?) { 
			$remote_user = $1;
		}
		print "Content-Type: text/gemini\n\n";
		print "# Welcome back, agent $remote_user!\n";
		# Maybe put a heredoc right here? Just an idea :)
} else {
	# Prompts the user to submit a cert.
	print "Status: 60\nContent-Type: Certificate (any) required."; 
}

Scripts that prompt the user to do something will run twice, so sign.cgi and login.cgi will run two times depending on what header (the first print) Doppio receives from them and will omit anything after.

Some advice... Most (all?) languages have environment varibles, so, maybe one could have a script print out each key and the value of each key so you know what you can work with. (a hint to search `x-programming-language environment variables`)

Additional advice... If you follow the OpenSSL instructions from Doppio's github page, I recommend setting `-days` (on the second command [it has `req` and `-x509`]) to a high number because it defaults at a low number which can be problematic for a protocol that TOFUs.