added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / sync / impl / sync_file.cpp
diff --git a/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp b/iOS/Pods/Realm/Realm/ObjectStore/src/sync/impl/sync_file.cpp
new file mode 100644 (file)
index 0000000..c58847e
--- /dev/null
@@ -0,0 +1,374 @@
+////////////////////////////////////////////////////////////////////////////
+//
+// Copyright 2016 Realm Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////
+
+#include "sync/impl/sync_file.hpp"
+
+#include "util/time.hpp"
+
+#include <realm/util/file.hpp>
+#include <realm/util/scope_exit.hpp>
+
+#include <iomanip>
+#include <sstream>
+#include <system_error>
+#include <fstream>
+
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+
+inline static int mkstemp(char* _template) { return _open(_mktemp(_template), _O_CREAT | _O_TEMPORARY, _S_IREAD | _S_IWRITE); }
+#else
+#include <unistd.h>
+#endif
+
+
+using File = realm::util::File;
+
+namespace realm {
+
+namespace {
+
+uint8_t value_of_hex_digit(char hex_digit)
+{
+    if (hex_digit >= '0' && hex_digit <= '9') {
+        return hex_digit - '0';
+    } else if (hex_digit >= 'A' && hex_digit <= 'F') {
+        return 10 + hex_digit - 'A';
+    } else if (hex_digit >= 'a' && hex_digit <= 'f') {
+        return 10 + hex_digit - 'a';
+    } else {
+        throw std::invalid_argument("Cannot get the value of a character that isn't a hex digit.");
+    }
+}
+
+bool filename_is_reserved(const std::string& filename) {
+    return (filename == "." || filename == "..");
+}
+
+bool character_is_unreserved(char character)
+{
+    bool is_capital_letter = (character >= 'A' && character <= 'Z');
+    bool is_lowercase_letter = (character >= 'a' && character <= 'z');
+    bool is_number = (character >= '0' && character <= '9');
+    bool is_allowed_symbol = (character == '-' || character == '_' || character == '.');
+    return is_capital_letter || is_lowercase_letter || is_number || is_allowed_symbol;
+}
+
+char decoded_char_for(const std::string& percent_encoding, size_t index)
+{
+    if (index+2 >= percent_encoding.length()) {
+        throw std::invalid_argument("Malformed string: not enough characters after '%' before end of string.");
+    }
+    REALM_ASSERT(percent_encoding[index] == '%');
+    return (16*value_of_hex_digit(percent_encoding[index + 1])) + value_of_hex_digit(percent_encoding[index + 2]);
+}
+
+} // (anonymous namespace)
+
+namespace util {
+
+std::string make_percent_encoded_string(const std::string& raw_string)
+{
+    std::string buffer;
+    buffer.reserve(raw_string.size());
+    for (size_t i=0; i<raw_string.size(); i++) {
+        unsigned char character = raw_string[i];
+        if (character_is_unreserved(character)) {
+            buffer.push_back(character);
+        } else {
+            buffer.resize(buffer.size() + 3);
+            // Format string must resolve to exactly 3 characters.
+            sprintf(&buffer.back() - 2, "%%%2X", character);
+        }
+    }
+    return buffer;
+}
+
+std::string make_raw_string(const std::string& percent_encoded_string)
+{
+    std::string buffer;
+    size_t input_len = percent_encoded_string.length();
+    buffer.reserve(input_len);
+    size_t idx = 0;
+    while (idx < input_len) {
+        char current = percent_encoded_string[idx];
+        if (current == '%') {
+            // Decode. +3.
+            buffer.push_back(decoded_char_for(percent_encoded_string, idx));
+            idx += 3;
+        } else {
+            // No need to decode. +1.
+            if (!character_is_unreserved(current)) {
+                throw std::invalid_argument("Input string is invalid: contains reserved characters.");
+            }
+            buffer.push_back(current);
+            idx++;
+        }
+    }
+    return buffer;
+}
+
+std::string file_path_by_appending_component(const std::string& path, const std::string& component, FilePathType path_type)
+{
+    // FIXME: Does this have to be changed to accomodate Windows platforms?
+    std::string buffer;
+    buffer.reserve(2 + path.length() + component.length());
+    buffer.append(path);
+    std::string terminal = "";
+    if (path_type == FilePathType::Directory && component[component.length() - 1] != '/') {
+        terminal = "/";
+    }
+    char path_last = path[path.length() - 1];
+    char component_first = component[0];
+    if (path_last == '/' && component_first == '/') {
+        buffer.append(component.substr(1));
+        buffer.append(terminal);
+    } else if (path_last == '/' || component_first == '/') {
+        buffer.append(component);
+        buffer.append(terminal);
+    } else {
+        buffer.append("/");
+        buffer.append(component);
+        buffer.append(terminal);
+    }
+    return buffer;
+}
+
+std::string file_path_by_appending_extension(const std::string& path, const std::string& extension)
+{
+    std::string buffer;
+    buffer.reserve(1 + path.length() + extension.length());
+    buffer.append(path);
+    char path_last = path[path.length() - 1];
+    char extension_first = extension[0];
+    if (path_last == '.' && extension_first == '.') {
+        buffer.append(extension.substr(1));
+    } else if (path_last == '.' || extension_first == '.') {
+        buffer.append(extension);
+    } else {
+        buffer.append(".");
+        buffer.append(extension);
+    }
+    return buffer;
+}
+
+std::string create_timestamped_template(const std::string& prefix, int wildcard_count)
+{
+    constexpr int WILDCARD_MAX = 20;
+    constexpr int WILDCARD_MIN = 6;
+    wildcard_count = std::min(WILDCARD_MAX, std::max(WILDCARD_MIN, wildcard_count));
+    std::time_t time = std::time(nullptr);
+    std::stringstream stream;
+    stream << prefix << "-" << util::put_time(time, "%Y%m%d-%H%M%S") << "-" << std::string(wildcard_count, 'X');
+    return stream.str();
+}
+
+std::string reserve_unique_file_name(const std::string& path, const std::string& template_string)
+{
+    REALM_ASSERT_DEBUG(template_string.find("XXXXXX") != std::string::npos);
+    std::string path_buffer = file_path_by_appending_component(path, template_string, FilePathType::File);
+    int fd = mkstemp(&path_buffer[0]);
+    if (fd < 0) {
+        int err = errno;
+        throw std::system_error(err, std::system_category());
+    }
+    // Remove the file so we can use the name for our own file.
+#ifdef _WIN32
+    _close(fd);
+    _unlink(path_buffer.c_str());
+#else
+    close(fd);
+    unlink(path_buffer.c_str());
+#endif
+    return path_buffer;
+}
+
+} // util
+
+constexpr const char SyncFileManager::c_sync_directory[];
+constexpr const char SyncFileManager::c_utility_directory[];
+constexpr const char SyncFileManager::c_recovery_directory[];
+constexpr const char SyncFileManager::c_metadata_directory[];
+constexpr const char SyncFileManager::c_metadata_realm[];
+constexpr const char SyncFileManager::c_user_info_file[];
+
+std::string SyncFileManager::get_special_directory(std::string directory_name) const
+{
+    auto dir_path = file_path_by_appending_component(get_base_sync_directory(),
+                                                     directory_name,
+                                                     util::FilePathType::Directory);
+    util::try_make_dir(dir_path);
+    return dir_path;
+}
+
+std::string SyncFileManager::get_base_sync_directory() const
+{
+    auto sync_path = file_path_by_appending_component(m_base_path,
+                                                      c_sync_directory,
+                                                      util::FilePathType::Directory);
+    util::try_make_dir(sync_path);
+    return sync_path;
+}
+
+std::string SyncFileManager::user_directory(const std::string& local_identity,
+                                            util::Optional<SyncUserIdentifier> user_info) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    std::string escaped = util::make_percent_encoded_string(local_identity);
+    if (filename_is_reserved(escaped))
+        throw std::invalid_argument("A user can't have an identifier reserved by the filesystem.");
+
+    auto user_path = file_path_by_appending_component(get_base_sync_directory(),
+                                                      escaped,
+                                                      util::FilePathType::Directory);
+    bool dir_created = util::try_make_dir(user_path);
+    if (dir_created && user_info) {
+        // Add a text file in the user directory containing the user identity, for backup purposes.
+        // Only do this the first time the directory is created.
+        auto info_path = util::file_path_by_appending_component(user_path, c_user_info_file);
+        std::ofstream info_file;
+        info_file.open(info_path.c_str());
+        if (info_file.is_open()) {
+            info_file << user_info->user_id << "\n" << user_info->auth_server_url << "\n";
+            info_file.close();
+        }
+    }
+    return user_path;
+}
+
+void SyncFileManager::remove_user_directory(const std::string& local_identity) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    const auto& escaped = util::make_percent_encoded_string(local_identity);
+    if (filename_is_reserved(escaped))
+        throw std::invalid_argument("A user can't have an identifier reserved by the filesystem.");
+
+    auto user_path = file_path_by_appending_component(get_base_sync_directory(),
+                                                      escaped,
+                                                      util::FilePathType::Directory);
+    util::try_remove_dir_recursive(user_path);
+}
+
+bool SyncFileManager::try_rename_user_directory(const std::string& old_name, const std::string& new_name) const
+{
+    REALM_ASSERT_DEBUG(old_name.length() > 0 && new_name.length() > 0);
+    const auto& old_name_escaped = util::make_percent_encoded_string(old_name);
+    const auto& new_name_escaped = util::make_percent_encoded_string(new_name);
+    const std::string& base = get_base_sync_directory();
+    if (filename_is_reserved(old_name_escaped) || filename_is_reserved(new_name_escaped))
+        throw std::invalid_argument("A user directory can't be renamed using a reserved identifier.");
+
+    const auto& old_path = file_path_by_appending_component(base, old_name_escaped, util::FilePathType::Directory);
+    const auto& new_path = file_path_by_appending_component(base, new_name_escaped, util::FilePathType::Directory);
+
+    try {
+        File::move(old_path, new_path);
+    } catch (File::NotFound const&) {
+        return false;
+    }
+    return true;
+}
+
+bool SyncFileManager::remove_realm(const std::string& absolute_path) const
+{
+    REALM_ASSERT(absolute_path.length() > 0);
+    bool success = true;
+    // Remove the Realm file (e.g. "example.realm").
+    success = File::try_remove(absolute_path);
+    // Remove the lock file (e.g. "example.realm.lock").
+    auto lock_path = util::file_path_by_appending_extension(absolute_path, "lock");
+    success = File::try_remove(lock_path);
+    // Remove the management directory (e.g. "example.realm.management").
+    auto management_path = util::file_path_by_appending_extension(absolute_path, "management");
+    try {
+        util::try_remove_dir_recursive(management_path);
+    }
+    catch (File::AccessError const&) {
+        success = false;
+    }
+    return success;
+}
+
+bool SyncFileManager::copy_realm_file(const std::string& old_path, const std::string& new_path) const
+{
+    REALM_ASSERT(old_path.length() > 0);
+    try {
+        if (File::exists(new_path)) {
+            return false;
+        }
+        File::copy(old_path, new_path);
+    }
+    catch (File::NotFound const&) {
+        return false;
+    }
+    catch (File::AccessError const&) {
+        return false;
+    }
+    return true;
+}
+
+bool SyncFileManager::remove_realm(const std::string& local_identity, const std::string& raw_realm_path) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    REALM_ASSERT(raw_realm_path.length() > 0);
+    if (filename_is_reserved(local_identity) || filename_is_reserved(raw_realm_path))
+        throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem.");
+
+    auto escaped = util::make_percent_encoded_string(raw_realm_path);
+    auto realm_path = util::file_path_by_appending_component(user_directory(local_identity), escaped);
+    return remove_realm(realm_path);
+}
+
+std::string SyncFileManager::path(const std::string& local_identity, const std::string& raw_realm_path,
+                                  util::Optional<SyncUserIdentifier> user_info) const
+{
+    REALM_ASSERT(local_identity.length() > 0);
+    REALM_ASSERT(raw_realm_path.length() > 0);
+    if (filename_is_reserved(local_identity) || filename_is_reserved(raw_realm_path))
+        throw std::invalid_argument("A user or Realm can't have an identifier reserved by the filesystem.");
+
+    auto escaped = util::make_percent_encoded_string(raw_realm_path);
+    auto realm_path = util::file_path_by_appending_component(user_directory(local_identity, user_info), escaped);
+    return realm_path;
+}
+
+std::string SyncFileManager::metadata_path() const
+{
+    auto dir_path = file_path_by_appending_component(get_utility_directory(),
+                                                     c_metadata_directory,
+                                                     util::FilePathType::Directory);
+    util::try_make_dir(dir_path);
+    return util::file_path_by_appending_component(dir_path, c_metadata_realm);
+}
+
+bool SyncFileManager::remove_metadata_realm() const
+{
+    auto dir_path = file_path_by_appending_component(get_utility_directory(),
+                                                     c_metadata_directory,
+                                                     util::FilePathType::Directory);
+    try {
+        util::try_remove_dir_recursive(dir_path);
+        return true;
+    }
+    catch (File::AccessError const&) {
+        return false;
+    }
+}
+
+} // realm