Initial commit.

This commit is contained in:
Matjaz
2022-01-07 22:36:22 +01:00
parent 69998716f0
commit 22369b1137
11 changed files with 377 additions and 0 deletions

3
.jshintrc Normal file
View File

@ -0,0 +1,3 @@
{
"esversion": 6
}

17
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "pwa-node",
"request": "launch",
"name": "Launch Program",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}\\index.js"
}
]
}

9
gopherroot/html.gop Normal file
View File

@ -0,0 +1,9 @@
# Spletne povezave
Kaj se dogaja, zakaj ne dela??
=> http://www.google.com Google
=> https://www.najdi.si Najdi.si
=> gopher://mozz.us/ Gopher
=> gemini://mozz.us/ Gemini :)

29
gopherroot/index.gop Normal file
View File

@ -0,0 +1,29 @@
# Uplink Gopher Server
A story of an old gopher server
A long time ago I wrote a little gopher client in C#. It was a fun excersize as I was learning to program. To test it I also wrote a little server to go along with it. Years passed and my gopher client can't handle a big part of the net that switched to IPv6. Oh well. The server works as intended, but the old 3.5 .net framework is outdated. I would need to recompile it but I cannot make the new mono compilers work on Solaris (the old one works fine). So I fel t a little stuck with the old server and old framework. And a half working client.
Then one day I found gemini protocol and several projects around it that also support gopher protocol. I especially liked Lagrange client that can handle gemini and gopher. I began research and experimentation. I like how it renders menues, how it displays text. It makes this old prtocol shine with the beautiful typografy. It really is my new favourite way to look at gopher.
=> gemini://gemini.circumlunar.space/docs/ Gemini protocol
=> gemini://skyjake.fi/lagrange/ Lagrange
Gemini (and Lagrange in particular) took several ideas from gopher and some from the www and created gemini. Even better - these new paradigms were then applied to gopher aswell.
Let me elaborate a little on this paradigm shift.
Gopher is a protocol that serves two kinds of content - menus and files (ok, there are also several others like images, links to telnet servers, search items and so on). Menus are - as far as gopher is concerned - just that - menus. Items can be seen as files, just like in ftp. There is even gopher file system driver - you can mount gopher server as filesystem under linux. Lagrange displays menus as a form of websites.
When I was creating my gopher client and server, now about ten years ago, I never tought about that. My client takes the menu content and renders it in a full window listbox element. I tought about this listbox as being a "page" ... but man, Lagrange really took it to the next level.
And this kind of thinking opens up new ideas - that got implemented into gemini servers. Gemtext files - gemini markup language. Markup for menus that are really pages. This could be applied to gopher aswell. Others probably already did it ... I had to try.
So I created a small gopher server in NodeJS. 99 lines of code at the moment. It takes gemtext file and transorms it - line by line - into gopher menu and then sends it over to the client. Works nicely.
99 lines of code is not a lot. I did not bother implementing folder browsing. Folder browsing is so ... 80's when it comes to gopher. Or maybe 2010. Gemini influence is ... at least for me - a new boost in interest for gopher.
But my previous server was capable of properly handling Gopher+ requests. The chalange is to do the same in NodeJS. That might prove to be a chalange indeed as the paradigm shift never took Gopher+ into account. Will try it, lets see hot it works.
And that is not all, by any mean. As gemtext supports some formating, similar to what is supported in markdown, you can mark titles, lists and so on. Markup is a really small subset of markdown, but nevertheless usefull. And that very same markup can be used also in gopher. And Lagrange will happily format the text accordingly. Wow.

19
gopherroot/new.gop Normal file
View File

@ -0,0 +1,19 @@
# Povezave na spletne strani:
Tukaj je še nekaj besedila, ki zaenkrat služi samo za testiranje.
Kaj bi še napisal? Ne vem zares :)
=> gopher://uplink.si/1/test/index.gop
=> gopher://uplink.si/1/test/index.gop
=> gopher://uplink.si/1/test/index.gop
=> gopher://uplink.si/1/test/index.gop
## Povezave na druge gopher strežnike
=> gopher://gopher.floodgap.com/ Floodgap strežnik
=> gopher://gopher.floodgap.com:70/1/new To je tudi en strežnik ...
=> gopher://gopher.floodgap.com:70/0/v2/help/indexer Indekser na Floodgapu :)
## Lokalne Povezave
=> gopher://localhost:1985/1/index.gop Notranja povezava na index ... da vidimo, če dela :)

View File

@ -0,0 +1,5 @@
# Dela?
Seveda dela, to je delujoča zadeva.
Gopher deluje. To je star protokol, ki ga je res enostavno implementirati tudi v modernih jezikih.

