added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / sync / sync_manager.cpp
1 ////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2016 Realm Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 ////////////////////////////////////////////////////////////////////////////
18
19 #include "sync/sync_manager.hpp"
20
21 #include "sync/impl/sync_client.hpp"
22 #include "sync/impl/sync_file.hpp"
23 #include "sync/impl/sync_metadata.hpp"
24 #include "sync/sync_session.hpp"
25 #include "sync/sync_user.hpp"
26
27 using namespace realm;
28 using namespace realm::_impl;
29
30 constexpr const char SyncManager::c_admin_identity[];
31
32 SyncManager& SyncManager::shared()
33 {
34     // The singleton is heap-allocated in order to fix an issue when running unit tests where tests would crash after
35     // they were done running because the manager was destroyed too early.
36     static SyncManager& manager = *new SyncManager;
37     return manager;
38 }
39
40 void SyncManager::configure_file_system(const std::string& base_file_path,
41                                         MetadataMode metadata_mode,
42                                         util::Optional<std::vector<char>> custom_encryption_key,
43                                         bool reset_metadata_on_error)
44 {
45     struct UserCreationData {
46         std::string identity;
47         std::string user_token;
48         std::string server_url;
49         bool is_admin;
50     };
51
52     std::vector<UserCreationData> users_to_add;
53     {
54         std::lock_guard<std::mutex> lock(m_file_system_mutex);
55
56         // Set up the file manager.
57         if (m_file_manager) {
58             REALM_ASSERT(m_file_manager->base_path() == base_file_path);
59         } else {
60             m_file_manager = std::make_unique<SyncFileManager>(base_file_path);
61         }
62
63         // Set up the metadata manager, and perform initial loading/purging work.
64         if (m_metadata_manager) {
65             return;
66         }
67         switch (metadata_mode) {
68             case MetadataMode::NoEncryption:
69                 m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
70                                                                            false);
71                 break;
72             case MetadataMode::Encryption:
73                 try {
74                     m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
75                                                                                true,
76                                                                                custom_encryption_key);
77                 } catch (RealmFileException const& ex) {
78                     if (reset_metadata_on_error && m_file_manager->remove_metadata_realm()) {
79                         m_metadata_manager = std::make_unique<SyncMetadataManager>(m_file_manager->metadata_path(),
80                                                                                    true,
81                                                                                    std::move(custom_encryption_key));
82                     } else {
83                         throw;
84                     }
85                 }
86                 break;
87             case MetadataMode::NoMetadata:
88                 return;
89         }
90
91         REALM_ASSERT(m_metadata_manager);
92         // Perform any necessary file actions.
93         std::vector<SyncFileActionMetadata> completed_actions;
94         SyncFileActionMetadataResults file_actions = m_metadata_manager->all_pending_actions();
95         for (size_t i = 0; i < file_actions.size(); i++) {
96             auto file_action = file_actions.get(i);
97             if (run_file_action(file_action)) {
98                 completed_actions.emplace_back(std::move(file_action));
99             }
100         }
101         for (auto& action : completed_actions) {
102             action.remove();
103         }
104         // Load persisted users into the users map.
105         SyncUserMetadataResults users = m_metadata_manager->all_unmarked_users();
106         for (size_t i = 0; i < users.size(); i++) {
107             // Note that 'admin' style users are not persisted.
108             auto user_data = users.get(i);
109             auto user_token = user_data.user_token();
110             auto identity = user_data.identity();
111             auto server_url = user_data.auth_server_url();
112             bool is_admin = user_data.is_admin();
113             if (user_token) {
114                 UserCreationData data = {
115                     std::move(identity),
116                     std::move(*user_token),
117                     std::move(server_url),
118                     is_admin,
119                 };
120                 users_to_add.emplace_back(std::move(data));
121             }
122         }
123         // Delete any users marked for death.
124         std::vector<SyncUserMetadata> dead_users;
125         SyncUserMetadataResults users_to_remove = m_metadata_manager->all_users_marked_for_removal();
126         dead_users.reserve(users_to_remove.size());
127         for (size_t i = 0; i < users_to_remove.size(); i++) {
128             auto user = users_to_remove.get(i);
129             // FIXME: delete user data in a different way? (This deletes a logged-out user's data as soon as the app
130             // launches again, which might not be how some apps want to treat their data.)
131             try {
132                 m_file_manager->remove_user_directory(user.local_uuid());
133                 dead_users.emplace_back(std::move(user));
134             } catch (util::File::AccessError const&) {
135                 continue;
136             }
137         }
138         for (auto& user : dead_users) {
139             user.remove();
140         }
141     }
142     {
143         std::lock_guard<std::mutex> lock(m_user_mutex);
144         for (auto& user_data : users_to_add) {
145             auto& identity = user_data.identity;
146             auto& server_url = user_data.server_url;
147             auto user = std::make_shared<SyncUser>(user_data.user_token, identity, server_url);
148             user->set_is_admin(user_data.is_admin);
149             m_users.insert({ {identity, server_url}, std::move(user) });
150         }
151     }
152 }
153
154 bool SyncManager::immediately_run_file_actions(const std::string& realm_path)
155 {
156     if (!m_metadata_manager) {
157         return false;
158     }
159     if (auto metadata = m_metadata_manager->get_file_action_metadata(realm_path)) {
160         if (run_file_action(*metadata)) {
161             metadata->remove();
162             return true;
163         }
164     }
165     return false;
166 }
167
168 // Perform a file action. Returns whether or not the file action can be removed.
169 bool SyncManager::run_file_action(const SyncFileActionMetadata& md)
170 {
171     switch (md.action()) {
172         case SyncFileActionMetadata::Action::DeleteRealm:
173             // Delete all the files for the given Realm.
174             m_file_manager->remove_realm(md.original_name());
175             return true;
176         case SyncFileActionMetadata::Action::BackUpThenDeleteRealm:
177             // Copy the primary Realm file to the recovery dir, and then delete the Realm.
178             auto new_name = md.new_name();
179             auto original_name = md.original_name();
180             if (!util::File::exists(original_name)) {
181                 // The Realm file doesn't exist anymore.
182                 return true;
183             }
184             if (new_name && !util::File::exists(*new_name) && m_file_manager->copy_realm_file(original_name, *new_name)) {
185                 // We successfully copied the Realm file to the recovery directory.
186                 m_file_manager->remove_realm(original_name);
187                 return true;
188             }
189             return false;
190     }
191     return false;
192 }
193
194 void SyncManager::reset_for_testing()
195 {
196     std::lock_guard<std::mutex> lock(m_file_system_mutex);
197     m_file_manager = nullptr;
198     m_metadata_manager = nullptr;
199     {
200         // Destroy all the users.
201         std::lock_guard<std::mutex> lock(m_user_mutex);
202         m_users.clear();
203         m_admin_token_users.clear();
204     }
205     {
206         std::lock_guard<std::mutex> lock(m_mutex);
207
208         // Stop the client. This will abort any uploads that inactive sessions are waiting for.
209         if (m_sync_client)
210             m_sync_client->stop();
211
212         {
213             std::lock_guard<std::mutex> lock(m_session_mutex);
214
215             // Callers of `SyncManager::reset_for_testing` should ensure there are no active sessions
216             // prior to calling `reset_for_testing`.
217             auto no_active_sessions = std::none_of(m_sessions.begin(), m_sessions.end(), [](auto& element){
218                 return element.second->existing_external_reference();
219             });
220             REALM_ASSERT_RELEASE(no_active_sessions);
221
222             // Destroy any inactive sessions.
223             // FIXME: We shouldn't have any inactive sessions at this point! Sessions are expected to
224             // remain inactive until their final upload completes, at which point they are unregistered
225             // and destroyed. Our call to `sync::Client::stop` above aborts all uploads, so all sessions
226             // should have already been destroyed.
227             m_sessions.clear();
228         }
229
230         // Destroy the client now that we have no remaining sessions.
231         m_sync_client = nullptr;
232
233         // Reset even more state.
234         // NOTE: these should always match the defaults.
235         m_log_level = util::Logger::Level::info;
236         m_logger_factory = nullptr;
237         m_client_reconnect_mode = ReconnectMode::normal;
238         m_multiplex_sessions = false;
239     }
240 }
241
242 void SyncManager::set_log_level(util::Logger::Level level) noexcept
243 {
244     std::lock_guard<std::mutex> lock(m_mutex);
245     m_log_level = level;
246 }
247
248 void SyncManager::set_logger_factory(SyncLoggerFactory& factory) noexcept
249 {
250     std::lock_guard<std::mutex> lock(m_mutex);
251     m_logger_factory = &factory;
252 }
253
254 void SyncManager::set_client_should_reconnect_immediately(bool reconnect_immediately)
255 {
256     std::lock_guard<std::mutex> lock(m_mutex);
257     m_client_reconnect_mode = reconnect_immediately ? ReconnectMode::immediate : ReconnectMode::normal;
258 }
259
260 bool SyncManager::client_should_reconnect_immediately() const noexcept
261 {
262     std::lock_guard<std::mutex> lock(m_mutex);
263     return m_client_reconnect_mode == ReconnectMode::immediate;
264 }
265
266 void SyncManager::reconnect()
267 {
268     std::lock_guard<std::mutex> lock(m_session_mutex);
269     for (auto& it : m_sessions) {
270         it.second->handle_reconnect();
271     }
272 }
273
274 util::Logger::Level SyncManager::log_level() const noexcept
275 {
276     std::lock_guard<std::mutex> lock(m_mutex);
277     return m_log_level;
278 }
279
280 bool SyncManager::perform_metadata_update(std::function<void(const SyncMetadataManager&)> update_function) const
281 {
282     std::lock_guard<std::mutex> lock(m_file_system_mutex);
283     if (!m_metadata_manager) {
284         return false;
285     }
286     update_function(*m_metadata_manager);
287     return true;
288 }
289
290 std::shared_ptr<SyncUser> SyncManager::get_user(const SyncUserIdentifier& identifier, std::string refresh_token)
291 {
292     std::lock_guard<std::mutex> lock(m_user_mutex);
293     auto it = m_users.find(identifier);
294     if (it == m_users.end()) {
295         // No existing user.
296         auto new_user = std::make_shared<SyncUser>(std::move(refresh_token),
297                                                    identifier.user_id,
298                                                    identifier.auth_server_url,
299                                                    none,
300                                                    SyncUser::TokenType::Normal);
301         m_users.insert({ identifier, new_user });
302         return new_user;
303     } else {
304         auto user = it->second;
305         if (user->state() == SyncUser::State::Error) {
306             return nullptr;
307         }
308         user->update_refresh_token(std::move(refresh_token));
309         return user;
310     }
311 }
312
313 std::shared_ptr<SyncUser> SyncManager::get_admin_token_user_from_identity(const std::string& identity,
314                                                                           util::Optional<std::string> server_url,
315                                                                           const std::string& token)
316 {
317     if (server_url)
318         return get_admin_token_user(*server_url, token, identity);
319
320     std::lock_guard<std::mutex> lock(m_user_mutex);
321     // Look up the user based off the identity.
322     // No server URL, so no migration possible.
323     auto it = m_admin_token_users.find(identity);
324     if (it == m_admin_token_users.end()) {
325         // No existing user.
326         auto new_user = std::make_shared<SyncUser>(token,
327                                                    c_admin_identity,
328                                                    std::move(server_url),
329                                                    identity,
330                                                    SyncUser::TokenType::Admin);
331         m_admin_token_users.insert({ identity, new_user });
332         return new_user;
333     } else {
334         return it->second;
335     }
336 }
337
338 std::shared_ptr<SyncUser> SyncManager::get_admin_token_user(const std::string& server_url,
339                                                             const std::string& token,
340                                                             util::Optional<std::string> old_identity)
341 {
342     std::shared_ptr<SyncUser> user;
343     {
344         std::lock_guard<std::mutex> lock(m_user_mutex);
345         // Look up the user based off the server URL.
346         auto it = m_admin_token_users.find(server_url);
347         if (it != m_admin_token_users.end())
348             return it->second;
349
350         // No existing user.
351         user = std::make_shared<SyncUser>(token,
352                                           c_admin_identity,
353                                           server_url,
354                                           c_admin_identity + server_url,
355                                           SyncUser::TokenType::Admin);
356         m_admin_token_users.insert({ server_url, user });
357     }
358     if (old_identity) {
359         // Try renaming the user's directory to use our new naming standard, if applicable.
360         std::lock_guard<std::mutex> fm_lock(m_file_system_mutex);
361         if (m_file_manager)
362             m_file_manager->try_rename_user_directory(*old_identity, c_admin_identity + server_url);
363     }
364     return user;
365 }
366
367 std::vector<std::shared_ptr<SyncUser>> SyncManager::all_logged_in_users() const
368 {
369     std::lock_guard<std::mutex> lock(m_user_mutex);
370     std::vector<std::shared_ptr<SyncUser>> users;
371     users.reserve(m_users.size() + m_admin_token_users.size());
372     for (auto& it : m_users) {
373         auto user = it.second;
374         if (user->state() == SyncUser::State::Active) {
375             users.emplace_back(std::move(user));
376         }
377     }
378     for (auto& it : m_admin_token_users) {
379         users.emplace_back(std::move(it.second));
380     }
381     return users;
382 }
383
384 std::shared_ptr<SyncUser> SyncManager::get_current_user() const
385 {
386     std::lock_guard<std::mutex> lock(m_user_mutex);
387
388     auto is_active_user = [](auto& el) { return el.second->state() == SyncUser::State::Active; };
389     auto it = std::find_if(m_users.begin(), m_users.end(), is_active_user);
390     if (it == m_users.end())
391         return nullptr;
392
393     if (std::find_if(std::next(it), m_users.end(), is_active_user) != m_users.end())
394         throw std::logic_error("Current user is not valid if more that one valid, logged-in user exists.");
395
396     return it->second;
397 }
398
399 std::shared_ptr<SyncUser> SyncManager::get_existing_logged_in_user(const SyncUserIdentifier& identifier) const
400 {
401     std::lock_guard<std::mutex> lock(m_user_mutex);
402     auto it = m_users.find(identifier);
403     if (it == m_users.end())
404         return nullptr;
405
406     auto user = it->second;
407     return user->state() == SyncUser::State::Active ? user : nullptr;
408 }
409
410 std::string SyncManager::path_for_realm(const SyncUser& user, const std::string& raw_realm_url) const
411 {
412     std::lock_guard<std::mutex> lock(m_file_system_mutex);
413     REALM_ASSERT(m_file_manager);
414     const auto& user_local_identity = user.local_identity();
415     util::Optional<SyncUserIdentifier> user_info;
416     if (user.token_type() == SyncUser::TokenType::Normal)
417         user_info = SyncUserIdentifier{ user.identity(), user.server_url() };
418
419     return m_file_manager->path(user_local_identity, raw_realm_url, std::move(user_info));
420 }
421
422 std::string SyncManager::recovery_directory_path() const
423 {
424     std::lock_guard<std::mutex> lock(m_file_system_mutex);
425     REALM_ASSERT(m_file_manager);
426     return m_file_manager->recovery_directory_path();
427 }
428
429 std::shared_ptr<SyncSession> SyncManager::get_existing_active_session(const std::string& path) const
430 {
431     std::lock_guard<std::mutex> lock(m_session_mutex);
432     if (auto session = get_existing_session_locked(path)) {
433         if (auto external_reference = session->existing_external_reference())
434             return external_reference;
435     }
436     return nullptr;
437 }
438
439 std::shared_ptr<SyncSession> SyncManager::get_existing_session_locked(const std::string& path) const
440 {
441     REALM_ASSERT(!m_session_mutex.try_lock());
442     auto it = m_sessions.find(path);
443     return it == m_sessions.end() ? nullptr : it->second;
444 }
445
446 std::shared_ptr<SyncSession> SyncManager::get_existing_session(const std::string& path) const
447 {
448     std::lock_guard<std::mutex> lock(m_session_mutex);
449     if (auto session = get_existing_session_locked(path))
450         return session->external_reference();
451
452     return nullptr;
453 }
454
455 std::shared_ptr<SyncSession> SyncManager::get_session(const std::string& path, const SyncConfig& sync_config)
456 {
457     auto& client = get_sync_client(); // Throws
458
459     std::lock_guard<std::mutex> lock(m_session_mutex);
460     if (auto session = get_existing_session_locked(path)) {
461         sync_config.user->register_session(session);
462         return session->external_reference();
463     }
464
465     auto shared_session = SyncSession::create(client, path, sync_config);
466     m_sessions[path] = shared_session;
467
468     // Create the external reference immediately to ensure that the session will become
469     // inactive if an exception is thrown in the following code.
470     auto external_reference = shared_session->external_reference();
471
472     sync_config.user->register_session(std::move(shared_session));
473
474     return external_reference;
475 }
476
477 void SyncManager::unregister_session(const std::string& path)
478 {
479     std::lock_guard<std::mutex> lock(m_session_mutex);
480     auto it = m_sessions.find(path);
481     REALM_ASSERT(it != m_sessions.end());
482
483     // If the session has an active external reference, leave it be. This will happen if the session
484     // moves to an inactive state while still externally reference, for instance, as a result of
485     // the session's user being logged out.
486     if (it->second->existing_external_reference())
487         return;
488
489     m_sessions.erase(path);
490 }
491
492 void SyncManager::enable_session_multiplexing()
493 {
494     std::lock_guard<std::mutex> lock(m_mutex);
495     if (m_sync_client)
496         throw std::logic_error("Cannot enable session multiplexing after creating the sync client");
497     m_multiplex_sessions = true;
498 }
499
500 SyncClient& SyncManager::get_sync_client() const
501 {
502     std::lock_guard<std::mutex> lock(m_mutex);
503     if (!m_sync_client)
504         m_sync_client = create_sync_client(); // Throws
505     return *m_sync_client;
506 }
507
508 std::unique_ptr<SyncClient> SyncManager::create_sync_client() const
509 {
510     REALM_ASSERT(!m_mutex.try_lock());
511
512     std::unique_ptr<util::Logger> logger;
513     if (m_logger_factory) {
514         logger = m_logger_factory->make_logger(m_log_level); // Throws
515     }
516     else {
517         auto stderr_logger = std::make_unique<util::StderrLogger>(); // Throws
518         stderr_logger->set_level_threshold(m_log_level);
519         logger = std::move(stderr_logger);
520     }
521     return std::make_unique<SyncClient>(std::move(logger), m_client_reconnect_mode, m_multiplex_sessions);
522 }
523
524 std::string SyncManager::client_uuid() const
525 {
526     REALM_ASSERT(m_metadata_manager);
527     return m_metadata_manager->client_uuid();
528 }