From ded63baf5e38c881f8cf798131146c539754bf1b Mon Sep 17 00:00:00 2001 From: PThorpe92 Date: Fri, 5 Apr 2024 09:02:30 -0400 Subject: [PATCH] feat: add cookie options, fix input screens --- src/app.rs | 51 ++++++----- src/display/inputopt.rs | 12 ++- src/display/menuopts.rs | 18 +++- src/display/mod.rs | 20 +++- src/request/curl.rs | 53 ++++++++++- src/screens/auth.rs | 4 +- src/screens/cookies.rs | 27 ++++++ src/screens/input/input.rs | 117 +++++++++++++++++++----- src/screens/input/request_body_input.rs | 4 +- src/screens/mod.rs | 1 + src/screens/more_flags.rs | 9 +- src/screens/render.rs | 3 + src/screens/request.rs | 8 +- src/screens/response.rs | 1 - src/screens/screen.rs | 14 ++- 15 files changed, 263 insertions(+), 79 deletions(-) create mode 100644 src/screens/cookies.rs diff --git a/src/app.rs b/src/app.rs index a7d543d..41686a5 100644 --- a/src/app.rs +++ b/src/app.rs @@ -124,8 +124,8 @@ impl<'a> App<'a> { pub fn goto_screen(&mut self, screen: &Screen) { self.input.reset(); - self.screen_stack.push(screen.clone()); self.current_screen = screen.clone(); + self.screen_stack.push(screen.clone()); self.cursor = 0; match screen { Screen::Method => { @@ -178,7 +178,9 @@ impl<'a> App<'a> { pub fn go_back_screen(&mut self) { let last = self.screen_stack.pop().unwrap_or_default(); // current screen match self.screen_stack.last().cloned() { - Some(screen) if screen == last => self.go_back_screen(), + Some(screen) if std::mem::discriminant(&screen) == std::mem::discriminant(&last) => { + self.go_back_screen() + } Some( Screen::InputMenu(_) | Screen::CmdMenu(_) @@ -187,13 +189,14 @@ impl<'a> App<'a> { ) => self.go_back_screen(), Some(Screen::RequestBodyInput) => self.goto_screen(&Screen::Method), Some(Screen::Error(_)) => self.goto_screen(&Screen::Home), - Some(Screen::RequestMenu(ref e)) => { - if !e.as_ref().is_some_and(|err| err.is_error()) { - self.goto_screen(&Screen::RequestMenu(e.clone())); + Some(Screen::RequestMenu(e)) => { + if e.as_ref().is_some() { + self.goto_screen(&Screen::RequestMenu(None)); } else { self.goto_screen(&Screen::Method); } } + Some(Screen::Method) => self.goto_screen(&Screen::Home), Some(screen) => { self.goto_screen(&screen); } @@ -298,10 +301,6 @@ impl<'a> App<'a> { } } - pub fn get_url(&self) -> &str { - self.command.get_url() - } - pub fn select_item(&mut self) { if let Some(state) = self.state.as_mut() { if let Some(selected) = state.selected() { @@ -410,18 +409,6 @@ impl<'a> App<'a> { Ok(()) } - pub fn has_auth(&self) -> bool { - self.command.has_auth() - } - - pub fn has_unix_socket(&self) -> bool { - self.command.has_unix_socket() - } - - pub fn has_url(&self) -> bool { - !self.command.get_url().is_empty() - } - pub fn delete_saved_key(&mut self, index: i32) -> Result<(), rusqlite::Error> { self.db.as_ref().delete_key(index)?; self.goto_screen(&Screen::SavedKeys); @@ -474,7 +461,10 @@ impl<'a> App<'a> { AppOptions::UserAgent(_) => self.command.set_user_agent(""), AppOptions::Referrer(_) => self.command.set_referrer(""), AppOptions::RequestBody(_) => self.command.set_request_body(""), - AppOptions::Cookie(_) => self.command.remove_headers(&opt.get_value()), + AppOptions::CookieJar(_) => self.command.set_cookie_jar(&opt.get_value()), + AppOptions::CookiePath(_) => self.command.set_cookie_path(&opt.get_value()), + AppOptions::NewCookie(_) => self.command.add_cookie(&opt.get_value()), + AppOptions::NewCookieSession => self.command.reset_cookie_session(), AppOptions::Headers(_) => self.command.remove_headers(&opt.get_value()), AppOptions::Auth(_) => self.command.set_auth(crate::request::curl::AuthKind::None), AppOptions::EnableHeaders => self.command.enable_response_headers(false), @@ -500,6 +490,7 @@ impl<'a> App<'a> { match opt { // push headers, reset everything else AppOptions::Headers(_) => true, + AppOptions::NewCookie(_) => true, _ => !self.has_app_option(opt), } } @@ -591,10 +582,14 @@ impl<'a> App<'a> { AppOptions::Outfile(outfile) => self.command.set_outfile(&outfile), - AppOptions::Cookie(cookie) => self.command.add_cookie(&cookie), + AppOptions::NewCookie(cookie) => self.command.add_cookie(&cookie), + + AppOptions::CookieJar(cookie) => self.command.set_cookie_jar(&cookie), AppOptions::Response(resp) => self.command.set_response(&resp), + AppOptions::CookiePath(cookie) => self.command.set_cookie_path(&cookie), + AppOptions::Referrer(referrer) => self.command.set_referrer(&referrer), AppOptions::UserAgent(agent) => self.command.set_user_agent(&agent), @@ -648,12 +643,18 @@ impl<'a> App<'a> { self.command.set_referrer(referrer); } } - AppOptions::Cookie(_) => { - if let AppOptions::Cookie(ref mut cookie) = opt { + AppOptions::CookiePath(_) => { + if let AppOptions::CookiePath(ref mut cookie) = opt { option.replace_value(cookie.clone()); self.command.add_cookie(cookie); } } + AppOptions::CookieJar(_) => { + if let AppOptions::CookieJar(ref mut cookie) = opt { + option.replace_value(cookie.clone()); + self.command.set_cookie_jar(cookie); + } + } AppOptions::CaPath(_) => { if let AppOptions::CaPath(ref ca_path) = opt { option.replace_value(String::from(ca_path)); diff --git a/src/display/inputopt.rs b/src/display/inputopt.rs index 734f24a..328942e 100644 --- a/src/display/inputopt.rs +++ b/src/display/inputopt.rs @@ -18,7 +18,11 @@ pub enum InputOpt { UnixSocket, UserAgent, MaxRedirects, - Cookie, + CookiePath, + NewCookie, + CookieJar, + CookieValue(String), // store the name + CookieExpires(String), // store the rest FtpAccount, CaPath, CaCert, @@ -52,7 +56,10 @@ impl Display for InputOpt { InputOpt::UnixSocket => write!(f, "| Unix Socket"), InputOpt::UserAgent => write!(f, "| User Agent"), InputOpt::MaxRedirects => write!(f, "| Max Redirects"), - InputOpt::Cookie => write!(f, "| Cookie"), + InputOpt::NewCookie => write!(f, "| Cookie"), + InputOpt::CookiePath => write!(f, "| Cookie"), + InputOpt::CookieValue(_) => write!(f, "| Cookie Val"), + InputOpt::CookieExpires(_) => write!(f, "| Cookie Expires"), InputOpt::CaPath => write!(f, "| Ca Path"), InputOpt::CaCert => write!(f, "| Ca Cert"), InputOpt::VerifyPeer => write!(f, "| Verify Peer DNS-Over-HTTPS"), @@ -63,6 +70,7 @@ impl Display for InputOpt { InputOpt::RenameCollection(_) => write!(f, "| Rename Collection"), InputOpt::RequestError(ref err) => write!(f, "| Error: {}", err), InputOpt::Method(method) => write!(f, "| Method: {}", method), + InputOpt::CookieJar => write!(f, "| Cookie Jar"), } } } diff --git a/src/display/menuopts.rs b/src/display/menuopts.rs index 1c3aa38..e52c95f 100644 --- a/src/display/menuopts.rs +++ b/src/display/menuopts.rs @@ -88,7 +88,8 @@ pub const DISPLAY_OPT_TCP_KEEPALIVE: &str = " Enable TCP keepalive 󰗶 "; pub const DISPLAY_OPT_MAX_REC: &str = " Specify recursive depth: "; pub const DISPLAY_OPT_OUTFILE: &str = " Specify output filepath: "; pub const DISPLAY_OPT_REFERRER: &str = " Specify Referrer: "; -pub const DISPLAY_OPT_COOKIE: &str = " Add Cookie: "; +pub const DISPLAY_OPT_COOKIE: &str = " Cookie Path: "; +pub const DISPLAY_OPT_COOKIE_JAR: &str = " Cookie Jar: "; pub const DISPLAY_OPT_USERAGENT: &str = " Specify User-Agent: "; pub const DISPLAY_OPT_PROXY_TUNNEL: &str = " Enable HTTP Proxy-Tunnel 󱠾 "; pub const DISPLAY_OPT_URL: &str = " Request URL: "; @@ -141,7 +142,7 @@ pub const COLLECTION_ALERT_MENU_OPTS: [&str; 4] = [ pub const REQUEST_MENU_OPTIONS: [&str; 12] = [ "Add a URL 󰖟 ", "Add a file for uploads  ", - "Add Unix Socket address 󰟩 ", + "Cookie options 󰖟  ", "Authentication 󰯄 ", "Header Options  ", "Enable verbose output [-v]", @@ -152,6 +153,14 @@ pub const REQUEST_MENU_OPTIONS: [&str; 12] = [ "More Options  ", "Clear all options  ", ]; + +pub const COOKIE_MENU_OPTIONS: [&str; 4] = [ + "Set Cookie file path (Use Cookies) 󰖟  ", + "Set Cookie-Jar path (Storage) 󰖟  ", + "Add New Cookie 󰆘 ", + "Reset Cookie Session 󰆘 ", +]; + pub const METHOD_MENU_OPTIONS: [&str; 7] = [ "OTHER (custom command)", "GET", @@ -176,10 +185,11 @@ pub const AUTHENTICATION_MENU_OPTIONS: [&str; 6] = [ "Ntlm", "SPNEGO", ]; -pub const MORE_FLAGS_MENU: [&str; 13] = [ +pub const MORE_FLAGS_MENU: [&str; 14] = [ "Follow Redirects 󱀀 ", "Specify Max redirects 󱀀 ", - "Add Cookie 󰆘 ", + "Add New Cookie 󰆘 ", + "Set Cookie-Jar path (Storage) 󰆘 ", "Enable HTTP Proxy-Tunnel 󱠾 ", "Unrestricted Auth  ", "Specify Referrer 󰆽 ", diff --git a/src/display/mod.rs b/src/display/mod.rs index 31deda6..9432f75 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -1,7 +1,7 @@ use std::fmt::{Display, Formatter}; use crate::{ - display::menuopts::{DISPLAY_OPT_MAX_REDIRECTS, DISPLAY_OPT_REFERRER}, + display::menuopts::{DISPLAY_OPT_COOKIE_JAR, DISPLAY_OPT_MAX_REDIRECTS, DISPLAY_OPT_REFERRER}, request::curl::AuthKind, }; @@ -46,7 +46,6 @@ pub mod menuopts; #[derive(Debug, Clone, PartialEq)] pub enum AppOptions { Verbose, - // TODO: support more headers Headers(String), URL(String), Outfile(String), @@ -56,7 +55,8 @@ pub enum AppOptions { SaveToken, UnixSocket(String), FollowRedirects, - Cookie(String), + CookieJar(String), + CookiePath(String), EnableHeaders, ContentHeaders(HeaderKind), ProgressBar, @@ -71,7 +71,9 @@ pub enum AppOptions { UnrestrictedAuth, MaxRedirects(usize), UploadFile(String), + NewCookie(String), RequestBody(String), + NewCookieSession, } impl AppOptions { @@ -97,7 +99,10 @@ impl AppOptions { AppOptions::UnixSocket(ref mut socket) => { *socket = val; } - AppOptions::Cookie(ref mut cookie) => { + AppOptions::CookiePath(ref mut cookie) => { + *cookie = val; + } + AppOptions::CookieJar(ref mut cookie) => { *cookie = val; } AppOptions::Referrer(ref mut referrer) => { @@ -135,6 +140,7 @@ impl AppOptions { AppOptions::UnixSocket(socket) => { format!("{}{}", DISPLAY_OPT_UNIX_SOCKET, socket.clone()) } + AppOptions::NewCookie(cookie) => format!("{}{}", DISPLAY_OPT_COOKIE, cookie.clone()), AppOptions::EnableHeaders => DISPLAY_OPT_HEADERS.to_string(), AppOptions::ProgressBar => String::from(DISPLAY_OPT_PROGRESS_BAR), AppOptions::FailOnError => String::from(DISPLAY_OPT_FAIL_ON_ERROR), @@ -143,7 +149,11 @@ impl AppOptions { AppOptions::MaxRedirects(max_redirects) => { format!("{}{}", DISPLAY_OPT_MAX_REDIRECTS, max_redirects) } - AppOptions::Cookie(cookie) => format!("{}{}", DISPLAY_OPT_COOKIE, cookie.clone()), + AppOptions::NewCookieSession => String::from("New Cookie Session"), + AppOptions::CookiePath(cookie) => format!("{}{}", DISPLAY_OPT_COOKIE, cookie.clone()), + AppOptions::CookieJar(cookie) => { + format!("{}{}", DISPLAY_OPT_COOKIE_JAR, cookie.clone()) + } AppOptions::Referrer(referrer) => { format!("{}{}", DISPLAY_OPT_REFERRER, referrer.clone()) } diff --git a/src/request/curl.rs b/src/request/curl.rs index 36a9ca5..2dda534 100644 --- a/src/request/curl.rs +++ b/src/request/curl.rs @@ -284,7 +284,7 @@ impl Display for AuthKind { AuthKind::None => write!(f, "None"), AuthKind::Ntlm => write!(f, "NTLM"), AuthKind::Basic(login) => write!(f, "Basic: {}", login), - AuthKind::Bearer(token) => write!(f, "Authorization: {}", token), + AuthKind::Bearer(token) => write!(f, "Authorization: Bearer {}", token), AuthKind::Digest(login) => write!(f, "Digest Auth: {}", login), AuthKind::AwsSigv4 => write!(f, "AWS SignatureV4"), AuthKind::Spnego => write!(f, "SPNEGO Auth"), @@ -377,6 +377,31 @@ impl<'a> Curl<'a> { self.resp = Some(String::from(response)); } + pub fn get_cookie_path(&self) -> Option { + let flag = &CurlFlag::CookieFile(CurlFlagType::CookieFile.get_value(), None); + self.opts + .iter() + .find(|pos| *pos == flag) + .and_then(|pos| pos.get_arg()) + } + + pub fn set_cookie_path(&mut self, path: &str) { + let flag = CurlFlag::CookieFile(CurlFlagType::CookieFile.get_value(), None); + self.toggle_flag(&flag); + self.curl.cookie_file(path).unwrap(); + } + + pub fn set_cookie_jar(&mut self, path: &str) { + let flag = CurlFlag::CookieJar(CurlFlagType::CookieJar.get_value(), None); + self.toggle_flag(&flag); + self.curl.cookie_jar(path).unwrap(); + } + pub fn reset_cookie_session(&mut self) { + let flag = CurlFlag::CookieSession(CurlFlagType::CookieSession.get_value(), None); + self.toggle_flag(&flag); + self.curl.cookie_session(true).unwrap(); + } + pub fn get_response(&self) -> String { self.resp.clone().unwrap_or_default() } @@ -484,6 +509,19 @@ impl<'a> Curl<'a> { let flag = &CurlFlag::UnixSocket(CurlFlagType::UnixSocket.get_value(), None); self.has_flag(flag) } + + pub fn get_unix_socket(&self) -> Option { + if self.has_unix_socket() { + let flag = &CurlFlag::UnixSocket(CurlFlagType::UnixSocket.get_value(), None); + self.opts + .iter() + .find(|pos| *pos == flag) + .and_then(|pos| pos.get_arg()) + } else { + None + } + } + pub fn set_method(&mut self, method: Method) { match method { Method::Get => self.set_get_method(), @@ -538,6 +576,14 @@ impl<'a> Curl<'a> { self.curl.progress(on).unwrap(); } + pub fn get_cookie_jar_path(&self) -> Option { + let flag = &CurlFlag::CookieJar(CurlFlagType::CookieJar.get_value(), None); + self.opts + .iter() + .find(|pos| *pos == flag) + .and_then(|pos| pos.get_arg()) + } + pub fn set_content_header(&mut self, kind: HeaderKind) { if kind == HeaderKind::None && self.headers.is_some() { self.headers @@ -902,7 +948,7 @@ impl<'a> Curl<'a> { pub fn set_bearer_auth(&mut self, token: &str) { self.add_flag(CurlFlag::Bearer( CurlFlagType::Bearer.get_value(), - Some(format!("Authorization: {token}")), + Some(format!("Authorization: Bearer {token}")), )); self.auth = AuthKind::Bearer(String::from(token)); } @@ -1026,6 +1072,7 @@ define_curl_flags! { CertInfo("--certinfo"), Headers("-H"), Digest("--digest"), + CookieSession("--junk-session-cookies"), Basic("-H"), AnyAuth("--any-auth"), UnixSocket("--unix-socket"), @@ -1049,6 +1096,8 @@ define_curl_flags! { SpnegoAuth("--negotiate -u:"), Progress("--progress-bar"), RequestBody("--data"), + CookieFile("-c"), + CookieJar("-b"), } #[cfg(test)] diff --git a/src/screens/auth.rs b/src/screens/auth.rs index 5a8f54f..f0f1b90 100644 --- a/src/screens/auth.rs +++ b/src/screens/auth.rs @@ -56,14 +56,14 @@ pub fn handle_authentication_screen(app: &mut App, frame: &mut Frame<'_>) { } } 4 => { - if app.has_auth() { + if app.command.has_auth() { app.remove_app_option(&AppOptions::Auth(AuthKind::None)); } app.add_app_option(AppOptions::Auth(AuthKind::Spnego)); app.goto_screen(&Screen::RequestMenu(None)); } 5 => { - if app.has_auth() { + if app.command.has_auth() { app.remove_app_option(&AppOptions::Auth(AuthKind::None)); } app.add_app_option(AppOptions::Auth(AuthKind::Ntlm)); diff --git a/src/screens/cookies.rs b/src/screens/cookies.rs new file mode 100644 index 0000000..11989c1 --- /dev/null +++ b/src/screens/cookies.rs @@ -0,0 +1,27 @@ +use tui::Frame; + +use crate::app::App; +use crate::display::inputopt::InputOpt; +use crate::display::AppOptions; + +use super::render::handle_screen_defaults; +use super::Screen; + +pub fn handle_cookies_menu(app: &mut App, frame: &mut Frame<'_>) { + handle_screen_defaults(app, frame); + if let Some(num) = app.selected { + match num { + // set cookie filepath + 0 => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::CookiePath))), + // set cookie jar path + 1 => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::CookieJar))), + // new cookie + 2 => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::NewCookie))), + 3 => { + app.add_app_option(AppOptions::NewCookieSession); + app.goto_screen(&Screen::RequestMenu(None)); + } + _ => {} + } + } +} diff --git a/src/screens/input/input.rs b/src/screens/input/input.rs index 771d97a..0f129ae 100644 --- a/src/screens/input/input.rs +++ b/src/screens/input/input.rs @@ -10,7 +10,7 @@ use crate::screens::{centered_rect, Screen}; use crate::{app::InputMode, display::inputopt::InputOpt}; use std::path::Path; use tui::prelude::Line; -use tui::style::Color; +use tui::style::{Color, Modifier}; use tui::widgets::Paragraph; use tui::widgets::{Block, Borders}; use tui::{ @@ -32,6 +32,12 @@ pub fn get_input_prompt(opt: InputOpt) -> Text<'static> { AuthType::Bearer => Text::from(INPUT_OPT_AUTH_BEARER), _ => Text::from(INPUT_OPT_AUTH_ANY), }, + InputOpt::CookiePath => Text::from("Enter the path to the cookie jar file"), + InputOpt::NewCookie => Text::from("Enter the name of the cookie"), + InputOpt::CookieValue(ref name) => Text::from(format!("Enter the value for {}", name)), + InputOpt::CookieExpires(_) => { + Text::from("Enter the (optional) expiration date for the cookie") + } InputOpt::ImportCollection => Text::from("Enter the path to the collection file.json"), _ => Text::from(INPUT_OPT_BASIC), } @@ -43,18 +49,23 @@ pub fn handle_default_input_screen(app: &mut App, frame: &mut Frame<'_>, opt: In .constraints(vec![Constraint::Percentage(15), Constraint::Percentage(85)]) .split(frame.size()); let input_textbox = chunks[0]; - let text_chunk = Block::default() - .borders(Borders::ALL) - .style( - Style::default() - .bg(app.config.get_bg_color()) - .fg(Color::LightGreen), - ) - .title("Press 'i' for insert mode, Esc to exit."); + let text_chunk = Block::default().borders(Borders::ALL).style( + Style::default() + .bg(app.config.get_bg_color()) + .fg(Color::LightGreen) + .add_modifier(tui::style::Modifier::BOLD), + ); frame.render_widget(text_chunk, input_textbox); let bottom_box = centered_rect(chunks[1], crate::screens::ScreenArea::Top); let prompt = get_input_prompt(opt.clone()); - frame.render_widget(Paragraph::new(prompt), bottom_box); + frame.render_widget( + Paragraph::new(prompt).style( + Style::default() + .fg(Color::LightGreen) + .add_modifier(Modifier::BOLD), + ), + centered_rect(bottom_box, crate::screens::ScreenArea::Top), + ); let top_box = Layout::default() .direction(Direction::Vertical) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref()) @@ -67,38 +78,68 @@ pub fn handle_default_input_screen(app: &mut App, frame: &mut Frame<'_>, opt: In // if the url has been entered already we populate the input box with it // we need to prevent this from happening multiple times, without clearing the url if !url.is_empty() && app.input.cursor() == 0 { + let _ = app.input.handle(InputRequest::InsertChar(' ')).is_some(); for ch in url.chars() { let _ = app.input.handle(InputRequest::InsertChar(ch)).is_some(); } url.clear(); - } else if app.input.value().is_empty() && app.input.cursor() == 0 { - for ch in "https://".chars() { - let _ = app.input.handle(InputRequest::InsertChar(ch)).is_some(); - } } } InputOpt::Auth(kind) => { if kind == AuthType::Basic || kind == AuthType::Bearer { let auth = app.command.get_token(); if auth.is_some() && app.input.value().is_empty() && app.input.cursor() == 0 { + let _ = app.input.handle(InputRequest::InsertChar(' ')).is_some(); for ch in auth.unwrap().chars() { if app.input.handle(InputRequest::InsertChar(ch)).is_some() {} } - } else if app.input.value().is_empty() && app.input.cursor() == 0 { - for ch in "Bearer ".chars() { - if app.input.handle(InputRequest::InsertChar(ch)).is_some() {} - } } } } InputOpt::UploadFile => { let file = app.command.get_upload_file(); if file.is_some() && app.input.value().is_empty() && app.input.cursor() == 0 { + let _ = app.input.handle(InputRequest::InsertChar(' ')).is_some(); for ch in file.unwrap().chars() { if app.input.handle(InputRequest::InsertChar(ch)).is_some() {} } } } + InputOpt::UnixSocket => { + if !app.command.get_url().is_empty() { + // would only need a unix socket if we don't have a url + app.goto_screen(&Screen::RequestMenu(Some(InputOpt::RequestError( + String::from("Error: You have already entered a URL"), + )))); + } + let socket = app.command.get_unix_socket(); + if socket.is_some() && app.input.value().is_empty() && app.input.cursor() == 0 { + let _ = app.input.handle(InputRequest::InsertChar(' ')).is_some(); + for ch in socket.unwrap().chars() { + if app.input.handle(InputRequest::InsertChar(ch)).is_some() {} + } + } + } + InputOpt::CookiePath => { + if let Some(cookie) = app.command.get_cookie_path() { + if app.input.value().is_empty() && app.input.cursor() == 0 { + let _ = app.input.handle(InputRequest::InsertChar(' ')).is_some(); + for ch in cookie.chars() { + if app.input.handle(InputRequest::InsertChar(ch)).is_some() {} + } + } + } + } + InputOpt::CookieJar => { + if let Some(cookie) = app.command.get_cookie_jar_path() { + if app.input.value().is_empty() && app.input.cursor() == 0 { + let _ = app.input.handle(InputRequest::InsertChar(' ')).is_some(); + for ch in cookie.chars() { + if app.input.handle(InputRequest::InsertChar(ch)).is_some() {} + } + } + } + } _ => {} } let input = Paragraph::new(app.input.value()) @@ -107,6 +148,22 @@ pub fn handle_default_input_screen(app: &mut App, frame: &mut Frame<'_>, opt: In InputMode::Editing => Style::default().fg(Color::White), }) .block(Block::default().borders(Borders::ALL).title("Input")); + let (msg, style) = match app.input_mode { + InputMode::Normal => ( + Line::from("Press 'i' to start editing"), + Style::default() + .fg(Color::Green) + .add_modifier(tui::style::Modifier::BOLD), + ), + InputMode::Editing => ( + Line::from("Press Enter to submit"), + Style::default() + .fg(Color::Yellow) + .add_modifier(tui::style::Modifier::BOLD), + ), + }; + let msg = Paragraph::new(msg).style(style); + frame.render_widget(msg, top_box[0]); frame.render_widget(input, top_box[1]); match app.input_mode { InputMode::Normal => {} @@ -177,13 +234,29 @@ pub fn parse_input(message: String, opt: InputOpt, app: &mut App) { } } InputOpt::Output => { - app.add_app_option(AppOptions::Outfile(message.clone())); + app.add_app_option(AppOptions::Outfile(message)); app.goto_screen(&Screen::RequestMenu(None)); } - InputOpt::Cookie => { - app.add_app_option(AppOptions::Cookie(message.clone())); + InputOpt::CookiePath => { + app.add_app_option(AppOptions::CookiePath(message)); app.goto_screen(&Screen::RequestMenu(None)); } + InputOpt::CookieJar => { + app.add_app_option(AppOptions::CookieJar(message)); + app.goto_screen(&Screen::RequestMenu(None)); + } + InputOpt::NewCookie => { + app.goto_screen(&Screen::RequestMenu(Some(InputOpt::CookieValue(message)))); + } + InputOpt::CookieValue(ref name) => { + let cookie = format!("{}={};", name, message); + app.goto_screen(&Screen::RequestMenu(Some(InputOpt::CookieExpires(cookie)))); + } + InputOpt::CookieExpires(ref cookie) => { + let cookie = format!("{} {}", cookie, message); + app.add_app_option(AppOptions::NewCookie(cookie)); + app.goto_screen(&Screen::RequestMenu(None)) + } InputOpt::Referrer => { app.add_app_option(AppOptions::Referrer(message.clone())); app.goto_screen(&Screen::RequestMenu(None)); @@ -294,7 +367,7 @@ fn parse_auth(auth: AuthType, app: &mut App, message: &str) { // above are the only auth options that would ever send us here _ => AuthKind::None, }); - if app.has_auth() { + if app.command.has_auth() { app.opts.retain(|x| !matches!(x, AppOptions::Auth(_))); } app.opts.push(AppOptions::Auth(match auth { diff --git a/src/screens/input/request_body_input.rs b/src/screens/input/request_body_input.rs index 9786a66..7ae9967 100644 --- a/src/screens/input/request_body_input.rs +++ b/src/screens/input/request_body_input.rs @@ -102,9 +102,7 @@ pub fn handle_req_body_input_screen(app: &mut App, frame: &mut Frame<'_>, _opt: // we have input (the user has typed something and pressed Enter while in insert mode) if !app.messages.is_empty() { app.add_app_option(AppOptions::RequestBody(app.messages[0].clone())); - app.goto_screen(&Screen::RequestMenu(Some(InputOpt::RequestError( - String::new(), - )))); + app.goto_screen(&Screen::RequestMenu(None)); app.messages.clear(); } } diff --git a/src/screens/mod.rs b/src/screens/mod.rs index 273d4ee..94e5273 100644 --- a/src/screens/mod.rs +++ b/src/screens/mod.rs @@ -4,6 +4,7 @@ use ::tui::style::Style; use ::tui::widgets::{Block, Borders, Paragraph, Wrap}; pub mod screen; // Method Select Screens +pub mod cookies; pub mod method; // Request Select Screens pub mod request; diff --git a/src/screens/more_flags.rs b/src/screens/more_flags.rs index 1642cdc..7c1ed0f 100644 --- a/src/screens/more_flags.rs +++ b/src/screens/more_flags.rs @@ -1,4 +1,3 @@ - use tui::Frame; use crate::app::App; @@ -15,16 +14,14 @@ pub fn handle_more_flags_screen(app: &mut App, frame: &mut Frame<'_>) { Some(0) => app.add_app_option(AppOptions::FollowRedirects), // specify max redirects Some(1) => app.goto_screen(&Screen::InputMenu(InputOpt::MaxRedirects)), - // add cookie - Some(2) => app.goto_screen(&Screen::InputMenu(InputOpt::Cookie)), // proxy tunnel Some(3) => app.add_app_option(AppOptions::ProxyTunnel), // Send auth to hosts if redirected Some(4) => app.add_app_option(AppOptions::UnrestrictedAuth), // specify referrer - Some(5) => app.goto_screen(&Screen::InputMenu(InputOpt::Referrer)), + Some(5) => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::Referrer))), // specify ca-path - Some(6) => app.goto_screen(&Screen::InputMenu(InputOpt::CaPath)), + Some(6) => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::CaPath))), // Request certificate info Some(7) => app.add_app_option(AppOptions::CertInfo), // add progress bar @@ -34,7 +31,7 @@ pub fn handle_more_flags_screen(app: &mut App, frame: &mut Frame<'_>) { // wildcard match Some(10) => app.add_app_option(AppOptions::MatchWildcard), // user agent - Some(11) => app.goto_screen(&Screen::InputMenu(InputOpt::UserAgent)), + Some(11) => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::UserAgent))), // enable tcp keepalive Some(12) => app.add_app_option(AppOptions::TcpKeepAlive), _ => {} diff --git a/src/screens/render.rs b/src/screens/render.rs index 61a419d..04497de 100644 --- a/src/screens/render.rs +++ b/src/screens/render.rs @@ -175,6 +175,9 @@ pub fn handle_screen(app: &mut App, frame: &mut Frame<'_>, screen: Screen) { Screen::CmdMenu(cmd) => { saved_commands::handle_alert_menu(app, frame, cmd); } + Screen::CookieOptions => { + cookies::handle_cookies_menu(app, frame); + } Screen::RequestBodyInput => input::request_body_input::handle_req_body_input_screen( app, frame, diff --git a/src/screens/request.rs b/src/screens/request.rs index 6932402..14d11be 100644 --- a/src/screens/request.rs +++ b/src/screens/request.rs @@ -24,8 +24,8 @@ pub fn handle_request_menu_screen(app: &mut App, frame: &mut Frame<'_>, opt: Opt Some(0) => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::URL))), // Add file to upload Some(1) => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::UploadFile))), - // Add Unix Socket address - Some(2) => app.goto_screen(&Screen::RequestMenu(Some(InputOpt::UnixSocket))), + // Cookie options + Some(2) => app.goto_screen(&Screen::CookieOptions), // Auth Some(3) => app.goto_screen(&Screen::Authentication), // Headers @@ -38,7 +38,7 @@ pub fn handle_request_menu_screen(app: &mut App, frame: &mut Frame<'_>, opt: Opt Some(7) => app.add_app_option(AppOptions::SaveCommand), // Save your token or login Some(8) => { - if !app.has_auth() { + if !app.command.has_auth() { app.goto_screen(&Screen::RequestMenu(Some(InputOpt::RequestError( String::from(SAVE_AUTH_ERROR), )))); @@ -48,7 +48,7 @@ pub fn handle_request_menu_screen(app: &mut App, frame: &mut Frame<'_>, opt: Opt } // Execute command Some(9) => { - if !app.has_url() && !app.has_unix_socket() { + if !app.command.get_url().is_empty() && !app.command.has_unix_socket() { app.goto_screen(&Screen::RequestMenu(Some(InputOpt::RequestError( String::from(VALID_COMMAND_ERROR), )))); diff --git a/src/screens/response.rs b/src/screens/response.rs index 3baac80..f875a3c 100644 --- a/src/screens/response.rs +++ b/src/screens/response.rs @@ -16,7 +16,6 @@ pub fn handle_response_screen(app: &mut App, frame: &mut Frame<'_>, resp: String app.items = app.current_screen.get_opts(None); app.state = Some(state.clone()); app.state.as_mut().unwrap().select(Some(app.cursor)); - frame.set_cursor(0, app.cursor as u16); frame.render_stateful_widget(new_list, area, &mut state); if let Some(num) = app.selected { match num { diff --git a/src/screens/screen.rs b/src/screens/screen.rs index 759711f..86cca16 100644 --- a/src/screens/screen.rs +++ b/src/screens/screen.rs @@ -1,9 +1,9 @@ use crate::display::inputopt::InputOpt; use crate::display::menuopts::{ AUTHENTICATION_MENU_OPTIONS, CMD_MENU_OPTIONS, COLLECTION_ALERT_MENU_OPTS, - COLLECTION_MENU_OPTIONS, HEADER_MENU_OPTIONS, KEY_MENU_OPTIONS, MAIN_MENU_OPTIONS, - METHOD_MENU_OPTIONS, MORE_FLAGS_MENU, NEWLINE, OPTION_PADDING_MAX, OPTION_PADDING_MID, - OPTION_PADDING_MIN, REQUEST_MENU_OPTIONS, RESPONSE_MENU_OPTIONS, + COLLECTION_MENU_OPTIONS, COOKIE_MENU_OPTIONS, HEADER_MENU_OPTIONS, KEY_MENU_OPTIONS, + MAIN_MENU_OPTIONS, METHOD_MENU_OPTIONS, MORE_FLAGS_MENU, NEWLINE, OPTION_PADDING_MAX, + OPTION_PADDING_MID, OPTION_PADDING_MIN, REQUEST_MENU_OPTIONS, RESPONSE_MENU_OPTIONS, }; use std::fmt::{Display, Formatter}; use tui::style::{Color, Modifier, Style}; @@ -33,6 +33,7 @@ pub enum Screen { CmdMenu(i32), KeysMenu(usize), RequestBodyInput, + CookieOptions, } impl Display for Screen { @@ -58,6 +59,7 @@ impl Display for Screen { Screen::SavedCollections => "Saved Collections", Screen::ViewSavedCollections => "View Saved Collections", Screen::ColMenu(_) => "Collection Menu", + Screen::CookieOptions => "Cookie Options", }; write!(f, "{}", screen) } @@ -184,10 +186,16 @@ impl<'a> Screen { .iter() .map(|c| ListItem::new(format!("{}{}", c, OPTION_PADDING_MIN))) .collect(), + Screen::SavedCollections => COLLECTION_MENU_OPTIONS .iter() .map(|i| ListItem::new(format!("{}{}", i, OPTION_PADDING_MAX))) .collect(), + + Screen::CookieOptions => COOKIE_MENU_OPTIONS + .iter() + .map(|c| ListItem::from(format!("{}{}", c, OPTION_PADDING_MID))) + .collect(), } }