| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "mtpd_server_impl.h" |
| |
| #include <base/logging.h> |
| #include <base/rand_util.h> |
| #include <base/stl_util.h> |
| #include <base/strings/string_number_conversions.h> |
| |
| #include "service_constants.h" |
| |
| namespace mtpd { |
| |
| namespace { |
| |
| // Maximum number of bytes to read from the device at one time. This is set low |
| // enough such that a reasonable device can read this much data before D-Bus |
| // times out. |
| const uint32_t kMaxReadCount = 1024 * 1024; |
| |
| const char kInvalidHandleErrorMessage[] = "Invalid handle "; |
| |
| void SetInvalidHandleError(const std::string& handle, DBus::Error* error) { |
| std::string error_msg = kInvalidHandleErrorMessage + handle; |
| error->set(kMtpdServiceError, error_msg.c_str()); |
| } |
| |
| template<typename ReturnType> |
| ReturnType InvalidHandle(const std::string& handle, DBus::Error* error) { |
| SetInvalidHandleError(handle, error); |
| return ReturnType(); |
| } |
| |
| |
| } // namespace |
| |
| MtpdServer::MtpdServer(DBus::Connection& connection) |
| : DBus::ObjectAdaptor(connection, kMtpdServicePath), |
| device_manager_(this) { |
| } |
| |
| MtpdServer::~MtpdServer() { |
| } |
| |
| std::vector<std::string> MtpdServer::EnumerateStorages(DBus::Error& error) { |
| return device_manager_.EnumerateStorages(); |
| } |
| |
| std::vector<uint8_t> MtpdServer::GetStorageInfo(const std::string& storageName, |
| DBus::Error& error) { |
| const StorageInfo* info = device_manager_.GetStorageInfo(storageName); |
| return info ? info->ToDBusFormat() : StorageInfo().ToDBusFormat(); |
| } |
| |
| std::vector<uint8_t> MtpdServer::GetStorageInfoFromDevice( |
| const std::string& storageName, |
| DBus::Error& error) { |
| const StorageInfo* info = |
| device_manager_.GetStorageInfoFromDevice(storageName); |
| return info ? info->ToDBusFormat() : StorageInfo().ToDBusFormat(); |
| } |
| |
| std::string MtpdServer::OpenStorage(const std::string& storageName, |
| const std::string& mode, |
| DBus::Error& error) { |
| if (!(mode == kReadOnlyMode || mode == kReadWriteMode)) { |
| std::string error_msg = "Cannot open " + storageName + " in mode: " + mode; |
| error.set(kMtpdServiceError, error_msg.c_str()); |
| return std::string(); |
| } |
| |
| if (!device_manager_.HasStorage(storageName)) { |
| std::string error_msg = "Cannot open unknown storage " + storageName; |
| error.set(kMtpdServiceError, error_msg.c_str()); |
| return std::string(); |
| } |
| |
| std::string id; |
| uint32_t random_data[4]; |
| do { |
| base::RandBytes(random_data, sizeof(random_data)); |
| id = base::HexEncode(random_data, sizeof(random_data)); |
| } while (ContainsKey(handle_map_, id)); |
| |
| handle_map_.insert(std::make_pair(id, std::make_pair(storageName, mode))); |
| return id; |
| } |
| |
| void MtpdServer::CloseStorage(const std::string& handle, DBus::Error& error) { |
| if (handle_map_.erase(handle) == 0) |
| SetInvalidHandleError(handle, &error); |
| } |
| |
| std::vector<uint32_t> MtpdServer::ReadDirectoryEntryIds( |
| const std::string& handle, |
| const uint32_t& fileId, |
| DBus::Error& error) { |
| std::vector<uint32_t> directory_listing; |
| std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty()) { |
| SetInvalidHandleError(handle, &error); |
| return directory_listing; |
| } |
| |
| if (!device_manager_.ReadDirectoryEntryIds(storage_name, |
| fileId, |
| &directory_listing)) { |
| error.set(kMtpdServiceError, "ReadDirectoryEntryIds failed"); |
| } |
| return directory_listing; |
| } |
| |
| std::vector<uint8_t> MtpdServer::GetFileInfo( |
| const std::string& handle, |
| const std::vector<uint32_t>& fileIds, |
| DBus::Error& error) { |
| if (fileIds.empty()) { |
| error.set(kMtpdServiceError, "GetFileInfo called with no file ids"); |
| return FileEntry::EmptyFileEntriesToDBusFormat(); |
| } |
| |
| std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty()) { |
| SetInvalidHandleError(handle, &error); |
| return FileEntry::EmptyFileEntriesToDBusFormat(); |
| } |
| |
| std::vector<FileEntry> file_info; |
| if (!device_manager_.GetFileInfo(storage_name, fileIds, &file_info)) { |
| error.set(kMtpdServiceError, "GetFileInfo failed"); |
| return FileEntry::EmptyFileEntriesToDBusFormat(); |
| } |
| return FileEntry::FileEntriesToDBusFormat(file_info); |
| } |
| |
| std::vector<uint8_t> MtpdServer::ReadFileChunk(const std::string& handle, |
| const uint32_t& fileId, |
| const uint32_t& offset, |
| const uint32_t& count, |
| DBus::Error& error) { |
| if (count > kMaxReadCount || count == 0) { |
| error.set(kMtpdServiceError, "Invalid count for ReadFileChunk"); |
| return std::vector<uint8_t>(); |
| } |
| std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty()) |
| return InvalidHandle<std::vector<uint8_t> >(handle, &error); |
| |
| std::vector<uint8_t> file_contents; |
| if (!device_manager_.ReadFileChunk(storage_name, fileId, offset, count, |
| &file_contents)) { |
| error.set(kMtpdServiceError, "ReadFileChunk failed"); |
| return std::vector<uint8_t>(); |
| } |
| return file_contents; |
| } |
| |
| void MtpdServer::CopyFileFromLocal(const std::string& handle, |
| const DBus::FileDescriptor& fileDescriptor, |
| const uint32_t& parentId, |
| const std::string& fileName, |
| DBus::Error& error) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) |
| return InvalidHandle<void>(handle, &error); |
| |
| if (!device_manager_.CopyFileFromLocal(storage_name, |
| fileDescriptor.get(), |
| parentId, |
| fileName)) { |
| error.set(kMtpdServiceError, "CopyFileFromLocal failed"); |
| } |
| } |
| |
| void MtpdServer::DeleteObject(const std::string& handle, |
| const uint32_t& objectId, |
| DBus::Error& error) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) |
| return InvalidHandle<void>(handle, &error); |
| |
| if (!device_manager_.DeleteObject(storage_name, objectId)) { |
| error.set(kMtpdServiceError, "DeleteObject failed"); |
| } |
| } |
| |
| void MtpdServer::RenameObject(const std::string& handle, |
| const uint32_t& objectId, |
| const std::string& newName, |
| DBus::Error& error) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) |
| return InvalidHandle<void>(handle, &error); |
| |
| if (!device_manager_.RenameObject(storage_name, objectId, newName)) { |
| error.set(kMtpdServiceError, "RenameObject failed"); |
| } |
| } |
| |
| void MtpdServer::CreateDirectory(const std::string& handle, |
| const uint32_t& parentId, |
| const std::string& directoryName, |
| DBus::Error& error) { |
| const std::string storage_name = LookupHandle(handle); |
| if (storage_name.empty() || !IsOpenedWithWrite(handle)) |
| return InvalidHandle<void>(handle, &error); |
| |
| if (!device_manager_.CreateDirectory(storage_name, parentId, directoryName)) { |
| error.set(kMtpdServiceError, "CreateDirectory failed."); |
| } |
| } |
| |
| bool MtpdServer::IsAlive(DBus::Error& error) { |
| return true; |
| } |
| |
| void MtpdServer::StorageAttached(const std::string& storage_name) { |
| // Fire DBus signal. |
| MTPStorageAttached(storage_name); |
| } |
| |
| void MtpdServer::StorageDetached(const std::string& storage_name) { |
| // Fire DBus signal. |
| MTPStorageDetached(storage_name); |
| } |
| |
| int MtpdServer::GetDeviceEventDescriptor() const { |
| return device_manager_.GetDeviceEventDescriptor(); |
| } |
| |
| void MtpdServer::ProcessDeviceEvents() { |
| device_manager_.ProcessDeviceEvents(); |
| } |
| |
| std::string MtpdServer::LookupHandle(const std::string& handle) { |
| HandleMap::const_iterator it = handle_map_.find(handle); |
| return (it == handle_map_.end()) ? std::string() : it->second.first; |
| } |
| |
| bool MtpdServer::IsOpenedWithWrite(const std::string& handle) { |
| HandleMap::const_iterator it = handle_map_.find(handle); |
| return (it == handle_map_.end()) ? false |
| : it->second.second == kReadWriteMode; |
| } |
| |
| } // namespace mtpd |