mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
Initial commit
This commit is contained in:
4
Frameworks/network/src/constants.cc
Normal file
4
Frameworks/network/src/constants.cc
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "constants.h"
|
||||
|
||||
std::string const kHTTPSignatureHeader = "x-amz-meta-x-signature";
|
||||
std::string const kHTTPSigneeHeader = "x-amz-meta-x-signee";
|
||||
9
Frameworks/network/src/constants.h
Normal file
9
Frameworks/network/src/constants.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef NETWORK_CONSTANTS_H_R7DVBN2M
|
||||
#define NETWORK_CONSTANTS_H_R7DVBN2M
|
||||
|
||||
#include <oak/misc.h>
|
||||
|
||||
PUBLIC extern std::string const kHTTPSignatureHeader;
|
||||
PUBLIC extern std::string const kHTTPSigneeHeader;
|
||||
|
||||
#endif /* end of include guard: NETWORK_CONSTANTS_H_R7DVBN2M */
|
||||
220
Frameworks/network/src/download.cc
Normal file
220
Frameworks/network/src/download.cc
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "download.h"
|
||||
#include "proxy.h"
|
||||
#include "user_agent.h"
|
||||
#include <cf/cf.h>
|
||||
#include <text/format.h>
|
||||
#include <text/case.h>
|
||||
#include <text/hexdump.h>
|
||||
#include <io/path.h>
|
||||
#include <oak/debug.h>
|
||||
|
||||
OAK_DEBUG_VAR(Network_Download);
|
||||
|
||||
namespace network
|
||||
{
|
||||
static const char kCRLF[] = "\r\n";
|
||||
|
||||
// =============
|
||||
// = request_t =
|
||||
// =============
|
||||
|
||||
request_t::request_t (std::string const& url, filter_t* firstFilter, ...) : _url(url)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, firstFilter);
|
||||
for(; firstFilter; firstFilter = va_arg(ap, filter_t*))
|
||||
_filters.push_back(firstFilter);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
request_t& request_t::add_filter (filter_t* filter) { _filters.push_back(filter); return *this; }
|
||||
request_t& request_t::set_user_agent (std::string const& user_agent) { _user_agent = user_agent; return *this; }
|
||||
request_t& request_t::set_entity_tag (std::string const& entity_tag) { _entity_tag = entity_tag; return *this; }
|
||||
request_t& request_t::watch_stop_flag (bool const* stopFlag) { _stop_flag = stopFlag; return *this; }
|
||||
|
||||
request_t& request_t::update_progress_variable (double* percentDone, double min, double max)
|
||||
{
|
||||
_progress = percentDone;
|
||||
_progress_min = min;
|
||||
_progress_max = max;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ============
|
||||
// = Download =
|
||||
// ============
|
||||
|
||||
namespace
|
||||
{
|
||||
struct user_data_t
|
||||
{
|
||||
user_data_t (request_t const& request) : request(request) { }
|
||||
|
||||
request_t const& request;
|
||||
bool receiving_body = false;
|
||||
std::string error = NULL_STR;
|
||||
};
|
||||
}
|
||||
|
||||
int receive_progress (void* udata, double dltotal, double dlnow, double ultotal, double ulnow)
|
||||
{
|
||||
user_data_t& userData = *((user_data_t*)udata);
|
||||
if(userData.request._progress && userData.receiving_body)
|
||||
*userData.request._progress = userData.request._progress_min + (userData.request._progress_max - userData.request._progress_min) * (dltotal ? dlnow / dltotal : 0);
|
||||
return userData.request._stop_flag ? *userData.request._stop_flag : false;
|
||||
}
|
||||
|
||||
size_t receive_header (void* ptr, size_t size, size_t nmemb, void* udata)
|
||||
{
|
||||
user_data_t& userData = *((user_data_t*)udata);
|
||||
|
||||
char const* bytes = (char const*)ptr;
|
||||
size_t len = nmemb * size;
|
||||
|
||||
if(len > 5 && strncmp("HTTP/", bytes, 5) == 0 && std::find(bytes, bytes + len, ':') == bytes + len)
|
||||
{
|
||||
D(DBF_Network_Download, bug("New Response: %.*s", (int)len, bytes););
|
||||
if(len > 12 && strncmp("HTTP/1", bytes, 6) == 0 && bytes[9] == '2')
|
||||
userData.receiving_body = true;
|
||||
|
||||
char const* first = (const char*)ptr;
|
||||
char const* last = std::search(first, first + size * nmemb, &kCRLF[0], &kCRLF[2]);
|
||||
iterate(filter, userData.request._filters)
|
||||
{
|
||||
if(!(*filter)->receive_status(std::string(first, last)))
|
||||
{
|
||||
userData.error = text::format("%s: receiving status", (*filter)->name().c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(len == 2 && strncmp("\r\n", bytes, 2) == 0)
|
||||
{
|
||||
D(DBF_Network_Download, bug("End of Response\n"););
|
||||
}
|
||||
else
|
||||
{
|
||||
while(len && (bytes[len-1] == '\r' || bytes[len-1] == '\n'))
|
||||
--len;
|
||||
|
||||
size_t keyLast = std::find(bytes, bytes + len, ':') - bytes;
|
||||
size_t valueFirst = keyLast;
|
||||
while(valueFirst < len && bytes[++valueFirst] == ' ')
|
||||
;
|
||||
|
||||
if(valueFirst != len)
|
||||
{
|
||||
iterate(filter, userData.request._filters)
|
||||
{
|
||||
if(!(*filter)->receive_header(text::lowercase(std::string(bytes, bytes + keyLast)), std::string(bytes + valueFirst, bytes + len)))
|
||||
{
|
||||
userData.error = text::format("%s: receiving header", (*filter)->name().c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
size_t receive_data (void* ptr, size_t size, size_t nmemb, void* udata)
|
||||
{
|
||||
user_data_t& userData = *((user_data_t*)udata);
|
||||
iterate(filter, userData.request._filters)
|
||||
{
|
||||
if(!(*filter)->receive_data((const char*)ptr, size * nmemb))
|
||||
{
|
||||
userData.error = text::format("%s: receiving data", (*filter)->name().c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
long download (request_t const& request, std::string* error)
|
||||
{
|
||||
iterate(filter, request._filters)
|
||||
{
|
||||
if(!(*filter)->setup())
|
||||
{
|
||||
if(error)
|
||||
*error = text::format("%s: setup", (*filter)->name().c_str());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
long res = 0;
|
||||
if(CURL* handle = curl_easy_init())
|
||||
{
|
||||
curl_easy_setopt(handle, CURLOPT_URL, request._url.c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_easy_setopt(handle, CURLOPT_FAILONERROR, true);
|
||||
curl_easy_setopt(handle, CURLOPT_ENCODING, "");
|
||||
|
||||
std::string const userAgent = request._user_agent == NULL_STR ? create_agent_info_string() : request._user_agent;
|
||||
curl_easy_setopt(handle, CURLOPT_USERAGENT, userAgent.c_str());
|
||||
|
||||
char errorbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errorbuf);
|
||||
|
||||
user_data_t userData(request);
|
||||
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &receive_data);
|
||||
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &userData);
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_PROGRESSFUNCTION, &receive_progress);
|
||||
curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, &userData);
|
||||
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, false);
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &receive_header);
|
||||
curl_easy_setopt(handle, CURLOPT_HEADERDATA, &userData);
|
||||
|
||||
struct curl_slist* headers = NULL;
|
||||
if(request._entity_tag != NULL_STR)
|
||||
{
|
||||
headers = curl_slist_append(headers, text::format("If-None-Match: %.*s", (int)request._entity_tag.size(), request._entity_tag.data()).c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
|
||||
if(auto proxySettings = get_proxy_settings())
|
||||
{
|
||||
curl_easy_setopt(handle, CURLOPT_PROXY, proxySettings.server.c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_PROXYPORT, proxySettings.port);
|
||||
if(proxySettings.password != NULL_STR)
|
||||
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, (proxySettings.user + ":" + proxySettings.password).c_str());
|
||||
}
|
||||
|
||||
CURLcode rc = curl_easy_perform(handle);
|
||||
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &res);
|
||||
curl_easy_cleanup(handle);
|
||||
curl_slist_free_all(headers);
|
||||
|
||||
if(rc == 0)
|
||||
{
|
||||
if(res == 304) // not modified so ignore filter errors
|
||||
{
|
||||
iterate(filter, request._filters)
|
||||
(*filter)->receive_end(userData.error);
|
||||
}
|
||||
else
|
||||
{
|
||||
iterate(filter, request._filters)
|
||||
{
|
||||
if(!(*filter)->receive_end(userData.error))
|
||||
{
|
||||
if(error)
|
||||
*error = userData.error;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(error)
|
||||
*error = errorbuf;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} /* network */
|
||||
49
Frameworks/network/src/download.h
Normal file
49
Frameworks/network/src/download.h
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef DOWNLOAD_H_S5YBYD2
|
||||
#define DOWNLOAD_H_S5YBYD2
|
||||
|
||||
#include <oak/oak.h>
|
||||
|
||||
struct PUBLIC filter_t
|
||||
{
|
||||
virtual ~filter_t () { }
|
||||
|
||||
virtual bool setup () { return true; }
|
||||
virtual bool receive_status (std::string const& statusString) { return true; }
|
||||
virtual bool receive_header (std::string const& header, std::string const& value) { return true; }
|
||||
virtual bool receive_data (char const* bytes, size_t len) { return true; }
|
||||
virtual bool receive_end (std::string& error) { return true; }
|
||||
virtual std::string name () { return "filter"; }
|
||||
};
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct PUBLIC request_t
|
||||
{
|
||||
request_t (std::string const& url, filter_t* firstFilter = NULL, ...);
|
||||
request_t& add_filter (filter_t* filter);
|
||||
request_t& set_user_agent (std::string const& user_agent);
|
||||
request_t& set_entity_tag (std::string const& entity_tag);
|
||||
request_t& watch_stop_flag (bool const* stopFlag);
|
||||
request_t& update_progress_variable (double* percentDone, double min = 0, double max = 1);
|
||||
|
||||
private:
|
||||
friend long download (request_t const& request, std::string* error);
|
||||
friend int receive_progress (void* udata, double dltotal, double dlnow, double ultotal, double ulnow);
|
||||
friend size_t receive_header (void* ptr, size_t size, size_t nmemb, void* udata);
|
||||
friend size_t receive_data (void* ptr, size_t size, size_t nmemb, void* udata);
|
||||
|
||||
std::string _url;
|
||||
std::vector<filter_t*> _filters;
|
||||
std::string _user_agent = NULL_STR;
|
||||
std::string _entity_tag = NULL_STR;
|
||||
bool const* _stop_flag = NULL;
|
||||
double* _progress = NULL;
|
||||
double _progress_min = 0;
|
||||
double _progress_max = 0;
|
||||
};
|
||||
|
||||
PUBLIC long download (request_t const& request, std::string* error);
|
||||
|
||||
} /* net */
|
||||
|
||||
#endif /* end of include guard: DOWNLOAD_NG_H_S5YBYD2 */
|
||||
233
Frameworks/network/src/download_tbz.cc
Normal file
233
Frameworks/network/src/download_tbz.cc
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "download_tbz.h"
|
||||
#include "filter_check_signature.h"
|
||||
#include "constants.h"
|
||||
#include "user_agent.h"
|
||||
#include "proxy.h"
|
||||
#include "tbz.h"
|
||||
#include <io/path.h>
|
||||
#include <io/swap_file_data.h>
|
||||
#include <io/move_path.h>
|
||||
#include <text/case.h>
|
||||
#include <text/decode.h>
|
||||
#include <text/format.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
static std::string const kHTTPEntityTagAttribute = "org.w3.http.etag";
|
||||
|
||||
namespace
|
||||
{
|
||||
struct user_data_t
|
||||
{
|
||||
user_data_t (key_chain_t const& keychain, double* progress, double start_progress, double stop_progress, bool const* stop_flag, int tbz_fd, int tmp_fd) : progress(progress), start_progress(start_progress), stop_progress(stop_progress), stop_flag(stop_flag), tbz_fd(tbz_fd), tmp_fd(tmp_fd), verify_signature(keychain, kHTTPSigneeHeader, kHTTPSignatureHeader)
|
||||
{
|
||||
verify_signature.setup();
|
||||
}
|
||||
|
||||
void receive (size_t len)
|
||||
{
|
||||
received += len;
|
||||
if(progress)
|
||||
*progress = start_progress + (stop_progress - start_progress) * (total ? received / (double)total : 0);
|
||||
}
|
||||
|
||||
bool should_stop () const
|
||||
{
|
||||
return stop_flag && *stop_flag;
|
||||
}
|
||||
|
||||
bool modified = true;
|
||||
|
||||
double* progress;
|
||||
double start_progress;
|
||||
double stop_progress;
|
||||
|
||||
bool const* stop_flag;
|
||||
|
||||
std::string etag = NULL_STR;
|
||||
|
||||
int tbz_fd;
|
||||
int tmp_fd;
|
||||
|
||||
network::check_signature_t verify_signature;
|
||||
|
||||
size_t received = 0;
|
||||
size_t total = 0;
|
||||
};
|
||||
|
||||
static size_t receive_header (void* ptr, size_t size, size_t nmemb, void* udata)
|
||||
{
|
||||
user_data_t& data = *((user_data_t*)udata);
|
||||
|
||||
char const* bytes = (char const*)ptr;
|
||||
size_t len = nmemb * size;
|
||||
|
||||
if(len > 7 && strncmp("HTTP/1.", bytes, 7) == 0 && std::find(bytes, bytes + len, ':') == bytes + len)
|
||||
{
|
||||
static const char kCRLF[] = "\r\n";
|
||||
char const* last = std::search(bytes, bytes + size * nmemb, &kCRLF[0], &kCRLF[2]);
|
||||
if(std::string(bytes, last).find("HTTP/1.1 304") == 0)
|
||||
data.modified = false;
|
||||
}
|
||||
else if(len != 2 || strncmp("\r\n", bytes, 2) != 0)
|
||||
{
|
||||
while(len && (bytes[len-1] == '\r' || bytes[len-1] == '\n'))
|
||||
--len;
|
||||
|
||||
size_t keyLast = std::find(bytes, bytes + len, ':') - bytes;
|
||||
size_t valueFirst = keyLast;
|
||||
while(valueFirst < len && bytes[++valueFirst] == ' ')
|
||||
;
|
||||
|
||||
if(valueFirst != len)
|
||||
{
|
||||
std::string header = text::lowercase(std::string(bytes, bytes + keyLast));
|
||||
std::string value(bytes + valueFirst, bytes + len);
|
||||
|
||||
if(header == "etag")
|
||||
data.etag = value;
|
||||
else if(header == "content-length")
|
||||
data.total = strtol(value.c_str(), NULL, 10);
|
||||
else if(header == kHTTPSigneeHeader || header == kHTTPSignatureHeader)
|
||||
data.verify_signature.receive_header(header, value);
|
||||
}
|
||||
}
|
||||
return data.should_stop() ? 0 : size * nmemb;
|
||||
}
|
||||
|
||||
static size_t receive_data (void* ptr, size_t size, size_t nmemb, void* udata)
|
||||
{
|
||||
user_data_t& data = *((user_data_t*)udata);
|
||||
|
||||
write(data.tbz_fd, ptr, size * nmemb);
|
||||
write(data.tmp_fd, ptr, size * nmemb);
|
||||
data.verify_signature.receive_data((char const*)ptr, size * nmemb);
|
||||
data.receive(size * nmemb);
|
||||
return data.should_stop() ? 0 : size * nmemb;
|
||||
}
|
||||
}
|
||||
|
||||
std::string download_tbz (std::string const& url, key_chain_t const& keyChain, std::string const& destination, std::string& error, double* progress, double progressStart, double progressStop, bool const* stopFlag)
|
||||
{
|
||||
std::string res = NULL_STR;
|
||||
if(CURL* handle = curl_easy_init())
|
||||
{
|
||||
std::string tbzDestination = path::temp("dl_archive_contents");
|
||||
mkdir(tbzDestination.c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH);
|
||||
int tbzInput, tbzOutput;
|
||||
pid_t tbzPid = launch_tbz(tbzDestination, tbzInput, tbzOutput, error);
|
||||
|
||||
std::string tmpPath = path::temp("dl_bytes");
|
||||
int tmpInput = open(tmpPath.c_str(), O_CREAT|O_TRUNC|O_WRONLY, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
|
||||
|
||||
// ========
|
||||
// = Curl =
|
||||
// ========
|
||||
|
||||
user_data_t data(keyChain, progress, progressStart, progressStop, stopFlag, tbzInput, tmpInput);
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, true);
|
||||
curl_easy_setopt(handle, CURLOPT_FAILONERROR, true);
|
||||
curl_easy_setopt(handle, CURLOPT_ENCODING, "");
|
||||
curl_easy_setopt(handle, CURLOPT_USERAGENT, create_agent_info_string().c_str());
|
||||
|
||||
char errorbuf[CURL_ERROR_SIZE];
|
||||
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errorbuf);
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &receive_data);
|
||||
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &data);
|
||||
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &receive_header);
|
||||
curl_easy_setopt(handle, CURLOPT_HEADERDATA, &data);
|
||||
|
||||
struct curl_slist* headers = NULL;
|
||||
std::string const etag = path::get_attr(destination, kHTTPEntityTagAttribute);
|
||||
if(etag != NULL_STR)
|
||||
{
|
||||
headers = curl_slist_append(headers, text::format("If-None-Match: %.*s", (int)etag.size(), etag.data()).c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
|
||||
}
|
||||
|
||||
if(auto proxySettings = get_proxy_settings())
|
||||
{
|
||||
curl_easy_setopt(handle, CURLOPT_PROXY, proxySettings.server.c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_PROXYPORT, proxySettings.port);
|
||||
if(proxySettings.password != NULL_STR)
|
||||
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, (proxySettings.user + ":" + proxySettings.password).c_str());
|
||||
}
|
||||
|
||||
long serverReply = 0;
|
||||
switch(curl_easy_perform(handle))
|
||||
{
|
||||
case 0: curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &serverReply); break;
|
||||
case CURLE_WRITE_ERROR: error = (stopFlag && *stopFlag ? "Download stopped." : errorbuf); break;
|
||||
default: error = errorbuf; break;
|
||||
}
|
||||
|
||||
curl_easy_cleanup(handle);
|
||||
curl_slist_free_all(headers);
|
||||
|
||||
// =============
|
||||
// = Post Curl =
|
||||
// =============
|
||||
|
||||
close(tmpInput);
|
||||
|
||||
bool goodSignature = false;
|
||||
if(serverReply == 200)
|
||||
{
|
||||
if(data.verify_signature.receive_end(error))
|
||||
{
|
||||
goodSignature = true;
|
||||
if(path::swap_and_unlink(tmpPath, destination))
|
||||
{
|
||||
path::set_attr(destination, kHTTPEntityTagAttribute, data.etag);
|
||||
path::set_attr(destination, kHTTPSigneeHeader, data.verify_signature.signee());
|
||||
path::set_attr(destination, kHTTPSignatureHeader, data.verify_signature.signature());
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "error with swap_and_unlink: %s → %s\n", tmpPath.c_str(), destination.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(serverReply == 304)
|
||||
{
|
||||
struct stat buf;
|
||||
int fd = open(destination.c_str(), O_RDONLY);
|
||||
if(fd != -1 && fstat(fd, &buf) != -1)
|
||||
{
|
||||
char bytes[4096];
|
||||
data.total = buf.st_size;
|
||||
while(data.received < data.total && !data.should_stop())
|
||||
{
|
||||
ssize_t len = read(fd, bytes, sizeof(bytes));
|
||||
if(len == -1)
|
||||
break;
|
||||
|
||||
write(tbzInput, bytes, len);
|
||||
data.receive(len);
|
||||
}
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
else if(serverReply != 0)
|
||||
{
|
||||
error = text::format("Unexpected server reply (%ld).", serverReply);
|
||||
}
|
||||
|
||||
unlink(tmpPath.c_str());
|
||||
|
||||
if(finish_tbz(tbzPid, tbzInput, tbzOutput, error))
|
||||
{
|
||||
if(serverReply == 304 || goodSignature)
|
||||
res = tbzDestination;
|
||||
}
|
||||
|
||||
if(res == NULL_STR)
|
||||
path::remove(tbzDestination);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} /* network */
|
||||
13
Frameworks/network/src/download_tbz.h
Normal file
13
Frameworks/network/src/download_tbz.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef NETWORK_DOWNLOAD_TBZ_H_MYCOASC1
|
||||
#define NETWORK_DOWNLOAD_TBZ_H_MYCOASC1
|
||||
|
||||
#include "key_chain.h"
|
||||
#include <oak/misc.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
PUBLIC std::string download_tbz (std::string const& url, key_chain_t const& keyChain, std::string const& destination, std::string& error, double* progress, double progressStart = 0, double progressStop = 1, bool const* stopFlag = NULL);
|
||||
|
||||
} /* network */
|
||||
|
||||
#endif /* end of include guard: NETWORK_DOWNLOAD_TBZ_H_MYCOASC1 */
|
||||
59
Frameworks/network/src/filter_check_signature.cc
Normal file
59
Frameworks/network/src/filter_check_signature.cc
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "filter_check_signature.h"
|
||||
#include <text/decode.h>
|
||||
#include <text/format.h>
|
||||
#include <oak/debug.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
check_signature_t::check_signature_t (key_chain_t const& keyChain, std::string const& signeeHeader, std::string const& signatureHeader) : _key_chain(keyChain), _signee_header(signeeHeader), _signature_header(signatureHeader)
|
||||
{
|
||||
}
|
||||
|
||||
bool check_signature_t::setup ()
|
||||
{
|
||||
return EVP_VerifyInit(&ctx, EVP_dss1()) == 1;
|
||||
}
|
||||
|
||||
bool check_signature_t::receive_header (std::string const& header, std::string const& value)
|
||||
{
|
||||
if(header == _signee_header)
|
||||
_signee = value;
|
||||
else if(header == _signature_header)
|
||||
_signature = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_signature_t::receive_data (char const* buf, size_t len)
|
||||
{
|
||||
return EVP_VerifyUpdate(&ctx, buf, len) == 1;
|
||||
}
|
||||
|
||||
bool check_signature_t::receive_end (std::string& error)
|
||||
{
|
||||
if(_signee == NULL_STR)
|
||||
return (error = "Missing signee."), false;
|
||||
if(_signature == NULL_STR)
|
||||
return (error = "Missing signature."), false;
|
||||
|
||||
if(key_chain_t::key_ptr key = _key_chain.find(_signee))
|
||||
{
|
||||
std::string signature = decode::base64(_signature);
|
||||
if(EVP_VerifyFinal(&ctx, (unsigned char*)&signature[0], signature.size(), *key) == 1)
|
||||
return true;
|
||||
|
||||
error = text::format("Bad signature.");
|
||||
}
|
||||
else
|
||||
{
|
||||
error = text::format("Unknown signee: ‘%s’.", _signee.c_str());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string check_signature_t::name ()
|
||||
{
|
||||
return "signature";
|
||||
}
|
||||
|
||||
} /* network */
|
||||
40
Frameworks/network/src/filter_check_signature.h
Normal file
40
Frameworks/network/src/filter_check_signature.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef PUBKEY_H_VC2ABIZU
|
||||
#define PUBKEY_H_VC2ABIZU
|
||||
|
||||
#include "download.h" // filter_t
|
||||
#include "key_chain.h"
|
||||
|
||||
// ======================
|
||||
// = Validate signature =
|
||||
// ======================
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct PUBLIC check_signature_t : filter_t
|
||||
{
|
||||
check_signature_t (key_chain_t const& keyChain, std::string const& signeeHeader, std::string const& signatureHeader);
|
||||
|
||||
bool setup ();
|
||||
bool receive_header (std::string const& header, std::string const& value);
|
||||
bool receive_data (char const* buf, size_t len);
|
||||
bool receive_end (std::string& error);
|
||||
|
||||
std::string name ();
|
||||
|
||||
std::string const& signee () const { return _signee_header; }
|
||||
std::string const& signature () const { return _signature_header; }
|
||||
|
||||
private:
|
||||
key_chain_t const _key_chain;
|
||||
std::string const _signee_header;
|
||||
std::string const _signature_header;
|
||||
|
||||
EVP_MD_CTX ctx;
|
||||
|
||||
std::string _signee = NULL_STR;
|
||||
std::string _signature = NULL_STR;
|
||||
};
|
||||
|
||||
} /* network */
|
||||
|
||||
#endif /* end of include guard: PUBKEY_H_VC2ABIZU */
|
||||
18
Frameworks/network/src/filter_etag.h
Normal file
18
Frameworks/network/src/filter_etag.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef FILTER_ETAG_H_TKYP6FQQ
|
||||
#define FILTER_ETAG_H_TKYP6FQQ
|
||||
|
||||
#include "download.h" // filter_t
|
||||
|
||||
struct etag_t : filter_t
|
||||
{
|
||||
bool receive_header (std::string const& header, std::string const& value)
|
||||
{
|
||||
if(header == "etag")
|
||||
etag = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string etag = NULL_STR;
|
||||
};
|
||||
|
||||
#endif /* end of include guard: FILTER_ETAG_H_TKYP6FQQ */
|
||||
58
Frameworks/network/src/filter_save.h
Normal file
58
Frameworks/network/src/filter_save.h
Normal file
@@ -0,0 +1,58 @@
|
||||
#ifndef ARCHIVE_H_XHCVW91K
|
||||
#define ARCHIVE_H_XHCVW91K
|
||||
|
||||
#include "download.h" // filter_t
|
||||
#include <io/io.h>
|
||||
#include <OakSystem/application.h>
|
||||
#include <text/format.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct save_t : filter_t
|
||||
{
|
||||
save_t (bool cleanup = true) : _cleanup(cleanup)
|
||||
{
|
||||
path = path::temp("dl_save_filter");
|
||||
}
|
||||
|
||||
~save_t ()
|
||||
{
|
||||
if(_fp)
|
||||
fclose(_fp);
|
||||
|
||||
if(_cleanup)
|
||||
path::remove(path);
|
||||
}
|
||||
|
||||
bool setup ()
|
||||
{
|
||||
return _fp = fopen(path.c_str(), "w");
|
||||
}
|
||||
|
||||
bool receive_data (char const* bytes, size_t len)
|
||||
{
|
||||
return fwrite(bytes, 1, len, _fp) == len;
|
||||
}
|
||||
|
||||
bool receive_end (std::string& error)
|
||||
{
|
||||
bool res = fclose(_fp) == 0;
|
||||
_fp = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string name ()
|
||||
{
|
||||
return "i/o";
|
||||
}
|
||||
|
||||
std::string path;
|
||||
|
||||
private:
|
||||
FILE* _fp = NULL;
|
||||
bool _cleanup;
|
||||
};
|
||||
|
||||
} /* network */
|
||||
|
||||
#endif /* end of include guard: ARCHIVE_H_XHCVW91K */
|
||||
115
Frameworks/network/src/filter_tbz.h
Normal file
115
Frameworks/network/src/filter_tbz.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef EXTRACT_H_L6SJUZHG
|
||||
#define EXTRACT_H_L6SJUZHG
|
||||
|
||||
#include "download.h" // filter_t
|
||||
#include <io/io.h>
|
||||
#include <OakSystem/application.h>
|
||||
#include <OakSystem/process.h>
|
||||
#include <text/format.h>
|
||||
#include <text/trim.h>
|
||||
|
||||
// =======================
|
||||
// = Extract tbz archive =
|
||||
// =======================
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct tbz_t : filter_t
|
||||
{
|
||||
bool setup ()
|
||||
{
|
||||
path = path::temp("dl_tbz_filter");
|
||||
if(mkdir(path.c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH) == 0)
|
||||
return launch_tar();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool receive_data (char const* buf, size_t len)
|
||||
{
|
||||
return write(input[1], buf, len) == len;
|
||||
}
|
||||
|
||||
bool receive_end (std::string& error)
|
||||
{
|
||||
close(input[1]);
|
||||
|
||||
std::string tarOutput;
|
||||
ssize_t len;
|
||||
char buf[512];
|
||||
while((len = read(output[0], buf, sizeof(buf))) > 0)
|
||||
tarOutput.insert(tarOutput.end(), buf, buf + len);
|
||||
close(output[0]);
|
||||
|
||||
int status = 0;
|
||||
if(waitpid(pid, &status, 0) == pid && WIFEXITED(status))
|
||||
{
|
||||
if(WEXITSTATUS(status) == 0 && tarOutput.empty())
|
||||
return true;
|
||||
error = text::format("Corrupt archive.");
|
||||
fprintf(stderr, "tar exit status %d\n%s\n", WEXITSTATUS(status), text::trim(tarOutput).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
error = text::format("Abnormal exit from tar (%d).", status);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name ()
|
||||
{
|
||||
return "tar";
|
||||
}
|
||||
|
||||
std::string path = NULL_STR;
|
||||
|
||||
private:
|
||||
static char const* strip_components_flag ()
|
||||
{
|
||||
SInt32 osVersion = 0;
|
||||
Gestalt(gestaltSystemVersion, &osVersion);
|
||||
return osVersion >= 0x1050 ? "--strip-components" : "--strip-path";
|
||||
}
|
||||
|
||||
bool launch_tar ()
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
pipe(&input[0]);
|
||||
pipe(&output[0]);
|
||||
|
||||
char const* argv[] = { "/usr/bin/tar", "-jxmkC", path.c_str(), strip_components_flag(), "1", NULL };
|
||||
oak::c_array env(oak::basic_environment());
|
||||
pid = vfork();
|
||||
if(pid == 0)
|
||||
{
|
||||
close(0); close(1); close(2);
|
||||
dup(input[0]); dup(output[1]); dup(output[1]);
|
||||
close(input[0]); close(input[1]); close(output[0]); close(output[1]);
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
execve(argv[0], (char* const*)argv, env);
|
||||
_exit(-1);
|
||||
}
|
||||
else if(pid == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
close(input[0]);
|
||||
close(output[1]);
|
||||
fcntl(input[1], F_SETFD, 1);
|
||||
fcntl(output[0], F_SETFD, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int input[2];
|
||||
int output[2];
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
} /* network */
|
||||
|
||||
#endif /* end of include guard: EXTRACT_H_L6SJUZHG */
|
||||
137
Frameworks/network/src/key_chain.cc
Normal file
137
Frameworks/network/src/key_chain.cc
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "key_chain.h"
|
||||
#include <oak/oak.h>
|
||||
#include <plist/plist.h>
|
||||
|
||||
key_chain_t::key_t::key_t (std::string const& identity, std::string const& name, std::string const& key_data) : _identity(identity), _name(name), _key_data(key_data)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
key_chain_t::key_t::key_t (key_t const& rhs) : _identity(rhs._identity), _name(rhs._name), _key_data(rhs._key_data)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
key_chain_t::key_t::~key_t ()
|
||||
{
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void key_chain_t::key_t::init () const
|
||||
{
|
||||
_ssl_key = NULL;
|
||||
_ssl_bio = NULL;
|
||||
_ssl_data = NULL;
|
||||
}
|
||||
|
||||
bool key_chain_t::key_t::setup () const
|
||||
{
|
||||
if(_ssl_key)
|
||||
return true;
|
||||
|
||||
bool res = false;
|
||||
if(_ssl_key = EVP_PKEY_new())
|
||||
{
|
||||
if(_ssl_bio = BIO_new_mem_buf((char*)_key_data.data(), _key_data.size()))
|
||||
{
|
||||
if(_ssl_data = PEM_read_bio_DSA_PUBKEY(_ssl_bio, NULL, NULL, NULL))
|
||||
{
|
||||
if(res = EVP_PKEY_assign_DSA(_ssl_key, _ssl_data) == 1)
|
||||
_ssl_data = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "*** error reading key\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "*** error creating BIO\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "*** error creating PKEY\n");
|
||||
}
|
||||
|
||||
if(!res)
|
||||
cleanup();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void key_chain_t::key_t::cleanup () const
|
||||
{
|
||||
if(_ssl_data)
|
||||
DSA_free(_ssl_data);
|
||||
|
||||
if(_ssl_bio)
|
||||
BIO_free(_ssl_bio);
|
||||
|
||||
if(_ssl_key)
|
||||
EVP_PKEY_free(_ssl_key);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
// =============
|
||||
// = Key Chain =
|
||||
// =============
|
||||
|
||||
void key_chain_t::load (std::string const& path)
|
||||
{
|
||||
keys.clear();
|
||||
|
||||
plist::dictionary_t identities;
|
||||
if(plist::get_key_path(plist::load(path), "identities", identities))
|
||||
{
|
||||
iterate(identity, identities)
|
||||
{
|
||||
std::string name, keyData;
|
||||
if(plist::get_key_path(identity->second, "name", name) && plist::get_key_path(identity->second, "key", keyData))
|
||||
{
|
||||
keys.push_back(key_ptr(new key_t(identity->first, name, keyData)));
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "no name/key in entry:\n%s", to_s(identity->second).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "no identities in ‘%s’\n", path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void key_chain_t::save (std::string const& path) const
|
||||
{
|
||||
plist::dictionary_t identities;
|
||||
iterate(key, keys)
|
||||
{
|
||||
plist::dictionary_t entry;
|
||||
entry.insert(std::make_pair("name", (*key)->name()));
|
||||
entry.insert(std::make_pair("key", (*key)->_key_data));
|
||||
identities.insert(std::make_pair((*key)->identity(), entry));
|
||||
}
|
||||
|
||||
plist::dictionary_t plist;
|
||||
plist.insert(std::make_pair("identities", identities));
|
||||
|
||||
plist::save(path, plist);
|
||||
}
|
||||
|
||||
void key_chain_t::add (key_t const& key)
|
||||
{
|
||||
keys.push_back(key_ptr(new key_t(key.identity(), key.name(), key._key_data)));
|
||||
}
|
||||
|
||||
key_chain_t::key_ptr key_chain_t::find (std::string const& identity) const
|
||||
{
|
||||
iterate(key, keys)
|
||||
{
|
||||
if((*key)->identity() == identity && (*key)->setup())
|
||||
return *key;
|
||||
}
|
||||
return key_ptr();
|
||||
}
|
||||
52
Frameworks/network/src/key_chain.h
Normal file
52
Frameworks/network/src/key_chain.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef KEY_CHAIN_H_VALL5FR2
|
||||
#define KEY_CHAIN_H_VALL5FR2
|
||||
|
||||
#include <oak/misc.h>
|
||||
#include <oak/debug.h>
|
||||
|
||||
struct PUBLIC key_chain_t
|
||||
{
|
||||
WATCH_LEAKS(key_chain_t);
|
||||
|
||||
struct PUBLIC key_t
|
||||
{
|
||||
WATCH_LEAKS(key_chain_t::key_t);
|
||||
|
||||
key_t (std::string const& identity, std::string const& name, std::string const& key_data);
|
||||
key_t (key_t const& rhs);
|
||||
key_t& operator= (key_t const& rhs) = delete;
|
||||
~key_t ();
|
||||
|
||||
std::string const& identity () const { return _identity; }
|
||||
std::string const& name () const { return _name; }
|
||||
|
||||
operator EVP_PKEY* () const { setup(); return _ssl_key; }
|
||||
|
||||
private:
|
||||
friend struct key_chain_t;
|
||||
std::string _identity;
|
||||
std::string _name;
|
||||
std::string _key_data;
|
||||
|
||||
mutable EVP_PKEY* _ssl_key;
|
||||
mutable BIO* _ssl_bio;
|
||||
mutable DSA* _ssl_data;
|
||||
|
||||
void init () const;
|
||||
bool setup () const;
|
||||
void cleanup () const;
|
||||
};
|
||||
|
||||
typedef std::tr1::shared_ptr<key_t> key_ptr;
|
||||
|
||||
void load (std::string const& path);
|
||||
void save (std::string const& path) const;
|
||||
|
||||
void add (key_t const& key);
|
||||
key_ptr find (std::string const& identity) const;
|
||||
|
||||
private:
|
||||
std::vector<key_ptr> keys;
|
||||
};
|
||||
|
||||
#endif /* end of include guard: KEY_CHAIN_H_VALL5FR2 */
|
||||
26
Frameworks/network/src/network.cc
Normal file
26
Frameworks/network/src/network.cc
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "network.h"
|
||||
|
||||
namespace network
|
||||
{
|
||||
bool can_reach_host (char const* host)
|
||||
{
|
||||
#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
|
||||
SCNetworkConnectionFlags flags;
|
||||
return SCNetworkCheckReachabilityByName(host, &flags) && (flags & kSCNetworkFlagsReachable);
|
||||
#else
|
||||
bool res = false;
|
||||
if(SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, host))
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
if(SCNetworkReachabilityGetFlags(ref, &flags))
|
||||
{
|
||||
if(flags & kSCNetworkReachabilityFlagsReachable)
|
||||
res = true;
|
||||
}
|
||||
CFRelease(ref);
|
||||
}
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
} /* network */
|
||||
18
Frameworks/network/src/network.h
Normal file
18
Frameworks/network/src/network.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef NETWORK_H_L3XXH7J6
|
||||
#define NETWORK_H_L3XXH7J6
|
||||
|
||||
#include "constants.h"
|
||||
#include "download.h"
|
||||
#include "filter_save.h"
|
||||
#include "filter_tbz.h"
|
||||
#include "filter_check_signature.h"
|
||||
#include "filter_etag.h"
|
||||
#include <oak/misc.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
PUBLIC bool can_reach_host (char const* host);
|
||||
|
||||
} /* network */
|
||||
|
||||
#endif /* end of include guard: NETWORK_H_L3XXH7J6 */
|
||||
82
Frameworks/network/src/post.cc
Normal file
82
Frameworks/network/src/post.cc
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "post.h"
|
||||
#include "proxy.h"
|
||||
#include "user_agent.h"
|
||||
#include <text/encode.h>
|
||||
#include <oak/oak.h>
|
||||
|
||||
// size_t receive_data (void* ptr, size_t size, size_t nmemb, void* udata)
|
||||
// {
|
||||
// std::string& buf = *(std::string*)udata;
|
||||
// buf.insert(buf.size(), (const char*)ptr, size * nmemb);
|
||||
// return size * nmemb;
|
||||
// }
|
||||
|
||||
long post_to_server (std::string const& url, std::map<std::string, std::string> const& payload)
|
||||
{
|
||||
struct curl_httppost* formpost = NULL;
|
||||
struct curl_httppost* lastptr = NULL;
|
||||
|
||||
iterate(pair, payload)
|
||||
{
|
||||
if(pair->second.size() < 2 || pair->second[0] != '@')
|
||||
{
|
||||
curl_formadd(&formpost, &lastptr,
|
||||
CURLFORM_PTRNAME, pair->first.data(),
|
||||
CURLFORM_NAMELENGTH, pair->first.size(),
|
||||
CURLFORM_PTRCONTENTS, pair->second.data(),
|
||||
CURLFORM_CONTENTSLENGTH, pair->second.size(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_formadd(&formpost, &lastptr,
|
||||
CURLFORM_PTRNAME, pair->first.data(),
|
||||
CURLFORM_NAMELENGTH, pair->first.size(),
|
||||
CURLFORM_FILE, pair->second.substr(1).c_str(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
}
|
||||
|
||||
long serverReply = 0;
|
||||
if(CURL* handle = curl_easy_init())
|
||||
{
|
||||
std::string const userAgent = create_agent_info_string();
|
||||
|
||||
char errorbuf[CURL_ERROR_SIZE];
|
||||
// std::string head = "", body = "";
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_HTTPPOST, formpost);
|
||||
curl_easy_setopt(handle, CURLOPT_USERAGENT, userAgent.c_str());
|
||||
|
||||
curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, errorbuf);
|
||||
curl_easy_setopt(handle, CURLOPT_FAILONERROR, true);
|
||||
|
||||
// curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, &receive_data);
|
||||
// curl_easy_setopt(handle, CURLOPT_HEADERDATA, &head);
|
||||
//
|
||||
// curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &receive_data);
|
||||
// curl_easy_setopt(handle, CURLOPT_WRITEDATA, &body);
|
||||
|
||||
if(proxy_settings_t const& proxySettings = get_proxy_settings())
|
||||
{
|
||||
curl_easy_setopt(handle, CURLOPT_PROXY, proxySettings.server.c_str());
|
||||
curl_easy_setopt(handle, CURLOPT_PROXYPORT, proxySettings.port);
|
||||
if(proxySettings.password != NULL_STR)
|
||||
curl_easy_setopt(handle, CURLOPT_PROXYUSERPWD, (proxySettings.user + ":" + proxySettings.password).c_str());
|
||||
}
|
||||
|
||||
CURLcode rc = curl_easy_perform(handle);
|
||||
if(rc == 0)
|
||||
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &serverReply);
|
||||
else fprintf(stderr, "curl error (%d): %s\n", rc, errorbuf);
|
||||
|
||||
// fprintf(stderr, "HEAD:\n%s\n", head.c_str());
|
||||
// fprintf(stderr, "BODY:\n%s\n", body.c_str());
|
||||
|
||||
curl_easy_cleanup(handle);
|
||||
}
|
||||
|
||||
curl_formfree(formpost);
|
||||
return serverReply;
|
||||
}
|
||||
8
Frameworks/network/src/post.h
Normal file
8
Frameworks/network/src/post.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef POST_H_HD2H7K9B
|
||||
#define POST_H_HD2H7K9B
|
||||
|
||||
#include <oak/misc.h>
|
||||
|
||||
PUBLIC long post_to_server (std::string const& url, std::map<std::string, std::string> const& payload);
|
||||
|
||||
#endif /* end of include guard: POST_H_HD2H7K9B */
|
||||
146
Frameworks/network/src/proxy.cc
Normal file
146
Frameworks/network/src/proxy.cc
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "proxy.h"
|
||||
#include <plist/plist.h>
|
||||
#include <oak/debug.h>
|
||||
#include <cf/cf.h>
|
||||
|
||||
OAK_DEBUG_VAR(Proxy);
|
||||
|
||||
static proxy_settings_t user_pw_settings (std::string const& server, UInt32 port)
|
||||
{
|
||||
D(DBF_Proxy, bug("%s:%zu\n", server.c_str(), (size_t)port););
|
||||
std::string user = NULL_STR, pw = NULL_STR;
|
||||
|
||||
FourCharCode protocol = kSecProtocolTypeHTTPProxy;
|
||||
SecKeychainAttribute attrs[3] = {
|
||||
{ kSecProtocolItemAttr, sizeof(protocol), &protocol, },
|
||||
{ kSecPortItemAttr, sizeof(port), &port, },
|
||||
{ kSecServerItemAttr, server.size(), (void*)server.data(), }
|
||||
};
|
||||
SecKeychainAttributeList attrList = { sizeofA(attrs), attrs };
|
||||
|
||||
SecKeychainSearchRef searchRef = NULL;
|
||||
if(SecKeychainSearchCreateFromAttributes(NULL, kSecInternetPasswordItemClass, &attrList, &searchRef) == noErr)
|
||||
{
|
||||
SecKeychainItemRef item;
|
||||
while(user == NULL_STR && SecKeychainSearchCopyNext(searchRef, &item) == noErr)
|
||||
{
|
||||
UInt32 tag = kSecAccountItemAttr;
|
||||
UInt32 format = CSSM_DB_ATTRIBUTE_FORMAT_STRING;
|
||||
SecKeychainAttributeInfo info = { 1, &tag, &format };
|
||||
|
||||
void* data = NULL;
|
||||
UInt32 dataLen = 0;
|
||||
|
||||
SecKeychainAttributeList* authAttrList = NULL;
|
||||
if(SecKeychainItemCopyAttributesAndData(item, &info, NULL, &authAttrList, &dataLen, &data) == noErr)
|
||||
{
|
||||
ASSERT(authAttrList->count == 1 && authAttrList->attr->tag == kSecAccountItemAttr);
|
||||
user = std::string((char const*)authAttrList->attr->data, ((char const*)authAttrList->attr->data) + authAttrList->attr->length);
|
||||
pw = std::string((char const*)data, ((char const*)data) + dataLen);
|
||||
SecKeychainItemFreeContent(authAttrList, data);
|
||||
D(DBF_Proxy, bug("found user ‘%s’\n", user.c_str()););
|
||||
}
|
||||
else
|
||||
{
|
||||
D(DBF_Proxy, bug("unable to obtain attributes from key chain entry\n"););
|
||||
}
|
||||
|
||||
CFRelease(item);
|
||||
}
|
||||
CFRelease(searchRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
D(DBF_Proxy, bug("failed creating key chain search\n"););
|
||||
}
|
||||
|
||||
return proxy_settings_t(true, server, port, user, pw);
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
|
||||
static void pac_proxy_callback (void* client, CFArrayRef proxyList, CFErrorRef error)
|
||||
{
|
||||
proxy_settings_t& settings = *(proxy_settings_t*)client;
|
||||
settings.enabled = true;
|
||||
|
||||
if(error)
|
||||
{
|
||||
CFStringRef str = CFErrorCopyDescription(error);
|
||||
fprintf(stderr, "proxy error: %s\n", cf::to_s(str).c_str());
|
||||
CFRelease(str);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!proxyList)
|
||||
return;
|
||||
|
||||
for(CFIndex i = 0; i < CFArrayGetCount(proxyList); ++i)
|
||||
{
|
||||
plist::dictionary_t dict = plist::convert((CFDictionaryRef)CFArrayGetValueAtIndex(proxyList, i));
|
||||
D(DBF_Proxy, bug("%s\n", to_s(dict).c_str()););
|
||||
|
||||
int32_t port;
|
||||
std::string type;
|
||||
if(plist::get_key_path(dict, cf::to_s(kCFProxyTypeKey), type) && type == cf::to_s(kCFProxyTypeHTTP) && plist::get_key_path(dict, cf::to_s(kCFProxyHostNameKey), settings.server) && plist::get_key_path(dict, cf::to_s(kCFProxyPortNumberKey), port))
|
||||
{
|
||||
settings.port = port;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
proxy_settings_t get_proxy_settings ()
|
||||
{
|
||||
proxy_settings_t res(false);
|
||||
|
||||
CFDictionaryRef tmp = SCDynamicStoreCopyProxies(NULL);
|
||||
plist::dictionary_t const& plist = plist::convert(tmp);
|
||||
D(DBF_Proxy, bug("%s\n", to_s(plist).c_str()););
|
||||
|
||||
bool enabled = false;
|
||||
if(plist::get_key_path(plist, cf::to_s(kSCPropNetProxiesHTTPEnable), enabled) && enabled)
|
||||
{
|
||||
D(DBF_Proxy, bug("proxy enabled: %s\n", BSTR(enabled)););
|
||||
std::string host; int32_t port;
|
||||
if(plist::get_key_path(plist, cf::to_s(kSCPropNetProxiesHTTPProxy), host) && plist::get_key_path(plist, cf::to_s(kSCPropNetProxiesHTTPPort), port))
|
||||
res = user_pw_settings(host, port);
|
||||
}
|
||||
else if(plist::get_key_path(plist, cf::to_s(kSCPropNetProxiesProxyAutoConfigEnable), enabled) && enabled)
|
||||
{
|
||||
D(DBF_Proxy, bug("pac enabled: %s\n", BSTR(enabled)););
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
|
||||
std::string pacString;
|
||||
if(plist::get_key_path(plist, cf::to_s(kSCPropNetProxiesProxyAutoConfigURLString), pacString))
|
||||
{
|
||||
D(DBF_Proxy, bug("pac script: %s\n", pacString.c_str()););
|
||||
CFStreamClientContext context = { 0, &res, NULL, NULL, NULL };
|
||||
|
||||
CFURLRef pacURL = CFURLCreateWithString(kCFAllocatorDefault, cf::wrap(pacString), NULL);
|
||||
CFURLRef targetURL = CFURLCreateWithString(kCFAllocatorDefault, CFSTR("http://macromates.com/"), NULL);
|
||||
CFRunLoopSourceRef runLoopSource = CFNetworkExecuteProxyAutoConfigurationURL(pacURL, targetURL, &pac_proxy_callback, &context);
|
||||
CFRelease(targetURL);
|
||||
CFRelease(pacURL);
|
||||
|
||||
CFStringRef runLoopMode = CFSTR("OakRunPACScriptRunLoopMode");
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, runLoopMode);
|
||||
while(!res.enabled)
|
||||
CFRunLoopRunInMode(runLoopMode, 0.1, TRUE);
|
||||
|
||||
if(CFRunLoopSourceIsValid(runLoopSource))
|
||||
CFRunLoopSourceInvalidate(runLoopSource);
|
||||
CFRelease(runLoopSource);
|
||||
|
||||
if(res.server == NULL_STR)
|
||||
res.enabled = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if(plist::get_key_path(plist, cf::to_s(kSCPropNetProxiesProxyAutoDiscoveryEnable), enabled) && enabled)
|
||||
{
|
||||
D(DBF_Proxy, bug("auto discovery enabled: %s\n", BSTR(enabled)););
|
||||
}
|
||||
CFRelease(tmp);
|
||||
|
||||
return res;
|
||||
}
|
||||
20
Frameworks/network/src/proxy.h
Normal file
20
Frameworks/network/src/proxy.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef PROXY_H_S8ZWZPU8
|
||||
#define PROXY_H_S8ZWZPU8
|
||||
|
||||
#include <oak/misc.h>
|
||||
|
||||
struct proxy_settings_t
|
||||
{
|
||||
proxy_settings_t (bool enabled = false, std::string const& server = NULL_STR, long port = 0, std::string const& user = NULL_STR, std::string const& password = NULL_STR) : enabled(enabled), server(server), port(port), user(user), password(password) { }
|
||||
EXPLICIT operator bool () const { return enabled; }
|
||||
|
||||
bool enabled;
|
||||
std::string server;
|
||||
long port;
|
||||
std::string user;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
PUBLIC proxy_settings_t get_proxy_settings ();
|
||||
|
||||
#endif /* end of include guard: PROXY_H_S8ZWZPU8 */
|
||||
83
Frameworks/network/src/tbz.cc
Normal file
83
Frameworks/network/src/tbz.cc
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "tbz.h"
|
||||
#include <text/format.h>
|
||||
#include <OakSystem/process.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
static char const* strip_components_flag ()
|
||||
{
|
||||
SInt32 osVersion = 0;
|
||||
Gestalt(gestaltSystemVersion, &osVersion);
|
||||
return osVersion >= 0x1050 ? "--strip-components" : "--strip-path";
|
||||
}
|
||||
|
||||
pid_t launch_tbz (std::string const& dest, int& input, int& output, std::string& error)
|
||||
{
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
int in[2], out[2];
|
||||
pipe(&in[0]);
|
||||
pipe(&out[0]);
|
||||
|
||||
char const* argv[] = { "/usr/bin/tar", "-jxmkC", dest.c_str(), strip_components_flag(), "1", NULL };
|
||||
oak::c_array env(oak::basic_environment());
|
||||
pid_t pid = vfork();
|
||||
if(pid == 0)
|
||||
{
|
||||
close(0); close(1); close(2);
|
||||
dup(in[0]); dup(out[1]); dup(out[1]);
|
||||
close(in[0]); close(in[1]); close(out[0]); close(out[1]);
|
||||
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
|
||||
execve(argv[0], (char* const*)argv, env);
|
||||
_exit(-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
close(in[0]);
|
||||
close(out[1]);
|
||||
|
||||
if(pid == -1)
|
||||
{
|
||||
close(in[1]);
|
||||
close(out[0]);
|
||||
|
||||
error = text::format("Error launching tar: %s", strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
fcntl(input = in[1], F_SETFD, 1);
|
||||
fcntl(output = out[0], F_SETFD, 1);
|
||||
}
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
bool finish_tbz (pid_t pid, int& input, int& output, std::string& error)
|
||||
{
|
||||
close(input);
|
||||
|
||||
std::string tbzOut;
|
||||
ssize_t len;
|
||||
char bytes[512];
|
||||
while((len = read(output, bytes, sizeof(bytes))) > 0)
|
||||
tbzOut.insert(tbzOut.end(), bytes, bytes + len);
|
||||
close(output);
|
||||
|
||||
int status = 0;
|
||||
if(waitpid(pid, &status, 0) == pid && WIFEXITED(status))
|
||||
{
|
||||
if(WEXITSTATUS(status) == 0 && tbzOut.empty())
|
||||
return true;
|
||||
error = "Corrupt archive.";
|
||||
// error = text::format("Unexpected exit code from tar (%d)\n%s\n", WEXITSTATUS(status), text::trim(tbzOut).c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
error = text::format("Abnormal exit from tar (%d).\n", status);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} /* network */
|
||||
13
Frameworks/network/src/tbz.h
Normal file
13
Frameworks/network/src/tbz.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef NETWORK_TBZ_H_NEU56OWR
|
||||
#define NETWORK_TBZ_H_NEU56OWR
|
||||
|
||||
#include <oak/misc.h>
|
||||
|
||||
namespace network
|
||||
{
|
||||
PUBLIC pid_t launch_tbz (std::string const& dest, int& input, int& output, std::string& error);
|
||||
PUBLIC bool finish_tbz (pid_t pid, int& input, int& output, std::string& error);
|
||||
|
||||
} /* network */
|
||||
|
||||
#endif /* end of include guard: NETWORK_TBZ_H_NEU56OWR */
|
||||
43
Frameworks/network/src/user_agent.cc
Normal file
43
Frameworks/network/src/user_agent.cc
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "user_agent.h"
|
||||
#include <OakSystem/application.h>
|
||||
#include <text/format.h>
|
||||
#include <plist/plist.h>
|
||||
#include <io/path.h>
|
||||
|
||||
static std::string hardware_info (int field, bool integer = false)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t bufSize = sizeof(buf);
|
||||
int request[] = { CTL_HW, field };
|
||||
|
||||
if(sysctl(request, sizeofA(request), buf, &bufSize, NULL, 0) != -1)
|
||||
{
|
||||
if(integer && bufSize == 4)
|
||||
return text::format("%d", *(int*)buf);
|
||||
return std::string(buf, buf + bufSize - 1);
|
||||
}
|
||||
|
||||
return "???";
|
||||
}
|
||||
|
||||
static std::string user_uuid ()
|
||||
{
|
||||
oak::uuid_t uuid;
|
||||
return plist::get_key_path(plist::load(path::join(path::home(), "Library/Preferences/com.apple.CrashReporter.plist")), "userUUID", uuid) ? to_s(uuid) : "???";
|
||||
}
|
||||
|
||||
std::string create_agent_info_string ()
|
||||
{
|
||||
SInt32 osMajor, osMinor, osBugfix;
|
||||
Gestalt(gestaltSystemVersionMajor, &osMajor);
|
||||
Gestalt(gestaltSystemVersionMinor, &osMinor);
|
||||
Gestalt(gestaltSystemVersionBugFix, &osBugfix);
|
||||
|
||||
return text::format("%s/%s/%s %zu.%zu.%zu/%s/%s/%s", oak::application_t::name().c_str(), oak::application_t::revision().c_str(), user_uuid().c_str(),
|
||||
(size_t)osMajor, (size_t)osMinor, (size_t)osBugfix,
|
||||
hardware_info(HW_MACHINE).c_str(),
|
||||
hardware_info(HW_MODEL).c_str(),
|
||||
hardware_info(HW_NCPU, true).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
6
Frameworks/network/src/user_agent.h
Normal file
6
Frameworks/network/src/user_agent.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef USER_AGENT_H_S2EYS361
|
||||
#define USER_AGENT_H_S2EYS361
|
||||
|
||||
std::string create_agent_info_string ();
|
||||
|
||||
#endif /* end of include guard: USER_AGENT_H_S2EYS361 */
|
||||
Reference in New Issue
Block a user