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'); });