/* * Copyright (C) 2005-2018 Team Kodi * This file is part of Kodi - https://kodi.tv * * SPDX-License-Identifier: GPL-2.0-or-later * See LICENSES/README.md for more information. */ #include "Filesystem.h" #include "FileItem.h" #include "Util.h" #include "addons/binary-addons/AddonDll.h" #include "filesystem/CurlFile.h" #include "filesystem/Directory.h" #include "filesystem/File.h" #include "filesystem/SpecialProtocol.h" #include "platform/Filesystem.h" #include "utils/Crc32.h" #include "utils/HttpHeader.h" #include "utils/StringUtils.h" #include "utils/URIUtils.h" #include "utils/log.h" #include #if defined(TARGET_WINDOWS) #ifndef S_IFLNK #define S_IFLNK 0120000 #endif #ifndef S_ISBLK #define S_ISBLK(m) (0) #endif #ifndef S_ISSOCK #define S_ISSOCK(m) (0) #endif #ifndef S_ISLNK #define S_ISLNK(m) ((m & S_IFLNK) != 0) #endif #ifndef S_ISCHR #define S_ISCHR(m) ((m & _S_IFCHR) != 0) #endif #ifndef S_ISDIR #define S_ISDIR(m) ((m & _S_IFDIR) != 0) #endif #ifndef S_ISFIFO #define S_ISFIFO(m) ((m & _S_IFIFO) != 0) #endif #ifndef S_ISREG #define S_ISREG(m) ((m & _S_IFREG) != 0) #endif #endif using namespace kodi; // addon-dev-kit namespace using namespace XFILE; extern "C" { namespace ADDON { void Interface_Filesystem::Init(AddonGlobalInterface* addonInterface) { addonInterface->toKodi->kodi_filesystem = new AddonToKodiFuncTable_kodi_filesystem(); addonInterface->toKodi->kodi_filesystem->can_open_directory = can_open_directory; addonInterface->toKodi->kodi_filesystem->create_directory = create_directory; addonInterface->toKodi->kodi_filesystem->directory_exists = directory_exists; addonInterface->toKodi->kodi_filesystem->remove_directory = remove_directory; addonInterface->toKodi->kodi_filesystem->remove_directory_recursive = remove_directory_recursive; addonInterface->toKodi->kodi_filesystem->get_directory = get_directory; addonInterface->toKodi->kodi_filesystem->free_directory = free_directory; addonInterface->toKodi->kodi_filesystem->file_exists = file_exists; addonInterface->toKodi->kodi_filesystem->stat_file = stat_file; addonInterface->toKodi->kodi_filesystem->delete_file = delete_file; addonInterface->toKodi->kodi_filesystem->rename_file = rename_file; addonInterface->toKodi->kodi_filesystem->copy_file = copy_file; addonInterface->toKodi->kodi_filesystem->get_file_md5 = get_file_md5; addonInterface->toKodi->kodi_filesystem->get_cache_thumb_name = get_cache_thumb_name; addonInterface->toKodi->kodi_filesystem->make_legal_filename = make_legal_filename; addonInterface->toKodi->kodi_filesystem->make_legal_path = make_legal_path; addonInterface->toKodi->kodi_filesystem->translate_special_protocol = translate_special_protocol; addonInterface->toKodi->kodi_filesystem->get_disk_space = get_disk_space; addonInterface->toKodi->kodi_filesystem->is_internet_stream = is_internet_stream; addonInterface->toKodi->kodi_filesystem->is_on_lan = is_on_lan; addonInterface->toKodi->kodi_filesystem->is_remote = is_remote; addonInterface->toKodi->kodi_filesystem->is_local = is_local; addonInterface->toKodi->kodi_filesystem->is_url = is_url; addonInterface->toKodi->kodi_filesystem->get_http_header = get_http_header; addonInterface->toKodi->kodi_filesystem->get_mime_type = get_mime_type; addonInterface->toKodi->kodi_filesystem->get_content_type = get_content_type; addonInterface->toKodi->kodi_filesystem->get_cookies = get_cookies; addonInterface->toKodi->kodi_filesystem->http_header_create = http_header_create; addonInterface->toKodi->kodi_filesystem->http_header_free = http_header_free; addonInterface->toKodi->kodi_filesystem->open_file = open_file; addonInterface->toKodi->kodi_filesystem->open_file_for_write = open_file_for_write; addonInterface->toKodi->kodi_filesystem->read_file = read_file; addonInterface->toKodi->kodi_filesystem->read_file_string = read_file_string; addonInterface->toKodi->kodi_filesystem->write_file = write_file; addonInterface->toKodi->kodi_filesystem->flush_file = flush_file; addonInterface->toKodi->kodi_filesystem->seek_file = seek_file; addonInterface->toKodi->kodi_filesystem->truncate_file = truncate_file; addonInterface->toKodi->kodi_filesystem->get_file_position = get_file_position; addonInterface->toKodi->kodi_filesystem->get_file_length = get_file_length; addonInterface->toKodi->kodi_filesystem->get_file_download_speed = get_file_download_speed; addonInterface->toKodi->kodi_filesystem->close_file = close_file; addonInterface->toKodi->kodi_filesystem->get_file_chunk_size = get_file_chunk_size; addonInterface->toKodi->kodi_filesystem->io_control_get_seek_possible = io_control_get_seek_possible; addonInterface->toKodi->kodi_filesystem->io_control_get_cache_status = io_control_get_cache_status; addonInterface->toKodi->kodi_filesystem->io_control_set_cache_rate = io_control_set_cache_rate; addonInterface->toKodi->kodi_filesystem->io_control_set_retry = io_control_set_retry; addonInterface->toKodi->kodi_filesystem->get_property_values = get_property_values; addonInterface->toKodi->kodi_filesystem->curl_create = curl_create; addonInterface->toKodi->kodi_filesystem->curl_add_option = curl_add_option; addonInterface->toKodi->kodi_filesystem->curl_open = curl_open; } void Interface_Filesystem::DeInit(AddonGlobalInterface* addonInterface) { if (addonInterface->toKodi) /* <-- Safe check, needed so long old addon way is present */ { delete addonInterface->toKodi->kodi_filesystem; addonInterface->toKodi->kodi_filesystem = nullptr; } } unsigned int Interface_Filesystem::TranslateFileReadBitsToKodi(unsigned int addonFlags) { unsigned int kodiFlags = 0; if (addonFlags & ADDON_READ_TRUNCATED) kodiFlags |= READ_TRUNCATED; if (addonFlags & ADDON_READ_CHUNKED) kodiFlags |= READ_CHUNKED; if (addonFlags & ADDON_READ_CACHED) kodiFlags |= READ_CACHED; if (addonFlags & ADDON_READ_NO_CACHE) kodiFlags |= READ_NO_CACHE; if (addonFlags & ADDON_READ_BITRATE) kodiFlags |= READ_BITRATE; if (addonFlags & ADDON_READ_MULTI_STREAM) kodiFlags |= READ_MULTI_STREAM; if (addonFlags & ADDON_READ_AUDIO_VIDEO) kodiFlags |= READ_AUDIO_VIDEO; if (addonFlags & ADDON_READ_AFTER_WRITE) kodiFlags |= READ_AFTER_WRITE; if (addonFlags & READ_REOPEN) kodiFlags |= READ_REOPEN; return kodiFlags; } bool Interface_Filesystem::can_open_directory(void* kodiBase, const char* url) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || url == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', url='{}')", __FUNCTION__, kodiBase, static_cast(url)); return false; } CFileItemList items; return CDirectory::GetDirectory(url, items, "", DIR_FLAG_DEFAULTS | DIR_FLAG_BYPASS_CACHE); } bool Interface_Filesystem::create_directory(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{}')", __FUNCTION__, kodiBase, static_cast(path)); return false; } return CDirectory::Create(path); } bool Interface_Filesystem::directory_exists(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{}')", __FUNCTION__, kodiBase, static_cast(path)); return false; } return CDirectory::Exists(path, false); } bool Interface_Filesystem::remove_directory(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{}')", __FUNCTION__, kodiBase, static_cast(path)); return false; } // Empty directory CFileItemList fileItems; CDirectory::GetDirectory(path, fileItems, "", DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE); for (int i = 0; i < fileItems.Size(); ++i) CFile::Delete(fileItems.Get(i)->GetPath()); return CDirectory::Remove(path); } bool Interface_Filesystem::remove_directory_recursive(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{}')", __FUNCTION__, kodiBase, static_cast(path)); return false; } return CDirectory::RemoveRecursive(path); } static void CFileItemListToVFSDirEntries(VFSDirEntry* entries, const CFileItemList& items) { for (unsigned int i = 0; i < static_cast(items.Size()); ++i) { entries[i].label = strdup(items[i]->GetLabel().c_str()); entries[i].path = strdup(items[i]->GetPath().c_str()); entries[i].size = items[i]->m_dwSize; entries[i].folder = items[i]->m_bIsFolder; items[i]->m_dateTime.GetAsTime(entries[i].date_time); } } bool Interface_Filesystem::get_directory(void* kodiBase, const char* path, const char* mask, struct VFSDirEntry** items, unsigned int* num_items) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr || mask == nullptr || items == nullptr || num_items == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{}', mask='{}', " "items='{}', num_items='{}'", __FUNCTION__, kodiBase, static_cast(path), static_cast(mask), static_cast(items), static_cast(num_items)); return false; } CFileItemList fileItems; if (!CDirectory::GetDirectory(path, fileItems, mask, DIR_FLAG_NO_FILE_DIRS | DIR_FLAG_BYPASS_CACHE)) return false; if (fileItems.Size() > 0) { *num_items = static_cast(fileItems.Size()); *items = new VFSDirEntry[fileItems.Size()]; CFileItemListToVFSDirEntries(*items, fileItems); } else { *num_items = 0; *items = nullptr; } return true; } void Interface_Filesystem::free_directory(void* kodiBase, struct VFSDirEntry* items, unsigned int num_items) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || items == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', items='{}')", __FUNCTION__, kodiBase, static_cast(items)); return; } for (unsigned int i = 0; i < num_items; ++i) { free(items[i].label); free(items[i].path); } delete[] items; } //------------------------------------------------------------------------------ bool Interface_Filesystem::file_exists(void* kodiBase, const char* filename, bool useCache) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}')", __FUNCTION__, kodiBase, static_cast(filename)); return false; } return CFile::Exists(filename, useCache); } bool Interface_Filesystem::stat_file(void* kodiBase, const char* filename, struct STAT_STRUCTURE* buffer) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr || buffer == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}', buffer='{}')", __FUNCTION__, kodiBase, static_cast(filename), static_cast(buffer)); return false; } struct __stat64 statBuffer; if (CFile::Stat(filename, &statBuffer) != 0) return false; buffer->deviceId = statBuffer.st_dev; buffer->fileSerialNumber = statBuffer.st_ino; buffer->size = statBuffer.st_size; buffer->accessTime = statBuffer.st_atime; buffer->modificationTime = statBuffer.st_mtime; buffer->statusTime = statBuffer.st_ctime; buffer->isDirectory = S_ISDIR(statBuffer.st_mode); buffer->isSymLink = S_ISLNK(statBuffer.st_mode); buffer->isBlock = S_ISBLK(statBuffer.st_mode); buffer->isCharacter = S_ISCHR(statBuffer.st_mode); buffer->isFifo = S_ISFIFO(statBuffer.st_mode); buffer->isRegular = S_ISREG(statBuffer.st_mode); buffer->isSocket = S_ISSOCK(statBuffer.st_mode); return true; } bool Interface_Filesystem::delete_file(void* kodiBase, const char* filename) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}')", __FUNCTION__, kodiBase, static_cast(filename)); return false; } return CFile::Delete(filename); } bool Interface_Filesystem::rename_file(void* kodiBase, const char* filename, const char* newFileName) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr || newFileName == nullptr) { CLog::Log( LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}', newFileName='{}')", __FUNCTION__, kodiBase, static_cast(filename), static_cast(newFileName)); return false; } return CFile::Rename(filename, newFileName); } bool Interface_Filesystem::copy_file(void* kodiBase, const char* filename, const char* dest) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr || dest == nullptr) { CLog::Log( LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}', dest='{}')", __FUNCTION__, kodiBase, static_cast(filename), static_cast(dest)); return false; } return CFile::Copy(filename, dest); } char* Interface_Filesystem::get_file_md5(void* kodiBase, const char* filename) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{})", __FUNCTION__, kodiBase, static_cast(filename)); return nullptr; } std::string string = CUtil::GetFileDigest(filename, KODI::UTILITY::CDigest::Type::MD5); char* buffer = strdup(string.c_str()); return buffer; } char* Interface_Filesystem::get_cache_thumb_name(void* kodiBase, const char* filename) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{})", __FUNCTION__, kodiBase, static_cast(filename)); return nullptr; } const auto crc = Crc32::ComputeFromLowerCase(filename); const auto hex = StringUtils::Format("{:08x}.tbn", crc); char* buffer = strdup(hex.c_str()); return buffer; } char* Interface_Filesystem::make_legal_filename(void* kodiBase, const char* filename) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{})", __FUNCTION__, kodiBase, static_cast(filename)); return nullptr; } std::string string = CUtil::MakeLegalFileName(filename); char* buffer = strdup(string.c_str()); return buffer; } char* Interface_Filesystem::make_legal_path(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{})", __FUNCTION__, kodiBase, static_cast(path)); return nullptr; } std::string string = CUtil::MakeLegalPath(path); char* buffer = strdup(string.c_str()); return buffer; } char* Interface_Filesystem::translate_special_protocol(void* kodiBase, const char* strSource) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || strSource == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', strSource='{})", __FUNCTION__, kodiBase, static_cast(strSource)); return nullptr; } return strdup(CSpecialProtocol::TranslatePath(strSource).c_str()); } bool Interface_Filesystem::get_disk_space( void* kodiBase, const char* path, uint64_t* capacity, uint64_t* free, uint64_t* available) { using namespace KODI::PLATFORM::FILESYSTEM; CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr || capacity == nullptr || free == nullptr || available == nullptr) { CLog::Log( LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{}, capacity='{}, free='{}, " "available='{})", __FUNCTION__, kodiBase, static_cast(path), static_cast(capacity), static_cast(free), static_cast(available)); return false; } std::error_code ec; auto freeSpace = space(CSpecialProtocol::TranslatePath(path), ec); if (ec.value() != 0) return false; *capacity = freeSpace.capacity; *free = freeSpace.free; *available = freeSpace.available; return true; } bool Interface_Filesystem::is_internet_stream(void* kodiBase, const char* path, bool strictCheck) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{})", __FUNCTION__, kodiBase, static_cast(path)); return false; } return URIUtils::IsInternetStream(path, strictCheck); } bool Interface_Filesystem::is_on_lan(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{})", __FUNCTION__, kodiBase, static_cast(path)); return false; } return URIUtils::IsOnLAN(path); } bool Interface_Filesystem::is_remote(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{})", __FUNCTION__, kodiBase, static_cast(path)); return false; } return URIUtils::IsRemote(path); } bool Interface_Filesystem::is_local(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{})", __FUNCTION__, kodiBase, static_cast(path)); return false; } return CURL(path).IsLocal(); } bool Interface_Filesystem::is_url(void* kodiBase, const char* path) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || path == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', path='{})", __FUNCTION__, kodiBase, static_cast(path)); return false; } return URIUtils::IsURL(path); } bool Interface_Filesystem::get_mime_type(void* kodiBase, const char* url, char** content, const char* useragent) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || url == nullptr || content == nullptr || useragent == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', url='{}', content='{}', " "useragent='{}')", __FUNCTION__, kodiBase, static_cast(url), static_cast(content), static_cast(useragent)); return false; } std::string kodiContent; bool ret = XFILE::CCurlFile::GetMimeType(CURL(url), kodiContent, useragent); if (ret && !kodiContent.empty()) { *content = strdup(kodiContent.c_str()); } return ret; } bool Interface_Filesystem::get_content_type(void* kodiBase, const char* url, char** content, const char* useragent) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || url == nullptr || content == nullptr || useragent == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', url='{}', content='{}', " "useragent='{}')", __FUNCTION__, kodiBase, static_cast(url), static_cast(content), static_cast(useragent)); return false; } std::string kodiContent; bool ret = XFILE::CCurlFile::GetContentType(CURL(url), kodiContent, useragent); if (ret && !kodiContent.empty()) { *content = strdup(kodiContent.c_str()); } return ret; } bool Interface_Filesystem::get_cookies(void* kodiBase, const char* url, char** cookies) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || url == nullptr || cookies == nullptr) { CLog::Log( LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', url='{}', cookies='{}')", __FUNCTION__, kodiBase, static_cast(url), static_cast(cookies)); return false; } std::string kodiCookies; bool ret = XFILE::CCurlFile::GetCookies(CURL(url), kodiCookies); if (ret && !kodiCookies.empty()) { *cookies = strdup(kodiCookies.c_str()); } return ret; } bool Interface_Filesystem::get_http_header(void* kodiBase, const char* url, struct KODI_HTTP_HEADER* headers) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || url == nullptr || headers == nullptr || headers->handle == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data pointer given", __func__); return false; } CHttpHeader* httpHeader = static_cast(headers->handle); return XFILE::CCurlFile::GetHttpHeader(CURL(url), *httpHeader); } //------------------------------------------------------------------------------ bool Interface_Filesystem::http_header_create(void* kodiBase, struct KODI_HTTP_HEADER* headers) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || headers == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', headers='{}')", __FUNCTION__, kodiBase, static_cast(headers)); return false; } headers->handle = new CHttpHeader; headers->get_value = http_header_get_value; headers->get_values = http_header_get_values; headers->get_header = http_header_get_header; headers->get_mime_type = http_header_get_mime_type; headers->get_charset = http_header_get_charset; headers->get_proto_line = http_header_get_proto_line; return true; } void Interface_Filesystem::http_header_free(void* kodiBase, struct KODI_HTTP_HEADER* headers) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || headers == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', headers='{}')", __FUNCTION__, kodiBase, static_cast(headers)); return; } delete static_cast(headers->handle); headers->handle = nullptr; } char* Interface_Filesystem::http_header_get_value(void* kodiBase, void* handle, const char* param) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || handle == nullptr || param == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', handle='{}', param='{}')", __FUNCTION__, kodiBase, handle, static_cast(param)); return nullptr; } std::string string = static_cast(handle)->GetValue(param); char* buffer = nullptr; if (!string.empty()) buffer = strdup(string.c_str()); return buffer; } char** Interface_Filesystem::http_header_get_values(void* kodiBase, void* handle, const char* param, int* length) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || handle == nullptr || param == nullptr || length == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', handle='{}', param='{}', " "length='{}')", __FUNCTION__, kodiBase, handle, static_cast(param), static_cast(length)); return nullptr; } std::vector values = static_cast(handle)->GetValues(param); *length = values.size(); char** ret = static_cast(malloc(sizeof(char*) * values.size())); for (int i = 0; i < *length; ++i) { ret[i] = strdup(values[i].c_str()); } return ret; } char* Interface_Filesystem::http_header_get_header(void* kodiBase, void* handle) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || handle == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', handle='{}')", __FUNCTION__, kodiBase, handle); return nullptr; } std::string string = static_cast(handle)->GetHeader(); char* buffer = nullptr; if (!string.empty()) buffer = strdup(string.c_str()); return buffer; } char* Interface_Filesystem::http_header_get_mime_type(void* kodiBase, void* handle) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || handle == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', handle='{}')", __FUNCTION__, kodiBase, handle); return nullptr; } std::string string = static_cast(handle)->GetMimeType(); char* buffer = nullptr; if (!string.empty()) buffer = strdup(string.c_str()); return buffer; } char* Interface_Filesystem::http_header_get_charset(void* kodiBase, void* handle) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || handle == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', handle='{}')", __FUNCTION__, kodiBase, handle); return nullptr; } std::string string = static_cast(handle)->GetCharset(); char* buffer = nullptr; if (!string.empty()) buffer = strdup(string.c_str()); return buffer; } char* Interface_Filesystem::http_header_get_proto_line(void* kodiBase, void* handle) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || handle == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', handle='{}')", __FUNCTION__, kodiBase, handle); return nullptr; } std::string string = static_cast(handle)->GetProtoLine(); char* buffer = nullptr; if (!string.empty()) buffer = strdup(string.c_str()); return buffer; } //------------------------------------------------------------------------------ void* Interface_Filesystem::open_file(void* kodiBase, const char* filename, unsigned int flags) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}')", __FUNCTION__, kodiBase, static_cast(filename)); return nullptr; } CFile* file = new CFile; if (file->Open(filename, TranslateFileReadBitsToKodi(flags))) return static_cast(file); delete file; return nullptr; } void* Interface_Filesystem::open_file_for_write(void* kodiBase, const char* filename, bool overwrite) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || filename == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', filename='{}')", __FUNCTION__, kodiBase, static_cast(filename)); return nullptr; } CFile* file = new CFile; if (file->OpenForWrite(filename, overwrite)) return static_cast(file); delete file; return nullptr; } ssize_t Interface_Filesystem::read_file(void* kodiBase, void* file, void* ptr, size_t size) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr || ptr == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}', ptr='{}')", __FUNCTION__, kodiBase, file, ptr); return -1; } return static_cast(file)->Read(ptr, size); } bool Interface_Filesystem::read_file_string(void* kodiBase, void* file, char* szLine, int lineLength) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr || szLine == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}', szLine=='{}')", __FUNCTION__, kodiBase, file, static_cast(szLine)); return false; } return static_cast(file)->ReadString(szLine, lineLength); } ssize_t Interface_Filesystem::write_file(void* kodiBase, void* file, const void* ptr, size_t size) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr || ptr == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}', ptr='{}')", __FUNCTION__, kodiBase, file, ptr); return -1; } return static_cast(file)->Write(ptr, size); } void Interface_Filesystem::flush_file(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return; } static_cast(file)->Flush(); } int64_t Interface_Filesystem::seek_file(void* kodiBase, void* file, int64_t position, int whence) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return -1; } return static_cast(file)->Seek(position, whence); } int Interface_Filesystem::truncate_file(void* kodiBase, void* file, int64_t size) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return -1; } return static_cast(file)->Truncate(size); } int64_t Interface_Filesystem::get_file_position(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return -1; } return static_cast(file)->GetPosition(); } int64_t Interface_Filesystem::get_file_length(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return -1; } return static_cast(file)->GetLength(); } double Interface_Filesystem::get_file_download_speed(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return 0.0; } return static_cast(file)->GetDownloadSpeed(); } void Interface_Filesystem::close_file(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return; } static_cast(file)->Close(); delete static_cast(file); } int Interface_Filesystem::get_file_chunk_size(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_VFS::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return -1; } return static_cast(file)->GetChunkSize(); } bool Interface_Filesystem::io_control_get_seek_possible(void* kodiBase, void* file) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_VFS::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return false; } return static_cast(file)->IoControl(EIoControl::IOCTRL_SEEK_POSSIBLE, nullptr) != 0 ? true : false; } bool Interface_Filesystem::io_control_get_cache_status(void* kodiBase, void* file, struct VFS_CACHE_STATUS_DATA* status) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr || status == nullptr) { CLog::Log(LOGERROR, "Interface_VFS::{} - invalid data (addon='{}', file='{}, status='{}')", __FUNCTION__, kodiBase, file, static_cast(status)); return false; } SCacheStatus data = {}; int ret = static_cast(file)->IoControl(EIoControl::IOCTRL_CACHE_STATUS, &data); if (ret >= 0) { status->forward = data.forward; status->maxrate = data.maxrate; status->currate = data.currate; status->lowrate = data.lowrate; return true; } return false; } bool Interface_Filesystem::io_control_set_cache_rate(void* kodiBase, void* file, uint32_t rate) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_VFS::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return false; } return static_cast(file)->IoControl(EIoControl::IOCTRL_CACHE_SETRATE, &rate) >= 0 ? true : false; } bool Interface_Filesystem::io_control_set_retry(void* kodiBase, void* file, bool retry) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_VFS::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return false; } return static_cast(file)->IoControl(EIoControl::IOCTRL_SET_RETRY, &retry) >= 0 ? true : false; } char** Interface_Filesystem::get_property_values( void* kodiBase, void* file, int type, const char* name, int* numValues) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr || name == nullptr || numValues == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}', name='{}', " "numValues='{}')", __FUNCTION__, kodiBase, file, static_cast(name), static_cast(numValues)); return nullptr; } XFILE::FileProperty internalType; switch (type) { case ADDON_FILE_PROPERTY_RESPONSE_PROTOCOL: internalType = XFILE::FILE_PROPERTY_RESPONSE_PROTOCOL; break; case ADDON_FILE_PROPERTY_RESPONSE_HEADER: internalType = XFILE::FILE_PROPERTY_RESPONSE_HEADER; break; case ADDON_FILE_PROPERTY_CONTENT_TYPE: internalType = XFILE::FILE_PROPERTY_CONTENT_TYPE; break; case ADDON_FILE_PROPERTY_CONTENT_CHARSET: internalType = XFILE::FILE_PROPERTY_CONTENT_CHARSET; break; case ADDON_FILE_PROPERTY_MIME_TYPE: internalType = XFILE::FILE_PROPERTY_MIME_TYPE; break; case ADDON_FILE_PROPERTY_EFFECTIVE_URL: internalType = XFILE::FILE_PROPERTY_EFFECTIVE_URL; break; default: CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return nullptr; }; std::vector values = static_cast(file)->GetPropertyValues(internalType, name); *numValues = values.size(); char** ret = static_cast(malloc(sizeof(char*) * values.size())); for (int i = 0; i < *numValues; ++i) { ret[i] = strdup(values[i].c_str()); } return ret; } void* Interface_Filesystem::curl_create(void* kodiBase, const char* url) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || url == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', url='{}')", __FUNCTION__, kodiBase, static_cast(url)); return nullptr; } CFile* file = new CFile; if (file->CURLCreate(url)) return static_cast(file); delete file; return nullptr; } bool Interface_Filesystem::curl_add_option( void* kodiBase, void* file, int type, const char* name, const char* value) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr || name == nullptr || value == nullptr) { CLog::Log( LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}', name='{}', value='{}')", __FUNCTION__, kodiBase, file, static_cast(name), static_cast(value)); return false; } XFILE::CURLOPTIONTYPE internalType; switch (type) { case ADDON_CURL_OPTION_OPTION: internalType = XFILE::CURL_OPTION_OPTION; break; case ADDON_CURL_OPTION_PROTOCOL: internalType = XFILE::CURL_OPTION_PROTOCOL; break; case ADDON_CURL_OPTION_CREDENTIALS: internalType = XFILE::CURL_OPTION_CREDENTIALS; break; case ADDON_CURL_OPTION_HEADER: internalType = XFILE::CURL_OPTION_HEADER; break; default: throw std::logic_error("Interface_Filesystem::curl_add_option - invalid curl option type"); return false; }; return static_cast(file)->CURLAddOption(internalType, name, value); } bool Interface_Filesystem::curl_open(void* kodiBase, void* file, unsigned int flags) { CAddonDll* addon = static_cast(kodiBase); if (addon == nullptr || file == nullptr) { CLog::Log(LOGERROR, "Interface_Filesystem::{} - invalid data (addon='{}', file='{}')", __FUNCTION__, kodiBase, file); return false; } return static_cast(file)->CURLOpen(TranslateFileReadBitsToKodi(flags)); } } /* namespace ADDON */ } /* extern "C" */