Update wrt-plugins-common_0.3.53
[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 <ecore-1/Ecore_File.h>
27 #include <eina-1/eina/eina_list.h>
28 #include <dpl/log/log.h>
29 #include <dpl/scoped_ptr.h>
30 #include <dpl/errno_string.h>
31 #include <Commons/Exception.h>
32 #include <Commons/Regex.h>
33 #include <Filesystem/PathUtils.h>
34 #include "Node.h"
35 #include "Utils.h"
36
37 namespace {
38 const char* PATH_ROOT = "/opt/media";
39 const char* PATH_DOWNLOADS = "/opt/media/Downloads";
40 const char* PATH_DOCUMENTS = "/opt/media/Documents";
41 const char* PATH_SOUNDS = "/opt/media/Music";
42 const char* PATH_IMAGES = "/opt/media/Images";
43 const char* PATH_VIDEOS = "/opt/media/Videos";
44 const char* PATH_SDCARD = "/opt/storage/sdcard";
45 }
46
47 namespace WrtDeviceApis {
48 namespace Filesystem {
49
50 using namespace Api;
51
52 Manager::Locations Manager::m_locations;
53 const std::size_t Manager::m_maxPathLength = 256;
54 NodeList Manager::m_openedNodes;
55
56 bool Manager::fileExists(const std::string &file)
57 {
58     errno = 0;
59     struct stat info;
60     memset(&info, 0, sizeof(struct stat));
61     int status = lstat(file.c_str(), &info);
62     if (status == 0) {
63         return true;
64     } else if (errno == ENOENT) {
65         return false;
66     }
67     ThrowMsg(Commons::PlatformException, "Cannot stat file.");
68 }
69
70 void Manager::addOpenedNode(const INodePtr& node)
71 {
72     NodeListIterator it = m_openedNodes.begin();
73     for (; it != m_openedNodes.end(); ++it) {
74         if (node.Get() == (*it).Get()) {
75             //node is added already
76             return;
77         }
78     }
79     m_openedNodes.push_back(node);
80 }
81
82 void Manager::removeOpenedNode(const INodePtr& node)
83 {
84     NodeListIterator it = m_openedNodes.begin();
85     for (; it != m_openedNodes.end(); ++it) {
86         if ((*it).Get() == node.Get()) {
87             m_openedNodes.erase(it);
88             break;
89         }
90     }
91 }
92
93 bool Manager::checkIfOpened(const IPathPtr& path) const
94 {
95     Assert(path);
96     NodeListIterator it = m_openedNodes.begin();
97     for (; it != m_openedNodes.end(); ++it) {
98         Assert(*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         Assert(path && "path is NULL");
191
192         DIR* dir = opendir(path->getFullPath().c_str());
193         if (!dir) {
194             return;
195         }
196
197         errno = 0;
198         struct dirent* entry = NULL;
199         while ((entry = readdir(dir))) {
200             if (event && event->checkCancelled()) {
201                 break;
202             }
203             if (!strncmp(entry->d_name, ".",
204                          1) || !strncmp(entry->d_name, "..", 2)) {
205                 continue;
206             }
207             IPathPtr childPath = *path + entry->d_name;
208             struct stat info;
209             memset(&info, 0, sizeof(struct stat));
210             if (lstat(childPath->getFullPath().c_str(), &info) == 0) {
211                 if (matchFilters(entry->d_name, info, filters)) {
212                     result.push_back(Node::resolve(childPath));
213                 }
214                 if (S_ISDIR(info.st_mode)) {
215                     find(childPath, filters, result, event);
216                 }
217             }
218         }
219
220         if (errno != 0) {
221             ThrowMsg(Commons::PlatformException,
222                      "Error while reading directory.");
223         }
224
225         if (closedir(dir) != 0) {
226             ThrowMsg(Commons::PlatformException,
227                      "Could not close platform node.");
228         }
229     }
230     Catch(Commons::Exception) {
231     }
232 }
233
234 void Manager::copyElement(
235         const std::string &src, const std::string &dest, bool recursive) const
236 {
237     LogDebug("Copying src: " << src << " to: " << dest);
238
239     //element is a file:
240     if (EINA_TRUE != ecore_file_is_dir(src.c_str())) {
241         if (EINA_TRUE != ecore_file_cp(src.c_str(), dest.c_str())) {
242             ThrowMsg(Commons::PlatformException, "Failed to copy file");
243         }
244         return;
245     }
246     //element is a directory -> create it:
247     if (EINA_TRUE != ecore_file_mkdir(dest.c_str())) {
248         LogDebug("Failed to create destination directory");
249         ThrowMsg(Commons::PlatformException, "Failed to copy directory");
250     }
251     //copy all elements of directory:
252     if (recursive) {
253         Eina_List* list = ecore_file_ls(src.c_str());
254         void* data;
255         EINA_LIST_FREE(list, data)
256         {
257             Try
258             {
259                 copyElement((src + '/' + static_cast<char*>(data)).c_str(),
260                             (dest + '/' + static_cast<char*>(data)).c_str());
261             }
262             Catch(Commons::PlatformException)
263             {
264                 //remove rest of the list
265                 EINA_LIST_FREE(list, data)
266                 {
267                     free(data);
268                 }
269                 ReThrowMsg(Commons::PlatformException, "Failed to copy element");
270             }
271             free(data);
272         }
273     }
274
275 }
276
277 bool Manager::access(const IPathPtr& path,
278         int accessType) const
279 {
280     int amode = 0;
281     if (accessType & AT_EXISTS) { amode |= F_OK; }
282     if (accessType & AT_READ) { amode |= R_OK; }
283     if (accessType & AT_WRITE) { amode |= W_OK; }
284     if (accessType & AT_EXEC) { amode |= X_OK; }
285     return (::access(path->getFullPath().c_str(), amode) == 0);
286 }
287
288 bool Manager::matchFilters(const std::string& name,
289         const struct stat& info,
290         const FiltersMap& filters)
291 {
292     FiltersMap::const_iterator it = filters.begin();
293     for (; it != filters.end(); ++it) {
294         if (it->first == FF_NAME) {
295             if (!pcrecpp::RE(it->second).PartialMatch(name)) { return false; }
296         } else if (it->first == FF_SIZE) {
297             std::size_t size;
298             std::stringstream ss(it->second);
299             ss >> size;
300             if (!ss ||
301                 (size != static_cast<size_t>(info.st_size))) { return false; }
302         } else if (it->first == FF_CREATED) {
303             std::time_t created;
304             std::stringstream ss(it->second);
305             ss >> created;
306             if (!ss || (created != info.st_ctime)) { return false; }
307         } else if (it->first == FF_MODIFIED) {
308             std::time_t modified;
309             std::stringstream ss(it->second);
310             ss >> modified;
311             if (!ss || (modified != info.st_mtime)) { return false; }
312         }
313     }
314     return true;
315 }
316
317 void Manager::OnRequestReceived(const EventResolvePtr& event)
318 {
319     try {
320         event->setResult(Node::resolve(event->getPath()));
321     }
322     catch (const Commons::PlatformException& ex) {
323         LogError("Exception: " << ex.GetMessage());
324         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
325     }
326     event->setCancelAllowed(true);
327 }
328
329 void Manager::OnRequestReceived(const EventCopyPtr& event)
330 {
331     Try {
332         INodePtr srcNode = Node::resolve(event->getSource());
333         int requiredAccess;
334         switch (srcNode->getType()) {
335             case NT_DIRECTORY:
336                 requiredAccess = AT_EXEC;
337                 break;
338             case NT_FILE:
339                 requiredAccess = AT_READ;
340                 break;
341         }
342         if (!access(srcNode->getPath(), requiredAccess)) {
343             ThrowMsg(Commons::SecurityException,
344                 "Not enough permissions to copy source node.");
345         }
346
347         IPathPtr src = event->getSource();
348         IPathPtr dest = event->getDestination();
349
350         Assert(dest);
351         if (!dest->isAbsolute()) {
352             dest = src->getPath() + *dest;
353         }
354
355         if (src == dest) {
356             ThrowMsg(Commons::PlatformException,
357                      "Destination is same as source: " <<
358                      srcNode->getPath()->getFullPath());
359         }
360
361         INodePtr parent;
362         Try {
363             parent = Node::resolve(IPath::create(dest->getPath()));
364         }
365         Catch(Commons::PlatformException) {
366             ReThrowMsg(Commons::PlatformException,
367                        "Could not get destination's parent node.");
368         }
369
370         if (parent->getType() != NT_DIRECTORY) {
371             ThrowMsg(Commons::PlatformException,
372                      "Destination's parent node is not directory.");
373         }
374
375         if (!access(parent->getPath(), AT_WRITE)) {
376             ThrowMsg(Commons::SecurityException,
377                 "Not enough permissions to copy source node to destination.");
378         }
379
380         std::string realSrc = src->getFullPath();
381         std::string realDest = dest->getFullPath();
382
383         errno = 0;
384         struct stat info;
385         memset(&info, 0, sizeof(struct stat));
386         int status = lstat(realDest.c_str(), &info);
387         if ((status != 0) && (errno != ENOENT)) {
388             ThrowMsg(Commons::PlatformException,
389                      "No access to platform destination node.");
390         }
391
392         if (0 == status) {
393             //no owerwrite flag setted -> exception
394             if ((event->getOptions() & OPT_OVERWRITE) == 0) {
395                 ThrowMsg(Commons::PlatformException, "Overwrite is not set.");
396             }
397
398             if (event->checkCancelled()) {
399                 //file is not copied yet, so we can cancel it now.
400                 event->setCancelAllowed(true);
401                 return;
402             }
403
404             //destination exist. Need to be removed
405             Try {
406                 INodePtr node = Node::resolve(event->getDestination());
407                 node->remove(event->getOptions());
408             }
409             Catch(Commons::PlatformException) {
410                 LogError("Exception: " << _rethrown_exception.GetMessage());
411                 event->setExceptionCode(
412                     Commons::ExceptionCodes::PlatformException);
413             }
414             Catch(Commons::SecurityException) {
415                 event->setExceptionCode(
416                     Commons::ExceptionCodes::SecurityException);
417             }
418         }
419         //Destination is not exist. Start copy now.
420         copyElement(realSrc, realDest);
421
422         event->setResult(Node::resolve(dest));
423     }
424     catch (const Commons::PlatformException& ex) {
425         LogError("Exception: " << ex.GetMessage());
426         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
427     }
428     catch (const Commons::SecurityException& ex) {
429         LogError("Exception: " << ex.GetMessage());
430         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
431     }
432     //file is copied already so we don't allow cancelling anymore.
433     event->setCancelAllowed(false);
434 }
435
436 void Manager::OnRequestReceived(const EventMovePtr& event)
437 {
438     try {
439         IPathPtr src = event->getSource();
440         IPathPtr dest = event->getDestination();
441
442         INodePtr srcNode = Node::resolve(src);
443         if (!access(srcNode->getParent()->getPath(), AT_WRITE)) {
444             ThrowMsg(Commons::SecurityException,
445                 "Not enough permissions to move source node.");
446         }
447
448         Assert(dest);
449         if (!dest->isAbsolute()) {
450             dest = src->getPath() + *dest;
451         }
452
453         if (src == dest) {
454             ThrowMsg(Commons::PlatformException,
455                      "Destination is same as source: " <<
456                      srcNode->getPath()->getFullPath());
457         }
458
459         INodePtr parent;
460         Try {
461             parent = Node::resolve(IPath::create(dest->getPath()));
462         }
463         Catch(Commons::PlatformException) {
464             ReThrowMsg(Commons::PlatformException,
465                        "Could not get destination's parent node.");
466         }
467
468         if (parent->getType() != NT_DIRECTORY) {
469             ThrowMsg(Commons::PlatformException,
470                      "Destination's parent node is not directory.");
471         }
472
473         if (!access(parent->getPath(), AT_WRITE)) {
474             ThrowMsg(Commons::SecurityException,
475                 "Not enough permissions to move source node to destination.");
476         }
477
478         errno = 0;
479         struct stat info;
480         memset(&info, 0, sizeof(info));
481         int status = lstat(dest->getFullPath().c_str(), &info);
482         if ((status != 0) && (errno != ENOENT)) {
483             ThrowMsg(Commons::PlatformException,
484                      "No access to platform destination node.");
485         }
486
487         if ((0 == (event->getOptions() & OPT_OVERWRITE)) && (0 == status)) {
488             ThrowMsg(Commons::PlatformException, "Overwrite is not set.");
489         }
490
491         if (event->checkCancelled()) {
492             //file is not moved yet, so we can cancel it now.
493             event->setCancelAllowed(true);
494             return;
495         }
496
497         errno = 0;
498         if (0 != ::rename(src->getFullPath().c_str(),
499                           dest->getFullPath().c_str()))
500         {
501             int error = errno;
502             switch (error)
503             {
504             case EXDEV:
505                 {
506                     if (0 == status) {
507                         //destination exist. Need to be removed
508                         Try {
509                             INodePtr node = Node::resolve(
510                                 event->getDestination());
511                             node->remove(event->getOptions());
512                         }
513                         Catch(Commons::PlatformException) {
514                             LogError("Exception while removing dest directory");
515                             event->setExceptionCode(
516                                 Commons::ExceptionCodes::PlatformException);
517                         }
518                         Catch(Commons::SecurityException) {
519                             event->setExceptionCode(
520                                 Commons::ExceptionCodes::SecurityException);
521                         }
522                     }
523
524                     copyElement(src->getFullPath(),
525                                 dest->getFullPath());
526                     //remove source files
527                     Try {
528                         INodePtr node = Node::resolve(event->getSource());
529                         node->remove(event->getOptions());
530                     }
531                     Catch(Commons::Exception) {
532                         LogError("Exception: "
533                                  << _rethrown_exception.GetMessage());
534                     }
535                     break;
536                 }
537             default:
538                 ThrowMsg(Commons::PlatformException,
539                          "Error on rename: " << DPL::GetErrnoString(error));
540                 break;
541             }
542         }
543
544         event->setResult(Node::resolve(dest));
545     }
546     catch (const Commons::PlatformException& ex) {
547         LogError("Exception: " << ex.GetMessage());
548         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
549     }
550     catch (const Commons::SecurityException& ex) {
551         LogError("Exception: " << ex.GetMessage());
552         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
553     }
554     event->setCancelAllowed(false);
555 }
556
557 void Manager::OnRequestReceived(const EventRemovePtr& event)
558 {
559     if (!event->checkCancelled()) {
560         Try {
561             INodePtr node = Node::resolve(event->getPath());
562             node->remove(event->getOptions());
563         }
564         Catch(Commons::PlatformException) {
565             LogError("Exception: " << _rethrown_exception.GetMessage());
566             event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
567         }
568         Catch(Commons::SecurityException) {
569             event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
570         }
571         event->setCancelAllowed(false);
572     } else {
573         event->setCancelAllowed(true);
574     }
575 }
576
577 void Manager::OnRequestReceived(const EventFindPtr& event)
578 {
579     try {
580         NodeList result;
581         find(event->getPath(), event->getFilters(), result, event);
582         event->setResult(result);
583     }
584     catch (const Commons::Exception& ex) {
585         LogError("Exception: " << ex.GetMessage());
586         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
587     }
588     event->setCancelAllowed(true);
589 }
590
591 bool Manager::init()
592 {
593     m_locations[LT_ROOT] = IPath::create(PATH_ROOT);
594     m_locations[LT_SDCARD] = IPath::create(PATH_SDCARD);
595     setupLocation(LT_DOWNLOADS, PATH_DOWNLOADS);
596     setupLocation(LT_DOCUMENTS, PATH_DOCUMENTS);
597     setupLocation(LT_SOUNDS, PATH_SOUNDS);
598     setupLocation(LT_IMAGES, PATH_IMAGES);
599     setupLocation(LT_VIDEOS, PATH_VIDEOS);
600
601     return true;
602 }
603
604 void Manager::setupLocation(LocationType location,
605         const char* path)
606 {
607     if (!nodeExists(path)) {
608         try {
609             makePath(path, 0755);
610         }
611         catch (const Commons::PlatformException& ex) {
612             LogError("Exception: " << ex.DumpToString());
613             return;
614         }
615     }
616     m_locations[location] = IPath::create(path);
617 }
618
619 } // Filesystem
620 } // WrtDeviceApis