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 19namespace OHOS { 20namespace NFC { 21namespace KITS { 22using KITS::NfcSdkCommon; 23NdefMessage::NdefMessage(std::vector<std::shared_ptr<NdefRecord>> ndefRecords) 24 : ndefRecordList_(std::move(ndefRecords)) 25{ 26} 27 28NdefMessage::~NdefMessage() 29{ 30 ndefRecordList_.clear(); 31} 32 33std::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 43std::shared_ptr<NdefMessage> NdefMessage::GetNdefMessage(std::vector<std::shared_ptr<NdefRecord>> ndefRecords) 44{ 45 return std::make_shared<NdefMessage>(std::move(ndefRecords)); 46} 47 48std::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 83std::vector<std::shared_ptr<NdefRecord>> NdefMessage::GetNdefRecords() const 84{ 85 return ndefRecordList_; 86} 87 88std::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 112std::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 123std::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 138std::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 166std::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 181void 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 211void 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 221bool 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 246void 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 268bool 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 291std::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 307std::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 323std::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 339void 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 354std::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 369std::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 385bool 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 413std::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