Пример #1
0
void IcingaApplication::DumpModifiedAttributes(void)
{
	String path = GetModAttrPath();

	std::fstream fp;
	String tempFilename = Utility::CreateTempFile(path + ".XXXXXX", 0644, fp);
	fp.exceptions(std::ofstream::failbit | std::ofstream::badbit);

	ConfigObject::Ptr previousObject;
	ConfigObject::DumpModifiedAttributes(boost::bind(&PersistModAttrHelper, boost::ref(fp), boost::ref(previousObject), _1, _2, _3));

	if (previousObject) {
		ConfigWriter::EmitRaw(fp, "\tobj.version = ");
		ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
		ConfigWriter::EmitRaw(fp, "\n}\n");
	}

	fp.close();

#ifdef _WIN32
	_unlink(path.CStr());
#endif /* _WIN32 */

	if (rename(tempFilename.CStr(), path.CStr()) < 0) {
		BOOST_THROW_EXCEPTION(posix_error()
		    << boost::errinfo_api_function("rename")
		    << boost::errinfo_errno(errno)
		    << boost::errinfo_file_name(tempFilename));
	}
}
Пример #2
0
static void ConfigObjectModifyAttribute(const String& attr, const Value& value)
{
	ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
	ConfigObject::Ptr self = vframe->Self;
	REQUIRE_NOT_NULL(self);
	return self->ModifyAttribute(attr, value);
}
Пример #3
0
void ApiListener::PersistMessage(const Dictionary::Ptr& message, const ConfigObject::Ptr& secobj)
{
	double ts = message->Get("ts");

	ASSERT(ts != 0);

	Dictionary::Ptr pmessage = new Dictionary();
	pmessage->Set("timestamp", ts);

	pmessage->Set("message", JsonEncode(message));

	if (secobj) {
		Dictionary::Ptr secname = new Dictionary();
		secname->Set("type", secobj->GetReflectionType()->GetName());
		secname->Set("name", secobj->GetName());
		pmessage->Set("secobj", secname);
	}

	boost::mutex::scoped_lock lock(m_LogLock);
	if (m_LogFile) {
		NetString::WriteStringToStream(m_LogFile, JsonEncode(pmessage));
		m_LogMessageCount++;
		SetLogMessageTimestamp(ts);

		if (m_LogMessageCount > 50000) {
			CloseLogFile();
			RotateLogFile();
			OpenLogFile();
		}
	}
}
Пример #4
0
static void PersistModAttrHelper(std::fstream& fp, ConfigObject::Ptr& previousObject, const ConfigObject::Ptr& object, const String& attr, const Value& value)
{
	if (object != previousObject) {
		if (previousObject) {
			ConfigWriter::EmitRaw(fp, "\tobj.version = ");
			ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
			ConfigWriter::EmitRaw(fp, "\n}\n\n");
		}

		ConfigWriter::EmitRaw(fp, "var obj = ");

		Array::Ptr args1 = new Array();
		args1->Add(object->GetReflectionType()->GetName());
		args1->Add(object->GetName());
		ConfigWriter::EmitFunctionCall(fp, "get_object", args1);

		ConfigWriter::EmitRaw(fp, "\nif (obj) {\n");
	}

	ConfigWriter::EmitRaw(fp, "\tobj.");

	Array::Ptr args2 = new Array();
	args2->Add(attr);
	args2->Add(value);
	ConfigWriter::EmitFunctionCall(fp, "modify_attribute", args2);

	ConfigWriter::EmitRaw(fp, "\n");

	previousObject = object;
}
Пример #5
0
static void ConfigObjectRestoreAttribute(const String& attr)
{
	ScriptFrame *vframe = ScriptFrame::GetCurrentFrame();
	ConfigObject::Ptr self = vframe->Self;
	REQUIRE_NOT_NULL(self);
	return self->RestoreAttribute(attr);
}
Пример #6
0
void CheckerComponent::ObjectHandler(const ConfigObject::Ptr& object)
{
	Checkable::Ptr checkable = dynamic_pointer_cast<Checkable>(object);

	if (!checkable)
		return;

	Zone::Ptr zone = Zone::GetByName(checkable->GetZoneName());
	bool same_zone = (!zone || Zone::GetLocalZone() == zone);

	{
		boost::mutex::scoped_lock lock(m_Mutex);

		if (object->IsActive() && !object->IsPaused() && same_zone) {
			if (m_PendingCheckables.find(checkable) != m_PendingCheckables.end())
				return;

			m_IdleCheckables.insert(GetCheckableScheduleInfo(checkable));
		} else {
			m_IdleCheckables.erase(checkable);
			m_PendingCheckables.erase(checkable);
		}

		m_CV.notify_all();
	}
}
Пример #7
0
		BOOST_FOREACH(const Object::Ptr& pobj, DependencyGraph::GetParents((obj))) {
			ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj);

			if (!configObj)
				continue;

			Dictionary::Ptr refInfo = new Dictionary();
			refInfo->Set("type", configObj->GetType()->GetName());
			refInfo->Set("name", configObj->GetName());
			used_by->Add(refInfo);
		}
