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 : #ifndef STAPPLER_FILESYSTEM_SPFILEPATH_H_
25 : #define STAPPLER_FILESYSTEM_SPFILEPATH_H_
26 :
27 : #include "SPStringView.h"
28 : #include "SPSpanView.h"
29 :
30 : namespace STAPPLER_VERSIONIZED stappler {
31 :
32 : using FilePath = ValueWrapper<StringView, class FilePathTag>;
33 :
34 : }
35 :
36 : namespace STAPPLER_VERSIONIZED stappler::filepath {
37 :
38 : // check if filepath is absolute
39 : bool isAbsolute(StringView path);
40 :
41 : bool isCanonical(StringView path);
42 :
43 : // check if filepath is in application bundle
44 : bool isBundled(StringView path);
45 :
46 : // check if filepath above it's current root
47 : bool isAboveRoot(StringView path);
48 :
49 : // check for ".", ".." and double slashes in path
50 : bool validatePath(StringView path);
51 :
52 : // remove any ".", ".." and double slashes from path
53 : template <typename Interface>
54 : auto reconstructPath(StringView path) -> typename Interface::StringType;
55 :
56 : // returns current absolute path for file (canonical prefix will be decoded), this path should not be cached
57 : // if writable flag is false, platform path will be returned with canonical prefix %PLATFORM%
58 : // if writable flag is true, system should resolve platform prefix to absolute path, if possible
59 : // if platform path can not be resolved (etc, it's in archive or another FS), empty string will be returned
60 : template <typename Interface>
61 : auto absolute(StringView, bool writable = false) -> typename Interface::StringType;
62 :
63 : // encodes path for long-term storage (default application dirs will be replaced with canonical prefix,
64 : // like %CACHE%/dir)
65 : template <typename Interface>
66 : auto canonical(StringView path) -> typename Interface::StringType;
67 :
68 : // extract root from path by removing last component (/dir/file.tar.bz -> /dir)
69 : StringView root(StringView path);
70 :
71 : // extract last component (/dir/file.tar.bz -> file.tar.bz)
72 : StringView lastComponent(StringView path);
73 : StringView lastComponent(StringView path, size_t allowedComponents);
74 :
75 : // extract full filename extension (/dir/file.tar.gz -> tar.gz)
76 : StringView fullExtension(StringView path);
77 :
78 : // extract last filename extension (/dir/file.tar.gz -> gz)
79 : StringView lastExtension(StringView path);
80 :
81 : // /dir/file.tar.bz -> file
82 : StringView name(StringView path);
83 :
84 : // /dir/file.tar.bz -> 2
85 : size_t extensionCount(StringView path);
86 :
87 : template <typename Interface>
88 : auto split(StringView) -> typename Interface::template VectorType<StringView>;
89 :
90 : void split(StringView, const Callback<void(StringView)> &);
91 :
92 : // merges two path component, removes or adds '/' where needed
93 : template <typename Interface>
94 : auto _merge(StringView root, StringView path) -> typename Interface::StringType;
95 :
96 : template <typename Interface>
97 : auto merge(SpanView<std::string>) -> typename Interface::StringType;
98 :
99 : template <typename Interface>
100 : auto merge(SpanView<memory::string>) -> typename Interface::StringType;
101 :
102 : template <typename Interface>
103 : auto merge(SpanView<StringView>) -> typename Interface::StringType;
104 :
105 : template <typename Interface>
106 : auto merge(stappler::memory::StandartInterface::StringType &&str) -> typename Interface::StringType;
107 :
108 : template <typename Interface>
109 : auto merge(stappler::memory::PoolInterface::StringType &&str) -> typename Interface::StringType;
110 :
111 : template <typename Interface, class... Args>
112 23184 : auto merge(StringView root, StringView path, Args&&... args) -> typename Interface::StringType {
113 23184 : return merge<Interface>(_merge<Interface>(root, path), std::forward<Args>(args)...);
114 : }
115 :
116 : // translate some MIME Content-Type to common extensions
117 : StringView extensionForContentType(StringView type);
118 :
119 : // replace root path component in filepath
120 : // replace(/my/dir/first/file, /my/dir/first, /your/dir/second)
121 : // [/my/dir/first -> /your/dir/second] /file
122 : // /my/dir/first/file -> /your/dir/second/file
123 : template <typename Interface>
124 : auto replace(StringView path, StringView source, StringView dest) -> typename Interface::StringType;
125 :
126 : // Implementation
127 :
128 : template <typename Interface>
129 25 : auto reconstructPath(StringView path) -> typename Interface::StringType {
130 25 : typename Interface::StringType ret; ret.reserve(path.size());
131 25 : bool start = (path.front() == '/');
132 25 : bool end = (path.back() == '/');
133 :
134 25 : typename Interface::template VectorType<StringView> retVec;
135 25 : StringView r(path);
136 200 : while (!r.empty()) {
137 175 : auto str = r.readUntil<StringView::Chars<'/'>>();
138 175 : if (str == ".." && str.size() == 2) {
139 0 : if (!retVec.empty()) {
140 0 : retVec.pop_back();
141 : }
142 175 : } else if ((str == "." && str.size() == 1) || str.size() == 0) {
143 150 : } else if (!str.empty()) {
144 150 : retVec.emplace_back(str);
145 : }
146 175 : if (r.is('/')) {
147 150 : ++ r;
148 : }
149 : }
150 :
151 25 : if (start) {
152 0 : ret.push_back('/');
153 : }
154 :
155 25 : bool f = false;
156 175 : for (auto &it : retVec) {
157 150 : if (f) {
158 125 : ret.push_back('/');
159 : } else {
160 25 : f = true;
161 : }
162 150 : ret.append(it.data(), it.size());
163 : }
164 25 : if (end) {
165 0 : ret.push_back('/');
166 : }
167 50 : return ret;
168 25 : }
169 :
170 : template <typename Interface>
171 25 : auto split(StringView str) -> typename Interface::template VectorType<StringView> {
172 25 : typename Interface::template VectorType<StringView> ret;
173 25 : StringView s(str);
174 : do {
175 300 : if (s.is('/')) {
176 300 : s ++;
177 : }
178 300 : auto path = s.readUntil<StringView::Chars<'/', '?', ';', '&', '#'>>();
179 300 : ret.push_back(path);
180 300 : } while (!s.empty() && s.is('/'));
181 50 : return ret;
182 0 : }
183 :
184 : inline void split(StringView str, const Callback<void(StringView)> &cb) {
185 : StringView s(str);
186 : do {
187 : if (s.is('/')) {
188 : s ++;
189 : }
190 : auto path = s.readUntil<StringView::Chars<'/', '?', ';', '&', '#'>>();
191 : cb(path);
192 : } while (!s.empty() && s.is('/'));
193 : }
194 :
195 : template <typename Interface>
196 0 : auto replace(StringView path, StringView source, StringView dest) -> typename Interface::StringType {
197 0 : if (path.starts_with(source)) {
198 0 : if (dest.empty()) {
199 0 : return path.sub(source.size()).str<Interface>();
200 : } else {
201 0 : return filepath::merge<Interface>(dest, path.sub(source.size()));
202 : }
203 : }
204 0 : return path.str<Interface>();
205 : }
206 :
207 :
208 : }
209 :
210 : #endif /* STAPPLER_FILESYSTEM_SPFILEPATH_H_ */
|