diff --git a/coding/internal/file_data.cpp b/coding/internal/file_data.cpp index 1e19f26644..cfdeb62fde 100644 --- a/coding/internal/file_data.cpp +++ b/coding/internal/file_data.cpp @@ -246,9 +246,8 @@ bool CopyFile(string const & fOld, string const & fNew) if (ofs.fail()) { - // Well, specification says that exception is thrown for critical errors. - // So just log stream fail state and continue. LOG(LWARNING, ("Bad or Fail bit is set while writing file:", fNew)); + return false; } return true; diff --git a/coding/internal/file_data.hpp b/coding/internal/file_data.hpp index 72f76b1599..8dd47d83d2 100644 --- a/coding/internal/file_data.hpp +++ b/coding/internal/file_data.hpp @@ -47,6 +47,7 @@ private: bool GetFileSize(string const & fName, uint64_t & sz); bool DeleteFileX(string const & fName); bool RenameFileX(string const & fOld, string const & fNew); +/// @return false if copy fails. DO NOT THROWS exceptions bool CopyFile(string const & fOld, string const & fNew); } diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 2d8febae2c..b5feeb3c49 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -433,7 +433,7 @@ namespace } } -string BookmarkCategory::GetValidFileName(string const & name) +string BookmarkCategory::RemoveInvalidSymbols(string const & name) { // Remove not allowed symbols strings::UniString uniName = strings::MakeUniString(name); @@ -447,8 +447,14 @@ string BookmarkCategory::GetValidFileName(string const & name) return (uniName.empty() ? "Bookmarks" : strings::ToUtf8(uniName)); } -string BookmarkCategory::GenerateUniqueFileName(const string & path, string const & name) +string BookmarkCategory::GenerateUniqueFileName(const string & path, string name) { + string const kmlExt(".kml"); + // check if file name already contains .kml extension + size_t const extPos = name.rfind(kmlExt); + if (extPos == name.size() - kmlExt.size()) + name.resize(name.size() - kmlExt.size()); + size_t counter = 1; string suffix; while (Platform::IsFileExistsByFullPath(path + name + suffix + ".kml")) @@ -461,18 +467,21 @@ bool BookmarkCategory::SaveToKMLFile() string oldFile; // Get valid file name from category name - string const name = GetValidFileName(m_name); + string const name = RemoveInvalidSymbols(m_name); if (!m_file.empty()) { size_t i2 = m_file.find_last_of('.'); - if (i2 == string::npos) i2 = m_file.size(); + if (i2 == string::npos) + i2 = m_file.size(); size_t i1 = m_file.find_last_of("\\/"); - if (i1 == string::npos) i1 = 0; - else ++i1; + if (i1 == string::npos) + i1 = 0; + else + ++i1; // If m_file doesn't match name, assign new m_file for this category and save old file name. - if (m_file.substr(i1, i2-i1).find(name) != 0) + if (m_file.substr(i1, i2 - i1).find(name) != 0) { oldFile = GenerateUniqueFileName(GetPlatform().WritableDir(), name); m_file.swap(oldFile); diff --git a/map/bookmark.hpp b/map/bookmark.hpp index 50dc74e6d9..579ec80796 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -103,9 +103,9 @@ public: static BookmarkCategory * CreateFromKMLFile(string const & file); /// Get valid file name from input (remove illegal symbols). - static string GetValidFileName(string const & name); - /// Get unique file name from path and valid file name. - static string GenerateUniqueFileName(const string & path, string const & name); + static string RemoveInvalidSymbols(string const & name); + /// Get unique bookmark file name from path and valid file name. + static string GenerateUniqueFileName(const string & path, string name); //@} }; diff --git a/map/framework.cpp b/map/framework.cpp index 8b5b6c6083..73c486d6e8 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -27,11 +27,14 @@ #include "../coding/internal/file_data.hpp" +#include "../coding/zip_reader.hpp" + #include "../geometry/angles.hpp" #include "../geometry/distance_on_sphere.hpp" #include "../base/math.hpp" #include "../base/timer.hpp" +#include "../base/scope_guard.hpp" #include "../std/algorithm.hpp" #include "../std/target_os.hpp" @@ -41,6 +44,9 @@ /// How many pixels around touch point are used to get bookmark or POI #define TOUCH_PIXEL_RADIUS 20 +#define KML_EXTENSION ".kml" +#define KMZ_EXTENSION ".kmz" + using namespace storage; @@ -349,7 +355,7 @@ void Framework::LoadBookmarks() string const dir = GetPlatform().WritableDir(); Platform::FilesList files; - Platform::GetFilesByExt(dir, ".kml", files); + Platform::GetFilesByExt(dir, KML_EXTENSION, files); for (size_t i = 0; i < files.size(); ++i) { BookmarkCategory * cat = BookmarkCategory::CreateFromKMLFile(dir + files[i]); @@ -525,6 +531,84 @@ void Framework::ClearBookmarks() m_bookmarks.clear(); } +namespace +{ +// @return extension with a dot (or empty string if no extension is present) +string const GetFileExt(string const & filePath) +{ + size_t const pos = filePath.rfind('.'); + if (pos == string::npos) + return string(); + string lowerCaseExtension = string(filePath, pos, filePath.size() - pos); + transform(lowerCaseExtension.begin(), lowerCaseExtension.end(), lowerCaseExtension.begin(), ::tolower); + return lowerCaseExtension; +} + +string const GetFileName(string const & filePath) +{ + size_t pos = filePath.rfind('/'); + if (pos == string::npos) + return filePath; + ++pos; + return string(filePath, pos, filePath.size() - pos); +} + +string const GenerateValidandUniqFilePathForKLM(string const & filename) +{ + string filePath = BookmarkCategory::RemoveInvalidSymbols(filename); + filePath = BookmarkCategory::GenerateUniqueFileName(GetPlatform().WritableDir(), filePath); + return filePath; +} +} + +bool Framework::AddBookmarksFile(string const & filePath) +{ + string const fileExt = GetFileExt(filePath); + if (fileExt == KML_EXTENSION) + { + string savePath = GenerateValidandUniqFilePathForKLM( GetFileName(filePath) ); + if (!my::CopyFile(filePath, savePath)) + return false; + } + else if (fileExt == KMZ_EXTENSION) + { + try + { + string const filename = GetFileName(filePath); + vector files; + ZipFileReader::FilesList(filePath, files); + string kmlFileName; + for (size_t i = 0; i < files.size();++i) + { + if (GetFileExt(files[i]) == KML_EXTENSION) + { + kmlFileName = files[i]; + break; + } + } + if (kmlFileName.empty()) + return false; + string const savePath = GenerateValidandUniqFilePathForKLM(kmlFileName); + ZipFileReader::UnzipFile(filePath, kmlFileName, savePath); + } + catch (RootException const & e) + { + LOG(LWARNING, ("Error unzipping file", filePath, e.Msg())); + return false; + } + } + else + { + LOG(LWARNING, ("Unknown file type", filePath)); + return false; + } + + // Update freshly added bookmarks + LoadBookmarks(); + + return true; +} + void Framework::GetLocalMaps(vector & outMaps) const { Platform & pl = GetPlatform(); diff --git a/map/framework.hpp b/map/framework.hpp index 23e23be2f8..8efe7f4acf 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -211,6 +211,8 @@ public: void ClearBookmarks(); + bool AddBookmarksFile(string const & filePath); + inline m2::PointD PtoG(m2::PointD const & p) const { return m_navigator.PtoG(p); } inline m2::PointD GtoP(m2::PointD const & p) const { return m_navigator.GtoP(p); } diff --git a/map/map_tests/bookmarks_test.cpp b/map/map_tests/bookmarks_test.cpp index d1226bbdfa..a8fd89187e 100644 --- a/map/map_tests/bookmarks_test.cpp +++ b/map/map_tests/bookmarks_test.cpp @@ -324,7 +324,7 @@ UNIT_TEST(Bookmarks_IllegalFileName) for (size_t i = 0; i < ARRAY_SIZE(arrIllegal); ++i) { - string const name = BookmarkCategory::GetValidFileName(arrIllegal[i]); + string const name = BookmarkCategory::RemoveInvalidSymbols(arrIllegal[i]); if (strlen(arrLegal[i]) == 0) {