96
gopherroot/sub.gop Normal file
View File

@ -0,0 +1,96 @@
# Subscribing to Gemini pages
## Introduction
This document describes a convention whereby Gemini clients can "subscribe" to a regularly updated Gemini page (such as the index page of a gemlog) even in the absence of a full-fledged syndication technology like Atom or RSS. It is intended as a lightweight alternative to such technologies to lower the barriers to publishing serial content in Geminispace which can be easily followed without tedious regular manual checking of bookmarks. In particular, it is an explicit goal that a simple, manually-updated, human readable index page of the type content authors would likely create anyway should be able to subscribed to without any special changes being necessary. Obviously, such a convention will be less powerful than more complicated technologies such as Atom and will not work as well as more complicated technologies in all conceivable use cases. Nevertheless, it is expected to function adequately for a wide range of reasonable use cases. Nothing in this convention prevents content authors from simultaneously publishing an Atom feed if they wish to. In fact, this convention can ease the generation of said feeds.
The remainder of this document describes how to interpret a single text/gemini document as if it were an Atom feed with all required elements present. The convention is described this way to ensure it is possible for clients to support both this lightweight subscription convention and subscribing to Atom feeds with a simplified codebase and consistent UI, and to demonstrate how simple automatic generation of Atom feeds is possible. Simpler clients which support only this subscription convention are free to ignore Atom elements as they see fit.
## Feed elements
The URL from which the text/gemini document is fetched serves as the feed's required "id" element and the recommended "link" element.
The contents of the first header line in the document beginning with a single # serves as the feed's required "title" element. For this reason, authors are encouraged to use titles which provide their own context, e.g. "Abelard Lindsay's gemlog" rather than "My gemlog" or "Gemlog index".
If a header line beginning with ## occurs in the document after the first line beginning with a single # but before any non-empty, non-header lines, its contents may serve as the feed's optional "subtitle" element.
A feed's required "updated" element should be set equal to the most recent value from all the associated entry's required "updated" elements. If no entries can be extracted from the document, then the feed is empty (which is permitted by the Atom standard), and the feed's "updated" element should be set equal to the time the document was fetched.
## Entry elements
A feed's entry elements are derived from a subset of its link lines, if any are present.
Each link line where the URL is followed by a label whose first 10 characters correspond to a date in ISO 8601 format (i.e. YYYY-MM-DD) represents a single entry. Link lines which do not meet this criteria are ignored.
An entry's required "id" element and required "link" element with rel="alternate" ("link" elements are optional in Atom entries in general, but this convention does not assign "content" elements to entries and therefore a rel="alternate" link becomes required) are both equal to the URL of the corresponding link line.
An entry's required "updated" element is noon UTC on the day indicated by the 10 character date stamp at the beginning of the corresponding link line's label.
An entry's required "title" element is derived from what remains of the corresponding link line's label after discarding the first whitespace-separated component (which necessarily includes the date stamp). Clients may simply take the entirety of the remainder, but some simple sanitisation may be attempted to account for the fact that users may e.g. use labels with a separator between date and title such as "1965-03-23 - Gemini 3 launch successful!".
## Example
The Gemini document below, served from gemini://gemini.jrandom.net/gemlog/:
```
# J. Random Geminaut's gemlog
Welcome to my Gemlog, where you can read every Friday about my adventures in urban gardening and abstract algebra!
## My posts
=> bokashi.gmi 2020-11-20 - Early Bokashi composting experiments
=> finite-simple-groups.gmi 2020-11-13 - Trying to get to grips with finite simple groups...
=> balcony.gmi 2020-11-06 - I started a balcony garden!
## Other gemlogs I enjoy
=> gemini://example.com/foo/ Abelard Lindsay's gemlog
=> gemini://example.net/bar/ Vladimir Harkonnen's gemlog
=> gemini://example.org/baz/ Case Pollard's gemlog
=> ../ Back to my homepage
Thanks for stopping by!
```
may be interpreted as equivalent to the following Atom feed:
```
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>J. Random Geminaut's gemlog</title>
<link href="gemini://gemini.jrandom.net/gemlog/"/>
<updated>2020-11-20T12:00:00Z</updated>
<id>gemini://gemini.jrandom.net/gemlog/</id>
<entry>
<title>Early Bokashi composting experiments</title>
<link rel="alternate" href="gemini://gemini.jrandom.net/gemlog/bokashmi.gmi"/>
<id>gemini://gemini.jrandom.net/gemlog/bokashmi.gmi</id>
<updated>2020-11-20T12:00:00Z</updated>
</entry>
<entry>
<title>Trying to get to grips with finite simple groups...</title>
<link rel="alternate" href="gemini://gemini.jrandom.net/gemlog/finite-simple-groups.gmi"/>
<id>gemini://gemini.jrandom.net/gemlog/finite-simple-groups.gmi</id>
<updated>2020-11-13T12:00:00Z</updated>
</entry>
<entry>
<title>I started a balcony garden!</title>
<link rel="alternate" href="gemini://gemini.jrandom.net/gemlog/balcony.gmi"/>
<id>gemini://gemini.jrandom.net/gemlog/balcony.gmi</id>
<updated>2020-11-06T12:00:00Z</updated>
</entry>
</feed>
```
## Shortcomings
The primary shortcoming of this convention is that it does not convey a time of day at which posts are made nor a timezone in which the date stamp is valid. This makes lightweight subscription a poor match for applications where multiple updates are expected each day and the relative order of updates (both within and across feed sources) is important, such as following breaking news headlines, weather updates, traffic conditions, etc. Such applications are strongly encouraged to instead implement more robust subscription technologies such as Atom or RSS.
This shortcoming is not expected to have serious implications for a wide range of common and valuable activities in Geminispace which operate at "human scale". For example, this convention is perfectly viable for an individual reader using their local client to subscribe to ten or twenty hand-picked gemlogs which update every few days with non-time-critical content about people's daily lives, hobbies, opinions on the state of the world, recipes, photos, etc. It is very rarely important to read content like this which was written by Alice on Wednesday morning before that which was written by Bob on Wednesday evening, or to know exactly when each person wrote their posts. If the time of day is relevant to the post content, the author will surely mention it.

