Skip to content
This repository was archived by the owner on May 16, 2024. It is now read-only.

Commit 6e10c07

Browse files
committed
Add source code location to exceptions, minor refactoring
1 parent a197f56 commit 6e10c07

File tree

8 files changed

+137
-40
lines changed

8 files changed

+137
-40
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ project(pcsc-cpp)
1010
add_library(${PROJECT_NAME}
1111
STATIC
1212
include/${PROJECT_NAME}/${PROJECT_NAME}.hpp
13+
include/${PROJECT_NAME}/${PROJECT_NAME}-utils.hpp
1314
include/${PROJECT_NAME}/comp_winscard.hpp
1415
include/flag-set-cpp/flag_set.hpp
1516
include/magic_enum/magic_enum.hpp
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) 2020 The Web eID Project
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#pragma once
24+
25+
#include <string>
26+
#include <sstream>
27+
#include <iomanip>
28+
29+
namespace pcsc_cpp
30+
{
31+
32+
/** Convert the given integer to a hex string. */
33+
template <typename T>
34+
inline std::string int2hexstr(const T value)
35+
{
36+
std::ostringstream hexStringBuilder;
37+
38+
hexStringBuilder << "0x" << std::setfill('0') << std::setw(sizeof(long) * 2) << std::hex
39+
<< value;
40+
41+
return hexStringBuilder.str();
42+
}
43+
44+
/** Remove absolute path prefix until "src" from the given path, '/path/to/src/main.cpp' becomes
45+
* 'src/main.cpp'. */
46+
inline std::string removeAbsolutePathPrefix(const std::string& filePath)
47+
{
48+
const auto lastSrc = filePath.rfind("src");
49+
return lastSrc == std::string::npos ? filePath : filePath.substr(lastSrc);
50+
}
51+
52+
} // namespace pcsc_cpp
53+
54+
#define THROW_WITH_CALLER_INFO(ExceptionType, message, file, line, func) \
55+
throw ExceptionType(std::string(message) + " in " + pcsc_cpp::removeAbsolutePathPrefix(file) \
56+
+ ':' + std::to_string(line) + ':' + func)
57+
58+
#define THROW(ExceptionType, message) \
59+
THROW_WITH_CALLER_INFO(ExceptionType, message, __FILE__, __LINE__, __func__)

include/pcsc-cpp/pcsc-cpp.hpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,18 +331,39 @@ class ScardError : public Error
331331
using Error::Error;
332332
};
333333

334-
/** Raised when the PC/SC service is not running. */
335-
class ScardServiceNotRunning : public ScardError
334+
/** Thrown when the PC/SC service is not running. */
335+
class ScardServiceNotRunningError : public ScardError
336336
{
337337
public:
338338
using ScardError::ScardError;
339339
};
340340

341-
/** Raised when no card readers are connected to the system. */
341+
/** Thrown when no card readers are connected to the system. */
342342
class ScardNoReadersError : public ScardError
343343
{
344344
public:
345345
using ScardError::ScardError;
346346
};
347347

348+
/** Thrown when no card is connected to the selected reader. */
349+
class ScardNoCardError : public ScardError
350+
{
351+
public:
352+
using ScardError::ScardError;
353+
};
354+
355+
/** Thrown when the card is removed from the selected reader. */
356+
class ScardCardRemovedError : public ScardError
357+
{
358+
public:
359+
using ScardError::ScardError;
360+
};
361+
362+
/** Thrown when the card transaction fails. */
363+
class ScardTransactionFailedError : public ScardError
364+
{
365+
public:
366+
using ScardError::ScardError;
367+
};
368+
348369
} // namespace pcsc_cpp

src/Context.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#pragma once
2424

2525
#include "pcsc-cpp/pcsc-cpp.hpp"
26+
#include "pcsc-cpp/pcsc-cpp-utils.hpp"
2627

2728
#include "SCardCall.hpp"
2829

