#!/usr/bin/perl use OpenBSD::Pledge; use OpenBSD::Unveil; unveil(); pledge("rpath", "inet", "dns", "proc", "exec"); use strict; use warnings; use IO::Socket::INET; our (@history, @link, @page, $current); $current = 0; sub view { return unless defined $page[$current]; # Prevent exiting when pager fails $SIG{PIPE} = sub { return; }; open(my $pipe, "|-", "less"); print $pipe $page[$current]; close $pipe; } sub fetch { unless (defined $_[0]) { print "No such url\n"; return; } my $url = $_[0]; $url =~ s+gopher://++; $url =~ s/:[0-9]+//; $url =~ s/ //g; my @part = split "/", $url; my $socket = IO::Socket::INET->new(PeerHost => $part[0], PeerPort => 70) or do { print "Can't connect to $part[0]\n"; return; }; my $index; my $offset; ++$current; if (defined $history[$current + 1]) { $index = 1; for (@history) { next unless defined $_; $offset = $index if $index eq $current + 1; ++$index; } splice @history, $offset, $index - $offset; splice @link, $offset, $index - $offset; } $offset = 0; if (defined $part [1]) { ++$offset if length $part[1] eq 1; } my $path; if (defined $part[1 + $offset]) { $path = "/" . join "/", @part[1 + $offset .. $#part]; } else { $path = "/"; } print $socket "$path\r\n"; $page[$current] = ""; $index = 1; my @text = <$socket>; $history[$current] = $part[0] . $path; # Check if this is a gopher menu. if ($text[-1] eq ".\r\n") { for (@text) { my $type = substr($_, 0, 1); substr($_, 0, 1) = ""; my @part = split "\t"; $_ =~ s:[\t\n\r]::g for @part; if ($type eq "i") { $page[$current] = "$page[$current]$part[0]\n"; next; } if ($type eq 1 or $type eq 0) { my $rest; if ($part[3] eq 70) { $link[$current][$index] = "$part[2]$part[1]"; $rest = "$part[2]$part[1]"; } else { $rest = "$part[2]:$part[3]/$part[1] [unknown]"; } $rest =~ s:$history[$current]:\./:; $rest =~ s://:/:; $page[$current] = "$page[$current]$index: $part[0]\t$rest\n"; ++$index; next; } last if $type eq "."; $page[$current] = "$page[$current]$_"; } } else { $page[$current] = join "", @text; } close $socket; view; } sub shift_history { my $new = $current + $_[0]; return unless defined $history[$new]; $current = $new; view; } while () { print "gc> "; my $input = ; unless (defined $input) { print "\n"; last; } chomp $input; next if $input eq ""; last if $input eq "q"; if ($input eq "v") { view; next; } if ($input eq "h") { my $index = 1; for (@history) { next unless defined $_; if ($index eq $current) { print "$index: $_ <-\n"; } else { print "$index: $_\n"; } ++$index; } next; } if ($input eq "-" or $input eq "+") { unless (defined $history[$current]) { print "No current url\n"; next; } my @part = split "/", $history[$current]; unless (defined $part[1]) { print "Can't go up\n"; next; } pop @part; fetch join "/", @part; next; } if ($input eq "p" or $input eq "b") { shift_history -1; next; } if ($input eq "n") { shift_history 1; next; } if ($input =~ /^h[0-9]+$/) { substr($input, 0, 1) = ""; unless (defined $history[$input]) { print "No such index\n"; next; } $current = $input; print "$history[$current]\n"; next; } if ($input =~ /^[0-9]+$/) { unless (defined $link[$current][$input]) { print "No such link\n"; next; } fetch $link[$current][$input]; next; } fetch $input; }