sos_extension_service/
lib.rs

1use sos_account::AccountSwitcherOptions;
2use sos_backend::{BackendTarget, InferOptions};
3use sos_core::{events::changes_feed, Paths};
4use sos_ipc::{
5    extension_helper::server::{
6        ExtensionHelperOptions, ExtensionHelperServer,
7    },
8    ServiceAppInfo,
9};
10use sos_net::{
11    NetworkAccount, NetworkAccountOptions, NetworkAccountSwitcher,
12};
13use std::sync::Arc;
14use tokio::sync::RwLock;
15use xclipboard::Clipboard;
16
17/// Entrypoint for an executable used to bridge JSON requests
18/// from browser extensions using the native messaging API
19/// to an in-memory extension helper web service.
20pub async fn run() -> anyhow::Result<()> {
21    let mut args = std::env::args().into_iter().collect::<Vec<_>>();
22
23    // Firefox passes two arguments, the last is the
24    // extension id (from Firefox 55) and Chrome passes
25    // a single argument on Mac and Linux. But on windows
26    // Chrome also passes a native window handle so we
27    // pop that first.
28    #[cfg(windows)]
29    args.pop();
30
31    let extension_id = args.pop().unwrap_or_else(String::new).to_string();
32    let changes_feed = changes_feed();
33
34    let mut accounts =
35        NetworkAccountSwitcher::new_with_options(AccountSwitcherOptions {
36            clipboard: Some(Clipboard::new_timeout(90)?),
37            ..Default::default()
38        });
39
40    let paths = Paths::new_client(Paths::data_dir()?);
41    let target = BackendTarget::infer(paths, InferOptions::default()).await?;
42
43    tracing::info!(backend_target = %target, "extension_service");
44
45    accounts
46        .load_accounts(
47            |identity| {
48                tracing::debug!(
49                    account_id = %identity.account_id(),
50                    "extension::load_account");
51                Box::pin(async move {
52                    let paths = Paths::new_client(Paths::data_dir()?)
53                        .with_account_id(identity.account_id());
54                    let target =
55                        BackendTarget::infer(paths, InferOptions::default())
56                            .await?;
57                    NetworkAccount::new_unauthenticated(
58                        *identity.account_id(),
59                        target,
60                        NetworkAccountOptions::default(),
61                    )
62                    .await
63                })
64            },
65            target,
66        )
67        .await?;
68
69    let info = ServiceAppInfo {
70        name: env!("CARGO_PKG_NAME").to_string(),
71        version: env!("CARGO_PKG_VERSION").to_string(),
72    };
73
74    let accounts = Arc::new(RwLock::new(accounts));
75    let options = ExtensionHelperOptions::new(extension_id, info);
76    let server = ExtensionHelperServer::new(options, accounts, |event| {
77        changes_feed.send_replace(event);
78    })
79    .await?;
80    server.listen().await;
81    Ok(())
82}