4230
|
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 }
|