diff --git a/.gitignore b/.gitignore
index d08e4307a..112130a25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,8 @@ DeviceTest/bin/
DeviceTest/obj/
Dirichlet.Numerics/bin
Dirichlet.Numerics/obj
+GemCard/bin
+GemCard/obj
GraphLib/bin
GraphLib/obj
MonitoringDB/bin
diff --git a/Config/Config.csproj b/Config/Config.csproj
index 8f65fd754..cca307760 100644
--- a/Config/Config.csproj
+++ b/Config/Config.csproj
@@ -119,6 +119,7 @@
Strings.resx
+
diff --git a/Config/Properties/AssemblyInfo.cs b/Config/Properties/AssemblyInfo.cs
index 651363788..ab6e4f278 100644
--- a/Config/Properties/AssemblyInfo.cs
+++ b/Config/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.18.891.0")]
-[assembly: AssemblyFileVersion("2.18.891.0")]
+[assembly: AssemblyVersion("2.18.893.0")]
+[assembly: AssemblyFileVersion("2.18.893.0")]
diff --git a/Config/Utils.cs b/Config/Utils.cs
new file mode 100644
index 000000000..f93c6f759
--- /dev/null
+++ b/Config/Utils.cs
@@ -0,0 +1,69 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Config
+{
+ public static class Utils
+ {
+ public static string SignificantDigitsToFmt(double value, int sigDigits)
+ {
+ if (sigDigits == 5)
+ {
+ if (value >= 9999.5 || value < -9999.5) return "F0";
+ else if (value >= 999.95 || value < -999.95) return "F1";
+ else if (value >= 99.995 || value < -99.995) return "F2";
+ else if (value >= 9.9995 || value < -9.9995) return "F3";
+ else if (value >= 0.99995 || value < -0.99995) return "F4";
+ else if (value >= 0.099995 || value < -0.099995) return "F5";
+ else if (value >= 0.0099995 || value < -0.0099995) return "F6";
+ else if (value >= 0.00099995 || value < -0.00099995) return "F7";
+ else if (value >= 0.000099995 || value < -0.000099995) return "F8";
+ else return "F9";
+ }
+ else if (sigDigits == 4)
+ {
+ if (value >= 999.5 || value < -999.5) return "F0";
+ else if (value >= 99.95 || value < -99.95) return "F1";
+ else if (value >= 9.995 || value < -9.995) return "F2";
+ else if (value >= 0.9995 || value < -0.9995) return "F3";
+ else if (value >= 0.09995 || value < -0.09995) return "F4";
+ else if (value >= 0.009995 || value < -0.009995) return "F5";
+ else if (value >= 0.0009995 || value < -0.0009995) return "F6";
+ else if (value >= 0.00009995 || value < -0.00009995) return "F7";
+ else return "F8";
+ }
+ else if (sigDigits == 3)
+ {
+ if (value >= 99.5 || value < -99.5) return "F0";
+ else if (value >= 9.95 || value < -9.95) return "F1";
+ else if (value >= 0.995 || value < -0.995) return "F2";
+ else if (value >= 0.0995 || value < -0.0995) return "F3";
+ else if (value >= 0.00995 || value < -0.00995) return "F4";
+ else if (value >= 0.000995 || value < -0.000995) return "F5";
+ else if (value >= 0.0000995 || value < -0.0000995) return "F6";
+ else return "F7";
+ }
+ else if (sigDigits == 2)
+ {
+ if (value >= 9.5 || value < -9.5) return "F0";
+ else if (value >= 0.95 || value < -0.95) return "F1";
+ else if (value >= 0.095 || value < -0.095) return "F2";
+ else if (value >= 0.0095 || value < -0.0095) return "F3";
+ else if (value >= 0.00095 || value < -0.00095) return "F4";
+ else if (value >= 0.000095 || value < -0.000095) return "F5";
+ else return "F6";
+ }
+ else /// if (sigDigits == 1)
+ {
+ if (value >= 0.95 || value < -0.95) return "F0";
+ else if (value >= 0.095 || value < -0.095) return "F1";
+ else if (value >= 0.0095 || value < -0.0095) return "F2";
+ else if (value >= 0.00095 || value < -0.00095) return "F3";
+ else if (value >= 0.000095 || value < -0.000095) return "F4";
+ else return "F5";
+ }
+ }
+ }
+}
diff --git a/GemCard/APDUCommand.cs b/GemCard/APDUCommand.cs
new file mode 100644
index 000000000..6b32fb369
--- /dev/null
+++ b/GemCard/APDUCommand.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Text;
+
+namespace GemCard
+{
+ ///
+ /// This class represents a command APDU
+ ///
+ public class APDUCommand
+ {
+ ///
+ /// Minimun size of an APDU command in bytes
+ ///
+ public const int APDU_MIN_LENGTH = 4;
+
+ public byte Class; /// Class byte
+ public byte Ins; /// Instruction byte
+ public byte P1; /// Parameter P1 byte
+ public byte P2; /// Parameter P2 byte
+ public byte[] Data; /// Data to send to the card if any, null if no data to send
+ public byte Le; /// Number of data expected, 0 if none
+
+ ///
+ /// Constructor
+ ///
+ /// Class byte
+ /// Instruction byte
+ /// Parameter P1 byte
+ /// Parameter P2 byte
+ /// Data to send to the card if any, null if no data to send
+ /// Number of data expected, 0 if none
+ public APDUCommand(byte bCla, byte bIns, byte bP1, byte bP2, byte[] baData, byte bLe)
+ {
+ this.Class = bCla;
+ this.Ins = bIns;
+ this.P1 = bP1;
+ this.P2 = bP2;
+ this.Data = baData;
+ this.Le = bLe;
+ }
+
+
+ ///
+ /// Update the current APDU with selected parameters
+ ///
+ /// APDU parameters
+ public void Update(APDUParam apduParam)
+ {
+ if (apduParam.UseData) Data = apduParam.Data;
+ if (apduParam.UseLe) Le = apduParam.Le;
+ if (apduParam.UseP1) P1 = apduParam.P1;
+ if (apduParam.UseP2) P2 = apduParam.P2;
+ if (apduParam.UseChannel) Class += apduParam.Channel;
+ }
+
+ ///
+ /// Overrides the ToString method to format to a string the APDUCommand object
+ ///
+ ///
+ public override string ToString()
+ {
+ string strData = null;
+ byte bLc = 0;
+ byte bP3 = Le;
+
+ if (Data != null)
+ {
+ StringBuilder sData = new StringBuilder(Data.Length * 2);
+ for (int nI = 0; nI < Data.Length; nI++)
+ {
+ sData.AppendFormat("{0:X02}", Data[nI]);
+ }
+
+ strData = "Data=" + sData.ToString();
+ bLc = (byte) Data.Length;
+ bP3 = bLc;
+ }
+
+ //string strApdu = string.Format("Class={0:X02} Ins={1:X02} P1={2:X02} P2={3:X02} Le={4:X02} Lc={5:X02} ",
+ //m_bCla, m_bIns, m_bP1, m_bP2, m_bLe, bLc);
+ StringBuilder strApdu = new StringBuilder();
+
+ strApdu.AppendFormat("Class={0:X02} Ins={1:X02} P1={2:X02} P2={3:X02} P3={4:X02} ",
+ Class, Ins, P1, P2, bP3);
+
+ if (Data != null)
+ {
+ strApdu.Append(strData);
+ }
+
+ return strApdu.ToString();
+ }
+ }
+}
diff --git a/GemCard/APDUParam.cs b/GemCard/APDUParam.cs
new file mode 100644
index 000000000..a9be136f7
--- /dev/null
+++ b/GemCard/APDUParam.cs
@@ -0,0 +1,147 @@
+using System;
+
+namespace GemCard
+{
+ ///
+ /// This class is used to update a set of parameters of an APDUCommand object
+ ///
+ public class APDUParam
+ {
+ byte bClass;
+ byte bChannel;
+ byte bP2;
+ byte bP1;
+
+ byte[] baData;
+ short nLe;
+
+ bool fUseP1;
+ bool fUseP2;
+ bool fChannel;
+ bool fData;
+ bool fClass;
+ bool fLe;
+
+ ///
+ /// Resets the current instance, all flags are set to false
+ ///
+ public void Reset()
+ {
+ bClass = 0;
+ bChannel = 0;
+ bP2 = 0;
+ bP1 = 0;
+
+ baData = null;
+ nLe = -1;
+
+ fUseP1 = false;
+ fUseP2 = false;
+ fChannel = false;
+ fData = false;
+ fClass = false;
+ fLe = false;
+ }
+
+ #region Constructors
+
+ public APDUParam()
+ {
+ Reset();
+ }
+
+ ///
+ /// Copy constructor (used for cloning)
+ ///
+ ///
+ public APDUParam(APDUParam param)
+ {
+ // Copy field
+ if (param.baData != null)
+ {
+ param.baData.CopyTo(baData, 0);
+ }
+
+ this.bClass = param.bClass;
+ this.bChannel = param.bChannel;
+ this.bP1 = param.bP1;
+ this.bP2 = param.bP2;
+ this.nLe = param.nLe;
+
+ // Copy flags field
+ this.fChannel = param.fChannel;
+ this.fClass = param.fClass;
+ this.fData = param.fData;
+ this.fLe = param.fLe;
+ this.fUseP1 = param.fUseP1;
+ this.fUseP2 = param.fUseP2;
+ }
+
+ public APDUParam(byte bClass, byte bP1, byte bP2, byte[] baData, short nLe)
+ {
+ this.Class = bClass;
+ this.P1 = bP1;
+ this.P2 = bP2;
+ this.Data = baData;
+ this.Le = (byte)nLe;
+ }
+
+ #endregion
+
+ ///
+ /// Clones the current APDUParam instance
+ ///
+ ///
+ public APDUParam Clone()
+ {
+ return new APDUParam(this);
+ }
+
+ #region Properties
+
+ public bool UseClass { get { return fClass; } }
+ public bool UseChannel { get { return fChannel; } }
+ public bool UseLe { get { return fLe; } }
+ public bool UseData { get { return fData; } }
+ public bool UseP1 { get { return fUseP1; } }
+ public bool UseP2 { get { return fUseP2; } }
+
+ public byte P1
+ {
+ get { return bP1; }
+ set { bP1 = value; fUseP1 = true; }
+ }
+
+ public byte P2
+ {
+ get { return bP2; }
+ set { bP2 = value; fUseP2 = true; }
+ }
+
+ public byte[] Data
+ {
+ get { return baData; }
+ set { baData = value; fData = true; }
+ }
+
+ public byte Le
+ {
+ get { return (byte)nLe; }
+ set { nLe = value; fLe = true; }
+ }
+
+ public byte Channel
+ {
+ get { return bChannel; }
+ set { bChannel = value; fChannel = true; }
+ }
+
+ public byte Class
+ {
+ get { return bClass; }
+ set { bClass = value; fClass = true; }
+ }
+
+ #endregion
+ }
+}
diff --git a/GemCard/APDUResponse.cs b/GemCard/APDUResponse.cs
new file mode 100644
index 000000000..f54171a41
--- /dev/null
+++ b/GemCard/APDUResponse.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Text;
+
+namespace GemCard
+{
+ ///
+ /// This class represents the APDU response sent by the card
+ ///
+ public class APDUResponse
+ {
+ ///
+ /// Status bytes length
+ ///
+ public const int SW_LENGTH = 2;
+
+ ///
+ /// Response data. Contains the data sent by the card minus the 2 status bytes (SW1, SW2)
+ /// null if no data were sent by the card
+ ///
+ public byte[] Data;
+ public byte SW1;
+ public byte SW2;
+
+ ///
+ /// Status get property
+ ///
+ public ushort Status
+ {
+ get { return (ushort)(((short)SW1 << 8) + (short)SW2); }
+ }
+
+ ///
+ /// Constructor from the byte data sent back by the card
+ ///
+ /// Buffer of data from the card
+ public APDUResponse(byte[] baData)
+ {
+ if (baData.Length > SW_LENGTH)
+ {
+ Data = new byte[baData.Length - SW_LENGTH];
+
+ for (int nI = 0; nI < baData.Length - SW_LENGTH; nI++)
+ {
+ this.Data[nI] = baData[nI];
+ }
+ }
+
+ this.SW1 = baData[baData.Length - 2];
+ this.SW2 = baData[baData.Length - 1];
+ }
+
+ ///
+ /// Overrides the ToString method to format to a string the APDUResponse object
+ ///
+ ///
+ public override string ToString()
+ {
+ if (Data != null)
+ {
+ StringBuilder sData = new StringBuilder(Data.Length * 2);
+ for (int nI = 0; nI < Data.Length; nI++)
+ {
+ sData.AppendFormat("{0:X02}", Data[nI]);
+ }
+
+ return sData.ToString();
+ }
+ else
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/GemCard/AssemblyInfo.cs b/GemCard/AssemblyInfo.cs
new file mode 100644
index 000000000..9f89a3282
--- /dev/null
+++ b/GemCard/AssemblyInfo.cs
@@ -0,0 +1,58 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("1.0.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("")]
+[assembly: AssemblyKeyName("")]
diff --git a/GemCard/CardBase.cs b/GemCard/CardBase.cs
new file mode 100644
index 000000000..3c4ae3102
--- /dev/null
+++ b/GemCard/CardBase.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace GemCard
+{
+ ///
+ /// Values for AttrId of SCardGetAttrib
+ ///
+ public class SCARD_ATTR_VALUE
+ {
+ private const uint
+ SCARD_CLASS_COMMUNICATIONS = 2,
+ SCARD_CLASS_PROTOCOL = 3,
+ SCARD_CLASS_MECHANICAL = 6,
+ SCARD_CLASS_VENDOR_DEFINED = 7,
+ SCARD_CLASS_IFD_PROTOCOL = 8,
+ SCARD_CLASS_ICC_STATE = 9,
+ SCARD_CLASS_SYSTEM = 0x7fff;
+
+ private static UInt32 SCardAttrValue(UInt32 attrClass, UInt32 val)
+ {
+ return (attrClass << 16) | val;
+ }
+
+ public static UInt32 CHANNEL_ID { get { return SCardAttrValue(SCARD_CLASS_COMMUNICATIONS, 0x0110); } }
+
+ public static UInt32 CHARACTERISTICS { get { return SCardAttrValue(SCARD_CLASS_MECHANICAL, 0x0150); } }
+
+ public static UInt32 CURRENT_PROTOCOL_TYPE { get { return SCardAttrValue(SCARD_CLASS_IFD_PROTOCOL, 0x0201); } }
+
+ public static UInt32 DEVICE_UNIT { get { return SCardAttrValue(SCARD_CLASS_SYSTEM, 0x0001); } }
+ public static UInt32 DEVICE_FRIENDLY_NAME { get { return SCardAttrValue(SCARD_CLASS_SYSTEM, 0x0003); } }
+ public UInt32 DEVICE_SYSTEM_NAME { get { return SCardAttrValue(SCARD_CLASS_SYSTEM, 0x0004); } }
+
+ public static UInt32 ICC_PRESENCE { get { return SCardAttrValue(SCARD_CLASS_ICC_STATE, 0x0300); } }
+ public static UInt32 ICC_INTERFACE_STATUS { get { return SCardAttrValue(SCARD_CLASS_ICC_STATE, 0x0301); } }
+ public static UInt32 ATR_STRING { get { return SCardAttrValue(SCARD_CLASS_ICC_STATE, 0x0303); } }
+ public static UInt32 ICC_TYPE_PER_ATR { get { return SCardAttrValue(SCARD_CLASS_ICC_STATE, 0x0304); } }
+
+ public static UInt32 PROTOCOL_TYPES { get { return SCardAttrValue(SCARD_CLASS_PROTOCOL, 0x0120); } }
+
+ public static UInt32 VENDOR_NAME { get { return SCardAttrValue(SCARD_CLASS_VENDOR_DEFINED, 0x0100); } }
+ public static UInt32 VENDOR_IFD_TYPE { get { return SCardAttrValue(SCARD_CLASS_VENDOR_DEFINED, 0x0101); } }
+ public static UInt32 VENDOR_IFD_VERSION { get { return SCardAttrValue(SCARD_CLASS_VENDOR_DEFINED, 0x0102); } }
+ public static UInt32 VENDOR_IFD_SERIAL_NO { get { return SCardAttrValue(SCARD_CLASS_VENDOR_DEFINED, 0x0103); } }
+ }
+
+ ///
+ /// Abstract class that adds a basic event management to the ICard interface.
+ ///
+ abstract public class CardBase : ICard
+ {
+ protected const uint INFINITE = 0xFFFFFFFF;
+ protected const uint WAIT_TIME = 250;
+
+ protected bool m_bRunCardDetection = true;
+ protected Thread m_thread = null;
+
+ ///
+ /// Event handler for the card insertion
+ ///
+ public event EventHandler OnCardInserted = null;
+
+ ///
+ /// Event handler for the card removal
+ ///
+ public event EventHandler OnCardRemoved = null;
+
+ ~CardBase()
+ {
+ // Stop any eventual card detection thread
+ StopCardEvents();
+ }
+
+ #region Abstract method that implement the ICard interface
+ abstract public string[] ListReaders();
+ abstract public void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols);
+ abstract public void Disconnect(DISCONNECT Disposition);
+ abstract public APDUResponse Transmit(APDUCommand ApduCmd);
+ abstract public void BeginTransaction();
+ abstract public void EndTransaction(DISCONNECT Disposition);
+ abstract public byte[] GetAttribute(UInt32 AttribId);
+ #endregion
+
+ ///
+ /// This method should start a thread that checks for card insertion or removal
+ ///
+ ///
+ public void StartCardEvents(string Reader)
+ {
+ if (m_thread == null)
+ {
+ m_bRunCardDetection = true;
+
+ m_thread = new Thread(new ParameterizedThreadStart(RunCardDetection));
+ m_thread.Start(Reader);
+ }
+ }
+
+ ///
+ /// Stops the card events thread
+ ///
+ public void StopCardEvents()
+ {
+ if (m_thread != null)
+ {
+ int
+ nTimeOut = 10,
+ nCount = 0;
+ bool m_bStop = false;
+ m_bRunCardDetection = false;
+
+ do
+ {
+ if (nCount > nTimeOut)
+ {
+ m_thread.Abort();
+ break;
+ }
+
+ if (m_thread.ThreadState == ThreadState.Aborted)
+ m_bStop = true;
+
+ if (m_thread.ThreadState == ThreadState.Stopped)
+ m_bStop = true;
+
+ Thread.Sleep(200);
+ ++nCount; // Manage time out
+ }
+ while (!m_bStop);
+
+ m_thread = null;
+ }
+ }
+
+ ///
+ /// This function must implement a card detection mechanism.
+ ///
+ /// When card insertion is detected, it must call the method CardInserted()
+ /// When card removal is detected, it must call the method CardRemoved()
+ ///
+ ///
+ /// Name of the reader to scan for card event
+ abstract protected void RunCardDetection(object Reader);
+
+ #region Event methods
+
+ protected void CardInserted(object sender, CardInsertedEventArgs args)
+ {
+ if (OnCardInserted != null) OnCardInserted(sender, args);
+ }
+
+ protected void CardRemoved(object sender, CardRemovedEventArgs args)
+ {
+ if (OnCardRemoved != null) OnCardRemoved(sender, args);
+ }
+
+ #endregion
+ }
+}
diff --git a/GemCard/CardCOM.cs b/GemCard/CardCOM.cs
new file mode 100644
index 000000000..26269999a
--- /dev/null
+++ b/GemCard/CardCOM.cs
@@ -0,0 +1,173 @@
+using System;
+using System.Runtime.InteropServices;
+using SCARDSSPLib;
+using GemCardExLib;
+
+namespace GemCard
+{
+ ///
+ /// Implements the ICard interface using the SCard COM objects from Microsoft and SCardDatabaseEx object
+ /// for the ListReaders function.
+ ///
+ public class CardCOM : CardBase
+ {
+ private ISCard m_itfCard = null;
+
+ ///
+ /// Default constructor
+ ///
+ public CardCOM()
+ {
+ // Create the SCard object
+ m_itfCard = new CSCardClass();
+ }
+
+ #region ICard Members
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardListReaders(SCARDCONTEXT hContext,
+ /// LPCTSTR mszGroups,
+ /// LPTSTR mszReaders,
+ /// LPDWORD pcchReaders
+ /// );
+ ///
+ /// A string array of the readers
+ public override string[] ListReaders()
+ {
+ ISCardDatabaseEx itfCardBase = new SCardDatabaseEx();
+
+ return (string[]) itfCardBase.ListReaders();
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardConnect(
+ /// IN SCARDCONTEXT hContext,
+ /// IN LPCTSTR szReader,
+ /// IN DWORD dwShareMode,
+ /// IN DWORD dwPreferredProtocols,
+ /// OUT LPSCARDHANDLE phCard,
+ /// OUT LPDWORD pdwActiveProtocol
+ /// );
+ ///
+ ///
+ ///
+ ///
+ public override void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols)
+ {
+ // Calls AttachReader to connect to the card
+ m_itfCard.AttachByReader(Reader, (SCARD_SHARE_MODES) ShareMode, (SCARD_PROTOCOLS) PreferredProtocols);
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardDisconnect(
+ /// IN SCARDHANDLE hCard,
+ /// IN DWORD dwDisposition
+ /// );
+ ///
+ ///
+ public override void Disconnect(DISCONNECT Disposition)
+ {
+ // Off the connection with the card
+ m_itfCard.Detach((SCARD_DISPOSITIONS) Disposition);
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardTransmit(
+ /// SCARDHANDLE hCard,
+ /// LPCSCARD_I0_REQUEST pioSendPci,
+ /// LPCBYTE pbSendBuffer,
+ /// DWORD cbSendLength,
+ /// LPSCARD_IO_REQUEST pioRecvPci,
+ /// LPBYTE pbRecvBuffer,
+ /// LPDWORD pcbRecvLength
+ /// );
+ ///
+ /// APDUCommand object with the APDU to send to the card
+ /// An APDUResponse object with the response from the card
+ public override APDUResponse Transmit(APDUCommand ApduCmd)
+ {
+ CSCardCmd itfCmd = new CSCardCmdClass();
+ CByteBuffer itfData = new CByteBufferClass();
+ int nLe = ApduCmd.Le;
+
+ if (ApduCmd.Data == null)
+ {
+ itfData.SetSize(0);
+ }
+ else
+ {
+ int nWrite = 0;
+
+ itfData.SetSize(ApduCmd.Data.Length);
+ itfData.Write(ref ApduCmd.Data[0], ApduCmd.Data.Length, ref nWrite);
+ }
+
+ // Build the APDU command
+ itfCmd.BuildCmd(ApduCmd.Class, ApduCmd.Ins, ApduCmd.P1, ApduCmd.P2, itfData, ref nLe);
+
+ // Send the command
+ m_itfCard.Transaction(ref itfCmd);
+
+ // Analyse the response
+ int nRead = 0;
+ byte[] pbResp = new byte[itfCmd.ApduReplyLength];
+
+ itfCmd.ApduReply.Read(ref pbResp[0], itfCmd.ApduReplyLength, ref nRead);
+
+ return new APDUResponse(pbResp);
+ }
+
+ ///
+ /// Wraps the PSCS function
+ /// LONG SCardBeginTransaction(
+ /// SCARDHANDLE hCard
+ // );
+ /// This function is not supported in the COM implementation
+ ///
+ public override void BeginTransaction()
+ {
+ throw new NotImplementedException("BeginTransaction is not supported in the COM implementation");
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardEndTransaction(
+ /// SCARDHANDLE hCard,
+ /// DWORD dwDisposition
+ /// );
+ /// This function is not supported in the COM implementation
+ ///
+ /// A value from DISCONNECT enum
+ public override void EndTransaction(DISCONNECT Disposition)
+ {
+ throw new NotImplementedException("EndTransaction is not supported in the COM implementation");
+ }
+
+ ///
+ /// Gets the attributes of the card
+ ///
+ /// Identifier for the Attribute to get
+ /// Attribute content
+ public override byte[] GetAttribute(UInt32 AttribId)
+ {
+ throw new NotImplementedException();
+ }
+ #endregion
+
+ ///
+ /// This function must implement a card detection mechanism.
+ ///
+ /// When card insertion is detected, it must call the method CardInserted()
+ /// When card removal is detected, it must call the method CardRemoved()
+ ///
+ ///
+ protected override void RunCardDetection(object Reader)
+ {
+ throw new Exception("The method or operation is not implemented.");
+ }
+ }
+}
diff --git a/GemCard/CardInsertedEventArgs.cs b/GemCard/CardInsertedEventArgs.cs
new file mode 100644
index 000000000..affae3196
--- /dev/null
+++ b/GemCard/CardInsertedEventArgs.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace GemCard
+{
+ public class CardInsertedEventArgs : EventArgs
+ {
+ public CardInsertedEventArgs() { }
+ }
+}
diff --git a/GemCard/CardNative.cs b/GemCard/CardNative.cs
new file mode 100644
index 000000000..fc7d02122
--- /dev/null
+++ b/GemCard/CardNative.cs
@@ -0,0 +1,620 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+
+namespace GemCard
+{
+ ///
+ /// CARD_STATE enumeration, used by the PC/SC function SCardGetStatusChanged
+ ///
+ enum CARD_STATE
+ {
+ UNAWARE = 0x00000000,
+ IGNORE = 0x00000001,
+ CHANGED = 0x00000002,
+ UNKNOWN = 0x00000004,
+ UNAVAILABLE = 0x00000008,
+ EMPTY = 0x00000010,
+ PRESENT = 0x00000020,
+ ATRMATCH = 0x00000040,
+ EXCLUSIVE = 0x00000080,
+ INUSE = 0x00000100,
+ MUTE = 0x00000200,
+ UNPOWERED = 0x00000400
+ }
+
+ ///
+ /// Wraps the SCARD_IO_STRUCTURE
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct SCard_IO_Request
+ {
+ public UInt32 m_dwProtocol;
+ public UInt32 m_cbPciLength;
+ }
+
+
+ ///
+ /// Wraps theSCARD_READERSTATE structure of PC/SC
+ ///
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct SCard_ReaderState
+ {
+ public string m_szReader;
+ public IntPtr m_pvUserData;
+ public UInt32 m_dwCurrentState;
+ public UInt32 m_dwEventState;
+ public UInt32 m_cbAtr;
+ [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)]
+ public byte[] m_rgbAtr;
+ }
+
+ ///
+ /// Implementation of ICard using native (P/Invoke) interoperability for PC/SC
+ ///
+ public class CardNative : CardBase
+ {
+ private UInt32 m_hContext = 0;
+ private UInt32 m_hCard = 0;
+ private UInt32 m_nProtocol = (uint) PROTOCOL.T0;
+ private int m_nLastError = 0;
+
+ #region PCSC_FUNCTIONS
+ ///
+ /// Native SCardGetStatusChanged from winscard.dll
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError = true)]
+ internal static extern int SCardGetStatusChange(UInt32 hContext,
+ UInt32 dwTimeout,
+ [In,Out] SCard_ReaderState[] rgReaderStates,
+ UInt32 cReaders);
+
+ ///
+ /// Native SCardListReaders function from winscard.dll
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError=true)]
+ internal static extern int SCardListReaders(UInt32 hContext,
+ [MarshalAs(UnmanagedType.LPTStr)] string mszGroups,
+ IntPtr mszReaders,
+ out UInt32 pcchReaders);
+
+ ///
+ /// Native SCardEstablishContext function from winscard.dll
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError=true)]
+ internal static extern int SCardEstablishContext(UInt32 dwScope,
+ IntPtr pvReserved1,
+ IntPtr pvReserved2,
+ IntPtr phContext);
+
+ ///
+ /// Native SCardReleaseContext function from winscard.dll
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError=true)]
+ internal static extern int SCardReleaseContext(UInt32 hContext);
+
+ ///
+ /// Native SCardConnect function from winscard.dll
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError=true, CharSet=CharSet.Auto)]
+ internal static extern int SCardConnect(UInt32 hContext,
+ [MarshalAs(UnmanagedType.LPTStr)] string szReader,
+ UInt32 dwShareMode,
+ UInt32 dwPreferredProtocols,
+ IntPtr phCard,
+ IntPtr pdwActiveProtocol);
+
+ ///
+ /// Native SCardDisconnect function from winscard.dll
+ ///
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError=true)]
+ internal static extern int SCardDisconnect(UInt32 hCard,
+ UInt32 dwDisposition);
+
+ ///
+ /// Native SCardTransmit function from winscard.dll
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError=true)]
+ internal static extern int SCardTransmit(UInt32 hCard,
+ [In] ref SCard_IO_Request pioSendPci,
+ byte[] pbSendBuffer,
+ UInt32 cbSendLength,
+ IntPtr pioRecvPci,
+ [Out] byte[] pbRecvBuffer,
+ out UInt32 pcbRecvLength
+ );
+
+ ///
+ /// Native SCardBeginTransaction function of winscard.dll
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError = true)]
+ internal static extern int SCardBeginTransaction(UInt32 hContext);
+
+ ///
+ /// Native SCardEndTransaction function of winscard.dll
+ ///
+ ///
+ ///
+ [DllImport("winscard.dll", SetLastError = true)]
+ internal static extern int SCardEndTransaction(UInt32 hContext, UInt32 dwDisposition);
+
+ [DllImport("winscard.dll", SetLastError = true)]
+ internal static extern int SCardGetAttrib(UInt32 hCard,
+ UInt32 dwAttribId,
+ [Out] byte[] pbAttr,
+ out UInt32 pcbAttrLen);
+
+ #endregion
+
+ ///
+ /// Default constructor
+ ///
+ public CardNative()
+ {
+ }
+
+ ///
+ /// Object destruction
+ ///
+ ~CardNative()
+ {
+ Disconnect(DISCONNECT.Unpower);
+
+ ReleaseContext();
+ }
+
+ #region ICard Members
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardListReaders(SCARDCONTEXT hContext,
+ /// LPCTSTR mszGroups,
+ /// LPTSTR mszReaders,
+ /// LPDWORD pcchReaders
+ /// );
+ ///
+ /// A string array of the readers
+ public override string[] ListReaders()
+ {
+ EstablishContext(SCOPE.User);
+
+ string[] sListReaders = null;
+ UInt32 pchReaders = 0;
+ IntPtr szListReaders = IntPtr.Zero;
+
+ m_nLastError = SCardListReaders(m_hContext, null, szListReaders, out pchReaders);
+ if (m_nLastError == 0)
+ {
+ szListReaders = Marshal.AllocHGlobal((int) pchReaders);
+ m_nLastError = SCardListReaders(m_hContext, null, szListReaders, out pchReaders);
+ if (m_nLastError == 0)
+ {
+ char[] caReadersData = new char[pchReaders];
+ int nbReaders = 0;
+ for (int nI = 0; nI < pchReaders; nI++)
+ {
+ caReadersData[nI] = (char) Marshal.ReadByte(szListReaders, nI);
+
+ if (caReadersData[nI] == 0)
+ nbReaders++;
+ }
+
+ // Remove last 0
+ --nbReaders;
+
+ if (nbReaders != 0)
+ {
+ sListReaders = new string[nbReaders];
+ char[] caReader = new char[pchReaders];
+ int nIdx = 0;
+ int nIdy = 0;
+ int nIdz = 0;
+ // Get the nJ string from the multi-string
+
+ while(nIdx < pchReaders - 1)
+ {
+ caReader[nIdy] = caReadersData[nIdx];
+ if (caReader[nIdy] == 0)
+ {
+ sListReaders[nIdz] = new string(caReader, 0, nIdy);
+ ++nIdz;
+ nIdy = 0;
+ caReader = new char[pchReaders];
+ }
+ else
+ ++nIdy;
+
+ ++nIdx;
+ }
+ }
+
+ }
+
+ Marshal.FreeHGlobal(szListReaders);
+ }
+
+ ReleaseContext();
+
+ return sListReaders;
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardEstablishContext(
+ /// IN DWORD dwScope,
+ /// IN LPCVOID pvReserved1,
+ /// IN LPCVOID pvReserved2,
+ /// OUT LPSCARDCONTEXT phContext
+ /// );
+ ///
+ ///
+ public void EstablishContext(SCOPE Scope)
+ {
+ IntPtr hContext = Marshal.AllocHGlobal(Marshal.SizeOf(m_hContext));
+
+ m_nLastError = SCardEstablishContext((uint) Scope, IntPtr.Zero, IntPtr.Zero, hContext);
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardEstablishContext error: " + m_nLastError;
+
+ Marshal.FreeHGlobal(hContext);
+ throw new Exception(msg);
+ }
+
+ m_hContext = (uint) Marshal.ReadInt32(hContext);
+
+ Marshal.FreeHGlobal(hContext);
+ }
+
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardReleaseContext(
+ /// IN SCARDCONTEXT hContext
+ /// );
+ ///
+ public void ReleaseContext()
+ {
+ if (m_hContext != 0)
+ {
+ m_nLastError = SCardReleaseContext(m_hContext);
+
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardReleaseContext error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+
+ m_hContext = 0;
+ }
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardConnect(
+ /// IN SCARDCONTEXT hContext,
+ /// IN LPCTSTR szReader,
+ /// IN DWORD dwShareMode,
+ /// IN DWORD dwPreferredProtocols,
+ /// OUT LPSCARDHANDLE phCard,
+ /// OUT LPDWORD pdwActiveProtocol
+ /// );
+ ///
+ ///
+ ///
+ ///
+ public override void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols)
+ {
+ EstablishContext(SCOPE.User);
+
+ IntPtr hCard = Marshal.AllocHGlobal(Marshal.SizeOf(m_hCard));
+ IntPtr pProtocol = Marshal.AllocHGlobal(Marshal.SizeOf(m_nProtocol));
+
+ m_nLastError = SCardConnect(m_hContext,
+ Reader,
+ (uint) ShareMode,
+ (uint) PreferredProtocols,
+ hCard,
+ pProtocol);
+
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardConnect error: " + m_nLastError;
+
+ Marshal.FreeHGlobal(hCard);
+ Marshal.FreeHGlobal(pProtocol);
+ throw new Exception(msg);
+ }
+
+ m_hCard = (uint) Marshal.ReadInt32(hCard);
+ m_nProtocol = (uint) Marshal.ReadInt32(pProtocol);
+
+ Marshal.FreeHGlobal(hCard);
+ Marshal.FreeHGlobal(pProtocol);
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardDisconnect(
+ /// IN SCARDHANDLE hCard,
+ /// IN DWORD dwDisposition
+ /// );
+ ///
+ ///
+ public override void Disconnect(DISCONNECT Disposition)
+ {
+ if (m_hCard != 0)
+ {
+ m_nLastError = SCardDisconnect(m_hCard, (uint) Disposition);
+ m_hCard = 0;
+
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardDisconnect error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+
+ ReleaseContext();
+ }
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardTransmit(
+ /// SCARDHANDLE hCard,
+ /// LPCSCARD_I0_REQUEST pioSendPci,
+ /// LPCBYTE pbSendBuffer,
+ /// DWORD cbSendLength,
+ /// LPSCARD_IO_REQUEST pioRecvPci,
+ /// LPBYTE pbRecvBuffer,
+ /// LPDWORD pcbRecvLength
+ /// );
+ ///
+ /// APDUCommand object with the APDU to send to the card
+ /// An APDUResponse object with the response from the card
+ public override APDUResponse Transmit(APDUCommand ApduCmd)
+ {
+ uint RecvLength = (uint) (ApduCmd.Le + APDUResponse.SW_LENGTH);
+ byte[] ApduBuffer = null;
+ byte[] ApduResponse = new byte[ApduCmd.Le + APDUResponse.SW_LENGTH];
+ SCard_IO_Request ioRequest = new SCard_IO_Request();
+ ioRequest.m_dwProtocol = m_nProtocol;
+ ioRequest.m_cbPciLength = 8;
+
+ // Build the command APDU
+ if (ApduCmd.Data == null)
+ {
+ ApduBuffer = new byte[APDUCommand.APDU_MIN_LENGTH + ((ApduCmd.Le != 0) ? 1 : 0)];
+
+ if (ApduCmd.Le != 0)
+ ApduBuffer[4] = (byte) ApduCmd.Le;
+ }
+ else
+ {
+ ApduBuffer = new byte[APDUCommand.APDU_MIN_LENGTH + 1 + ApduCmd.Data.Length];
+
+ for (int nI = 0; nI < ApduCmd.Data.Length; nI++)
+ ApduBuffer[APDUCommand.APDU_MIN_LENGTH + 1 + nI] = ApduCmd.Data[nI];
+
+ ApduBuffer[APDUCommand.APDU_MIN_LENGTH] = (byte) ApduCmd.Data.Length;
+ }
+
+ ApduBuffer[0] = ApduCmd.Class;
+ ApduBuffer[1] = ApduCmd.Ins;
+ ApduBuffer[2] = ApduCmd.P1;
+ ApduBuffer[3] = ApduCmd.P2;
+
+ m_nLastError = SCardTransmit(m_hCard, ref ioRequest, ApduBuffer, (uint) ApduBuffer.Length, IntPtr.Zero, ApduResponse, out RecvLength);
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardTransmit error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+
+ byte[] ApduData = new byte[RecvLength];
+
+ for (int nI = 0; nI < RecvLength; nI++)
+ ApduData[nI] = ApduResponse[nI];
+
+ return new APDUResponse(ApduData);
+ }
+
+
+ ///
+ /// Wraps the PSCS function
+ /// LONG SCardBeginTransaction(
+ /// SCARDHANDLE hCard
+ // );
+ ///
+ public override void BeginTransaction()
+ {
+ if (m_hCard != 0)
+ {
+ m_nLastError = SCardBeginTransaction(m_hCard);
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardBeginTransaction error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+ }
+ }
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardEndTransaction(
+ /// SCARDHANDLE hCard,
+ /// DWORD dwDisposition
+ /// );
+ ///
+ /// A value from DISCONNECT enum
+ public override void EndTransaction(DISCONNECT Disposition)
+ {
+ if (m_hCard != 0)
+ {
+ m_nLastError = SCardEndTransaction(m_hCard, (UInt32)Disposition);
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardEndTransaction error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+ }
+ }
+
+ ///
+ /// Gets the attributes of the card
+ ///
+ /// Identifier for the Attribute to get
+ /// Attribute content
+ public override byte[] GetAttribute(UInt32 AttribId)
+ {
+ byte[] attr = null;
+ UInt32 attrLen = 0;
+
+ m_nLastError = SCardGetAttrib(m_hCard, AttribId, attr, out attrLen);
+ if (m_nLastError == 0)
+ {
+ if (attrLen != 0)
+ {
+ attr = new byte[attrLen];
+ m_nLastError = SCardGetAttrib(m_hCard, AttribId, attr, out attrLen);
+ if (m_nLastError != 0)
+ {
+ string msg = "SCardGetAttr error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+ }
+ }
+ else
+ {
+ string msg = "SCardGetAttr error: " + m_nLastError;
+ throw new Exception(msg);
+ }
+
+ return attr;
+ }
+ #endregion
+
+ ///
+ /// This function must implement a card detection mechanism.
+ ///
+ /// When card insertion is detected, it must call the method CardInserted()
+ /// When card removal is detected, it must call the method CardRemoved()
+ ///
+ ///
+ protected override void RunCardDetection(object Reader)
+ {
+ bool bFirstLoop = true;
+ UInt32 hContext = 0; // Local context
+ IntPtr phContext;
+
+ phContext = Marshal.AllocHGlobal(Marshal.SizeOf(hContext));
+
+ if (SCardEstablishContext((uint) SCOPE.User, IntPtr.Zero, IntPtr.Zero, phContext) == 0)
+ {
+ hContext = (uint)Marshal.ReadInt32(phContext);
+ Marshal.FreeHGlobal(phContext);
+
+ UInt32 nbReaders = 1;
+ SCard_ReaderState[] readerState = new SCard_ReaderState[nbReaders];
+
+ readerState[0].m_dwCurrentState = (UInt32) CARD_STATE.UNAWARE;
+ readerState[0].m_szReader = (string)Reader;
+
+ UInt32 eventState;
+ UInt32 currentState = readerState[0].m_dwCurrentState;
+
+ // Card detection loop
+ do
+ {
+ if (SCardGetStatusChange(hContext, WAIT_TIME
+ , readerState, nbReaders) == 0)
+ {
+ eventState = readerState[0].m_dwEventState;
+ currentState = readerState[0].m_dwCurrentState;
+
+ // Check state
+ if (((eventState & (uint) CARD_STATE.CHANGED) == (uint) CARD_STATE.CHANGED) && !bFirstLoop)
+ {
+ // State has changed
+ if ((eventState & (uint) CARD_STATE.EMPTY) == (uint) CARD_STATE.EMPTY)
+ {
+ // There is no card, card has been removed -> Fire CardRemoved event
+ CardRemoved(this, new CardRemovedEventArgs());
+ }
+
+ if (((eventState & (uint)CARD_STATE.PRESENT) == (uint)CARD_STATE.PRESENT) &&
+ ((eventState & (uint) CARD_STATE.PRESENT) != (currentState & (uint) CARD_STATE.PRESENT)))
+ {
+ // There is a card in the reader -> Fire CardInserted event
+ CardInserted(this, new CardInsertedEventArgs());
+ }
+
+ if ((eventState & (uint) CARD_STATE.ATRMATCH) == (uint) CARD_STATE.ATRMATCH)
+ {
+ // There is a card in the reader and it matches the ATR we were expecting-> Fire CardInserted event
+ CardInserted(this, new CardInsertedEventArgs());
+ }
+ }
+
+ // The current stateis now the event state
+ readerState[0].m_dwCurrentState = eventState;
+
+ bFirstLoop = false;
+ }
+
+ Thread.Sleep(100);
+
+ if (m_bRunCardDetection == false)
+ break;
+ }
+ while (true); // Exit on request
+ }
+ else
+ {
+ Marshal.FreeHGlobal(phContext);
+ throw new Exception("PC/SC error");
+ }
+
+ SCardReleaseContext(hContext);
+ }
+ }
+}
diff --git a/GemCard/CardRemovedEventArgs.cs b/GemCard/CardRemovedEventArgs.cs
new file mode 100644
index 000000000..df65d35c8
--- /dev/null
+++ b/GemCard/CardRemovedEventArgs.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace GemCard
+{
+ public class CardRemovedEventArgs : EventArgs
+ {
+ public CardRemovedEventArgs() { }
+ }
+}
diff --git a/GemCard/GemCard.csproj b/GemCard/GemCard.csproj
new file mode 100644
index 000000000..bc954c36b
--- /dev/null
+++ b/GemCard/GemCard.csproj
@@ -0,0 +1,194 @@
+
+
+
+ Local
+ 8.0.50727
+ 2.0
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}
+ Debug
+ AnyCPU
+
+
+
+
+ GemCard
+
+
+ JScript
+ Grid
+ IE50
+ false
+ Library
+ GemCard
+ OnBuildSuccess
+
+
+
+
+
+
+ 3.5
+ v4.0
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+
+ bin\Debug\
+ true
+ 285212672
+ false
+
+
+ DEBUG;TRACE
+
+
+ true
+ 4096
+ false
+
+
+ false
+ false
+ false
+ false
+ 4
+
+
+ bin\Release\
+ true
+ 285212672
+ false
+
+
+ TRACE
+
+
+ false
+ 4096
+ false
+
+
+ true
+ false
+ false
+ false
+ 4
+
+
+ true
+ bin\x86\Debug\
+ DEBUG;TRACE
+ 285212672
+ true
+ 4096
+ x86
+ true
+ true
+ false
+
+
+ bin\x86\Release\
+ TRACE
+ 285212672
+ true
+ true
+ 4096
+ x86
+ false
+ false
+
+
+
+ System
+
+
+ System.Data
+
+
+ System.XML
+
+
+ {F1FF634B-BEDF-461C-A409-5B88E05F5117}
+ 1
+ 0
+ 0
+ tlbimp
+ False
+
+
+ {82C38704-19F1-11D3-A11F-00C04F79F800}
+ 1
+ 0
+ 0
+ tlbimp
+
+
+ {00020430-0000-0000-C000-000000000046}
+ 2
+ 0
+ 0
+ primary
+ False
+
+
+
+
+ Code
+
+
+
+ Code
+
+
+
+
+
+ Code
+
+
+
+ Code
+
+
+ Code
+
+
+
+
+ False
+ .NET Framework 3.5 SP1 Client Profile
+ false
+
+
+ False
+ .NET Framework 3.5 SP1
+ true
+
+
+ False
+ Windows Installer 3.1
+ true
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GemCard/GemCard.csproj.user b/GemCard/GemCard.csproj.user
new file mode 100644
index 000000000..fc73aa615
--- /dev/null
+++ b/GemCard/GemCard.csproj.user
@@ -0,0 +1,70 @@
+
+
+
+ 8.0.50215
+ Debug
+ AnyCPU
+
+
+
+
+
+
+ 0
+ ProjectFiles
+ 0
+
+
+
+
+
+
+ en-US
+ false
+
+
+ false
+ false
+ false
+ false
+ false
+
+
+ Project
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+ false
+ false
+ false
+ false
+
+
+ Project
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+ true
+
+
\ No newline at end of file
diff --git a/GemCard/ICard.cs b/GemCard/ICard.cs
new file mode 100644
index 000000000..b6b88e50a
--- /dev/null
+++ b/GemCard/ICard.cs
@@ -0,0 +1,149 @@
+using System;
+
+namespace GemCard
+{
+ ///
+ /// This interface gives access to the basic card functions. It must be implemented by a class.
+ ///
+ public interface ICard
+ {
+ ///
+ /// Wraps the PCSC funciton
+ /// LONG SCardListReaders(SCARDCONTEXT hContext,
+ /// LPCTSTR mszGroups,
+ /// LPTSTR mszReaders,
+ /// LPDWORD pcchReaders
+ /// );
+ ///
+ /// A string array of the readers
+ string[] ListReaders();
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardConnect(
+ /// IN SCARDCONTEXT hContext,
+ /// IN LPCTSTR szReader,
+ /// IN DWORD dwShareMode,
+ /// IN DWORD dwPreferredProtocols,
+ /// OUT LPSCARDHANDLE phCard,
+ /// OUT LPDWORD pdwActiveProtocol
+ /// );
+ ///
+ ///
+ ///
+ ///
+ void Connect(string Reader, SHARE ShareMode, PROTOCOL PreferredProtocols);
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardDisconnect(
+ /// IN SCARDHANDLE hCard,
+ /// IN DWORD dwDisposition
+ /// );
+ ///
+ ///
+ void Disconnect(DISCONNECT Disposition);
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardTransmit(
+ /// SCARDHANDLE hCard,
+ /// LPCSCARD_I0_REQUEST pioSendPci,
+ /// LPCBYTE pbSendBuffer,
+ /// DWORD cbSendLength,
+ /// LPSCARD_IO_REQUEST pioRecvPci,
+ /// LPBYTE pbRecvBuffer,
+ /// LPDWORD pcbRecvLength
+ /// );
+ ///
+ /// APDUCommand object with the APDU to send to the card
+ /// An APDUResponse object with the response from the card
+ APDUResponse Transmit(APDUCommand ApduCmd);
+
+ ///
+ /// Wraps the PSCS function
+ /// LONG SCardBeginTransaction(
+ /// SCARDHANDLE hCard
+ // );
+ ///
+ void BeginTransaction();
+
+ ///
+ /// Wraps the PCSC function
+ /// LONG SCardEndTransaction(
+ /// SCARDHANDLE hCard,
+ /// DWORD dwDisposition
+ /// );
+ ///
+ void EndTransaction(DISCONNECT Disposition);
+
+ ///
+ /// Gets the attributes of the card
+ ///
+ /// Identifier for the Attribute to get
+ /// Attribute content
+ byte[] GetAttribute(UInt32 AttribId);
+ }
+
+ ///
+ /// SCOPE context
+ ///
+ public enum SCOPE
+ {
+ ///
+ /// The context is a user context, and any database operations are performed within the
+ /// domain of the user.
+ ///
+ User,
+
+ ///
+ /// The context is that of the current terminal, and any database operations are performed
+ /// within the domain of that terminal. (The calling application must have appropriate
+ /// access permissions for any database actions.)
+ ///
+ Terminal,
+
+ ///
+ /// The context is the system context, and any database operations are performed within the
+ /// domain of the system. (The calling application must have appropriate access
+ /// permissions for any database actions.)
+ ///
+ System
+ }
+
+ ///
+ /// SHARE mode enumeration
+ ///
+ public enum SHARE
+ {
+ Exclusive = 1, /// This app. is not willing to share this card with other applications.
+ Shared, /// This app. is willing to share this card with other applications.
+ Direct, /// This app. demands direct control of the reader, so it is not available to other applications.
+ }
+
+
+ ///
+ /// PROTOCOL enumeration
+ ///
+ public enum PROTOCOL
+ {
+ Undefined = 0x00000000, /// There is no active protocol.
+ T0 = 0x00000001, /// T=0 is the active protocol.
+ T1 = 0x00000002, /// T=1 is the active protocol.
+ T0orT1 = T0 | T1, /// T=1 or T=0 can be the active protocol
+ Raw = 0x00010000, /// Raw is the active protocol.
+ Default = unchecked ((int) 0x80000000), /// Use implicit PTS.
+ }
+
+
+ ///
+ /// DISCONNECT action enumeration
+ ///
+ public enum DISCONNECT
+ {
+ Leave, /// Don't do anything special on close
+ Reset, /// Reset the card on close
+ Unpower, /// Power down the card on close
+ Eject, /// Eject(!) the card on close
+ }
+}
diff --git a/GemCard/SmartCardException.cs b/GemCard/SmartCardException.cs
new file mode 100644
index 000000000..507ea0d0f
--- /dev/null
+++ b/GemCard/SmartCardException.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace GemCard
+{
+ ///
+ /// Smart card exceptions
+ ///
+ public class SmartCardException : Exception
+ {
+
+ public SmartCardException() : base("Smart card exception")
+ {
+ }
+
+ public SmartCardException(string Message) : base(Message)
+ {
+ }
+ }
+
+
+ public class ApduCommandException : Exception
+ {
+ public const string
+ NotValidDocument = "The file is not a valid APDU command document",
+ NoSuchCommand = "No such APDU command in the document",
+ ParamLeFormat = "Le parameter format is not correct",
+ ParamP3Format = "P3 parameter format is not correct",
+ NoSuchSequence = "No such APDU sequence in the document",
+ MissingApduOrCommand = "An Apdu or a Sequence is missing in this Sequence";
+
+ public ApduCommandException() : base("APDU command exception")
+ {
+ }
+
+ public ApduCommandException(string Message) : base(Message)
+ {
+ }
+
+ }
+}
diff --git a/Results/Properties/AssemblyInfo.cs b/Results/Properties/AssemblyInfo.cs
index 440539a72..b0b811000 100644
--- a/Results/Properties/AssemblyInfo.cs
+++ b/Results/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.18.888.0")]
-[assembly: AssemblyFileVersion("2.18.888.0")]
+[assembly: AssemblyVersion("2.18.893.0")]
+[assembly: AssemblyFileVersion("2.18.893.0")]
diff --git a/Results/Utils.cs b/Results/Utils.cs
index 877c5b1ba..35855a215 100644
--- a/Results/Utils.cs
+++ b/Results/Utils.cs
@@ -1,5 +1,5 @@
///
-/// Copyright (c) 2016 Sensus Metering Systems
+/// Copyright (c) 2016-2018 Sensus Slovensko a.s.
///
using System;
using System.Text;
@@ -31,110 +31,6 @@ namespace Results
}
}
-
- ///
- /// Converts float number to a string with the specified number of significant digits
- ///
- /// Float value to be converted to a string
- /// Number of significant digits: 4, 3, or 2 (otherwise a full precision number is printed)
- /// String representation of the float number
- public static string DoubleToStr(double value, int validDigits)
- {
- if (-float.Epsilon <= value && value <= float.Epsilon)
- {
- return "0";
- }
- else if (validDigits == 4)
- {
- if (value >= 999.5 || value < -999.5) return value.ToString("F0");
- else if (value >= 99.95 || value < -99.95) return value.ToString("F1");
- else if (value >= 9.995 || value < -9.995) return value.ToString("F2");
- else if (value >= 0.9995 || value < -0.9995) return value.ToString("F3");
- else if (value >= 0.09995 || value < -0.09995) return value.ToString("F4");
- else if (value >= 0.009995 || value < -0.009995) return value.ToString("F5");
- else return value.ToString("F6");
- }
- else if (validDigits == 3)
- {
- if (value >= 99.5 || value < -99.5) return value.ToString("F0");
- else if (value >= 9.95 || value < -9.95) return value.ToString("F1");
- else if (value >= 0.995 || value < -0.995) return value.ToString("F2");
- else if (value >= 0.0995 || value < -0.0995) return value.ToString("F3");
- else if (value >= 0.00995 || value < -0.00995) return value.ToString("F4");
- else if (value >= 0.000995 || value < -0.000995) return value.ToString("F5");
- else return value.ToString("F6");
- }
- else if (validDigits == 2)
- {
- if (value >= 9.5 || value < -9.5) return value.ToString("F0");
- else if (value >= 0.95 || value < -0.95) return value.ToString("F1");
- else if (value >= 0.095 || value < -0.095) return value.ToString("F2");
- else if (value >= 0.0095 || value < -0.0095) return value.ToString("F3");
- else if (value >= 0.00095 || value < -0.00095) return value.ToString("F4");
- else return value.ToString("F5");
- }
- else return value.ToString();
- }
-
-
- public static string SignificantDigitsToFmt(double value, int sigDigits)
- {
- if (sigDigits == 5)
- {
- if (value >= 9999.5 || value < -9999.5) return "F0";
- else if (value >= 999.95 || value < -999.95) return "F1";
- else if (value >= 99.995 || value < -99.995) return "F2";
- else if (value >= 9.9995 || value < -9.9995) return "F3";
- else if (value >= 0.99995 || value < -0.99995) return "F4";
- else if (value >= 0.099995 || value < -0.099995) return "F5";
- else if (value >= 0.0099995 || value < -0.0099995) return "F6";
- else if (value >= 0.00099995 || value < -0.00099995) return "F7";
- else if (value >= 0.000099995 || value < -0.000099995) return "F8";
- else return "F9";
- }
- else if (sigDigits == 4)
- {
- if (value >= 999.5 || value < -999.5) return "F0";
- else if (value >= 99.95 || value < -99.95) return "F1";
- else if (value >= 9.995 || value < -9.995) return "F2";
- else if (value >= 0.9995 || value < -0.9995) return "F3";
- else if (value >= 0.09995 || value < -0.09995) return "F4";
- else if (value >= 0.009995 || value < -0.009995) return "F5";
- else if (value >= 0.0009995 || value < -0.0009995) return "F6";
- else if (value >= 0.00009995 || value < -0.00009995) return "F7";
- else return "F8";
- }
- else if (sigDigits == 3)
- {
- if (value >= 99.5 || value < -99.5) return "F0";
- else if (value >= 9.95 || value < -9.95) return "F1";
- else if (value >= 0.995 || value < -0.995) return "F2";
- else if (value >= 0.0995 || value < -0.0995) return "F3";
- else if (value >= 0.00995 || value < -0.00995) return "F4";
- else if (value >= 0.000995 || value < -0.000995) return "F5";
- else if (value >= 0.0000995 || value < -0.0000995) return "F6";
- else return "F7";
- }
- else if (sigDigits == 2)
- {
- if (value >= 9.5 || value < -9.5) return "F0";
- else if (value >= 0.95 || value < -0.95) return "F1";
- else if (value >= 0.095 || value < -0.095) return "F2";
- else if (value >= 0.0095 || value < -0.0095) return "F3";
- else if (value >= 0.00095 || value < -0.00095) return "F4";
- else if (value >= 0.000095 || value < -0.000095) return "F5";
- else return "F6";
- }
- else /// if (sigDigits == 1)
- {
- if (value >= 0.95 || value < -0.95) return "F0";
- else if (value >= 0.095 || value < -0.095) return "F1";
- else if (value >= 0.0095 || value < -0.0095) return "F2";
- else if (value >= 0.00095 || value < -0.00095) return "F3";
- else if (value >= 0.000095 || value < -0.000095) return "F4";
- else return "F5";
- }
- }
///
/// Converts ErrorFlags integer to string. Supports up to 31 flags (E1..E31).
@@ -170,6 +66,5 @@ namespace Results
return (useRedGreenColor) ? (passed ? "OK|Green" : "NOK|Red") : (passed ? "OK|White" : "NOK|White");
#endif
}
-
}
}
diff --git a/Results/WMeterRsltItemSpec.cs b/Results/WMeterRsltItemSpec.cs
index ae4d2ad10..170be9149 100644
--- a/Results/WMeterRsltItemSpec.cs
+++ b/Results/WMeterRsltItemSpec.cs
@@ -465,7 +465,7 @@ namespace Results
int significantDigits;
if (int.TryParse(precision.Substring(1), out significantDigits))
{
- precision = Utils.SignificantDigitsToFmt(val, significantDigits);
+ precision = Config.Utils.SignificantDigitsToFmt(val, significantDigits);
}
}
@@ -503,7 +503,7 @@ namespace Results
int significantDigits;
if (int.TryParse(precision.Substring(1), out significantDigits))
{
- precision = Utils.SignificantDigitsToFmt(val1, significantDigits);
+ precision = Config.Utils.SignificantDigitsToFmt(val1, significantDigits);
}
}
diff --git a/TBF.sln b/TBF.sln
index 96de27ef3..2677fa39f 100644
--- a/TBF.sln
+++ b/TBF.sln
@@ -28,6 +28,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dirichlet.Numerics", "Dirichlet.Numerics\Dirichlet.Numerics.csproj", "{439D0878-C76E-452B-B17D-209A89E91D36}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Users", "Users\Users.csproj", "{6E5CB0E9-E1B6-4E5D-AC6E-B1049E180F2B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697} = {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement", "UserManagement\UserManagement.csproj", "{B2CD81B3-AF09-4978-996C-02EDB5F819B7}"
ProjectSection(ProjectDependencies) = postProject
@@ -35,9 +38,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement", "UserManag
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphLib", "GraphLib\GraphLib.csproj", "{0C0A1F4D-1363-4544-A7C5-196C76D26CCA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {743DF7DB-C7B6-42EB-986D-0F485E5588E4} = {743DF7DB-C7B6-42EB-986D-0F485E5588E4}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MonitoringDB", "MonitoringDB\MonitoringDB.csproj", "{EFEC8A31-3022-4DA7-A8F6-16D30A94C3FF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GemCard", "GemCard\GemCard.csproj", "{8B10D15A-39DE-4B56-8DD1-710C1EB3A697}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -154,6 +162,18 @@ Global
{EFEC8A31-3022-4DA7-A8F6-16D30A94C3FF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{EFEC8A31-3022-4DA7-A8F6-16D30A94C3FF}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{EFEC8A31-3022-4DA7-A8F6-16D30A94C3FF}.Release|x86.ActiveCfg = Release|Any CPU
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Debug|x86.ActiveCfg = Debug|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Debug|x86.Build.0 = Debug|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Release|Mixed Platforms.Build.0 = Release|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Release|x86.ActiveCfg = Release|x86
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/TBF/Forms/LoginDlgWithBenchSelection.cs b/TBF/Forms/LoginDlgWithBenchSelection.cs
index 660335eee..d2c2ae338 100644
--- a/TBF/Forms/LoginDlgWithBenchSelection.cs
+++ b/TBF/Forms/LoginDlgWithBenchSelection.cs
@@ -1,9 +1,11 @@
///
-/// Copyright (c) 2013-2017 Sensus Metering Systems
+/// Copyright (c) 2013-2018 Sensus Slovensko a.s.
///
using System;
using System.Windows.Forms;
using TBF.Resources;
+using GemCard;
+using System.Drawing;
namespace TBF.Forms
@@ -15,12 +17,19 @@ namespace TBF.Forms
///
public partial class LoginDlgWithBenchSelection : Form
{
+ public const string UseTheTagPassword = "UseTheTag0293578";
+
/// Private fields
string alias;
string password;
string benchName = null;
string legalizator;
+ /// Smart card support, card S/N is used as user.Tag
+ GemCard.CardNative card;
+ string[] cardReaders;
+ string smartCardReader;
+
/// Readonly public properties to do the authorization.
public string Alias { get { return alias; } }
public string Password { get { return password; } }
@@ -87,6 +96,39 @@ namespace TBF.Forms
testBenchComboBox.Text = benchName;
}
}
+
+ smartCardReader = null; /// null = No smart card reader detected
+
+#if TURA_IPERL || TURA_SPECIAL
+ card = new CardNative();
+ cardReaders = card.ListReaders();
+ foreach (var crd in cardReaders)
+ {
+ if (crd.Contains("NFC"))
+ {
+ smartCardReader = crd; /// NFC smart card reader detected
+ Text += " (NFC)";
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(smartCardReader))
+ {
+ card.OnCardInserted += delegate(object sndr, CardInsertedEventArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnCardInserted), sndr, args); }
+ else OnCardInserted(sndr, args);
+ };
+
+ card.OnCardRemoved += delegate(object sndr, CardRemovedEventArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnCardRemoved), sndr, args); }
+ else OnCardRemoved(sndr, args);
+ };
+
+ card.StartCardEvents(smartCardReader);
+ }
+#endif
}
void Localize()
@@ -179,5 +221,53 @@ namespace TBF.Forms
Method = 0;
aliasLabel.Text = GetAliasLabel();
}
+
+
+
+ void OnCardInserted(object sender, CardInsertedEventArgs args)
+ {
+ BackColor = Color.Green;
+ card.Connect(smartCardReader, SHARE.Shared, PROTOCOL.T0orT1);
+
+ /// Read the serial number of the card
+ APDUResponse response = card.Transmit(new APDUCommand(0xFF, 0xCA, 0x00, 0x00, null, 7));
+ string tag = response.ToString();
+
+ card.Disconnect(DISCONNECT.Leave);
+
+ if (testBenchComboBox.SelectedItem != null)
+ {
+ alias = tag;
+ password = UseTheTagPassword;
+ benchName = testBenchComboBox.SelectedItem.ToString();
+ if (legalizatorComboBox.Enabled)
+ {
+ legalizator = legalizatorComboBox.Text;
+ Program.LocalSettings.UpdateHistory(legalizator, ref Program.LocalSettings.LastLegalizators);
+ }
+
+ DialogResult = DialogResult.OK;
+ Close();
+ return;
+ }
+ else
+ {
+ MessageBox.Show(Strings.PLs_select_testbench, Strings.Warning, MessageBoxButtons.OK);
+ }
+ }
+
+
+ void OnCardRemoved(object sender, CardRemovedEventArgs args)
+ {
+ BackColor = SystemColors.Control;
+ }
+
+ private void LoginDlgWithBenchSelection_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (!string.IsNullOrEmpty(smartCardReader))
+ {
+ card.StopCardEvents();
+ }
+ }
}
}
diff --git a/TBF/Forms/LoginDlgWithBenchSelection.designer.cs b/TBF/Forms/LoginDlgWithBenchSelection.designer.cs
index 3312c262f..ad13c8c52 100644
--- a/TBF/Forms/LoginDlgWithBenchSelection.designer.cs
+++ b/TBF/Forms/LoginDlgWithBenchSelection.designer.cs
@@ -128,6 +128,7 @@ namespace TBF.Forms
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "LoginDlgWithBenchSelection";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LoginDlgWithBenchSelection_FormClosing);
this.Load += new System.EventHandler(this.LoginDlgWithBenchSelection_Load);
this.ResumeLayout(false);
this.PerformLayout();
diff --git a/TBF/Program.cs b/TBF/Program.cs
index 0dca768c4..fc0b6bd40 100644
--- a/TBF/Program.cs
+++ b/TBF/Program.cs
@@ -248,25 +248,32 @@ namespace TBF
#if TURA_IPERL || TURA_SPECIAL
requiredGroupMembership = Grp.GID.Testers;
#endif
-
- switch (loginDlgBench.Method)
- {
- default:
- case Users.Entities.LoginMethod.UserName:
- loadedUser = Users.Entities.User.LoadUserByName(loginDlgBench.Alias, db);
- if (loadedUser != null) authorized = loadedUser.Authorize(loginDlgBench.Alias, loginDlgBench.Password, requiredGroupMembership);
- break;
- case Users.Entities.LoginMethod.FullName:
- loadedUser = Users.Entities.User.LoadUserByFullName(loginDlgBench.Alias, db);
- if (loadedUser != null) authorized = loadedUser.AuthorizeFullName(loginDlgBench.Alias, loginDlgBench.Password, requiredGroupMembership);
- break;
- case Users.Entities.LoginMethod.Number:
- int number;
- if (!int.TryParse(loginDlgBench.Alias, out number)) break;
- loadedUser = Users.Entities.User.LoadUserByNumber(number, db);
- if (loadedUser != null) authorized = loadedUser.AuthorizeNumber(number, loginDlgBench.Password, requiredGroupMembership);
- break;
- }
+ if (loginDlgBench.Password == LoginDlgWithBenchSelection.UseTheTagPassword)
+ {
+ loadedUser = Users.Entities.User.LoadUserByTag(loginDlgBench.Alias, db);
+ if (loadedUser != null) authorized = loadedUser.AuthorizeTag(loginDlgBench.Alias, requiredGroupMembership);
+ }
+ else
+ {
+ switch (loginDlgBench.Method)
+ {
+ default:
+ case Users.Entities.LoginMethod.UserName:
+ loadedUser = Users.Entities.User.LoadUserByName(loginDlgBench.Alias, db);
+ if (loadedUser != null) authorized = loadedUser.Authorize(loginDlgBench.Alias, loginDlgBench.Password, requiredGroupMembership);
+ break;
+ case Users.Entities.LoginMethod.FullName:
+ loadedUser = Users.Entities.User.LoadUserByFullName(loginDlgBench.Alias, db);
+ if (loadedUser != null) authorized = loadedUser.AuthorizeFullName(loginDlgBench.Alias, loginDlgBench.Password, requiredGroupMembership);
+ break;
+ case Users.Entities.LoginMethod.Number:
+ int number;
+ if (!int.TryParse(loginDlgBench.Alias, out number)) break;
+ loadedUser = Users.Entities.User.LoadUserByNumber(number, db);
+ if (loadedUser != null) authorized = loadedUser.AuthorizeNumber(number, loginDlgBench.Password, requiredGroupMembership);
+ break;
+ }
+ }
}
catch
{
diff --git a/TBF/Properties/AssemblyInfo.cs b/TBF/Properties/AssemblyInfo.cs
index 96b2183ec..bdf6e5931 100644
--- a/TBF/Properties/AssemblyInfo.cs
+++ b/TBF/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
-[assembly: AssemblyVersion("2.18.892.0")]
-[assembly: AssemblyFileVersion("2.18.892.0")]
+[assembly: AssemblyVersion("2.18.893.0")]
+[assembly: AssemblyFileVersion("2.18.893.0")]
diff --git a/TBF/Screens/GraphsTabPageCtrl.cs b/TBF/Screens/GraphsTabPageCtrl.cs
index 7d81a6ee9..1d3151cbe 100644
--- a/TBF/Screens/GraphsTabPageCtrl.cs
+++ b/TBF/Screens/GraphsTabPageCtrl.cs
@@ -304,7 +304,7 @@ namespace TBF.Screens
private string RenderYLabel(DataSource s, float value)
{
- return string.Format("{0:0.0}", value);
+ return value.ToString(Config.Utils.SignificantDigitsToFmt(value, 2));
}
private void checkBox1_CheckedChanged(object sender, EventArgs e) { Program.LocalSettings.Graph1_On = checkBox1.Checked; AnyCBChanged(); }
diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj
index 3a0822c1b..9fe5ea648 100644
--- a/TBF/TBF.csproj
+++ b/TBF/TBF.csproj
@@ -2877,6 +2877,10 @@
{439D0878-C76E-452B-B17D-209A89E91D36}
Dirichlet.Numerics
+
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}
+ GemCard
+
{0C0A1F4D-1363-4544-A7C5-196C76D26CCA}
GraphLib
diff --git a/TBF/Utils.cs b/TBF/Utils.cs
index e9f00c4ef..b5a4d2d55 100644
--- a/TBF/Utils.cs
+++ b/TBF/Utils.cs
@@ -341,7 +341,7 @@ namespace TBF
}
else if (significantDigits <= 5)
{
- string fmt = Results.Utils.SignificantDigitsToFmt(value, significantDigits);
+ string fmt = Config.Utils.SignificantDigitsToFmt(value, significantDigits);
return altCulture ? value.ToString(fmt, Program.AltCulture) : value.ToString(fmt);
}
else
diff --git a/UserManagement/Properties/AssemblyInfo.cs b/UserManagement/Properties/AssemblyInfo.cs
index d672dcb8a..6a2cff483 100644
--- a/UserManagement/Properties/AssemblyInfo.cs
+++ b/UserManagement/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.18.798.0")]
-[assembly: AssemblyFileVersion("2.18.798.0")]
+[assembly: AssemblyVersion("2.18.893.0")]
+[assembly: AssemblyFileVersion("2.18.893.0")]
diff --git a/Users/Entities/User.cs b/Users/Entities/User.cs
index 8cf650948..36bc66f88 100644
--- a/Users/Entities/User.cs
+++ b/Users/Entities/User.cs
@@ -16,6 +16,7 @@ namespace Users.Entities
public virtual int Id { get; protected set; }
public virtual string UserName { get; set; } /// = name, alias, abbreviation
public virtual string FullName { get; set; } /// = description
+ public virtual string Tag { get; set; }
public virtual int Number { get; set; }
public virtual string Password { get; set; }
public virtual DateTime LastPwChange { get; set; }
@@ -33,6 +34,7 @@ namespace Users.Entities
this.powerUser = false;
Groups = new List();
Number = 0;
+ Tag = string.Empty;
}
public User(string userName, int number, bool powerUser)
@@ -61,6 +63,7 @@ namespace Users.Entities
User newUser = new User(UserName, Number, false);
newUser.FullName = FullName;
newUser.Number = Number;
+ newUser.Tag = Tag;
newUser.Password = Password;
newUser.LastPwChange = LastPwChange;
return newUser;
@@ -250,6 +253,44 @@ namespace Users.Entities
return AuthorizeFullName(fullName, password, Grp.GID.None);
}
+ ///
+ /// The FIRST of TWO possible user authorization method to be used
+ /// when a specific group membership is required (you can use Grp.GID.None).
+ /// If the user is not authorized, the current user remains to be a current user
+ /// (i.e. the access rights were not risen to a higher level).
+ /// If you require different behavior, use Unauthorize() before calling Authorize().
+ ///
+ /// Tag (RFID, NFC, ... s/n)
+ ///
+ /// true = authorized
+ public virtual bool AuthorizeTag(string tag, Grp.GID requiredGroupMembership)
+ {
+ if (Tag == tag)
+ {
+ if (IsMemberOf(requiredGroupMembership))
+ {
+ GlobalData.CurrentUser = this;
+ GlobalData.LastAuthorization = DateTime.Now;
+ log.FatalFormat("User with Tag={0} authorized @level '{1}'", tag, requiredGroupMembership);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// The SECOND of TWO possible user authorization method to be used when
+ /// no specific group membership is required.
+ ///
+ public virtual bool AuthorizeTag(string tag)
+ {
+ return AuthorizeTag(tag, Grp.GID.None);
+ }
+
///
/// Returns a 'User' with a given username from a database.
@@ -371,6 +412,41 @@ namespace Users.Entities
}
+ ///
+ /// Returns a 'User' with a given RFID/NFC tag s/n from an ARBITRARY database.
+ ///
+ /// Tag of a user for the query
+ /// reference to a 'User' (if it exists) or null
+ public static User LoadUserByTag(string tag, DBSettings dbSettings)
+ {
+ if (dbSettings == null || string.IsNullOrEmpty(dbSettings.ConnectionString)) return null;
+
+ DB.DbType = dbSettings.DbType;
+ DB.ConnectionString = dbSettings.ConnectionString;
+ IList listOfUsers = DB.CreateSession()
+ .QueryOver()
+ .Where(x => (x.Tag == tag))
+ .List();
+ if (listOfUsers.Count > 0) return listOfUsers[0];
+ return null;
+ }
+
+ ///
+ /// Returns a 'User' with a given RFID/NFC tag s/n from the users database.
+ ///
+ /// Tag of a user for the query
+ /// reference to a 'User' (if it exists) or null
+ public static User LoadUserByTag(string tag)
+ {
+ IList listOfUsers = DB.CreateSession()
+ .QueryOver()
+ .Where(x => (x.Tag == tag))
+ .List();
+ if (listOfUsers.Count > 0) return listOfUsers[0];
+ return null;
+ }
+
+
///
/// returns an IList of all Users
///
diff --git a/Users/Forms/EditSelectedUser.Designer.cs b/Users/Forms/EditSelectedUser.Designer.cs
index 96722c9e1..2257b8c8b 100644
--- a/Users/Forms/EditSelectedUser.Designer.cs
+++ b/Users/Forms/EditSelectedUser.Designer.cs
@@ -35,8 +35,8 @@ namespace Users.Forms
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EditSelectedUser));
this.txtName = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
- this.btnOk = new System.Windows.Forms.Button();
- this.btnCancel = new System.Windows.Forms.Button();
+ this.okBtn = new System.Windows.Forms.Button();
+ this.cancelBtn = new System.Windows.Forms.Button();
this.txtPassword = new System.Windows.Forms.TextBox();
this.passwordLabel = new System.Windows.Forms.Label();
this.txtDescription = new System.Windows.Forms.TextBox();
@@ -48,6 +48,8 @@ namespace Users.Forms
this.txtRepeatPassword = new System.Windows.Forms.TextBox();
this.codeLabel = new System.Windows.Forms.Label();
this.txtCode = new System.Windows.Forms.TextBox();
+ this.tagLabel = new System.Windows.Forms.Label();
+ this.txtTag = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// txtName
@@ -63,19 +65,19 @@ namespace Users.Forms
resources.ApplyResources(this.nameLabel, "nameLabel");
this.nameLabel.Name = "nameLabel";
//
- // btnOk
+ // okBtn
//
- resources.ApplyResources(this.btnOk, "btnOk");
- this.btnOk.Name = "btnOk";
- this.btnOk.UseVisualStyleBackColor = true;
- this.btnOk.Click += new System.EventHandler(this.btnOk_Click);
+ resources.ApplyResources(this.okBtn, "okBtn");
+ this.okBtn.Name = "okBtn";
+ this.okBtn.UseVisualStyleBackColor = true;
+ this.okBtn.Click += new System.EventHandler(this.okBtn_Click);
//
- // btnCancel
+ // cancelBtn
//
- resources.ApplyResources(this.btnCancel, "btnCancel");
- this.btnCancel.Name = "btnCancel";
- this.btnCancel.UseVisualStyleBackColor = true;
- this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);
+ resources.ApplyResources(this.cancelBtn, "cancelBtn");
+ this.cancelBtn.Name = "cancelBtn";
+ this.cancelBtn.UseVisualStyleBackColor = true;
+ this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click);
//
// txtPassword
//
@@ -135,18 +137,30 @@ namespace Users.Forms
this.txtCode.Name = "txtCode";
this.txtCode.TextChanged += new System.EventHandler(this.txtCode_TextChanged);
//
+ // tagLabel
+ //
+ resources.ApplyResources(this.tagLabel, "tagLabel");
+ this.tagLabel.Name = "tagLabel";
+ //
+ // txtTag
+ //
+ resources.ApplyResources(this.txtTag, "txtTag");
+ this.txtTag.Name = "txtTag";
+ //
// EditSelectedUser
//
resources.ApplyResources(this, "$this");
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.Controls.Add(this.tagLabel);
+ this.Controls.Add(this.txtTag);
this.Controls.Add(this.codeLabel);
this.Controls.Add(this.txtCode);
this.Controls.Add(this.repeatPasswordLabel);
this.Controls.Add(this.txtRepeatPassword);
this.Controls.Add(this.groupsLabel);
this.Controls.Add(this.checkedListBoxGroups);
- this.Controls.Add(this.btnCancel);
- this.Controls.Add(this.btnOk);
+ this.Controls.Add(this.cancelBtn);
+ this.Controls.Add(this.okBtn);
this.Controls.Add(this.descriptionLabel);
this.Controls.Add(this.passwordLabel);
this.Controls.Add(this.nameLabel);
@@ -157,6 +171,7 @@ namespace Users.Forms
this.MaximizeBox = false;
this.Name = "EditSelectedUser";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.EditSelectedUser_FormClosing);
this.Load += new System.EventHandler(this.EditSelectedUser_Load);
this.Shown += new System.EventHandler(this.EditSelectedUser_Shown);
this.ResumeLayout(false);
@@ -168,8 +183,8 @@ namespace Users.Forms
private System.Windows.Forms.TextBox txtName;
private System.Windows.Forms.Label nameLabel;
- private System.Windows.Forms.Button btnOk;
- private System.Windows.Forms.Button btnCancel;
+ private System.Windows.Forms.Button okBtn;
+ private System.Windows.Forms.Button cancelBtn;
private System.Windows.Forms.TextBox txtPassword;
private System.Windows.Forms.Label passwordLabel;
private System.Windows.Forms.TextBox txtDescription;
@@ -181,5 +196,7 @@ namespace Users.Forms
private System.Windows.Forms.TextBox txtRepeatPassword;
private System.Windows.Forms.Label codeLabel;
private System.Windows.Forms.TextBox txtCode;
+ private System.Windows.Forms.Label tagLabel;
+ private System.Windows.Forms.TextBox txtTag;
}
}
\ No newline at end of file
diff --git a/Users/Forms/EditSelectedUser.cs b/Users/Forms/EditSelectedUser.cs
index 79244c782..b13d039e4 100644
--- a/Users/Forms/EditSelectedUser.cs
+++ b/Users/Forms/EditSelectedUser.cs
@@ -8,6 +8,8 @@ using System.Windows.Forms;
using NHibernate;
using Users.Entities;
using Users.Resources;
+using GemCard;
+using System.Drawing;
namespace Users.Forms
{
@@ -18,12 +20,18 @@ namespace Users.Forms
bool[] oriGroupMember; /// index is gid, size is Grp.Count
+ /// Smart card support, card S/N is used as user.Tag
+ GemCard.CardNative card;
+ string[] cardReaders;
+ string smartCardReader;
+
+
public EditSelectedUser()
{
InitializeComponent();
this.toolTip1.SetToolTip(this.txtName, string.Format(Strings.max_0_alphanum_chars, 20));
oriGroupMember = new bool[Grp.Count];
- btnOk.Enabled = false;
+ okBtn.Enabled = false;
}
public EditSelectedUser(NHibernate.ISession session, User user)
@@ -39,10 +47,47 @@ namespace Users.Forms
txtName.Text = user.UserName;
txtCode.Text = user.Number.ToString();
+ txtTag.Text = string.IsNullOrEmpty(user.Tag) ? string.Empty : user.Tag;
txtDescription.Text = user.FullName;
txtPassword.Text = string.Empty;
txtRepeatPassword.Text = string.Empty;
+
ShowGroups();
+
+ smartCardReader = null; /// null = No smart card reader detected
+
+#if TURA_IPERL || TURA_SPECIAL
+ card = new CardNative();
+ cardReaders = card.ListReaders();
+ foreach (var crd in cardReaders)
+ {
+ if (crd.Contains("NFC"))
+ {
+ smartCardReader = crd; /// NFC smart card reader detected
+ Text += " (NFC)";
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(smartCardReader))
+ {
+ card.OnCardInserted += delegate(object sndr, CardInsertedEventArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnCardInserted), sndr, args); }
+ else OnCardInserted(sndr, args);
+ };
+
+ card.OnCardRemoved += delegate(object sndr, CardRemovedEventArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnCardRemoved), sndr, args); }
+ else OnCardRemoved(sndr, args);
+ };
+
+ card.StartCardEvents(smartCardReader);
+ }
+
+ txtTag.Enabled = true;
+#endif
}
void Localize()
@@ -52,10 +97,11 @@ namespace Users.Forms
passwordLabel.Text = Strings.Password;
repeatPasswordLabel.Text = Strings.Repeat_password;
codeLabel.Text = Strings.User_code;
+ tagLabel.Text = Strings.Tag;
descriptionLabel.Text = Strings.DescriptionColHdr;
groupsLabel.Text = Strings.Groups;
- btnOk.Text = Strings.OkBtnText;
- btnCancel.Text = Strings.CancelBtnText;
+ okBtn.Text = Strings.OkBtnText;
+ cancelBtn.Text = Strings.CancelBtnText;
}
public void ShowGroups()
@@ -81,12 +127,18 @@ namespace Users.Forms
public bool Save()
{
// todo: check if this name is given to another user
- if (IsUserNameAllreadyTaken(txtName.Text))
+ if (IsUserNameAlreadyTaken(txtName.Text))
{
MessageBox.Show(Strings.Username_taken);
return false;
}
+ if (IsTagAlreadyTaken(txtTag.Text))
+ {
+ MessageBox.Show(Strings.Tag_taken);
+ return false;
+ }
+
ITransaction transaction = session.BeginTransaction();
try
@@ -96,6 +148,7 @@ namespace Users.Forms
user.UserName = txtName.Text;
user.FullName = txtDescription.Text;
user.Number = int.Parse(txtCode.Text);
+ user.Tag = string.IsNullOrEmpty(txtTag.Text) ? null : txtTag.Text;
if (txtPassword.Text != string.Empty)
{
@@ -146,19 +199,41 @@ namespace Users.Forms
///
/// returns true if the username allready exists for another user
///
- private bool IsUserNameAllreadyTaken(string Username)
+ private bool IsUserNameAlreadyTaken(string userName)
{
- // have a look into all other users
+ // Have a look into all other users
IList ListOfUsers = User.GetAllUsers();
foreach (var person in ListOfUsers)
{
if (person.Id != user.Id)
{
- // other user
- if (person.UserName.ToLower() == Username.ToLower())
+ /// Other user then the current one
+ if (person.UserName.ToLower() == userName.ToLower())
{
- // Name is equal, so allready taken
- return true;
+ return true; // Name is equal = already taken
+ }
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// returns true if the username allready exists for another user
+ ///
+ private bool IsTagAlreadyTaken(string tag)
+ {
+ if (string.IsNullOrEmpty(tag)) return false; /// No tag
+
+ // Have a look into all other users
+ IList ListOfUsers = User.GetAllUsers();
+ foreach (var person in ListOfUsers)
+ {
+ if (person.Id != user.Id)
+ {
+ // Other user then the current one
+ if (person.Tag == tag)
+ {
+ return true; // Tag is equal = already taken
}
}
}
@@ -166,7 +241,7 @@ namespace Users.Forms
}
- private void btnOk_Click(object sender, EventArgs e)
+ private void okBtn_Click(object sender, EventArgs e)
{
if (Save())
{
@@ -175,7 +250,7 @@ namespace Users.Forms
}
}
- private void btnCancel_Click(object sender, EventArgs e)
+ private void cancelBtn_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
this.Close();
@@ -196,9 +271,36 @@ namespace Users.Forms
{
int userCode;
- btnOk.Enabled = (txtName.Text != string.Empty)
+ okBtn.Enabled = (txtName.Text != string.Empty)
&& int.TryParse(txtCode.Text, out userCode)
&& (txtPassword.Text == txtRepeatPassword.Text);
}
+
+
+ void OnCardInserted(object sender, CardInsertedEventArgs args)
+ {
+ BackColor = Color.Green;
+ card.Connect(smartCardReader, SHARE.Shared, PROTOCOL.T0orT1);
+
+ /// Read the serial number of the card
+ APDUResponse response = card.Transmit(new APDUCommand(0xFF, 0xCA, 0x00, 0x00, null, 7));
+ txtTag.Text = response.ToString();
+
+ card.Disconnect(DISCONNECT.Leave);
+ }
+
+
+ void OnCardRemoved(object sender, CardRemovedEventArgs args)
+ {
+ BackColor = SystemColors.Control;
+ }
+
+ private void EditSelectedUser_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (!string.IsNullOrEmpty(smartCardReader))
+ {
+ card.StopCardEvents();
+ }
+ }
}
}
diff --git a/Users/Forms/EditSelectedUser.resx b/Users/Forms/EditSelectedUser.resx
index 2b6f00e1f..90fef2903 100644
--- a/Users/Forms/EditSelectedUser.resx
+++ b/Users/Forms/EditSelectedUser.resx
@@ -138,7 +138,7 @@
$this
- 13
+ 15
True
@@ -165,58 +165,62 @@
$this
- 10
+ 12
-
- 137, 356
+
+
+ NoControl
-
- 84, 40
+
+ 137, 365
-
- 10
+
+ 94, 31
-
+
+ 15
+
+
OK
-
- btnOk
+
+ okBtn
-
+
System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
+
$this
-
- 7
+
+ 9
-
- 253, 356
+
+ 243, 365
-
- 84, 40
+
+ 94, 31
-
- 11
+
+ 16
-
+
Cancel
-
- btnCancel
+
+ cancelBtn
-
+
System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
+
$this
-
- 6
+
+ 8
- 137, 38
+ 137, 35
*
@@ -237,13 +241,13 @@
$this
- 12
+ 14
True
- 9, 41
+ 9, 38
53, 13
@@ -264,10 +268,10 @@
$this
- 9
+ 11
- 137, 122
+ 137, 135
True
@@ -276,7 +280,7 @@
200, 59
- 7
+ 12
txtDescription
@@ -288,19 +292,19 @@
$this
- 11
+ 13
True
- 9, 125
+ 9, 138
60, 13
- 6
+ 11
Description
@@ -315,16 +319,16 @@
$this
- 8
+ 10
- 137, 190
+ 137, 199
200, 154
- 9
+ 14
checkedListBoxGroups
@@ -336,19 +340,19 @@
$this
- 5
+ 7
True
- 9, 190
+ 9, 199
41, 13
- 8
+ 13
Groups
@@ -363,7 +367,7 @@
$this
- 4
+ 6
17, 17
@@ -371,12 +375,11 @@
True
-
NoControl
- 9, 69
+ 9, 63
90, 13
@@ -397,10 +400,10 @@
$this
- 2
+ 4
- 137, 66
+ 137, 60
*
@@ -421,7 +424,7 @@
$this
- 3
+ 5
True
@@ -430,13 +433,13 @@
NoControl
- 9, 97
+ 9, 88
32, 13
- 12
+ 6
Code
@@ -451,16 +454,16 @@
$this
- 0
+ 2
- 137, 94
+ 137, 85
- 66, 20
+ 200, 20
- 13
+ 7
txtCode
@@ -472,6 +475,60 @@
$this
+ 3
+
+
+ True
+
+
+ NoControl
+
+
+ 9, 113
+
+
+ 26, 13
+
+
+ 8
+
+
+ Tag
+
+
+ tagLabel
+
+
+ System.Windows.Forms.Label, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
+ 0
+
+
+ False
+
+
+ 137, 110
+
+
+ 200, 20
+
+
+ 9
+
+
+ txtTag
+
+
+ System.Windows.Forms.TextBox, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ $this
+
+
1
diff --git a/Users/Forms/LoginDlg.Designer.cs b/Users/Forms/LoginDlg.Designer.cs
index ac46d22f6..14938db91 100644
--- a/Users/Forms/LoginDlg.Designer.cs
+++ b/Users/Forms/LoginDlg.Designer.cs
@@ -94,6 +94,7 @@ namespace Users.Forms
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Name = "LoginDlg";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LoginDlg_FormClosing);
this.Load += new System.EventHandler(this.LoginDlg_Load);
this.ResumeLayout(false);
this.PerformLayout();
diff --git a/Users/Forms/LoginDlg.cs b/Users/Forms/LoginDlg.cs
index 745433c0c..c6eeac810 100644
--- a/Users/Forms/LoginDlg.cs
+++ b/Users/Forms/LoginDlg.cs
@@ -1,9 +1,11 @@
///
-/// Copyright (c) 2017 Sensus Metering Systems
+/// Copyright (c) 2017-2018 Sensus Slovensko a.s.
///
using System;
using System.Windows.Forms;
+using GemCard;
using Users.Resources;
+using System.Drawing;
namespace Users.Forms
{
@@ -19,11 +21,14 @@ namespace Users.Forms
Grp.GID requiredGroupMembership = Grp.GID.Invalid;
bool noDatabase;
-
+ /// Smart card support, card S/N is used as user.Tag
+ GemCard.CardNative card;
+ string[] cardReaders;
+ string smartCardReader;
+
public Users.Entities.LoginMethod Method;
public string VersionString;
-
///
/// Constructor.
///
@@ -79,6 +84,39 @@ namespace Users.Forms
userNameTextBox.Select();
else
passwordTextBox.Select();
+
+ smartCardReader = null; /// null = No smart card reader detected
+
+#if TURA_IPERL || TURA_SPECIAL
+ card = new CardNative();
+ cardReaders = card.ListReaders();
+ foreach (var crd in cardReaders)
+ {
+ if (crd.Contains("NFC"))
+ {
+ smartCardReader = crd; /// NFC smart card reader detected
+ Text += " (NFC)";
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(smartCardReader))
+ {
+ card.OnCardInserted += delegate(object sndr, CardInsertedEventArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnCardInserted), sndr, args); }
+ else OnCardInserted(sndr, args);
+ };
+
+ card.OnCardRemoved += delegate(object sndr, CardRemovedEventArgs args)
+ {
+ if (InvokeRequired) { Invoke(new EventHandler(OnCardRemoved), sndr, args); }
+ else OnCardRemoved(sndr, args);
+ };
+
+ card.StartCardEvents(smartCardReader);
+ }
+#endif
}
void Localize()
@@ -124,27 +162,31 @@ namespace Users.Forms
foreach (var db in dbs)
{
- Users.Entities.User loadedUser = Users.Entities.User.LoadUserByName(user, db);
- if (loadedUser != null)
- {
- switch (Method)
- {
- default:
- case Entities.LoginMethod.UserName:
- authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.Authorize(user, password) : loadedUser.Authorize(user, password, requiredGroupMembership);
- break;
- case Entities.LoginMethod.FullName:
- authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.AuthorizeFullName(user, password) : loadedUser.AuthorizeFullName(user, password, requiredGroupMembership);
- break;
- case Entities.LoginMethod.Number:
- int number;
- if (!int.TryParse(user, out number)) break;
- authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.AuthorizeNumber(number, password) : loadedUser.AuthorizeNumber(number, password, requiredGroupMembership);
- break;
- }
- }
+ try
+ {
+ Users.Entities.User loadedUser = Users.Entities.User.LoadUserByName(user, db);
+ if (loadedUser != null)
+ {
+ switch (Method)
+ {
+ default:
+ case Entities.LoginMethod.UserName:
+ authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.Authorize(user, password) : loadedUser.Authorize(user, password, requiredGroupMembership);
+ break;
+ case Entities.LoginMethod.FullName:
+ authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.AuthorizeFullName(user, password) : loadedUser.AuthorizeFullName(user, password, requiredGroupMembership);
+ break;
+ case Entities.LoginMethod.Number:
+ int number;
+ if (!int.TryParse(user, out number)) break;
+ authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.AuthorizeNumber(number, password) : loadedUser.AuthorizeNumber(number, password, requiredGroupMembership);
+ break;
+ }
+ }
+ }
+ catch (Exception) { }
- if (authorized) break;
+ if (authorized) break;
authorizedAs = Entities.AuthorizedAs.LocalUser;
}
@@ -200,5 +242,65 @@ namespace Users.Forms
okButton_Click(sender, e);
}
}
+
+
+ void OnCardInserted(object sender, CardInsertedEventArgs args)
+ {
+ BackColor = Color.Green;
+ card.Connect(smartCardReader, SHARE.Shared, PROTOCOL.T0orT1);
+
+ /// Read the serial number of the card
+ APDUResponse response = card.Transmit(new APDUCommand(0xFF, 0xCA, 0x00, 0x00, null, 7));
+ string tag = response.ToString();
+
+ card.Disconnect(DISCONNECT.Leave);
+
+ DBSettings[] dbs = new DBSettings[] { GlobalData.RemoteUsersDB, GlobalData.LocalUsersDB };
+ bool authorized = false;
+ Entities.AuthorizedAs authorizedAs = Entities.AuthorizedAs.RemoteUser;
+ ///
+ foreach (var db in dbs)
+ {
+ try
+ {
+ Users.Entities.User loadedUser = Users.Entities.User.LoadUserByTag(tag, db);
+ if (loadedUser != null)
+ {
+ authorized = (requiredGroupMembership == Grp.GID.Invalid) ? loadedUser.AuthorizeTag(tag) : loadedUser.AuthorizeTag(tag, requiredGroupMembership);
+ }
+ }
+ catch (Exception) { }
+
+ if (authorized) break;
+
+ authorizedAs = Entities.AuthorizedAs.LocalUser;
+ }
+
+ if (authorized)
+ {
+ Users.GlobalData.AuthorizedAs = authorizedAs;
+ DialogResult = DialogResult.OK;
+ Close();
+ return;
+ }
+
+ MessageBox.Show(Strings.Invalid_username_or_password, Strings.Error, MessageBoxButtons.OK);
+ DialogResult = DialogResult.None;
+ return;
+ }
+
+
+ void OnCardRemoved(object sender, CardRemovedEventArgs args)
+ {
+ BackColor = SystemColors.Control;
+ }
+
+ private void LoginDlg_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ if (!string.IsNullOrEmpty(smartCardReader))
+ {
+ card.StopCardEvents();
+ }
+ }
}
}
diff --git a/Users/Forms/UserManagementDlg.cs b/Users/Forms/UserManagementDlg.cs
index adfdf1874..e0b88116e 100644
--- a/Users/Forms/UserManagementDlg.cs
+++ b/Users/Forms/UserManagementDlg.cs
@@ -36,10 +36,13 @@ namespace Users.Forms
deleteButton.Text = Strings.RemoveBtnText;
closeButton.Text = Strings.CloseBtnText;
- listViewUsers.Columns.Add(Strings.User_name, 120);
- listViewUsers.Columns.Add(Strings.User_code, 80);
- listViewUsers.Columns.Add(Strings.Full_name, 160);
- listViewUsers.Columns.Add(Strings.Groups, 300);
+ listViewUsers.Columns.Add(Strings.User_name, 100);
+ listViewUsers.Columns.Add(Strings.User_code, 70);
+ listViewUsers.Columns.Add(Strings.Full_name, 150);
+#if TURA_IPERL || TURA_SPECIAL
+ listViewUsers.Columns.Add(Strings.Tag, 110);
+#endif
+ listViewUsers.Columns.Add(Strings.Groups, 370);
try
{
@@ -79,6 +82,9 @@ namespace Users.Forms
ListViewItem item = new ListViewItem(u.UserName);
item.SubItems.Add(u.Number.ToString());
item.SubItems.Add(u.FullName);
+#if TURA_IPERL || TURA_SPECIAL
+ item.SubItems.Add(string.IsNullOrEmpty(u.Tag) ? string.Empty : u.Tag);
+#endif
item.SubItems.Add(grps);
item.Tag = u;
this.listViewUsers.Items.Add(item);
@@ -115,8 +121,11 @@ namespace Users.Forms
private void editButton_Click(object sender, EventArgs e)
{
- new Forms.EditSelectedUser(session, (User)listViewUsers.SelectedItems[0].Tag).ShowDialog();
- ListUsers();
+ if (listViewUsers.SelectedItems.Count == 1 && listViewUsers.SelectedItems[0].Tag is User)
+ {
+ new Forms.EditSelectedUser(session, (User)listViewUsers.SelectedItems[0].Tag).ShowDialog();
+ ListUsers();
+ }
}
private void listViewUsers_DoubleClick(object sender, EventArgs e)
diff --git a/Users/Forms/UserManagementDlg.resx b/Users/Forms/UserManagementDlg.resx
index 1f703b774..2a960f81a 100644
--- a/Users/Forms/UserManagementDlg.resx
+++ b/Users/Forms/UserManagementDlg.resx
@@ -126,7 +126,7 @@
0, 0
- 480, 502
+ 821, 502
@@ -247,7 +247,7 @@
0, 502
- 615, 22
+ 956, 22
2
@@ -331,10 +331,10 @@
1
- 615, 502
+ 956, 502
- 480
+ 821
3
@@ -358,7 +358,7 @@
6, 13
- 615, 524
+ 956, 524
User Management
diff --git a/Users/Mappings/UserMap.cs b/Users/Mappings/UserMap.cs
index e3318b1e6..7e47dd801 100644
--- a/Users/Mappings/UserMap.cs
+++ b/Users/Mappings/UserMap.cs
@@ -1,5 +1,5 @@
///
-/// Copyright (c) 2016 Sensus Metering Systems
+/// Copyright (c) 2016-2018 Sensus Slovensko a.s.
///
using FluentNHibernate.Mapping;
@@ -12,7 +12,10 @@ namespace Users.Mappings
Id(x => x.Id);
Map(x => x.UserName).Column("Name");
Map(x => x.Number);
- Map(x => x.Password);
+#if TURA_IPERL || TURA_SPECIAL
+ Map(x => x.Tag);
+#endif
+ Map(x => x.Password);
Map(x => x.FullName).Column("Description");
Map(x => x.LastPwChange);
HasMany(x => x.Groups)
diff --git a/Users/Properties/AssemblyInfo.cs b/Users/Properties/AssemblyInfo.cs
index 2fb00bc27..308668b83 100644
--- a/Users/Properties/AssemblyInfo.cs
+++ b/Users/Properties/AssemblyInfo.cs
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.18.843.0")]
-[assembly: AssemblyFileVersion("2.18.843.0")]
+[assembly: AssemblyVersion("2.18.893.0")]
+[assembly: AssemblyFileVersion("2.18.893.0")]
diff --git a/Users/Resources/Strings.Designer.cs b/Users/Resources/Strings.Designer.cs
index ba7374126..ed4bf51c4 100644
--- a/Users/Resources/Strings.Designer.cs
+++ b/Users/Resources/Strings.Designer.cs
@@ -330,6 +330,24 @@ namespace Users.Resources {
}
}
+ ///
+ /// Looks up a localized string similar to NFC Tag.
+ ///
+ internal static string Tag {
+ get {
+ return ResourceManager.GetString("Tag", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to This NFC tag has already been taken. Please use another one..
+ ///
+ internal static string Tag_taken {
+ get {
+ return ResourceManager.GetString("Tag_taken", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to tester.
///
diff --git a/Users/Resources/Strings.cs.resx b/Users/Resources/Strings.cs.resx
index 703c4ecbb..b60a642dc 100644
--- a/Users/Resources/Strings.cs.resx
+++ b/Users/Resources/Strings.cs.resx
@@ -216,4 +216,7 @@
neplatný
+
+ Tento NFC tag již byl použit. Prosím, použite jiný.
+
\ No newline at end of file
diff --git a/Users/Resources/Strings.resx b/Users/Resources/Strings.resx
index ec9c0ec38..d78cf1e59 100644
--- a/Users/Resources/Strings.resx
+++ b/Users/Resources/Strings.resx
@@ -228,4 +228,10 @@
invalid
+
+ NFC Tag
+
+
+ This NFC tag has already been taken. Please use another one.
+
\ No newline at end of file
diff --git a/Users/Users.csproj b/Users/Users.csproj
index 7582325d3..0a4cbff41 100644
--- a/Users/Users.csproj
+++ b/Users/Users.csproj
@@ -91,7 +91,12 @@
Strings.resx
-
+
+
+ {8B10D15A-39DE-4B56-8DD1-710C1EB3A697}
+ GemCard
+
+
EditSelectedUser.cs
diff --git a/clean.bat b/clean.bat
index 450f20eee..ec7c80f62 100644
--- a/clean.bat
+++ b/clean.bat
@@ -4,6 +4,8 @@ rmdir /s /q DeviceTest\bin
rmdir /s /q DeviceTest\obj
rmdir /s /q Dirichlet.Numerics\bin
rmdir /s /q Dirichlet.Numerics\obj
+rmdir /s /q GemCard\bin
+rmdir /s /q GemCard\obj
rmdir /s /q GraphLib\bin
rmdir /s /q GraphLib\obj
rmdir /s /q MonitoringDB\bin