diff --git a/plugins/channelrx/demodm17/m17demodprocessor.cpp b/plugins/channelrx/demodm17/m17demodprocessor.cpp index c0bc4c2f4..ba264ced3 100644 --- a/plugins/channelrx/demodm17/m17demodprocessor.cpp +++ b/plugins/channelrx/demodm17/m17demodprocessor.cpp @@ -390,7 +390,7 @@ bool M17DemodProcessor::decode_packet(modemm17::M17FrameDecoder::packet_buffer_t << " Via: " << ax25.m_via << " Type: " << ax25.m_type << " PID: " << ax25.m_pid - << " Data: " << ax25.m_dataASCII; + << " Data: " << QString::fromUtf8(ax25.m_data); if (m_demodInputMessageQueue) { @@ -402,7 +402,7 @@ bool M17DemodProcessor::decode_packet(modemm17::M17FrameDecoder::packet_buffer_t ax25.m_via, ax25.m_type, ax25.m_pid, - ax25.m_dataASCII + ax25.m_data ); msg->getPacket() = packet; m_demodInputMessageQueue->push(msg); diff --git a/plugins/channelrx/demodpacket/packetdemod.cpp b/plugins/channelrx/demodpacket/packetdemod.cpp index 926700635..1e58a7c47 100644 --- a/plugins/channelrx/demodpacket/packetdemod.cpp +++ b/plugins/channelrx/demodpacket/packetdemod.cpp @@ -216,7 +216,7 @@ bool PacketDemod::handleMessage(const Message& cmd) << "\"" << ax25.m_via << "\"," << ax25.m_type << "," << ax25.m_pid << "," - << "\"" << ax25.m_dataASCII << "\"," + << "\"" << QString::fromUtf8(ax25.m_data) << "\"," << "\"" << ax25.m_dataHex << "\"\n"; } else @@ -348,7 +348,7 @@ void PacketDemod::applySettings(const PacketDemodSettings& settings, bool force) if (newFile) { // Write header - m_logStream << "Date,Time,Data,From,To,Via,Type,PID,Data ASCII,Data Hex\n"; + m_logStream << "Date,Time,Data,From,To,Via,Type,PID,Data UTF-8,Data Hex\n"; } } else diff --git a/plugins/channelrx/demodpacket/packetdemodgui.cpp b/plugins/channelrx/demodpacket/packetdemodgui.cpp index b013e9410..26256648a 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.cpp +++ b/plugins/channelrx/demodpacket/packetdemodgui.cpp @@ -59,7 +59,7 @@ void PacketDemodGUI::resizeTable() ui->packets->setItem(row, PACKET_COL_VIA, new QTableWidgetItem("123456-15-")); ui->packets->setItem(row, PACKET_COL_TYPE, new QTableWidgetItem("Type-")); ui->packets->setItem(row, PACKET_COL_PID, new QTableWidgetItem("PID-")); - ui->packets->setItem(row, PACKET_COL_DATA_ASCII, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); + ui->packets->setItem(row, PACKET_COL_DATA_STRING, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); ui->packets->setItem(row, PACKET_COL_DATA_HEX, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); ui->packets->resizeColumnsToContents(); ui->packets->removeRow(row); @@ -168,7 +168,7 @@ void PacketDemodGUI::packetReceived(const QByteArray& packet, QDateTime dateTime QTableWidgetItem *viaItem = new QTableWidgetItem(); QTableWidgetItem *typeItem = new QTableWidgetItem(); QTableWidgetItem *pidItem = new QTableWidgetItem(); - QTableWidgetItem *dataASCIIItem = new QTableWidgetItem(); + QTableWidgetItem *dataStringItem = new QTableWidgetItem(); QTableWidgetItem *dataHexItem = new QTableWidgetItem(); ui->packets->setItem(row, PACKET_COL_DATE, dateItem); ui->packets->setItem(row, PACKET_COL_TIME, timeItem); @@ -177,7 +177,7 @@ void PacketDemodGUI::packetReceived(const QByteArray& packet, QDateTime dateTime ui->packets->setItem(row, PACKET_COL_VIA, viaItem); ui->packets->setItem(row, PACKET_COL_TYPE, typeItem); ui->packets->setItem(row, PACKET_COL_PID, pidItem); - ui->packets->setItem(row, PACKET_COL_DATA_ASCII, dataASCIIItem); + ui->packets->setItem(row, PACKET_COL_DATA_STRING, dataStringItem); ui->packets->setItem(row, PACKET_COL_DATA_HEX, dataHexItem); dateItem->setText(dateTime.date().toString()); timeItem->setText(dateTime.time().toString()); @@ -186,7 +186,7 @@ void PacketDemodGUI::packetReceived(const QByteArray& packet, QDateTime dateTime viaItem->setText(ax25.m_via); typeItem->setText(ax25.m_type); pidItem->setText(ax25.m_pid); - dataASCIIItem->setText(ax25.m_dataASCII); + dataStringItem->setText(QString::fromUtf8(ax25.m_data)); // Should possibly support different encodings here. PacketMod uses UTF8. dataHexItem->setText(ax25.m_dataHex); filterRow(row); ui->packets->setSortingEnabled(true); diff --git a/plugins/channelrx/demodpacket/packetdemodgui.h b/plugins/channelrx/demodpacket/packetdemodgui.h index b89fd5229..b4a0c0d3c 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.h +++ b/plugins/channelrx/demodpacket/packetdemodgui.h @@ -116,7 +116,7 @@ private: PACKET_COL_VIA, PACKET_COL_TYPE, PACKET_COL_PID, - PACKET_COL_DATA_ASCII, + PACKET_COL_DATA_STRING, PACKET_COL_DATA_HEX }; diff --git a/plugins/channelrx/demodpacket/packetdemodgui.ui b/plugins/channelrx/demodpacket/packetdemodgui.ui index f81552e44..126996967 100644 --- a/plugins/channelrx/demodpacket/packetdemodgui.ui +++ b/plugins/channelrx/demodpacket/packetdemodgui.ui @@ -736,10 +736,10 @@ - Data (ASCII) + Data - Packet data as ASCII + Packet data as UTF-8 character string @@ -757,9 +757,10 @@ - ButtonSwitch - QToolButton -
gui/buttonswitch.h
+ RollupContents + QWidget +
gui/rollupcontents.h
+ 1
ValueDialZ @@ -768,10 +769,9 @@ 1 - RollupContents - QWidget -
gui/rollupcontents.h
- 1 + ButtonSwitch + QToolButton +
gui/buttonswitch.h
LevelMeterSignalDB diff --git a/plugins/channelrx/demodpacket/readme.md b/plugins/channelrx/demodpacket/readme.md index 0747558cb..57e4176a7 100644 --- a/plugins/channelrx/demodpacket/readme.md +++ b/plugins/channelrx/demodpacket/readme.md @@ -92,5 +92,5 @@ The received packets table displays the contents of the packets that have been r * Via - List of addresses of repeaters the packet has passed through or directed via. * Type - The AX.25 frame type. * PID - Protocol Identifier. -* Data (ASCII) - The AX.25 information field displayed as ASCII. +* Data - The AX.25 information field displayed as UTF-8 character string. * Data (Hex) - The AX.25 information field displayed as hexadecimal. diff --git a/plugins/feature/aprs/aprsgui.cpp b/plugins/feature/aprs/aprsgui.cpp index 4abcdd5b2..eea47c40b 100644 --- a/plugins/feature/aprs/aprsgui.cpp +++ b/plugins/feature/aprs/aprsgui.cpp @@ -408,7 +408,7 @@ bool APRSGUI::handleMessage(const Message& message) else { qDebug() << "APRSGUI::handleMessage: Failed to decode as APRS"; - qDebug() << ax25.m_from << " " << ax25.m_to << " " << ax25.m_via << " " << ax25.m_type << " " << ax25.m_pid << " "<< ax25.m_dataASCII; + qDebug() << "From:" << ax25.m_from << "To:" << ax25.m_to << "Via:" << ax25.m_via << "Type:" << ax25.m_type << "PID:" << ax25.m_pid << "Data:" << QString::fromLatin1(ax25.m_data); } } else diff --git a/plugins/feature/aprs/aprsworker.cpp b/plugins/feature/aprs/aprsworker.cpp index 997296b3d..2a830dd7b 100644 --- a/plugins/feature/aprs/aprsworker.cpp +++ b/plugins/feature/aprs/aprsworker.cpp @@ -102,23 +102,25 @@ bool APRSWorker::handleMessage(const Message& cmd) { MainCore::MsgPacket& report = (MainCore::MsgPacket&) cmd; AX25Packet ax25; - APRSPacket *aprs = new APRSPacket(); + if (ax25.decode(report.getPacket())) { - // #2029 - Forward data even if we can't decode it fully - aprs->decode(ax25); + APRSPacket aprs; - if (!aprs->m_data.isEmpty()) + // #2029 - Forward data even if we can't decode it fully + aprs.decode(ax25); + + if (!aprs.m_data.isEmpty()) { // See: http://www.aprs-is.net/IGateDetails.aspx for gating rules - if (!aprs->m_via.contains("TCPIP") - && !aprs->m_via.contains("TCPXX") - && !aprs->m_via.contains("NOGATE") - && !aprs->m_via.contains("RFONLY")) + if (!aprs.m_via.contains("TCPIP") + && !aprs.m_via.contains("TCPXX") + && !aprs.m_via.contains("NOGATE") + && !aprs.m_via.contains("RFONLY")) { - aprs->m_dateTime = report.getDateTime(); - QString igateMsg = aprs->toTNC2(m_settings.m_igateCallsign); - send(igateMsg.toUtf8(), igateMsg.length()); + aprs.m_dateTime = report.getDateTime(); + QByteArray igateMsg = aprs.toTNC2(m_settings.m_igateCallsign); + send(igateMsg.data(), igateMsg.length()); } } } @@ -210,7 +212,7 @@ void APRSWorker::recv() if (!m_loggedIn) { // Log in with callsign and passcode - QString login = QString("user %1 pass %2 vers SDRangel 6.4.0%3\r\n").arg(m_settings.m_igateCallsign).arg(m_settings.m_igatePasscode).arg(m_settings.m_igateFilter.isEmpty() ? "" : QString(" filter %1").arg(m_settings.m_igateFilter)); + QString login = QString("user %1 pass %2 vers SDRangel 7.19.2%3\r\n").arg(m_settings.m_igateCallsign).arg(m_settings.m_igatePasscode).arg(m_settings.m_igateFilter.isEmpty() ? "" : QString(" filter %1").arg(m_settings.m_igateFilter)); send(login.toLatin1(), login.length()); m_loggedIn = true; if (m_msgQueueToFeature) diff --git a/sdrbase/util/aprs.cpp b/sdrbase/util/aprs.cpp index 44d726181..51ba9bf6d 100644 --- a/sdrbase/util/aprs.cpp +++ b/sdrbase/util/aprs.cpp @@ -44,7 +44,7 @@ inline int charToIntAscii(QString&s, int idx) bool APRSPacket::decode(AX25Packet packet) { // Check type, PID and length of packet - if ((packet.m_type == "UI") && (packet.m_pid == "f0") && (packet.m_dataASCII.length() >= 1)) + if ((packet.m_type == "UI") && (packet.m_pid == "f0") && (packet.m_data.length() >= 1)) { // Check destination address QRegularExpression re("^(AIR.*|ALL.*|AP.*|BEACON|CQ.*|GPS.*|DF.*|DGPS.*|DRILL.*|DX.*|ID.*|JAVA.*|MAIL.*|MICE.*|QST.*|QTH.*|RTCM.*|SKY.*|SPACE.*|SPC.*|SYM.*|TEL.*|TEST.*|TLM.*|WX.*|ZIP.*)"); @@ -54,7 +54,10 @@ bool APRSPacket::decode(AX25Packet packet) m_from = packet.m_from; m_to = packet.m_to; m_via = packet.m_via; - m_data = packet.m_dataASCII; + m_data = packet.m_data; + + // UTF-8 is supported: http://aprs.org/aprs12/utf-8.txt + QString data = QString::fromUtf8(packet.m_data); if (packet.m_to.startsWith("GPS") || packet.m_to.startsWith("SPC") || packet.m_to.startsWith("SYM")) { @@ -64,20 +67,20 @@ bool APRSPacket::decode(AX25Packet packet) // Source address SSID can be used to specify a symbol // First byte of information field is data type ID - char dataType = packet.m_dataASCII[0].toLatin1(); + char dataType = data[0].toLatin1(); int idx = 1; switch (dataType) { case '!': // Position without timestamp or Ultimeter 2000 WX Station - parsePosition(packet.m_dataASCII, idx); + parsePosition(data, idx); if (m_symbolCode == '_') - parseWeather(packet.m_dataASCII, idx, false); + parseWeather(data, idx, false); else if (m_symbolCode == '@') - parseStorm(packet.m_dataASCII, idx); + parseStorm(data, idx); else { - parseDataExension(packet.m_dataASCII, idx); - parseComment(packet.m_dataASCII, idx); + parseDataExension(data, idx); + parseComment(data, idx); } break; case '#': // Peet Bros U-II Weather Station @@ -85,85 +88,85 @@ bool APRSPacket::decode(AX25Packet packet) case '%': // Agrelo DFJr / MicroFinder break; case ')': // Item - parseItem(packet.m_dataASCII, idx); - parsePosition(packet.m_dataASCII, idx); - parseDataExension(packet.m_dataASCII, idx); - parseComment(packet.m_dataASCII, idx); + parseItem(data, idx); + parsePosition(data, idx); + parseDataExension(data, idx); + parseComment(data, idx); break; case '*': // Peet Bros U-II Weather Station break; case '/': // Position with timestamp (no APRS messaging) - parseTime(packet.m_dataASCII, idx); - parsePosition(packet.m_dataASCII, idx); + parseTime(data, idx); + parsePosition(data, idx); if (m_symbolCode == '_') - parseWeather(packet.m_dataASCII, idx, false); + parseWeather(data, idx, false); else if (m_symbolCode == '@') - parseStorm(packet.m_dataASCII, idx); + parseStorm(data, idx); else { - parseDataExension(packet.m_dataASCII, idx); - parseComment(packet.m_dataASCII, idx); + parseDataExension(data, idx); + parseComment(data, idx); } break; case ':': // Message - parseMessage(packet.m_dataASCII, idx); + parseMessage(data, idx); break; case ';': // Object - parseObject(packet.m_dataASCII, idx); - parseTime(packet.m_dataASCII, idx); - parsePosition(packet.m_dataASCII, idx); + parseObject(data, idx); + parseTime(data, idx); + parsePosition(data, idx); if (m_symbolCode == '_') - parseWeather(packet.m_dataASCII, idx, false); + parseWeather(data, idx, false); else if (m_symbolCode == '@') - parseStorm(packet.m_dataASCII, idx); + parseStorm(data, idx); else { - parseDataExension(packet.m_dataASCII, idx); - parseComment(packet.m_dataASCII, idx); + parseDataExension(data, idx); + parseComment(data, idx); } break; case '<': // Station Capabilities break; case '=': // Position without timestamp (with APRS messaging) - parsePosition(packet.m_dataASCII, idx); + parsePosition(data, idx); if (m_symbolCode == '_') - parseWeather(packet.m_dataASCII, idx, false); + parseWeather(data, idx, false); else if (m_symbolCode == '@') - parseStorm(packet.m_dataASCII, idx); + parseStorm(data, idx); else { - parseDataExension(packet.m_dataASCII, idx); - parseComment(packet.m_dataASCII, idx); + parseDataExension(data, idx); + parseComment(data, idx); } break; case '>': // Status - parseStatus(packet.m_dataASCII, idx); + parseStatus(data, idx); break; case '?': // Query break; case '@': // Position with timestamp (with APRS messaging) - parseTime(packet.m_dataASCII, idx); - parsePosition(packet.m_dataASCII, idx); + parseTime(data, idx); + parsePosition(data, idx); if (m_symbolCode == '_') - parseWeather(packet.m_dataASCII, idx, false); + parseWeather(data, idx, false); else if (m_symbolCode == '@') - parseStorm(packet.m_dataASCII, idx); + parseStorm(data, idx); else { - parseDataExension(packet.m_dataASCII, idx); - parseComment(packet.m_dataASCII, idx); + parseDataExension(data, idx); + parseComment(data, idx); } break; case 'T': // Telemetry data - parseTelemetry(packet.m_dataASCII, idx); + parseTelemetry(data, idx); break; case '_': // Weather report (without position) - parseTimeMDHM(packet.m_dataASCII, idx); - parseWeather(packet.m_dataASCII, idx, true); + parseTimeMDHM(data, idx); + parseWeather(data, idx, true); break; case '`': // Mic-E Information Field Data (current) case '\'': // Mic-E Information Field Data (old) - parseMicE(packet.m_dataASCII, idx, m_to); + parseMicE(data, idx, m_to); break; case '{': // User-defined APRS packet format break; @@ -182,10 +185,9 @@ bool APRSPacket::decode(AX25Packet packet) qDebug() << "APRSPacket::decode: AX.25 Destination did not match known regexp " << m_to; } } else { - qDebug() << "APRSPacket::decode: Invalid value in type=" << packet.m_type << " pid=" << packet.m_pid << " length of " << packet.m_dataASCII; + qDebug() << "APRSPacket::decode: Not ARPS: type=" << packet.m_type << " pid=" << packet.m_pid << " length=" << packet.m_data.length(); } - return false; } diff --git a/sdrbase/util/aprs.h b/sdrbase/util/aprs.h index 22fff583a..8aaedaad6 100644 --- a/sdrbase/util/aprs.h +++ b/sdrbase/util/aprs.h @@ -32,7 +32,7 @@ struct SDRBASE_API APRSPacket { QString m_from; QString m_to; QString m_via; - QString m_data; // Original ASCII data + QByteArray m_data; // Original binary data QDateTime m_dateTime; // Date/time of reception / decoding @@ -244,9 +244,36 @@ struct SDRBASE_API APRSPacket { return QString("%1,%2").arg(m_latitude).arg(m_longitude); } - QString toTNC2(QString igateCallsign) + QByteArray toTNC2(QString igateCallsign) { - return m_from + ">" + m_to + (m_via.isEmpty() ? "" : ("," + m_via)) + ",qAR," + igateCallsign + ":" + m_data + "\r\n"; + QByteArray data; + + data.append(m_from.toLatin1()); + data.append('>'); + data.append(m_to.toLatin1()); + if (!m_via.isEmpty()) + { + data.append(','); + data.append(m_via.toLatin1()); + } + data.append(",qAR,"); + data.append(igateCallsign.toLatin1()); + data.append(':'); + + // #2028 - Protect against APRS-IS server command injection, by only sending up to first CR/LF + int idx = m_data.indexOf("\r\n"); + if (idx >= 0) + { + data.append(m_data.left(idx + 2)); + } + else + { + data.append(m_data); + data.append('\r'); + data.append('\n'); + } + + return data; } // Convert a TNC2 formatted packet (as sent by APRS-IS Igates) to an AX25 byte array diff --git a/sdrbase/util/ax25.cpp b/sdrbase/util/ax25.cpp index 5e569bb3e..68c862378 100644 --- a/sdrbase/util/ax25.cpp +++ b/sdrbase/util/ax25.cpp @@ -132,7 +132,7 @@ bool AX25Packet::decode(QByteArray packet) infoStart = i; infoEnd = packet.size()-2-i; QByteArray info(packet.mid(infoStart, infoEnd)); - m_dataASCII = QString::fromLatin1(info); + m_data = info; m_dataHex = QString(info.toHex()); return true; diff --git a/sdrbase/util/ax25.h b/sdrbase/util/ax25.h index 012aebafd..d2c70c0a8 100644 --- a/sdrbase/util/ax25.h +++ b/sdrbase/util/ax25.h @@ -32,7 +32,7 @@ struct SDRBASE_API AX25Packet { QString m_via; QString m_type; QString m_pid; - QString m_dataASCII; + QByteArray m_data; QString m_dataHex; bool decode(QByteArray packet);