Commit 4630733b authored by Volker Krause's avatar Volker Krause
Browse files

Clean up UIC 918.3 block and U_TLAY field reading code a bit

This now all uses the new decoding utilities that do proper boundary
checks, and reduces the amount of duplicated magic numbers. This also
found two issues in the existing boundary checks, and a wrong deciding
of the RCT2 field format flag.
parent f97a51da
......@@ -220,6 +220,7 @@ ecm_generate_headers(KItinerary_Uic9183_FORWARDING_HEADERS
Uic9183Block
Uic9183Parser
Uic9183TicketLayout
Uic9183Utils
Vendor0080Block
PREFIX KItinerary
REQUIRED_HEADERS KItinerary_Uic9183_HEADERS
......
......@@ -5,6 +5,7 @@
*/
#include "uic9183block.h"
#include "uic9183utils.h"
#include "logging.h"
#include <cstring>
......@@ -26,9 +27,18 @@ Uic9183Block& Uic9183Block::operator=(const Uic9183Block&) = default;
Uic9183Block& Uic9183Block::operator=(Uic9183Block&&) = default;
Uic9183Block::Uic9183Block(const QByteArray &data, int offset)
: m_data(data)
, m_offset(offset)
: m_offset(offset)
{
if (data.size() < offset + BlockHeaderSize) {
return;
}
const auto blockSize = Uic9183Utils::readAsciiEncodedNumber(data, offset + BlockSizeOffset, BlockSizeSize);
if (data.size() < (blockSize + offset) || blockSize < BlockHeaderSize) {
return;
}
m_data = data;
}
// 6x header name
......@@ -59,10 +69,7 @@ const char* Uic9183Block::content() const
int Uic9183Block::size() const
{
if (m_data.size() < m_offset + BlockHeaderSize) {
return 0;
}
return m_data.mid(m_offset + BlockSizeOffset, BlockSizeSize).toInt();
return Uic9183Utils::readAsciiEncodedNumber(m_data, m_offset + BlockSizeOffset, BlockSizeSize);
}
int Uic9183Block::contentSize() const
......@@ -72,15 +79,12 @@ int Uic9183Block::contentSize() const
int Uic9183Block::version() const
{
if (isNull()) {
return 0;
}
return m_data.mid(m_offset + BlockVersionOffset, BlockVersionSize).toInt();
return Uic9183Utils::readAsciiEncodedNumber(m_data, m_offset + BlockVersionOffset, BlockVersionSize);
}
bool Uic9183Block::isNull() const
{
return (m_data.size() < m_offset + BlockHeaderSize) || (size() > m_data.size() + m_offset);
return m_data.isEmpty();
}
Uic9183Block Uic9183Block::nextBlock() const
......@@ -90,5 +94,5 @@ Uic9183Block Uic9183Block::nextBlock() const
QString Uic9183Block::contentText() const
{
return QString::fromUtf8(content(), contentSize());
return Uic9183Utils::readUtf8String(m_data, m_offset + BlockHeaderSize, contentSize());
}
......@@ -6,7 +6,6 @@
#include "uic9183ticketlayout.h"
#include "uic9183block.h"
#include "uic9183utils.h"
#include "logging.h"
#include <QDateTime>
......@@ -15,20 +14,6 @@
#include <cstring>
static int asciiToInt(const char *s, int size)
{
if (!s) {
return 0;
}
int v = 0;
for (int i = 0; i < size; ++i) {
v *= 10;
v += (*(s + i)) - '0';
}
return v;
}
using namespace KItinerary;
namespace KItinerary {
......@@ -41,6 +26,10 @@ public:
}
enum {
FieldHeaderSize = 13,
};
Uic9183TicketLayoutField::Uic9183TicketLayoutField() = default;
// 2x field line, number as ascii text
......@@ -50,75 +39,41 @@ Uic9183TicketLayoutField::Uic9183TicketLayoutField() = default;
// 1x field format
// 4x text length
// Nx text content
Uic9183TicketLayoutField::Uic9183TicketLayoutField(const char *data, int size)
: m_data(data)
, m_size(size)
Uic9183TicketLayoutField::Uic9183TicketLayoutField(const Uic9183Block &block, int offset)
: m_offset(offset)
{
if (size <= 13) { // too small
qCWarning(Log) << "Found too small U_TLAY field:" << size;
m_data = nullptr;
const auto remainingSize = block.contentSize() - offset;
if (remainingSize <= FieldHeaderSize) { // too small
qCWarning(Log) << "Found too small U_TLAY field:" << remainingSize;
return;
}
// invalid format
if (!std::all_of(data, data + 8, isdigit) || !std::all_of(data + 9, data + 13, isdigit)) {
// invalid format - we need to be very specific here, due to the workaround in ::next() below
if (!std::all_of(block.content() + offset, block.content() + offset + 8, isdigit)
|| !std::all_of(block.content() + offset + 9, block.content() + offset + FieldHeaderSize, isdigit)) {
qCWarning(Log) << "Found U_TLAY field with invalid format";
m_data = nullptr;
return;
}
// size is too large
if (this->size() > m_size) {
qCWarning(Log) << "Found U_TLAY field with invalid size" << this->size() << m_size;
m_data = nullptr;
const auto fieldSize = Uic9183Utils::readAsciiEncodedNumber(block, offset + 9, 4) + FieldHeaderSize;
if (fieldSize + offset > block.contentSize()) {
qCWarning(Log) << "Found U_TLAY field with invalid size" << fieldSize << block.size();
return;
}
}
bool Uic9183TicketLayoutField::isNull() const
{
return !m_data || m_size <= 13;
}
int Uic9183TicketLayoutField::size() const
{
return asciiToInt(m_data + 9, 4) + 13;
}
int Uic9183TicketLayoutField::row() const
{
return asciiToInt(m_data, 2);
}
int Uic9183TicketLayoutField::column() const
{
return asciiToInt(m_data + 2, 2);
}
int Uic9183TicketLayoutField::height() const
{
return asciiToInt(m_data + 4, 2);
m_data = block;
}
int Uic9183TicketLayoutField::width() const
{
return asciiToInt(m_data + 6, 2);
}
int Uic9183TicketLayoutField::format() const
{
return asciiToInt(m_data + 12, 1);
}
QString Uic9183TicketLayoutField::text() const
bool Uic9183TicketLayoutField::isNull() const
{
return QString::fromUtf8(m_data + 13, asciiToInt(m_data + 9, 4));
return m_data.isNull();
}
Uic9183TicketLayoutField Uic9183TicketLayoutField::next() const
{
const auto thisSize = size();
const auto remaining = m_size - size();
const auto thisSize = size() + FieldHeaderSize;
const auto remaining = m_data.contentSize() - thisSize - m_offset;
if (remaining < 0) {
return {};
}
......@@ -126,8 +81,8 @@ Uic9183TicketLayoutField Uic9183TicketLayoutField::next() const
// search for the next field
// in theory this should always trigger at i == 0, unless
// the size field is wrong, which happens unfortunately
for (int i = 0; i < remaining - 13; ++i) {
Uic9183TicketLayoutField f(m_data + thisSize + i, remaining - i);
for (int i = 0; i < remaining - FieldHeaderSize; ++i) {
Uic9183TicketLayoutField f(m_data, m_offset + thisSize + i);
if (!f.isNull()) {
return f;
}
......@@ -140,7 +95,7 @@ Uic9183TicketLayoutField Uic9183TicketLayout::firstField() const
{
const auto contentSize = d->block.contentSize();
if (contentSize > 8) {
return Uic9183TicketLayoutField(d->block.content() + 8, contentSize - 8);
return Uic9183TicketLayoutField(d->block, 8);
}
return {};
}
......
......@@ -8,6 +8,8 @@
#define KITINERARY_UIC9183TICKETLAYOUT_H
#include "kitinerary_export.h"
#include "uic9183block.h"
#include "uic9183utils.h"
#include <QExplicitlySharedDataPointer>
#include <QMetaType>
......@@ -25,31 +27,32 @@ class Uic9183TicketLayoutPrivate;
*/
class KITINERARY_EXPORT Uic9183TicketLayoutField
{
Q_GADGET
UIC_NUM_PROPERTY(row, 0 + m_offset, 2)
UIC_NUM_PROPERTY(column, 2 + m_offset, 2)
UIC_NUM_PROPERTY(height, 4 + m_offset, 2)
UIC_NUM_PROPERTY(width, 6 + m_offset, 2)
UIC_NUM_PROPERTY(format, 8 + m_offset, 1)
/** Size of the text content. */
UIC_NUM_PROPERTY(size, 9 + m_offset, 4)
UIC_STR_PROPERTY(text, 13 + m_offset, size())
public:
Uic9183TicketLayoutField();
/** Create a new U_TLAY field starting at @p data.
/** Create a new U_TLAY field starting at @p offset in @p block.
* @param size The size of the remaining U_TLAY field array (not just this field!).
*/
Uic9183TicketLayoutField(const char *data, int size);
Uic9183TicketLayoutField(const Uic9183Block &block, int offset);
/** Returns @true if this is an empty field, e.g. when iterating beyond the last one. */
bool isNull() const;
/** Size of the entire field data (not the size of the text content!). */
int size() const;
int row() const;
int column() const;
int height() const;
int width() const;
int format() const;
QString text() const;
/** Returns the next field object, or a null one if there isn't one. */
Uic9183TicketLayoutField next() const;
private:
const char *m_data = nullptr;
int m_size = 0;
Uic9183Block m_data;
int m_offset;
};
......
......@@ -8,6 +8,8 @@
#ifndef KITINERARY_UIC9183UTILS_H
#define KITINERARY_UIC9183UTILS_H
#include "kitinerary_export.h"
#include <QString>
namespace KItinerary {
......@@ -16,18 +18,19 @@ class Uic9183Block;
/*
* Low-level decoding utilities for UIC 918.3 ticket content.
* @internal
*/
namespace Uic9183Utils
{
/** Reads an ASCII encoded numerical value. */
int readAsciiEncodedNumber(const char *data, int size, int offset, int length);
int readAsciiEncodedNumber(const QByteArray &data, int offset, int length);
int readAsciiEncodedNumber(const Uic9183Block &block, int offset, int length);
KITINERARY_EXPORT int readAsciiEncodedNumber(const Uic9183Block &block, int offset, int length);
/** Reads a UTF8 encoded string. */
QString readUtf8String(const char *data, int size, int offset, int length);
QString readUtf8String(const QByteArray &data, int offset, int length);
QString readUtf8String(const Uic9183Block &block, int offset, int length);
KITINERARY_EXPORT QString readUtf8String(const Uic9183Block &block, int offset, int length);
}
#define UIC_NUM_PROPERTY(Name, Offset, Length) \
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment