comparison OrthancFramework/Sources/HttpServer/WebDavStorage.cpp @ 4230:b313a0001893

WebDavStorage
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 06 Oct 2020 18:14:26 +0200
parents
children 688435755466
comparison
equal deleted inserted replaced
4229:013d6c6b2387 4230:b313a0001893
1 /**
2 * Orthanc - A Lightweight, RESTful DICOM Store
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
4 * Department, University Hospital of Liege, Belgium
5 * Copyright (C) 2017-2020 Osimis S.A., Belgium
6 *
7 * This program is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation, either version 3 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/>.
20 **/
21
22
23 #include "../PrecompiledHeaders.h"
24 #include "WebDavStorage.h"
25
26 #include "../OrthancException.h"
27 #include "../SystemToolbox.h"
28 #include "../TemporaryFile.h"
29 #include "../Toolbox.h"
30
31 namespace Orthanc
32 {
33 class WebDavStorage::StorageFile : public boost::noncopyable
34 {
35 private:
36 std::unique_ptr<TemporaryFile> file_;
37 std::string content_;
38 MimeType mime_;
39 boost::posix_time::ptime time_;
40
41 void Touch()
42 {
43 time_ = boost::posix_time::second_clock::universal_time();
44 }
45
46 public:
47 StorageFile() :
48 mime_(MimeType_Binary)
49 {
50 Touch();
51 }
52
53 void SetContent(const std::string& content,
54 MimeType mime,
55 bool isMemory)
56 {
57 if (isMemory)
58 {
59 content_ = content;
60 file_.reset();
61 }
62 else
63 {
64 content_.clear();
65 file_.reset(new TemporaryFile);
66 file_->Write(content);
67 }
68
69 mime_ = mime;
70 Touch();
71 }
72
73 MimeType GetMimeType() const
74 {
75 return mime_;
76 }
77
78 void GetContent(std::string& target) const
79 {
80 if (file_.get() == NULL)
81 {
82 target = content_;
83 }
84 else
85 {
86 file_->Read(target);
87 }
88 }
89
90 const boost::posix_time::ptime& GetTime() const
91 {
92 return time_;
93 }
94
95 uint64_t GetContentLength() const
96 {
97 if (file_.get() == NULL)
98 {
99 return content_.size();
100 }
101 else
102 {
103 return file_->GetFileSize();
104 }
105 }
106 };
107
108
109 class WebDavStorage::StorageFolder : public boost::noncopyable
110 {
111 private:
112 typedef std::map<std::string, StorageFile*> Files;
113 typedef std::map<std::string, StorageFolder*> Subfolders;
114
115 Files files_;
116 Subfolders subfolders_;
117
118 void CheckName(const std::string& name)
119 {
120 if (name.empty() ||
121 name.find('/') != std::string::npos ||
122 name.find('\\') != std::string::npos ||
123 name.find('\0') != std::string::npos)
124 {
125 throw OrthancException(ErrorCode_ParameterOutOfRange,
126 "Bad resource name for WebDAV: " + name);
127 }
128 }
129
130 bool IsExisting(const std::string& name) const
131 {
132 return (files_.find(name) != files_.end() ||
133 subfolders_.find(name) != subfolders_.end());
134 }
135
136 public:
137 ~StorageFolder()
138 {
139 for (Files::iterator it = files_.begin(); it != files_.end(); ++it)
140 {
141 assert(it->second != NULL);
142 delete it->second;
143 }
144
145 for (Subfolders::iterator it = subfolders_.begin(); it != subfolders_.end(); ++it)
146 {
147 assert(it->second != NULL);
148 delete it->second;
149 }
150 }
151
152 const StorageFile* LookupFile(const std::string& name) const
153 {
154 Files::const_iterator found = files_.find(name);
155 if (found == files_.end())
156 {
157 return NULL;
158 }
159 else
160 {
161 assert(found->second != NULL);
162 return found->second;
163 }
164 }
165
166 bool CreateSubfolder(const std::string& name)
167 {
168 CheckName(name);
169
170 if (IsExisting(name))
171 {
172 LOG(ERROR) << "WebDAV folder already existing: " << name;
173 return false;
174 }
175 else
176 {
177 subfolders_[name] = new StorageFolder;
178 return true;
179 }
180 }
181
182 bool StoreFile(const std::string& name,
183 const std::string& content,
184 MimeType mime,
185 bool isMemory)
186 {
187 CheckName(name);
188
189 if (subfolders_.find(name) != subfolders_.end())
190 {
191 LOG(ERROR) << "WebDAV folder already existing: " << name;
192 return false;
193 }
194
195 Files::iterator found = files_.find(name);
196 if (found == files_.end())
197 {
198 std::unique_ptr<StorageFile> f(new StorageFile);
199 f->SetContent(content, mime, isMemory);
200 files_[name] = f.release();
201 }
202 else
203 {
204 assert(found->second != NULL);
205 found->second->SetContent(content, mime, isMemory);
206 }
207
208 return true;
209 }
210
211 StorageFolder* LookupFolder(const std::vector<std::string>& path)
212 {
213 if (path.empty())
214 {
215 return this;
216 }
217 else
218 {
219 Subfolders::const_iterator found = subfolders_.find(path[0]);
220 if (found == subfolders_.end())
221 {
222 return NULL;
223 }
224 else
225 {
226 assert(found->second != NULL);
227
228 std::vector<std::string> p(path.begin() + 1, path.end());
229 return found->second->LookupFolder(p);
230 }
231 }
232 }
233
234 void ListCollection(Collection& collection) const
235 {
236 for (Files::const_iterator it = files_.begin(); it != files_.end(); ++it)
237 {
238 assert(it->second != NULL);
239
240 std::unique_ptr<File> f(new File(it->first));
241 f->SetContentLength(it->second->GetContentLength());
242 f->SetCreationTime(it->second->GetTime());
243 collection.AddResource(f.release());
244 }
245
246 for (Subfolders::const_iterator it = subfolders_.begin(); it != subfolders_.end(); ++it)
247 {
248 collection.AddResource(new Folder(it->first));
249 }
250 }
251 };
252
253
254 WebDavStorage::StorageFolder* WebDavStorage::LookupParentFolder(const std::vector<std::string>& path)
255 {
256 if (path.empty())
257 {
258 throw OrthancException(ErrorCode_ParameterOutOfRange);
259 }
260
261 std::vector<std::string> p(path.begin(), path.end() - 1);
262 return root_->LookupFolder(p);
263 }
264
265
266 WebDavStorage::WebDavStorage(bool isMemory) :
267 root_(new StorageFolder),
268 isMemory_(isMemory)
269 {
270 }
271
272
273 bool WebDavStorage::IsExistingFolder(const std::vector<std::string>& path)
274 {
275 boost::recursive_mutex::scoped_lock lock(mutex_);
276
277 return (root_->LookupFolder(path) != NULL);
278 }
279
280
281 bool WebDavStorage::ListCollection(Collection& collection,
282 const std::vector<std::string>& path)
283 {
284 boost::recursive_mutex::scoped_lock lock(mutex_);
285
286 const StorageFolder* folder = root_->LookupFolder(path);
287 if (folder == NULL)
288 {
289 return false;
290 }
291 else
292 {
293 folder->ListCollection(collection);
294 return true;
295 }
296 }
297
298
299 bool WebDavStorage::GetFileContent(MimeType& mime,
300 std::string& content,
301 boost::posix_time::ptime& modificationTime,
302 const std::vector<std::string>& path)
303 {
304 boost::recursive_mutex::scoped_lock lock(mutex_);
305
306 const StorageFolder* folder = LookupParentFolder(path);
307 if (folder == NULL)
308 {
309 return false;
310 }
311 else
312 {
313 const StorageFile* file = folder->LookupFile(path.back());
314 if (file == NULL)
315 {
316 return false;
317 }
318 else
319 {
320 mime = file->GetMimeType();
321 file->GetContent(content);
322 modificationTime = file->GetTime();
323 return true;
324 }
325 }
326 }
327
328
329 bool WebDavStorage::StoreFile(const std::string& content,
330 const std::vector<std::string>& path)
331 {
332 boost::recursive_mutex::scoped_lock lock(mutex_);
333
334 StorageFolder* folder = LookupParentFolder(path);
335 if (folder == NULL)
336 {
337 LOG(WARNING) << "Inexisting folder in WebDAV: " << Toolbox::FlattenUri(path);
338 return false;
339 }
340 else
341 {
342 LOG(INFO) << "Storing " << content.size()
343 << " bytes in WebDAV bucket: " << Toolbox::FlattenUri(path);;
344
345 MimeType mime = SystemToolbox::AutodetectMimeType(path.back());
346 return folder->StoreFile(path.back(), content, mime, isMemory_);
347 }
348 }
349
350
351 bool WebDavStorage::CreateFolder(const std::vector<std::string>& path)
352 {
353 boost::recursive_mutex::scoped_lock lock(mutex_);
354
355 StorageFolder* folder = LookupParentFolder(path);
356 if (folder == NULL)
357 {
358 LOG(WARNING) << "Inexisting folder in WebDAV: " << Toolbox::FlattenUri(path);
359 return false;
360 }
361 else
362 {
363 LOG(INFO) << "Creating folder in WebDAV bucket: " << Toolbox::FlattenUri(path);
364 return folder->CreateSubfolder(path.back());
365 }
366 }
367 }