void test_matches(const std::string& proto_key, const LLSD& possibles, const char** begin, const char** end) { std::set<std::string> succeed(begin, end); LLSD prototype(possibles[proto_key]); for (LLSD::map_const_iterator pi(possibles.beginMap()), pend(possibles.endMap()); pi != pend; ++pi) { std::string match(llsd_matches(prototype, pi->second)); std::set<std::string>::const_iterator found = succeed.find(pi->first); if (found != succeed.end()) { // This test is supposed to succeed. Comparing to the // empty string ensures that if the test fails, it will // display the string received so we can tell what failed. ensure_equals("match", match, ""); } else { // This test is supposed to fail. If we get a false match, // the string 'match' will be empty, which doesn't tell us // much about which case went awry. So construct a more // detailed description string. ensure(proto_key + " shouldn't match " + pi->first, ! match.empty()); } } }
LLSD validateResponse(const std::string& pumpName, const LLSD& response) { // Validate the response. If we don't recognize it, things // could get ugly. std::string mismatch(llsd_matches(mValidAuthResponse, response)); if (! mismatch.empty()) { LL_ERRS("LLLogin") << "Received unrecognized event (" << mismatch << ") on " << pumpName << "pump: " << response << LL_ENDL; return LLSD(); } return response; }
bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const { DispatchMap::const_iterator found = mDispatch.find(name); if (found == mDispatch.end()) { // The reason we only return false, leaving it up to our caller to die // with LL_ERRS, is that different callers have different amounts of // available information. return false; } // Found the name, so it's plausible to even attempt the call. But first, // validate the syntax of the event itself. std::string mismatch(llsd_matches(found->second.mRequired, event)); if (! mismatch.empty()) { LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name << "': bad request: " << mismatch << LL_ENDL; } // Event syntax looks good, go for it! (found->second.mFunc)(event); return true; // tell caller we were able to call }
bool LLWebSharing::validateConfig() { // Check the OpenID Cookie has been set. if (mOpenIDCookie.empty()) { mEnabled = false; return mEnabled; } if (!mConfig.isMap()) { mEnabled = false; return mEnabled; } // Template to match the received config against. LLSD required(LLSD::emptyMap()); required["gadgetSpecUrl"] = ""; required["loginTokenUrl"] = ""; required["openIdAuthUrl"] = ""; required["photoPageUrlTemplate"] = ""; required["openSocialRpcUrlTemplate"] = ""; required["securityTokenUrl"] = ""; required["tokenBasedLoginUrlTemplate"] = ""; required["viewerIdUrl"] = ""; std::string mismatch(llsd_matches(required, mConfig)); if (!mismatch.empty()) { LL_WARNS("WebSharing") << "Malformed config data response: " << mismatch << LL_ENDL; mEnabled = false; return mEnabled; } mEnabled = true; return mEnabled; }
void llsdutil_object::test<9>() { set_test_name("llsd_matches"); // for this test, construct a map of all possible LLSD types LLSD map; map.insert("empty", LLSD()); map.insert("Boolean", LLSD::Boolean()); map.insert("Integer", LLSD::Integer(0)); map.insert("Real", LLSD::Real(0.0)); map.insert("String", LLSD::String("bah")); map.insert("NumString", LLSD::String("1")); map.insert("UUID", LLSD::UUID()); map.insert("Date", LLSD::Date()); map.insert("URI", LLSD::URI()); map.insert("Binary", LLSD::Binary()); map.insert("Map", LLSD().with("foo", LLSD())); // Only an empty array can be constructed on the fly LLSD array; array.append(LLSD()); map.insert("Array", array); // These iterators are declared outside our various for loops to avoid // fatal MSVC warning: "I used to be broken, but I'm all better now!" LLSD::map_const_iterator mi, mend(map.endMap()); /*-------------------------- llsd_matches --------------------------*/ // empty prototype matches anything for (mi = map.beginMap(); mi != mend; ++mi) { ensure_equals(std::string("empty matches ") + mi->first, llsd_matches(LLSD(), mi->second), ""); } LLSD proto_array, data_array; for (int i = 0; i < 3; ++i) { proto_array.append(LLSD()); data_array.append(LLSD()); } // prototype array matches only array for (mi = map.beginMap(); mi != mend; ++mi) { ensure(std::string("array doesn't match ") + mi->first, ! llsd_matches(proto_array, mi->second).empty()); } // data array must be at least as long as prototype array proto_array.append(LLSD()); ensure_equals("data array too short", llsd_matches(proto_array, data_array), "Array size 4 required instead of Array size 3"); data_array.append(LLSD()); ensure_equals("data array just right", llsd_matches(proto_array, data_array), ""); data_array.append(LLSD()); ensure_equals("data array longer", llsd_matches(proto_array, data_array), ""); // array element matching data_array[0] = LLSD::String(); ensure_equals("undefined prototype array entry", llsd_matches(proto_array, data_array), ""); proto_array[0] = LLSD::Binary(); ensure_equals("scalar prototype array entry", llsd_matches(proto_array, data_array), "[0]: Binary required instead of String"); data_array[0] = LLSD::Binary(); ensure_equals("matching prototype array entry", llsd_matches(proto_array, data_array), ""); // build a coupla maps LLSD proto_map, data_map; data_map["got"] = LLSD(); data_map["found"] = LLSD(); for (LLSD::map_const_iterator dmi(data_map.beginMap()), dmend(data_map.endMap()); dmi != dmend; ++dmi) { proto_map[dmi->first] = dmi->second; } proto_map["foo"] = LLSD(); proto_map["bar"] = LLSD(); // prototype map matches only map for (mi = map.beginMap(); mi != mend; ++mi) { ensure(std::string("map doesn't match ") + mi->first, ! llsd_matches(proto_map, mi->second).empty()); } // data map must contain all keys in prototype map std::string error(llsd_matches(proto_map, data_map)); ensure_contains("missing keys", error, "missing keys"); ensure_contains("missing foo", error, "foo"); ensure_contains("missing bar", error, "bar"); ensure_does_not_contain("found found", error, "found"); ensure_does_not_contain("got got", error, "got"); data_map["bar"] = LLSD(); error = llsd_matches(proto_map, data_map); ensure_contains("missing foo", error, "foo"); ensure_does_not_contain("got bar", error, "bar"); data_map["foo"] = LLSD(); ensure_equals("data map just right", llsd_matches(proto_map, data_map), ""); data_map["extra"] = LLSD(); ensure_equals("data map with extra", llsd_matches(proto_map, data_map), ""); // map element matching data_map["foo"] = LLSD::String(); ensure_equals("undefined prototype map entry", llsd_matches(proto_map, data_map), ""); proto_map["foo"] = LLSD::Binary(); ensure_equals("scalar prototype map entry", llsd_matches(proto_map, data_map), "['foo']: Binary required instead of String"); data_map["foo"] = LLSD::Binary(); ensure_equals("matching prototype map entry", llsd_matches(proto_map, data_map), ""); // String { static const char* matches[] = { "String", "NumString", "Boolean", "Integer", "Real", "UUID", "Date", "URI" }; test_matches("String", map, boost::begin(matches), boost::end(matches)); } // Boolean, Integer, Real static const char* numerics[] = { "Boolean", "Integer", "Real" }; for (const char **ni = boost::begin(numerics), **nend = boost::end(numerics); ni != nend; ++ni) { static const char* matches[] = { "Boolean", "Integer", "Real", "String", "NumString" }; test_matches(*ni, map, boost::begin(matches), boost::end(matches)); } // UUID { static const char* matches[] = { "UUID", "String", "NumString" }; test_matches("UUID", map, boost::begin(matches), boost::end(matches)); } // Date { static const char* matches[] = { "Date", "String", "NumString" }; test_matches("Date", map, boost::begin(matches), boost::end(matches)); } // URI { static const char* matches[] = { "URI", "String", "NumString" }; test_matches("URI", map, boost::begin(matches), boost::end(matches)); } // Binary { static const char* matches[] = { "Binary" }; test_matches("Binary", map, boost::begin(matches), boost::end(matches)); } /*-------------------------- llsd_equals ---------------------------*/ // Cross-product of each LLSD type with every other for (LLSD::map_const_iterator lmi(map.beginMap()), lmend(map.endMap()); lmi != lmend; ++lmi) { for (LLSD::map_const_iterator rmi(map.beginMap()), rmend(map.endMap()); rmi != rmend; ++rmi) { // Name this test based on the map keys naming the types of // interest, e.g "String::Integer". // We expect the values (xmi->second) to be equal if and only // if the type names (xmi->first) are equal. ensure(STRINGIZE(lmi->first << "::" << rmi->first), bool(lmi->first == rmi->first) == bool(llsd_equals(lmi->second, rmi->second))); } } // Array cases LLSD rarray; rarray.append(1.0); rarray.append(2); rarray.append("3"); LLSD larray(rarray); ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray)); rarray[2] = "4"; ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray)); rarray = larray; rarray.append(LLSD::Date()); ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray)); rarray = larray; rarray.erase(2); ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray)); // Map cases LLSD rmap; rmap["San Francisco"] = 65; rmap["Phoenix"] = 92; rmap["Boston"] = 77; LLSD lmap(rmap); ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap)); rmap["Boston"] = 80; ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap)); rmap = lmap; rmap["Atlanta"] = 95; ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap)); rmap = lmap; lmap["Seattle"] = 72; ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap)); }
// see docstring in .h file std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx) { // An undefined prototype means that any data is valid. // An undefined slot in an array or map prototype means that any data // may fill that slot. if (prototype.isUndefined()) return ""; // A prototype array must match a data array with at least as many // entries. Moreover, every prototype entry must match the // corresponding data entry. if (prototype.isArray()) { if (! data.isArray()) { return STRINGIZE(colon(pfx) << "Array" << op << sTypes.lookup(data.type())); } if (data.size() < prototype.size()) { return STRINGIZE(colon(pfx) << "Array size " << prototype.size() << op << "Array size " << data.size()); } for (LLSD::Integer i = 0; i < prototype.size(); ++i) { std::string match(llsd_matches(prototype[i], data[i], STRINGIZE('[' << i << ']'))); if (! match.empty()) { return match; } } return ""; } // A prototype map must match a data map. Every key in the prototype // must have a corresponding key in the data map; every value in the // prototype must match the corresponding key's value in the data. if (prototype.isMap()) { if (! data.isMap()) { return STRINGIZE(colon(pfx) << "Map" << op << sTypes.lookup(data.type())); } // If there are a number of keys missing from the data, it would be // frustrating to a coder to discover them one at a time, with a big // build each time. Enumerate all missing keys. std::ostringstream out; out << colon(pfx); const char* init = "Map missing keys: "; const char* sep = init; for (LLSD::map_const_iterator mi = prototype.beginMap(); mi != prototype.endMap(); ++mi) { if (! data.has(mi->first)) { out << sep << mi->first; sep = ", "; } } // So... are we missing any keys? if (sep != init) { return out.str(); } // Good, the data block contains all the keys required by the // prototype. Now match the prototype entries. for (LLSD::map_const_iterator mi2 = prototype.beginMap(); mi2 != prototype.endMap(); ++mi2) { std::string match(llsd_matches(mi2->second, data[mi2->first], STRINGIZE("['" << mi2->first << "']"))); if (! match.empty()) { return match; } } return ""; } // A String prototype can match String, Boolean, Integer, Real, UUID, // Date and URI, because any of these can be converted to String. if (prototype.isString()) { static LLSD::Type accept[] = { LLSD::TypeBoolean, LLSD::TypeInteger, LLSD::TypeReal, LLSD::TypeUUID, LLSD::TypeDate, LLSD::TypeURI }; return match_types(prototype.type(), TypeVector(boost::begin(accept), boost::end(accept)), data.type(), pfx); } // Boolean, Integer, Real match each other or String. TBD: ensure that // a String value is numeric. if (prototype.isBoolean() || prototype.isInteger() || prototype.isReal()) { static LLSD::Type all[] = { LLSD::TypeBoolean, LLSD::TypeInteger, LLSD::TypeReal, LLSD::TypeString }; // Funny business: shuffle the set of acceptable types to include all // but the prototype's type. Get the acceptable types in a set. std::set<LLSD::Type> rest(boost::begin(all), boost::end(all)); // Remove the prototype's type because we pass that separately. rest.erase(prototype.type()); return match_types(prototype.type(), TypeVector(rest.begin(), rest.end()), data.type(), pfx); } // UUID, Date and URI match themselves or String. if (prototype.isUUID() || prototype.isDate() || prototype.isURI()) { static LLSD::Type accept[] = { LLSD::TypeString }; return match_types(prototype.type(), TypeVector(boost::begin(accept), boost::end(accept)), data.type(), pfx); } // We don't yet know the conversion semantics associated with any new LLSD // data type that might be added, so until we've been extended to handle // them, assume it's strict: the new type matches only itself. (This is // true of Binary, which is why we don't handle that case separately.) Too // bad LLSD doesn't define isConvertible(Type to, Type from). return match_types(prototype.type(), TypeVector(), data.type(), pfx); }