added iOS source code
[wl-app.git] / iOS / Pods / Realm / Realm / ObjectStore / src / sync / impl / sync_metadata.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/impl/sync_metadata.hpp"
20
21 #include "object_schema.hpp"
22 #include "object_store.hpp"
23 #include "property.hpp"
24 #include "results.hpp"
25 #include "schema.hpp"
26 #include "util/uuid.hpp"
27 #if REALM_PLATFORM_APPLE
28 #include "impl/apple/keychain_helper.hpp"
29 #endif
30
31 #include <realm/descriptor.hpp>
32 #include <realm/table.hpp>
33
34 namespace {
35 static const char * const c_sync_userMetadata = "UserMetadata";
36 static const char * const c_sync_marked_for_removal = "marked_for_removal";
37 static const char * const c_sync_identity = "identity";
38 static const char * const c_sync_local_uuid = "local_uuid";
39 static const char * const c_sync_auth_server_url = "auth_server_url";
40 static const char * const c_sync_user_token = "user_token";
41 static const char * const c_sync_user_is_admin = "user_is_admin";
42
43 static const char * const c_sync_fileActionMetadata = "FileActionMetadata";
44 static const char * const c_sync_original_name = "original_name";
45 static const char * const c_sync_new_name = "new_name";
46 static const char * const c_sync_action = "action";
47 static const char * const c_sync_url = "url";
48
49 static const char * const c_sync_clientMetadata = "ClientMetadata";
50 static const char * const c_sync_uuid = "uuid";
51
52 realm::Schema make_schema()
53 {
54     using namespace realm;
55     return Schema{
56         {c_sync_userMetadata, {
57             {c_sync_identity, PropertyType::String},
58             {c_sync_local_uuid, PropertyType::String},
59             {c_sync_marked_for_removal, PropertyType::Bool},
60             {c_sync_user_token, PropertyType::String|PropertyType::Nullable},
61             {c_sync_auth_server_url, PropertyType::String},
62             {c_sync_user_is_admin, PropertyType::Bool},
63         }},
64         {c_sync_fileActionMetadata, {
65             {c_sync_original_name, PropertyType::String, Property::IsPrimary{true}},
66             {c_sync_new_name, PropertyType::String|PropertyType::Nullable},
67             {c_sync_action, PropertyType::Int},
68             {c_sync_url, PropertyType::String},
69             {c_sync_identity, PropertyType::String},
70         }},
71         {c_sync_clientMetadata, {
72             {c_sync_uuid, PropertyType::String},
73         }}
74     };
75 }
76
77 } // anonymous namespace
78
79 namespace realm {
80
81 // MARK: - Sync metadata manager
82
83 SyncMetadataManager::SyncMetadataManager(std::string path,
84                                          bool should_encrypt,
85                                          util::Optional<std::vector<char>> encryption_key)
86 {
87     constexpr uint64_t SCHEMA_VERSION = 2;
88
89     Realm::Config config;
90     config.path = path;
91     config.schema = make_schema();
92     config.schema_version = SCHEMA_VERSION;
93     config.schema_mode = SchemaMode::Automatic;
94 #if REALM_PLATFORM_APPLE
95     if (should_encrypt && !encryption_key) {
96         encryption_key = keychain::metadata_realm_encryption_key(File::exists(path));
97     }
98 #endif
99     if (should_encrypt) {
100         if (!encryption_key) {
101             throw std::invalid_argument("Metadata Realm encryption was specified, but no encryption key was provided.");
102         }
103         config.encryption_key = std::move(*encryption_key);
104     }
105
106     config.migration_function = [](SharedRealm old_realm, SharedRealm realm, Schema&) {
107         if (old_realm->schema_version() < 2) {
108             TableRef old_table = ObjectStore::table_for_object_type(old_realm->read_group(), c_sync_userMetadata);
109             TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
110
111             // Get all the SyncUserMetadata objects.
112             Results results(old_realm, *old_table);
113
114             // Column indices.
115             size_t old_idx_identity = old_table->get_column_index(c_sync_identity);
116             size_t old_idx_url = old_table->get_column_index(c_sync_auth_server_url);
117             size_t idx_local_uuid = table->get_column_index(c_sync_local_uuid);
118             size_t idx_url = table->get_column_index(c_sync_auth_server_url);
119
120             for (size_t i = 0; i < results.size(); i++) {
121                 RowExpr entry = results.get(i);
122                 // Set the UUID equal to the user identity for existing users.
123                 auto identity = entry.get_string(old_idx_identity);
124                 table->set_string(idx_local_uuid, entry.get_index(), identity);
125                 // Migrate the auth server URLs to a non-nullable property.
126                 auto url = entry.get_string(old_idx_url);
127                 table->set_string(idx_url, entry.get_index(), url.is_null() ? "" : url);
128             }
129         }
130     };
131
132     SharedRealm realm = Realm::get_shared_realm(config);
133
134     // Get data about the (hardcoded) schemas
135     auto object_schema = realm->schema().find(c_sync_userMetadata);
136     m_user_schema = {
137         object_schema->persisted_properties[0].table_column,
138         object_schema->persisted_properties[1].table_column,
139         object_schema->persisted_properties[2].table_column,
140         object_schema->persisted_properties[3].table_column,
141         object_schema->persisted_properties[4].table_column,
142         object_schema->persisted_properties[5].table_column,
143     };
144
145     object_schema = realm->schema().find(c_sync_fileActionMetadata);
146     m_file_action_schema = {
147         object_schema->persisted_properties[0].table_column,
148         object_schema->persisted_properties[1].table_column,
149         object_schema->persisted_properties[2].table_column,
150         object_schema->persisted_properties[3].table_column,
151         object_schema->persisted_properties[4].table_column,
152     };
153
154     object_schema = realm->schema().find(c_sync_clientMetadata);
155     m_client_schema = {
156         object_schema->persisted_properties[0].table_column,
157     };
158
159     m_metadata_config = std::move(config);
160 }
161
162 SyncUserMetadataResults SyncMetadataManager::all_unmarked_users() const
163 {
164     return get_users(false);
165 }
166
167 SyncUserMetadataResults SyncMetadataManager::all_users_marked_for_removal() const
168 {
169     return get_users(true);
170 }
171
172 SyncUserMetadataResults SyncMetadataManager::get_users(bool marked) const
173 {
174     SharedRealm realm = Realm::get_shared_realm(m_metadata_config);
175
176     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
177     Query query = table->where().equal(m_user_schema.idx_marked_for_removal, marked);
178
179     Results results(realm, std::move(query));
180     return SyncUserMetadataResults(std::move(results), std::move(realm), m_user_schema);
181 }
182
183 SyncFileActionMetadataResults SyncMetadataManager::all_pending_actions() const
184 {
185     SharedRealm realm = Realm::get_shared_realm(m_metadata_config);
186     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
187     Results results(realm, table->where());
188     return SyncFileActionMetadataResults(std::move(results), std::move(realm), m_file_action_schema);
189 }
190
191 bool SyncMetadataManager::delete_metadata_action(const std::string& original_name) const
192 {
193     auto shared_realm = Realm::get_shared_realm(m_metadata_config);
194
195     // Retrieve the row for this object.
196     TableRef table = ObjectStore::table_for_object_type(shared_realm->read_group(), c_sync_fileActionMetadata);
197     shared_realm->begin_transaction();
198     size_t row_idx = table->find_first_string(m_file_action_schema.idx_original_name, original_name);
199     if (row_idx == not_found) {
200         shared_realm->cancel_transaction();
201         return false;
202     }
203     table->move_last_over(row_idx);
204     shared_realm->commit_transaction();
205     return true;
206 }
207
208 util::Optional<SyncUserMetadata> SyncMetadataManager::get_or_make_user_metadata(const std::string& identity,
209                                                                                 const std::string& url,
210                                                                                 bool make_if_absent) const
211 {
212     auto realm = Realm::get_shared_realm(m_metadata_config);
213     auto& schema = m_user_schema;
214
215     // Retrieve or create the row for this object.
216     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_userMetadata);
217     Query query = table->where().equal(schema.idx_identity, identity).equal(schema.idx_auth_server_url, url);
218     Results results(realm, std::move(query));
219     REALM_ASSERT_DEBUG(results.size() < 2);
220     auto row = results.first();
221
222     if (!row) {
223         if (!make_if_absent)
224             return none;
225
226         realm->begin_transaction();
227         // Check the results again.
228         row = results.first();
229         if (!row) {
230             auto row = table->get(table->add_empty_row());
231             std::string uuid = util::uuid_string();
232             row.set_string(schema.idx_identity, identity);
233             row.set_string(schema.idx_auth_server_url, url);
234             row.set_string(schema.idx_local_uuid, uuid);
235             row.set_bool(schema.idx_user_is_admin, false);
236             row.set_bool(schema.idx_marked_for_removal, false);
237             realm->commit_transaction();
238             return SyncUserMetadata(schema, std::move(realm), std::move(row));
239         } else {
240             // Someone beat us to adding this user.
241             if (row->get_bool(schema.idx_marked_for_removal)) {
242                 // User is dead. Revive or return none.
243                 if (make_if_absent) {
244                     row->set_bool(schema.idx_marked_for_removal, false);
245                     realm->commit_transaction();
246                 } else {
247                     realm->cancel_transaction();
248                     return none;
249                 }
250             } else {
251                 // User is alive, nothing else to do.
252                 realm->cancel_transaction();
253             }
254             return SyncUserMetadata(schema, std::move(realm), std::move(*row));
255         }
256     }
257
258     // Got an existing user.
259     if (row->get_bool(schema.idx_marked_for_removal)) {
260         // User is dead. Revive or return none.
261         if (make_if_absent) {
262             realm->begin_transaction();
263             row->set_bool(schema.idx_marked_for_removal, false);
264             realm->commit_transaction();
265         } else {
266             return none;
267         }
268     }
269     return SyncUserMetadata(schema, std::move(realm), std::move(*row));
270 }
271
272 SyncFileActionMetadata SyncMetadataManager::make_file_action_metadata(const std::string &original_name,
273                                                                       const std::string &url,
274                                                                       const std::string &local_uuid,
275                                                                       SyncFileActionMetadata::Action action,
276                                                                       util::Optional<std::string> new_name) const
277 {
278     size_t raw_action = static_cast<size_t>(action);
279
280     // Open the Realm.
281     auto realm = Realm::get_shared_realm(m_metadata_config);
282     auto& schema = m_file_action_schema;
283
284     // Retrieve or create the row for this object.
285     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
286     realm->begin_transaction();
287     size_t row_idx = table->find_first_string(schema.idx_original_name, original_name);
288     if (row_idx == not_found) {
289         row_idx = table->add_empty_row();
290         table->set_string(schema.idx_original_name, row_idx, original_name);
291     }
292     table->set_string(schema.idx_new_name, row_idx, new_name);
293     table->set_int(schema.idx_action, row_idx, raw_action);
294     table->set_string(schema.idx_url, row_idx, url);
295     table->set_string(schema.idx_user_identity, row_idx, local_uuid);
296     realm->commit_transaction();
297     return SyncFileActionMetadata(schema, std::move(realm), table->get(row_idx));
298 }
299
300 util::Optional<SyncFileActionMetadata> SyncMetadataManager::get_file_action_metadata(const std::string& original_name) const
301 {
302     auto realm = Realm::get_shared_realm(m_metadata_config);
303     auto schema = m_file_action_schema;
304     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_fileActionMetadata);
305     size_t row_idx = table->find_first_string(schema.idx_original_name, original_name);
306     if (row_idx == not_found)
307         return none;
308
309     return SyncFileActionMetadata(std::move(schema), std::move(realm), table->get(row_idx));
310 }
311
312 std::string SyncMetadataManager::client_uuid() const
313 {
314     auto realm = Realm::get_shared_realm(m_metadata_config);
315     TableRef table = ObjectStore::table_for_object_type(realm->read_group(), c_sync_clientMetadata);
316     if (table->is_empty()) {
317         realm->begin_transaction();
318         if (table->is_empty()) {
319             size_t idx = table->add_empty_row();
320             REALM_ASSERT_DEBUG(idx == 0);
321             auto uuid = uuid_string();
322             table->set_string(m_client_schema.idx_uuid, idx, uuid);
323             realm->commit_transaction();
324             return uuid;
325         }
326         realm->cancel_transaction();
327     }
328
329     return table->get_string(m_client_schema.idx_uuid, 0);
330 }
331
332 // MARK: - Sync user metadata
333
334 SyncUserMetadata::SyncUserMetadata(Schema schema, SharedRealm realm, RowExpr row)
335 : m_realm(std::move(realm))
336 , m_schema(std::move(schema))
337 , m_row(row)
338 { }
339
340 std::string SyncUserMetadata::identity() const
341 {
342     REALM_ASSERT(m_realm);
343     m_realm->verify_thread();
344     return m_row.get_string(m_schema.idx_identity);
345 }
346
347 std::string SyncUserMetadata::local_uuid() const
348 {
349     REALM_ASSERT(m_realm);
350     m_realm->verify_thread();
351     return m_row.get_string(m_schema.idx_local_uuid);
352 }
353
354 util::Optional<std::string> SyncUserMetadata::user_token() const
355 {
356     REALM_ASSERT(m_realm);
357     m_realm->verify_thread();
358     StringData result = m_row.get_string(m_schema.idx_user_token);
359     return result.is_null() ? util::none : util::make_optional(std::string(result));
360 }
361
362 std::string SyncUserMetadata::auth_server_url() const
363 {
364     REALM_ASSERT(m_realm);
365     m_realm->verify_thread();
366     return m_row.get_string(m_schema.idx_auth_server_url);
367 }
368
369 bool SyncUserMetadata::is_admin() const
370 {
371     REALM_ASSERT(m_realm);
372     m_realm->verify_thread();
373     return m_row.get_bool(m_schema.idx_user_is_admin);
374 }
375
376 void SyncUserMetadata::set_user_token(util::Optional<std::string> user_token)
377 {
378     if (m_invalid)
379         return;
380
381     REALM_ASSERT_DEBUG(m_realm);
382     m_realm->verify_thread();
383     m_realm->begin_transaction();
384     m_row.set_string(m_schema.idx_user_token, *user_token);
385     m_realm->commit_transaction();
386 }
387
388 void SyncUserMetadata::set_is_admin(bool is_admin)
389 {
390     if (m_invalid)
391         return;
392
393     REALM_ASSERT_DEBUG(m_realm);
394     m_realm->verify_thread();
395     m_realm->begin_transaction();
396     m_row.set_bool(m_schema.idx_user_is_admin, is_admin);
397     m_realm->commit_transaction();
398 }
399
400 void SyncUserMetadata::mark_for_removal()
401 {
402     if (m_invalid)
403         return;
404
405     m_realm->verify_thread();
406     m_realm->begin_transaction();
407     m_row.set_bool(m_schema.idx_marked_for_removal, true);
408     m_realm->commit_transaction();
409 }
410
411 void SyncUserMetadata::remove()
412 {
413     m_invalid = true;
414     m_realm->begin_transaction();
415     TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_userMetadata);
416     table->move_last_over(m_row.get_index());
417     m_realm->commit_transaction();
418     m_realm = nullptr;
419 }
420
421 // MARK: - File action metadata
422
423 SyncFileActionMetadata::SyncFileActionMetadata(Schema schema, SharedRealm realm, RowExpr row)
424 : m_realm(std::move(realm))
425 , m_schema(std::move(schema))
426 , m_row(row)
427 { }
428
429 std::string SyncFileActionMetadata::original_name() const
430 {
431     REALM_ASSERT(m_realm);
432     m_realm->verify_thread();
433     return m_row.get_string(m_schema.idx_original_name);
434 }
435
436 util::Optional<std::string> SyncFileActionMetadata::new_name() const
437 {
438     REALM_ASSERT(m_realm);
439     m_realm->verify_thread();
440     StringData result = m_row.get_string(m_schema.idx_new_name);
441     return result.is_null() ? util::none : util::make_optional(std::string(result));
442 }
443
444 std::string SyncFileActionMetadata::user_local_uuid() const
445 {
446     REALM_ASSERT(m_realm);
447     m_realm->verify_thread();
448     return m_row.get_string(m_schema.idx_user_identity);
449 }
450
451 SyncFileActionMetadata::Action SyncFileActionMetadata::action() const
452 {
453     REALM_ASSERT(m_realm);
454     m_realm->verify_thread();
455     return static_cast<SyncFileActionMetadata::Action>(m_row.get_int(m_schema.idx_action));
456 }
457
458 std::string SyncFileActionMetadata::url() const
459 {
460     REALM_ASSERT(m_realm);
461     m_realm->verify_thread();
462     return m_row.get_string(m_schema.idx_url);
463 }
464
465 void SyncFileActionMetadata::remove()
466 {
467     REALM_ASSERT(m_realm);
468     m_realm->verify_thread();
469     m_realm->begin_transaction();
470     TableRef table = ObjectStore::table_for_object_type(m_realm->read_group(), c_sync_fileActionMetadata);
471     table->move_last_over(m_row.get_index());
472     m_realm->commit_transaction();
473     m_realm = nullptr;
474 }
475
476 } // namespace realm