const { ipcMain, app, BrowserWindow } = require("electron") const gemini = require("gemini-server") const fs = require("fs") const url = require("url") const path = require("path") const sigils_dir = "sigils" const gemini_port = 1965 const electron_options = { show: false, webPreferences: { sandbox: true, contextIsolation: true, nodeIntegration: false, enableRemoteModule: false, webgl: false, disableDialogs: true, preload: path.join(__dirname, "preload.js"), }, } const server_options = { cert: fs.readFileSync('tls/cert.pem'), key: fs.readFileSync('tls/key.pem') } const server = gemini(server_options) async function handle_request(request, response) { if (!request.query) { response.data("Please supply a url in the query string.") return } const target_url = decodeURIComponent(request.query) const win = new BrowserWindow(electron_options) const load_timeout_ms = 2000 const exec_timeout_ms = 2000 async function timeout(timeout_ms, reason = "") { return new Promise((resolve, reject) => { setTimeout( reject, timeout_ms, reason + " timed out after " + timeout_ms + " miliseconds" ) }) } var error var current_url var page_sigil_name var page_sigil var data = "" var available_sigils = fs.readdirSync(sigils_dir) win.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => { return callback(false) }) win.webContents.setWindowOpenHandler(() => { console.log("Blocked a popup request") return {action: 'deny'} }) console.log(`Got request for "${target_url}"`) await Promise.race([ win.loadURL(target_url) .catch((err) => { // there was an error loading the page if (err.errno !== -3) { error = err.message } }), timeout(load_timeout_ms) ]).catch(() => { // loading timed out win.webContents.stop() }) try { current_url = new URL(win.webContents.getURL()) } catch (err) { error = err.code response.data(error) win.destroy() return } for (let sigil_name of available_sigils) { if (current_url.host === sigil_name) { page_sigil_name = sigil_name console.log(`Using sigil "${sigil_name}"`) break } else if ( sigil_name[0] === "*" && current_url.host.endsWith(sigil_name.replace("*.", "")) ){ page_sigil_name = sigil_name console.log(`Using sigil "${sigil_name}"`) break } } if (! page_sigil_name) { page_sigil = fs.readFileSync(sigils_dir + "/default") } else { page_sigil = fs.readFileSync(sigils_dir + "/" + page_sigil_name) } ipcMain.on("page-gemtext", (event, arg) => { data += arg }) await Promise.race([ win.webContents.executeJavaScript(page_sigil).catch( (err) => error = err ), timeout(exec_timeout_ms, "Script execution") ]).catch((err) => error = err) ipcMain.removeAllListeners() win.destroy() if (error) { response.data(error) console.log(`Error handling request for "${target_url}": ${error}`) } else { response.data(data, mimeType="text/gemini") console.log(`Handled request for "${target_url}"`) } } app.on('window-all-closed', () => { ; }) app.whenReady().then(() => { server.on("*", handle_request) server.listen(() => { console.log(`listening on localhost:${gemini_port}`) }, gemini_port) });