PageRenderTime 22ms CodeModel.GetById 1ms app.highlight 16ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llmessage/llservicebuilder.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 236 lines | 185 code | 10 blank | 41 comment | 29 complexity | b5e1756938a95f4a5809c53883cce710 MD5 | raw file
  1/** 
  2 * @file llservicebuilder.cpp
  3 * @brief Implementation of the LLServiceBuilder class.
  4 *
  5 * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28#include "llapp.h"
 29#include "llfile.h"
 30#include "llservicebuilder.h"
 31#include "llsd.h"
 32#include "llsdserialize.h"
 33
 34void LLServiceBuilder::loadServiceDefinitionsFromFile(
 35	const std::string& service_filename)
 36{
 37	llifstream service_file(service_filename, std::ios::binary);
 38	if(service_file.is_open())
 39	{
 40		LLSD service_data;
 41		LLSDSerialize::fromXMLDocument(service_data, service_file);
 42		service_file.close();
 43		// Load service 
 44		LLSD service_map = service_data["services"];
 45		for(LLSD::array_iterator array_itr = service_map.beginArray();
 46			array_itr != service_map.endArray();
 47			++array_itr)
 48		{	
 49			LLSD service_llsd = (*array_itr)["service-builder"];
 50			std::string service_name = (*array_itr)["name"].asString();
 51			createServiceDefinition(service_name, service_llsd);
 52		}
 53		llinfos << "loaded config file: " << service_filename << llendl;
 54	}
 55	else
 56	{
 57		llwarns << "unable to find config file: " << service_filename << llendl;
 58	}
 59}
 60
 61void LLServiceBuilder::createServiceDefinition(
 62	const std::string& service_name,
 63	LLSD& service_llsd)
 64{
 65	if(service_llsd.isString())
 66	{
 67		mServiceMap[ service_name ] = service_llsd.asString();
 68	}			
 69	else if(service_llsd.isMap())
 70	{
 71		for(LLSD::map_iterator map_itr = service_llsd.beginMap();
 72			map_itr != service_llsd.endMap();
 73			++map_itr)
 74		{
 75			std::string compound_builder_name = service_name;
 76			compound_builder_name.append("-");
 77			compound_builder_name.append((*map_itr).first);
 78			mServiceMap[ compound_builder_name ] = (*map_itr).second.asString();
 79		}
 80	}
 81}
 82
 83static
 84bool starts_with(const std::string& text, const char* prefix)
 85{
 86	return text.substr(0, strlen(prefix)) == prefix;
 87}
 88
 89// TODO: Build a real services.xml for windows development.
 90//       and remove the base_url logic below.
 91std::string LLServiceBuilder::buildServiceURI(const std::string& service_name) const
 92{
 93	std::ostringstream service_url;
 94	// Find the service builder
 95	std::map<std::string, std::string>::const_iterator it =
 96		mServiceMap.find(service_name);
 97	if(it != mServiceMap.end())
 98	{
 99		// construct the service builder url
100		LLApp* app = LLApp::instance();
101		if(app)
102		{
103			// We define a base-url for some development configurations
104			// In production neither of these are defined and all services have full urls
105			LLSD base_url;
106
107			if (starts_with(service_name,"cap"))
108			{
109				base_url = app->getOption("cap-base-url");
110			}
111
112			if (base_url.asString().empty())
113			{
114				base_url = app->getOption("services-base-url");
115			}
116			service_url << base_url.asString();
117		}
118		service_url << it->second;
119	}
120	else
121	{
122		llwarns << "Cannot find service " << service_name << llendl;
123	}
124	return service_url.str();
125}
126
127std::string LLServiceBuilder::buildServiceURI(
128	const std::string& service_name,
129	const LLSD& option_map) const
130{
131	return russ_format(buildServiceURI(service_name), option_map);
132}
133
134std::string russ_format(const std::string& format_str, const LLSD& context)
135{
136	std::string service_url(format_str);
137	if(!service_url.empty() && context.isMap())
138	{
139		// throw in a ridiculously large limiter to make sure we don't
140		// loop forever with bad input.
141		int iterations = 100;
142		bool keep_looping = true;
143		while(keep_looping)
144		{
145			if(0 == --iterations)
146			{
147				keep_looping = false;
148			}
149
150			int depth = 0;
151			int deepest = 0;
152			bool find_match = false;
153			std::string::iterator iter(service_url.begin());
154			std::string::iterator end(service_url.end());
155			std::string::iterator deepest_node(service_url.end());
156			std::string::iterator deepest_node_end(service_url.end());
157			// parse out the variables to replace by going through {}s
158			// one at a time, starting with the "deepest" in series
159			// {{}}, and otherwise replacing right-to-left
160			for(; iter != end; ++iter)
161			{
162				switch(*iter)
163				{
164				case '{':
165					++depth;
166					if(depth > deepest)
167					{
168						deepest = depth;
169						deepest_node = iter;
170						find_match = true;
171					}
172					break;
173				case '}':
174					--depth;
175					if(find_match)
176					{
177						deepest_node_end = iter;
178						find_match = false;
179					}
180					break;
181				default:
182					break;
183				}
184			}
185			if((deepest_node == end) || (deepest_node_end == end))
186			{
187				break;
188			}
189			//replace the variable we found in the {} above.
190			// *NOTE: since the c++ implementation only understands
191			// params and straight string substitution, so it's a
192			// known distance of 2 to skip the directive.
193			std::string key(deepest_node + 2, deepest_node_end);
194			LLSD value = context[key];
195			switch(*(deepest_node + 1))
196			{
197			case '$':
198				if(value.isDefined())
199				{
200					service_url.replace(
201						deepest_node,
202						deepest_node_end + 1,
203						value.asString());
204				}
205				else
206				{
207					llwarns << "Unknown key: " << key << " in option map: "
208						<< LLSDOStreamer<LLSDNotationFormatter>(context)
209						<< llendl;
210					keep_looping = false;
211				}
212				break;
213			case '%':
214				{
215					std::string query_str = LLURI::mapToQueryString(value);
216					service_url.replace(
217						deepest_node,
218						deepest_node_end + 1,
219						query_str);
220				}
221				break;
222			default:
223				llinfos << "Unknown directive: " << *(deepest_node + 1)
224					<< llendl;
225				keep_looping = false;
226				break;
227			}
228		}
229	}
230	if (service_url.find('{') != std::string::npos)
231	{
232		llwarns << "Constructed a likely bogus service URL: " << service_url
233			<< llendl;
234	}
235	return service_url;
236}