11 #ifdef SL_BUILD_WITH_OPENSSL
30 memset(&sa, 0,
sizeof(sa));
39 int Socket::connectTo(
string ip,
42 struct addrinfo* res = NULL;
43 int ret = getaddrinfo(ip.c_str(), NULL, NULL, &res);
50 if (res->ai_family == AF_INET)
52 memset(&sa, 0,
sizeof(sa));
53 sa.sin_family = AF_INET;
54 sa.sin_addr = (((
struct sockaddr_in*)res->ai_addr))->sin_addr;
55 sa.sin_port = htons(port);
58 else if (res->ai_family == AF_INET6)
60 memset(&sa6, 0,
sizeof(sa6));
61 sa6.sin6_family = AF_INET6;
62 sa6.sin6_addr = (((
struct sockaddr_in6*)res->ai_addr))->sin6_addr;
63 sa6.sin6_port = htons(port);
64 addrlen =
sizeof(sa6);
72 fd = (int)socket(res->ai_family, SOCK_STREAM, 0);
75 Utils::log(
"Socket ",
"Error creating socket");
85 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (
const char*)&tv,
sizeof tv);
88 if (connect(fd, (
struct sockaddr*)&sa, addrlen))
91 Utils::log(
"Socket ",
"Error connecting to server.\n");
104 int Socket::sendData(
const char* data,
107 int len = (int)send(fd, data, (
int)size, 0);
120 void Socket::disconnect()
133 # define BUFFER_SIZE 500
134 int Socket::receive(
function<
int(
char* data,
int size)> dataCB,
int max)
138 char buf[BUFFER_SIZE];
141 if (max != 0 && max - n <= BUFFER_SIZE)
143 len = (int)recv(fd, buf, max - n, 0);
147 if (errno != ECONNRESET)
148 len = (int)recv(fd, buf, max - n, 0);
150 len = (int)recv(fd, buf, max - n, 0);
156 if (dataCB(buf, len) != 0)
162 len = (int)recv(fd, buf, BUFFER_SIZE, 0);
167 if (errno != ECONNRESET)
168 len = (int)recv(fd, buf, BUFFER_SIZE, 0);
170 len = (int)recv(fd, buf, BUFFER_SIZE, 0);
176 if (dataCB(buf, len) != 0)
180 }
while (!_interrupt && len > 0);
191 int SecureSocket::connectTo(
string ip,
int port)
193 Socket::connectTo(ip, port);
194 SSL_load_error_strings();
195 const SSL_METHOD* meth = TLS_client_method();
196 SSL_CTX* ctx = SSL_CTX_new(meth);
200 Utils::log(
"SecureSocket",
"Error creating SSL");
205 int err = SSL_connect(ssl);
208 Utils::log(
"SecureSocket",
"Error creating SSL connection. err = %d", err);
221 int SecureSocket::sendData(
const char* data,
size_t size)
223 int len = SSL_write(ssl, data, (
int)size);
226 int err = SSL_get_error(ssl, len);
229 case SSL_ERROR_WANT_WRITE:
231 case SSL_ERROR_WANT_READ:
233 case SSL_ERROR_ZERO_RETURN:
234 case SSL_ERROR_SYSCALL:
248 int SecureSocket::receive(
function<
int(
char* data,
int size)> dataCB,
253 char buf[BUFFER_SIZE];
256 if (max != 0 && max - n <= BUFFER_SIZE)
258 len = SSL_read(ssl, buf, max - n);
261 len = SSL_read(ssl, buf, max - n);
264 Utils::log(
"SecureSocket",
"SSL_read return -1");
269 if (dataCB(buf, len) != 0)
275 len = SSL_read(ssl, buf, BUFFER_SIZE);
278 len = SSL_read(ssl, buf, BUFFER_SIZE);
281 Utils::log(
"SecureSocket",
"SSL_read return -1");
287 if (dataCB(buf, len) != 0)
290 }
while (!_interrupt && len > 0);
300 void SecureSocket::disconnect()
311 DNSRequest::DNSRequest(
string host)
313 char s[250] = {
'\0'};
315 struct hostent* h =
nullptr;
316 struct addrinfo* res = NULL;
317 int ret = getaddrinfo(host.c_str(), NULL, NULL, &res);
318 bool prefer_ipv4 =
true;
328 for (
struct addrinfo* p = res; p != NULL; p = p->ai_next) {
331 if (p->ai_family == AF_INET) {
338 if (p->ai_family == AF_INET6) {
345 if (res->ai_family == AF_INET)
347 struct sockaddr_in sa;
348 memset(&sa, 0,
sizeof(sa));
349 sa.sin_family = AF_INET;
350 sa.sin_addr = (((
struct sockaddr_in*)res->ai_addr))->sin_addr;
351 inet_ntop(AF_INET, &(((
struct sockaddr_in*)res->ai_addr))->sin_addr,
s, maxlen);
354 h = gethostbyaddr((
const char*)&sa.sin_addr,
sizeof(sa.sin_addr), sa.sin_family);
356 h = gethostbyaddr(&sa.sin_addr,
sizeof(sa.sin_addr), sa.sin_family);
359 else if (res->ai_family == AF_INET6)
361 struct sockaddr_in6 sa;
362 memset(&sa, 0,
sizeof(sa));
363 sa.sin6_family = AF_INET6;
364 sa.sin6_addr = (((
struct sockaddr_in6*)res->ai_addr))->sin6_addr;
365 inet_ntop(AF_INET6, &(((
struct sockaddr_in6*)res->ai_addr))->sin6_addr,
s, maxlen);
367 h = gethostbyaddr((
const char*)&sa.sin6_addr,
sizeof(sa.sin6_addr), sa.sin6_family);
369 h = gethostbyaddr(&sa.sin6_addr,
sizeof(sa.sin6_addr), sa.sin6_family);
373 if (h !=
nullptr && h->h_length > 0)
374 hostname = string(h->h_name);
387 string DNSRequest::getAddr()
396 string DNSRequest::getHostname()
406 static string base64(
const string data)
408 static constexpr
char sEncodingTable[] = {
'A',
473 size_t in_len = data.size();
474 size_t out_len = 4 * ((in_len + 2) / 3);
475 string ret(out_len,
'\0');
477 char* p =
const_cast<char*
>(ret.c_str());
479 for (i = 0; i < in_len - 2; i += 3)
481 *p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
482 *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
483 *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
484 *p++ = sEncodingTable[data[i + 2] & 0x3F];
488 *p++ = sEncodingTable[(data[i] >> 2) & 0x3F];
489 if (i == (in_len - 1))
491 *p++ = sEncodingTable[((data[i] & 0x3) << 4)];
496 *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
497 *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)];
513 static void parseURL(
string url,
525 if (url.find(
"https://") != string::npos)
530 else if (url.find(
"http://") != string::npos)
535 size_t pos = url.find(
"/", offset);
536 if (pos != string::npos)
538 path = url.substr(pos, url.length() - pos);
539 host = url.substr(offset, pos - offset);
544 host = url.substr(offset);
554 HttpUtils::GetRequest::GetRequest(
string url,
561 parseURL(url, host, path, isSecure);
563 DNSRequest dns(host);
564 host = dns.getHostname();
565 addr = dns.getAddr();
567 request =
"GET " + path +
" HTTP/1.1\r\n";
572 "Authorization: Basic " +
573 base64(user +
":" + pwd) +
"\r\n";
575 request = request +
"Host: " + host +
"\r\n\r\n";
579 s =
new SecureSocket();
594 int HttpUtils::GetRequest::processHttpHeaders(std::vector<char>& data)
596 string h = string(data.begin(),
598 (data.size() > 1000 ? 1000 : data.size()));
599 size_t contentPos = h.find(
"\r\n\r\n");
600 if (contentPos == string::npos)
602 Utils::log(
"HttpUtils",
"Invalid http response");
605 headers = string(data.begin(), data.begin() + contentPos);
608 std::cout << headers << std::endl;
609 size_t pos = headers.find(
"HTTP");
611 if (pos != string::npos)
613 string str = headers.substr(pos, headers.find(
"\r", pos) - pos);
615 for (codeIdx = 0; codeIdx < str.length() && !isdigit(str.at(codeIdx)); codeIdx++)
618 if (codeIdx == str.length())
620 Utils::log(
"HttpUtils",
"Invalid http response");
624 size_t endCodeIdx = str.find(
" ", codeIdx);
626 stoi(str.substr(codeIdx, endCodeIdx - codeIdx));
627 status = str.substr(endCodeIdx);
631 pos = headers.find(
"Content-Length:");
632 if (pos != string::npos)
634 string str = headers.substr(pos + 16,
635 headers.find(
"\r", pos + 16) - pos - 16);
639 pos = headers.find(
"Content-Type:");
640 if (pos != string::npos)
642 string str = headers.substr(pos + 13,
643 headers.find(
"\r", pos + 13) - pos - 13);
646 return (
int)contentPos;
653 int HttpUtils::GetRequest::send()
655 if (
s->connectTo(addr, port) < 0)
661 s->sendData(request.c_str(), request.length() + 1);
663 std::vector<char>* v = &firstBytes;
665 ret =
s->receive([v](
char* buf,
int size) ->
int
667 v->reserve(v->size() + size);
668 copy(&buf[0], &buf[size], back_inserter(*v));
672 contentOffset = processHttpHeaders(firstBytes);
683 int HttpUtils::GetRequest::getContent(
function<
int(
char* buf,
int size)> contentCB)
685 if (
s->connectTo(addr, port) < 0)
691 s->sendData(request.c_str(), request.length() + 1);
692 std::vector<char>* v = &firstBytes;
694 s->receive([v](
char* buf,
int size) ->
int
696 v->reserve(v->size() + size);
697 copy(&buf[0], &buf[size], back_inserter(*v));
701 int ret =
s->receive(contentCB, 0);
710 std::vector<string> HttpUtils::GetRequest::getListing()
712 std::vector<char> content;
713 getContent([&content](
char* buf,
int size) ->
int
715 content.reserve(content.size() + size);
716 copy(&buf[0], &buf[size], back_inserter(content));
719 string c = string(content.data());
720 std::vector<string> listing;
726 pos = c.find(
"<", pos);
727 end = c.find(
">", pos) + 1;
728 if (pos == string::npos || end == string::npos)
731 string token = c.substr(pos + 1, end - pos - 2);
734 if (
string(token.begin(), token.begin() + 2) ==
"a ")
736 size_t href = token.find(
"href");
737 if (href != string::npos)
739 href = token.find(
"\"", href);
740 size_t hrefend = token.find(
"\"", href + 1);
741 if (token.find(
"?") == string::npos)
743 token = token.substr(href + 1, hrefend - href - 1);
744 if (token ==
"../" || token ==
".")
749 listing.push_back(token);
768 int HttpUtils::download(
string url,
769 function<
int(
string path,
string file,
size_t size)> processFile,
770 function<
int(
char* data,
int size)> writeChunk,
771 function<
int(
string)> processDir,
776 HttpUtils::GetRequest req = HttpUtils::GetRequest(url, user, pwd);
778 return SERVER_NOT_REACHABLE;
781 if (req.contentType ==
"text/html")
783 if (url.back() !=
'/')
786 if (!processDir(base))
787 return CANT_CREATE_DIR;
789 std::vector<string> listing = req.getListing();
791 for (
string str : listing)
793 if (str.at(0) !=
'/')
794 return download(url + str,
805 string file = url.substr(url.rfind(
"/") + 1);
807 int possibleSplit = (int)base.rfind(file);
808 if (possibleSplit != string::npos && base.size() - possibleSplit - 1 == file.size())
809 base = base.substr(0, possibleSplit);
813 if (processFile(base, file, req.contentLength) != 0)
814 return CANT_CREATE_FILE;
816 if (req.getContent(writeChunk) == -1)
817 return CONNECTION_CLOSED;
832 int HttpUtils::download(
string url,
836 function<
int(
size_t curr,
size_t filesize)> progress)
839 size_t totalBytes = 0;
840 size_t writtenByte = 0;
842 bool dstIsDir =
true;
852 [&fs, &totalBytes, &dst, &dstIsDir](
string path,
859 fs.open(path + file, std::ios::out | std::ios::binary);
861 fs.open(dst, std::ios::out | std::ios::binary);
863 catch (std::exception& e)
865 std::cerr << e.what() <<
'\n';
871 [&fs, progress, &writtenByte, &totalBytes](
char* data,
int size) ->
int
877 fs.write(data, size);
878 if (progress && progress(writtenByte += size, totalBytes) != 0)
884 catch (
const std::exception& e)
886 std::cerr << e.what() <<
'\n';
894 progress(totalBytes, totalBytes);
899 [&dstIsDir](
string dir) ->
int
917 int HttpUtils::download(
string url,
919 function<
int(
size_t curr,
size_t filesize)> progress)
921 return download(url, dst,
"",
"", progress);
925 int HttpUtils::length(
string url,
string user,
string pwd)
927 HttpUtils::GetRequest req = HttpUtils::GetRequest(url, user, pwd);
931 return (
int)req.contentLength;
The SLScene class represents the top level instance holding the scene structure.
void close(SLIOStream *stream)
Closes and deletes a stream.
string unifySlashes(const string &inputDir, bool withTrailingSlash)
Returns the inputDir string with unified forward slashes, e.g.: "dirA/dirB/".
bool fileExists(const string &pathfilename)
Returns true if a file exists.
bool makeDir(const string &path)
Creates a directory with given path.
string trimString(const string &s, const string &drop)
Trims a string at both end.
bool makeDirRecurse(std::string path)
void log(const char *tag, const char *format,...)
logs a formatted string platform independently