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