// 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 #include #include #include #include #include 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; using ContentValue = DefaultValue; using HeaderName = std::variant; 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 inline bool isKnown(NameOrValue const &nameOrValue, KnownHeader const &knownHeader) { return std::visit( [&](auto const &val) { using Val = utils::remove_cref_t; if constexpr (std::is_same_v) { return val == knownHeader.id; } if constexpr (std::is_same_v) { 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 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 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 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 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 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(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 static bool parseHeader(Octets &octets, PageShiftPred pageShiftPred, NamePred namePred, ValuePred valuePred) { HeaderName name = std::uint8_t(0); if (!parseGeneric(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; if constexpr (std::is_same_v) { nameUint = n; } }, name); if (nameUint > 0) { if (nameUint < PageShift) { pageShiftPred(static_cast(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(octets, foo); } HeaderValue value = std::uint8_t(0); if (!parseGeneric(octets, value)) { return false; } valuePred(std::move(value)); return true; } template static bool parseHeaders(Octets &octets, std::array const &headerNames, std::array &headerValues) { std::array 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 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 pushValues; if (!Parser::parseHeaders( headersOctets, std::array{{{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 mmsValues; if (!Parser::parseHeaders( octets, std::array{{{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(mmsNotificationFields.fromAddress)) { Octets octets = std::get(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(mmsNotificationFields.contentLocation)) { location = std::get(mmsNotificationFields.contentLocation); } return std::optional(MmsNotification(address.str(), location.str())); } std::optional parse(std::string const &message) { return parse(message, Connectionless); } } // namespace pdu