remove more C stuff, networking is now Rust

master
Lukáš Hozda 4 years ago
parent c6584dad87
commit 131bbcd27c

@ -22,75 +22,20 @@ use std::time::Duration;
use std::ffi::{CStr, CString};
use std::fs::{self, File};
use std::mem;
use std::io::{Write, BufReader, BufRead};
use std::io::{Write, BufReader, BufRead, Read, self};
use std::net::TcpStream;
use simple_input::input;
use tempfile::NamedTempFile;
extern "C" {
#[no_mangle]
fn socket(
__domain: libc::c_int,
__type: libc::c_int,
__protocol: libc::c_int,
) -> libc::c_int;
#[no_mangle]
fn connect(
__fd: libc::c_int,
__addr: *const sockaddr,
__len: socklen_t,
) -> libc::c_int;
#[no_mangle]
fn getaddrinfo(
__name: *const libc::c_char,
__service: *const libc::c_char,
__req: *const addrinfo,
__pai: *mut *mut addrinfo,
) -> libc::c_int;
#[no_mangle]
fn open(__file: *const libc::c_char, __oflag: libc::c_int, _: ...) -> libc::c_int;
#[no_mangle]
fn close(__fd: libc::c_int) -> libc::c_int;
#[no_mangle]
fn read(__fd: libc::c_int, __buf: *mut libc::c_void, __nbytes: size_t) -> ssize_t;
#[no_mangle]
fn write(__fd: libc::c_int, __buf: *const libc::c_void, __n: size_t) -> ssize_t;
#[no_mangle]
fn __errno_location() -> *mut libc::c_int;
#[no_mangle]
fn snprintf(
_: *mut libc::c_char,
_: libc::c_ulong,
_: *const libc::c_char,
_: ...
) -> libc::c_int;
#[no_mangle]
fn memset(
_: *mut libc::c_void,
_: libc::c_int,
_: libc::c_ulong,
) -> *mut libc::c_void;
#[no_mangle]
fn strcpy(_: *mut libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
#[no_mangle]
fn strcmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
#[no_mangle]
fn strncmp(
_: *const libc::c_char,
_: *const libc::c_char,
_: libc::c_ulong,
) -> libc::c_int;
#[no_mangle]
fn strlen(_: *const libc::c_char) -> libc::c_ulong;
#[no_mangle]
fn strerror(_: libc::c_int) -> *mut libc::c_char;
}
pub type __off_t = libc::c_long;
pub type __off64_t = libc::c_long;
pub type __pid_t = libc::c_int;
pub type __ssize_t = libc::c_long;
pub type __socklen_t = libc::c_uint;
pub type pid_t = __pid_t;
pub type ssize_t = __ssize_t;
pub type size_t = libc::c_ulong;
pub type socklen_t = __socklen_t;
@ -131,7 +76,7 @@ pub struct Link {
pub which: Option<char>,
pub key: usize,
pub host: String,
pub port: usize,
pub port: u16,
pub selector: String,
}
#[derive(Clone, Debug)]
@ -152,11 +97,11 @@ pub struct BrowserState {
pub links: Option<Box<Link>>,
pub history: Option<Box<Link>>,
pub link_key: usize,
pub current_host: [libc::c_char; 512],
pub current_port: usize,
pub current_host: String,
pub current_port: u16,
pub current_selector: String,
pub parsed_host: String,
pub parsed_port: usize,
pub parsed_port: u16,
pub parsed_selector: String,
pub bookmarks: Vec<String>,
pub config: Config,
@ -169,7 +114,7 @@ impl BrowserState {
links: None,
history: None,
link_key: 0,
current_host: [0; 512],
current_host: String::new(),
current_port: 0,
current_selector: String::new(),
parsed_host: String::new(),
@ -206,7 +151,7 @@ impl BrowserState {
x => {
eprintln!("invalid key in config: {}", x);
exit(1)
},
}
}
}
@ -217,9 +162,10 @@ impl BrowserState {
return;
}
};
file.lines().filter_map(|x| x.ok()).filter(|x| !x.starts_with('#')).for_each(|line| {
self.parse_config_line(&line)
})
file.lines()
.filter_map(|x| x.ok())
.filter(|x| !x.starts_with('#'))
.for_each(|line| self.parse_config_line(&line))
}
pub unsafe fn init_config(&mut self) {
@ -233,43 +179,29 @@ impl BrowserState {
pub unsafe fn download_file(
&mut self,
mut host: &str,
mut port: usize,
mut port: u16,
mut selector: &str,
mut file: &mut File,
) -> bool {
let mut srvfd: libc::c_int = 0;
let mut len: usize = 0;
let mut total: usize = 0;
let mut buffer: [u8; 4096] = [0; 4096];
if self.config.verbose
{
println!("downloading [{}]...", selector);
if self.config.verbose {
eprintln!("downloading [{}]...", selector);
}
srvfd = dial(&host, port, selector);
if srvfd == -(1 as libc::c_int) {
println!("error: downloading [{}] failed", selector);
let mut stream = dial(&host, port, selector);
if stream.is_none() {
eprintln!("error: downloading [{}] failed", selector);
return false;
}
loop {
len = read(
srvfd,
buffer.as_mut_ptr() as *mut libc::c_void,
mem::size_of::<[libc::c_char; 4096]>() as libc::c_ulong,
) as usize;
if !(len > 0) {
break;
}
file.write(&buffer[..len]).expect("failed to write to file");
total += len;
if self.config.verbose
{
println!("downloading [{}] ({} kb)...", selector, total / 1024);
// todo show progress
match io::copy(&mut stream.unwrap(), file) {
Ok(b) => eprintln!("downloaded {} bytes", b),
Err(e) => {
eprintln!("error: failed to download file: {}", e);
return false;
}
}
close(srvfd);
if self.config.verbose
{
println!("downloading [{}] complete", selector);
};
if self.config.verbose {
eprintln!("downloading [{}] complete", selector);
}
true
}
@ -277,7 +209,7 @@ impl BrowserState {
pub unsafe fn download_temp(
&mut self,
mut host: &str,
mut port: usize,
mut port: u16,
mut selector: &str,
) -> bool {
let mut tmpfile = match NamedTempFile::new() {
@ -300,7 +232,7 @@ impl BrowserState {
mut which: char,
mut name: String,
mut host: String,
mut port: usize,
mut port: u16,
mut selector: String,
) {
let mut a: char = '\0';
@ -310,17 +242,15 @@ impl BrowserState {
return;
}
let mut link = Link {
which: Some(which as u8 as char),
key: self.link_key,
which: Some(which as u8 as char),
key: self.link_key,
host,
port,
selector,
next: if self.links.is_none() { None } else { self.links.clone() },
next: if self.links.is_none() { None } else { self.links.clone() },
};
println!("links: {:?}", self.links);
self.links = Some(Box::new(link));
println!("links post: {:?}", self.links);
let fresh4 = self.link_key;
self.link_key += 1;
make_key_str(fresh4, &mut a, &mut b, &mut c);
@ -336,16 +266,7 @@ impl BrowserState {
let mut link: Link = Link {
which: None,
key: 0,
host: CString::new(
self.current_host
.iter()
.take_while(|x| **x != 0)
.map(|x| *x as u8)
.collect::<Vec<_>>(),
)
.unwrap()
.into_string()
.unwrap(),
host: self.current_host.clone(),
port: self.current_port,
selector: self.current_selector.clone(),
next: if self.history.is_none() { None } else { self.history.clone() },
@ -363,26 +284,42 @@ impl BrowserState {
match line.chars().next() {
Some('i') | Some('3') => println!(" {}", fields[0]),
Some('.') => println!("\0"), // some gopher servers use this
Some(w @ '0')
| Some(w @ '1')
| Some(w @ '5')
| Some(w @ '7')
| Some(w @ '8')
| Some(w @ '9')
| Some(w @ 'g')
| Some(w @ 'I')
| Some(w @ 'p')
| Some(w @ 'h')
| Some(w @ 's') => {
self.add_link(
w,
fields[0].to_string(),
fields[2].to_string(),
fields[3].parse().unwrap_or(70), // todo oof
fields[1].to_string(),
);
},
Some(x) => println!("miss [{}]: {}", x, fields[0]),
Some(w @ '0') | Some(w @ '1') | Some(w @ '5') | Some(w @ '7')
| Some(w @ '8') | Some(w @ '9') | Some(w @ 'g') | Some(w @ 'I')
| Some(w @ 'p') | Some(w @ 'h') | Some(w @ 's') => {
match fields.len() {
1 => self.add_link(
w,
fields[0].to_string(),
self.current_host.clone(),
self.current_port,
fields[0].to_string(),
),
2 => self.add_link(
w,
fields[0].to_string(),
self.current_host.clone(),
self.current_port,
fields[1].to_string(),
),
3 => self.add_link(
w,
fields[0].to_string(),
fields[2].to_string(),
self.current_port,
fields[1].to_string(),
),
x if x >= 4 => self.add_link(
w,
fields[0].to_string(),
fields[2].to_string(),
fields[3].parse().unwrap_or(70), // todo oof
fields[1].to_string(),
),
_ => (),
}
}
Some(x) => eprintln!("miss [{}]: {}", x, fields[0]),
None => (),
}
}
@ -390,119 +327,74 @@ impl BrowserState {
pub unsafe fn view_directory(
&mut self,
mut host: &str,
mut port: usize,
mut port: u16,
mut selector: &str,
mut make_current: libc::c_int,
) {
let mut is_dir: libc::c_int = 0;
let mut srvfd: libc::c_int = 0;
let mut i: libc::c_int = 0;
let mut head_read: libc::c_int = 0;
let mut line: [libc::c_char; 1024] = [0; 1024];
let mut head: [[libc::c_char; 1024]; 5] = [[0; 1024]; 5];
srvfd = dial(&host, port, selector);
if srvfd != -(1 as libc::c_int) {
/* only adapt current prompt when successful */
/* make history entry */
if make_current != 0 {
self.add_history();
}
/* don't overwrite the current_* things... */
if host
!= CString::new(
self.current_host
.iter()
.take_while(|x| **x != 0)
.map(|x| *x as u8)
.collect::<Vec<_>>(),
)
.unwrap()
.into_string()
.unwrap()
{
snprintf(
self.current_host.as_mut_ptr(),
mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong,
b"%s\x00" as *const u8 as *const libc::c_char,
host,
); /* clear links *AFTER* dialing out!! */
} /* quit if not successful */
if port != self.current_port {
self.current_port = port;
}
if selector != self.current_selector {
self.current_selector = selector.to_string();
}
}
let stream = dial(&host, port, selector);
let mut buffer = String::new();
self.clear_links();
if srvfd == -(1 as libc::c_int) {
return;
let mut stream = match stream {
Some(s) => s,
None => return,
};
/* only adapt current prompt when successful */
/* make history entry */
if make_current != 0 {
self.add_history();
}
head_read = 0 as libc::c_int;
is_dir = 1 as libc::c_int;
while head_read < 5 as libc::c_int
&& read_line(
srvfd,
line.as_mut_ptr(),
mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
) != 0
{
strcpy(head[head_read as usize].as_mut_ptr(), line.as_mut_ptr());
if is_valid_directory_entry(head[head_read as usize].as_mut_ptr()) == 0 {
is_dir = 0 as libc::c_int;
break;
} else {
head_read += 1
}
/* don't overwrite the current_* things... */
if host != self.current_host {
self.current_host = host.to_string();
} /* quit if not successful */
if port != self.current_port {
self.current_port = port;
}
if is_dir == 0 {
println!("error: Not a directory");
close(srvfd);
return;
if selector != self.current_selector {
self.current_selector = selector.to_string();
}
i = 0 as libc::c_int;
while i < head_read {
self.handle_directory_line(&CString::from_raw(head[i as usize].as_mut_ptr()).into_string().unwrap());
i += 1;
if let Err(e) = stream.read_to_string(&mut buffer) {
eprintln!("failed to read response body: {}", e);
}
while read_line(
srvfd,
line.as_mut_ptr(),
mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
) != 0
{
self.handle_directory_line(&CString::from_raw(line.as_mut_ptr()).into_string().unwrap());
for line in buffer.lines() {
if !is_valid_directory_entry(line.chars().next().unwrap_or('\0')) {
println!(
"error: not a directory: [{}] {}",
line.chars().next().unwrap_or('\0'),
line.chars().skip(1).collect::<String>()
);
return;
}
self.handle_directory_line(line);
}
close(srvfd);
}
pub unsafe fn view_file(
&mut self,
mut cmd: &str,
mut host: &str,
mut port: usize,
mut port: u16,
mut selector: &str,
) {
if !self.download_temp(host, port, selector) {
return;
}
if self.config.verbose
{
if self.config.verbose {
println!("h({}) p({}) s({})", host, port, selector);
}
if self.config.verbose
{
if self.config.verbose {
println!("executing: {} {}", cmd, self.tmpfilename);
}
/* execute */
match Command::new(cmd)
.arg(&self.tmpfilename)
.spawn()
{
Ok(mut c) => if let Err(e) = c.wait() {
eprintln!("failed to wait for command to exit: {}", e);
},
match Command::new(cmd).arg(&self.tmpfilename).spawn() {
Ok(mut c) =>
if let Err(e) = c.wait() {
eprintln!("failed to wait for command to exit: {}", e);
},
Err(e) => eprintln!("error: failed to run command: {}", e),
}
@ -514,7 +406,7 @@ impl BrowserState {
pub unsafe fn view_download(
&mut self,
mut host: &str,
mut port: usize,
mut port: u16,
mut selector: &str,
) {
let mut filename: String =
@ -534,14 +426,8 @@ impl BrowserState {
filename = line; // TODO something stinky going on here
let mut file = match File::create(&filename) {
Ok(f) => f,
Err(_) => {
println!(
"error: unable to create file [{}]: {}",
filename,
CString::from_raw(strerror(*__errno_location()))
.into_string()
.unwrap()
);
Err(e) => {
println!("error: unable to create file [{}]: {}", filename, e,);
return;
}
};
@ -555,34 +441,17 @@ impl BrowserState {
pub unsafe fn view_search(
&mut self,
mut host: &str,
mut port: usize,
mut port: u16,
mut selector: &str,
) {
let mut search_selector: [libc::c_char; 1024] = [0; 1024];
let mut line: [libc::c_char; 1024] = [0; 1024];
println!("enter search string: ");
if read_line(
0 as libc::c_int,
line.as_mut_ptr(),
mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
) == 0
{
println!("search aborted");
return;
}
snprintf(
search_selector.as_mut_ptr(),
mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
b"%s\t%s\x00" as *const u8 as *const libc::c_char,
selector,
line.as_mut_ptr(),
);
self.view_directory(
host,
port,
&CString::from_raw(search_selector.as_mut_ptr()).into_string().unwrap(),
1 as libc::c_int,
);
let search_selector: String = match input("enter search string: ").as_str() {
"" => {
println!("search aborted");
return;
}
s => format!("{}\t{}", selector, s),
};
self.view_directory(host, port, &search_selector, 1 as libc::c_int);
}
pub unsafe fn view_history(&mut self, mut key: Option<usize>) {
@ -635,13 +504,7 @@ impl BrowserState {
i = 0;
for bookmark in &self.bookmarks {
make_key_str(i, &mut a, &mut b, &mut c);
println!(
"{}{}{} {}",
a,
b,
c,
bookmark
);
println!("{}{}{} {}", a, b, c, bookmark);
}
} else if let Some(key) = key {
for (i, bookmark) in self.bookmarks.clone().iter().enumerate() {
@ -654,10 +517,7 @@ impl BrowserState {
0 as libc::c_int,
);
} else {
println!(
"invalid gopher URI: {}",
bookmark,
);
println!("invalid gopher URI: {}", bookmark,);
}
return;
}
@ -854,18 +714,7 @@ impl BrowserState {
// todo color prompt
println!(
"{}:{}{} ",
CString::new(
self.current_host
.iter()
.take_while(|x| **x != 0)
.map(|x| *x as u8)
.collect::<Vec<_>>()
)
.unwrap()
.into_string()
.unwrap(),
self.current_port,
&self.current_selector
self.current_host, self.current_port, &self.current_selector
);
if read_line(
0 as libc::c_int,
@ -886,18 +735,8 @@ impl BrowserState {
}
42 => {
self.view_directory(
&CString::new(
self.current_host
.clone()
.iter()
.take_while(|x| **x != 0)
.map(|x| *x as u8)
.collect::<Vec<_>>(),
)
.unwrap()
.into_string()
.unwrap(),
self.current_port.clone(),
&self.current_host.clone(),
self.current_port,
&self.current_selector.clone(),
0 as libc::c_int,
);
@ -968,74 +807,25 @@ pub fn banner(to_error: bool) {
}
}
pub unsafe fn dial(mut host: &str, mut port: usize, mut selector: &str) -> libc::c_int {
let mut hints: addrinfo = addrinfo {
ai_flags: 0,
ai_family: 0,
ai_socktype: 0,
ai_protocol: 0,
ai_addrlen: 0,
ai_addr: 0 as *mut sockaddr,
ai_canonname: 0 as *mut libc::c_char,
ai_next: 0 as *mut addrinfo,
};
let mut res: *mut addrinfo = 0 as *mut addrinfo;
let mut r: *mut addrinfo = 0 as *mut addrinfo;
let mut srv: libc::c_int = -(1 as libc::c_int);
let mut l: libc::c_int = 0;
let mut request: [libc::c_char; 512] = [0; 512];
memset(
&mut hints as *mut addrinfo as *mut libc::c_void,
0 as libc::c_int,
mem::size_of::<addrinfo>() as libc::c_ulong,
);
hints.ai_family = 0 as libc::c_int;
hints.ai_socktype = SOCK_STREAM as libc::c_int;
if getaddrinfo(
CString::new(host).unwrap().into_raw(),
CString::new(port.to_string()).unwrap().into_raw(),
&mut hints,
&mut res,
) != 0 as libc::c_int
{
eprintln!(
"error: cannot resolve hostname '{}:{}': {}",
host,
port,
CStr::from_ptr(strerror(*__errno_location())).to_str().unwrap()
);
return -(1 as libc::c_int);
}
r = res;
while !r.is_null() {
srv = socket((*r).ai_family, (*r).ai_socktype, (*r).ai_protocol);
if !(srv == -(1 as libc::c_int)) {
if connect(srv, (*r).ai_addr, (*r).ai_addrlen) == 0 as libc::c_int {
break;
}
close(srv);
pub unsafe fn dial(
mut host: &str,
mut port: u16,
mut selector: &str,
) -> Option<TcpStream> {
let mut stream = match TcpStream::connect((host, port)) {
Ok(s) => s,
Err(e) => {
eprintln!("failed to dial server: {}", e);
return None;
}
r = (*r).ai_next
}
if r.is_null() {
eprintln!("error: cannot connect to host '{}:{}'", host, port);
return -(1 as libc::c_int);
}
snprintf(
request.as_mut_ptr(),
mem::size_of::<[libc::c_char; 512]>() as libc::c_ulong,
b"%s\r\n\x00" as *const u8 as *const libc::c_char,
selector,
);
l = strlen(request.as_mut_ptr()) as libc::c_int;
if write(srv, request.as_mut_ptr() as *const libc::c_void, l as size_t)
!= l as libc::c_long
{
eprintln!("error: cannot complete request");
close(srv);
return -(1 as libc::c_int);
}
return srv;
};
if let Err(e) = writeln!(stream, "{}", selector) {
eprintln!("failed to send request to server");
return None;
};
Some(stream)
}
pub unsafe fn read_line(
@ -1117,28 +907,23 @@ pub unsafe fn make_key_str(
};
}
pub unsafe fn is_valid_directory_entry(mut line: *const libc::c_char) -> libc::c_int {
match *line.offset(0 as libc::c_int as isize) as libc::c_int {
105 | 51 | 46 | 48 | 49 | 53 | 55 | 56 | 57 | 103 | 73 | 112 | 104 | 115 => {
/* some gopher servers use this */
return 1 as libc::c_int;
}
_ => return 0 as libc::c_int,
};
pub unsafe fn is_valid_directory_entry(kind: char) -> bool {
match kind {
'i' | '3' | '.' | '0' | '1' | '5' | '7' | '8' | '9' | 'g' | 'I' | 'p' | 'h'
| 's' => true,
_ => false,
}
}
pub unsafe fn view_telnet(mut host: &str, mut port: usize) {
pub unsafe fn view_telnet(mut host: &str, mut port: u16) {
println!("executing: telnet {} {}", host, port);
// TODO check stdio
match Command::new("telnet")
.arg(host)
.arg(port.to_string())
.spawn()
{
Ok(mut c) => if let Err(e) = c.wait() {
eprintln!("failed to wait for telnet: {}", e);
},
match Command::new("telnet").arg(host).arg(port.to_string()).spawn() {
Ok(mut c) =>
if let Err(e) = c.wait() {
eprintln!("failed to wait for telnet: {}", e);
},
Err(e) => eprintln!("failed to start telnet: {}", e),
}
println!("(done)");

Loading…
Cancel
Save