Page Menu
Home
Phabricator (Chris)
Search
Configure Global Search
Log In
Files
F103653
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
40 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/Guitar.pro b/Guitar.pro
index 6c49827..44dabd8 100644
--- a/Guitar.pro
+++ b/Guitar.pro
@@ -1,440 +1,440 @@
QT += core gui widgets svg network
win32:QT += winextras
CONFIG(debug,debug|release):TARGET = Guitard
CONFIG(release,debug|release):TARGET = Guitar
TEMPLATE = app
-CONFIG += c++17
+CONFIG += c++17 nostrip debug_info
DESTDIR = $$PWD/_bin
TRANSLATIONS = $$PWD/src/resources/translations/Guitar_ja.ts
TRANSLATIONS += $$PWD/src/resources/translations/Guitar_ru.ts
TRANSLATIONS += $$PWD/src/resources/translations/Guitar_zh-CN.ts
TRANSLATIONS += $$PWD/src/resources/translations/Guitar_zh-TW.ts
DEFINES += APP_GUITAR
DEFINES += HAVE_POSIX_OPENPT
macx:DEFINES += HAVE_SYS_TIME_H
macx:DEFINES += HAVE_UTMPX
gcc:QMAKE_CXXFLAGS += -Wall -Wextra -Werror=return-type -Werror=trigraphs -Wno-switch -Wno-reorder
linux:QMAKE_RPATHDIR += $ORIGIN
macx:QMAKE_RPATHDIR += @executable_path/../Frameworks
-linux:QTPLUGIN += ibusplatforminputcontextplugin
+#linux:QTPLUGIN += ibusplatforminputcontextplugin
#linux:QTPLUGIN += fcitxplatforminputcontextplugin
INCLUDEPATH += $$PWD/src
INCLUDEPATH += $$PWD/src/texteditor
win32:INCLUDEPATH += $$PWD/misc/winpty
win32:LIBS += $$PWD/misc/winpty/winpty.lib
# OpenSSL
linux {
static_link_openssl {
LIBS += $$OPENSSL_LIB_DIR/libssl.a $$OPENSSL_LIB_DIR/libcrypto.a -ldl
} else {
LIBS += -lssl -lcrypto
}
}
haiku:LIBS += -lssl -lcrypto -lnetwork
macx:INCLUDEPATH += /usr/local/include
macx:LIBS += /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a
win32:msvc {
INCLUDEPATH += "C:\Program Files (x86)\OpenSSL\include"
INCLUDEPATH += $$PWD/../zlib
LIBS += "-LC:\Program Files (x86)\OpenSSL\lib"
# OpenSSL 1.0
# LIBS += -llibeay32 -lssleay32
# OpenSSL 1.1
LIBS += -llibcrypto -llibssl
}
win32:gcc {
INCLUDEPATH += C:\Qt\Tools\mingw530_32\opt\include
LIBS += -LC:\Qt\Tools\mingw530_32\opt\lib
LIBS += -lcrypto -lssl
}
# execute 'ruby prepare.rb' automatically
prepare.target = prepare
prepare.commands = cd $$PWD && ruby -W0 prepare.rb
QMAKE_EXTRA_TARGETS += prepare
PRE_TARGETDEPS += prepare
# zlib
win32:msvc {
CONFIG(debug, debug|release):LIBS += $$PWD/_bin/libz.lib
CONFIG(release, debug|release):LIBS += $$PWD/_bin/libz.lib
}
win32:gcc {
CONFIG(debug, debug|release):LIBS += $$PWD/_bin/liblibz.a
CONFIG(release, debug|release):LIBS += $$PWD/_bin/liblibz.a
}
!haiku {
use_system_zlib {
unix:LIBS += -lz
} else {
unix:CONFIG(debug, debug|release):LIBS += $$PWD/_bin/libzd.a
unix:CONFIG(release, debug|release):LIBS += $$PWD/_bin/libz.a
}
}
haiku:LIBS += -lz
win32 {
LIBS += -ladvapi32 -lshell32 -luser32 -lws2_32
RC_FILE = win.rc
QMAKE_SUBSYSTEM_SUFFIX=,5.01
}
macx {
QMAKE_INFO_PLIST = Info.plist
ICON += src/resources/Guitar.icns
t.path=Contents/Resources
QMAKE_BUNDLE_DATA += t
}
SOURCES += \
src/AboutDialog.cpp \
src/AbstractProcess.cpp \
src/AbstractSettingForm.cpp \
src/ApplicationGlobal.cpp \
src/AreYouSureYouWantToContinueConnectingDialog.cpp \
src/AvatarLoader.cpp \
src/BasicMainWindow.cpp \
src/BasicRepositoryDialog.cpp \
src/BigDiffWindow.cpp \
src/BlameWindow.cpp \
src/CheckoutDialog.cpp \
src/CherryPickDialog.cpp \
src/ClearButton.cpp \
src/CloneDialog.cpp \
src/CloneFromGitHubDialog.cpp \
src/CommitDialog.cpp \
src/CommitExploreWindow.cpp \
src/CommitPropertyDialog.cpp \
src/CommitViewWindow.cpp \
src/ConfigCredentialHelperDialog.cpp \
src/ConfigSigningDialog.cpp \
src/CreateRepositoryDialog.cpp \
src/DeleteBranchDialog.cpp \
src/DeleteTagsDialog.cpp \
src/DialogHeaderFrame.cpp \
src/DirectoryLineEdit.cpp \
src/DoYouWantToInitDialog.cpp \
src/EditGitIgnoreDialog.cpp \
src/EditRemoteDialog.cpp \
src/EditTagsDialog.cpp \
src/ExperimentDialog.cpp \
src/FileDiffSliderWidget.cpp \
src/FileDiffWidget.cpp \
src/FileHistoryWindow.cpp \
src/FilePropertyDialog.cpp \
src/FileUtil.cpp \
src/FileViewWidget.cpp \
src/FilesListWidget.cpp \
src/FindCommitDialog.cpp \
src/Git.cpp \
src/GitDiff.cpp \
src/GitHubAPI.cpp \
src/GitObjectManager.cpp \
src/GitPack.cpp \
src/GitPackIdxV2.cpp \
src/HyperLinkLabel.cpp \
src/ImageViewWidget.cpp \
src/InputNewTagDialog.cpp \
src/JumpDialog.cpp \
src/Languages.cpp \
src/LineEditDialog.cpp \
src/LocalSocketReader.cpp \
src/LogTableWidget.cpp \
src/MainWindow.cpp \
src/MaximizeButton.cpp \
src/MemoryReader.cpp \
src/MenuButton.cpp \
src/MergeDialog.cpp \
src/MyImageViewWidget.cpp \
src/MyProcess.cpp \
src/MySettings.cpp \
src/MyTableWidgetDelegate.cpp \
src/MyTextEditorWidget.cpp \
src/MyToolButton.cpp \
src/ObjectBrowserDialog.cpp \
src/Photoshop.cpp \
src/PushDialog.cpp \
src/ReadOnlyLineEdit.cpp \
src/ReadOnlyPlainTextEdit.cpp \
src/ReflogWindow.cpp \
src/RemoteAdvancedOptionWidget.cpp \
src/RemoteRepositoriesTableWidget.cpp \
src/RemoteWatcher.cpp \
src/RepositoriesTreeWidget.cpp \
src/RepositoryData.cpp \
src/RepositoryInfoFrame.cpp \
src/RepositoryLineEdit.cpp \
src/RepositoryPropertyDialog.cpp \
src/SearchFromGitHubDialog.cpp \
src/SelectCommandDialog.cpp \
src/SelectGpgKeyDialog.cpp \
src/SelectItemDialog.cpp \
src/SetGlobalUserDialog.cpp \
src/SetGpgSigningDialog.cpp \
src/SetRemoteUrlDialog.cpp \
src/SetUserDialog.cpp \
src/SettingBehaviorForm.cpp \
src/SettingExampleForm.cpp \
src/SettingGeneralForm.cpp \
src/SettingNetworkForm.cpp \
src/SettingProgramsForm.cpp \
src/SettingsDialog.cpp \
src/StatusLabel.cpp \
src/SubmoduleAddDialog.cpp \
src/SubmoduleUpdateDialog.cpp \
src/SubmodulesDialog.cpp \
src/Terminal.cpp \
src/TextEditDialog.cpp \
src/Theme.cpp \
src/UserEvent.cpp \
src/WelcomeWizardDialog.cpp \
src/charvec.cpp \
src/common/joinpath.cpp \
src/common/misc.cpp \
src/darktheme/DarkStyle.cpp \
src/darktheme/NinePatch.cpp \
src/darktheme/StandardStyle.cpp \
src/darktheme/TraditionalWindowsStyleTreeControl.cpp \
src/gpg.cpp \
src/gunzip.cpp \
src/main.cpp\
src/texteditor/AbstractCharacterBasedApplication.cpp \
src/texteditor/InputMethodPopup.cpp \
src/texteditor/TextEditorTheme.cpp \
src/texteditor/TextEditorWidget.cpp \
src/texteditor/UnicodeWidth.cpp \
src/texteditor/unicode.cpp \
src/urlencode.cpp \
src/webclient.cpp \
HEADERS += \
src/AboutDialog.h \
src/AbstractProcess.h \
src/AbstractSettingForm.h \
src/ApplicationGlobal.h \
src/AreYouSureYouWantToContinueConnectingDialog.h \
src/AvatarLoader.h \
src/BasicMainWindow.h \
src/BasicRepositoryDialog.h \
src/BigDiffWindow.h \
src/BlameWindow.h \
src/CheckoutDialog.h \
src/CherryPickDialog.h \
src/ClearButton.h \
src/CloneDialog.h \
src/CloneFromGitHubDialog.h \
src/CommitDialog.h \
src/CommitExploreWindow.h \
src/CommitPropertyDialog.h \
src/CommitViewWindow.h \
src/ConfigCredentialHelperDialog.h \
src/ConfigSigningDialog.h \
src/CreateRepositoryDialog.h \
src/Debug.h \
src/DeleteBranchDialog.h \
src/DeleteTagsDialog.h \
src/DialogHeaderFrame.h \
src/DirectoryLineEdit.h \
src/DoYouWantToInitDialog.h \
src/EditGitIgnoreDialog.h \
src/EditRemoteDialog.h \
src/EditTagsDialog.h \
src/ExperimentDialog.h \
src/FileDiffSliderWidget.h \
src/FileDiffWidget.h \
src/FileHistoryWindow.h \
src/FilePropertyDialog.h \
src/FileUtil.h \
src/FilesListWidget.h \
src/FindCommitDialog.h \
src/Git.h \
src/GitDiff.h \
src/GitHubAPI.h \
src/GitObjectManager.h \
src/GitPack.h \
src/GitPackIdxV2.h \
src/HyperLinkLabel.h \
src/ImageViewWidget.h \
src/InputNewTagDialog.h \
src/JumpDialog.h \
src/Languages.h \
src/LineEditDialog.h \
src/LocalSocketReader.h \
src/LogTableWidget.h \
src/MainWindow.h \
src/MaximizeButton.h \
src/MemoryReader.h \
src/MenuButton.h \
src/MergeDialog.h \
src/MyImageViewWidget.h \
src/MyProcess.h \
src/MySettings.h \
src/MyTableWidgetDelegate.h \
src/MyTextEditorWidget.h \
src/MyToolButton.h \
src/ObjectBrowserDialog.h \
src/Photoshop.h \
src/PushDialog.h \
src/ReadOnlyLineEdit.h \
src/ReadOnlyPlainTextEdit.h \
src/ReflogWindow.h \
src/RemoteAdvancedOptionWidget.h \
src/RemoteRepositoriesTableWidget.h \
src/RemoteWatcher.h \
src/RepositoriesTreeWidget.h \
src/RepositoryData.h \
src/RepositoryInfoFrame.h \
src/RepositoryLineEdit.h \
src/RepositoryPropertyDialog.h \
src/SearchFromGitHubDialog.h \
src/SelectCommandDialog.h \
src/SelectGpgKeyDialog.h \
src/SelectItemDialog.h \
src/SetGlobalUserDialog.h \
src/SetGpgSigningDialog.h \
src/SetRemoteUrlDialog.h \
src/SetUserDialog.h \
src/SettingBehaviorForm.h \
src/SettingExampleForm.h \
src/SettingGeneralForm.h \
src/SettingNetworkForm.h \
src/SettingProgramsForm.h \
src/SettingsDialog.h \
src/StatusLabel.h \
src/SubmoduleAddDialog.h \
src/SubmoduleUpdateDialog.h \
src/SubmodulesDialog.h \
src/Terminal.h \
src/TextEditDialog.h \
src/Theme.h \
src/UserEvent.h \
src/WelcomeWizardDialog.h \
src/charvec.h \
src/common/joinpath.h \
src/common/misc.h \
src/darktheme/DarkStyle.h \
src/darktheme/NinePatch.h \
src/darktheme/StandardStyle.h \
src/darktheme/TraditionalWindowsStyleTreeControl.h \
src/gpg.h \
src/gunzip.h \
src/main.h \
src/platform.h \
src/texteditor/AbstractCharacterBasedApplication.h \
src/texteditor/InputMethodPopup.h \
src/texteditor/TextEditorTheme.h \
src/texteditor/TextEditorWidget.h \
src/texteditor/UnicodeWidth.h \
src/texteditor/unicode.h \
src/urlencode.h \
src/webclient.h
HEADERS += src/version.h
FORMS += \
src/AboutDialog.ui \
src/AreYouSureYouWantToContinueConnectingDialog.ui \
src/BigDiffWindow.ui \
src/BlameWindow.ui \
src/CheckoutDialog.ui \
src/CherryPickDialog.ui \
src/CloneDialog.ui \
src/CloneFromGitHubDialog.ui \
src/CommitDialog.ui \
src/CommitExploreWindow.ui \
src/CommitPropertyDialog.ui \
src/CommitViewWindow.ui \
src/ConfigCredentialHelperDialog.ui \
src/ConfigSigningDialog.ui \
src/CreateRepositoryDialog.ui \
src/DeleteBranchDialog.ui \
src/DeleteTagsDialog.ui \
src/DoYouWantToInitDialog.ui \
src/EditGitIgnoreDialog.ui \
src/EditRemoteDialog.ui \
src/EditTagsDialog.ui \
src/ExperimentDialog.ui \
src/FileDiffWidget.ui \
src/FileHistoryWindow.ui \
src/FilePropertyDialog.ui \
src/FindCommitDialog.ui \
src/InputNewTagDialog.ui \
src/JumpDialog.ui \
src/LineEditDialog.ui \
src/MainWindow.ui \
src/MergeDialog.ui \
src/ObjectBrowserDialog.ui \
src/PushDialog.ui \
src/ReflogWindow.ui \
src/RemoteAdvancedOptionWidget.ui \
src/RepositoryPropertyDialog.ui \
src/SearchFromGitHubDialog.ui \
src/SelectCommandDialog.ui \
src/SelectGpgKeyDialog.ui \
src/SelectItemDialog.ui \
src/SetGlobalUserDialog.ui \
src/SetGpgSigningDialog.ui \
src/SetRemoteUrlDialog.ui \
src/SetUserDialog.ui \
src/SettingBehaviorForm.ui \
src/SettingExampleForm.ui \
src/SettingGeneralForm.ui \
src/SettingNetworkForm.ui \
src/SettingProgramsForm.ui \
src/SettingsDialog.ui \
src/SubmoduleAddDialog.ui \
src/SubmoduleUpdateDialog.ui \
src/SubmodulesDialog.ui \
src/TextEditDialog.ui \
src/WelcomeWizardDialog.ui
RESOURCES += \
src/resources/resources.qrc
unix {
SOURCES += \
src/unix/UnixProcess.cpp \
src/unix/UnixPtyProcess.cpp
HEADERS += \
src/unix/UnixProcess.h \
src/unix/UnixPtyProcess.h
}
win32 {
SOURCES += \
src/win32/Win32Process.cpp \
src/win32/Win32PtyProcess.cpp \
src/win32/event.cpp \
src/win32/thread.cpp \
src/win32/win32.cpp
HEADERS += \
src/win32/Win32Process.h \
src/win32/Win32PtyProcess.h \
src/win32/event.h \
src/win32/mutex.h \
src/win32/thread.h \
src/win32/win32.h
}
diff --git a/src/webclient.cpp b/src/webclient.cpp
index 7dae696..b40b3a2 100644
--- a/src/webclient.cpp
+++ b/src/webclient.cpp
@@ -1,1237 +1,1233 @@
#include "webclient.h"
#include <cstring>
#include <cstdint>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
-//#if USE_OPENSSL
-//#pragma comment(lib, "libeay32.lib")
-//#pragma comment(lib, "ssleay32.lib")
-//#endif
typedef SOCKET socket_t;
#pragma warning(disable:4996)
#else
#include <arpa/inet.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define closesocket(S) ::close(S)
using socket_t = int;
#define INVALID_SOCKET (-1)
#define SOCKET_ERROR (-1)
#define stricmp(A, B) strcasecmp(A, B)
#define strnicmp(A, B, C) strncasecmp(A, B, C)
#endif
#if USE_OPENSSL
#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#else
typedef void SSL;
typedef void SSL_CTX;
#endif
#include <cassert>
#include "charvec.h"
#define USER_AGENT "Generic Web Client"
struct WebContext::Private {
SSL_CTX *ctx = nullptr;
bool use_keep_alive = false;
WebProxy http_proxy;
WebProxy https_proxy;
};
WebClient::URL::URL(std::string const &addr)
{
data.full_request = addr;
char const *str = addr.c_str();
char const *left;
char const *right;
left = str;
right = strstr(left, "://");
if (right) {
data.scheme.assign(str, right - str);
left = right + 3;
}
right = strchr(left, '/');
if (!right) {
right = left + strlen(left);
}
if (right) {
char const *p = strchr(left, ':');
if (p && left < p && p < right) {
int n = 0;
char const *q = p + 1;
while (q < right) {
if (isdigit(*q & 0xff)) {
n = n * 10 + (*q - '0');
} else {
n = -1;
break;
}
q++;
}
data.host.assign(left, p - left);
if (n > 0 && n < 65536) {
data.port = n;
}
} else {
data.host.assign(left, right - left);
}
data.path = right;
}
}
bool WebClient::URL::isssl() const
{
if (scheme() == "https") return true;
if (scheme() == "http") return false;
if (port() == 443) return true;
return false;
}
void WebClientHandler::abort(std::string const &message)
{
throw WebClient::Error(message);
}
struct WebClient::Private {
std::vector<std::string> request_header;
Error error;
WebClient::Response response;
WebContext *webcx;
int crlf_state = 0;
size_t content_offset = 0;
std::string last_host_name;
int last_port = 0;
bool keep_alive = false;
socket_t sock = INVALID_SOCKET;
SSL *ssl = nullptr;
};
WebClient::WebClient(WebContext *webcx)
: m(new Private)
{
assert(webcx);
m->webcx = webcx;
}
WebClient::~WebClient()
{
close();
delete m;
}
void WebClient::initialize()
{
#ifdef _WIN32
WSADATA wsaData;
WORD wVersionRequested;
wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &wsaData);
atexit(cleanup);
#endif
#if USE_OPENSSL
OpenSSL_add_all_algorithms();
#endif
}
void WebClient::cleanup()
{
#if USE_OPENSSL
ERR_free_strings();
#endif
#ifdef _WIN32
WSACleanup();
#endif
}
void WebClient::reset()
{
// m->request_header.clear();
m->error = Error();
m->response = Response();
m->crlf_state = 0;
m->content_offset = 0;
}
void WebClient::output_debug_string(char const *str)
{
if (0) {
#ifdef _WIN32
OutputDebugStringA(str);
#else
fwrite(str, 1, strlen(str), stderr);
#endif
}
}
void WebClient::output_debug_strings(std::vector<std::string> const &vec)
{
for (std::string const &s : vec) {
output_debug_string((s + '\n').c_str());
}
}
WebClient::Error const &WebClient::error() const
{
return m->error;
}
void WebClient::clear_error()
{
m->error = Error();
}
int WebClient::get_port(URL const *url, char const *scheme, char const *protocol)
{
int port = url->port();
if (port < 1 || port > 65535) {
struct servent *s;
s = getservbyname(url->scheme().c_str(), protocol);
if (s) {
port = ntohs(s->s_port);
} else {
s = getservbyname(scheme, protocol);
if (s) {
port = ntohs(s->s_port);
}
}
if (port < 1 || port > 65535) {
port = 80;
}
}
return port;
}
static inline std::string to_s(size_t n)
{
char tmp[100];
sprintf(tmp, "%u", (int)n);
return tmp;
}
void WebClient::set_default_header(Request const &req, Post const *post, RequestOption const &opt)
{
std::vector<std::string> header;
auto AddHeader = [&](std::string const &s){
header.push_back(s);
};
AddHeader("Host: " + req.url.host());
AddHeader("User-Agent: " USER_AGENT);
AddHeader("Accept: */*");
if (opt.keep_alive) {
AddHeader("Connection: keep-alive");
} else {
AddHeader("Connection: close");
}
if (post) {
AddHeader("Content-Length: " + to_s(post->data.size()));
std::string ct = "Content-Type: ";
if (post->content_type.empty()) {
ct += CT_APPLICATION_OCTET_STREAM;
} else if (post->content_type == CT_MULTIPART_FORM_DATA) {
ct += post->content_type;
if (!post->boundary.empty()) {
ct += "; boundary=";
ct += post->boundary;
}
} else {
ct += post->content_type;
}
AddHeader(ct);
}
if (req.auth.type == Authorization::Basic) {
std::string s = req.auth.uid + ':' + req.auth.pwd;
AddHeader("Authorization: Basic " + base64_encode(s));
}
// header.insert(header.end(), m->request_header.begin(), m->request_header.end());
header.insert(header.end(), req.headers.begin(), req.headers.end());
m->request_header = std::move(header);
}
std::string WebClient::make_http_request(Request const &req, Post const *post, WebProxy const *proxy, bool https)
{
std::string str;
str = post ? "POST " : "GET ";
if (proxy && !https) {
str += req.url.data.full_request;
str += " HTTP/1.0";
str += "\r\n";
} else {
str += req.url.path();
str += " HTTP/1.0";
str += "\r\n";
}
for (std::string const &s: m->request_header) {
str += s;
str += "\r\n";
}
str += "\r\n";
return str;
}
void WebClient::parse_http_header(char const *begin, char const *end, std::vector<std::string> *header)
{
if (begin < end) {
char const *left = begin;
char const *right = left;
while (1) {
if (right >= end) {
break;
}
if (*right == '\r' || *right == '\n') {
if (left < right) {
header->push_back(std::string(left, right));
}
if (right + 1 < end && *right == '\r' && right[1] == '\n') {
right++;
}
right++;
if (*right == '\r' || *right == '\n') {
if (right + 1 < end && *right == '\r' && right[1] == '\n') {
right++;
}
right++;
left = right;
break;
}
left = right;
} else {
right++;
}
}
}
}
void WebClient::parse_http_header(char const *begin, char const *end, WebClient::Response *out)
{
*out = Response();
parse_http_header(begin, end, &out->header);
parse_header(&out->header, out);
}
static void send_(socket_t s, char const *ptr, int len)
{
while (len > 0) {
int n = send(s, ptr, len, 0);
if (n < 1 || n > len) {
throw WebClient::Error("send request failed.");
}
ptr += n;
len -= n;
}
}
void WebClient::on_end_header(std::vector<char> const *vec, WebClientHandler *handler)
{
if (vec->empty()) return;
char const *begin = &vec->at(0);
char const *end = begin + vec->size();
parse_http_header(begin, end, &m->response);
if (handler) {
handler->checkHeader(this);
}
}
void WebClient::append(char const *ptr, size_t len, std::vector<char> *out, WebClientHandler *handler)
{
size_t offset = out->size();
out->insert(out->end(), ptr, ptr + len);
if (m->crlf_state < 0) {
// nop
} else {
for (size_t i = 0; i < len; i++) {
int c = ptr[i] & 0xff;
if (c == '\r') {
m->crlf_state |= 1;
} else if (c == '\n') {
m->crlf_state |= 1;
m->crlf_state++;
} else {
m->crlf_state = 0;
}
if (m->crlf_state == 4) {
m->content_offset = offset + i + 1;
on_end_header(out, handler);
m->crlf_state = -1;
break;
}
}
}
if (handler && m->content_offset > 0) {
offset = out->size();
if (offset > m->content_offset) {
size_t len = offset - m->content_offset;
char const *ptr = &out->at(m->content_offset);
handler->checkContent(ptr, len);
}
}
}
static char *stristr(char *str1, char const *str2)
{
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
for (size_t i = 0; i + len2 <= len1; i++) {
if (strnicmp(str1 + i, str2, len2) == 0) {
return str1 + i;
}
}
return nullptr;
}
class ResponseHeader {
public:
size_t pos = 0;
std::vector<char> line;
int content_length = -1;
bool connection_keep_alive = false;
bool connection_close = false;
int lf = 0;
enum State {
Header,
Content,
};
State state = Header;
void put(int c)
{
pos++;
if (state == Header) {
if (c== '\r' || c == '\n') {
if (!line.empty()) {
line.push_back(0);
char *begin = &line[0];
char *p = strchr(begin, ':');
if (p && *p == ':') {
*p++ = 0;
auto IS = [&](char const *name){ return stricmp(begin, name) == 0; };
if (IS("content-length")) {
content_length = strtol(p, nullptr, 10);
} else if (IS("connection")) {
if (stristr(p, "keep-alive")) {
connection_keep_alive = true;
} else if (stristr(p, "close")) {
connection_close = true;
}
}
}
line.clear();
}
if (c== '\r') {
return;
}
if (c == '\n') {
lf++;
if (lf == 2) {
state = Content;
}
return;
}
}
lf = 0;
line.push_back(c);
}
}
};
void WebClient::receive_(RequestOption const &opt, std::function<int(char *, int)> const &rcv, std::vector<char> *out)
{
char buf[4096];
size_t pos = 0;
ResponseHeader rh;
while (1) {
int n;
if (rh.state == ResponseHeader::Content && rh.content_length >= 0) {
n = rh.pos + rh.content_length - pos;
if (n > (int)sizeof(buf)) {
n = sizeof(buf);
}
if (n < 1) break;
} else {
n = sizeof(buf);
}
n = rcv(buf, n);
if (n < 1) break;
if (0) { // debug
fwrite(buf, 1, n, stderr);
}
append(buf, n, out, opt.handler);
pos += n;
if (rh.state == ResponseHeader::Header) {
for (int i = 0; i < n; i++) {
rh.put(buf[i]);
if (rh.state == ResponseHeader::Content) {
m->keep_alive = rh.connection_keep_alive && !rh.connection_close;
break;
}
}
}
}
}
namespace {
struct sockaddr_in getinetaddr(char const *name)
{
struct sockaddr_in addr = {};
#if 0
struct hostent *host;
uint32_t a = inet_addr(name);
if (a != INADDR_NONE) {
host = gethostbyaddr((const char *)&a, 4, AF_INET);
} else {
host = gethostbyname(name);
}
if (!host) {
throw WebClient::Error(std::string("gethostbyname failed: ") + name);
}
addr.sin_family = AF_INET;
memcpy((char *)&addr.sin_addr, host->h_addr, host->h_length);
#else
struct addrinfo hints = {};
struct addrinfo *res = nullptr;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
getaddrinfo(name, nullptr, &hints, &res);
if (res) {
if (res->ai_family == AF_INET) {
addr = *reinterpret_cast<struct sockaddr_in *>(res->ai_addr);
}
freeaddrinfo(res);
}
if (addr.sin_family == 0) {
throw WebClient::Error(std::string("getaddrinfo failed: ") + name);
}
#endif
return addr;
}
}
bool WebClient::http_get(Request const &request, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
clear_error();
out->clear();
Request server_req;
WebProxy const *proxy = m->webcx->http_proxy();
if (proxy) {
server_req = Request(proxy->server);
} else {
server_req = request;
}
std::string hostname = server_req.url.host();
int port = get_port(&server_req.url, "http", "tcp");
m->keep_alive = opt.keep_alive && hostname == m->last_host_name && port == m->last_port;
if (!m->keep_alive) close();
if (m->sock == INVALID_SOCKET) {
struct sockaddr_in server = getinetaddr(server_req.url.host().c_str());
server.sin_port = htons(port);
m->sock = socket(AF_INET, SOCK_STREAM, 0);
if (m->sock == INVALID_SOCKET) {
throw Error("socket failed.");
}
if (connect(m->sock, (struct sockaddr*) &server, sizeof(server)) == SOCKET_ERROR) {
throw Error("connect failed.");
}
}
m->last_host_name = hostname;
m->last_port = port;
set_default_header(request, post, opt);
std::string req = make_http_request(request, post, proxy, false);
send_(m->sock, req.c_str(), (int)req.size());
if (post && !post->data.empty()) {
send_(m->sock, (char const *)&post->data[0], (int)post->data.size());
}
m->crlf_state = 0;
m->content_offset = 0;
receive_(opt, [&](char *ptr, int len){
return recv(m->sock, ptr, len, 0);
}, out);
if (!m->keep_alive) close();
return true;
}
bool WebClient::https_get(Request const &request_req, Post const *post, RequestOption const &opt, std::vector<char> *out)
{
#if USE_OPENSSL
auto *sslctx = m->webcx->m->ctx;
if (!m->webcx || !sslctx) {
output_debug_string("SSL context is null.\n");
return false;
}
clear_error();
out->clear();
auto get_ssl_error = []()->std::string{
char tmp[1000];
unsigned long e = ERR_get_error();
ERR_error_string_n(e, tmp, sizeof(tmp));
return tmp;
};
Request server_req;
WebProxy const *proxy = m->webcx->https_proxy();
if (proxy) {
server_req = Request(proxy->server);
} else {
server_req = request_req;
}
std::string hostname = server_req.url.host();
int port = get_port(&server_req.url, "https", "tcp");
m->keep_alive = opt.keep_alive && hostname == m->last_host_name && port == m->last_port;
if (!m->keep_alive) close();
socket_t sock = m->sock;
SSL *ssl = m->ssl;
if (sock == INVALID_SOCKET || !ssl) {
struct sockaddr_in server = getinetaddr(server_req.url.host().c_str());
server.sin_port = htons(port);
if (sock == INVALID_SOCKET) {
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
throw Error("socket failed.");
}
if (connect(sock, (struct sockaddr*) &server, sizeof(server)) == SOCKET_ERROR) {
throw Error("connect failed.");
}
}
if (proxy) { // testing
char port[10];
sprintf(port, ":%u", get_port(&request_req.url, "https", "tcp"));
std::string str = "CONNECT ";
str += request_req.url.data.host;
str += port;
str += " HTTP/1.0\r\n\r\n";
send_(sock, str.c_str(), str.size());
char tmp[1000];
int n = recv(sock, tmp, sizeof(tmp), 0);
int i;
for (i = 0; i < n; i++) {
char c = tmp[i];
if (c < 0x20) break;
}
if (i > 0) {
std::string s(tmp, i);
s = "proxy: " + s + '\n';
#ifdef _WIN32
OutputDebugStringA(s.c_str());
#else
fprintf(stderr, "%s", tmp);
#endif
}
}
ssl = SSL_new(sslctx);
if (!ssl) {
throw Error(get_ssl_error());
}
SSL_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_set_options(ssl, SSL_OP_NO_SSLv3);
int ret;
ret = SSL_set_fd(ssl, sock);
if (ret == 0) {
throw Error(get_ssl_error());
}
RAND_poll();
while (RAND_status() == 0) {
unsigned short rand_ret = rand() % 65536;
RAND_seed(&rand_ret, sizeof(rand_ret));
}
ret = SSL_connect(ssl);
if (ret != 1) {
throw Error(get_ssl_error());
}
std::string cipher = SSL_get_cipher(ssl);
cipher += '\n';
output_debug_string(cipher.c_str());
std::string version = SSL_get_cipher_version(ssl);
version += '\n';
output_debug_string(version.c_str());
X509 *x509 = SSL_get_peer_certificate(ssl);
if (x509) {
#ifndef OPENSSL_NO_SHA1
std::string fingerprint;
for (int i = 0; i < SHA_DIGEST_LENGTH; i++) {
if (i > 0) {
fingerprint += ':';
}
char tmp[10];
sprintf(tmp, "%02X", x509->sha1_hash[i]);
fingerprint += tmp;
}
fingerprint += '\n';
output_debug_string(fingerprint.c_str());
#endif
long l = SSL_get_verify_result(ssl);
if (l == X509_V_OK) {
// ok
} else {
// wrong
std::string err = X509_verify_cert_error_string(l);
err += '\n';
output_debug_string(err.c_str());
}
std::vector<std::string> vec;
auto GETSTRINGS = [](X509_NAME *x509name, std::vector<std::string> *out){
out->clear();
if (x509name) {
int n = X509_NAME_entry_count(x509name);
for (int i = 0; i < n; i++) {
X509_NAME_ENTRY *entry = X509_NAME_get_entry(x509name, i);
ASN1_STRING *asn1str = X509_NAME_ENTRY_get_data(entry);
int asn1len = ASN1_STRING_length(asn1str);
unsigned char *p = ASN1_STRING_data(asn1str);
std::string str((char const *)p, asn1len);
out->push_back(str);
}
}
};
X509_NAME *subject = X509_get_subject_name(x509);
GETSTRINGS(subject, &vec);
output_debug_string("--- subject ---\n");
output_debug_strings(vec);
X509_NAME *issuer = X509_get_issuer_name(x509);
GETSTRINGS(issuer, &vec);
output_debug_string("--- issuer ---\n");
output_debug_strings(vec);
ASN1_TIME *not_before = X509_get_notBefore(x509);
ASN1_TIME *not_after = X509_get_notAfter(x509);
(void)not_before;
(void)not_after;
X509_free(x509);
} else {
// wrong
}
}
m->last_host_name = hostname;
m->last_port = port;
set_default_header(request_req, post, opt);
std::string request = make_http_request(request_req, post, proxy, true);
auto SEND = [&](char const *ptr, int len){
while (len > 0) {
int n = SSL_write(ssl, ptr, len);
if (n < 1 || n > len) {
throw WebClient::Error(get_ssl_error());
}
ptr += n;
len -= n;
}
};
SEND(request.c_str(), (int)request.size());
if (post && !post->data.empty()) {
SEND((char const *)&post->data[0], (int)post->data.size());
}
m->crlf_state = 0;
m->content_offset = 0;
receive_(opt, [&](char *ptr, int len){
return SSL_read(ssl, ptr, len);
}, out);
m->sock = sock;
m->ssl = ssl;
if (!m->keep_alive) close();
return true;
#endif
return false;
}
bool WebClient::get(Request const &req, Post const *post, Response *out, WebClientHandler *handler)
{
reset();
try {
if (!m->webcx->m) {
throw Error("WebContext is null.");
}
RequestOption opt;
opt.keep_alive = m->webcx->m->use_keep_alive;
opt.handler = handler;
std::vector<char> res;
if (req.url.isssl()) {
#if USE_OPENSSL
https_get(req, post, opt, &res);
#endif
} else {
http_get(req, post, opt, &res);
}
if (!res.empty()) {
char const *begin = &res[0];
char const *end = begin + res.size();
char const *ptr = begin + m->content_offset;
if (ptr < end) {
out->content.assign(ptr, end);
}
}
return true;
} catch (Error const &e) {
m->error = e;
close();
}
*out = Response();
return false;
}
void WebClient::parse_header(std::vector<std::string> const *header, WebClient::Response *res)
{
if (!header->empty()) {
std::string const &line = header->at(0);
char const *begin = line.c_str();
char const *end = begin + line.size();
if (line.size() > 5 && strncmp(line.c_str(), "HTTP/", 5) == 0) {
int state = 0;
res->version.hi = res->version.lo = res->code = 0;
char const *ptr = begin + 5;
while (1) {
int c = 0;
if (ptr < end) {
c = *ptr & 0xff;
}
switch (state) {
case 0:
if (isdigit(c)) {
res->version.hi = res->version.hi * 10 + (c - '0');
} else if (c == '.') {
state = 1;
} else {
state = -1;
}
break;
case 1:
if (isdigit(c)) {
res->version.lo = res->version.lo * 10 + (c - '0');
} else if (isspace(c)) {
state = 2;
} else {
state = -1;
}
break;
case 2:
if (isspace(c)) {
if (res->code != 0) {
state = -1;
}
} else if (isdigit(c)) {
res->code = res->code * 10 + (c - '0');
} else {
state = -1;
}
break;
default:
state = -1;
break;
}
if (c == 0 || state < 0) {
break;
}
ptr++;
}
}
}
}
std::string WebClient::header_value(std::vector<std::string> const *header, std::string const &name)
{
for (size_t i = 1; i < header->size(); i++) {
std::string const &line = header->at(i);
char const *begin = line.c_str();
char const *end = begin + line.size();
char const *colon = strchr(begin, ':');
if (colon) {
if (strnicmp(begin, name.c_str(), name.size()) == 0) {
char const *ptr = colon + 1;
while (ptr < end && isspace(*ptr & 0xff)) ptr++;
return std::string(ptr, end);
}
}
}
return std::string();
}
std::string WebClient::header_value(std::string const &name) const
{
return header_value(&m->response.header, name);
}
std::string WebClient::content_type() const
{
std::string s = header_value("Content-Type");
char const *begin = s.c_str();
char const *end = begin + s.size();
char const *ptr = begin;
while (ptr < end) {
int c = *ptr & 0xff;
if (c == ';' || c < 0x21) break;
ptr++;
}
if (ptr < end) return std::string(begin, ptr);
return s;
}
size_t WebClient::content_length() const
{
return m->response.content.size();
}
char const *WebClient::content_data() const
{
if (m->response.content.empty()) return "";
return &m->response.content[0];
}
int WebClient::get(Request const &req, WebClientHandler *handler)
{
get(req, nullptr, &m->response, handler);
return m->response.code;
}
int WebClient::post(Request const &req, Post const *post, WebClientHandler *handler)
{
get(req, post, &m->response, handler);
return m->response.code;
}
void WebClient::close()
{
#if USE_OPENSSL
if (m->ssl) {
SSL_shutdown(m->ssl);
SSL_free(m->ssl);
m->ssl = nullptr;
}
#endif
if (m->sock != INVALID_SOCKET) {
shutdown(m->sock, 2); // SD_BOTH or SHUT_RDWR
closesocket(m->sock);
m->sock = INVALID_SOCKET;
}
}
void WebClient::add_header(std::string const &text)
{
m->request_header.push_back(text);
}
WebClient::Response const &WebClient::response() const
{
return m->response;
}
void WebClient::make_application_www_form_urlencoded(char const *begin, char const *end, WebClient::Post *out)
{
*out = WebClient::Post();
out->content_type = CT_APPLICATION_X_WWW_FORM_URLENCODED;
print(&out->data, begin, end - begin);
}
void WebClient::make_multipart_form_data(std::vector<Part> const &parts, WebClient::Post *out, std::string const &boundary)
{
*out = WebClient::Post();
out->content_type = CT_MULTIPART_FORM_DATA;
out->boundary = boundary;
for (Part const &part : parts) {
print(&out->data, "--");
print(&out->data, out->boundary);
print(&out->data, "\r\n");
if (!part.content_disposition.type.empty()) {
ContentDisposition const &cd = part.content_disposition;
std::string s;
s = "Content-Disposition: ";
s += cd.type;
auto Add = [&s](std::string const &name, std::string const &value){
if (!value.empty()) {
s += "; " + name + "=\"";
s += value;
s += '\"';
}
};
Add("name", cd.name);
Add("filename", cd.filename);
print(&out->data, s);
print(&out->data, "\r\n");
}
if (!part.content_type.empty()) {
print(&out->data, "Content-Type: " + part.content_type + "\r\n");
}
if (!part.content_transfer_encoding.empty()) {
print(&out->data, "Content-Transfer-Encoding: " + part.content_transfer_encoding + "\r\n");
}
print(&out->data, "\r\n");
print(&out->data, part.data, part.size);
print(&out->data, "\r\n");
}
print(&out->data, "--");
print(&out->data, out->boundary);
print(&out->data, "--\r\n");
}
void WebClient::make_multipart_form_data(char const *data, size_t size, WebClient::Post *out, std::string const &boundary)
{
Part part;
part.data = data;
part.size = size;
std::vector<Part> parts;
parts.push_back(part);
make_multipart_form_data(parts, out, boundary);
}
//
WebContext::WebContext()
: m(new Private)
{
#if USE_OPENSSL
SSL_load_error_strings();
SSL_library_init();
m->ctx = SSL_CTX_new(SSLv23_client_method());
#endif
}
WebContext::~WebContext()
{
#if USE_OPENSSL
SSL_CTX_free(m->ctx);
#endif
delete m;
}
void WebContext::set_keep_alive_enabled(bool f)
{
m->use_keep_alive = f;
}
void WebContext::set_http_proxy(std::string const &proxy)
{
m->http_proxy = WebProxy();
m->http_proxy.server = proxy;
}
void WebContext::set_https_proxy(std::string const &proxy)
{
m->https_proxy = WebProxy();
m->https_proxy.server = proxy;
}
const WebProxy *WebContext::http_proxy() const
{
if (!m->http_proxy.empty()) {
return &m->http_proxy;
}
return nullptr;
}
const WebProxy *WebContext::https_proxy() const
{
if (!m->https_proxy.empty()) {
return &m->https_proxy;
}
if (!m->http_proxy.empty()) {
return &m->http_proxy;
}
return nullptr;
}
bool WebContext::load_cacert(char const *path)
{
#if USE_OPENSSL
int r = SSL_CTX_load_verify_locations(m->ctx, path, nullptr);
return r == 1;
#else
return false;
#endif
}
//
static unsigned char const PAD = '=';
static const unsigned char _encode_table[] = {
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
0x77, 0x78, 0x79, 0x7a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2b, 0x2f,
};
static const unsigned char _decode_table[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
};
static inline unsigned char enc(int c)
{
return _encode_table[c & 63];
}
static inline unsigned char dec(int c)
{
return _decode_table[c & 127];
}
void base64_encode(char const *src, size_t length, std::vector<char> *out)
{
size_t srcpos, dstlen, dstpos;
dstlen = (length + 2) / 3 * 4;
out->resize(dstlen);
if (dstlen == 0) {
return;
}
char *dst = &out->at(0);
dstpos = 0;
for (srcpos = 0; srcpos < length; srcpos += 3) {
int v = (unsigned char)src[srcpos] << 16;
if (srcpos + 1 < length) {
v |= (unsigned char)src[srcpos + 1] << 8;
if (srcpos + 2 < length) {
v |= (unsigned char)src[srcpos + 2];
dst[dstpos + 3] = enc(v);
} else {
dst[dstpos + 3] = PAD;
}
dst[dstpos + 2] = enc(v >> 6);
} else {
dst[dstpos + 2] = PAD;
dst[dstpos + 3] = PAD;
}
dst[dstpos + 1] = enc(v >> 12);
dst[dstpos] = enc(v >> 18);
dstpos += 4;
}
}
void base64_decode(char const *src, size_t length, std::vector<char> *out)
{
unsigned char const *begin = (unsigned char const *)src;
unsigned char const *end = begin + length;
unsigned char const *ptr = begin;
out->clear();
out->reserve(length * 3 / 4);
int count = 0;
int bits = 0;
while (1) {
if (isspace(*ptr)) {
ptr++;
} else {
unsigned char c = 0xff;
if (ptr < end && *ptr < 0x80) {
c = dec(*ptr);
}
if (c < 0x40) {
bits = (bits << 6) | c;
count++;
} else {
if (count < 4) {
bits <<= (4 - count) * 6;
}
c = 0xff;
}
if (count == 4 || c == 0xff) {
if (count >= 2) {
out->push_back(bits >> 16);
if (count >= 3) {
out->push_back(bits >> 8);
if (count == 4) {
out->push_back(bits);
}
}
}
count = 0;
bits = 0;
if (c == 0xff) {
break;
}
}
ptr++;
}
}
}
void base64_encode(std::vector<char> const *src, std::vector<char> *out)
{
base64_encode(&src->at(0), src->size(), out);
}
void base64_decode(std::vector<char> const *src, std::vector<char> *out)
{
base64_decode(&src->at(0), src->size(), out);
}
void base64_encode(char const *src, std::vector<char> *out)
{
base64_encode((char const *)src, strlen(src), out);
}
void base64_decode(char const *src, std::vector<char> *out)
{
base64_decode((char const *)src, strlen(src), out);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Feb 7, 10:21 AM (11 h, 48 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
55878
Default Alt Text
(40 KB)
Attached To
Mode
R77 Guitar
Attached
Detach File
Event Timeline
Log In to Comment