@@ -38,8 +39,9 @@ class Context
3839
{
3940
SCard(EstablishContext, SCARD_SCOPE_USER, nullptr, nullptr, &contextHandle);
4041
if (!contextHandle) {
41-
throw ScardError("Context:SCardEstablishContext: service unavailable "
42-
"(null context handle)");
42+
THROW(ScardError,
43+
"Context:SCardEstablishContext: service unavailable "
44+
"(null context handle)");
4345
}
4446
}
4547

src/SCardCall.hpp

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,29 +22,27 @@
2222

2323
#pragma once
2424

25+
#include "pcsc-cpp/pcsc-cpp.hpp"
2526
#include "pcsc-cpp/comp_winscard.hpp"
27+
#include "pcsc-cpp/pcsc-cpp-utils.hpp"
2628

2729
#include <string>
2830

2931
namespace pcsc_cpp
3032
{
3133

3234
inline std::string buildErrorMessage(const char* callerFunctionName, const char* scardFunctionName,
33-
const LONG result)
35+
const LONG result, const char* file, int line)
3436
{
35-
return std::string(callerFunctionName) + ':' + scardFunctionName + ": "
36-
+ std::to_string(result);
37+
return std::string(scardFunctionName) + " returned " + int2hexstr(result) + " in "
38+
+ removeAbsolutePathPrefix(file) + ':' + std::to_string(line) + ':' + callerFunctionName;
3739
}
3840

3941
template <typename Func, typename... Args>
4042
void SCardCall(const char* callerFunctionName, const char* file, int line,
4143
const char* scardFunctionName, Func scardFunction, Args... args)
4244
{
43-
// TODO: Add logging, for example:
44-
// DEBUG(callerFunctionName, file, line, "%s: %s (0x%08x)", scardFunctionName,
45-
// pcsc_stringify_error(result), result);
46-
(void)file;
47-
(void)line;
45+
// TODO: Add logging - or is exception error message enough?
4846
const uint32_t result = scardFunction(args...);
4947

5048
// TODO: Add more cases when needed.
@@ -53,12 +51,23 @@ void SCardCall(const char* callerFunctionName, const char* file, int line,
5351
return;
5452
case SCARD_E_NO_SERVICE:
5553
case SCARD_E_SERVICE_STOPPED:
56-
throw ScardServiceNotRunning(
57-
buildErrorMessage(callerFunctionName, scardFunctionName, result));
54+
throw ScardServiceNotRunningError(
55+
buildErrorMessage(callerFunctionName, scardFunctionName, result, file, line));
5856
case SCARD_E_NO_READERS_AVAILABLE:
59-
throw ScardNoReadersError(buildErrorMessage(callerFunctionName, scardFunctionName, result));
57+
throw ScardNoReadersError(
58+
buildErrorMessage(callerFunctionName, scardFunctionName, result, file, line));
59+
case SCARD_E_NO_SMARTCARD:
60+
throw ScardNoCardError(
61+
buildErrorMessage(callerFunctionName, scardFunctionName, result, file, line));
62+
case SCARD_W_REMOVED_CARD:
63+
throw ScardCardRemovedError(
64+
buildErrorMessage(callerFunctionName, scardFunctionName, result, file, line));
65+
case SCARD_E_NOT_TRANSACTED:
66+
throw ScardTransactionFailedError(
67+
buildErrorMessage(callerFunctionName, scardFunctionName, result, file, line));
6068
default:
61-
throw ScardError(buildErrorMessage(callerFunctionName, scardFunctionName, result));
69+
throw ScardError(
70+
buildErrorMessage(callerFunctionName, scardFunctionName, result, file, line));
6271
}
6372
}
6473

src/SmartCard.cpp

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ inline SmartCard::Protocol convertToSmartCardProtocol(const DWORD protocol)
5050
case SCARD_PROTOCOL_T1:
5151
return SmartCard::Protocol::T1;
5252
default:
53-
throw Error("Invalid card protocol: " + std::to_string(protocol));
53+
THROW(Error, "Invalid card protocol: " + std::to_string(protocol));
5454
}
5555
}
5656

