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