blob: 75409ac7fd517dd21f03b77ae79460da52ba4a08 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "vm_tools/garcon/file_chooser_dbus_service.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <base/functional/bind.h>
#include <base/logging.h>
#include <base/memory/ptr_util.h>
#include "vm_tools/garcon/host_notifier.h"
namespace {
const char kPortalServiceName[] = "org.freedesktop.impl.portal.desktop.cros";
const char kPortalInterfaceName[] = "org.freedesktop.impl.portal.FileChooser";
const char kPortalServicePath[] = "/org/freedesktop/portal/desktop";
void HandleSynchronousDBusMethodCall(
base::RepeatingCallback<std::unique_ptr<dbus::Response>(dbus::MethodCall*)>
handler,
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
auto response = handler.Run(method_call);
if (!response)
response = dbus::Response::FromMethodCall(method_call);
std::move(response_sender).Run(std::move(response));
}
} // namespace
namespace vm_tools {
namespace garcon {
FileChooserDBusService::FileChooserDBusService(
vm_tools::garcon::HostNotifier* host_notifier)
: host_notifier_(host_notifier) {}
// static
std::unique_ptr<FileChooserDBusService> FileChooserDBusService::Create(
vm_tools::garcon::HostNotifier* host_notifier) {
auto service = base::WrapUnique(new FileChooserDBusService(host_notifier));
if (!service->Init())
return nullptr;
return service;
}
bool FileChooserDBusService::Init() {
dbus::Bus::Options options;
bus_ = new dbus::Bus(options);
if (!bus_->Connect()) {
LOG(ERROR) << "Failed to connect to session bus";
return false;
}
exported_object_ =
bus_->GetExportedObject(dbus::ObjectPath(kPortalServicePath));
if (!exported_object_) {
LOG(ERROR) << "Failed to export " << kPortalServicePath << " object";
return false;
}
if (!RegisterMethods()) {
LOG(ERROR) << "Failed to export methods";
return false;
}
if (!bus_->RequestOwnershipAndBlock(kPortalServiceName,
dbus::Bus::REQUIRE_PRIMARY)) {
LOG(ERROR) << "Unable to take ownership of " << kPortalServiceName;
return false;
}
return true;
}
bool FileChooserDBusService::RegisterMethods() {
using ServiceMethod = std::unique_ptr<dbus::Response> (
FileChooserDBusService::*)(dbus::MethodCall*);
const std::map<const char*, ServiceMethod> kServiceMethods = {
{"OpenFile", &FileChooserDBusService::OpenFile},
{"SaveFile", &FileChooserDBusService::SaveFile},
{"SaveFiles", &FileChooserDBusService::SaveFiles},
};
for (const auto& iter : kServiceMethods) {
const bool ret = exported_object_->ExportMethodAndBlock(
kPortalInterfaceName, iter.first,
base::BindRepeating(
&HandleSynchronousDBusMethodCall,
base::BindRepeating(iter.second, base::Unretained(this))));
if (!ret) {
LOG(ERROR) << "Failed to export method " << iter.first;
return false;
}
}
return true;
}
std::unique_ptr<dbus::Response> FileChooserDBusService::OpenFile(
dbus::MethodCall* method_call) {
return SelectFile(method_call, "open-file");
}
std::unique_ptr<dbus::Response> FileChooserDBusService::SaveFile(
dbus::MethodCall* method_call) {
return SelectFile(method_call, "saveas-file");
}
std::unique_ptr<dbus::Response> FileChooserDBusService::SaveFiles(
dbus::MethodCall* method_call) {
return SelectFile(method_call, "saveas-file");
}
std::unique_ptr<dbus::Response> FileChooserDBusService::SelectFile(
dbus::MethodCall* method_call, const std::string& type) {
dbus::MessageReader reader(method_call);
std::string app_id;
reader.PopString(&app_id);
std::string reason;
reader.PopString(&reason);
std::string parent_window;
reader.PopString(&parent_window);
std::string title;
reader.PopString(&title);
std::string default_path;
std::string allowed_extensions;
std::vector<std::string> files;
std::unique_ptr<dbus::Response> dbus_response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(dbus_response.get());
if (!host_notifier_->SelectFile(type, title, default_path, allowed_extensions,
&files)) {
writer.AppendUint32(1); // error
return dbus_response;
}
writer.AppendUint32(0); // success
dbus::MessageWriter array_writer(nullptr);
dbus::MessageWriter dict_entry_writer(nullptr);
dbus::MessageWriter array_of_strings_writer(nullptr);
writer.OpenArray("{sv}", &array_writer);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString("uris");
dict_entry_writer.OpenVariant("as", &array_of_strings_writer);
array_of_strings_writer.AppendArrayOfStrings(files);
dict_entry_writer.CloseContainer(&array_of_strings_writer);
array_writer.CloseContainer(&dict_entry_writer);
array_writer.OpenDictEntry(&dict_entry_writer);
dict_entry_writer.AppendString("writable");
dict_entry_writer.AppendVariantOfBool(true);
array_writer.CloseContainer(&dict_entry_writer);
writer.CloseContainer(&array_writer);
return dbus_response;
}
} // namespace garcon
} // namespace vm_tools