// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "Pdu.hpp"
#include <Utils.hpp>
#include <algorithm>
#include <array>
#include <cassert>
#include <cstdint>
#include <variant>
namespace pdu::constants
{
// PDU Types
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p98
enum Type : std::uint8_t
{
Reserved = 0x00,
Connect,
ConnectReply,
Redirect,
Reply,
Disconnect,
Push,
ConfirmedPush,
Suspend,
Resume,
Get = 0x40,
Options,
Head,
Delete,
Trace,
Post = 0x60,
Put,
DataFragment = 0x80
};
// Well-known Header Field Names
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p103
enum FieldName : std::uint8_t
{
/* v1.1 */
Accept = 0x00,
AcceptCharset11, // deprecated
AcceptEncoding11, // deprecated
AcceptLanguage,
AcceptRanges,
Age,
Allow,
Authorization,
CacheControl11, // deprecated
Connection,
ContentBase11, // deprecated
ContentEncoding,
ContentLanguage,
ContentLength,
ContentLocation,
ContentMD5,
ContentRange11, // deprecated
ContentType,
Date,
Etag,
Expires,
From,
Host,
IfModifiedSince,
IfMatch,
IfNoneMatch,
IfRange,
IfUnmodifiedSince,
Location,
LastModified,
MaxForwards,
Pragma,
ProxyAuthenticate,
ProxyAuthorization,
Public,
Range,
Referer,
RetryAfter,
Server,
TransferEncoding,
Upgrade,
UserAgent,
Vary,
Via,
Warning,
WWWAuthenticate,
ContentDisposition11, // deprecated
/* v1.2 */
XWapApplicationId, // X-Wap-Application-ID
XWapContentURI,
XWapInitiatorURI,
AcceptApplication,
BearerIndication,
PushFlag,
Profile,
ProfileDiff,
ProfileWarning12, // deprecated
/* v1.3 */
Expect13,
TE,
Trailer,
AcceptCharset,
AcceptEncoding,
CacheControl13, // deprecated
ContentRange,
XWapTod,
ContentID,
SetCookie,
Cookie,
EncodingVersion,
/* v1.4 */
ProfileWarning,
ContentDisposition,
XWAPSecurity,
CacheControl,
/* v1.5 */
Expect,
XWapLocInvocation,
XWapLocDelivery
};
// Basic Well-Known Content Types
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p105
// http://www.wapforum.org/wina/wsp-content-type.htm
enum ContentType : std::uint8_t
{
AnyAny = 0x00, // "*/*"
TextAny, // "text/*"
TextHtml, // "text/html"
TextPlain, // "text/plain"
TextXHdml, // "text/x-hdml"
TextXTtml, // "text/x-ttml"
TextXVCalendar, // "text/x-vCalendar"
TextXVCard, // "text/x-vCard"
TextVndWapWml, // "text/vnd.wap.wml"
TextVndWapWmlScript, // "text/vnd.wap.wmlscript"
TextVndWapWtaEvent, // "text/vnd.wap.wta-event"
MultipartAny, // "multipart/*"
MultipartMixed, // "multipart/mixed"
MultipartFormData, // "multipart/form-data"
MultipartByteranges, // "multipart/byteranges"
MultipartAlternative, // "multipart/alternative"
ApplicationAny, // "application/*"
ApplicationJavaVm, // "application/java-vm"
ApplicationXWwwFormUrlencoded, // "application/x-www-form-urlencoded"
ApplicationXHdmlc, // "application/x-hdmlc"
ApplicationVndWapWmlc, // "application/vnd.wap.wmlc"
ApplicationVndWapWmlscriptc, // "application/vnd.wap.wmlscriptc"
ApplicationVndWapWtaEventc, // "application/vnd.wap.wta-eventc"
ApplicationVndWapUaprof, // "application/vnd.wap.uaprof"
ApplicationVndWapWtlsCaCertificate, // "application/vnd.wap.wtls-ca-certificate"
ApplicationVndWapWtlsUserCertificate, // "application/vnd.wap.wtls-user-certificate"
ApplicationXX509CaCert, // "application/x-x509-ca-cert"
ApplicationXX509UserCert, // "application/x-x509-user-cert"
ImageAny, // "image/*"
ImageGif, // "image/gif"
ImageJpeg, // "image/jpeg"
ImageTiff, // "image/tiff"
ImagePng, // "image/png"
ImageVndWapWbmp, // "image/vnd.wap.wbmp"
ApplicationVndWapMultipartAny, // "application/vnd.wap.multipart.*"
ApplicationVndWapMultipartMixed, // "application/vnd.wap.multipart.mixed"
ApplicationVndWapMultipartFormData, // "application/vnd.wap.multipart.form-data"
ApplicationVndWapMultipartByteranges, // "application/vnd.wap.multipart.byteranges"
ApplicationVndWapMultipartAlternative, // "application/vnd.wap.multipart.alternative"
ApplicationXml, // "application/xml"
TextXml, // "text/xml"
ApplicationVndWapWbxml, // "application/vnd.wap.wbxml"
ApplicationXX968CrossCert, // "application/x-x968-cross-cert"
ApplicationXX968CaCert, // "application/x-x968-ca-cert"
ApplicationXX968UserCert, // "application/x-x968-user-cert"
TextVndWapSi, // "text/vnd.wap.si"
ApplicationVndWapSic, // "application/vnd.wap.sic"
TextVndWapSl, // "text/vnd.wap.sl"
ApplicationVndWapSlc, // "application/vnd.wap.slc"
TextVndWapCo, // "text/vnd.wap.co"
ApplicationVndWapCoc, // "application/vnd.wap.coc"
ApplicationVndWapRelated, // "application/vnd.wap.multipart.related"
ApplicationVndWapSia, // "application/vnd.wap.sia"
TextVndWapConnectivityXml, // "text/vnd.wap.connectivity-xml"
ApplicationVndWapConnectivityWbxml, // "application/vnd.wap.connectivity-wbxml"
ApplicationPkcs7Mime, // "application/pkcs7-mime"
ApplicationVndWapHashedCertificate, // "application/vnd.wap.hashed-certificate"
ApplicationVndWapSignedCertificate, // "application/vnd.wap.signed-certificate"
ApplicationVndWapCertResponse, // "application/vnd.wap.cert-response"
ApplicationXhtmlXml, // "application/xhtml+xml"
ApplicationWmlXml, // "application/wml+xml"
TextCss, // "text/css"
ApplicationVndWapMmsMessage, // "application/vnd.wap.mms-message"
ApplicationVndWapRolloverCertificate, // "application/vnd.wap.rollover-certificate"
ApplicationVndWapLoccWbxml, // "application/vnd.wap.locc+wbxml"
ApplicationVndWapLocXml, // "application/vnd.wap.loc+xml"
ApplicationVndSyncmlDmWbxml, // "application/vnd.syncml.dm+wbxml"
ApplicationVndSyncmlDmXml, // "application/vnd.syncml.dm+xml"
ApplicationVndSyncmlNotification, // "application/vnd.syncml.notification"
ApplicationVndWapXhtmlXml, // "application/vnd.wap.xhtml+xml"
ApplicationVndWvXspCir, // "application/vnd.wv.csp.cir"
ApplicationVndOmaDdXml, // "application/vnd.oma.dd+xml"
ApplicationVndOmaDrmMessage, // "application/vnd.oma.drm.message"
ApplicationVndOmaDrmContent, // "application/vnd.oma.drm.content"
ApplicationVndOmaDrmRightsXml, // "application/vnd.oma.drm.rights+xml"
ApplicationVndOmaDrmRightsWbxml, // "application/vnd.oma.drm.rights+wbxml"
};
enum PushApplicationId : std::uint8_t
{
XWapApplicationAny = 0x00, // x-wap-application:*
XWapApplicationPushSia, // x-wap-application:push.sia
XWapApplicationWmlUa, // x-wap-application:wml.ua
XWapApplicationWtaUa, // x-wap-application:wta.ua
XWapApplicationMmsUa, // x-wap-application:mms.ua
XWapApplicationPushSyncml, // x-wap-application:push.syncml
XWapApplicationLocUa, // x-wap-application:loc.ua
XWapApplicationSyncmlDm, // x-wap-application:syncml.dm
XWapApplicationDrmUa, // x-wap-application:drm.ua
XWapApplicationEmnUa, // x-wap-application:emn.ua
XWapApplicationWvUa, // x-wap-application:wv.ua
};
// MMS
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p64
enum MmsFieldName : std::uint8_t
{
MmsBcc = 0x01,
MmsCc,
XMmsContentLocation,
MmsContentType,
MmsDate,
XMmsDeliveryReport,
XMmsDeliveryTime,
XMmsExpiry,
MmsFrom,
XMmsMessageClass,
MmsMessageID,
XMmsMessageType,
XMmsMMSVersion,
XMmsMessageSize,
XMmsPriority,
XMmsReadReport,
XMmsReportAllowed,
XMmsResponseStatus,
XMmsResponseText,
XMmsSenderVisibility,
XMmsStatus,
MmsSubject,
MmsTo,
XMmsTransactionId,
XMmsRetrieveStatus,
XMmsRetrieveText,
XMmsReadStatus,
XMmsReplyCharging,
XMmsReplyChargingDeadline,
XMmsReplyChargingID,
XMmsReplyChargingSize,
XMmsPreviouslySentBy,
XMmsPreviouslySentDate,
XMmsStore,
XMmsMMState,
XMmsMMFlags,
XMmsStoreStatus,
XMmsStoreStatusText,
XMmsStored,
XMmsAttributes,
XMmsTotals,
XMmsMboxTotals,
XMmsQuotas,
XMmsMboxQuotas,
XMmsMessageCount,
MmsContent,
XMmsStart,
MmsAdditionalheaders,
XMmsDistributionIndicator,
XMmsElementDescriptor,
XMmsLimit,
XMmsRecommendedRetrievalMode,
XMmsRecommendedRetrievalModeText,
XMmsStatusText,
XMmsApplicID,
XMmsReplyApplicID,
XMmsAuxApplicInfo,
XMmsContentClass,
XMmsDRMContent,
XMmsAdaptationAllowed,
XMmsReplaceID,
XMmsCancelID,
XMmsCancelStatus
};
enum MmsMessageType : std::uint8_t
{
MmsMSendReq = 0x00, // m-send-req
MmsMSendConf, // m-send-conf
MmsMNotificationInd, // m-notification-ind
MmsMNotifyrespInd, // m-notifyresp-ind
MmsMRetrieveConf, // m-retrieve-conf
MmsMAcknowledgeInd, // m-acknowledge-ind
MmsMDeliveryInd, // m-delivery-ind
MmsMReadRecInd, // m-read-rec-ind
MmsMReadOrigInd, // m-read-orig-ind
MmsMForwardReq, // m-forward-req
MmsMForwardComf, // m-forward-conf
MmsMMboxStoreReq, // m-mbox-store-req
MmsMMboxStoreConf, // m-mbox-store-conf
MmsMMboxViewReq, // m-mbox-view-req
MmsMMboxViewConf, // m-mbox-view-conf
MmsMMboxUploadReq, // m-mbox-upload-req
MmsMMboxUploadConf, // m-mbox-upload-conf
MmsMMboxDeleteReq, // m-mbox-delete-req
MmsMMboxDeleteConf, // m-mbox-delete-conf
MmsMMboxDescr, // m-mbox-descr
MmsMDeleteReq, // m-delete-req
MmsMDeleteConf, // m-delete-conf
MmsMCancelReq, // m-cancel-req
MmsMCancelConf // m-cancel-conf
};
} // namespace pdu::constants
namespace pdu
{
// A range of characters
struct CharRange
{
using Iterator = std::string::const_iterator;
CharRange() : m_begin(), m_end()
{}
CharRange(Iterator begin, Iterator end) : m_begin(begin), m_end(end)
{
assert(m_begin <= m_end);
}
Iterator begin() const
{
return m_begin;
}
Iterator end() const
{
return m_end;
}
bool empty() const
{
return m_begin == m_end;
}
std::uint32_t size() const
{
return m_end - m_begin;
}
std::string str() const
{
return std::string(m_begin, m_end);
}
friend bool operator==(CharRange const &rng, std::string const &str)
{
return rng.size() == str.size() && std::equal(rng.m_begin, rng.m_end, str.begin());
}
friend bool operator==(std::string const &str, CharRange const &rng)
{
return rng == str;
}
friend bool operator==(CharRange const &rng, const char *cstr)
{
return cstr[rng.size()] == '\0' && std::equal(rng.m_begin, rng.m_end, cstr);
}
friend bool operator==(const char *cstr, CharRange const &rng)
{
return rng == cstr;
}
protected:
Iterator m_begin;
Iterator m_end;
};
// A buffer representing input octets
struct Octets : CharRange
{
Octets() = default;
Octets(Iterator begin, Iterator end) : CharRange(begin, end)
{}
bool peek(std::uint8_t &result) const
{
if (m_begin != m_end) {
result = *m_begin;
return true;
}
return false;
}
bool next(std::uint8_t &result)
{
if (m_begin != m_end) {
result = *m_begin++;
return true;
}
return false;
}
void ignore()
{
if (m_begin != m_end) {
++m_begin;
}
}
void ignore(std::uint32_t n)
{
m_begin = advancedByN(n);
}
Octets subOctets(std::uint32_t n)
{
Iterator it = advancedByN(n);
Octets result(m_begin, it);
m_begin = it;
return result;
}
private:
Iterator advancedByN(std::uint32_t n) const
{
return n < size() ? m_begin + n : m_end;
}
};
// A range representing text, excluding last \0 character
struct Text : CharRange
{
Text() = default;
Text(Iterator begin, Iterator end) : CharRange(begin, end)
{}
};
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p79
using DefaultValue = std::variant<std::uint8_t, Text, Octets>;
using ContentValue = DefaultValue;
using HeaderName = std::variant<std::uint8_t, Text>;
using HeaderValue = DefaultValue;
// Representing known header names and header values
// Intended to be used with string literals
// If passing an object is needed replace const char*
// with std::string or std::string_view
struct KnownHeader
{
std::uint8_t id;
const char *idCStr;
};
template <typename NameOrValue>
inline bool isKnown(NameOrValue const &nameOrValue, KnownHeader const &knownHeader)
{
return std::visit(
[&](auto const &val) {
using Val = utils::remove_cref_t<decltype(val)>;
if constexpr (std::is_same_v<Val, std::uint8_t>) {
return val == knownHeader.id;
}
if constexpr (std::is_same_v<Val, Text>) {
return val == knownHeader.idCStr;
}
return false;
},
nameOrValue);
}
struct Parser
{
static constexpr std::uint8_t Zero = 0;
static constexpr std::uint8_t EndOfString = 0;
static constexpr std::uint8_t UintvarEscape = 31;
static constexpr std::uint8_t TextMin = 32;
static constexpr std::uint8_t TextMax = 127;
static constexpr std::uint8_t TextEscape = 127;
static constexpr std::uint8_t PageShift = 127;
static constexpr std::uint32_t StringMaxLength = 1024;
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// s63
static bool parseUintvar(Octets &octets, std::uint32_t &val)
{
val = 0x00000000;
bool isEndFound = false;
for (int i = 0; i < 5; ++i) {
std::uint8_t octet = 0x00;
if (!octets.next(octet)) {
return false;
}
val = (val << 7) | octet;
if ((octet & 0x80) == 0x00) {
isEndFound = true;
break;
}
}
return isEndFound;
}
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// s63
static bool parseUint(Octets &octets, std::uint32_t &val, std::uint32_t length)
{
if (length > 4) {
return false;
}
val = 0x00000000;
for (std::uint32_t i = 0; i < length; ++i) {
std::uint8_t octet = 0x00;
if (!octets.next(octet)) {
return false;
}
val = (val << 8) | octet;
}
return true;
}
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p79
static bool parseTextN(Octets &octets, Text &text, std::uint32_t length)
{
if (length > StringMaxLength) {
return false;
}
if (length == 0) {
text = Text(octets.begin(), octets.begin());
return true;
}
// Avoid including EoS character
Octets::Iterator const begin = octets.begin();
octets.ignore(length - 1);
Octets::Iterator const end = octets.begin();
std::uint8_t octet = 0x00;
if (!octets.next(octet)) {
return false;
}
if (octet == EndOfString) {
text = Text(begin, end);
}
else {
text = Text(begin, octets.begin());
}
return true;
}
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p79
static bool parseText(Octets &octets, Text &text)
{
Octets::Iterator begin = octets.begin();
std::uint8_t octet = 0x00;
if (!octets.next(octet)) {
return false;
}
if (octet == TextEscape) {
begin = octets.begin();
}
// Avoid including EoS character
Octets::Iterator end = begin;
while (octet != EndOfString) {
end = octets.begin();
if (!octets.next(octet)) {
return false;
}
}
text = Text(begin, end);
return true;
}
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p79
// Value may be:
// - decoded short integer
// - text
// - data as parsable octets range
struct DefaultPolicy
{
static bool data(Octets &octets, std::uint8_t lengthOctet, DefaultValue &value)
{
octets.ignore();
std::uint32_t length = lengthOctet;
if (lengthOctet == UintvarEscape) {
if (!parseUintvar(octets, length)) {
return false;
}
}
if (octets.size() < length) {
return false;
}
value = octets.subOctets(length);
return true;
}
template <typename Value>
static bool text(Octets &octets, std::uint8_t charOctet, Value &value)
{
// Do not ignore the first character, it's part of the text
Text text(octets.begin(), octets.begin());
if (!parseText(octets, text)) {
return false;
}
value = text;
return true;
}
static bool shortInteger(Octets &octets, std::uint8_t uintOctet, DefaultValue &value)
{
octets.ignore();
value = std::uint8_t(uintOctet & 0x7F);
return true;
}
};
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p81
// Name may be:
// - not decoded short integer well-known field name [128, 255]
// - text token starting from octet [32, 126]
// - shortcut page shift delimiter [0, 31]
// - page shift delimter 127 (then header value is one octet page id and
// shouldn't be parsed with parseHeaderValue)
struct HeaderNamePolicy
{
static bool data(Octets &octets, std::uint8_t lengthOctet, HeaderName &value)
{
octets.ignore();
value = lengthOctet;
return true;
}
static bool text(Octets &octets, std::uint8_t charOctet, HeaderName &value)
{
if (charOctet == PageShift) {
octets.ignore();
value = charOctet;
return true;
}
else {
return DefaultPolicy::text(octets, charOctet, value);
}
}
static bool shortInteger(Octets &octets, std::uint8_t uintOctet, HeaderName &value)
{
octets.ignore();
value = uintOctet;
return true;
}
};
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p81
struct IgnoreHeaderValuePolicy
{
template <typename Value>
static bool data(Octets &octets, std::uint8_t lengthOctet, Value &value)
{
octets.ignore();
std::uint32_t length = lengthOctet;
if (lengthOctet == UintvarEscape) {
if (!parseUintvar(octets, length)) {
return false;
}
}
if (octets.size() < length) {
return false;
}
octets.ignore(length);
return true;
}
template <typename Value>
static bool text(Octets &octets, std::uint8_t charOctet, Value &value)
{
octets.ignore();
while (charOctet != EndOfString) {
if (!octets.next(charOctet)) {
return false;
}
}
return true;
}
template <typename Value>
static bool shortInteger(Octets &octets, std::uint8_t uintOctet, Value &value)
{
octets.ignore();
return true;
}
};
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p79
template <typename Policy, typename Value>
static bool parseGeneric(Octets &octets, Value &value)
{
std::uint8_t octet = 0x00;
if (!octets.peek(octet)) {
return false;
}
if (octet < TextMin) {
return Policy::data(octets, octet, value);
}
else if (octet <= TextMax) {
return Policy::text(octets, octet, value);
}
else { // octet > TextMax
return Policy::shortInteger(octets, octet, value);
}
}
// Value may be:
// - decoded short integer well-known field name
// - long integer well-known field name
// - text
// - integer/text type mashed together with optional parameter
static bool parseContentType(Octets &octets, ContentValue &value)
{
return parseGeneric<DefaultPolicy>(octets, value);
}
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p81
template <typename PageShiftPred, typename NamePred, typename ValuePred>
static bool parseHeader(Octets &octets, PageShiftPred pageShiftPred, NamePred namePred, ValuePred valuePred)
{
HeaderName name = std::uint8_t(0);
if (!parseGeneric<HeaderNamePolicy>(octets, name)) {
return false;
}
// Distinguish between page shift and short integer
std::uint32_t nameUint = 0;
std::visit(
[&](auto const &n) {
using n_t = utils::remove_cref_t<decltype(n)>;
if constexpr (std::is_same_v<n_t, std::uint8_t>) {
nameUint = n;
}
},
name);
if (nameUint > 0) {
if (nameUint < PageShift) {
pageShiftPred(static_cast<std::uint8_t>(nameUint));
return true;
}
else if (nameUint == PageShift) {
std::uint8_t octet = 0;
if (!octets.next(octet)) {
return false;
}
pageShiftPred(octet);
return true;
}
else if (nameUint > TextMax && nameUint <= 0xFF) {
name = std::uint8_t(nameUint & 0x7F); // decode short integer
}
else if (nameUint > 0xFF) {
return false; // This should not happen
}
}
if (!namePred(std::move(name))) {
int foo;
return parseGeneric<IgnoreHeaderValuePolicy>(octets, foo);
}
HeaderValue value = std::uint8_t(0);
if (!parseGeneric<DefaultPolicy>(octets, value)) {
return false;
}
valuePred(std::move(value));
return true;
}
template <std::size_t N>
static bool parseHeaders(Octets &octets,
std::array<KnownHeader, N> const &headerNames,
std::array<HeaderValue, N> &headerValues)
{
std::array<bool, N> headersFound;
headersFound.fill(false);
std::size_t foundCount = 0;
while (!octets.empty()) {
std::size_t nameId = N;
HeaderValue headerValue = std::uint8_t(0);
bool pageShift = false;
auto pageShiftPred = [&](std::uint8_t page) { pageShift = true; };
auto namePred = [&](auto &&name) {
for (std::size_t i = 0; i < N; ++i) {
if (headersFound[i]) {
// Header already found
continue;
}
else if (isKnown(name, headerNames[i])) {
headersFound[i] = true;
++foundCount;
nameId = i;
return true;
}
}
return false;
};
auto valuePred = [&](auto &&value) {
if (nameId < N) {
headerValues[nameId] = std::move(value);
}
};
if (!Parser::parseHeader(octets, pageShiftPred, namePred, valuePred)) {
return false;
}
// NOTE: Page shifts are currently not supported
if (pageShift) {
return false;
}
if (foundCount == N) {
break;
}
}
return true;
}
};
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p64
struct CommonFields
{
std::uint8_t id = 0x00; // set in connectionless mode
std::uint8_t type = 0x00;
};
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p70
struct PushFields
{
std::uint32_t headersLen = 0;
ContentValue contentType = std::uint8_t{0};
HeaderValue applicationId = std::uint8_t{0};
};
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p17
struct MmsCommonFields
{
HeaderValue messageType = std::uint8_t{0};
};
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p21, p55, p53
struct MmsNotificationFields
{
HeaderValue fromAddress = std::uint8_t{0};
HeaderValue contentLocation = std::uint8_t{0};
};
enum ConnectionMode
{
Connectionless,
Connectionmode
};
std::optional<MmsNotification> parse(std::string const &message, ConnectionMode connectionMode)
{
Octets octets(message.begin(), message.end());
// http://www.openmobilealliance.org/release/Browser_Protocol_Stack/V2_1-20110315-A/OMA-WAP-TS-WSP-V1_0-20110315-A.pdf,
// p64
CommonFields commonFields;
if (connectionMode == Connectionless && !octets.next(commonFields.id)) {
return std::nullopt;
}
if (!octets.next(commonFields.type)) {
return std::nullopt;
}
if (commonFields.type != constants::Push) {
return std::nullopt; // unsupported PDU type
}
PushFields pushFields;
if (!Parser::parseUintvar(octets, pushFields.headersLen)) {
return std::nullopt;
}
Octets headersOctets = octets.subOctets(pushFields.headersLen);
if (!Parser::parseContentType(headersOctets, pushFields.contentType)) {
return std::nullopt;
}
if (!isKnown(pushFields.contentType,
{constants::ApplicationVndWapMmsMessage, "application/vnd.wap.mms-message"})) {
return std::nullopt; // unsupported Push type
}
// Push headers
{
std::array<HeaderValue, 1> pushValues;
if (!Parser::parseHeaders(
headersOctets,
std::array<KnownHeader, 1>{{{constants::XWapApplicationId, "X-Wap-Application-ID"}}},
pushValues)) {
return std::nullopt;
}
pushFields.applicationId = std::move(pushValues[0]);
}
if (!isKnown(pushFields.applicationId, {constants::XWapApplicationMmsUa, "x-wap-application:mms.ua"})) {
return std::nullopt; // unsupported WAP Push app
}
// MMS headers
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p21
MmsCommonFields mmsCommonFields;
MmsNotificationFields mmsNotificationFields;
{
std::array<HeaderValue, 3> mmsValues;
if (!Parser::parseHeaders(
octets,
std::array<KnownHeader, 3>{{{constants::XMmsMessageType, "X-Mms-Message-Type"},
{constants::MmsFrom, "From"},
{constants::XMmsContentLocation, "X-Mms-Content-Location"}}},
mmsValues)) {
return std::nullopt;
}
mmsCommonFields.messageType = std::move(mmsValues[0]);
mmsNotificationFields.fromAddress = std::move(mmsValues[1]);
mmsNotificationFields.contentLocation = std::move(mmsValues[2]);
}
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p56
if (!isKnown(mmsCommonFields.messageType, {constants::MmsMNotificationInd, "m-notification-ind"})) {
return std::nullopt; // unsuported MMS type
}
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p55
Text address;
if (std::holds_alternative<Octets>(mmsNotificationFields.fromAddress)) {
Octets octets = std::get<Octets>(mmsNotificationFields.fromAddress);
std::uint8_t token = 0;
if (!octets.next(token)) {
return std::nullopt;
}
constexpr std::uint8_t AddressPresentToken = 128;
if (token == AddressPresentToken) {
if (!Parser::parseTextN(octets, address, octets.size())) {
return std::nullopt;
}
}
}
// https://www.openmobilealliance.org/release/MMS/V1_3-20110913-A/OMA-TS-MMS_ENC-V1_3-20110913-A.pdf, p53
Text location;
if (std::holds_alternative<Text>(mmsNotificationFields.contentLocation)) {
location = std::get<Text>(mmsNotificationFields.contentLocation);
}
return std::optional<MmsNotification>(MmsNotification(address.str(), location.str()));
}
std::optional<MmsNotification> parse(std::string const &message)
{
return parse(message, Connectionless);
}
} // namespace pdu