• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MakeApplicationRecord(const std::string & packageName)166 std::shared_ptr<NdefRecord> NdefMessage::MakeApplicationRecord(const std::string& packageName)
167 {
168     if (packageName.empty()) {
169         ErrorLog("MakeApplicationRecord, packageName is null.");
170         return std::shared_ptr<NdefRecord>();
171     }
172 
173     std::string id = "";
174     std::string tagRtdType = NfcSdkCommon::StringToHexString(GetTagRtdType(EmRtdType::RTD_OHOS_APP));
175     return CreateNdefRecord(TNF_EXTERNAL_TYPE, id, NfcSdkCommon::StringToHexString(packageName), tagRtdType);
176 }
177 
MessageToString(std::weak_ptr<NdefMessage> ndefMessage)178 std::string NdefMessage::MessageToString(std::weak_ptr<NdefMessage> ndefMessage)
179 {
180     std::string buffer;
181     if (ndefMessage.expired()) {
182         ErrorLog("MessageToString, ndefMessage invalid.");
183         return buffer;
184     }
185     for (size_t i = 0; i < ndefMessage.lock()->ndefRecordList_.size(); i++) {
186         bool bIsMB = (i == 0);                                                // first record
187         bool bIsME = (i == ndefMessage.lock()->ndefRecordList_.size() - 1);  // last record
188         NdefRecordToString(ndefMessage.lock()->ndefRecordList_.at(i), buffer, bIsMB, bIsME);
189     }
190     return buffer;
191 }
192 
NdefRecordToString(std::weak_ptr<NdefRecord> record,std::string & buffer,bool bIsMB,bool bIsME)193 void NdefMessage::NdefRecordToString(std::weak_ptr<NdefRecord> record, std::string& buffer, bool bIsMB, bool bIsME)
194 {
195     if (record.expired()) {
196         ErrorLog("NdefRecordToString, record invalid.");
197         return;
198     }
199     std::string payload = record.lock()->payload_;
200     uint32_t tnf = record.lock()->tnf_;
201     std::string id = record.lock()->id_;
202     std::string rtdType = record.lock()->tagRtdType_;
203     bool sr = NfcSdkCommon::GetHexStrBytesLen(payload) < SHORT_RECORD_SIZE;
204     bool il = (tnf == TNF_EMPTY) ? true : (NfcSdkCommon::GetHexStrBytesLen(id) > 0);
205     unsigned char flag = (unsigned char)((bIsMB ? FLAG_MB : 0) | (bIsME ? FLAG_ME : 0)
206         | (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0)) | (char)tnf;
207     buffer.append(NfcSdkCommon::UnsignedCharToHexString(flag));
208     buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(rtdType)));
209     if (sr) {
210         buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(payload)));
211     } else {
212         buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(payload)));
213     }
214     if (il) {
215         buffer.append(NfcSdkCommon::IntToHexString(NfcSdkCommon::GetHexStrBytesLen(id)));
216     }
217 
218     buffer.append(rtdType);
219     buffer.append(id);
220     buffer.append(payload);
221 }
222 
ParseRecordLayoutHead(RecordLayout & layout,unsigned char head)223 void NdefMessage::ParseRecordLayoutHead(RecordLayout& layout, unsigned char head)
224 {
225     layout.mb = (head & FLAG_MB) != 0;
226     layout.me = (head & FLAG_ME) != 0;
227     layout.cf = (head & FLAG_CF) != 0;
228     layout.sr = (head & FLAG_SR) != 0;
229     layout.il = (head & FLAG_IL) != 0;
230     layout.tnf = static_cast<short>(head & FLAG_TNF);
231 }
232 
IsInvalidRecordLayoutHead(RecordLayout & layout,bool isChunkFound,uint32_t parsedRecordSize,bool isMbMeIgnored)233 bool NdefMessage::IsInvalidRecordLayoutHead(RecordLayout& layout, bool isChunkFound,
234     uint32_t parsedRecordSize, bool isMbMeIgnored)
235 {
236     if (!layout.mb && parsedRecordSize == 0 && !isChunkFound && !isMbMeIgnored) {
237         ErrorLog("IsInvalidRecordLayoutHead, 1st error for mb and size.");
238         return true;
239     } else if (layout.mb && (parsedRecordSize != 0 || isChunkFound) && !isMbMeIgnored) {
240         ErrorLog("IsInvalidRecordLayoutHead, 2nd error for mb and size");
241         return true;
242     } else if (isChunkFound && layout.il) {
243         ErrorLog("IsInvalidRecordLayoutHead, 3rd error for il");
244         return true;
245     } else if (layout.cf && layout.me) {
246         ErrorLog("IsInvalidRecordLayoutHead, 4th error for cf and me");
247         return true;
248     } else if (isChunkFound && layout.tnf != TNF_UNCHANGED) {
249         ErrorLog("IsInvalidRecordLayoutHead, 5th error for tnf");
250         return true;
251     } else if (!isChunkFound && layout.tnf == TNF_UNCHANGED) {
252         ErrorLog("IsInvalidRecordLayoutHead, 6th error for tnf");
253         return true;
254     }
255     return false;
256 }
257 
ParseRecordLayoutLength(RecordLayout & layout,bool isChunkFound,const std::string & data,uint32_t & parsedDataIndex)258 void NdefMessage::ParseRecordLayoutLength(RecordLayout& layout, bool isChunkFound,
259     const std::string& data, uint32_t& parsedDataIndex)
260 {
261     layout.typeLength = NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++) & 0xFF;
262     if (layout.sr) {
263         layout.payloadLength = NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++) & 0xFF;
264     } else {
265         if (NfcSdkCommon::GetHexStrBytesLen(data) < parsedDataIndex + int(sizeof(int))) {
266             layout.payloadLength = 0;
267         } else {
268             std::string lenString = data.substr(parsedDataIndex * HEX_BYTE_LEN, sizeof(int) * HEX_BYTE_LEN);
269             layout.payloadLength = 0;
270             for (unsigned int i = 0; i < sizeof(int); i++) {
271                 layout.payloadLength +=
272                     (NfcSdkCommon::GetByteFromHexStr(lenString, i) << ((sizeof(int) - i - 1) * ONE_BYTE_SHIFT));
273             }
274             parsedDataIndex += sizeof(int);
275         }
276     }
277     layout.idLength = layout.il ? (NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++) & 0xFF) : 0;
278 }
279 
IsRecordLayoutLengthInvalid(RecordLayout & layout,bool isChunkFound)280 bool NdefMessage::IsRecordLayoutLengthInvalid(RecordLayout& layout, bool isChunkFound)
281 {
282     // for the middle chunks record, need the type length is zero.
283     if (isChunkFound && layout.typeLength != 0) {
284         ErrorLog("IsInvalidRecordLayoutHead, 1st error for typeLength");
285         return true;
286     }
287 
288     // for the first chunk, expected has type.
289     if (layout.cf && !isChunkFound) {
290         if (layout.typeLength == 0 && layout.tnf != TNF_UNKNOWN) {
291             ErrorLog("IsInvalidRecordLayoutHead, 2nd error for typeLength and tnf");
292             return true;
293         }
294     }
295 
296     if (layout.payloadLength > MAX_PAYLOAD_SIZE) {
297         ErrorLog("IsInvalidRecordLayoutHead, 3rd error for payloadLength");
298         return true;
299     }
300     return false;
301 }
302 
ParseRecordType(RecordLayout & layout,const std::string & data,uint32_t & parsedDataIndex)303 std::string NdefMessage::ParseRecordType(RecordLayout& layout, const std::string& data, uint32_t& parsedDataIndex)
304 {
305     if (layout.typeLength <= 0) {
306         ErrorLog("IsInvalidRecordLayoutHead, typeLength less than 0.");
307         return "";
308     }
309     if (NfcSdkCommon::GetHexStrBytesLen(data) < parsedDataIndex + layout.typeLength) {
310         ErrorLog("data len.%{public}d index.%{public}d rtdtype len.%{public}d error",
311             NfcSdkCommon::GetHexStrBytesLen(data), parsedDataIndex, layout.typeLength);
312         return "";
313     }
314     std::string type = data.substr(parsedDataIndex * HEX_BYTE_LEN, layout.typeLength * HEX_BYTE_LEN);
315     parsedDataIndex += layout.typeLength;
316     return type;
317 }
318 
ParseRecordId(RecordLayout & layout,const std::string & data,uint32_t & parsedDataIndex)319 std::string NdefMessage::ParseRecordId(RecordLayout& layout, const std::string& data, uint32_t& parsedDataIndex)
320 {
321     if (layout.idLength <= 0) {
322         ErrorLog("ParseRecordId, idLength <= 0");
323         return "";
324     }
325     if (NfcSdkCommon::GetHexStrBytesLen(data) < parsedDataIndex + layout.idLength) {
326         ErrorLog("data len.%{public}d index.%{public}d id len.%{public}d error",
327             NfcSdkCommon::GetHexStrBytesLen(data), parsedDataIndex, layout.idLength);
328         return "";
329     }
330     std::string id = data.substr(parsedDataIndex * HEX_BYTE_LEN, layout.idLength * HEX_BYTE_LEN);
331     parsedDataIndex += layout.idLength;
332     return id;
333 }
334 
ParseRecordPayload(RecordLayout & layout,const std::string & data,uint32_t & parsedDataIndex)335 std::string NdefMessage::ParseRecordPayload(RecordLayout& layout, const std::string& data, uint32_t& parsedDataIndex)
336 {
337     if (layout.payloadLength <= 0) {
338         ErrorLog("ParseRecordPayload, payloadLength <= 0");
339         return "";
340     }
341     if (NfcSdkCommon::GetHexStrBytesLen(data) < (parsedDataIndex + layout.payloadLength)) {
342         ErrorLog("data len.%{public}d index.%{public}d payload len.%{public}d error",
343             NfcSdkCommon::GetHexStrBytesLen(data), parsedDataIndex, layout.payloadLength);
344         return "";
345     }
346     std::string payload = data.substr(parsedDataIndex * HEX_BYTE_LEN, layout.payloadLength * HEX_BYTE_LEN);
347     parsedDataIndex += layout.payloadLength;
348     return payload;
349 }
350 
SaveRecordChunks(RecordLayout & layout,bool isChunkFound,std::vector<std::string> & chunks,char & chunkTnf,const std::string & payload)351 void NdefMessage::SaveRecordChunks(RecordLayout& layout, bool isChunkFound,
352     std::vector<std::string>& chunks, char& chunkTnf, const std::string& payload)
353 {
354     // handle for the first chunk.
355     if (layout.cf && !isChunkFound) {
356         chunks.clear();
357         chunkTnf = layout.tnf;
358     }
359 
360     // save the payload for all(first/middle/last) chunk.
361     if (layout.cf || isChunkFound) {
362         chunks.push_back(payload);
363     }
364 }
365 
MergePayloadByChunks(RecordLayout & layout,bool isChunkFound,std::vector<std::string> & chunks,char chunkTnf,const std::string & payload)366 std::string NdefMessage::MergePayloadByChunks(RecordLayout& layout, bool isChunkFound,
367     std::vector<std::string>& chunks, char chunkTnf, const std::string& payload)
368 {
369     // it's the last chunk, merge the payload for NdefRecord.
370     if (!layout.cf && isChunkFound) {
371         std::string mergedPayload;
372         for (std::string n : chunks) {
373             mergedPayload += n;
374         }
375         layout.tnf = chunkTnf;
376         return mergedPayload;
377     }
378     return payload;
379 }
380 
CreateNdefRecord(short tnf,const std::string & id,const std::string & payload,const std::string & tagRtdType)381 std::shared_ptr<NdefRecord> NdefMessage::CreateNdefRecord(short tnf, const std::string& id,
382     const std::string& payload, const std::string& tagRtdType)
383 {
384     bool isValidTnf = CheckTnf(tnf, tagRtdType, id, payload);
385     if (!isValidTnf) {
386         ErrorLog("CreateNdefRecord, isValidTnf failed.");
387         return std::shared_ptr<NdefRecord>();
388     }
389     std::shared_ptr<NdefRecord> ndefRecord = std::make_shared<NdefRecord>();
390     ndefRecord->tnf_ = tnf;
391     ndefRecord->id_ = id;
392     ndefRecord->payload_ = payload;
393     ndefRecord->tagRtdType_ = tagRtdType;
394     return ndefRecord;
395 }
396 
CheckTnf(short tnf,const std::string & tagRtdType,const std::string & id,const std::string & payload)397 bool NdefMessage::CheckTnf(short tnf, const std::string& tagRtdType, const std::string& id, const std::string& payload)
398 {
399     switch (tnf) {
400         case TNF_EMPTY:
401             if (!tagRtdType.empty() || !id.empty() || !payload.empty()) {
402                 ErrorLog("CheckTnf, TNF_EMPTY error.");
403                 return false;
404             }
405             break;
406         case TNF_WELL_KNOWN:
407         case TNF_MIME_MEDIA:
408         case TNF_ABSOLUTE_URI:
409         case TNF_EXTERNAL_TYPE:
410             return true;
411         case TNF_UNKNOWN:
412         case TNF_RESERVED:
413             if (tagRtdType.empty()) {
414                 return false;
415             }
416             return true;
417         case TNF_UNCHANGED:
418             return false;
419         default:
420             break;
421     }
422     return false;
423 }
424 
ParseRecord(const std::string & data,bool isMbMeIgnored)425 std::vector<std::shared_ptr<NdefRecord>> NdefMessage::ParseRecord(const std::string& data, bool isMbMeIgnored)
426 {
427     std::vector<std::shared_ptr<NdefRecord>> recordList;
428     if (data.empty()) {
429         ErrorLog("ParseRecord, raw data empty.");
430         return recordList;
431     }
432 
433     std::string tagRtdType;
434     std::string id;
435     std::vector<std::string> chunks;
436     bool isChunkFound = false;
437     char chunkTnf = 0;
438     bool isMessageEnd = false;
439     uint32_t parsedDataIndex = 0;
440     while (!isMessageEnd) {
441         RecordLayout layout;
442         ParseRecordLayoutHead(layout, NfcSdkCommon::GetByteFromHexStr(data, parsedDataIndex++));
443         isMessageEnd = layout.me;
444 
445         if (data.size() / HEX_BYTE_LEN < parsedDataIndex) {
446             return recordList;
447         }
448         if ((data.size() / HEX_BYTE_LEN - parsedDataIndex) < MIN_RECORD_LEN && !isMessageEnd) {
449             return recordList;
450         }
451         if (IsInvalidRecordLayoutHead(layout, isChunkFound, recordList.size(), isMbMeIgnored)) {
452             return recordList;
453         }
454 
455         ParseRecordLayoutLength(layout, isChunkFound, data, parsedDataIndex);
456 
457         if (IsRecordLayoutLengthInvalid(layout, isChunkFound)) {
458             return recordList;
459         }
460 
461         if (!isChunkFound) {
462             // don't parse the type and id for the middle chunks record, allowed them tobe empty.
463             tagRtdType = ParseRecordType(layout, data, parsedDataIndex);
464             id = ParseRecordId(layout, data, parsedDataIndex);
465         }
466 
467         // parse the payload.
468         std::string payload = ParseRecordPayload(layout, data, parsedDataIndex);
469         SaveRecordChunks(layout, isChunkFound, chunks, chunkTnf, payload);
470         payload = MergePayloadByChunks(layout, isChunkFound, chunks, chunkTnf, payload);
471         if (NfcSdkCommon::GetHexStrBytesLen(payload) > MAX_PAYLOAD_SIZE) {
472             ErrorLog("ParseRecord, payload > MAX_PAYLOAD_SIZE");
473             return recordList;
474         }
475 
476         // if not the last chunk, continue to parse again.
477         isChunkFound = layout.cf;
478         if (isChunkFound) {
479             continue;
480         }
481 
482         // all chunks parsed end, add a new NdefRecord.
483         std::shared_ptr<NdefRecord> record = CreateNdefRecord(layout.tnf, id, payload, tagRtdType);
484         recordList.push_back(record);
485 
486         // isMbMeIgnored is true, means that single record need tobe parsed.
487         if (isMbMeIgnored) {
488             break;
489         }
490     }
491     return recordList;
492 }
493 }  // namespace KITS
494 }  // namespace NFC
495 }  // namespace OHOS
496