@@ -63,9 +63,6 @@ std::pair<SCARDHANDLE, DWORD> connectToCard(const SCARDCONTEXT ctx, const string
6363

6464
SCard(Connect, ctx, readerName.c_str(), SCARD_SHARE_SHARED, requestedProtocol, &cardHandle,
6565
&protocolOut);
66-
// TODO: Handle SCARD_E_PROTO_MISMATCH?
67-
// TODO: if (requestedProtocol != protocolOut) throw Error? What about Protocol::AUTO?
68-
// TODO: reconnect needed under certain circumstances.
6966

7067
return std::pair<SCARDHANDLE, DWORD> {cardHandle, protocolOut};
7168
}
@@ -188,7 +185,7 @@ class CardImpl
188185
ResponseApdu toResponse(byte_vector& responseBytes, size_t responseLength) const
189186
{
190187
if (responseLength > responseBytes.size()) {
191-
throw Error("SCardTransmit: received more bytes than buffer size");
188+
THROW(Error, "SCardTransmit: received more bytes than buffer size");
192189
}
193190
responseBytes.resize(responseLength);
194191

@@ -208,12 +205,13 @@ class CardImpl
208205
case ResponseApdu::WRONG_LE_LENGTH: // See next if block.
209206
break;
210207
default:
211-
throw Error("Error response: '" + bytes2hexstr({response.sw1, response.sw2})
212-
+ "', protocol " + std::to_string(protocol()));
208+
THROW(Error,
209+
"Error response: '" + bytes2hexstr({response.sw1, response.sw2}) + "', protocol "
210+
+ std::to_string(protocol()));
213211
}
214212

215213
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
216-
throw Error("Wrong LE length (SW1=0x6C) in response, please set LE");
214+
THROW(Error, "Wrong LE length (SW1=0x6C) in response, please set LE");
217215
}
218216

