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 "APKDirectory.h"
10 
11 #include "APKFile.h"
12 #include "FileItem.h"
13 #include "utils/CharsetConverter.h"
14 #include "utils/StringUtils.h"
15 #include "utils/URIUtils.h"
16 #include "utils/log.h"
17 
18 #include <zip.h>
19 
20 using namespace XFILE;
21 
22 // Android apk directory i/o. Depends on libzip
23 // Basically the same format as zip.
24 // We might want to refactor CZipDirectory someday...
25 //////////////////////////////////////////////////////////////////////
GetDirectory(const CURL & url,CFileItemList & items)26 bool CAPKDirectory::GetDirectory(const CURL& url, CFileItemList &items)
27 {
28   // uses a <fully qualified path>/filename.apk/...
29   std::string path = url.GetFileName();
30   const std::string& host = url.GetHostName();
31   URIUtils::AddSlashAtEnd(path);
32 
33   int zip_flags = 0, zip_error = 0;
34   struct zip *zip_archive;
35   zip_archive = zip_open(host.c_str(), zip_flags, &zip_error);
36   if (!zip_archive || zip_error)
37   {
38     CLog::Log(LOGERROR, "CAPKDirectory::GetDirectory: Unable to open archive : '%s'",
39       host.c_str());
40     return false;
41   }
42 
43   std::string test_name;
44   int numFiles = zip_get_num_files(zip_archive);
45   for (int zip_index = 0; zip_index < numFiles; zip_index++)
46   {
47     test_name = zip_get_name(zip_archive, zip_index, zip_flags);
48 
49     // check for non matching path.
50     if (!StringUtils::StartsWith(test_name, path))
51       continue;
52 
53     // libzip does not index folders, only filenames. We search for a /,
54     // add it if it's not in our list already, and hope that no one has
55     // any "file/name.exe" files in a zip.
56 
57     size_t dir_marker = test_name.find('/', path.size() + 1);
58     if (dir_marker != std::string::npos)
59     {
60       // return items relative to path
61       test_name=test_name.substr(0, dir_marker);
62 
63       if (items.Contains(host + "/" + test_name))
64         continue;
65     }
66 
67     struct zip_stat sb;
68     zip_stat_init(&sb);
69     if (zip_stat_index(zip_archive, zip_index, zip_flags, &sb) != -1)
70     {
71       g_charsetConverter.unknownToUTF8(test_name);
72       CFileItemPtr pItem(new CFileItem(test_name));
73       pItem->m_dwSize    = sb.size;
74       pItem->m_dateTime  = sb.mtime;
75       pItem->m_bIsFolder = dir_marker > 0 ;
76       pItem->SetPath(host + "/" + test_name);
77       pItem->SetLabel(test_name.substr(path.size()));
78       items.Add(pItem);
79     }
80   }
81   zip_close(zip_archive);
82 
83   return true;
84 }
85 
ContainsFiles(const CURL & url)86 bool CAPKDirectory::ContainsFiles(const CURL& url)
87 {
88   //! @todo why might we need this ?
89   return false;
90 }
91 
GetCacheType(const CURL & url) const92 DIR_CACHE_TYPE CAPKDirectory::GetCacheType(const CURL& url) const
93 {
94   return DIR_CACHE_ALWAYS;
95 }
96 
Exists(const CURL & url)97 bool CAPKDirectory::Exists(const CURL& url)
98 {
99   // uses a <fully qualified path>/filename.apk/...
100   CAPKFile apk;
101   return apk.Exists(url);
102 }
103