LCOV - code coverage report
Current view: top level - core/filesystem - SPFilepath.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 83 234 35.5 %
Date: 2024-05-12 00:16:13 Functions: 19 51 37.3 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPFilepath.h"
      25             : #include "SPFilesystem.h"
      26             : 
      27             : namespace STAPPLER_VERSIONIZED stappler::filepath {
      28             : 
      29       25004 : SPUNUSED static bool inAppBundle(StringView path) {
      30       25004 :         if (filepath::isAbsolute(path)) {
      31       24979 :                 return false;
      32             :         }
      33          50 :         if (filepath::isBundled(path) ||
      34          25 :                         (!filepath::isAboveRoot(path) && filesystem::platform::_exists(path))) {
      35           0 :                 return true;
      36             :         }
      37          25 :         return false;
      38             : }
      39             : 
      40             : // check if filepath is absolute
      41       58712 : bool isAbsolute(StringView path) {
      42       58712 :         if (path.empty()) {
      43           0 :                 return true;
      44             :         }
      45       58712 :         return path[0] == '/';
      46             : }
      47             : 
      48           0 : bool isCanonical(StringView path) {
      49           0 :         if (path.empty()) {
      50           0 :                 return false;
      51             :         }
      52           0 :         return path[0] == '%';
      53             : }
      54             : 
      55             : // check if filepath is in application bundle
      56         757 : bool isBundled(StringView path) {
      57         757 :         if (path.size() >= "%PLATFORM%:"_len) {
      58         757 :                 return path.starts_with("%PLATFORM%:");
      59             :         }
      60           0 :         return false;
      61             : }
      62             : 
      63             : // check if filepath above it's current root
      64         238 : bool isAboveRoot(StringView path) {
      65         238 :         size_t components = 0;
      66         238 :         StringView r(path);
      67         664 :         while (!r.empty()) {
      68         476 :                 auto str = r.readUntil<StringView::Chars<'/'>>();
      69         476 :                 if (str == ".." && str.size() == 2) {
      70           0 :                         if (components == 0) {
      71          50 :                                 return true;
      72             :                         }
      73           0 :                         -- components;
      74         476 :                 } else if ((str == "." && str.size() == 1) || str.size() == 0) {return false;
      75             :                 } else {
      76         426 :                         ++ components;
      77             :                 }
      78         426 :                 if (r.is('/')) {
      79         238 :                         ++ r;
      80             :                 }
      81             :         }
      82         188 :         return false;
      83             : }
      84             : 
      85             : // check for ".", ".." and double slashes in path
      86       27248 : bool validatePath(StringView path) {
      87       27248 :         StringView r(path);
      88       27248 :         if (r.is('/')) {
      89       27104 :                 ++ r;
      90             :         }
      91      233915 :         while (!r.empty()) {
      92      206692 :                 auto str = r.readUntil<StringView::Chars<'/'>>();
      93      206692 :                 if ((str == ".." && str.size() == 2) || (str == "." && str.size() == 1) || str.size() == 0) {
      94          25 :                         return false;
      95             :                 }
      96      206667 :                 if (r.is('/')) {
      97      179444 :                         ++ r;
      98             :                 }
      99             :         }
     100       27223 :         return true;
     101             : }
     102             : 
     103             : template <typename Interface>
     104       27248 : auto absolute_fn(StringView path, bool writable) -> typename Interface::StringType {
     105       27248 :         if (path.empty()) {
     106           0 :                 return typename Interface::StringType();
     107             :         }
     108       27248 :         if (path.front() == '%') {
     109           0 :                 if (path.starts_with("%CACHE%")) {
     110           0 :                         return filesystem::cachesPath<Interface>(path.sub(7), true);
     111           0 :                 } else if (path.starts_with("%DOCUMENTS%")) {
     112           0 :                         return filesystem::documentsPath<Interface>(path.sub(11), true);
     113           0 :                 } else if (path.starts_with("%WRITEABLE%")) {
     114           0 :                         return filesystem::writablePath<Interface>(path.sub(11), true);
     115           0 :                 } else if (path.starts_with("%CURRENT%")) {
     116           0 :                         return filesystem::currentDir<Interface>(path.sub(9), true);
     117           0 :                 } else if (path.starts_with("%PLATFORM%:")) {
     118           0 :                         writable = false;
     119             :                 }
     120             :         }
     121             : 
     122       27248 :         if (isAbsolute(path)) {
     123       27104 :                 return validatePath(path)?path.str<Interface>():reconstructPath<Interface>(path);
     124             :         }
     125             : 
     126         144 :         if (!writable && !isAboveRoot(path)) {
     127         119 :                 if (validatePath(path)) {
     128          94 :                         return filesystem::platform::_exists(path)?path.str<Interface>():filesystem::writablePath<Interface>(path);
     129             :                 } else {
     130          25 :                         auto ret = reconstructPath<Interface>(path);
     131          25 :                         return filesystem::platform::_exists(ret)?ret:filesystem::writablePath<Interface>(ret);
     132          25 :                 }
     133             :         }
     134             : 
     135          25 :         return validatePath(path)
     136             :                         ? filesystem::writablePath<Interface>(path)
     137          25 :                         : reconstructPath<Interface>(filesystem::writablePath<Interface>(path));
     138             : }
     139             : 
     140             : template<>
     141       27223 : auto absolute<memory::StandartInterface>(StringView path, bool writable) -> memory::StandartInterface::StringType {
     142       27223 :         return absolute_fn<memory::StandartInterface>(path, writable);
     143             : }
     144             : 
     145             : template<>
     146          25 : auto absolute<memory::PoolInterface>(StringView path, bool writable) -> memory::PoolInterface::StringType {
     147          25 :         return absolute_fn<memory::PoolInterface>(path, writable);
     148             : }
     149             : 
     150             : template <typename Interface>
     151           0 : auto canonical_fn(StringView path) -> typename Interface::StringType {
     152           0 :         if (path.empty()) {
     153           0 :                 return typename Interface::StringType();
     154             :         }
     155           0 :         if (path.front() == '%') {
     156           0 :                 return path.str<Interface>();
     157             :         }
     158             : 
     159           0 :         bool isPlatform = filepath::isBundled(path);
     160           0 :         if (!isPlatform && inAppBundle(path)) {
     161           0 :                 return StringView::merge<Interface>("%PLATFORM%:", path);
     162           0 :         } else if (isPlatform) {
     163           0 :                 return path.str<Interface>();
     164             :         }
     165             : 
     166           0 :         auto cachePath = filesystem::cachesPath<Interface>();
     167           0 :         if (path.starts_with(StringView(cachePath))) {
     168           0 :                 return merge<Interface>("%CACHE%", path.sub(cachePath.size()));
     169             :         }
     170             : 
     171           0 :         auto documentsPath = filesystem::documentsPath<Interface>();
     172           0 :         if (path.starts_with(StringView(documentsPath)) == 0) {
     173           0 :                 return merge<Interface>("%DOCUMENTS%", path.sub(documentsPath.size()));
     174             :         }
     175             : 
     176           0 :         auto writablePath = filesystem::writablePath<Interface>();
     177           0 :         if (path.starts_with(StringView(writablePath)) == 0) {
     178           0 :                 return merge<Interface>("%WRITEABLE%", path.sub(writablePath.size()));
     179             :         }
     180             : 
     181           0 :         auto currentDir = filesystem::currentDir<Interface>();
     182           0 :         if (path.starts_with(StringView(currentDir)) == 0) {
     183           0 :                 return merge<Interface>("%CURRENT%", path.sub(currentDir.size()));
     184             :         }
     185             : 
     186           0 :         return path.str<Interface>();
     187           0 : }
     188             : 
     189             : template<>
     190           0 : auto canonical<memory::StandartInterface>(StringView path) -> memory::StandartInterface::StringType {
     191           0 :         return canonical_fn<memory::StandartInterface>(path);
     192             : }
     193             : 
     194             : template<>
     195           0 : auto canonical<memory::PoolInterface>(StringView path) -> memory::PoolInterface::StringType {
     196           0 :         return canonical_fn<memory::PoolInterface>(path);
     197             : }
     198             : 
     199         250 : StringView root(StringView path) {
     200         250 :         size_t pos = path.rfind('/');
     201         250 :         if (pos == maxOf<size_t>()) {
     202           0 :                 return StringView();
     203             :         } else {
     204         250 :                 if (pos == 0) {
     205           0 :                         return StringView("/");
     206             :                 } else {
     207         250 :                         return path.sub(0, pos);
     208             :                 }
     209             :         }
     210             : }
     211             : 
     212       25700 : StringView lastComponent(StringView path) {
     213       25700 :         size_t pos = path.rfind('/');
     214       25700 :         if (pos != maxOf<size_t>()) {
     215       25675 :                 return path.sub(pos + 1);
     216             :         } else {
     217          25 :                 return path;
     218             :         }
     219             : }
     220             : 
     221           0 : StringView lastComponent(StringView path, size_t allowedComponents) {
     222           0 :         if (allowedComponents == 0) {
     223           0 :                 return "";
     224             :         }
     225           0 :         size_t pos = path.rfind('/');
     226           0 :         allowedComponents --;
     227           0 :         if (pos == 0) {
     228           0 :                 pos = maxOf<size_t>();
     229             :         }
     230             : 
     231           0 :         while (pos != maxOf<size_t>() && allowedComponents > 0) {
     232           0 :                 pos = path.rfind('/', pos - 1);
     233           0 :                 allowedComponents --;
     234           0 :                 if (pos == 0) {
     235           0 :                         pos = maxOf<size_t>();
     236             :                 }
     237             :         }
     238             : 
     239           0 :         if (pos != maxOf<size_t>()) {
     240           0 :                 return path.sub(pos + 1);
     241             :         } else {
     242           0 :                 return path;
     243             :         }
     244             : }
     245             : 
     246           0 : StringView fullExtension(StringView path) {
     247           0 :         auto cmp = lastComponent(path);
     248             : 
     249           0 :         size_t pos = cmp.find('.');
     250           0 :         if (pos == maxOf<size_t>()) {
     251           0 :                 return StringView();
     252             :         } else {
     253           0 :                 return cmp.sub(pos + 1);
     254             :         }
     255             : }
     256             : 
     257       17250 : StringView lastExtension(StringView path) {
     258       17250 :         auto cmp = lastComponent(path);
     259             : 
     260       17250 :         size_t pos = cmp.rfind('.');
     261       17250 :         if (pos == maxOf<size_t>()) {
     262           0 :                 return "";
     263             :         } else {
     264       17250 :                 return cmp.sub(pos + 1);
     265             :         }
     266             : }
     267             : 
     268        7925 : StringView name(StringView path) {
     269        7925 :         auto cmp = lastComponent(path);
     270             : 
     271        7925 :         size_t pos = cmp.find('.');
     272        7925 :         if (pos == maxOf<size_t>()) {
     273           0 :                 return cmp;
     274             :         } else {
     275        7925 :                 return cmp.sub(0, pos);
     276             :         }
     277             : }
     278             : 
     279             : template <typename Interface>
     280       23184 : auto do_merge(StringView root, StringView path) -> typename Interface::StringType {
     281       23184 :         if (path.empty()) {
     282           0 :                 return root.str<Interface>();
     283             :         }
     284       23184 :         return StringView::merge<Interface, '/'>(root, path);
     285             : }
     286             : 
     287             : 
     288             : template <typename SourceVector>
     289           0 : static size_t getMergeSize(const SourceVector &vec) {
     290           0 :         size_t ret = vec.size();
     291           0 :         for (auto it = vec.begin(); it != vec.end(); it ++) {
     292           0 :                 ret += it->size();
     293             :         }
     294           0 :         return ret;
     295             : }
     296             : 
     297             : template <typename Buf, typename SourceVector>
     298           0 : static void doMerge(Buf &out, const SourceVector &vec) {
     299           0 :         auto stripSeps = [] (auto str) {
     300           0 :                 StringView tmp(str);
     301           0 :                 tmp.trimChars<StringView::Chars<'/'>>();
     302           0 :                 return tmp;
     303             :         };
     304             : 
     305           0 :         bool hasSeparator = true;
     306           0 :         auto it = vec.begin();
     307           0 :         for (; it != vec.end(); it ++) {
     308           0 :                 if (it->empty()) {
     309           0 :                         continue;
     310             :                 }
     311             : 
     312           0 :                 if (!hasSeparator) {
     313           0 :                         out.push_back('/');
     314             :                 } else {
     315           0 :                         hasSeparator = false;
     316             :                 }
     317             : 
     318           0 :                 auto tmp = stripSeps(*it);
     319           0 :                 out.append(tmp.data(), tmp.size());
     320             :         }
     321           0 : }
     322             : 
     323             : template <>
     324       22109 : auto _merge<memory::StandartInterface>(StringView root, StringView path) -> memory::StandartInterface::StringType {
     325       22109 :         return do_merge<memory::StandartInterface>(root, path);
     326             : }
     327             : 
     328             : template <>
     329        1075 : auto _merge<memory::PoolInterface>(StringView root, StringView path) -> memory::PoolInterface::StringType {
     330        1075 :         return do_merge<memory::PoolInterface>(root, path);
     331             : }
     332             : 
     333             : template <>
     334           0 : auto merge<memory::StandartInterface>(SpanView<std::string> vec) -> memory::StandartInterface::StringType {
     335           0 :         memory::StandartInterface::StringType ret; ret.reserve(getMergeSize(vec));
     336           0 :         doMerge(ret, vec);
     337           0 :         return ret;
     338           0 : }
     339             : 
     340             : template <>
     341           0 : auto merge<memory::PoolInterface>(SpanView<std::string> vec) -> memory::PoolInterface::StringType {
     342           0 :         memory::PoolInterface::StringType ret; ret.reserve(getMergeSize(vec));
     343           0 :         doMerge(ret, vec);
     344           0 :         return ret;
     345           0 : }
     346             : 
     347             : 
     348             : template <>
     349           0 : auto merge<memory::StandartInterface>(SpanView<memory::string> vec) -> memory::StandartInterface::StringType {
     350           0 :         memory::StandartInterface::StringType ret; ret.reserve(getMergeSize(vec));
     351           0 :         doMerge(ret, vec);
     352           0 :         return ret;
     353           0 : }
     354             : 
     355             : template <>
     356           0 : auto merge<memory::PoolInterface>(SpanView<memory::string> vec) -> memory::PoolInterface::StringType {
     357           0 :         memory::PoolInterface::StringType ret; ret.reserve(getMergeSize(vec));
     358           0 :         doMerge(ret, vec);
     359           0 :         return ret;
     360           0 : }
     361             : 
     362             : 
     363             : template <>
     364           0 : auto merge<memory::StandartInterface>(SpanView<StringView> vec) -> memory::StandartInterface::StringType {
     365           0 :         memory::StandartInterface::StringType ret; ret.reserve(getMergeSize(vec));
     366           0 :         doMerge(ret, vec);
     367           0 :         return ret;
     368           0 : }
     369             : 
     370             : template <>
     371           0 : auto merge<memory::PoolInterface>(SpanView<StringView> vec) -> memory::PoolInterface::StringType {
     372           0 :         memory::PoolInterface::StringType ret; ret.reserve(getMergeSize(vec));
     373           0 :         doMerge(ret, vec);
     374           0 :         return ret;
     375           0 : }
     376             : 
     377             : 
     378             : template <>
     379       22109 : auto merge<memory::StandartInterface>(stappler::memory::StandartInterface::StringType &&str) -> memory::StandartInterface::StringType {
     380       22109 :         return str;
     381             : }
     382             : 
     383             : template <>
     384           0 : auto merge<memory::PoolInterface>(stappler::memory::StandartInterface::StringType &&str) -> memory::PoolInterface::StringType {
     385           0 :         return StringView(str).str<memory::PoolInterface>();
     386             : }
     387             : 
     388             : 
     389             : template <>
     390           0 : auto merge<memory::StandartInterface>(stappler::memory::PoolInterface::StringType &&str) -> memory::StandartInterface::StringType {
     391           0 :         return StringView(str).str<memory::StandartInterface>();
     392             : }
     393             : 
     394             : template <>
     395        1075 : auto merge<memory::PoolInterface>(stappler::memory::PoolInterface::StringType &&str) -> memory::PoolInterface::StringType {
     396        1075 :         return str;
     397             : }
     398             : 
     399           0 : size_t extensionCount(StringView path) {
     400           0 :         size_t ret = 0;
     401           0 :         auto cmp = lastComponent(path);
     402           0 :         for (auto c : cmp) {
     403           0 :                 if (c == '.') { ret ++; }
     404             :         }
     405           0 :         return ret;
     406             : }
     407             : 
     408           0 : StringView extensionForContentType(StringView ct) {
     409             :         // TODO: reimplement with list from Serenity
     410           0 :         if (ct.equals("application/pdf") == 0 || ct.equals("application/x-pdf") == 0) {
     411           0 :                 return ".pdf";
     412           0 :         } else if (ct.equals("image/jpeg") == 0 || ct.equals("image/pjpeg") == 0) {
     413           0 :                 return ".jpeg";
     414           0 :         } else if (ct.equals("image/png") == 0) {
     415           0 :                 return ".png";
     416           0 :         } else if (ct.equals("image/gif") == 0) {
     417           0 :                 return ".gif";
     418           0 :         } else if (ct.equals("image/tiff") == 0) {
     419           0 :                 return ".tiff";
     420           0 :         } else if (ct.equals("application/json") == 0) {
     421           0 :                 return ".json";
     422           0 :         } else if (ct.equals("application/zip") == 0) {
     423           0 :                 return ".zip";
     424             :         }
     425           0 :         return StringView();
     426             : }
     427             : 
     428             : }

Generated by: LCOV version 1.14