1 /*
2  *  Copyright (C) 2012-2018 Team Kodi
3  *  This file is part of Kodi - https://kodi.tv
4  *
5  *  SPDX-License-Identifier: GPL-2.0-or-later
6  *  See LICENSES/README.md for more information.
7  */
8 
9 #include "FileExtensionProvider.h"
10 
11 #include "ServiceBroker.h"
12 #include "addons/AddonManager.h"
13 #include "addons/AudioDecoder.h"
14 #include "settings/AdvancedSettings.h"
15 #include "settings/SettingsComponent.h"
16 #include "utils/URIUtils.h"
17 
18 #include <string>
19 #include <vector>
20 
21 using namespace ADDON;
22 
23 const std::vector<TYPE> ADDON_TYPES = {
24   ADDON_VFS,
25   ADDON_IMAGEDECODER,
26   ADDON_AUDIODECODER
27 };
28 
CFileExtensionProvider(ADDON::CAddonMgr & addonManager)29 CFileExtensionProvider::CFileExtensionProvider(ADDON::CAddonMgr& addonManager)
30   : m_advancedSettings(CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()),
31     m_addonManager(addonManager)
32 {
33   SetAddonExtensions();
34 
35   m_addonManager.Events().Subscribe(this, &CFileExtensionProvider::OnAddonEvent);
36 }
37 
~CFileExtensionProvider()38 CFileExtensionProvider::~CFileExtensionProvider()
39 {
40   m_addonManager.Events().Unsubscribe(this);
41 
42   m_advancedSettings.reset();
43   m_addonExtensions.clear();
44 }
45 
GetDiscStubExtensions() const46 std::string CFileExtensionProvider::GetDiscStubExtensions() const
47 {
48   return m_advancedSettings->m_discStubExtensions;
49 }
50 
GetMusicExtensions() const51 std::string CFileExtensionProvider::GetMusicExtensions() const
52 {
53   std::string extensions(m_advancedSettings->m_musicExtensions);
54   extensions += '|' + GetAddonExtensions(ADDON_VFS);
55   extensions += '|' + GetAddonExtensions(ADDON_AUDIODECODER);
56 
57   return extensions;
58 }
59 
GetPictureExtensions() const60 std::string CFileExtensionProvider::GetPictureExtensions() const
61 {
62   std::string extensions(m_advancedSettings->m_pictureExtensions);
63   extensions += '|' + GetAddonExtensions(ADDON_VFS);
64   extensions += '|' + GetAddonExtensions(ADDON_IMAGEDECODER);
65 
66   return extensions;
67 }
68 
GetSubtitleExtensions() const69 std::string CFileExtensionProvider::GetSubtitleExtensions() const
70 {
71   std::string extensions(m_advancedSettings->m_subtitlesExtensions);
72   extensions += '|' + GetAddonExtensions(ADDON_VFS);
73 
74   return extensions;
75 }
76 
GetVideoExtensions() const77 std::string CFileExtensionProvider::GetVideoExtensions() const
78 {
79   std::string extensions(m_advancedSettings->m_videoExtensions);
80   if (!extensions.empty())
81     extensions += '|';
82   extensions += GetAddonExtensions(ADDON_VFS);
83 
84   return extensions;
85 }
86 
GetFileFolderExtensions() const87 std::string CFileExtensionProvider::GetFileFolderExtensions() const
88 {
89   std::string extensions(GetAddonFileFolderExtensions(ADDON_VFS));
90   if (!extensions.empty())
91     extensions += '|';
92   extensions += GetAddonFileFolderExtensions(ADDON_AUDIODECODER);
93 
94   return extensions;
95 }
96 
CanOperateExtension(const std::string & path) const97 bool CFileExtensionProvider::CanOperateExtension(const std::string& path) const
98 {
99   /*!
100    * @todo Improve this function to support all cases and not only audio decoder
101    * with tracks inside.
102    */
103 
104   // Get file extensions to find addon related to it.
105   std::string strExtension = URIUtils::GetExtension(path);
106   StringUtils::ToLower(strExtension);
107   if (!strExtension.empty() && CServiceBroker::IsBinaryAddonCacheUp())
108   {
109     std::vector<AddonInfoPtr> addonInfos;
110     m_addonManager.GetAddonInfos(addonInfos, true, ADDON_AUDIODECODER);
111     for (const auto& addonInfo : addonInfos)
112     {
113       if (CAudioDecoder::HasTracks(addonInfo))
114       {
115         const auto exts = StringUtils::Split(CAudioDecoder::GetExtensions(addonInfo), "|");
116         if (std::find(exts.begin(), exts.end(), strExtension) != exts.end())
117         {
118           /* Call addon to start a dir read about given file, if success, return
119            * as true.
120            */
121           CAudioDecoder result(addonInfo);
122           if (result.CreateDecoder() && result.ContainsFiles(CURL(path)))
123             return true;
124 
125           /* If extension is supported and addon creation failed, we expect the
126            * file is not usable and return false here.
127            */
128           return false;
129         }
130       }
131     }
132 
133     /*!
134      * We expect that VFS addons can support the file, and return true.
135      *
136      * @todo Check VFS addons can also be types in conflict with Kodi's
137      * supported parts!
138      */
139     return true;
140   }
141 
142   /*!
143    * If no file extensions present, mark it as not supported.
144    */
145   return false;
146 }
147 
GetAddonExtensions(const TYPE & type) const148 std::string CFileExtensionProvider::GetAddonExtensions(const TYPE &type) const
149 {
150   auto it = m_addonExtensions.find(type);
151   if (it != m_addonExtensions.end())
152     return it->second;
153 
154   return "";
155 }
156 
GetAddonFileFolderExtensions(const TYPE & type) const157 std::string CFileExtensionProvider::GetAddonFileFolderExtensions(const TYPE &type) const
158 {
159   auto it = m_addonExtensions.find(type);
160   if (it != m_addonExtensions.end())
161     return it->second;
162 
163   return "";
164 }
165 
SetAddonExtensions()166 void CFileExtensionProvider::SetAddonExtensions()
167 {
168   for (auto const type : ADDON_TYPES)
169   {
170     SetAddonExtensions(type);
171   }
172 }
173 
SetAddonExtensions(const TYPE & type)174 void CFileExtensionProvider::SetAddonExtensions(const TYPE& type)
175 {
176   std::vector<std::string> extensions;
177   std::vector<std::string> fileFolderExtensions;
178   std::vector<AddonInfoPtr> addonInfos;
179   m_addonManager.GetAddonInfos(addonInfos, true, type);
180   for (const auto& addonInfo : addonInfos)
181   {
182     std::string info = ADDON_VFS == type ? "@extensions" : "@extension";
183     std::string ext = addonInfo->Type(type)->GetValue(info).asString();
184     if (!ext.empty())
185     {
186       extensions.push_back(ext);
187       if (type == ADDON_VFS || type == ADDON_AUDIODECODER)
188       {
189         std::string info2 = ADDON_VFS == type ? "@filedirectories" : "@tracks";
190         if (addonInfo->Type(type)->GetValue(info2).asBoolean())
191           fileFolderExtensions.push_back(ext);
192       }
193       if (type == ADDON_VFS)
194       {
195         if (addonInfo->Type(type)->GetValue("@encodedhostname").asBoolean())
196         {
197           std::string prot = addonInfo->Type(type)->GetValue("@protocols").asString();
198           auto prots = StringUtils::Split(prot, "|");
199           for (const std::string& it : prots)
200             m_encoded.push_back(it);
201         }
202       }
203     }
204   }
205 
206   m_addonExtensions.insert(make_pair(type, StringUtils::Join(extensions, "|")));
207   if (!fileFolderExtensions.empty())
208     m_addonFileFolderExtensions.insert(make_pair(type, StringUtils::Join(fileFolderExtensions, "|")));
209 }
210 
OnAddonEvent(const AddonEvent & event)211 void CFileExtensionProvider::OnAddonEvent(const AddonEvent& event)
212 {
213   if (typeid(event) == typeid(AddonEvents::Enabled) ||
214       typeid(event) == typeid(AddonEvents::Disabled) ||
215       typeid(event) == typeid(AddonEvents::ReInstalled))
216   {
217     for (auto &type : ADDON_TYPES)
218     {
219       if (m_addonManager.HasType(event.id, type))
220       {
221         SetAddonExtensions(type);
222         break;
223       }
224     }
225   }
226   else if (typeid(event) == typeid(AddonEvents::UnInstalled))
227   {
228     SetAddonExtensions();
229   }
230 }
231 
EncodedHostName(const std::string & protocol) const232 bool CFileExtensionProvider::EncodedHostName(const std::string& protocol) const
233 {
234   return std::find(m_encoded.begin(),m_encoded.end(),protocol) != m_encoded.end();
235 }
236