1 /*
2 * Copyright (C) 2023 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 #include <sstream>
16
17 #include "media_file_uri.h"
18 #include "media_file_utils.h"
19 #include "media_log.h"
20 #include "medialibrary_errno.h"
21 #include "medialibrary_helper_container.h"
22 #include "medialibrary_type_const.h"
23 #include "photo_album_column.h"
24
25 using namespace std;
26 namespace OHOS {
27 namespace Media {
28 const size_t LEAST_PATH_LENGTH = 2;
29 const std::string MEDIA_FILE_ID_DEFAULT = "-1";
SolveMediaTypeV9(MediaType mediaType)30 static std::string SolveMediaTypeV9(MediaType mediaType)
31 {
32 switch (mediaType) {
33 case MEDIA_TYPE_AUDIO:
34 return MEDIALIBRARY_TYPE_AUDIO_URI;
35 case MEDIA_TYPE_VIDEO:
36 return MEDIALIBRARY_TYPE_VIDEO_URI;
37 case MEDIA_TYPE_IMAGE:
38 return MEDIALIBRARY_TYPE_IMAGE_URI;
39 case MEDIA_TYPE_FILE:
40 default:
41 return MEDIALIBRARY_TYPE_FILE_URI;
42 }
43 }
44
SolveMediaTypeV10(MediaType mediaType)45 static std::string SolveMediaTypeV10(MediaType mediaType)
46 {
47 switch (mediaType) {
48 case MEDIA_TYPE_AUDIO:
49 return AudioColumn::AUDIO_TYPE_URI;
50 case MEDIA_TYPE_VIDEO:
51 case MEDIA_TYPE_IMAGE:
52 return PhotoColumn::PHOTO_TYPE_URI;
53 case MEDIA_TYPE_FILE:
54 default:
55 return MEDIALIBRARY_TYPE_FILE_URI;
56 }
57 }
58
SolveMediaType(MediaType mediaType)59 static std::string SolveMediaType(MediaType mediaType)
60 {
61 switch (mediaType) {
62 case MEDIA_TYPE_AUDIO:
63 return MEDIALIBRARY_TYPE_AUDIO_URI;
64 case MEDIA_TYPE_VIDEO:
65 return MEDIALIBRARY_TYPE_VIDEO_URI;
66 case MEDIA_TYPE_IMAGE:
67 return MEDIALIBRARY_TYPE_IMAGE_URI;
68 case MEDIA_TYPE_ALBUM:
69 return MEDIALIBRARY_TYPE_ALBUM_URI;
70 case MEDIA_TYPE_SMARTALBUM:
71 return MEDIALIBRARY_TYPE_SMART_URI;
72 case MEDIA_TYPE_FILE:
73 default:
74 return MEDIALIBRARY_TYPE_FILE_URI;
75 }
76 }
77
ParseUri(const string & uri)78 void MediaFileUri::ParseUri(const string &uri)
79 {
80 if (MediaFileUtils::StartsWith(uri, PhotoColumn::PHOTO_URI_PREFIX)) {
81 uriType_ = API10_PHOTO_URI;
82 } else if (MediaFileUtils::StartsWith(uri, PhotoAlbumColumns::ALBUM_URI_PREFIX)) {
83 uriType_ = API10_PHOTOALBUM_URI;
84 } else if (MediaFileUtils::StartsWith(uri, AudioColumn::AUDIO_URI_PREFIX)) {
85 uriType_ = API10_AUDIO_URI;
86 } else {
87 uriType_ = API9_URI;
88 }
89 }
90
GetMediaTypeUri(MediaType mediaType,const int32_t & apiVersion)91 std::string MediaFileUri::GetMediaTypeUri(MediaType mediaType, const int32_t &apiVersion)
92 {
93 switch (apiVersion) {
94 case MEDIA_API_VERSION_V9:
95 return SolveMediaTypeV9(mediaType);
96 case MEDIA_API_VERSION_V10:
97 return SolveMediaTypeV10(mediaType);
98 case MEDIA_API_VERSION_DEFAULT:
99 default:
100 return SolveMediaType(mediaType);
101 }
102 }
103
MediaFileUriConstruct(MediaType mediaType,const std::string & fileId,const std::string & networkId,const int32_t & apiVersion,const std::string & extrUri)104 std::string MediaFileUri::MediaFileUriConstruct(MediaType mediaType, const std::string &fileId,
105 const std::string &networkId, const int32_t &apiVersion, const std::string &extrUri)
106 {
107 std::string uri = ML_FILE_URI_PREFIX;
108 uri += GetMediaTypeUri(mediaType, apiVersion);
109 if (!fileId.empty()) {
110 uri += "/" + fileId;
111 }
112
113 if (!networkId.empty()) {
114 uri += ML_URI_NETWORKID_EQUAL + networkId;
115 }
116
117 if (apiVersion == MEDIA_API_VERSION_V10) {
118 uri += extrUri;
119 uri = MediaFileUtils::Encode(uri);
120 }
121 ParseUri(uri);
122 return uri;
123 }
124
SetQueryMap(MediaFileUri * uri,std::unordered_map<std::string,std::string> & queryMap)125 static void SetQueryMap(MediaFileUri* uri, std::unordered_map<std::string,
126 std::string> &queryMap)
127 {
128 // file://media/image/12?networkid=xxxx&api_version=xxxx
129 std::string query = uri->GetQuery();
130 std::string pairString;
131 std::stringstream queryStream(query);
132
133 while (getline(queryStream, pairString, '&')) {
134 size_t splitIndex = pairString.find('=');
135 if (splitIndex == std::string::npos || splitIndex == (pairString.length() - 1)) {
136 MEDIA_ERR_LOG("failed to parse query, query field is %{private}s!", pairString.c_str());
137 continue;
138 }
139 queryMap[pairString.substr(0, splitIndex)] = pairString.substr(splitIndex + 1);
140 }
141 return;
142 }
143
CalNetworkId(MediaFileUri * uri,std::unordered_map<std::string,std::string> queryMap)144 static std::string CalNetworkId(MediaFileUri* uri, std::unordered_map<std::string,
145 std::string> queryMap)
146 {
147 std::string scheme = uri->GetScheme();
148 if (scheme == ML_FILE_SCHEME) {
149 if (queryMap.find(ML_URI_NETWORKID) != queryMap.end()) {
150 return queryMap[ML_URI_NETWORKID];
151 }
152 return "";
153 } else if (scheme == ML_DATA_SHARE_SCHEME) {
154 return uri->GetAuthority();
155 }
156 MEDIA_ERR_LOG("CalNetworkId scheme is invalid, scheme is %{private}s", scheme.c_str());
157 return "";
158 }
159
GetNetworkId()160 std::string MediaFileUri::GetNetworkId()
161 {
162 if (this->networkId_ != MEDIA_FILE_URI_EMPTY) {
163 return this->networkId_;
164 }
165 SetQueryMap(this, this->queryMap_);
166 this->networkId_ = CalNetworkId(this, this->queryMap_);
167 return this->networkId_;
168 }
169
ParsePathWithExtrPara(std::string & path)170 static void ParsePathWithExtrPara(std::string &path)
171 {
172 auto index = path.rfind('/');
173 if (index == std::string::npos) {
174 MEDIA_ERR_LOG("find split for last string failed, %{private}s", path.c_str());
175 return;
176 }
177 auto lastStr = path.substr(index + 1);
178 auto uriTempNext = path.substr(0, index);
179 index = uriTempNext.rfind('/');
180 if (index == std::string::npos) {
181 MEDIA_ERR_LOG("find split for next string failed %{private}s", uriTempNext.c_str());
182 return;
183 }
184 auto preStr = uriTempNext.substr(index + 1);
185 if (lastStr.find('.') != std::string::npos) {
186 if (!all_of(preStr.begin(), preStr.end(), ::isdigit)) {
187 path = uriTempNext.substr(0, index);
188 return;
189 }
190 preStr = uriTempNext.substr(0, index);
191 index = preStr.rfind('/');
192 if (index == std::string::npos) {
193 path = uriTempNext;
194 return;
195 }
196 path = preStr;
197 }
198 }
199
CalFileId(MediaFileUri * uri)200 static std::string CalFileId(MediaFileUri* uri)
201 {
202 std::string path = uri->GetPath();
203 if (uri->IsApi10()) {
204 ParsePathWithExtrPara(path);
205 }
206
207 if (path.length() < LEAST_PATH_LENGTH) {
208 MEDIA_ERR_LOG("path is too short, path is %{private}s", path.c_str());
209 return MEDIA_FILE_ID_DEFAULT;
210 }
211
212 std::size_t index = path.rfind("/");
213 if (index == std::string::npos || index == path.length() - 1) {
214 MEDIA_ERR_LOG("failed to rfind /, path is %{private}s", path.c_str());
215 return MEDIA_FILE_ID_DEFAULT;
216 }
217
218 std::string fileId = path.substr(index + 1);
219 if (!std::all_of(fileId.begin(), fileId.end(), ::isdigit)) {
220 MEDIA_DEBUG_LOG("fileId is not all digit, fileId is %{private}s", fileId.c_str());
221 return MEDIA_FILE_ID_DEFAULT;
222 }
223
224 return fileId;
225 }
226
GetFileId()227 std::string MediaFileUri::GetFileId()
228 {
229 if (this->fileId_ != MEDIA_FILE_URI_EMPTY) {
230 return this->fileId_;
231 }
232 this->fileId_ = CalFileId(this);
233 return this->fileId_;
234 }
235
GetTableName()236 std::string MediaFileUri::GetTableName()
237 {
238 static std::map<std::string, std::string> tableNameMap = {
239 { MEDIALIBRARY_TYPE_IMAGE_URI, PhotoColumn::PHOTOS_TABLE },
240 { MEDIALIBRARY_TYPE_VIDEO_URI, PhotoColumn::PHOTOS_TABLE },
241 { MEDIALIBRARY_TYPE_AUDIO_URI, AudioColumn::AUDIOS_TABLE },
242 { MEDIALIBRARY_TYPE_FILE_URI, MEDIALIBRARY_TABLE },
243 { AudioColumn::AUDIO_TYPE_URI, AudioColumn::AUDIOS_TABLE },
244 { PhotoColumn::PHOTO_TYPE_URI, PhotoColumn::PHOTOS_TABLE }
245 };
246
247 std::string uriString = ToString();
248 for (const auto &iter : tableNameMap) {
249 if (uriString.find(iter.first) != std::string::npos) {
250 return iter.second;
251 }
252 }
253 return "";
254 }
255
GetFilePath()256 std::string MediaFileUri::GetFilePath()
257 {
258 /* get helper */
259 std::shared_ptr<DataShare::DataShareHelper> dataShareHelper =
260 MediaLibraryHelperContainer::GetInstance()->GetDataShareHelper();
261 if (dataShareHelper == nullptr) {
262 MEDIA_ERR_LOG("get data share helper err");
263 return "";
264 }
265
266 DataShare::DatashareBusinessError error;
267 const std::string uriString = ToString();
268 std::string queryUri(UFM_QUERY_PHOTO);
269 DataShare::DataSharePredicates predicates;
270 std::vector<std::string> columns;
271 /* check api version */
272 if (uriString.find(PhotoColumn::PHOTO_TYPE_URI) != std::string::npos) {
273 predicates.EqualTo(MediaColumn::MEDIA_ID, GetFileId());
274 columns.emplace_back(MediaColumn::MEDIA_FILE_PATH);
275 MediaFileUtils::UriAppendKeyValue(queryUri, URI_PARAM_API_VERSION);
276 } else {
277 predicates.EqualTo(MEDIA_DATA_DB_ID, GetFileId());
278 columns.emplace_back(MEDIA_DATA_DB_FILE_PATH);
279 }
280 Uri uri(queryUri);
281 /* query and check */
282 auto resultSet = dataShareHelper->Query(uri, predicates, columns, &error);
283 int32_t ret = error.GetCode();
284 if (ret != 0) {
285 MEDIA_ERR_LOG("data share query err %{public}d", ret);
286 return "";
287 }
288 int32_t rowCount;
289 ret = resultSet->GetRowCount(rowCount);
290 if (ret != 0) {
291 MEDIA_ERR_LOG("result set get row count err %{public}d", ret);
292 return "";
293 }
294 if (rowCount != 1) {
295 MEDIA_ERR_LOG("more than one record");
296 return "";
297 }
298
299 /* get val */
300 ret = resultSet->GoToNextRow();
301 if (ret != 0) {
302 MEDIA_ERR_LOG("result set go to next row err %{public}d", ret);
303 return "";
304 }
305 std::string val;
306 ret = resultSet->GetString(0, val);
307 if (ret != 0) {
308 MEDIA_ERR_LOG("result set get string err %{public}d", ret);
309 return "";
310 }
311 return val;
312 }
313
IsValid()314 bool MediaFileUri::IsValid()
315 {
316 std::string scheme = this->GetScheme();
317 if (scheme != ML_FILE_SCHEME &&
318 scheme != ML_DATA_SHARE_SCHEME) {
319 MEDIA_ERR_LOG("scheme is invalid, uri is %{private}s", this->ToString().c_str());
320 return false;
321 }
322
323 if (this->GetAuthority() != ML_URI_AUTHORITY &&
324 this->GetPath().find(MEDIALIBRARY_DATA_URI_IDENTIFIER) != 0) {
325 MEDIA_ERR_LOG("failed to find /media, uri is %{private}s", this->ToString().c_str());
326 return false;
327 }
328
329 std::string fileId = this->GetFileId();
330 if (fileId == MEDIA_FILE_ID_DEFAULT) {
331 MEDIA_ERR_LOG("fileid is invaild, uri is %{private}s", this->ToString().c_str());
332 return false;
333 }
334
335 return true;
336 }
337
GetQueryKeys()338 std::unordered_map<std::string, std::string> &MediaFileUri::GetQueryKeys()
339 {
340 if (queryMap_.empty()) {
341 SetQueryMap(this, this->queryMap_);
342 }
343 return queryMap_;
344 }
345
IsApi10()346 bool MediaFileUri::IsApi10()
347 {
348 if ((ToString().find(PhotoColumn::PHOTO_TYPE_URI) != std::string::npos) ||
349 (ToString().find(AudioColumn::AUDIO_TYPE_URI) != std::string::npos)) {
350 return true;
351 }
352 return false;
353 }
354
GetUriType()355 int MediaFileUri::GetUriType()
356 {
357 return uriType_;
358 }
359
GetMediaTypeFromUri(const std::string & uri)360 MediaType MediaFileUri::GetMediaTypeFromUri(const std::string &uri)
361 {
362 if (MediaFileUtils::StartsWith(uri, PhotoColumn::PHOTO_URI_PREFIX)) {
363 return MEDIA_TYPE_PHOTO;
364 } else if (MediaFileUtils::StartsWith(uri, AudioColumn::AUDIO_URI_PREFIX)) {
365 return MEDIA_TYPE_AUDIO;
366 } else if (MediaFileUtils::StartsWith(uri, PhotoAlbumColumns::ALBUM_URI_PREFIX)) {
367 return Media::MEDIA_TYPE_ALBUM;
368 } else if (MediaFileUtils::StartsWith(uri, AUDIO_URI_PREFIX)) {
369 return Media::MEDIA_TYPE_AUDIO;
370 } else if (MediaFileUtils::StartsWith(uri, VIDEO_URI_PREFIX)) {
371 return Media::MEDIA_TYPE_VIDEO;
372 } else if (MediaFileUtils::StartsWith(uri, IMAGE_URI_PREFIX)) {
373 return Media::MEDIA_TYPE_IMAGE;
374 } else if (MediaFileUtils::StartsWith(uri, ALBUM_URI_PREFIX)) {
375 return Media::MEDIA_TYPE_ALBUM;
376 } else if (MediaFileUtils::StartsWith(uri, FILE_URI_PREFIX)) {
377 return Media::MEDIA_TYPE_FILE;
378 }
379 return Media::MEDIA_TYPE_DEFAULT;
380 }
381
RemoveAllFragment(std::string & uri)382 void MediaFileUri::RemoveAllFragment(std::string &uri)
383 {
384 size_t fragIndex = uri.find_first_of('#');
385 if (fragIndex != std::string::npos) {
386 uri = uri.substr(0, fragIndex);
387 }
388 }
389
UriValidCheck(Uri & uri)390 static int32_t UriValidCheck(Uri &uri)
391 {
392 std::string scheme = uri.GetScheme();
393 if (scheme != ML_FILE_SCHEME && scheme != ML_DATA_SHARE_SCHEME) {
394 MEDIA_ERR_LOG("scheme is invalid, uri is %{private}s", uri.ToString().c_str());
395 return E_INVALID_URI;
396 }
397
398 if (uri.GetAuthority() != ML_URI_AUTHORITY && uri.GetPath().find(MEDIALIBRARY_DATA_URI_IDENTIFIER) != 0) {
399 MEDIA_ERR_LOG("failed to find /media, uri is %{private}s", uri.ToString().c_str());
400 return E_INVALID_URI;
401 }
402 return E_OK;
403 }
404
HandleOldUriPath(std::string & path)405 static inline void HandleOldUriPath(std::string &path)
406 {
407 // Handle datashare:///media and datashare:///media/file_operation case
408 if (MediaFileUtils::StartsWith(path, MEDIALIBRARY_DATA_URI_IDENTIFIER)) {
409 path = path.substr(MEDIALIBRARY_DATA_URI_IDENTIFIER.size());
410 return;
411 }
412 }
413
RemovePrecedSlash(std::string & path)414 static inline void RemovePrecedSlash(std::string &path)
415 {
416 if (MediaFileUtils::StartsWith(path, SLASH_STR)) {
417 path = path.substr(SLASH_STR.size());
418 }
419 }
420
GetValidPath(Uri & uri,std::string & path)421 static void GetValidPath(Uri &uri, std::string &path)
422 {
423 if (UriValidCheck(uri) < 0) {
424 path = "";
425 return;
426 }
427
428 path = uri.GetPath();
429 HandleOldUriPath(path);
430 RemovePrecedSlash(path);
431 }
432
GetPathFirstDentry(Uri & uri)433 std::string MediaFileUri::GetPathFirstDentry(Uri &uri)
434 {
435 std::string path;
436 GetValidPath(uri, path);
437 // Example: file:://media/photo_operation/query, return the "photo_operation" part
438 return path.substr(0, path.find_first_of('/'));
439 }
440
GetPathSecondDentry(Uri & uri)441 std::string MediaFileUri::GetPathSecondDentry(Uri &uri)
442 {
443 std::string ret;
444 std::string firstDentry = GetPathFirstDentry(uri);
445 if (firstDentry.empty()) {
446 return ret;
447 }
448 std::string path;
449 GetValidPath(uri, path);
450 if (path.size() < firstDentry.size() + 1) {
451 return ret;
452 }
453 // Example: file:://media/photo_operation/query, return the "query" part
454 return path.substr(firstDentry.size() + 1);
455 }
456
GetPhotoId(const std::string & uri)457 std::string MediaFileUri::GetPhotoId(const std::string &uri)
458 {
459 if (!MediaFileUtils::StartsWith(uri, PhotoColumn::PHOTO_URI_PREFIX)) {
460 return "";
461 }
462 std::string tmp = uri.substr(PhotoColumn::PHOTO_URI_PREFIX.size());
463 return tmp.substr(0, tmp.find_first_of('/'));
464 }
465 } // namespace Media
466 } // namespace OHOS
467