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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#![allow(non_camel_case_types)]
#![allow(dead_code)]

use std::marker;
use std::ffi::CStr;
use std::{fmt, ptr, str};
use libc::{c_char, c_int};

use curl_ffi as ffi;

#[allow(missing_copy_implementations)]
pub struct Version { inner: *mut ffi::curl_version_info_data }

impl Version {

    pub fn version_str<'a>(&'a self) -> &'a str {
        as_str(unsafe { (*self.inner).version }).unwrap()
    }

    pub fn version_major(&self) -> u32 {
        (unsafe { (*self.inner).version_num } as u32 & 0xFF0000) >> 16
    }

    pub fn version_minor(&self) -> u32 {
        (unsafe { (*self.inner).version_num } as u32 & 0xFF00) >> 8
    }

    pub fn version_patch(&self) -> u32 {
        (unsafe { (*self.inner).version_num } as u32 & 0xFF)
    }

    pub fn host<'a>(&'a self) -> &'a str {
        as_str(unsafe { (*self.inner).host }).unwrap()
    }

    fn features(&self) -> c_int { unsafe { (*self.inner).features } }

    pub fn is_ipv6_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_IPV6) == ffi::CURL_VERSION_IPV6
    }

    pub fn is_kerbos4_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_KERBEROS4) == ffi::CURL_VERSION_KERBEROS4
    }

    pub fn is_ssl_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_SSL) == ffi::CURL_VERSION_SSL
    }

    pub fn is_libz_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_LIBZ) == ffi::CURL_VERSION_LIBZ
    }

    pub fn is_ntlm_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_NTLM) == ffi::CURL_VERSION_NTLM
    }

    pub fn is_gss_negotiate_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_GSSNEGOTIATE) == ffi::CURL_VERSION_GSSNEGOTIATE
    }

    pub fn is_debug_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_DEBUG) == ffi::CURL_VERSION_DEBUG
    }

    pub fn is_async_dns_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_ASYNCHDNS) == ffi::CURL_VERSION_ASYNCHDNS
    }

    pub fn is_spengo_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_SPNEGO) == ffi::CURL_VERSION_SPNEGO
    }

    pub fn is_large_file_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_LARGEFILE) == ffi::CURL_VERSION_LARGEFILE
    }

    pub fn is_idn_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_IDN) == ffi::CURL_VERSION_IDN
    }

    pub fn is_sspi_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_SSPI) == ffi::CURL_VERSION_SSPI
    }

    pub fn is_conv_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_CONV) == ffi::CURL_VERSION_CONV
    }

    pub fn is_curl_debug_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_CURLDEBUG) == ffi::CURL_VERSION_CURLDEBUG
    }

    pub fn is_tls_auth_srp_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_TLSAUTH_SRP) == ffi::CURL_VERSION_TLSAUTH_SRP
    }

    pub fn is_ntlm_wb_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_NTLM_WB) == ffi::CURL_VERSION_NTLM_WB
    }

    pub fn is_http2_enabled(&self) -> bool {
        (self.features() & ffi::CURL_VERSION_HTTP2) == ffi::CURL_VERSION_HTTP2
    }

    pub fn ssl_version<'a>(&'a self) -> Option<&'a str> {
        as_str(unsafe { (*self.inner).ssl_version })
    }

    pub fn libz_version<'a>(&'a self) -> Option<&'a str> {
        as_str(unsafe { (*self.inner).libz_version })
    }

    pub fn protocols<'a>(&'a self) -> Protocols<'a> {
        Protocols {
            curr: unsafe { (*self.inner).protocols },
            _marker: marker::PhantomData
        }
    }

    pub fn ares_version<'a>(&'a self) -> Option<&'a str> {
        as_str(unsafe { (*self.inner).ares })
    }

    pub fn ares_version_num(&self) -> Option<u32> {
        match self.ares_version() {
            Some(_) => Some(unsafe { (*self.inner).ares_num } as u32),
            None => None
        }
    }

    pub fn idn_version<'a>(&'a self) -> Option<&'a str> {
        if self.is_idn_enabled() {
            as_str(unsafe { (*self.inner).libidn })
        }
        else {
            None
        }
    }

    pub fn iconv_version(self) -> Option<u32> {
        if self.is_conv_enabled() {
            Some(unsafe { (*self.inner).iconv_ver_num } as u32)
        }
        else {
            None
        }
    }

    pub fn ssh_version<'a>(&'a self) -> Option<&'a str> {
        as_str(unsafe { (*self.inner).libssh_version })
    }
}

#[derive(Copy, Clone)]
pub struct Protocols<'a> {
    curr: *const *const c_char,
    _marker: marker::PhantomData<&'a str>,
}

impl<'a> Iterator for Protocols<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        unsafe {
            let proto = *self.curr;

            if proto == ptr::null() {
                return None;
            }

            self.curr = self.curr.offset(1);
            as_str(proto)
        }
    }
}

impl<'a> fmt::Display for Protocols<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut i = self.clone();

        try!(write!(fmt, "["));

        match i.next() {
            Some(proto) => try!(write!(fmt, "{}", proto)),
            None => return write!(fmt, "]")
        }

        for proto in i {
            try!(write!(fmt, ", {}", proto));
        }

        write!(fmt, "]")
    }
}

fn as_str<'a>(p: *const c_char) -> Option<&'a str> {
    if p == ptr::null() {
        return None;
    }

    unsafe {
        str::from_utf8(CStr::from_ptr(p as *const _).to_bytes()).ok()
    }
}

pub fn version_info() -> Version {
    Version {
        inner: unsafe { ffi::curl_version_info(ffi::CURL_VERSION_NOW) },
    }
}

pub fn version() -> &'static str {
    unsafe {
        let version = ffi::curl_version();
        str::from_utf8(CStr::from_ptr(version as *const _).to_bytes()).unwrap()
    }
}