1 | use std::ffi::CStr; |
2 | use std::fmt; |
3 | use std::str; |
4 | |
5 | use libc::{c_char, c_int}; |
6 | |
7 | /// Version information about libcurl and the capabilities that it supports. |
8 | pub struct Version { |
9 | inner: *mut curl_sys::curl_version_info_data, |
10 | } |
11 | |
12 | unsafe impl Send for Version {} |
13 | unsafe impl Sync for Version {} |
14 | |
15 | /// An iterator over the list of protocols a version supports. |
16 | #[derive (Clone)] |
17 | pub struct Protocols<'a> { |
18 | cur: *const *const c_char, |
19 | _inner: &'a Version, |
20 | } |
21 | |
22 | impl Version { |
23 | /// Returns the libcurl version that this library is currently linked against. |
24 | pub fn num() -> &'static str { |
25 | unsafe { |
26 | let s = CStr::from_ptr(curl_sys::curl_version() as *const _); |
27 | str::from_utf8(s.to_bytes()).unwrap() |
28 | } |
29 | } |
30 | |
31 | /// Returns the libcurl version that this library is currently linked against. |
32 | pub fn get() -> Version { |
33 | unsafe { |
34 | let ptr = curl_sys::curl_version_info(curl_sys::CURLVERSION_NOW); |
35 | assert!(!ptr.is_null()); |
36 | Version { inner: ptr } |
37 | } |
38 | } |
39 | |
40 | /// Returns the human readable version string, |
41 | pub fn version(&self) -> &str { |
42 | unsafe { crate::opt_str((*self.inner).version).unwrap() } |
43 | } |
44 | |
45 | /// Returns a numeric representation of the version number |
46 | /// |
47 | /// This is a 24 bit number made up of the major number, minor, and then |
48 | /// patch number. For example 7.9.8 will return 0x070908. |
49 | pub fn version_num(&self) -> u32 { |
50 | unsafe { (*self.inner).version_num as u32 } |
51 | } |
52 | |
53 | /// Returns true if this was built with the vendored version of libcurl. |
54 | pub fn vendored(&self) -> bool { |
55 | curl_sys::vendored() |
56 | } |
57 | |
58 | /// Returns a human readable string of the host libcurl is built for. |
59 | /// |
60 | /// This is discovered as part of the build environment. |
61 | pub fn host(&self) -> &str { |
62 | unsafe { crate::opt_str((*self.inner).host).unwrap() } |
63 | } |
64 | |
65 | /// Returns whether libcurl supports IPv6 |
66 | pub fn feature_ipv6(&self) -> bool { |
67 | self.flag(curl_sys::CURL_VERSION_IPV6) |
68 | } |
69 | |
70 | /// Returns whether libcurl supports SSL |
71 | pub fn feature_ssl(&self) -> bool { |
72 | self.flag(curl_sys::CURL_VERSION_SSL) |
73 | } |
74 | |
75 | /// Returns whether libcurl supports HTTP deflate via libz |
76 | pub fn feature_libz(&self) -> bool { |
77 | self.flag(curl_sys::CURL_VERSION_LIBZ) |
78 | } |
79 | |
80 | /// Returns whether libcurl supports HTTP NTLM |
81 | pub fn feature_ntlm(&self) -> bool { |
82 | self.flag(curl_sys::CURL_VERSION_NTLM) |
83 | } |
84 | |
85 | /// Returns whether libcurl supports HTTP GSSNEGOTIATE |
86 | pub fn feature_gss_negotiate(&self) -> bool { |
87 | self.flag(curl_sys::CURL_VERSION_GSSNEGOTIATE) |
88 | } |
89 | |
90 | /// Returns whether libcurl was built with debug capabilities |
91 | pub fn feature_debug(&self) -> bool { |
92 | self.flag(curl_sys::CURL_VERSION_DEBUG) |
93 | } |
94 | |
95 | /// Returns whether libcurl was built with SPNEGO authentication |
96 | pub fn feature_spnego(&self) -> bool { |
97 | self.flag(curl_sys::CURL_VERSION_SPNEGO) |
98 | } |
99 | |
100 | /// Returns whether libcurl was built with large file support |
101 | pub fn feature_largefile(&self) -> bool { |
102 | self.flag(curl_sys::CURL_VERSION_LARGEFILE) |
103 | } |
104 | |
105 | /// Returns whether libcurl was built with support for IDNA, domain names |
106 | /// with international letters. |
107 | pub fn feature_idn(&self) -> bool { |
108 | self.flag(curl_sys::CURL_VERSION_IDN) |
109 | } |
110 | |
111 | /// Returns whether libcurl was built with support for SSPI. |
112 | pub fn feature_sspi(&self) -> bool { |
113 | self.flag(curl_sys::CURL_VERSION_SSPI) |
114 | } |
115 | |
116 | /// Returns whether libcurl was built with asynchronous name lookups. |
117 | pub fn feature_async_dns(&self) -> bool { |
118 | self.flag(curl_sys::CURL_VERSION_ASYNCHDNS) |
119 | } |
120 | |
121 | /// Returns whether libcurl was built with support for character |
122 | /// conversions. |
123 | pub fn feature_conv(&self) -> bool { |
124 | self.flag(curl_sys::CURL_VERSION_CONV) |
125 | } |
126 | |
127 | /// Returns whether libcurl was built with support for TLS-SRP. |
128 | pub fn feature_tlsauth_srp(&self) -> bool { |
129 | self.flag(curl_sys::CURL_VERSION_TLSAUTH_SRP) |
130 | } |
131 | |
132 | /// Returns whether libcurl was built with support for NTLM delegation to |
133 | /// winbind helper. |
134 | pub fn feature_ntlm_wb(&self) -> bool { |
135 | self.flag(curl_sys::CURL_VERSION_NTLM_WB) |
136 | } |
137 | |
138 | /// Returns whether libcurl was built with support for unix domain socket |
139 | pub fn feature_unix_domain_socket(&self) -> bool { |
140 | self.flag(curl_sys::CURL_VERSION_UNIX_SOCKETS) |
141 | } |
142 | |
143 | /// Returns whether libcurl was built with support for HTTP2. |
144 | pub fn feature_http2(&self) -> bool { |
145 | self.flag(curl_sys::CURL_VERSION_HTTP2) |
146 | } |
147 | |
148 | /// Returns whether libcurl was built with support for HTTP3. |
149 | pub fn feature_http3(&self) -> bool { |
150 | self.flag(curl_sys::CURL_VERSION_HTTP3) |
151 | } |
152 | |
153 | /// Returns whether libcurl was built with support for Brotli. |
154 | pub fn feature_brotli(&self) -> bool { |
155 | self.flag(curl_sys::CURL_VERSION_BROTLI) |
156 | } |
157 | |
158 | /// Returns whether libcurl was built with support for Alt-Svc. |
159 | pub fn feature_altsvc(&self) -> bool { |
160 | self.flag(curl_sys::CURL_VERSION_ALTSVC) |
161 | } |
162 | |
163 | /// Returns whether libcurl was built with support for zstd |
164 | pub fn feature_zstd(&self) -> bool { |
165 | self.flag(curl_sys::CURL_VERSION_ZSTD) |
166 | } |
167 | |
168 | /// Returns whether libcurl was built with support for unicode |
169 | pub fn feature_unicode(&self) -> bool { |
170 | self.flag(curl_sys::CURL_VERSION_UNICODE) |
171 | } |
172 | |
173 | /// Returns whether libcurl was built with support for hsts |
174 | pub fn feature_hsts(&self) -> bool { |
175 | self.flag(curl_sys::CURL_VERSION_HSTS) |
176 | } |
177 | |
178 | /// Returns whether libcurl was built with support for gsasl |
179 | pub fn feature_gsasl(&self) -> bool { |
180 | self.flag(curl_sys::CURL_VERSION_GSASL) |
181 | } |
182 | |
183 | fn flag(&self, flag: c_int) -> bool { |
184 | unsafe { (*self.inner).features & flag != 0 } |
185 | } |
186 | |
187 | /// Returns the version of OpenSSL that is used, or None if there is no SSL |
188 | /// support. |
189 | pub fn ssl_version(&self) -> Option<&str> { |
190 | unsafe { crate::opt_str((*self.inner).ssl_version) } |
191 | } |
192 | |
193 | /// Returns the version of libz that is used, or None if there is no libz |
194 | /// support. |
195 | pub fn libz_version(&self) -> Option<&str> { |
196 | unsafe { crate::opt_str((*self.inner).libz_version) } |
197 | } |
198 | |
199 | /// Returns an iterator over the list of protocols that this build of |
200 | /// libcurl supports. |
201 | pub fn protocols(&self) -> Protocols { |
202 | unsafe { |
203 | Protocols { |
204 | _inner: self, |
205 | cur: (*self.inner).protocols, |
206 | } |
207 | } |
208 | } |
209 | |
210 | /// If available, the human readable version of ares that libcurl is linked |
211 | /// against. |
212 | pub fn ares_version(&self) -> Option<&str> { |
213 | unsafe { |
214 | if (*self.inner).age >= curl_sys::CURLVERSION_SECOND { |
215 | crate::opt_str((*self.inner).ares) |
216 | } else { |
217 | None |
218 | } |
219 | } |
220 | } |
221 | |
222 | /// If available, the version of ares that libcurl is linked against. |
223 | pub fn ares_version_num(&self) -> Option<u32> { |
224 | unsafe { |
225 | if (*self.inner).age >= curl_sys::CURLVERSION_SECOND { |
226 | Some((*self.inner).ares_num as u32) |
227 | } else { |
228 | None |
229 | } |
230 | } |
231 | } |
232 | |
233 | /// If available, the version of libidn that libcurl is linked against. |
234 | pub fn libidn_version(&self) -> Option<&str> { |
235 | unsafe { |
236 | if (*self.inner).age >= curl_sys::CURLVERSION_THIRD { |
237 | crate::opt_str((*self.inner).libidn) |
238 | } else { |
239 | None |
240 | } |
241 | } |
242 | } |
243 | |
244 | /// If available, the version of iconv libcurl is linked against. |
245 | pub fn iconv_version_num(&self) -> Option<u32> { |
246 | unsafe { |
247 | if (*self.inner).age >= curl_sys::CURLVERSION_FOURTH { |
248 | Some((*self.inner).iconv_ver_num as u32) |
249 | } else { |
250 | None |
251 | } |
252 | } |
253 | } |
254 | |
255 | /// If available, the version of libssh that libcurl is linked against. |
256 | pub fn libssh_version(&self) -> Option<&str> { |
257 | unsafe { |
258 | if (*self.inner).age >= curl_sys::CURLVERSION_FOURTH { |
259 | crate::opt_str((*self.inner).libssh_version) |
260 | } else { |
261 | None |
262 | } |
263 | } |
264 | } |
265 | |
266 | /// If available, the version of brotli libcurl is linked against. |
267 | pub fn brotli_version_num(&self) -> Option<u32> { |
268 | unsafe { |
269 | if (*self.inner).age >= curl_sys::CURLVERSION_FIFTH { |
270 | Some((*self.inner).brotli_ver_num) |
271 | } else { |
272 | None |
273 | } |
274 | } |
275 | } |
276 | |
277 | /// If available, the version of brotli libcurl is linked against. |
278 | pub fn brotli_version(&self) -> Option<&str> { |
279 | unsafe { |
280 | if (*self.inner).age >= curl_sys::CURLVERSION_FIFTH { |
281 | crate::opt_str((*self.inner).brotli_version) |
282 | } else { |
283 | None |
284 | } |
285 | } |
286 | } |
287 | |
288 | /// If available, the version of nghttp2 libcurl is linked against. |
289 | pub fn nghttp2_version_num(&self) -> Option<u32> { |
290 | unsafe { |
291 | if (*self.inner).age >= curl_sys::CURLVERSION_SIXTH { |
292 | Some((*self.inner).nghttp2_ver_num) |
293 | } else { |
294 | None |
295 | } |
296 | } |
297 | } |
298 | |
299 | /// If available, the version of nghttp2 libcurl is linked against. |
300 | pub fn nghttp2_version(&self) -> Option<&str> { |
301 | unsafe { |
302 | if (*self.inner).age >= curl_sys::CURLVERSION_SIXTH { |
303 | crate::opt_str((*self.inner).nghttp2_version) |
304 | } else { |
305 | None |
306 | } |
307 | } |
308 | } |
309 | |
310 | /// If available, the version of quic libcurl is linked against. |
311 | pub fn quic_version(&self) -> Option<&str> { |
312 | unsafe { |
313 | if (*self.inner).age >= curl_sys::CURLVERSION_SIXTH { |
314 | crate::opt_str((*self.inner).quic_version) |
315 | } else { |
316 | None |
317 | } |
318 | } |
319 | } |
320 | |
321 | /// If available, the built-in default of CURLOPT_CAINFO. |
322 | pub fn cainfo(&self) -> Option<&str> { |
323 | unsafe { |
324 | if (*self.inner).age >= curl_sys::CURLVERSION_SEVENTH { |
325 | crate::opt_str((*self.inner).cainfo) |
326 | } else { |
327 | None |
328 | } |
329 | } |
330 | } |
331 | |
332 | /// If available, the built-in default of CURLOPT_CAPATH. |
333 | pub fn capath(&self) -> Option<&str> { |
334 | unsafe { |
335 | if (*self.inner).age >= curl_sys::CURLVERSION_SEVENTH { |
336 | crate::opt_str((*self.inner).capath) |
337 | } else { |
338 | None |
339 | } |
340 | } |
341 | } |
342 | |
343 | /// If avaiable, the numeric zstd version |
344 | /// |
345 | /// Represented as `(MAJOR << 24) | (MINOR << 12) | PATCH` |
346 | pub fn zstd_ver_num(&self) -> Option<u32> { |
347 | unsafe { |
348 | if (*self.inner).age >= curl_sys::CURLVERSION_EIGHTH { |
349 | Some((*self.inner).zstd_ver_num) |
350 | } else { |
351 | None |
352 | } |
353 | } |
354 | } |
355 | |
356 | /// If available, the human readable version of zstd |
357 | pub fn zstd_version(&self) -> Option<&str> { |
358 | unsafe { |
359 | if (*self.inner).age >= curl_sys::CURLVERSION_EIGHTH { |
360 | crate::opt_str((*self.inner).zstd_version) |
361 | } else { |
362 | None |
363 | } |
364 | } |
365 | } |
366 | |
367 | /// If available, the human readable version of hyper |
368 | pub fn hyper_version(&self) -> Option<&str> { |
369 | unsafe { |
370 | if (*self.inner).age >= curl_sys::CURLVERSION_NINTH { |
371 | crate::opt_str((*self.inner).hyper_version) |
372 | } else { |
373 | None |
374 | } |
375 | } |
376 | } |
377 | |
378 | /// If available, the human readable version of hyper |
379 | pub fn gsasl_version(&self) -> Option<&str> { |
380 | unsafe { |
381 | if (*self.inner).age >= curl_sys::CURLVERSION_TENTH { |
382 | crate::opt_str((*self.inner).gsasl_version) |
383 | } else { |
384 | None |
385 | } |
386 | } |
387 | } |
388 | } |
389 | |
390 | impl fmt::Debug for Version { |
391 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
392 | let mut f = f.debug_struct("Version" ); |
393 | f.field("version" , &self.version()) |
394 | .field("rust_crate_version" , &env!("CARGO_PKG_VERSION" )) |
395 | .field("rust_sys_crate_version" , &curl_sys::rust_crate_version()) |
396 | .field("vendored" , &self.vendored()) |
397 | .field("host" , &self.host()) |
398 | .field("feature_ipv6" , &self.feature_ipv6()) |
399 | .field("feature_ssl" , &self.feature_ssl()) |
400 | .field("feature_libz" , &self.feature_libz()) |
401 | .field("feature_ntlm" , &self.feature_ntlm()) |
402 | .field("feature_gss_negotiate" , &self.feature_gss_negotiate()) |
403 | .field("feature_debug" , &self.feature_debug()) |
404 | .field("feature_spnego" , &self.feature_spnego()) |
405 | .field("feature_largefile" , &self.feature_largefile()) |
406 | .field("feature_idn" , &self.feature_idn()) |
407 | .field("feature_sspi" , &self.feature_sspi()) |
408 | .field("feature_async_dns" , &self.feature_async_dns()) |
409 | .field("feature_conv" , &self.feature_conv()) |
410 | .field("feature_tlsauth_srp" , &self.feature_tlsauth_srp()) |
411 | .field("feature_ntlm_wb" , &self.feature_ntlm_wb()) |
412 | .field( |
413 | "feature_unix_domain_socket" , |
414 | &self.feature_unix_domain_socket(), |
415 | ) |
416 | .field("feature_altsvc" , &self.feature_altsvc()) |
417 | .field("feature_zstd" , &self.feature_zstd()) |
418 | .field("feature_unicode" , &self.feature_unicode()) |
419 | .field("feature_http3" , &self.feature_http3()) |
420 | .field("feature_http2" , &self.feature_http2()) |
421 | .field("feature_gsasl" , &self.feature_gsasl()) |
422 | .field("feature_brotli" , &self.feature_brotli()); |
423 | |
424 | if let Some(s) = self.ssl_version() { |
425 | f.field("ssl_version" , &s); |
426 | } |
427 | if let Some(s) = self.libz_version() { |
428 | f.field("libz_version" , &s); |
429 | } |
430 | if let Some(s) = self.ares_version() { |
431 | f.field("ares_version" , &s); |
432 | } |
433 | if let Some(s) = self.libidn_version() { |
434 | f.field("libidn_version" , &s); |
435 | } |
436 | if let Some(s) = self.iconv_version_num() { |
437 | f.field("iconv_version_num" , &format!(" {:x}" , s)); |
438 | } |
439 | if let Some(s) = self.libssh_version() { |
440 | f.field("libssh_version" , &s); |
441 | } |
442 | if let Some(s) = self.brotli_version_num() { |
443 | f.field("brotli_version_num" , &format!(" {:x}" , s)); |
444 | } |
445 | if let Some(s) = self.brotli_version() { |
446 | f.field("brotli_version" , &s); |
447 | } |
448 | if let Some(s) = self.nghttp2_version_num() { |
449 | f.field("nghttp2_version_num" , &format!(" {:x}" , s)); |
450 | } |
451 | if let Some(s) = self.nghttp2_version() { |
452 | f.field("nghttp2_version" , &s); |
453 | } |
454 | if let Some(s) = self.quic_version() { |
455 | f.field("quic_version" , &s); |
456 | } |
457 | if let Some(s) = self.zstd_ver_num() { |
458 | f.field("zstd_ver_num" , &format!(" {:x}" , s)); |
459 | } |
460 | if let Some(s) = self.zstd_version() { |
461 | f.field("zstd_version" , &s); |
462 | } |
463 | if let Some(s) = self.cainfo() { |
464 | f.field("cainfo" , &s); |
465 | } |
466 | if let Some(s) = self.capath() { |
467 | f.field("capath" , &s); |
468 | } |
469 | if let Some(s) = self.hyper_version() { |
470 | f.field("hyper_version" , &s); |
471 | } |
472 | if let Some(s) = self.gsasl_version() { |
473 | f.field("gsasl_version" , &s); |
474 | } |
475 | |
476 | f.field("protocols" , &self.protocols().collect::<Vec<_>>()); |
477 | |
478 | f.finish() |
479 | } |
480 | } |
481 | |
482 | impl<'a> Iterator for Protocols<'a> { |
483 | type Item = &'a str; |
484 | |
485 | fn next(&mut self) -> Option<&'a str> { |
486 | unsafe { |
487 | if (*self.cur).is_null() { |
488 | return None; |
489 | } |
490 | let ret: &str = crate::opt_str(*self.cur).unwrap(); |
491 | self.cur = self.cur.offset(count:1); |
492 | Some(ret) |
493 | } |
494 | } |
495 | } |
496 | |
497 | impl<'a> fmt::Debug for Protocols<'a> { |
498 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
499 | f.debug_list().entries(self.clone()).finish() |
500 | } |
501 | } |
502 | |