56
gopherroot/test.gop Normal file
View File

@ -0,0 +1,56 @@
[last updated 2020-12-11]
=> gopher://mozz.us:70/1/about About this server
# HISTORICAL INTERESTS
=> gopher://mozz.us:70/1/ccso CCSO Nameserver Resources
=> gopher://mozz.us:70/1/wayback The Gopherspace Wayback Machine
=> gopher://mozz.us:70/1/funnybone Funny Bone ASCII E-Zine Archive
# SERVICES HOSTED HERE
=> gopher://ascii.mozz.us:7070/1/ Enter the ASCII Art Emporium
=> gopher://mozz.us:7003/1/ Browse over 2,000 drink recipes
=> gopher://mozz.us:7005/1/ Build modern gopherholes w/ python
=> gopher://mozz.us:7006/1/ Play text-based adventure games
# MY AWARD WINNING CONTENT (gopher exclusive!)
=> gopher://mozz.us:70/1/phlog My single page phlog
=> gopher://mozz.us:70/1/advent 2018 advent beer reviews
=> gopher://mozz.us:70/1/bookmarks Unsorted web bookmarks
=> gopher://tilde.town:70/1/~mozz A tiny literature service (tilde.town/~mozz)
# GOPHER APPLETS (pocket-sized tools and games)
=> gopher://mozz.us:70/1/apps/hacking Play Terminal Hacker
=> gopher://mozz.us:70/1/apps/montyhall Monty Hall Problem
=> gopher://mozz.us:70/1/apps/morsecode Morse Code Generator
=> gopher://mozz.us:70/1/apps/wish Digital Wishing Well
=> gopher://mozz.us:70/1/apps/dadjoke A Random Dad Joke
=> gopher://mozz.us:70/1/apps/moon Phase of the Moon
# MISCELLANEOUS FILES
=> gopher://mozz.us:70/1/music Music Library
=> gopher://mozz.us:70/1/flash Flash Library
# BEYOND GOPHER
=> https://portal.mozz.us/gemini/mozz.us/ gemini://mozz.us (new!)
=> finger://mozz.us/michael finger://mozz.us/michael
telnet://mozz.us
```
/ mozz.us 23
```
=> https://mozz.us https://mozz.us
Also check out my other gopherhole, hosted on a Raspberry Pi!
=> gopher://hngopher.com:70/1/ The Underground Hacker News Mirror (hngopher.com)
This server is proudly running CentOS on a $10 DigitalOcean Droplet.
So long, and thanks for all the fish.
```
\
,d@@b, \.
._.__._._@@@@@@__...__.._..___._. _) `----._ ._..__._._.__.___._._.__.
-_-__-_- _.' e`.__
-_-__- _ ,-'..---~~~)/---'~~~
- '.,',',- '
Your IP: 89.143.45.81 _ -_ - Powered by Flask-Gopher/2.2.0

123
index.js Normal file
View File

