summaryrefslogtreecommitdiffstats
path: root/xbmc/addons/binary-addons/AddonDll.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/addons/binary-addons/AddonDll.cpp')
-rw-r--r--xbmc/addons/binary-addons/AddonDll.cpp553
1 files changed, 553 insertions, 0 deletions
diff --git a/xbmc/addons/binary-addons/AddonDll.cpp b/xbmc/addons/binary-addons/AddonDll.cpp
new file mode 100644
index 0000000..4ff5254
--- /dev/null
+++ b/xbmc/addons/binary-addons/AddonDll.cpp
@@ -0,0 +1,553 @@
+/*
+ * 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 "AddonDll.h"
+
+#include "ServiceBroker.h"
+#include "addons/AddonStatusHandler.h"
+#include "addons/addoninfo/AddonInfo.h"
+#include "addons/binary-addons/BinaryAddonBase.h"
+#include "addons/binary-addons/BinaryAddonManager.h"
+#include "addons/binary-addons/DllAddon.h"
+#include "addons/interfaces/AddonBase.h"
+#include "addons/kodi-dev-kit/include/kodi/versions.h"
+#include "addons/settings/AddonSettings.h"
+#include "events/EventLog.h"
+#include "events/NotificationEvent.h"
+#include "filesystem/File.h"
+#include "filesystem/SpecialProtocol.h"
+#include "messaging/helpers/DialogOKHelper.h"
+#include "settings/lib/SettingSection.h"
+#include "utils/URIUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <utility>
+
+using namespace KODI::MESSAGING;
+
+namespace ADDON
+{
+
+CAddonDll::CAddonDll(const AddonInfoPtr& addonInfo, BinaryAddonBasePtr addonBase)
+ : CAddon(addonInfo, addonInfo->MainType()), m_binaryAddonBase(std::move(addonBase))
+{
+}
+
+CAddonDll::CAddonDll(const AddonInfoPtr& addonInfo, AddonType addonType)
+ : CAddon(addonInfo, addonType),
+ m_binaryAddonBase(CServiceBroker::GetBinaryAddonManager().GetRunningAddonBase(addonInfo->ID()))
+{
+}
+
+CAddonDll::~CAddonDll()
+{
+ if (m_initialized)
+ Destroy();
+}
+
+std::string CAddonDll::GetDllPath(const std::string &libPath)
+{
+ std::string strFileName = libPath;
+ std::string strLibName = URIUtils::GetFileName(strFileName);
+
+ if (strLibName.empty())
+ return "";
+
+ /* Check if lib being loaded exists, else check in XBMC binary location */
+#if defined(TARGET_ANDROID)
+ if (XFILE::CFile::Exists(strFileName))
+ {
+ bool doCopy = true;
+ std::string dstfile = URIUtils::AddFileToFolder(CSpecialProtocol::TranslatePath("special://xbmcaltbinaddons/"), strLibName);
+
+ struct __stat64 dstFileStat;
+ if (XFILE::CFile::Stat(dstfile, &dstFileStat) == 0)
+ {
+ struct __stat64 srcFileStat;
+ if (XFILE::CFile::Stat(strFileName, &srcFileStat) == 0)
+ {
+ if (dstFileStat.st_size == srcFileStat.st_size && dstFileStat.st_mtime > srcFileStat.st_mtime)
+ doCopy = false;
+ }
+ }
+
+ if (doCopy)
+ {
+ CLog::Log(LOGDEBUG, "ADDON: caching {} to {}", strFileName, dstfile);
+ XFILE::CFile::Copy(strFileName, dstfile);
+ }
+
+ strFileName = dstfile;
+ }
+ if (!XFILE::CFile::Exists(strFileName))
+ {
+ std::string tempbin = getenv("KODI_ANDROID_LIBS");
+ strFileName = tempbin + "/" + strLibName;
+ }
+#endif
+
+ if (!XFILE::CFile::Exists(strFileName))
+ {
+ std::string strAltFileName;
+
+ std::string altbin = CSpecialProtocol::TranslatePath("special://xbmcaltbinaddons/");
+ if (!altbin.empty())
+ {
+ strAltFileName = altbin + strLibName;
+ if (!XFILE::CFile::Exists(strAltFileName))
+ {
+ std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/addons/");
+ strAltFileName = strFileName;
+ strAltFileName.erase(0, temp.size());
+ strAltFileName = altbin + strAltFileName;
+ }
+ CLog::Log(LOGDEBUG, "ADDON: Trying to load {}", strAltFileName);
+ }
+
+ if (XFILE::CFile::Exists(strAltFileName))
+ strFileName = strAltFileName;
+ else
+ {
+ std::string temp = CSpecialProtocol::TranslatePath("special://xbmc/");
+ std::string tempbin = CSpecialProtocol::TranslatePath("special://xbmcbin/");
+ strFileName.erase(0, temp.size());
+ strFileName = tempbin + strFileName;
+ if (!XFILE::CFile::Exists(strFileName))
+ {
+ CLog::Log(LOGERROR, "ADDON: Could not locate {}", strLibName);
+ strFileName.clear();
+ }
+ }
+ }
+
+ return strFileName;
+}
+
+std::string CAddonDll::LibPath() const
+{
+ return GetDllPath(CAddon::LibPath());
+}
+
+bool CAddonDll::LoadDll()
+{
+ if (m_pDll)
+ return true;
+
+ std::string strFileName = LibPath();
+ if (strFileName.empty())
+ return false;
+
+ /* Load the Dll */
+ m_pDll = new DllAddon;
+ m_pDll->SetFile(strFileName);
+ m_pDll->EnableDelayedUnload(false);
+ if (!m_pDll->Load())
+ {
+ delete m_pDll;
+ m_pDll = nullptr;
+
+ std::string heading =
+ StringUtils::Format("{}: {}", CAddonInfo::TranslateType(Type(), true), Name());
+ HELPERS::ShowOKDialogLines(CVariant{heading}, CVariant{24070}, CVariant{24071});
+
+ return false;
+ }
+
+ return true;
+}
+
+ADDON_STATUS CAddonDll::Create(KODI_ADDON_INSTANCE_STRUCT* firstKodiInstance)
+{
+ CLog::Log(LOGDEBUG, "ADDON: Dll Initializing - {}", Name());
+ m_initialized = false;
+
+ if (!LoadDll())
+ {
+ return ADDON_STATUS_PERMANENT_FAILURE;
+ }
+
+ /* Check versions about global parts on add-on (parts used on all types) */
+ for (unsigned int id = ADDON_GLOBAL_MAIN; id <= ADDON_GLOBAL_MAX; ++id)
+ {
+ if (!CheckAPIVersion(id))
+ return ADDON_STATUS_PERMANENT_FAILURE;
+ }
+
+ /* Allocate the helper function class to allow crosstalk over
+ helper add-on headers */
+ if (!Interface_Base::InitInterface(this, m_interface, firstKodiInstance))
+ return ADDON_STATUS_PERMANENT_FAILURE;
+
+ /* Call Create to make connections, initializing data or whatever is
+ needed to become the AddOn running */
+ ADDON_STATUS status = m_pDll->Create(&m_interface);
+
+ // "C" ABI related call, if on add-on used.
+ if (status == ADDON_STATUS_OK && m_interface.toAddon->create)
+ status = m_interface.toAddon->create(m_interface.firstKodiInstance, &m_interface.addonBase);
+
+ if (status == ADDON_STATUS_OK)
+ {
+ m_initialized = true;
+ }
+ else if (status == ADDON_STATUS_NEED_SETTINGS)
+ {
+ if ((status = TransferSettings(ADDON_SETTINGS_ID)) == ADDON_STATUS_OK)
+ m_initialized = true;
+ else
+ new CAddonStatusHandler(ID(), ADDON_SETTINGS_ID, status, false);
+ }
+ else
+ { // Addon failed initialization
+ CLog::Log(LOGERROR,
+ "ADDON: Dll {} - Client returned bad status ({}) from Create and is not usable",
+ Name(), status);
+
+ // @todo currently a copy and paste from other function and becomes improved.
+ std::string heading =
+ StringUtils::Format("{}: {}", CAddonInfo::TranslateType(Type(), true), Name());
+ HELPERS::ShowOKDialogLines(CVariant{ heading }, CVariant{ 24070 }, CVariant{ 24071 });
+ }
+
+ return status;
+}
+
+void CAddonDll::Destroy()
+{
+ /* Unload library file */
+ if (m_pDll)
+ {
+ if (m_interface.toAddon->destroy)
+ m_interface.toAddon->destroy(m_interface.addonBase);
+ m_pDll->Unload();
+ }
+
+ Interface_Base::DeInitInterface(m_interface);
+
+ if (m_pDll)
+ {
+ delete m_pDll;
+ m_pDll = nullptr;
+ CLog::Log(LOGINFO, "ADDON: Dll Destroyed - {}", Name());
+ }
+
+ ResetSettings(ADDON_SETTINGS_ID);
+
+ m_initialized = false;
+}
+
+ADDON_STATUS CAddonDll::CreateInstance(KODI_ADDON_INSTANCE_STRUCT* instance)
+{
+ assert(instance != nullptr);
+ assert(instance->functions != nullptr);
+ assert(instance->info != nullptr);
+ assert(instance->info->functions != nullptr);
+
+ ADDON_STATUS status = ADDON_STATUS_OK;
+
+ if (!m_initialized)
+ status = Create(instance);
+ if (status != ADDON_STATUS_OK)
+ return status;
+
+ /* Check version of requested instance type */
+ if (!CheckAPIVersion(instance->info->type))
+ return ADDON_STATUS_PERMANENT_FAILURE;
+
+ status = m_interface.toAddon->create_instance(m_interface.addonBase, instance);
+
+ if (instance->info)
+ {
+ m_usedInstances[instance->info->kodi] = instance;
+ }
+
+ return status;
+}
+
+void CAddonDll::DestroyInstance(KODI_ADDON_INSTANCE_STRUCT* instance)
+{
+ if (m_usedInstances.empty())
+ return;
+
+ auto it = m_usedInstances.find(instance->info->kodi);
+ if (it != m_usedInstances.end())
+ {
+ m_interface.toAddon->destroy_instance(m_interface.addonBase, it->second);
+ m_usedInstances.erase(it);
+ }
+
+ if (m_usedInstances.empty())
+ Destroy();
+}
+
+bool CAddonDll::IsInUse() const
+{
+ if (m_informer)
+ return m_informer->IsInUse(ID());
+ return false;
+}
+
+void CAddonDll::RegisterInformer(CAddonDllInformer* informer)
+{
+ m_informer = informer;
+}
+
+AddonPtr CAddonDll::GetRunningInstance() const
+{
+ if (CServiceBroker::IsAddonInterfaceUp())
+ return CServiceBroker::GetBinaryAddonManager().GetRunningAddon(ID());
+
+ return AddonPtr();
+}
+
+void CAddonDll::OnPreInstall()
+{
+ if (m_binaryAddonBase)
+ m_binaryAddonBase->OnPreInstall();
+}
+
+void CAddonDll::OnPostInstall(bool update, bool modal)
+{
+ if (m_binaryAddonBase)
+ m_binaryAddonBase->OnPostInstall(update, modal);
+}
+
+void CAddonDll::OnPreUnInstall()
+{
+ if (m_binaryAddonBase)
+ m_binaryAddonBase->OnPreUnInstall();
+}
+
+void CAddonDll::OnPostUnInstall()
+{
+ if (m_binaryAddonBase)
+ m_binaryAddonBase->OnPostUnInstall();
+}
+
+bool CAddonDll::DllLoaded(void) const
+{
+ return m_pDll != nullptr;
+}
+
+CAddonVersion CAddonDll::GetTypeVersionDll(int type) const
+{
+ return CAddonVersion(m_pDll ? m_pDll->GetAddonTypeVersion(type) : nullptr);
+}
+
+CAddonVersion CAddonDll::GetTypeMinVersionDll(int type) const
+{
+ return CAddonVersion(m_pDll ? m_pDll->GetAddonTypeMinVersion(type) : nullptr);
+}
+
+void CAddonDll::SaveSettings(AddonInstanceId id /* = ADDON_SETTINGS_ID */)
+{
+ // must save first, as TransferSettings() reloads saved settings!
+ CAddon::SaveSettings(id);
+ if (m_initialized)
+ TransferSettings(id);
+}
+
+ADDON_STATUS CAddonDll::TransferSettings(AddonInstanceId instanceId)
+{
+ bool restart = false;
+ ADDON_STATUS reportStatus = ADDON_STATUS_OK;
+
+ CLog::Log(LOGDEBUG, "Calling TransferSettings for: {}", Name());
+
+ LoadSettings(false, true, instanceId);
+
+ auto settings = GetSettings(instanceId);
+ if (settings != nullptr)
+ {
+ KODI_ADDON_INSTANCE_FUNC* instanceTarget{nullptr};
+ KODI_ADDON_INSTANCE_HDL instanceHandle{nullptr};
+ if (instanceId != ADDON_SETTINGS_ID)
+ {
+ const auto it = std::find_if(
+ m_usedInstances.begin(), m_usedInstances.end(),
+ [instanceId](const auto& data) { return data.second->info->number == instanceId; });
+ if (it == m_usedInstances.end())
+ return ADDON_STATUS_UNKNOWN;
+
+ instanceTarget = it->second->functions;
+ instanceHandle = it->second->hdl;
+ }
+
+ for (const auto& section : settings->GetSections())
+ {
+ for (const auto& category : section->GetCategories())
+ {
+ for (const auto& group : category->GetGroups())
+ {
+ for (const auto& setting : group->GetSettings())
+ {
+ if (StringUtils::StartsWith(setting->GetId(), ADDON_SETTING_INSTANCE_GROUP))
+ continue; // skip internal settings
+
+ ADDON_STATUS status = ADDON_STATUS_OK;
+ const char* id = setting->GetId().c_str();
+
+ switch (setting->GetType())
+ {
+ case SettingType::Boolean:
+ {
+ bool tmp = std::static_pointer_cast<CSettingBool>(setting)->GetValue();
+ if (instanceId == ADDON_SETTINGS_ID)
+ {
+ if (m_interface.toAddon->setting_change_boolean)
+ status =
+ m_interface.toAddon->setting_change_boolean(m_interface.addonBase, id, tmp);
+ }
+ else if (instanceTarget && instanceHandle)
+ {
+ if (instanceTarget->instance_setting_change_boolean)
+ status =
+ instanceTarget->instance_setting_change_boolean(instanceHandle, id, tmp);
+ }
+ break;
+ }
+
+ case SettingType::Integer:
+ {
+ int tmp = std::static_pointer_cast<CSettingInt>(setting)->GetValue();
+ if (instanceId == ADDON_SETTINGS_ID)
+ {
+ if (m_interface.toAddon->setting_change_integer)
+ status =
+ m_interface.toAddon->setting_change_integer(m_interface.addonBase, id, tmp);
+ }
+ else if (instanceTarget && instanceHandle)
+ {
+ if (instanceTarget->instance_setting_change_integer)
+ status =
+ instanceTarget->instance_setting_change_integer(instanceHandle, id, tmp);
+ }
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ float tmpf = static_cast<float>(std::static_pointer_cast<CSettingNumber>(setting)->GetValue());
+ if (instanceId == ADDON_SETTINGS_ID)
+ {
+ if (m_interface.toAddon->setting_change_float)
+ status =
+ m_interface.toAddon->setting_change_float(m_interface.addonBase, id, tmpf);
+ }
+ else if (instanceTarget && instanceHandle)
+ {
+ if (instanceTarget->instance_setting_change_float)
+ status =
+ instanceTarget->instance_setting_change_float(instanceHandle, id, tmpf);
+ }
+ break;
+ }
+
+ case SettingType::String:
+ {
+ if (instanceId == ADDON_SETTINGS_ID)
+ {
+ if (m_interface.toAddon->setting_change_string)
+ status = m_interface.toAddon->setting_change_string(
+ m_interface.addonBase, id,
+ std::static_pointer_cast<CSettingString>(setting)->GetValue().c_str());
+ }
+ else if (instanceTarget && instanceHandle)
+ {
+ if (instanceTarget->instance_setting_change_string)
+ status = instanceTarget->instance_setting_change_string(
+ instanceHandle, id,
+ std::static_pointer_cast<CSettingString>(setting)->GetValue().c_str());
+ }
+ break;
+ }
+
+ default:
+ {
+ // log unknowns as an error, but go ahead and transfer the string
+ CLog::Log(LOGERROR, "Unknown setting type of '{}' for {}", id, Name());
+ if (instanceId == ADDON_SETTINGS_ID)
+ {
+ if (m_interface.toAddon->setting_change_string)
+ status = m_interface.toAddon->setting_change_string(
+ m_interface.addonBase, id, setting->ToString().c_str());
+ }
+ else if (instanceTarget && instanceHandle)
+ {
+ if (instanceTarget->instance_setting_change_string)
+ status = instanceTarget->instance_setting_change_string(
+ instanceHandle, id, setting->ToString().c_str());
+ }
+ break;
+ }
+ }
+
+ if (status == ADDON_STATUS_NEED_RESTART)
+ restart = true;
+ else if (status != ADDON_STATUS_OK)
+ reportStatus = status;
+ }
+ }
+ }
+ }
+ }
+
+ if (restart || reportStatus != ADDON_STATUS_OK)
+ {
+ new CAddonStatusHandler(ID(), instanceId, restart ? ADDON_STATUS_NEED_RESTART : reportStatus,
+ true);
+ }
+
+ return ADDON_STATUS_OK;
+}
+
+bool CAddonDll::CheckAPIVersion(int type)
+{
+ /* check the API version */
+ CAddonVersion kodiMinVersion(kodi::addon::GetTypeMinVersion(type));
+ CAddonVersion addonVersion(m_pDll->GetAddonTypeVersion(type));
+ CAddonVersion addonMinVersion = m_pDll->GetAddonTypeMinVersion_available()
+ ? CAddonVersion(m_pDll->GetAddonTypeMinVersion(type))
+ : addonVersion;
+
+ /* Check the global usage from addon
+ * if not used from addon, empty version is returned
+ */
+ if (type <= ADDON_GLOBAL_MAX && addonVersion.empty())
+ return true;
+
+ /* If a instance (not global) version becomes checked must be the version
+ * present.
+ */
+ if (kodiMinVersion > addonVersion ||
+ addonMinVersion > CAddonVersion(kodi::addon::GetTypeVersion(type)))
+ {
+ CLog::Log(LOGERROR, "Add-on '{}' is using an incompatible API version for type '{}'. Kodi API min version = '{}/{}', add-on API version '{}/{}'",
+ Name(),
+ kodi::addon::GetTypeName(type),
+ kodi::addon::GetTypeVersion(type),
+ kodiMinVersion.asString(),
+ addonMinVersion.asString(),
+ addonVersion.asString());
+
+ if (CServiceBroker::GetGUI())
+ {
+ CEventLog* eventLog = CServiceBroker::GetEventLog();
+ if (eventLog)
+ eventLog->AddWithNotification(
+ EventPtr(new CNotificationEvent(Name(), 24152, EventLevel::Error)));
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+} /* namespace ADDON */