Пример #8
0
bool Zone::CanAccessObject(const ConfigObject::Ptr& object)
{
	Zone::Ptr object_zone;

	if (object->GetReflectionType() == Zone::TypeInstance)
		object_zone = static_pointer_cast<Zone>(object);
	else
		object_zone = static_pointer_cast<Zone>(object->GetZone());

	if (!object_zone)
		object_zone = Zone::GetLocalZone();

	return object_zone->IsChildOf(this);
}
Пример #9
0
bool ConfigObjectUtility::DeleteObjectHelper(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors)
{
	std::vector<Object::Ptr> parents = DependencyGraph::GetParents(object);

	Type::Ptr type = object->GetReflectionType();

	if (!parents.empty() && !cascade) {
		if (errors)
			errors->Add("Object '" + object->GetName() + "' of type '" + type->GetName() +
			    "' cannot be deleted because other objects depend on it. "
			    "Use cascading delete to delete it anyway.");

		return false;
	}

	for (const Object::Ptr& pobj : parents) {
		ConfigObject::Ptr parentObj = dynamic_pointer_cast<ConfigObject>(pobj);

		if (!parentObj)
			continue;

		DeleteObjectHelper(parentObj, cascade, errors);
	}

	ConfigItem::Ptr item = ConfigItem::GetByTypeAndName(type, object->GetName());

	try {
		/* mark this object for cluster delete event */
		object->SetExtension("ConfigObjectDeleted", true);
		/* triggers signal for DB IDO and other interfaces */
		object->Deactivate(true);

		if (item)
			item->Unregister();
		else
			object->Unregister();

	} catch (const std::exception& ex) {
		if (errors)
			errors->Add(DiagnosticInformation(ex));

		return false;
	}

	String path = GetObjectConfigPath(object->GetReflectionType(), object->GetName());

	if (Utility::PathExists(path)) {
		if (unlink(path.CStr()) < 0 && errno != ENOENT) {
			BOOST_THROW_EXCEPTION(posix_error()
			    << boost::errinfo_api_function("unlink")
			    << boost::errinfo_errno(errno)
			    << boost::errinfo_file_name(path));
		}
	}

	return true;
}
Пример #10
0
bool ConfigObjectUtility::DeleteObject(const ConfigObject::Ptr& object, bool cascade, const Array::Ptr& errors)
{
	if (object->GetPackage() != "_api") {
		if (errors)
			errors->Add("Object cannot be deleted because it was not created using the API.");

		return false;
	}

	return DeleteObjectHelper(object, cascade, errors);
}
Пример #11
0
void ConfigType::UnregisterObject(const ConfigObject::Ptr& object)
{
	String name = object->GetName();

	{
		boost::mutex::scoped_lock lock(m_Mutex);

		m_ObjectMap.erase(name);
		m_ObjectVector.erase(std::remove(m_ObjectVector.begin(), m_ObjectVector.end(), object), m_ObjectVector.end());
	}
}
Пример #12
0
void ConfigType::UnregisterObject(const ConfigObject::Ptr& object)
{
	String name = object->GetName();

	{
		ObjectLock olock(this);

		m_ObjectMap.erase(name);
		m_ObjectVector.erase(std::remove(m_ObjectVector.begin(), m_ObjectVector.end(), object), m_ObjectVector.end());
	}
}
Пример #13
0
ValidationError::ValidationError(const ConfigObject::Ptr& object, const std::vector<String>& attributePath, const String& message)
	: m_Object(object), m_AttributePath(attributePath), m_Message(message)
{
	String path;

	BOOST_FOREACH(const String& attribute, attributePath) {
		if (!path.IsEmpty())
			path += " -> ";

		path += "'" + attribute + "'";
	}

	Type::Ptr type = object->GetReflectionType();
	m_What = "Validation failed for object '" + object->GetName() + "' of type '" + type->GetName() + "'";

	if (!path.IsEmpty())
		m_What += "; Attribute " + path;

	m_What += ": " + message;
}
Пример #14
0
void ConfigObject::RestoreObject(const String& message, int attributeTypes)
{
	Dictionary::Ptr persistentObject = JsonDecode(message);

	String type = persistentObject->Get("type");
	String name = persistentObject->Get("name");

	ConfigObject::Ptr object = GetObject(type, name);

	if (!object)
		return;

#ifdef I2_DEBUG
	Log(LogDebug, "ConfigObject")
		<< "Restoring object '" << name << "' of type '" << type << "'.";
#endif /* I2_DEBUG */
	Dictionary::Ptr update = persistentObject->Get("update");
	Deserialize(object, update, false, attributeTypes);
	object->OnStateLoaded();
	object->SetStateLoaded(true);
}
Пример #15
0
void ConfigType::RegisterObject(const ConfigObject::Ptr& object)
{
	String name = object->GetName();

	{
		ObjectLock olock(this);

		ObjectMap::iterator it = m_ObjectMap.find(name);

		if (it != m_ObjectMap.end()) {
			if (it->second == object)
				return;

			BOOST_THROW_EXCEPTION(ScriptError("An object with type '" + m_Name + "' and name '" + name + "' already exists (" +
			    Convert::ToString(it->second->GetDebugInfo()) + "), new declaration: " + Convert::ToString(object->GetDebugInfo()),
			    object->GetDebugInfo()));
		}

		m_ObjectMap[name] = object;
		m_ObjectVector.push_back(object);
	}
}
Пример #16
0
	BOOST_FOREACH(const ConfigObject::Ptr& obj, objs) {
		Dictionary::Ptr result1 = new Dictionary();
		results->Add(result1);

		Dictionary::Ptr resultAttrs = new Dictionary();
		result1->Set("attrs", resultAttrs);

		BOOST_FOREACH(const String& joinAttr, joinAttrs) {
			Object::Ptr joinedObj;
			String prefix;

			if (joinAttr.IsEmpty()) {
				joinedObj = obj;
				prefix = type->GetName();
			} else {
				int fid = type->GetFieldId(joinAttr);
				joinedObj = obj->NavigateField(fid);

				if (!joinedObj)
					continue;

				Field field = type->GetFieldInfo(fid);
				prefix = field.NavigationName;
			}

			boost::algorithm::to_lower(prefix);

			Type::Ptr joinedType = joinedObj->GetReflectionType();

			for (int fid = 0; fid < joinedType->GetFieldCount(); fid++) {
				Field field = joinedType->GetFieldInfo(fid);
				String aname = prefix + "." + field.Name;
				if (!attrs.empty() && attrs.find(aname) == attrs.end())
					continue;

				Value val = joinedObj->GetField(fid);

				/* hide internal navigation fields */
				if (field.Attributes & FANavigation) {
					Value nval = joinedObj->NavigateField(fid);

					if (val == nval)
						continue;
				}

				Value sval = Serialize(val, FAConfig | FAState);
				resultAttrs->Set(aname, sval);
			}
		}
Пример #17
0
void ConfigType::RegisterObject(const ConfigObject::Ptr& object)
{
	String name = object->GetName();

	{
		boost::mutex::scoped_lock lock(m_Mutex);

		auto it = m_ObjectMap.find(name);

		if (it != m_ObjectMap.end()) {
			if (it->second == object)
				return;

			Type *type = dynamic_cast<Type *>(this);

			BOOST_THROW_EXCEPTION(ScriptError("An object with type '" + type->GetName() + "' and name '" + name + "' already exists (" +
			    Convert::ToString(it->second->GetDebugInfo()) + "), new declaration: " + Convert::ToString(object->GetDebugInfo()),
			    object->GetDebugInfo()));
		}

		m_ObjectMap[name] = object;
		m_ObjectVector.push_back(object);
	}
}
Пример #18
0
Dictionary::Ptr ApiActions::RemoveAcknowledgement(const ConfigObject::Ptr& object,
    const Dictionary::Ptr& params)
{
	Checkable::Ptr checkable = static_pointer_cast<Checkable>(object);

	if (!checkable)
		return ApiActions::CreateResult(404,
		    "Cannot remove acknowlegement for non-existent checkable object "
		    + object->GetName() + ".");

	checkable->ClearAcknowledgement();
	checkable->RemoveCommentsByType(CommentAcknowledgement);

	return ApiActions::CreateResult(200, "Successfully removed acknowledgement for object '" + checkable->GetName() + "'.");
}
Пример #19
0
void ApiListener::SyncRelayMessage(const MessageOrigin::Ptr& origin,
    const ConfigObject::Ptr& secobj, const Dictionary::Ptr& message, bool log)
{
	double ts = Utility::GetTime();
	message->Set("ts", ts);

	Log(LogNotice, "ApiListener")
	    << "Relaying '" << message->Get("method") << "' message";

	if (origin && origin->FromZone)
		message->Set("originZone", origin->FromZone->GetName());

	Zone::Ptr target_zone;

	if (secobj) {
		if (secobj->GetReflectionType() == Zone::TypeInstance)
			target_zone = static_pointer_cast<Zone>(secobj);
		else
			target_zone = static_pointer_cast<Zone>(secobj->GetZone());
	}

	if (!target_zone)
		target_zone = Zone::GetLocalZone();

	Endpoint::Ptr master = GetMaster();

	bool need_log = !RelayMessageOne(target_zone, origin, message, master);

	for (const Zone::Ptr& zone : target_zone->GetAllParents()) {
		if (!RelayMessageOne(zone, origin, message, master))
			need_log = true;
	}

	if (log && need_log)
		PersistMessage(message, secobj);
}
Пример #20
0
void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
{
	if (!GetConnected())
		return;

	DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);

	if (dbobj) {
		bool active = object->IsActive();

		if (active) {
			ActivateObject(dbobj);

			dbobj->SendConfigUpdate();
			dbobj->SendStatusUpdate();
		} else
			DeactivateObject(dbobj);
	}
}
Пример #21
0
void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
{
	if (!GetConnected())
		return;

	DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);

	if (dbobj) {
		bool dbActive = GetObjectActive(dbobj);
		bool active = object->IsActive();

		if (active && !dbActive) {
			ActivateObject(dbobj);
			dbobj->SendConfigUpdate();
			dbobj->SendStatusUpdate();
		} else if (!active) {
			/* Deactivate the deleted object no matter
			 * which state it had in the database.
			 */
			DeactivateObject(dbobj);
		}
	}
}
Пример #22
0
void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
{
	if (!GetConnected() || Application::IsShuttingDown())
		return;

	DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);

	if (dbobj) {
		bool dbActive = GetObjectActive(dbobj);
		bool active = object->IsActive();

		if (active) {
			if (!dbActive)
				ActivateObject(dbobj);

			Dictionary::Ptr configFields = dbobj->GetConfigFields();
			String configHash = dbobj->CalculateConfigHash(configFields);
			ASSERT(configHash.GetLength() <= 64);
			configFields->Set("config_hash", configHash);

			String cachedHash = GetConfigHash(dbobj);

			if (cachedHash != configHash) {
				dbobj->SendConfigUpdateHeavy(configFields);
				dbobj->SendStatusUpdate();
			} else {
				dbobj->SendConfigUpdateLight();
			}
		} else if (!active) {
			/* Deactivate the deleted object no matter
			 * which state it had in the database.
			 */
			DeactivateObject(dbobj);
		}
	}
}
Пример #23
0
DbObject::Ptr DbObject::GetOrCreateByObject(const ConfigObject::Ptr& object)
{
	boost::mutex::scoped_lock lock(GetStaticMutex());

	DbObject::Ptr dbobj = object->GetExtension("DbObject");

	if (dbobj)
		return dbobj;

	DbType::Ptr dbtype = DbType::GetByName(object->GetType()->GetName());

	if (!dbtype)
		return DbObject::Ptr();

	Service::Ptr service;
	String name1, name2;

	service = dynamic_pointer_cast<Service>(object);

	if (service) {
		Host::Ptr host = service->GetHost();

		name1 = service->GetHost()->GetName();
		name2 = service->GetShortName();
	} else {
		if (object->GetType() == ConfigType::GetByName("CheckCommand") ||
		    object->GetType() == ConfigType::GetByName("EventCommand") ||
		    object->GetType() == ConfigType::GetByName("NotificationCommand")) {
			Command::Ptr command = dynamic_pointer_cast<Command>(object);
			name1 = CompatUtility::GetCommandName(command);
		}
		else
			name1 = object->GetName();
	}

	dbobj = dbtype->GetOrCreateObjectByName(name1, name2);

	dbobj->SetObject(object);
	object->SetExtension("DbObject", dbobj);

	return dbobj;
}
Пример #24
0
String icinga::DiagnosticInformation(const std::exception& ex, bool verbose, StackTrace *stack, ContextTrace *context)
{
	std::ostringstream result;

	String message = ex.what();

	const ValidationError *vex = dynamic_cast<const ValidationError *>(&ex);

	if (message.IsEmpty())
		result << boost::diagnostic_information(ex);
	else
		result << "Error: " << message;

	const ScriptError *dex = dynamic_cast<const ScriptError *>(&ex);

	if (dex && !dex->GetDebugInfo().Path.IsEmpty()) {
		result << "\nLocation:\n";
		ShowCodeFragment(result, dex->GetDebugInfo());
	}

	if (vex) {
		DebugInfo di;

		ConfigObject::Ptr dobj = vex->GetObject();
		if (dobj)
			di = dobj->GetDebugInfo();

		Dictionary::Ptr currentHint = vex->GetDebugHint();
		Array::Ptr messages;

		if (currentHint) {
			BOOST_FOREACH(const String& attr, vex->GetAttributePath()) {
				Dictionary::Ptr props = currentHint->Get("properties");

				if (!props)
					break;

				currentHint = props->Get(attr);

				if (!currentHint)
					break;

				messages = currentHint->Get("messages");
			}
		}

		if (messages && messages->GetLength() > 0) {
			Array::Ptr message = messages->Get(messages->GetLength() - 1);

			di.Path = message->Get(1);
			di.FirstLine = message->Get(2);
			di.FirstColumn = message->Get(3);
			di.LastLine = message->Get(4);
			di.LastColumn = message->Get(5);
		}

		if (!di.Path.IsEmpty()) {
			result << "\nLocation:\n";
			ShowCodeFragment(result, di);
		}
	}

	const user_error *uex = dynamic_cast<const user_error *>(&ex);
	const posix_error *pex = dynamic_cast<const posix_error *>(&ex);

	if (!uex && !pex && verbose) {
		const StackTrace *st = boost::get_error_info<StackTraceErrorInfo>(ex);

		if (st) {
			result << *st;
		} else {
			result << std::endl;

			if (!stack)
				stack = GetLastExceptionStack();

			if (stack)
				result << *stack;

		}

		if (boost::get_error_info<ContextTraceErrorInfo>(ex) == NULL) {
			result << std::endl;

			if (!context)
				context = GetLastExceptionContext();

			if (context)
				result << *context;
		}
	}

	return result.str();
}
Пример #25
0
/**
 * Commits the configuration item by creating a ConfigObject
 * object.
 *
 * @returns The ConfigObject that was created/updated.
 */
