- fixed: windows http get exceptions stop cleanup.

- made premium auth errors more tidy.
This commit is contained in:
Nick Bolton 2014-02-05 16:28:29 +00:00
parent 5ca1c17549
commit 456e56d5dc
4 changed files with 179 additions and 56 deletions

View File

@ -24,7 +24,7 @@
// we use syntool to authenticate because Qt's http library is very // we use syntool to authenticate because Qt's http library is very
// unreliable, and since we're writing platform specific code, use the // unreliable, and since we're writing platform specific code, use the
// synergy code since there we can use integ tests. // synergy code since there we can use integ tests.
QString PremiumAuth::auth(const QString& email, const QString& password) QString PremiumAuth::request(const QString& email, const QString& password)
{ {
QString program(QCoreApplication::applicationDirPath() + "/syntool"); QString program(QCoreApplication::applicationDirPath() + "/syntool");
QStringList args("--premium-auth"); QStringList args("--premium-auth");
@ -40,7 +40,7 @@ QString PremiumAuth::auth(const QString& email, const QString& password)
process.write(credentials.toStdString().c_str()); process.write(credentials.toStdString().c_str());
if (process.waitForFinished()) { if (process.waitForFinished()) {
return process.readLine(); return process.readAll();
} }
} }

View File

@ -22,5 +22,5 @@
class PremiumAuth class PremiumAuth
{ {
public: public:
QString auth(const QString& email, const QString& password); QString request(const QString& email, const QString& password);
}; };

View File

@ -253,13 +253,17 @@ bool SetupWizard::isPremiumLoginValid(QMessageBox& message)
QString password = m_pLineEditPremiumPassword->text(); QString password = m_pLineEditPremiumPassword->text();
PremiumAuth auth; PremiumAuth auth;
QString responseJson = auth.auth(email, password); QString responseJson = auth.request(email, password);
// this feels like a lot of work, but its cheaper than getting a json if (responseJson.trimmed() == "") {
// parsing library involved. message.setText(tr("Login failed, could not communicate with server."));
QRegExp regex(".*\"result\":\\s*([^,}\\s]+).*"); message.exec();
if (regex.exactMatch(responseJson)) { return false;
QString boolString = regex.cap(1); }
QRegExp resultRegex(".*\"result\".*:.*(true|false).*");
if (resultRegex.exactMatch(responseJson)) {
QString boolString = resultRegex.cap(1);
if (boolString == "true") { if (boolString == "true") {
return true; return true;
} }
@ -269,8 +273,20 @@ bool SetupWizard::isPremiumLoginValid(QMessageBox& message)
return false; return false;
} }
} }
else {
QRegExp errorRegex(".*\"error\".*:.*\"(.+)\".*");
if (errorRegex.exactMatch(responseJson)) {
message.setText(tr("Login failed, an error occurred.\n\nServer response:\n\n%1").arg(responseJson.trimmed())); // replace "\n" with real new lines.
QString error = errorRegex.cap(1).replace("\\n", "\n");
message.setText(tr("Login failed, an error occurred.\n\n%1").arg(error));
message.exec();
return false;
}
}
message.setText(tr("Login failed, an error occurred.\n\nServer response:\n\n%1").arg(responseJson));
message.exec(); message.exec();
return false; return false;
} }

View File

