diff -Nru android-file-transfer-3.9+dfsg/android-file-transfer-linux.json android-file-transfer-4.2/android-file-transfer-linux.json --- android-file-transfer-3.9+dfsg/android-file-transfer-linux.json 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/android-file-transfer-linux.json 2020-12-29 16:10:28.000000000 +0000 @@ -13,7 +13,7 @@ "config-opts": [ "-DCMAKE_BUILD_TYPE=Release" ], "sources": [{ "type": "git", - "tag": "v3.3", + "tag": "v4.2", "url": "https://github.com/whoozle/android-file-transfer-linux.git" }] } diff -Nru android-file-transfer-3.9+dfsg/cli/cli.cpp android-file-transfer-4.2/cli/cli.cpp --- android-file-transfer-3.9+dfsg/cli/cli.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/cli.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -72,6 +72,8 @@ bool claimInterface = true; bool showEvents = false; bool resetDevice = false; + bool listDevices = false; + std::string deviceFilter; const char *fileInput = nullptr; if (!isatty(STDIN_FILENO)) @@ -88,6 +90,8 @@ {"no-claim", no_argument, 0, 'C' }, {"input-file", required_argument, 0, 'f' }, {"reset-device", no_argument , 0, 'R' }, + {"device-name", required_argument, 0, 'd' }, + {"device-list", required_argument, 0, 'l' }, {0, 0, 0, 0 } }; @@ -100,7 +104,7 @@ while(true) { int optionIndex = 0; //index of matching option - int c = getopt_long(argc, argv, "ibehvVCRf:", long_options, &optionIndex); + int c = getopt_long(argc, argv, "ibehvVCRf:ld:", long_options, &optionIndex); if (c == -1) break; switch(c) @@ -127,6 +131,12 @@ case 'R': resetDevice = true; break; + case 'l': + listDevices = true; + break; + case 'd': + deviceFilter = optarg; + break; case '?': case 'h': default: @@ -161,6 +171,8 @@ "-f\t--input-file\tuse file to read input commands\n" "-C\t--no-claim\tno usb interface claim\n" "-R\t--reset-device\treset usb device before connecting\n" + "-d\t--device-name\tuse device name (could be partial name, e.g. model or manufacturer)\n" + "-l\t--device-list\tlist devices\n" "-V\t--version\tshow version information" ); exit(0); @@ -172,37 +184,70 @@ exit(0); } - auto mtp = Device::FindFirst(claimInterface, resetDevice); - if (!mtp) - { - error("no mtp device found"); - exit(1); + usb::ContextPtr ctx(new usb::Context); + if (listDevices) { + for (usb::DeviceDescriptorPtr desc : ctx->GetDevices()) + { + try + { + auto device = Device::Open(ctx, desc, claimInterface, resetDevice); + if (device) { + auto di = device->GetInfo(); + if (di.Matches(deviceFilter)) { + print(di.GetFilesystemFriendlyName()); + } + } + } + catch (const std::exception & ex) + { error("Device::Find failed:", ex.what()); } + } + return 0; } + cli::SessionPtr session; try { - bool hasCommands = optind >= argc; - cli::Session session(mtp, showPrompt); - gSession.store(&session); - if (!session.SetFirstStorage()) + auto device = Device::FindFirst(ctx, deviceFilter, claimInterface, resetDevice); + if (!device) + { + error("device not found (filter = '", deviceFilter, "'"); + exit(1); + } + + session = std::make_shared(device->OpenSession(1), showPrompt); + if (!session->SetFirstStorage()) { error("your device may be locked or does not have any storage available"); - exit(2); + session.reset(); + device.reset(); } - session.ShowEvents(showEvents); + } + catch(const std::exception &ex) + { error("Device::Find failed:", ex.what()); } + + ctx.reset(); + if (!session) + exit(1); + + try + { + bool hasCommands = optind >= argc; + gSession.store(session.get()); + session->ShowEvents(showEvents); - if (forceInteractive || (session.IsInteractive() && hasCommands)) + if (forceInteractive || (session->IsInteractive() && hasCommands)) { - session.InteractiveInput(); + session->InteractiveInput(); } else { for(int i = optind; i < argc; ++i) { - session.ProcessCommand(argv[i]); + session->ProcessCommand(argv[i]); } } gSession.store(nullptr); + session.reset(); exit(0); } diff -Nru android-file-transfer-3.9+dfsg/cli/CMakeLists.txt android-file-transfer-4.2/cli/CMakeLists.txt --- android-file-transfer-3.9+dfsg/cli/CMakeLists.txt 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/CMakeLists.txt 2020-12-29 16:10:28.000000000 +0000 @@ -9,6 +9,7 @@ cli.cpp) if (READLINE_FOUND) + set(CMAKE_REQUIRED_INCLUDES ${Readline_INCLUDE_DIR}) set(CMAKE_REQUIRED_LIBRARIES ${Readline_LIBRARY}) set(READLINE_TEST_SRC " #include @@ -35,6 +36,7 @@ endif() endif() + set(CMAKE_REQUIRED_INCLUDES) set(CMAKE_REQUIRED_LIBRARIES) endif() @@ -47,5 +49,28 @@ endif() add_executable(aft-mtp-cli ${CLI_SOURCES}) +target_include_directories(aft-mtp-cli SYSTEM PRIVATE ${Readline_INCLUDE_DIR}) target_link_libraries(aft-mtp-cli ${MTP_LIBRARIES} ${CLI_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS aft-mtp-cli RUNTIME DESTINATION bin) + +if (BUILD_QT_UI AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) + add_custom_command(TARGET aft-mtp-cli POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../../Frameworks/" $) + add_custom_command(TARGET aft-mtp-cli POST_BUILD COMMAND ${OTOOL_BIN} -L $) + set(AFT_MTP_CLI_PATH "${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin/aft-mtp-cli") + set(MACOSX_BUNDLE_LIBS_INSTALL " + ${MACOSX_BUNDLE_LIBS_INSTALL} + file(INSTALL \"${CMAKE_CURRENT_BINARY_DIR}/aft-mtp-cli\" DESTINATION \"${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin\") + ") + foreach(DYNLIB ${MACOSX_BUNDLE_LIBS}) + get_filename_component(DYNLIB_NAME "${DYNLIB}" NAME) + set(MACOSX_BUNDLE_LIBS_INSTALL " + ${MACOSX_BUNDLE_LIBS_INSTALL} + message(STATUS \"cli: fixing ${DYNLIB_NAME}\") + execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -change \"${DYNLIB}\" \"@executable_path/../../Frameworks/${DYNLIB_NAME}\" \"${AFT_MTP_CLI_PATH}\") + ") + endforeach() + set(MACOSX_BUNDLE_LIBS_INSTALL " + ${MACOSX_BUNDLE_LIBS_INSTALL} + execute_process(COMMAND \"${OTOOL_BIN}\" -L \"${AFT_MTP_CLI_PATH}\") + " PARENT_SCOPE) +endif() diff -Nru android-file-transfer-3.9+dfsg/cli/Command.cpp android-file-transfer-4.2/cli/Command.cpp --- android-file-transfer-3.9+dfsg/cli/Command.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Command.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/cli/Command.h android-file-transfer-4.2/cli/Command.h --- android-file-transfer-3.9+dfsg/cli/Command.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Command.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_COMMAND_H -#define AFT_CLI_COMMAND_H +#ifndef AFTL_CLI_COMMAND_H +#define AFTL_CLI_COMMAND_H #include #include diff -Nru android-file-transfer-3.9+dfsg/cli/CommandLine.cpp android-file-transfer-4.2/cli/CommandLine.cpp --- android-file-transfer-3.9+dfsg/cli/CommandLine.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/CommandLine.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/cli/CommandLine.h android-file-transfer-4.2/cli/CommandLine.h --- android-file-transfer-3.9+dfsg/cli/CommandLine.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/CommandLine.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_READLINE_H -#define AFT_CLI_READLINE_H +#ifndef AFTL_CLI_COMMANDLINE_H +#define AFTL_CLI_COMMANDLINE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/cli/CommandLineStub.cpp android-file-transfer-4.2/cli/CommandLineStub.cpp --- android-file-transfer-3.9+dfsg/cli/CommandLineStub.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/CommandLineStub.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/cli/MakeTuple.h android-file-transfer-4.2/cli/MakeTuple.h --- android-file-transfer-3.9+dfsg/cli/MakeTuple.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/MakeTuple.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTP_MAKE_TUPLE_H -#define MTP_MAKE_TUPLE_H +#ifndef AFTL_CLI_MAKETUPLE_H +#define AFTL_CLI_MAKETUPLE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/cli/PosixStreams.h android-file-transfer-4.2/cli/PosixStreams.h --- android-file-transfer-3.9+dfsg/cli/PosixStreams.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/PosixStreams.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_POSIXSTREAMS_H -#define AFT_CLI_POSIXSTREAMS_H +#ifndef AFTL_CLI_POSIXSTREAMS_H +#define AFTL_CLI_POSIXSTREAMS_H #include diff -Nru android-file-transfer-3.9+dfsg/cli/ProgressBar.h android-file-transfer-4.2/cli/ProgressBar.h --- android-file-transfer-3.9+dfsg/cli/ProgressBar.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/ProgressBar.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef PROGRESSBAR_H -#define PROGRESSBAR_H +#ifndef AFTL_CLI_PROGRESSBAR_H +#define AFTL_CLI_PROGRESSBAR_H #include #include diff -Nru android-file-transfer-3.9+dfsg/cli/Session.cpp android-file-transfer-4.2/cli/Session.cpp --- android-file-transfer-3.9+dfsg/cli/Session.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Session.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -28,11 +28,17 @@ #include #include #include +#include -#include +#include +#include + +#include #include +#include #include +#include #include #include #include @@ -50,15 +56,18 @@ const char *b = prefix.c_str(); return strncasecmp(a, b, prefix.size()) == 0; } + + mtp::u64 FromHex(const std::string & str) + { return strtoll(str.c_str(), NULL, 16); } } namespace cli { - Session::Session(const mtp::DevicePtr &device, bool showPrompt): - _device(device), - _session(_device->OpenSession(1)), + Session::Session(const mtp::SessionPtr &session, bool showPrompt): + _session(session), + _trustedApp(mtp::TrustedApp::Create(_session, GetMtpzDataPath())), _gdi(_session->GetDeviceInfo()), _cs(mtp::Session::AllStorages), _cd(mtp::Session::Root), @@ -84,6 +93,8 @@ _terminalWidth = ts.ws_col; #endif } + try { if (_trustedApp) _trustedApp->Authenticate(); } + catch(const std::exception & ex) { error("mtpz authentication failed: ", ex.what()); } AddCommand("help", "shows this help", make_function([this]() -> void { Help(); })); @@ -107,6 +118,8 @@ make_function([this]() -> void { List(true, true); })); AddCommand("lsext-r", " lists objects in [extended info, recursive]", make_function([this](const Path &path) -> void { List(path, true, true); })); + AddCommand("ls-objects", "", + make_function([this](const std::string &format) -> void { ListObjects(format); })); AddCommand("put", "put uploads file to directory", make_function([this](const LocalPath &path, const Path &dst) -> void { Put(path, dst); })); @@ -138,7 +151,7 @@ AddCommand("pwd", "resolved current object directory", make_function([this]() -> void { CurrentDirectory(); })); AddCommand("rm", " removes object (WARNING: RECURSIVE, be careful!)", - make_function([this](const LocalPath &path) -> void { Delete(path); })); + make_function([this](const Path &path) -> void { Delete(path); })); AddCommand("rm-id", " removes object by id (WARNING: RECURSIVE, be careful!)", make_function([this](mtp::u32 id) -> void { Delete(mtp::ObjectId(id)); })); AddCommand("mkdir", " makes directory", @@ -146,7 +159,7 @@ AddCommand("mkpath", " create directory structure specified in path", make_function([this](const Path &path) -> void { MakePath(path); })); AddCommand("type", " shows type of file (recognized by libmagic/extension)", - make_function([this](const LocalPath &path) -> void { ShowType(path); })); + make_function([](const LocalPath &path) -> void { ShowType(path); })); AddCommand("rename", "renames object", make_function([this](const Path & path, const std::string & newName) -> void { Rename(path, newName); })); @@ -161,6 +174,17 @@ AddCommand("storage-info", " displays storage information", make_function([this](const StoragePath &path) -> void { DisplayStorageInfo(path); })); + AddCommand("get-refs", "returns object-associated refs", + make_function([this](const StoragePath &path) -> void { GetObjectReferences(path); })); + + if (Library::Supported(_session)) + { + AddCommand("zune-init", "load media library", + make_function([this]() -> void { ZuneInit(); })); + AddCommand("zune-import", " import file using metadata", + make_function([this](const LocalPath &path) -> void { ZuneImport(path); })); + } + AddCommand("test-property-list", "test GetObjectPropList on given object", make_function([this](const Path &path) -> void { TestObjectPropertyList(path); })); @@ -174,6 +198,15 @@ })); } + Session::~Session() + { } + + std::string Session::GetMtpzDataPath() + { + char * home = getenv("HOME"); + return std::string(home? home: ".") + "/.mtpz-data"; + } + bool Session::SetFirstStorage() { auto ids = _session->GetStorageIDs(); @@ -289,6 +322,11 @@ { std::stringstream ss; ss << _gdi.Manufacturer << " " << _gdi.Model; + if (_deviceFriendlyNameSupported) { + auto name = _session->GetDeviceStringProperty(mtp::DeviceProperty::DeviceFriendlyName); + if (!name.empty()) + ss << " / " << name; + } if (_batterySupported) { auto level = _session->GetDeviceIntegerProperty(mtp::DeviceProperty::BatteryLevel); @@ -315,17 +353,22 @@ std::stringstream ss; ss << "supported op codes: "; for(OperationCode code : _gdi.OperationsSupported) - { - ss << hex(code, 4) << " "; - } - ss << "\n"; - ss << "supported properties: "; - for(u16 code : _gdi.DevicePropertiesSupported) - { - ss << hex(code, 4) << " "; - if (mtp::DeviceProperty(code) == mtp::DeviceProperty::BatteryLevel) - _batterySupported = true; - } + ss << ToString(code) << " "; + + ss << "\nsupported capture formats: "; + for(auto code : _gdi.CaptureFormats) + ss << ToString(code) << " "; + ss << "\nsupported image formats: "; + for(auto code : _gdi.ImageFormats) + ss << ToString(code) << " "; + + ss << "\nsupported properties: "; + _batterySupported = _gdi.Supports(mtp::DeviceProperty::BatteryLevel); + _deviceFriendlyNameSupported = _gdi.Supports(mtp::DeviceProperty::DeviceFriendlyName); + + for(DeviceProperty code : _gdi.DevicePropertiesSupported) + ss << ToString(code) << " "; + ss << "\n"; debug(ss.str()); } @@ -492,24 +535,35 @@ { try { - msg::ObjectInfo info = _session->GetObjectInfo(objectId); + std::string filename; if (extended) + { + msg::ObjectInfo info = _session->GetObjectInfo(objectId); + filename = info.Filename; print( std::left, width(objectId, 10), " ", width(info.StorageId.Id, 10), " ", std::right, - hex(info.ObjectFormat, 4), " ", + ToString(info.ObjectFormat), " ", width(info.ObjectCompressedSize, 10), " ", std::left, width(!info.CaptureDate.empty()? FormatTime(info.CaptureDate): FormatTime(info.ModificationDate), 20), " ", prefix + info.Filename, " " ); + } else - print(std::left, width(objectId, 10), " ", prefix + info.Filename); - - if (recursive && info.ObjectFormat == mtp::ObjectFormat::Association) - List(objectId, extended, recursive, prefix + info.Filename + "/"); + { + filename = _session->GetObjectStringProperty(objectId, ObjectProperty::ObjectFilename); + print(std::left, width(objectId, 10), " ", prefix + filename); + } + + if (recursive) + { + auto format = mtp::ObjectFormat(_session->GetObjectIntegerProperty(objectId, ObjectProperty::ObjectFormat)); + if (format == mtp::ObjectFormat::Association) + List(objectId, extended, recursive, prefix + filename + "/"); + } } catch(const std::exception &ex) { @@ -772,7 +826,7 @@ msg::ObjectInfo oi; oi.Filename = filename; oi.ObjectFormat = ObjectFormatFromFilename(src); - oi.SetSize(stream->GetSize()); + oi.ObjectCompressedSize = stream->GetSize(); if (_showEvents) { @@ -805,7 +859,7 @@ { auto objectId = ResolveObjectChild(parentDir, filename); ObjectFormat format = ObjectFormat(_session->GetObjectIntegerProperty(objectId, ObjectProperty::ObjectFormat)); - print("format ", hex((int)format)); + print("format ", ToString(format)); if (format != ObjectFormat::Association) targetFilename = filename; //path exists and it's not directory } @@ -827,41 +881,89 @@ mtp::ObjectId Session::MakeDirectory(mtp::ObjectId parentId, const std::string & name) { - using namespace mtp; - msg::ObjectInfo oi; - oi.Filename = name; - oi.ObjectFormat = ObjectFormat::Association; - auto noi = _session->SendObjectInfo(oi, GetUploadStorageId(), parentId); + auto noi = _session->CreateDirectory(name, _cd, GetUploadStorageId()); return noi.ObjectId; } void Session::ShowType(const LocalPath &src) { mtp::ObjectFormat format = mtp::ObjectFormatFromFilename(src); - print("mtp object format = ", hex(format, 4)); + mtp::print("mtp object format = ", ToString(format)); + } + + static void PrintFormat(const mtp::ByteArray & format, const mtp::ByteArray & value) + { + using namespace mtp; + InputStream is(format); + u16 prop = is.Read16(); + DataTypeCode type = DataTypeCode(is.Read16()); + u16 rw = is.Read8(); + + std::string defValue; + u32 groupCode = 0; + u8 formFlag = 0; + try + { + switch(type) + { +#define CASE(BITS) \ + case mtp::DataTypeCode::Uint##BITS: \ + case mtp::DataTypeCode::Int##BITS: \ + defValue = std::to_string(is.Read##BITS ()); break + CASE(8); CASE(16); CASE(32); CASE(64); CASE(128); +#undef CASE + case mtp::DataTypeCode::String: + defValue = is.ReadString(); break; + case mtp::DataTypeCode::ArrayUint8: + { + u32 size = is.Read32(); + std::stringstream ss; + HexDump(ss, "raw bytes", size, is); + defValue = ss.str(); + } + break; + default: + throw std::runtime_error("invalid type " + std::to_string((u16)type)); + } + groupCode = is.Read32(); + formFlag = is.Read8(); + } + catch(const std::exception & ex) + { defValue = ""; } + + print("property ", mtp::ToString(ObjectProperty(prop)), ", type: ", ToString(type), ", rw: ", rw, ", default: ", defValue, ", groupCode: ", groupCode, ", form flag: ", formFlag, ", value: ", ToString(type, value)); + //HexDump("raw", format, true); } void Session::ListProperties(mtp::ObjectId id) { - auto ops = _session->GetObjectPropertiesSupported(id); - std::stringstream ss; - ss << "properties supported: "; + mtp::ObjectFormat format = mtp::ObjectFormat(_session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectFormat)); + mtp::print("querying supported properties for format ", mtp::ToString(format)); + + auto ops = _session->GetObjectPropertiesSupported(format); + mtp::print("properties supported: "); for(mtp::ObjectProperty prop: ops.ObjectPropertyCodes) { - ss << mtp::hex(prop, 4) << " "; + PrintFormat(_session->GetObjectPropertyDesc(prop), _session->GetObjectProperty(id, prop)); } - ss << "\n"; - mtp::print(ss.str()); } void Session::ListDeviceProperties() { using namespace mtp; - for(u16 code : _gdi.DevicePropertiesSupported) + for(DeviceProperty code : _gdi.DevicePropertiesSupported) { - print("property code: ", hex(code, 4)); - ByteArray data = _session->GetDeviceProperty((mtp::DeviceProperty)code); - HexDump("value", data, true); + auto desc = _session->GetDevicePropertyDesc(code); + if (code == DeviceProperty::PerceivedDeviceType) + { + auto value = _session->GetDeviceIntegerProperty(code); + print("property: ", ToString(code), " ", ToString(desc.Type), " ", desc.Writeable? "rw ": "ro ", ToString(PerceivedDeviceType(value))); + } + else + { + ByteArray value = _session->GetDeviceProperty(code); + print("property: ", ToString(code), " ", ToString(desc.Type), " ", desc.Writeable? "rw ": "ro ", ToString(desc.Type, value)); + } } } @@ -893,7 +995,7 @@ void Session::GetObjectPropertyList(mtp::ObjectId parent, const std::set &originalObjectList, const mtp::ObjectProperty property) { using namespace mtp; - print("testing property 0x", hex(property, 4), "..."); + print("testing property ", ToString(property), "..."); std::set objectList; ByteArray data = _session->GetObjectPropertyList(parent, ObjectFormat::Any, property, 0, 1); @@ -903,12 +1005,12 @@ bool ok = true; - parser.Parse(data, [this, &objectList, property, &ok](mtp::ObjectId objectId, ObjectProperty p, const ByteArray & ) { + parser.Parse(data, [&objectList, property, &ok](mtp::ObjectId objectId, ObjectProperty p, const ByteArray & ) { if ((p == property || property == ObjectProperty::All)) objectList.insert(objectId); else { - print("extra property 0x", hex(p, 4), " returned for object ", objectId.Id, ", while querying property list 0x", mtp::hex(property, 4)); + print("extra property 0x", ToString(p), " returned for object ", objectId.Id, ", while querying property list ", mtp::ToString(property)); ok = false; } }); @@ -918,14 +1020,14 @@ if (!extraData.empty()) { - print("inconsistent GetObjectPropertyList for property 0x", mtp::hex(property, 4)); + print("inconsistent GetObjectPropertyList for property 0x", mtp::ToString(property)); for(auto objectId : extraData) { - print("missing 0x", mtp::hex(property, 4), " for object ", objectId); + print("missing 0x", mtp::ToString(property), " for object ", objectId); ok = false; } } - print("getting object property list of type 0x", hex(property, 4), " ", ok? "PASSED": "FAILED"); + print("getting object property list of type 0x", ToString(property), " ", ok? "PASSED": "FAILED"); } @@ -968,4 +1070,82 @@ print("used ", usedBytes, " (", usedPercents, "%), free ", si.FreeSpaceInBytes, " bytes of ", si.MaxCapacity); } + void Session::ListObjects(const std::string & format) + { ListObjects(mtp::ObjectFormat(FromHex(format))); } + + void Session::ListObjects(mtp::ObjectFormat format) + { + mtp::print("querying all objects of format ", mtp::ToString(format)); + auto objects = _session->GetObjectHandles(mtp::Session::AllStorages, format, mtp::Session::Device); + auto & handles = objects.ObjectHandles; + for(auto id : handles) + { + std::string filename, name; + try { filename = _session->GetObjectStringProperty(id, mtp::ObjectProperty::ObjectFilename); } + catch(const std::exception & ex) { mtp::error("error getting filename: ", ex.what()); } + try { name = _session->GetObjectStringProperty(id, mtp::ObjectProperty::Name); } + catch(const std::exception & ex) { mtp::error("error getting name: ", ex.what()); } + mtp::print(id.Id, "\t", filename, "\t", name); + } + } + + void Session::GetObjectReferences(const Path & src) + { + using namespace mtp; + auto refs = _session->GetObjectReferences(Resolve(src)); + for (auto id: refs.ObjectHandles) + { + debug(id.Id, "\t", _session->GetObjectStringProperty(id, ObjectProperty::ObjectFilename)); + } + } + + void Session::ZuneInit() + { + if (!_library) + _library = std::make_shared(_session); + } + + void Session::ZuneImport(const LocalPath & path) + { + ZuneInit(); + if (!_library) + throw std::runtime_error("library failed to initialise"); + + using namespace mtp; + auto stream = std::make_shared(path); + stream->SetTotal(stream->GetSize()); + try { stream->SetProgressReporter(ProgressBar(path, _terminalWidth / 3, _terminalWidth)); } catch(const std::exception &ex) { } + + auto meta = Metadata::Read(path); + if (!meta) + throw std::runtime_error("no metadata"); + + print("metadata: ", meta->Artist, " / ", meta->Album, " (", meta->Year, ") / ", meta->Title); + + auto artist = _library->GetArtist(meta->Artist); + if (!artist) + artist = _library->CreateArtist(meta->Artist); + if (!artist) + throw std::runtime_error("can't create artist with name " + meta->Artist); + debug("got artist record"); + + auto album = _library->GetAlbum(artist, meta->Album); + if (!album) + album = _library->CreateAlbum(artist, meta->Album, meta->Year); + if (!album) + throw std::runtime_error("can't create album with name " + meta->Album); + + debug("got album record"); + + ObjectFormat format = ObjectFormatFromFilename(path); + auto slashpos = path.rfind('/'); + auto filename = slashpos != path.npos? path.substr(slashpos + 1): std::string(path); + debug("track format: " + ToString(format)); + auto songId = _library->CreateTrack(artist, album, format, meta->Title, meta->Genre, meta->Track, filename, stream->GetSize()); + _session->SendObject(stream); + + _library->AddTrack(album, songId); + } + + } diff -Nru android-file-transfer-3.9+dfsg/cli/Session.h android-file-transfer-4.2/cli/Session.h --- android-file-transfer-3.9+dfsg/cli/Session.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Session.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_SESSION_H -#define AFT_CLI_SESSION_H +#ifndef AFTL_CLI_SESSION_H +#define AFTL_CLI_SESSION_H #include #include @@ -31,12 +31,22 @@ #include #include +namespace mtp +{ + class TrustedApp; + DECLARE_PTR(TrustedApp); + class Library; + DECLARE_PTR(Library); +} + namespace cli { class Session { mtp::DevicePtr _device; mtp::SessionPtr _session; + mtp::TrustedAppPtr _trustedApp; + mtp::LibraryPtr _library; mtp::msg::DeviceInfo _gdi; mtp::StorageId _cs; //current storage std::string _csName; //current storage name @@ -48,6 +58,7 @@ std::string _prompt; unsigned _terminalWidth; bool _batterySupported; + bool _deviceFriendlyNameSupported; std::multimap _commands; @@ -65,8 +76,11 @@ mtp::StorageId GetUploadStorageId() { return _cs == mtp::Session::AllStorages? mtp::Session::AnyStorage: _cs; } + static std::string GetMtpzDataPath(); + public: - Session(const mtp::DevicePtr &device, bool showPrompt); + Session(const mtp::SessionPtr &session, bool showPrompt); + ~Session(); bool SetFirstStorage(); @@ -103,6 +117,8 @@ void Put(mtp::ObjectId parentId, const LocalPath &src, const std::string &targetFilename = std::string()); void Put(const LocalPath &src, const Path &dst); mtp::ObjectId MakeDirectory(mtp::ObjectId parentId, const std::string & name); + void ListObjects(const std::string & format); + void ListObjects(mtp::ObjectFormat format); void ListProperties(mtp::ObjectId id); void ListDeviceProperties(); void TestObjectPropertyList(const Path &path); @@ -142,6 +158,7 @@ void MakePath(const Path &path) { Resolve(path, true); } + void MakeArtist(const std::string & name); void Delete(const Path &path) { Delete(Resolve(path)); } @@ -151,13 +168,18 @@ void ListProperties(const Path &path) { ListProperties(Resolve(path)); } + void GetObjectReferences(const Path & src); static void ShowType(const LocalPath &src); + void ZuneInit(); + void ZuneImport(const LocalPath & path); + template void AddCommand(const std::string &name, const std::string &help, std::function && callback) { _commands.insert(std::make_pair(name, ICommandPtr(new Command(help, std::move(callback))))); } }; + DECLARE_PTR(Session); } #endif diff -Nru android-file-transfer-3.9+dfsg/cli/Tokenizer.cpp android-file-transfer-4.2/cli/Tokenizer.cpp --- android-file-transfer-3.9+dfsg/cli/Tokenizer.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Tokenizer.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/cli/Tokenizer.h android-file-transfer-4.2/cli/Tokenizer.h --- android-file-transfer-3.9+dfsg/cli/Tokenizer.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Tokenizer.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_TOKENIZER_H -#define AFT_CLI_TOKENIZER_H +#ifndef AFTL_CLI_TOKENIZER_H +#define AFTL_CLI_TOKENIZER_H #include #include diff -Nru android-file-transfer-3.9+dfsg/cli/Tokens.h android-file-transfer-4.2/cli/Tokens.h --- android-file-transfer-3.9+dfsg/cli/Tokens.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/cli/Tokens.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_TOKENS_H -#define AFT_CLI_TOKENS_H +#ifndef AFTL_CLI_TOKENS_H +#define AFTL_CLI_TOKENS_H #include #include diff -Nru android-file-transfer-3.9+dfsg/CMakeLists.txt android-file-transfer-4.2/CMakeLists.txt --- android-file-transfer-3.9+dfsg/CMakeLists.txt 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/CMakeLists.txt 2020-12-29 16:10:28.000000000 +0000 @@ -1,9 +1,10 @@ +cmake_minimum_required(VERSION 2.8.12) project(android-file-transfer) set (CMAKE_CXX_STANDARD 11) -set(VERSION_MAJOR "3") -set(VERSION_MINOR "9") +set(VERSION_MAJOR "4") +set(VERSION_MINOR "2") set(VERSION_PATCH "0") set(VERSION_COUNT 3) set(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") @@ -12,10 +13,10 @@ if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(USB_BACKEND_DARWIN TRUE) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13) + find_program(OTOOL_BIN otool) endif() -cmake_minimum_required(VERSION 2.8) -set(CMAKE_USE_RELATIVE_PATHS TRUE) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) include(FindPkgConfig) @@ -25,16 +26,51 @@ find_package ( Threads ) option(BUILD_FUSE "Build fuse mount helper" ON) +option(BUILD_MTPZ "Build with MTPZ support" ON) +option(BUILD_PYTHON "Build python module" ON) +option(BUILD_TAGLIB "Build with taglib support" ON) + +if (BUILD_PYTHON) + find_package (Python COMPONENTS Interpreter Development QUIET) +endif() + +if (BUILD_SHARED_LIB) + set(LIB_NAME mtp-ng) +else() + set(LIB_NAME mtp-ng-static) +endif() if (BUILD_FUSE) pkg_check_modules ( FUSE fuse ) endif() +if (BUILD_TAGLIB) + pkg_check_modules (TAGLIB taglib IMPORTED_TARGET) + if (TAGLIB_FOUND) + add_definitions(-DHAVE_TAGLIB=1) + endif() +endif() + if (FUSE_FOUND) message(STATUS "fuse found, building mount helper") add_definitions(${FUSE_CFLAGS} -DFUSE_USE_VERSION=26) endif() +if (PYTHON_FOUND) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + find_package(pybind11) + if (NOT pybind11_FOUND) + set(CMAKE_REQUIRED_INCLUDES ${Python_INCLUDE_DIRS}) + check_include_files("pybind11/pybind11.h" pybind11_FOUND LANGUAGE CXX) + set(CMAKE_REQUIRED_INCLUDES) + endif() + if (pybind11_FOUND) + add_subdirectory(python) + else() + message(WARNING "pybind11 not found, skipping python bindings") + endif() +endif() + check_include_files (magic.h HAVE_MAGIC_H) check_library_exists(magic magic_open "" HAVE_LIBMAGIC) @@ -54,6 +90,7 @@ endif() add_definitions(-Wall -pthread) +add_definitions(-D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64) if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) @@ -63,14 +100,15 @@ add_definitions(-ggdb) endif() -include_directories(. ${CMAKE_SOURCE_DIR}) -include_directories(mtp/backend/posix) - set(SOURCES mtp/log.cpp - mtp/ByteArray.cpp + mtp/ptp/DataTypeCode.cpp mtp/ptp/Device.cpp + mtp/ptp/DeviceProperty.cpp + mtp/ptp/Messages.cpp mtp/ptp/ObjectFormat.cpp + mtp/ptp/ObjectProperty.cpp + mtp/ptp/OperationCode.cpp mtp/ptp/PipePacketer.cpp mtp/ptp/Response.cpp mtp/ptp/Session.cpp @@ -81,15 +119,24 @@ mtp/backend/posix/FileHandler.cpp mtp/backend/posix/Exception.cpp + + mtp/metadata/Metadata.cpp + mtp/metadata/Library.cpp + + mtp/mtpz/TrustedApp.cpp ) -exec_program(git ARGS describe --tags OUTPUT_VARIABLE AFT_VERSION RETURN_VALUE GIT_STATUS) -if (GIT_STATUS GREATER 0) +exec_program(git ARGS describe --tags --match='v*' OUTPUT_VARIABLE AFT_VERSION RETURN_VALUE GIT_STATUS) +if (GIT_STATUS EQUAL 0) + string(REGEX MATCH "v([0-9a-fA-F.]+)-" _VERSION ${AFT_VERSION}) + set(AFT_BASE_VERSION ${CMAKE_MATCH_1}) +else() file(READ ${CMAKE_SOURCE_DIR}/README.md README_MD) string(REGEX MATCH "version-([0-9a-fA-F.]+)-" _VERSION ${README_MD}) - set(AFT_VERSION "v${CMAKE_MATCH_1}-snapshot") + set(AFT_BASE_VERSION ${CMAKE_MATCH_1}) + set(AFT_VERSION "v${AFT_BASE_VERSION}-snapshot") endif() -message(STATUS "version: ${AFT_VERSION}") +message(STATUS "version: ${AFT_VERSION}, base version: ${AFT_BASE_VERSION}") set(VERSION_SRC ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/version.cpp) configure_file(mtp/version.cpp.in ${VERSION_SRC}) @@ -97,10 +144,7 @@ list(APPEND SOURCES ${VERSION_SRC}) if (USB_BACKEND_LIBUSB) - pkg_check_modules(LIBUSB libusb-1.0 REQUIRED) - include_directories(${LIBUSB_INCLUDE_DIRS}) - include_directories(mtp/backend/libusb) add_definitions(-DUSB_BACKEND_LIBUSB) list(APPEND SOURCES @@ -114,7 +158,6 @@ find_library(CORE_LIBRARY CoreFoundation) find_library(IOKIT_LIBRARY IOKit) list(APPEND MTP_LIBRARIES ${IOKIT_LIBRARY} ${CORE_LIBRARY}) - include_directories(mtp/backend/darwin) list(APPEND SOURCES mtp/backend/darwin/usb/Context.cpp mtp/backend/darwin/usb/Device.cpp @@ -123,7 +166,6 @@ mtp/backend/darwin/usb/Interface.cpp ) else() - include_directories(mtp/backend/linux) list(APPEND SOURCES mtp/backend/linux/usb/Endpoint.cpp mtp/backend/linux/usb/Context.cpp @@ -133,26 +175,81 @@ ) endif() +#Always add TrustedApp to avoid polluting client code. +list(APPEND SOURCES + mtp/mtpz/TrustedApp.cpp +) + +if (BUILD_MTPZ) + include(FindOpenSSL) + if (OPENSSL_FOUND) + set(_BUILD_MTPZ TRUE) + message(STATUS "building with MTPZ support") + add_definitions(-DMTPZ_ENABLED) + list(APPEND MTP_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + list(APPEND MTP_SHARED_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) + else() + message(WARNING "building without MTPZ support, openssl not found") + endif() +endif() + +if (BUILD_SHARED_LIB) + set(LIB_NAME mtp-ng) +else() + set(LIB_NAME mtp-ng-static) +endif() if (BUILD_SHARED_LIB) - add_library(mtp-ng SHARED ${SOURCES}) - set_target_properties(mtp-ng PROPERTIES SOVERSION ${VERSION}) - target_link_libraries(mtp-ng ${CMAKE_THREAD_LIBS_INIT} ${MTP_SHARED_LIBRARIES}) + add_library(${LIB_NAME} SHARED ${SOURCES}) + set_target_properties(${LIB_NAME} PROPERTIES SOVERSION ${VERSION}) + target_link_libraries(${LIB_NAME} PRIVATE ${CMAKE_THREAD_LIBS_INIT} ${MTP_SHARED_LIBRARIES}) if (USB_BACKEND_LIBUSB) - target_link_libraries(mtp-ng ${LIBUSB_LIBRARIES}) + target_link_libraries(${LIB_NAME} ${LIBUSB_LIBRARIES}) endif() +else () + add_library(${LIB_NAME} STATIC ${SOURCES}) + target_link_libraries(${LIB_NAME} PRIVATE ${MTP_LIBRARIES}) +endif () + +list(INSERT MTP_LIBRARIES 0 ${LIB_NAME}) +install(TARGETS ${LIB_NAME} LIBRARY DESTINATION "lib${LIB_SUFFIX}" ARCHIVE DESTINATION "lib${LIB_SUFFIX}") +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/mtp/backend/posix) + +if (_BUILD_MTPZ) + target_include_directories(${LIB_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}) +endif() + +if (TAGLIB_FOUND) + target_include_directories(${LIB_NAME} PRIVATE ${TAGLIB_INCLUDE_DIRS}) + target_link_libraries(${LIB_NAME} PRIVATE PkgConfig::TAGLIB) +endif() - list(INSERT MTP_LIBRARIES 0 mtp-ng) - install(TARGETS mtp-ng LIBRARY DESTINATION "lib${LIB_SUFFIX}" ARCHIVE DESTINATION "lib${LIB_SUFFIX}") -else (BUILD_SHARED_LIB) - add_library(mtp-ng-static STATIC ${SOURCES}) - target_link_libraries(mtp-ng-static ${MTP_LIBRARIES}) - list(INSERT MTP_LIBRARIES 0 mtp-ng-static) - install(TARGETS mtp-ng-static LIBRARY DESTINATION "lib${LIB_SUFFIX}" ARCHIVE DESTINATION "lib${LIB_SUFFIX}") -endif (BUILD_SHARED_LIB) +if (USB_BACKEND_LIBUSB) + target_include_directories(${LIB_NAME} PUBLIC ${LIBUSB_INCLUDE_DIRS}) + target_include_directories(${LIB_NAME} PUBLIC mtp/backend/libusb) +elseif (USB_BACKEND_DARWIN) + target_include_directories(${LIB_NAME} PUBLIC mtp/backend/darwin) +else() + target_include_directories(${LIB_NAME} PUBLIC mtp/backend/linux) +endif() -add_definitions(-D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64) +if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(MACOSX_BUNDLE_LIBS) + if (OPENSSL_FOUND) + list(APPEND MACOSX_BUNDLE_LIBS /usr/local/opt/openssl@1.1/lib/libcrypto.1.1.dylib) + endif() + if (TAGLIB_FOUND) + list(APPEND MACOSX_BUNDLE_LIBS /usr/local/opt/taglib/lib/libtag.1.dylib) + endif() + if (FUSE_FOUND) + list(APPEND MACOSX_BUNDLE_LIBS /usr/local/lib/libosxfuse.2.dylib) + endif() + + set(MACOSX_BUNDLE_LIBS_INSTALL) + set(MACOSX_BUNDLE_ROOT_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_PROJECT_NAME}.app") + message(STATUS "bundle root dir: ${MACOSX_BUNDLE_ROOT_DIR}") +endif() add_subdirectory(cli) diff -Nru android-file-transfer-3.9+dfsg/debian/changelog android-file-transfer-4.2/debian/changelog --- android-file-transfer-3.9+dfsg/debian/changelog 2020-03-23 06:09:36.000000000 +0000 +++ android-file-transfer-4.2/debian/changelog 2021-02-02 16:38:41.000000000 +0000 @@ -1,8 +1,16 @@ -android-file-transfer (3.9+dfsg-1build1) focal; urgency=medium +android-file-transfer (4.2-1) unstable; urgency=medium - * No-change rebuild for libgcc-s1 package name change. + * Update VCS fields to point to the Debian namespace on salsa. + * New upstream version + * Standards-Version: 4.5.1 (routine-update) + * debhelper-compat 13 (routine-update) + * Add salsa-ci file (routine-update) + * Rules-Requires-Root: no (routine-update) + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + * Add qttools5-dev to BD - -- Matthias Klose Mon, 23 Mar 2020 07:09:36 +0100 + -- Dylan Aïssi Tue, 02 Feb 2021 17:38:41 +0100 android-file-transfer (3.9+dfsg-1) unstable; urgency=medium diff -Nru android-file-transfer-3.9+dfsg/debian/control android-file-transfer-4.2/debian/control --- android-file-transfer-3.9+dfsg/debian/control 2019-09-09 19:33:30.000000000 +0000 +++ android-file-transfer-4.2/debian/control 2021-02-02 16:38:41.000000000 +0000 @@ -2,16 +2,18 @@ Maintainer: Dylan Aïssi Section: misc Priority: optional -Build-Depends: debhelper-compat (= 12), +Build-Depends: debhelper-compat (= 13), cmake, qtbase5-dev, libfuse-dev, libreadline-dev, - pkg-config -Standards-Version: 4.4.0 -Vcs-Browser: https://salsa.debian.org/daissi/android-file-transfer -Vcs-Git: https://salsa.debian.org/daissi/android-file-transfer.git + pkg-config, + qttools5-dev +Standards-Version: 4.5.1 +Vcs-Browser: https://salsa.debian.org/debian/android-file-transfer +Vcs-Git: https://salsa.debian.org/debian/android-file-transfer.git Homepage: https://whoozle.github.io/android-file-transfer-linux/ +Rules-Requires-Root: no Package: android-file-transfer Architecture: any diff -Nru android-file-transfer-3.9+dfsg/debian/salsa-ci.yml android-file-transfer-4.2/debian/salsa-ci.yml --- android-file-transfer-3.9+dfsg/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/debian/salsa-ci.yml 2021-02-02 16:38:41.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru android-file-transfer-3.9+dfsg/debian/upstream/metadata android-file-transfer-4.2/debian/upstream/metadata --- android-file-transfer-3.9+dfsg/debian/upstream/metadata 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/debian/upstream/metadata 2021-02-02 16:38:41.000000000 +0000 @@ -0,0 +1,4 @@ +Bug-Database: https://github.com/whoozle/android-file-transfer-linux/issues +Bug-Submit: https://github.com/whoozle/android-file-transfer-linux/issues/new +Repository: https://github.com/whoozle/android-file-transfer-linux.git +Repository-Browse: https://github.com/whoozle/android-file-transfer-linux diff -Nru android-file-transfer-3.9+dfsg/.editorconfig android-file-transfer-4.2/.editorconfig --- android-file-transfer-3.9+dfsg/.editorconfig 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/.editorconfig 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,11 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +indent_style = tab +indent_size = 4 diff -Nru android-file-transfer-3.9+dfsg/FAQ.md android-file-transfer-4.2/FAQ.md --- android-file-transfer-3.9+dfsg/FAQ.md 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/FAQ.md 2020-12-29 16:10:28.000000000 +0000 @@ -3,7 +3,7 @@ ## Is it possible to use cli tool for automated file management? Yes. There's two options of achieving this. -1. Pass every command as **single** command line argument, e.g. ```aft-mtp-cli "get Pictures" "cd Pictures" "ls"```. If you need quoted argument for commands, you have to escape them, e.g. ```aft-mtp-cli "get \"Pictures\"". +1. Pass every command as **single** command line argument, e.g. ```aft-mtp-cli "get Pictures" "cd Pictures" "ls"```. If you need quoted argument for commands, you have to escape them, e.g. ```aft-mtp-cli "get \"Pictures\""```. 2. Pass -b command line option and feed your script to stdin. ## How to unmount my device? diff -Nru android-file-transfer-3.9+dfsg/fuse/CMakeLists.txt android-file-transfer-4.2/fuse/CMakeLists.txt --- android-file-transfer-3.9+dfsg/fuse/CMakeLists.txt 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/fuse/CMakeLists.txt 2020-12-29 16:10:28.000000000 +0000 @@ -2,3 +2,27 @@ target_link_libraries(aft-mtp-mount ${MTP_LIBRARIES} ${FUSE_LDFLAGS} ${CMAKE_THREAD_LIBS_INIT}) install(TARGETS aft-mtp-mount RUNTIME DESTINATION bin) + +if (BUILD_QT_UI AND (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")) + add_custom_command(TARGET aft-mtp-mount POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../../Frameworks/" $) + add_custom_command(TARGET aft-mtp-mount POST_BUILD COMMAND ${OTOOL_BIN} -L $) + set(AFT_MTP_MOUNT_PATH "${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin/aft-mtp-mount") + set(MACOSX_BUNDLE_LIBS_INSTALL " + ${MACOSX_BUNDLE_LIBS_INSTALL} + file(INSTALL \"${CMAKE_CURRENT_BINARY_DIR}/aft-mtp-mount\" DESTINATION \"${MACOSX_BUNDLE_ROOT_DIR}/Contents/SharedSupport/bin\") + ") + foreach(DYNLIB ${MACOSX_BUNDLE_LIBS}) + get_filename_component(DYNLIB_NAME "${DYNLIB}" NAME) + set(MACOSX_BUNDLE_LIBS_INSTALL " + ${MACOSX_BUNDLE_LIBS_INSTALL} + message(STATUS \"fuse: fixing ${DYNLIB_NAME}\") + execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -change \"${DYNLIB}\" \"@executable_path/../../Frameworks/${DYNLIB_NAME}\" \"${AFT_MTP_MOUNT_PATH}\") + ") + endforeach() + + set(MACOSX_BUNDLE_LIBS_INSTALL " + ${MACOSX_BUNDLE_LIBS_INSTALL} + execute_process(COMMAND \"${OTOOL_BIN}\" -L \"${AFT_MTP_MOUNT_PATH}\") + " PARENT_SCOPE) + +endif() diff -Nru android-file-transfer-3.9+dfsg/fuse/Exception.h android-file-transfer-4.2/fuse/Exception.h --- android-file-transfer-3.9+dfsg/fuse/Exception.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/fuse/Exception.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_FUSE_EXCEPTION_H -#define AFS_FUSE_EXCEPTION_H +#ifndef AFTL_FUSE_EXCEPTION_H +#define AFTL_FUSE_EXCEPTION_H #include #include @@ -36,7 +36,7 @@ static std::string GetErrorMessage(int returnCode) { char buf[1024]; -#ifdef _GNU_SOURCE +#if defined(_GNU_SOURCE) && defined(__GLIBC__) std::string text(strerror_r(returnCode, buf, sizeof(buf))); #else int r = strerror_r(returnCode, buf, sizeof(buf)); diff -Nru android-file-transfer-3.9+dfsg/fuse/FuseDirectory.h android-file-transfer-4.2/fuse/FuseDirectory.h --- android-file-transfer-3.9+dfsg/fuse/FuseDirectory.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/fuse/FuseDirectory.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_FUSE_FUSEDIRECTORY_H -#define AFS_FUSE_FUSEDIRECTORY_H +#ifndef AFTL_FUSE_FUSEDIRECTORY_H +#define AFTL_FUSE_FUSEDIRECTORY_H #include diff -Nru android-file-transfer-3.9+dfsg/fuse/FuseEntry.h android-file-transfer-4.2/fuse/FuseEntry.h --- android-file-transfer-3.9+dfsg/fuse/FuseEntry.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/fuse/FuseEntry.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_FUSE_FUSEENTRY_H -#define AFS_FUSE_FUSEENTRY_H +#ifndef AFTL_FUSE_FUSEENTRY_H +#define AFTL_FUSE_FUSEENTRY_H #include diff -Nru android-file-transfer-3.9+dfsg/fuse/FuseId.h android-file-transfer-4.2/fuse/FuseId.h --- android-file-transfer-3.9+dfsg/fuse/FuseId.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/fuse/FuseId.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_FUSE_FUSEID_H -#define AFS_FUSE_FUSEID_H +#ifndef AFTL_FUSE_FUSEID_H +#define AFTL_FUSE_FUSEID_H #include diff -Nru android-file-transfer-3.9+dfsg/fuse/fuse_ll.cpp android-file-transfer-4.2/fuse/fuse_ll.cpp --- android-file-transfer-3.9+dfsg/fuse/fuse_ll.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/fuse/fuse_ll.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -30,10 +30,12 @@ #include +#include #include #include #include #include +#include #include #include @@ -45,15 +47,21 @@ class FuseWrapper { std::mutex _mutex; + std::string _deviceFilter; bool _claimInterface; + bool _resetDevice; mtp::DevicePtr _device; mtp::SessionPtr _session; bool _editObjectSupported; bool _getObjectPropertyListSupported; + bool _getPartialObjectSupported; time_t _connectTime; + size_t _partialObjectCacheSize; - typedef std::map ChildrenObjects; - typedef std::map Files; + static constexpr size_t DefaultPartialObjectCacheSize = 3; //just 3 object entries. + + using ChildrenObjects = std::map; + using Files = std::map; Files _files; typedef std::map ObjectAttrs; @@ -73,6 +81,21 @@ std::map _storageToName; std::map _storageFromName; + struct ObjectCacheEntry + { + mtp::ObjectId Id; + mtp::ByteArray Data; + + ObjectCacheEntry() + { } + + ObjectCacheEntry(mtp::ObjectId id, mtp::ByteArray && data): Id(id), Data(std::move(data)) + { } + }; + + using ObjectCache = std::list; + ObjectCache _objectCache; + private: static FuseId ToFuse(mtp::ObjectId id) { return FuseId(id.Id + MtpObjectShift); } @@ -103,7 +126,7 @@ void GetObjectInfo(ChildrenObjects &cache, mtp::ObjectId id) { - mtp::msg::ObjectInfo oi = _session->GetObjectInfo(id); + auto oi = _session->GetObjectInfo(id); FuseId inode = ToFuse(id); cache.emplace(oi.Filename, inode); @@ -280,8 +303,6 @@ catch(const std::exception &ex) { } - - return cache; } } @@ -310,7 +331,7 @@ storageId = _session->GetObjectStorage(parentId); mtp::debug(" creating object in storage ", mtp::hex(storageId.Id), ", parent: ", mtp::hex(parentId.Id, 8)); - mtp::Session::NewObjectInfo noi; + mtp::msg::NewObjectInfo noi; if (format != mtp::ObjectFormat::Association) { mtp::msg::ObjectInfo oi; @@ -377,7 +398,8 @@ } public: - FuseWrapper(bool claimInterface): _claimInterface(claimInterface) + FuseWrapper(const std::string & deviceFilter, bool claimInterface, bool resetDevice): + _deviceFilter(deviceFilter), _claimInterface(claimInterface), _resetDevice(resetDevice), _partialObjectCacheSize(DefaultPartialObjectCacheSize) { Connect(); } void Connect() @@ -390,17 +412,20 @@ _directoryCache.clear(); _session.reset(); _device.reset(); - _device = mtp::Device::FindFirst(_claimInterface); + _device = mtp::Device::FindFirst(_deviceFilter, _claimInterface, _resetDevice); if (!_device) throw std::runtime_error("no MTP device found"); _session = _device->OpenSession(1); _editObjectSupported = _session->EditObjectSupported(); if (!_editObjectSupported) - mtp::error("your device does not have android EditObject extension, mounting read-only\n"); + mtp::error("your device does not have android EditObject extension, mounting read-only"); _getObjectPropertyListSupported = _session->GetObjectPropertyListSupported(); if (!_getObjectPropertyListSupported) - mtp::error("your device does not have GetObjectPropertyList extension, expect slow enumeration of big directories\n"); + mtp::error("your device does not have GetObjectPropertyList extension, expect slow enumeration of big directories"); + _getPartialObjectSupported = _session->GetDeviceInfo().Supports(mtp::OperationCode::GetPartialObject); + if (!_getPartialObjectSupported) + mtp::error("your device does not have GetPartialObject extension (beware, this will download the latest N objects you accessed and keep them in a small in-memory cache)"); _connectTime = time(NULL); PopulateStorages(); @@ -430,14 +455,23 @@ _storageToName[id] = path; } } + std::string GetMountpoint() + { return _session->GetDeviceInfo().GetFilesystemFriendlyName(); } void Init(void *, fuse_conn_info *conn) { +#if 0 + /* + Disable FUSE_CAP_BIG_WRITES until further investigation + https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v5.4&id=1fb027d7596464d3fad3ed59f70f43807ef926c6 + https://github.com/netheril96/securefs/commit/e5b9e5d41a6dcdee60ab4a18b46b9a0c6475ba21 + */ mtp::scoped_mutex_lock l(_mutex); conn->want |= conn->capable & FUSE_CAP_BIG_WRITES; //big writes static const size_t MaxWriteSize = 1024 * 1024; if (conn->max_write < MaxWriteSize) conn->max_write = MaxWriteSize; +#endif } void Lookup (fuse_req_t req, FuseId parent, const char *name) @@ -521,9 +555,48 @@ off_t rsize = std::min(attr.st_size - begin, size); mtp::debug("reading ", rsize, " bytes"); mtp::ByteArray data; + auto objectId = FromFuse(ino); if (rsize > 0) - data = _session->GetPartialObject(FromFuse(ino), begin, rsize); - mtp::debug("read", data.size(), "bytes of data"); + { + if (!_getPartialObjectSupported) + { + std::size_t size = 0; + + ObjectCache::iterator it; + for(it = _objectCache.begin(); it != _objectCache.end(); ++it, ++size) + { + auto & entry = *it; + if (entry.Id == objectId) + { + mtp::debug("in-memory cache hit"); + auto src = entry.Data.data() + begin; + std::copy(src, src + rsize, std::back_inserter(data)); + if (it != _objectCache.begin()) + std::swap(*_objectCache.begin(), *it); + break; + } + } + if (it == _objectCache.end()) + { + mtp::debug("in-memory cache miss"); + auto stream = std::make_shared(); + _session->GetObject(objectId, stream); + auto src = stream->GetData().data() + begin; + std::copy(src, src + rsize, std::back_inserter(data)); + _objectCache.emplace_front(objectId, std::move(stream->GetData())); + ++size; + } + + while (size > _partialObjectCacheSize) + { + mtp::debug("purging last entry from cache..."); + _objectCache.pop_back(); + --size; + } + } else + data = _session->GetPartialObject(objectId, begin, rsize); + } + mtp::debug("read ", data.size(), " bytes of data"); FUSE_CALL(fuse_reply_buf(req, static_cast(static_cast(data.data())), data.size())); } @@ -591,11 +664,6 @@ FUSE_CALL(fuse_reply_err(req, 0)); } - void Rename(fuse_req_t req, FuseId parent, const char *name, FuseId newparent, const char *newname) - { - fuse_reply_err(req, EXDEV); //return cross-device link, so user space should re-create file and copy it - } - void SetAttr(fuse_req_t req, FuseId inode, struct stat *attr, int to_set, struct fuse_file_info *fi) { mtp::scoped_mutex_lock l(_mutex); @@ -616,11 +684,46 @@ entry.ReplyError(ENOENT); } + void UnlinkImpl(FuseId inode) + { + mtp::debug(" unlinking inode ", inode.Inode); + mtp::ObjectId id = FromFuse(inode); + _session->DeleteObject(id); + _openedFiles.erase(inode); + _objectAttrs.erase(id); + } + + void Unlink(fuse_req_t req, FuseId parent, const char *name) + { + mtp::scoped_mutex_lock l(_mutex); + ChildrenObjects &children = GetChildren(parent); + auto i = children.find(name); + if (i == children.end()) + { + FUSE_CALL(fuse_reply_err(req, ENOENT)); + return; + } + + FuseId inode = i->second; + UnlinkImpl(inode); + _directoryCache.erase(parent); + children.erase(i); + + FUSE_CALL(fuse_reply_err(req, 0)); + } + void RemoveDir (fuse_req_t req, FuseId parent, const char *name) { Unlink(req, parent, name); } - void Unlink(fuse_req_t req, FuseId parent, const char *name) + void Rename(fuse_req_t req, FuseId parent, const char *name, FuseId newparent, const char *newName) { + if (parent != newparent) { + //no renames across directory boundary, sorry + //return cross-device link, so user space should re-create file and copy it + FUSE_CALL(fuse_reply_err(req, EXDEV)); + return; + } + mtp::scoped_mutex_lock l(_mutex); ChildrenObjects &children = GetChildren(parent); auto i = children.find(name); @@ -631,14 +734,23 @@ } FuseId inode = i->second; - mtp::debug(" unlinking inode ", inode.Inode); + mtp::debug(" renaming inode ", inode.Inode, " to ", newName); + + auto old = children.find(newName); + if (old != children.end()) + { + mtp::debug(" unlinking target inode ", old->second.Inode); + UnlinkImpl(old->second); + children.erase(old); + } + mtp::ObjectId id = FromFuse(inode); - _directoryCache.erase(parent); - _openedFiles.erase(inode); - _objectAttrs.erase(id); + _session->SetObjectProperty(id, mtp::ObjectProperty::ObjectFilename, std::string(newName)); + children.erase(i); + children.emplace(newName, inode); + _directoryCache.erase(parent); - _session->DeleteObject(id); FUSE_CALL(fuse_reply_err(req, 0)); } @@ -758,19 +870,47 @@ int main(int argc, char **argv) { + std::string deviceFilter; bool claimInterface = true; + bool resetDevice = false; + bool showHelp = false; + + std::vector args; + args.push_back(argv[0]); + for(int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-C") == 0) + if (strcmp(argv[i], "-R") == 0) + resetDevice = false; + else if (strcmp(argv[i], "-C") == 0) claimInterface = false; - if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "-odebug") == 0) + else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "-odebug") == 0) + { + args.push_back(argv[i]); mtp::g_debug = true; - if (i + 1 < argc && strcmp(argv[i], "-o") == 0 && strcmp(argv[i + 1], "debug") == 0) + } + else if (i + 1 < argc && strcmp(argv[i], "-o") == 0 && strcmp(argv[i + 1], "debug") == 0) + { mtp::g_debug = true; + args.push_back(argv[i]); + } + else if (i + 1 < argc && strcmp(argv[i], "-D") == 0) + { + deviceFilter = argv[i + 1]; + ++i; + } + else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) + { + args.push_back(argv[i]); + showHelp = true; + } else + args.push_back(argv[i]); } + args.push_back(nullptr); + try - { g_wrapper.reset(new FuseWrapper(claimInterface)); } + { g_wrapper.reset(new FuseWrapper(deviceFilter, claimInterface, resetDevice)); } catch(const std::exception &ex) { mtp::error("connect failed: ", ex.what()); return 1; } @@ -793,34 +933,50 @@ ops.unlink = &Unlink; ops.statfs = &StatFS; - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_args fuse_args = FUSE_ARGS_INIT(static_cast(args.size() - 1), args.data()); struct fuse_chan *ch; char *mountpoint; int err = -1; int multithreaded = 0, foreground = 0; - if (fuse_parse_cmdline(&args, &mountpoint, &multithreaded, &foreground) != -1 && - mountpoint != NULL && - (ch = fuse_mount(mountpoint, &args)) != NULL) { - struct fuse_session *se; - - se = fuse_lowlevel_new(&args, &ops, - sizeof(ops), NULL); - if (se != NULL) { - if (fuse_set_signal_handlers(se) != -1) - { - fuse_session_add_chan(se, ch); - if (fuse_daemonize(foreground) == -1) - perror("fuse_daemonize"); - err = (multithreaded? fuse_session_loop_mt: fuse_session_loop)(se); - fuse_remove_signal_handlers(se); - fuse_session_remove_chan(ch); + if (fuse_parse_cmdline(&fuse_args, &mountpoint, &multithreaded, &foreground) != -1) + { + if (!mountpoint) + { + auto mp = g_wrapper->GetMountpoint(); + mountpoint = strdup(mp.c_str()); + mkdir(mountpoint, 0700); + } + + if (mountpoint != NULL && (ch = fuse_mount(mountpoint, &fuse_args)) != NULL) + { + struct fuse_session *se = fuse_lowlevel_new(&fuse_args, &ops, sizeof(ops), NULL); + if (se != NULL) + { + if (fuse_set_signal_handlers(se) != -1) + { + fuse_session_add_chan(se, ch); + if (fuse_daemonize(foreground) == -1) + perror("fuse_daemonize"); + err = (multithreaded? fuse_session_loop_mt: fuse_session_loop)(se); + fuse_remove_signal_handlers(se); + fuse_session_remove_chan(ch); + } + fuse_session_destroy(se); } - fuse_session_destroy(se); + fuse_unmount(mountpoint, ch); } - fuse_unmount(mountpoint, ch); + } else { + mtp::error("fuse_parse_cmdline failed"); + } + if (showHelp) { + mtp::print(); + mtp::print("Additional AFT options: "); + mtp::print(" -R reset device"); + mtp::print(" -C do not claim USB interface"); + mtp::print(" -v AFT verbose output"); } - fuse_opt_free_args(&args); + fuse_opt_free_args(&fuse_args); return err ? 1 : 0; } diff -Nru android-file-transfer-3.9+dfsg/.gitignore android-file-transfer-4.2/.gitignore --- android-file-transfer-3.9+dfsg/.gitignore 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/.gitignore 2020-12-29 16:10:28.000000000 +0000 @@ -5,6 +5,5 @@ nbproject build *.user -aft-* doc .vscode diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/call.h android-file-transfer-4.2/mtp/backend/darwin/usb/call.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/call.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/call.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,18 +17,20 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_USB_H -#define USB_USB_H +#ifndef AFTL_MTP_BACKEND_DARWIN_USB_CALL_H +#define AFTL_MTP_BACKEND_DARWIN_USB_CALL_H #include #include #include +#include #define USB_CALL(...) \ do { int _r_ = (__VA_ARGS__); \ switch(_r_) { \ case kIOReturnSuccess : break; \ case kIOReturnNoDevice : throw mtp::usb::DeviceNotFoundException(); \ + case kIOReturnExclusiveAccess: throw mtp::usb::DeviceBusyException(); \ default: throw mtp::usb::Exception(#__VA_ARGS__, _r_) ; \ } \ } while(false) diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Context.cpp android-file-transfer-4.2/mtp/backend/darwin/usb/Context.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Context.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Context.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Context.h android-file-transfer-4.2/mtp/backend/darwin/usb/Context.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Context.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Context.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_CONTEXT_H -#define USB_CONTEXT_H +#ifndef AFTL_MTP_BACKEND_DARWIN_USB_CONTEXT_H +#define AFTL_MTP_BACKEND_DARWIN_USB_CONTEXT_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Device.cpp android-file-transfer-4.2/mtp/backend/darwin/usb/Device.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Device.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Device.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -42,10 +42,13 @@ { return 0; } void Device::SetConfiguration(int idx) - { } + { USB_CALL((*_dev)->SetConfiguration(_dev, idx)); } void Device::ClearHalt(const EndpointPtr & ep) - { } + { + IOUSBInterfaceInterface ** interface = ep->GetInterfaceHandle(); + USB_CALL((*interface)->ClearPipeStall(interface, ep->GetRefIndex())); + } void Device::WriteBulk(const EndpointPtr & ep, const IObjectInputStreamPtr &inputStream, int timeout) { diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/DeviceDescriptor.cpp android-file-transfer-4.2/mtp/backend/darwin/usb/DeviceDescriptor.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/DeviceDescriptor.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/DeviceDescriptor.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -19,6 +19,7 @@ #include #include +#include //https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/USBBook/USBDeviceInterfaces/USBDevInterfaces.html @@ -133,7 +134,11 @@ DevicePtr DeviceDescriptor::TryOpen(ContextPtr context) { int r = (*_dev)->USBDeviceOpen(_dev); - return r == kIOReturnSuccess? std::make_shared(context, _dev): nullptr; + if (r == kIOReturnSuccess) + return std::make_shared(context, _dev); + + debug("USBDeviceOpen failed: ", Exception::GetErrorMessage(r)); + return nullptr; } DeviceDescriptor::~DeviceDescriptor() diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/DeviceDescriptor.h android-file-transfer-4.2/mtp/backend/darwin/usb/DeviceDescriptor.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/DeviceDescriptor.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/DeviceDescriptor.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_DEVICE_H -#define USB_DEVICE_H +#ifndef AFTL_MTP_BACKEND_DARWIN_USB_DEVICEDESCRIPTOR_H +#define AFTL_MTP_BACKEND_DARWIN_USB_DEVICEDESCRIPTOR_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Device.h android-file-transfer-4.2/mtp/backend/darwin/usb/Device.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Device.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Device.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef DEVICE_H -#define DEVICE_H +#ifndef AFTL_MTP_BACKEND_DARWIN_USB_DEVICE_H +#define AFTL_MTP_BACKEND_DARWIN_USB_DEVICE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Exception.cpp android-file-transfer-4.2/mtp/backend/darwin/usb/Exception.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Exception.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Exception.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Exception.h android-file-transfer-4.2/mtp/backend/darwin/usb/Exception.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Exception.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Exception.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTP_EXCEPTION_H -#define MTP_EXCEPTION_H +#ifndef AFTL_MTP_BACKEND_DARWIN_USB_EXCEPTION_H +#define AFTL_MTP_BACKEND_DARWIN_USB_EXCEPTION_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Interface.cpp android-file-transfer-4.2/mtp/backend/darwin/usb/Interface.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Interface.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Interface.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Interface.h android-file-transfer-4.2/mtp/backend/darwin/usb/Interface.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/Interface.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/Interface.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef INTERFACE_H -#define INTERFACE_H +#ifndef AFTL_MTP_BACKEND_DARWIN_USB_INTERFACE_H +#define AFTL_MTP_BACKEND_DARWIN_USB_INTERFACE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/usb.h android-file-transfer-4.2/mtp/backend/darwin/usb/usb.h --- android-file-transfer-3.9+dfsg/mtp/backend/darwin/usb/usb.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/darwin/usb/usb.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/call.h android-file-transfer-4.2/mtp/backend/libusb/usb/call.h --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/call.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/call.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_USB_H -#define USB_USB_H +#ifndef AFTL_MTP_BACKEND_LIBUSB_USB_CALL_H +#define AFTL_MTP_BACKEND_LIBUSB_USB_CALL_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Context.cpp android-file-transfer-4.2/mtp/backend/libusb/usb/Context.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Context.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Context.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Context.h android-file-transfer-4.2/mtp/backend/libusb/usb/Context.h --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Context.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Context.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_CONTEXT_H -#define USB_CONTEXT_H +#ifndef AFTL_MTP_BACKEND_LIBUSB_USB_CONTEXT_H +#define AFTL_MTP_BACKEND_LIBUSB_USB_CONTEXT_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Device.cpp android-file-transfer-4.2/mtp/backend/libusb/usb/Device.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Device.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Device.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/DeviceDescriptor.cpp android-file-transfer-4.2/mtp/backend/libusb/usb/DeviceDescriptor.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/DeviceDescriptor.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/DeviceDescriptor.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/DeviceDescriptor.h android-file-transfer-4.2/mtp/backend/libusb/usb/DeviceDescriptor.h --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/DeviceDescriptor.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/DeviceDescriptor.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_DEVICE_H -#define USB_DEVICE_H +#ifndef AFTL_MTP_BACKEND_LIBUSB_USB_DEVICEDESCRIPTOR_H +#define AFTL_MTP_BACKEND_LIBUSB_USB_DEVICEDESCRIPTOR_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Device.h android-file-transfer-4.2/mtp/backend/libusb/usb/Device.h --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Device.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Device.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef DEVICE_H -#define DEVICE_H +#ifndef AFTL_MTP_BACKEND_LIBUSB_USB_DEVICE_H +#define AFTL_MTP_BACKEND_LIBUSB_USB_DEVICE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Exception.cpp android-file-transfer-4.2/mtp/backend/libusb/usb/Exception.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Exception.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Exception.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Exception.h android-file-transfer-4.2/mtp/backend/libusb/usb/Exception.h --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Exception.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Exception.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTP_EXCEPTION_H -#define MTP_EXCEPTION_H +#ifndef AFTL_MTP_BACKEND_LIBUSB_USB_EXCEPTION_H +#define AFTL_MTP_BACKEND_LIBUSB_USB_EXCEPTION_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Interface.cpp android-file-transfer-4.2/mtp/backend/libusb/usb/Interface.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Interface.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Interface.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Interface.h android-file-transfer-4.2/mtp/backend/libusb/usb/Interface.h --- android-file-transfer-3.9+dfsg/mtp/backend/libusb/usb/Interface.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/libusb/usb/Interface.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef INTERFACE_H -#define INTERFACE_H +#ifndef AFTL_MTP_BACKEND_LIBUSB_USB_INTERFACE_H +#define AFTL_MTP_BACKEND_LIBUSB_USB_INTERFACE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/BufferAllocator.h android-file-transfer-4.2/mtp/backend/linux/usb/BufferAllocator.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/BufferAllocator.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/BufferAllocator.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,13 +17,15 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_BACKEND_LINUX_USB_BUFFERALLOCATOR_H -#define AFT_BACKEND_LINUX_USB_BUFFERALLOCATOR_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_BUFFERALLOCATOR_H +#define AFTL_MTP_BACKEND_LINUX_USB_BUFFERALLOCATOR_H #include #include #include #include +#include + #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Context.cpp android-file-transfer-4.2/mtp/backend/linux/usb/Context.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Context.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Context.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Context.h android-file-transfer-4.2/mtp/backend/linux/usb/Context.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Context.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Context.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_CONTEXT_H -#define USB_CONTEXT_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_CONTEXT_H +#define AFTL_MTP_BACKEND_LINUX_USB_CONTEXT_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Device.cpp android-file-transfer-4.2/mtp/backend/linux/usb/Device.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Device.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Device.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -68,7 +68,8 @@ } - Device::Device(int fd, const EndpointPtr &controlEp): _fd(fd), _capabilities(0), _controlEp(controlEp) + Device::Device(int fd, const EndpointPtr &controlEp, u8 configuration): + _fd(fd), _capabilities(0), _controlEp(controlEp), _configuration(configuration) { try { IOCTL(_fd.Get(), USBDEVFS_GET_CAPABILITIES, &_capabilities); } catch(const std::exception &ex) @@ -92,6 +93,8 @@ PRINT_CAP(USBDEVFS_CAP_REAP_AFTER_DISCONNECT, ""); PRINT_CAP(USBDEVFS_CAP_MMAP, ""); PRINT_CAP(USBDEVFS_CAP_DROP_PRIVILEGES, ""); + PRINT_CAP(USBDEVFS_CAP_CONNINFO_EX, ""); + PRINT_CAP(USBDEVFS_CAP_SUSPEND, ""); if (capabilities) debug(""); } @@ -104,20 +107,26 @@ void Device::Reset() { + debug("resetting device..."); try - { IOCTL(_fd.Get(), USBDEVFS_RESET); } + { + IOCTL(_fd.Get(), USBDEVFS_RESET); + SetConfiguration(_configuration); + } catch(const std::exception &ex) { error("resetting device failed: ", ex.what()); } } int Device::GetConfiguration() const { - return 0; + return _configuration; } void Device::SetConfiguration(int idx) { - error("SetConfiguration(", idx, "): not implemented"); + debug("SetConfiguration(", idx, ")"); + IOCTL(_fd.Get(), USBDEVFS_SETCONFIGURATION, &idx); + _configuration = idx; } struct Device::Urb : usbdevfs_urb, Noncopyable @@ -270,7 +279,7 @@ usbdevfs_urb * completedKernelUrb = static_cast(Reap(timeout)); if (urb->GetKernelUrb() != completedKernelUrb) { - error("got unknown urb: ", completedKernelUrb); + error("got unknown urb: ", completedKernelUrb, " of size ", completedKernelUrb->buffer_length); continue; } else diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/DeviceDescriptor.cpp android-file-transfer-4.2/mtp/backend/linux/usb/DeviceDescriptor.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/DeviceDescriptor.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/DeviceDescriptor.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -30,11 +30,13 @@ { DeviceDescriptor::DeviceDescriptor(int busId, const std::string &path): _busId(busId), _path(path) { + debug("creating device descriptor at ", path); _vendor = Directory::ReadInt(path + "/idVendor"); _product = Directory::ReadInt(path + "/idProduct"); _deviceNumber = Directory::ReadInt(path + "/devnum", 10); _controlEp = std::make_shared(path + "/ep_00"); _descriptor = Directory::ReadAll(path + "/descriptors"); + _configurationValue = Directory::ReadInt(path + "/bConfigurationValue", 10); } DevicePtr DeviceDescriptor::Open(ContextPtr context) @@ -51,7 +53,7 @@ snprintf(fname, sizeof(fname), "/dev/bus/usb/%03d/%03u", _busId, _deviceNumber); int fd = open(fname, O_RDWR); if (fd != -1) - return std::make_shared(fd, _controlEp); + return std::make_shared(fd, _controlEp, _configurationValue); debug("error: ", posix::Exception::GetErrorMessage(errno)); return nullptr; } diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/DeviceDescriptor.h android-file-transfer-4.2/mtp/backend/linux/usb/DeviceDescriptor.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/DeviceDescriptor.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/DeviceDescriptor.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_DEVICE_H -#define USB_DEVICE_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_DEVICEDESCRIPTOR_H +#define AFTL_MTP_BACKEND_LINUX_USB_DEVICEDESCRIPTOR_H #include #include @@ -57,6 +57,7 @@ std::string _path; u16 _vendor, _product; int _deviceNumber; + int _configurationValue; std::map _configurationMap; std::vector _configuration; EndpointPtr _controlEp; diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Device.h android-file-transfer-4.2/mtp/backend/linux/usb/Device.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Device.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Device.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef DEVICE_H -#define DEVICE_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_DEVICE_H +#define AFTL_MTP_BACKEND_LINUX_USB_DEVICE_H #include #include @@ -53,6 +53,7 @@ posix::FileHandler _fd; u32 _capabilities; EndpointPtr _controlEp; + u8 _configuration; BufferAllocatorPtr _bufferAllocator; struct Urb; @@ -60,7 +61,7 @@ std::queue> _controls; public: - Device(int fd, const EndpointPtr &controlEp); + Device(int fd, const EndpointPtr &controlEp, u8 configuration); ~Device(); InterfaceTokenPtr ClaimInterface(const InterfacePtr & interface) diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Directory.h android-file-transfer-4.2/mtp/backend/linux/usb/Directory.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Directory.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Directory.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef DIRECTORY_H -#define DIRECTORY_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_DIRECTORY_H +#define AFTL_MTP_BACKEND_LINUX_USB_DIRECTORY_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Endpoint.cpp android-file-transfer-4.2/mtp/backend/linux/usb/Endpoint.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Endpoint.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Endpoint.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Endpoint.h android-file-transfer-4.2/mtp/backend/linux/usb/Endpoint.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Endpoint.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Endpoint.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USBFS_ENDPOINT_H -#define USBFS_ENDPOINT_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_ENDPOINT_H +#define AFTL_MTP_BACKEND_LINUX_USB_ENDPOINT_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Interface.cpp android-file-transfer-4.2/mtp/backend/linux/usb/Interface.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Interface.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Interface.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Interface.h android-file-transfer-4.2/mtp/backend/linux/usb/Interface.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/Interface.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/Interface.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef INTERFACE_H -#define INTERFACE_H +#ifndef AFTL_MTP_BACKEND_LINUX_USB_INTERFACE_H +#define AFTL_MTP_BACKEND_LINUX_USB_INTERFACE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/linux/usbdevice_fs.h android-file-transfer-4.2/mtp/backend/linux/usb/linux/usbdevice_fs.h --- android-file-transfer-3.9+dfsg/mtp/backend/linux/usb/linux/usbdevice_fs.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/linux/usb/linux/usbdevice_fs.h 2020-12-29 16:10:28.000000000 +0000 @@ -75,10 +75,30 @@ unsigned char slow; }; +struct usbdevfs_conninfo_ex { + __u32 size; /* Size of the structure from the kernel's */ + /* point of view. Can be used by userspace */ + /* to determine how much data can be */ + /* used/trusted. */ + __u32 busnum; /* USB bus number, as enumerated by the */ + /* kernel, the device is connected to. */ + __u32 devnum; /* Device address on the bus. */ + __u32 speed; /* USB_SPEED_* constants from ch9.h */ + __u8 num_ports; /* Number of ports the device is connected */ + /* to on the way to the root hub. It may */ + /* be bigger than size of 'ports' array so */ + /* userspace can detect overflows. */ + __u8 ports[7]; /* List of ports on the way from the root */ + /* hub to the device. Current limit in */ + /* USB specification is 7 tiers (root hub, */ + /* 5 intermediate hubs, device), which */ + /* gives at most 6 port entries. */ +}; + #define USBDEVFS_URB_SHORT_NOT_OK 0x01 #define USBDEVFS_URB_ISO_ASAP 0x02 #define USBDEVFS_URB_BULK_CONTINUATION 0x04 -#define USBDEVFS_URB_NO_FSBR 0x20 +#define USBDEVFS_URB_NO_FSBR 0x20 /* Not used */ #define USBDEVFS_URB_ZERO_PACKET 0x40 #define USBDEVFS_URB_NO_INTERRUPT 0x80 @@ -136,6 +156,8 @@ #define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10 #define USBDEVFS_CAP_MMAP 0x20 #define USBDEVFS_CAP_DROP_PRIVILEGES 0x40 +#define USBDEVFS_CAP_CONNINFO_EX 0x80 +#define USBDEVFS_CAP_SUSPEND 0x100 /* USBDEVFS_DISCONNECT_CLAIM flags & struct */ @@ -156,6 +178,11 @@ unsigned char eps[0]; }; +/* + * USB_SPEED_* values returned by USBDEVFS_GET_SPEED are defined in + * linux/usb/ch9.h + */ + #define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer) #define USBDEVFS_CONTROL32 _IOWR('U', 0, struct usbdevfs_ctrltransfer32) #define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer) @@ -190,5 +217,14 @@ #define USBDEVFS_ALLOC_STREAMS _IOR('U', 28, struct usbdevfs_streams) #define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams) #define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32) +#define USBDEVFS_GET_SPEED _IO('U', 31) +/* + * Returns struct usbdevfs_conninfo_ex; length is variable to allow + * extending size of the data returned. + */ +#define USBDEVFS_CONNINFO_EX(len) _IOC(_IOC_READ, 'U', 32, len) +#define USBDEVFS_FORBID_SUSPEND _IO('U', 33) +#define USBDEVFS_ALLOW_SUSPEND _IO('U', 34) +#define USBDEVFS_WAIT_FOR_RESUME _IO('U', 35) #endif /* _UAPI_LINUX_USBDEVICE_FS_H */ diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/posix/Exception.cpp android-file-transfer-4.2/mtp/backend/posix/Exception.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/posix/Exception.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/posix/Exception.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -35,7 +35,7 @@ std::string Exception::GetErrorMessage(int returnCode) { char buf[1024]; -#ifdef _GNU_SOURCE +#if defined(_GNU_SOURCE) && defined(__GLIBC__) std::string text(strerror_r(returnCode, buf, sizeof(buf))); #else int r = strerror_r(returnCode, buf, sizeof(buf)); @@ -44,4 +44,4 @@ return text; } -}} \ No newline at end of file +}} diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/posix/Exception.h android-file-transfer-4.2/mtp/backend/posix/Exception.h --- android-file-transfer-3.9+dfsg/mtp/backend/posix/Exception.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/posix/Exception.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTP_POSIX_EXCEPTION_H -#define MTP_POSIX_EXCEPTION_H +#ifndef AFTL_MTP_BACKEND_POSIX_EXCEPTION_H +#define AFTL_MTP_BACKEND_POSIX_EXCEPTION_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/posix/FileHandler.cpp android-file-transfer-4.2/mtp/backend/posix/FileHandler.cpp --- android-file-transfer-3.9+dfsg/mtp/backend/posix/FileHandler.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/posix/FileHandler.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/backend/posix/FileHandler.h android-file-transfer-4.2/mtp/backend/posix/FileHandler.h --- android-file-transfer-3.9+dfsg/mtp/backend/posix/FileHandler.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/backend/posix/FileHandler.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef POSIX_FILEHANDLER_H -#define POSIX_FILEHANDLER_H +#ifndef AFTL_MTP_BACKEND_POSIX_FILEHANDLER_H +#define AFTL_MTP_BACKEND_POSIX_FILEHANDLER_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/ByteArray.cpp android-file-transfer-4.2/mtp/ByteArray.cpp --- android-file-transfer-3.9+dfsg/mtp/ByteArray.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ByteArray.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -23,41 +23,4 @@ namespace mtp { - - void HexDump(const std::string &prefix, const ByteArray &data, bool force) - { - if (!g_debug && !force) - return; - - std::stringstream ss; - ss << prefix << "[" << data.size() << "]:\n"; - size_t i; - - std::string chars; - chars.reserve(16); - for(i = 0; i < data.size(); ++i) - { - bool first = ((i & 0xf) == 0); - bool last = ((i & 0xf) == 0x0f); - if (first) - ss << hex(i, 8) << ": "; - - u8 value = data[i]; - ss << hex(value, 2); - chars.push_back(value < 0x20 || value >= 0x7f? '.': value); - if (last) - { - ss << " " << chars << "\n"; - chars.clear(); - } - else - ss << " "; - } - if (chars.size()) - { - ss << std::string((size_t)(16 - chars.size()) * 3, ' ') << chars << "\n"; - } - - error(ss.str()); - } } diff -Nru android-file-transfer-3.9+dfsg/mtp/ByteArray.h android-file-transfer-4.2/mtp/ByteArray.h --- android-file-transfer-3.9+dfsg/mtp/ByteArray.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ByteArray.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef BYTEARRAY_H -#define BYTEARRAY_H +#ifndef AFTL_MTP_BYTEARRAY_H +#define AFTL_MTP_BYTEARRAY_H #include #include @@ -28,9 +28,6 @@ { using ByteArray = std::vector; //! \typedef ByteArray : vector of bytes DECLARE_PTR(ByteArray); - - //! output hex dump to debug channel - void HexDump(const std::string &prefix, const ByteArray &data, bool force = false); } #endif /* BYTEARRAY_H */ diff -Nru android-file-transfer-3.9+dfsg/mtp/Demangle.h android-file-transfer-4.2/mtp/Demangle.h --- android-file-transfer-3.9+dfsg/mtp/Demangle.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/Demangle.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_MTP_DEMANGLE_H -#define AFS_MTP_DEMANGLE_H +#ifndef AFTL_MTP_DEMANGLE_H +#define AFTL_MTP_DEMANGLE_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/function_invoker.h android-file-transfer-4.2/mtp/function_invoker.h --- android-file-transfer-3.9+dfsg/mtp/function_invoker.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/function_invoker.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTP_FUNCTION_INVOKER_H -#define MTP_FUNCTION_INVOKER_H +#ifndef AFTL_MTP_FUNCTION_INVOKER_H +#define AFTL_MTP_FUNCTION_INVOKER_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/log.cpp android-file-transfer-4.2/mtp/log.cpp --- android-file-transfer-3.9+dfsg/mtp/log.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/log.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -16,10 +16,54 @@ along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +#include namespace mtp { bool g_debug = false; + void HexDump(std::stringstream & ss, const std::string &prefix, size_t size, InputStream & is) + { + ss << prefix << "[" << size << "]:\n"; + size_t i; + + std::string chars; + chars.reserve(16); + for(i = 0; i < size; ++i) + { + bool first = ((i & 0xf) == 0); + bool last = ((i & 0xf) == 0x0f); + if (first) + ss << hex(i, 8) << ": "; + + u8 value = is.Read8(); + ss << hex(value, 2); + chars.push_back(value < 0x20 || value >= 0x7f? '.': value); + if (last) + { + ss << " " << chars << "\n"; + chars.clear(); + } + else + ss << " "; + } + if (chars.size()) + { + ss << std::string((size_t)(16 - chars.size()) * 3, ' ') << chars << "\n"; + } + } + + void HexDump(const std::string &prefix, const ByteArray &data, bool force) + { + if (!g_debug && !force) + return; + + std::stringstream ss; + InputStream is(data); + HexDump(ss, prefix, data.size(), is); + error(ss.str()); + } + } diff -Nru android-file-transfer-3.9+dfsg/mtp/log.h android-file-transfer-4.2/mtp/log.h --- android-file-transfer-3.9+dfsg/mtp/log.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/log.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,13 +17,15 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_LOG_H -#define AFT_LOG_H +#ifndef AFTL_MTP_LOG_H +#define AFTL_MTP_LOG_H +#include #include - +#include #include #include +#include //I love libstdc++! inline std::ostream & operator << (std::ostream & stream, unsigned char v) @@ -34,6 +36,7 @@ namespace mtp { + class InputStream; inline std::ostream & operator << (std::ostream & stream, unsigned char v) { @@ -63,6 +66,13 @@ stream.fill(oldFill); return stream; } + + std::string ToString() const + { + std::stringstream ss; + (*this) >> ss; + return ss.str(); + } }; } @@ -109,6 +119,10 @@ if (g_debug) error(value, args...); } + + //! output hex dump to debug channel + void HexDump(const std::string &prefix, const ByteArray &data, bool force = false); + void HexDump(std::stringstream & ss, const std::string &prefix, size_t size, InputStream & is); } #endif diff -Nru android-file-transfer-3.9+dfsg/mtp/make_function.h android-file-transfer-4.2/mtp/make_function.h --- android-file-transfer-3.9+dfsg/mtp/make_function.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/make_function.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_CLI_MAKE_FUNCTION_H -#define AFT_CLI_MAKE_FUNCTION_H +#ifndef AFTL_MTP_MAKE_FUNCTION_H +#define AFTL_MTP_MAKE_FUNCTION_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/metadata/Library.cpp android-file-transfer-4.2/mtp/metadata/Library.cpp --- android-file-transfer-3.9+dfsg/mtp/metadata/Library.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/metadata/Library.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include +#include + +namespace mtp +{ + namespace + { + const std::string UknownArtist ("UknownArtist"); + const std::string UknownAlbum ("UknownAlbum"); + const std::string VariousArtists ("VariousArtists"); + } + + Library::NameToObjectIdMap Library::ListAssociations(ObjectId parentId) + { + NameToObjectIdMap list; + + ByteArray data = _session->GetObjectPropertyList(parentId, ObjectFormat::Association, ObjectProperty::ObjectFilename, 0, 1); + ObjectPropertyListParser parser; + parser.Parse(data, [&](ObjectId id, ObjectProperty property, const std::string &name) { + list.insert(std::make_pair(name, id)); + }); + return list; + } + + ObjectId Library::GetOrCreate(ObjectId parentId, const std::string &name) + { + auto objects = _session->GetObjectHandles(_storage, mtp::ObjectFormat::Association, parentId); + for (auto id : objects.ObjectHandles) + { + auto oname = _session->GetObjectStringProperty(id, ObjectProperty::ObjectFilename); + if (name == oname) + return id; + } + return _session->CreateDirectory(name, parentId, _storage).ObjectId; + } + + Library::Library(const mtp::SessionPtr & session, ProgressReporter && reporter): _session(session) + { + auto storages = _session->GetStorageIDs(); + if (storages.StorageIDs.empty()) + throw std::runtime_error("no storages found"); + + u64 progress = 0, total = 0; + if (reporter) + reporter(State::Initialising, progress, total); + + _artistSupported = _session->GetDeviceInfo().Supports(ObjectFormat::Artist); + debug("device supports ObjectFormat::Artist: ", _artistSupported? "yes": "no"); + { + auto propsSupported = _session->GetObjectPropertiesSupported(ObjectFormat::AbstractAudioAlbum); + _albumDateAuthoredSupported = std::find(propsSupported.ObjectPropertyCodes.begin(), propsSupported.ObjectPropertyCodes.end(), ObjectProperty::DateAuthored) != propsSupported.ObjectPropertyCodes.end(); + } + + _storage = storages.StorageIDs[0]; //picking up first storage. + //zune fails to create artist/album without storage id + { + ByteArray data = _session->GetObjectPropertyList(Session::Root, ObjectFormat::Association, ObjectProperty::ObjectFilename, 0, 1); + ObjectStringPropertyListParser::Parse(data, [&](ObjectId id, ObjectProperty property, const std::string &name) + { + if (name == "Artists") + _artistsFolder = id; + else if (name == "Albums") + _albumsFolder = id; + else if (name == "Music") + _musicFolder = id; + }); + } + if (_artistSupported && _artistsFolder == ObjectId()) + _artistsFolder = _session->CreateDirectory("Artists", Session::Root, _storage).ObjectId; + if (_albumsFolder == ObjectId()) + _albumsFolder = _session->CreateDirectory("Albums", Session::Root, _storage).ObjectId; + if (_musicFolder == ObjectId()) + _musicFolder = _session->CreateDirectory("Music", Session::Root, _storage).ObjectId; + + debug("artists folder: ", _artistsFolder != ObjectId()? _artistsFolder.Id: 0); + debug("albums folder: ", _albumsFolder.Id); + debug("music folder: ", _musicFolder.Id); + + auto musicFolders = ListAssociations(_musicFolder); + + using namespace mtp; + + ByteArray artists, albums; + if (_artistSupported) + { + debug("getting artists..."); + if (reporter) + reporter(State::QueryingArtists, progress, total); + + artists = _session->GetObjectPropertyList(Session::Root, mtp::ObjectFormat::Artist, mtp::ObjectProperty::Name, 0, 1); + HexDump("artists", artists); + + total += ObjectStringPropertyListParser::GetSize(artists); + } + { + debug("getting albums..."); + if (reporter) + reporter(State::QueryingAlbums, progress, total); + + albums = _session->GetObjectPropertyList(Session::Root, mtp::ObjectFormat::AbstractAudioAlbum, mtp::ObjectProperty::Name, 0, 1); + HexDump("albums", artists); + + total += ObjectStringPropertyListParser::GetSize(albums); + } + + if (_artistSupported) + { + if (reporter) + reporter(State::LoadingArtists, progress, total); + + ObjectStringPropertyListParser::Parse(artists, [&](ObjectId id, ObjectProperty property, const std::string &name) + { + debug("artist: ", name, "\t", id.Id); + auto artist = std::make_shared(); + artist->Id = id; + artist->Name = name; + auto it = musicFolders.find(name); + if (it != musicFolders.end()) + artist->MusicFolderId = it->second; + else + artist->MusicFolderId = _session->CreateDirectory(name, _musicFolder, _storage).ObjectId; + + _artists.insert(std::make_pair(name, artist)); + if (reporter) + reporter(State::LoadingArtists, ++progress, total); + }); + } + + if (reporter) + reporter(State::LoadingAlbums, progress, total); + + std::unordered_map albumFolders; + ObjectStringPropertyListParser::Parse(albums, [&](ObjectId id, ObjectProperty property, const std::string &name) + { + auto artistName = _session->GetObjectStringProperty(id, ObjectProperty::Artist); + + std::string albumDate; + if (_albumDateAuthoredSupported) + albumDate = _session->GetObjectStringProperty(id, ObjectProperty::DateAuthored); + + auto artist = GetArtist(artistName); + if (!artist) + artist = CreateArtist(artistName); + + debug("album: ", artistName, " -- ", name, "\t", id.Id, "\t", albumDate); + auto album = std::make_shared(); + album->Name = name; + album->Artist = artist; + album->Id = id; + album->Year = !albumDate.empty()? ConvertDateTime(albumDate): 0; + if (albumFolders.find(artist) == albumFolders.end()) { + albumFolders[artist] = ListAssociations(artist->MusicFolderId); + } + auto it = albumFolders.find(artist); + if (it == albumFolders.end()) + throw std::runtime_error("no iterator after insert, internal error"); + + const auto & albums = it->second; + auto alit = albums.find(name); + if (alit != albums.end()) + album->MusicFolderId = alit->second; + else + album->MusicFolderId = _session->CreateDirectory(name, artist->MusicFolderId, _storage).ObjectId; + + _albums.insert(std::make_pair(std::make_pair(artist, name), album)); + if (reporter) + reporter(State::LoadingAlbums, ++progress, total); + }); + + if (reporter) + reporter(State::Loaded, progress, total); + } + + Library::~Library() + { } + + Library::ArtistPtr Library::GetArtist(std::string name) + { + if (name.empty()) + name = UknownArtist; + + auto it = _artists.find(name); + return it != _artists.end()? it->second: ArtistPtr(); + } + + + Library::ArtistPtr Library::CreateArtist(std::string name) + { + if (name.empty()) + name = UknownArtist; + + auto artist = std::make_shared(); + artist->Name = name; + artist->MusicFolderId = GetOrCreate(_musicFolder, name); + + if (_artistSupported) + { + ByteArray propList; + OutputStream os(propList); + + os.Write32(2); //number of props + + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Name)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(name); + + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ObjectFilename)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(name + ".art"); + + auto response = _session->SendObjectPropList(_storage, _artistsFolder, ObjectFormat::Artist, 0, propList); + artist->Id = response.ObjectId; + } + + _artists.insert(std::make_pair(name, artist)); + return artist; + } + + Library::AlbumPtr Library::GetAlbum(const ArtistPtr & artist, std::string name) + { + if (name.empty()) + name = UknownAlbum; + + auto it = _albums.find(std::make_pair(artist, name)); + return it != _albums.end()? it->second: AlbumPtr(); + } + + Library::AlbumPtr Library::CreateAlbum(const ArtistPtr & artist, std::string name, int year) + { + if (!artist) + throw std::runtime_error("artists is required"); + + if (name.empty()) + name = UknownAlbum; + + ByteArray propList; + OutputStream os(propList); + bool sendYear = year != 0 && _albumDateAuthoredSupported; + + os.Write32(3 + (sendYear? 1: 0)); //number of props + + if (_artistSupported) + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ArtistId)); + os.Write16(static_cast(DataTypeCode::Uint32)); + os.Write32(artist->Id.Id); + } + else + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Artist)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(artist->Name); + } + + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Name)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(name); + + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ObjectFilename)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(artist->Name + "--" + name + ".alb"); + + if (sendYear) + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::DateAuthored)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(ConvertYear(year)); + } + + auto album = std::make_shared(); + album->Artist = artist; + album->Name = name; + album->Year = year; + album->MusicFolderId = GetOrCreate(artist->MusicFolderId, name); + + auto response = _session->SendObjectPropList(_storage, _albumsFolder, ObjectFormat::AbstractAudioAlbum, 0, propList); + album->Id = response.ObjectId; + + _albums.insert(std::make_pair(std::make_pair(artist, name), album)); + return album; + } + + bool Library::HasTrack(const AlbumPtr & album, const std::string &name, int trackIndex) + { + if (!album) + return false; + + LoadRefs(album); + + auto & tracks = album->Tracks; + auto range = tracks.equal_range(name); + for(auto i = range.first; i != range.second; ++i) + { + if (i->second == trackIndex) + return true; + } + + return false; + } + + Library::NewTrackInfo Library::CreateTrack(const ArtistPtr & artist, + const AlbumPtr & album, + ObjectFormat type, + std::string name, const std::string & genre, int trackIndex, + const std::string &filename, size_t size) + { + ByteArray propList; + OutputStream os(propList); + + os.Write32(3 + (!genre.empty()? 1: 0) + (trackIndex? 1: 0)); //number of props + + if (_artistSupported) + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ArtistId)); + os.Write16(static_cast(DataTypeCode::Uint32)); + os.Write32(artist->Id.Id); + } + else + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Artist)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(artist->Name); + } + + + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Name)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(name); + + if (trackIndex) + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Track)); + os.Write16(static_cast(DataTypeCode::Uint16)); + os.Write16(trackIndex); + } + + if (!genre.empty()) + { + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::Genre)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(genre); + } + + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ObjectFilename)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(filename); + + auto response = _session->SendObjectPropList(_storage, album->MusicFolderId, type, size, propList); + NewTrackInfo ti; + ti.Id = response.ObjectId; + ti.Name = name; + ti.Index = trackIndex; + return ti; + } + + void Library::LoadRefs(AlbumPtr album) + { + if (!album || album->RefsLoaded) + return; + + auto refs = _session->GetObjectReferences(album->Id).ObjectHandles; + std::copy(refs.begin(), refs.end(), std::inserter(album->Refs, album->Refs.begin())); + for(auto trackId : refs) + { + auto name = _session->GetObjectStringProperty(trackId, ObjectProperty::Name); + auto index = _session->GetObjectIntegerProperty(trackId, ObjectProperty::Track); + debug("[", index, "]: ", name); + album->Tracks.insert(std::make_pair(name, index)); + } + album->RefsLoaded = true; + } + + void Library::AddTrack(AlbumPtr album, const NewTrackInfo & ti) + { + if (!album) + return; + + LoadRefs(album); + + auto & refs = album->Refs; + auto & tracks = album->Tracks; + + msg::ObjectHandles handles; + std::copy(refs.begin(), refs.end(), std::back_inserter(handles.ObjectHandles)); + handles.ObjectHandles.push_back(ti.Id); + _session->SetObjectReferences(album->Id, handles); + refs.insert(ti.Id); + tracks.insert(std::make_pair(ti.Name, ti.Index)); + } + + + bool Library::Supported(const mtp::SessionPtr & session) + { + auto & gdi = session->GetDeviceInfo(); + return + gdi.Supports(OperationCode::GetObjectPropList) && + gdi.Supports(OperationCode::SendObjectPropList) && + gdi.Supports(OperationCode::SetObjectReferences) && + gdi.Supports(ObjectFormat::AbstractAudioAlbum); + ; + } + +} diff -Nru android-file-transfer-3.9+dfsg/mtp/metadata/Library.h android-file-transfer-4.2/mtp/metadata/Library.h --- android-file-transfer-3.9+dfsg/mtp/metadata/Library.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/metadata/Library.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,109 @@ +#ifndef AFTL_MTP_METADATA_LIBRARY_H +#define AFTL_MTP_METADATA_LIBRARY_H + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mtp +{ + class Session; + DECLARE_PTR(Session); + + class Library + { + SessionPtr _session; + StorageId _storage; + + public: + enum struct State { + Initialising, + QueryingArtists, + LoadingArtists, + QueryingAlbums, + LoadingAlbums, + Loaded + }; + using ProgressReporter = std::function; + + struct Artist + { + ObjectId Id; + ObjectId MusicFolderId; + std::string Name; + }; + DECLARE_PTR(Artist); + + struct Album + { + ObjectId Id; + ObjectId MusicFolderId; + ArtistPtr Artist; + std::string Name; + time_t Year = 0; + bool RefsLoaded = false; + + void LoadRefs(); + + std::unordered_set Refs; + std::unordered_multimap Tracks; + }; + DECLARE_PTR(Album); + + struct NewTrackInfo + { + ObjectId Id; + std::string Name; + int Index; + }; + + private: + ObjectId _artistsFolder; + ObjectId _albumsFolder; + ObjectId _musicFolder; + bool _artistSupported; + bool _albumDateAuthoredSupported; + + using ArtistMap = std::unordered_map; + ArtistMap _artists; + + using AlbumKey = std::pair; + struct AlbumKeyHash + { size_t operator() (const AlbumKey & key) const { + return std::hash()(key.first) + std::hash()(key.second); + }}; + + using AlbumMap = std::unordered_map; + AlbumMap _albums; + + using NameToObjectIdMap = std::unordered_map; + NameToObjectIdMap ListAssociations(ObjectId parentId); + + ObjectId GetOrCreate(ObjectId parentId, const std::string &name); + void LoadRefs(AlbumPtr album); + + public: + Library(const mtp::SessionPtr & session, ProgressReporter && reporter = ProgressReporter()); + ~Library(); + + static bool Supported(const mtp::SessionPtr & session); + + //search by Metadata? + ArtistPtr GetArtist(std::string name); + ArtistPtr CreateArtist(std::string name); + + AlbumPtr GetAlbum(const ArtistPtr & artist, std::string name); + AlbumPtr CreateAlbum(const ArtistPtr & artist, std::string name, int year); + bool HasTrack(const AlbumPtr & album, const std::string &name, int trackIndex); + NewTrackInfo CreateTrack(const ArtistPtr & artist, const AlbumPtr & album, ObjectFormat type, std::string name, const std::string & genre, int trackIndex, const std::string &filename, size_t size); + void AddTrack(AlbumPtr album, const NewTrackInfo &ti); + }; + DECLARE_PTR(Library); +} + +#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/metadata/Metadata.cpp android-file-transfer-4.2/mtp/metadata/Metadata.cpp --- android-file-transfer-3.9+dfsg/mtp/metadata/Metadata.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/metadata/Metadata.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,44 @@ +#include + +#ifdef HAVE_TAGLIB +# include +# include +# include +#endif + +namespace mtp +{ +#ifdef HAVE_TAGLIB + MetadataPtr Metadata::Read(const std::string & path) + { + TagLib::FileRef f(path.c_str()); + auto tag = f.tag(); + if (f.isNull() || !tag) + return nullptr; + + auto meta = std::make_shared(); + auto artist = tag->artist(); + + const auto & props = tag->properties(); + auto album_it = props.find("ALBUMARTIST"); + if (album_it == props.end()) + album_it = props.find("ALBUM ARTIST"); + if (album_it == props.end()) + album_it = props.find("MUSICBRAINZ_ALBUMARTIST"); + if (album_it != props.end()) + artist = album_it->second.toString(); + + meta->Title = tag->title().to8Bit(true); + meta->Artist = artist.to8Bit(true); + meta->Album = tag->album().to8Bit(true); + meta->Genre = tag->genre().to8Bit(true); + meta->Year = tag->year(); + meta->Track = tag->track(); + return meta; + } + +#else + MetadataPtr Metadata::Read(const std::string & path) + { return nullptr; } +#endif +} diff -Nru android-file-transfer-3.9+dfsg/mtp/metadata/Metadata.h android-file-transfer-4.2/mtp/metadata/Metadata.h --- android-file-transfer-3.9+dfsg/mtp/metadata/Metadata.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/metadata/Metadata.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,27 @@ +#ifndef AFTL_MTP_METADATA_METADATA_H +#define AFTL_MTP_METADATA_METADATA_H + +#include +#include +#include + +namespace mtp +{ + struct Metadata; + DECLARE_PTR(Metadata); + + struct Metadata + { + std::string Title; + std::string Artist; + std::string Album; + std::string Genre; + unsigned Year; + unsigned Track; + + static MetadataPtr Read(const std::string & path); + }; + +} + +#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/mtpz/TrustedApp.cpp android-file-transfer-4.2/mtp/mtpz/TrustedApp.cpp --- android-file-transfer-3.9+dfsg/mtp/mtpz/TrustedApp.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/mtpz/TrustedApp.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,479 @@ +/* + This file is part of Android File Transfer For Linux. + Copyright (C) 2015-2020 Vladimir Menshakov + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include + +#ifdef MTPZ_ENABLED +# include +# include +# include +# include +# include +# include +# include +# include +#endif + +namespace mtp +{ + std::once_flag crypto_init; + + TrustedApp::TrustedApp(const SessionPtr & session, const std::string &mtpzDataPath): + _session(session), _keys(LoadKeys(mtpzDataPath)) + {} + + TrustedApp::~TrustedApp() + { + try + { _session->GenericOperation(OperationCode::DisableTrustedFilesOperations); } + catch(const std::exception& ex) + { error("DisableTrustedFilesOperations failed: ", ex.what()); } + try + { _session->GenericOperation(OperationCode::EndTrustedAppSession); } + catch(const std::exception& ex) + { error("EndTrustedAppSession failed: ", ex.what()); } + } + + TrustedAppPtr TrustedApp::Create(const SessionPtr & session, const std::string &mtpzDataPath) + { + return TrustedAppPtr(Probe(session)? new TrustedApp(session, mtpzDataPath): nullptr); + } + +#ifdef MTPZ_ENABLED + + struct TrustedApp::Keys + { + ByteArray skey; //session key + BIGNUM *exp, *mod, *pkey; + RSA * rsa; + ByteArray certificate; + + Keys(): exp(), mod(), pkey(), rsa(RSA_new()) + { } + + void Update() + { +#if OPENSSL_VERSION_NUMBER < 0x10100000L + BN_free(rsa->n); rsa->n = mod; + BN_free(rsa->e); rsa->e = exp; + BN_free(rsa->d); rsa->d = pkey; +#else + if (RSA_set0_key(rsa, mod, exp, pkey)) + { + debug("created RSA key"); + } + else + throw std::runtime_error("failed to create RSA key"); +#endif + mod = exp = pkey = NULL; + } + + ~Keys() { + if (exp) + BN_free(exp); + if (mod) + BN_free(mod); + if (pkey) + BN_free(pkey); + if (rsa) + RSA_free(rsa); + } + + static ByteArray HKDF(const u8 * message, size_t messageSize, size_t keySize) + { + static constexpr size_t blockSize = SHA_DIGEST_LENGTH; + size_t blocks = (keySize + blockSize - 1) / blockSize; + ByteArray key(blocks * blockSize); + ByteArray ctr(messageSize + 4); + const auto ctrPtr = std::copy(message, message + messageSize, ctr.data()); + + u8 * dst = key.data(); + for(size_t i = 0; i < blocks; ++i, dst += blockSize) + { + ctrPtr[0] = i >> 24; + ctrPtr[1] = i >> 16; + ctrPtr[2] = i >> 8; + ctrPtr[3] = i; + SHA1(ctr.data(), ctr.size(), dst); + } + + return key; + } + + std::tuple GenerateCertificateMessage() + { + static const size_t messageSize = 156 + certificate.size(); + ByteArray challenge(16); + RAND_bytes(challenge.data(), challenge.size()); + + ByteArray message(messageSize); + auto dst = message.data(); + *dst++ = 0x02; + *dst++ = 0x01; + *dst++ = 0x01; + *dst++ = 0x00; + *dst++ = 0x00; + *dst++ = certificate.size() >> 8; + *dst++ = certificate.size(); + dst = std::copy(certificate.begin(), certificate.end(), dst); + + *dst++ = challenge.size() >> 8; + *dst++ = challenge.size(); + dst = std::copy(challenge.begin(), challenge.end(), dst); + + ByteArray hash(SHA_DIGEST_LENGTH); + { + ByteArray salt(SHA_DIGEST_LENGTH + 8); + SHA1(message.data() + 2, dst - message.data() - 2, salt.data() + 8); + SHA1(salt.data(), salt.size(), hash.data()); + } + + ByteArray key = HKDF(hash.data(), hash.size(), 107); + //HexDump("key", key); + + ByteArray signature(RSA_size(rsa)); + signature[106] = 1; + for(size_t i = 0; i < hash.size(); ++i) + signature[i + 107] = hash[i]; + for(size_t i = 0; i < 107; ++i) + signature[i] ^= key[i]; + + signature[0] &= 127; + signature[127] = 188; + //HexDump("signature", signature); + + *dst++ = 1; + *dst++ = 0; + *dst++ = signature.size(); + + if (RSA_private_decrypt(signature.size(), signature.data(), dst, rsa, RSA_NO_PADDING) == -1) + throw std::runtime_error("RSA_private_encrypt failed"); + + return std::make_tuple(challenge, message); + } + + static ByteArray Decrypt(const ByteArray & key, const u8 * src, size_t size) + { + AES_KEY aesKey; + AES_set_decrypt_key(key.data(), key.size() * 8, &aesKey); + + ByteArray iv(16); + ByteArray result(size); + AES_cbc_encrypt(src, result.data(), size, &aesKey, iv.data(), 0); + return result; + } + + void CMAC(const u8 * key, size_t keySize, const u8 * src, size_t size, u8 *dst) + { + CMAC_CTX *ctx = CMAC_CTX_new(); + if (!ctx) + throw std::runtime_error("CMAC_CTX_new failed"); + + if (!CMAC_Init(ctx, key, keySize, EVP_aes_128_cbc(), NULL)) + error("CMAC_Init failed"); + if (!CMAC_Update(ctx, src, size)) + error("CMAC_Update failed"); + size_t len = 0; + if (!CMAC_Final(ctx, dst, &len)) + error("CMAC_Final failed"); + CMAC_CTX_free(ctx); + } + + ByteArray SignResponse(const ByteArray & key) + { + ByteArray text(16); + text[15] = 1; + + ByteArray message(20); + u8 * dst = message.data(); + *dst++ = 2; + *dst++ = 3; + *dst++ = 0; + *dst++ = 16; + CMAC(key.data(), 16, text.data(), text.size(), dst); + return message; + } + + void SignSessionRequest(u32 cmac[4], const ByteArray & key) + { + u8 signature[16]; + CMAC(key.data(), 16, key.data() + 16, 4, signature); + //HexDump("signature", ByteArray(signature, signature + 16)); + const u8 *src = signature; + u32 *dst = cmac; + for(size_t i = 0; i < 4; ++i) + { + u32 value = 0; + for(size_t j = 0; j < 4; ++j) + value |= static_cast(*src++) << ((3 - j) << 3); + + *dst++ = value; + } + debug("secure session enabler: ", hex(cmac[0]), "-", + hex(cmac[1]), "-", + hex(cmac[2]), "-", + hex(cmac[3])); + } + + ByteArray VerifyResponse(const ByteArray & message, const ByteArray & originalChallenge) + { + +#define CHECK_MORE(ARRAY, SIZE) if (src - (ARRAY).data() + static_cast(SIZE) > (ARRAY).size()) \ + throw std::runtime_error("input buffer overrun"); + + const u8 * src = message.data(); + + CHECK_MORE(message, 4); + if (*src++ != 2) + throw std::runtime_error("invalid tag"); + if (*src++ != 2) + throw std::runtime_error("invalid tag"); + + size_t signatureSize = *src++ << 8; + signatureSize += *src++; + + if (signatureSize < 0x80 || signatureSize != static_cast(RSA_size(rsa))) + throw std::runtime_error("invalid signature size"); + + CHECK_MORE(message, signatureSize); + ByteArray signature(signatureSize); + if (RSA_private_decrypt(signatureSize, src, signature.data(), rsa, RSA_NO_PADDING) == -1) + throw std::runtime_error("RSA_private_decrypt failed"); + src += signatureSize; + + { + ByteArray hash = HKDF(signature.data() + 21, 107, 20); + for(size_t i = 0; i < 20; ++i) + signature[1 + i] ^= hash[i]; + ByteArray key = HKDF(signature.data() + 1, 20, 107); + for(size_t i = 0; i < 107; ++i) + signature[21 + i] ^= key[i]; + } + //HexDump("signature out", signature); + ByteArray key = ByteArray(signature.begin() + 0x70, signature.end()); + //HexDump("key", key); + + CHECK_MORE(message, 4); + if (*src++ != 0) + throw std::runtime_error("invalid record"); + if (*src++ != 0) + throw std::runtime_error("invalid record"); + + size_t payloadSize = *src++ << 8; + payloadSize += *src++; + CHECK_MORE(message, payloadSize); + //debug("payload size ", payloadSize); + ByteArray payload = Decrypt(key, src, payloadSize); + //HexDump("payload", payload); + + src = payload.data(); + if (*src++ != 1) + throw std::runtime_error("decryption failed"); + + size_t certificateSize = *src++ << 24; + certificateSize |= *src++ << 16; + certificateSize |= *src++ << 8; + certificateSize |= *src++; + //debug("certificate size ", certificateSize); + CHECK_MORE(payload, certificateSize); + src += certificateSize; + + CHECK_MORE(payload, 2); + size_t challengeSize = *src++ << 8; + challengeSize |= *src++; + //debug("challenge size ", challengeSize); + if (challengeSize != originalChallenge.size()) + throw std::runtime_error("invalid challenge size"); + + CHECK_MORE(payload, challengeSize); + if (memcmp(src, originalChallenge.data(), challengeSize) != 0) + throw std::runtime_error("challenge does not match"); + src += challengeSize; + //debug("challenge matches..."); + + CHECK_MORE(payload, 2); + challengeSize = *src++ << 8; + challengeSize |= *src++; + + //debug("device challenge size ", challengeSize); + CHECK_MORE(payload, challengeSize); + src += challengeSize; + + CHECK_MORE(payload, 3); + if (*src++ != 1) + throw std::runtime_error("invalid signature"); + + signatureSize = *src++ << 8; + signatureSize |= *src++; + //debug("signature size: ", signatureSize); + CHECK_MORE(payload, signatureSize); + src += signatureSize; + + CHECK_MORE(payload, 3); + if (*src++ != 1) + throw std::runtime_error("invalid cmac record"); + challengeSize = *src++ << 8; + challengeSize |= *src++; + //debug("cmac size: ", challengeSize); + CHECK_MORE(payload, challengeSize); + + return ByteArray(src, src + challengeSize); + } + + static u8 FromHex(char ch) + { + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + throw std::runtime_error(std::string("invalid hex character ") + ch); + } + + static ByteArray FromHex(const char *buf, size_t bufsize) + { + ByteArray data; + data.reserve((bufsize + 1) / 2); + while(buf[0] && buf[1]) { + u8 h = FromHex(*buf++); + u8 l = FromHex(*buf++); + data.push_back((h << 4) | l); + } + if (buf[0] != 0 && !isspace(buf[0])) + throw std::runtime_error("tailing character"); + + return data; + } + }; + + bool TrustedApp::Probe(const SessionPtr & session) + { + auto & di = session->GetDeviceInfo(); + bool supported = + di.Supports(OperationCode::SendWMDRMPDAppRequest) && + di.Supports(OperationCode::GetWMDRMPDAppResponse) && + di.Supports(OperationCode::EnableTrustedFilesOperations) && + di.Supports(OperationCode::DisableTrustedFilesOperations) && + di.Supports(OperationCode::EndTrustedAppSession); + + debug("MTPZ supported: " , supported? "yes": "no"); + return supported; + } + + void TrustedApp::Authenticate() + { + if (!_keys) + return; + + auto & di = _session->GetDeviceInfo(); + if (di.Supports(DeviceProperty::SessionInitiatorVersionInfo)) + _session->SetDeviceProperty(DeviceProperty::SessionInitiatorVersionInfo, + "Android File Transfer for Linux - MTPZClassDriver"); + + _session->GenericOperation(OperationCode::EndTrustedAppSession); + ByteArray challenge, message; + std::tie(challenge, message) = _keys->GenerateCertificateMessage(); + //HexDump("certificate payload", message); + _session->GenericOperation(OperationCode::SendWMDRMPDAppRequest, message); + + ByteArray response = _session->GenericOperation(OperationCode::GetWMDRMPDAppResponse); + //HexDump("device response", response); + ByteArray cmacKey = _keys->VerifyResponse(response, challenge); + debug("validated MTPZ device response..."); + //HexDump("cmac key", cmacKey); + ByteArray signature = _keys->SignResponse(cmacKey); + //HexDump("signature", signature); + _session->GenericOperation(OperationCode::SendWMDRMPDAppRequest, signature); + debug("authentication finished, enabling secure session..."); + u32 cmac[4]; + _keys->SignSessionRequest(cmac, cmacKey); + _session->EnableSecureFileOperations(cmac); + debug("handshake finished"); + } + + TrustedApp::KeysPtr TrustedApp::LoadKeys(const std::string & path) + { + BIO * bio = BIO_new_file(path.c_str(), "rt"); + if (bio == NULL) { + error("could not open ", path); + return nullptr; + } + + try + { + auto keys = std::make_shared(); + + char buf[4096]; + //public exp + BIO_gets(bio, buf, sizeof(buf)); + if (BN_hex2bn(&keys->exp, buf) <= 0) + throw std::runtime_error("can't read public exponent"); + + //session key + auto r = BIO_gets(bio, buf, sizeof(buf)); + if (r <= 0) + throw std::runtime_error("BIO_gets: short read"); + keys->skey = Keys::FromHex(buf, r); + //HexDump("session key", keys->skey); + + //public mod + BIO_gets(bio, buf, sizeof(buf)); + if (BN_hex2bn(&keys->mod, buf) <= 0) + throw std::runtime_error("can't read public modulus"); + + //private exponent + BIO_gets(bio, buf, sizeof(buf)); + if (BN_hex2bn(&keys->pkey, buf) <= 0) + throw std::runtime_error("can't read private key"); + + r = BIO_gets(bio, buf, sizeof(buf)); + if (r <= 0) + throw std::runtime_error("BIO_gets: short read"); + keys->certificate = Keys::FromHex(buf, r); + //HexDump("certificate", keys->certificate); + keys->Update(); + BIO_free(bio); + return keys; + } + catch(const std::exception & ex) + { + BIO_free(bio); + error("loading keys failed: ", ex.what()); + } + return nullptr; + } + +#else + + bool TrustedApp::Probe(const SessionPtr & session) + { return false; } + + TrustedApp::KeysPtr TrustedApp::LoadKeys(const std::string & path) + { return nullptr; } + + void TrustedApp::Authenticate() + { } + +#endif +} diff -Nru android-file-transfer-3.9+dfsg/mtp/mtpz/TrustedApp.h android-file-transfer-4.2/mtp/mtpz/TrustedApp.h --- android-file-transfer-3.9+dfsg/mtp/mtpz/TrustedApp.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/mtpz/TrustedApp.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,62 @@ +/* + This file is part of Android File Transfer For Linux. + Copyright (C) 2015-2020 Vladimir Menshakov + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef AFTL_MTP_MTPZ_TRUSTEDAPP_H +#define AFTL_MTP_MTPZ_TRUSTEDAPP_H + +#include +#include + +namespace mtp +{ + class Session; + DECLARE_PTR(Session); + + class TrustedApp; + DECLARE_PTR(TrustedApp); + + class TrustedApp + { + private: + struct Keys; + DECLARE_PTR(Keys); + + private: + SessionPtr _session; + KeysPtr _keys; + + public: + static bool Probe(const SessionPtr & session); + + ~TrustedApp(); + + static TrustedAppPtr Create(const SessionPtr & session, const std::string &mtpzDataPath); + void Authenticate(); + + bool KeysLoaded() const + { return !!_keys; } + + private: + TrustedApp(const SessionPtr & session, const std::string & mtpzDataPath); + static KeysPtr LoadKeys(const std::string & path); + + }; +} + +#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ByteArrayObjectStream.h android-file-transfer-4.2/mtp/ptp/ByteArrayObjectStream.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ByteArrayObjectStream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ByteArrayObjectStream.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef BYTEARRAYOBJECTSTREAM_H -#define BYTEARRAYOBJECTSTREAM_H +#ifndef AFTL_MTP_PTP_BYTEARRAYOBJECTSTREAM_H +#define AFTL_MTP_PTP_BYTEARRAYOBJECTSTREAM_H #include #include @@ -62,7 +62,10 @@ const ByteArray &GetData() const { return _data; } - virtual size_t Write(const u8 *data, size_t size) + ByteArray &GetData() + { return _data; } + + size_t Write(const u8 *data, size_t size) override { CheckCancelled(); std::copy(data, data + size, std::back_inserter(_data)); @@ -82,7 +85,7 @@ const ByteArray &GetData() const { return _data; } - virtual size_t Write(const u8 *data, size_t size) + size_t Write(const u8 *data, size_t size) override { CheckCancelled(); size_t n = std::min(size, _data.size() - _offset); diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Container.h android-file-transfer-4.2/mtp/ptp/Container.h --- android-file-transfer-3.9+dfsg/mtp/ptp/Container.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Container.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef CONTAINER_H -#define CONTAINER_H +#ifndef AFTL_MTP_PTP_CONTAINER_H +#define AFTL_MTP_PTP_CONTAINER_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/DataTypeCode.cpp android-file-transfer-4.2/mtp/ptp/DataTypeCode.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/DataTypeCode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/DataTypeCode.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,31 @@ +#include +#include + +namespace mtp +{ + std::string ToString(DataTypeCode type) + { + switch(type) + { +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(DataTypeCode, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(DataTypeCode, type, 4); + } + } + bool IsArray(DataTypeCode type) + { + switch(type) + { +# define CASE(BITS) case DataTypeCode::ArrayInt##BITS : case DataTypeCode::ArrayUint##BITS : + CASE(8) + CASE(16) + CASE(32) + CASE(64) + CASE(128) + return true; + default: + return false; + } + } + +} diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/DataTypeCode.h android-file-transfer-4.2/mtp/ptp/DataTypeCode.h --- android-file-transfer-3.9+dfsg/mtp/ptp/DataTypeCode.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/DataTypeCode.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,22 @@ +#ifndef AFTL_MTP_PTP_DATATYPECODE_H +#define AFTL_MTP_PTP_DATATYPECODE_H + +#include +#include + +namespace mtp +{ + enum struct DataTypeCode : u16 + { +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include +#undef ENUM_VALUE + }; + DECLARE_ENUM(DataTypeCode, u16); + + bool IsArray(DataTypeCode type); + std::string ToString(DataTypeCode type); + +} + +#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/DataTypeCode.values.h android-file-transfer-4.2/mtp/ptp/DataTypeCode.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/DataTypeCode.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/DataTypeCode.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,22 @@ +ENUM_VALUE(Undefined, 0x0000) +ENUM_VALUE(Int8, 0x0001) +ENUM_VALUE(Uint8, 0x0002) +ENUM_VALUE(Int16, 0x0003) +ENUM_VALUE(Uint16, 0x0004) +ENUM_VALUE(Int32, 0x0005) +ENUM_VALUE(Uint32, 0x0006) +ENUM_VALUE(Int64, 0x0007) +ENUM_VALUE(Uint64, 0x0008) +ENUM_VALUE(Int128, 0x0009) +ENUM_VALUE(Uint128, 0x000a) +ENUM_VALUE(ArrayInt8, 0x4001) +ENUM_VALUE(ArrayUint8, 0x4002) +ENUM_VALUE(ArrayInt16, 0x4003) +ENUM_VALUE(ArrayUint16, 0x4004) +ENUM_VALUE(ArrayInt32, 0x4005) +ENUM_VALUE(ArrayUint32, 0x4006) +ENUM_VALUE(ArrayInt64, 0x4007) +ENUM_VALUE(ArrayUint64, 0x4008) +ENUM_VALUE(ArrayInt128, 0x4009) +ENUM_VALUE(ArrayUint128, 0x400a) +ENUM_VALUE(String, 0xffff) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Device.cpp android-file-transfer-4.2/mtp/ptp/Device.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/Device.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Device.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -49,7 +49,7 @@ return std::make_shared(_packeter.GetPipe(), sessionId); } - int Device::GetInterfaceStringIndex(usb::DeviceDescriptorPtr desc, u8 number) + u8 Device::GetInterfaceStringIndex(usb::DeviceDescriptorPtr desc, u8 number) { static const u16 DT_INTERFACE = 4; @@ -75,22 +75,40 @@ { debug("probing device ", hex(desc->GetVendorId(), 4), ":", hex(desc->GetProductId(), 4)); usb::DevicePtr device = desc->TryOpen(ctx); - if (!device) + + if (resetDevice && device) { + device->Reset(); + device.reset(); + device = desc->TryOpen(ctx); + } + + if (!device) { + debug("descriptor->TryOpen() failed"); return nullptr; + } int confs = desc->GetConfigurationsCount(); - //debug("configurations: ", confs); + debug("configurations: ", confs); for(int i = 0; i < confs; ++i) { usb::ConfigurationPtr conf = desc->GetConfiguration(i); int interfaces = conf->GetInterfaceCount(); - //debug("interfaces: ", interfaces); + if (interfaces == 0) { + debug("device not configured (no interfaces), setting configuration now..."); + int index = conf->GetIndex(); + conf.reset(); + device->SetConfiguration(index); + debug("device configured, retrieving configuration descriptor again..."); + conf = desc->GetConfiguration(i); + interfaces = conf->GetInterfaceCount(); + } + debug("interfaces: ", interfaces); for(int j = 0; j < interfaces; ++j) { usb::InterfacePtr iface = conf->GetInterface(device, conf, j, 0); usb::InterfaceTokenPtr token = claimInterface? device->ClaimInterface(iface): nullptr; - debug("Device usb interface: ", i, ':', j, ", index: ", iface->GetIndex(), ", enpoints: ", iface->GetEndpointsCount()); + debug("Device usb interface: ", i, ':', j, ", index: ", iface->GetIndex(), ", endpoints: ", iface->GetEndpointsCount()); #ifdef USB_BACKEND_LIBUSB std::string name = iface->GetName(); @@ -100,26 +118,50 @@ if (data.size() < 4 || data[1] != (u8)usb::DescriptorType::String) continue; - int interfaceStringIndex = GetInterfaceStringIndex(desc, j); u16 langId = data[2] | ((u16)data[3] << 8); - data = usb::DeviceRequest(device).GetDescriptor(usb::DescriptorType::String, interfaceStringIndex, langId); - HexDump("interface name", data); - if (data.size() < 4 || data[1] != (u8)usb::DescriptorType::String) - continue; - u8 len = data[0]; - InputStream stream(data, 2); - std::string name = stream.ReadString((len - 2) / 2); -#endif - if (name == "MTP") + std::string name; + + try { - //device->SetConfiguration(configuration->GetIndex()); - if (resetDevice) - device->Reset(); + data = usb::DeviceRequest(device).GetDescriptor(usb::DescriptorType::String, static_cast(usb::DeviceRequest::Request::GetOSStringDescriptor), langId); + HexDump("OSStringDescriptor", data); + if (data.size() < 0x12 || data.at(2) != 'M' || data.at(4) != 'S' || data.at(6) != 'F' || data.at(8) != 'T') + throw std::runtime_error("invalid OSString descriptor"); + u8 command = data.at(0x10); + debug("vendor code: 0x", hex(command, 2)); + + //getting extended OS compat descriptor + data.resize(255); + device->ReadControl(static_cast(usb::RequestType::DeviceToHost | usb::RequestType::Vendor | usb::RequestType::Device), + command, 0, 4, data, usb::BaseRequest::DefaultTimeout); + HexDump("extended compat id os feature desctriptor", data); + if (data.at(0x12) == 'M' && data.at(0x13) == 'T' && data.at(0x14) == 'P') + name = "MTP"; + } + catch (const std::exception & ex) + { debug("winusb handshake failed: ", ex.what()); } + if (name.find("MTP") == name.npos) + { + auto interfaceStringIndex = GetInterfaceStringIndex(desc, j); + data = usb::DeviceRequest(device).GetDescriptor(usb::DescriptorType::String, interfaceStringIndex, langId); + HexDump("interface name", data); + if (data.size() < 4 || data[1] != (u8)usb::DescriptorType::String) + continue; + + u8 len = data[0]; + InputStream stream(data, 2); + name = stream.ReadString((len - 2) / 2); + } +#endif + if (name.find("MTP") != name.npos) + { usb::BulkPipePtr pipe = usb::BulkPipe::Create(device, conf, iface, token); return std::make_shared(pipe); - } + } else + debug("skipping interface with name ", name); + if (iface->GetClass() == 6 && iface->GetSubclass() == 1) { usb::BulkPipePtr pipe = usb::BulkPipe::Create(device, conf, iface, token); @@ -130,15 +172,31 @@ return nullptr; } - DevicePtr Device::FindFirst(bool claimInterface, bool resetDevice) + DevicePtr Device::FindFirst(const std::string & filter, bool claimInterface, bool resetDevice) { usb::ContextPtr ctx(new usb::Context); + return FindFirst(ctx, filter, claimInterface, resetDevice); + } + + DevicePtr Device::FindFirst(usb::ContextPtr ctx, const std::string & filter, bool claimInterface, bool resetDevice) + { + int vendor, product; + if (sscanf(filter.c_str(), "%x:%x", &vendor, &product) != 2) + { + vendor = product = -1; + } for (usb::DeviceDescriptorPtr desc : ctx->GetDevices()) try { + if (vendor >= 0 && product >= 0) + { + if (desc->GetVendorId() != vendor || desc->GetProductId() != product) + continue; + } + auto device = Open(ctx, desc, claimInterface, resetDevice); - if (device) + if (device && device->Matches(filter)) return device; } catch(const std::exception &ex) @@ -147,4 +205,16 @@ return nullptr; } + msg::DeviceInfo Device::GetInfo() + { return Session::GetDeviceInfo(_packeter); } + + bool Device::Matches(const std::string & filter) + { + if (filter.empty()) + return true; + + auto di = GetInfo(); + return di.Matches(filter); + } + } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Device.h android-file-transfer-4.2/mtp/ptp/Device.h --- android-file-transfer-3.9+dfsg/mtp/ptp/Device.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Device.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_PTP_DEVICE -#define AFT_PTP_DEVICE +#ifndef AFTL_MTP_PTP_DEVICE_H +#define AFTL_MTP_PTP_DEVICE_H #include #include @@ -32,15 +32,21 @@ PipePacketer _packeter; private: - static int GetInterfaceStringIndex(usb::DeviceDescriptorPtr desc, u8 number); + static u8 GetInterfaceStringIndex(usb::DeviceDescriptorPtr desc, u8 number); public: Device(usb::BulkPipePtr pipe); + msg::DeviceInfo GetInfo(); + + ///This could be very expensive, it calls GetDeviceInfo + bool Matches(const std::string & filter); + SessionPtr OpenSession(u32 sessionId, int timeout = Session::DefaultTimeout); static DevicePtr Open(usb::ContextPtr context, usb::DeviceDescriptorPtr desc, bool claimInterface = true, bool resetDevice = false); //fixme: add flags here - static DevicePtr FindFirst(bool claimInterface = true, bool resetDevice = false); + static DevicePtr FindFirst(usb::ContextPtr context, const std::string & filter = std::string(), bool claimInterface = true, bool resetDevice = false); + static DevicePtr FindFirst(const std::string & filter = std::string(), bool claimInterface = true, bool resetDevice = false); }; } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/DeviceProperty.cpp android-file-transfer-4.2/mtp/ptp/DeviceProperty.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/DeviceProperty.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/DeviceProperty.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,27 @@ +#include +#include + +namespace mtp +{ + std::string ToString(DeviceProperty property) + { + switch(property) + { +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(DeviceProperty, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(DeviceProperty, property, 4); +# undef ENUM_VALUE + } + } + + std::string ToString(PerceivedDeviceType property) + { + switch(property) + { +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(PerceivedDeviceType, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(PerceivedDeviceType, property, 8); +# undef ENUM_VALUE + } + } +} diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/DeviceProperty.h android-file-transfer-4.2/mtp/ptp/DeviceProperty.h --- android-file-transfer-3.9+dfsg/mtp/ptp/DeviceProperty.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/DeviceProperty.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,57 +17,31 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef DEVICEPROPERTY_H -#define DEVICEPROPERTY_H +#ifndef AFTL_MTP_PTP_DEVICEPROPERTY_H +#define AFTL_MTP_PTP_DEVICEPROPERTY_H + +#include +#include namespace mtp { + enum struct PerceivedDeviceType : u32 + { +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include "PerceivedDeviceType.values.h" +#undef ENUM_VALUE + }; + std::string ToString(PerceivedDeviceType property); - enum struct DeviceProperty + enum struct DeviceProperty : u16 { - Undefined = 0x5000, - BatteryLevel = 0x5001, - FunctionalMode = 0x5002, - ImageSize = 0x5003, - CompressionSetting = 0x5004, - WhiteBalance = 0x5005, - RgbGain = 0x5006, - FNumber = 0x5007, - FocalLength = 0x5008, - FocusDistance = 0x5009, - FocusMode = 0x500a, - ExposureMeteringMode = 0x500b, - FlashMode = 0x500c, - ExposureTime = 0x500d, - ExposureProgramMode = 0x500e, - ExposureIndex = 0x500f, - ExposureBiasCompensation = 0x5010, - Datetime = 0x5011, - CaptureDelay = 0x5012, - StillCaptureMode = 0x5013, - Contrast = 0x5014, - Sharpness = 0x5015, - DigitalZoom = 0x5016, - EffectMode = 0x5017, - BurstNumber = 0x5018, - BurstInterval = 0x5019, - TimelapseNumber = 0x501a, - TimelapseInterval = 0x501b, - FocusMeteringMode = 0x501c, - UploadUrl = 0x501d, - Artist = 0x501e, - CopyrightInfo = 0x501f, - SynchronizationPartner = 0xd401, - DeviceFriendlyName = 0xd402, - Volume = 0xd403, - SupportedFormatsOrdered = 0xd404, - DeviceIcon = 0xd405, - PlaybackRate = 0xd410, - PlaybackObject = 0xd411, - PlaybackContainerIndex = 0xd412, - SessionInitiatorVersionInfo = 0xd406, - PerceivedDeviceType = 0xd407 +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include "DeviceProperty.values.h" +#undef ENUM_VALUE }; + DECLARE_ENUM(DeviceProperty, u16); + + std::string ToString(DeviceProperty property); } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/DeviceProperty.values.h android-file-transfer-4.2/mtp/ptp/DeviceProperty.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/DeviceProperty.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/DeviceProperty.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,47 @@ +ENUM_VALUE(Undefined, 0x5000) +ENUM_VALUE(BatteryLevel, 0x5001) +ENUM_VALUE(FunctionalMode, 0x5002) //0 - normal, 1 - sleeping +ENUM_VALUE(ImageSize, 0x5003) +ENUM_VALUE(CompressionSetting, 0x5004) +ENUM_VALUE(WhiteBalance, 0x5005) +ENUM_VALUE(RgbGain, 0x5006) +ENUM_VALUE(FNumber, 0x5007) +ENUM_VALUE(FocalLength, 0x5008) +ENUM_VALUE(FocusDistance, 0x5009) +ENUM_VALUE(FocusMode, 0x500a) +ENUM_VALUE(ExposureMeteringMode, 0x500b) +ENUM_VALUE(FlashMode, 0x500c) +ENUM_VALUE(ExposureTime, 0x500d) +ENUM_VALUE(ExposureProgramMode, 0x500e) +ENUM_VALUE(ExposureIndex, 0x500f) +ENUM_VALUE(ExposureBiasCompensation, 0x5010) +ENUM_VALUE(Datetime, 0x5011) +ENUM_VALUE(CaptureDelay, 0x5012) +ENUM_VALUE(StillCaptureMode, 0x5013) +ENUM_VALUE(Contrast, 0x5014) +ENUM_VALUE(Sharpness, 0x5015) +ENUM_VALUE(DigitalZoom, 0x5016) +ENUM_VALUE(EffectMode, 0x5017) +ENUM_VALUE(BurstNumber, 0x5018) +ENUM_VALUE(BurstInterval, 0x5019) +ENUM_VALUE(TimelapseNumber, 0x501a) +ENUM_VALUE(TimelapseInterval, 0x501b) +ENUM_VALUE(FocusMeteringMode, 0x501c) +ENUM_VALUE(UploadUrl, 0x501d) +ENUM_VALUE(Artist, 0x501e) +ENUM_VALUE(CopyrightInfo, 0x501f) + +ENUM_VALUE(DeviceEUI64, 0xd210) + +ENUM_VALUE(SynchronizationPartner, 0xd401) +ENUM_VALUE(DeviceFriendlyName, 0xd402) +ENUM_VALUE(Volume, 0xd403) +ENUM_VALUE(SupportedFormatsOrdered, 0xd404) +ENUM_VALUE(DeviceIcon, 0xd405) +ENUM_VALUE(PlaybackRate, 0xd410) +ENUM_VALUE(PlaybackObject, 0xd411) +ENUM_VALUE(PlaybackContainerIndex, 0xd412) +ENUM_VALUE(PlaybackPosition, 0xd413) + +ENUM_VALUE(SessionInitiatorVersionInfo, 0xd406) +ENUM_VALUE(PerceivedDeviceType, 0xd407) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/EventCode.h android-file-transfer-4.2/mtp/ptp/EventCode.h --- android-file-transfer-3.9+dfsg/mtp/ptp/EventCode.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/EventCode.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,32 @@ +#ifndef AFTL_MTP_PTP_EVENTCODE_H +#define AFTL_MTP_PTP_EVENTCODE_H + +#include + +namespace mtp +{ + enum struct EventCode : u16 + { + TransactionCancelled = 0x4001, + ObjectAdded = 0x4002, + ObjectRemoved = 0x4003, + StoreAdded = 0x4004, + StoreRemoved = 0x4005, + DevicePropChanged = 0x4006, + ObjectInfoChanged = 0x4007, + DeviceInfoChanged = 0x4008, + RequestObjectTransfer = 0x4009, + StoreFull = 0x400a, + DeviceReset = 0x400b, + StorageInfoChanged = 0x400c, + CaptureComplete = 0x400d, + UnreportedStatus = 0x400e, + + ObjectPropChanged = 0xc801, + ObjectPropDescChanged = 0xc802, + ObjectReferenceChanged = 0xc803 + }; + DECLARE_ENUM(EventCode, u16); +} + +#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/InputStream.h android-file-transfer-4.2/mtp/ptp/InputStream.h --- android-file-transfer-3.9+dfsg/mtp/ptp/InputStream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/InputStream.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef STREAM_H -#define STREAM_H +#ifndef AFTL_MTP_PTP_INPUTSTREAM_H +#define AFTL_MTP_PTP_INPUTSTREAM_H #include #include @@ -69,6 +69,12 @@ return l | ((u64)h << 32); } + u64 Read128() { + u64 l = Read64(); + Skip(8); //fixme: truncated 128 bit value + return l; + } + std::string ReadString() { return ReadString(Read8()); } @@ -128,6 +134,18 @@ inline InputStream & operator >> (InputStream &stream, u64 &value) { value = stream.Read64(); return stream; } + inline InputStream & operator >> (InputStream &stream, s8 &value) + { value = stream.Read8(); return stream; } + + inline InputStream & operator >> (InputStream &stream, s16 &value) + { value = stream.Read16(); return stream; } + + inline InputStream & operator >> (InputStream &stream, s32 &value) + { value = stream.Read32(); return stream; } + + inline InputStream & operator >> (InputStream &stream, s64 &value) + { value = stream.Read64(); return stream; } + inline InputStream & operator >> (InputStream &stream, std::string &value) { value = stream.ReadString(); return stream; } @@ -156,6 +174,15 @@ s >> value; return value; } + + template + ResponseType ParseResponse(const ByteArray & data) + { + ResponseType response; + InputStream is(data); + response.Read(is); + return response; + } } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/IObjectStream.h android-file-transfer-4.2/mtp/ptp/IObjectStream.h --- android-file-transfer-3.9+dfsg/mtp/ptp/IObjectStream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/IObjectStream.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef IOBJECTSTREAM_H -#define IOBJECTSTREAM_H +#ifndef AFTL_MTP_PTP_IOBJECTSTREAM_H +#define AFTL_MTP_PTP_IOBJECTSTREAM_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/JoinedObjectStream.h android-file-transfer-4.2/mtp/ptp/JoinedObjectStream.h --- android-file-transfer-3.9+dfsg/mtp/ptp/JoinedObjectStream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/JoinedObjectStream.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef JOINEDOBJECTSTREAM_H -#define JOINEDOBJECTSTREAM_H +#ifndef AFTL_MTP_PTP_JOINEDOBJECTSTREAM_H +#define AFTL_MTP_PTP_JOINEDOBJECTSTREAM_H #include @@ -90,7 +90,7 @@ public: JoinedObjectOutputStreamBase(): _stream1Exhausted(false) { } - virtual size_t Write(const u8 *data, size_t size) + size_t Write(const u8 *data, size_t size) override { CheckCancelled(); size_t r; diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/MemoryObjectStream.h android-file-transfer-4.2/mtp/ptp/MemoryObjectStream.h --- android-file-transfer-3.9+dfsg/mtp/ptp/MemoryObjectStream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/MemoryObjectStream.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -#ifndef AFT_MTP_PTP_MEMORYOBJECTSTREAM_H -#define AFT_MTP_PTP_MEMORYOBJECTSTREAM_H - -#include -#include - -namespace mtp -{ - class MemoryObjectOutputStream final: - public IObjectOutputStream, - public CancellableStream - { - ByteArrayPtr _data; - - public: - MemoryObjectOutputStream(): _data(new ByteArray()) { } - - size_t Write(const u8 *data, size_t size) override - { - auto & storage = *_data; - auto offset = storage.size(); - storage.resize(offset + size); - std::copy(data, data + size, storage.data() + offset); - return size; - } - - const ByteArrayPtr & GetData() const - { return _data; } - }; -} - -#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Messages.cpp android-file-transfer-4.2/mtp/ptp/Messages.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/Messages.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Messages.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,64 @@ +#include +#include +#include +#include + +namespace mtp { namespace msg +{ + bool DeviceInfo::Supports(OperationCode opcode) const + { + auto i = std::find(OperationsSupported.begin(), OperationsSupported.end(), opcode); + return i != OperationsSupported.end(); + } + + bool DeviceInfo::Supports(DeviceProperty property) const + { + auto i = std::find(DevicePropertiesSupported.begin(), DevicePropertiesSupported.end(), property); + return i != DevicePropertiesSupported.end(); + } + + bool DeviceInfo::Supports(EventCode event) const + { + auto i = std::find(EventsSupported.begin(), EventsSupported.end(), event); + return i != EventsSupported.end(); + } + + bool DeviceInfo::Supports(ObjectFormat format) const + { + auto i = std::find(ImageFormats.begin(), ImageFormats.end(), format); + return i != ImageFormats.end(); + } + + bool DeviceInfo::Matches(const std::string & haystack, const std::string & needle) + { return strcasestr(haystack.c_str(), needle.c_str()); } + + bool DeviceInfo::Matches(const std::string & filter) const + { + if (filter.empty()) + return true; + auto fsname = GetFilesystemFriendlyName(); + return Matches(fsname, filter); + } + + + namespace + { + std::string Strip(std::string str) + { + str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end()); + return str; + } + } + + std::string DeviceInfo::GetFilesystemFriendlyName() const + { + std::stringstream ss; + ss << Strip(Manufacturer); + ss << '-'; + ss << Strip(Model); + ss << '-'; + ss << Strip(SerialNumber); + return ss.str(); + } + +}} diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Messages.h android-file-transfer-4.2/mtp/ptp/Messages.h --- android-file-transfer-3.9+dfsg/mtp/ptp/Messages.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Messages.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,16 +17,19 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MESSAGES_H -#define MESSAGES_H +#ifndef AFTL_MTP_PTP_MESSAGES_H +#define AFTL_MTP_PTP_MESSAGES_H +#include #include #include #include #include #include #include -#include +#include +#include +#include namespace mtp { namespace msg { @@ -38,10 +41,10 @@ std::string VendorExtensionDesc; u16 FunctionalMode; std::vector OperationsSupported; - std::vector EventsSupported; - std::vector DevicePropertiesSupported; - std::vector CaptureFormats; - std::vector ImageFormats; + std::vector EventsSupported; + std::vector DevicePropertiesSupported; + std::vector CaptureFormats; + std::vector ImageFormats; std::string Manufacturer; std::string Model; std::string DeviceVersion; @@ -65,11 +68,19 @@ stream >> SerialNumber; } - bool Supports(OperationCode opcode) const - { - auto i = std::find(OperationsSupported.begin(), OperationsSupported.end(), opcode); - return i != OperationsSupported.end(); - } + bool Supports(OperationCode opcode) const; + + bool Supports(DeviceProperty property) const; + + bool Supports(EventCode event) const; + + bool Supports(ObjectFormat format) const; + + std::string GetFilesystemFriendlyName() const; + + static bool Matches(const std::string & haystack, const std::string & needle); + + bool Matches(const std::string & filter) const; }; struct ObjectHandles //! MTP ObjectHandles message @@ -80,6 +91,11 @@ { stream >> ObjectHandles; } + + void Write(OutputStream & stream) const + { + stream.WriteArray(ObjectHandles); + } }; struct StorageIDs //! MTP StorageIDs message @@ -124,7 +140,7 @@ mtp::StorageId StorageId; mtp::ObjectFormat ObjectFormat; u16 ProtectionStatus; - u32 ObjectCompressedSize; + u64 ObjectCompressedSize; u16 ThumbFormat; u32 ThumbCompressedSize; u32 ThumbPixWidth; @@ -148,17 +164,14 @@ SequenceNumber() { } - void SetSize(u64 size) - { - ObjectCompressedSize = (size > MaxObjectSize)? MaxObjectSize: size; - } - void Read(InputStream &stream) { stream >> StorageId; stream >> ObjectFormat; stream >> ProtectionStatus; - stream >> ObjectCompressedSize; + u32 objectCompressedSize; + stream >> objectCompressedSize; + ObjectCompressedSize = objectCompressedSize; stream >> ThumbFormat; stream >> ThumbCompressedSize; stream >> ThumbPixWidth; @@ -181,7 +194,8 @@ stream << StorageId; stream << ObjectFormat; stream << ProtectionStatus; - stream << ObjectCompressedSize; + u32 objectCompressedSize = (ObjectCompressedSize > MaxObjectSize)? MaxObjectSize: ObjectCompressedSize; + stream << objectCompressedSize; stream << ThumbFormat; stream << ThumbCompressedSize; stream << ThumbPixWidth; @@ -211,6 +225,61 @@ } }; + struct SendObjectPropListResponse + { + mtp::StorageId StorageId; + mtp::ObjectId ParentObjectId; + mtp::ObjectId ObjectId; + u32 FailedPropertyIndex; + + SendObjectPropListResponse(): + StorageId(), ParentObjectId(), ObjectId(), + FailedPropertyIndex() + { } + + void Read(InputStream &stream) + { + stream >> StorageId; + stream >> ParentObjectId; + stream >> ObjectId; + stream >> FailedPropertyIndex; + } + + }; + + struct DevicePropertyDesc + { + DeviceProperty Property; + DataTypeCode Type; + bool Writeable; + + DevicePropertyDesc(): Property(), Type(DataTypeCode::Undefined), Writeable() + { } + + void Read(InputStream &stream) + { + stream >> Property; + stream >> Type; + u8 writeable; + stream >> writeable; + Writeable = writeable; + } + }; + + struct NewObjectInfo + { + mtp::StorageId StorageId; + mtp::ObjectId ParentObjectId; + mtp::ObjectId ObjectId; + + void Read(InputStream & is) + { + is >> StorageId; + is >> ParentObjectId; + is >> ObjectId; + } + }; + }} diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectFormat.cpp android-file-transfer-4.2/mtp/ptp/ObjectFormat.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectFormat.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectFormat.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -55,7 +56,7 @@ MAP_TYPE("inode/directory", ObjectFormat::Association); MAP_TYPE("audio/mpeg", ObjectFormat::Mp3); MAP_TYPE("text/plain", ObjectFormat::Text); - MAP_TYPE("image/jpeg", ObjectFormat::Jfif); + MAP_TYPE("image/jpeg", ObjectFormat::ExifJpeg); MAP_TYPE("image/gif", ObjectFormat::Gif); MAP_TYPE("image/x-ms-bmp", ObjectFormat::Bmp); MAP_TYPE("image/png", ObjectFormat::Png); @@ -64,9 +65,8 @@ MAP_TYPE("audio/x-flac", ObjectFormat::Flac); MAP_TYPE("audio/x-m4a", ObjectFormat::Aac); MAP_TYPE("audio/audio/x-wav", ObjectFormat::Aiff); - MAP_TYPE("video/x-ms-asf", ObjectFormat::Asf); MAP_TYPE("audio/mp4", ObjectFormat::Mp4); - MAP_TYPE("application/x-mpegurl", ObjectFormat::M3u); + MAP_TYPE("application/x-mpegurl", ObjectFormat::M3uPlaylist); #undef MAP_TYPE } @@ -100,7 +100,7 @@ //libmagic missing mime type for m3u files auto ext = GetExtension(filename); if (ext == "m3u") - return mtp::ObjectFormat::M3u; + return mtp::ObjectFormat::M3uPlaylist; static Magic magic; { @@ -114,7 +114,7 @@ else if (ext == "txt") return mtp::ObjectFormat::Text; else if (ext == "jpeg" || ext == "jpg") - return mtp::ObjectFormat::Jfif; + return mtp::ObjectFormat::ExifJpeg; else if (ext == "gif") return mtp::ObjectFormat::Gif; else if (ext == "bmp") @@ -135,6 +135,8 @@ return mtp::ObjectFormat::Wmv; else if (ext == "mp4") return mtp::ObjectFormat::Mp4; + else if (ext == "m4a") + return mtp::ObjectFormat::M4a; else if (ext == "3gp") return mtp::ObjectFormat::_3gp; else if (ext == "asf") @@ -143,6 +145,33 @@ return ObjectFormat::Undefined; } + bool IsAudioFormat(ObjectFormat format) + { + switch(format) + { + case ObjectFormat::Aiff: + case ObjectFormat::Wav: + case ObjectFormat::Mp3: + case ObjectFormat::M4a: + case ObjectFormat::UndefinedAudio: + case ObjectFormat::Wma: + case ObjectFormat::Ogg: + case ObjectFormat::Aac: + case ObjectFormat::Audible: + case ObjectFormat::Flac: + return true; + default: + return false; + } + } + + bool IsImageFormat(ObjectFormat format) + { + u16 type = static_cast(format) >> 8; + return type == 0x38; + } + + time_t ConvertDateTime(const std::string ×pec) { struct tm time = {}; @@ -161,4 +190,26 @@ size_t r = strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", &bdt); return std::string(buf, r); } + + std::string ConvertYear(int year) + { + struct tm bdt = {}; + bdt.tm_mday = 1; + bdt.tm_year = year - 1900; + auto ts = mktime(&bdt); + if (ts == (time_t) -1) + throw std::runtime_error("mktime failed"); + return ConvertDateTime(ts); + } + + std::string ToString(ObjectFormat property) + { + switch(property) + { +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(ObjectFormat, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(ObjectFormat, property, 4); + } + } + } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectFormat.h android-file-transfer-4.2/mtp/ptp/ObjectFormat.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectFormat.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectFormat.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef OBJECTFORMAT_H -#define OBJECTFORMAT_H +#ifndef AFTL_MTP_PTP_OBJECTFORMAT_H +#define AFTL_MTP_PTP_OBJECTFORMAT_H #include #include @@ -32,70 +32,13 @@ enum struct ObjectFormat : u16 { - Any = 0x0000, - Undefined = 0x3000, - Association = 0x3001, - Script = 0x3002, - Executable = 0x3003, - Text = 0x3004, - Html = 0x3005, - Dpof = 0x3006, - Aiff = 0x3007, - Wav = 0x3008, - Mp3 = 0x3009, - Avi = 0x300a, - Mpeg = 0x300b, - Asf = 0x300c, - UndefinedImage = 0x3800, - ExifJpeg = 0x3801, - TiffEp = 0x3802, - Flashpix = 0x3803, - Bmp = 0x3804, - Ciff = 0x3805, - Reserved = 0x3806, - Gif = 0x3807, - Jfif = 0x3808, - Pcd = 0x3809, - Pict = 0x380a, - Png = 0x380b, - Reserved2 = 0x380c, - Tiff = 0x380d, - TiffIt = 0x380e, - Jp2 = 0x380f, - Jpx = 0x3810, - - //audio - Wma = 0xb901, - Ogg = 0xb902, - Aac = 0xb903, - Audible = 0xb904, - Flac = 0xb906, - - //video - Wmv = 0xb980, - Mp4 = 0xb982, - Mp2 = 0xb983, - _3gp = 0xb984, - - AudioAlbum = 0xba03, - - //playlists - Wpl = 0xba10, - M3u = 0xba11, - Mpl = 0xba12, - Asx = 0xba13, - Pls = 0xba14, - - Xml = 0xba82, - Doc = 0xba83, - Mht = 0xba84, - Xls = 0xba85, - Ppt = 0xba86, - - VCard2 = 0xbb82 +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include +#undef ENUM_VALUE }; DECLARE_ENUM(ObjectFormat, u16); + std::string ToString(ObjectFormat property); enum struct AssociationType : u16 { @@ -111,9 +54,20 @@ DECLARE_ENUM(AssociationType, u16); ObjectFormat ObjectFormatFromFilename(const std::string &filename); + + bool IsAudioFormat(ObjectFormat format); + bool IsImageFormat(ObjectFormat format); + time_t ConvertDateTime(const std::string ×pec); std::string ConvertDateTime(time_t); + std::string ConvertYear(int year); } +namespace std +{ + template<> struct hash + { public: size_t operator()(mtp::ObjectFormat format) const { return static_cast(format); } }; +} + #endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectFormat.values.h android-file-transfer-4.2/mtp/ptp/ObjectFormat.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectFormat.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectFormat.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,93 @@ +ENUM_VALUE(Any, 0x0000) +ENUM_VALUE(Undefined, 0x3000) +ENUM_VALUE(Association, 0x3001) +ENUM_VALUE(Script, 0x3002) +ENUM_VALUE(Executable, 0x3003) +ENUM_VALUE(Text, 0x3004) +ENUM_VALUE(Html, 0x3005) +ENUM_VALUE(Dpof, 0x3006) +ENUM_VALUE(Aiff, 0x3007) +ENUM_VALUE(Wav, 0x3008) +ENUM_VALUE(Mp3, 0x3009) +ENUM_VALUE(Avi, 0x300a) +ENUM_VALUE(Mpeg, 0x300b) +ENUM_VALUE(Asf, 0x300c) + +ENUM_VALUE(UndefinedImage, 0x3800) +ENUM_VALUE(ExifJpeg, 0x3801) +ENUM_VALUE(TiffEp, 0x3802) +ENUM_VALUE(Flashpix, 0x3803) +ENUM_VALUE(Bmp, 0x3804) +ENUM_VALUE(Ciff, 0x3805) +ENUM_VALUE(Gif, 0x3807) +ENUM_VALUE(Jfif, 0x3808) +ENUM_VALUE(Pcd, 0x3809) +ENUM_VALUE(Pict, 0x380a) +ENUM_VALUE(Png, 0x380b) +ENUM_VALUE(Tiff, 0x380d) +ENUM_VALUE(TiffIt, 0x380e) +ENUM_VALUE(Jp2, 0x380f) +ENUM_VALUE(Jpx, 0x3810) +ENUM_VALUE(Dng, 0x3811) +ENUM_VALUE(Heif, 0x3812) + +ENUM_VALUE(NetworkAssociation, 0xb102) + +ENUM_VALUE(M4a, 0xb215) +ENUM_VALUE(Artist, 0xb218) + +ENUM_VALUE(UndefinedFirmware, 0xb800) +ENUM_VALUE(UndefinedFirmwareAndroid, 0xb802) +ENUM_VALUE(WindowsImageFormat, 0xb881) + +ENUM_VALUE(UndefinedAudio, 0xb900) +ENUM_VALUE(Wma, 0xb901) +ENUM_VALUE(Ogg, 0xb902) +ENUM_VALUE(Aac, 0xb903) +ENUM_VALUE(Audible, 0xb904) +ENUM_VALUE(Flac, 0xb906) + +ENUM_VALUE(UndefinedVideo, 0xb980) +ENUM_VALUE(Wmv, 0xb981) +ENUM_VALUE(Mp4, 0xb982) +ENUM_VALUE(Mp2, 0xb983) +ENUM_VALUE(_3gp, 0xb984) + +ENUM_VALUE(UndefinedCollection, 0xba00) +ENUM_VALUE(AbstractMultimediaAlbum, 0xba01) +ENUM_VALUE(AbstractImageAlbum, 0xba02) +ENUM_VALUE(AbstractAudioAlbum, 0xba03) +ENUM_VALUE(AbstractVideoAlbum, 0xba04) +ENUM_VALUE(AbstractAVPlaylist, 0xba05) +ENUM_VALUE(AbstractContactGroup, 0xba06) +ENUM_VALUE(AbstractMessageFolder, 0xba07) +ENUM_VALUE(AbstractChapteredProduction, 0xba08) +ENUM_VALUE(AbstractAudioPlaylist, 0xba09) +ENUM_VALUE(AbstractVideoPlaylist, 0xba0a) +ENUM_VALUE(AbstractMediacast, 0xba0b) +ENUM_VALUE(WplPlaylist, 0xba10) +ENUM_VALUE(M3uPlaylist, 0xba11) +ENUM_VALUE(MplPlaylist, 0xba12) +ENUM_VALUE(AsxPlaylist, 0xba13) +ENUM_VALUE(PlsPlaylist, 0xba14) + +ENUM_VALUE(UndefinedDocument, 0xba80) +ENUM_VALUE(AbstractDocument, 0xba81) +ENUM_VALUE(XmlDocument, 0xba82) +ENUM_VALUE(MicrosoftWordDocument, 0xba83) +ENUM_VALUE(MhtCompiledHtmlDocument, 0xba84) +ENUM_VALUE(MicrosoftExcelSpreadsheet, 0xba85) +ENUM_VALUE(MicrosoftPowerPointPresentation, 0xba86) + +ENUM_VALUE(UndefinedMessage, 0xbb00) +ENUM_VALUE(AbstractMessage, 0xbb01) +ENUM_VALUE(UndefinedContact, 0xbb80) +ENUM_VALUE(AbstractContact, 0xbb81) +ENUM_VALUE(VCard2, 0xbb82) +ENUM_VALUE(VCard3, 0xbb83) + +ENUM_VALUE(UndefinedCalendarItem, 0xbe00) +ENUM_VALUE(AbstractCalendarItem, 0xbe01) +ENUM_VALUE(VCalendar1, 0xbe02) +ENUM_VALUE(VCalendar2, 0xbe03) +ENUM_VALUE(UndefinedWindowsExecutable, 0xbe80) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectId.h android-file-transfer-4.2/mtp/ptp/ObjectId.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectId.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectId.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_PTP_OBJECTID_H -#define AFT_PTP_OBJECTID_H +#ifndef AFTL_MTP_PTP_OBJECTID_H +#define AFTL_MTP_PTP_OBJECTID_H #include @@ -53,6 +53,13 @@ } +namespace std +{ + template<> struct hash + { public: size_t operator()(const mtp::ObjectId &id) const { return id.Id; } }; + template<> struct hash + { public: size_t operator()(const mtp::StorageId &id) const { return id.Id; } }; +} #endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectProperty.cpp android-file-transfer-4.2/mtp/ptp/ObjectProperty.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectProperty.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectProperty.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,98 @@ +#include +#include +#include + +namespace mtp +{ + std::string ToString(ObjectProperty property) + { + switch(property) + { +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(ObjectProperty, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(ObjectProperty, property, 4); + } + } + namespace + { + template + void ArrayToString(std::stringstream & ss, InputStream & is, u32 size) + { + ss << "[ "; + while(size--) { + T value; + is >> value; + ss << value; + if (size) + ss << " "; + } + ss << "]"; + } + + void ToString(std::stringstream & ss, InputStream & is, DataTypeCode type) + { + if (IsArray(type)) + { + u32 size = is.Read32(); + + switch(type) + { +#define CASE(CODE, TYPE) case CODE : ArrayToString(ss, is, size); break + CASE(DataTypeCode::ArrayInt8, s8); + CASE(DataTypeCode::ArrayInt16, s16); + CASE(DataTypeCode::ArrayInt32, s32); + CASE(DataTypeCode::ArrayInt64, s64); + CASE(DataTypeCode::ArrayUint16, u16); + CASE(DataTypeCode::ArrayUint32, u32); + CASE(DataTypeCode::ArrayUint64, u64); + case DataTypeCode::ArrayUint8: + HexDump(ss, "value", size, is); + break; +#undef CASE + + default: + ss << "(value of unknown type " << ToString(type) << ")"; + } + } + else + { + switch(type) + { +#define CASE(BITS) \ + case DataTypeCode::Uint##BITS: \ + case DataTypeCode::Int##BITS: \ + ss << is.Read##BITS (); break; + CASE(8); CASE(16); CASE(32); CASE(64); +#undef CASE + case mtp::DataTypeCode::Uint128: + case mtp::DataTypeCode::Int128: + HexDump(ss, "value", 16, is); + break; + case mtp::DataTypeCode::String: + ss << is.ReadString(); break; + default: + ss << "(value of unknown type " << ToString(type) << ")"; + } + } + } + + bool IsString(const ByteArray & value) + { + return true; + } + } + + std::string ToString(DataTypeCode type, const ByteArray & value) + { + std::stringstream ss; + InputStream is(value); + if (type == DataTypeCode::ArrayUint16 && IsString(value)) + { + u32 size = is.Read32(); + ss << is.ReadString(size); + } else + ToString(ss, is, type); + return ss.str(); + } + +} diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectProperty.h android-file-transfer-4.2/mtp/ptp/ObjectProperty.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectProperty.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectProperty.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,80 +17,28 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef OBJECTPROPERTY_H -#define OBJECTPROPERTY_H +#ifndef AFTL_MTP_PTP_OBJECTPROPERTY_H +#define AFTL_MTP_PTP_OBJECTPROPERTY_H +#include +#include #include +#include namespace mtp { - enum struct DataTypeCode : u16 - { - Undefined = 0x0000, - - Int8 = 0x0001, - Uint8 = 0x0002, - Int16 = 0x0003, - Uint16 = 0x0004, - Int32 = 0x0005, - Uint32 = 0x0006, - Int64 = 0x0007, - Uint64 = 0x0008, - Int128 = 0x0009, - Uint128 = 0x000a, - - ArrayInt8 = 0x4001, - ArrayUint8 = 0x4002, - ArrayInt16 = 0x4003, - ArrayUint16 = 0x4004, - ArrayInt32 = 0x4005, - ArrayUint32 = 0x4006, - ArrayInt64 = 0x4007, - ArrayUint64 = 0x4008, - ArrayInt128 = 0x4009, - ArrayUint128 = 0x400a, - - String = 0xffff - }; - DECLARE_ENUM(DataTypeCode, u16); enum struct ObjectProperty : u16 { - StorageId = 0xdc01, - ObjectFormat = 0xdc02, - ProtectionStatus = 0xdc03, - ObjectSize = 0xdc04, - AssociationType = 0xdc05, - AssociationDesc = 0xdc06, - ObjectFilename = 0xdc07, - DateCreated = 0xdc08, - DateModified = 0xdc09, - Keywords = 0xdc0a, - ParentObject = 0xdc0b, - AllowedFolderContents = 0xdc0c, - Hidden = 0xdc0d, - SystemObject = 0xdc0e, - - PersistentUniqueObjectId = 0xdc41, - SyncId = 0xdc42, - Name = 0xdc44, - Artist = 0xdc46, - DateAuthored = 0xdc47, - DateAdded = 0xdc4e, - - RepresentativeSampleFormat = 0xdc81, - RepresentativeSampleData = 0xdc86, - - DisplayName = 0xdce0, - BodyText = 0xdce1, - Subject = 0xdce2, - Priority = 0xdce3, - - MediaGUID = 0xdd72, - All = 0xffff +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include +#undef ENUM_VALUE }; DECLARE_ENUM(ObjectProperty, u16); + std::string ToString(ObjectProperty property); + std::string ToString(DataTypeCode type, const ByteArray & value); + } #endif /* OBJECTPROPERTY_H */ diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectPropertyListParser.h android-file-transfer-4.2/mtp/ptp/ObjectPropertyListParser.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectPropertyListParser.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectPropertyListParser.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_MTP_PTP_OBJECTPROPERTYLISTPARSER_H -#define AFS_MTP_PTP_OBJECTPROPERTYLISTPARSER_H +#ifndef AFTL_MTP_PTP_OBJECTPROPERTYLISTPARSER_H +#define AFTL_MTP_PTP_OBJECTPROPERTYLISTPARSER_H #include #include @@ -73,7 +73,15 @@ template class Parser = impl::ObjectPropertyParser> struct ObjectPropertyListParser { - void Parse(const ByteArray & data, const std::function &func) + static u32 GetSize(const ByteArray & data) + { + InputStream stream(data); + u32 n; + stream >> n; + return n; + } + + static void Parse(const ByteArray & data, const std::function &func) { InputStream stream(data); u32 n; @@ -93,6 +101,8 @@ } } }; + + using ObjectStringPropertyListParser = ObjectPropertyListParser; } #endif diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ObjectProperty.values.h android-file-transfer-4.2/mtp/ptp/ObjectProperty.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ObjectProperty.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ObjectProperty.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,113 @@ +ENUM_VALUE(BuyFlag, 0xd901) +ENUM_VALUE(HostEUI64Array, 0xd920) + +ENUM_VALUE(ContentTypeUUID, 0xda97) //see WPD_OBJECT_CONTENT_TYPE +ENUM_VALUE(ArtistId, 0xdab9) +ENUM_VALUE(AlbumId, 0xdabb) +ENUM_VALUE(StorageId, 0xdc01) +ENUM_VALUE(ObjectFormat, 0xdc02) +ENUM_VALUE(ProtectionStatus, 0xdc03) +ENUM_VALUE(ObjectSize, 0xdc04) +ENUM_VALUE(AssociationType, 0xdc05) +ENUM_VALUE(AssociationDesc, 0xdc06) +ENUM_VALUE(ObjectFilename, 0xdc07) +ENUM_VALUE(DateCreated, 0xdc08) +ENUM_VALUE(DateModified, 0xdc09) +ENUM_VALUE(Keywords, 0xdc0a) +ENUM_VALUE(ParentObject, 0xdc0b) +ENUM_VALUE(AllowedFolderContents, 0xdc0c) +ENUM_VALUE(Hidden, 0xdc0d) +ENUM_VALUE(SystemObject, 0xdc0e) + +ENUM_VALUE(PersistentUniqueObjectId, 0xdc41) +ENUM_VALUE(SyncId, 0xdc42) +ENUM_VALUE(PropertyBag, 0xdc43) +ENUM_VALUE(Name, 0xdc44) +ENUM_VALUE(CreatedBy, 0xdc45) +ENUM_VALUE(Artist, 0xdc46) +ENUM_VALUE(DateAuthored, 0xdc47) +ENUM_VALUE(Description, 0xdc48) +ENUM_VALUE(UrlReference, 0xdc49) +ENUM_VALUE(LanguageLocale, 0xdc4a) +ENUM_VALUE(CopyrightInformation, 0xdc4b) +ENUM_VALUE(Source, 0xdc4c) +ENUM_VALUE(OriginLocation, 0xdc4d) +ENUM_VALUE(DateAdded, 0xdc4e) +ENUM_VALUE(NonConsumable, 0xdc4f) +ENUM_VALUE(CorruptedNonPlayable, 0xdc50) +ENUM_VALUE(ProducerSerialNumber, 0xdc51) + +ENUM_VALUE(RepresentativeSampleFormat, 0xdc81) +ENUM_VALUE(RepresentativeSampleSize, 0xdc82) +ENUM_VALUE(RepresentativeSampleHeight, 0xdc83) +ENUM_VALUE(RepresentativeSampleWidth, 0xdc84) +ENUM_VALUE(RepresentativeSampleDuration, 0xdc85) +ENUM_VALUE(RepresentativeSampleData, 0xdc86) +ENUM_VALUE(Width, 0xdc87) +ENUM_VALUE(Height, 0xdc88) +ENUM_VALUE(Duration, 0xdc89) +ENUM_VALUE(UserRating, 0xdc8a) +ENUM_VALUE(Track, 0xdc8b) +ENUM_VALUE(Genre, 0xdc8c) +ENUM_VALUE(Credits, 0xdc8d) +ENUM_VALUE(Lyrics, 0xdc8e) +ENUM_VALUE(SubscriptionContentId, 0xdc8f) +ENUM_VALUE(ProducedBy, 0xdc90) +ENUM_VALUE(UseCount, 0xdc91) +ENUM_VALUE(SkipCount, 0xdc92) +ENUM_VALUE(LastAccessed, 0xdc93) +ENUM_VALUE(ParentalRating, 0xdc94) +ENUM_VALUE(MetaGenre, 0xdc95) +ENUM_VALUE(Composer, 0xdc96) +ENUM_VALUE(EffectiveRating, 0xdc97) +ENUM_VALUE(Subtitle, 0xdc98) +ENUM_VALUE(OriginalReleaseDate, 0xdc99) +ENUM_VALUE(AlbumName, 0xdc9a) +ENUM_VALUE(AlbumArtist, 0xdc9b) +ENUM_VALUE(Mood, 0xdc9c) +ENUM_VALUE(DrmProtectionStatus, 0xdc9d) +ENUM_VALUE(Subdescription, 0xdc9e) + +ENUM_VALUE(IsCropped, 0xdcd1) +ENUM_VALUE(IsColourCorrected, 0xdcd2) +ENUM_VALUE(ImageBitDepth, 0xdcd3) +ENUM_VALUE(FNumber, 0xdcd4) +ENUM_VALUE(ExposureTime, 0xdcd5) +ENUM_VALUE(ExposureIndex, 0xdcd6) + +ENUM_VALUE(TotalBitrate, 0xde91) +ENUM_VALUE(BitrateType, 0xde92) +ENUM_VALUE(SampleRate, 0xde93) +ENUM_VALUE(NumberOfChannels, 0xde94) +ENUM_VALUE(AudioBitDepth, 0xde95) +ENUM_VALUE(BlockAlignment, 0xde96) +ENUM_VALUE(ScanType, 0xde97) +ENUM_VALUE(ColourRange, 0xde98) +ENUM_VALUE(AudioWaveCodec, 0xde99) +ENUM_VALUE(AudioBitrate, 0xde9a) +ENUM_VALUE(VideoFourCCCodec, 0xde9b) +ENUM_VALUE(VideoBitrate, 0xde9c) +ENUM_VALUE(FramesPerMilliseconds, 0xde9d) +ENUM_VALUE(KeyframeDistance, 0xde9e) +ENUM_VALUE(BufferSize, 0xde9f) +ENUM_VALUE(EncodingQuality, 0xdea0) +ENUM_VALUE(EncodingProfile, 0xdea1) + +ENUM_VALUE(DisplayName, 0xdce0) +ENUM_VALUE(BodyText, 0xdce1) +ENUM_VALUE(Subject, 0xdce2) +ENUM_VALUE(Priority, 0xdce3) + +ENUM_VALUE(Owner, 0xdd5d) +ENUM_VALUE(Editor, 0xdd5e) +ENUM_VALUE(WebMaster, 0xdd5f) +ENUM_VALUE(UrlSource, 0xdd60) +ENUM_VALUE(UrlDestination, 0xdd61) +ENUM_VALUE(TimeBookmark, 0xdd62) +ENUM_VALUE(ObjectBookmark, 0xdd63) +ENUM_VALUE(ByteBookmark, 0xdd64) + +ENUM_VALUE(LastBuildDate, 0xdd70) +ENUM_VALUE(TimeToLive, 0xdd71) +ENUM_VALUE(MediaGUID, 0xdd72) +ENUM_VALUE(All, 0xffff) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/OperationCode.cpp android-file-transfer-4.2/mtp/ptp/OperationCode.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/OperationCode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/OperationCode.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,15 @@ +#include +#include + +namespace mtp +{ + std::string ToString(OperationCode property) + { + switch(property) + { +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(OperationCode, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(OperationCode, property, 4); + } + } +} diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/OperationCode.h android-file-transfer-4.2/mtp/ptp/OperationCode.h --- android-file-transfer-3.9+dfsg/mtp/ptp/OperationCode.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/OperationCode.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef OPERATIONCODE_H -#define OPERATIONCODE_H +#ifndef AFTL_MTP_PTP_OPERATIONCODE_H +#define AFTL_MTP_PTP_OPERATIONCODE_H #include @@ -26,59 +26,13 @@ { enum class OperationCode : u16 //! MTP operation code { - GetDeviceInfo = 0x1001, - OpenSession = 0x1002, - CloseSession = 0x1003, - GetStorageIDs = 0x1004, - GetStorageInfo = 0x1005, - GetNumObjects = 0x1006, - GetObjectHandles = 0x1007, - GetObjectInfo = 0x1008, - GetObject = 0x1009, - GetThumb = 0x100a, - DeleteObject = 0x100b, - SendObjectInfo = 0x100c, - SendObject = 0x100d, - InitiateCapture = 0x100e, - FormatStore = 0x100f, - ResetDevice = 0x1010, - SelfTest = 0x1011, - SetObjectProtection = 0x1012, - PowerDown = 0x1013, - GetDevicePropDesc = 0x1014, - GetDevicePropValue = 0x1015, - SetDevicePropValue = 0x1016, - ResetDevicePropValue = 0x1017, - TerminateOpenCapture = 0x1018, - MoveObject = 0x1019, - CopyObject = 0x101a, - GetPartialObject = 0x101b, - InitiateOpenCapture = 0x101c, - - CancelTransaction = 0x4001, - - GetPartialObject64 = 0x95c1, - SendPartialObject = 0x95c2, - TruncateObject = 0x95c3, - BeginEditObject = 0x95c4, - EndEditObject = 0x95c5, - - GetObjectPropsSupported = 0x9801, - GetObjectPropDesc = 0x9802, - GetObjectPropValue = 0x9803, - SetObjectPropValue = 0x9804, - - GetObjectPropList = 0x9805, - SetObjectPropList = 0x9806, - GetInterdependentPropDesc = 0x9807, - SendObjectPropList = 0x9808, - - GetObjectReferences = 0x9810, - SetObjectReferences = 0x9811, - Skip = 0x9820 +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include +#undef ENUM_VALUE }; DECLARE_ENUM(OperationCode, u16); + std::string ToString(OperationCode property); } #endif /* OPERATIONCODE_H */ diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/OperationCode.values.h android-file-transfer-4.2/mtp/ptp/OperationCode.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/OperationCode.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/OperationCode.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,81 @@ +ENUM_VALUE(GetDeviceInfo, 0x1001) +ENUM_VALUE(OpenSession, 0x1002) +ENUM_VALUE(CloseSession, 0x1003) +ENUM_VALUE(GetStorageIDs, 0x1004) +ENUM_VALUE(GetStorageInfo, 0x1005) +ENUM_VALUE(GetNumObjects, 0x1006) +ENUM_VALUE(GetObjectHandles, 0x1007) +ENUM_VALUE(GetObjectInfo, 0x1008) +ENUM_VALUE(GetObject, 0x1009) +ENUM_VALUE(GetThumb, 0x100a) +ENUM_VALUE(DeleteObject, 0x100b) +ENUM_VALUE(SendObjectInfo, 0x100c) +ENUM_VALUE(SendObject, 0x100d) +ENUM_VALUE(InitiateCapture, 0x100e) +ENUM_VALUE(FormatStore, 0x100f) +ENUM_VALUE(ResetDevice, 0x1010) +ENUM_VALUE(SelfTest, 0x1011) +ENUM_VALUE(SetObjectProtection, 0x1012) +ENUM_VALUE(PowerDown, 0x1013) +ENUM_VALUE(GetDevicePropDesc, 0x1014) +ENUM_VALUE(GetDevicePropValue, 0x1015) +ENUM_VALUE(SetDevicePropValue, 0x1016) +ENUM_VALUE(ResetDevicePropValue, 0x1017) +ENUM_VALUE(TerminateOpenCapture, 0x1018) +ENUM_VALUE(MoveObject, 0x1019) +ENUM_VALUE(CopyObject, 0x101a) +ENUM_VALUE(GetPartialObject, 0x101b) +ENUM_VALUE(InitiateOpenCapture, 0x101c) + +ENUM_VALUE(CancelTransaction, 0x4001) + +ENUM_VALUE(JanusGetSecureTimeChallenge, 0x9101) +ENUM_VALUE(JanusSetSecureTimeResponse, 0x9102) +ENUM_VALUE(JanusSetLicenseResponse, 0x9103) +ENUM_VALUE(JanusGetSyncList, 0x9104) +ENUM_VALUE(JanusSendMeterChallengeQuery, 0x9105) +ENUM_VALUE(JanusGetMeterChallenge, 0x9106) +ENUM_VALUE(JanusSetMeterResponse, 0x9107) +ENUM_VALUE(JanusCleanDataStore, 0x9108) +ENUM_VALUE(JanusGetLicenseState, 0x9109) +ENUM_VALUE(JanusSendCommand, 0x910a) +ENUM_VALUE(JanusSendRequest, 0x910b) +ENUM_VALUE(ProcessWCFObject, 0x9122) + +ENUM_VALUE(OpenMediaSession, 0x9170) +ENUM_VALUE(CloseMediaSession, 0x9171) +ENUM_VALUE(GetNextDataBlock, 0x9172) +ENUM_VALUE(SetCurrentTimePosition, 0x9173) + +ENUM_VALUE(WMPMetadataRoundTrip, 0x9201) +ENUM_VALUE(WmpGetAcquiredContent, 0x9202) + +ENUM_VALUE(SendWMDRMPDAppRequest, 0x9212) +ENUM_VALUE(GetWMDRMPDAppResponse, 0x9213) +ENUM_VALUE(EnableTrustedFilesOperations, 0x9214) +ENUM_VALUE(DisableTrustedFilesOperations, 0x9215) +ENUM_VALUE(EndTrustedAppSession, 0x9216) + +ENUM_VALUE(SendTinyCLRData, 0x9401) +ENUM_VALUE(GetTinyCLRData, 0x9402) + +ENUM_VALUE(GetPartialObject64, 0x95c1) +ENUM_VALUE(SendPartialObject, 0x95c2) +ENUM_VALUE(TruncateObject, 0x95c3) +ENUM_VALUE(BeginEditObject, 0x95c4) +ENUM_VALUE(EndEditObject, 0x95c5) + +ENUM_VALUE(GetObjectPropsSupported, 0x9801) +ENUM_VALUE(GetObjectPropDesc, 0x9802) +ENUM_VALUE(GetObjectPropValue, 0x9803) +ENUM_VALUE(SetObjectPropValue, 0x9804) +ENUM_VALUE(GetObjectPropList, 0x9805) +ENUM_VALUE(SetObjectPropList, 0x9806) +ENUM_VALUE(GetInterdependentPropDesc, 0x9807) +ENUM_VALUE(SendObjectPropList, 0x9808) +ENUM_VALUE(GetObjectReferences, 0x9810) +ENUM_VALUE(SetObjectReferences, 0x9811) +ENUM_VALUE(UpdateDeviceFirmware, 0x9812) +ENUM_VALUE(ResetObjectPropValue, 0x9813) + +ENUM_VALUE(Skip, 0x9820) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/OperationRequest.h android-file-transfer-4.2/mtp/ptp/OperationRequest.h --- android-file-transfer-3.9+dfsg/mtp/ptp/OperationRequest.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/OperationRequest.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef OPERATIONREQUEST_H -#define OPERATIONREQUEST_H +#ifndef AFTL_MTP_PTP_OPERATIONREQUEST_H +#define AFTL_MTP_PTP_OPERATIONREQUEST_H #include #include diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/OutputStream.h android-file-transfer-4.2/mtp/ptp/OutputStream.h --- android-file-transfer-3.9+dfsg/mtp/ptp/OutputStream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/OutputStream.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef OUTPUTSTREAM_H -#define OUTPUTSTREAM_H +#ifndef AFTL_MTP_PTP_OUTPUTSTREAM_H +#define AFTL_MTP_PTP_OUTPUTSTREAM_H #include #include @@ -63,6 +63,13 @@ return size; } + void WriteData(const ByteArray & data) + { + _data.reserve(data.size() + _data.size()); + for(auto b : data) + Write8(b); + } + void WriteString(const std::string &value) { if (value.empty()) @@ -105,15 +112,18 @@ Write16(0); } - template - void WriteArray(const std::vector &array) + template + void WriteArray(const ArrayType & array) { Write32(array.size()); - for(const auto &el : array) + for(const auto & el : array) (*this) << el; } }; + inline OutputStream & operator << (OutputStream &stream, char value) + { stream.Write8(value); return stream; } + inline OutputStream & operator << (OutputStream &stream, u8 value) { stream.Write8(value); return stream; } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/PerceivedDeviceType.values.h android-file-transfer-4.2/mtp/ptp/PerceivedDeviceType.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/PerceivedDeviceType.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/PerceivedDeviceType.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,7 @@ +ENUM_VALUE(GenericMtpDevice, 0) +ENUM_VALUE(DigitalStillCamera, 1) +ENUM_VALUE(MediaPlayer, 2) +ENUM_VALUE(MobilePhone, 3) +ENUM_VALUE(DigitalVideoCamera, 4) +ENUM_VALUE(PDA, 5) +ENUM_VALUE(AudioRecorder, 6) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/PipePacketer.cpp android-file-transfer-4.2/mtp/ptp/PipePacketer.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/PipePacketer.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/PipePacketer.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include #include +#include #include @@ -48,18 +50,23 @@ u64 _size; private: - IObjectOutputStreamPtr GetStream1() const + IObjectOutputStreamPtr GetStream1() const override { return _header; } - IObjectOutputStreamPtr GetStream2() const + IObjectOutputStreamPtr GetStream2() const override { return _stream; } public: - MessageParsingStream(IObjectOutputStreamPtr stream): _header(new FixedSizeByteArrayObjectOutputStream(4)), _stream(stream), _offset(0), _size(4) { } + MessageParsingStream(IObjectOutputStreamPtr stream): + _header(new FixedSizeByteArrayObjectOutputStream(4)), + _stream(stream), _offset(0), _size(4) + { } + + bool Finished() const + { return _offset >= _size; } void OnStream1Exhausted() override { - _stream1Exhausted = true; InputStream is(_header->GetData()); u32 size; is >> size; @@ -72,8 +79,10 @@ { size_t r = JoinedObjectOutputStreamBase::Write(data, size); _offset += r; - if (_offset == _header->GetData().size()) + if (!_stream1Exhausted && _offset >= _header->GetData().size()) { + _stream1Exhausted = true; OnStream1Exhausted(); + } return r; } }; @@ -81,14 +90,11 @@ } void PipePacketer::ReadMessage(const IObjectOutputStreamPtr &outputStream, int timeout) - { - MessageParsingStreamPtr output(new MessageParsingStream(outputStream)); - _pipe->Read(output, timeout); - } + { _pipe->Read(outputStream, timeout); } - void PipePacketer::PollEvent() + void PipePacketer::PollEvent(int timeout) { - ByteArray interruptData = _pipe->ReadInterrupt(); + ByteArray interruptData = _pipe->ReadInterrupt(timeout); if (interruptData.empty()) return; @@ -96,7 +102,7 @@ InputStream stream(interruptData); ContainerType containerType; u32 size; - u16 eventCode; + EventCode eventCode; u32 sessionId; u32 transactionId; stream >> size; @@ -113,7 +119,7 @@ { struct DummyOutputStream final: IObjectOutputStream, public CancellableStream { - virtual size_t Write(const u8 *data, size_t size) + size_t Write(const u8 *data, size_t size) override { return size; } }; @@ -130,14 +136,13 @@ ResponseType _responseCode; private: - virtual IObjectOutputStreamPtr GetStream1() const + virtual IObjectOutputStreamPtr GetStream1() const override { return _header; } - virtual IObjectOutputStreamPtr GetStream2() const + virtual IObjectOutputStreamPtr GetStream2() const override { if (!_output) throw std::runtime_error("no data stream"); return _output; } - virtual void OnStream1Exhausted() + void OnStream1Exhausted() override { - _stream1Exhausted = true; InputStream stream(_header->GetData()); Response header; header.Read(stream); @@ -184,12 +189,14 @@ bool Finished() const { return _finished; } - virtual size_t Write(const u8 *data, size_t size) + size_t Write(const u8 *data, size_t size) override { size_t r = JoinedObjectOutputStreamBase::Write(data, size); _offset += r; - if (_offset == _header->GetData().size()) + if (!_stream1Exhausted && _offset >= _header->GetData().size()) { + _stream1Exhausted = true; OnStream1Exhausted(); + } return r; } }; @@ -198,23 +205,30 @@ void PipePacketer::Read(u32 transaction, const IObjectOutputStreamPtr &object, ResponseType &code, ByteArray &response, int timeout) { - try - { PollEvent(); } - catch(const std::exception &ex) - { error("exception in interrupt: ", ex.what()); } - response.clear(); + HeaderParserObjectOutputStreamPtr parser; + MessageParsingStreamPtr output; + while(true) { - HeaderParserObjectOutputStreamPtr parser(new HeaderParserObjectOutputStream(transaction, object)); - ReadMessage(parser, timeout); + if (!parser) + parser.reset(new HeaderParserObjectOutputStream(transaction, object)); + if (!output) + output.reset(new MessageParsingStream(parser)); + + ReadMessage(output, timeout); if (parser->Finished()) { response = parser->GetResponse(); code = parser->GetResponseCode(); break; } + + if (output->Finished()) { + parser.reset(); + output.reset(); + } } //HexDump("response", response); @@ -236,7 +250,7 @@ _pipe->GetDevice()->WriteControl( (u8)(usb::RequestType::HostToDevice | usb::RequestType::Class | usb::RequestType::Interface), 0x64, - 0, 0, req.Data, timeout); + 0, _pipe->GetInterface()->GetIndex(), req.Data, timeout); } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/PipePacketer.h android-file-transfer-4.2/mtp/ptp/PipePacketer.h --- android-file-transfer-3.9+dfsg/mtp/ptp/PipePacketer.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/PipePacketer.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef PIPEPACKETER_H -#define PIPEPACKETER_H +#ifndef AFTL_MTP_PTP_PIPEPACKETER_H +#define AFTL_MTP_PTP_PIPEPACKETER_H #include #include @@ -45,7 +45,7 @@ void Read(u32 transaction, const IObjectOutputStreamPtr &outputStream, ResponseType &code, ByteArray &response, int timeout); void Read(u32 transaction, ByteArray &data, ResponseType &code, ByteArray &response, int timeout); - void PollEvent(); + void PollEvent(int timeout); void Abort(u32 transaction, int timeout); private: diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Response.cpp android-file-transfer-4.2/mtp/ptp/Response.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/Response.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Response.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -18,70 +18,29 @@ */ #include +#include #include namespace mtp { # define R(NAME) case ResponseType::NAME: return #NAME - namespace + std::string ToString(ResponseType responseType) { - std::string GetResponseName(ResponseType r) + switch(responseType) { - switch(r) - { - R(OK); - R(GeneralError); - R(SessionNotOpen); - R(InvalidTransaction); - R(OperationNotSupported); - R(ParameterNotSupported); - R(IncompleteTransfer); - R(InvalidStorageID); - R(InvalidObjectHandle); - R(DevicePropNotSupported); - R(InvalidObjectFormatCode); - R(StoreFull); - R(ObjectWriteProtected); - R(StoreReadOnly); - R(AccessDenied); - R(NoThumbnailPresent); - R(SelfTestFailed); - R(PartialDeletion); - R(StoreNotAvailable); - R(SpecificationByFormatUnsupported); - R(NoValidObjectInfo); - R(InvalidCodeFormat); - R(UnknownVendorCode); - R(CaptureAlreadyTerminated); - R(DeviceBusy); - R(InvalidParentObject); - R(InvalidDevicePropFormat); - R(InvalidDevicePropValue); - R(InvalidParameter); - R(SessionAlreadyOpen); - R(TransactionCancelled); - R(SpecificationOfDestinationUnsupported); - R(InvalidObjectPropCode); - R(InvalidObjectPropFormat); - R(InvalidObjectPropValue); - R(InvalidObjectReference); - R(GroupNotSupported); - R(InvalidDataset); - R(UnsupportedSpecByGroup); - R(UnsupportedSpecByDepth); - R(ObjectTooLarge); - R(ObjectPropNotSupported); - - default: - return "Unknown"; - } +# define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_TO_STRING(ResponseType, NAME, VALUE) +# include + ENUM_VALUE_TO_STRING_DEFAULT(ResponseType, responseType, 4); } + } + namespace + { std::string FormatMessage(ResponseType r) { char buf[1024]; - snprintf(buf, sizeof(buf), "invalid response code %s (0x%04hx)", GetResponseName(r).c_str(), static_cast(r)); + snprintf(buf, sizeof(buf), "invalid response code %s (0x%04hx)", ToString(r).c_str(), static_cast(r)); return buf; } } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Response.h android-file-transfer-4.2/mtp/ptp/Response.h --- android-file-transfer-3.9+dfsg/mtp/ptp/Response.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Response.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef RESPONSE_H -#define RESPONSE_H +#ifndef AFTL_MTP_PTP_RESPONSE_H +#define AFTL_MTP_PTP_RESPONSE_H #include #include @@ -36,50 +36,11 @@ enum struct ResponseType : u16 { - OK = 0x2001, - GeneralError = 0x2002, - SessionNotOpen = 0x2003, - InvalidTransaction = 0x2004, - OperationNotSupported = 0x2005, - ParameterNotSupported = 0x2006, - IncompleteTransfer = 0x2007, - InvalidStorageID = 0x2008, - InvalidObjectHandle = 0x2009, - DevicePropNotSupported = 0x200a, - InvalidObjectFormatCode = 0x200b, - StoreFull = 0x200c, - ObjectWriteProtected = 0x200d, - StoreReadOnly = 0x200e, - AccessDenied = 0x200f, - NoThumbnailPresent = 0x2010, - SelfTestFailed = 0x2011, - PartialDeletion = 0x2012, - StoreNotAvailable = 0x2013, - SpecificationByFormatUnsupported = 0x2014, - NoValidObjectInfo = 0x2015, - InvalidCodeFormat = 0x2016, - UnknownVendorCode = 0x2017, - CaptureAlreadyTerminated = 0x2018, - DeviceBusy = 0x2019, - InvalidParentObject = 0x201a, - InvalidDevicePropFormat = 0x201b, - InvalidDevicePropValue = 0x201c, - InvalidParameter = 0x201d, - SessionAlreadyOpen = 0x201e, - TransactionCancelled = 0x201f, - SpecificationOfDestinationUnsupported = 0x2020, - - InvalidObjectPropCode = 0xa801, - InvalidObjectPropFormat = 0xa802, - InvalidObjectPropValue = 0xa803, - InvalidObjectReference = 0xa804, - GroupNotSupported = 0xa805, - InvalidDataset = 0xa806, - UnsupportedSpecByGroup = 0xa807, - UnsupportedSpecByDepth = 0xa808, - ObjectTooLarge = 0xa809, - ObjectPropNotSupported = 0xa80a +#define ENUM_VALUE(NAME, VALUE) ENUM_VALUE_DECL(NAME, VALUE) +# include +#undef ENUM_VALUE }; + std::string ToString(ResponseType response); DECLARE_ENUM(ResponseType, u16); struct Response //! MTP Response class diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/ResponseType.values.h android-file-transfer-4.2/mtp/ptp/ResponseType.values.h --- android-file-transfer-3.9+dfsg/mtp/ptp/ResponseType.values.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/ResponseType.values.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,44 @@ +ENUM_VALUE(OK, 0x2001) +ENUM_VALUE(GeneralError, 0x2002) +ENUM_VALUE(SessionNotOpen, 0x2003) +ENUM_VALUE(InvalidTransaction, 0x2004) +ENUM_VALUE(OperationNotSupported, 0x2005) +ENUM_VALUE(ParameterNotSupported, 0x2006) +ENUM_VALUE(IncompleteTransfer, 0x2007) +ENUM_VALUE(InvalidStorageID, 0x2008) +ENUM_VALUE(InvalidObjectHandle, 0x2009) +ENUM_VALUE(DevicePropNotSupported, 0x200a) +ENUM_VALUE(InvalidObjectFormatCode, 0x200b) +ENUM_VALUE(StoreFull, 0x200c) +ENUM_VALUE(ObjectWriteProtected, 0x200d) +ENUM_VALUE(StoreReadOnly, 0x200e) +ENUM_VALUE(AccessDenied, 0x200f) +ENUM_VALUE(NoThumbnailPresent, 0x2010) +ENUM_VALUE(SelfTestFailed, 0x2011) +ENUM_VALUE(PartialDeletion, 0x2012) +ENUM_VALUE(StoreNotAvailable, 0x2013) +ENUM_VALUE(SpecificationByFormatUnsupported, 0x2014) +ENUM_VALUE(NoValidObjectInfo, 0x2015) +ENUM_VALUE(InvalidCodeFormat, 0x2016) +ENUM_VALUE(UnknownVendorCode, 0x2017) +ENUM_VALUE(CaptureAlreadyTerminated, 0x2018) +ENUM_VALUE(DeviceBusy, 0x2019) +ENUM_VALUE(InvalidParentObject, 0x201a) +ENUM_VALUE(InvalidDevicePropFormat, 0x201b) +ENUM_VALUE(InvalidDevicePropValue, 0x201c) +ENUM_VALUE(InvalidParameter, 0x201d) +ENUM_VALUE(SessionAlreadyOpen, 0x201e) +ENUM_VALUE(TransactionCancelled, 0x201f) +ENUM_VALUE(SpecificationOfDestinationUnsupported, 0x2020) +ENUM_VALUE(TinyCLRNotResponding, 0xa401) +ENUM_VALUE(NoDataWaiting, 0xa402) +ENUM_VALUE(InvalidObjectPropCode, 0xa801) +ENUM_VALUE(InvalidObjectPropFormat, 0xa802) +ENUM_VALUE(InvalidObjectPropValue, 0xa803) +ENUM_VALUE(InvalidObjectReference, 0xa804) +ENUM_VALUE(GroupNotSupported, 0xa805) +ENUM_VALUE(InvalidDataset, 0xa806) +ENUM_VALUE(UnsupportedSpecByGroup, 0xa807) +ENUM_VALUE(UnsupportedSpecByDepth, 0xa808) +ENUM_VALUE(ObjectTooLarge, 0xa809) +ENUM_VALUE(ObjectPropNotSupported, 0xa80) diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Session.cpp android-file-transfer-4.2/mtp/ptp/Session.cpp --- android-file-transfer-3.9+dfsg/mtp/ptp/Session.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Session.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -42,14 +42,18 @@ throw InvalidResponseException(__func__, (RCODE)); \ } while(false) - Session::Session(usb::BulkPipePtr pipe, u32 sessionId): - _packeter(pipe), _sessionId(sessionId), _nextTransactionId(1), _transaction(), - _getObjectModificationTimeBuggy(false), + Session::Session(const PipePacketer & packeter, u32 sessionId): + _packeter(packeter), _sessionId(sessionId), _nextTransactionId(1), _transaction(), + _getObjectModificationTimeBuggy(false), _separateBulkWrites(false), _defaultTimeout(DefaultTimeout) { - _deviceInfo = GetDeviceInfoImpl(); + _deviceInfo = GetDeviceInfo(_packeter, _defaultTimeout); + if (_deviceInfo.Manufacturer == "Microsoft") + _separateBulkWrites = true; + _getPartialObject64Supported = _deviceInfo.Supports(OperationCode::GetPartialObject64); _getObjectPropertyListSupported = _deviceInfo.Supports(OperationCode::GetObjectPropList); + _getObjectPropValueSupported = _deviceInfo.Supports(OperationCode::GetObjectPropValue); _editObjectSupported = _deviceInfo.Supports(OperationCode::BeginEditObject) && _deviceInfo.Supports(OperationCode::EndEditObject) && _deviceInfo.Supports(OperationCode::TruncateObject) && @@ -79,13 +83,20 @@ _transaction->Id = _nextTransactionId++; } + void Session::Send(PipePacketer &packeter, const OperationRequest &req, int timeout) + { + if (timeout <= 0) + timeout = DefaultTimeout; + Container container(req); + packeter.Write(container.Data, timeout); + } + void Session::Send(const OperationRequest &req, int timeout) { if (timeout <= 0) timeout = _defaultTimeout; - Container container(req); - _packeter.Write(container.Data, timeout); + Send(_packeter, req, timeout); } void Session::Close() @@ -98,102 +109,122 @@ //HexDump("payload", data); } - ByteArray Session::Get(u32 transaction, int timeout) + ByteArray Session::Get(PipePacketer &packeter, u32 transaction, ByteArray & response, int timeout) { if (timeout <= 0) - timeout = _defaultTimeout; - ByteArray data, response; + timeout = DefaultTimeout; + + ByteArray data; ResponseType responseCode; - _packeter.Read(transaction, data, responseCode, response, timeout); + packeter.Read(transaction, data, responseCode, response, timeout); CHECK_RESPONSE(responseCode); return data; } + ByteArray Session::Get(u32 transaction, ByteArray &response, int timeout) + { + if (timeout <= 0) + timeout = _defaultTimeout; + return Get(_packeter, transaction, response, timeout); + } + template - ByteArray Session::RunTransactionWithDataRequest(int timeout, OperationCode code, const IObjectInputStreamPtr & inputStream, Args && ... args) + ByteArray Session::RunTransactionWithDataRequest(int timeout, OperationCode code, ByteArray & response, const IObjectInputStreamPtr & inputStream, Args && ... args) { +#if 0 + try + { _packeter.PollEvent(_defaultTimeout); } + catch(const std::exception &ex) + { error("exception in interrupt: ", ex.what()); } +#endif + scoped_mutex_lock l(_mutex); + if (!_deviceInfo.Supports(code)) + throw std::runtime_error("Operation code " + ToString(code) + " not supported."); Transaction transaction(this); Send(OperationRequest(code, transaction.Id, std::forward(args) ... ), timeout); if (inputStream) { DataRequest req(code, transaction.Id); Container container(req, inputStream); - _packeter.Write(std::make_shared(std::make_shared(container.Data), inputStream), timeout); + if (_separateBulkWrites) + { + _packeter.Write(std::make_shared(container.Data), timeout); + _packeter.Write(inputStream, timeout); + } else + _packeter.Write(std::make_shared(std::make_shared(container.Data), inputStream), timeout); } - return Get(transaction.Id); + return Get(transaction.Id, response); } - msg::DeviceInfo Session::GetDeviceInfoImpl() + msg::DeviceInfo Session::GetDeviceInfo(PipePacketer& packeter, int timeout) { - auto data = RunTransaction(_defaultTimeout, OperationCode::GetDeviceInfo); - InputStream stream(data); //operation code + session id - msg::DeviceInfo gdi; - gdi.Read(stream); - return gdi; + ByteArray response; + Send(packeter, OperationRequest(OperationCode::GetDeviceInfo, 0), timeout); + auto info = Get(packeter, 0, response, timeout); + return ParseResponse(info); } msg::ObjectHandles Session::GetObjectHandles(StorageId storageId, ObjectFormat objectFormat, ObjectId parent, int timeout) - { - auto data = RunTransaction(timeout, OperationCode::GetObjectHandles, storageId.Id, static_cast(objectFormat), parent.Id); - InputStream stream(data); - - msg::ObjectHandles goh; - goh.Read(stream); - return goh; - } + { return ParseResponse(RunTransaction(timeout, OperationCode::GetObjectHandles, storageId.Id, static_cast(objectFormat), parent.Id)); } msg::StorageIDs Session::GetStorageIDs() - { - auto data = RunTransaction(_defaultTimeout, OperationCode::GetStorageIDs); - InputStream stream(data); + { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetStorageIDs)); } - msg::StorageIDs gsi; - gsi.Read(stream); - return gsi; + msg::StorageInfo Session::GetStorageInfo(StorageId storageId) + { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetStorageInfo, storageId.Id)); } + + msg::SendObjectPropListResponse Session::SendObjectPropList(StorageId storageId, ObjectId parentId, ObjectFormat format, u64 objectSize, const ByteArray & propList) + { + ByteArray responseData; + IObjectInputStreamPtr inputStream = std::make_shared(propList); + RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SendObjectPropList, responseData, inputStream, storageId.Id, parentId.Id, static_cast(format), static_cast(objectSize >> 32), static_cast(objectSize)); + return ParseResponse(responseData); } - msg::StorageInfo Session::GetStorageInfo(StorageId storageId) + msg::NewObjectInfo Session::CreateDirectory(const std::string &name, ObjectId parentId, StorageId storageId, AssociationType type) { - auto data = RunTransaction(_defaultTimeout, OperationCode::GetStorageInfo, storageId.Id); - InputStream stream(data); - msg::StorageInfo gsi; - gsi.Read(stream); - return gsi; - } - - Session::NewObjectInfo Session::CreateDirectory(const std::string &name, ObjectId parentId, StorageId storageId, AssociationType type) - { - mtp::msg::ObjectInfo oi; - oi.Filename = name; - oi.ParentObject = parentId; - oi.StorageId = storageId; - oi.ObjectFormat = mtp::ObjectFormat::Association; - oi.AssociationType = type; - mtp::Session::NewObjectInfo noi = SendObjectInfo(oi, storageId, parentId); + if (_deviceInfo.Supports(OperationCode::SendObjectPropList)) + { + //modern way of creating objects + ByteArray propList; + { + OutputStream os(propList); + os.Write32(1); //number of props + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ObjectFilename)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(name); + } + auto response = SendObjectPropList(storageId, parentId, ObjectFormat::Association, 0, propList); - //SendObject(std::make_shared(ByteArray())); - return noi; + msg::NewObjectInfo noi; + noi.StorageId = response.StorageId; + noi.ParentObjectId = response.ParentObjectId; + noi.ObjectId = response.ObjectId; + return noi; + } + else + { + mtp::msg::ObjectInfo oi; + oi.Filename = name; + oi.ParentObject = parentId; + oi.StorageId = storageId; + oi.ObjectFormat = mtp::ObjectFormat::Association; + oi.AssociationType = type; + return SendObjectInfo(oi, storageId, parentId); + } } msg::ObjectInfo Session::GetObjectInfo(ObjectId objectId) - { - auto data = RunTransaction(_defaultTimeout, OperationCode::GetObjectInfo, objectId.Id); - InputStream stream(data); - msg::ObjectInfo goi; - goi.Read(stream); - return goi; - } + { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetObjectInfo, objectId.Id)); } - msg::ObjectPropertiesSupported Session::GetObjectPropertiesSupported(ObjectId objectId) - { - auto data = RunTransaction(_defaultTimeout, OperationCode::GetObjectPropsSupported, objectId.Id); - InputStream stream(data); - msg::ObjectPropertiesSupported ops; - ops.Read(stream); - return ops; - } + msg::ObjectPropertiesSupported Session::GetObjectPropertiesSupported(ObjectFormat format) + { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetObjectPropsSupported, static_cast(format))); } + + ByteArray Session::GetObjectPropertyDesc(ObjectProperty code) + { return RunTransaction(_defaultTimeout, OperationCode::GetObjectPropDesc, static_cast(code)); } void Session::GetObject(ObjectId objectId, const IObjectOutputStreamPtr &outputStream) { @@ -230,10 +261,33 @@ } - Session::NewObjectInfo Session::SendObjectInfo(const msg::ObjectInfo &objectInfo, StorageId storageId, ObjectId parentObject) + msg::NewObjectInfo Session::SendObjectInfo(const msg::ObjectInfo &objectInfo, StorageId storageId, ObjectId parentObject) { if (objectInfo.Filename.empty()) throw std::runtime_error("object filename must not be empty"); + + if (_deviceInfo.Supports(OperationCode::SendObjectPropList)) + { + //modern way of creating objects + ByteArray propList; + { + OutputStream os(propList); + os.Write32(1); //number of props + os.Write32(0); //object handle + os.Write16(static_cast(ObjectProperty::ObjectFilename)); + os.Write16(static_cast(DataTypeCode::String)); + os.WriteString(objectInfo.Filename); + } + + auto response = SendObjectPropList(storageId, parentObject, objectInfo.ObjectFormat, objectInfo.ObjectCompressedSize, propList); + + msg::NewObjectInfo noi; + noi.StorageId = response.StorageId; + noi.ParentObjectId = response.ParentObjectId; + noi.ObjectId = response.ObjectId; + return noi; + } + scoped_mutex_lock l(_mutex); Transaction transaction(this); Send(OperationRequest(OperationCode::SendObjectInfo, transaction.Id, storageId.Id, parentObject.Id)); @@ -249,12 +303,7 @@ _packeter.Read(transaction.Id, data, responseCode, response, _defaultTimeout); //HexDump("response", response); CHECK_RESPONSE(responseCode); - InputStream stream(response); - NewObjectInfo noi; - stream >> noi.StorageId; - stream >> noi.ParentObjectId; - stream >> noi.ObjectId; - return noi; + return ParseResponse(response); } void Session::SendObject(const IObjectInputStreamPtr &inputStream, int timeout) @@ -265,9 +314,15 @@ { DataRequest req(OperationCode::SendObject, transaction.Id); Container container(req, inputStream); - _packeter.Write(std::make_shared(std::make_shared(container.Data), inputStream), timeout); + if (_separateBulkWrites) + { + _packeter.Write(std::make_shared(container.Data), timeout); + _packeter.Write(inputStream, timeout); + } else + _packeter.Write(std::make_shared(std::make_shared(container.Data), inputStream), timeout); } - Get(transaction.Id); + ByteArray response; + Get(transaction.Id, response); } void Session::BeginEditObject(ObjectId objectId) @@ -276,7 +331,8 @@ void Session::SendPartialObject(ObjectId objectId, u64 offset, const ByteArray &data) { IObjectInputStreamPtr inputStream = std::make_shared(data); - RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SendPartialObject, inputStream, objectId.Id, offset, offset >> 32, data.size()); + ByteArray response; + RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SendPartialObject, response, inputStream, objectId.Id, offset, offset >> 32, data.size()); } void Session::TruncateObject(ObjectId objectId, u64 size) @@ -288,7 +344,8 @@ void Session::SetObjectProperty(ObjectId objectId, ObjectProperty property, const ByteArray &value) { IObjectInputStreamPtr inputStream = std::make_shared(value); - RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetObjectPropValue, inputStream, objectId.Id, (u16)property); + ByteArray response; + RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetObjectPropValue, response, inputStream, objectId.Id, (u16)property); } StorageId Session::GetObjectStorage(mtp::ObjectId id) @@ -315,7 +372,45 @@ { return RunTransaction(_defaultTimeout, OperationCode::GetObjectPropValue, objectId.Id, (u16)property); } u64 Session::GetObjectIntegerProperty(ObjectId objectId, ObjectProperty property) - { return ReadSingleInteger(GetObjectProperty(objectId, property)); } + { + if (!_getObjectPropValueSupported) { + auto info = GetObjectInfo(objectId); + switch(property) + { + case ObjectProperty::StorageId: + return info.StorageId.Id; + case ObjectProperty::ObjectFormat: + return static_cast(info.ObjectFormat); + case ObjectProperty::ProtectionStatus: + return info.ProtectionStatus; + case ObjectProperty::ObjectSize: + return info.ObjectCompressedSize; + case ObjectProperty::RepresentativeSampleFormat: + return info.ThumbFormat; + case ObjectProperty::RepresentativeSampleSize: + return info.ThumbCompressedSize; + case ObjectProperty::RepresentativeSampleWidth: + return info.ThumbPixWidth; + case ObjectProperty::RepresentativeSampleHeight: + return info.ThumbPixHeight; + case ObjectProperty::Width: + return info.ImagePixWidth; + case ObjectProperty::Height: + return info.ImagePixHeight; + case ObjectProperty::ImageBitDepth: + return info.ImageBitDepth; + case ObjectProperty::ParentObject: + return info.ParentObject.Id; + case ObjectProperty::AssociationType: + return static_cast(info.AssociationType); + case ObjectProperty::AssociationDesc: + return info.AssociationDesc; + default: + throw std::runtime_error("Device does not support object properties and no ObjectInfo fallback for " + ToString(property) + "."); + } + } + return ReadSingleInteger(GetObjectProperty(objectId, property)); + } void Session::SetObjectProperty(ObjectId objectId, ObjectProperty property, u64 value) { @@ -334,8 +429,36 @@ SetObjectProperty(objectId, property, ByteArray(data.begin(), data.begin() + i)); } + void Session::SetObjectPropertyAsArray(ObjectId objectId, ObjectProperty property, const ByteArray &value) + { + auto n = value.size(); + + ByteArray array; + OutputStream out(array); + array.reserve(n + 4); + out.WriteArray(value); + + SetObjectProperty(objectId, property, array); + } + std::string Session::GetObjectStringProperty(ObjectId objectId, ObjectProperty property) { + if (!_getObjectPropValueSupported) { + auto info = GetObjectInfo(objectId); + switch(property) + { + case ObjectProperty::ObjectFilename: + return info.Filename; + case ObjectProperty::DateCreated: + case ObjectProperty::DateAuthored: + case ObjectProperty::DateAdded: + return info.CaptureDate; + case ObjectProperty::DateModified: + return info.ModificationDate; + default: + throw std::runtime_error("Device does not support object properties and no ObjectInfo fallback for " + ToString(property) + "."); + } + } return ReadSingleString(GetObjectProperty(objectId, property)); } @@ -366,18 +489,47 @@ std::string Session::GetDeviceStringProperty(DeviceProperty property) { return ReadSingleString(GetDeviceProperty(property)); } - ByteArray Session::GetObjectPropertyList(ObjectId objectId, ObjectFormat format, ObjectProperty property, u32 groupCode, u32 depth, int timeout) + void Session::SetDeviceProperty(DeviceProperty property, const ByteArray &value) { - if (objectId == Root) //ffffffff -> 0 - objectId = Device; - return RunTransaction(timeout, OperationCode::GetObjectPropList, objectId.Id, (u32)format, property != ObjectProperty::All? (u32)property: 0xffffffffu, groupCode, depth); + IObjectInputStreamPtr inputStream = std::make_shared(value); + ByteArray response; + RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetDevicePropValue, response, inputStream, (u16)property); } + void Session::SetDeviceProperty(DeviceProperty property, const std::string &value) + { + ByteArray data; + data.reserve(value.size() * 2 + 1); + OutputStream stream(data); + stream << value; + SetDeviceProperty(property, data); + } + + + ByteArray Session::GetObjectPropertyList(ObjectId objectId, ObjectFormat format, ObjectProperty property, u32 groupCode, u32 depth, int timeout) + { return RunTransaction(timeout, OperationCode::GetObjectPropList, objectId.Id, (u32)format, property != ObjectProperty::All? (u32)property: 0xffffffffu, groupCode, depth); } + void Session::DeleteObject(ObjectId objectId, int timeout) { RunTransaction(timeout, OperationCode::DeleteObject, objectId.Id, 0); } + msg::DevicePropertyDesc Session::GetDevicePropertyDesc(DeviceProperty property) + { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetDevicePropDesc, static_cast(property))); } + ByteArray Session::GetDeviceProperty(DeviceProperty property) - { return RunTransaction(_defaultTimeout, OperationCode::GetDevicePropValue, (u16)property); } + { return RunTransaction(_defaultTimeout, OperationCode::GetDevicePropValue, static_cast(property)); } + + ByteArray Session::GenericOperation(OperationCode code) + { return RunTransaction(_defaultTimeout, code); } + + ByteArray Session::GenericOperation(OperationCode code, const ByteArray & payload) + { + IObjectInputStreamPtr inputStream = std::make_shared(payload); + ByteArray response; + return RunTransactionWithDataRequest(_defaultTimeout, code, response, inputStream); + } + + void Session::EnableSecureFileOperations(u32 cmac[4]) + { RunTransaction(_defaultTimeout, OperationCode::EnableTrustedFilesOperations, cmac[0], cmac[1], cmac[2], cmac[3]); } Session::ObjectEditSession::ObjectEditSession(const SessionPtr & session, ObjectId objectId): _session(session), _objectId(objectId) { session->BeginEditObject(objectId); } @@ -403,4 +555,18 @@ _packeter.Abort(transactionId, timeout); } + void Session::SetObjectReferences(ObjectId objectId, const msg::ObjectHandles & objects) + { + ByteArray data; + OutputStream out(data); + objects.Write(out); + + IObjectInputStreamPtr inputStream = std::make_shared(data); + ByteArray response; + RunTransactionWithDataRequest(_defaultTimeout, OperationCode::SetObjectReferences, response, inputStream, objectId.Id); + } + + msg::ObjectHandles Session::GetObjectReferences(ObjectId objectId) + { return ParseResponse(RunTransaction(_defaultTimeout, OperationCode::GetObjectReferences, objectId.Id)); } + } diff -Nru android-file-transfer-3.9+dfsg/mtp/ptp/Session.h android-file-transfer-4.2/mtp/ptp/Session.h --- android-file-transfer-3.9+dfsg/mtp/ptp/Session.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/ptp/Session.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef SESSION_H -#define SESSION_H +#ifndef AFTL_MTP_PTP_SESSION_H +#define AFTL_MTP_PTP_SESSION_H #include #include @@ -50,7 +50,9 @@ bool _getPartialObject64Supported; bool _editObjectSupported; bool _getObjectPropertyListSupported; + bool _getObjectPropValueSupported; bool _getObjectModificationTimeBuggy; + bool _separateBulkWrites; int _defaultTimeout; public: @@ -62,13 +64,6 @@ static const ObjectId Device; static const ObjectId Root; - struct NewObjectInfo - { - mtp::StorageId StorageId; - mtp::ObjectId ParentObjectId; - mtp::ObjectId ObjectId; - }; - ///sub-session object which handles partial writes and truncation class ObjectEditSession : Noncopyable { @@ -84,7 +79,7 @@ }; DECLARE_PTR(ObjectEditSession); - Session(usb::BulkPipePtr pipe, u32 sessionId); + Session(const PipePacketer & packeter, u32 sessionId); ~Session(); const msg::DeviceInfo & GetDeviceInfo() const @@ -94,12 +89,12 @@ msg::StorageIDs GetStorageIDs(); msg::StorageInfo GetStorageInfo(StorageId storageId); - NewObjectInfo CreateDirectory(const std::string &name, ObjectId parentId, StorageId storageId = AnyStorage, AssociationType type = AssociationType::GenericFolder); + msg::NewObjectInfo CreateDirectory(const std::string &name, ObjectId parentId, StorageId storageId = AnyStorage, AssociationType type = AssociationType::GenericFolder); msg::ObjectInfo GetObjectInfo(ObjectId objectId); void GetObject(ObjectId objectId, const IObjectOutputStreamPtr &outputStream); void GetThumb(ObjectId objectId, const IObjectOutputStreamPtr &outputStream); ByteArray GetPartialObject(ObjectId objectId, u64 offset, u32 size); - NewObjectInfo SendObjectInfo(const msg::ObjectInfo &objectInfo, StorageId storageId = AnyStorage, ObjectId parentObject = Device); + msg::NewObjectInfo SendObjectInfo(const msg::ObjectInfo &objectInfo, StorageId storageId = AnyStorage, ObjectId parentObject = Device); void SendObject(const IObjectInputStreamPtr &inputStream, int timeout = LongTimeout); void DeleteObject(ObjectId objectId, int timeout = LongTimeout); @@ -111,11 +106,13 @@ static ObjectEditSessionPtr EditObject(const SessionPtr &session, ObjectId objectId) { return std::make_shared(session, objectId); } - msg::ObjectPropertiesSupported GetObjectPropertiesSupported(ObjectId objectId); + msg::ObjectPropertiesSupported GetObjectPropertiesSupported(ObjectFormat format); + ByteArray GetObjectPropertyDesc(ObjectProperty code); void SetObjectProperty(ObjectId objectId, ObjectProperty property, const ByteArray &value); void SetObjectProperty(ObjectId objectId, ObjectProperty property, u64 value); void SetObjectProperty(ObjectId objectId, ObjectProperty property, const std::string &value); + void SetObjectPropertyAsArray(ObjectId objectId, ObjectProperty property, const ByteArray &value); time_t GetObjectModificationTime(ObjectId id); //common properties shortcuts @@ -127,31 +124,48 @@ std::string GetObjectStringProperty(ObjectId objectId, ObjectProperty property); ByteArray GetObjectPropertyList(ObjectId objectId, ObjectFormat format, ObjectProperty property, u32 groupCode, u32 depth, int timeout = LongTimeout); + msg::SendObjectPropListResponse SendObjectPropList(StorageId storageId, ObjectId parentId, ObjectFormat format, u64 objectSize, const ByteArray & propList); + msg::DevicePropertyDesc GetDevicePropertyDesc(DeviceProperty property); ByteArray GetDeviceProperty(DeviceProperty property); u64 GetDeviceIntegerProperty(DeviceProperty property); std::string GetDeviceStringProperty(DeviceProperty property); + void SetDeviceProperty(DeviceProperty property, const ByteArray & value); + void SetDeviceProperty(DeviceProperty property, const std::string & value); void AbortCurrentTransaction(int timeout = DefaultTimeout); + ByteArray GenericOperation(OperationCode code); + ByteArray GenericOperation(OperationCode code, const ByteArray & payload); + + void SetObjectReferences(ObjectId objectId, const msg::ObjectHandles &objects); + msg::ObjectHandles GetObjectReferences(ObjectId objectId); + + //windows specific + void EnableSecureFileOperations(u32 cmac1[4]); + + static msg::DeviceInfo GetDeviceInfo(PipePacketer& packeter, int timeout = 0); + private: template ByteArray RunTransaction(int timeout, OperationCode code, Args && ... args) - { return RunTransactionWithDataRequest(timeout, code, nullptr, std::forward(args) ... ); } + { ByteArray response; return RunTransactionWithDataRequest(timeout, code, response, nullptr, std::forward(args) ... ); } template - ByteArray RunTransactionWithDataRequest(int timeout, OperationCode code, const IObjectInputStreamPtr & inputStream, Args && ... args); + ByteArray RunTransactionWithDataRequest(int timeout, OperationCode code, ByteArray & response, const IObjectInputStreamPtr & inputStream, Args && ... args); void SetCurrentTransaction(Transaction *); - msg::DeviceInfo GetDeviceInfoImpl(); void BeginEditObject(ObjectId objectId); void SendPartialObject(ObjectId objectId, u64 offset, const ByteArray &data); void TruncateObject(ObjectId objectId, u64 size); void EndEditObject(ObjectId objectId); - ByteArray Get(u32 transaction, int timeout = 0); + ByteArray Get(u32 transaction, ByteArray & response, int timeout = 0); + static ByteArray Get(PipePacketer &packeter, u32 transaction, ByteArray & response, int timeout = 0); + void Send(const OperationRequest &req, int timeout = 0); + static void Send(PipePacketer &packeter, const OperationRequest &req, int timeout = 0); void Close(); }; diff -Nru android-file-transfer-3.9+dfsg/mtp/scope_guard.h android-file-transfer-4.2/mtp/scope_guard.h --- android-file-transfer-3.9+dfsg/mtp/scope_guard.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/mtp/scope_guard.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,24 @@ +#ifndef AFTL_MTP_SCOPE_GUARD_H +#define AFTL_MTP_SCOPE_GUARD_H + +#include +#include + +namespace mtp +{ + class scope_guard : Noncopyable + { + using Callback = std::function; + Callback _callback; + + public: + scope_guard(Callback && c): _callback(c) + { } + + ~scope_guard() + { _callback(); } + }; + +} + +#endif diff -Nru android-file-transfer-3.9+dfsg/mtp/Token.h android-file-transfer-4.2/mtp/Token.h --- android-file-transfer-3.9+dfsg/mtp/Token.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/Token.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTP_TOKEN_H -#define MTP_TOKEN_H +#ifndef AFTL_MTP_TOKEN_H +#define AFTL_MTP_TOKEN_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/types.h android-file-transfer-4.2/mtp/types.h --- android-file-transfer-3.9+dfsg/mtp/types.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/types.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef TYPES_H -#define TYPES_H +#ifndef AFTL_MTP_TYPES_H +#define AFTL_MTP_TYPES_H #include #include @@ -83,5 +83,9 @@ Stream &operator >> (Stream & stream, TYPE &format) \ { BASETYPE value; stream >> value; format = static_cast(value); return stream; } +#define ENUM_VALUE_DECL(NAME, VALUE) NAME = VALUE , +#define ENUM_VALUE_TO_STRING(TYPE, NAME, VALUE) case TYPE :: NAME : return #NAME ; +#define ENUM_VALUE_TO_STRING_DEFAULT(TYPE, VALUE, PADDING) default: return hex(static_cast( VALUE ), PADDING ).ToString(); + #endif diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/BulkPipe.cpp android-file-transfer-4.2/mtp/usb/BulkPipe.cpp --- android-file-transfer-3.9+dfsg/mtp/usb/BulkPipe.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/BulkPipe.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -28,10 +28,6 @@ BulkPipe::BulkPipe(DevicePtr device, ConfigurationPtr conf, InterfacePtr interface, EndpointPtr in, EndpointPtr out, EndpointPtr interrupt, ITokenPtr claimToken): _device(device), _conf(conf), _interface(interface), _in(in), _out(out), _interrupt(interrupt), _claimToken(claimToken) { - int currentConfigurationIndex = _device->GetConfiguration(); - if (conf->GetIndex() != currentConfigurationIndex) - _device->SetConfiguration(conf->GetIndex()); - try { device->ClearHalt(in); } catch(const std::exception & ex) @@ -49,16 +45,14 @@ DevicePtr BulkPipe::GetDevice() const { return _device; } - ByteArray BulkPipe::ReadInterrupt() + InterfacePtr BulkPipe::GetInterface() const + { return _interface; } + + ByteArray BulkPipe::ReadInterrupt(int timeout) { -#if 0 -#warning causes out of memory errors in some time ByteArrayObjectOutputStreamPtr s(new ByteArrayObjectOutputStream()); - try { _device->ReadBulk(_interrupt, s, 0); } catch(const TimeoutException &ex) { return ByteArray(); } - return std::move(s->GetData()); -#else - return ByteArray(); -#endif + try { _device->ReadBulk(_interrupt, s, timeout); } catch(const TimeoutException &ex) { return ByteArray(); } + return s->GetData(); } void BulkPipe::SetCurrentStream(const ICancellableStreamPtr &stream) diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/BulkPipe.h android-file-transfer-4.2/mtp/usb/BulkPipe.h --- android-file-transfer-3.9+dfsg/mtp/usb/BulkPipe.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/BulkPipe.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef BULKPIPE_H -#define BULKPIPE_H +#ifndef AFTL_MTP_USB_BULKPIPE_H +#define AFTL_MTP_USB_BULKPIPE_H #include #include @@ -58,8 +58,9 @@ ~BulkPipe(); DevicePtr GetDevice() const; + InterfacePtr GetInterface() const; - ByteArray ReadInterrupt(); + ByteArray ReadInterrupt(int timeout); void Read(const IObjectOutputStreamPtr &outputStream, int timeout = 10000); void Write(const IObjectInputStreamPtr &inputStream, int timeout = 10000); diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/DeviceBusyException.cpp android-file-transfer-4.2/mtp/usb/DeviceBusyException.cpp --- android-file-transfer-3.9+dfsg/mtp/usb/DeviceBusyException.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/DeviceBusyException.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,3 +1,22 @@ +/* + This file is part of Android File Transfer For Linux. + Copyright (C) 2015-2020 Vladimir Menshakov + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + #include #include #ifdef __linux__ @@ -13,11 +32,12 @@ namespace mtp { namespace usb { + +#ifdef __linux__ namespace { std::string ReadLink(const std::string &path) { -#ifdef __linux__ char buf[NAME_MAX]; auto pathSize = readlink(path.c_str(), buf, sizeof(buf)); if (pathSize >= 0) @@ -25,10 +45,9 @@ debug("Readlink ", path, ": ", posix::Exception::GetErrorMessage(errno)); return buf; -#endif - return std::string(); } } +#endif DeviceBusyException::DeviceBusyException(int fd, const std::string &msg): std::runtime_error(msg) @@ -107,8 +126,10 @@ } void DeviceBusyException::Kill() const + { Kill(Processes); } + void DeviceBusyException::Kill(const std::vector & processes) { - for(auto desc : Processes) + for(auto & desc : processes) { try { Kill(desc); } diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/DeviceBusyException.h android-file-transfer-4.2/mtp/usb/DeviceBusyException.h --- android-file-transfer-3.9+dfsg/mtp/usb/DeviceBusyException.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/DeviceBusyException.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_DEVICEBUSYEXCEPTION_H -#define USB_DEVICEBUSYEXCEPTION_H +#ifndef AFTL_MTP_USB_DEVICEBUSYEXCEPTION_H +#define AFTL_MTP_USB_DEVICEBUSYEXCEPTION_H #include #include @@ -41,6 +41,7 @@ void Kill() const; static void Kill(const ProcessDescriptor & desc); + static void Kill(const std::vector & processes); }; }} diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/DeviceNotFoundException.h android-file-transfer-4.2/mtp/usb/DeviceNotFoundException.h --- android-file-transfer-3.9+dfsg/mtp/usb/DeviceNotFoundException.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/DeviceNotFoundException.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFS_USB_DEVICENOTFOUNDEXCEPTION_H -#define AFS_USB_DEVICENOTFOUNDEXCEPTION_H +#ifndef AFTL_MTP_USB_DEVICENOTFOUNDEXCEPTION_H +#define AFTL_MTP_USB_DEVICENOTFOUNDEXCEPTION_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/Request.cpp android-file-transfer-4.2/mtp/usb/Request.cpp --- android-file-transfer-3.9+dfsg/mtp/usb/Request.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/Request.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/Request.h android-file-transfer-4.2/mtp/usb/Request.h --- android-file-transfer-3.9+dfsg/mtp/usb/Request.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/Request.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_USB_REQUEST_H -#define AFT_USB_REQUEST_H +#ifndef AFTL_MTP_USB_REQUEST_H +#define AFTL_MTP_USB_REQUEST_H #include #include @@ -84,14 +84,15 @@ enum struct Request : u8 { - GetStatus = 0, - ClearFeature = 1, - SetFeature = 3, - SetAddress = 5, - GetDescriptor = 6, - SetDescriptor = 7, - GetConfiguration = 8, - SetConfiguration = 9 + GetStatus = 0, + ClearFeature = 1, + SetFeature = 3, + SetAddress = 5, + GetDescriptor = 6, + SetDescriptor = 7, + GetConfiguration = 8, + SetConfiguration = 9, + GetOSStringDescriptor = 0xee }; u16 GetStatus(); diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/TimeoutException.h android-file-transfer-4.2/mtp/usb/TimeoutException.h --- android-file-transfer-3.9+dfsg/mtp/usb/TimeoutException.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/TimeoutException.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_TIMEOUTEXCEPTION_H -#define USB_TIMEOUTEXCEPTION_H +#ifndef AFTL_MTP_USB_TIMEOUTEXCEPTION_H +#define AFTL_MTP_USB_TIMEOUTEXCEPTION_H #include diff -Nru android-file-transfer-3.9+dfsg/mtp/usb/types.h android-file-transfer-4.2/mtp/usb/types.h --- android-file-transfer-3.9+dfsg/mtp/usb/types.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/usb/types.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef USB_TYPES_H -#define USB_TYPES_H +#ifndef AFTL_MTP_USB_TYPES_H +#define AFTL_MTP_USB_TYPES_H namespace mtp { namespace usb { diff -Nru android-file-transfer-3.9+dfsg/mtp/version.h android-file-transfer-4.2/mtp/version.h --- android-file-transfer-3.9+dfsg/mtp/version.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/mtp/version.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef AFT_MTP_VERSION_H -#define AFT_MTP_VERSION_H +#ifndef AFTL_MTP_VERSION_H +#define AFTL_MTP_VERSION_H #include diff -Nru android-file-transfer-3.9+dfsg/osx/Info.plist android-file-transfer-4.2/osx/Info.plist --- android-file-transfer-3.9+dfsg/osx/Info.plist 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/osx/Info.plist 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - Android File Transfer - CFBundleIconFile - Android File Transfer - CFBundleIdentifier - com.whoozle.androidFileTransfer - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - Android File Transfer - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleVersion - 1.25 - LSMinimumSystemVersion - 10.4 - NSMainNibFile - MainMenu - NSPrincipalClass - SWELLApplication - - diff -Nru android-file-transfer-3.9+dfsg/python/aftl.cpp android-file-transfer-4.2/python/aftl.cpp --- android-file-transfer-3.9+dfsg/python/aftl.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/python/aftl.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,320 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace py = pybind11; + +using namespace mtp; + +using system_clock = std::chrono::system_clock; + +namespace pybind11 { namespace detail { + template <> struct type_caster { + public: + PYBIND11_TYPE_CASTER(ByteArray, _("ByteArray")); + + bool load(handle src, bool) { + PyObject *source = src.ptr(); + if (!PyByteArray_Check(source)) + return false; + + auto size = PyByteArray_Size(source); + u8 * ptr = reinterpret_cast(PyByteArray_AsString(source)); + if (ptr && size) + value.assign(ptr, ptr + size); + else + value.clear(); + + return !PyErr_Occurred(); + } + + static handle cast(const ByteArray & src, return_value_policy /* policy */, handle /* parent */) { + return PyByteArray_FromStringAndSize(reinterpret_cast(src.data()), src.size()); + } + }; +}} // namespace pybind11::detail + +namespace +{ + struct PythonInputStream final : public IObjectInputStream + { + py::object ReadMethod; + u64 Size; + bool Cancelled = false; + + PythonInputStream(py::object object, u64 size): + ReadMethod(object.attr("read")), + Size(size) + { } + + void Cancel() override + { Cancelled = true; } + + u64 GetSize() const override + { return Size; } + + size_t Read(u8 *data, size_t maxSize) override + { + if (Cancelled) + return 0; + + py::bytes value = ReadMethod(maxSize); + char *buffer; + ssize_t size; + if (PYBIND11_BYTES_AS_STRING_AND_SIZE(value.ptr(), &buffer, &size) || !buffer || size > maxSize) + py::pybind11_fail("Unable to extract bytes contents!"); + + memcpy(data, buffer, size); + return PyErr_Occurred()? 0: size; + } + }; + + struct PythonOutputStream final : public IObjectOutputStream + { + py::object WriteMethod; + bool Cancelled = false; + + PythonOutputStream(py::object object): WriteMethod(object.attr("write")) + { } + + void Cancel() override + { Cancelled = true; } + size_t Write(const u8* data, size_t size) override + { + if (Cancelled) + return 0; + py::bytes payload(reinterpret_cast(data), size); + WriteMethod(payload).cast(); + return PyErr_Occurred()? 0: size; + } + }; + +} + +static void EnableDebug(bool enable) { + g_debug = enable; +} + +PYBIND11_MODULE(aftl, m) { + m.doc() = "Android File Transfer for Linux python bindings"; + m.def("debug", &EnableDebug, "Enables logs from MTP library"); + + py::class_ session(m, "Session"); + py::class_ objectId(m, "ObjectId"); + +#define VALUE(TYPE, NAME) .value(#NAME, TYPE :: NAME) +#define DEF_READONLY(TYPE, NAME) .def_readonly(#NAME, & TYPE :: NAME ) + + py::enum_(m, "ObjectFormat", "MTP Object format for querying specific types of media, or Any") +#define ENUM_VALUE(NAME, _) VALUE(ObjectFormat, NAME) +# include +#undef ENUM_VALUE + ; + py::enum_(m, "ObjectProperty", "MTP object property") +#define ENUM_VALUE(NAME, _) VALUE(ObjectProperty, NAME) +# include +#undef ENUM_VALUE + ; + py::enum_(m, "DeviceProperty", "MTP device property") +#define ENUM_VALUE(NAME, _) VALUE(DeviceProperty, NAME) +# include +#undef ENUM_VALUE + ; + + py::enum_(m, "AssociationType", "MTP Association Type") + VALUE(AssociationType, GenericFolder) + VALUE(AssociationType, Album) + VALUE(AssociationType, TimeSequence) + VALUE(AssociationType, HorizontalPanoramic) + VALUE(AssociationType, VerticalPanoramic) + VALUE(AssociationType, Panoramic2D) + VALUE(AssociationType, AncillaryData) + ; + + py::enum_(m, "OperationCode", "MTP Operation Code") +#define ENUM_VALUE(NAME, _) VALUE(OperationCode, NAME) +# include +#undef ENUM_VALUE + ; + + py::class_(m, "DeviceDescriptor"). + def_property_readonly("vendor_id", &usb::DeviceDescriptor::GetVendorId). + def_property_readonly("product_id", &usb::DeviceDescriptor::GetProductId). + def("__repr__", + [](const usb::DeviceDescriptor f) -> std::string { + return "DeviceDescriptor(" + std::to_string(f.GetVendorId()) + ":" + std::to_string(f.GetProductId()) + ")"; + }) + ; + ; + + py::class_(m, "Device"). + def_static("find_first", static_cast(&Device::FindFirst), + py::arg("filter_device") = std::string(), py::arg("claim_interface") = true, py::arg("reset_device") = false). + def("open_session", &Device::OpenSession, + py::arg("session_id") = 1, py::arg("timeout") = static_cast(Session::DefaultTimeout)) + ; + + py::class_(m, "StorageId"). + def("__repr__", + [](const StorageId &id) { return "StorageId(" + std::to_string(id.Id) + ")"; }); + + objectId. + def("__repr__", + [](const ObjectId &id) { return "ObjectId(" + std::to_string(id.Id) + ")"; }); + + py::class_(m, "NewObjectInfo", "information about newly created object"). + def_readonly("storage_id", &msg::NewObjectInfo::StorageId). + def_readonly("parent_object_id", &msg::NewObjectInfo::ParentObjectId). + def_readonly("object_id", &msg::NewObjectInfo::ObjectId) + ; + + py::class_(m, "ObjectInfo"). + def(py::init<>()) + DEF_READONLY(msg::ObjectInfo, ObjectFormat) + DEF_READONLY(msg::ObjectInfo, ProtectionStatus) + DEF_READONLY(msg::ObjectInfo, ObjectCompressedSize) + DEF_READONLY(msg::ObjectInfo, ThumbFormat) + DEF_READONLY(msg::ObjectInfo, ThumbCompressedSize) + DEF_READONLY(msg::ObjectInfo, ThumbPixWidth) + DEF_READONLY(msg::ObjectInfo, ThumbPixHeight) + DEF_READONLY(msg::ObjectInfo, ImagePixWidth) + DEF_READONLY(msg::ObjectInfo, ImagePixHeight) + DEF_READONLY(msg::ObjectInfo, ImageBitDepth) + DEF_READONLY(msg::ObjectInfo, ParentObject) + DEF_READONLY(msg::ObjectInfo, AssociationType) + DEF_READONLY(msg::ObjectInfo, AssociationDesc) + DEF_READONLY(msg::ObjectInfo, SequenceNumber) + DEF_READONLY(msg::ObjectInfo, Filename) + DEF_READONLY(msg::ObjectInfo, CaptureDate) + DEF_READONLY(msg::ObjectInfo, ModificationDate) + DEF_READONLY(msg::ObjectInfo, Keywords) + ; + + py::class_(m, "StorageInfo") + DEF_READONLY(msg::StorageInfo, StorageType) + DEF_READONLY(msg::StorageInfo, FilesystemType) + DEF_READONLY(msg::StorageInfo, AccessCapability) + DEF_READONLY(msg::StorageInfo, MaxCapacity) + DEF_READONLY(msg::StorageInfo, FreeSpaceInBytes) + DEF_READONLY(msg::StorageInfo, FreeSpaceInImages) + DEF_READONLY(msg::StorageInfo, StorageDescription) + DEF_READONLY(msg::StorageInfo, VolumeLabel) + ; + py::class_(m, "DeviceInfo") + DEF_READONLY(msg::DeviceInfo, StandardVersion) + DEF_READONLY(msg::DeviceInfo, VendorExtensionId) + DEF_READONLY(msg::DeviceInfo, VendorExtensionVersion) + DEF_READONLY(msg::DeviceInfo, VendorExtensionDesc) + DEF_READONLY(msg::DeviceInfo, FunctionalMode) + DEF_READONLY(msg::DeviceInfo, OperationsSupported) + DEF_READONLY(msg::DeviceInfo, EventsSupported) + DEF_READONLY(msg::DeviceInfo, DevicePropertiesSupported) + DEF_READONLY(msg::DeviceInfo, CaptureFormats) + DEF_READONLY(msg::DeviceInfo, ImageFormats) + DEF_READONLY(msg::DeviceInfo, Manufacturer) + DEF_READONLY(msg::DeviceInfo, Model) + DEF_READONLY(msg::DeviceInfo, DeviceVersion) + DEF_READONLY(msg::DeviceInfo, SerialNumber) + ; + + py::class_(m, "ObjectEditSession"). + def("truncate", &Session::ObjectEditSession::Truncate). + def("send", &Session::ObjectEditSession::Send) + ; + + + session. + // def_readonly_static("DefaultTimeout", &Session::DefaultTimeout). + // def_readonly_static("LongTimeout", &Session::LongTimeout). + + def_readonly_static("AllStorages", &Session::AllStorages). + def_readonly_static("AnyStorage", &Session::AnyStorage). + def_readonly_static("Device", &Session::Device). + def_readonly_static("Root", &Session::Root). + + def("get_storage_ids", [](Session * self) -> std::vector { + std::vector result; + auto sids = self->GetStorageIDs(); + result.reserve(sids.StorageIDs.size()); + for(auto & sid: sids.StorageIDs) { + result.push_back(sid); + } + return result; + }). + + def("get_storage_info", &Session::GetStorageInfo). + + def("get_object_handles", [](Session *self, StorageId storageId, ObjectFormat objectFormat, ObjectId parent, int timeout) -> std::vector { + std::vector result; + auto objects = self->GetObjectHandles(storageId, objectFormat, parent, timeout); + result.reserve(objects.ObjectHandles.size()); + for(auto & oid: objects.ObjectHandles) { + result.push_back(oid); + } + return result; + }, py::arg("storageId"), py::arg("objectFormat") = ObjectFormat::Any, py::arg("parent") = Session::Root, py::arg("timeout") = static_cast(Session::LongTimeout)). + + def("get_object_info", &Session::GetObjectInfo). + def("get_object_properties_supported", [](Session *self, ObjectFormat format) -> std::vector { + std::vector result; + auto props = self->GetObjectPropertiesSupported(format); + result.reserve(props.ObjectPropertyCodes.size()); + for(auto prop: props.ObjectPropertyCodes) { + result.push_back(prop); + } + return result; + }). + + def("get_object", [](Session * self, ObjectId object, py::object stream) { + self->GetObject(object, std::make_shared(stream)); + }). + def("get_thumb", [](Session * self, ObjectId object, py::object stream) { + self->GetThumb(object, std::make_shared(stream)); + }). + + def("send_object", [](Session * self, py::object stream, long size, int timeout) { + self->SendObject(std::make_shared(stream, size), timeout); + }, py::arg("stream"), py::arg("size"), py::arg("timeout") = static_cast(Session::LongTimeout)). + + def("get_object_property", &Session::GetObjectProperty). + def("get_object_string_property", &Session::GetObjectStringProperty). + def("get_object_integer_property", &Session::GetObjectIntegerProperty). + + def("set_object_property", (void (Session::*)(ObjectId, ObjectProperty, u64)) &Session::SetObjectProperty). + def("set_object_property", (void (Session::*)(ObjectId, ObjectProperty, const std::string &)) &Session::SetObjectProperty). + def("set_object_property", (void (Session::*)(ObjectId, ObjectProperty, const ByteArray &)) &Session::SetObjectProperty). + + def("get_object_storage", &Session::GetObjectStorage). + def("get_object_parent", &Session::GetObjectParent). + def("delete_object", &Session::DeleteObject). + + def("edit_object_supported", &Session::EditObjectSupported). + def("get_object_property_list_supported", &Session::GetObjectPropertyListSupported). + def("get_object_property_list", &Session::GetObjectPropertyList). + def("get_object_modification_time", [](Session * self, ObjectId objectId) -> std::chrono::system_clock::time_point { + time_t t = self->GetObjectModificationTime(objectId); + return system_clock::from_time_t(t); + }). + + def("get_partial_object", &Session::GetPartialObject). + def("edit_object", &Session::EditObject). + + def("create_directory", &Session::CreateDirectory, py::arg("name"), py::arg("parent"), py::arg("storage") = Session::AnyStorage, py::arg("association_type") = AssociationType::GenericFolder). + def("send_object_info", &Session::SendObjectInfo, py::arg("object_info"), py::arg("storage"), py::arg("parent")). + + def("get_device_info", static_cast(&Session::GetDeviceInfo)). + def("get_device_property", &Session::GetDeviceProperty). + def("get_device_integer_property", &Session::GetDeviceIntegerProperty). + def("get_device_string_property", &Session::GetDeviceStringProperty). + def("set_device_property", (void (Session::*)(DeviceProperty, const std::string &)) &Session::SetDeviceProperty). + def("set_device_property", (void (Session::*)(DeviceProperty, const ByteArray &)) &Session::SetDeviceProperty). + + def("abort_current_transaction", &Session::AbortCurrentTransaction, py::arg("timeout") = static_cast(Session::DefaultTimeout)) + ; + ; +} diff -Nru android-file-transfer-3.9+dfsg/python/CMakeLists.txt android-file-transfer-4.2/python/CMakeLists.txt --- android-file-transfer-3.9+dfsg/python/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/python/CMakeLists.txt 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,6 @@ +message(STATUS ${CMAKE_CURRENT_SOURCE_DIR}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}) +include(pybind11Tools) + +pybind11_add_module(aftl aftl.cpp) +target_link_libraries(aftl PRIVATE ${LIB_NAME}) diff -Nru android-file-transfer-3.9+dfsg/python/example/menu.py android-file-transfer-4.2/python/example/menu.py --- android-file-transfer-3.9+dfsg/python/example/menu.py 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/python/example/menu.py 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,91 @@ +#!/usr/bin/python3 + +# -*- coding: utf-8 -*- +""" +hierarchical prompt usage example +""" +from __future__ import print_function, unicode_literals +from PyInquirer import style_from_dict, Token, prompt, Separator + +import aftl + +def get_storage_prompt(session, s): + info = session.get_storage_info(s) + name = info.StorageDescription + if not name: + name = repr(s) + return { 'name': name, 'value': s } + +def get_storage(session): + storage_prompt = { + 'type': 'list', + 'name': 'storage', + 'message': 'Select storage', + 'choices': list(map(lambda s: get_storage_prompt(session, s), session.get_storage_ids())) + [{'name': 'Quit', 'value': None}] + } + answers = prompt(storage_prompt) + return answers.get('storage') + +def get_object_prompt(session, object): + info = session.get_object_info(object) + name = info.Filename + if info.ObjectFormat == aftl.ObjectFormat.Association: + name += '/' + return {'name': name, 'value': (object, info.ObjectFormat) } + +def get_object(session, storage, parent): + objects = session.get_object_handles(storage, aftl.ObjectFormat.Any, parent) + object_prompt = { + 'type': 'list', + 'name': 'object', + 'message': 'Browse objects', + 'choices': [{'name': '..', 'value': (None, None) }] + \ + list(map(lambda o: get_object_prompt(session, o), objects)) + } + answers = prompt(object_prompt) + return answers.get('object') + +def get_object_action(session): + action_prompt = { + 'type': 'expand', + 'name': 'action', + 'message': 'Object Action (press h for help)', + 'choices': [ + { 'key': 'b', 'name': 'Back', 'value': 'back' }, + Separator(), + { 'key': 'd', 'name': 'Download', 'value': 'download' }, + ] + } + return prompt(action_prompt).get('action') + +def main(): + session = aftl.Device.find_first().open_session() + while True: + storage = get_storage(session) + if storage is None: + break + + path = [aftl.Session.Root] + while path: + object, type = get_object(session, storage, path[-1]) + if type == aftl.ObjectFormat.Association: + path.append(object) + continue + + if object is None: + path.pop() + continue + + while True: + action = get_object_action(session) + if action == 'back': + break + + if action == 'download': + name = session.get_object_string_property(object, aftl.ObjectProperty.ObjectFilename) + with open(name, "wb") as f: + session.get_object(object, f) + + +if __name__ == '__main__': + main() diff -Nru android-file-transfer-3.9+dfsg/python/example/requirements.txt android-file-transfer-4.2/python/example/requirements.txt --- android-file-transfer-3.9+dfsg/python/example/requirements.txt 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/python/example/requirements.txt 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1 @@ +pyinquirer diff -Nru android-file-transfer-3.9+dfsg/python/FindPythonLibsNew.cmake android-file-transfer-4.2/python/FindPythonLibsNew.cmake --- android-file-transfer-3.9+dfsg/python/FindPythonLibsNew.cmake 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/python/FindPythonLibsNew.cmake 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,202 @@ +# - Find python libraries +# This module finds the libraries corresponding to the Python interpreter +# FindPythonInterp provides. +# This code sets the following variables: +# +# PYTHONLIBS_FOUND - have the Python libs been found +# PYTHON_PREFIX - path to the Python installation +# PYTHON_LIBRARIES - path to the python library +# PYTHON_INCLUDE_DIRS - path to where Python.h is found +# PYTHON_MODULE_EXTENSION - lib extension, e.g. '.so' or '.pyd' +# PYTHON_MODULE_PREFIX - lib name prefix: usually an empty string +# PYTHON_SITE_PACKAGES - path to installation site-packages +# PYTHON_IS_DEBUG - whether the Python interpreter is a debug build +# +# Thanks to talljimbo for the patch adding the 'LDVERSION' config +# variable usage. + +#============================================================================= +# Copyright 2001-2009 Kitware, Inc. +# Copyright 2012 Continuum Analytics, Inc. +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the names of Kitware, Inc., the Insight Software Consortium, +# nor the names of their contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +# Checking for the extension makes sure that `LibsNew` was found and not just `Libs`. +if(PYTHONLIBS_FOUND AND PYTHON_MODULE_EXTENSION) + return() +endif() + +# Use the Python interpreter to find the libs. +if(PythonLibsNew_FIND_REQUIRED) + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION} REQUIRED) +else() + find_package(PythonInterp ${PythonLibsNew_FIND_VERSION}) +endif() + +if(NOT PYTHONINTERP_FOUND) + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# According to http://stackoverflow.com/questions/646518/python-how-to-detect-debug-interpreter +# testing whether sys has the gettotalrefcount function is a reliable, cross-platform +# way to detect a CPython debug interpreter. +# +# The library suffix is from the config var LDVERSION sometimes, otherwise +# VERSION. VERSION will typically be like "2.7" on unix, and "27" on windows. +execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" + "from distutils import sysconfig as s;import sys;import struct; +print('.'.join(str(v) for v in sys.version_info)); +print(sys.prefix); +print(s.get_python_inc(plat_specific=True)); +print(s.get_python_lib(plat_specific=True)); +print(s.get_config_var('SO')); +print(hasattr(sys, 'gettotalrefcount')+0); +print(struct.calcsize('@P')); +print(s.get_config_var('LDVERSION') or s.get_config_var('VERSION')); +print(s.get_config_var('LIBDIR') or ''); +print(s.get_config_var('MULTIARCH') or ''); +" + RESULT_VARIABLE _PYTHON_SUCCESS + OUTPUT_VARIABLE _PYTHON_VALUES + ERROR_VARIABLE _PYTHON_ERROR_VALUE) + +if(NOT _PYTHON_SUCCESS MATCHES 0) + if(PythonLibsNew_FIND_REQUIRED) + message(FATAL_ERROR + "Python config failure:\n${_PYTHON_ERROR_VALUE}") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# Convert the process output into a list +if(WIN32) + string(REGEX REPLACE "\\\\" "/" _PYTHON_VALUES ${_PYTHON_VALUES}) +endif() +string(REGEX REPLACE ";" "\\\\;" _PYTHON_VALUES ${_PYTHON_VALUES}) +string(REGEX REPLACE "\n" ";" _PYTHON_VALUES ${_PYTHON_VALUES}) +list(GET _PYTHON_VALUES 0 _PYTHON_VERSION_LIST) +list(GET _PYTHON_VALUES 1 PYTHON_PREFIX) +list(GET _PYTHON_VALUES 2 PYTHON_INCLUDE_DIR) +list(GET _PYTHON_VALUES 3 PYTHON_SITE_PACKAGES) +list(GET _PYTHON_VALUES 4 PYTHON_MODULE_EXTENSION) +list(GET _PYTHON_VALUES 5 PYTHON_IS_DEBUG) +list(GET _PYTHON_VALUES 6 PYTHON_SIZEOF_VOID_P) +list(GET _PYTHON_VALUES 7 PYTHON_LIBRARY_SUFFIX) +list(GET _PYTHON_VALUES 8 PYTHON_LIBDIR) +list(GET _PYTHON_VALUES 9 PYTHON_MULTIARCH) + +# Make sure the Python has the same pointer-size as the chosen compiler +# Skip if CMAKE_SIZEOF_VOID_P is not defined +if(CMAKE_SIZEOF_VOID_P AND (NOT "${PYTHON_SIZEOF_VOID_P}" STREQUAL "${CMAKE_SIZEOF_VOID_P}")) + if(PythonLibsNew_FIND_REQUIRED) + math(EXPR _PYTHON_BITS "${PYTHON_SIZEOF_VOID_P} * 8") + math(EXPR _CMAKE_BITS "${CMAKE_SIZEOF_VOID_P} * 8") + message(FATAL_ERROR + "Python config failure: Python is ${_PYTHON_BITS}-bit, " + "chosen compiler is ${_CMAKE_BITS}-bit") + endif() + set(PYTHONLIBS_FOUND FALSE) + set(PythonLibsNew_FOUND FALSE) + return() +endif() + +# The built-in FindPython didn't always give the version numbers +string(REGEX REPLACE "\\." ";" _PYTHON_VERSION_LIST ${_PYTHON_VERSION_LIST}) +list(GET _PYTHON_VERSION_LIST 0 PYTHON_VERSION_MAJOR) +list(GET _PYTHON_VERSION_LIST 1 PYTHON_VERSION_MINOR) +list(GET _PYTHON_VERSION_LIST 2 PYTHON_VERSION_PATCH) + +# Make sure all directory separators are '/' +string(REGEX REPLACE "\\\\" "/" PYTHON_PREFIX "${PYTHON_PREFIX}") +string(REGEX REPLACE "\\\\" "/" PYTHON_INCLUDE_DIR "${PYTHON_INCLUDE_DIR}") +string(REGEX REPLACE "\\\\" "/" PYTHON_SITE_PACKAGES "${PYTHON_SITE_PACKAGES}") + +if(CMAKE_HOST_WIN32 AND NOT (MINGW AND DEFINED ENV{MSYSTEM})) + set(PYTHON_LIBRARY + "${PYTHON_PREFIX}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + + # when run in a venv, PYTHON_PREFIX points to it. But the libraries remain in the + # original python installation. They may be found relative to PYTHON_INCLUDE_DIR. + if(NOT EXISTS "${PYTHON_LIBRARY}") + get_filename_component(_PYTHON_ROOT ${PYTHON_INCLUDE_DIR} DIRECTORY) + set(PYTHON_LIBRARY + "${_PYTHON_ROOT}/libs/Python${PYTHON_LIBRARY_SUFFIX}.lib") + endif() + + # raise an error if the python libs are still not found. + if(NOT EXISTS "${PYTHON_LIBRARY}") + message(FATAL_ERROR "Python libraries not found") + endif() + +else() + if(PYTHON_MULTIARCH) + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}/${PYTHON_MULTIARCH}" "${PYTHON_LIBDIR}") + else() + set(_PYTHON_LIBS_SEARCH "${PYTHON_LIBDIR}") + endif() + #message(STATUS "Searching for Python libs in ${_PYTHON_LIBS_SEARCH}") + # Probably this needs to be more involved. It would be nice if the config + # information the python interpreter itself gave us were more complete. + find_library(PYTHON_LIBRARY + NAMES "python${PYTHON_LIBRARY_SUFFIX}" + PATHS ${_PYTHON_LIBS_SEARCH} + NO_DEFAULT_PATH) + + # If all else fails, just set the name/version and let the linker figure out the path. + if(NOT PYTHON_LIBRARY) + set(PYTHON_LIBRARY python${PYTHON_LIBRARY_SUFFIX}) + endif() +endif() + +MARK_AS_ADVANCED( + PYTHON_LIBRARY + PYTHON_INCLUDE_DIR +) + +# We use PYTHON_INCLUDE_DIR, PYTHON_LIBRARY and PYTHON_DEBUG_LIBRARY for the +# cache entries because they are meant to specify the location of a single +# library. We now set the variables listed by the documentation for this +# module. +SET(PYTHON_INCLUDE_DIRS "${PYTHON_INCLUDE_DIR}") +SET(PYTHON_LIBRARIES "${PYTHON_LIBRARY}") +SET(PYTHON_DEBUG_LIBRARIES "${PYTHON_DEBUG_LIBRARY}") + +find_package_message(PYTHON + "Found PythonLibs: ${PYTHON_LIBRARY}" + "${PYTHON_EXECUTABLE}${PYTHON_VERSION}") + +set(PYTHONLIBS_FOUND TRUE) +set(PythonLibsNew_FOUND TRUE) diff -Nru android-file-transfer-3.9+dfsg/python/pybind11Tools.cmake android-file-transfer-4.2/python/pybind11Tools.cmake --- android-file-transfer-3.9+dfsg/python/pybind11Tools.cmake 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/python/pybind11Tools.cmake 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,227 @@ +# tools/pybind11Tools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +# Add a CMake parameter for choosing a desired Python version +if(NOT PYBIND11_PYTHON_VERSION) + set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") +endif() + +set(Python_ADDITIONAL_VERSIONS 3.8 3.7 3.6 3.5 3.4) +find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) + +include(CheckCXXCompilerFlag) +include(CMakeParseArguments) + +if(NOT PYBIND11_CPP_STANDARD AND NOT CMAKE_CXX_STANDARD) + if(NOT MSVC) + check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) + + if (HAS_CPP14_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++14) + else() + check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) + if (HAS_CPP11_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++11) + else() + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() + endif() + elseif(MSVC) + set(PYBIND11_CPP_STANDARD /std:c++14) + endif() + + set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING + "C++ standard flag, e.g. -std=c++11, -std=c++14, /std:c++14. Defaults to C++14 mode." FORCE) +endif() + +# Checks whether the given CXX/linker flags can compile and link a cxx file. cxxflags and +# linkerflags are lists of flags to use. The result variable is a unique variable name for each set +# of flags: the compilation result will be cached base on the result variable. If the flags work, +# sets them in cxxflags_out/linkerflags_out internal cache variables (in addition to ${result}). +function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) + set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) + check_cxx_compiler_flag("${cxxflags}" ${result}) + if (${result}) + set(${cxxflags_out} "${cxxflags}" CACHE INTERNAL "" FORCE) + set(${linkerflags_out} "${linkerflags}" CACHE INTERNAL "" FORCE) + endif() +endfunction() + +# Internal: find the appropriate link time optimization flags for this compiler +function(_pybind11_add_lto_flags target_name prefer_thin_lto) + if (NOT DEFINED PYBIND11_LTO_CXX_FLAGS) + set(PYBIND11_LTO_CXX_FLAGS "" CACHE INTERNAL "") + set(PYBIND11_LTO_LINKER_FLAGS "" CACHE INTERNAL "") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(cxx_append "") + set(linker_append "") + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) + # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it + set(linker_append ";$<$:-O3>") + elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") + set(cxx_append ";-fno-fat-lto-objects") + endif() + + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND prefer_thin_lto) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO_THIN + "-flto=thin${cxx_append}" "-flto=thin${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (NOT HAS_FLTO_THIN) + _pybind11_return_if_cxx_and_linker_flags_work(HAS_FLTO + "-flto${cxx_append}" "-flto${linker_append}" + PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + elseif (CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Intel equivalent to LTO is called IPO + _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO + "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + elseif(MSVC) + # cmake only interprets libraries as linker flags when they start with a - (otherwise it + # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags + # with - instead of /, even if it is a bit non-standard: + _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG + "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) + endif() + + if (PYBIND11_LTO_CXX_FLAGS) + message(STATUS "LTO enabled") + else() + message(STATUS "LTO disabled (not supported by the compiler and/or linker)") + endif() + endif() + + # Enable LTO flags if found, except for Debug builds + if (PYBIND11_LTO_CXX_FLAGS) + target_compile_options(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_CXX_FLAGS}>") + endif() + if (PYBIND11_LTO_LINKER_FLAGS) + target_link_libraries(${target_name} PRIVATE "$<$>:${PYBIND11_LTO_LINKER_FLAGS}>") + endif() +endfunction() + +# Build a Python extension module: +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] +# [NO_EXTRAS] [SYSTEM] [THIN_LTO] source1 [source2 ...]) +# +function(pybind11_add_module target_name) + set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS SYSTEM THIN_LTO) + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + if(ARG_MODULE AND ARG_SHARED) + message(FATAL_ERROR "Can't be both MODULE and SHARED") + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() + + if(ARG_EXCLUDE_FROM_ALL) + set(exclude_from_all EXCLUDE_FROM_ALL) + endif() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) + + if(ARG_SYSTEM) + set(inc_isystem SYSTEM) + endif() + + target_include_directories(${target_name} ${inc_isystem} + PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt + PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config + PRIVATE ${PYTHON_INCLUDE_DIRS}) + + # Python debug libraries expose slightly different objects + # https://docs.python.org/3.6/c-api/intro.html#debugging-builds + # https://stackoverflow.com/questions/39161202/how-to-work-around-missing-pymodule-create2-in-amd64-win-python35-d-lib + if(PYTHON_IS_DEBUG) + target_compile_definitions(${target_name} PRIVATE Py_DEBUG) + endif() + + # The prefix and extension are provided by FindPythonLibsNew.cmake + set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + + # -fvisibility=hidden is required to allow multiple modules compiled against + # different pybind versions to work properly, and for some features (e.g. + # py::module_local). We force it on everything inside the `pybind11` + # namespace; also turning it on for a pybind module compilation here avoids + # potential warnings or issues from having mixed hidden/non-hidden types. + set_target_properties(${target_name} PROPERTIES CXX_VISIBILITY_PRESET "hidden") + set_target_properties(${target_name} PROPERTIES CUDA_VISIBILITY_PRESET "hidden") + + if(WIN32 OR CYGWIN) + # Link against the Python shared library on Windows + target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) + elseif(APPLE) + # It's quite common to have multiple copies of the same Python version + # installed on one's system. E.g.: one copy from the OS and another copy + # that's statically linked into an application like Blender or Maya. + # If we link our plugin library against the OS Python here and import it + # into Blender or Maya later on, this will cause segfaults when multiple + # conflicting Python instances are active at the same time (even when they + # are of the same version). + + # Windows is not affected by this issue since it handles DLL imports + # differently. The solution for Linux and Mac OS is simple: we just don't + # link against the Python library. The resulting shared library will have + # missing symbols, but that's perfectly fine -- they will be resolved at + # import time. + + target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") + + if(ARG_SHARED) + # Suppress CMake >= 3.0 warning for shared libraries + set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) + endif() + endif() + + # Make sure C++11/14 are enabled + if(CMAKE_VERSION VERSION_LESS 3.3) + target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + else() + target_compile_options(${target_name} PUBLIC $<$:${PYBIND11_CPP_STANDARD}>) + endif() + + if(ARG_NO_EXTRAS) + return() + endif() + + _pybind11_add_lto_flags(${target_name} ${ARG_THIN_LTO}) + + if (NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/Mac OS + if(CMAKE_STRIP) + if(APPLE) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} -x $) + else() + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} $) + endif() + endif() + endif() + + if(MSVC) + # /MP enables multithreaded builds (relevant when there are many files), /bigobj is + # needed for bigger binding projects due to the limit to 64k addressable sections + target_compile_options(${target_name} PRIVATE /bigobj) + if(CMAKE_VERSION VERSION_LESS 3.11) + target_compile_options(${target_name} PRIVATE $<$>:/MP>) + else() + # Only set these options for C++ files. This is important so that, for + # instance, projects that include other types of source files like CUDA + # .cu files don't get these options propagated to nvcc since that would + # cause the build to fail. + target_compile_options(${target_name} PRIVATE $<$>:$<$:/MP>>) + endif() + endif() +endfunction() diff -Nru android-file-transfer-3.9+dfsg/qt/android-file-transfer.qrc android-file-transfer-4.2/qt/android-file-transfer.qrc --- android-file-transfer-3.9+dfsg/qt/android-file-transfer.qrc 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/android-file-transfer.qrc 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,13 @@ android-file-transfer.png - translations/android-file-transfer-linux_ru.qm + icons/dark/folder-new.svg + icons/dark/go-next.svg + icons/dark/go-previous.svg + icons/dark/view-refresh.svg + icons/light/folder-new.svg + icons/light/go-next.svg + icons/light/go-previous.svg + icons/light/view-refresh.svg diff -Nru android-file-transfer-3.9+dfsg/qt/CMakeLists.txt android-file-transfer-4.2/qt/CMakeLists.txt --- android-file-transfer-3.9+dfsg/qt/CMakeLists.txt 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/CMakeLists.txt 2020-12-29 16:10:28.000000000 +0000 @@ -1,13 +1,15 @@ -project(mtp-qt) - if(DESIRED_QT_VERSION) if(DESIRED_QT_VERSION MATCHES 4) find_package(Qt4 QUIET) else() find_package(Qt5Widgets QUIET) + find_package(Qt5Network QUIET) + find_package(Qt5LinguistTools QUIET) endif() else() find_package(Qt5Widgets QUIET) + find_package(Qt5Network QUIET) + find_package(Qt5LinguistTools QUIET) find_package(Qt4 QUIET) endif() @@ -20,7 +22,9 @@ progressdialog.cpp createdirectorydialog.cpp renamedialog.cpp + devicesdialog.cpp qtobjectstream.cpp + utils.cpp ) set(HEADERS mainwindow.h @@ -30,6 +34,7 @@ progressdialog.h createdirectorydialog.h renamedialog.h + devicesdialog.h qtobjectstream.h ) @@ -37,74 +42,131 @@ progressdialog.ui createdirectorydialog.ui renamedialog.ui + devicesdialog.ui +) + +set(TS_FILES + translations/android-file-transfer-linux_cs.ts + translations/android-file-transfer-linux_it.ts + translations/android-file-transfer-linux_nl.ts + translations/android-file-transfer-linux_ru.ts ) if (Qt5Widgets_FOUND) message(STATUS "Using Qt5") qt5_wrap_cpp(HEADERS_MOC ${HEADERS}) qt5_wrap_ui(FORMS_HEADERS ${FORMS}) - qt5_add_resources(RESOURCES android-file-transfer.qrc) - include_directories(${Qt5Widgets_INCLUDE_DIRS}) - set(EXTRA_QT_LINK ${Qt5Widgets_LIBRARIES}) + + qt5_add_translation(QM_FILES ${TS_FILES}) + set(QM_FILES_XML "") + foreach(_QM ${QM_FILES}) + get_filename_component(_QM ${_QM} NAME) + set(QM_FILES_XML "${QM_FILES_XML}${_QM}") + endforeach() + configure_file(translations/translations.qrc.in translations.qrc) + + qt5_add_resources(RESOURCES android-file-transfer.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) + include_directories(${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS}) + set(EXTRA_QT_LINK ${Qt5Widgets_LIBRARIES} ${Qt5Network_LIBRARIES}) elseif(Qt4_FOUND OR QT_FOUND OR QT4_FOUND) message(STATUS "Using Qt4") qt4_wrap_cpp(HEADERS_MOC ${HEADERS}) qt4_wrap_ui(FORMS_HEADERS ${FORMS}) - qt4_add_resources(RESOURCES android-file-transfer.qrc) + + qt4_add_translation(QM_FILES ${TS_FILES}) + set(QM_FILES_XML "") + foreach(_QM ${QM_FILES}) + get_filename_component(_QM ${_QM} NAME) + set(QM_FILES_XML "${QM_FILES_XML}${_QM}") + endforeach() + configure_file(translations/translations.qrc.in translations.qrc) + + qt4_add_resources(RESOURCES android-file-transfer.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) include(${QT_USE_FILE}) - set(EXTRA_QT_LINK ${QT_LIBRARIES}) + set(EXTRA_QT_LINK Qt4::QtNetwork ${QT_LIBRARIES}) else() message(WARNING "No Qt found") set(BUILD_QT_UI FALSE) endif() -if (BUILD_QT_UI) - set(APP_NAME "android-file-transfer") +if (BUILD_MTPZ) + set(MTPZ_DATA_SOURCE "\"https://raw.githubusercontent.com/kbhomes/libmtp-zune/master/src/.mtpz-data\"") + add_definitions(-DMTPZ_DATA_SOURCE=${MTPZ_DATA_SOURCE}) +endif() +if (BUILD_QT_UI) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - set(MACOSX_BUNDLE_INFO_STRING "${APP_NAME} ${PROJECT_VERSION}") - set(MACOSX_BUNDLE_BUNDLE_VERSION "${APP_NAME} ${PROJECT_VERSION}") - set(MACOSX_BUNDLE_LONG_VERSION_STRING "${APP_NAME} ${PROJECT_VERSION}") - set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") - set(MACOSX_BUNDLE_COPYRIGHT "2015-2018 Vladimir Menshakov") - set(MACOSX_BUNDLE_ICON_FILE "android-file-transfer.icns") - set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.android-file-transfer") - set(MACOSX_BUNDLE_BUNDLE_NAME "${APP_NAME}") - set(MACOSX_BUNDLE_RESOURCES "${CMAKE_CURRENT_BINARY_DIR}/${APP_NAME}.app/Contents/Resources") - execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${MACOSX_BUNDLE_RESOURCES}) + set(MACOSX_BUNDLE_INFO_STRING "${CMAKE_PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_BUNDLE_VERSION "${AFT_BASE_VERSION}") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${CMAKE_PROJECT_NAME} ${PROJECT_VERSION}") + set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + set(MACOSX_BUNDLE_COPYRIGHT "2015-2020 Vladimir Menshakov") + set(MACOSX_BUNDLE_ICON_FILE "android-file-transfer.icns") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "io.github.whoozle.android-file-transfer-linux") + set(MACOSX_BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_NAME}") + set(MACOSX_BUNDLE_EXECUTABLE_NAME "${CMAKE_PROJECT_NAME}") + set(MACOSX_BUNDLE_RESOURCES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.app/Contents/Resources") + execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${MACOSX_BUNDLE_RESOURCES}) + set(MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) + message(STATUS "generated bundle info is in ${MACOSX_BUNDLE_INFO_PLIST}") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in ${MACOSX_BUNDLE_INFO_PLIST}) - set(APPLICATION_ICON ${CMAKE_CURRENT_SOURCE_DIR}/../osx/android-file-transfer.icns) - set_source_files_properties(${APPLICATION_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + set(APPLICATION_ICON ${CMAKE_SOURCE_DIR}/osx/android-file-transfer.icns) + set_source_files_properties(${APPLICATION_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") else() - set(APPLICATION_ICON "") + set(APPLICATION_ICON "") endif() add_definitions(${QT_DEFINITIONS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) - add_executable("${APP_NAME}" MACOSX_BUNDLE ${APPLICATION_ICON} ${SOURCES} ${HEADERS_MOC} ${FORMS_HEADERS} ${RESOURCES}) - target_link_libraries("${APP_NAME}" ${EXTRA_QT_LINK} ${MTP_LIBRARIES}) - - install(TARGETS ${APP_NAME} - RUNTIME DESTINATION bin - BUNDLE DESTINATION .) + add_executable("${CMAKE_PROJECT_NAME}" MACOSX_BUNDLE ${APPLICATION_ICON} ${SOURCES} ${HEADERS_MOC} ${FORMS_HEADERS} ${RESOURCES}) + set_property(TARGET ${CMAKE_PROJECT_NAME} PROPERTY MACOSX_BUNDLE_INFO_PLIST "${MACOSX_BUNDLE_INFO_PLIST}") + target_link_libraries("${CMAKE_PROJECT_NAME}" ${EXTRA_QT_LINK} ${MTP_LIBRARIES}) + + install(TARGETS ${CMAKE_PROJECT_NAME} + RUNTIME DESTINATION bin + BUNDLE DESTINATION .) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - if (Qt5Widgets_FOUND) - set(MACDEPLOYQT_TOOL ${Qt5Widgets_DIR}/../../../bin/macdeployqt) - else() - get_filename_component(QT_BIN_DIR ${QT_QMAKE_EXECUTABLE} PATH) - set(MACDEPLOYQT_TOOL ${QT_BIN_DIR}) - endif() - message(STATUS "Macdeployqt path: ${MACDEPLOYQT_TOOL}") - install (CODE " - message(STATUS \"Running deploy Qt tool: ${MACDEPLOYQT_TOOL}\") - execute_process(COMMAND \"${MACDEPLOYQT_TOOL}\" ${CMAKE_INSTALL_PREFIX}/android-file-transfer.app ERROR_QUIET) - execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -add_rpath @executable_path/../Frameworks \"${CMAKE_INSTALL_PREFIX}/${APP_NAME}.app/Contents/MacOS/${APP_NAME}\") - " COMPONENT Runtime) + set(MACOSX_BUNDLE_APP_PATH "${MACOSX_BUNDLE_ROOT_DIR}/Contents/MacOS/${CMAKE_PROJECT_NAME}") + + add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD + COMMAND ${OTOOL_BIN} -L "$" + ) + if (Qt5Widgets_FOUND) + set(MACDEPLOYQT_TOOL ${Qt5Widgets_DIR}/../../../bin/macdeployqt) + else() + get_filename_component(QT_BIN_DIR ${QT_QMAKE_EXECUTABLE} PATH) + set(MACDEPLOYQT_TOOL ${QT_BIN_DIR}) + endif() + message(STATUS "Macdeployqt path: ${MACDEPLOYQT_TOOL}") + + if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $) + set(MACOSX_BUNDLE_LIBS_PRE_INSTALL) + foreach(DYNLIB ${MACOSX_BUNDLE_LIBS}) + message(STATUS "additional bundle lib ${DYNLIB}") + get_filename_component(DYNLIB_NAME "${DYNLIB}" NAME) + set(MACOSX_BUNDLE_LIBS_PRE_INSTALL " + ${MACOSX_BUNDLE_LIBS_PRE_INSTALL} + file(INSTALL \"${DYNLIB}\" DESTINATION \"${MACOSX_BUNDLE_ROOT_DIR}/Contents/Frameworks\" FOLLOW_SYMLINK_CHAIN) + message(STATUS \"qt: fixing ${DYNLIB_NAME}\") + execute_process(COMMAND \"${CMAKE_INSTALL_NAME_TOOL}\" -change \"${DYNLIB_NAME}\" \"@executable_path/../Frameworks/${DYNLIB_NAME}\" \"${MACOSX_BUNDLE_APP_PATH}\") + ") + endforeach() + endif() + + install (CODE " + message(STATUS \"Running deploy Qt tool: ${MACDEPLOYQT_TOOL}\") + execute_process(COMMAND \"${MACDEPLOYQT_TOOL}\" ${MACOSX_BUNDLE_ROOT_DIR}) + ${MACOSX_BUNDLE_LIBS_PRE_INSTALL} + ${MACOSX_BUNDLE_LIBS_INSTALL} + execute_process(COMMAND \"${OTOOL_BIN}\" -L \"${MACOSX_BUNDLE_APP_PATH}\") + " COMPONENT Runtime) else() - install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.desktop DESTINATION share/applications) - install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.appdata.xml DESTINATION share/metainfo) - install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.png DESTINATION share/icons/hicolor/512x512/apps) + install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.desktop DESTINATION share/applications) + install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.appdata.xml DESTINATION share/metainfo) + install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/android-file-transfer.png DESTINATION share/icons/hicolor/512x512/apps) endif() endif() diff -Nru android-file-transfer-3.9+dfsg/qt/commandqueue.cpp android-file-transfer-4.2/qt/commandqueue.cpp --- android-file-transfer-3.9+dfsg/qt/commandqueue.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/commandqueue.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -20,12 +20,17 @@ #include "commandqueue.h" #include "mtpobjectsmodel.h" #include "utils.h" + #include #include #include #include #include +#include +#include +#include + void FinishQueue::execute(CommandQueue &queue) { queue.finish(DirectoryId); } @@ -38,6 +43,44 @@ void DownloadFile::execute(CommandQueue &queue) { queue.downloadFile(Filename, ObjectId); } +void ImportFile::execute(CommandQueue &queue) +{ queue.importFile(Filename); } + +void LoadLibrary::execute(CommandQueue &queue) +{ queue.loadLibrary(); } + +void CommandQueue::loadLibrary() +{ + using namespace mtp; + auto session = _model->session(); + qDebug() << "loading media library..."; + _library.reset(); + auto reporter = [&](Library::State state , u64 c , u64 t) + { + qDebug() << "progress " << static_cast(state) << ", " << c << " of "<< t; + switch(state) + { + case Library::State::Initialising: start(tr("Loading media library…")); break; + case Library::State::QueryingArtists: start(tr("Querying artists…")); break; + case Library::State::LoadingArtists: start(tr("Loading artists…")); break; + case Library::State::QueryingAlbums: start(tr("Querying albums…")); break; + case Library::State::LoadingAlbums: start(tr("Loading albums…")); break; + case Library::State::Loaded: start(tr("Done")); break; + } + + if (t) + emit total(t); + + if (c) + emit progress(c); + }; + + try + { _library = std::make_shared(session, reporter); } + catch (const std::exception & ex) + { qWarning() << "loading media library failed: " << ex.what(); } +} + void CommandQueue::downloadFile(const QString &filename, mtp::ObjectId objectId) { if (_aborted) @@ -91,6 +134,97 @@ addProgress(fi.size()); } +void CommandQueue::importFile(const QString &filename) +{ + if (_aborted || !_library) + return; + + QFileInfo fi(filename); + + mtp::scope_guard r([this, &fi]() { addProgress(fi.size()); }); + std::string utfFilename = toUtf8(filename); + mtp::ObjectFormat format = mtp::ObjectFormatFromFilename(utfFilename); + + if (mtp::IsImageFormat(format)) + { + qDebug() << "image: " << filename; + int score = GetCoverScore(fi.baseName()); + qDebug() << "image cover score: " << score << " for " << fi.baseName(); + auto dir = fi.dir().path(); + auto it = _covers.find(dir); + if (it == _covers.end()) { + Cover cover; + cover.Score = score; + cover.Path = filename; + _covers.insert(std::make_pair(dir, std::move(cover))); + } else if (score > it->second.Score) { + it->second.Score = score; + it->second.Path = filename; + } + + return; + } + + if (!mtp::IsAudioFormat(format)) + return; + + auto metadata = mtp::Metadata::Read(utfFilename); + if (!metadata) + return; + + qDebug() << "import: " << filename << + ", format: " << fromUtf8(mtp::ToString(format)) << + ", artist: " << fromUtf8(metadata->Artist) << + ", album: " << fromUtf8(metadata->Album) << + ", title: " << fromUtf8(metadata->Title) << + ", year: " << metadata->Year << + ", track: " << metadata->Track << + ", genre: " << fromUtf8(metadata->Genre) << + ", size: " << fi.size(); + + + auto artist = _library->GetArtist(metadata->Artist); + if (!artist) + artist = _library->CreateArtist(metadata->Artist); + if (!artist) + { + qDebug() << "can't create artist"; + return; + } + + auto album = _library->GetAlbum(artist, metadata->Album); + if (!album) + album = _library->CreateAlbum(artist, metadata->Album, metadata->Year); + if (!album) + { + qDebug() << "can't create album"; + return; + } + + auto dir = fi.dir().path(); + if (_albums.find(dir)== _albums.end()) + { + qDebug() << "registering " << dir << " as a path to album"; + _albums.insert(std::make_pair(dir, album)); + } + + if (_library->HasTrack(album, metadata->Title, metadata->Track)) { + qDebug() << "skipping" << filename << ", already uploaded"; + return; + } + + auto songId = _library->CreateTrack( + artist, album, + format, + metadata->Title, metadata->Genre, + metadata->Track, toUtf8(fi.fileName()), fi.size()); + + start(fi.fileName()); + _model->sendFile(filename); + + _library->AddTrack(album, songId); +} + void CommandQueue::createDirectory(const QString &srcPath) { if (_aborted) @@ -133,10 +267,16 @@ qDebug() << "upload worker stopped"; } +mtp::LibraryPtr CommandQueue::library() const +{ return _library; } + void CommandQueue::execute(Command *ptr) { std::unique_ptr cmd(ptr); - cmd->execute(*this); + try + { cmd->execute(*this); } + catch(const std::exception & ex) + { qWarning() << "exception in command queue: " << ex.what(); } } void CommandQueue::start(const QString &filename) @@ -153,9 +293,49 @@ } catch(const std::exception &ex) { qDebug() << "finalizing commands failed: " << fromUtf8(ex.what()); } + for(auto & akv : _albums) + { + auto & albumPath = akv.first; + auto & album = akv.second; + + if (_aborted) + break; + + QString albumName = fromUtf8(akv.second->Name); + qDebug() << "looking for a cover for album " << albumName; + + QString bestPath; + for(auto & ckv : _covers) + { + auto & cover = ckv.second; + + if (cover.Path.startsWith(albumPath) && albumPath.size() > bestPath.size()) { + bestPath = cover.Path; + } + } + + if (bestPath.isEmpty()) + continue; + + emit started(tr("Setting cover for album %1").arg(albumName)); + + QFile file(bestPath); + qDebug() << "setting cover from " << bestPath; + if (!file.open(QIODevice::ReadOnly)) + continue; + + auto buffer = file.readAll(); + mtp::ByteArray value(buffer.begin(), buffer.end()); + try { _model->session()->SetObjectPropertyAsArray(album->Id, mtp::ObjectProperty::RepresentativeSampleData, value); } + catch(const std::exception & ex) + { qWarning() << "setting cover failed: " << ex.what(); } + } + _covers.clear(); + _model->moveToThread(QApplication::instance()->thread()); _completedFilesSize = 0; _directories.clear(); + _albums.clear(); _aborted = false; emit finished(); } diff -Nru android-file-transfer-3.9+dfsg/qt/commandqueue.h android-file-transfer-4.2/qt/commandqueue.h --- android-file-transfer-3.9+dfsg/qt/commandqueue.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/commandqueue.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,18 +17,25 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef COMMANDQUEUE_H -#define COMMANDQUEUE_H +#ifndef AFTL_QT_COMMANDQUEUE_H +#define AFTL_QT_COMMANDQUEUE_H #include #include #include #include #include +#include class MtpObjectsModel; class CommandQueue; +namespace mtp +{ + class Library; + DECLARE_PTR(Library); +} + struct Command { virtual ~Command() = default; @@ -62,6 +69,12 @@ void execute(CommandQueue &queue); }; +struct ImportFile : public FileCommand +{ + ImportFile(const QString &filename) : FileCommand(filename) { } + void execute(CommandQueue &queue); +}; + struct DownloadFile : public FileCommand { mtp::ObjectId ObjectId; @@ -70,6 +83,11 @@ void execute(CommandQueue &queue); }; +struct LoadLibrary : public Command +{ + void execute(CommandQueue &queue); +}; + class CommandQueue: public QObject { Q_OBJECT @@ -78,6 +96,16 @@ MtpObjectsModel * _model; qint64 _completedFilesSize; QMap _directories; + std::map _albums; + + struct Cover + { + QString Path; + int Score; + }; + + std::map _covers; + mtp::LibraryPtr _library; volatile bool _aborted; public: @@ -87,9 +115,13 @@ MtpObjectsModel *model() const { return _model; } + mtp::LibraryPtr library() const; + + void loadLibrary(); void createDirectory(const QString &path); void uploadFile(const QString &file); void downloadFile(const QString &filename, mtp::ObjectId objectId); + void importFile(const QString &file); public slots: void onFileProgress(qint64, qint64); @@ -102,6 +134,7 @@ signals: void started(QString); void progress(qint64 bytes); + void total(qint64 bytes); void finished(); }; diff -Nru android-file-transfer-3.9+dfsg/qt/createdirectorydialog.cpp android-file-transfer-4.2/qt/createdirectorydialog.cpp --- android-file-transfer-3.9+dfsg/qt/createdirectorydialog.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/createdirectorydialog.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/qt/createdirectorydialog.h android-file-transfer-4.2/qt/createdirectorydialog.h --- android-file-transfer-3.9+dfsg/qt/createdirectorydialog.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/createdirectorydialog.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef CREATEDIRECTORYDIALOG_H -#define CREATEDIRECTORYDIALOG_H +#ifndef AFTL_QT_CREATEDIRECTORYDIALOG_H +#define AFTL_QT_CREATEDIRECTORYDIALOG_H #include diff -Nru android-file-transfer-3.9+dfsg/qt/devicesdialog.cpp android-file-transfer-4.2/qt/devicesdialog.cpp --- android-file-transfer-3.9+dfsg/qt/devicesdialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/devicesdialog.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,177 @@ +#include "devicesdialog.h" +#include "ui_devicesdialog.h" +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include + +DevicesDialog::DevicesDialog(bool resetDevice, QWidget *parent) : + QDialog(parent), + _resetDevice(resetDevice), + ui(new Ui::DevicesDialog) +{ + ui->setupUi(this); + _buttonScan = ui->buttonBox->addButton(tr("Rescan Devices"), QDialogButtonBox::ActionRole); + _buttonKill = ui->buttonBox->addButton(tr("Kill Users"), QDialogButtonBox::ActionRole); + connect(ui->listWidget, SIGNAL(itemClicked(QListWidgetItem *)), SLOT(itemClicked(QListWidgetItem *))); + connect(ui->listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), SLOT(itemDoubleClicked(QListWidgetItem *))); + connect(_buttonScan, SIGNAL(clicked()), SLOT(scan())); + connect(_buttonKill, SIGNAL(clicked()), SLOT(kill())); +} + +mtp::DevicePtr DevicesDialog::getDevice() +{ + int row = ui->listWidget->currentRow(); + if (row >= 0 && row < static_cast(_devices.size())) + return _devices[row].Device; + else + return nullptr; +} + +void DevicesDialog::kill() +{ + qDebug("kill"); + + int index = ui->listWidget->currentRow(); + if (index < 0 && index >= static_cast(_devices.size())) + return; + + auto & row = _devices[index]; + + bool canKill = !row.Processes.empty(); + QString processList; + for (auto & desc : row.Processes) + { + processList += QString("%1 (pid: %2)\n").arg(fromUtf8(desc.Name)).arg(desc.Id); + } + if (canKill) + processList = tr("The following processes are keeping file descriptors for your device:\n") + processList; + + QMessageBox dialog( + QMessageBox::Warning, + tr("Device is busy"), + tr("Device is busy, maybe another process is using it.\n\n") + + processList + + tr("Close other MTP applications and restart Android File Transfer.\n" + "\nPress Abort to kill them or Ignore to try next device."), + (canKill? QMessageBox::Ok: QMessageBox::StandardButton(0)) | QMessageBox::Cancel, + this + ); + + dialog.setDefaultButton(QMessageBox::Ignore); + dialog.setEscapeButton(QMessageBox::Ignore); + auto r = dialog.exec(); + + if ((r & QMessageBox::Ok) == QMessageBox::Ok) + { + qDebug("kill'em all"); + mtp::usb::DeviceBusyException::Kill(row.Processes); + scan(); + } +} + +void DevicesDialog::scan() +{ + qDebug("scan"); + bool claimInterface = true; + bool resetDevice = _resetDevice; + + _devices.clear(); + + mtp::usb::ContextPtr ctx(new mtp::usb::Context); + + auto devices = ctx->GetDevices(); + for (auto desc = devices.begin(); desc != devices.end(); ++desc) + { + qDebug("probing device..."); + try + { + auto device = mtp::Device::Open(ctx, *desc, claimInterface, resetDevice); + _devices.push_back({ *desc, device, {} }); + } + catch(const mtp::usb::DeviceBusyException &ex) + { + if (!ex.Processes.empty()) + _devices.push_back(Row { *desc, nullptr, ex.Processes }); + } + catch(const std::exception &ex) + { qWarning("Device::Find failed: %s", ex.what()); } + } + + std::stable_sort(_devices.begin(), _devices.end(), [](const Row & a, const Row & b) { + return a.Device > b.Device; + }); + + auto it = std::remove_if(_devices.begin(), _devices.end(), [](const Row & row) { + return !row.Device && row.Processes.empty(); + }); + _devices.erase(it, _devices.end()); + + ui->listWidget->clear(); + for(auto & row : _devices) + { + QString name; + if (row.Device) + { + auto info = row.Device->GetInfo(); + name = QString("%1 %2 %3") + .arg(fromUtf8(info.Manufacturer)) + .arg(fromUtf8(info.Model)) + .arg(fromUtf8(info.SerialNumber)); + } + else + { + auto & desc = row.Descriptor; + name = QString(tr("USB Device %1:%2")) + .arg(static_cast(desc->GetVendorId()), 4, 16, QChar('0')) + .arg(static_cast(desc->GetProductId()), 4, 16, QChar('0')); + } + ui->listWidget->addItem(name); + } + + if (!_devices.empty()) + ui->listWidget->setCurrentRow(0); + + updateButtons(); +} + + +int DevicesDialog::exec() +{ + scan(); + + if (_devices.empty()) + return QDialog::Rejected; + + if (_devices.size() == 1 && _devices.front().Device) + { + return QDialog::Accepted; + } + + return QDialog::exec(); +} + +void DevicesDialog::updateButtons() +{ + _buttonKill->setEnabled(false); + int row = ui->listWidget->currentRow(); + if (row >= 0 && row < static_cast(_devices.size())) + { + _buttonKill->setEnabled(!_devices[row].Processes.empty()); + } +} + +void DevicesDialog::itemClicked(QListWidgetItem *) +{ updateButtons(); } + +void DevicesDialog::itemDoubleClicked(QListWidgetItem *) +{ updateButtons(); accept(); } + +DevicesDialog::~DevicesDialog() +{ + delete ui; +} diff -Nru android-file-transfer-3.9+dfsg/qt/devicesdialog.h android-file-transfer-4.2/qt/devicesdialog.h --- android-file-transfer-3.9+dfsg/qt/devicesdialog.h 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/devicesdialog.h 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,51 @@ +#ifndef DEVICESDIALOG_H +#define DEVICESDIALOG_H + +#include +#include +#include +#include + +namespace Ui { +class DevicesDialog; +} +class QListWidgetItem; + +class DevicesDialog : public QDialog +{ + Q_OBJECT + +private: + struct Row + { + mtp::usb::DeviceDescriptorPtr Descriptor; + mtp::DevicePtr Device; + std::vector Processes; + }; + + bool _resetDevice; + std::vector _devices; + +public: + explicit DevicesDialog(bool resetDevice, QWidget *parent = nullptr); + ~DevicesDialog(); + + mtp::DevicePtr getDevice(); + + int exec() override; + +private slots: + void scan(); + void kill(); + void itemClicked(QListWidgetItem *); + void itemDoubleClicked(QListWidgetItem *); + void updateButtons(); + +private: + Ui::DevicesDialog * ui; + + QPushButton * _buttonScan; + QPushButton * _buttonKill; +}; + +#endif // DEVICESDIALOG_H diff -Nru android-file-transfer-3.9+dfsg/qt/devicesdialog.ui android-file-transfer-4.2/qt/devicesdialog.ui --- android-file-transfer-3.9+dfsg/qt/devicesdialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/devicesdialog.ui 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,67 @@ + + + DevicesDialog + + + + 0 + 0 + 615 + 447 + + + + Dialog + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DevicesDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DevicesDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/fileuploader.cpp android-file-transfer-4.2/qt/fileuploader.cpp --- android-file-transfer-3.9+dfsg/qt/fileuploader.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/fileuploader.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -37,6 +37,7 @@ connect(&_workerThread, SIGNAL(finished()), SLOT(deleteLater())); connect(this, SIGNAL(executeCommand(Command*)), _worker, SLOT(execute(Command*))); connect(_worker, SIGNAL(progress(qint64)), SLOT(onProgress(qint64))); + connect(_worker, SIGNAL(total(qint64)), SLOT(onTotal(qint64))); connect(_worker, SIGNAL(started(QString)), SLOT(onStarted(QString))); connect(_worker, SIGNAL(finished()), SLOT(onFinished())); _workerThread.start(); @@ -48,6 +49,21 @@ _workerThread.wait(); } +void FileUploader::tryCreateLibrary() +{ + mtp::ObjectId currentParentId = _model->parentObjectId(); + emit executeCommand(new LoadLibrary()); + emit executeCommand(new FinishQueue(currentParentId)); +} + +mtp::LibraryPtr FileUploader::library() const +{ return _worker? _worker->library(): nullptr; } + +void FileUploader::onTotal(qint64 total) +{ + _total = total; +} + void FileUploader::onProgress(qint64 current) { //qDebug() << "progress " << current << " of " << _total; @@ -103,6 +119,7 @@ else if (fi.isDir()) { commands.push_back(new MakeDirectory(next)); + files.push_back(next); } } } @@ -179,6 +196,64 @@ emit executeCommand(new FinishQueue(currentParentId)); } +void FileUploader::importMusic(const QString & path) +{ + qDebug() << "importMusic " << path; + + _model->moveToThread(&_workerThread); + _total = 0; + + QStringList files; + files.push_back(path); + + QList commands; + while(!files.empty()) + { + QString currentFile = files.front(); + files.pop_front(); + QFileInfo currentFileInfo(currentFile); + if (currentFileInfo.isDir()) + { + qDebug() << "going into subdirectory" << currentFile; + QDirIterator it(currentFile, QDirIterator::Subdirectories); + QStringList dirs; + while(it.hasNext()) + { + QString next = it.next(); + QFileInfo fi(next); + QString filename = fi.fileName(); + if (filename == "." || filename == "..") + continue; + + if (fi.isFile()) + files.push_back(next); + else if (fi.isDir()) + dirs.push_back(next); + } + files += dirs; + } + else if (currentFileInfo.isFile()) + { + commands.push_back(new ImportFile(currentFile)); + _total += currentFileInfo.size(); + } + } + qDebug() << "uploading" << _total << "bytes"; + if (_total < 1) + _total = 1; + + _startedAt = QDateTime::currentDateTime(); + _aborted = false; + + for(auto command: commands) + { + if (_aborted) + break; + emit executeCommand(command); + } + emit executeCommand(new FinishQueue(mtp::Session::Root)); +} + void FileUploader::abort() { qDebug() << "abort request"; diff -Nru android-file-transfer-3.9+dfsg/qt/fileuploader.h android-file-transfer-4.2/qt/fileuploader.h --- android-file-transfer-3.9+dfsg/qt/fileuploader.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/fileuploader.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,12 +17,14 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef FILEUPLOADER_H -#define FILEUPLOADER_H +#ifndef AFTL_QT_FILEUPLOADER_H +#define AFTL_QT_FILEUPLOADER_H #include #include #include +#include +#include class MtpObjectsModel; struct Command; @@ -31,6 +33,8 @@ namespace mtp { struct ObjectId; + class Library; + DECLARE_PTR(Library); } class FileUploader : public QObject @@ -46,6 +50,7 @@ bool _aborted; private slots: + void onTotal(qint64 total); void onProgress(qint64 current); void onStarted(const QString &file); void onFinished(); @@ -54,7 +59,11 @@ explicit FileUploader(MtpObjectsModel * model, QObject *parent = 0); ~FileUploader(); + void tryCreateLibrary(); + mtp::LibraryPtr library() const; + void upload(QStringList files); + void importMusic(const QString & path); void download(const QString &path, const QVector & objectIds); public slots: diff -Nru android-file-transfer-3.9+dfsg/qt/icons/dark/folder-new.svg android-file-transfer-4.2/qt/icons/dark/folder-new.svg --- android-file-transfer-3.9+dfsg/qt/icons/dark/folder-new.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/dark/folder-new.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/dark/go-next.svg android-file-transfer-4.2/qt/icons/dark/go-next.svg --- android-file-transfer-3.9+dfsg/qt/icons/dark/go-next.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/dark/go-next.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,10 @@ + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/dark/go-previous.svg android-file-transfer-4.2/qt/icons/dark/go-previous.svg --- android-file-transfer-3.9+dfsg/qt/icons/dark/go-previous.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/dark/go-previous.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,10 @@ + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/dark/view-refresh.svg android-file-transfer-4.2/qt/icons/dark/view-refresh.svg --- android-file-transfer-3.9+dfsg/qt/icons/dark/view-refresh.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/dark/view-refresh.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/light/folder-new.svg android-file-transfer-4.2/qt/icons/light/folder-new.svg --- android-file-transfer-3.9+dfsg/qt/icons/light/folder-new.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/light/folder-new.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,18 @@ + + + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/light/go-next.svg android-file-transfer-4.2/qt/icons/light/go-next.svg --- android-file-transfer-3.9+dfsg/qt/icons/light/go-next.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/light/go-next.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,10 @@ + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/light/go-previous.svg android-file-transfer-4.2/qt/icons/light/go-previous.svg --- android-file-transfer-3.9+dfsg/qt/icons/light/go-previous.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/light/go-previous.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,10 @@ + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/icons/light/view-refresh.svg android-file-transfer-4.2/qt/icons/light/view-refresh.svg --- android-file-transfer-3.9+dfsg/qt/icons/light/view-refresh.svg 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/icons/light/view-refresh.svg 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,8 @@ + + + + + + diff -Nru android-file-transfer-3.9+dfsg/qt/Info.plist.in android-file-transfer-4.2/qt/Info.plist.in --- android-file-transfer-3.9+dfsg/qt/Info.plist.in 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/Info.plist.in 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,48 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + Android File Transfer for Linux and MacOSX + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLocalizations + + en + it + ru + + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + CSResourcesFileMapped + + NSPrincipalClass + NSApplication + NSHighResolutionCapable + True + LSMinimumSystemVersion + 10.12.0 + CFBundleSignature + ???? + + diff -Nru android-file-transfer-3.9+dfsg/qt/main.cpp android-file-transfer-4.2/qt/main.cpp --- android-file-transfer-3.9+dfsg/qt/main.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/main.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -24,6 +24,10 @@ #include #include #include +#include +#if QT_VERSION >= 0x050000 +# include +#endif namespace { @@ -55,6 +59,10 @@ QCoreApplication::setOrganizationDomain("whoozle.github.io"); QCoreApplication::setOrganizationName("whoozle.github.io"); +#if QT_VERSION >= 0x050000 + QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), @@ -62,10 +70,18 @@ app.installTranslator(&qtTranslator); QTranslator aTranslator; - aTranslator.load(":/translations/android-file-transfer-linux_" + QLocale::system().name()); + aTranslator.load(":/android-file-transfer-linux_" + QLocale::system().name()); app.installTranslator(&aTranslator); MainWindow w; + + for (int i = 0; i < argc; ++i) { + if (strcmp(argv[i], "-R") == 0) + w.enableDeviceReset(true); + else if (strcmp(argv[i], "-v") == 0) + mtp::g_debug = true; + } + w.show(); if (!w.started()) diff -Nru android-file-transfer-3.9+dfsg/qt/mainwindow.cpp android-file-transfer-4.2/qt/mainwindow.cpp --- android-file-transfer-3.9+dfsg/qt/mainwindow.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mainwindow.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -25,8 +25,12 @@ #include "mtpobjectsmodel.h" #include "mtpstoragesmodel.h" #include "fileuploader.h" +#include "devicesdialog.h" #include "utils.h" -#include +#include +#include +#include +#include #include #include #include @@ -37,20 +41,44 @@ #include #include #include +#include +#include +#include +#include +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - _ui(new Ui::MainWindow), + _ui(new Ui::MainWindow), _nam(), _clipboard(QApplication::clipboard()), _proxyModel(new QSortFilterProxyModel), _storageModel(), _objectModel(new MtpObjectsModel()), - _uploader(new FileUploader(_objectModel, this)) + _uploader(new FileUploader(_objectModel, this)), + _resetDevice(false), + _networkReply() { _ui->setupUi(this); setWindowIcon(QIcon(":/android-file-transfer.png")); + QString theme; + { + auto value = palette().text().color().value(); + //trying to guess if it's dark or light theme + theme = value > 128? "dark": "light"; + qDebug() << "current text color value: " << value << ", guessed theme: " << theme; + } + _ui->actionBack->setIcon(QIcon(":/icons/" + theme + "/go-previous.svg")); + _ui->actionGoDown->setIcon(QIcon(":/icons/" + theme + "/go-next.svg")); + _ui->actionCreateDirectory->setIcon(QIcon(":/icons/" + theme + "/folder-new.svg")); + _ui->actionRefresh->setIcon(QIcon(":/icons/" + theme + "/view-refresh.svg")); + _ui->listView->setModel(_proxyModel); _proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); @@ -63,10 +91,12 @@ connect(_ui->listView, SIGNAL(doubleClicked(QModelIndex)), SLOT(onActivated(QModelIndex))); connect(_ui->listView, SIGNAL(customContextMenuRequested(QPoint)), SLOT(showContextMenu(QPoint))); connect(_ui->actionBack, SIGNAL(triggered()), SLOT(back())); - connect(_ui->actionGo_Down, SIGNAL(triggered()), SLOT(down())); + connect(_ui->actionGoDown, SIGNAL(triggered()), SLOT(down())); connect(_ui->actionCreateDirectory, SIGNAL(triggered()), SLOT(createDirectory())); connect(_ui->actionUploadDirectory, SIGNAL(triggered()), SLOT(uploadDirectories())); - connect(_ui->actionUpload_Album, SIGNAL(triggered()), SLOT(uploadAlbum())); + connect(_ui->actionUploadAlbum, SIGNAL(triggered()), SLOT(uploadAlbum())); + connect(_ui->actionImportMusic, SIGNAL(triggered()), SLOT(importMusic())); + connect(_ui->actionImportMusicFiles, SIGNAL(triggered()), SLOT(importMusicFiles())); connect(_ui->actionUpload, SIGNAL(triggered()), SLOT(uploadFiles())); connect(_ui->actionRename, SIGNAL(triggered()), SLOT(renameFile())); connect(_ui->actionDownload, SIGNAL(triggered()), SLOT(downloadFiles())); @@ -75,16 +105,21 @@ connect(_ui->actionRefresh, SIGNAL(triggered()), SLOT(refresh())); connect(_ui->actionPaste, SIGNAL(triggered()), SLOT(pasteFromClipboard())); connect(_ui->actionShowThumbnails, SIGNAL(triggered(bool)), SLOT(showThumbnails(bool))); + connect(_ui->actionRemoveCover, SIGNAL(triggered(bool)), SLOT(removeCover())); + connect(_ui->actionAttachCover, SIGNAL(triggered(bool)), SLOT(attachCover())); - connect(_objectModel, SIGNAL(onFilesDropped(QStringList)), SLOT(uploadFiles(QStringList))); + connect(_objectModel, SIGNAL(onFilesDropped(QStringList)), SLOT(onFilesDropped(QStringList))); connect(_objectModel, SIGNAL(existingFileOverwrite(QString)), SLOT(confirmOverwrite(QString)), Qt::BlockingQueuedConnection); connect(_clipboard, SIGNAL(dataChanged()), SLOT(validateClipboard())); validateClipboard(); + QToolButton * importMusic = dynamic_cast(_ui->mainToolBar->widgetForAction(_ui->actionImportMusic)); + importMusic->setMenu(new QMenu(tr("Import Music"))); + importMusic->menu()->addAction(_ui->actionImportMusicFiles); //fixme: find out how to specify alternative in designer _ui->actionBack->setShortcuts(_ui->actionBack->shortcuts() << QKeySequence("Alt+Up") << QKeySequence("Esc")); - _ui->actionGo_Down->setShortcuts(_ui->actionGo_Down->shortcuts() << QKeySequence("Alt+Down") << QKeySequence("Enter")); + _ui->actionGoDown->setShortcuts(_ui->actionGoDown->shortcuts() << QKeySequence("Alt+Down") << QKeySequence("Enter")); _ui->actionCreateDirectory->setShortcuts(_ui->actionCreateDirectory->shortcuts() << QKeySequence("F7")); _ui->actionRefresh->setShortcuts(_ui->actionRefresh->shortcuts() << QKeySequence("Ctrl+R")); _ui->listView->setFocus(); @@ -119,86 +154,86 @@ QMainWindow::closeEvent(event); } -bool MainWindow::reconnectToDevice() +void MainWindow::replyReadyRead() { - _session.reset(); - _device.reset(); - bool claimInterface = true; - bool resetDevice = false; + if (!_networkReply) { + qWarning() << "replyReadyRead called without network reply object"; + return; + } - mtp::usb::ContextPtr ctx(new mtp::usb::Context); + auto data = _networkReply->read(128 * 1024); + qDebug() << "read: " << data.size() << " bytes..."; + _networkReplyBody.append(data); +} - auto devices = ctx->GetDevices(); - for (auto desc = devices.begin(); desc != devices.end();) +void MainWindow::replyFinished(QNetworkReply * reply) +{ + _networkReply = NULL; + qDebug() << "got reply " << reply << " with status " << reply->error(); + auto title = tr("MTPZ Keys Download"); + if (reply->error() != 0) { - qDebug("probing device..."); - try - { - _device = mtp::Device::Open(ctx, *desc, claimInterface, resetDevice); - ++desc; - } - catch(const mtp::usb::DeviceBusyException &ex) - { - bool canKill = !ex.Processes.empty(); - QString processList; - for (auto & desc : ex.Processes) - { - processList += QString("%1 (pid: %2)\n").arg(fromUtf8(desc.Name)).arg(desc.Id); - } - if (canKill) - processList = tr("The following processes are keeping file descriptors for your device:\n") + processList; + QMessageBox::warning(this, title, tr("Could not download keys, please find the error below:\n\n%1\n\nPlease look for .mtpz-data file on the internet and manually install it to your home directory.").arg(reply->errorString())); + return; + } + reply->open(QIODevice::ReadOnly); + auto mtpzDataPath = getMtpzDataPath(); + QFile destination(mtpzDataPath); + qDebug() << "writing to " << destination.fileName(); + if (!destination.open(QIODevice::WriteOnly)) { + QMessageBox::warning(this, title, tr("Could not write keys to %1").arg(mtpzDataPath)); + return; + } - QMessageBox dialog( - QMessageBox::Warning, - tr("Device is busy"), - tr("Device is busy, maybe another process is using it.\n\n") + - processList + - tr("Close other MTP applications and restart Android File Transfer.\n" - "\nPress Abort to kill them or Ignore to try next device."), - (canKill? QMessageBox::Abort: QMessageBox::StandardButton(0)) | QMessageBox::Ignore, - this - ); + if (destination.write(_networkReplyBody) == -1) + QMessageBox::warning(this, title, tr("Could not write keys, please find the error below:\n\n%1\n\nPlease look for .mtpz-data file on the internet and manually install it to your home directory.").arg(destination.errorString())); - dialog.setDefaultButton(QMessageBox::Ignore); - dialog.setEscapeButton(QMessageBox::Ignore); - auto r = dialog.exec(); + destination.close(); + reply->close(); + reply->deleteLater(); + try + { + _trustedApp = mtp::TrustedApp::Create(_session, toUtf8(mtpzDataPath)); + if (!_trustedApp->KeysLoaded()) + throw std::runtime_error("failed to load new keys"); - if ((r & QMessageBox::Abort) == QMessageBox::Abort) - { - qDebug("kill'em all"); - ex.Kill(); - qDebug("retrying..."); //do not increment desc, retry device - } - if ((r & QMessageBox::Ignore) == QMessageBox::Ignore) - ++desc; - } - catch(const std::exception &ex) - { - qWarning("Device::Find failed: %s", ex.what()); - QMessageBox dialog( - QMessageBox::Warning, - tr("Device::Find failed"), - tr( - "MTP device could not be opened at the moment\n\nFailure: %1\n" - "Press Reset to reset them or Ignore to try next device.").arg(fromUtf8(ex.what())), - QMessageBox::Reset | QMessageBox::Ignore, - this - ); - dialog.setDefaultButton(QMessageBox::Ignore); - dialog.setEscapeButton(QMessageBox::Ignore); - auto r = dialog.exec(); + qDebug() << "new keys loaded, authenticating..."; - if ((r & QMessageBox::Reset) == QMessageBox::Reset) - { - qDebug("retry with reset..."); - resetDevice = true; - } - if ((r & QMessageBox::Ignore) == QMessageBox::Ignore) - ++desc; - } + _trustedApp->Authenticate(); - if (_device) - break; + QMessageBox::information(this, title, tr("MTPZ keys have been installed to your system.")); + tryCreateLibrary(); + } + catch (const std::exception & ex) + { + qWarning() << "failed to recreate session: " << ex.what(); + QMessageBox::warning(this, title, tr("Your MTPZ keys failed to install or load.\n\nPlease restart the application to try again.\n\nException: %1").arg(ex.what())); + } + +} + +QString MainWindow::getMtpzDataPath() +{ +#if QT_VERSION >= 0x050000 + auto path = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); +#else + auto path = QDesktopServices::storageLocation(QDesktopServices::HomeLocation); +#endif + path += "/.mtpz-data"; + return path; +} + +bool MainWindow::reconnectToDevice() +{ + _session.reset(); + _device.reset(); + + { + DevicesDialog dialog(_resetDevice, this); + int r = dialog.exec(); + if (r == QDialog::Rejected) + return false; + _device = dialog.getDevice(); } if (!_device) @@ -217,6 +252,50 @@ _session = _device->OpenSession(1); mtp::msg::DeviceInfo di = _session->GetDeviceInfo(); qDebug() << "device info" << fromUtf8(di.Manufacturer) << " " << fromUtf8(di.Model); + auto path = getMtpzDataPath(); + qDebug() << "mtpz-data path: " << path; + _trustedApp = mtp::TrustedApp::Create(_session, toUtf8(path)); + if (_trustedApp && !_trustedApp->KeysLoaded()) { + QString title = tr("MTPZ Keys are Missing"); + QString header = tr( + "It seems your computer is missing an important bit for talking with MTPZ device: " + "private keys and certificate material from Microsoft.\n" + "This means that you can't download and upload files from/to such devices.\n\n" + "Microsoft could have released that key material and documentation for MTPZ devices " + "as they are not interested in those anymore.\n\n" + "Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, " + "just because you own it.\n\n" + ); +#ifdef MTPZ_DATA_SOURCE + QMessageBox downloadKeys(QMessageBox::Question, + title, + header + tr( + "Alternatively I (as an app) can offer you to download keys from the Internet.\n" + "Can I download keys for you?\n\n" + + "(Please press Yes only if all of the above is legal in your country or you just don't care)." + ), + QMessageBox::Yes | QMessageBox::No + ); + int r = downloadKeys.exec(); + if (r & QMessageBox::Yes) { + qDebug() << "downloading keys"; + if (!_nam) { + _nam = new QNetworkAccessManager(this); + connect(_nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); + } + _networkReply = _nam->get(QNetworkRequest(QUrl(MTPZ_DATA_SOURCE))); + connect(_networkReply, SIGNAL(readyRead()), this, SLOT(replyReadyRead())); + } +#else + QMessageBox downloadKeys(QMessageBox::Warning, + title, + header + tr( + "You can look for .mtpz-data file on the internet, download it and place it in your home directory." + )); + downloadKeys.exec(); +#endif + } break; } catch(const mtp::usb::TimeoutException &ex) @@ -295,6 +374,54 @@ onStorageChanged(_ui->storageList->currentIndex()); qDebug() << "session opened, starting"; _proxyModel->setSourceModel(_objectModel); + if (_trustedApp && _trustedApp->KeysLoaded()) + { + qDebug() << "keys loaded, authenticated"; + _trustedApp->Authenticate(); + } + + tryCreateLibrary(); + + bool canUpload = _session->GetDeviceInfo().Supports(mtp::OperationCode::SendObjectInfo); + _ui->actionCreateDirectory->setVisible(canUpload); + _ui->actionUploadAlbum->setVisible(canUpload); + _ui->actionUploadDirectory->setVisible(canUpload); + _ui->actionUpload->setVisible(canUpload); + } +} + +void MainWindow::tryCreateLibrary() +{ + if (_uploader->library()) + return; + + _ui->actionUploadAlbum->setVisible(true); + _ui->actionImportMusic->setVisible(false); + + if (mtp::Library::Supported(_session)) { + ProgressDialog progressDialog(this, false); + progressDialog.setWindowTitle(tr("Loading Media Library")); + progressDialog.setModal(true); + progressDialog.setValue(0); + + connect(_uploader, SIGNAL(uploadProgress(float)), &progressDialog, SLOT(setValue(float))); + connect(_uploader, SIGNAL(uploadSpeed(qint64)), &progressDialog, SLOT(setSpeed(qint64))); + connect(_uploader, SIGNAL(uploadStarted(QString)), &progressDialog, SLOT(setFilename(QString))); + connect(_uploader, SIGNAL(finished()), &progressDialog, SLOT(accept())); + connect(&progressDialog, SIGNAL(abort()), &progressDialog, SLOT(reject())); + + try + { + _uploader->tryCreateLibrary(); + _ui->actionUploadAlbum->setVisible(false); + _ui->actionImportMusic->setVisible(true); + } + catch (const std::exception & ex) + { + qWarning() << "importing music disabled: " << ex.what(); + } + + progressDialog.exec(); } } @@ -340,7 +467,7 @@ _ui->actionDelete->setEnabled(!rows.empty()); _ui->actionDownload->setEnabled(!rows.empty()); _ui->actionRename->setEnabled(rows.size() == 1); - _ui->actionGo_Down->setEnabled(rows.size() == 1); + _ui->actionGoDown->setEnabled(rows.size() == 1); _ui->actionBack->setEnabled(!_history.empty()); QStringList statusList; @@ -417,6 +544,38 @@ menu.addAction(_ui->actionRename); menu.addAction(_ui->actionDownload); menu.addAction(_ui->actionDelete); + + bool showRSMenu = false; + + std::unordered_set visited; + for(QModelIndex row : rows) + { + row = mapIndex(row); + auto id = _objectModel->objectIdAt(row.row()); + try { + mtp::ObjectFormat format = static_cast(_session->GetObjectIntegerProperty(id, mtp::ObjectProperty::ObjectFormat)); + if (visited.find(format) != visited.end()) + continue; + + visited.insert(format); + auto supportedProperties = _session->GetObjectPropertiesSupported(format); + auto & properties = supportedProperties.ObjectPropertyCodes; + auto it = std::find(properties.begin(), properties.end(), mtp::ObjectProperty::RepresentativeSampleData); + if (it != properties.end()) { + showRSMenu = true; + break; + } + } catch (const std::exception & ex) { + qWarning() << "checking representative sample failed"; + } + } + if (showRSMenu) { + menu.addSeparator(); + if (rows.size() == 1) + menu.addAction(_ui->actionAttachCover); + menu.addAction(_ui->actionRemoveCover); + } + menu.exec(_ui->listView->mapToGlobal(pos)); } @@ -611,30 +770,66 @@ } } -namespace +void MainWindow::importMusic() +{ importMusic(true); } + +void MainWindow::importMusicFiles() +{ importMusic(false); } + +void MainWindow::importMusic(bool directoryMode) +{ + qDebug() << "import music, directory mode: " << directoryMode; + + QFileDialog d(this); + QSettings settings; + { + QVariant ld = settings.value("the-latest-directory"); + if (ld.isValid()) + d.setDirectory(ld.toString()); + } + + d.setAcceptMode(QFileDialog::AcceptOpen); + d.setOption(QFileDialog::ReadOnly, true); + if (directoryMode) { + d.setFileMode(QFileDialog::Directory); + d.setOption(QFileDialog::ShowDirsOnly, true); + } else { + d.setFileMode(QFileDialog::ExistingFiles); + } + restoreGeometry("import-music", d); + if (!d.exec()) + return; + + saveGeometry("import-music", d); + settings.setValue("the-latest-directory", d.directory().absolutePath()); + QStringList selected = d.selectedFiles(); + if (selected.isEmpty()) + return; + + for(const auto &path : selected) + { + importMusic(path); + } +} + +void MainWindow::onFilesDropped(const QStringList &files) { - int GetScore(const QString &str_) + if (_uploader->library()) { - QString str = str_.toLower(); - int score = 0; - if (str.contains("art")) - score += 1; - if (str.contains("album")) - score += 1; - if (str.contains("cover")) - score += 2; - if (str.contains("large")) - score += 2; - if (str.contains("small")) - score += 1; - if (str.contains("folder")) - score += 1; - return score; + for(auto & file : files) + { + importMusic(file); + } } + else + uploadFiles(files); +} +namespace +{ bool HeuristicLess(const QString &s1, const QString &s2) { - return GetScore(s1) > GetScore(s2); + return GetCoverScore(s1) > GetCoverScore(s2); } } @@ -652,7 +847,7 @@ { QStringList ext({"*.png", "*.jpg", "*.jpeg"}); covers = dir.entryList(ext, QDir::Files); - qSort(covers.begin(), covers.end(), &HeuristicLess); + std::sort(covers.begin(), covers.end(), &HeuristicLess); qDebug() << "covers" << covers; if (!covers.isEmpty()) cover = covers.front(); @@ -676,6 +871,24 @@ uploadFiles(files); } +void MainWindow::importMusic(QString path) +{ + qDebug() << "import music from " << path; + + ProgressDialog progressDialog(this); + progressDialog.setWindowTitle(tr("Importing Progress")); + progressDialog.show(); + + connect(_uploader, SIGNAL(uploadProgress(float)), &progressDialog, SLOT(setValue(float))); + connect(_uploader, SIGNAL(uploadSpeed(qint64)), &progressDialog, SLOT(setSpeed(qint64))); + connect(_uploader, SIGNAL(uploadStarted(QString)), &progressDialog, SLOT(setFilename(QString))); + connect(_uploader, SIGNAL(finished()), &progressDialog, SLOT(accept())); + connect(&progressDialog, SIGNAL(abort()), _uploader, SLOT(abort())); + _uploader->importMusic(path); + + progressDialog.exec(); +} + void MainWindow::validateClipboard() { QStringList files = _objectModel->extractMimeData(_clipboard->mimeData()); @@ -718,3 +931,72 @@ _ui->listView->setViewMode(enable? QListView::IconMode: QListView::ListMode); _objectModel->enableThumbnail(enable, maxSize); //resets model/size hints, etc } + +void MainWindow::attachCover() +{ + QItemSelectionModel *selection =_ui->listView->selectionModel(); + QModelIndexList rows = selection->selectedRows(); + if (rows.empty()) + return; + + auto row = mapIndex(rows.at(0)); + auto targetObjectId = _objectModel->objectIdAt(row.row()); + qDebug() << "attaching cover to object " << targetObjectId.Id; + + QFileDialog d(this); + + QSettings settings; + { + QVariant ld = settings.value("the-latest-directory"); + if (ld.isValid()) + d.setDirectory(ld.toString()); + } + + d.setAcceptMode(QFileDialog::AcceptOpen); + d.setFileMode(QFileDialog::ExistingFile); + d.setOption(QFileDialog::ShowDirsOnly, false); + d.setOption(QFileDialog::ReadOnly, true); + restoreGeometry("upload-files", d); + if (!d.exec()) + return; + + saveGeometry("upload-files", d); + settings.setValue("the-latest-directory", d.directory().absolutePath()); + auto files = d.selectedFiles(); + if (files.empty()) + return; + + QFile file(files.at(0)); + if (!file.open(QIODevice::ReadOnly)) { + QMessageBox::warning(this, tr("Attach Cover"), tr("Could not open selected file")); + return; + } + auto buffer = file.readAll(); + qDebug() << "read " << buffer.size() << " bytes of cover file"; + file.close(); + + mtp::ByteArray value(buffer.begin(), buffer.end()); + try { _session->SetObjectPropertyAsArray(targetObjectId, mtp::ObjectProperty::RepresentativeSampleData, value); } + catch (const std::exception & ex) + { + QMessageBox::warning(this, tr("Attach Cover"), tr("Could not attach cover: %1").arg(ex.what())); + } +} + +void MainWindow::removeCover() +{ + QItemSelectionModel *selection =_ui->listView->selectionModel(); + QModelIndexList rows = selection->selectedRows(); + + mtp::ByteArray value; + + for(QModelIndex row : rows) + { + row = mapIndex(row); + auto id = _objectModel->objectIdAt(row.row()); + try + { _session->SetObjectPropertyAsArray(id, mtp::ObjectProperty::RepresentativeSampleData, value); } + catch(const std::exception & ex) + { qWarning() << "failed to remove cover:" << ex.what(); } + } +} diff -Nru android-file-transfer-3.9+dfsg/qt/mainwindow.h android-file-transfer-4.2/qt/mainwindow.h --- android-file-transfer-3.9+dfsg/qt/mainwindow.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mainwindow.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,16 +17,28 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H +#ifndef AFTL_QT_MAINWINDOW_H +#define AFTL_QT_MAINWINDOW_H -#include #include #include #include +#include +#include +#include namespace Ui { -class MainWindow; + class MainWindow; +} +namespace mtp { + class Device; + DECLARE_PTR(Device); + class TrustedApp; + DECLARE_PTR(TrustedApp); + class Library; + DECLARE_PTR(Library); + class Session; + DECLARE_PTR(Session); } class MtpObjectsModel; @@ -35,6 +47,8 @@ class QSortFilterProxyModel; class QClipboard; +class QNetworkAccessManager; +class QNetworkReply; class MainWindow : public QMainWindow { @@ -47,12 +61,18 @@ bool started() const { return _device != 0; } + void enableDeviceReset(bool enable) + { _resetDevice = enable; } + private: void showEvent(QShowEvent *e); void closeEvent(QCloseEvent *event); QModelIndex mapIndex(const QModelIndex &index); void saveGeometry(const QString &name, const QWidget &widget); void restoreGeometry(const QString &name, QWidget &widget); + static QString getMtpzDataPath(); + void tryCreateLibrary(); + void importMusic(bool directoryMode); private slots: bool reconnectToDevice(); @@ -68,22 +88,31 @@ void uploadDirectories(); void uploadAlbum(); void uploadAlbum(QString path); + void importMusic(); + void importMusicFiles(); + void importMusic(QString path); void downloadFiles(); void renameFile(); void deleteFiles(); void downloadFiles(const QVector &objects); void uploadFiles(const QStringList &files); + void onFilesDropped(const QStringList &files); void onStorageChanged(int idx); void validateClipboard(); void pasteFromClipboard(); bool confirmOverwrite(const QString &file); void showThumbnails(bool enable); + void attachCover(); + void removeCover(); public slots: void downloadFiles(const QString & path, const QVector &objects); + void replyReadyRead(); + void replyFinished(QNetworkReply*); private: Ui::MainWindow * _ui; + QNetworkAccessManager * _nam; QClipboard * _clipboard; QSortFilterProxyModel * _proxyModel; MtpStoragesModel * _storageModel; @@ -95,6 +124,11 @@ mtp::DevicePtr _device; mtp::SessionPtr _session; + mtp::TrustedAppPtr _trustedApp; + mtp::LibraryPtr _mediaLibrary; + bool _resetDevice; + QNetworkReply * _networkReply; + QByteArray _networkReplyBody; }; #endif // MAINWINDOW_H diff -Nru android-file-transfer-3.9+dfsg/qt/mainwindow.ui android-file-transfer-4.2/qt/mainwindow.ui --- android-file-transfer-3.9+dfsg/qt/mainwindow.ui 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mainwindow.ui 2020-12-29 16:10:28.000000000 +0000 @@ -6,8 +6,8 @@ 0 0 - 671 - 588 + 839 + 621 @@ -56,8 +56,8 @@ 0 0 - 671 - 34 + 839 + 39 @@ -80,12 +80,18 @@ &Navigate - + + + false + + + false + TopToolBarArea @@ -93,13 +99,14 @@ false - + - + + @@ -111,10 +118,6 @@ - - - .. - &CreateDirectory @@ -128,10 +131,6 @@ - - - .. - &Back @@ -139,11 +138,7 @@ Backspace - - - - .. - + &Go Down @@ -151,16 +146,12 @@ Return - + Upload Album - - - .. - &Rename @@ -169,10 +160,6 @@ - - - .. - D&elete @@ -181,10 +168,6 @@ - - - .. - &Download @@ -201,10 +184,6 @@ - - - .. - Re&fresh @@ -231,6 +210,29 @@ Show Thumbnails of images where available + + + Import Music + + + + + Remove Cover + + + + + Attach Cover + + + + + Import Music Files + + + Import individual music files + + diff -Nru android-file-transfer-3.9+dfsg/qt/mtpmimedata.cpp android-file-transfer-4.2/qt/mtpmimedata.cpp --- android-file-transfer-3.9+dfsg/qt/mtpmimedata.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mtpmimedata.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/qt/mtpmimedata.h android-file-transfer-4.2/qt/mtpmimedata.h --- android-file-transfer-3.9+dfsg/qt/mtpmimedata.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mtpmimedata.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTPMIMEDATA_H -#define MTPMIMEDATA_H +#ifndef AFTL_QT_MTPMIMEDATA_H +#define AFTL_QT_MTPMIMEDATA_H class MtpMimeData : public QMimeData diff -Nru android-file-transfer-3.9+dfsg/qt/mtpobjectsmodel.cpp android-file-transfer-4.2/qt/mtpobjectsmodel.cpp --- android-file-transfer-3.9+dfsg/qt/mtpobjectsmodel.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mtpobjectsmodel.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -30,7 +30,7 @@ #include #include -#include +#include #include //for mtime MtpObjectsModel::MtpObjectsModel(QObject *parent): @@ -130,21 +130,25 @@ { if (!_thumbnail) { - auto stream = std::make_shared(); + auto stream = std::make_shared(); _thumbnail = std::make_shared(); try { - qDebug() << "requesting thumbnail for " << ObjectId.Id; + auto format = GetInfo(session)->ThumbFormat; + if (format == 0) + throw std::runtime_error("invalid thumbnail format"); + + qDebug() << "requesting thumbnail for " << ObjectId.Id << ", format: " << format; session->GetThumb(ObjectId, stream); - auto data = stream->GetData(); + auto & data = stream->GetData(); QPixmap pixmap; - if (!pixmap.loadFromData(data->data(), data->size())) + if (!pixmap.loadFromData(data.data(), data.size())) throw std::runtime_error("couldn't load pixmap"); *_thumbnail = pixmap.scaled(maxSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - qDebug() << "loaded " << data->size() << " bytes of thumbnail data"; + qDebug() << "loaded " << data.size() << " bytes of thumbnail data"; } catch(const std::exception &ex) { @@ -158,7 +162,7 @@ bool MtpObjectsModel::Row::IsAssociation(mtp::SessionPtr session) { mtp::ObjectFormat format = GetInfo(session)->ObjectFormat; - return format == mtp::ObjectFormat::Association || format == mtp::ObjectFormat::AudioAlbum; + return format == mtp::ObjectFormat::Association; } void MtpObjectsModel::rename(int idx, const QString &fileName) @@ -242,7 +246,7 @@ return _rows.at(existingDir.row()).ObjectId; mtp::StorageId storageId = _storageId != mtp::Session::AllStorages? _storageId: mtp::Session::AnyStorage; - mtp::Session::NewObjectInfo noi = _session->CreateDirectory(toUtf8(name), parentObjectId, storageId, type); + auto noi = _session->CreateDirectory(toUtf8(name), parentObjectId, storageId, type); if (parentObjectId == _parentObjectId) { beginInsertRows(QModelIndex(), _rows.size(), _rows.size()); @@ -293,8 +297,8 @@ mtp::msg::ObjectInfo oi; oi.Filename = toUtf8(filename); oi.ObjectFormat = objectFormat; - oi.SetSize(fileInfo.size()); - mtp::Session::NewObjectInfo noi; + oi.ObjectCompressedSize = fileInfo.size(); + mtp::msg::NewObjectInfo noi; try { noi = _session->SendObjectInfo(oi, _storageId != mtp::Session::AllStorages? _storageId: mtp::Session::AnyStorage, parentObjectId); @@ -316,6 +320,19 @@ return true; } +bool MtpObjectsModel::sendFile(const QString &filePath) +{ + try + { + std::shared_ptr object(new QtObjectInputStream(filePath)); + connect(object.get(), SIGNAL(positionChanged(qint64,qint64)), this, SIGNAL(filePositionChanged(qint64,qint64))); + _session->SendObject(object); + return true; + } + catch(const std::exception &ex) + { qWarning() << "failed to send file " << fromUtf8(ex.what()); return false; } +} + bool MtpObjectsModel::downloadFile(const QString &filePath, mtp::ObjectId objectId) { auto object = std::make_shared(filePath); diff -Nru android-file-transfer-3.9+dfsg/qt/mtpobjectsmodel.h android-file-transfer-4.2/qt/mtpobjectsmodel.h --- android-file-transfer-3.9+dfsg/qt/mtpobjectsmodel.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mtpobjectsmodel.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTPOBJECTSMODEL_H -#define MTPOBJECTSMODEL_H +#ifndef AFTL_QT_MTPOBJECTSMODEL_H +#define AFTL_QT_MTPOBJECTSMODEL_H #include #include @@ -104,6 +104,7 @@ bool uploadFile(mtp::ObjectId parentObjectId, const QString &filePath, QString filename = QString()); bool uploadFile(const QString &filePath, QString filename = QString()) { return uploadFile(_parentObjectId, filePath, filename); } + bool sendFile(const QString &filePath); bool downloadFile(const QString &filePath, mtp::ObjectId objectId); void rename(int idx, const QString &fileName); ObjectInfo getInfoById(mtp::ObjectId objectId) const; diff -Nru android-file-transfer-3.9+dfsg/qt/mtpstoragesmodel.cpp android-file-transfer-4.2/qt/mtpstoragesmodel.cpp --- android-file-transfer-3.9+dfsg/qt/mtpstoragesmodel.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mtpstoragesmodel.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/qt/mtpstoragesmodel.h android-file-transfer-4.2/qt/mtpstoragesmodel.h --- android-file-transfer-3.9+dfsg/qt/mtpstoragesmodel.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/mtpstoragesmodel.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef MTPSTORAGESMODEL_H -#define MTPSTORAGESMODEL_H +#ifndef AFTL_QT_MTPSTORAGESMODEL_H +#define AFTL_QT_MTPSTORAGESMODEL_H #include #include diff -Nru android-file-transfer-3.9+dfsg/qt/progressdialog.cpp android-file-transfer-4.2/qt/progressdialog.cpp --- android-file-transfer-3.9+dfsg/qt/progressdialog.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/progressdialog.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -24,7 +24,7 @@ #include #include -ProgressDialog::ProgressDialog(QWidget *parent) : +ProgressDialog::ProgressDialog(QWidget *parent, bool showAbort) : QDialog(parent), ui(new Ui::ProgressDialog), _progress(0), _duration(0) { @@ -37,9 +37,14 @@ _animation->setEndValue(1); _animation->start(); - QPushButton *b = ui->buttonBox->button(QDialogButtonBox::Abort); - Q_ASSERT(b); - connect(b, SIGNAL(clicked(bool)), SLOT(onAbortButtonPressed())); + if (showAbort) + { + QPushButton *b = ui->buttonBox->button(QDialogButtonBox::Abort); + Q_ASSERT(b); + connect(b, SIGNAL(clicked(bool)), SLOT(onAbortButtonPressed())); + } + else + ui->buttonBox->clear(); } ProgressDialog::~ProgressDialog() @@ -92,15 +97,15 @@ void ProgressDialog::setSpeed(qint64 speed) { - static constexpr double Kb = 1000; - static constexpr double Mb = 1000 * Kb; //haha - static constexpr double Gb = 1000 * Mb; - if (speed < 2 * Mb) - ui->speedLabel->setText(tr("Speed: ") + QString().sprintf("%.1f", speed / Kb) + tr(" Kb/s")); - else if (speed < 2 * Gb) - ui->speedLabel->setText(tr("Speed: ") + QString().sprintf("%.1f", speed / Mb) + tr(" Mb/s")); + static constexpr double kB = 1000; + static constexpr double MB = 1000 * kB; // k, M, G are metric prefixes + static constexpr double GB = 1000 * MB; // 1024 is for binary prefixes + if (speed < 2 * MB) + ui->speedLabel->setText(tr("Speed: %1 kB/s").arg(speed / kB, 0, 'f', 1)); + else if (speed < 2 * GB) + ui->speedLabel->setText(tr("Speed: %1 MB/s").arg(speed / MB, 0, 'f', 1)); else - ui->speedLabel->setText(tr("Speed: ") + QString().sprintf("%.1f", speed / Gb) + tr(" Gb/s")); + ui->speedLabel->setText(tr("Speed: %1 GB/s").arg(speed / GB, 0, 'f', 1)); } void ProgressDialog::setFilename(const QString &filename) diff -Nru android-file-transfer-3.9+dfsg/qt/progressdialog.h android-file-transfer-4.2/qt/progressdialog.h --- android-file-transfer-3.9+dfsg/qt/progressdialog.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/progressdialog.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef PROGRESSDIALOG_H -#define PROGRESSDIALOG_H +#ifndef AFTL_QT_PROGRESSDIALOG_H +#define AFTL_QT_PROGRESSDIALOG_H #include @@ -34,7 +34,7 @@ Q_PROPERTY(float progress READ progress WRITE setProgress); public: - explicit ProgressDialog(QWidget *parent = 0); + explicit ProgressDialog(QWidget *parent = 0, bool showAbort = true); ~ProgressDialog(); float progress() const diff -Nru android-file-transfer-3.9+dfsg/qt/qtobjectstream.cpp android-file-transfer-4.2/qt/qtobjectstream.cpp --- android-file-transfer-3.9+dfsg/qt/qtobjectstream.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/qtobjectstream.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/qt/qtobjectstream.h android-file-transfer-4.2/qt/qtobjectstream.h --- android-file-transfer-3.9+dfsg/qt/qtobjectstream.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/qtobjectstream.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef QTOBJECTSTREAM_H -#define QTOBJECTSTREAM_H +#ifndef AFTL_QT_QTOBJECTSTREAM_H +#define AFTL_QT_QTOBJECTSTREAM_H #include #include diff -Nru android-file-transfer-3.9+dfsg/qt/renamedialog.cpp android-file-transfer-4.2/qt/renamedialog.cpp --- android-file-transfer-3.9+dfsg/qt/renamedialog.cpp 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/renamedialog.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff -Nru android-file-transfer-3.9+dfsg/qt/renamedialog.h android-file-transfer-4.2/qt/renamedialog.h --- android-file-transfer-3.9+dfsg/qt/renamedialog.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/renamedialog.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef RENAMEDIALOG_H -#define RENAMEDIALOG_H +#ifndef AFTL_QT_RENAMEDIALOG_H +#define AFTL_QT_RENAMEDIALOG_H #include diff -Nru android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_cs.ts android-file-transfer-4.2/qt/translations/android-file-transfer-linux_cs.ts --- android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_cs.ts 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/translations/android-file-transfer-linux_cs.ts 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,495 @@ + + + + + CommandQueue + + + Loading media library… + Načítání knihovny médií... + + + + Querying artists… + Zjišťuji umělce... + + + + Loading artists… + Načítám umělce... + + + + Querying albums… + Zjišťuji alba... + + + + Loading albums… + Načítám alba... + + + + Done + Hotovo + + + + Setting cover for album %1 + Nastavuji obal pro album %1 + + + + CreateDirectoryDialog + + + Dialog + Dialog + + + + Create New Directory + Vytvořit Novou Složku + + + + MainWindow + + + Android File Transfer for Linux + Android File Transfer pro Linux + + + + &File + &Soubor + + + + &Navigate + &Navigovat + + + + Upload + Nahrát + + + + Ctrl+U + Ctrl+U + + + + &CreateDirectory + &Vytvořit Složku + + + + Ctrl+N + Ctrl+N + + + + UploadDirectory + Nahrrát Složku + + + + &Back + &Zpět + + + + Backspace + Backspace + + + + &Go Down + &Jít Dolů + + + + Return + Zpět + + + + Upload Album + Nahrát Album + + + + &Rename + &Přejmenovat + + + + F2 + F2 + + + + D&elete + &Smazat + + + + Del + Del + + + + &Download + &Stáhnout + + + + Ctrl+D + Ctrl+D + + + + E&xit + U&končit + + + + Ctrl+Q + Ctrl+Q + + + + Re&fresh + &Obnovit + + + + F5 + F5 + + + + &Paste + &Vložit + + + + Ctrl+V + Ctrl+V + + + + Show Thumbnails + Zobrazit Náhledy + + + + Show Thumbnails of images where available + Zobrazit Náhledy obrázků + + + + + Import Music + Importovat Hudbu + + + + Remove Cover + Odstranit Obal + + + + + + Attach Cover + Vložit Obal + + + + Import Music Files + Importovat Hudební Soubory + + + + Import individual music files + Importovat jednotlivé hudební soubory + + + + MTPZ Keys Download + Stáhnout MTPZ klíče + + + + Could not download keys, please find the error below: + +%1 + +Please look for .mtpz-data file on the internet and manually install it to your home directory. + Nemohu stáhnout klíče, prosím chybu najdete tady: + +%1 + +Prosím hledejte .mtpz-data soubor na internetu a vložte jej do své domovské složky. + + + + Could not write keys to %1 + Nemohu zapsat klíče do %1 + + + + Could not write keys, please find the error below: + +%1 + +Please look for .mtpz-data file on the internet and manually install it to your home directory. + Nemohu zapsat klíče, prosím chybu najdete tady: + +%1 + +Prosím hledejte .mtpz-data soubor na internetu a vložte jej do své domovské složky. + + + + MTPZ keys have been installed to your system. + MTPZ klíče byly nainstalovány do vašeho systému. + + + + Your MTPZ keys failed to install or load. + +Please restart the application to try again. + +Exception: %1 + Instalace nebo načtení Vašich MTPZ klíčů selhala + +Prosím restartujte aplikaci pro nový pokus + +Vyjímka: %1 + + + + The following processes are keeping file descriptors for your device: + + Následující procesy drží popisovače souboru pro vaše zařízení: + + + + + Device is busy + Zařízení je zaneprázdněno + + + + Device is busy, maybe another process is using it. + + + Zařízení je zaneprázdněno, možná jej používá jiný proces. + + + + + + Close other MTP applications and restart Android File Transfer. + +Press Abort to kill them or Ignore to try next device. + Zavřete ostatní MTP aplikace a restartujte program. + +Zmáčkněte Zrušit pro jejich ukončení nebo Ignorovat pro použití dalšího zařízení. + + + + Device::Find failed + Zařízení::Hledání selhalo + + + + MTP device could not be opened at the moment + +Failure: %1 +Press Reset to reset them or Ignore to try next device. + MTP zařízení nemohlo být nyní otevřeno + +Problém: %1 +Zmáčkněte Reset pro obnovu nebo Ignorovat pro použití dalšího zařízení. + + + + No MTP device found + Nenalezeno žádné MTP zařízení + + + + MTPZ Keys are Missing + MTPZ Klíče Nenalezeny + + + + It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. +This means that you can't download and upload files from/to such devices. + +Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. + +Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. + + + Vypadá to, že váš počítač postrádá důležitou součást pro komunikaci s MTPZ zařízením: privítní klíče a certifikát od Microsoftu +To znamená, že nemůžete stáhovat nebo nahrávat soubory z/do tohoto zařízení. + +Microsoft mohl vydat tyto klíčové soubory a dokumentaci pro MTPZ zařízení protože už o to nemají zájem. + +Protože je monžost překročení legálních restrikcí, nemůžeme přiložit tyto klíče, přestože v některých zemích je to legální - spravit věci tak, aby byly opět funkční, protože je vlastníte. + + + + + + Alternatively I (as an app) can offer you to download keys from the Internet. +Can I download keys for you? + +(Please press Yes only if all of the above is legal in your country or you just don't care). + Jako alternativa pro vás mohu stáhnout klíče z Internetu automaticky. +Stáhnout klíče? + +(Zmáčkněte Ano jen v případě, že je řečené u vás legální nebo je vám to jedno). + + + + You can look for .mtpz-data file on the internet, download it and place it in your home directory. + Můžete najít .mtpz-data soubor na internetu a stáhnou jej do vaší domovské složky. + + + + + MTP + MTP + + + + MTP device does not respond + MTP zařízení neodpovídá + + + + Could not open MTP session: %1 + Nemohu otevřít MTP sezení: %1 + + + + No MTP Storages + Žádné MTP Uložiště + + + + No MTP storage found, your device might be locked. +Please unlock and press Retry to continue or Abort to exit. + Nenalezeno žádné MTP uložiště, vaše zařízení může být zamčeno. +Prosím odemčete jej a zmáčknětě Zkust znovu pro pokračování nebo Zrušit pro ukončení. + + + + Loading Media Library + Načítání Knihovny Médií + + + + Deleting file(s) + Mazání souboru(ů) + + + + Are you sure? + Opravdu? + + + + Error + Chyba + + + + Failed to create new directory: + + Vytvoření nové složky selhalo: + + + + + Download Progress + Postup Stahování + + + + Importing Progress + Postup Importu + + + + Overwrite confirmation + Potvrzení přepsání + + + + You are about to overwrite file + Chystáte se přepsat soubor + + + + Could not open selected file + Nemohu otevřít vybraný soubor + + + + Could not attach cover: %1 + Nemohu vložit obal: %1 + + + + MtpStoragesModel + + + All storages (BUGS, BEWARE) + Všechna uložiště (POZOR, S CHYBAMI) + + + + ProgressDialog + + + Upload Progress + Postup Nahrávání + + + + Speed: %1 kB/s + Rychlost: %1 kB/s + + + + Speed: %1 MB/s + Rychlost: %1 MB/s + + + + Speed: %1 GB/s + Rychlost: %1 GB/s + + + + RenameDialog + + + Dialog + Dialog + + + + Rename Object + Přejmenovat Objekt + + + diff -Nru android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_it.ts android-file-transfer-4.2/qt/translations/android-file-transfer-linux_it.ts --- android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_it.ts 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/translations/android-file-transfer-linux_it.ts 2020-12-29 16:10:28.000000000 +0000 @@ -240,18 +240,18 @@ - Kb/s - Kb/s + kB/s + kB/s - Mb/s - Mb/s + MB/s + MB/s - Gb/s - Gb/s + GB/s + GB/s diff -Nru android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_nl.ts android-file-transfer-4.2/qt/translations/android-file-transfer-linux_nl.ts --- android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_nl.ts 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/translations/android-file-transfer-linux_nl.ts 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,495 @@ + + + + + CommandQueue + + + Loading media library… + Bezig met laden van mediaverzameling… + + + + Querying artists… + Bezig met zoeken naar artiesten… + + + + Loading artists… + Bezig met laden van artiesten… + + + + Querying albums… + Bezig met zoeken naar albums… + + + + Loading albums… + Bezig met laden van albums… + + + + Done + Voltooid + + + + Setting cover for album %1 + Bezig met instellen van hoes bij %1 + + + + CreateDirectoryDialog + + + Dialog + Venster + + + + Create New Directory + Nieuwe map + + + + MainWindow + + + Android File Transfer for Linux + Android-bestandsoverdracht voor Linux + + + + &File + &Bestand + + + + &Navigate + &Ga + + + + Upload + Uploaden + + + + Ctrl+U + Ctrl+U + + + + &CreateDirectory + Nieuwe &map + + + + Ctrl+N + Ctrl+N + + + + UploadDirectory + Map uploaden + + + + &Back + &Terug + + + + Backspace + Backspace + + + + &Go Down + &Ga omlaag + + + + Return + Enter + + + + Upload Album + Album uploaden + + + + &Rename + Naam &wijzigen + + + + F2 + F2 + + + + D&elete + Verwijd&eren + + + + Del + Del + + + + &Download + &Downloaden + + + + Ctrl+D + Ctrl+D + + + + E&xit + Af&sluiten + + + + Ctrl+Q + Ctrl+Q + + + + Re&fresh + Her&laden + + + + F5 + F5 + + + + &Paste + &Plakken + + + + Ctrl+V + Ctrl+V + + + + Show Thumbnails + Miniaturen tonen + + + + Show Thumbnails of images where available + Toon miniatuurvoorbeelden (indien beschikbaar) + + + + + Import Music + Muziek importeren + + + + Remove Cover + Hoes verwijderen + + + + + + Attach Cover + Hoes bijsluiten + + + + Import Music Files + Muziekbestanden importeren + + + + Import individual music files + Importeer losse muziekbestanden + + + + MTPZ Keys Download + MTPZ-sleutels downloaden + + + + Could not download keys, please find the error below: + +%1 + +Please look for .mtpz-data file on the internet and manually install it to your home directory. + De sleutels kunnen niet worden opgehaald vanwege de volgende fout: + +%1 + +Zoek handmatig naar .mtpz-gegevensbestanden op het internet en plaats ze in je persoonlijke map. + + + + Could not write keys to %1 + De sleutels kunnen niet worden weggeschreven naar %1 + + + + Could not write keys, please find the error below: + +%1 + +Please look for .mtpz-data file on the internet and manually install it to your home directory. + De sleutels kunnen niet worden weggeschreven vanwege de volgende fout: + +%1 + +Zoek handmatig naar .mtpz-gegevensbestanden op het internet en plaats ze in je persoonlijke map. + + + + MTPZ keys have been installed to your system. + De mtpz-sleutels zijn op je systeem geplaatst. + + + + Your MTPZ keys failed to install or load. + +Please restart the application to try again. + +Exception: %1 + Je mtpz-sleutels kunnen niet worden geplaatst of geladen. + +Herstart het programma en probeer het opnieuw. + +Uitzonderingsfout: %1 + + + + The following processes are keeping file descriptors for your device: + + De volgende processen bevatten bestandsomschrijvingen van je apparaat: + + + + + Device is busy + Apparaat is bezig + + + + Device is busy, maybe another process is using it. + + + Het apparaat is bezig. Mogelijk is het in gebruik door een ander proces. + + + + + + Close other MTP applications and restart Android File Transfer. + +Press Abort to kill them or Ignore to try next device. + Sluit andere mtp-programma's en herstart Android-bestandsoverdracht. + +Druk op 'Afbreken' om ze af te sluiten of 'Negeren' om een ander apparaat te proberen. + + + + Device::Find failed + Apparaat niet aangetroffen + + + + MTP device could not be opened at the moment + +Failure: %1 +Press Reset to reset them or Ignore to try next device. + Het mtp-apparaat kan momenteel niet worden benaderd. + +Foutmelding: %1 +Druk op 'Herstellen' om te herstellen of 'Negeren' om een ander apparaat te proberen. + + + + No MTP device found + Geen mtp-apparaat aangetroffen + + + + MTPZ Keys are Missing + De mtpz-sleutels ontbreken + + + + It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. +This means that you can't download and upload files from/to such devices. + +Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. + +Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. + + + Het lijkt er op dat je computer een belangrijk onderdeel mist voor het communiceren met je mtpz-apparaat: privésleutels en certificaatmateriaal van Microsoft. +Dit houdt in dat je geen bestanden kunt downloaden en uploaden van/naar zulke apparaten. + +Microsoft had dit materiaal beschikbaar kunnen stellen aangezien ze niet meer geïnteresseerd zijn in deze apparaten. + +Vanwege juridische conflicten kunnen deze sleutels niet meeleveren, zelfs al is dat in jouw land mogelijk legaal. + + + + + + Alternatively I (as an app) can offer you to download keys from the Internet. +Can I download keys for you? + +(Please press Yes only if all of the above is legal in your country or you just don't care). + Het enige wat ik je kan bieden is de mogelijkheid deze sleutels te downloaden van het internet. +Zal ik dat voor je doen? + +(klik alleen op 'Ja' als dit legaal is in je land of als de wet je niet interesseert) + + + + You can look for .mtpz-data file on the internet, download it and place it in your home directory. + Je kunt .mtpz-gegevens vinden op het internet en deze in je persoonlijke map plaatsen. + + + + + MTP + MTP + + + + MTP device does not respond + MTP-apparaat reageert niet + + + + Could not open MTP session: %1 + De mtp-sessie kan niet worden geopend: %1 + + + + No MTP Storages + Geen mtp-apparaten + + + + No MTP storage found, your device might be locked. +Please unlock and press Retry to continue or Abort to exit. + Geen mtp-apparaat aangetroffen - wellicht is je apparaat vergrendeld. +Ontgrendel je apparaat en druk op 'Opnieuw' of 'Afbreken'. + + + + Loading Media Library + Bezig met laden van mediaverzameling + + + + Deleting file(s) + Bezig met verwijderen van bestand(en) + + + + Are you sure? + Weet je het zeker? + + + + Error + Fout + + + + Failed to create new directory: + + De map kan niet worden aangemaakt: + + + + + Download Progress + Downloadvoortgang + + + + Importing Progress + Importvoortgang + + + + Overwrite confirmation + Overschrijven bevestigen + + + + You are about to overwrite file + Je staat op het punt om het volgende te overschrijven: + + + + Could not open selected file + Kan het geselecteerde bestand niet openen + + + + Could not attach cover: %1 + De hoes kan niet worden bijgesloten: %1 + + + + MtpStoragesModel + + + All storages (BUGS, BEWARE) + Alle opslagapparaten (LET OP: MOGELIJK ONBETROUWBAAR) + + + + ProgressDialog + + + Upload Progress + Uploadvoortgang + + + + Speed: %1 kB/s + Snelheid: %1 kB/s + + + + Speed: %1 MB/s + Snelheid: %1 MB/s + + + + Speed: %1 GB/s + Snelheid: %1 GB/s + + + + RenameDialog + + + Dialog + Venster + + + + Rename Object + Itemnaam wijzigen + + + Binary files /tmp/tmp724SmP/3PWMnrkmQ4/android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_ru.qm and /tmp/tmp724SmP/fWy7MMM7WP/android-file-transfer-4.2/qt/translations/android-file-transfer-linux_ru.qm differ diff -Nru android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_ru.ts android-file-transfer-4.2/qt/translations/android-file-transfer-linux_ru.ts --- android-file-transfer-3.9+dfsg/qt/translations/android-file-transfer-linux_ru.ts 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/translations/android-file-transfer-linux_ru.ts 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,44 @@ - + + + CommandQueue + + + Loading media library… + Загрузка библиотеки… + + + + Querying artists… + Поиск исполнителей… + + + + Loading artists… + Загрузка исполнителей… + + + + Querying albums… + Поиск альбомов… + + + + Loading albums… + Загрузка альбомов… + + + + Done + Загрузка завершена + + + + Setting cover for album %1 + Устанавливаем обложку для альбома %1 + + CreateDirectoryDialog @@ -32,189 +70,392 @@ &Навигация - + Upload Закачать - + Ctrl+U - + &CreateDirectory Создать &каталог - + Ctrl+N - + UploadDirectory Закачать каталог - + &Back &Назад - + Backspace - + &Go Down &Зайти - + Return - + Upload Album Закачать альбом - + &Rename &Переименовать - + F2 - + D&elete &Удалить - + Del - + &Download &Скачать - + Ctrl+D - + E&xit В&ыход - + Ctrl+Q - + Re&fresh &Обновить - + F5 - + &Paste &Вставить + + Show Thumbnails + Показывать эскизы + + + + Show Thumbnails of images where available + Показывай эскизы где можно + + + + + Import Music + Импортировать музыку + + + + Remove Cover + Убрать обложку + + + + + + Attach Cover + Установить обложку + + + + Import Music Files + Импортировать файлы с музыкой + + + + Import individual music files + Импортировать отдельные файлы с музыкой + + Paste - Вставить + Вставить - + Ctrl+V - + Device is busy Устройство занято - Device is busy, maybe another process is using it. Close other MTP applications and restart Android File Transfer. - Устройство занято, вероятно, какой-то другой процесс в данный момент его использует. + Устройство занято, вероятно, какой-то другой процесс в данный момент его использует. Закройте другие MTP приложения и перезапустите Android File Transfer for Linux. - + + MTPZ Keys Download + Загрузить ключи MTPZ + + + + Could not download keys, please find the error below: + +%1 + +Please look for .mtpz-data file on the internet and manually install it to your home directory. + Не получилось загрузить ключи, ошибка: + +%1 + +Вы можете самостоятельно найти файл .mtpz-data в интернете и положить его в вашу домашную директорию. + + + + Could not write keys to %1 + Не получилось записать ключи в %1 + + + + Could not write keys, please find the error below: + +%1 + +Please look for .mtpz-data file on the internet and manually install it to your home directory. + Не получилось загрузить ключи, ошибка: + +%1 + +Вы можете самостоятельно найти файл .mtpz-data в интернете и положить его в вашу домашную директорию. + + + + MTPZ keys have been installed to your system. + MTPZ ключи были успешно установлены. + + + + Your MTPZ keys failed to install or load. + +Please restart the application to try again. + +Exception: %1 + Не удалось установить ваши MTPZ ключи. + + Перезапустите приложение чтобы попробовать ещё раз. + +Ошибка: %1 + + + + The following processes are keeping file descriptors for your device: + + Следующие процессы удерживают файловые дескрипторы вашего устройства: + + + + + Device is busy, maybe another process is using it. + + + Устройство занято, может быть какой-нибудь другой процесс его использует. + + + + + + Close other MTP applications and restart Android File Transfer. + +Press Abort to kill them or Ignore to try next device. + Закройте другие MTP приложения и перезапустите Android File Transfer + +Нажмите "Прервать" чтобы убить сторонний процесс, или "Игнорировать" чтобы попробовать следующее устройство. + + + + Device::Find failed + Device::Find взорвалось + + + + MTP device could not be opened at the moment + +Failure: %1 +Press Reset to reset them or Ignore to try next device. + MTP устройство не может быть открыто в данный момент + +Ошибка: %1 +Нажмите "Сброс" чтобы попытаться сбросить устройство или "Игнорировать" чтобы попробовать другие устройства. + + + No MTP device found Не найдено ни одного устройства MTP - + + MTPZ Keys are Missing + MTPZ ключи не найдены + + + + It seems your computer is missing an important bit for talking with MTPZ device: private keys and certificate material from Microsoft. +This means that you can't download and upload files from/to such devices. + +Microsoft could have released that key material and documentation for MTPZ devices as they are not interested in those anymore. + +Because of the legal risks we can't bundle those keys, even though in some countries it's lawful to modify things to make them working again, just because you own it. + + + Похоже, что ваш компьютер не содержит важного файла для общения с MTPZ устройствами: ключи и сертификаты от Microsoft. +Это означает, вы не может скачивать и закачивать файлы на MTPZ устройства. + +Майкрософт мог бы выложить ключи и документацию о MTPZ, так как они не очень заинтересованы в развитии MTPZ или производстве MTPZ устройств. + +Так как поставлять чужие ключи может оказаться юридически рискованным делом, мы напоминаем, что в некоторых странах (включая Россию) — вы можете легально модифицировать ваше неработающее устройство, чтобы оно начало выполнять свои функции. + + + + + + Alternatively I (as an app) can offer you to download keys from the Internet. +Can I download keys for you? + +(Please press Yes only if all of the above is legal in your country or you just don't care). + В качестве альтернативы, я (как приложение) могу предложить вам скачать ключи из сети интернет. +Скачать ключи? + +Пожалуйста нажмите "Да" только если всё вышеуказанное легально в вашей стране. (Или вам всё равно) + + + + You can look for .mtpz-data file on the internet, download it and place it in your home directory. + Вы можете найти файл ".mtpz-data" в интернете и просто положить его в домашнюю директорию. + + + + MTP - + MTP device does not respond MTP устройство не отвечает - + + Could not open MTP session: %1 + Не получается открыть MTP сессию: %1 + + + No MTP Storages Нет хранилищ MTP - + No MTP storage found, your device might be locked. Please unlock and press Retry to continue or Abort to exit. Нет ни одного хранилища, возможно ваше устройство заблокировано Разблокируйте устройство и нажмите Повторить чтобы попробовать ещё раз или Прервать, чтобы выйти. - + + Loading Media Library + Загружаем библиотеку + + + Deleting file(s) Удаление файла(ов) - + Are you sure? Вы уверены? - + Error Ошибка - + Failed to create new directory: Не удалось создать новый каталог: - + + Download Progress + Загрузка + + + + Importing Progress + Импорт + + + Overwrite confirmation Подтверждение перезаписи - + You are about to overwrite file Вы собираетесь перезаписать файл + + + Could not open selected file + Не получилось открыть выбранный файл + + + + Could not attach cover: %1 + Не удалось установить обложку: %1 + MtpStoragesModel @@ -232,26 +473,47 @@ Прогресс - - - Speed: - Скорость: + Скорость: + + + kB/s + кБ/с + + + MB/s + МБ/с + + + GB/s + ГБ/с + + + Speed: %1 Kb/s + Скорость: %1 Кб/c + + + Speed: %1 Mb/s + Скорость: %1 Мб/c + + + Speed: %1 Gb/s + Скорость: %1 Гб/c - - Kb/s - Кб/с + + Speed: %1 kB/s + Скорость: %1 КБайт/c - - Mb/s - Мб/с + + Speed: %1 MB/s + Скорость: %1 Мбайт/c - - Gb/s - Гб/с + + Speed: %1 GB/s + Скорость: %1 Гбайт/c diff -Nru android-file-transfer-3.9+dfsg/qt/translations/translations.qrc.in android-file-transfer-4.2/qt/translations/translations.qrc.in --- android-file-transfer-3.9+dfsg/qt/translations/translations.qrc.in 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/translations/translations.qrc.in 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,5 @@ + + + ${QM_FILES_XML} + + diff -Nru android-file-transfer-3.9+dfsg/qt/utils.cpp android-file-transfer-4.2/qt/utils.cpp --- android-file-transfer-3.9+dfsg/qt/utils.cpp 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/qt/utils.cpp 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,35 @@ +#include "utils.h" + +int GetCoverScore(const QString & str_) +{ + QString str = str_.toLower(); + int score = 0; + if (str.contains("0001")) + score += 1001; + else if (str.contains("0000")) + score += 1000; + else if (str.contains("001")) + score += 101; + else if (str.contains("000")) + score += 100; + else if (str.contains("01")) + score += 11; + else if (str.contains("00")) + score += 10; + else if (str.contains("1")) + score += 1; + + if (str.contains("art")) + score += 10000; + if (str.contains("album")) + score += 10000; + if (str.contains("cover")) + score += 20000; + if (str.contains("large")) + score += 20000; + if (str.contains("small")) + score += 10000; + if (str.contains("folder")) + score += 10000; + return score; +} diff -Nru android-file-transfer-3.9+dfsg/qt/utils.h android-file-transfer-4.2/qt/utils.h --- android-file-transfer-3.9+dfsg/qt/utils.h 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/qt/utils.h 2020-12-29 16:10:28.000000000 +0000 @@ -1,6 +1,6 @@ /* This file is part of Android File Transfer For Linux. - Copyright (C) 2015-2018 Vladimir Menshakov + Copyright (C) 2015-2020 Vladimir Menshakov This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -17,8 +17,8 @@ Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef UTILS -#define UTILS +#ifndef AFTL_QT_UTILS_H +#define AFTL_QT_UTILS_H #include #include @@ -34,5 +34,7 @@ return std::string(utf8.data(), utf8.size()); } +int GetCoverScore(const QString &str_); + #endif // UTILS diff -Nru android-file-transfer-3.9+dfsg/README.md android-file-transfer-4.2/README.md --- android-file-transfer-3.9+dfsg/README.md 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/README.md 2020-12-29 16:10:28.000000000 +0000 @@ -1,8 +1,9 @@ # Android File Transfer For Linux (FreeBSD and Mac OS X!) [![License](http://img.shields.io/:license-LGPLv2.1-blue.svg)](https://github.com/whoozle/android-file-transfer-linux/blob/master/LICENSE) -[![Version](http://img.shields.io/:version-3.9-green.svg)](https://github.com/whoozle/android-file-transfer-linux) +[![Version](http://img.shields.io/:version-4.2-green.svg)](https://github.com/whoozle/android-file-transfer-linux) [![Build Status](https://travis-ci.org/whoozle/android-file-transfer-linux.svg?branch=master)](https://travis-ci.org/whoozle/android-file-transfer-linux) +[![Android-File-Transfer-Linux](https://snapcraft.io//android-file-transfer-linux/badge.svg)](https://snapcraft.io/android-file-transfer-linux) Android File Transfer for Linux — reliable MTP client with minimalistic UI similar to [Android File Transfer for Mac](https://www.android.com/intl/en_us/filetransfer/). @@ -14,12 +15,28 @@ If you're suffering from crashes, missing tags, album covers, usb freezes and corrupted files, this software is right for you. +## Pre-built Packages + +If your distribution does not provide `android-file-transfer-linux` package, you can still install it in your system. +There's quite a few packages available: +- Snapcraft: https://snapcraft.io/android-file-transfer-linux +- AppImage: https://github.com/whoozle/android-file-transfer-linux/releases +- MacOSX DMG image: https://github.com/whoozle/android-file-transfer-linux/releases +- MacOSX Homebrew: `brew cask install whoozle-android-file-transfer` or `brew cask install whoozle-android-file-transfer-nightly` + +## Support me +If you want to help me with development, click on the link below and follow the instructions. I'm developing AFTL in my spare time and try to fix everything as fast as possible, sometimes adding features in realtime (more than 100 tickes closed by now). +Any amount would help relieving pain of using MTP. :D + +https://www.paypal.me/whoozle + ## Features * Simple Qt UI with progress dialogs. * FUSE wrapper (If you'd prefer mounting your device), supporting partial read/writes, allowing instant access to your files. * No file size limits. * Automatically renames album cover to make it visible from media player. +* Supports Zune/Zune HD. * USB 'Zerocopy' support found in recent Linux kernel (no user/kernel data copying) * No extra dependencies (e.g. `libptp`/`libmtp`). * Available as static/shared library. @@ -28,12 +45,6 @@ ## FAQ [Please take a look at FAQ if you have issues with your operating system](FAQ.md). It's not that big, but those are the questions asked very often. -## Support me -If you want to help me with development, click on the link below and follow the instructions. I'm developing AFTL in my spare time and try to fix everything as fast as possible, sometimes adding features in realtime (more than 100 tickes closed by now). -Any amount would help relieving pain of using MTP. :D - -https://www.paypal.me/whoozle - ## Building instructions ### Gentoo @@ -59,7 +70,7 @@ * For ubuntu and other debian-based distros use the following command: ```shell - sudo apt-get install build-essential cmake libqt4-dev ninja-build libfuse-dev libreadline-dev + sudo apt-get install build-essential cmake qt5-default ninja-build libfuse-dev libreadline-dev ``` For Fedora: @@ -152,7 +163,7 @@ * Sometimes downloading fails with usb timeout, then phone becomes unresponsive. [Android bug #75259](https://code.google.com/p/android/issues/detail?id=75259) * Objects created in UI will not show up in FUSE filesystem. [Android bug #169547](https://code.google.com/p/android/issues/detail?id=169547) -The actual list of all known problems and bugs available [here](https://github.com/whoozle/android-file-transfer-linux/issues) +Up to date list of all known problems and bugs available [here](https://github.com/whoozle/android-file-transfer-linux/issues) ## Contacts Please do not hesitate to contact me if you have any further questions, my email address is . @@ -160,10 +171,10 @@ ## Special thanks * All who filed bugs on github and wrote emails, many features appeared only because of your feedback. Thanks! * Alexey [gazay](https://github.com/gazay) Gaziev for useful suggestions, support and invaluable help with MacBook and MacOSX port. -* @ssnjrthegr8 for new logo! +* @ssnjrthegr8 for the new logo! ## License Android File Transfer for Linux is released under [GNU LGPLv2.1 License](https://github.com/whoozle/android-file-transfer-linux/blob/master/LICENSE). -Copyright © 2015-2018 Vladimir Menshakov +Copyright © 2015-2020 Vladimir Menshakov diff -Nru android-file-transfer-3.9+dfsg/replace-guards.py android-file-transfer-4.2/replace-guards.py --- android-file-transfer-3.9+dfsg/replace-guards.py 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/replace-guards.py 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,42 @@ +#!/usr/bin/python3 + +#!/usr/bin/env python + +import os +import os.path +import re + +guard_re = re.compile(r'#ifndef\s+(\w+)\n#define\s+(\w+)', re.M | re.S) +base_dir = os.path.dirname(os.path.realpath(__file__)) +clean_re = re.compile(r'[^\w]') +print(base_dir) + +for root, dirs, files in os.walk('.'): + root = os.path.relpath(root, base_dir) + if (root[0] == '.' and len(root) > 1) or root.startswith("build"): + dirs[:] = [] + continue + if root == "mtp/backend/linux/usb/linux": + continue + + for file in files: + name, ext = os.path.splitext(file) + if ext != '.h' or name == "arg_lexer.l" or name.endswith(".values"): + continue + + fname = os.path.join(root, file) + with open(fname) as f: + data = f.read() + + guard = 'AFTL_' + clean_re.sub('_', os.path.relpath(os.path.join(root, file), base_dir)).upper() + + def replace_guard(m): + return "#ifndef %s\n#define %s" %(guard, guard) + + data, n = guard_re.subn(replace_guard, data, count=1) + if n == 0: + print("missing guard in %s" %file) + else: + with open(fname, 'w') as f: + f.write(data) + diff -Nru android-file-transfer-3.9+dfsg/replace-license.py android-file-transfer-4.2/replace-license.py --- android-file-transfer-3.9+dfsg/replace-license.py 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/replace-license.py 2020-12-29 16:10:28.000000000 +0000 @@ -5,27 +5,22 @@ import re license = """/* -MIT License + This file is part of Android File Transfer For Linux. + Copyright (C) 2015-2020 Vladimir Menshakov -Copyright (c) 2008-2019 Vladimir Menshakov - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, + or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ """ diff -Nru android-file-transfer-3.9+dfsg/snap/snapcraft.yaml android-file-transfer-4.2/snap/snapcraft.yaml --- android-file-transfer-3.9+dfsg/snap/snapcraft.yaml 1970-01-01 00:00:00.000000000 +0000 +++ android-file-transfer-4.2/snap/snapcraft.yaml 2020-12-29 16:10:28.000000000 +0000 @@ -0,0 +1,99 @@ + + # After registering a name on build.snapcraft.io, commit an uncommented line: + name: android-file-transfer-linux + version: '4.0+git' # just for humans, typically '1.2+git' or '1.3.2' + summary: Simple and reliable MTP client + description: | + Simple UI + Fuse wrapper + cli tool for exploring, downloading, uploading + data from/to your MTP device. Git version has experimental Zune support. + + grade: devel # must be 'stable' to release into candidate/stable channels + confinement: devmode # use 'strict' once you have the right plugs and slots + base: core18 + + apps: + android-file-transfer: + command: bin/desktop-launch android-file-transfer + desktop: share/applications/android-file-transfer.desktop + plugs: [x11, network, network-bind, desktop, gsettings, wayland] + environment: + # Use GTK3 cursor theme, icon theme and open/save file dialogs. + QT_QPA_PLATFORMTHEME: gtk3 + + aft-mtp-cli: + command: bin/aft-mtp-cli + aft-mtp-mount: + command: bin/aft-mtp-mount + + parts: + desktop-qt5: + source: https://github.com/ubuntu/snapcraft-desktop-helpers.git + source-subdir: qt + plugin: make + make-parameters: ["FLAVOR=qt5"] + build-packages: + - build-essential + - qtbase5-dev + - dpkg-dev + stage-packages: + - libxkbcommon0 + - ttf-ubuntu-font-family + - dmz-cursor-theme + - light-themes + - adwaita-icon-theme + - gnome-themes-standard + - shared-mime-info + - libqt5gui5 + - qtwayland5 + - libgdk-pixbuf2.0-0 + - libqt5svg5 # for loading icon themes which are svg + - try: [appmenu-qt5] # not available on core18 + - locales-all + - xdg-user-dirs + - fcitx-frontend-qt5 + + aftl: + # See 'snapcraft plugins' + source: . + after: [desktop-qt5] + plugin: cmake + + configflags: + - -DCMAKE_BUILD_TYPE=Release + + build-packages: + - qt5-default + - qttools5-dev + - qttools5-dev-tools + - libfuse-dev + - libmagic-dev + - libtag1-dev + - libssl-dev + - libreadline-dev + - pkg-config + + stage-packages: + - libqt5gui5 + - libqt5widgets5 + - libqt5network5 + - libfuse-dev + - libmagic-dev + - libtag1-dev + - libssl-dev + - libreadline-dev + + plugs: + # Support for common GTK themes + # https://forum.snapcraft.io/t/how-to-use-the-system-gtk-theme-via-the-gtk-common-themes-snap/6235 + gtk-3-themes: + interface: content + target: $SNAP/data-dir/themes + default-provider: gtk-common-themes + icon-themes: + interface: content + target: $SNAP/data-dir/icons + default-provider: gtk-common-themes + sound-themes: + interface: content + target: $SNAP/data-dir/sounds + default-provider: gtk-common-themes diff -Nru android-file-transfer-3.9+dfsg/.travis.yml android-file-transfer-4.2/.travis.yml --- android-file-transfer-3.9+dfsg/.travis.yml 2019-05-24 11:25:21.000000000 +0000 +++ android-file-transfer-4.2/.travis.yml 2020-12-29 16:10:28.000000000 +0000 @@ -7,24 +7,20 @@ include: - os: linux compiler: gcc + - os: freebsd + compiler: clang - os: osx osx_image: xcode10.1 compiler: clang -before_install: - - | - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then - sudo add-apt-repository ppa:beineri/opt-qt592-trusty -y && - sudo apt-get update -qq - fi - install: - | if [ "${TRAVIS_OS_NAME}" = "linux" ]; then - sudo apt-get -y install qt59base libfuse-dev libmagic-dev && - source /opt/qt*/bin/qt*-env.sh + sudo apt-get -y install qt5-default qttools5-dev qttools5-dev-tools libfuse-dev libmagic-dev libtag1-dev libssl-dev + elif [ "${TRAVIS_OS_NAME}" = "freebsd" ]; then + sudo pkg install -y ninja qt5-qmake qt5-buildtools qt5-widgets qt5-linguist fusefs-libs pybind11 taglib elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then - brew tap homebrew/cask && brew install qt5 homebrew/cask/osxfuse + brew tap homebrew/cask && brew install qt5 homebrew/cask/osxfuse pybind11 taglib openssl@1.1 fi script: @@ -40,15 +36,16 @@ export VERSION=$(git rev-parse --short HEAD) && ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -bundle-non-qt-libs && ./linuxdeployqt-continuous-x86_64.AppImage appdir/usr/share/applications/*.desktop -appimage + elif [ "${TRAVIS_OS_NAME}" = "freebsd" ]; then + cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DUSB_BACKEND_LIBUSB=YES -DBUILD_PYTHON=OFF + cmake --build build elif [ "${TRAVIS_OS_NAME}" = "osx" ]; then mkdir build && cd build && - cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5 -DCMAKE_INSTALL_PREFIX=$(pwd)/installdir && + cmake .. -DCMAKE_OSX_DEPLOYMENT_TARGET=10.12 -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/usr/local/opt/qt5 -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 -DOPENSSL_LIBRARIES=/usr/local/opt/openssl@1.1/lib -DCMAKE_INSTALL_PREFIX=$(pwd)/installdir && make -j$(sysctl -n hw.ncpu) && make install && mv installdir/android-file-transfer.app installdir/Android\ File\ Transfer\ for\ Linux.app && - mkdir -p installdir/Android\ File\ Transfer\ for\ Linux.app/Contents/SharedSupport/ && - mv installdir/bin installdir/Android\ File\ Transfer\ for\ Linux.app/Contents/SharedSupport/ && git clone https://github.com/andreyvit/create-dmg.git && cd create-dmg && ./create-dmg --volicon "../../osx/android-file-transfer.icns" --icon-size 96 --icon "Android File Transfer for Linux" 110 100 --app-drop-link 380 100 AndroidFileTransferForLinux.dmg ../installdir/Android\ File\ Transfer\ for\ Linux.app