395 lines
10 KiB
JavaScript
Executable File
395 lines
10 KiB
JavaScript
Executable File
//Get required packages
|
|
const electron = require('electron');
|
|
const path = require('path');
|
|
const url = require('url');
|
|
const fs = require('fs');
|
|
const asn1js = require('asn1js');
|
|
const util = require('util');
|
|
const notifier = require('node-notifier');
|
|
const smartcard = require('smartcard');
|
|
const axios = require('axios')
|
|
|
|
let class_code = "";
|
|
|
|
let counter = 0;
|
|
|
|
const {
|
|
app,
|
|
BrowserWindow,
|
|
Menu,
|
|
ipcMain
|
|
} = electron;
|
|
|
|
let mainWindow;
|
|
let AddWindow;
|
|
|
|
//Listen for the app to be ready
|
|
app.on('ready', function () {
|
|
//Create new window
|
|
mainWindow = new BrowserWindow({
|
|
width: 500,
|
|
height: 600,
|
|
webPreferences: {
|
|
nodeIntegration: true
|
|
}});
|
|
|
|
|
|
//Load html into window
|
|
mainWindow.loadURL(url.format({
|
|
pathname: path.join(__dirname, "classcode.html"),
|
|
protocol: "file:",
|
|
slashes: true
|
|
}));
|
|
|
|
//Quit app when closed
|
|
mainWindow.on("closed", function () {
|
|
app.quit();
|
|
})
|
|
|
|
//Menu from template
|
|
const mainMenu = Menu.buildFromTemplate(mainMenuTemplate);
|
|
Menu.setApplicationMenu(mainMenu);
|
|
});
|
|
|
|
function readNewCard(application) {
|
|
application.issueCommand([0x00, 0xA4, 0x04, 0x04, 0x07, 0xD6, 0x16, 0x00, 0x00, 0x30, 0x01, 0x01, 0x00])
|
|
.then(response => {
|
|
console.info(`Response: '${response}' '${response.meaning()}'`);
|
|
application.issueCommand([0x00, 0xA4, 0x02, 0x04, 0x02, 0x00, 0x02, 0x00])
|
|
.then(response => {
|
|
console.info(`Response: '${response}' '${response.meaning()}'`);
|
|
var response2 = `${response}`;
|
|
|
|
if(response2.indexOf("6a82") == 0) {
|
|
console.log("ALERTTTTTT");
|
|
readNewCard(application);
|
|
return;
|
|
}
|
|
application.issueCommand([0x00, 0xB0, 0x00, 0x00, 0xE6])
|
|
.then(response => {
|
|
console.info(`Response: '${response}' '${response.meaning()}'`);
|
|
|
|
var response2 = `${response}`;
|
|
|
|
if(response2.indexOf("000000") == 0) {
|
|
console.log("ALERTTTTTT");
|
|
readNewCard(application);
|
|
return;
|
|
}
|
|
var start = response2.indexOf("2a8468016504010101");
|
|
start = start + 18;
|
|
response2 = response2.substring(start);
|
|
|
|
while (1) {
|
|
|
|
var type = response2.substring(0, 2);
|
|
|
|
if (type == "30") break;
|
|
|
|
if (parseInt(response2.substring(2, 4), 16) > 127) {
|
|
|
|
var count = parseInt(response2.substring(2, 4), 16) - 128;
|
|
var length = "";
|
|
var k = 1;
|
|
|
|
|
|
for (k = 1; k <= count; k++) {
|
|
|
|
length += response2.substring(2 + (k * 2), 4 + (k * 2));
|
|
|
|
}
|
|
|
|
var total_length = parseInt(length, 16) * 2;
|
|
response2 = response2.substr(4 + (k - 1) * 2, total_length);
|
|
|
|
} else {
|
|
|
|
var total_length = parseInt(response2.substring(2, 4), 16) * 2 + 4;
|
|
response2 = response2.substr(4, total_length);
|
|
|
|
}
|
|
}
|
|
|
|
//region How to work with ASN.1 strings
|
|
let bmp_string_encoded = new ArrayBuffer(response2.length / 2);
|
|
|
|
let bmp_string_view = new Uint8Array(bmp_string_encoded);
|
|
|
|
var kk = 0;
|
|
|
|
for (var i = 0; i < (response2.length / 2); i++) {
|
|
|
|
bmp_string_view[i] = "0x" + response2.substring(kk, kk + 2);
|
|
kk += 2;
|
|
|
|
}
|
|
|
|
let bmp_string_decoded = asn1js.fromBER(bmp_string_encoded);
|
|
|
|
if (bmp_string_decoded.offset === (-1)) return; // Error during decoding
|
|
|
|
let obj = bmp_string_decoded.result.valueBlock.value;
|
|
|
|
var names = obj[4].valueBlock.value[0].valueBlock.value;
|
|
var surnames = obj[3].valueBlock.value[0].valueBlock.value;
|
|
var index = obj[5].valueBlock.value;
|
|
|
|
var output = [names, surnames, index];
|
|
|
|
printStudent(output);
|
|
|
|
}).catch(error => {
|
|
console.error('Error:', error, error.stack + '\n');
|
|
});
|
|
}).catch(error => {
|
|
console.error('Error:', error, error.stack + '\n');
|
|
});
|
|
}).catch(error => {
|
|
console.error('Error:', error, error.stack + '\n');
|
|
});
|
|
|
|
}
|
|
|
|
function readOldCard(application) {
|
|
application.selectFile([0xD6, 0x16, 0x00, 0x00, 0x30, 0x01, 0x01])
|
|
.then(response => {
|
|
console.info(`Response: '${response}' '${response.meaning()}'`);
|
|
application.issueCommand([0x00, 0xA4, 0x02, 0x00, 0x02, 0x00, 0x02, 0x12])
|
|
.then(response => {
|
|
console.info(`Response: '${response}' '${response.meaning()}'`);
|
|
application.issueCommand([0x00, 0xB0, 0x00, 0x00, 0xF8])
|
|
.then(response => {
|
|
console.info(`Response: '${response}' '${response.meaning()}'`);
|
|
|
|
var response2 = `${response}`;
|
|
|
|
var start = response2.indexOf("2a8468016504010101");
|
|
start = start + 18;
|
|
response2 = response2.substring(start);
|
|
|
|
while (1) {
|
|
|
|
var type = response2.substring(0, 2);
|
|
|
|
if (type == "30") break;
|
|
|
|
if (parseInt(response2.substring(2, 4), 16) > 127) {
|
|
|
|
var count = parseInt(response2.substring(2, 4), 16) - 128;
|
|
var length = "";
|
|
var k = 1;
|
|
|
|
for (k = 1; k <= count; k++) {
|
|
|
|
length += response2.substring(2 + (k * 2), 4 + (k * 2));
|
|
|
|
}
|
|
|
|
var total_length = parseInt(length, 16) * 2;
|
|
response2 = response2.substr(4 + (k - 1) * 2, total_length);
|
|
|
|
} else {
|
|
|
|
var total_length = parseInt(response2.substring(2, 4), 16) * 2 + 4;
|
|
response2 = response2.substr(4, total_length);
|
|
|
|
}
|
|
}
|
|
|
|
//region How to work with ASN.1 strings
|
|
let bmp_string_encoded = new ArrayBuffer(response2.length / 2);
|
|
|
|
let bmp_string_view = new Uint8Array(bmp_string_encoded);
|
|
|
|
var kk = 0;
|
|
|
|
for (var i = 0; i < (response2.length / 2); i++) {
|
|
|
|
bmp_string_view[i] = "0x" + response2.substring(kk, kk + 2);
|
|
kk += 2;
|
|
|
|
}
|
|
|
|
let bmp_string_decoded = asn1js.fromBER(bmp_string_encoded);
|
|
|
|
if (bmp_string_decoded.offset === (-1)) return; // Error during decoding
|
|
|
|
let obj = bmp_string_decoded.result.valueBlock.value;
|
|
|
|
var names = obj[4].valueBlock.value[0].valueBlock.value;
|
|
var surnames = obj[3].valueBlock.value[0].valueBlock.value;
|
|
var index = obj[5].valueBlock.value;
|
|
|
|
var output = [names, surnames, index];
|
|
|
|
printStudent(output);
|
|
|
|
}).catch(error => {
|
|
console.error('Error:', error, error.stack + '\n');
|
|
});
|
|
}).catch(error => {
|
|
console.error('Error:', error, error.stack + '\n');
|
|
}); }).catch(error => {
|
|
console.error('Error:', error, error.stack + '\n');
|
|
});
|
|
}
|
|
|
|
var Devices = smartcard.Devices;
|
|
var devices = new Devices();
|
|
|
|
devices.on('device-activated', event => {
|
|
console.log('Smart card reader active');
|
|
var device = event.device;
|
|
|
|
device.on('card-inserted', event => {
|
|
var card = event.card;
|
|
|
|
//console.log(`Card '${card.getAtr()}' inserted into '${event.device}'` + '\n');
|
|
var Iso7816Application = smartcard.Iso7816Application;
|
|
var application = new Iso7816Application(card);
|
|
|
|
console.log(card.getAtr());
|
|
|
|
if(card.getAtr() == "3bdd18008131fe4580f9a000000077010800079000fe") {
|
|
readNewCard(application);
|
|
} else {
|
|
readOldCard(application);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
//Add student manually
|
|
function createAddWindow() {
|
|
//Create new window
|
|
AddWindow = new BrowserWindow({
|
|
width: 300,
|
|
height: 400,
|
|
title: "Add student manually"
|
|
});
|
|
|
|
//Load html into window
|
|
AddWindow.loadURL(url.format({
|
|
pathname: path.join(__dirname, "addstudent.html"),
|
|
protocol: "file:",
|
|
slashes: true
|
|
}));
|
|
|
|
//Garbage
|
|
AddWindow.on("close", function () {
|
|
AddWindow = null;
|
|
});
|
|
}
|
|
|
|
function printStudent(item) {
|
|
|
|
var dateTime = require('node-datetime');
|
|
var dt = dateTime.create();
|
|
var formatted = dt.format('d-m-Y H:M:S');
|
|
|
|
item.push(formatted);
|
|
mainWindow.webContents.send("item:add", item);
|
|
|
|
notifier.notify({
|
|
title: "Student registered",
|
|
message: item[0] + " " + item[1],
|
|
icon: path.join(__dirname, 'ok.png')
|
|
|
|
});
|
|
|
|
axios.post('http://127.0.0.1:8889', JSON.stringify({
|
|
classes_code: class_code,
|
|
student_index: item[2],
|
|
student_name: item[0],
|
|
student_surname: item[1]
|
|
}))
|
|
.then((res) => {
|
|
console.log(`statusCode: ${res.statusCode}`)
|
|
console.log(res)
|
|
})
|
|
.catch((error) => {
|
|
console.error(error)
|
|
})
|
|
|
|
}
|
|
|
|
//Catch
|
|
ipcMain.on("item:add", function (e, item) {
|
|
|
|
printStudent(item);
|
|
|
|
AddWindow.close();
|
|
})
|
|
|
|
ipcMain.on("card:read", function (e) {
|
|
card_read();
|
|
})
|
|
|
|
ipcMain.on("set:classcode", function (e,classcode) {
|
|
class_code = classcode;
|
|
|
|
mainWindow.loadURL(url.format({
|
|
pathname: path.join(__dirname, "index.html"),
|
|
protocol: "file:",
|
|
slashes: true
|
|
}));
|
|
})
|
|
|
|
//Menu template
|
|
const mainMenuTemplate = [{
|
|
label: "Student",
|
|
submenu: [{
|
|
label: "Add student",
|
|
accelerator: process.platfrom == "darwin" ? "Command+E" : "Ctrl+E",
|
|
click() {
|
|
createAddWindow();
|
|
}
|
|
},
|
|
{
|
|
label: "Quit program",
|
|
accelerator: process.platfrom == "darwin" ? "Command+Q" : "Ctrl+Q",
|
|
click() {
|
|
app.quit();
|
|
}
|
|
}
|
|
]
|
|
}, {
|
|
label: "Classes",
|
|
submenu: [{
|
|
label: "Add classes",
|
|
click() {
|
|
createAddClassesWindow();
|
|
}
|
|
},
|
|
{
|
|
label: "Pick classes",
|
|
click() {
|
|
createSelectClassesWindow();
|
|
}
|
|
}
|
|
]
|
|
}];
|
|
|
|
//If mac psuh empty obj to Menu
|
|
if (process.platfrom == "darwin") {
|
|
mainMenuTemplate.unshift({});
|
|
}
|
|
|
|
//Add dev tools on DEV
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
mainMenuTemplate.push({
|
|
label: "DEV",
|
|
submenu: [{
|
|
label: "Toggle dev tools",
|
|
accelerator: process.platfrom == "darwin" ? "Command+I" : "Ctrl+I",
|
|
click(item, focusedWindow) {
|
|
focusedWindow.toggleDevTools();
|
|
}
|
|
},
|
|
{
|
|
role: "reload"
|
|
}
|
|
]
|
|
});
|
|
}
|