tizen beta release
[framework/web/wrt-plugins-common.git] / src / modules / tizen / Filesystem / Manager.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 #include "Manager.h"
17
18 #include <unistd.h>
19 #include <sys/stat.h>
20 #include <errno.h>
21 #include <pcrecpp.h>
22 #include <ctime>
23 #include <cstdio>
24 #include <sstream>
25 #include <dirent.h>
26 #include <dpl/log/log.h>
27 #include <dpl/scoped_ptr.h>
28 #include <dpl/errno_string.h>
29 #include <Commons/Exception.h>
30 #include <Commons/Regex.h>
31 #include <Filesystem/PathUtils.h>
32 #include "Node.h"
33 #include "System.h"
34 #include "CopyCommand.h"
35 #include "MoveCommand.h"
36 #include "RemoveCommand.h"
37 #include "Utils.h"
38
39 namespace {
40 const char* PATH_ROOT = "/opt/media";
41 const char* PATH_DOWNLOADS = "/opt/media/Downloads";
42 const char* PATH_DOCUMENTS = "/opt/media/Documents";
43 const char* PATH_SOUNDS = "/opt/media/Music";
44 const char* PATH_IMAGES = "/opt/media/Images";
45 const char* PATH_VIDEOS = "/opt/media/Videos";
46 const char* PATH_SDCARD = "/opt/storage/sdcard";
47 }
48
49 namespace WrtDeviceApis {
50 namespace Filesystem {
51
52 using namespace Api;
53
54 Manager::Locations Manager::m_locations;
55 const std::size_t Manager::m_maxPathLength = 256;
56 NodeList Manager::m_openedNodes;
57
58 bool Manager::fileExists(const std::string &file)
59 {
60     errno = 0;
61     struct stat info;
62     memset(&info, 0, sizeof(struct stat));
63     int status = lstat(file.c_str(), &info);
64     if (status == 0) {
65         return true;
66     } else if (errno == ENOENT) {
67         return false;
68     }
69     ThrowMsg(Commons::PlatformException, "Cannot stat file.");
70 }
71
72 void Manager::addOpenedNode(const INodePtr& node)
73 {
74     NodeListIterator it = m_openedNodes.begin();
75     for (; it != m_openedNodes.end(); ++it) {
76         if (node.Get() == (*it).Get()) {
77             //node is added already
78             return;
79         }
80     }
81     m_openedNodes.push_back(node);
82 }
83
84 void Manager::removeOpenedNode(const INodePtr& node)
85 {
86     NodeListIterator it = m_openedNodes.begin();
87     for (; it != m_openedNodes.end(); ++it) {
88         if ((*it).Get() == node.Get()) {
89             m_openedNodes.erase(it);
90             break;
91         }
92     }
93 }
94
95 bool Manager::checkIfOpened(const IPathPtr& path) const
96 {
97     NodeListIterator it = m_openedNodes.begin();
98     for (; it != m_openedNodes.end(); ++it) {
99         if (*path == *(*it)->getPath()) {
100             return true;
101         }
102     }
103     return false;
104 }
105
106 Manager::Manager()
107 {
108     static bool initialized = init();
109     (void) initialized;
110 }
111
112 Manager::~Manager()
113 {
114 }
115
116 IPathPtr Manager::getBasePath() const
117 {
118     Locations::const_iterator it = m_locations.find(LT_ROOT);
119     if (it == m_locations.end()) {
120         ThrowMsg(Commons::PlatformException, "Base path not available.");
121     }
122     return it->second;
123 }
124
125 IPathPtr Manager::getLocationPath(LocationType type) const
126 {
127     Locations::const_iterator it = m_locations.find(type);
128     if (it != m_locations.end()) {
129         return it->second->clone();
130     }
131     return IPathPtr();
132 }
133
134 LocationPaths Manager::getLocationPaths() const
135 {
136     LocationPaths result;
137     Locations::const_iterator it = m_locations.begin();
138     for (; it != m_locations.end(); ++it) {
139         result.push_back(it->second->clone());
140     }
141     return result;
142 }
143
144 LocationTypes Manager::getLocations() const
145 {
146     LocationTypes result;
147     Locations::const_iterator it = m_locations.begin();
148     for (; it != m_locations.end(); ++it) {
149         result.push_back(it->first);
150     }
151     return result;
152 }
153
154 void Manager::getNode(const EventResolvePtr& event)
155 {
156     EventRequestReceiver<EventResolve>::PostRequest(event);
157 }
158
159 std::size_t Manager::getMaxPathLength() const
160 {
161     return m_maxPathLength;
162 }
163
164 void Manager::copy(const EventCopyPtr& event)
165 {
166     EventRequestReceiver<EventCopy>::PostRequest(event);
167 }
168
169 void Manager::move(const EventMovePtr& event)
170 {
171     EventRequestReceiver<EventMove>::PostRequest(event);
172 }
173
174 void Manager::remove(const EventRemovePtr& event)
175 {
176     EventRequestReceiver<EventRemove>::PostRequest(event);
177 }
178
179 void Manager::find(const EventFindPtr& event)
180 {
181     EventRequestReceiver<EventFind>::PostRequest(event);
182 }
183
184 void Manager::find(const IPathPtr& path,
185         const FiltersMap& filters,
186         NodeList& result,
187         const EventFindPtr& event)
188 {
189     Try {
190         DIR* dir = opendir(path->getFullPath().c_str());
191         if (!dir) {
192             return;
193         }
194
195         errno = 0;
196         struct dirent* entry = NULL;
197         while ((entry = readdir(dir))) {
198             if (event && event->checkCancelled()) {
199                 break;
200             }
201             if (!strncmp(entry->d_name, ".",
202                          1) || !strncmp(entry->d_name, "..", 2)) {
203                 continue;
204             }
205             IPathPtr childPath = *path + entry->d_name;
206             struct stat info;
207             memset(&info, 0, sizeof(struct stat));
208             if (lstat(childPath->getFullPath().c_str(), &info) == 0) {
209                 if (matchFilters(entry->d_name, info, filters)) {
210                     result.push_back(Node::resolve(childPath));
211                 }
212                 if (S_ISDIR(info.st_mode)) {
213                     find(childPath, filters, result, event);
214                 }
215             }
216         }
217
218         if (errno != 0) {
219             ThrowMsg(Commons::PlatformException,
220                      "Error while reading directory.");
221         }
222
223         if (closedir(dir) != 0) {
224             ThrowMsg(Commons::PlatformException,
225                      "Could not close platform node.");
226         }
227     }
228     Catch(Commons::Exception) {
229     }
230 }
231
232 bool Manager::access(const IPathPtr& path,
233         int accessType) const
234 {
235     int amode = 0;
236     if (accessType & AT_EXISTS) { amode |= F_OK; }
237     if (accessType & AT_READ) { amode |= R_OK; }
238     if (accessType & AT_WRITE) { amode |= W_OK; }
239     if (accessType & AT_EXEC) { amode |= X_OK; }
240     return (::access(path->getFullPath().c_str(), amode) == 0);
241 }
242
243 bool Manager::matchFilters(const std::string& name,
244         const struct stat& info,
245         const FiltersMap& filters)
246 {
247     FiltersMap::const_iterator it = filters.begin();
248     for (; it != filters.end(); ++it) {
249         if (it->first == FF_NAME) {
250             if (!pcrecpp::RE(it->second).PartialMatch(name)) { return false; }
251         } else if (it->first == FF_SIZE) {
252             std::size_t size;
253             std::stringstream ss(it->second);
254             ss >> size;
255             if (!ss ||
256                 (size != static_cast<size_t>(info.st_size))) { return false; }
257         } else if (it->first == FF_CREATED) {
258             std::time_t created;
259             std::stringstream ss(it->second);
260             ss >> created;
261             if (!ss || (created != info.st_ctime)) { return false; }
262         } else if (it->first == FF_MODIFIED) {
263             std::time_t modified;
264             std::stringstream ss(it->second);
265             ss >> modified;
266             if (!ss || (modified != info.st_mtime)) { return false; }
267         }
268     }
269     return true;
270 }
271
272 void Manager::OnRequestReceived(const EventResolvePtr& event)
273 {
274     try {
275         event->setResult(Node::resolve(event->getPath()));
276     }
277     catch (const Commons::PlatformException& ex) {
278         LogError("Exception: " << ex.GetMessage());
279         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
280     }
281     event->setCancelAllowed(true);
282 }
283
284 void Manager::OnRequestReceived(const EventCopyPtr& event)
285 {
286     Try {
287         INodePtr srcNode = Node::resolve(event->getSource());
288         if ((srcNode->getMode() & PERM_READ) == 0) {
289             ThrowMsg(Commons::SecurityException,
290                      "Not enough permissions to read source node.");
291         }
292
293         IPathPtr src = event->getSource();
294         IPathPtr dest = event->getDestination();
295         if (!dest->isAbsolute()) {
296             dest = src->getPath() + *dest;
297         }
298
299         if (src == dest) {
300             ThrowMsg(Commons::PlatformException,
301                      "Destination is same as source.");
302         }
303
304         INodePtr parent;
305         Try {
306             parent = Node::resolve(IPath::create(dest->getPath()));
307         }
308         Catch(Commons::PlatformException) {
309             ReThrowMsg(Commons::PlatformException,
310                        "Could not get destination's parent node.");
311         }
312
313         if (parent->getType() != NT_DIRECTORY) {
314             ThrowMsg(Commons::PlatformException,
315                      "Destination's parent node is not directory.");
316         }
317
318         std::string realSrc = src->getFullPath();
319         std::string realDest = dest->getFullPath();
320
321         errno = 0;
322         struct stat info;
323         memset(&info, 0, sizeof(struct stat));
324         int status = lstat(realDest.c_str(), &info);
325         if ((status != 0) && (errno != ENOENT)) {
326             ThrowMsg(Commons::PlatformException,
327                      "No access to platform destination node.");
328         }
329
330         if (((event->getOptions() & OPT_OVERWRITE) == 0) && (status == 0)) {
331             ThrowMsg(Commons::PlatformException, "Overwrite is not set.");
332         }
333
334         if (event->checkCancelled()) {
335             //file is not copied yet, so we can cancel it now.
336             event->setCancelAllowed(true);
337             return;
338         }
339
340         if (event->getOptions() & OPT_OVERWRITE) {
341             DPL::ScopedPtr<RemoveCommand> remove(new RemoveCommand(dest));
342             remove->setRecursive(true);
343             remove->setForce(true);
344             System::run(remove.Get());
345         }
346
347         DPL::ScopedPtr<CopyCommand> copy(new CopyCommand(src, dest));
348         copy->setRecursive(true);
349         System::run(copy.Get());
350
351         event->setResult(Node::resolve(dest));
352     }
353     Catch(Commons::PlatformException) {
354         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
355     }
356     Catch(Commons::SecurityException) {
357         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
358     }
359     //file is copied already so we don't allow cancelling anymore.
360     event->setCancelAllowed(false);
361 }
362
363 void Manager::OnRequestReceived(const EventMovePtr& event)
364 {
365     try {
366         IPathPtr src = event->getSource();
367         IPathPtr dest = event->getDestination();
368
369         INodePtr srcNode = Node::resolve(src);
370         if ((srcNode->getMode() & PERM_WRITE) == 0)
371         {
372             ThrowMsg(Commons::SecurityException,
373                      "Not enough permissions to move source node.");
374         }
375
376         if (!dest->isAbsolute()) {
377             dest = src->getPath() + *dest;
378         }
379
380         if (src == dest) {
381             ThrowMsg(Commons::PlatformException,
382                      "Destination is same as source.");
383         }
384
385         INodePtr parent;
386         Try {
387             parent = Node::resolve(IPath::create(dest->getPath()));
388         }
389         Catch(Commons::PlatformException) {
390             ReThrowMsg(Commons::PlatformException,
391                        "Could not get destination's parent node.");
392         }
393
394         if (parent->getType() != NT_DIRECTORY) {
395             ThrowMsg(Commons::PlatformException,
396                      "Destination's parent node is not directory.");
397         }
398
399         errno = 0;
400         struct stat info;
401         memset(&info, 0, sizeof(info));
402         int status = lstat(dest->getFullPath().c_str(), &info);
403         if ((status != 0) && (errno != ENOENT)) {
404             ThrowMsg(Commons::PlatformException,
405                      "No access to platform destination node.");
406         }
407
408         if ((0 == (event->getOptions() & OPT_OVERWRITE)) && (0 == status)) {
409             ThrowMsg(Commons::PlatformException, "Overwrite is not set.");
410         }
411
412         if (event->checkCancelled()) {
413             //file is not moved yet, so we can cancel it now.
414             event->setCancelAllowed(true);
415             return;
416         }
417
418         errno = 0;
419         if (0 != ::rename(src->getFullPath().c_str(),
420                           dest->getFullPath().c_str()))
421         {
422             int error = errno;
423             switch (error)
424             {
425             case EXDEV:
426                 {
427                     if ((srcNode->getMode() & PERM_READ) == 0)
428                     {
429                         ThrowMsg(Commons::SecurityException,
430                                  "Not enough permissions to move source node.");
431                     }
432                     DPL::ScopedPtr<MoveCommand>
433                             move(new MoveCommand(src, dest));
434                     move->setForce(event->getOptions() & OPT_OVERWRITE);
435                     System::run(move.Get());
436                     break;
437                 }
438             default:
439                 ThrowMsg(Commons::PlatformException,
440                          "Error on rename: " << DPL::GetErrnoString(error));
441                 break;
442             }
443         }
444
445         event->setResult(Node::resolve(dest));
446     }
447     catch (const Commons::PlatformException& ex) {
448         LogError("Exception: " << ex.GetMessage());
449         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
450     }
451     catch (const Commons::SecurityException& ex) {
452         LogError("Exception: " << ex.GetMessage());
453         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
454     }
455     event->setCancelAllowed(false);
456 }
457
458 void Manager::OnRequestReceived(const EventRemovePtr& event)
459 {
460     if (!event->checkCancelled()) {
461         Try {
462             INodePtr node = Node::resolve(event->getPath());
463             node->remove(event->getOptions());
464         }
465         Catch(Commons::PlatformException) {
466             LogError("Exception: " << _rethrown_exception.GetMessage());
467             event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
468         }
469         Catch(Commons::SecurityException) {
470             event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
471         }
472         event->setCancelAllowed(false);
473     } else {
474         event->setCancelAllowed(true);
475     }
476 }
477
478 void Manager::OnRequestReceived(const EventFindPtr& event)
479 {
480     try {
481         NodeList result;
482         find(event->getPath(), event->getFilters(), result, event);
483         event->setResult(result);
484     }
485     catch (const Commons::Exception& ex) {
486         LogError("Exception: " << ex.GetMessage());
487         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
488     }
489     event->setCancelAllowed(true);
490 }
491
492 bool Manager::init()
493 {
494     m_locations[LT_ROOT] = IPath::create(PATH_ROOT);
495     m_locations[LT_SDCARD] = IPath::create(PATH_SDCARD);
496     setupLocation(LT_DOWNLOADS, PATH_DOWNLOADS);
497     setupLocation(LT_DOCUMENTS, PATH_DOCUMENTS);
498     setupLocation(LT_SOUNDS, PATH_SOUNDS);
499     setupLocation(LT_IMAGES, PATH_IMAGES);
500     setupLocation(LT_VIDEOS, PATH_VIDEOS);
501
502     return true;
503 }
504
505 void Manager::setupLocation(LocationType location,
506         const char* path)
507 {
508     if (!nodeExists(path)) {
509         try {
510             makePath(path, 0755);
511         }
512         catch (const Commons::PlatformException& ex) {
513             LogError("Exception: " << ex.DumpToString());
514             return;
515         }
516     }
517     m_locations[location] = IPath::create(path);
518 }
519
520 } // Filesystem
521 } // WrtDeviceApis