/*
* Copyright (c) 2018-2019 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
namespace sts::sf::hipc {
namespace impl {
class HipcManager : public IServiceObject {
private:
enum class CommandId {
ConvertCurrentObjectToDomain = 0,
CopyFromCurrentDomain = 1,
CloneCurrentObject = 2,
QueryPointerBufferSize = 3,
CloneCurrentObjectEx = 4,
};
private:
ServerDomainSessionManager *manager;
ServerSession *session;
bool is_mitm_session;
private:
Result CloneCurrentObjectImpl(Handle *out_client_handle, ServerSessionManager *tagged_manager) {
/* Clone the object. */
cmif::ServiceObjectHolder &&clone = this->session->srv_obj_holder.Clone();
R_UNLESS(clone, ResultHipcDomainObjectNotFound);
/* Create new session handles. */
Handle server_handle;
R_ASSERT(hipc::CreateSession(&server_handle, out_client_handle));
/* Register with manager. */
if (!is_mitm_session) {
R_ASSERT(tagged_manager->RegisterSession(server_handle, std::move(clone)));
} else {
/* Clone the forward service. */
std::shared_ptr<::Service> new_forward_service(new ::Service(), [](::Service *srv) {
serviceClose(srv);
delete srv;
});
R_ASSERT(serviceClone(this->session->forward_service.get(), new_forward_service.get()));
R_ASSERT(tagged_manager->RegisterMitmSession(server_handle, std::move(clone), std::move(new_forward_service)));
}
return ResultSuccess;
}
public:
explicit HipcManager(ServerDomainSessionManager *m, ServerSession *s) : manager(m), session(s), is_mitm_session(s->forward_service != nullptr) {
/* ... */
}
Result ConvertCurrentObjectToDomain(sf::Out out) {
/* TODO */
return ResultHipcOutOfDomains;
}
Result CopyFromCurrentDomain(sf::OutMoveHandle out, cmif::DomainObjectId object_id) {
/* Get domain. */
auto domain = this->session->srv_obj_holder.GetServiceObject();
R_UNLESS(domain != nullptr, ResultHipcTargetNotDomain);
/* Get domain object. */
auto &&object = domain->GetObject(object_id);
if (!object) {
R_UNLESS(this->is_mitm_session, ResultHipcDomainObjectNotFound);
return cmifCopyFromCurrentDomain(this->session->forward_service->session, object_id.value, out.GetHandlePointer());
}
if (!this->is_mitm_session || object_id.value != serviceGetObjectId(this->session->forward_service.get())) {
/* Create new session handles. */
Handle server_handle;
R_ASSERT(hipc::CreateSession(&server_handle, out.GetHandlePointer()));
/* Register. */
R_ASSERT(this->manager->RegisterSession(server_handle, std::move(object)));
} else {
/* Copy from the target domain. */
Handle new_forward_target;
R_TRY(cmifCopyFromCurrentDomain(this->session->forward_service->session, object_id.value, &new_forward_target));
/* Create new session handles. */
Handle server_handle;
R_ASSERT(hipc::CreateSession(&server_handle, out.GetHandlePointer()));
/* Register. */
std::shared_ptr<::Service> new_forward_service(new ::Service(), [](::Service *srv) {
serviceClose(srv);
delete srv;
});
serviceCreate(new_forward_service.get(), new_forward_target);
R_ASSERT(this->manager->RegisterMitmSession(server_handle, std::move(object), std::move(new_forward_service)));
}
return ResultSuccess;
}
Result CloneCurrentObject(sf::OutMoveHandle out) {
return this->CloneCurrentObjectImpl(out.GetHandlePointer(), this->manager);
}
void QueryPointerBufferSize(sf::Out out) {
out.SetValue(this->session->pointer_buffer.GetSize());
}
Result CloneCurrentObjectEx(sf::OutMoveHandle out, u32 tag) {
return this->CloneCurrentObjectImpl(out.GetHandlePointer(), this->manager->GetSessionManagerByTag(tag));
}
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(ConvertCurrentObjectToDomain),
MAKE_SERVICE_COMMAND_META(CopyFromCurrentDomain),
MAKE_SERVICE_COMMAND_META(CloneCurrentObject),
MAKE_SERVICE_COMMAND_META(QueryPointerBufferSize),
MAKE_SERVICE_COMMAND_META(CloneCurrentObjectEx),
};
};
}
Result ServerDomainSessionManager::DispatchManagerRequest(ServerSession *session, const cmif::PointerAndSize &in_message, const cmif::PointerAndSize &out_message) {
/* Make a stack object, and pass a shared pointer to it to DispatchRequest. */
/* Note: This is safe, as no additional references to the hipc manager can ever be stored. */
/* The shared pointer to stack object is definitely gross, though. */
impl::HipcManager hipc_manager(this, session);
return this->DispatchRequest(cmif::ServiceObjectHolder(std::shared_ptr(&hipc_manager, [](impl::HipcManager *) { /* Empty deleter. */ })), session, in_message, out_message);
}
}