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

Loading…
Cancel
Save