@ -19,61 +19,91 @@
#include "CArch.h" #include "CArch.h"
#include "Version.h" #include "Version.h"
#include "XArchWindows.h" #include "XArchWindows.h"
#include <sstream> #include <sstream>
#include <Wininet.h> #include <Wininet.h>
struct CWinINetUrl {
CString m_scheme;
CString m_host;
CString m_path;
INTERNET_PORT m_port;
DWORD m_flags;
};
class CWinINetRequest {
public:
CWinINetRequest(const CString& url);
~CWinINetRequest();
CString send();
void openSession();
void connect();
void openRequest();
private:
HINTERNET m_session;
HINTERNET m_connect;
HINTERNET m_request;
CWinINetUrl m_url;
bool m_used;
};
//
// CArchInternetWindows
//
CString CString
CArchInternetWindows::get(const CString& url) CArchInternetWindows::get(const CString& url)
{ {
std::stringstream userAgent; CWinINetRequest request(url);
userAgent << "Synergy "; return request.send();
userAgent << kVersion; }
HINTERNET session = InternetOpen( //
userAgent.str().c_str(), // CWinINetRequest
INTERNET_OPEN_TYPE_PRECONFIG, //
NULL, NULL, NULL);
if (session == NULL) { static CWinINetUrl parseUrl(const CString& url);
throw XArch(new XArchEvalWindows());
CWinINetRequest::CWinINetRequest(const CString& url) :
m_session(NULL),
m_connect(NULL),
m_request(NULL),
m_used(false),
m_url(parseUrl(url))
{
}
CWinINetRequest::~CWinINetRequest()
{
if (m_request != NULL) {
InternetCloseHandle(m_request);
} }
// InternetCrackUrl didn't seem to work too well, this isn't quite if (m_connect != NULL) {
// as robust, but it should do just fine for basic URLs. InternetCloseHandle(m_connect);
size_t schemeEnd = url.find("://");
size_t hostEnd = url.find('/', schemeEnd + 3);
CString scheme = url.substr(0, schemeEnd);
CString host = url.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
CString path = url.substr(hostEnd);
INTERNET_PORT port = INTERNET_DEFAULT_HTTP_PORT;
DWORD requestFlags = 0;
if (scheme.find("https") != CString::npos) {
port = INTERNET_DEFAULT_HTTPS_PORT;
requestFlags = INTERNET_FLAG_SECURE;
} }
HINTERNET connect = InternetConnect( if (m_session != NULL) {
session, host.c_str(), port, NULL, NULL, InternetCloseHandle(m_session);
INTERNET_SERVICE_HTTP, NULL, NULL); }
}
CString
CWinINetRequest::send()
{
if (m_used) {
throw XArch("class is one time use.");
}
m_used = true;
openSession();
connect();
openRequest();
if (connect == NULL) {
throw XArch(new XArchEvalWindows());
}
HINTERNET request = HttpOpenRequest(
connect, "GET", path.c_str(),
HTTP_VERSION, NULL,
NULL, requestFlags, NULL);
if (request == NULL) {
throw XArch(new XArchEvalWindows());
}
CString headers("Content-Type: text/html"); CString headers("Content-Type: text/html");
if (!HttpSendRequest(request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) { if (!HttpSendRequest(m_request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) {
int error = GetLastError();
throw XArch(new XArchEvalWindows()); throw XArch(new XArchEvalWindows());
} }
@ -81,15 +111,92 @@ CArchInternetWindows::get(const CString& url)
CHAR buffer[1025]; CHAR buffer[1025];
DWORD read = 0; DWORD read = 0;
while (InternetReadFile(request, buffer, sizeof(buffer) - 1, &read) && (read != 0)) { while (InternetReadFile(m_request, buffer, sizeof(buffer) - 1, &read) && (read != 0)) {
buffer[read] = 0; buffer[read] = 0;
result << buffer; result << buffer;
read = 0; read = 0;
} }
InternetCloseHandle(request);
InternetCloseHandle(connect);
InternetCloseHandle(session);
return result.str(); return result.str();
} }
void
CWinINetRequest::openSession()
{
std::stringstream userAgent;
userAgent << "Synergy ";
userAgent << kVersion;
m_session = InternetOpen(
userAgent.str().c_str(),
INTERNET_OPEN_TYPE_PRECONFIG,
NULL,
NULL,
NULL);
if (m_session == NULL) {
throw XArch(new XArchEvalWindows());
}
}
void
CWinINetRequest::connect()
{
m_connect = InternetConnect(
m_session,
m_url.m_host.c_str(),
m_url.m_port,
NULL,
NULL,
INTERNET_SERVICE_HTTP,
NULL,
NULL);
if (m_connect == NULL) {
throw XArch(new XArchEvalWindows());
}
}
void
CWinINetRequest::openRequest()
{
m_request = HttpOpenRequest(
m_connect,
"GET",
m_url.m_path.c_str(),
HTTP_VERSION,
NULL,
NULL,
m_url.m_flags,
NULL);
if (m_request == NULL) {
throw XArch(new XArchEvalWindows());
}
}
// nb: i tried to use InternetCrackUrl here, but couldn't quite get that to
// work. here's some (less robust) code to split the url into components.
// this works fine with simple urls, but doesn't consider the full url spec.
static CWinINetUrl
parseUrl(const CString& url)
{
CWinINetUrl parsed;
size_t schemeEnd = url.find("://");
size_t hostEnd = url.find('/', schemeEnd + 3);
parsed.m_scheme = url.substr(0, schemeEnd);
parsed.m_host = url.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
parsed.m_path = url.substr(hostEnd);
parsed.m_port = INTERNET_DEFAULT_HTTP_PORT;
parsed.m_flags = 0;
if (parsed.m_scheme.find("https") != CString::npos) {
parsed.m_port = INTERNET_DEFAULT_HTTPS_PORT;
parsed.m_flags = INTERNET_FLAG_SECURE;
}
return parsed;
}