|
|
@ -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 {
|
|
|
|
println!("downloading [{}] complete", selector);
|
|
|
|
eprintln!("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';
|
|
|
@ -318,9 +250,7 @@ impl BrowserState {
|
|
|
|
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,
|
|
|
|
|
|
|
|
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,
|
|
|
|
w,
|
|
|
|
fields[0].to_string(),
|
|
|
|
fields[0].to_string(),
|
|
|
|
fields[2].to_string(),
|
|
|
|
fields[2].to_string(),
|
|
|
|
fields[3].parse().unwrap_or(70), // todo oof
|
|
|
|
fields[3].parse().unwrap_or(70), // todo oof
|
|
|
|
fields[1].to_string(),
|
|
|
|
fields[1].to_string(),
|
|
|
|
);
|
|
|
|
),
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
Some(x) => println!("miss [{}]: {}", x, fields[0]),
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(x) => eprintln!("miss [{}]: {}", x, fields[0]),
|
|
|
|
None => (),
|
|
|
|
None => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -390,42 +327,25 @@ 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;
|
|
|
|
self.clear_links();
|
|
|
|
let mut head_read: libc::c_int = 0;
|
|
|
|
let mut stream = match stream {
|
|
|
|
let mut line: [libc::c_char; 1024] = [0; 1024];
|
|
|
|
Some(s) => s,
|
|
|
|
let mut head: [[libc::c_char; 1024]; 5] = [[0; 1024]; 5];
|
|
|
|
None => return,
|
|
|
|
srvfd = dial(&host, port, selector);
|
|
|
|
};
|
|
|
|
if srvfd != -(1 as libc::c_int) {
|
|
|
|
|
|
|
|
/* only adapt current prompt when successful */
|
|
|
|
/* only adapt current prompt when successful */
|
|
|
|
/* make history entry */
|
|
|
|
/* make history entry */
|
|
|
|
if make_current != 0 {
|
|
|
|
if make_current != 0 {
|
|
|
|
self.add_history();
|
|
|
|
self.add_history();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* don't overwrite the current_* things... */
|
|
|
|
/* don't overwrite the current_* things... */
|
|
|
|
if host
|
|
|
|
if host != self.current_host {
|
|
|
|
!= CString::new(
|
|
|
|
self.current_host = host.to_string();
|
|
|
|
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 */
|
|
|
|
} /* quit if not successful */
|
|
|
|
if port != self.current_port {
|
|
|
|
if port != self.current_port {
|
|
|
|
self.current_port = port;
|
|
|
|
self.current_port = port;
|
|
|
@ -433,74 +353,46 @@ impl BrowserState {
|
|
|
|
if selector != self.current_selector {
|
|
|
|
if selector != self.current_selector {
|
|
|
|
self.current_selector = selector.to_string();
|
|
|
|
self.current_selector = selector.to_string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if let Err(e) = stream.read_to_string(&mut buffer) {
|
|
|
|
|
|
|
|
eprintln!("failed to read response body: {}", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.clear_links();
|
|
|
|
|
|
|
|
if srvfd == -(1 as libc::c_int) {
|
|
|
|
for line in buffer.lines() {
|
|
|
|
return;
|
|
|
|
if !is_valid_directory_entry(line.chars().next().unwrap_or('\0')) {
|
|
|
|
}
|
|
|
|
println!(
|
|
|
|
head_read = 0 as libc::c_int;
|
|
|
|
"error: not a directory: [{}] {}",
|
|
|
|
is_dir = 1 as libc::c_int;
|
|
|
|
line.chars().next().unwrap_or('\0'),
|
|
|
|
while head_read < 5 as libc::c_int
|
|
|
|
line.chars().skip(1).collect::<String>()
|
|
|
|
&& 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
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if is_dir == 0 {
|
|
|
|
|
|
|
|
println!("error: Not a directory");
|
|
|
|
|
|
|
|
close(srvfd);
|
|
|
|
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i = 0 as libc::c_int;
|
|
|
|
|
|
|
|
while i < head_read {
|
|
|
|
self.handle_directory_line(line);
|
|
|
|
self.handle_directory_line(&CString::from_raw(head[i as usize].as_mut_ptr()).into_string().unwrap());
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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() {
|
|
|
|
{
|
|
|
|
|
|
|
|
Ok(mut c) => if let Err(e) = c.wait() {
|
|
|
|
|
|
|
|
eprintln!("failed to wait for command to exit: {}", e);
|
|
|
|
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: ");
|
|
|
|
|
|
|
|
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");
|
|
|
|
println!("search aborted");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
snprintf(
|
|
|
|
s => format!("{}\t{}", selector, s),
|
|
|
|
search_selector.as_mut_ptr(),
|
|
|
|
};
|
|
|
|
mem::size_of::<[libc::c_char; 1024]>() as libc::c_ulong,
|
|
|
|
self.view_directory(host, port, &search_selector, 1 as libc::c_int);
|
|
|
|
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() {
|
|
|
|
|
|
|
|
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(
|
|
|
|
pub unsafe fn read_line(
|
|
|
@ -1117,26 +907,21 @@ 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()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
Ok(mut c) => if let Err(e) = c.wait() {
|
|
|
|
|
|
|
|
eprintln!("failed to wait for telnet: {}", e);
|
|
|
|
eprintln!("failed to wait for telnet: {}", e);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Err(e) => eprintln!("failed to start telnet: {}", e),
|
|
|
|
Err(e) => eprintln!("failed to start telnet: {}", e),
|
|
|
|