I am using a combination of electron.js and xmpp.js in order to make my own client. My main project is consisted of these 2 files:
Boostrapping electron on: index.js
:
const {app,BrowserWindow,ipcMain,dialog}=require('electron'); // const app=electron.app; // const BrowserWindow=electron.BrowserWindow; // Handle creating/removing shortcuts on Windows when installing/uninstalling. if (require('electron-squirrel-startup')) { // eslint-disable-line global-require app.quit(); } // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let mainWindow; const createWindow = () => { console.log("Message"); // Create the browser window. mainWindow = new BrowserWindow(); // and load the index.html of the app. mainWindow.loadURL(`file://${__dirname}/ui/index.html`); var env = process.env.NODE_ENV || 'production'; if(env==='dev'){ // Open the DevTools. mainWindow.webContents.openDevTools(); } // Emitted when the window is closed. mainWindow.on('closed', () => { // Dereference the window object, usually you would store windows // in an array if your app supports multi windows, this is the time // when you should delete the corresponding element. mainWindow = null; }); mainWindow.maximize(); const xmpp=require('./xmpp.js'); console.log(xmpp) xmpp.xmppCli(mainWindow,ipcMain,dialog); }; // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.on('ready', createWindow); // Quit when all windows are closed. app.on('window-all-closed', () => { // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit(); } }); app.on('activate', () => { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (mainWindow === null) { createWindow(); } }); // In this file you can include the rest of your app's specific main process // code. You can also put them in separate files and import them here.
And handling it with this file xmpp.js
:
const {client, xml, jid} = require('@xmpp/client') var clientInstance=null; console.log('xmpp.js loaded') const initXmpp= function(xmpp){ xmpp.on('error', err => { console.error("Error occured",err.toString()) dialog.showErrorBox('Internal Error',err.toString()) }) xmpp.on('offline', () => { console.log('🛈', 'offline') }) xmpp.on('online', async address => { dialog.showMessageBox({'type':'info','message':"Online as:"+address.toString()}) }) xmpp.on('stanza', stanza => { console.log('⮈', stanza.toString()) xmpp.stop() }) process.on('unhandledRejection', function (reason, p) { console.log('Possibly Unhandled Rejection at: Promise ', p, ' reason: ', reason) }) xmpp.start() } module.exports.xmppCli=function(mainWindow,ipcMain,dialog){ ipcMain.on('login',(event,params)=>{ let jidVal=jid(params.username); params.server="xmpp://"+params.server console.log(jidVal.getLocal(),jidVal.getDomain(),params.server) if(!clientInstance){ console.log("Client Works"); try{ clientInstance=new client({ 'service':params.server, 'domain': jidVal.getDomain(), 'username':jidVal.getLocal(), 'password':params.password, }) initXmpp(clientInstance) } catch(e) { console.error('Internal Error',e.message) console.error(e.stack) clientInstance=null; } } }); }
Also my ui is consisted by index.html
:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Login Page</title> <link rel="stylesheet" href="../../node_modules/bootstrap/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="../../node_modules/roboto-fontface/css/roboto/roboto-fontface.css"> <style> html, body, .container-fluid { height: 100%; font-family: 'Roboto', sans-serif; } #form_wrapper{ border:1px solid; border-radius: 10px; } </style> <script>window.$ = window.jQuery = require('../../node_modules/jquery/dist/jquery.min.js');</script> <script src="../../node_modules/jquery/dist/jquery.min.js"></script> <script src="./index_renderer.js"></script> </head> <body class="bg-secondary"> <div class="container-fluid d-flex justify-content-center align-items-center"> <div id="form_wrapper" class="p-2" style="background-color:white;"> <h1 class="text-center">XMPP KEY AGREEMENT CLI</h1> <form id="loginForm"> <div class="form-group"> <label for="server">XMPP Server</label> <input id="server" class="form-control" type="text" name="server" placeholder="eg. example.com" required> </div> <div class="form-group"> <label for="username">Username</label> <input id="username" class="form-control" type="text" name="username" placeholder="eg. user@example.com" required> </div> <div class="form-group"> <label for="password">Password</label> <input id="password" class="form-control" type="text" name="password" placeholder="Type your password here" required> </div> <button type="submit" class="btn btn-primary btn-lg btn-block">Login</button> </form> </div> </div> </body> </html>
With the following renderer:
$(document).ready(function(){ const ipcRenderer = require('electron').ipcRenderer; $("#loginForm").on('submit',function(e){ e.preventDefault(); console.log("Form Submitted"); //https://stackoverflow.com/a/29000408/4706711 let data=$(this).serializeArray().reduce(function(a, x) { a[x.name] = x.value; return a; }, {}); ipcRenderer.send('login',data) }) })
But my problem is that when I fired up my own local opefireserver using self-signed certificates. Because of that I get a mysterious error that is displayed like that:
So my problems are:
- How I can debug these types of errors? I mean I cannot find out the location of this error.
- How I can manually tell either to electron or xmpp.js manually to accept self-signed certificates>
I tried to manually figure out where this error is generated via changing:
xmpp.on('error', err => { console.error("Error occured",err.toString()) dialog.showErrorBox('Internal Error',err.toString()) })
Into this:
xmpp.on('error', err => { console.error("Error occured",err.toString()) })
Still getting strange errors.
Advertisement
Answer
The @xmpp/client
library seems that lacks the api calls when connection error happens as seen in the following example:
const {client, xml, jid} = require('@xmpp/client') const initXmpp=function(xmpp){ xmpp.on('error', err => { console.error("Error occured",err.toString()) // dialog.showErrorBox('Internal Error',err.toString()) }) xmpp.on('offline', () => { console.log('🛈', 'offline') }) xmpp.on('online', async address => { dialog.showMessageBox({'type':'info','message':"Online as:"+address.toString()}) }) xmpp.on('stanza', stanza => { console.log('⮈', stanza.toString()) xmpp.stop() }) process.on('unhandledRejection', function (reason, p) { console.error('Possibly Unhandled Rejection at: Promise ', p, ' reason: ', reason) }) try{ xmpp.start() } catch(e) { console.error(e.message) } } try{ clientInstance=new client({ 'service':"xmpp://0.0.0.0:5222", 'domain': "example.com", 'username':"admin", 'password':"admin", }); initXmpp(clientInstance); } catch(e) { console.error(e); }
That I get the following error:
events.js:183 throw er; // Unhandled 'error' event ^ Error: self signed certificate at TLSSocket.<anonymous> (_tls_wrap.js:1105:38) at emitNone (events.js:106:13) at TLSSocket.emit (events.js:208:7) at TLSSocket._finishInit (_tls_wrap.js:639:8) at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:469:38)
And offers no way to handle it, that is because the SSL/TLS connections are handled by the nodejs itself thus the only solution can be found on this gthub answer.
Based on the solution mentioned above actually combined with NODE_ENV
environmental variable I placed the following code snippet on index.js
:
if(env==='dev' || env=='debug'){ process.env.NODE_TLS_REJECT_UNAUTHORIZED=0; }
Also if using this I would suggest to fork it and modify it to use latest version of ubuntu (or even a more stable docker image) and later version of openfire such as this guy did.