1use std::ffi::CStr;
2use std::fmt;
3use std::str;
4
5use libc::{c_char, c_int};
6
7/// Version information about libcurl and the capabilities that it supports.
8pub struct Version {
9 inner: *mut curl_sys::curl_version_info_data,
10}
11
12unsafe impl Send for Version {}
13unsafe impl Sync for Version {}
14
15/// An iterator over the list of protocols a version supports.
16#[derive(Clone)]
17pub struct Protocols<'a> {
18 cur: *const *const c_char,
19 _inner: &'a Version,
20}
21
22impl 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
390impl 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
482impl<'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
497impl<'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