1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #define MLOG_TAG "Scanner"
16 
17 #include "scanner_utils.h"
18 
19 #include <cerrno>
20 #include <fstream>
21 
22 #include "directory_ex.h"
23 #include "media_column.h"
24 #include "media_log.h"
25 #include "medialibrary_type_const.h"
26 namespace OHOS {
27 namespace Media {
28 using namespace std;
29 
30 // Check if file exists or not
IsExists(const string & path)31 bool ScannerUtils::IsExists(const string &path)
32 {
33     struct stat statInfo {};
34 
35     if (path.empty()) {
36         MEDIA_ERR_LOG("Given path name is empty");
37         return false;
38     }
39 
40     return ((stat(path.c_str(), &statInfo)) == ERR_SUCCESS);
41 }
42 
43 // Get the file name from file URI
GetFileNameFromUri(const string & path)44 string ScannerUtils::GetFileNameFromUri(const string &path)
45 {
46     if (!path.empty()) {
47         size_t lastSlashPosition = path.rfind("/");
48         if (lastSlashPosition != string::npos) {
49             if (path.size() > lastSlashPosition) {
50                 return path.substr(lastSlashPosition + 1);
51             }
52         }
53     }
54 
55     MEDIA_ERR_LOG("Failed to obtain file name because given pathname is empty");
56     return "";
57 }
58 
59 // Get file extension from the given filepath uri
GetFileExtension(const string & path)60 string ScannerUtils::GetFileExtension(const string &path)
61 {
62     if (!path.empty()) {
63         size_t dotIndex = path.rfind(".");
64         if (dotIndex != string::npos) {
65             return path.substr(dotIndex + 1);
66         }
67     }
68 
69     MEDIA_ERR_LOG("Failed to obtain file extension because given pathname is empty");
70     return "";
71 }
72 
73 // Check if the given path is a directory path
IsDirectory(const string & path)74 bool ScannerUtils::IsDirectory(const string &path)
75 {
76     struct stat s;
77 
78     if (!path.empty()) {
79         if (stat(path.c_str(), &s) == 0) {
80             if (s.st_mode & S_IFDIR) {
81                 return true;
82             }
83         }
84     }
85 
86     MEDIA_ERR_LOG("Either path is empty or it is not a directory");
87     return false;
88 }
89 
IsRegularFile(const string & path)90 bool ScannerUtils::IsRegularFile(const string &path)
91 {
92     struct stat s;
93     if (!path.empty()) {
94         if (stat(path.c_str(), &s) == 0) {
95             if (s.st_mode & S_IFREG) {
96                 return true;
97             }
98         }
99     }
100 
101     return false;
102 }
103 
104 // Check if the given file starts with '.' , i.e. if it is hidden
IsFileHidden(const string & path)105 bool ScannerUtils::IsFileHidden(const string &path)
106 {
107     if (!path.empty()) {
108         string fileName = GetFileNameFromUri(path);
109         if (!fileName.empty() && fileName.at(0) == '.') {
110             return true;
111         }
112     }
113 
114     return false;
115 }
116 
117 // Get the parent path
GetParentPath(const string & path)118 string ScannerUtils::GetParentPath(const string &path)
119 {
120     if (!path.empty()) {
121         size_t lastSlashPosition = path.rfind("/");
122         if (lastSlashPosition != string::npos && path.size() > lastSlashPosition) {
123             return path.substr(0, lastSlashPosition);
124         }
125     }
126 
127     MEDIA_ERR_LOG("Failed to obtain the parent path");
128     return "";
129 }
130 
GetRootMediaDir(string & dir)131 void ScannerUtils::GetRootMediaDir(string &dir)
132 {
133     dir = ROOT_MEDIA_DIR;
134 }
135 
GetFileTitle(const string & displayName)136 string ScannerUtils::GetFileTitle(const string &displayName)
137 {
138     string::size_type pos = displayName.find_last_of('.');
139     return (pos == string::npos) ? displayName : displayName.substr(0, pos);
140 }
141 
IsDirHidden(const string & path,bool skipPhoto)142 bool ScannerUtils::IsDirHidden(const string &path, bool skipPhoto)
143 {
144     bool dirHid = false;
145 
146     if (!path.empty()) {
147         string dirName = ScannerUtils::GetFileNameFromUri(path);
148         if (!dirName.empty() && dirName.at(0) == '.') {
149             MEDIA_DEBUG_LOG("hidden Directory, name:%{private}s path:%{private}s", dirName.c_str(), path.c_str());
150             return true;
151         }
152 
153         string curPath = path;
154         string excludePath = curPath.append("/.nomedia");
155         // Check is the folder consist of .nomedia file
156         if (ScannerUtils::IsExists(excludePath)) {
157             return true;
158         }
159 
160         // Check is the dir is part of skiplist
161         if (skipPhoto && CheckSkipScanList(path)) {
162             MEDIA_DEBUG_LOG("skip Directory, path:%{private}s", path.c_str());
163             return true;
164         }
165     }
166 
167     return dirHid;
168 }
169 
IsDirHiddenRecursive(const string & path,bool skipPhoto)170 bool ScannerUtils::IsDirHiddenRecursive(const string &path, bool skipPhoto)
171 {
172     bool dirHid = false;
173     string curPath = path;
174 
175     do {
176         dirHid = IsDirHidden(curPath, skipPhoto);
177         if (dirHid) {
178             break;
179         }
180 
181         curPath = ScannerUtils::GetParentPath(curPath);
182         if (curPath.empty()) {
183             break;
184         }
185     } while (true);
186 
187     return dirHid;
188 }
189 
190 // Check if path is part of Skip scan list
CheckSkipScanList(const string & path)191 bool ScannerUtils::CheckSkipScanList(const string &path)
192 {
193     if (path.length() <= ROOT_MEDIA_DIR.length()) {
194         return false;
195     }
196     static const string AUDIO = "Audios";
197     static const string CAMERA = "Camera";
198     static const string Pictures = "Pictures";
199     static const string Videos = "Videos";
200     static const string Doc = "Documents";
201     static const string Download = "Download";
202     // white list
203     static vector<string> list = {
204         { ROOT_MEDIA_DIR + AUDIO },
205         { ROOT_MEDIA_DIR + CAMERA },
206         { ROOT_MEDIA_DIR + Pictures },
207         { ROOT_MEDIA_DIR + Videos },
208         { ROOT_MEDIA_DIR + Doc },
209         { ROOT_MEDIA_DIR + Download },
210     };
211     for (const auto &pathPrefix : list) {
212         if (path.find(pathPrefix) != string::npos) {
213             return false;
214         }
215     }
216     return true;
217 }
218 } // namespace Media
219 } // namespace OHOS
220