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