1 /*
2  * Copyright (C) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include "ndef_message.h"
16 #include "loghelper.h"
17 #include "nfc_sdk_common.h"
18 
19 namespace OHOS {
20 namespace NFC {
21 namespace KITS {
22 using KITS::NfcSdkCommon;
NdefMessage(std::vector<std::shared_ptr<NdefRecord>> ndefRecords)23 NdefMessage::NdefMessage(std::vector<std::shared_ptr<NdefRecord>> ndefRecords)
24     : ndefRecordList_(std::move(ndefRecords))
25 {
26 }
27 
~NdefMessage()28 NdefMessage::~NdefMessage()
29 {
30     ndefRecordList_.clear();
31 }
32 
GetNdefMessage(const std::string & data)33 std::shared_ptr<NdefMessage> NdefMessage::GetNdefMessage(const std::string& data)
34 {
35     std::vector<std::shared_ptr<NdefRecord>> ndefRecords = ParseRecord(data, false);
36     if (ndefRecords.empty()) {
37         ErrorLog("GetNdefMessage, ndefRecords invalid.");
38         return std::shared_ptr<NdefMessage>();
39     }
40     return GetNdefMessage(ndefRecords);
41 }
42 
GetNdefMessage(std::vector<std::shared_ptr<NdefRecord>> ndefRecords)43 std::shared_ptr<NdefMessage> NdefMessage::GetNdefMessage(std::vector<std::shared_ptr<NdefRecord>> ndefRecords)
44 {
45     return std::make_shared<NdefMessage>(std::move(ndefRecords));
46 }
47 
GetTagRtdType(EmRtdType rtdtype)48 std::string NdefMessage::GetTagRtdType(EmRtdType rtdtype)
49 {
50     std::string rtd;
51     switch (rtdtype) {
52         case EmRtdType::RTD_TEXT:
53             rtd = "T";  // 0x54
54             break;
55         case EmRtdType::RTD_URI:
56             rtd = "U";  // 0x55
57             break;
58         case EmRtdType::RTD_SMART_POSTER:
59             rtd = "Sp";  // 0x53, 0x70
60             break;
61         case EmRtdType::RTD_ALTERNATIVE_CARRIER:
62             rtd = "ac";  // 0x61, 0x63
63             break;
64         case EmRtdType::RTD_HANDOVER_CARRIER:
65             rtd = "Hc";  // 0x48, 0x63
66             break;
67         case EmRtdType::RTD_HANDOVER_REQUEST:
68             rtd = "Hr";  // 0x48, 0x72
69             break;
70         case EmRtdType::RTD_HANDOVER_SELECT:
71             rtd = "Hs";  // 0x48, 0x73
72             break;
73         case EmRtdType::RTD_OHOS_APP:
74             rtd = "ohos.com:pkg";  // "ohos.com:pkg"
75             break;
76         default:
77             rtd.clear();
78             break;
79     }
80     return rtd;
81 }
82 
GetNdefRecords() const83 std::vector<std::shared_ptr<NdefRecord>> NdefMessage::GetNdefRecords() const
84 {
85     return ndefRecordList_;
86 }
87 
MakeUriRecord(const std::string & uriString)88 std::shared_ptr<NdefRecord> NdefMessage::MakeUriRecord(const std::string& uriString)
89 {
90     if (uriString.empty()) {
91         ErrorLog("MakeUriRecord, uriString invalid.");
92         return std::shared_ptr<NdefRecord>();
93     }
94 
95     std::string payload = "00";
96     std::string uri = uriString;
97     for (size_t i = 1; i < g_uriPrefix.size() - 1; i++) {
98         if (!uriString.compare(0, g_uriPrefix[i].size(), g_uriPrefix[i])) {
99             payload = NfcSdkCommon::UnsignedCharToHexString(i & 0xFF);
100             uri = uriString.substr(g_uriPrefix[i].size());
101             break;
102         }
103     }
104 
105     payload += NfcSdkCommon::StringToHexString(uri);
106 
107     std::string id = "";
108     std::string tagRtdType = NfcSdkCommon::StringToHexString(GetTagRtdType(EmRtdType::RTD_URI));
109     return CreateNdefRecord(TNF_WELL_KNOWN, id, payload, tagRtdType);
110 }
111 
MakeTextRecord(const std::string & text,const std::string & locale)112 std::shared_ptr<NdefRecord> NdefMessage::MakeTextRecord(const std::string& text, const std::string& locale)
113 {
114     std::string tagRtdType = NfcSdkCommon::StringToHexString(GetTagRtdType(EmRtdType::RTD_TEXT));
115     std::string id = "";
116     int localeLen = locale.size() & 0xFF;
117     std::string payload = NfcSdkCommon::UnsignedCharToHexString(localeLen);
118     payload += NfcSdkCommon::StringToHexString(locale);
119     payload += NfcSdkCommon::StringToHexString(text);
120     return CreateNdefRecord(TNF_WELL_KNOWN, id, payload, tagRtdType);
121 }
122 
MakeMimeRecord(const std::string & mimeType,const std::string & mimeData)123 std::shared_ptr<NdefRecord> NdefMessage::MakeMimeRecord(const std::string& mimeType, const std::string& mimeData)
124 {
125     if (mimeType.empty() || mimeData.empty()) {
126         ErrorLog("MakeMimeRecord, mimeType or mimeData invalid.");
127         return std::shared_ptr<NdefRecord>();
128     }
129     std::string id = "";
130     size_t t = mimeType.find_first_of('/');
131     if (t == 0 || t == (mimeType.size() - 1)) {
132         ErrorLog("MakeMimeRecord, mimeType should have major and minor type if '/' exists.");
133         return std::shared_ptr<NdefRecord>();
134     }
135     return CreateNdefRecord(TNF_MIME_MEDIA, id, mimeData, NfcSdkCommon::StringToHexString(mimeType));
136 }
137 
MakeExternalRecord(const std::string & domainName,const std::string & serviceName,const std::string & externalData)138 std::shared_ptr<NdefRecord> NdefMessage::MakeExternalRecord(const std::string& domainName,
139                                                             const std::string& serviceName,
140                                                             const std::string& externalData)
141 {
142     if (domainName.empty() || serviceName.empty() || externalData.empty()) {
143         ErrorLog("MakeExternalRecord, domainName or serviceName invalid.");
144         return std::shared_ptr<NdefRecord>();
145     }
146 
147     std::string domain = domainName;
148     std::string service = serviceName;
149     domain.erase(0, domain.find_first_not_of("\r\t\n "));
150     domain.erase(domain.find_last_not_of("\r\t\n ") + 1);
151     transform(domain.begin(), domain.end(), domain.begin(), ::tolower);
152     service.erase(0, service.find_first_not_of("\r\t\n "));
153     service.erase(service.find_last_not_of("\r\t\n ") + 1);
154     transform(service.begin(), service.end(), service.begin(), ::tolower);
155 
156     if (domain.empty() || service.empty()) {
157         return std::shared_ptr<NdefRecord>();
158     }
159 
160     std::string tagRtdType = NfcSdkCommon::StringToHexString(domain + ":" + service);
161     std::string id = "";
162 
163     return CreateNdefRecord(TNF_EXTERNAL_TYPE, id, externalData, tagRtdType);
164 }
165 
MessageToString(std::weak_ptr<NdefMessage> ndefMessage)166 std::string NdefMessage::MessageToString(std::weak_ptr<NdefMessage> ndefMessage)
167 {
168     std::string buffer;
169     if (ndefMessage.expired()) {
170         ErrorLog("MessageToString, ndefMessage invalid.");
171         return buffer;
172     }
173     for (size_t i = 0; i < ndefMessage.lock()->ndefRecordList_.size(); i++) {
174         bool bIsMB = (i == 0);                                                // first record
175         bool bIsME = (i == ndefMessage.lock()->ndefRecordList_.size() - 1);  // last record
176         NdefRecordToString(ndefMessage.lock()->ndefRecordList_.at(i), buffer, bIsMB, bIsME);
177     }
178     return buffer;
179 }
180 
NdefRecordToString(std::weak_ptr<NdefRecord> record,std::string & buffer,bool bIsMB,bool bIsME)181 void NdefMessage::NdefRecordToString(std::weak_ptr<NdefRecord> record, std::string& buffer, bool bIsMB, bool bIsME)
182 {
183     if (record.expired()) {
184         ErrorLog("NdefRecordToString, record invalid.");
185         return;
186     }
187     std::string payload = record.lock()->payload_;
188     uint32_t tnf = record.lock()->tnf_;
189     std::string id = record.lock()->id_;
190     std::string rtdType = record.lock()->tagRtdType_;
191     bool sr = NfcSdkCommon::GetHexStrBytesLen(payload) < SHORT_RECORD_SIZE;
192     bool il = (tnf == TNF_EMPTY) ? true : (NfcSdkCommon::GetHexStrBytesLen(id) > 0);
193     unsigned char flag = (unsigned char)((bIsMB ? FLAG_MB : 0) | (bIsME ? FLAG_ME : 0)
194         | (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0)) | (char)tnf;
195     buffer.append(NfcSdkCommon::UnsignedCharToHexString(flag));
196     buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(rtdType)));
197     if (sr) {
198         buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(payload)));
199     } else {
200         buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(payload)));
201     }
202     if (il) {
203         buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(id)));
204     }
205 
206     buffer.append(rtdType);
207     buffer.append(id);
208     buffer.append(payload);
209 }
210 
ParseRecordLayoutHead(RecordLayout & layout,unsigned char head)211 void NdefMessage::ParseRecordLayoutHead(RecordLayout& layout, unsigned char head)
212 {
213     layout.mb = (head & FLAG_MB) != 0;
214     layout.me = (head & FLAG_ME) != 0;
215     layout.cf = (head & FLAG_CF) != 0;
216     layout.sr = (head & FLAG_SR) != 0;
217     layout.il = (head & FLAG_IL) != 0;
218     layout.tnf = static_cast<short>(head & FLAG_TNF);
219 }
220 
IsInvalidRecordLayoutHead(RecordLayout & layout,bool isChunkFound,uint32_t parsedRecordSize,bool isMbMeIgnored)221 bool NdefMessage::IsInvalidRecordLayoutHead(RecordLayout& layout, bool isChunkFound,
222     uint32_t parsedRecordSize, bool isMbMeIgnored)
223 {
224     if (!layout.mb && parsedRecordSize == 0 && !isChunkFound && !isMbMeIgnored) {
225         ErrorLog("IsInvalidRecordLayoutHead, 1st error for mb and size.");
226         return true;
227     } else if (layout.mb && (parsedRecordSize != 0 || isChunkFound) && !isMbMeIgnored) {
228         ErrorLog("IsInvalidRecordLayoutHead, 2nd error for mb and size");
229         return true;
230     } else if (isChunkFound && layout.il) {
231         ErrorLog("IsInvalidRecordLayoutHead, 3rd error for il");
232         return true;
233     } else if (layout.cf && layout.me) {
234         ErrorLog("IsInvalidRecordLayoutHead, 4th error for cf and me");
235         return true;
236     } else if (isChunkFound && layout.tnf != TNF_UNCHANGED) {
237         ErrorLog("IsInvalidRecordLayoutHead, 5th error for tnf");
238         return true;
239     } else if (!isChunkFound && layout.tnf == TNF_UNCHANGED) {
240         ErrorLog("IsInvalidRecordLayoutHead, 6th error for tnf");
241         return true;
242     }
243     return false;
244 }
245 
ParseRecordLayoutLength(RecordLayout & layout,bool isChunkFound,const std::string & data,uint32_t & parsedDataIndex)246 void NdefMessage::ParseRecordLayoutLength(RecordLayout& layout, bool isChunkFound,
247     const std::string& data, uint32_t& parsedDataIndex)
248 {
249     layout.typeLength = NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++) & 0xFF;
250     if (layout.sr) {
251         layout.payloadLength = NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++) & 0xFF;
252     } else {
253         if (NfcSdkCommon::GetHexStrBytesLen(data) < parsedDataIndex + int(sizeof(int))) {
254             layout.payloadLength = 0;
255         } else {
256             std::string lenString = data.substr(parsedDataIndex * HEX_BYTE_LEN, sizeof(int) * HEX_BYTE_LEN);
257             layout.payloadLength = 0;
258             for (unsigned int i = 0; i < sizeof(int); i++) {
259                 layout.payloadLength +=
260                     (NfcSdkCommon::GetByteFromHexStr(lenString, i) << ((sizeof(int) - i - 1) * ONE_BYTE_SHIFT));
261             }
262             parsedDataIndex += sizeof(int);
263         }
264     }
265     layout.idLength = layout.il ? (NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++) & 0xFF) : 0;
266 }
267 
IsRecordLayoutLengthInvalid(RecordLayout & layout,bool isChunkFound)268 bool NdefMessage::IsRecordLayoutLengthInvalid(RecordLayout& layout, bool isChunkFound)
269 {
270     // for the middle chunks record, need the type length is zero.
271     if (isChunkFound && layout.typeLength != 0) {
272         ErrorLog("IsInvalidRecordLayoutHead, 1st error for typeLength");
273         return true;
274     }
275 
276     // for the first chunk, expected has type.
277     if (layout.cf && !isChunkFound) {
278         if (layout.typeLength == 0 && layout.tnf != TNF_UNKNOWN) {
279             ErrorLog("IsInvalidRecordLayoutHead, 2nd error for typeLength and tnf");
280             return true;
281         }
282     }
283 
284     if (layout.payloadLength > MAX_PAYLOAD_SIZE) {
285         ErrorLog("IsInvalidRecordLayoutHead, 3rd error for payloadLength");
286         return true;
287     }
288     return false;
289 }
290 
ParseRecordType(RecordLayout & layout,const std::string & data,uint32_t & parsedDataIndex)291 std::string NdefMessage::ParseRecordType(RecordLayout& layout, const std::string& data, uint32_t& parsedDataIndex)
292 {
293     if (layout.typeLength <= 0) {
294         ErrorLog("IsInvalidRecordLayoutHead, typeLength less than 0.");
295         return "";
296     }
297     if (NfcSdkCommon::GetHexStrBytesLen(data) < parsedDataIndex + layout.typeLength) {
298         ErrorLog("data len.%{public}d index.%{public}d rtdtype len.%{public}d error",
299             NfcSdkCommon::GetHexStrBytesLen(data), parsedDataIndex, layout.typeLength);
300         return "";
301     }
302     std::string type = data.substr(parsedDataIndex * HEX_BYTE_LEN, layout.typeLength * HEX_BYTE_LEN);
303     parsedDataIndex += layout.typeLength;
304     return type;
305 }
306 
ParseRecordId(RecordLayout & layout,const std::string & data,uint32_t & parsedDataIndex)307 std::string NdefMessage::ParseRecordId(RecordLayout& layout, const std::string& data, uint32_t& parsedDataIndex)
308 {
309     if (layout.idLength <= 0) {
310         ErrorLog("ParseRecordId, idLength <= 0");
311         return "";
312     }
313     if (NfcSdkCommon::GetHexStrBytesLen(data) < parsedDataIndex + layout.idLength) {
314         ErrorLog("data len.%{public}d index.%{public}d id len.%{public}d error",
315             NfcSdkCommon::GetHexStrBytesLen(data), parsedDataIndex, layout.idLength);
316         return "";
317     }
318     std::string id = data.substr(parsedDataIndex * HEX_BYTE_LEN, layout.idLength * HEX_BYTE_LEN);
319     parsedDataIndex += layout.idLength;
320     return id;
321 }
322 
ParseRecordPayload(RecordLayout & layout,const std::string & data,uint32_t & parsedDataIndex)323 std::string NdefMessage::ParseRecordPayload(RecordLayout& layout, const std::string& data, uint32_t& parsedDataIndex)
324 {
325     if (layout.payloadLength <= 0) {
326         ErrorLog("ParseRecordPayload, payloadLength <= 0");
327         return "";
328     }
329     if (NfcSdkCommon::GetHexStrBytesLen(data) < (parsedDataIndex + layout.payloadLength)) {
330         ErrorLog("data len.%{public}d index.%{public}d payload len.%{public}d error",
331             NfcSdkCommon::GetHexStrBytesLen(data), parsedDataIndex, layout.payloadLength);
332         return "";
333     }
334     std::string payload = data.substr(parsedDataIndex * HEX_BYTE_LEN, layout.payloadLength * HEX_BYTE_LEN);
335     parsedDataIndex += layout.payloadLength;
336     return payload;
337 }
338 
SaveRecordChunks(RecordLayout & layout,bool isChunkFound,std::vector<std::string> & chunks,char & chunkTnf,const std::string & payload)339 void NdefMessage::SaveRecordChunks(RecordLayout& layout, bool isChunkFound,
340     std::vector<std::string>& chunks, char& chunkTnf, const std::string& payload)
341 {
342     // handle for the first chunk.
343     if (layout.cf && !isChunkFound) {
344         chunks.clear();
345         chunkTnf = layout.tnf;
346     }
347 
348     // save the payload for all(first/middle/last) chunk.
349     if (layout.cf || isChunkFound) {
350         chunks.push_back(payload);
351     }
352 }
353 
MergePayloadByChunks(RecordLayout & layout,bool isChunkFound,std::vector<std::string> & chunks,char chunkTnf,const std::string & payload)354 std::string NdefMessage::MergePayloadByChunks(RecordLayout& layout, bool isChunkFound,
355     std::vector<std::string>& chunks, char chunkTnf, const std::string& payload)
356 {
357     // it's the last chunk, merge the payload for NdefRecord.
358     if (!layout.cf && isChunkFound) {
359         std::string mergedPayload;
360         for (std::string n : chunks) {
361             mergedPayload += n;
362         }
363         layout.tnf = chunkTnf;
364         return mergedPayload;
365     }
366     return payload;
367 }
368 
CreateNdefRecord(short tnf,const std::string & id,const std::string & payload,const std::string & tagRtdType)369 std::shared_ptr<NdefRecord> NdefMessage::CreateNdefRecord(short tnf, const std::string& id,
370     const std::string& payload, const std::string& tagRtdType)
371 {
372     bool isValidTnf = CheckTnf(tnf, tagRtdType, id, payload);
373     if (!isValidTnf) {
374         ErrorLog("CreateNdefRecord, isValidTnf failed.");
375         return std::shared_ptr<NdefRecord>();
376     }
377     std::shared_ptr<NdefRecord> ndefRecord = std::make_shared<NdefRecord>();
378     ndefRecord->tnf_ = tnf;
379     ndefRecord->id_ = id;
380     ndefRecord->payload_ = payload;
381     ndefRecord->tagRtdType_ = tagRtdType;
382     return ndefRecord;
383 }
384 
CheckTnf(short tnf,const std::string & tagRtdType,const std::string & id,const std::string & payload)385 bool NdefMessage::CheckTnf(short tnf, const std::string& tagRtdType, const std::string& id, const std::string& payload)
386 {
387     switch (tnf) {
388         case TNF_EMPTY:
389             if (!tagRtdType.empty() || !id.empty() || !payload.empty()) {
390                 ErrorLog("CheckTnf, TNF_EMPTY error.");
391                 return false;
392             }
393             break;
394         case TNF_WELL_KNOWN:
395         case TNF_MIME_MEDIA:
396         case TNF_ABSOLUTE_URI:
397         case TNF_EXTERNAL_TYPE:
398             return true;
399         case TNF_UNKNOWN:
400         case TNF_RESERVED:
401             if (tagRtdType.empty()) {
402                 return false;
403             }
404             return true;
405         case TNF_UNCHANGED:
406             return false;
407         default:
408             break;
409     }
410     return false;
411 }
412 
ParseRecord(const std::string & data,bool isMbMeIgnored)413 std::vector<std::shared_ptr<NdefRecord>> NdefMessage::ParseRecord(const std::string& data, bool isMbMeIgnored)
414 {
415     std::vector<std::shared_ptr<NdefRecord>> recordList;
416     if (data.empty()) {
417         ErrorLog("ParseRecord, raw data empty.");
418         return recordList;
419     }
420 
421     std::string tagRtdType;
422     std::string id;
423     std::vector<std::string> chunks;
424     bool isChunkFound = false;
425     char chunkTnf = 0;
426     bool isMessageEnd = false;
427     uint32_t parsedDataIndex = 0;
428     while (!isMessageEnd) {
429         RecordLayout layout;
430         ParseRecordLayoutHead(layout, NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++));
431         isMessageEnd = layout.me;
432 
433         if (data.size() / HEX_BYTE_LEN < parsedDataIndex) {
434             return recordList;
435         }
436         if ((data.size() / HEX_BYTE_LEN - parsedDataIndex) < MIN_RECORD_LEN && !isMessageEnd) {
437             return recordList;
438         }
439         if (IsInvalidRecordLayoutHead(layout, isChunkFound, recordList.size(), isMbMeIgnored)) {
440             return recordList;
441         }
442 
443         ParseRecordLayoutLength(layout, isChunkFound, data, parsedDataIndex);
444 
445         if (IsRecordLayoutLengthInvalid(layout, isChunkFound)) {
446             return recordList;
447         }
448 
449         if (!isChunkFound) {
450             // don't parse the type and id for the middle chunks record, allowed them tobe empty.
451             tagRtdType = ParseRecordType(layout, data, parsedDataIndex);
452             id = ParseRecordId(layout, data, parsedDataIndex);
453         }
454 
455         // parse the payload.
456         std::string payload = ParseRecordPayload(layout, data, parsedDataIndex);
457         SaveRecordChunks(layout, isChunkFound, chunks, chunkTnf, payload);
458         payload = MergePayloadByChunks(layout, isChunkFound, chunks, chunkTnf, payload);
459         if (NfcSdkCommon::GetHexStrBytesLen(payload) > MAX_PAYLOAD_SIZE) {
460             ErrorLog("ParseRecord, payload > MAX_PAYLOAD_SIZE");
461             return recordList;
462         }
463 
464         // if not the last chunk, continue to parse again.
465         isChunkFound = layout.cf;
466         if (isChunkFound) {
467             continue;
468         }
469 
470         // all chunks parsed end, add a new NdefRecord.
471         std::shared_ptr<NdefRecord> record = CreateNdefRecord(layout.tnf, id, payload, tagRtdType);
472         recordList.push_back(record);
473 
474         // isMbMeIgnored is true, means that single record need tobe parsed.
475         if (isMbMeIgnored) {
476             break;
477         }
478     }
479     return recordList;
480 }
481 }  // namespace KITS
482 }  // namespace NFC
483 }  // namespace OHOS
484