ConfigObject::Ptr ConfigItem::Commit(bool discard)
{
#ifdef I2_DEBUG
	Log(LogDebug, "ConfigItem")
	    << "Commit called for ConfigItem Type=" << GetType() << ", Name=" << GetName();
#endif /* I2_DEBUG */

	/* Make sure the type is valid. */
	Type::Ptr type = Type::GetByName(GetType());
	ASSERT(type && ConfigObject::TypeInstance->IsAssignableFrom(type));

	if (IsAbstract())
		return ConfigObject::Ptr();

	ConfigObject::Ptr dobj = static_pointer_cast<ConfigObject>(type->Instantiate());

	dobj->SetDebugInfo(m_DebugInfo);
	dobj->SetZoneName(m_Zone);
	dobj->SetPackage(m_Package);
	dobj->SetName(m_Name);

	DebugHint debugHints;

	ScriptFrame frame(dobj);
	if (m_Scope)
		m_Scope->CopyTo(frame.Locals);
	try {
		m_Expression->Evaluate(frame, &debugHints);
	} catch (const std::exception& ex) {
		if (m_IgnoreOnError) {
			Log(LogWarning, "ConfigObject")
			    << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex);

			return ConfigObject::Ptr();
		}

		throw;
	}

	if (discard)
		m_Expression.reset();

	String item_name;
	String short_name = dobj->GetShortName();

	if (!short_name.IsEmpty()) {
		item_name = short_name;
		dobj->SetName(short_name);
	} else
		item_name = m_Name;

	String name = item_name;

	NameComposer *nc = dynamic_cast<NameComposer *>(type.get());

	if (nc) {
		name = nc->MakeName(name, dobj);

		if (name.IsEmpty())
			BOOST_THROW_EXCEPTION(std::runtime_error("Could not determine name for object"));
	}

	if (name != item_name)
		dobj->SetShortName(item_name);

	dobj->SetName(name);

	try {
		dobj->OnConfigLoaded();
	} catch (const std::exception& ex) {
		if (m_IgnoreOnError) {
			Log(LogWarning, "ConfigObject")
			    << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex);

			return ConfigObject::Ptr();
		}

		throw;
	}

	Dictionary::Ptr persistentItem = new Dictionary();

	persistentItem->Set("type", GetType());
	persistentItem->Set("name", GetName());
	persistentItem->Set("properties", Serialize(dobj, FAConfig));

	Dictionary::Ptr dhint = debugHints.ToDictionary();
	persistentItem->Set("debug_hints", dhint);

	Array::Ptr di = new Array();
	di->Add(m_DebugInfo.Path);
	di->Add(m_DebugInfo.FirstLine);
	di->Add(m_DebugInfo.FirstColumn);
	di->Add(m_DebugInfo.LastLine);
	di->Add(m_DebugInfo.LastColumn);
	persistentItem->Set("debug_info", di);

	try {
		DefaultValidationUtils utils;
		dobj->Validate(FAConfig, utils);
	} catch (ValidationError& ex) {
		if (m_IgnoreOnError) {
			Log(LogWarning, "ConfigObject")
			    << "Ignoring config object '" << m_Name << "' of type '" << m_Type << "' due to errors: " << DiagnosticInformation(ex);

			return ConfigObject::Ptr();
		}

		ex.SetDebugHint(dhint);
		throw;
	}

	ConfigCompilerContext::GetInstance()->WriteObject(persistentItem);
	persistentItem.reset();

	dhint.reset();

	dobj->Register();

	m_Object = dobj;

	return dobj;
}
Пример #26
0
static bool ObjectNameLessComparer(const ConfigObject::Ptr& a, const ConfigObject::Ptr& b)
{
	return a->GetName() < b->GetName();
}
Пример #27
0
bool ObjectQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& request, HttpResponse& response, const Dictionary::Ptr& params)
{
	if (request.RequestUrl->GetPath().size() < 3 || request.RequestUrl->GetPath().size() > 4)
		return false;

	if (request.RequestMethod != "GET")
		return false;

	Type::Ptr type = FilterUtility::TypeFromPluralName(request.RequestUrl->GetPath()[2]);

	if (!type) {
		HttpUtility::SendJsonError(response, params, 400, "Invalid type specified.");
		return true;
	}

	QueryDescription qd;
	qd.Types.insert(type->GetName());
	qd.Permission = "objects/query/" + type->GetName();

	Array::Ptr uattrs, ujoins, umetas;

	try {
		uattrs = params->Get("attrs");
	} catch (const std::exception&) {
		HttpUtility::SendJsonError(response, params, 400,
			"Invalid type for 'attrs' attribute specified. Array type is required.");
		return true;
	}

	try {
		ujoins = params->Get("joins");
	} catch (const std::exception&) {
		HttpUtility::SendJsonError(response, params, 400,
			"Invalid type for 'joins' attribute specified. Array type is required.");
		return true;
	}

	try {
		umetas = params->Get("meta");
	} catch (const std::exception&) {
		HttpUtility::SendJsonError(response, params, 400,
			"Invalid type for 'meta' attribute specified. Array type is required.");
		return true;
	}

	bool allJoins = HttpUtility::GetLastParameter(params, "all_joins");

	params->Set("type", type->GetName());

	if (request.RequestUrl->GetPath().size() >= 4) {
		String attr = type->GetName();
		boost::algorithm::to_lower(attr);
		params->Set(attr, request.RequestUrl->GetPath()[3]);
	}

	std::vector<Value> objs;

	try {
		objs = FilterUtility::GetFilterTargets(qd, params, user);
	} catch (const std::exception& ex) {
		HttpUtility::SendJsonError(response, params, 404,
			"No objects found.",
			DiagnosticInformation(ex));
		return true;
	}

	ArrayData results;
	results.reserve(objs.size());

	std::set<String> joinAttrs;
	std::set<String> userJoinAttrs;

	if (ujoins) {
		ObjectLock olock(ujoins);
		for (const String& ujoin : ujoins) {
			userJoinAttrs.insert(ujoin.SubStr(0, ujoin.FindFirstOf(".")));
		}
	}

	for (int fid = 0; fid < type->GetFieldCount(); fid++) {
		Field field = type->GetFieldInfo(fid);

		if (!(field.Attributes & FANavigation))
			continue;

		if (!allJoins && userJoinAttrs.find(field.NavigationName) == userJoinAttrs.end())
			continue;

		joinAttrs.insert(field.Name);
	}

	for (const ConfigObject::Ptr& obj : objs) {
		DictionaryData result1{
			{ "name", obj->GetName() },
			{ "type", obj->GetReflectionType()->GetName() }
		};

		DictionaryData metaAttrs;

		if (umetas) {
			ObjectLock olock(umetas);
			for (const String& meta : umetas) {
				if (meta == "used_by") {
					Array::Ptr used_by = new Array();
					metaAttrs.emplace_back("used_by", used_by);

					for (const Object::Ptr& pobj : DependencyGraph::GetParents((obj)))
					{
						ConfigObject::Ptr configObj = dynamic_pointer_cast<ConfigObject>(pobj);

						if (!configObj)
							continue;

						used_by->Add(new Dictionary({
							{ "type", configObj->GetReflectionType()->GetName() },
							{ "name", configObj->GetName() }
						}));
					}
				} else if (meta == "location") {
					metaAttrs.emplace_back("location", obj->GetSourceLocation());
				} else {
					HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for meta: " + meta);
					return true;
				}
			}
		}

		result1.emplace_back("meta", new Dictionary(std::move(metaAttrs)));

		try {
			result1.emplace_back("attrs", SerializeObjectAttrs(obj, String(), uattrs, false, false));
		} catch (const ScriptError& ex) {
			HttpUtility::SendJsonError(response, params, 400, ex.what());
			return true;
		}

		DictionaryData joins;

		for (const String& joinAttr : joinAttrs) {
			Object::Ptr joinedObj;
			int fid = type->GetFieldId(joinAttr);

			if (fid < 0) {
				HttpUtility::SendJsonError(response, params, 400, "Invalid field specified for join: " + joinAttr);
				return true;
			}

			Field field = type->GetFieldInfo(fid);

			if (!(field.Attributes & FANavigation)) {
				HttpUtility::SendJsonError(response, params, 400, "Not a joinable field: " + joinAttr);
				return true;
			}

			joinedObj = obj->NavigateField(fid);

			if (!joinedObj)
				continue;

			String prefix = field.NavigationName;

			try {
				joins.emplace_back(prefix, SerializeObjectAttrs(joinedObj, prefix, ujoins, true, allJoins));
			} catch (const ScriptError& ex) {
				HttpUtility::SendJsonError(response, params, 400, ex.what());
				return true;
			}
		}

		result1.emplace_back("joins", new Dictionary(std::move(joins)));

		results.push_back(new Dictionary(std::move(result1)));
	}

	Dictionary::Ptr result = new Dictionary({
		{ "results", new Array(std::move(results)) }
	});

	response.SetStatus(200, "OK");
	HttpUtility::SendJsonBody(response, params, result);

	return true;
}