#include "MessageHelper.h" #include "terminal_utils.h" #include "aes128.h" #include #include #define IUC_ASYNCHPOS_COINCOIDE_H 0x09 #define IUC_ASYNCHPOS_COINCOIDE_L 0x78 #define IUC_ASYNCHPOS_MAX_ARRAY_SIZE 1024 #define IUC_ASYNCHPOS_MAX_TX_PACKET_SIZE 300 #define IUC_ASYNCHPOS_MAX_RX_PACKET_SIZE 10000 // 17000 #define IUC_ASYNCHPOS_MIN_PACKET_SIZE 16 #define IUC_ASYNCHPOS_MIN_BASE_DATA_SIZE 32 #define IUC_ASYNCHPOS_MIN_BASE_BYTE_DATA_SIZE 16 #define IUC_ASYNCHPOS_POLYNOME 0xedb88320 // 0x04C11DB7 #define IUC_ASYNCHPOS_POLYNOME_INITIAL 0 // 0xFFFFFFFF #define IUC_ASYNCHPOS_PRINTTIMOUT 1000 #define PACKET_ID_SIZE 8 #define MAX_POSID_LENGTH 255 #define STX ((char)0x01) #define ETX1 ((char)0x02) #define ETX2 ((char)0x03) #define EOT ((char)0x04) #define ENQ ((char)0x05) #define ACK1 ((char)0x06) #define ACK2 ((char)0x07) #define DLE ((char)0x10) #define NAK ((char)0x15) #define DBG_HEADER "(" << __func__ << ":" << __LINE__ << ")" #define DBG_EMERGENCY (0) // System is unusable #define DBG_ALERT (1) // Action must be taken immediately #define DBG_CRITICAL (2) // Critical conditions #define DBG_ERROR (3) // Error conditions #define DBG_WARNING (4) // Warning conditions #define DBG_NOTICE (5) // Normal but significant conditions // Conditions that are not error conditions, but that may require special handling #define DBG_INFORMATION (6) // Informational messages // Confirmation that the program is working as expected #define DBG_DEBUG (7) // Debug-level messages // Messages that contain information normally of use only when debugging a program static int DBG_LEVEL = DBG_INFORMATION; //static int DBG_LEVEL = DBG_DEBUG; struct MessageHeader { uint8_t packetType; uint8_t packetID[PACKET_ID_SIZE]; uint8_t POSIDLength; uint8_t POSID[MAX_POSID_LENGTH]; }; MessageHelper::AsynchBillData MessageHelper::m_asyncBillData; MessageHelper::AuthorizationResult MessageHelper::m_authorizationResult; MessageHelper::MessageHelper(QString const &posID, QString const &apak) : m_posID(posID.toUtf8().constData()) , m_posIDLength(m_posID.size()) , m_messageHeaderPrefix(1 + PACKET_ID_SIZE + 1, 0x00) , m_rawPacket(IUC_ASYNCHPOS_MAX_TX_PACKET_SIZE, 0x00) { m_messageHeaderPrefix[9] = (uint8_t)m_posID.size(); for (int p = 0; p < apak.size(); p+=2) { uint8_t n = strtoul(apak.mid(p, 2).toStdString().c_str(), nullptr, 16); m_apak.push_back(n); } if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << apak << m_apak.toHex(':'); qCritical() << DBG_HEADER << m_posID.toHex(':'); qCritical() << DBG_HEADER << m_messageHeaderPrefix.toHex(':'); } } MessageHelper::MessageHelper(QByteArray const &posID, QString const &apak) : m_posID(posID) , m_posIDLength(m_posID.size()) , m_messageHeaderPrefix(1 + PACKET_ID_SIZE + 1, 0x00) , m_rawPacket(IUC_ASYNCHPOS_MAX_TX_PACKET_SIZE, 0x00) { m_messageHeaderPrefix[9] = (uint8_t)m_posID.size(); for (int p = 0; p < apak.size(); p+=2) { uint8_t n = strtoul(apak.mid(p, 2).toStdString().c_str(), nullptr, 16); m_apak.push_back(n); } if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << apak << m_apak.toHex(':'); qCritical() << DBG_HEADER << m_posID.toHex(':'); qCritical() << DBG_HEADER << m_messageHeaderPrefix.toHex(':'); } } MessageHelper::~MessageHelper() { } void MessageHelper::handleMessage(char const *pData) { #if 0 //unsigned char marker = pData[0]; // unsigned int tagNameLength = pData[1]; unsigned char tagName[IUC_ASYNCHPOS_MIN_BASE_DATA_SIZE]; // unsigned int curElementLength = 0; //unsigned char curElement[IUC_ASYNCHPOS_MIN_BASE_DATA_SIZE]; //unsigned char receiptData[IUC_ASYNCHPOS_MAX_ARRAY_SIZE + 1]; unsigned char rxBuf[20]; // unsigned char flags[129]; // unsigned char docNr[32]; //unsigned char messageType = pData[1]; unsigned int uitmp = 0; unsigned int uitmp2 = 0; iuc_asynchpos_sub_initArray(tagName, IUC_ASYNCHPOS_MIN_BASE_DATA_SIZE); //iuc_asynchpos_sub_initArray(curElement, IUC_ASYNCHPOS_MIN_BASE_DATA_SIZE); uitmp = biox_FindStrInBufferInt("LoginResult",pData) | biox_FindStrInBuffer("LOGINRESULT",pData); if (uitmp) { asynchState = 0x02; //eingeloggt /*rxBuf[0] = 0x45; rxBuf[1] = 0x00; rxBuf[2] = 0x00; vmc_SendWithBuffer(rxBuf,40,0x69,0);*/ } uitmp = biox_FindStrInBufferInt("AuthorizationStarted",pData) | biox_FindStrInBuffer("AUTHORIZATIONSTARTED",pData); if (uitmp) { iuc_asynchpos_sub_initArray(rxBuf,5); rxBuf[0] = 0x53; rxBuf[1] = 0x00; rxBuf[2] = 0x00; vmc_SendWithBuffer(rxBuf,5,0x69,0); } uitmp = biox_FindStrInBufferInt("AuthorizationResult",pData) | biox_FindStrInBuffer("AUTHORIZATIONRESULT",pData); if (uitmp) { asynchState = 0x03; //successfully authorized iuc_asynchpos_sub_clear_message(0x00); uitmp = biox_FindStrInBufferInt("ID",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.id, 0, pData[uitmp + 1]); uitmp = biox_FindStrInBufferInt("DocNr",pData) | biox_FindStrInBuffer("DOCNR",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.docNr, 0, pData[uitmp + 1]); //uitmp = biox_FindStrInBuffer("Result",pData) | biox_FindStrInBuffer("RESULT",pData); //biox_CopyBlock(pData, uitmp + 2, asynchBill.result, 0, pData[uitmp + 1]); brd_SendDiagStr("->IUC ASYNCHPOS message ID: ",0); brd_SendDiagBlock(asynchBill.id,1,36); /*if (iuc_asynch_PrintControl) { brd_SendDiagStr("->IUC ASYNCHPOS print data send AUTH: ",1); vmc_SendWithBuffer(receiptData,IUC_ASYNCHPOS_MAX_ARRAY_SIZE,0x69,0); iuc_asynch_PrintControl = 0x00; //biox_delay_ms(750); } else { brd_SendDiagStr("->IUC ASYNCHPOS sending authorization: ",1); iuc_asynch_PrintControl = 0x01; }*/ uitmp = biox_FindStrInBufferInt("ERROR",pData); if (!uitmp/*biox_FindStrInBuffer("OK",pData) | biox_FindStrInBuffer("Approved",pData) | biox_FindStrInBuffer("APPROVED",asynchBill.result)*/) { iuc_asynch_PrintControl |= 0xF0; /*uitmp = biox_FindStrInBufferInt("Amount",pData) | biox_FindStrInBufferInt("AMOUNT",pData); //if (pData[uitmp + 1] <= 10) { biox_CopyBlock(pData, uitmp + 2, asynchBill.amount, 0, pData[uitmp + 1]); //} else { //biox_CopyBlock(pData, uitmp + 2, asynchBill.amount, 0, 10); //}*/ //uitmp = biox_FindStrInBuffer("Token",pData); //biox_CopyBlock(pData, uitmp + 2, asynchBill.token, 0, pData[uitmp + 1]); /*uitmp = biox_FindStrInBufferInt("Authcode",pData) | biox_FindStrInBufferInt("AUTHCODE",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.authCode, 0, pData[uitmp + 1]); uitmp = biox_FindStrInBufferInt("RRN",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.rrn, 0, pData[uitmp + 1]); uitmp = biox_FindStrInBufferInt("STAN",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.stan, 0, pData[uitmp + 1]); uitmp = biox_FindStrInBufferInt("CardType",pData) | biox_FindStrInBufferInt("CARDTYPE",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.cardtype, 0, pData[uitmp + 1]);*/ asynchState = 0x04; brd_SendDiagStr("->IUC ASYNCHPOS authorization confirmed.",1); /*iuc_asynchpos_sub_initArray(rxBuf,20); rxBuf[0] = 0x45; rxBuf[1] = 0x00; rxBuf[2] = 0x00; vmc_SendWithBuffer(rxBuf,20,0x69,0);*/ //iuc_asynchpos_resetBuffers(0x01); //iuc_asynchpos_command_close_Document(); } else { iuc_asynch_PrintControl |= 0xF0; //uitmp = biox_FindStrInBufferInt("ErrCode",pData) | biox_FindStrInBufferInt("ERRCODE",pData); //biox_CopyBlock(pData, uitmp + 2, asynchBill.errCode, 0, pData[uitmp + 1]); brd_SendDiagStr("->IUC ASYNCHPOS authorization failed.",1); /*iuc_asynchpos_sub_initArray(rxBuf,20); rxBuf[0] = 0x45; rxBuf[1] = 0xFF; biox_CopyBlock(pData, uitmp + 2, rxBuf, 2, pData[uitmp + 1]); vmc_SendWithBuffer(rxBuf,20,0x69,0);*/ iuc_asynch_keepAlive = 0x00; //VendRequest=0; } } uitmp = biox_FindStrInBufferInt("PrintReceipt",pData) | biox_FindStrInBufferInt("PRINTRECEIPT",pData); if (uitmp) { asynchState = 0x03; //Customer receipt recieved //iuc_asynchpos_sub_initArray(flags,129); //uitmp = biox_FindStrInBufferInt("Flag",pData) | biox_FindStrInBufferInt("FLAG",pData) | biox_FindStrInBufferInt("Flags",pData) | biox_FindStrInBufferInt("FLAGS",pData); /*if (uitmp) { biox_CopyBlock(pData, uitmp + 2, flags, 0, pData[uitmp + 1]); uitmp = biox_FindStrInBufferInt("CD",flags) | biox_FindStrInBufferInt("LR",flags); }*/ /*iuc_asynchpos_sub_clear_message(0x00); uitmp = biox_FindStrInBufferInt("ID",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.id, 0, pData[uitmp + 1]); uitmp = biox_FindStrInBufferInt("DocNr",pData) | biox_FindStrInBuffer("DOCNR",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.docNr, 0, pData[uitmp + 1]);*/ iuc_asynchpos_sub_initArray(asynchBill.printId,129); uitmp = biox_FindStrInBufferInt("ID",pData); biox_CopyBlock(pData, uitmp + 2, asynchBill.printId, 0, pData[uitmp + 1]); //if(asynchState == 0x02/* && uitmp biox_FindStrInBufferInt("CD",flags) || biox_FindStrInBufferInt("LR",flags)*/) { if((!iuc_asynch_PRNrecieved) && (biox_FindStrInBufferInt("CD",pData) || biox_FindStrInBufferInt("LR",pData)) ) { iuc_asynch_PRNrecieved = 0x01; iuc_asynchpos_sub_initArray(receiptData,IUC_ASYNCHPOS_MAX_ARRAY_SIZE + 1); uitmp = /*biox_FindStrInBuffer("ReceiptText",pData) | */biox_FindStrInBufferInt("RECEIPTTEXT",pData); uitmp2 = (pData[uitmp] * 256) + pData[uitmp + 1]; //if (uitmp2 <= IUC_ASYNCHPOS_MAX_ARRAY_SIZE) { if (uitmp2 > IUC_ASYNCHPOS_MAX_ARRAY_SIZE) { uitmp2 = IUC_ASYNCHPOS_MAX_ARRAY_SIZE; brd_SendDiagStr("->IUC ASYNCHPOS receipt: ERROR. Receipt too large! Cutting off.",1); /*receiptData[0] = 0x50; biox_CopyBlock(pData, uitmp + 2, receiptData, 1, uitmp2/*IUC_ASYNCHPOS_MAX_ARRAY_SIZE*); //uitmp += IUC_ASYNCHPOS_MAX_ARRAY_SIZE; //uitmp2 -= IUC_ASYNCHPOS_MAX_ARRAY_SIZE; brd_SendDiagStr("->IUC ASYNCHPOS receipt: ",0); brd_SendDiagBlock(receiptData,1,IUC_ASYNCHPOS_MAX_ARRAY_SIZE); iuc_asynch_PrintControl |= 0x0F;*/ /*if (iuc_asynch_PrintControl) { brd_SendDiagStr("->IUC ASYNCHPOS print data send: ",1); vmc_SendWithBuffer(receiptData,IUC_ASYNCHPOS_MAX_ARRAY_SIZE,0x69,0); iuc_asynch_PrintControl = 0x00; } else { brd_SendDiagStr("->IUC ASYNCHPOS print data stored: ",1); iuc_asynch_PrintControl = 0x01; }*/ //biox_delay_ms(750); //iuc_asynchpos_resetBuffers(0x01); //iuc_asynchpos_command_close_Document(); //iuc_asynchpos_resetBuffers(0x02); //iuc_asynchpos_command_print_Result(0x01); } receiptData[0] = 0x50; biox_CopyBlock(pData, uitmp + 2, receiptData, 1, uitmp2/*IUC_ASYNCHPOS_MAX_ARRAY_SIZE*/); brd_SendDiagStr("->IUC ASYNCHPOS receipt: ",0); brd_SendDiagBlock(receiptData,1,IUC_ASYNCHPOS_MAX_ARRAY_SIZE); iuc_asynch_PrintControl |= 0x0F; /* else { //receiptData[0] = 0x50; //iuc_asynchpos_sub_initZero(receiptData,1); brd_SendDiagStr("->IUC ASYNCHPOS receipt: ERROR. Receipt too large!",1); iuc_asynch_PrintControl |= 0x0E; } */ /* else { //iuc_asynchpos_resetBuffers(0x02); iuc_asynchpos_command_print_Result(0x00); }*/ } //++iuc_print_counter; //if(asynchState == 0x04 && iuc_asynch_PrintControl == 0) { //iuc_asynchpos_resetBuffers(0x01); //iuc_asynchpos_resetBuffers(0x00); //iuc_asynchpos_command_print_Result(0x01); /*} /else { //iuc_asynchpos_resetBuffers(0x02); iuc_asynchpos_command_print_Result(0x01); }*/ //iuc_asynchpos_command_print_Result(0x01); /*while (uitmp2 > IUC_ASYNCHPOS_MAX_ARRAY_SIZE) { biox_CopyBlock(pData, uitmp + 2, receiptData, 0, IUC_ASYNCHPOS_MAX_ARRAY_SIZE); uitmp += IUC_ASYNCHPOS_MAX_ARRAY_SIZE; uitmp2 -= IUC_ASYNCHPOS_MAX_ARRAY_SIZE; vmc_SendWithBuffer(receiptData,IUC_ASYNCHPOS_MAX_ARRAY_SIZE,0x69,0); } //Rest des Packets biox_CopyBlock(pData, uitmp + 2, receiptData, 0, uitmp2); vmc_SendWithBuffer(receiptData,uitmp2,0x69,0); //iuc_asynchpos_resetBuffers(0x02); iuc_asynchpos_command_print_Result(0x01);*/ } uitmp = biox_FindStrInBufferInt("VoidResult",pData) | biox_FindStrInBufferInt("VOIDRESULT",pData); if (uitmp) { asynchState = 0x01; //There was a cancel. Relogin and try again. uitmp = biox_FindStrInBufferInt("ERROR",pData); if (uitmp) { rxBuf[0] = 0x45; rxBuf[1] = 0x56; rxBuf[2] = 0x45; vmc_SendWithBuffer(rxBuf,3,0x69,0); } uitmp = biox_FindStrInBufferInt("REFUND",pData); if (uitmp) { rxBuf[0] = 0x45; rxBuf[1] = 0x56; rxBuf[2] = 0x52; vmc_SendWithBuffer(rxBuf,3,0x69,0); //TODO refund bill here. Should not trigger, but it might. } } uitmp = biox_FindStrInBufferInt("DocClosed",pData); if (uitmp) { asynchState = 0x01; //Transaction successful //if (VendRequest) GWstate.VendRequest=0; } #endif } void MessageHelper::handleCommand(AsyncPosCommand command, char status) { //r - registration, a - authorization, c - cancel, s - storno, k - kassenschnitt #if 0 UCHAR tempBuf[3]; UCHAR rxBuf[8]; // UINT uitemp = 0; UINT uitmp= 700; tempBuf[0] = 0x00; tempBuf[1] = 0x00; tempBuf[2] = 0x00; timeHoldISMAS = GWglobTime.SecOfDay; iuc_asynchpos_sub_initArray(rxBuf,8); //iuc_asynchpos_resetBuffers(0x00); switch (command) { case 0x72: //registration //iuc_asynchpos_init(); asynchState = 0x01; iuc_asynchpos_command_Login(); GWstate.VendRequest=1; break; case 0x61: //authorisation iuc_asynch_keepAlive = 0x01; iuc_asynch_PrintControl = 0; iuc_asynchpos_sub_clear_message(0x01); //VendRequest=1; iuc_asynchpos_resetBuffers(0x00); ///#ifdef IUC_ASYCHNPOS_TESTMODE //iuc_asynchpos_command_authorize(uitmp); //#else iuc_asynchpos_command_authorize(Vend.Amount); //#endif break; case 0x63: //cancel case 0x73: //storno /*if (asynchState <= 0x02 != 0) { //Authorization result recieved? iuc_asynchpos_command_cancel_authorize(); } else {*/ iuc_asynchpos_command_close_Document(0x01); //} iuc_asynch_keepAlive = 0x00; //VendRequest=0; break; case 0x6B: //kassenschnitt iuc_asynchpos_command_Logout(); break; case 0x62: //get last bill //iuc_zvt_getLastBill(tempBuf); break; case 0x01: iuc_asynchpos_command_close_Document(0x00); iuc_asynch_keepAlive = 0x00; //VendRequest=0; break; case 0x70: iuc_asynchpos_command_ping_terminal(); break; default: break; } #endif } void MessageHelper::createRawPacket(PacketType packetType, QByteArray const &encryptedPacketID, QByteArray const &message) { if (createMessageHeaderPrefix(packetType, encryptedPacketID)) { QByteArray ba(m_messageHeaderPrefix); ba = ba.append(m_posID); ba = ba.append(message); uint16_t const size = ba.size(); ba.push_front((char)size); ba.push_front((char)(size >> 8)); m_rawPacket = ba; } } bool MessageHelper::setMessageHeaderPacketType(PacketType packetType) { switch (packetType) { case PacketType::POS_ECR: case PacketType::MESSAGE_RECEIVED_POSITIVE_ACK: case PacketType::MESSAGE_RECEIVED_NEGATIVE_ACK: m_messageHeaderPrefix[0] = (uint8_t)packetType; break; default: return false; } return true; } bool MessageHelper::createMessageHeaderPrefix(PacketType packetType, QByteArray const &encryptedPacketID) { if (encryptedPacketID.size() == PACKET_ID_SIZE) { if (setMessageHeaderPacketType(packetType)) { for (int i = 1; i <= 8; ++i) { m_messageHeaderPrefix[i] = (uint8_t)encryptedPacketID[i-1]; } if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << m_messageHeaderPrefix.toHex(':'); } return true; } } return false; } QByteArray const &MessageHelper::generateUniqueTransactionID(QString const &machineNr, QString const &customerNr) { // TODO: wieder entfernen QDateTime dt(QDateTime::fromString("2024-06-18T12:00:00", Qt::ISODate)); uint64_t const transID = MessageHelper::secsSinceJan2017(dt); m_uniqueTransactionID.clear(); m_uniqueTransactionID = m_uniqueTransactionID.append(QByteArray(std::to_string(transID).c_str(), 9).rightJustified(10, '0')); m_uniqueTransactionID = m_uniqueTransactionID.append(QByteArray(machineNr.toStdString().c_str()).rightJustified(5, '0')); m_uniqueTransactionID = m_uniqueTransactionID.append(QByteArray(customerNr.toStdString().c_str()).rightJustified(4, '0')); return m_uniqueTransactionID; } // actual payment. amount to pay is known. void MessageHelper::createAuthorizeMessage() { m_authorizeMessage.clear(); m_authorizeMessage.push_back((char)0x89); // 9 in 0x89 is the size m_authorizeMessage = m_authorizeMessage.append(QByteArray("Authorize")); QString const &price= m_price.setNum(1000); m_authorizeMessage.push_back((char)0x06); m_authorizeMessage = m_authorizeMessage.append(QByteArray("Amount")); m_authorizeMessage.push_back((char)0x00); m_authorizeMessage.push_back((char)price.size()); m_authorizeMessage = m_authorizeMessage.append(QByteArray(price.toStdString().c_str())); m_authorizeMessage.push_back((char)0x04); m_authorizeMessage = m_authorizeMessage.append(QByteArray("Cash")); m_authorizeMessage.push_back((char)0x00); m_authorizeMessage.push_back((char)0x01); m_authorizeMessage.push_back((char)0x30); m_authorizeMessage.push_back((char)0x08); m_authorizeMessage = m_authorizeMessage.append(QByteArray("Currency")); m_authorizeMessage.push_back((char)0x00); m_authorizeMessage.push_back((char)0x03); m_authorizeMessage = m_authorizeMessage.append(QByteArray("978")); m_authorizeMessage.push_back((char)0x05); m_authorizeMessage = m_authorizeMessage.append(QByteArray("DocNr")); m_authorizeMessage.push_back((char)0x00); m_authorizeMessage.push_back((char)0x13); m_authorizeMessage = m_authorizeMessage.append(generateUniqueTransactionID("1000", "100")); m_authorizeMessage.push_back((char)0x04); m_authorizeMessage = m_authorizeMessage.append(QByteArray("Time")); m_authorizeMessage.push_back((char)0x00); m_authorizeMessage.push_back((char)0x13); QDateTime current = QDateTime::currentDateTime(); // TODO: wieder entfernen current.setTime(QTime(12, 0, 0)); current.setDate(QDate(2024, 6, 18)); QByteArray time(current.toString(Qt::ISODate).toStdString().c_str()); time[10] = ' '; m_authorizeMessage = m_authorizeMessage.append(QByteArray(time)); m_authorizeMessage.push_back((char)0x04); m_authorizeMessage = m_authorizeMessage.append(QByteArray("Lang")); m_authorizeMessage.push_back((char)0x00); m_authorizeMessage.push_back((char)0x02); m_authorizeMessage = m_authorizeMessage.append(QByteArray("lt")); m_authorizeMessage.push_back((char)0x00); } void MessageHelper::createCancelAuthorizeMessage() { m_cancelAuthorizeMessage.clear(); m_cancelAuthorizeMessage.push_back((char)0x96); // 0x80 + 0x16 m_cancelAuthorizeMessage = m_cancelAuthorizeMessage.append(QByteArray("AuthorizationCancelled")); m_cancelAuthorizeMessage.push_back((char)0x02); m_cancelAuthorizeMessage = m_authorizeMessage.append(QByteArray("ID")); m_cancelAuthorizeMessage.push_back((char)0x00); m_cancelAuthorizeMessage.push_back((char)QByteArray(m_asyncBillData.id).size()); m_cancelAuthorizeMessage = m_cancelAuthorizeMessage.append(QByteArray(m_asyncBillData.id)); m_cancelAuthorizeMessage.push_back((char)0x00); } void MessageHelper::createPingMessage() { m_pingMessage.clear(); m_pingMessage.push_back((char)0x84); // 4 in 0x84 is the size m_pingMessage = m_pingMessage.append(QByteArray("Ping")); m_pingMessage.push_back((char)0x04); m_pingMessage = m_authorizeMessage.append(QByteArray("Time")); m_pingMessage.push_back((char)0x00); m_pingMessage.push_back((char)0x13); QDateTime current = QDateTime::currentDateTime(); // TODO: wieder entfernen current.setTime(QTime(12, 0, 0)); current.setDate(QDate(2024, 6, 18)); QByteArray time(current.toString(Qt::ISODate).toStdString().c_str()); time[10] = ' '; m_pingMessage = m_pingMessage.append(QByteArray(time)); m_pingMessage.push_back((char)0x00); } void MessageHelper::createCloseDocumentMessage(bool storno) { m_closeDocumentMessage.clear(); m_closeDocumentMessage.push_back((char)0x89); // 9 in 0x89 is the size m_closeDocumentMessage = m_closeDocumentMessage.append(QByteArray("DocClosed")); m_closeDocumentMessage.push_back((char)0x05); m_closeDocumentMessage = m_closeDocumentMessage.append(QByteArray("DocNr")); uint16_t const docNrSize = m_uniqueTransactionID.size(); m_closeDocumentMessage.push_back((char)(docNrSize >> 8)); m_closeDocumentMessage.push_back((char)(docNrSize)); m_closeDocumentMessage = m_closeDocumentMessage.append(m_uniqueTransactionID); if (!storno) { m_closeDocumentMessage.push_back((char)0x06); m_closeDocumentMessage = m_closeDocumentMessage.append(QByteArray("AuthID")); QByteArray ba(m_authorizationResult.m_id.toStdString().c_str()); uint16_t const authIdSize = ba.size(); m_closeDocumentMessage.push_back((char)(authIdSize >> 8)); m_closeDocumentMessage.push_back((char)(authIdSize)); m_closeDocumentMessage = m_closeDocumentMessage.append(ba); } m_closeDocumentMessage.push_back((char)0x00); } void MessageHelper::createPrintResultMessage() { m_printResultMessage.clear(); } uint32_t MessageHelper::secsSinceJan2017(QDateTime const &dt) { return QDateTime(QDateTime::fromString("2017-01-01T00:00:00", Qt::ISODate)).secsTo(dt); } void MessageHelper::createLoginMessage() { m_loginMessage.clear(); m_loginMessage.push_back((char)0x85); // 5 in 0x85 is the size m_loginMessage = m_loginMessage.append(QByteArray("Login")); m_loginMessage.push_back((char)0x04); m_loginMessage = m_loginMessage.append(QByteArray("Time")); m_loginMessage.push_back((char)0x00); m_loginMessage.push_back((char)0x13); QDateTime current = QDateTime::currentDateTime(); // TODO: wieder entfernen current.setTime(QTime(12, 0, 0)); current.setDate(QDate(2024, 6, 18)); QByteArray time(current.toString(Qt::ISODate).toStdString().c_str()); time[10] = ' '; m_loginMessage = m_loginMessage.append(time); m_loginMessage.push_back((char)0x05); m_loginMessage = m_loginMessage.append(QByteArray("Flags")); m_loginMessage.push_back((char)0x00); m_loginMessage.push_back((char)0x06); m_loginMessage = m_loginMessage.append(QByteArray("AP3|LR")); m_loginMessage.push_back((char)0x00); if (DBG_LEVEL >= DBG_INFORMATION) { qCritical() << DBG_HEADER << "loginMessage" << m_loginMessage.toHex(':'); } } void MessageHelper::createLogoutMessage() { m_logoutMessage.clear(); m_logoutMessage.push_back((char)0x86); // 6 in 0x86 is the size m_logoutMessage = m_loginMessage.append(QByteArray("Logout")); m_loginMessage.push_back((char)0x04); m_loginMessage = m_loginMessage.append(QByteArray("Time")); m_loginMessage.push_back((char)0x00); m_loginMessage.push_back((char)0x13); QDateTime current = QDateTime::currentDateTime(); // TODO: wieder entfernen current.setTime(QTime(12, 0, 0)); current.setDate(QDate(2024, 6, 18)); QByteArray time(current.toString(Qt::ISODate).toStdString().c_str()); time[10] = ' '; m_logoutMessage = m_logoutMessage.append(time); m_loginMessage.push_back((char)0x00); if (DBG_LEVEL >= DBG_INFORMATION) { qCritical() << DBG_HEADER << "loginMessage" << m_logoutMessage.toHex(':'); } } QByteArrayList const &MessageHelper::createMessageChunksToSend(AsyncPosCommand cmd, char etx) { QByteArray encryptedPacketID(QByteArray("\x01\x02\x03\x04\x05\x06\x07\x08")); m_messageChunkList.clear(); switch (cmd) { case (int)MessageHelper::AsyncPosCommand::LOGIN: createLoginMessage(); createRawPacket(PacketType::POS_ECR, encryptedPacketID, m_loginMessage); break; case (int)MessageHelper::AsyncPosCommand::LOGOUT: createLogoutMessage(); createRawPacket(PacketType::POS_ECR, encryptedPacketID, m_logoutMessage); break; case (int)MessageHelper::AsyncPosCommand::AUTHORIZE: createAuthorizeMessage(); createRawPacket(PacketType::POS_ECR, encryptedPacketID, m_authorizeMessage); break; case (int)MessageHelper::AsyncPosCommand::CLOSE_DOCUMENT: createCloseDocumentMessage(); // actung: hier default parameter createRawPacket(PacketType::POS_ECR, encryptedPacketID, m_closeDocumentMessage); break; default:; } if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << m_rawPacket.toHex(':'); } QByteArray const &ba = m_rawPacket.mid(11); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << ba.toHex(':'); } // calculate crc32 on message starting from (including) POSID length uint32_t crc = TU::crc32(ba); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << "crc32" << hex << crc; } unsigned char cipherText[256]; memset(cipherText, 0, sizeof(cipherText)); // XOR crc32-value (4 bytes) with APAK starting from left // (rest of APAK untouched) QByteArray clearText(m_apak); clearText[0] = clearText[0] ^ ((uint8_t)(crc >> 24)); clearText[1] = clearText[1] ^ ((uint8_t)(crc >> 16)); clearText[2] = clearText[2] ^ ((uint8_t)(crc >> 8)); clearText[3] = clearText[3] ^ ((uint8_t)(crc >> 0)); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << "clearText" << clearText.toHex(); } // encrypt XOR result with APAK using AES129, ECB mode aes_encrypt((uint8_t *)clearText.data(), (uint8_t *)cipherText, (uint8_t *)m_apak.toStdString().c_str()); // 8 left bytes of encryption result is signature (Packet ID) encryptedPacketID = QByteArray((const char *)cipherText, 8); if (DBG_LEVEL >= DBG_INFORMATION) { qCritical() << DBG_HEADER << "cipherText (new PacketID)" << encryptedPacketID.toHex(':'); } // insert PacketID in packet if (insertEncryptedPacketID(encryptedPacketID)) { // build chunks to be sent over serial line int const chunks = m_rawPacket.size() / IUC_ASYNCHPOS_MIN_PACKET_SIZE; if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << "nr of chunks" << chunks; } int i = 0; for (; i < chunks; ++i) { QByteArray messageChunk = m_rawPacket.mid(IUC_ASYNCHPOS_MIN_PACKET_SIZE*i, IUC_ASYNCHPOS_MIN_PACKET_SIZE); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << i << "unmasked" << messageChunk.toHex(':'); } messageChunk = MessageHelper::mask(messageChunk); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << i << " masked" << messageChunk.toHex(':'); } messageChunk.push_back(etx == ACK1 ? ETX2 : ETX1); // etx must be ACK1 or ACK2 char const lrc = TU::lrc(messageChunk); messageChunk.push_back(lrc); messageChunk.push_front(STX); if (DBG_LEVEL >= DBG_INFORMATION) { qCritical() << DBG_HEADER << "chunk to send" << messageChunk.toHex(':'); } m_messageChunkList += messageChunk; } int const rest = m_rawPacket.size() % IUC_ASYNCHPOS_MIN_PACKET_SIZE; if (rest) { QByteArray messageChunk = m_rawPacket.mid(IUC_ASYNCHPOS_MIN_PACKET_SIZE*chunks, rest); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << i << "unmasked" << messageChunk.toHex(':'); } messageChunk = mask(messageChunk); if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << i << " masked" << messageChunk.toHex(':'); } messageChunk.push_back(etx == ACK1 ? ETX2 : ETX1); // etx must be ACK1 or ACK2 char const lrc = TU::lrc(messageChunk); messageChunk.push_back(lrc); messageChunk.push_front(STX); if (DBG_LEVEL >= DBG_INFORMATION) { qCritical() << DBG_HEADER << "chunk to send" << messageChunk.toHex(':'); } m_messageChunkList += messageChunk; } } return m_messageChunkList; } QByteArrayList MessageHelper::createLoginMessageChunksToSend(char etx) { return createMessageChunksToSend(AsyncPosCommand::LOGIN, etx); } QByteArrayList MessageHelper::createLogoutMessageChunksToSend(char etx) { return createMessageChunksToSend(AsyncPosCommand::LOGOUT, etx); } QByteArrayList MessageHelper::createAuthorizeMessageChunksToSend(char etx) { return createMessageChunksToSend(AsyncPosCommand::AUTHORIZE, etx); } QByteArrayList MessageHelper::createCloseDocumentMessageChunksToSend(char etx) { return createMessageChunksToSend(AsyncPosCommand::CLOSE_DOCUMENT, etx); } bool MessageHelper::insertEncryptedPacketID(QByteArray const &encryptedPacketID) { if (encryptedPacketID.size() == PACKET_ID_SIZE) { // m_rawPacket has already full length for (int i = 0; i < PACKET_ID_SIZE; ++i) { m_messageHeaderPrefix[i+1] = encryptedPacketID[i]; m_rawPacket[i+3] = encryptedPacketID[i]; } if (DBG_LEVEL >= DBG_DEBUG) { qCritical() << DBG_HEADER << m_messageHeaderPrefix.toHex(':'); qCritical() << DBG_HEADER << m_rawPacket.toHex(':'); } return true; } return false; } QByteArray const &MessageHelper::mask(QByteArray &messageChunk) { QByteArray ba; for (int i = 0; i < messageChunk.size(); ++i) { char const c = messageChunk[i]; switch(c) { case STX: __attribute__((fallthrough)); case ETX1: __attribute__((fallthrough)); case ETX2: __attribute__((fallthrough)); case EOT: __attribute__((fallthrough)); case ENQ: __attribute__((fallthrough)); case ACK1: __attribute__((fallthrough)); case ACK2: __attribute__((fallthrough)); case DLE: __attribute__((fallthrough)); case NAK: ba.push_back(char(DLE)); ba.push_back(c + 0x30); break; default: ba.push_back(c); } } messageChunk = ba; return messageChunk; } QByteArray const &MessageHelper::unMask(QByteArray &messageChunk) { QByteArray ba; for (int i = 0; i < messageChunk.size(); ++i) { char c = messageChunk[i]; if (c == (char)0x10) /* DEL */ { if ((i+1) < messageChunk.size()) { c = messageChunk[i+1] - (char)0x30; } } ba.push_back(c); } messageChunk = ba; return messageChunk; }