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. 4 5 #include "device/nfc/nfc_ndef_record.h" 6 7 #include <map> 8 9 #include "base/logging.h" 10 11 using base::DictionaryValue; 12 using base::ListValue; 13 14 namespace device { 15 16 namespace { 17 18 typedef std::map<std::string, base::Value::Type> FieldValueMap; 19 20 bool CheckFieldsAreValid( 21 const FieldValueMap& required_fields, 22 const FieldValueMap& optional_fields, 23 const DictionaryValue* data) { 24 size_t required_count = 0; 25 for (DictionaryValue::Iterator iter(*data); 26 !iter.IsAtEnd(); iter.Advance()) { 27 FieldValueMap::const_iterator field_iter = 28 required_fields.find(iter.key()); 29 if (field_iter == required_fields.end()) { 30 // Field wasn't one of the required fields. Check if optional. 31 field_iter = optional_fields.find(iter.key()); 32 33 if (field_iter == optional_fields.end()) { 34 // If the field isn't one of the optional fields either, then it's 35 // invalid. 36 VLOG(1) << "Tried to populate record with invalid field: " 37 << iter.key(); 38 return false; 39 } 40 } else { 41 required_count++; 42 } 43 // The field is invalid, if the type of its value is incorrect. 44 if (field_iter->second != iter.value().GetType()) { 45 VLOG(1) << "Provided value for field \"" << iter.key() << "\" has type " 46 << iter.value().GetType() << ", expected: " 47 << field_iter->second; 48 return false; 49 } 50 } 51 // Check for required fields. 52 if (required_count != required_fields.size()) { 53 VLOG(1) << "Provided data did not contain all required fields for " 54 << "requested NDEF type."; 55 return false; 56 } 57 return true; 58 } 59 60 // Verifies that the contents of |data| conform to the fields of NDEF type 61 // "Text". 62 bool HandleTypeText(const DictionaryValue* data) { 63 VLOG(1) << "Populating record with type \"Text\"."; 64 FieldValueMap required_fields; 65 required_fields[NfcNdefRecord::kFieldText] = base::Value::TYPE_STRING; 66 FieldValueMap optional_fields; 67 optional_fields[NfcNdefRecord::kFieldEncoding] = base::Value::TYPE_STRING; 68 optional_fields[NfcNdefRecord::kFieldLanguageCode] = base::Value::TYPE_STRING; 69 if (!CheckFieldsAreValid(required_fields, optional_fields, data)) { 70 VLOG(1) << "Failed to populate record."; 71 return false; 72 } 73 return true; 74 } 75 76 // Verifies that the contents of |data| conform to the fields of NDEF type 77 // "SmartPoster". 78 bool HandleTypeSmartPoster(const DictionaryValue* data) { 79 VLOG(1) << "Populating record with type \"SmartPoster\"."; 80 FieldValueMap required_fields; 81 required_fields[NfcNdefRecord::kFieldURI] = base::Value::TYPE_STRING; 82 FieldValueMap optional_fields; 83 optional_fields[NfcNdefRecord::kFieldAction] = base::Value::TYPE_STRING; 84 optional_fields[NfcNdefRecord::kFieldMimeType] = base::Value::TYPE_STRING; 85 // base::Value restricts the number types to BOOL, INTEGER, and DOUBLE only. 86 // uint32 will automatically get converted to a double. "target size" is 87 // really a uint32 but we define it as a double for this reason. 88 // (See dbus/values_util.h). 89 optional_fields[NfcNdefRecord::kFieldTargetSize] = base::Value::TYPE_DOUBLE; 90 optional_fields[NfcNdefRecord::kFieldTitles] = base::Value::TYPE_LIST; 91 if (!CheckFieldsAreValid(required_fields, optional_fields, data)) { 92 VLOG(1) << "Failed to populate record."; 93 return false; 94 } 95 // Verify that the "titles" field was formatted correctly, if it exists. 96 const ListValue* titles = NULL; 97 if (data->GetList(NfcNdefRecord::kFieldTitles, &titles)) { 98 if (titles->empty()) { 99 VLOG(1) << "\"titles\" field of SmartPoster is empty."; 100 return false; 101 } 102 for (ListValue::const_iterator iter = titles->begin(); 103 iter != titles->end(); ++iter) { 104 const DictionaryValue* title_data = NULL; 105 if (!(*iter)->GetAsDictionary(&title_data)) { 106 VLOG(1) << "\"title\" entry for SmartPoster contains an invalid value " 107 << "type"; 108 return false; 109 } 110 if (!HandleTypeText(title_data)) { 111 VLOG(1) << "Badly formatted \"title\" entry for SmartPoster."; 112 return false; 113 } 114 } 115 } 116 return true; 117 } 118 119 // Verifies that the contents of |data| conform to the fields of NDEF type 120 // "URI". 121 bool HandleTypeUri(const DictionaryValue* data) { 122 VLOG(1) << "Populating record with type \"URI\"."; 123 FieldValueMap required_fields; 124 required_fields[NfcNdefRecord::kFieldURI] = base::Value::TYPE_STRING; 125 FieldValueMap optional_fields; 126 optional_fields[NfcNdefRecord::kFieldMimeType] = base::Value::TYPE_STRING; 127 optional_fields[NfcNdefRecord::kFieldTargetSize] = base::Value::TYPE_DOUBLE; 128 if (!CheckFieldsAreValid(required_fields, optional_fields, data)) { 129 VLOG(1) << "Failed to populate record."; 130 return false; 131 } 132 return true; 133 } 134 135 } // namespace 136 137 // static 138 const char NfcNdefRecord::kFieldEncoding[] = "encoding"; 139 // static 140 const char NfcNdefRecord::kFieldLanguageCode[] = "languageCode"; 141 // static 142 const char NfcNdefRecord::kFieldText[] = "text"; 143 // static 144 const char NfcNdefRecord::kFieldURI[] = "uri"; 145 // static 146 const char NfcNdefRecord::kFieldMimeType[] = "mimeType"; 147 // static 148 const char NfcNdefRecord::kFieldTargetSize[] = "targetSize"; 149 // static 150 const char NfcNdefRecord::kFieldTitles[] = "titles"; 151 // static 152 const char NfcNdefRecord::kFieldAction[] = "action"; 153 // static 154 const char NfcNdefRecord::kEncodingUtf8[] = "UTF-8"; 155 // static 156 const char NfcNdefRecord::kEncodingUtf16[] = "UTF-16"; 157 // static 158 const char NfcNdefRecord::kSmartPosterActionDo[] = "do"; 159 // static 160 const char NfcNdefRecord::kSmartPosterActionSave[] = "save"; 161 // static 162 const char NfcNdefRecord::kSmartPosterActionOpen[] = "open"; 163 164 NfcNdefRecord::NfcNdefRecord() : type_(kTypeUnknown) { 165 } 166 167 NfcNdefRecord::~NfcNdefRecord() { 168 } 169 170 bool NfcNdefRecord::IsPopulated() const { 171 return type_ != kTypeUnknown; 172 } 173 174 bool NfcNdefRecord::Populate(Type type, const DictionaryValue* data) { 175 if (IsPopulated()) 176 return false; 177 178 DCHECK(data_.empty()); 179 180 // At this time, only "Text", "URI", and "SmartPoster" are supported. 181 bool result = false; 182 switch (type) { 183 case kTypeText: 184 result = HandleTypeText(data); 185 break; 186 case kTypeSmartPoster: 187 result = HandleTypeSmartPoster(data); 188 break; 189 case kTypeURI: 190 result = HandleTypeUri(data); 191 break; 192 default: 193 VLOG(1) << "Unsupported NDEF type: " << type; 194 break; 195 } 196 if (!result) 197 return false; 198 type_ = type; 199 data_.MergeDictionary(data); 200 return true; 201 } 202 203 NfcNdefMessage::NfcNdefMessage() { 204 } 205 206 NfcNdefMessage::~NfcNdefMessage() { 207 } 208 209 void NfcNdefMessage::AddRecord(NfcNdefRecord* record) { 210 records_.push_back(record); 211 } 212 213 } // namespace device 214