1use crate::*;
20use std::collections::HashMap;
21
22pub type RequestToken = AccessToken;
24
25pub fn create_request_token_request(
29 uri: Uri,
30 callback: Option<Uri>,
31 consumer: ConsumerCredentials,
32 extra_params: Box<[Parameter]>,
33) -> Result<OAuthRequest, Box<dyn std::error::Error>> {
34 OAuthRequest::builder(
35 uri,
36 AuthenticationLevel::Consumer(consumer),
37 Box::new(signature::HmacSha1Signature),
38 )
39 .add_auth_parameters(&[Parameter::new(
40 "oauth_callback",
41 callback
42 .as_ref()
43 .map(Uri::to_string)
44 .unwrap_or("oob".to_string()),
45 )])
46 .add_auth_parameters(&extra_params)
47 .build()
48}
49
50pub fn get_request_token_from_response(
52 response: &str,
53) -> Result<(RequestToken, HashMap<String, String>), Box<dyn std::error::Error>> {
54 let mut parameters = HashMap::new();
55
56 for parameter in response.split('&') {
57 let (key, value) = parameter
58 .split_once('=')
59 .ok_or(OAuthError::InvalidResponse)?;
60 let key = percent_encoding::decode_string(key)?;
61 let value = percent_encoding::decode_string(value)?;
62
63 parameters.insert(key, value);
64 }
65
66 match parameters.remove("oauth_callback_confirmed") {
67 Some(c) if c == "true" => Ok(()),
68 None | Some(_) => Err(OAuthError::CallbackNotConfirmed),
69 }?;
70 let token = parameters
71 .remove("oauth_token")
72 .ok_or(OAuthError::InvalidResponse)?;
73 let secret = parameters
74 .remove("oauth_token_secret")
75 .ok_or(OAuthError::InvalidResponse)?;
76
77 Ok((RequestToken::new(&token, &secret), parameters))
78}
79
80pub fn create_request_uri(
82 uri: Uri,
83 token: RequestToken,
84 extra_params: Vec<Parameter>,
85) -> Result<Uri, Box<dyn std::error::Error>> {
86 let mut params = extra_params;
88
89 params.push(Parameter::new("oauth_token", token.token));
90
91 params.sort();
92
93 let mut uri = uri.into_parts();
94
95 let params = params
96 .iter()
97 .map(|p| p.encoded())
98 .intersperse(String::from('&'))
99 .collect::<String>();
100
101 match uri.path_and_query {
102 None => uri.path_and_query = Some(PathAndQuery::from_str(&(String::from("?") + ¶ms))?),
103 Some(pq) => match pq.query() {
104 None => {
105 uri.path_and_query = Some(PathAndQuery::from_str(&format!(
106 "{}?{}",
107 pq.path(),
108 params
109 ))?)
110 }
111 Some(q) => {
112 uri.path_and_query = Some(PathAndQuery::from_str(&format!(
113 "{}?{}&{}",
114 pq.path(),
115 q,
116 params
117 ))?)
118 }
119 },
120 };
121
122 Ok(Uri::from_parts(uri)?)
123}
124
125pub fn create_token_exchange_request(
127 uri: Uri,
128 verifier: String,
129 request_token: RequestToken,
130 consumer: ConsumerCredentials,
131 extra_params: Box<[Parameter]>,
132) -> Result<OAuthRequest, Box<dyn std::error::Error>> {
133 OAuthRequest::builder(
134 uri,
135 AuthenticationLevel::Token(consumer, request_token),
136 Box::new(HmacSha1Signature),
137 )
138 .add_auth_parameters(&[Parameter::new("oauth_verifier", verifier)])
139 .add_auth_parameters(&extra_params)
140 .build()
141}
142
143pub fn get_access_token_from_response(
145 response: &str,
146) -> Result<(AccessToken, HashMap<String, String>), Box<dyn std::error::Error>> {
147 let mut parameters = HashMap::new();
148
149 for parameter in response.split('&') {
150 let (key, value) = parameter
151 .split_once('=')
152 .ok_or(OAuthError::InvalidResponse)?;
153 let key = percent_encoding::decode_string(key)?;
154 let value = percent_encoding::decode_string(value)?;
155
156 parameters.insert(key, value);
157 }
158
159 let token = parameters
160 .remove("oauth_token")
161 .ok_or(OAuthError::InvalidResponse)?;
162 let secret = parameters
163 .remove("oauth_token_secret")
164 .ok_or(OAuthError::InvalidResponse)?;
165
166 Ok((AccessToken::new(&token, &secret), parameters))
167}