1use crate::config::{proxy::Proxy, request::SetOpt};
4use std::{
5 fmt,
6 ops::{BitOr, BitOrAssign},
7};
8
9#[derive(Clone)]
12pub struct Credentials {
13 username: String,
14 password: String,
15}
16
17impl Credentials {
18 pub fn new(username: impl Into<String>, password: impl Into<String>) -> Self {
20 Self {
21 username: username.into(),
22 password: password.into(),
23 }
24 }
25}
26
27impl SetOpt for Credentials {
28 fn set_opt<H>(&self, easy: &mut curl::easy::Easy2<H>) -> Result<(), curl::Error> {
29 easy.username(&self.username)?;
30 easy.password(&self.password)
31 }
32}
33
34impl SetOpt for Proxy<Credentials> {
35 fn set_opt<H>(&self, easy: &mut curl::easy::Easy2<H>) -> Result<(), curl::Error> {
36 easy.proxy_username(&self.0.username)?;
37 easy.proxy_password(&self.0.password)
38 }
39}
40
41impl fmt::Debug for Credentials {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.debug_struct("Credentials")
46 .field("username", &self.username)
47 .field("password", &"*****")
48 .finish()
49 }
50}
51
52#[derive(Clone, Debug)]
54pub struct Authentication(u8);
55
56impl Default for Authentication {
57 fn default() -> Self {
58 Self::none()
59 }
60}
61
62impl Authentication {
63 pub const fn none() -> Self {
65 Authentication(0)
66 }
67
68 pub const fn all() -> Self {
70 #[allow(unused_mut)]
71 let mut all = Self::basic().0 | Self::digest().0;
72
73 #[cfg(feature = "spnego")]
74 {
75 all |= Self::negotiate().0;
76 }
77
78 Authentication(all)
79 }
80
81 pub const fn basic() -> Self {
87 Authentication(0b0001)
88 }
89
90 pub const fn digest() -> Self {
96 Authentication(0b0010)
97 }
98
99 #[cfg(feature = "spnego")]
115 pub const fn negotiate() -> Self {
116 Authentication(0b0100)
117 }
118
119 const fn contains(&self, other: Self) -> bool {
120 (self.0 & other.0) == other.0
121 }
122
123 fn as_auth(&self) -> curl::easy::Auth {
124 let mut auth = curl::easy::Auth::new();
125
126 if self.contains(Authentication::basic()) {
127 auth.basic(true);
128 }
129
130 if self.contains(Authentication::digest()) {
131 auth.digest(true);
132 }
133
134 #[cfg(feature = "spnego")]
135 {
136 if self.contains(Authentication::negotiate()) {
137 auth.gssnegotiate(true);
138 }
139 }
140
141 auth
142 }
143}
144
145impl BitOr for Authentication {
146 type Output = Self;
147
148 fn bitor(mut self, other: Self) -> Self {
149 self |= other;
150 self
151 }
152}
153
154impl BitOrAssign for Authentication {
155 fn bitor_assign(&mut self, rhs: Self) {
156 self.0 |= rhs.0;
157 }
158}
159
160impl SetOpt for Authentication {
161 fn set_opt<H>(&self, easy: &mut curl::easy::Easy2<H>) -> Result<(), curl::Error> {
162 #[cfg(feature = "spnego")]
163 {
164 if self.contains(Authentication::negotiate()) {
165 easy.username("")?;
168 easy.password("")?;
169 }
170 }
171
172 easy.http_auth(&self.as_auth())
173 }
174}
175
176impl SetOpt for Proxy<Authentication> {
177 fn set_opt<H>(&self, easy: &mut curl::easy::Easy2<H>) -> Result<(), curl::Error> {
178 #[cfg(feature = "spnego")]
179 {
180 if self.0.contains(Authentication::negotiate()) {
181 easy.proxy_username("")?;
184 easy.proxy_password("")?;
185 }
186 }
187
188 easy.proxy_auth(&self.0.as_auth())
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::Authentication;
195
196 #[test]
197 fn auth_default() {
198 let auth = Authentication::default();
199
200 assert!(!auth.contains(Authentication::basic()));
201 assert!(!auth.contains(Authentication::digest()));
202 }
203
204 #[test]
205 fn auth_all() {
206 let auth = Authentication::all();
207
208 assert!(auth.contains(Authentication::basic()));
209 assert!(auth.contains(Authentication::digest()));
210 }
211
212 #[test]
213 fn auth_single() {
214 let auth = Authentication::basic();
215
216 assert!(auth.contains(Authentication::basic()));
217 assert!(!auth.contains(Authentication::digest()));
218
219 let auth = Authentication::digest();
220
221 assert!(!auth.contains(Authentication::basic()));
222 assert!(auth.contains(Authentication::digest()));
223 }
224}