modified the install to avoid the newlines
in folder names added hash cracking function, though this doesn't work yet...
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
use core::hash;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
@@ -7,6 +8,7 @@ use std::path::PathBuf;
|
|||||||
use std::process;
|
use std::process;
|
||||||
use std::result;
|
use std::result;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::thread::spawn;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::io::stdin;
|
use std::io::stdin;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
@@ -460,4 +462,127 @@ pub fn password_spray_help(project: &Project, season: String, lseason: String, y
|
|||||||
sink.sleep_until_end();
|
sink.sleep_until_end();
|
||||||
clear().unwrap();
|
clear().unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn crack_hashes(cracking_rig: &String, project: &Project, terminal: &String, rockyou: &String, rule: &String){
|
||||||
|
let mut hash_file = String::new();
|
||||||
|
println!("trying to automatically find hashes.txt file...");
|
||||||
|
let find_result = find_file(&project.files_folder, "hashes.txt");
|
||||||
|
if find_result.is_some(){
|
||||||
|
hash_file = find_result.unwrap();
|
||||||
|
println!("hash file found!");
|
||||||
|
let mut res = String::new();
|
||||||
|
println!("is {} the file you want to crack?", hash_file);
|
||||||
|
match stdin().read_line(&mut res){
|
||||||
|
Ok(_r) => (),
|
||||||
|
Err(_e) => {println!("we need input here dummy! returning..."); return;}
|
||||||
|
}
|
||||||
|
res = res.to_lowercase();
|
||||||
|
if res.contains("n"){
|
||||||
|
println!("ooof ok, where is the file you want then?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut hash_file){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummy! try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if res.contains("y"){
|
||||||
|
println!("nice! checking for cracking directory...");
|
||||||
|
let listing_res = process::Command::new("ssh").arg(&cracking_rig).arg("'ls ~'").output();
|
||||||
|
if listing_res.is_err(){
|
||||||
|
println!("Error checking for cracking directory!");
|
||||||
|
println!("Error: {}", listing_res.unwrap_err());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
let listing_stdout = listing_res.unwrap().stdout;
|
||||||
|
let listing = String::from_utf8_lossy(&listing_stdout);
|
||||||
|
if listing.contains("hash_cracking") == false{
|
||||||
|
println!("no folder found, creating it...");
|
||||||
|
let mkdir = process::Command::new("ssh").arg(&cracking_rig).arg("'mkdir ~/hash_cracking'").status();
|
||||||
|
if mkdir.is_err(){
|
||||||
|
println!("error creating directory! try again...");
|
||||||
|
println!("Error: {}", mkdir.unwrap_err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let scp_arg = format!("'scp {} {}:~/hash_cracking/{}'", &hash_file, cracking_rig, &hash_file);
|
||||||
|
let scp_res = process::Command::new("ssh").arg(&cracking_rig).arg(scp_arg).status();
|
||||||
|
if scp_res.is_err(){
|
||||||
|
println!("error uploading hashes file!");
|
||||||
|
println!("Error: {}", scp_res.unwrap_err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
println!("nice, hach file uploaded! determining correct terminal command...");
|
||||||
|
let terminal = terminal.split(" ").collect::<Vec<&str>>()[0];
|
||||||
|
process::Command::new(terminal).arg("-e").arg("ssh").arg(&cracking_rig);
|
||||||
|
let mut hash_type_res = String::new();
|
||||||
|
let mut cracking_arg = String::new();
|
||||||
|
let mut crack_box = String::new();
|
||||||
|
let mut crack_box_res = String::new();
|
||||||
|
println!("do you use a distrobox to crack passwords?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut crack_box_res){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummy, try again")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crack_box_res = crack_box_res.to_lowercase();
|
||||||
|
if crack_box_res.contains("y"){
|
||||||
|
println!("What's the distrobox's name?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut crack_box){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummy, try again!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
crack_box = "no".to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
if crack_box == "no".to_owned(){
|
||||||
|
cracking_arg = format!("sudo hashcat -m ||MODE|| -a 0 -r {} /home/{}/hash_cracking/{} {}", rule, cracking_rig.split("@").collect::<Vec<&str>>()[1], &hash_file, &rockyou);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
cracking_arg = format!("distrobox enter --root {} -- sudo -S hashcat -m ||MODE|| -a 0 -r {} /home/{}/hash_cracking/{} {}", crack_box, rule, cracking_rig.split("@").collect::<Vec<&str>>()[1], &hash_file, &rockyou);
|
||||||
|
}
|
||||||
|
loop{
|
||||||
|
println!("Hash type?");
|
||||||
|
print!("
|
||||||
|
1.) kerberos
|
||||||
|
2.) ntlm
|
||||||
|
3.) other
|
||||||
|
");
|
||||||
|
let _res = stdin().read_line(&mut hash_type_res);
|
||||||
|
if _res.is_err(){
|
||||||
|
println!("we need input here dummy! try again...");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
match hash_type_res.as_str(){
|
||||||
|
"1\n" => {cracking_arg = cracking_arg.replace("||MODE||", "19700"); break;},
|
||||||
|
"2\n" => {cracking_arg = cracking_arg.replace("||MODE||", "1000"); break;},
|
||||||
|
"3\n" => {let mut mode = String::new();
|
||||||
|
println!("code for the mode you want to use?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut mode){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummy, try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cracking_arg = cracking_arg.replace("||MODE||", &mode);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
_ => println!("unknown selection... try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let spaw_res = process::Command::new(terminal).arg("-e").arg("ssh").arg(&cracking_rig).arg(cracking_arg).spawn();
|
||||||
|
if spaw_res.is_err(){
|
||||||
|
println!("error spawing new terminal to ssh with!");
|
||||||
|
println!("ERROR: {}", spaw_res.unwrap_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ use std::process;
|
|||||||
use std::process::exit;
|
use std::process::exit;
|
||||||
|
|
||||||
|
|
||||||
fn setup_folders(config_path: &PathBuf) -> (String, String, String, String, String){
|
fn setup_folders(config_path: &PathBuf) -> (String, String, String, String, String, String, String, String, String){
|
||||||
let mut delete_for_cleanup = config_path.clone();
|
let mut delete_for_cleanup = config_path.clone();
|
||||||
delete_for_cleanup.pop();
|
delete_for_cleanup.pop();
|
||||||
let mut failed = false;
|
let mut failed = false;
|
||||||
@@ -20,6 +20,10 @@ fn setup_folders(config_path: &PathBuf) -> (String, String, String, String, Stri
|
|||||||
let mut upcomming_files_folder = String::new();
|
let mut upcomming_files_folder = String::new();
|
||||||
let mut upcomming_notes_folder = String::new();
|
let mut upcomming_notes_folder = String::new();
|
||||||
let mut tools_folder = String::new();
|
let mut tools_folder = String::new();
|
||||||
|
let mut cracking_rig = String::new();
|
||||||
|
let mut cracking_user = String::new();
|
||||||
|
let mut rockyou = String::new();
|
||||||
|
let mut rule = String::new();
|
||||||
while new_files_folder.is_empty() || new_notes_folder.is_empty() || tools_folder.is_empty(){
|
while new_files_folder.is_empty() || new_notes_folder.is_empty() || tools_folder.is_empty(){
|
||||||
if new_files_folder.is_empty(){
|
if new_files_folder.is_empty(){
|
||||||
println!("path to save active project files?");
|
println!("path to save active project files?");
|
||||||
@@ -56,12 +60,62 @@ fn setup_folders(config_path: &PathBuf) -> (String, String, String, String, Stri
|
|||||||
Err(_e) => println!("we need input here dummy... We will reprompt on the next loop...")
|
Err(_e) => println!("we need input here dummy... We will reprompt on the next loop...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if cracking_rig.is_empty(){
|
||||||
|
let mut have_rig = String::new();
|
||||||
|
println!("do you have a separate computer that you can ssh into to crack passwords on?");
|
||||||
|
match stdin().read_line(&mut have_rig){
|
||||||
|
Ok(_r) => (),
|
||||||
|
Err(_e) => println!("we need input here dummy, try again...")
|
||||||
|
}
|
||||||
|
have_rig = have_rig.to_lowercase();
|
||||||
|
if have_rig.contains("y"){
|
||||||
|
println!("excellent! Whats the IP or hostname?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut cracking_rig){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummy, try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("user to ssh as for the cracking rig?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut cracking_user){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummy, try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Path to rockyou.txt on the cracking rig?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut rockyou){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummyu try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Path to one rule to rule them all on the cracking rig?");
|
||||||
|
loop{
|
||||||
|
match stdin().read_line(&mut rule){
|
||||||
|
Ok(_r) => break,
|
||||||
|
Err(_e) => println!("we need input here dummyu try again...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if have_rig.contains("n"){
|
||||||
|
println!("ooof ok freeloader");
|
||||||
|
cracking_rig = "n".to_owned();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
new_files_folder = new_files_folder.trim_end().to_owned();
|
||||||
|
new_notes_folder = new_notes_folder.trim_end().to_owned();
|
||||||
|
upcomming_files_folder = upcomming_files_folder.trim_end().to_owned();
|
||||||
|
upcomming_notes_folder = upcomming_notes_folder.trim_end().to_owned();
|
||||||
|
tools_folder = tools_folder.trim_end().to_owned();
|
||||||
|
cracking_rig = cracking_rig.trim_end().to_owned();
|
||||||
|
cracking_user = cracking_user.trim_end().to_owned();
|
||||||
let new_files_path = PathBuf::from(&new_files_folder);
|
let new_files_path = PathBuf::from(&new_files_folder);
|
||||||
let new_notes_path = PathBuf::from(&new_notes_folder);
|
let new_notes_path = PathBuf::from(&new_notes_folder);
|
||||||
let upcomming_files_path = PathBuf::from(&upcomming_files_folder);
|
let upcomming_files_path = PathBuf::from(&upcomming_files_folder);
|
||||||
let upcomming_notes_path = PathBuf::from(&upcomming_notes_folder);
|
let upcomming_notes_path = PathBuf::from(&upcomming_notes_folder);
|
||||||
let tools_path = PathBuf::from(&tools_folder);
|
let tools_path = PathBuf::from(&tools_folder.trim_end());
|
||||||
if new_files_path.exists() == false{
|
if new_files_path.exists() == false{
|
||||||
println!("active project file folder does not exist, creating...");
|
println!("active project file folder does not exist, creating...");
|
||||||
match fs::create_dir_all(&new_files_folder){
|
match fs::create_dir_all(&new_files_folder){
|
||||||
@@ -106,7 +160,7 @@ fn setup_folders(config_path: &PathBuf) -> (String, String, String, String, Stri
|
|||||||
}
|
}
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
return (new_files_folder, new_notes_folder, tools_folder, upcomming_files_folder, upcomming_notes_folder);
|
return (new_files_folder, new_notes_folder, tools_folder, upcomming_files_folder, upcomming_notes_folder, cracking_rig, cracking_user, rockyou, rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -156,7 +210,7 @@ pub fn install(config_path: &PathBuf){
|
|||||||
else{
|
else{
|
||||||
_terminal_command = _terminal_commands[terminal_response.trim_end()].to_owned();
|
_terminal_command = _terminal_commands[terminal_response.trim_end()].to_owned();
|
||||||
}
|
}
|
||||||
let (files_response, notes_response, tools_response, project_folder_path, project_note_path) = setup_folders(&config_path);
|
let (files_response, notes_response, tools_response, project_folder_path, project_note_path, cracking_rig, cracking_user, rockyou, rule) = setup_folders(&config_path);
|
||||||
print!("
|
print!("
|
||||||
This tool is mainly to handle distrobox creation and usage.
|
This tool is mainly to handle distrobox creation and usage.
|
||||||
It's expecting you to have a distrobox that you will use as a template.
|
It's expecting you to have a distrobox that you will use as a template.
|
||||||
@@ -175,7 +229,7 @@ Do you have a distrobox set up to function as your template for all new projects
|
|||||||
let _list = process::Command::new("distrobox").arg("list").arg("--root").status();
|
let _list = process::Command::new("distrobox").arg("list").arg("--root").status();
|
||||||
println!("distrobox template name?");
|
println!("distrobox template name?");
|
||||||
std::io::stdin().read_line(&mut template_name).unwrap();
|
std::io::stdin().read_line(&mut template_name).unwrap();
|
||||||
let config_string = format!("Project_files:{}\nProject_notes:{}\ntools_folder:{}\nbox_template:{}\nterminal:{}", files_response.trim_end(), notes_response.trim_end(), tools_response.trim_end(),template_name.trim_end(), _terminal_command.trim_end());
|
let config_string = format!("Project_files:{}\nProject_notes:{}\ntools_folder:{}\nbox_template:{}\nterminal:{}\ncracking_rig:{}@{}\nrockyou_location:{}\nrule_location:{}", files_response.trim_end(), notes_response.trim_end(), tools_response.trim_end(),template_name.trim_end(), _terminal_command.trim_end(), cracking_user.trim_ascii_end(), cracking_rig.trim_end(), rockyou.trim_end(), rule.trim_end());
|
||||||
config_file.write_all(config_string.as_bytes()).expect("error writing to config file");
|
config_file.write_all(config_string.as_bytes()).expect("error writing to config file");
|
||||||
let default_projectline = format!("default:default:{}:{}:yes:{}", ¬es_response.trim_end(), &files_response.trim_end(), &template_name.trim_end());
|
let default_projectline = format!("default:default:{}:{}:yes:{}", ¬es_response.trim_end(), &files_response.trim_end(), &template_name.trim_end());
|
||||||
projects_conf_file.write_all(default_projectline.as_bytes()).expect("error writing default project line");
|
projects_conf_file.write_all(default_projectline.as_bytes()).expect("error writing default project line");
|
||||||
@@ -195,10 +249,6 @@ Do you have a distrobox set up to function as your template for all new projects
|
|||||||
install_path.push("new_projects.conf");
|
install_path.push("new_projects.conf");
|
||||||
password_spray_template_path.push("passwordspray.md");
|
password_spray_template_path.push("passwordspray.md");
|
||||||
let password_spray_template_path = install_path.clone();
|
let password_spray_template_path = install_path.clone();
|
||||||
let mut conf_file = fs::File::create(install_path).expect("error creating config file");
|
|
||||||
write!(conf_file, "project_folder_path:{}
|
|
||||||
project_notes_path:{}
|
|
||||||
", project_folder_path.trim_end(), project_note_path.trim_end()).expect("error writing config file");
|
|
||||||
let mut passpray_file = fs::File::create(password_spray_template_path).expect("error creating passwordspray file");
|
let mut passpray_file = fs::File::create(password_spray_template_path).expect("error creating passwordspray file");
|
||||||
write!(passpray_file, "
|
write!(passpray_file, "
|
||||||
- [ ] useraspass
|
- [ ] useraspass
|
||||||
|
|||||||
@@ -53,6 +53,9 @@ fn main() {
|
|||||||
let mut tools_folder = PathBuf::new();
|
let mut tools_folder = PathBuf::new();
|
||||||
let mut terminal_command = String::new();
|
let mut terminal_command = String::new();
|
||||||
let mut box_template = String::new();
|
let mut box_template = String::new();
|
||||||
|
let mut cracking_rig = String::new();
|
||||||
|
let mut rockyou = String::new();
|
||||||
|
let mut rule = String::new();
|
||||||
println!("\nconfig already generated\nloading config file...\n");
|
println!("\nconfig already generated\nloading config file...\n");
|
||||||
let settings_string = fs::read_to_string(&config_path).expect("error reading config file");
|
let settings_string = fs::read_to_string(&config_path).expect("error reading config file");
|
||||||
let settings: Vec<&str> = settings_string.split("\n").collect();
|
let settings: Vec<&str> = settings_string.split("\n").collect();
|
||||||
@@ -65,6 +68,9 @@ fn main() {
|
|||||||
"tools_folder" => tools_folder.push(setting_vec[1].trim_end()),
|
"tools_folder" => tools_folder.push(setting_vec[1].trim_end()),
|
||||||
"box_template" => box_template = setting_vec[1].trim_end().to_owned(),
|
"box_template" => box_template = setting_vec[1].trim_end().to_owned(),
|
||||||
"terminal" => terminal_command = setting_vec[1].trim_end().to_owned(),
|
"terminal" => terminal_command = setting_vec[1].trim_end().to_owned(),
|
||||||
|
"cracking_rig" => cracking_rig = setting_vec[1].trim_end().to_owned(),
|
||||||
|
"rockyou_location" => rockyou = setting_vec[1].trim_ascii_end().to_owned(),
|
||||||
|
"rule_location" => rule = setting_vec[1].trim_end().to_owned(),
|
||||||
_ => println!("error unknown setting: {}", setting_vec[0])
|
_ => println!("error unknown setting: {}", setting_vec[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,12 +80,13 @@ fn main() {
|
|||||||
Note Folders: {}
|
Note Folders: {}
|
||||||
Tools Folder: {}
|
Tools Folder: {}
|
||||||
distrobox template: {}
|
distrobox template: {}
|
||||||
terminal_command: {}\n
|
terminal_command: {}
|
||||||
", project_base_folder.display(), project_base_notes.display(), tools_folder.display(), box_template, terminal_command);
|
cracking_rig: {}\n
|
||||||
|
", project_base_folder.display(), project_base_notes.display(), tools_folder.display(), box_template, terminal_command, cracking_rig);
|
||||||
println!("loading project configs...");
|
println!("loading project configs...");
|
||||||
let projects = project_controls::get_projects(&config_path);
|
let projects = project_controls::get_projects(&config_path);
|
||||||
println!("Enter to start main menu");
|
println!("Enter to start main menu");
|
||||||
let mut enter = String::new();
|
let mut enter = String::new();
|
||||||
std::io::stdin().read_line(&mut enter).unwrap();
|
std::io::stdin().read_line(&mut enter).unwrap();
|
||||||
menu::main_menu(projects, config_path, &project_base_folder, &project_base_notes, &tools_folder, box_template, terminal_command);
|
menu::main_menu(projects, config_path, &project_base_folder, &project_base_notes, &tools_folder, box_template, terminal_command, cracking_rig, rockyou, rule);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ fn get_active_project(projects: &Vec<Project>) -> &Project{
|
|||||||
return active_project
|
return active_project
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_menu(mut projects: Vec<Project>, config_path: PathBuf, base_files: &PathBuf, base_notes: &PathBuf, tools_dir: &PathBuf, boxtemplate: String, terminal: String){
|
pub fn main_menu(mut projects: Vec<Project>, config_path: PathBuf, base_files: &PathBuf, base_notes: &PathBuf, tools_dir: &PathBuf, boxtemplate: String, terminal: String, cracking_rig: String, rockyou: String, rule: String){
|
||||||
let mut loopize = true;
|
let mut loopize = true;
|
||||||
let mut new_id = next_project_id(&config_path);
|
let mut new_id = next_project_id(&config_path);
|
||||||
loop {
|
loop {
|
||||||
@@ -126,7 +126,8 @@ Year: {}
|
|||||||
16.) build portscan command from scope in notes
|
16.) build portscan command from scope in notes
|
||||||
17.) Stop All Distroboxes
|
17.) Stop All Distroboxes
|
||||||
18.) Password Spray (will print password to spray, and wait the obervation window time)
|
18.) Password Spray (will print password to spray, and wait the obervation window time)
|
||||||
19.) Quit Application
|
19.) crack password hashes on your cracking rig
|
||||||
|
20.) Quit Application
|
||||||
\n", active_project.customer, active_project.project_name, active_project.files_folder.display(), active_project.notes_folder.display(), active_project.boxname, terminal, season, year);
|
\n", active_project.customer, active_project.project_name, active_project.files_folder.display(), active_project.notes_folder.display(), active_project.boxname, terminal, season, year);
|
||||||
std::io::stdin().read_line(&mut response).expect("error getting menu input");
|
std::io::stdin().read_line(&mut response).expect("error getting menu input");
|
||||||
clear().expect("error clearing screen");
|
clear().expect("error clearing screen");
|
||||||
@@ -152,7 +153,8 @@ Year: {}
|
|||||||
"16" => info_controls::build_cs_portscan_cmd(&active_project),
|
"16" => info_controls::build_cs_portscan_cmd(&active_project),
|
||||||
"17" => box_controls::stop_all_boxes(&projects),
|
"17" => box_controls::stop_all_boxes(&projects),
|
||||||
"18" => info_controls::password_spray_help(&active_project, season, lseason, year, &tools_dir, &config_path),
|
"18" => info_controls::password_spray_help(&active_project, season, lseason, year, &tools_dir, &config_path),
|
||||||
"19" => {project_controls::save_projects(&projects, &config_path);
|
"19" => info_controls::crack_hashes(&cracking_rig, &active_project, &terminal, &rockyou, &rule),
|
||||||
|
"20" => {project_controls::save_projects(&projects, &config_path);
|
||||||
let mut stop = String::new();
|
let mut stop = String::new();
|
||||||
println!("stop all boxes?\ny/n");
|
println!("stop all boxes?\ny/n");
|
||||||
std::io::stdin().read_line(&mut stop).unwrap();
|
std::io::stdin().read_line(&mut stop).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user