[common] Use thread_local (instead of static) objects for access checks
[platform/core/api/webapi-plugins.git] / src / common / tools.cc
1 /*
2  * Copyright (c) 2015 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
17 #include "common/tools.h"
18
19 #include <app_manager.h>
20 #include <pkgmgr-info.h>
21 #include <privilegemgr/privilege_manager.h>
22 #include <sys/stat.h>
23 #include <mutex>
24
25 #ifdef PRIVILEGE_USE_DB
26 #include <sqlite3.h>
27 #include "common/current_application.h"
28 #elif PRIVILEGE_USE_ACE
29 //#include <privilege_checker.h>
30 #elif PRIVILEGE_USE_CYNARA
31 #include <unistd.h>
32
33 #include <cynara/cynara-client.h>
34 #include <sys/smack.h>
35 #endif
36
37 #include "common/logger.h"
38 #include "common/scope_exit.h"
39
40 namespace common {
41 namespace tools {
42
43 void ReportSuccess(picojson::object& out) {
44   ScopeLogger();
45   out.insert(std::make_pair("status", picojson::value("success")));
46 }
47
48 void ReportSuccess(const picojson::value& result, picojson::object& out) {
49   ScopeLogger();
50   out.insert(std::make_pair("status", picojson::value("success")));
51   out.insert(std::make_pair("result", result));
52 }
53
54 void ReportError(picojson::object& out) {
55   LoggerE("Error without error code");
56   out.insert(std::make_pair("status", picojson::value("error")));
57 }
58
59 void ReportError(const PlatformException& ex, picojson::object& out) {
60   LoggerE("PlatformException: %s, message: %s", ex.name().c_str(), ex.message().c_str());
61   out.insert(std::make_pair("status", picojson::value("error")));
62   out.insert(std::make_pair("error", ex.ToJSON()));
63 }
64
65 void ReportError(const PlatformResult& error, picojson::object* out) {
66   LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
67           error.message().c_str());
68   out->insert(std::make_pair("status", picojson::value("error")));
69   out->insert(std::make_pair("error", error.ToJSON()));
70 }
71
72 void PushJSONToBinary(const picojson::value& in, std::vector<uint8_t>* out) {
73   ScopeLogger();
74   auto out_json_data = in.serialize();
75   // As binary data used in 'out' vector after a JSON can be coded as 1, 2, 4 or
76   // 8 bytes per element, there could be an error in JS layer when trying to
77   // parse element starting from 'wrong' index depending on data length. To
78   // overcome a problem we always allign the array to end with length of bytes
79   // being a multiply of 8 to keep simplicity of implementation.
80   size_t trailing_bytes = (out_json_data.size() + 4) % 8;
81   if (trailing_bytes != 0) {
82     std::string align_bytes((8 - trailing_bytes), ' ');
83     out_json_data.insert(out_json_data.end(), align_bytes.begin(), align_bytes.end());
84   }
85
86   size_t out_json_size = out_json_data.size();
87   auto out_json_size_vec = std::vector<uint8_t>{
88       static_cast<uint8_t>(out_json_size >> 24), static_cast<uint8_t>(out_json_size >> 16),
89       static_cast<uint8_t>(out_json_size >> 8), static_cast<uint8_t>(out_json_size)};
90   out->insert(out->end(), out_json_size_vec.begin(), out_json_size_vec.end());
91   out->insert(out->end(), out_json_data.begin(), out_json_data.end());
92 }
93
94 void ReportSuccessToBinary(const picojson::value& result, std::vector<uint8_t>* out) {
95   ScopeLogger();
96   picojson::value wrapped_result{picojson::object{}};
97   picojson::object& obj = wrapped_result.get<picojson::object>();
98
99   ReportSuccess(result, obj);
100   PushJSONToBinary(wrapped_result, out);
101 }
102
103 void ReportErrorToBinary(const PlatformResult& error, std::vector<uint8_t>* out) {
104   LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
105           error.message().c_str());
106   picojson::value wrapped_result{picojson::object{}};
107   picojson::object& obj = wrapped_result.get<picojson::object>();
108
109   ReportError(error, &obj);
110   PushJSONToBinary(wrapped_result, out);
111 }
112
113 void ReportDataToBinary(const std::vector<uint8_t>& in, std::vector<uint8_t>* out) {
114   ScopeLogger();
115   unsigned long long out_data_size = in.size();
116   auto out_data_size_vec = std::vector<uint8_t>{
117       static_cast<uint8_t>(out_data_size >> 56), static_cast<uint8_t>(out_data_size >> 48),
118       static_cast<uint8_t>(out_data_size >> 40), static_cast<uint8_t>(out_data_size >> 32),
119       static_cast<uint8_t>(out_data_size >> 24), static_cast<uint8_t>(out_data_size >> 16),
120       static_cast<uint8_t>(out_data_size >> 8),  static_cast<uint8_t>(out_data_size)};
121   out->insert(out->end(), out_data_size_vec.begin(), out_data_size_vec.end());
122   out->insert(out->end(), in.begin(), in.end());
123 }
124
125 namespace {
126
127 #ifdef PRIVILEGE_USE_DB
128
129 class AccessControlImpl {
130  public:
131   AccessControlImpl() : initialized_(false) {
132     ScopeLogger("Privilege access checked using DB.");
133
134     const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
135     sqlite3* db = nullptr;
136
137     int ret = sqlite3_open(kWrtDBPath, &db);
138     if (SQLITE_OK != ret) {
139       LoggerE("Failed to access WRT database");
140       return;
141     }
142
143     const char* kQuery =
144         "select name from WidgetFeature where app_id = "
145         "(select app_id from WidgetInfo where tizen_appid = ?)"
146         " and rejected = 0";
147     const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
148     sqlite3_stmt* stmt = nullptr;
149
150     ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
151     ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
152
153     SCOPE_EXIT {
154       sqlite3_finalize(stmt);
155       sqlite3_close(db);
156     };
157
158     if (SQLITE_OK != ret) {
159       LoggerE("Failed to query WRT database");
160       return;
161     }
162
163     while (sqlite3_step(stmt) == SQLITE_ROW) {
164       const char* privilege = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
165       SLoggerD("Granted: %s", privilege);
166       granted_privileges_.push_back(privilege);
167     }
168
169     initialized_ = true;
170   }
171
172   ~AccessControlImpl() {
173   }
174
175   bool CheckAccess(const std::vector<std::string>& privileges) {
176     ScopeLogger();
177     if (!initialized_) {
178       return false;
179     }
180
181     for (const auto& privilege : privileges) {
182       if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) ==
183           granted_privileges_.end()) {
184         return false;
185       }
186     }
187
188     return true;
189   }
190
191  private:
192   bool initialized_;
193   std::vector<std::string> granted_privileges_;
194 };
195
196 #elif PRIVILEGE_USE_ACE
197
198 class AccessControlImpl {
199  public:
200   AccessControlImpl() {
201     LoggerD("Privilege access checked using ACE.");
202   }
203
204   ~AccessControlImpl() {
205   }
206
207   bool CheckAccess(const std::vector<std::string>& privileges) {
208     ScopeLogger();
209
210     return true;
211   }
212 };
213
214 #elif PRIVILEGE_USE_CYNARA
215
216 class AccessControlImpl {
217  public:
218   AccessControlImpl() : cynara_(nullptr) {
219     ScopeLogger("Privilege access checked using Cynara.");
220
221     char* smack_label = nullptr;
222     SCOPE_EXIT {
223       free(smack_label);
224     };
225
226     char path[1024] = {
227         0,
228     };
229     snprintf(path, sizeof(path), "/proc/%lu/attr/current", static_cast<unsigned long>(gettid()));
230     int ret = smack_getlabel(path, &smack_label, SMACK_LABEL_ACCESS);
231
232     if (0 == ret && nullptr != smack_label && 0 < strlen(smack_label)) {
233       auto uid = getuid();
234
235       SLoggerD("uid: [%u]", uid);
236       SLoggerD("smack label: [%s]", smack_label);
237
238       uid_ = std::to_string(uid);
239       smack_label_ = smack_label;
240     } else {
241       LoggerE("Failed to get smack label");
242       return;
243     }
244
245     ret = cynara_initialize(&cynara_, nullptr);
246     if (CYNARA_API_SUCCESS != ret) {
247       LoggerE("Failed to initialize Cynara");
248       cynara_ = nullptr;
249     }
250   }
251
252   ~AccessControlImpl() {
253     if (cynara_) {
254       auto ret = cynara_finish(cynara_);
255       if (CYNARA_API_SUCCESS != ret) {
256         LoggerE("Failed to finalize Cynara");
257       }
258       cynara_ = nullptr;
259     }
260   }
261
262   bool CheckAccess(const std::vector<std::string>& privileges) {
263     if (cynara_) {
264       for (const auto& privilege : privileges) {
265         if (CYNARA_API_ACCESS_ALLOWED != cynara_check(cynara_,               // p_cynara
266                                                       smack_label_.c_str(),  // client
267                                                       "",                    // client_session
268                                                       uid_.c_str(),          // user
269                                                       privilege.c_str())     // privilege
270             ) {
271           return false;
272         }
273       }
274       return true;
275     } else {
276       return false;
277     }
278   }
279
280  private:
281   cynara* cynara_;
282   std::string uid_;
283   std::string smack_label_;
284 };
285
286 #else
287
288 class AccessControlImpl {
289  public:
290   AccessControlImpl() {
291     LoggerD("Privilege access - deny all.");
292   }
293
294   bool CheckAccess(const std::vector<std::string>& privileges) {
295     return false;
296   }
297 };
298
299 #endif
300
301 class AccessControl {
302  public:
303   static AccessControl& GetInstance() {
304     thread_local AccessControl instance;
305     return instance;
306   }
307
308   bool CheckAccess(const std::string& privilege) {
309     return CheckAccess(std::vector<std::string>{privilege});
310   }
311
312   bool CheckAccess(const std::vector<std::string>& privileges) {
313     return impl_.CheckAccess(privileges);
314   }
315
316  private:
317   AccessControl() {
318   }
319   ~AccessControl() {
320   }
321   AccessControlImpl impl_;
322 };
323
324 }  // namespace
325
326 PlatformResult CheckAccess(const std::string& privilege) {
327   return CheckAccess(std::vector<std::string>{privilege});
328 }
329
330 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
331   ScopeLogger();
332
333   /*
334    * This lock is crucial to avoid concurrent access of static variables in this function and
335    * in AccessControlImpl class.
336    * Concurrent access is likely in web service apps. If multiple are running, each of them is
337    * executed in
338    * a thread of the same process.
339    */
340   static std::mutex check_access_mutex;
341   const std::lock_guard<std::mutex> lock{check_access_mutex};
342
343   // Local cache of mapped privilege strings. This routine can be called many times, especially
344   // during application launch, generating a high overhead of retrieving mapped privileges from
345   // the underlying databases. This is especially the case since the same mappings can end up
346   // being retrieved several times.
347   using MappedPrivilegeCache = std::map<std::string, std::vector<std::string>>;
348   thread_local MappedPrivilegeCache mapped_privilege_cache;
349
350   std::string api_version;
351   PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
352   if (res.IsError()) {
353     return res;
354   }
355   LoggerD("Application api version: %s", api_version.c_str());
356
357   for (auto input_priv : privileges) {
358     LoggerD("Input privilege: %s", input_priv.c_str());
359     GList* input_glist = nullptr;
360     GList* mapped_glist = nullptr;
361
362     SCOPE_EXIT {
363       g_list_free(input_glist);
364       g_list_free(mapped_glist);
365     };
366
367     std::vector<std::string>* mapped_vector_ptr = nullptr;
368
369     // Check if mapped privilege is in local cache first
370     MappedPrivilegeCache::const_iterator it = mapped_privilege_cache.find(input_priv);
371     if (mapped_privilege_cache.end() == it) {
372       LoggerD("Mapped privileges - need to be fetched from database");
373       // Not in cache - retrieve from underlying databases.
374       input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
375       int ret = privilege_manager_get_mapped_privilege_list(
376           api_version.c_str(), PRVMGR_PACKAGE_TYPE_WRT, input_glist, &mapped_glist);
377       if (PRVMGR_ERR_NONE != ret) {
378         return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
379       }
380
381       // initialize empty cache vector
382       mapped_privilege_cache[input_priv] = std::vector<std::string>();
383       LoggerD("Mapped privileges:");
384       auto push_elem = [](gpointer data, gpointer user_data) -> void {
385         if (data && user_data) {
386           std::vector<std::string>* mapped_vector =
387               static_cast<std::vector<std::string>*>(user_data);
388           char* char_data = static_cast<char*>(data);
389           mapped_vector->push_back(char_data);
390           LoggerD("mapped to: %s", char_data);
391         }
392       };
393       // fill the vector with data
394       g_list_foreach(mapped_glist, push_elem, &mapped_privilege_cache[input_priv]);
395       mapped_vector_ptr = &mapped_privilege_cache[input_priv];
396     } else {
397       // Retrieve from local cache
398       LoggerD("Mapped privileges already in cache");
399       mapped_vector_ptr = (std::vector<std::string>*)&(it->second);
400     }
401
402     if (!AccessControl::GetInstance().CheckAccess(*mapped_vector_ptr)) {
403       for (const auto& mapped_priv : *mapped_vector_ptr) {
404         LoggerD("Access to privilege: %s has been denied.", mapped_priv.c_str());
405       }
406       return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
407     }
408   }
409   return PlatformResult(ErrorCode::NO_ERROR);
410 }
411
412 PlatformResult GetPkgApiVersion(std::string* api_version) {
413   ScopeLogger();
414
415   // Local cache of API version string.  This can be expensive to retrieve from
416   // underlying databases and this routine can be called many times during
417   // application launch.
418
419   // TODO: don't use PID to identify the application when getting
420   // API version (since Tizen 6.5 web service apps share PID)
421   static std::string cached_api_version;
422   static int cached_pid = -1;
423
424   char* app_id = nullptr;
425   char* pkgid = nullptr;
426   char* api_ver = nullptr;
427   app_info_h app_handle = nullptr;
428   pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr;
429
430   SCOPE_EXIT {
431     if (app_id) {
432       free(app_id);
433     }
434     if (pkgid) {
435       free(pkgid);
436     }
437     if (app_handle) {
438       app_info_destroy(app_handle);
439     }
440     if (pkginfo_handle) {
441       pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
442     }
443   };
444
445   pid_t pid = getpid();
446   if (cached_pid == pid) {
447     *api_version = cached_api_version;  // Retrieve from local cache
448   } else {
449     int ret = app_manager_get_app_id(pid, &app_id);
450     if (ret != APP_MANAGER_ERROR_NONE) {
451       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app id");
452     }
453
454     ret = app_info_create(app_id, &app_handle);
455     if (ret != APP_MANAGER_ERROR_NONE) {
456       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app info");
457     }
458
459     ret = app_info_get_package(app_handle, &pkgid);
460     if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) {
461       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
462     }
463
464     ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle);
465     if (ret != PMINFO_R_OK) {
466       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
467     }
468
469     ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
470     if (ret != PMINFO_R_OK) {
471       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
472     }
473
474     cached_api_version = api_ver;  // Save in local cache
475     cached_pid = pid;
476
477     *api_version = api_ver;
478   }
479
480   return PlatformResult(ErrorCode::NO_ERROR);
481 }
482
483 std::vector<std::string> SplitString(const std::string& str, const std::string& delim) {
484   std::vector<std::string> tokens;
485   size_t prev = 0, pos = 0;
486   do {
487     pos = str.find(delim, prev);
488     if (pos == std::string::npos) pos = str.length();
489     std::string token = str.substr(prev, pos - prev);
490     if (!token.empty()) tokens.push_back(token);
491     prev = pos + delim.length();
492   } while (pos < str.length() && prev < str.length());
493   return tokens;
494 }
495
496 bool IsAppVersionEarlierThan(const std::string& ver) {
497   ScopeLogger();
498   std::string app_ver;
499   auto res = GetPkgApiVersion(&app_ver);
500   if (!res) {
501     LoggerE(
502         "Failed to get application version, "
503         "assuming that application version is same as platform");
504     return false;
505   }
506   std::vector<std::string> vec_app = SplitString(app_ver, ".");
507   std::vector<std::string> vec_ref = SplitString(ver, ".");
508
509   size_t length = std::min(vec_app.size(), vec_ref.size());
510   for (size_t i = 0; i < length; ++i) {
511     int num_ver = std::stoi(vec_ref[i]);
512     int num_app = std::stoi(vec_app[i]);
513     if (num_app < num_ver) {
514       return true;
515     } else if (num_app > num_ver) {
516       return false;
517     }
518   }
519   return false;
520 }
521
522 /*
523  * RequestStoragePrivilegeChecker structure is used to check whether the file stating is needed.
524  * File stating is done to ensure that the application has given access to files on
525  * internal/external memory. The checking is done since Tizen 5.0 for applications, which
526  * require at least 5.0 version of Tizen.
527  * */
528 struct RequestStoragePrivilegeChecker {
529   bool isStorageAccessCheckNeeded;
530   RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) {
531   }
532 };
533
534 bool IsStoragePrivilegeCheckNeeded() {
535   thread_local RequestStoragePrivilegeChecker checker{};
536   return checker.isStorageAccessCheckNeeded;
537 }
538
539 std::string GetErrorString(int error_code) {
540   static const size_t kSize = 1024;
541   char msg[kSize] = {0};
542   strerror_r(error_code, msg, kSize);
543   return msg;
544 }
545
546 int HexToInt(char c) {
547   if (c >= '0' && c <= '9') {
548     return c - '0';
549   } else if (c >= 'A' && c <= 'Z') {
550     return c - 'A' + 10;
551   } else {
552     return c - 'a' + 10;
553   }
554 }
555
556 unsigned char* HexToBin(const char* hex, int size, unsigned char* bin, int bin_size) {
557   for (int i = 0; i < size - 1 && i / 2 < bin_size; i += 2) {
558     bin[i / 2] = HexToInt(hex[i]) << 4;
559     bin[i / 2] |= HexToInt(hex[i + 1]);
560   }
561   return bin;
562 }
563
564 char* BinToHex(const unsigned char* bin, int size, char* hex, int hex_size) {
565   static const char* const digits = "0123456789ABCDEF";
566   for (int i = 0; i < size && i < hex_size / 2; i++) {
567     hex[i * 2] = digits[bin[i] >> 4];
568     hex[i * 2 + 1] = digits[bin[i] & 0xF];
569   }
570   return hex;
571 }
572
573 bool IsPathValid(const std::string& path) {
574   ScopeLogger();
575
576   /*
577    * Directory dot-referencing is not allowed
578    */
579   return std::string::npos == path.find("/../") && std::string::npos == path.find("/./") &&
580          0 != path.find("./") && 0 != path.find("../") && path.length() - 2 != path.rfind("/.") &&
581          path.length() - 3 != path.rfind("/..");
582 }
583
584 PlatformResult CheckStorageAccess(const std::string& path) {
585   ScopeLogger("path: [%s]", path.c_str());
586   struct stat buf {};
587   char* real_path = realpath(path.c_str(), nullptr);
588   SCOPE_EXIT {
589     free(real_path);
590   };
591   if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
592     LoggerE("The application does not have necessary privilege to access given file: [%s]",
593             path.c_str());
594     return PlatformResult(ErrorCode::SECURITY_ERR,
595                           "Permission denied: accessing files requires proper storage privilege");
596   }
597   return PlatformResult(ErrorCode::NO_ERROR);
598 }
599
600 TizenResult CheckStorageAccessAndReturn(const std::string& path) {
601   ScopeLogger("path: [%s]", path.c_str());
602   struct stat buf {};
603   char* real_path = realpath(path.c_str(), nullptr);
604   SCOPE_EXIT {
605     free(real_path);
606   };
607   if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
608     LoggerE("The application does not have necessary privilege to access given file: [%s]",
609             path.c_str());
610     return SecurityError("Permission denied: accessing files requires proper storage privilege");
611   }
612   return TizenSuccess();
613 }
614
615 PlatformResult CheckFileStatus(const std::string& path) {
616   ScopeLogger();
617
618   struct stat buf;
619
620   if (stat(path.c_str(), &buf)) {
621     LoggerD("Failed to stat path: %s", path.c_str());
622
623     if (ENOENT == errno) {
624       return PlatformResult(ErrorCode::NOT_FOUND_ERR, "File does not exist: " + path);
625     } else if (EACCES == errno) {
626       return PlatformResult(ErrorCode::IO_ERR, "The user cannot access the file: " + path);
627     }
628
629     LoggerD("stat() error: %s", common::tools::GetErrorString(errno).c_str());
630     return PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get status of the file: " + path);
631   }
632
633   if (!S_ISREG(buf.st_mode)) {
634     return PlatformResult(ErrorCode::NOT_FOUND_ERR,
635                           "Path does not point to a regular file: " + path);
636   }
637
638   if (!(S_IRUSR & buf.st_mode)) {
639     return PlatformResult(ErrorCode::IO_ERR, "The user cannot read the file: " + path);
640   }
641
642   return PlatformResult(ErrorCode::NO_ERROR);
643 }
644
645 PlatformResult CheckFileAvailability(const std::string& path) {
646   ScopeLogger();
647
648   if (!IsPathValid(path)) {
649     return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid path: " + path);
650   }
651
652   return CheckFileStatus(path);
653 }
654
655 void PrintDeprecationWarningFor(const char* className, const char* replacement) {
656   if (nullptr == replacement) {
657     LoggerW("DEPRECATION WARNING: %s is deprecated and using it is not recommended.", className);
658   } else {
659     LoggerW(
660         "DEPRECATION WARNING: %s is deprecated and using it is not recommended. Try using %s "
661         "instead",
662         className, replacement);
663   }
664 }
665
666 std::string ConvertToLowerCase(const std::string& input_string) {
667   std::string output_string = input_string;
668   std::transform(output_string.begin(), output_string.end(), output_string.begin(), ::tolower);
669   return output_string;
670 }
671
672 std::string ltrim(const std::string& s) {
673   ScopeLogger();
674   std::string str = s;
675   auto pos = str.find_first_not_of(" ");
676   str.erase(0, pos);
677   return str;
678 }
679
680 std::string ConvertUriToPath(const std::string& str) {
681   const std::string URI_PREFIX = "file://";
682   ScopeLogger();
683   std::string path = ltrim(str);
684   std::string prefix = path.substr(0, URI_PREFIX.size());
685
686   if (prefix == URI_PREFIX) {
687     return path.substr(URI_PREFIX.size());
688   } else {
689     return path;
690   }
691 }
692
693 }  // namespace tools
694 }  // namespace common