diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 723 |
1 files changed, 42 insertions, 681 deletions
diff --git a/src/main.rs b/src/main.rs index d06fa9a..dd0041c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +mod parser; +use crate::parser::*; + use base64::prelude::*; use yaml_rust2::parser::Parser; use yaml_rust2::Event; @@ -16,6 +19,45 @@ Options: ); } +#[derive(Debug)] +enum AuthMethod { + Basic, + Bearer{realm: String, service: String, scope: String}, +} + +impl AuthMethod { + fn from_header(header: &ureq::http::HeaderValue) -> Option<Self> { + let header_str = header.to_str().unwrap(); + let (parse, _) = parse_challenge(&header_str.chars().collect::<Vec<char>>(), 0).unwrap(); + + if &header_str[parse.scheme.clone()] == "Basic" { + return Some(AuthMethod::Basic); + } + + if &header_str[parse.scheme.clone()] == "Bearer" { + let mut realm_param = None; + let mut scope_param = None; + let mut service_param = None; + for param in parse.params { + match &header_str[param.key.clone()] { + "realm" => realm_param = Some(param), + "scope" => scope_param = Some(param), + "service" => service_param = Some(param), + _ => {}, + } + } + + return Some(AuthMethod::Bearer{ + realm: header_str[realm_param.unwrap().value].to_string(), + scope: header_str[scope_param.unwrap().value].to_string(), + service: header_str[service_param.unwrap().value].to_string(), + }); + } + + return None; + } +} + struct AuthInfo { host: String, username: String, @@ -109,687 +151,6 @@ impl AuthState{ } } -#[derive(Debug)] -enum AuthMethod { - Basic, - Bearer{realm: String, service: String, scope: String}, -} - -#[derive(Debug)] -struct Challenge { - scheme: Range<usize>, - params: Vec<AuthParam>, -} - -fn parse_token(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - while str.len() > pos { - match str[pos] { - 'a'..'z' | 'A'..'Z' => pos += 1, - _ => break, - } - } - - if pos == start { - return None; - } - - return Some((start..pos, pos)); -} - -fn parse_qdtext(str: &Vec<char>, start: usize) -> Option<usize> { - match str[start] { - '\x00'..'\x1F' | '"' | '\x7F' => return None, - _ => return Some(start + 1), - } -} - -fn parse_quotedpair(str: &Vec<char>, start: usize) -> Option<usize> { - if str[start] != '\\' { - return None; - } - - return Some(start + 2); -} - -fn parse_quotedstring(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - if str[pos] != '"' { - return None; - } - pos += 1; - - loop { - if let Some(npos) = parse_qdtext(str, pos) { - pos = npos; - } else if let Some(npos) = parse_quotedpair(str, pos) { - pos = npos; - } else { - break; - } - } - - if str[pos] != '"' { - return None; - } - pos += 1; - - return Some((start+1..pos-1, pos)); -} - -fn parse_sp(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - if str.len() <= start { - return None; - } - - let mut pos = start; - match str[pos] { - ' ' => pos += 1, - _ => return None, - } - - if pos == start { - return None; - } - - return Some((start..pos, pos)); -} - -fn parse_crlf(str: &Vec<char>, start: usize) -> Option<usize> { - if str.len() - 2 < start { - return None; - } - - if str[start] == '\r' && str[start+1] == '\n' { - return Some(start + 2); - } - - return None; -} - -fn parse_lws(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - match parse_crlf(str, pos) { - Some(npos) => pos = npos, - None => {}, - } - - match parse_sp(str, pos) { - Some((_, npos)) => pos = npos, - None => return None, - } - loop { - match parse_sp(str, pos) { - Some((_, npos)) => pos = npos, - None => break, - } - } - - return Some((start..pos, pos)); -} - -#[derive(Debug)] -struct AuthParam { - key: Range<usize>, - value: Range<usize>, -} - -fn parse_param(str: &Vec<char>, start: usize) -> Option<(AuthParam, usize)> { - let pos = start; - let (param_range, pos) = parse_token(str, pos)?; - - let mut pos = pos; - if str[pos] == '=' { - pos += 1; - } else { - todo!(); - } - - let value_range; - if let Some((range, npos)) = parse_token(str, pos) { - value_range = range; - pos = npos; - } else if let Some((range, npos)) = parse_quotedstring(str, pos) { - value_range = range; - pos = npos; - } else { - todo!("{:?}", &str[pos..]); - } - - return Some((AuthParam{key: param_range, value: value_range}, pos)); -} - -fn parse_challenge(str: &Vec<char>, start: usize) -> Option<(Challenge, usize)> { - let pos = start; - let (scheme_range, pos) = parse_token(str, pos).unwrap(); - - let (_, pos) = parse_sp(str, pos).unwrap(); - let mut pos = pos; - loop { - match parse_sp(str, pos) { - Some((_, npos)) => pos = npos, - None => break, - } - } - - let mut params = vec!(); - - let mut pos = pos; - loop { - match parse_lws(str, pos) { - Some((_, npos)) => pos = npos, - None => break, - } - } - let (param, pos) = parse_param(str, pos).unwrap(); - params.push(param); - let mut pos = pos; - loop { - loop { - match parse_lws(str, pos) { - Some((_, npos)) => pos = npos, - None => break, - } - } - - if pos >= str.len() || str[pos] != ',' { - break; - } - pos += 1; - - loop { - match parse_lws(str, pos) { - Some((_, npos)) => pos = npos, - None => break, - } - } - - match parse_param(str, pos) { - Some((param, npos)) => { - pos = npos; - params.push(param); - }, - None => return None, - } - } - - if pos < str.len() { - return None; - } - return Some((Challenge {scheme: scheme_range, params}, pos)); -} - -impl AuthMethod { - fn from_header(header: &ureq::http::HeaderValue) -> Option<Self> { - let header_str = header.to_str().unwrap(); - let (parse, _) = parse_challenge(&header_str.chars().collect::<Vec<char>>(), 0).unwrap(); - - if &header_str[parse.scheme.clone()] == "Basic" { - return Some(AuthMethod::Basic); - } - - if &header_str[parse.scheme.clone()] == "Bearer" { - let mut realm_param = None; - let mut scope_param = None; - let mut service_param = None; - for param in parse.params { - match &header_str[param.key.clone()] { - "realm" => realm_param = Some(param), - "scope" => scope_param = Some(param), - "service" => service_param = Some(param), - _ => {}, - } - } - - return Some(AuthMethod::Bearer{ - realm: header_str[realm_param.unwrap().value].to_string(), - scope: header_str[scope_param.unwrap().value].to_string(), - service: header_str[service_param.unwrap().value].to_string(), - }); - } - - return None; - } -} - -#[derive(Debug)] -enum LinkParam { - Rel{relation: Range<usize>}, -} - -impl LinkParam { - fn value(&self) -> &Range<usize> { - match self { - LinkParam::Rel{relation} => &relation, - } - } -} - -#[derive(Debug)] -struct LinkValue { - uri: Range<usize>, - params: Vec<LinkParam>, -} - -fn parse_alpha(str: &Vec<char>, pos: usize) -> Option<(Range<usize>, usize)> { - return match str[pos] { - 'a'..'z' | 'A'..'Z' => Some((pos..pos+1, pos+1)), - _ => None, - } -} - -fn parse_digit(str: &Vec<char>, pos: usize) -> Option<(Range<usize>, usize)> { - return match str[pos] { - '0'..'9' => Some((pos..pos+1, pos+1)), - _ => None, - } -} - -fn parse_scheme(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - let (_, npos) = parse_alpha(str, pos)?; - pos = npos; - - loop { - if let Some((_, npos)) = parse_alpha(str, pos) { - pos = npos; - } else { - break; - } - }; - - return None; -} - -fn parse_authority(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - return None; -} - -fn parse_relative_part_1(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - if str[pos] != '/' && str[pos+1] != '/' { - return None; - } - pos += 2; - - let (_, npos) = parse_authority(str, pos)?; - pos = npos; - - return Some((start..pos, pos)); -} - -fn parse_unreserved(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - if let Some(x) = parse_alpha(str, start) { - return Some(x); - } - - if let Some(x) = parse_digit(str, start) { - return Some(x); - } - - match str[start] { - '-' | '.' | '_' | '~' => Some((start..start+1, start+1)), - _ => None - } -} - -fn parse_sub_delims(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - match str[start] { - '!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' => Some((start..start+1, start+1)), - _ => None - } - -} - -fn parse_pchar(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - if let Some(x) = parse_unreserved(str, start) { - return Some(x); - } - - if let Some(x) = parse_sub_delims(str, start) { - return Some(x); - } - - return None; -} - -fn parse_segment_nz(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - let (_, npos) = parse_pchar(str, pos)?; - pos = npos; - - loop { - if let Some((_, npos)) = parse_pchar(str, pos) { - pos = npos; - } else { - break; - } - } - - return Some((start..pos, pos)); -} - -fn parse_segment(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - loop { - if let Some((_, npos)) = parse_pchar(str, pos) { - pos = npos; - } else { - break; - } - } - - return Some((start..pos, pos)); -} - -fn parse_path_absolute(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - if str[pos] != '/' { - return None; - } - pos += 1; - - if let Some((_, npos)) = parse_segment_nz(str, pos) { - pos = npos; - - loop { - if str[pos] != '/' { - break; - } - - if let Some((_, npos)) = parse_segment(str, pos+1) { - pos = npos; - } else { - break; - } - } - } - - return Some((start..pos, pos)); -} - -fn parse_relative_part(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - if let Some((_, npos)) = parse_relative_part_1(str, pos) { - pos = npos; - return Some((start..pos, pos)); - } - - if let Some((_, npos)) = parse_path_absolute(str, pos) { - pos = npos; - return Some((start..pos, pos)); - } - - return None; -} - -fn parse_query(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - loop { - if let Some((_, npos)) = parse_pchar(str, pos) { - pos = npos; - } else if str[pos] == '/' { - pos += 1; - } else if str[pos] == '?' { - pos += 1; - } else { - break; - } - } - - return Some((start..pos, pos)); -} - -fn parse_fragment(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - return None; -} - -fn parse_relative_ref(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - let (_, npos) = parse_relative_part(str, pos)?; - pos = npos; - - if str[pos] == '?' && let Some((_, npos)) = parse_query(str, pos+1) { - pos = npos; - } - - if str[pos] == '#' && let Some((_, npos)) = parse_fragment(str, pos+1) { - pos = npos; - } - - return Some((start..pos, pos)); -} - -fn parse_uri(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - let(_, npos) = parse_scheme(str, pos)?; - pos = npos; - - return Some((start..pos, pos)); -} - -fn parse_exact_string(str: &Vec<char>, start: usize, to_match: &str) -> Option<(Range<usize>, usize)> { - let mut pos = start; - let end = pos + to_match.len(); - - if end > str.len() { - return None; - } - - if !to_match.chars().eq(str[start..start+to_match.len()].iter().map(|x| *x)) { - return None; - } - pos += to_match.len(); - - return Some((start..pos, pos)); -} - -fn parse_loalpha(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - match str[start] { - 'a'..'z' => Some((start..start+1, start+1)), - _ => None, - } -} - -fn parse_relation_type(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - let (_, npos) = parse_loalpha(str, pos)?; - pos = npos; - - loop { - dbg!(&str[pos..]); - if let Some((_, npos)) = parse_loalpha(str, pos) { - pos = npos; - continue; - } else if let Some((_, npos)) = parse_digit(str, pos) { - pos = npos; - continue; - } else if let Some((_, npos)) = parse_exact_string(str, pos, ".") { - pos = npos; - continue; - } else if let Some((_, npos)) = parse_exact_string(str, pos, "-") { - pos = npos; - continue; - } else { - break; - } - } - - return Some((start..pos, pos)); -} - -fn parse_relation_types(str: &Vec<char>, start: usize) -> Option<(Range<usize>, usize)> { - let mut pos = start; - - if let Some((_, npos)) = parse_relation_type(str, pos) { - pos = npos; - - return Some((start..pos, pos)); - } else if let Some((_, npos)) = parse_exact_string(str, pos, "\"") { - pos = npos; - - if let Some((_, npos)) = parse_relation_type(str, pos) { - pos = npos; - - loop { - if let Some((_, npos)) = parse_sp(str, pos) { - - loop { - if let Some((_, npos)) = parse_sp(str, pos) { - pos = npos; - } else { - break; - } - } - - pos = npos; - if let Some((_, npos)) = parse_relation_type(str, pos) { - pos = npos; - continue; - } - } - break; - } - - if let Some((_, npos)) = parse_exact_string(str, pos, "\"") { - pos = npos; - return Some((start+1..pos-1, pos)); - } - } - } - - return None; -} - -fn parse_link_param(str: &Vec<char>, start: usize) -> Option<(LinkParam, usize)> { - let mut pos = start; - - // Non-standard but seen in the wild - if let Some((_, npos)) = parse_sp(str, pos) { - pos = npos; - } - - if let Some((_, npos)) = parse_exact_string(str, pos, "rel=") { - if let Some((relation, npos)) = parse_relation_types(str, npos) { - pos = npos; - return Some((LinkParam::Rel{relation}, pos)); - } - } - - return None; -} - -fn parse_link_value(str: &Vec<char>, start: usize) -> Option<(LinkValue, usize)> { - let mut pos = start; - - if str[pos] == '<' { - pos += 1; - } else { - return None; - } - - let uri_range; - let mut params = vec!(); - - if let Some((range, npos)) = parse_uri(str, pos) { - uri_range = range; - pos = npos; - } else if let Some((range, npos)) = parse_relative_ref(str, pos) { - uri_range = range; - pos = npos; - } else { - return None; - } - - if str[pos] == '>' { - pos += 1; - } else { - return None; - } - - loop { - if let Some((_, npos)) = parse_exact_string(str, pos, ";") { - pos = npos; - } else { - break; - } - - if let Some((param, npos)) = parse_link_param(str, pos) { - pos = npos; - params.push(param); - } else { - break; - } - } - - return Some((LinkValue{ uri: uri_range, params: params }, pos)); -} - -fn parse_link(str: &Vec<char>, start: usize) -> Option<(Vec<LinkValue>, usize)> { - let mut pos = start; - - let mut values = vec!(); - - loop { - match parse_lws(str, pos) { - Some((_, npos)) => pos = npos, - None => break, - } - } - - let (param, npos) = parse_link_value(str, pos)?; - values.push(param); - pos = npos; - - loop { - let mut ipos = pos; - loop { - match parse_lws(str, ipos) { - Some((_, npos)) => ipos = npos, - None => break, - } - } - - if ipos >= str.len() || str[ipos] != ',' { - break; - } - ipos += 1; - - loop { - match parse_lws(str, ipos) { - Some((_, npos)) => ipos = npos, - None => break, - } - } - - match parse_link_value(str, ipos) { - Some((param, npos)) => { - ipos = npos; - values.push(param); - }, - None => { - break; - }, - } - - pos = ipos; - } - - - return Some((values, pos)); -} - fn perform_registry_request(registry: &str, url: &str, auth: &mut Auth) -> Result<ureq::http::Response<ureq::Body>, ()> { let mut state = auth.states.get_mut(registry); |
