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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! NetworkManager secret agent for credential prompting over D-Bus.
//!
//! When NetworkManager needs credentials it does not already have — a Wi-Fi
//! password was forgotten, a VPN token expired, an 802.1X password is required
//! — it calls every registered **secret agent** via D-Bus. This module lets
//! `nmrs` consumers register such an agent and respond to those requests
//! without touching raw D-Bus.
//!
//! # Three-stream model
//!
//! [`SecretAgentBuilder::register()`](crate::agent::SecretAgentBuilder::register)
//! returns a handle and three logical streams:
//!
//! 1. **Request stream** — the primary
//! [`mpsc::Receiver<SecretRequest>`](futures::channel::mpsc::Receiver)
//! returned alongside the handle. Each item is a credential prompt from
//! NetworkManager. Respond through the attached
//! [`SecretResponder`](crate::agent::SecretResponder).
//!
//! 2. **Cancellation stream** — accessed via
//! [`SecretAgentHandle::cancellations()`](crate::agent::SecretAgentHandle::cancellations). Yields
//! [`CancelReason`](crate::agent::CancelReason) items when
//! NetworkManager aborts a pending request. The agent replies to
//! NetworkManager automatically; this stream exists so the consumer can
//! tear down any UI it may have shown.
//!
//! 3. **Store event stream** — accessed via
//! [`SecretAgentHandle::store_events()`](crate::agent::SecretAgentHandle::store_events). Yields
//! [`SecretStoreEvent`](crate::agent::SecretStoreEvent) items when
//! NetworkManager asks the agent to save or delete persisted secrets.
//! Since `nmrs` delegates persistence to the consumer, these events are
//! optional and the agent always acknowledges them.
//!
//! # Lifecycle
//!
//! ```text
//! SecretAgent::builder()
//! .with_identifier("com.example.MyApp")
//! .register().await?
//! │
//! ├── (SecretAgentHandle, request stream)
//! │
//! │ ┌──────── consumer loop ────────┐
//! │ │ while let Some(req) = rx … { │
//! │ │ req.responder.wifi_psk(…) │
//! │ │ } │
//! │ └───────────────────────────────┘
//! │
//! └── handle.unregister().await?
//! ```
//!
//! The identifier is only passed to NetworkManager; it is not a D-Bus
//! well-known name. The agent object is served at NetworkManager's standard
//! `/org/freedesktop/NetworkManager/SecretAgent` path by default.
//!
//! GUI apps should register one long-lived agent during application or applet
//! startup and keep the returned [`SecretAgentHandle`](crate::agent::SecretAgentHandle)
//! alive for the process lifetime. Dropping or unregistering the handle tears
//! down the registration and closes the request stream.
//!
//! If NetworkManager restarts while the agent is running, call
//! [`SecretAgentHandle::reregister()`](crate::agent::SecretAgentHandle::reregister)
//! after detecting that NetworkManager is available again.
//!
//! # Example
//!
//! ```no_run
//! use futures::StreamExt;
//! use nmrs::agent::{SecretAgent, SecretAgentFlags, SecretSetting};
//!
//! # async fn run() -> nmrs::Result<()> {
//! let (handle, mut requests) = SecretAgent::builder()
//! .with_identifier("com.example.demo")
//! .register()
//! .await?;
//!
//! let request_task = tokio::spawn(async move {
//! while let Some(req) = requests.next().await {
//! if !req.flags.contains(SecretAgentFlags::ALLOW_INTERACTION) {
//! req.responder.no_secrets().await?;
//! continue;
//! }
//!
//! match req.setting {
//! SecretSetting::WifiPsk { ref ssid } => {
//! println!("Password needed for {ssid}");
//! req.responder.wifi_psk("secret").await?;
//! }
//! _ => req.responder.cancel().await?,
//! }
//! }
//!
//! Ok::<(), nmrs::ConnectionError>(())
//! });
//!
//! // Keep `handle` alive for as long as the app should answer prompts.
//! // After a NetworkManager restart, call:
//! // handle.reregister().await?;
//!
//! request_task.abort();
//! handle.unregister().await?;
//! # Ok(())
//! # }
//! ```
//!
//! A foreground loop works too:
//!
//! ```no_run
//! use futures::StreamExt;
//! use nmrs::agent::{SecretAgent, SecretAgentFlags, SecretSetting};
//!
//! # async fn run() -> nmrs::Result<()> {
//! let (handle, mut requests) = SecretAgent::builder()
//! .with_identifier("com.example.demo")
//! .register()
//! .await?;
//!
//! while let Some(req) = requests.next().await {
//! if !req.flags.contains(SecretAgentFlags::ALLOW_INTERACTION) {
//! req.responder.no_secrets().await?;
//! continue;
//! }
//! match req.setting {
//! SecretSetting::WifiPsk { ref ssid } => {
//! println!("Password needed for {ssid}");
//! req.responder.wifi_psk("secret").await?;
//! }
//! _ => req.responder.cancel().await?,
//! }
//! }
//!
//! handle.unregister().await?;
//! # Ok(())
//! # }
//! ```
pub
pub use ;
pub use ;
/// Type alias so agent consumers only need one error type.
pub type AgentError = crateConnectionError;