219217
return response;
@@ -274,7 +272,7 @@ bool SmartCard::readerHasPinPad() const
274272
ResponseApdu SmartCard::transmit(const CommandApdu& command) const
275273
{
276274
if (!transactionInProgress) {
277-
throw std::logic_error("Call SmartCard::transmit() inside a transaction");
275+
THROW(std::logic_error, "Call SmartCard::transmit() inside a transaction");
278276
}
279277

280278
return card->transmitBytes(command.toBytes());
@@ -283,7 +281,7 @@ ResponseApdu SmartCard::transmit(const CommandApdu& command) const
283281
ResponseApdu SmartCard::transmitCTL(const CommandApdu& command, uint16_t lang, uint8_t minlen) const
284282
{
285283
if (!transactionInProgress) {
286-
throw std::logic_error("Call SmartCard::transmit() inside a transaction");
284+
THROW(std::logic_error, "Call SmartCard::transmit() inside a transaction");
287285
}
288286

289287
return card->transmitBytesCTL(command.toBytes(), lang, minlen);

src/utils.cpp

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
#include "pcsc-cpp/pcsc-cpp.hpp"
24+
#include "pcsc-cpp/pcsc-cpp-utils.hpp"
2425

2526
#include <sstream>
2627
#include <iomanip>
@@ -55,11 +56,13 @@ class UnexpectedResponseError : public Error
5556
public:
5657
explicit UnexpectedResponseError(const CommandApdu& command,
5758
const byte_vector& expectedResponseBytes,
58-
const ResponseApdu& response) :
59+
const ResponseApdu& response, const char* file, const int line,
60+
const char* callerFunctionName) :
5961
Error("transmitApduWithExpectedResponse(): Unexpected response to command '"s
6062
+ bytes2hexstr(command.toBytes()) + "' - expected '"s
61-
+ bytes2hexstr(expectedResponseBytes) + "', got '"s
62-
+ bytes2hexstr(response.toBytes()))
63+
+ bytes2hexstr(expectedResponseBytes) + "', got '"s + bytes2hexstr(response.toBytes())
64+
+ " in " + removeAbsolutePathPrefix(file) + ':' + std::to_string(line) + ':'
65+
+ callerFunctionName)
6366
{
6467
}
6568
};
@@ -95,7 +98,8 @@ void transmitApduWithExpectedResponse(const SmartCard& card, const CommandApdu&
9598
{
9699
const auto response = card.transmit(command);
97100
if (response.toBytes() != expectedResponseBytes) {
98-
throw UnexpectedResponseError(command, expectedResponseBytes, response);
101+
throw UnexpectedResponseError(command, expectedResponseBytes, response, __FILE__, __LINE__,
102+
__func__);
99103
}
100104
}
101105

@@ -111,17 +115,19 @@ size_t readDataLengthFromAsn1(const SmartCard& card)
111115
// Verify expected DER header, first byte must be SEQUENCE.
112116
if (response.data[0] != DER_SEQUENCE_TYPE_TAG) {
113117
// TODO: more specific exception
114-
throw Error("readDataLengthFromAsn1(): First byte must be SEQUENCE (0x30), but is 0x"s
115-
+ bytes2hexstr({response.data[0]}));
118+
THROW(Error,
119+
"readDataLengthFromAsn1(): First byte must be SEQUENCE (0x30), but is 0x"s
120+
+ bytes2hexstr({response.data[0]}));
116121
}
117122

118123
// TODO: support other lenghts besides 2.
119124
// Assume 2-byte length, so second byte must be 0x82.
120125
if (response.data[1] != DER_TWO_BYTE_LENGTH) {
121126
// TODO: more specific exception
122-
throw Error("readDataLengthFromAsn1(): Second byte must be two-byte length indicator "s
123-
"(0x82), but is 0x"s
124-
+ bytes2hexstr({response.data[1]}));
127+
THROW(Error,
128+
"readDataLengthFromAsn1(): Second byte must be two-byte length indicator "s
129+
"(0x82), but is 0x"s
130+
+ bytes2hexstr({response.data[1]}));
125131
}
126132

127133
// Read 2-byte length field at offset 2 and 3.
@@ -130,8 +136,9 @@ size_t readDataLengthFromAsn1(const SmartCard& card)
130136
const auto length = size_t((response.data[2] << 8) + response.data[3] + 4);
131137
if (length < 128 || length > 0x0f00) {
132138
// TODO: more specific exception
133-
throw Error("readDataLengthFromAsn1(): Unexpected data length in DER header: "s
134-
+ std::to_string(length));
139+
THROW(Error,
140+
"readDataLengthFromAsn1(): Unexpected data length in DER header: "s
141+
+ std::to_string(length));
135142
}
136143

137144
return length;
@@ -162,7 +169,7 @@ byte_vector readBinary(const SmartCard& card, const size_t length, const size_t
162169

163170
if (resultBytes.size() != length) {
164171
// TODO: more specific exception
165-
throw Error("readBinary(): Invalid length: "s + std::to_string(resultBytes.size()));
172+
THROW(Error, "readBinary(): Invalid length: "s + std::to_string(resultBytes.size()));
166173
}
167174

168175
return resultBytes;

tests/mock/test-select-card-reader-and-card.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ TEST(pcsc_cpp_test, listReadersNoService)
5959

6060
PcscMock::addReturnValueForScardFunctionCall("SCardEstablishContext", SCARD_E_NO_SERVICE);
6161

62-
EXPECT_THROW({ listReaders(); }, ScardServiceNotRunning);
62+
EXPECT_THROW({ listReaders(); }, ScardServiceNotRunningError);
6363

6464
PcscMock::reset();
6565
}

0 commit comments

Comments
 (0)