Update wrt-plugins-common_0.3.53
[framework/web/wrt-plugins-common.git] / src / modules / tizen / Filesystem / Node.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 "Node.h"
17 #include <algorithm>
18 #include <memory>
19 #include <typeinfo>
20 #include <sys/types.h>
21 #include <cstdio>
22 #include <unistd.h>
23 #include <dirent.h>
24 #include <errno.h>
25 #include <pcrecpp.h>
26 #include <sstream>
27 #include <Commons/Exception.h>
28 #include <Filesystem/PathUtils.h>
29 #include <Filesystem/Enums.h>
30 #include "Manager.h"
31 #include "NodeFilterMatcher.h"
32
33 namespace WrtDeviceApis {
34 namespace Filesystem {
35
36 using namespace Api;
37
38 INodePtr Node::resolve(const IPathPtr& path)
39 {
40     struct stat info;
41     if (lstat(path->getFullPath().c_str(), &info) != 0) {
42         LogError("File: " << path->getFullPath().c_str());
43         ThrowMsg(Commons::PlatformException,
44                  "Node does not exist or access denied.");
45     }
46
47     if (!S_ISDIR(info.st_mode) & !S_ISREG(info.st_mode)) {
48         ThrowMsg(Commons::PlatformException,
49                  "Platform node is of unsupported type.");
50     }
51
52     NodeType type = S_ISDIR(info.st_mode) ? NT_DIRECTORY : NT_FILE;
53     NodePtr result(new Node(path, type));
54     return DPL::StaticPointerCast<INode>(result);
55 }
56
57 IPathPtr Node::getPath() const
58 {
59     return IPath::create(m_path->getFullPath());
60 }
61
62 INodePtr Node::getChild(const IPathPtr& path)
63 {
64     if (m_type != NT_DIRECTORY) {
65         ThrowMsg(Commons::PlatformException, "Not a directory.");
66     }
67     return Node::resolve(*m_path + *path);
68 }
69
70 NodeType Node::getType() const
71 {
72     return m_type;
73 }
74
75 int Node::getPermissions() const
76 {
77     return m_perms;
78 }
79
80 void Node::setPermissions(int perms)
81 {
82     m_perms = perms;
83 }
84
85 Node::NameList Node::getChildNames() const
86 {
87     if (m_type != NT_DIRECTORY) {
88         ThrowMsg(Commons::PlatformException, "Node is not directory.");
89     }
90
91     if ((m_perms & PERM_READ) == 0) {
92         ThrowMsg(Commons::SecurityException, "No permission.");
93     }
94
95     DIR* dir = opendir(m_path->getFullPath().c_str());
96     if (!dir) {
97         ThrowMsg(Commons::PlatformException,
98                  "Node has been deleted from platform.");
99     }
100
101     NameList result;
102     errno = 0;
103     struct dirent *entry = NULL;
104     while ((entry = readdir(dir))) {
105         if (!strncmp(entry->d_name, ".",
106                      1) || !strncmp(entry->d_name, "..", 2)) {
107             continue;
108         }
109         result.push_back(entry->d_name);
110     }
111     if (errno != 0) {
112         ThrowMsg(Commons::PlatformException, "Error while reading directory.");
113     }
114
115     if (closedir(dir) != 0) {
116         ThrowMsg(Commons::PlatformException, "Could not close platform node.");
117     }
118
119     return result;
120 }
121
122 NodeList Node::getChildNodes(const NodeFilterPtr& filter) const
123 {
124     if (m_type != NT_DIRECTORY) {
125         ThrowMsg(Commons::PlatformException, "Node is not directory.");
126     }
127
128     if ((m_perms & PERM_READ) == 0) {
129         ThrowMsg(Commons::SecurityException, "No permission.");
130     }
131
132     DIR* dir = opendir(m_path->getFullPath().c_str());
133     if (!dir) {
134         ThrowMsg(Commons::PlatformException,
135                  "Node has been deleted from platform.");
136     }
137
138     errno = 0;
139     NodeList result;
140     struct dirent *entry = NULL;
141     while ((entry = readdir(dir))) {
142         if (!strncmp(entry->d_name, ".",
143                      1) || !strncmp(entry->d_name, "..", 2)) {
144             continue;
145         }
146         Try {
147             Assert(m_path);
148             INodePtr node = Node::resolve(*m_path + entry->d_name);
149             node->setPermissions(getPermissions()); // inherit access rights
150             if (NodeFilterMatcher::match(node, filter)) {
151                 result.push_back(node);
152             }
153         }
154         Catch(Commons::PlatformException) {
155         }
156     }
157
158     if (errno != 0) {
159         ThrowMsg(Commons::PlatformException, "Error while reading directory.");
160     }
161
162     if (closedir(dir) != 0) {
163         ThrowMsg(Commons::PlatformException, "Could not close platform node.");
164     }
165
166     return result;
167 }
168
169 void Node::getChildNodes(const EventListNodesPtr& event)
170 {
171     LogDebug("ENTER");
172     EventRequestReceiver<EventListNodes>::PostRequest(event);
173 }
174
175 INodePtr Node::createChild(
176         const IPathPtr& path,
177         NodeType type,
178         int options)
179 {
180     if (m_type != NT_DIRECTORY) {
181         ThrowMsg(Commons::PlatformException, "Parent node is not a directory.");
182     }
183
184     if ((m_perms & PERM_WRITE) == 0) {
185         ThrowMsg(Commons::SecurityException, "Not enough permissions.");
186     }
187
188     Assert(m_path);
189     Assert(path);
190     IPathPtr childPath = *m_path + *path;
191     if (exists(childPath)) {
192         ThrowMsg(Commons::PlatformException, "Node already exists.");
193     }
194
195     NodePtr result;
196     switch (type) {
197     case NT_FILE:
198         result.Reset(createAsFile(childPath, options));
199         break;
200     case NT_DIRECTORY:
201         result.Reset(createAsDirectory(childPath, options));
202         break;
203     default:
204         ThrowMsg(Commons::PlatformException, "Unsupported node type.");
205     }
206     if (!!result) {
207         result->m_perms = m_perms;
208     } else {
209         ThrowMsg(Commons::PlatformException, "Node creation error");
210     }
211
212     return DPL::StaticPointerCast<INode>(result);
213 }
214
215 IStreamPtr Node::open(int mode)
216 {
217     if (m_type == NT_DIRECTORY) {
218         ThrowMsg(Commons::PlatformException,
219                  "Cannot attach stream to directory.");
220     }
221
222     if (((mode & AM_READ) && ((m_perms & PERM_READ) == 0)) ||
223         (((mode & AM_WRITE) ||
224           (mode & AM_APPEND)) && ((m_perms & PERM_WRITE) == 0))) {
225         ThrowMsg(Commons::SecurityException, "Not enough permissions.");
226     }
227
228     DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
229     StreamPtr stream(new Stream(SharedFromThis(), mode));
230     m_openStreams.insert(stream);
231     IManager::getInstance().addOpenedNode(DPL::StaticPointerCast<INode>(
232                                               SharedFromThis()));
233     return DPL::StaticPointerCast<IStream>(stream);
234 }
235
236 void Node::open(const EventOpenPtr& event)
237 {
238     LogDebug("ENTER");
239     EventRequestReceiver<EventOpen>::PostRequest(event);
240 }
241
242 void Node::remove(int options)
243 {
244     switch (m_type) {
245     case NT_FILE:
246         removeAsFile(m_path);
247         break;
248     case NT_DIRECTORY:
249         removeAsDirectory(m_path, (options & OPT_RECURSIVE));
250         break;
251     }
252 }
253
254 std::size_t Node::getSize() const
255 {
256     if (m_type == NT_DIRECTORY) {
257         ThrowMsg(Commons::PlatformException,
258                  "Getting size for directories is not supported.");
259     }
260
261     struct stat info = stat(m_path);
262     if (!S_ISREG(info.st_mode)) {
263         ThrowMsg(Commons::PlatformException,
264                  "Specified node is not a regular file.");
265     }
266
267     return info.st_size;
268 }
269
270 std::time_t Node::getCreated() const
271 {
272     return stat(m_path).st_ctime;
273 }
274
275 std::time_t Node::getModified() const
276 {
277     return stat(m_path).st_mtime;
278 }
279
280 // TODO Optimize it, maybe store a flag indicating that node is a root.
281 INodePtr Node::getParent() const
282 {
283     LocationPaths roots = IManager::getInstance().getLocationPaths();
284     for (LocationPaths::iterator it = roots.begin(); it != roots.end(); ++it) {
285         Assert(*it);
286         Assert(m_path);
287         if (*(*it) == *m_path) {
288             return INodePtr();
289         }
290     }
291     return Node::resolve(IPath::create(m_path->getPath()));
292 }
293
294 int Node::getMode() const
295 {
296     int result = 0;
297     struct stat info = stat(m_path);
298     if (info.st_mode & S_IRUSR) { result |= PM_USER_READ; }
299     if (info.st_mode & S_IWUSR) { result |= PM_USER_WRITE; }
300     if (info.st_mode & S_IXUSR) { result |= PM_USER_EXEC; }
301     if (info.st_mode & S_IRGRP) { result |= PM_GROUP_READ; }
302     if (info.st_mode & S_IWGRP) { result |= PM_GROUP_WRITE; }
303     if (info.st_mode & S_IXGRP) { result |= PM_GROUP_EXEC; }
304     if (info.st_mode & S_IROTH) { result |= PM_OTHER_READ; }
305     if (info.st_mode & S_IWOTH) { result |= PM_OTHER_WRITE; }
306     if (info.st_mode & S_IXOTH) { result |= PM_OTHER_EXEC; }
307     return result;
308 }
309
310 void Node::read(const EventReadTextPtr& event)
311 {
312     LogDebug("ENTER");
313     EventRequestReceiver<EventReadText>::PostRequest(event);
314 }
315
316 void Node::onStreamClose(const StreamPtr& stream)
317 {
318     {
319         DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
320         m_openStreams.erase(stream);
321     }
322     if (m_openStreams.empty()) {
323         IManager::getInstance().removeOpenedNode(DPL::StaticPointerCast<INode>(
324                                                      SharedFromThis()));
325     }
326 }
327
328 bool Node::exists(const IPathPtr& path)
329 {
330     struct stat info;
331     memset(&info, 0, sizeof(struct stat));
332     int status = lstat(path->getFullPath().c_str(), &info);
333     if ((status == 0) || ((status != 0) && (errno != ENOENT))) {
334         return true;
335     }
336     return false;
337 }
338
339 struct stat Node::stat(const IPathPtr& path)
340 {
341     struct stat result;
342     memset(&result, 0, sizeof(struct stat));
343     if (::stat(path->getFullPath().c_str(),
344                 &result) != 0)
345     {
346         LogError("File: " << path->getFullPath().c_str());
347         ThrowMsg(Commons::PlatformException, "Node does not exist or no access");
348     }
349     return result;
350 }
351
352 Node::Node(const IPathPtr& path,
353            NodeType type) :
354     m_path(path),
355     m_type(type),
356     m_perms(PERM_NONE)
357 {
358 }
359
360 Node* Node::createAsFile(const IPathPtr& path,
361         int /* options */)
362 {
363     LogDebug("ENTER");
364     createAsFileInternal(path);
365     return new Node(path, NT_FILE);
366 }
367
368 void Node::createAsFileInternal(const IPathPtr& path)
369 {
370     LogDebug("ENTER");
371     FILE* file = std::fopen(path->getFullPath().c_str(), "wb");
372     if (!file) {
373         ThrowMsg(Commons::PlatformException,
374                  "Platform node could not be created.");
375     }
376     std::fclose(file);
377 }
378
379 Node* Node::createAsDirectory(const IPathPtr& path,
380         int options)
381 {
382     if (options & OPT_RECURSIVE) {
383         PathUtils::PathList parts = PathUtils::getParts(path);
384         PathUtils::PathListIterator it = parts.begin();
385         for (; it != parts.end(); ++it) {
386             if (!exists(*it)) { createAsDirectoryInternal(*it); }
387         }
388     }
389     createAsDirectoryInternal(path);
390     return new Node(path, NT_DIRECTORY);
391 }
392
393 void Node::createAsDirectoryInternal(const IPathPtr& path)
394 {
395     if (mkdir(path->getFullPath().c_str(), S_IRWXU | S_IRWXG | S_IROTH |
396               S_IXOTH) != 0) {
397         ThrowMsg(Commons::PlatformException,
398                  "Platform node could not be created.");
399     }
400 }
401
402 void Node::removeAsFile(const IPathPtr& path)
403 {
404     DPL::Mutex::ScopedLock lock(&m_openStreamsMutex);
405     if (!m_openStreams.empty()) {
406         ThrowMsg(Commons::PlatformException, "Node is locked for I/O.");
407     }
408     if (IManager::getInstance().checkIfOpened(path)) {
409         ThrowMsg(Commons::PlatformException, "Node is locked for I/O.");
410     }
411
412     if (unlink(path->getFullPath().c_str()) != 0) {
413         ThrowMsg(Commons::PlatformException,
414                  "Error while removing platform node.");
415     }
416 }
417
418 void Node::removeAsDirectory(const IPathPtr& path,
419                              bool recursive)
420 {
421     Assert(path);
422     if (recursive) {
423         DIR* dir = opendir(path->getFullPath().c_str());
424         if (!dir) {
425             LogError("File: " << path->getFullPath().c_str());
426             ThrowMsg(Commons::PlatformException,
427                      "Node does not exist or access denied.");
428         }
429         errno = 0;
430         struct dirent *entry = NULL;
431         while ((entry = readdir(dir))) {
432             if (!strncmp(entry->d_name, ".",
433                          1) || !strncmp(entry->d_name, "..", 2)) {
434                 continue;
435             }
436             IPathPtr subPath = *path + entry->d_name;
437             struct stat info;
438             memset(&info, 0, sizeof(struct stat));
439             if (lstat(subPath->getFullPath().c_str(), &info) == 0) {
440                 Try {
441                     if (S_ISDIR(info.st_mode)) {
442                         removeAsDirectory(subPath, true);
443                     } else if (S_ISREG(info.st_mode)) {
444                         removeAsFile(subPath);
445                     }
446                 }
447                 Catch(Commons::PlatformException) {
448                 }
449                 // TODO: Not sure if above exception should be swallowed.
450             }
451         }
452         closedir(dir);
453     }
454
455     errno = 0;
456     if (rmdir(path->getFullPath().c_str()) != 0) {
457         if (errno == EEXIST) {
458             ThrowMsg(Commons::PlatformException, "Node has child nodes.");
459         }
460         ThrowMsg(Commons::PlatformException,
461                  "Error while removing platform node.");
462     }
463 }
464
465 void Node::OnRequestReceived(const EventListNodesPtr& event)
466 {
467     try {
468         NodeList list = event->getNode()->getChildNodes(event->getFilter());
469         event->setResult(list);
470     }
471     catch (const Commons::PlatformException& ex) {
472         LogError("Exception: " << ex.GetMessage());
473         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
474     }
475     catch (const Commons::SecurityException& ex) {
476         LogError("Exception: " << ex.GetMessage());
477         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
478     }
479 }
480
481 void Node::OnRequestReceived(const EventOpenPtr& event)
482 {
483     if (!event->checkCancelled()) {
484         try {
485             IStreamPtr result = open(event->getMode());
486             event->setResult(result);
487         }
488         catch (const Commons::PlatformException& ex) {
489             LogError("Exception: " << ex.GetMessage());
490             event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
491         }
492         catch (const Commons::SecurityException& ex) {
493             LogError("Exception: " << ex.GetMessage());
494             event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
495         }
496         //event can be cancelled before executing this code.
497         //when it comes here we doesn't allow it anymore
498         event->setCancelAllowed(false);
499     } else {
500         event->setCancelAllowed(true);
501     }
502 }
503
504 void Node::OnRequestReceived(const EventReadTextPtr& event)
505 {
506     Try {
507         event->setResult(readText());
508         LogDebug("LEAVIN GRACEFULLY");
509     }
510     Catch(Commons::PlatformException) {
511         event->setExceptionCode(Commons::ExceptionCodes::PlatformException);
512     }
513     Catch(Commons::SecurityException) {
514         event->setExceptionCode(Commons::ExceptionCodes::SecurityException);
515     }
516     //this function doesn't change state of the platform,
517     //so we can allow to cancel it and discard results.
518     event->setCancelAllowed(true);
519 }
520
521 std::string Node::readText()
522 {
523     if (m_type != NT_FILE) {
524         ThrowMsg(Commons::PlatformException, "Node is not a file.");
525     }
526
527     if ((m_perms & PERM_READ) == 0) {
528         ThrowMsg(Commons::SecurityException, "No permission.");
529     }
530
531     std::stringstream result;
532     DPL::SharedPtr<Stream> stream(new Stream(SharedFromThis(), AM_READ));
533     while (!stream->isEof()) {
534         result << stream->getLine();
535         if(!stream->isEof())
536             result << '\n';
537     }
538     stream->close();
539     return result.str();
540 }
541
542 std::string Node::toUri(int /*widgetId*/) const
543 {
544     // TODO I believe moving this feature to WrtWrapper would make more sense.
545     return "file://" + m_path->getFullPath();
546 }
547
548 } // Filesystem
549 } // WrtDeviceApis