1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "device/nfc/nfc_ndef_record.h"
9 #include "base/logging.h"
12 using base::DictionaryValue
;
13 using base::ListValue
;
19 typedef std::map
<std::string
, base::Value::Type
> FieldValueMap
;
21 bool ValidateURI(const DictionaryValue
* data
) {
23 if (!data
->GetString(NfcNdefRecord::kFieldURI
, &uri
)) {
24 VLOG(1) << "No URI entry in data.";
29 // Use GURL to check validity.
31 if (!url
.is_valid()) {
32 LOG(ERROR
) << "Invalid URI given: " << uri
;
38 bool CheckFieldsAreValid(
39 const FieldValueMap
& required_fields
,
40 const FieldValueMap
& optional_fields
,
41 const DictionaryValue
* data
) {
42 size_t required_count
= 0;
43 for (DictionaryValue::Iterator
iter(*data
);
44 !iter
.IsAtEnd(); iter
.Advance()) {
45 FieldValueMap::const_iterator field_iter
=
46 required_fields
.find(iter
.key());
47 if (field_iter
== required_fields
.end()) {
48 // Field wasn't one of the required fields. Check if optional.
49 field_iter
= optional_fields
.find(iter
.key());
51 if (field_iter
== optional_fields
.end()) {
52 // If the field isn't one of the optional fields either, then it's
54 VLOG(1) << "Tried to populate record with invalid field: "
61 // The field is invalid, if the type of its value is incorrect.
62 if (field_iter
->second
!= iter
.value().GetType()) {
63 VLOG(1) << "Provided value for field \"" << iter
.key() << "\" has type "
64 << iter
.value().GetType() << ", expected: "
65 << field_iter
->second
;
68 // Make sure that the value is non-empty, if the value is a string.
69 std::string string_value
;
70 if (iter
.value().GetAsString(&string_value
) && string_value
.empty()) {
71 VLOG(1) << "Empty value given for field of type string: " << iter
.key();
75 // Check for required fields.
76 if (required_count
!= required_fields
.size()) {
77 VLOG(1) << "Provided data did not contain all required fields for "
78 << "requested NDEF type.";
84 // Verifies that the contents of |data| conform to the fields of NDEF type
86 bool HandleTypeText(const DictionaryValue
* data
) {
87 VLOG(1) << "Populating record with type \"Text\".";
88 FieldValueMap required_fields
;
89 required_fields
[NfcNdefRecord::kFieldText
] = base::Value::TYPE_STRING
;
90 required_fields
[NfcNdefRecord::kFieldEncoding
] = base::Value::TYPE_STRING
;
91 required_fields
[NfcNdefRecord::kFieldLanguageCode
] = base::Value::TYPE_STRING
;
92 FieldValueMap optional_fields
;
93 if (!CheckFieldsAreValid(required_fields
, optional_fields
, data
)) {
94 VLOG(1) << "Failed to populate record.";
98 // Verify that the "Encoding" property has valid values.
100 if (!data
->GetString(NfcNdefRecord::kFieldEncoding
, &encoding
)) {
101 if (encoding
!= NfcNdefRecord::kEncodingUtf8
||
102 encoding
!= NfcNdefRecord::kEncodingUtf16
) {
103 VLOG(1) << "Invalid \"Encoding\" value:" << encoding
;
110 // Verifies that the contents of |data| conform to the fields of NDEF type
112 bool HandleTypeSmartPoster(const DictionaryValue
* data
) {
113 VLOG(1) << "Populating record with type \"SmartPoster\".";
114 FieldValueMap required_fields
;
115 required_fields
[NfcNdefRecord::kFieldURI
] = base::Value::TYPE_STRING
;
116 FieldValueMap optional_fields
;
117 optional_fields
[NfcNdefRecord::kFieldAction
] = base::Value::TYPE_STRING
;
118 optional_fields
[NfcNdefRecord::kFieldMimeType
] = base::Value::TYPE_STRING
;
119 // base::Value restricts the number types to BOOL, INTEGER, and DOUBLE only.
120 // uint32 will automatically get converted to a double. "target size" is
121 // really a uint32 but we define it as a double for this reason.
122 // (See dbus/values_util.h).
123 optional_fields
[NfcNdefRecord::kFieldTargetSize
] = base::Value::TYPE_DOUBLE
;
124 optional_fields
[NfcNdefRecord::kFieldTitles
] = base::Value::TYPE_LIST
;
125 if (!CheckFieldsAreValid(required_fields
, optional_fields
, data
)) {
126 VLOG(1) << "Failed to populate record.";
129 // Verify that the "titles" field was formatted correctly, if it exists.
130 const ListValue
* titles
= NULL
;
131 if (data
->GetList(NfcNdefRecord::kFieldTitles
, &titles
)) {
132 if (titles
->empty()) {
133 VLOG(1) << "\"titles\" field of SmartPoster is empty.";
136 for (ListValue::const_iterator iter
= titles
->begin();
137 iter
!= titles
->end(); ++iter
) {
138 const DictionaryValue
* title_data
= NULL
;
139 if (!(*iter
)->GetAsDictionary(&title_data
)) {
140 VLOG(1) << "\"title\" entry for SmartPoster contains an invalid value "
144 if (!HandleTypeText(title_data
)) {
145 VLOG(1) << "Badly formatted \"title\" entry for SmartPoster.";
150 return ValidateURI(data
);
153 // Verifies that the contents of |data| conform to the fields of NDEF type
155 bool HandleTypeUri(const DictionaryValue
* data
) {
156 VLOG(1) << "Populating record with type \"URI\".";
157 FieldValueMap required_fields
;
158 required_fields
[NfcNdefRecord::kFieldURI
] = base::Value::TYPE_STRING
;
159 FieldValueMap optional_fields
;
160 optional_fields
[NfcNdefRecord::kFieldMimeType
] = base::Value::TYPE_STRING
;
161 optional_fields
[NfcNdefRecord::kFieldTargetSize
] = base::Value::TYPE_DOUBLE
;
163 // Allow passing TargetSize as an integer, but convert it to a double.
164 if (!CheckFieldsAreValid(required_fields
, optional_fields
, data
)) {
165 VLOG(1) << "Failed to populate record.";
168 return ValidateURI(data
);
174 const char NfcNdefRecord::kFieldEncoding
[] = "encoding";
176 const char NfcNdefRecord::kFieldLanguageCode
[] = "languageCode";
178 const char NfcNdefRecord::kFieldText
[] = "text";
180 const char NfcNdefRecord::kFieldURI
[] = "uri";
182 const char NfcNdefRecord::kFieldMimeType
[] = "mimeType";
184 const char NfcNdefRecord::kFieldTargetSize
[] = "targetSize";
186 const char NfcNdefRecord::kFieldTitles
[] = "titles";
188 const char NfcNdefRecord::kFieldAction
[] = "action";
190 const char NfcNdefRecord::kEncodingUtf8
[] = "UTF-8";
192 const char NfcNdefRecord::kEncodingUtf16
[] = "UTF-16";
194 const char NfcNdefRecord::kSmartPosterActionDo
[] = "do";
196 const char NfcNdefRecord::kSmartPosterActionSave
[] = "save";
198 const char NfcNdefRecord::kSmartPosterActionOpen
[] = "open";
200 NfcNdefRecord::NfcNdefRecord() : type_(kTypeUnknown
) {
203 NfcNdefRecord::~NfcNdefRecord() {
206 bool NfcNdefRecord::IsPopulated() const {
207 return type_
!= kTypeUnknown
;
210 bool NfcNdefRecord::Populate(Type type
, const DictionaryValue
* data
) {
214 DCHECK(data_
.empty());
216 // At this time, only "Text", "URI", and "SmartPoster" are supported.
220 result
= HandleTypeText(data
);
222 case kTypeSmartPoster
:
223 result
= HandleTypeSmartPoster(data
);
226 result
= HandleTypeUri(data
);
229 VLOG(1) << "Unsupported NDEF type: " << type
;
235 data_
.MergeDictionary(data
);
239 NfcNdefMessage::NfcNdefMessage() {
242 NfcNdefMessage::~NfcNdefMessage() {
245 void NfcNdefMessage::AddRecord(NfcNdefRecord
* record
) {
246 records_
.push_back(record
);
249 bool NfcNdefMessage::RemoveRecord(NfcNdefRecord
* record
) {
250 for (RecordList::iterator iter
= records_
.begin();
251 iter
!= records_
.end(); ++iter
) {
252 if (*iter
== record
) {
253 records_
.erase(iter
);
260 } // namespace device