1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::HashSet;

mod engine;
mod record;
mod ser;

use anyhow::Result;
pub use engine::Engine;
pub use sync15_traits::client::{ClientData, DeviceType, RemoteClient};

// These are what desktop uses.
const CLIENTS_TTL: u32 = 1_814_400; // 21 days
pub(crate) const CLIENTS_TTL_REFRESH: u64 = 604_800; // 7 days

/// A command processor applies incoming commands like wipes and resets for all
/// stores, and returns commands to send to other clients. It also manages
/// settings like the device name and type, which is stored in the special
/// `clients` collection.
///
/// In practice, this trait only has one implementation, in the sync manager.
/// It's split this way because the clients engine depends on internal `sync15`
/// structures, and can't be implemented as a syncable store...but `sync15`
/// doesn't know anything about multiple engines. This lets the sync manager
/// provide its own implementation for handling wipe and reset commands for all
/// the engines that it manages.
pub trait CommandProcessor {
    fn settings(&self) -> &Settings;

    /// Fetches commands to send to other clients. An error return value means
    /// commands couldn't be fetched, and halts the sync.
    fn fetch_outgoing_commands(&self) -> Result<HashSet<Command>>;

    /// Applies a command sent to this client from another client. This method
    /// should return a `CommandStatus` indicating whether the command was
    /// processed.
    ///
    /// An error return value means the sync manager encountered an error
    /// applying the command, and halts the sync to prevent unexpected behavior
    /// (for example, merging local and remote bookmarks, when we were told to
    /// wipe our local bookmarks).
    fn apply_incoming_command(&self, command: Command) -> Result<CommandStatus>;
}

/// Indicates if a command was applied successfully, ignored, or not supported.
/// Applied and ignored commands are removed from our client record, and never
/// retried. Unsupported commands are put back into our record, and retried on
/// subsequent syncs. This is to handle clients adding support for new data
/// types.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum CommandStatus {
    Applied,
    Ignored,
    Unsupported,
}

impl From<&record::ClientRecord> for RemoteClient {
    fn from(record: &record::ClientRecord) -> RemoteClient {
        RemoteClient {
            fxa_device_id: record.fxa_device_id.clone(),
            device_name: record.name.clone(),
            device_type: record.typ.as_ref().and_then(DeviceType::try_from_str),
        }
    }
}

/// Information about this device to include in its client record. This should
/// be persisted across syncs, as part of the sync manager state.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Settings {
    /// The FxA device ID of this client, also used as this client's record ID
    /// in the clients collection.
    pub fxa_device_id: String,
    /// The name of this client. This should match the client's name in the
    /// FxA device manager.
    pub device_name: String,
    /// The type of this client: mobile, tablet, desktop, or other.
    pub device_type: DeviceType,
}

#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Command {
    /// Erases all local data.
    WipeAll,
    /// Erases all local data for a specific engine.
    Wipe(String),
    /// Resets local sync state for all engines.
    ResetAll,
    /// Resets local sync state for a specific engine.
    Reset(String),
}