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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
mod widget;
use widget::{form::list::item::value::Value, Widget};
use crate::app::browser::window::Action;
use crate::profile::Profile;
use gtk::{
gio::{prelude::TlsCertificateExt, TlsCertificate},
glib::Uri,
prelude::IsA,
};
use std::rc::Rc;
const DATE_FORMAT: &str = "%Y.%m.%d";
pub struct Gemini {
// profile: Rc<Profile>,
widget: Rc<Widget>,
}
impl Gemini {
// Construct
/// Create new `Self` for given Profile
pub fn new(profile: Rc<Profile>, action: Rc<Action>, auth_uri: Uri) -> Self {
// Init widget
let widget = Rc::new(Widget::new(profile.clone()));
// Init shared components
let auth_url = auth_uri.to_string();
// Set first record selected by default
let mut selected: u32 = 0;
// Add guest option
widget.form.list.append(
Value::UseGuestSession,
"Guest session",
"No identity for this request",
);
// Add new identity option
widget.form.list.append(
Value::GenerateNewAuth,
"Create new",
"Generate long-term certificate",
);
// Add import existing identity option
widget.form.list.append(
Value::ImportPem,
"Import identity",
"Use existing certificate",
);
// Collect additional options from database
let mut i = 2; // start from 3'th
match profile.identity.gemini.database.records() {
Ok(identities) => {
for identity in identities {
i += 1;
// Get certificate details
let certificate = match TlsCertificate::from_pem(&identity.pem) {
Ok(certificate) => certificate,
Err(reason) => todo!("{reason}"),
};
// Append record option
widget.form.list.append(
Value::ProfileIdentityGeminiId(identity.id),
&certificate.subject_name().unwrap().replace("CN=", ""), // trim prefix
&format!(
"{} - {} | auth: {}",
certificate
.not_valid_before()
.unwrap()
.format(DATE_FORMAT)
.unwrap(),
certificate
.not_valid_after()
.unwrap()
.format(DATE_FORMAT)
.unwrap(),
profile
.identity
.gemini
.auth
.database
.records_scope(None)
.unwrap()
.iter()
.filter(|this| this.profile_identity_gemini_id == identity.id)
.count(),
),
);
// Is selected?
if profile
.identity
.gemini
.auth
.database
.records_scope(Some(auth_url.as_str()))
.unwrap()
.iter()
.filter(|this| this.profile_identity_gemini_id == identity.id)
.count()
> 0
{
selected = i;
}
}
// Select list item
widget.form.list.gobject.set_selected(selected);
}
Err(_) => todo!(),
}
// Init events
widget.on_apply({
let widget = widget.clone();
move |response| {
// Get option match user choice
let option = match response {
Value::ProfileIdentityGeminiId(value) => Some(value),
Value::UseGuestSession => None,
Value::GenerateNewAuth => Some(
match profile
.identity
.gemini
.make(None, &widget.form.name.value().unwrap())
{
Ok(profile_identity_gemini_id) => profile_identity_gemini_id,
Err(reason) => todo!("{}", reason.to_string()),
},
),
Value::ImportPem => Some(
match profile
.identity
.gemini
.add(&widget.form.file.pem.take().unwrap())
{
Ok(profile_identity_gemini_id) => profile_identity_gemini_id,
Err(reason) => todo!("{}", reason.to_string()),
},
),
};
// Apply auth
match option {
// Activate identity for `auth_uri`
Some(profile_identity_gemini_id) => {
if let Err(reason) = profile
.identity
.gemini
.auth
.apply(profile_identity_gemini_id, auth_url.as_str())
{
todo!("{}", reason.to_string())
};
}
// Remove all identity auths for `auth_uri`
None => {
if let Err(reason) =
profile.identity.gemini.auth.remove_scope(auth_url.as_str())
{
todo!("{}", reason.to_string())
};
}
}
// Reload page to apply changes
action.reload.activate();
}
});
// Return activated `Self`
Self {
// profile,
widget,
}
}
// Actions
/// Show dialog for parent [Widget](https://docs.gtk.org/gtk4/class.Widget.html)
pub fn present(&self, parent: Option<&impl IsA<gtk::Widget>>) {
self.widget.present(parent);
}
}