Files
kicad-source-mirror/common/remote_provider_utils.cpp
Seth Hillbrand 001c196e7f UrlEncode: percent-encode UTF-8 bytes, not Unicode code points
RFC 3986 requires percent-encoding individual UTF-8 bytes.  The
previous implementation iterated wxUniChar code points, producing
incorrect output for non-ASCII characters (e.g. é became %E9 instead
of %C3%A9, and multi-byte characters like emoji produced invalid
percent-encoding with more than two hex digits).
2026-03-11 13:42:54 -07:00

144 lines
4.0 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <remote_provider_utils.h>
#include <wx/intl.h>
#include <wx/uri.h>
wxString UrlEncode( const wxString& aValue )
{
wxString encoded;
const wxScopedCharBuffer utf8 = aValue.ToUTF8();
const char* p = utf8.data();
size_t len = utf8.length();
for( size_t i = 0; i < len; ++i )
{
unsigned char byte = static_cast<unsigned char>( p[i] );
if( ( byte >= 'A' && byte <= 'Z' ) || ( byte >= 'a' && byte <= 'z' )
|| ( byte >= '0' && byte <= '9' ) || byte == '-' || byte == '_'
|| byte == '.' || byte == '~' )
{
encoded.Append( static_cast<char>( byte ) );
}
else
{
encoded.Append( wxString::Format( wxS( "%%%02X" ), byte ) );
}
}
return encoded;
}
wxString RemoteProviderJsonString( const nlohmann::json& aObject, const char* aKey )
{
auto it = aObject.find( aKey );
if( it != aObject.end() && it->is_string() )
return wxString::FromUTF8( it->get_ref<const std::string&>().c_str() );
return wxString();
}
bool IsLoopbackHost( const wxString& aHost )
{
wxString host = aHost.Lower();
if( host.StartsWith( wxS( "[" ) ) && host.EndsWith( wxS( "]" ) ) )
{
host.RemoveLast();
host.Remove( 0, 1 );
}
return host == wxS( "localhost" ) || host == wxS( "127.0.0.1" ) || host == wxS( "::1" );
}
bool ValidateRemoteUrlSecurity( const wxString& aUrl, bool aAllowInsecureLocalhost,
wxString& aError, const wxString& aLabel )
{
if( aUrl.IsEmpty() )
return true;
wxURI uri( aUrl );
wxString scheme = uri.GetScheme().Lower();
if( scheme == wxS( "https" ) )
return true;
if( scheme == wxS( "http" ) && aAllowInsecureLocalhost && IsLoopbackHost( uri.GetServer() ) )
return true;
aError = wxString::Format(
_( "%s must use HTTPS unless allow_insecure_localhost is enabled for a loopback URL." ),
aLabel );
return false;
}
wxString NormalizedUrlOrigin( const wxString& aUrl )
{
if( aUrl.IsEmpty() )
return wxString();
wxURI uri( aUrl );
if( !uri.HasScheme() || !uri.HasServer() )
return wxString();
wxString scheme = uri.GetScheme().Lower();
wxString host = uri.GetServer().Lower();
wxString port = uri.GetPort();
if( port.IsEmpty() )
{
if( scheme == wxS( "https" ) )
port = wxS( "443" );
else if( scheme == wxS( "http" ) )
port = wxS( "80" );
}
return scheme + wxS( "://" ) + host + wxS( ":" ) + port;
}
void COLLECTING_JSON_ERROR_HANDLER::error( const nlohmann::json::json_pointer& aPointer,
const nlohmann::json& aInstance,
const std::string& aMessage )
{
wxUnusedVar( aInstance );
m_errors.emplace_back( wxString::Format( wxS( "%s: %s" ),
wxString::FromUTF8( aPointer.to_string().c_str() ),
wxString::FromUTF8( aMessage.c_str() ) ) );
}
wxString COLLECTING_JSON_ERROR_HANDLER::FirstError() const
{
if( m_errors.empty() )
return wxString();
return m_errors.front();
}