grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026/** BEGIN COPYRIGHT BLOCK * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; version 2 of the License. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA. * * Copyright (C) 2005 Red Hat, Inc. * All rights reserved. * END COPYRIGHT BLOCK **/ imports.gi.versions.Gtk = '3.0' const Gtk = imports.gi.Gtk; const Lang = imports.lang; const Coolkey = imports.gi.Coolkey; const Gio = imports.gi.Gio; const GObject = imports.gi.GObject; const Pango = imports.gi.Pango; const GLib = imports.gi.GLib; const PhoneHome = imports.phoneHome; const OperationDialog = imports.operationDialog; const PinDialog = imports.pinDialog; const ESC_CONFIG_FILE_NAME = "esc.conf"; const ESC_CONFIG_GROUP = "ESC"; const ESC_PWORD_FILE = "pword"; const STATUS_ENROLLED="Enrolled"; const STATUS_FORMATTED="Formatted"; const STATUS_UNFORMATTED="Unformatted"; const STATUS_UNKNOWN="Unknown"; const FORMATTED_CODE=2; const UNKNOWN_LABEL = "unknown"; const CoolKeyNotifyIface = ' \ \ \ \ \ \ \ \ \ \ '; // Get application folder and add it into the imports path function getAppFileInfo() { let stack = (new Error()).stack, stackLine = stack.split('\n')[1], coincidence, path, file; if (!stackLine) throw new Error('Can not find path!'); coincidence = new RegExp('@(.+):\\d+').exec(stackLine); if (!coincidence) throw new Error('Could not find current file!'); path = coincidence[1]; file = Gio.File.new_for_path(path); return [file.get_path(), file.get_parent().get_path(), file.get_basename()]; } const path = getAppFileInfo()[1]; const CoolKeyNotify = new Lang.Class({ Name: 'CoolKeyNotify', _init: function(client) { this._client = client; this._unique_name = "Unknown"; this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(CoolKeyNotifyIface, this); this._dbusImpl.export(Gio.DBus.session, '/com/jmagne/CoolKeyNotify'); this._dbusId = Gio.DBus.session.own_name( 'com.rm5248', Gio.BusNameOwnerFlags.NONE, this._nameAcquired.bind(this), this._nameLost.bind(this)); }, _nameAcquired: function( name ) { this._unique_name = name.unique_name; this._client._createCoolKeyMgr(this._unique_name); }, _nameLost: function( name ) { if(this._client._window) { this._client._window.destroy(); } }, notifyCoolKeyEvent: function(aKeyType, aKeyID, aKeyState, aData, strData) { // print("In notifyCoolKeyEvent: aKeyType: " + aKeyType + " aKeyID: " + aKeyID // + " aKeyState: " + aKeyState + " aData: " + aData + " strData: " + strData) ; this._client._notifyCoolkey(aKeyType, aKeyID, aKeyState, aData, strData); }, _getUniqueName() { return this._unique_name; } }); class ESC { constructor() { this.application = new Gtk.Application(); this.application.connect('activate', this._onActivate.bind(this)); this.application.connect('startup', this._onStartup.bind(this)); this._selectedTokenInfo = null; this._notifyCallback = null; this._currentDir = path; } _setNotifyCallback(cb) { this._notifyCallback = cb; } _clearNotifyCallback() { this._notifyCallback = null; } _onActivate() { this._window.present(); } _onStartup() { this.mgr = null; this._buildUI(); this.notify = new CoolKeyNotify(this); } _onWindowDestroy() { if(this.mgr) { this.mgr.cleanup(); } if(this.notify) { this.notify._dbusImpl.unexport(); } } _onCertDetail() { this._promptPin(); } _onFormat() { this._operationMgr = new OperationDialog.operationDialog(this); this._operationMgr.launchOperation(this._selectedTokenInfo,OperationDialog.OP_FORMAT); } _onEnroll() { this._operationMgr = new OperationDialog.operationDialog(this); this._operationMgr.launchOperation(this._selectedTokenInfo,OperationDialog.OP_ENROLL); } _onResetPin() { this._operationMgr = new OperationDialog.operationDialog(this); this._operationMgr.launchOperation(this._selectedTokenInfo,OperationDialog.OP_PIN_RESET); } _onPhoneHome() { this._phoneHomeMgr.getPhoneHomeInfo(this._selectedTokenInfo,true); } _onExit() { if(this.notify) { this.notify._dbusImpl.unexport(); this.notify = null; } if(this.mgr) { this.mgr.cleanup(); this.mgr = null; } this._window.destroy(); } _notifyCoolkey(aKeyType, aKeyID, aKeyState, aData, strData) { switch(aKeyState) { case 1000: this._insertCoolkey(aKeyType, aKeyID, aKeyState, aData, strData); break; case 1001: this._removeCoolkey(aKeyType, aKeyID, aKeyState, aData, strData); break; default: if(this._notifyCallback) { this._notifyCallback(aKeyType, aKeyID,aKeyState, aData,strData); } break; } } _getStatusString(intStatus) { switch(intStatus) { case 4: return STATUS_ENROLLED; break; case 2: return STATUS_FORMATTED; break; case 1: return STATUS_UNFORMATTED; break; default: return STATUS_UNKNOWN; break; } } _phoneHome(url) { let info = this.mgr.phone_home(url); return info; } _insertCoolkey(aKeyType, aKeyID, aKeyState, aData, strData) { if(aKeyType && aKeyID) { let inserted = new Coolkey.Token({ key_type: String(aKeyType) , cuid: aKeyID } ); this.mgr.get_token_info(inserted); let issuer = inserted.issuer; let issuedTo = inserted.issued_to; let cachedIssuer = null; if(inserted.is_a_cool_key) { cachedIssuer = this._phoneHomeMgr.getCachedIssuer(aKeyID); } if(cachedIssuer != null) { issuer = cachedIssuer; } if(issuer == null || issuer.length == 0) { issuer = STATUS_UNKNOWN; } if(issuedTo == null || issuedTo.length == 0) { issuedTo = STATUS_UNKNOWN; } let newStatus = inserted.status; if(newStatus == FORMATTED_CODE) { issuedTo = STATUS_UNKNOWN; } this._tokenStore.set (this._tokenStore.append(), [0, 1, 2, 3, 4], [issuer, issuedTo, this._getStatusString(newStatus), aKeyID,aKeyType]); let [ isSelected, iter] = this._tokenStore.get_iter_first(); while(isSelected) { if(aKeyID == this._tokenStore.get_value(iter,3)) { this.selection.select_iter(iter); break; } isSelected = this._tokenStore.iter_next(iter); } } } _updateCoolkey(originalKey) { if(originalKey) { this._removeCoolkey(originalKey.key_type, originalKey.cuid,null, null, null); this._insertCoolkey(originalKey.key_type, originalKey.cuid,null, null, null); } } _updateOperationsArea(insertedToken, doReset) { if(!insertedToken && !doReset) { return; } if(doReset) { this._operationsGrid.remove(this._phoneHomeButton); this._operationsGrid.remove(this._enrollButton); this._operationsGrid.remove(this._resetPinButton); this._operationsGrid.remove(this._formatButton); this._operationsGrid.remove(this._certDetailButton); this._operationsGrid.show_all(); return; } if(insertedToken.is_a_cool_key) { this._operationsGrid.add(this._phoneHomeButton); this._operationsGrid.add(this._formatButton); if(insertedToken.status == 2 ) { this._operationsGrid.add(this._enrollButton); } // if enrolled if(insertedToken.status == 4) { this._operationsGrid.add(this._enrollButton); this._operationsGrid.add(this._resetPinButton); this._operationsGrid.add(this._certDetailButton); } this._operationsGrid.show_all(); } else { if(insertedToken.status == 4) { this._operationsGrid.add(this._certDetailButton); this._operationsGrid.show_all(); } } } _removeCoolkey(aKeyType, aKeyID, aKeyState, aData, strData) { let [ isSelected, iter] = this._tokenStore.get_iter_first(); while(isSelected) { if(aKeyID == this._tokenStore.get_value(iter,3)) { this._tokenStore.remove(iter); this._tokenInfoBuffer.text = ""; this._updateOperationsArea(null,true); return; } isSelected = this._tokenStore.iter_next(iter); } this._updateOperationsArea(null,true); } _updateTokenInfoTextView(coolkey_token) { this._tokenInfoBuffer.text = ""; let cuid = coolkey_token.cuid; let atr = coolkey_token.atr; let issuer_info = coolkey_token.issuer_info; this._tokenInfoBuffer.text = "Token ID: " + cuid + "\n" + "ATR: " + atr + "\n" + "Token Issuer URL: " + issuer_info + "\n" + "Token is a CoolKey: " + coolkey_token.is_a_cool_key + "\n\n\n " ; } _getCertListDetail(certsList,pFileName) { let i = 0; let detail = "\n\nCerts In Detail \n\n"; let curDetail = ""; if(certsList == null) { return null; } //right now for some reason specifying a nickname will list all certs for(i = 0 ; i < 1; i++ ) { curDetail = this._getCertDetail(certsList[i],pFileName); if(curDetail.length < 100) { detail += "Error finding cert detail."; } else { detail += curDetail + "\n"; } } return detail; } _getCertDetail(certObj, pFileName) { if(certObj == null || pFileName == null) { return null; } let certDetail = ""; let token = ""; let nick = ""; if(certObj.token == null) token = "internal"; else { token = certObj.token; } nick = certObj.nick; let tokenNick = '"' + token + ":" + nick + '"' ; token = '"' + token + '"'; let argv1 = ['/usr/bin/certutil','-L','-d', this._getConfigPath(), '-h', token, '-f' , pFileName, '-n', tokenNick]; print("argv1: " + argv1); certDetail = this._execProgram(argv1); return certDetail; } _getCertList(coolkey_token,pFileName) { let certInfo = ""; certInfo = this._execProgram(['/usr/bin/certutil','-L','-d', this._getConfigPath(), '-h', 'all', '-f' , pFileName]); let certs = this._parseCertList(certInfo); let i = 0; let certsInfo = "Certificate List\n"; if(certs.length == 0 ) { certsInfo = certsInfo + "No Certificates Found\n"; return certsInfo; } for(i = 0 ; i < certs.length; i++ ) { certsInfo += "\n" + certs[i].token + " : " + certs[i].nick + "\n"; } certsInfo += this._getCertListDetail(certs,pFileName) + "\n"; return certsInfo; } _parseCertList(listStr) { if(!listStr) return; let lines = listStr.split('\n'); let line = ""; let i = 0; let token = ""; let nick = ""; let certlist = []; for(i = 3 ; i < lines.length; i++) { token = ""; nick = ""; let index = lines[i].search(/,/i); if(index > 0) { line = lines[i]; index -= 2; line = line.substring(0,index); line = line.trim(); let sepIndex = line.indexOf(":"); if(sepIndex > 0 ) { let res = line.split(":"); if(res.length == 2) { token = res[0]; nick = res[1]; } else { token = ""; nick = line; } certlist.push({ token: token, nick: nick, certDetail: "" }); } } } return certlist; } _execProgram(args) { let result = -1; try { let proc = new Gio.Subprocess({ argv: args , flags: Gio.SubprocessFlags.STDOUT_PIPE }); proc.init(null); let [res,stdoutb,stderrb] = proc.communicate_utf8(null,null); if(res == false) { result = -1; return result; } result = stdoutb.toString(); } catch (e) { return result; } return result; } _onTokenTreeViewSelectionChanged() { let [ isSelected, model, iter ] = this.selection.get_selected(); if(isSelected == false) return; let selected = new Coolkey.Token({ key_type: this._tokenStore.get_value(iter,4) , cuid: this._tokenStore.get_value(iter,3) } ); this.mgr.get_token_info(selected); this._updateOperationsArea(selected,true); this._updateOperationsArea(selected,false); this._selectedTokenInfo = selected; this._updateTokenInfoTextView(selected); } _configValueWithKeyID(aKeyID,configValue) { if(!configValue || !aKeyID) return null; return configValue + "-" + aKeyID; } _setConfigValue(name,value) { if(!this._configFile) { return; } try { this._configFile.set_string(ESC_CONFIG_GROUP,name,value); this._configFile.save_to_file(this._configFileName); } catch(e) { return; } } _getConfigValue(name) { if(!this._configFile) { return null; } let value = null; try { value = this._configFile.get_string(ESC_CONFIG_GROUP,name); } catch(e) { return null; } return value; } _getPropertyValue(name) { if(!this._propFile) { return null; } let value = null; try { value = this._propFile.get_string(ESC_CONFIG_GROUP,name); } catch(e) { return null; } return value; } _initProperties() { this._propFile = new GLib.KeyFile(); this._propFileName = this._currentDir + "/esc.properties"; let res = false; try { res = this._propFile.load_from_file (this._propFileName, 0 ); } catch(e) { this._onExitClicked(); //IO error } } _initConfig() { // See if it exists already, otherwise create this._configFile = new GLib.KeyFile(); this._configPath = GLib.get_user_config_dir() + "/esc"; let configDir = Gio.File.new_for_path(this._configPath); try { configDir.make_directory(null); } catch (e) { if(e.matches (Gio.IOErrorEnum, Gio.IOErrorEnum.EXISTS)) { } else { this._onExitClicked(); } } this._configFileName = this._configPath + "/esc.conf"; let res = false; try { res = this._configFile.load_from_file (this._configFileName, 0 ); } catch(e) { // need to create config file } if (res == false) { this._configFile.set_string("ESC","name","Smart Card Utility"); this._configFile.save_to_file(this._configFileName); } } _initConfigTokenManuIDs() { this._setConfigValue("esc.token.manu_id.0","Volkswagen AG"); } _buildUI() { // Create the application window this._window = new Gtk.ApplicationWindow({ application: this.application, window_position: Gtk.WindowPosition.CENTER, border_width: 0}); try { this._window.set_icon_name('application-x-executable'); } catch (err) { this._window.set_icon_name('application-x-executable'); } this._hb = new Gtk.HeaderBar({spacing: 10 }); this._hb.set_title("Smart Card Manager"); this._hb.set_show_close_button(true); this._window.set_titlebar(this._hb); this._infoLabel = new Gtk.Label({label: "Token Information",use_markup: true}); this._tokenLabel = new Gtk.Label({label: "Tokens",use_markup: true}); this._vSep = new Gtk.VSeparator(); this._hb.add(this._tokenLabel); this._hb.pack_end(this._infoLabel); //this._hb.add(this._vSep); this._initConfig(); this._initConfigTokenManuIDs(); this._initProperties(); this._statusMessages = null; this._getStatusMessages(); this._outerGrid = new Gtk.Grid({orientation: Gtk.Orientation.HORIZONTAL, border_width: 0}); this._tokenFrame = new Gtk.Frame({hexpand: false, width_request: 300}); this._infoFrame = new Gtk.Frame({border_width: 0}); this._tokenBox = new Gtk.Grid({orientation: Gtk.Orientation.VERTICAL, border_width: 0,hexpand: true}); this._tokenFrame.add(this._tokenBox); this._infoBox = new Gtk.Grid({orientation: Gtk.Orientation.VERTICAL, border_width: 0,hexpand: true}); this._infoFrame.add(this._infoBox); this._outerGrid.add(this._tokenFrame); this._outerGrid.add(this._infoFrame); this._window.add(this._outerGrid); this._currentToken = null; // Create ListStore and Tree View this._tokenStore = new Gtk.ListStore (); this._tokenStore.set_column_types ([ GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING, GObject.TYPE_STRING]); // Create the treeview this._tokenTreeView = new Gtk.TreeView ({ hexpand: true, vexpand: true, model: this._tokenStore }); let issuer = new Gtk.TreeViewColumn ({ title: "Issuer" }); let issuedTo = new Gtk.TreeViewColumn ({ title: "Issued To" }); let status = new Gtk.TreeViewColumn ({ title: "Status" }); //let tokenId = new Gtk.TreeViewColumn({ title: "Token Id"}); let bold = new Gtk.CellRendererText ({ weight: Pango.Weight.BOLD}); let normal = new Gtk.CellRendererText (); issuer.pack_start (bold, true); issuedTo.pack_start (normal, true); status.pack_start (normal, true); //tokenId.pack_start(normal,true); issuer.add_attribute (bold, "text", 0); issuedTo.add_attribute (normal, "text", 1); status.add_attribute (normal, "text", 2); //tokenId.add_attribute(normal,"text",3); this._tokenTreeView.insert_column (issuer, 0); this._tokenTreeView.insert_column (issuedTo, 1); this._tokenTreeView.insert_column (status, 2); //this._tokenTreeView.insert_column (tokenId, 3); // Connect view slection to a method this._label = new Gtk.Label ({ label: "" }); this.selection = this._tokenTreeView.get_selection(); this.selection.set_mode(Gtk.SelectionMode.BROWSE); // When something new is selected, call _on_changed this.selection.connect ('changed', Lang.bind (this, this._onTokenTreeViewSelectionChanged)); //Create a TextView with it's own TextBuffer this._tokenInfoBuffer = new Gtk.TextBuffer(); this._tokenInfoTextView = new Gtk.TextView ({ buffer: this._tokenInfoBuffer, editable: false, wrap_mode: Gtk.WrapMode.WORD, hexpand: true }); this._scrolledTokenInfoWindow = new Gtk.ScrolledWindow ({ hscrollbar_policy: Gtk.PolicyType.AUTOMATIC, vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, shadow_type: Gtk.ShadowType.ETCHED_IN, height_request: 500, width_request: 400 }); this._scrolledTokenInfoWindow.add_with_viewport (this._tokenInfoTextView); //this._infoBox.add(this._headerInfoBox); //this._escImage = new Gtk.Image({file: this._currentDir + "/" + "enrolled-keyx2.png"}); //this._infoBox.add(this._escImage); this._infoBox.add(this._scrolledTokenInfoWindow); this._operationsGrid = new Gtk.Grid({orientation: Gtk.Orientation.HORIZONTAL, halign: Gtk.Align.CENTER, valign: Gtk.Align.CENTER, row_spacing: 10, column_spacing: 6, border_width: 6}); this._operationsLabel = new Gtk.Label({label: "Operations", use_markup: true}); this._certDetailButton = new Gtk.Button({ label: "Cert Info" }); this._certDetailButton.connect("clicked", this._onCertDetail.bind(this)); this._infoBox.add(this._operationsGrid); this._exitButton = new Gtk.Button({ label: "Exit" }); this._exitButton.connect("clicked", this._onExit.bind(this)); this._operationsGrid.add(this._exitButton); this._operationsGrid.add(this._operationsLabel); // operations buttons this._phoneHomeButton = new Gtk.Button({ label: "Phone Home" }); this._phoneHomeButton.connect("clicked", this._onPhoneHome.bind(this)); this._formatButton = new Gtk.Button({ label: "Format" }); this._formatButton.connect("clicked", this._onFormat.bind(this)); this._enrollButton = new Gtk.Button({ label: "Enroll" }); this._enrollButton.connect("clicked", this._onEnroll.bind(this)); this._resetPinButton = new Gtk.Button({ label: "Reset Pin" }); this._resetPinButton.connect("clicked", this._onResetPin.bind(this)); this._tokenBox.add(this._tokenTreeView); this._window.set_default_size(950, 500); this._window.connect('destroy',this._onWindowDestroy.bind(this)); this._window.show_all(); } _createCoolKeyMgr(dbusName) { this.mgr = new Coolkey.Mgr({ dbusUniqueName: dbusName, config_dir: this._configPath, config_file_name: ESC_CONFIG_FILE_NAME }); this._phoneHomeMgr = new PhoneHome.phoneHome(this); if(!this.mgr || !this._phoneHomeMgr) { this._onExitClicked(); } } _alert(msg) { this._messageDialog = new Gtk.MessageDialog ({ transient_for: this._window, modal: true, buttons: Gtk.ButtonsType.OK, message_type: Gtk.MessageType.WARNING, text: msg }); this._messageDialog.connect ('response', Lang.bind(this, this._response_cb)); this._messageDialog.show(); } _promptPin() { let status = this._selectedTokenInfo.status; if(status == 4 /* enrolled */) { let coolkey_token = this._selectedTokenInfo; this._tokenInfoBuffer.text += this.mgr.get_certs_info(coolkey_token); } } _promptPinDone(tempFileName) { } _response_cb() { if(this._messageDialog) { this._messageDialog.destroy(); } this._messageDialog = null; } _getConfigPath() { return this._configPath; } _getStatusMessages() { this._statusMessages = new Array( this._getPropertyValue("errorNone"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorProblemCommToken"), this._getPropertyValue("errorProblemResetTokenPin"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorLifeCyclePDU"), this._getPropertyValue("errorTokenEnrollment"), this._getPropertyValue("errorProblemCommToken"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorTermSecureConn"), this._getPropertyValue("errorAuthFailure"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorTokenDisabled"), this._getPropertyValue("errorSecureChannel"), this._getPropertyValue("errorServerMisconfig"), this._getPropertyValue("errorTokenUpgrade"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorExternalAuth"), this._getPropertyValue("errorInvalidTokenType"), this._getPropertyValue("errorInvalidTokenTypeParams"), this._getPropertyValue("errorCannotPublish"), this._getPropertyValue("errorCommTokenDB"), this._getPropertyValue("errorTokenSuspended"), this._getPropertyValue("errorPinResetable"), this._getPropertyValue("errorConnLost"), this._getPropertyValue("errorEntryTokenDB"), this._getPropertyValue("errorNoTokenState"), this._getPropertyValue("errorInvalidLostTokenReason"), this._getPropertyValue("errorTokenUnusable"), this._getPropertyValue("errorNoInactiveToken"), this._getPropertyValue("errorProcessMultiTokens"), this._getPropertyValue("errorTokenTerminated"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorKeyRecoveryFailed"), this._getPropertyValue("errorInternalServer"), this._getPropertyValue("errorKeyArchival"), this._getPropertyValue("errorConnTKS"), this._getPropertyValue("errorFailUpdateTokenDB"), this._getPropertyValue("errorCertRevocation"), this._getPropertyValue("errorNotOwnToken"), this._getPropertyValue("errorESCMisconfigured"), this._getPropertyValue("errorESCNoCommCardReader"), this._getPropertyValue("errorESCNoTokenSession"), this._getPropertyValue("errorESCNoTalkTPS"), this._getPropertyValue("errorESCNoTalkTokenReader") ); } _getErrorMessage(status_code) { let result = this._getPropertyValue("errorInternalServer"); if(status_code < 0 && status_code >= this._statusMessages.length) { return result; } return this._statusMessages[status_code]; } } let app = new ESC(); app.application.run (ARGV);