@ -0,0 +1,123 @@
const net = require('net');
const fs = require('fs');
const url = require('url');
const settings_json = fs.readFileSync('settings.json');
const settings = JSON.parse(settings_json);
function getPage(path, next) {
var index;
fs.readFile(settings.root + path, 'utf8', function(err, data) {
if(err) {
console.log('ERROR: ' + err);
return;
} else {
index = data.split(/\r?\n/);
next(index);
}
});
}
function fileExist(path) {
return fs.existsSync(settings.root + path);
}
function isDirectory(path) {
return fs.statSync(settings.root + path).isDirectory();
}
const server = new net.createServer(function(c) {
//console.log('Server Connected');
c.on('end', function() {
//console.log('Server Disconnected');
});
c.on('data', function(data) {
var datetime = new Date();
if(data.toString().trim() == '') data = settings.index;
console.log(data);
data = decodeURIComponent(data.toString());
var gopherSelector = data.toString().trimEnd();
console.log('selector: <' + gopherSelector + '>');
var gopherItems = gopherSelector.split('\t');
console.log(gopherItems);
if(fileExist(gopherItems[0]) && (gopherItems[0].indexOf('..') == -1)) {
if(isDirectory(gopherItems[0])) {
if(gopherItems[0][gopherItems[0].length - 1] != '/') {
gopherItems[0] = gopherItems[0] + '/' + settings.index;
} else gopherItems[0] = gopherItems[0] + settings.index;
}
getPage(gopherItems[0], function(index) {
console.log(datetime.toISOString() + ' - ' + c.remoteAddress + ':' + c.remotePort + ' - <' + gopherItems[0] + '>');
for(var i = 0; i < index.length; i++) {
if(index[i].startsWith('=> ')) {
var selector = index[i];
selector = selector.replace('=>', '').trimStart();
var address = selector.split(' ');
selector = selector.replace(address[0], '').trimStart();
var server = 'noserver.host';
var path = 'nopath';
var protocol = 'gopher';
var type = '1';
var port = '70';
// gopher://uplink.si:1985/1index.gop
if(address[0].includes('://')) {
var myUrl = new url.URL(address[0]); // gopher://uplink.si:1985/1index.gop
protocol = myUrl.protocol; // gopher
if(protocol == 'gopher:') {
server = myUrl.hostname; // uplink.si
path = myUrl.pathname; // /1index.gop
path = path.substring(1, path.length); // 1index.gop
if(path.startsWith('/'))
path = path.substring(1, path.length - 1); // 1index.gop
type = path.substring(0, 1); // 1
path = path.substring(1, path.length - 1); // 1index.gop
if(type.trim() == '') type = '1'; //
if(path.startsWith('/'))
path = path.substring(1, path.length - 1); // 1index.gop
if(myUrl.port) port = myUrl.port; // 1985
c.write(type + selector + '\t' + path + '\t' + server + '\t' + port + '\t+\r\n');
} else if((protocol == 'http:') || (protocol == 'https:') || (protocol == 'gemini:')) {
type = 'h';
path = 'URL:' + address[0];
server = settings.domain;
port = settings.port;
c.write(type + selector + '\t' + path + '\t' + server + '\t' + port + '\r\n');
}
}
} else {
if(gopherItems[1] == '$') {
c.write('+INFO: i' + index[i] +
'\tfake.selector' +
'\tfake.serv' +
'\t70' +
'\t+\r\n' +
'+ADMIN:\r\n' +
' Admin: ' + settings.admin + '\r\n' +
' Mod-Date: Wed Jul 28 17:02:01 1993 <19930728170201>\r\n' +
'+VIEWS:\r\n' +
' Text/plain: <10k>\r\n' +
' application/postscript: <100k>\r\n' +
' Text/plain De_DE: <15k>\r\n' +
' application/MacWriteII: <45K>\r\n' +
'+ABSTRACT:\r\n' +
' This is a short (but multi-line) abstract about the\r\n' +
' item. Two or three lines ought to be enough\r\n');
} else {
c.write('i' + index[i] + '\tfake.selector\tfake.serv\t70\t+\r\n');
}
}
}
c.write('.\r\n');
c.end();
});
} else {
console.log(datetime.toISOString() + ' - ' + c.remoteAddress + ':' + c.remotePort + ' - <' + gopherItems[0] + '>' + ' - ERROR');
c.write('iError: ' + gopherItems[0] + ' not found.\terror.serv\terror.serv\t70\r\n.\r\n');
c.end();
}
});
});
server.listen(settings.listen_port, function() {
console.log('Server Bound');
});

12
package.json Normal file
View File

@ -0,0 +1,12 @@
{
"name": "gopher",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node index.js"
},
"author": "",
"license": "ISC"
}

8
settings.json Normal file
View File

@ -0,0 +1,8 @@
{
"port": 70,
"listen_port": 1985,
"domain": "uplink.si",
"root": "gopherroot/",
"index": "index.gop",
"admin": "Space-Cowboy <space.cowboy@uplink.si>"
}