From 28a6b16875e085fc29a374a93ce9250342c5fc57 Mon Sep 17 00:00:00 2001 From: Nick Bolton Date: Fri, 14 Mar 2014 20:34:19 +0000 Subject: [PATCH] fixed: Bug #3933 - Plus signs in the email address cause premium login to fail --- CMakeLists.txt | 2 +- src/lib/arch/unix/ArchInternetUnix.cpp | 126 ++++++++++++------ src/lib/arch/unix/ArchInternetUnix.h | 1 + src/lib/arch/win32/ArchInternetWindows.cpp | 22 +++ src/lib/arch/win32/ArchInternetWindows.h | 1 + src/lib/base/String.cpp | 31 +++++ src/lib/base/String.h | 2 + src/lib/synergy/ToolApp.cpp | 2 +- .../integtests/arch/ArchInternetTests.cpp | 7 + 9 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 src/lib/base/String.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c3ef929..3b0c9364 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -298,7 +298,7 @@ if (UNIX) else() # not-unix - list(APPEND libs Wtsapi32 Userenv Wininet comsuppw) + list(APPEND libs Wtsapi32 Userenv Wininet comsuppw Shlwapi) add_definitions( /DWIN32 diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp index cdbd44f5..6f832f53 100644 --- a/src/lib/arch/unix/ArchInternetUnix.cpp +++ b/src/lib/arch/unix/ArchInternetUnix.cpp @@ -24,6 +24,39 @@ #include #include +class CurlFacade { +public: + CurlFacade(); + ~CurlFacade(); + CString get(const CString& url); + CString urlEncode(const CString& url); + +private: + CURL* m_curl; +}; + +// +// CArchInternetUnix +// + +CString +CArchInternetUnix::get(const CString& url) +{ + CurlFacade curl; + return curl.get(url); +} + +CString +CArchInternetUnix::urlEncode(const CString& url) +{ + CurlFacade curl; + return curl.urlEncode(url); +} + +// +// CurlFacade +// + static size_t curlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp) { @@ -31,47 +64,64 @@ curlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp) return size * nmemb; } -CString -CArchInternetUnix::get(const CString& url) +CurlFacade::CurlFacade() : + m_curl(NULL) { - std::string result; - curl_global_init(CURL_GLOBAL_DEFAULT); - - try { - CURL *curl = curl_easy_init(); - if (curl == NULL) { - throw XArch("CURL init failed."); - } - - try { - std::stringstream userAgent; - userAgent << "Synergy "; - userAgent << kVersion; - - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.str().c_str()); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteCallback); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); - - CURLcode code = curl_easy_perform(curl); - if (code != CURLE_OK) { - LOG((CLOG_ERR "curl perform error: %s", curl_easy_strerror(code))); - throw XArch("CURL perform failed."); - } - - curl_easy_cleanup(curl); - } - catch (...) { - curl_easy_cleanup(curl); - throw; - } - - curl_global_cleanup(); + CURLcode init = curl_global_init(CURL_GLOBAL_ALL); + if (init != CURLE_OK) { + throw XArch("CURL global init failed."); } - catch (...) { - curl_global_cleanup(); - throw; + + m_curl = curl_easy_init(); + if (m_curl == NULL) { + throw XArch("CURL easy init failed."); + } +} + +CurlFacade::~CurlFacade() +{ + if (m_curl != NULL) { + curl_easy_cleanup(m_curl); + } + + curl_global_cleanup(); +} + +CString +CurlFacade::get(const CString& url) +{ + curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curlWriteCallback); + + std::stringstream userAgent; + userAgent << "Synergy "; + userAgent << kVersion; + curl_easy_setopt(m_curl, CURLOPT_USERAGENT, userAgent.str().c_str()); + + std::string result; + curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &result); + + CURLcode code = curl_easy_perform(m_curl); + if (code != CURLE_OK) { + LOG((CLOG_ERR "curl perform error: %s", curl_easy_strerror(code))); + throw XArch("CURL perform failed."); } return result; } + +CString +CurlFacade::urlEncode(const CString& url) +{ + char* resultCStr = curl_easy_escape(m_curl, url.c_str(), 0); + + if (resultCStr == NULL) { + curl_free(resultCStr); + throw XArch("CURL escape failed."); + } + + std::string result(resultCStr); + curl_free(resultCStr); + + return result; +} diff --git a/src/lib/arch/unix/ArchInternetUnix.h b/src/lib/arch/unix/ArchInternetUnix.h index e83069e9..2e70a500 100644 --- a/src/lib/arch/unix/ArchInternetUnix.h +++ b/src/lib/arch/unix/ArchInternetUnix.h @@ -24,4 +24,5 @@ class CArchInternetUnix { public: CString get(const CString& url); + CString urlEncode(const CString& url); }; diff --git a/src/lib/arch/win32/ArchInternetWindows.cpp b/src/lib/arch/win32/ArchInternetWindows.cpp index bf35ab2c..a23095e6 100644 --- a/src/lib/arch/win32/ArchInternetWindows.cpp +++ b/src/lib/arch/win32/ArchInternetWindows.cpp @@ -22,6 +22,7 @@ #include #include +#include struct CWinINetUrl { CString m_scheme; @@ -60,6 +61,27 @@ CArchInternetWindows::get(const CString& url) return request.send(); } +CString +CArchInternetWindows::urlEncode(const CString& url) +{ + TCHAR buffer[1024]; + DWORD bufferSize = sizeof(buffer); + + if (UrlEscape(url.c_str(), buffer, &bufferSize, URL_ESCAPE_UNSAFE) != S_OK) { + throw XArch(new XArchEvalWindows()); + } + + CString result(buffer); + + // the win32 url encoding funcitons are pretty useless (to us) and only + // escape "unsafe" chars, but not + or =, so we need to replace these + // manually (and probably many other chars). + find_replace_all(result, "+", "%2B"); + find_replace_all(result, "=", "%3D"); + + return result; +} + // // CWinINetRequest // diff --git a/src/lib/arch/win32/ArchInternetWindows.h b/src/lib/arch/win32/ArchInternetWindows.h index 01fc9cbd..661d0964 100644 --- a/src/lib/arch/win32/ArchInternetWindows.h +++ b/src/lib/arch/win32/ArchInternetWindows.h @@ -24,4 +24,5 @@ class CArchInternetWindows { public: CString get(const CString& url); + CString urlEncode(const CString& url); }; diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp new file mode 100644 index 00000000..cea40b0d --- /dev/null +++ b/src/lib/base/String.cpp @@ -0,0 +1,31 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2014 Bolton Software Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file COPYING that should have accompanied this file. + * + * This package 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 . + */ + +#include "base/String.h" + +void +find_replace_all( + CString& subject, + const CString& find, + const CString& replace) +{ + size_t pos = 0; + while ((pos = subject.find(find, pos)) != CString::npos) { + subject.replace(pos, find.length(), replace); + pos += replace.length(); + } +} diff --git a/src/lib/base/String.h b/src/lib/base/String.h index eae7c2fc..670b3f48 100644 --- a/src/lib/base/String.h +++ b/src/lib/base/String.h @@ -23,3 +23,5 @@ // use standard C++ string class for our string class typedef std::string CString; + +void find_replace_all(CString& subject, const CString& find, const CString& replace); diff --git a/src/lib/synergy/ToolApp.cpp b/src/lib/synergy/ToolApp.cpp index 6ab2fb91..c2935487 100644 --- a/src/lib/synergy/ToolApp.cpp +++ b/src/lib/synergy/ToolApp.cpp @@ -44,7 +44,7 @@ CToolApp::run(int argc, char** argv) std::stringstream ss; ss << PREMIUM_AUTH_URL; - ss << "?email=" << email; + ss << "?email=" << ARCH->internet().urlEncode(email); ss << "&password=" << password; std::cout << ARCH->internet().get(ss.str()) << std::endl; diff --git a/src/test/integtests/arch/ArchInternetTests.cpp b/src/test/integtests/arch/ArchInternetTests.cpp index ee2aa27a..a77909af 100644 --- a/src/test/integtests/arch/ArchInternetTests.cpp +++ b/src/test/integtests/arch/ArchInternetTests.cpp @@ -28,3 +28,10 @@ TEST(ArchInternetTests, get) CString result = internet.get(TEST_URL); ASSERT_EQ("Hello world!", result); } + +TEST(ArchInternetTests, urlEncode) +{ + ARCH_INTERNET internet; + CString result = internet.urlEncode("hello=+&world"); + ASSERT_EQ("hello%3D%2B%26world", result); +}