/** * @file llservicebuilder.cpp * @brief Implementation of the LLServiceBuilder class. * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "linden_common.h" #include "llapp.h" #include "llfile.h" #include "llservicebuilder.h" #include "llsd.h" #include "llsdserialize.h" void LLServiceBuilder::loadServiceDefinitionsFromFile( const std::string& service_filename) { llifstream service_file(service_filename, std::ios::binary); if(service_file.is_open()) { LLSD service_data; LLSDSerialize::fromXMLDocument(service_data, service_file); service_file.close(); // Load service LLSD service_map = service_data["services"]; for(LLSD::array_iterator array_itr = service_map.beginArray(); array_itr != service_map.endArray(); ++array_itr) { LLSD service_llsd = (*array_itr)["service-builder"]; std::string service_name = (*array_itr)["name"].asString(); createServiceDefinition(service_name, service_llsd); } llinfos << "loaded config file: " << service_filename << llendl; } else { llwarns << "unable to find config file: " << service_filename << llendl; } } void LLServiceBuilder::createServiceDefinition( const std::string& service_name, LLSD& service_llsd) { if(service_llsd.isString()) { mServiceMap[ service_name ] = service_llsd.asString(); } else if(service_llsd.isMap()) { for(LLSD::map_iterator map_itr = service_llsd.beginMap(); map_itr != service_llsd.endMap(); ++map_itr) { std::string compound_builder_name = service_name; compound_builder_name.append("-"); compound_builder_name.append((*map_itr).first); mServiceMap[ compound_builder_name ] = (*map_itr).second.asString(); } } } static bool starts_with(const std::string& text, const char* prefix) { return text.substr(0, strlen(prefix)) == prefix; } // TODO: Build a real services.xml for windows development. // and remove the base_url logic below. std::string LLServiceBuilder::buildServiceURI(const std::string& service_name) const { std::ostringstream service_url; // Find the service builder std::map<std::string, std::string>::const_iterator it = mServiceMap.find(service_name); if(it != mServiceMap.end()) { // construct the service builder url LLApp* app = LLApp::instance(); if(app) { // We define a base-url for some development configurations // In production neither of these are defined and all services have full urls LLSD base_url; if (starts_with(service_name,"cap")) { base_url = app->getOption("cap-base-url"); } if (base_url.asString().empty()) { base_url = app->getOption("services-base-url"); } service_url << base_url.asString(); } service_url << it->second; } else { llwarns << "Cannot find service " << service_name << llendl; } return service_url.str(); } std::string LLServiceBuilder::buildServiceURI( const std::string& service_name, const LLSD& option_map) const { return russ_format(buildServiceURI(service_name), option_map); } std::string russ_format(const std::string& format_str, const LLSD& context) { std::string service_url(format_str); if(!service_url.empty() && context.isMap()) { // throw in a ridiculously large limiter to make sure we don't // loop forever with bad input. int iterations = 100; bool keep_looping = true; while(keep_looping) { if(0 == --iterations) { keep_looping = false; } int depth = 0; int deepest = 0; bool find_match = false; std::string::iterator iter(service_url.begin()); std::string::iterator end(service_url.end()); std::string::iterator deepest_node(service_url.end()); std::string::iterator deepest_node_end(service_url.end()); // parse out the variables to replace by going through {}s // one at a time, starting with the "deepest" in series // {{}}, and otherwise replacing right-to-left for(; iter != end; ++iter) { switch(*iter) { case '{': ++depth; if(depth > deepest) { deepest = depth; deepest_node = iter; find_match = true; } break; case '}': --depth; if(find_match) { deepest_node_end = iter; find_match = false; } break; default: break; } } if((deepest_node == end) || (deepest_node_end == end)) { break; } //replace the variable we found in the {} above. // *NOTE: since the c++ implementation only understands // params and straight string substitution, so it's a // known distance of 2 to skip the directive. std::string key(deepest_node + 2, deepest_node_end); LLSD value = context[key]; switch(*(deepest_node + 1)) { case '$': if(value.isDefined()) { service_url.replace( deepest_node, deepest_node_end + 1, value.asString()); } else { llwarns << "Unknown key: " << key << " in option map: " << LLSDOStreamer<LLSDNotationFormatter>(context) << llendl; keep_looping = false; } break; case '%': { std::string query_str = LLURI::mapToQueryString(value); service_url.replace( deepest_node, deepest_node_end + 1, query_str); } break; default: llinfos << "Unknown directive: " << *(deepest_node + 1) << llendl; keep_looping = false; break; } } } if (service_url.find('{') != std::string::npos) { llwarns << "Constructed a likely bogus service URL: " << service_url << llendl; } return service_url; }