22#include <wx/tokenzr.h>
32wxString queryValue(
const wxString& aQuery,
const wxString& aKey )
34 wxStringTokenizer queryTokenizer( aQuery, wxS(
"&" ) );
36 while( queryTokenizer.HasMoreTokens() )
38 const wxString pair = queryTokenizer.GetNextToken();
39 const int eqPos = pair.Find(
'=' );
41 if( eqPos == wxNOT_FOUND )
44 const wxString
name = pair.Left( eqPos );
45 const wxString value = pair.Mid( eqPos + 1 );
48 return wxURI::Unescape( value );
57 const wxString& aExpectedState ) :
82 addr.Hostname( wxS(
"127.0.0.1" ) );
85 std::unique_ptr<wxSocketServer> server =
86 std::make_unique<wxSocketServer>( addr, wxSOCKET_REUSEADDR );
91 server->SetEventHandler( *
this );
92 server->SetNotify( wxSOCKET_CONNECTION_FLAG );
93 server->Notify(
true );
96 server->GetLocal( local );
114 const wxString& aExpectedPath,
115 const wxString& aExpectedState,
122 wxStringTokenizer tokenizer( aRequestLine, wxS(
" " ) );
124 if( !tokenizer.HasMoreTokens() )
126 aError =
_(
"Missing HTTP method in callback request." );
130 tokenizer.GetNextToken();
132 if( !tokenizer.HasMoreTokens() )
134 aError =
_(
"Missing callback URL in request." );
138 const wxString target = tokenizer.GetNextToken();
139 const int queryPos = target.Find(
'?' );
140 const wxString
path = queryPos == wxNOT_FOUND ? target : target.Left( queryPos );
141 const wxString query = queryPos == wxNOT_FOUND ? wxString() : target.Mid( queryPos + 1 );
143 if(
path != aExpectedPath )
145 aError =
_(
"Unexpected callback path." );
149 wxString state = queryValue( query, wxS(
"state" ) );
151 if( state != aExpectedState )
153 aError =
_(
"OAuth callback state did not match." );
157 aResponse.
state = state;
158 aResponse.
error = queryValue( query, wxS(
"error" ) );
161 if( !aResponse.
error.IsEmpty() )
163 aError =
_(
"Authorization server returned an OAuth error." );
167 aResponse.
code = queryValue( query, wxS(
"code" ) );
169 if( aResponse.
code.IsEmpty() )
171 aError =
_(
"Missing authorization code in callback." );
181 if( !
m_server || aEvent.GetSocketEvent() != wxSOCKET_CONNECTION )
184 std::unique_ptr<wxSocketBase> client(
m_server->Accept(
false ) );
193 wxUnusedVar( aEvent );
203 aClient->SetTimeout( 5 );
204 aClient->SetFlags( wxSOCKET_NONE );
207 request.reserve( 512 );
210 while( aClient->IsConnected() )
212 if( !aClient->WaitForRead( 1, 0 ) )
215 aClient->Read( buffer,
sizeof( buffer ) );
216 const size_t count = aClient->LastCount();
221 request.append( buffer, count );
223 if( request.find(
"\r\n\r\n" ) != std::string::npos || request.size() > 4096 )
227 const wxString requestWx = wxString::FromUTF8( request.data(), request.size() );
228 const int endOfLine = requestWx.Find( wxS(
"\r\n" ) );
229 const wxString requestLine = endOfLine == wxNOT_FOUND ? requestWx : requestWx.Mid( 0, endOfLine );
249 const wxString body = aSuccess
250 ? wxS(
"<!DOCTYPE html><html><body><p>Authentication complete.</p></body></html>" )
251 : wxS(
"<!DOCTYPE html><html><body><p>Authentication failed.</p></body></html>" );
252 wxScopedCharBuffer bodyUtf8 = body.ToUTF8();
255 response << wxS(
"HTTP/1.1 200 OK\r\n" )
256 << wxS(
"Content-Type: text/html; charset=utf-8\r\n" )
257 << wxS(
"Cache-Control: no-store\r\n" )
258 << wxS(
"Connection: close\r\n" )
259 << wxS(
"Content-Length: " ) << bodyUtf8.length() << wxS(
"\r\n\r\n" );
261 wxScopedCharBuffer header = response.ToUTF8();
262 aClient->Write( header.data(), header.length() );
263 aClient->Write( bodyUtf8.data(), bodyUtf8.length() );
278 wxCommandEvent evt( EVT_OAUTH_LOOPBACK_RESULT );
279 evt.SetInt( aSuccess ? 1 : 0 );
284 wxQueueEvent(
m_owner, evt.Clone() );
static bool ParseAuthorizationResponse(const wxString &aRequestLine, const wxString &aExpectedPath, const wxString &aExpectedState, OAUTH_AUTHORIZATION_RESPONSE &aResponse, wxString &aError)
~OAUTH_LOOPBACK_SERVER() override
void HandleClient(wxSocketBase *aClient)
OAUTH_LOOPBACK_SERVER(wxEvtHandler *aOwner, const wxString &aCallbackPath, const wxString &aExpectedState)
void OnSocketEvent(wxSocketEvent &aEvent)
void OnTimeout(wxTimerEvent &aEvent)
void SendHttpResponse(wxSocketBase *aClient, bool aSuccess)
wxString GetRedirectUri() const
std::optional< OAUTH_AUTHORIZATION_RESPONSE > m_response
void Finish(bool aSuccess)
std::unique_ptr< wxSocketServer > m_server
wxDEFINE_EVENT(EVT_OAUTH_LOOPBACK_RESULT, wxCommandEvent)
wxString error_description