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 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
crate::ix!();
pub fn callrpc(
rh: &mut Box<dyn BaseRequestHandler>,
str_method: &str,
args: &Vec<String>,
rpcwallet: Option<&str>) -> Result<UniValue,StdException> {
todo!("in order to uncomment this, need a proper libevent-sys with http headers included (among others) -- see github: https://github.com/jmagnuson/libevent-rs/pull/19");
/*
let mut host = String::default();
// In preference order, we choose the following for the port:
// 1. -rpcport
// 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
// 3. default port for chain
let mut port: u16 = base_params().rpc_port();
split_host_port(
G_ARGS
.lock()
//.unwrap()
.get_arg("-rpcconnect", DEFAULT_RPCCONNECT),
&mut port,
&mut host
);
port
= G_ARGS
.lock()
//.unwrap()
.get_int_arg("-rpcport", port.into()) as u16;
// Obtain event base
let mut base: Box<event_base> = obtain_event_base();;
let base_raw = Box::into_raw(base);
// Synchronously look up hostname
let mut evcon: Box<evhttp_connection>
= obtain_evhttp_connection_base(
base_raw,
&host,
port
);
let evcon_raw = Box::into_raw(evcon);
pub const YEAR_IN_SECONDS: i32 = 0;
// Set connection timeout
{
let timeout: i32
= G_ARGS
.lock()
//.unwrap()
.get_int_arg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT.into())
.try_into()
.unwrap();
if timeout > 0 {
unsafe {
evhttp_connection_set_timeout(
evcon_raw,
timeout
);
}
} else {
// Indefinite request timeouts are
// not possible in libevent-http, so we set
// the timeout to a very long time period
// instead.
{
// Average length of year in Gregorian calendar
pub const YEAR_IN_SECONDS: i32 = 31556952;
unsafe {
evhttp_connection_set_timeout(
evcon_raw,
5 * YEAR_IN_SECONDS
);
}
}
}
}
let mut response = HTTPReply::default();
let req: Box<evhttp_request>
= obtain_evhttp_request(
http_request_done,
&mut response as *mut _ as *mut libc::c_void
);
let raw_req = Box::into_raw(req);
if raw_req == std::ptr::null_mut() {
return Err(runtime_error("create http request failed"));
}
#[cfg(LIBEVENT_VERSION_NUMBER_GTE_0x02010300)]
evhttp_request_set_error_cb(req, http_error_cb);
// Get credentials
let mut str_rpc_user_colon_pass = String::default();;
let mut failed_to_get_auth_cookie: bool = false;
if G_ARGS
.lock()
//.unwrap()
.get_arg("-rpcpassword", "") == "" {
// Try fall back to cookie-based
// authentication if no password is
// provided
if !get_auth_cookie(&mut str_rpc_user_colon_pass) {
failed_to_get_auth_cookie = true;
}
} else {
str_rpc_user_colon_pass
= format!{
"{}:{}",
G_ARGS
.lock()
//.unwrap()
.get_arg("-rpcuser", ""),
G_ARGS
.lock()
//.unwrap()
.get_arg("-rpcpassword", "")
};
}
let output_headers: *mut evkeyvalq
= unsafe {
evhttp_request_get_output_headers(raw_req)
};
assert!(output_headers != null_mut());
unsafe {
evhttp_add_header(
output_headers,
"Host".as_ptr() as *const i8,
host.as_ptr() as *const i8
);
unsafe {
evhttp_add_header(
output_headers,
"Connection".as_ptr() as *const i8,
"close".as_ptr() as *const i8
);
evhttp_add_header(
output_headers,
"Content-Type".as_ptr() as *const i8,
"application/json".as_ptr() as *const i8
);
}
}
let authorization_msg
= format!(
"Basic {}",
encode_base64(&str_rpc_user_colon_pass)
);
unsafe {
evhttp_add_header(
output_headers,
"Authorization".as_ptr() as *const i8,
authorization_msg.as_ptr() as *const i8
);
}
// Attach request data
let str_request: String
= unsafe {
(*rh)
.prepare_request(str_method, args)
.unwrap()
.write(None, None) + "\n"
};
let output_buffer: *mut evbuffer = unsafe {
evhttp_request_get_output_buffer(raw_req)
};
assert!(output_buffer != null_mut());
unsafe {
evbuffer_add(
output_buffer,
str_request.as_ptr() as *const c_void,
str_request.len().try_into().unwrap()
);
}
// check if we should use a special wallet
// endpoint
let mut endpoint: String = "/".to_string();
if rpcwallet.is_some() {
//TODO: this may not be correct
//because rust strings use unicode
let encoded_uri: *mut i8 = unsafe {
evhttp_uriencode(
rpcwallet.unwrap().as_ptr() as *const i8,
rpcwallet.unwrap().len().try_into().unwrap(),
0 /*false*/
)
};
if encoded_uri != null_mut() {
//TODO: may want a better way to print
//encoded_uri, which is just a ptr
endpoint = format!("/wallet/{:?}", encoded_uri);
unsafe {
libc::free(encoded_uri as *mut c_void);
}
} else {
return Err(connection_failed("uri-encode failed"));
}
}
// ownership of raw_req moved to evcon in the
// evhttp_make_request call
let r: i32 = unsafe {
evhttp_make_request(
evcon_raw,
raw_req,
evhttp_cmd_type_EVHTTP_REQ_POST,
endpoint.as_ptr() as *const i8
)
};
if r != 0 {
return Err(connection_failed("send http request failed"));
}
unsafe {
event_base_dispatch(base_raw);
}
if response.status == 0 {
let mut response_error_message = String::default();
if response.error != -1 {
response_error_message
= format!(
" (error code {} - \"{}\")",
response.error,
http_errorstring(response.error)
);
}
let msg = formatdoc!(
"Could not connect to the server {}:{}{}\n\n
Make sure the bitcoind server is running and
that you are connecting to the correct RPC port.",
host,
port,
response_error_message
);
return Err(connection_failed(&msg));
} else {
if response.status
== HTTPStatusCode::HTTP_UNAUTHORIZED as i32 {
if failed_to_get_auth_cookie {
let conf = G_ARGS
.lock()
//.unwrap()
.get_arg("-conf", BITCOIN_CONF_FILENAME);
let msg = formatdoc!(
"Could not locate RPC credentials.
No authentication cookie could be found, and
RPC password is not set.
See -rpcpassword and -stdinrpcpass.
Configuration file: ({})",
get_config_file(&conf).to_str().unwrap()
);
return Err(runtime_error(&msg));
} else {
let msg = "Authorization failed: Incorrect rpcuser or rpcpassword";
return Err(runtime_error(&msg));
}
} else {
if response.status == HTTPStatusCode::HTTP_SERVICE_UNAVAILABLE as i32 {
return Err(runtime_error(&format!("Server response: {}",response.body)));
} else {
let code = response.status;
if code >= 400
&& code != HTTPStatusCode::HTTP_BAD_REQUEST as i32
&& code != HTTPStatusCode::HTTP_NOT_FOUND as i32
&& code != HTTPStatusCode::HTTP_INTERNAL_SERVER_ERROR as i32 {
return Err(runtime_error(&format!("server returned HTTP error {}",response.status)));
} else {
if response.body.is_empty() {
return Err(runtime_error("no response from server"));
}
}
}
}
}
// Parse reply
let mut val_reply: UniValue = UniValue::new(uni_value::VType::VSTR, None);
if !val_reply.read(response.body.as_ptr(), response.body.len()) {
return Err(runtime_error("couldn't parse reply from server"));
}
if let Ok(reply) = unsafe { (*rh).process_reply(&val_reply) } {
Ok(reply)
} else {
let msg = "expected reply to have result, error and id properties";
return Err(runtime_error(msg));
}
*/
}